Massive retooling of drive-watching code to reduce complexity and thus, (hopefully) bugs.

Squished the CD-exchanging bug which I thought I got last checkin.
Added a minor bug which will get fixed later -- clicking on a data track will cause the track menu and the track name to disappear


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@14239 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
DarkWyrm
2005-09-24 21:13:21 +00:00
parent f282ec8116
commit 9b56c14770
13 changed files with 377 additions and 1838 deletions
-558
View File
@@ -1,558 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Debug.h>
#include <Entry.h>
#include <Directory.h>
#include <Path.h>
#include <errno.h>
#include "scsi.h"
#include "CDEngine.h"
#include "PlayList.h"
static PlayList sPlayList;
CDAudioDevice gCDDevice;
const bigtime_t kPulseRate = 500000;
PeriodicWatcher::PeriodicWatcher(void)
{
}
BHandler *
PeriodicWatcher::RecipientHandler() const
{
return engine;
}
void
PeriodicWatcher::DoPulse()
{
// control the period here
if(UpdateState())
Notify();
}
void
PeriodicWatcher::UpdateNow()
{
if(UpdateState())
Notify();
}
PlayState::PlayState(CDEngine *engine)
: oldState(kInit),
fEngine(engine)
{
}
bool
PlayState::UpdateState(void)
{
CDState state = gCDDevice.GetState();
if(state == kStopped)
{
if(fEngine->GetState() == kPlaying)
{
// this means we have come to the end of a song, but probably not
// the last song in the playlist
int16 next = sPlayList.GetNextTrack();
if(next > 0)
{
gCDDevice.Play(next);
return CurrentState(kPlaying);
}
}
else
{
int16 count = gCDDevice.CountTracks();
if(count != sPlayList.TrackCount())
sPlayList.SetTrackCount(count);
// TODO: Is this correct?
/*
int16 start = gCDDevice.GetTrack();
if(start != sPlayList.StartingTrack())
sPlayList.SetStartingTrack(start);
*/
return CurrentState(kStopped);
}
}
if(state == kPlaying && fEngine->GetState() == kStopped)
{
// This happens when the CD drive is started by R5's player
// or this app is started while the drive is playing. We should
// reset the to start at the current track and finish at the
// last one and send a notification.
sPlayList.SetTrackCount(gCDDevice.CountTracks());
sPlayList.SetStartingTrack(gCDDevice.GetTrack());
return CurrentState(kPlaying);
}
return CurrentState(state);
}
CDState
PlayState::GetState() const
{
return oldState;
}
bool
PlayState::CurrentState(CDState newState)
{
if (newState != oldState)
{
oldState = newState;
return true;
}
return false;
}
TrackState::TrackState(void)
: currentTrack(0)
{
}
int32
TrackState::GetTrack() const
{
return currentTrack;
}
bool
TrackState::UpdateState()
{
// It's possible that the state of the cd drive was changed by outside means
// As a result, we want to make sure this hasn't happened. If it has, then we
// need to update the playlist's position.
int16 cdTrack, count;
if(gCDDevice.GetState() == kPlaying)
{
cdTrack = gCDDevice.GetTrack();
if(cdTrack != sPlayList.GetCurrentTrack())
sPlayList.SetCurrentTrack(cdTrack);
return CurrentState(cdTrack, trackCount);
}
// If we're not playing, just monitor the current track in the playlist
count = gCDDevice.CountTracks();
if(count>0)
{
cdTrack = sPlayList.GetCurrentTrack();
}
else
{
sPlayList.SetTrackCount(0);
cdTrack=-1;
}
return CurrentState(cdTrack,count);
}
int32
TrackState::GetNumTracks() const
{
return gCDDevice.CountTracks();
}
bool
TrackState::CurrentState(int32 track, int32 count)
{
if( (track != currentTrack) || (count != trackCount) )
{
currentTrack = track;
trackCount = count;
return true;
}
return false;
}
bool
TimeState::UpdateState()
{
// check the current CD time and force a notification to
// be sent if it changed from last time
// currently only supports global time
cdaudio_time track;
cdaudio_time disc;
if(gCDDevice.GetTime(track,disc))
{
cdaudio_time ttrack;
cdaudio_time tdisc;
int16 ctrack = gCDDevice.GetTrack();
gCDDevice.GetTimeForDisc(tdisc);
gCDDevice.GetTimeForTrack(ctrack,ttrack);
return CurrentState(track,ttrack,disc,tdisc);
}
else
{
track.minutes = -1;
track.seconds = -1;
disc.minutes = -1;
disc.seconds = -1;
return CurrentState(disc,disc,track,track);
}
}
bool
TimeState::CurrentState(cdaudio_time tracktime, cdaudio_time totaltracktime,
cdaudio_time disctime, cdaudio_time totaldisctime)
{
if (disctime.minutes == fDiscTime.minutes && disctime.seconds == fDiscTime.seconds)
return false;
fDiscTime = disctime;
fTotalDiscTime = totaldisctime;
fTrackTime = tracktime;
fTotalTrackTime = totaltracktime;
return true;
}
void
TimeState::GetDiscTime(int32 &minutes, int32 &seconds) const
{
minutes = fDiscTime.minutes;
seconds = fDiscTime.seconds;
}
void
TimeState::GetTotalDiscTime(int32 &minutes, int32 &seconds) const
{
minutes = fTotalDiscTime.minutes;
seconds = fTotalDiscTime.seconds;
}
void
TimeState::GetTrackTime(int32 &minutes, int32 &seconds) const
{
minutes = fTrackTime.minutes;
seconds = fTrackTime.seconds;
}
void
TimeState::GetTotalTrackTime(int32 &minutes, int32 &seconds) const
{
minutes = fTotalTrackTime.minutes;
seconds = fTotalTrackTime.seconds;
}
CDContentWatcher::CDContentWatcher(void)
: cddbQuery("us.freedb.org", 888),
discID(-1)
{
}
bool
CDContentWatcher::GetContent(BString *title, vector<BString> *tracks)
{
if(discID == -1)
{
title->SetTo("");
tracks->empty();
tracks->push_back("");
return true;
}
return cddbQuery.GetTitles(title, tracks, 1000000);
}
bool
CDContentWatcher::UpdateState()
{
int32 newDiscID = -1;
if (engine->PlayStateWatcher()->GetState() == kNoCD)
{
if(discID != -1)
{
discID = -1;
return true;
}
else
return false;
}
// Check the table of contents to see if the new one is different
// from the old one whenever there is a CD in the drive
newDiscID = gCDDevice.GetDiscID();
if (discID == newDiscID)
return false;
// We have changed CDs, so we are not ready until the CDDB lookup finishes
cddbQuery.SetToCD(gCDDevice.GetDrivePath());
// Notify when the query is ready
if(cddbQuery.Ready())
{
discID = newDiscID;
return true;
}
return false;
}
VolumeState::VolumeState(void)
: fVolume(-1)
{
}
bool
VolumeState::UpdateState(void)
{
uint8 volume = gCDDevice.GetVolume();
if(fVolume == volume)
return false;
fVolume = volume;
return true;
}
int32
VolumeState::GetVolume(void) const
{
return fVolume;
}
CDEngine::CDEngine(void)
: BHandler("CDEngine"),
playState(this),
fEngineState(kStopped)
{
sPlayList.SetTrackCount(gCDDevice.CountTracks());
}
CDEngine::~CDEngine()
{
}
void
CDEngine::AttachedToLooper(BLooper *looper)
{
looper->AddHandler(this);
playState.AttachedToLooper(this);
trackState.AttachedToLooper(this);
timeState.AttachedToLooper(this);
volumeState.AttachedToLooper(this);
contentWatcher.AttachedToLooper(this);
}
void
CDEngine::Pause()
{
gCDDevice.Pause();
fEngineState = gCDDevice.GetState();
}
void
CDEngine::Play()
{
if(fEngineState == kPaused)
{
gCDDevice.Resume();
fEngineState = gCDDevice.GetState();
}
else
if(fEngineState == kPlaying)
{
Pause();
}
else
{
gCDDevice.Play(sPlayList.GetCurrentTrack());
fEngineState = gCDDevice.GetState();
}
}
void
CDEngine::Stop()
{
fEngineState = kStopped;
gCDDevice.Stop();
}
void
CDEngine::Eject()
{
gCDDevice.Eject();
fEngineState = gCDDevice.GetState();
}
void
CDEngine::SkipOneForward()
{
int16 track = sPlayList.GetNextTrack();
if(track <= 0)
{
// force a "wrap around" when possible. This makes it
// possible for the user to be able to, for example, jump
// back to the first track from the last one with 1 button push
track = sPlayList.GetFirstTrack();
if(track <= 0)
return;
}
CDState state = gCDDevice.GetState();
if(state == kPlaying)
gCDDevice.Play(track);
if(state == kPaused)
{
gCDDevice.Play(track);
gCDDevice.Pause();
}
trackState.UpdateNow();
}
void
CDEngine::SkipOneBackward()
{
int16 track = sPlayList.GetPreviousTrack();
if(track <= 0)
{
// force a "wrap around" when possible. This way the user
// can search backwards to get to a later track
track = sPlayList.GetLastTrack();
if(track <= 0)
return;
}
CDState state = gCDDevice.GetState();
if(state == kPlaying)
gCDDevice.Play(track);
if(state == kPaused)
{
gCDDevice.Play(track);
gCDDevice.Pause();
}
trackState.UpdateNow();
}
void
CDEngine::StartSkippingBackward()
{
gCDDevice.StartRewind();
}
void
CDEngine::StartSkippingForward()
{
gCDDevice.StartFastFwd();
}
void
CDEngine::StopSkipping()
{
gCDDevice.StopFastFwd();
}
void
CDEngine::SelectTrack(int32 trackNumber)
{
sPlayList.SetCurrentTrack(trackNumber);
if(GetState() == kPlaying)
gCDDevice.Play(trackNumber);
trackState.UpdateNow();
}
void
CDEngine::SetVolume(uint8 value)
{
gCDDevice.SetVolume(value);
}
void
CDEngine::ToggleShuffle(void)
{
if(sPlayList.IsShuffled())
{
// If already in random mode, we will play to the end of the cd
int16 track = sPlayList.GetCurrentTrack();
sPlayList.SetShuffle(false);
sPlayList.SetStartingTrack(track);
sPlayList.SetTrackCount(gCDDevice.CountTracks());
}
else
{
// Not shuffled, so we will play the entire CD and randomly pick
sPlayList.SetTrackCount(gCDDevice.CountTracks());
sPlayList.SetShuffle(true);
}
}
bool
CDEngine::IsShuffled(void)
{
return sPlayList.IsShuffled();
}
void
CDEngine::ToggleRepeat(void)
{
if(sPlayList.IsLoop())
sPlayList.SetLoop(false);
else
sPlayList.SetLoop(true);
}
bool
CDEngine::IsRepeated(void)
{
return sPlayList.IsLoop();
}
void
CDEngine::DoPulse()
{
// this is the CDEngine's heartbeat; Since it is a Notifier, it checks if
// any values changed since the last hearbeat and sends notices to observers
bigtime_t time = system_time();
if (time > lastPulse && time < lastPulse + kPulseRate)
return;
// every pulse rate have all the different state watchers check the
// curent state and send notifications if anything changed
lastPulse = time;
playState.DoPulse();
trackState.DoPulse();
timeState.DoPulse();
volumeState.DoPulse();
contentWatcher.DoPulse();
}
void
CDEngine::MessageReceived(BMessage *message)
{
// handle observing
if (!Notifier::HandleObservingMessages(message) &&
!CDEngineFunctorFactory::DispatchIfFunctionObject(message))
BHandler::MessageReceived(message);
}
-232
View File
@@ -1,232 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
// This defines an engine that tracks the state of the CD player
// and supports the CD player control and status calls
#ifndef __CD_ENGINE__
#define __CD_ENGINE__
#include <Looper.h>
#include <String.h>
#include <View.h>
#include <vector>
#include "CDAudioDevice.h"
#include "Observer.h"
#include "FunctionObjectMessage.h"
#include "CDDBSupport.h"
#include "PlayList.h"
class CDEngine;
// watcher sits somewhere were it can get pulses and makes sure
// notices get sent if state changes
class PeriodicWatcher : public Notifier
{
public:
PeriodicWatcher(void);
virtual ~PeriodicWatcher() {}
void DoPulse();
void UpdateNow();
void AttachedToLooper(CDEngine *engine)
{ this->engine = engine; }
virtual BHandler *RecipientHandler() const;
protected:
virtual bool UpdateState() = 0;
CDEngine *engine;
};
// this watcher sends notices to observers that are interrested
// about play state changes
class PlayState : public PeriodicWatcher
{
public:
PlayState(CDEngine *engine);
CDState GetState() const;
private:
bool UpdateState();
bool CurrentState(CDState);
CDState oldState;
CDEngine *fEngine;
};
// this watcher sends notices to observers that are interested
// about changes in the current track
class TrackState : public PeriodicWatcher
{
public:
TrackState(void);
int32 GetTrack() const;
int32 GetNumTracks() const;
private:
bool UpdateState();
bool CurrentState(int32,int32);
int32 currentTrack;
int32 trackCount;
};
// this watcher sends notices to observers that are interested
// about changes in the current time
class TimeState : public PeriodicWatcher
{
public:
TimeState(void) : PeriodicWatcher() { }
void GetDiscTime(int32 &minutes, int32 &seconds) const;
void GetTotalDiscTime(int32 &minutes, int32 &seconds) const;
void GetTrackTime(int32 &minutes, int32 &seconds) const;
void GetTotalTrackTime(int32 &minutes, int32 &seconds) const;
private:
bool UpdateState();
bool CurrentState(cdaudio_time tracktime,
cdaudio_time totaltracktime,
cdaudio_time disctime,
cdaudio_time totaldisctime);
cdaudio_time fDiscTime,
fTotalDiscTime,
fTrackTime,
fTotalTrackTime;
};
class CDContentWatcher : public PeriodicWatcher
{
public:
CDContentWatcher(void);
bool GetContent(BString *title, vector<BString> *tracks);
private:
bool UpdateState();
CDDBQuery cddbQuery;
int32 discID;
};
// this watcher sends notices to observers that are interested
// about changes in the current volume
// currently not used yet
class VolumeState : public PeriodicWatcher
{
public:
VolumeState(void);
int32 GetVolume() const;
private:
bool UpdateState();
int32 fVolume;
};
// The CD engine defines all the different CD control calls; also,
// it hosts the different state watchers and helps them send notices
// to observers about the CD state changes
class CDEngine : public BHandler
{
public:
CDEngine(void);
virtual ~CDEngine();
// observing support
virtual void MessageReceived(BMessage *);
void AttachedToLooper(BLooper *);
void DoPulse();
// control calls
void Play();
void Pause();
void Stop();
void Eject();
void SkipOneForward();
void SkipOneBackward();
void StartSkippingBackward();
void StartSkippingForward();
void StopSkipping();
void SelectTrack(int32);
void SetVolume(uint8 value);
void ToggleShuffle(void);
bool IsShuffled(void);
void ToggleRepeat(void);
bool IsRepeated(void);
CDState GetState(void) const { return fEngineState; }
// to find the current Track, you may call the GetTrack function
// TrackState defines
TrackState *TrackStateWatcher()
{ return &trackState; }
// to find the current play state, you may call the GetState function
// PlayState defines
PlayState *PlayStateWatcher()
{ return &playState; }
// to find the current location on the CD, you may call the GetTime function
// TimeState defines
TimeState *TimeStateWatcher()
{ return &timeState; }
CDContentWatcher *ContentWatcher()
{ return &contentWatcher; }
// to find the current location on the CD, you may call the GetVolume function
// VolumeState defines
VolumeState *VolumeStateWatcher()
{ return &volumeState; }
private:
PlayState playState;
TrackState trackState;
TimeState timeState;
VolumeState volumeState;
CDContentWatcher contentWatcher;
bigtime_t lastPulse;
CDState fEngineState;
};
// some function object glue
class CDEngineFunctorFactory : public FunctorFactoryCommon
{
public:
static BMessage *NewFunctorMessage(void (CDEngine::*func)(),
CDEngine *target)
{
PlainMemberFunctionObject<void (CDEngine::*)(),
CDEngine> tmp(func, target);
return NewMessage(&tmp);
}
static BMessage *NewFunctorMessage(void (CDEngine::*func)(ulong),
CDEngine *target, ulong param)
{
SingleParamMemberFunctionObject<void (CDEngine::*)(ulong),
CDEngine, ulong> tmp(func, target, param);
return NewMessage(&tmp);
}
};
extern CDAudioDevice gCDDevice;
#endif
+358 -207
View File
@@ -48,17 +48,18 @@ enum
}; };
CDPlayer::CDPlayer(BRect frame, const char *name, uint32 resizeMask, uint32 flags) CDPlayer::CDPlayer(BRect frame, const char *name, uint32 resizeMask, uint32 flags)
: BView(frame, name, resizeMask, flags | B_FRAME_EVENTS) : BView(frame, name, resizeMask, flags | B_FRAME_EVENTS),
fCDQuery("freedb.freedb.org")
{ {
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
// TODO: Support multiple CD drives fDiscID=-1;
engine = new CDEngine; fVolume=255;
fUseTrackNames=false;
BuildGUI(); BuildGUI();
CDAudioDevice cd; if(fCDDrive.CountDrives()<1)
if(cd.CountDrives()<1)
{ {
BAlert *alert = new BAlert("CDPlayer","It appears that there are no CD drives on your" BAlert *alert = new BAlert("CDPlayer","It appears that there are no CD drives on your"
"computer or there is no system software to support one." "computer or there is no system software to support one."
@@ -66,12 +67,15 @@ CDPlayer::CDPlayer(BRect frame, const char *name, uint32 resizeMask, uint32 flag
alert->Go(); alert->Go();
be_app->PostMessage(B_QUIT_REQUESTED); be_app->PostMessage(B_QUIT_REQUESTED);
} }
fWindowState=fCDDrive.GetState();
fVolumeSlider->SetValue(fCDDrive.GetVolume());
WatchCDState();
} }
CDPlayer::~CDPlayer() CDPlayer::~CDPlayer()
{ {
engine->Stop(); fCDDrive.Stop();
delete engine;
} }
void CDPlayer::BuildGUI(void) void CDPlayer::BuildGUI(void)
@@ -120,7 +124,7 @@ void CDPlayer::BuildGUI(void)
fCurrentTrack = new BStringView( view->Bounds(),"TrackNumber","Track:",B_FOLLOW_ALL); fCurrentTrack = new BStringView( view->Bounds(),"TrackNumber","Track:",B_FOLLOW_ALL);
view->AddChild(fCurrentTrack); view->AddChild(fCurrentTrack);
fCurrentTrack->SetHighColor(fPlayColor); fCurrentTrack->SetHighColor(fStopColor);
fCurrentTrack->SetFont(be_bold_font); fCurrentTrack->SetFont(be_bold_font);
r.OffsetBy(0, r.Height() + 5); r.OffsetBy(0, r.Height() + 5);
@@ -147,15 +151,15 @@ void CDPlayer::BuildGUI(void)
fDiscTime->SetHighColor(120,120,255); fDiscTime->SetHighColor(120,120,255);
fDiscTime->SetFont(be_bold_font); fDiscTime->SetFont(be_bold_font);
fVolume = new BSlider( BRect(0,0,75,30), "VolumeSlider", "Volume", new BMessage(M_SET_VOLUME),0,255); fVolumeSlider = new BSlider( BRect(0,0,75,30), "VolumeSlider", "Volume", new BMessage(M_SET_VOLUME),0,255);
fVolume->MoveTo(5, Bounds().bottom - 10 - fVolume->Frame().Height()); fVolumeSlider->MoveTo(5, Bounds().bottom - 10 - fVolumeSlider->Frame().Height());
AddChild(fVolume); AddChild(fVolumeSlider);
fStop = new DrawButton( BRect(0,0,1,1), "Stop", BTranslationUtils::GetBitmap(B_PNG_FORMAT,"stop_up"), fStop = new DrawButton( BRect(0,0,1,1), "Stop", BTranslationUtils::GetBitmap(B_PNG_FORMAT,"stop_up"),
BTranslationUtils::GetBitmap(B_PNG_FORMAT,"stop_down"), new BMessage(M_STOP), BTranslationUtils::GetBitmap(B_PNG_FORMAT,"stop_down"), new BMessage(M_STOP),
B_FOLLOW_BOTTOM, B_WILL_DRAW); B_FOLLOW_BOTTOM, B_WILL_DRAW);
fStop->ResizeToPreferred(); fStop->ResizeToPreferred();
fStop->MoveTo(fVolume->Frame().right + 10, Bounds().bottom - 5 - fStop->Frame().Height()); fStop->MoveTo(fVolumeSlider->Frame().right + 10, Bounds().bottom - 5 - fStop->Frame().Height());
fStop->SetDisabled(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"stop_disabled")); fStop->SetDisabled(BTranslationUtils::GetBitmap(B_PNG_FORMAT,"stop_disabled"));
AddChild(fStop); AddChild(fStop);
@@ -252,70 +256,123 @@ CDPlayer::MessageReceived(BMessage *msg)
{ {
case M_SET_VOLUME: case M_SET_VOLUME:
{ {
engine->SetVolume(fVolume->Value()); fCDDrive.SetVolume(fVolumeSlider->Value());
break; break;
} }
case M_STOP: case M_STOP:
{ {
if(engine->GetState()==kPlaying) fWindowState=kStopped;
fPlay->SetState(0); fCDDrive.Stop();
engine->Stop();
break; break;
} }
case M_PLAY: case M_PLAY:
{ {
// If we are currently playing, then we will be showing // If we are currently playing, then we will be showing
// the pause images and will want to switch back to the play images // the pause images and will want to switch back to the play images
if(engine->GetState()==kPlaying) if(fWindowState==kPlaying)
{ {
fPlay->SetState(0); fWindowState=kPaused;
engine->Pause(); fCDDrive.Pause();
}
else
if(fWindowState==kPaused)
{
fWindowState=kPlaying;
fCDDrive.Resume();
} }
else else
{ {
fPlay->SetState(1); fWindowState=kPlaying;
engine->Play(); fCDDrive.Play(fPlayList.GetCurrentTrack());
} }
break; break;
} }
case M_SELECT_TRACK: case M_SELECT_TRACK:
{ {
engine->SelectTrack(fTrackMenu->Value()+1); fPlayList.SetCurrentTrack(fTrackMenu->Value()+1);
break; fWindowState=kPlaying;
} fCDDrive.Play(fPlayList.GetCurrentTrack());
case M_NEXT_TRACK:
{
engine->SkipOneForward();
break;
}
case M_PREV_TRACK:
{
engine->SkipOneBackward();
break; break;
} }
case M_EJECT: case M_EJECT:
{ {
engine->Eject(); fCDDrive.Eject();
break;
}
case M_NEXT_TRACK:
{
int16 next = fPlayList.GetNextTrack();
if(next <= 0)
{
// force a "wrap around" when possible. This makes it
// possible for the user to be able to, for example, jump
// back to the first track from the last one with 1 button push
next = fPlayList.GetFirstTrack();
}
if(next > 0)
{
CDState state = fCDDrive.GetState();
if(state == kPlaying)
fCDDrive.Play(next);
else
if(state == kPaused)
{
fCDDrive.Play(next);
fCDDrive.Pause();
}
else
fPlayList.SetCurrentTrack(next);
// Force an update for better responsiveness
WatchCDState();
}
break;
}
case M_PREV_TRACK:
{
int16 prev = fPlayList.GetPreviousTrack();
if(prev <= 0)
{
// force a "wrap around" when possible. This makes it
// possible for the user to be able to, for example, jump
// back to the first track from the last one with 1 button push
prev = fPlayList.GetLastTrack();
}
if(prev > 0)
{
CDState state = fCDDrive.GetState();
if(state == kPlaying)
fCDDrive.Play(prev);
else
if(state == kPaused)
{
fCDDrive.Play(prev);
fCDDrive.Pause();
}
else
fPlayList.SetCurrentTrack(prev);
// Force an update for better responsiveness
WatchCDState();
}
break; break;
} }
case M_FFWD: case M_FFWD:
{ {
if(fFastFwd->Value() == B_CONTROL_ON) if(fFastFwd->Value() == B_CONTROL_ON)
engine->StartSkippingForward(); fCDDrive.StartFastFwd();
else else
engine->StopSkipping(); fCDDrive.StopFastFwd();
break; break;
} }
case M_REWIND: case M_REWIND:
{ {
if(fRewind->Value() == B_CONTROL_ON) if(fRewind->Value() == B_CONTROL_ON)
engine->StartSkippingBackward(); fCDDrive.StartRewind();
else else
engine->StopSkipping(); fCDDrive.StopRewind();
break; break;
} }
case M_SAVE: case M_SAVE:
@@ -326,131 +383,40 @@ CDPlayer::MessageReceived(BMessage *msg)
case M_SHUFFLE: case M_SHUFFLE:
{ {
engine->ToggleShuffle(); if(fPlayList.IsShuffled())
if(engine->IsShuffled() && fShuffle->GetState()==0)
{ {
fShuffle->SetState(1); int16 track = fPlayList.GetCurrentTrack();
fPlayList.SetShuffle(false);
fPlayList.SetStartingTrack(track);
fPlayList.SetTrackCount(fCDDrive.CountTracks());
fShuffle->SetState(0);
} }
else else
{ {
if(fShuffle->GetState()==1) fPlayList.SetTrackCount(fCDDrive.CountTracks());
fShuffle->SetState(0); fPlayList.SetShuffle(true);
fShuffle->SetState(1);
} }
break; break;
} }
case M_REPEAT: case M_REPEAT:
{ {
engine->ToggleRepeat(); if(fPlayList.IsLoop())
if(engine->IsRepeated() && fRepeat->GetState()==0)
{ {
fRepeat->SetState(1); fPlayList.SetLoop(false);
fRepeat->SetState(0);
} }
else else
{ {
if(fRepeat->GetState()==1) fPlayList.SetLoop(true);
fRepeat->SetState(0); fRepeat->SetState(1);
} }
break; break;
} }
default:
{
if (!Observer::HandleObservingMessages(msg))
{
// just support observing messages
BView::MessageReceived(msg);
break;
}
}
}
}
void
CDPlayer::NoticeChange(Notifier *notifier)
{
PlayState *ps;
TrackState *trs;
TimeState *tms;
CDContentWatcher *ccw;
VolumeState *vs;
ps = dynamic_cast<PlayState *>(notifier);
trs = dynamic_cast<TrackState *>(notifier);
tms = dynamic_cast<TimeState *>(notifier);
ccw = dynamic_cast<CDContentWatcher *>(notifier);
vs = dynamic_cast<VolumeState *>(notifier);
if(ps)
{
AdjustButtonStates();
HandlePlayState();
}
else
if(trs)
{
if(fTrackMenu->CountItems() != engine->TrackStateWatcher()->GetNumTracks())
fTrackMenu->SetItemCount(engine->TrackStateWatcher()->GetNumTracks());
fTrackMenu->SetValue(engine->TrackStateWatcher()->GetTrack()-1);
UpdateCDInfo();
}
else
if(tms)
{
UpdateTimeInfo();
}
else
if(ccw)
{
UpdateCDInfo();
}
else
if(vs)
{
fVolume->SetValue(engine->VolumeStateWatcher()->GetVolume());
}
}
void
CDPlayer::HandlePlayState(void)
{
switch(engine->PlayStateWatcher()->GetState())
{
case kNoCD:
{
fCurrentTrack->SetHighColor(fStopColor);
fCurrentTrack->Invalidate();
break;
}
case kStopped:
{
fCurrentTrack->SetHighColor(fStopColor);
fCurrentTrack->Invalidate();
break;
}
case kPaused:
{
fCurrentTrack->SetHighColor(fPlayColor);
break;
}
case kPlaying:
{
fCurrentTrack->SetHighColor(fPlayColor);
fCurrentTrack->Invalidate();
break;
}
case kSkipping:
{
fCurrentTrack->SetHighColor(fStopColor);
break;
}
default: default:
{ {
break; BView::MessageReceived(msg);
break;
} }
} }
} }
@@ -458,7 +424,7 @@ CDPlayer::HandlePlayState(void)
void void
CDPlayer::AdjustButtonStates(void) CDPlayer::AdjustButtonStates(void)
{ {
CDState state = gCDDevice.GetState(); /* CDState state = gCDDevice.GetState();
if(state==kNoCD) if(state==kNoCD)
{ {
@@ -485,56 +451,13 @@ CDPlayer::AdjustButtonStates(void)
fPlay->SetState(1); fPlay->SetState(1);
else else
fPlay->SetState(0); fPlay->SetState(0);
} */
void
CDPlayer::UpdateCDInfo(void)
{
BString CDName, currentTrackName;
vector<BString> trackNames;
int32 currentTrack = engine->TrackStateWatcher()->GetTrack();
bool trackresult = engine->ContentWatcher()->GetContent(&CDName,&trackNames);
if(currentTrack < 0)
{
fCDTitle->SetText("");
fCurrentTrack->SetText("");
return;
}
if(currentTrack == 0)
currentTrack++;
if(trackresult)
{
if(CDName.CountChars()<1)
{
// if the CD name is NULL, then it means we have no disc in the drive.
fCDTitle->SetText("");
fCurrentTrack->SetText("");
}
else
{
currentTrackName << "Track " << currentTrack << ": " << trackNames[ currentTrack - 1];
fCurrentTrack->SetText(currentTrackName.String());
fCDTitle->SetText(CDName.String());
}
return;
}
else
{
fCDTitle->SetText("Audio CD");
currentTrackName << "Track " << currentTrack;
fCurrentTrack->SetText(currentTrackName.String());
}
} }
void void
CDPlayer::UpdateTimeInfo(void) CDPlayer::UpdateTimeInfo(void)
{ {
/*
int32 min,sec; int32 min,sec;
char string[1024]; char string[1024];
@@ -561,21 +484,13 @@ CDPlayer::UpdateTimeInfo(void)
else else
sprintf(string,"Track --:-- / --:--"); sprintf(string,"Track --:-- / --:--");
fTrackTime->SetText(string); fTrackTime->SetText(string);
*/
} }
void void
CDPlayer::AttachedToWindow() CDPlayer::AttachedToWindow()
{ {
fVolumeSlider->SetTarget(this);
// start observing
engine->AttachedToLooper(Window());
StartObserving(engine->TrackStateWatcher());
StartObserving(engine->PlayStateWatcher());
StartObserving(engine->ContentWatcher());
StartObserving(engine->TimeStateWatcher());
StartObserving(engine->VolumeStateWatcher());
fVolume->SetTarget(this);
fStop->SetTarget(this); fStop->SetTarget(this);
fPlay->SetTarget(this); fPlay->SetTarget(this);
fNextTrack->SetTarget(this); fNextTrack->SetTarget(this);
@@ -619,7 +534,243 @@ CDPlayer::FrameResized(float new_width, float new_height)
void void
CDPlayer::Pulse() CDPlayer::Pulse()
{ {
engine->DoPulse(); WatchCDState();
}
void CDPlayer::WatchCDState(void)
{
// One watcher function to rule them all
// first, watch the one setting independent of having a CD: volume
uint8 drivevolume = fCDDrive.GetVolume();
if(fVolume == drivevolume)
{
fVolume=drivevolume;
fVolumeSlider->SetValue(fVolume);
}
// Second, establish whether or not we have a CD in the drive
CDState playstate = fCDDrive.GetState();
if(playstate == kNoCD)
{
// Yes, we have no bananas!
if(fWindowState != kNoCD)
{
// We have just discovered that we have no bananas
fWindowState = kNoCD;
// Because we are changing play states, we will need to update the GUI
fDiscID=-1;
fCDTitle->SetText("No CD");
fCurrentTrack->SetText("");
fCurrentTrack->SetHighColor(fStopColor);
fCurrentTrack->Invalidate();
fTrackMenu->SetItemCount(0);
fTrackTime->SetText("Track --:-- / --:--");
fDiscTime->SetText("Disc --:-- / --:--");
fPlayList.SetTrackCount(0);
fPlayList.SetStartingTrack(1);
fPlayList.SetCurrentTrack(1);
if(fPlay->GetState()==1)
fPlay->SetState(0);
}
else
{
// No change in the app's play state, so do nothing
}
return;
}
//------------------------------------------------------------------------------------------------
// Now otherwise handle the play state
if(playstate == kStopped)
{
if(fWindowState == kPlaying)
{
// This means that the drive finished playing the song, so get the next one
// from the list and play it
int16 next = fPlayList.GetNextTrack();
if(next > 0)
fCDDrive.Play(next);
}
if(fPlay->GetState()==1)
{
fPlay->SetState(0);
fCurrentTrack->SetHighColor(fStopColor);
fCurrentTrack->Invalidate();
}
}
else
if(playstate == kPlaying)
{
if(fPlay->GetState()==0)
fPlay->SetState(1);
fCurrentTrack->SetHighColor(fPlayColor);
fCurrentTrack->Invalidate();
}
else
if(playstate == kPaused)
{
fPlay->SetState(0);
}
//------------------------------------------------------------------------------------------------
// If we got this far, then there must be a CD in the drive. The next order on the agenda
// is to find out which CD it is
int32 discid = fCDDrive.GetDiscID();
bool update_track_gui=false;
if(discid != fDiscID)
{
update_track_gui = true;
// Apparently the disc has changed since we last looked.
if(fCDQuery.CurrentDiscID()!=discid)
{
fCDQuery.SetToCD(fCDDrive.GetDrivePath());
}
if(fCDQuery.Ready())
{
fDiscID = discid;
// Note that we only update the CD title for now. We still need a track number
// in order to update the display for the selected track
if(fCDQuery.GetTitles(&fCDName, &fTrackNames, 1000000))
{
fCDTitle->SetText(fCDName.String());
fUseTrackNames=true;
}
else
{
fCDName="Audio CD";
fCDTitle->SetText("Audio CD");
fUseTrackNames=false;
}
}
}
//------------------------------------------------------------------------------------------------
// Now that we know which CD it is, update the track info
int16 drivecount = fCDDrive.CountTracks();
int16 drivetrack = fCDDrive.GetTrack();
int16 playlisttrack = fPlayList.GetCurrentTrack();
int16 playlistcount = fPlayList.TrackCount();
if(playstate == kPlaying)
{
// The main thing is that we need to make sure that the playlist and the drive's track
// stay in sync. The CD's track may have been changed by an outside source, so if
// the drive is playing, check for playlist sync.
if(playlisttrack != drivetrack)
{
playlisttrack = drivetrack;
fPlayList.SetTrackCount(drivecount);
fPlayList.SetCurrentTrack(drivetrack);
}
update_track_gui=true;
}
else
{
if(playlistcount != drivecount)
{
// This happens only when CDs are changed
if(drivecount<0)
{
// There is no CD in the drive. The playlist needs to have its track
// count set to 0 and it also needs to be rewound.
fPlayList.SetStartingTrack(1);
fPlayList.SetTrackCount(0);
playlisttrack=1;
playlistcount=0;
}
else
{
// Two possible cases here: playlist is empty or playlist has a different
// number of tracks. In either case, the playlist needs to be reinitialized
// to the current track data
fPlayList.SetStartingTrack(1);
fPlayList.SetTrackCount(drivecount);
playlisttrack=fPlayList.GetCurrentTrack();
playlistcount=drivecount;
}
}
else
{
// CD has not changed, so check for change in tracks
if(playlisttrack != drivetrack)
{
update_track_gui=true;
}
else
{
// do nothing. Everything is hunky-dory
}
}
}
if(update_track_gui)
{
BString currentTrackName;
if(playlisttrack >= 0)
{
int16 whichtrack = playlisttrack;
if(whichtrack == 0)
whichtrack++;
if(fUseTrackNames && fTrackNames.size()>0)
currentTrackName << "Track " << whichtrack << ": " << fTrackNames[ whichtrack - 1];
else
currentTrackName << "Track " << whichtrack;
fCurrentTrack->SetText(currentTrackName.String());
fTrackMenu->SetItemCount(playlistcount);
fTrackMenu->SetValue(playlisttrack-1);
}
else
{
fCurrentTrack->SetText("");
fTrackMenu->SetItemCount(0);
fTrackMenu->SetValue(1);
}
}
//------------------------------------------------------------------------------------------------
// Now update the time info
cdaudio_time tracktime;
cdaudio_time disctime;
cdaudio_time tracktotal;
cdaudio_time disctotal;
char timestring[1024];
if(fCDDrive.GetTime(tracktime, disctime))
{
fCDDrive.GetTimeForDisc(disctotal);
sprintf(timestring,"Disc %ld:%.2ld / %ld:%.2ld",disctime.minutes,disctime.seconds,
disctotal.minutes,disctotal.seconds);
fDiscTime->SetText(timestring);
fCDDrive.GetTimeForTrack(playlisttrack,tracktotal);
sprintf(timestring,"Track %ld:%.2ld / %ld:%.2ld",tracktime.minutes,tracktime.seconds,
tracktotal.minutes,tracktotal.seconds);
fTrackTime->SetText(timestring);
}
else
{
fTrackTime->SetText("Track --:-- / --:--");
fDiscTime->SetText("Disc --:-- / --:--");
}
} }
class CDPlayerWindow : public BWindow class CDPlayerWindow : public BWindow
@@ -631,7 +782,7 @@ public:
CDPlayerWindow::CDPlayerWindow(void) CDPlayerWindow::CDPlayerWindow(void)
: BWindow(BRect (100, 100, 610, 200), "CD Player", B_TITLED_WINDOW, B_NOT_V_RESIZABLE | : BWindow(BRect (100, 100, 610, 200), "CD Player", B_TITLED_WINDOW, B_NOT_V_RESIZABLE |
B_NOT_ZOOMABLE) B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS)
{ {
float wmin,wmax,hmin,hmax; float wmin,wmax,hmin,hmax;
+16 -13
View File
@@ -16,15 +16,16 @@
#include <TextControl.h> #include <TextControl.h>
#include <StringView.h> #include <StringView.h>
#include "Observer.h"
#include "CDEngine.h"
#include "TrackMenu.h" #include "TrackMenu.h"
#include "CDAudioDevice.h"
#include "CDDBSupport.h"
#include "PlayList.h"
class DrawButton; class DrawButton;
class DoubleShotDrawButton; class DoubleShotDrawButton;
class TwoStateDrawButton; class TwoStateDrawButton;
class CDPlayer : public BView, private Observer class CDPlayer : public BView
{ {
public: public:
CDPlayer(BRect frame, const char *name, CDPlayer(BRect frame, const char *name,
@@ -40,19 +41,11 @@ public:
virtual void FrameResized(float new_width, float new_height); virtual void FrameResized(float new_width, float new_height);
virtual void MessageReceived(BMessage *); virtual void MessageReceived(BMessage *);
// observing overrides
virtual BHandler *RecipientHandler() const { return (BHandler *)this; }
virtual void NoticeChange(Notifier *);
private: private:
void HandlePlayState(void); void WatchCDState(void);
void UpdateCDInfo(void);
void UpdateTimeInfo(void); void UpdateTimeInfo(void);
void AdjustButtonStates(void); void AdjustButtonStates(void);
CDEngine *engine;
DrawButton *fStop, DrawButton *fStop,
*fNextTrack, *fNextTrack,
*fPrevTrack, *fPrevTrack,
@@ -67,7 +60,7 @@ private:
*fRepeat, *fRepeat,
*fPlay; *fPlay;
BSlider *fVolume; BSlider *fVolumeSlider;
BStringView *fCDTitle, BStringView *fCDTitle,
*fCurrentTrack, *fCurrentTrack,
@@ -84,6 +77,16 @@ private:
rgb_color fStopColor; rgb_color fStopColor;
rgb_color fPlayColor; rgb_color fPlayColor;
CDAudioDevice fCDDrive;
PlayList fPlayList;
CDState fWindowState;
int32 fDiscID;
CDDBQuery fCDQuery;
uint8 fVolume;
bool fUseTrackNames;
vector<BString> fTrackNames;
BString fCDName;
}; };
@@ -1,50 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
// This defines code for sending funciton objects in messages
#ifndef _BE_H
#include <Debug.h>
#endif
#include "FunctionObjectMessage.h"
BMessage *
FunctorFactoryCommon::NewMessage(const FunctionObject *functor)
{
BMessage *result = new BMessage('fCmG');
ASSERT(result);
long error = result->AddData("functor", B_RAW_TYPE,
functor, functor->Size());
if (error != B_NO_ERROR) {
delete result;
result = NULL;
}
return result;
}
bool
FunctorFactoryCommon::DispatchIfFunctionObject(BMessage *message)
{
if (message->what != 'fCmG')
return false;
// find the functor
long size;
FunctionObject *functor;
status_t error = message->FindData("functor", B_RAW_TYPE, (const void**)&functor, &size);
if (error != B_NO_ERROR)
return false;
ASSERT(functor);
// functor found, call it
(*functor)();
return true;
}
-73
View File
@@ -1,73 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
// This defines some function object glue code
// See the Be Newsletter article about using Function Objects in the Be messaging
// model for more information
#ifndef __FUNCTION_OBJECT_MESSAGE__
#define __FUNCTION_OBJECT_MESSAGE__
#ifndef _BE_H
#include <Message.h>
#include <MessageFilter.h>
#endif
class FunctionObject {
public:
virtual void operator()() = 0;
virtual ~FunctionObject() {}
virtual ulong Size() const = 0;
};
template<class FT, class T>
class PlainMemberFunctionObject : public FunctionObject {
public:
PlainMemberFunctionObject(FT callThis, T *onThis)
: function(callThis),
target(onThis)
{
}
virtual ~PlainMemberFunctionObject() {}
virtual void operator()()
{ (target->*function)(); }
virtual ulong Size() const { return sizeof(*this); }
private:
FT function;
T *target;
};
template<class FT, class T, class P>
class SingleParamMemberFunctionObject : public FunctionObject {
public:
SingleParamMemberFunctionObject(FT callThis, T *onThis, P withThis)
: function(callThis),
target(onThis),
parameter(withThis)
{
}
virtual ~SingleParamMemberFunctionObject() {}
virtual void operator()()
{ (target->*function)(parameter); }
virtual ulong Size() const { return sizeof(*this); }
private:
FT function;
T *target;
P parameter;
};
class FunctorFactoryCommon {
public:
static bool DispatchIfFunctionObject(BMessage *);
protected:
static BMessage *NewMessage(const FunctionObject *);
};
#endif
-4
View File
@@ -4,15 +4,11 @@ AddResources CDPlayer : CDPlayer.rdef ;
App CDPlayer : App CDPlayer :
CDAudioDevice.cpp CDAudioDevice.cpp
CDDBSupport.cpp CDDBSupport.cpp
CDEngine.cpp
CDPlayer.cpp CDPlayer.cpp
DoubleShotDrawButton.cpp DoubleShotDrawButton.cpp
DrawButton.cpp DrawButton.cpp
FunctionObjectMessage.cpp
Observer.cpp
PlayList.cpp PlayList.cpp
TrackMenu.cpp TrackMenu.cpp
TwoStateDrawButton.cpp TwoStateDrawButton.cpp
TypedList.cpp
; ;
LinkSharedOSLibs CDPlayer : be net netapi translation ; LinkSharedOSLibs CDPlayer : be net netapi translation ;
-180
View File
@@ -1,180 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
// This defines the Observer and Notifier classes
#include <Message.h>
#include <Looper.h>
#include <Debug.h>
#include "Observer.h"
Observer::Observer(Notifier *target)
: observedList(4, true)
{
if (target)
StartObserving(target);
}
Observer::~Observer()
{
StopObserving();
// tell everyone to stop sending us notices
}
void
Observer::StartObserving(Notifier *target)
{
ASSERT(target->RecipientHandler());
ASSERT(RecipientHandler()->Looper());
// send a message to the notifier to start sending us notices
if (target->RecipientHandler()->Looper()) {
BMessage *message = new BMessage(kStartObserving);
message->AddPointer("observer", this);
message->AddPointer("observed", target);
// send the message to the looper associated with the
// notifier
target->RecipientHandler()->Looper()->PostMessage(message,
target->RecipientHandler());
NotifierListEntry *entry = new NotifierListEntry;
entry->observed = target;
entry->handler = target->RecipientHandler();
entry->looper = target->RecipientHandler()->Looper();
observedList.AddItem(entry);
}
}
void
Observer::SendStopObserving(NotifierListEntry *target)
{
// send a message to the notifier to start sending us notices
if (target->looper) {
BMessage *message = new BMessage(kEndObserving);
message->AddPointer("observer", this);
message->AddPointer("observed", target->observed);
target->looper->PostMessage(message, target->handler);
}
}
NotifierListEntry *
StopObservingOne(NotifierListEntry *observed, void *castToObserver)
{
((Observer *)castToObserver)->SendStopObserving(observed);
return 0;
}
void
Observer::StopObserving()
{
// send a message to all the notifiers to start sending us notices
observedList.EachElement(StopObservingOne, this);
observedList.MakeEmpty();
}
bool
Observer::HandleObservingMessages(const BMessage *message)
{
switch (message->what) {
case kNoticeChange:
{
// look for notice messages from notifiers
Notifier *observed = NULL;
Observer *observer = NULL;
message->FindPointer("observed", (void**)&observed);
message->FindPointer("observer", (void**)&observer);
ASSERT(observed);
ASSERT(observer);
if (!observed || !observer)
return false;
ASSERT(dynamic_cast<Observer *>(observer));
// this is a notice for us, call the NoticeChange function
observer->NoticeChange(observed);
return true;
}
default:
return false;
}
}
static ObserverListEntry *
NotifyOne(ObserverListEntry *observer, void *castToObserved)
{
if (observer->looper) {
BMessage *message = new BMessage(kNoticeChange);
message->AddPointer("observed", castToObserved);
message->AddPointer("observer", observer->observer);
observer->looper->PostMessage(message, observer->handler);
}
return 0;
}
void
Notifier::Notify()
{
// send notices to all the observers
observerList.EachElement(NotifyOne, this);
}
static ObserverListEntry *
FindItemWithObserver(ObserverListEntry *item, void *castToObserver)
{
if (item->observer == castToObserver)
return item;
return 0;
}
void
Notifier::AddObserver(Observer *observer)
{
ObserverListEntry *item = new ObserverListEntry;
item->observer = observer;
item->handler = observer->RecipientHandler();
item->looper = observer->RecipientHandler()->Looper();
ASSERT(item->looper);
observerList.AddUnique(item);
}
void
Notifier::RemoveObserver(Observer *observer)
{
ObserverListEntry *item = observerList.EachElement(
FindItemWithObserver, observer);
observerList.RemoveItem(item);
delete item;
}
bool
Notifier::HandleObservingMessages(const BMessage *message)
{
switch (message->what) {
case kStartObserving:
case kEndObserving:
{
// handle messages about stopping and starting observing
Observer *observer = 0;
Notifier *observed = 0;
message->FindPointer("observer", (void**)&observer);
message->FindPointer("observed", (void**)&observed);
ASSERT(observer);
ASSERT(observed);
if (!observer || !observed)
return false;
if (message->what == kStartObserving)
observed->AddObserver(observer);
else
observed->RemoveObserver(observer);
return true;
}
default:
return false;
}
}
-98
View File
@@ -1,98 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
// This defines the Observer and Notifier classes
// The idea of observing is make it easier to support a client-server
// setup where a client want's to react to changes in the server state,
// for instance a view displaying a track number needs to change whenever
// a track changes. Normally this is done by the client periodically checking
// the server from within a Pulse call or a simillar mechanism. With Observer
// and Notifier, the Observer (client) starts observing a Notifier (server)
// and then just sits back and wait to get a notice, whenever the Notifier
// changes.
#ifndef __OBSERVER__
#define __OBSERVER__
#include "TypedList.h"
#include <Handler.h>
const uint32 kNoticeChange = 'notc';
const uint32 kStartObserving = 'stob';
const uint32 kEndObserving = 'edob';
class Notifier;
class NotifierListEntry {
public:
Notifier *observed;
BHandler *handler;
BLooper *looper;
};
class Observer {
public:
Observer(Notifier *target = NULL);
virtual ~Observer();
void StartObserving(Notifier *);
// start observing a speficied notifier
void StopObserving(Notifier *);
// stop observing a speficied notifier
void StopObserving();
// stop observing all the observed notifiers
virtual void NoticeChange(Notifier *) = 0;
// override this to get your job done, your class will get called
// whenever the Notifier changes
static bool HandleObservingMessages(const BMessage *message);
// call this from subclasses MessageReceived
virtual BHandler *RecipientHandler() const = 0;
// hook this up to return subclasses looper
private:
void SendStopObserving(NotifierListEntry *);
// keep a list of all the observed notifiers
TypedList<NotifierListEntry *> observedList;
friend NotifierListEntry *StopObservingOne(NotifierListEntry *, void *);
};
class ObserverListEntry {
public:
Observer *observer;
BHandler *handler;
BLooper *looper;
};
class Notifier {
public:
Notifier()
{}
virtual ~Notifier()
{}
virtual void Notify();
// call this when the notifier object changes to send notices
// to all the observers
static bool HandleObservingMessages(const BMessage *message);
// call this from subclasses MessageReceived
virtual BHandler *RecipientHandler() const = 0;
// hook this up to return subclasses looper
// keep a list of all the observers so that we can send them notices
void AddObserver(Observer *);
void RemoveObserver(Observer *);
private:
TypedList<ObserverListEntry *> observerList;
friend class Observer;
};
#endif
+3 -9
View File
@@ -3,7 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define DEBUG_PLAYLIST //#define DEBUG_PLAYLIST
#ifdef DEBUG_PLAYLIST #ifdef DEBUG_PLAYLIST
#include <stdio.h> #include <stdio.h>
@@ -47,7 +47,7 @@ PlayList::SetTrackCount(const int16 &count)
STRACE(("PlayList::SetTrackCount(%d)\n",count)); STRACE(("PlayList::SetTrackCount(%d)\n",count));
if(count < 0) if(count <= 0)
{ {
fTrackCount = 0; fTrackCount = 0;
fTrackIndex = 0; fTrackIndex = 0;
@@ -71,13 +71,7 @@ PlayList::SetStartingTrack(const int16 &start)
STRACE(("PlayList::SetStartingTrack(%d)\n",start)); STRACE(("PlayList::SetStartingTrack(%d)\n",start));
if(start >= TrackCount()) fStartingTrack = start;
fStartingTrack = TrackCount() - 1;
else
if(start < 1)
fStartingTrack = 1;
else
fStartingTrack = start;
fLocker.Unlock(); fLocker.Unlock();
} }
-1
View File
@@ -109,7 +109,6 @@ void
TrackMenu::MouseDown(BPoint point) TrackMenu::MouseDown(BPoint point)
{ {
BPoint pt(point); BPoint pt(point);
int32 saveditem = fCurrentItem; int32 saveditem = fCurrentItem;
int32 item = ItemAt(pt); int32 item = ItemAt(pt);
-87
View File
@@ -1,87 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
// TypedList is a type-safe template version of BList
#include <Debug.h>
#include "TypedList.h"
void *
_PointerList::EachElement(GenericEachFunction func, void *passThru)
{
// iterates through all elements, calling func on each
// if each function returns a nonzero value, terminates early
void *result = NULL;
int32 numElements = CountItems();
for (int32 index = 0; index < numElements; index++)
if ((result = func(ItemAtFast(index), passThru)) != NULL)
break;
return result;
}
_PointerList::_PointerList(const _PointerList &list)
: BList(list),
owning(list.owning)
{
}
_PointerList::_PointerList(int32 itemsPerBlock = 20, bool owningList)
: BList(itemsPerBlock),
owning(owningList)
{
}
_PointerList::~_PointerList()
{
}
bool
_PointerList::Owning() const
{
return owning;
}
bool
_PointerList::AddUnique(void *newItem)
{
if (IndexOf(newItem) >= 0)
return false;
return AddItem(newItem);
}
struct OneMatchParams {
void *matchThis;
_PointerList::GenericCompareFunction matchFunction;
};
static void *
MatchOne(void *item, void *castToParams)
{
OneMatchParams *params = (OneMatchParams *)castToParams;
if (params->matchFunction(item, params->matchThis) == 0)
// got a match, terminate search
return item;
return 0;
}
bool
_PointerList::AddUnique(void *newItem, GenericCompareFunction function)
{
OneMatchParams params;
params.matchThis = newItem;
params.matchFunction = function;
if (EachElement(MatchOne, &params))
// already in list
return false;
return AddItem(newItem);
}
-326
View File
@@ -1,326 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
// TypedList is a garden-variety of a type-safe template version of BList
#ifndef __LIST_TEMPLATE__
#define __LIST_TEMPLATE__
#ifndef _BE_H
#include <List.h>
#endif
#include <Debug.h>
class _PointerList : public BList {
public:
_PointerList(const _PointerList &list);
_PointerList(int32 itemsPerBlock = 20, bool owning = false);
virtual ~_PointerList();
typedef void *(* GenericEachFunction)(void *, void *);
typedef int (* GenericCompareFunction)(const void *, const void *);
void *EachElement(GenericEachFunction, void *);
bool AddUnique(void *);
// return true if item added or already in the list
bool AddUnique(void *, GenericCompareFunction);
bool Owning() const;
private:
const bool owning;
};
// TypedList -
// to be used as a list of pointers to objects; this class should contain
// pretty much no code, just stubs that do proper type conversion
// it uses BetterEachBList for all of it's functionality and provides a
// typed interface
template<class T>
class TypedList : public _PointerList {
public:
TypedList(int32 itemsPerBlock = 20, bool owning = false);
TypedList(const TypedList&);
virtual ~TypedList();
TypedList &operator=(const TypedList &);
// iteration and sorting
typedef T (* EachFunction)(T, void *);
typedef const T (* ConstEachFunction)(const T, void *);
typedef int (* CompareFunction)(const T *, const T *);
// adding and removing
bool AddItem(T);
bool AddItem(T, int32);
bool AddList(TypedList *);
bool AddList(TypedList *, int32);
bool AddUnique(T);
bool AddUnique(T, CompareFunction);
bool RemoveItem(T);
T RemoveItem(int32);
T RemoveItemAt(int32);
// same as RemoveItem(int32), RemoveItem does not work when T is a scalar
void MakeEmpty();
// item access
T ItemAt(int32) const;
T ItemAtFast(int32) const;
// does not do an index range check
T FirstItem() const;
T LastItem() const;
T Items() const;
// misc. getters
int32 IndexOf(const T) const;
bool HasItem(const T) const;
bool IsEmpty() const;
int32 CountItems() const;
T EachElement(EachFunction, void *);
const T EachElement(ConstEachFunction, void *) const;
// Do for each are obsoleted by this list, possibly add
// them for convenience
void SortItems(CompareFunction);
bool ReplaceItem(int32 index, T item);
bool SwapItems(int32 a, int32 b);
bool MoveItem(int32 from, int32 to);
private:
friend class ParseArray;
};
template<class T>
TypedList<T>::TypedList(int32 itemsPerBlock, bool owning)
: _PointerList(itemsPerBlock, owning)
{
}
template<class T>
TypedList<T>::TypedList(const TypedList<T> &list)
: _PointerList(list)
{
ASSERT(!list.Owning());
// copying owned lists does not work yet
}
template<class T>
TypedList<T>::~TypedList()
{
if (Owning())
// have to nuke elements first
MakeEmpty();
}
template<class T>
TypedList<T> &
TypedList<T>::operator=(const TypedList<T> &from)
{
ASSERT(!from.Owning());
// copying owned lists does not work yet
return (TypedList<T> &)BList::operator=(from);
}
template<class T>
bool
TypedList<T>::AddItem(T item)
{
return _PointerList::AddItem(item);
}
template<class T>
bool
TypedList<T>::AddItem(T item, int32 atIndex)
{
return _PointerList::AddItem(item, atIndex);
}
template<class T>
bool
TypedList<T>::AddList(TypedList<T> *newItems)
{
return _PointerList::AddList(newItems);
}
template<class T>
bool
TypedList<T>::AddList(TypedList<T> *newItems, int32 atIndex)
{
return _PointerList::AddList(newItems, atIndex);
}
template<class T>
bool
TypedList<T>::AddUnique(T item)
{
return _PointerList::AddUnique(item);
}
template<class T>
bool
TypedList<T>::AddUnique(T item, CompareFunction function)
{
return _PointerList::AddUnique(item, (GenericCompareFunction)function);
}
template<class T>
bool
TypedList<T>::RemoveItem(T item)
{
bool result = _PointerList::RemoveItem(item);
if (result && Owning()) {
delete item;
}
return result;
}
template<class T>
T
TypedList<T>::RemoveItem(int32 index)
{
return (T)_PointerList::RemoveItem(index);
}
template<class T>
T
TypedList<T>::RemoveItemAt(int32 index)
{
return (T)_PointerList::RemoveItem(index);
}
template<class T>
T
TypedList<T>::ItemAt(int32 index) const
{
return (T)_PointerList::ItemAt(index);
}
template<class T>
T
TypedList<T>::ItemAtFast(int32 index) const
{
return (T)_PointerList::ItemAtFast(index);
}
template<class T>
int32
TypedList<T>::IndexOf(const T item) const
{
return _PointerList::IndexOf(item);
}
template<class T>
T
TypedList<T>::FirstItem() const
{
return (T)_PointerList::FirstItem();
}
template<class T>
T
TypedList<T>::LastItem() const
{
return (T)_PointerList::LastItem();
}
template<class T>
bool
TypedList<T>::HasItem(const T item) const
{
return _PointerList::HasItem(item);
}
template<class T>
bool
TypedList<T>::IsEmpty() const
{
return _PointerList::IsEmpty();
}
template<class T>
int32
TypedList<T>::CountItems() const
{
return _PointerList::CountItems();
}
template<class T>
void
TypedList<T>::MakeEmpty()
{
if (Owning()) {
int32 numElements = CountItems();
for (int32 count = 0; count < numElements; count++)
// this is probably not the most efficient, but
// is relatively indepenent of BList implementation
// details
RemoveItem(LastItem());
}
_PointerList::MakeEmpty();
}
template<class T>
T
TypedList<T>::EachElement(EachFunction func, void *params)
{
return (T)_PointerList::EachElement((GenericEachFunction)func, params);
}
template<class T>
const T
TypedList<T>::EachElement(ConstEachFunction func, void *params) const
{
return (const T)
const_cast<TypedList<T> *>(this)->_PointerList::EachElement(
(GenericEachFunction)func, params);
}
template<class T>
T
TypedList<T>::Items() const
{
return (T)_PointerList::Items();
}
template<class T>
void
TypedList<T>::SortItems(CompareFunction function)
{
ASSERT(sizeof(T) == sizeof(void *));
_PointerList::SortItems((GenericCompareFunction)function);
}
template<class T>
bool TypedList<T>::ReplaceItem(int32 index, T item)
{
return _PointerList::ReplaceItem(index, (void *)item);
}
template<class T>
bool TypedList<T>::SwapItems(int32 a, int32 b)
{
return _PointerList::SwapItems(a, b);
}
template<class T>
bool TypedList<T>::MoveItem(int32 from, int32 to)
{
return _PointerList::MoveItem(from, to);
}
#endif