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:
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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,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;
|
||||
}
|
||||
|
||||
@@ -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,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 ;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,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();
|
||||
}
|
||||
|
||||
@@ -109,7 +109,6 @@ void
|
||||
TrackMenu::MouseDown(BPoint point)
|
||||
{
|
||||
BPoint pt(point);
|
||||
|
||||
int32 saveditem = fCurrentItem;
|
||||
int32 item = ItemAt(pt);
|
||||
|
||||
|
||||
@@ -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, ¶ms))
|
||||
// already in list
|
||||
return false;
|
||||
|
||||
return AddItem(newItem);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user