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)
: 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));
// TODO: Support multiple CD drives
engine = new CDEngine;
fDiscID=-1;
fVolume=255;
fUseTrackNames=false;
BuildGUI();
CDAudioDevice cd;
if(cd.CountDrives()<1)
if(fCDDrive.CountDrives()<1)
{
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."
@@ -66,12 +67,15 @@ CDPlayer::CDPlayer(BRect frame, const char *name, uint32 resizeMask, uint32 flag
alert->Go();
be_app->PostMessage(B_QUIT_REQUESTED);
}
fWindowState=fCDDrive.GetState();
fVolumeSlider->SetValue(fCDDrive.GetVolume());
WatchCDState();
}
CDPlayer::~CDPlayer()
{
engine->Stop();
delete engine;
fCDDrive.Stop();
}
void CDPlayer::BuildGUI(void)
@@ -120,7 +124,7 @@ void CDPlayer::BuildGUI(void)
fCurrentTrack = new BStringView( view->Bounds(),"TrackNumber","Track:",B_FOLLOW_ALL);
view->AddChild(fCurrentTrack);
fCurrentTrack->SetHighColor(fPlayColor);
fCurrentTrack->SetHighColor(fStopColor);
fCurrentTrack->SetFont(be_bold_font);
r.OffsetBy(0, r.Height() + 5);
@@ -147,15 +151,15 @@ void CDPlayer::BuildGUI(void)
fDiscTime->SetHighColor(120,120,255);
fDiscTime->SetFont(be_bold_font);
fVolume = 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());
AddChild(fVolume);
fVolumeSlider = new BSlider( BRect(0,0,75,30), "VolumeSlider", "Volume", new BMessage(M_SET_VOLUME),0,255);
fVolumeSlider->MoveTo(5, Bounds().bottom - 10 - fVolumeSlider->Frame().Height());
AddChild(fVolumeSlider);
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),
B_FOLLOW_BOTTOM, B_WILL_DRAW);
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"));
AddChild(fStop);
@@ -252,70 +256,123 @@ CDPlayer::MessageReceived(BMessage *msg)
{
case M_SET_VOLUME:
{
engine->SetVolume(fVolume->Value());
fCDDrive.SetVolume(fVolumeSlider->Value());
break;
}
case M_STOP:
{
if(engine->GetState()==kPlaying)
fPlay->SetState(0);
engine->Stop();
fWindowState=kStopped;
fCDDrive.Stop();
break;
}
case M_PLAY:
{
// If we are currently playing, then we will be showing
// the pause images and will want to switch back to the play images
if(engine->GetState()==kPlaying)
// the pause images and will want to switch back to the play images
if(fWindowState==kPlaying)
{
fPlay->SetState(0);
engine->Pause();
fWindowState=kPaused;
fCDDrive.Pause();
}
else
if(fWindowState==kPaused)
{
fWindowState=kPlaying;
fCDDrive.Resume();
}
else
{
fPlay->SetState(1);
engine->Play();
fWindowState=kPlaying;
fCDDrive.Play(fPlayList.GetCurrentTrack());
}
break;
}
case M_SELECT_TRACK:
{
engine->SelectTrack(fTrackMenu->Value()+1);
break;
}
case M_NEXT_TRACK:
{
engine->SkipOneForward();
break;
}
case M_PREV_TRACK:
{
engine->SkipOneBackward();
fPlayList.SetCurrentTrack(fTrackMenu->Value()+1);
fWindowState=kPlaying;
fCDDrive.Play(fPlayList.GetCurrentTrack());
break;
}
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;
}
case M_FFWD:
{
if(fFastFwd->Value() == B_CONTROL_ON)
engine->StartSkippingForward();
fCDDrive.StartFastFwd();
else
engine->StopSkipping();
fCDDrive.StopFastFwd();
break;
}
case M_REWIND:
{
if(fRewind->Value() == B_CONTROL_ON)
engine->StartSkippingBackward();
fCDDrive.StartRewind();
else
engine->StopSkipping();
fCDDrive.StopRewind();
break;
}
case M_SAVE:
@@ -326,131 +383,40 @@ CDPlayer::MessageReceived(BMessage *msg)
case M_SHUFFLE:
{
engine->ToggleShuffle();
if(engine->IsShuffled() && fShuffle->GetState()==0)
if(fPlayList.IsShuffled())
{
fShuffle->SetState(1);
int16 track = fPlayList.GetCurrentTrack();
fPlayList.SetShuffle(false);
fPlayList.SetStartingTrack(track);
fPlayList.SetTrackCount(fCDDrive.CountTracks());
fShuffle->SetState(0);
}
else
{
if(fShuffle->GetState()==1)
fShuffle->SetState(0);
fPlayList.SetTrackCount(fCDDrive.CountTracks());
fPlayList.SetShuffle(true);
fShuffle->SetState(1);
}
break;
}
case M_REPEAT:
{
engine->ToggleRepeat();
if(engine->IsRepeated() && fRepeat->GetState()==0)
if(fPlayList.IsLoop())
{
fRepeat->SetState(1);
fPlayList.SetLoop(false);
fRepeat->SetState(0);
}
else
{
if(fRepeat->GetState()==1)
fRepeat->SetState(0);
fPlayList.SetLoop(true);
fRepeat->SetState(1);
}
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:
{
break;
BView::MessageReceived(msg);
break;
}
}
}
@@ -458,7 +424,7 @@ CDPlayer::HandlePlayState(void)
void
CDPlayer::AdjustButtonStates(void)
{
CDState state = gCDDevice.GetState();
/* CDState state = gCDDevice.GetState();
if(state==kNoCD)
{
@@ -485,56 +451,13 @@ CDPlayer::AdjustButtonStates(void)
fPlay->SetState(1);
else
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
CDPlayer::UpdateTimeInfo(void)
{
/*
int32 min,sec;
char string[1024];
@@ -561,21 +484,13 @@ CDPlayer::UpdateTimeInfo(void)
else
sprintf(string,"Track --:-- / --:--");
fTrackTime->SetText(string);
*/
}
void
CDPlayer::AttachedToWindow()
{
// start observing
engine->AttachedToLooper(Window());
StartObserving(engine->TrackStateWatcher());
StartObserving(engine->PlayStateWatcher());
StartObserving(engine->ContentWatcher());
StartObserving(engine->TimeStateWatcher());
StartObserving(engine->VolumeStateWatcher());
fVolume->SetTarget(this);
fVolumeSlider->SetTarget(this);
fStop->SetTarget(this);
fPlay->SetTarget(this);
fNextTrack->SetTarget(this);
@@ -619,7 +534,243 @@ CDPlayer::FrameResized(float new_width, float new_height)
void
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
@@ -631,7 +782,7 @@ public:
CDPlayerWindow::CDPlayerWindow(void)
: 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;
+16 -13
View File
@@ -16,15 +16,16 @@
#include <TextControl.h>
#include <StringView.h>
#include "Observer.h"
#include "CDEngine.h"
#include "TrackMenu.h"
#include "CDAudioDevice.h"
#include "CDDBSupport.h"
#include "PlayList.h"
class DrawButton;
class DoubleShotDrawButton;
class TwoStateDrawButton;
class CDPlayer : public BView, private Observer
class CDPlayer : public BView
{
public:
CDPlayer(BRect frame, const char *name,
@@ -40,19 +41,11 @@ public:
virtual void FrameResized(float new_width, float new_height);
virtual void MessageReceived(BMessage *);
// observing overrides
virtual BHandler *RecipientHandler() const { return (BHandler *)this; }
virtual void NoticeChange(Notifier *);
private:
void HandlePlayState(void);
void UpdateCDInfo(void);
void WatchCDState(void);
void UpdateTimeInfo(void);
void AdjustButtonStates(void);
CDEngine *engine;
DrawButton *fStop,
*fNextTrack,
*fPrevTrack,
@@ -67,7 +60,7 @@ private:
*fRepeat,
*fPlay;
BSlider *fVolume;
BSlider *fVolumeSlider;
BStringView *fCDTitle,
*fCurrentTrack,
@@ -84,6 +77,16 @@ private:
rgb_color fStopColor;
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 :
CDAudioDevice.cpp
CDDBSupport.cpp
CDEngine.cpp
CDPlayer.cpp
DoubleShotDrawButton.cpp
DrawButton.cpp
FunctionObjectMessage.cpp
Observer.cpp
PlayList.cpp
TrackMenu.cpp
TwoStateDrawButton.cpp
TypedList.cpp
;
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 <string.h>
#define DEBUG_PLAYLIST
//#define DEBUG_PLAYLIST
#ifdef DEBUG_PLAYLIST
#include <stdio.h>
@@ -47,7 +47,7 @@ PlayList::SetTrackCount(const int16 &count)
STRACE(("PlayList::SetTrackCount(%d)\n",count));
if(count < 0)
if(count <= 0)
{
fTrackCount = 0;
fTrackIndex = 0;
@@ -71,13 +71,7 @@ PlayList::SetStartingTrack(const int16 &start)
STRACE(("PlayList::SetStartingTrack(%d)\n",start));
if(start >= TrackCount())
fStartingTrack = TrackCount() - 1;
else
if(start < 1)
fStartingTrack = 1;
else
fStartingTrack = start;
fStartingTrack = start;
fLocker.Unlock();
}
-1
View File
@@ -109,7 +109,6 @@ void
TrackMenu::MouseDown(BPoint point)
{
BPoint pt(point);
int32 saveditem = fCurrentItem;
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