diff --git a/src/MediaPlayer.cpp b/src/MediaPlayer.cpp index 0513bae..7a5a2bf 100644 --- a/src/MediaPlayer.cpp +++ b/src/MediaPlayer.cpp @@ -11,9 +11,15 @@ #include "Song.h" -MediaPlayer::MediaPlayer(int32 window) +// This seems a fair amount of Pulse()-es to wait before spam-checking windows. +const int32 kTargetHops = 20; + + +MediaPlayer::MediaPlayer(int32 window, target followType) { fWindowIndex = window; + fWindowTarget = followType; + fWindowTargetHops = 0; } @@ -21,7 +27,9 @@ MediaPlayer::MediaPlayer(BMessage* data) : BArchivable(data) { - fWindowIndex = data->GetInt32("_window", 0); + fWindowIndex = data->GetInt32("_window", -1); + fWindowTarget = (target)data->GetInt32("_windowtype", MP_BY_TYPE_AUDIO); + fWindowTargetHops = 0; } @@ -30,6 +38,7 @@ MediaPlayer::Archive(BMessage* data, bool deep) const { status_t status = BArchivable::Archive(data, deep); data->AddInt32("_window", fWindowIndex); + data->AddInt32("_windowtype", fWindowTarget); data->AddString("class", "MediaPlayer"); data->AddString("add_on", APP_SIGNATURE); return status; @@ -45,6 +54,16 @@ MediaPlayer::Instantiate(BMessage* data) } +bool +MediaPlayer::IsValid() +{ + BString type; + BMessage send, reply; + _ScriptingCall("Suites", &send, &reply, MP_NO_TRACK, true); + return reply.FindString("suites", &type) == B_OK && type == "suite/vnd.Haiku-MediaPlayer"; +} + + bool MediaPlayer::IsPlaying() { @@ -113,6 +132,33 @@ MediaPlayer::PlaylistItem(Song* song, int32 index, bool duration) } +int32 +MediaPlayer::Window() +{ + if (fWindowTarget == MP_BY_INDEX || fWindowTargetHops-- > 0) + return fWindowIndex; + + fWindowTargetHops = kTargetHops; + + for (int32 i = CountWindows() - 1; i >= 0; i--) { + MediaPlayer mp(i, MP_BY_INDEX); + if (mp.IsValid()) { + if (fWindowTarget == MP_BY_LATEST) + return i; + + Song current; + BString type; + if (CurrentItem(¤t, false) + && BNode(current.Path().Path()).ReadAttrString("BEOS:TYPE", &type) == B_OK) + if ((type.StartsWith("audio") && fWindowTarget == MP_BY_TYPE_AUDIO) + || (type.StartsWith("video") && fWindowTarget == MP_BY_TYPE_VIDEO)) + return i; + } + } + return fWindowIndex; +} + + int32 MediaPlayer::SetWindow(int32 index) { @@ -127,7 +173,7 @@ int32 MediaPlayer::CountWindows() { BMessage send(B_COUNT_PROPERTIES), reply; - _ScriptingCall("Window", &send, &reply, MP_NO_TRACK); + _ScriptingCall("Window", &send, &reply, MP_NO_TRACK, false); int32 count; if (reply.FindInt32("result", &count) == B_OK) @@ -136,8 +182,23 @@ MediaPlayer::CountWindows() } +target +MediaPlayer::Target() +{ + return fWindowTarget; +} + + +target +MediaPlayer::SetTarget(target target) +{ + fWindowTargetHops = 0; + return fWindowTarget = target; +} + + bool -MediaPlayer::_GetSong(Song* song, int32 trackIndex, bool durationRequired) +MediaPlayer::_GetSong(Song* song, int32 trackIndex, bool durationReq) { BMessage send, reply; _ScriptingCall("URI", &send, &reply, trackIndex); @@ -146,19 +207,25 @@ MediaPlayer::_GetSong(Song* song, int32 trackIndex, bool durationRequired) return false; int64 duration = -1; - if (durationRequired) { + if (durationReq) { _ScriptingCall("Duration", &send, &reply, trackIndex); if (reply.FindInt64("result", &duration) != B_OK) return false; } + if (trackIndex == MP_CURRENT_TRACK && fWindowTarget != MP_BY_INDEX + && uriString != fLastItemPath) { + fLastItemPath.SetTo(uriString); + fWindowTargetHops = 0; + } *song = Song(_UriToPath(uriString), duration); return true; } void -MediaPlayer::_ScriptingCall(const char* attribute, BMessage* send, BMessage* reply, int32 trackIndex) +MediaPlayer::_ScriptingCall(const char* attribute, BMessage* send, BMessage* reply, int32 trackIndex, + bool window) { if (send->what == 0) send->what = B_GET_PROPERTY; @@ -169,9 +236,14 @@ MediaPlayer::_ScriptingCall(const char* attribute, BMessage* send, BMessage* rep else if (trackIndex == MP_CURRENT_TRACK) send->AddSpecifier("CurrentTrack"); - send->AddSpecifier("Window", fWindowIndex); + if (window) + send->AddSpecifier("Window", Window()); - BMessenger("application/x-vnd.Haiku-MediaPlayer").SendMessage(send, reply); + if (BMessenger("application/x-vnd.Haiku-MediaPlayer").SendMessage(send, reply) != B_OK) + return; + + if (reply != NULL && reply->what == B_MESSAGE_NOT_UNDERSTOOD) + fWindowTargetHops = 0; } diff --git a/src/MediaPlayer.h b/src/MediaPlayer.h index d524628..77b4135 100644 --- a/src/MediaPlayer.h +++ b/src/MediaPlayer.h @@ -19,17 +19,26 @@ enum { }; +enum target { + MP_BY_INDEX, + MP_BY_TYPE_AUDIO, + MP_BY_TYPE_VIDEO, + MP_BY_LATEST +}; + + class MediaPlayer : public BArchivable { public: - MediaPlayer(int32 window = 0); + MediaPlayer(int32 window = 0, target followType = MP_BY_TYPE_AUDIO); MediaPlayer(BMessage* archive); virtual status_t Archive(BMessage* data, bool deep = true) const; static MediaPlayer* Instantiate(BMessage* data); + bool IsValid(); bool IsPlaying(); - int64 Duration(); + int64 Duration(); int64 Position(); void SetPosition(int64 position); float Volume(); @@ -38,16 +47,25 @@ public: bool CurrentItem(Song* song, bool duration = true); bool PlaylistItem(Song* song, int32 index, bool duration = true); + int32 Window(); int32 SetWindow(int32 index); int32 CountWindows(); + target Target(); + target SetTarget(target target); + private: - bool _GetSong(Song* song, int32 trackIndex, bool durationRequired = true); + bool _GetSong(Song* song, int32 trackIndex, bool duration = true); void _ScriptingCall(const char* attribute, BMessage* send, BMessage* reply, - int32 trackIndex = MP_NO_TRACK); + int32 trackIndex = MP_NO_TRACK, bool window = true); + const char* _UriToPath(BString URI); int32 fWindowIndex; + target fWindowTarget; + int32 fWindowTargetHops; // Amount of pulses to wait before re-checking windows + + BString fLastItemPath; }; #endif // MEDIAPLAYER_H diff --git a/src/ReplicantView.cpp b/src/ReplicantView.cpp index 7872656..8b64d3b 100644 --- a/src/ReplicantView.cpp +++ b/src/ReplicantView.cpp @@ -36,7 +36,7 @@ ReplicantView::ReplicantView(BRect frame, const char* name, uint32 draggerPlacem fTransparentInactivity = false; fTransparentDragger = false; - fMediaPlayer = new MediaPlayer(0); + fMediaPlayer = new MediaPlayer(); } @@ -71,7 +71,7 @@ ReplicantView::Archive(BMessage* data, bool deep) const data->AddString("class", "ReplicantView"); data->AddString("add_on", APP_SIGNATURE); - return status; + return status; } @@ -89,6 +89,25 @@ ReplicantView::MessageReceived(BMessage* msg) { switch (msg->what) { + case REPL_WIN_BY_AUDIO: + fMediaPlayer->SetTarget(MP_BY_TYPE_AUDIO); + break; + case REPL_WIN_BY_VIDEO: + fMediaPlayer->SetTarget(MP_BY_TYPE_VIDEO); + break; + case REPL_WIN_BY_LATEST: + fMediaPlayer->SetTarget(MP_BY_LATEST); + break; + case REPL_WIN_BY_INDEX: + { + int32 index = 0; + if (msg->FindInt32("index", &index) == B_OK) { + fMediaPlayer->SetTarget(MP_BY_INDEX); + fMediaPlayer->SetWindow(index); + } + break; + } + case REPL_TRANSPARENTLY_INACTIVE: case REPL_TRANSPARENTLY_DRAG: { if (msg->what == REPL_TRANSPARENTLY_INACTIVE) @@ -136,7 +155,6 @@ ReplicantView::RightClickPopUp(BPopUpMenu* menu) BMenu* hideMenu = new BMenu("Hide when inactive"); menu->AddItem(hideMenu); - BMenuItem* hideInactive = hideMenu->Superitem(); hideInactive->SetMessage(new BMessage(REPL_TRANSPARENTLY_INACTIVE)); hideInactive->SetMarked(fTransparentInactivity); @@ -148,12 +166,29 @@ ReplicantView::RightClickPopUp(BPopUpMenu* menu) hideDragger->SetTarget(this); hideMenu->AddItem(hideDragger); + BMenu* windowMenu = new BMenu("Target window…"); + menu->AddItem(windowMenu); + + BMenu* indexMenu = _WindowIndexMenu(); + indexMenu->SetTargetForItems(this); + windowMenu->AddItem(indexMenu); + + BMenu* typeMenu = new BMenu("By type…"); + windowMenu->AddItem(typeMenu); + + BMenuItem* audioItem = new BMenuItem("Audio", new BMessage(REPL_WIN_BY_AUDIO)); + BMenuItem* videoItem = new BMenuItem("Video", new BMessage(REPL_WIN_BY_VIDEO)); + audioItem->SetMarked(fMediaPlayer->Target() == MP_BY_TYPE_AUDIO); + videoItem->SetMarked(fMediaPlayer->Target() == MP_BY_TYPE_VIDEO); + typeMenu->AddItem(audioItem); + typeMenu->AddItem(videoItem); + typeMenu->SetTargetForItems(this); + menu->AddSeparatorItem(); BString aboutLabel = "About %replicant" B_UTF8_ELLIPSIS; aboutLabel.ReplaceAll("%replicant", Name()); BMenuItem* aboutItem = new BMenuItem(aboutLabel, new BMessage(B_ABOUT_REQUESTED)); - aboutItem->SetTarget(this); menu->AddItem(aboutItem); if (fReplicated) { @@ -182,3 +217,31 @@ ReplicantView::SetInactive(bool inactive) Invalidate(); } + + +BMenu* +ReplicantView::_WindowIndexMenu() +{ + BMenu* menu = new BMenu("By index…"); + + BMenuItem* latestItem = new BMenuItem("Latest", new BMessage(REPL_WIN_BY_LATEST)); + latestItem->SetMarked(fMediaPlayer->Target() == MP_BY_LATEST); + menu->AddItem(latestItem); + + int32 windowCount = fMediaPlayer->CountWindows(); + for (int32 i = 0; i < windowCount; i++) + if (MediaPlayer(i, MP_BY_INDEX).IsValid()) { + BString label(""); + label << i; + + BMessage* msg = new BMessage(REPL_WIN_BY_INDEX); + msg->AddInt32("index", i); + + BMenuItem* indexItem = new BMenuItem(label, msg); + indexItem->SetMarked(fMediaPlayer->Target() == MP_BY_INDEX + && fMediaPlayer->Window() == i); + menu->AddItem(indexItem); + } + + return menu; +} diff --git a/src/ReplicantView.h b/src/ReplicantView.h index e6d9708..0829b0a 100644 --- a/src/ReplicantView.h +++ b/src/ReplicantView.h @@ -8,19 +8,18 @@ #include class BDragger; +class BMenu; class BPopUpMenu; class MediaPlayer; -enum { - MP_AUDIO_WINDOW = 'mpaw', - MP_VIDEO_WINDOW = 'mpvw', - MP_NEWEST_WINDOW = 'mpnw' -}; - enum { REPL_TRANSPARENTLY_INACTIVE = 'rtti', - REPL_TRANSPARENTLY_DRAG = 'rttd' + REPL_TRANSPARENTLY_DRAG = 'rttd', + REPL_WIN_BY_INDEX = 'rwbi', + REPL_WIN_BY_LATEST = 'rwbl', + REPL_WIN_BY_AUDIO = 'rwba', + REPL_WIN_BY_VIDEO = 'rwbv' }; @@ -53,6 +52,9 @@ public: // Set inactivity state, re-render accordingly virtual void SetInactive(bool inactive); +private: + BMenu* _WindowIndexMenu(); + protected: MediaPlayer* fMediaPlayer; BDragger* fDragger;