diff --git a/Makefile b/Makefile index 6e09d50..3becb40 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ SRCS = src/App.cpp \ src/LyricsView.cpp \ src/MainWindow.cpp \ src/MediaPlayer.cpp \ + src/ReplicantView.cpp \ src/Song.cpp # Specify the resource definition files to use. Full or relative paths can be @@ -97,7 +98,9 @@ LOCALES = # use. For example, setting DEFINES to "DEBUG=1" will cause the compiler # option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" would pass # "-DDEBUG" on the compiler's command line. -DEFINES = +DEFINES = BUILD_DATE="\"$(shell date +"%Y-%m-%d %H:%M")\"" \ + APP_NAME="\"$(NAME)"\" \ + APP_SIGNATURE="\"$(APP_MIME_SIG)"\" # Specify the warning level. Either NONE (suppress all warnings), # ALL (enable all warnings), or leave blank (enable default warnings). diff --git a/src/App.cpp b/src/App.cpp index 18630cd..13cc835 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -5,32 +5,15 @@ #include "App.h" -#include - #include "MainWindow.h" -BString -get_string_mediaplayer(const char* attribute) -{ - BMessage message, reply; - message.what = B_GET_PROPERTY; - message.AddSpecifier(attribute); - message.AddSpecifier("Window", 0); - BMessenger("application/x-vnd.Haiku-MediaPlayer").SendMessage(&message, &reply); - - BString result; - reply.FindString("result", &result); - return result; -} - - App::App() : - BApplication("application/x-vnd.mediamonitor") + BApplication(APP_SIGNATURE) { MainWindow* win = new MainWindow(); - win->SetPulseRate(5000000); + win->SetPulseRate(2500000); win->Show(); } diff --git a/src/CoverView.cpp b/src/CoverView.cpp index 2d8eb4e..12b1114 100644 --- a/src/CoverView.cpp +++ b/src/CoverView.cpp @@ -14,32 +14,20 @@ CoverView::CoverView(BRect frame) : - BView(frame, "Cover", B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_TRANSPARENT_BACKGROUND | B_PULSE_NEEDED) + ReplicantView(frame, "Cover", B_FOLLOW_LEFT) { - BRect dragRect(0, 0, 10, frame.Height()); - fDragger = new BDragger(dragRect, this, - B_FOLLOW_LEFT | B_FOLLOW_BOTTOM, B_WILL_DRAW); - fDragger->SetViewColor(B_TRANSPARENT_COLOR); - AddChild(fDragger); - - fMediaPlayer = new MediaPlayer(0); - - fCover = NULL; SetViewColor(B_TRANSPARENT_COLOR); + fCover = NULL; Pulse(); } CoverView::CoverView(BMessage* data) : - BView(data) + ReplicantView(data) { - BMessage mediaplayer; - data->FindMessage("mediaplayer", &mediaplayer); - fMediaPlayer = new MediaPlayer(&mediaplayer); - - fCover = NULL; SetViewColor(B_TRANSPARENT_COLOR); + fCover = NULL; Pulse(); } @@ -47,13 +35,10 @@ CoverView::CoverView(BMessage* data) status_t CoverView::Archive(BMessage* data, bool deep) const { - status_t status = BView::Archive(data, deep); + status_t status = ReplicantView::Archive(data, deep); - BMessage mediaPlayer; - fMediaPlayer->Archive(&mediaPlayer); - data->AddMessage("mediaplayer", &mediaPlayer); data->AddString("class", "CoverView"); - data->AddString("add_on", "application/x-vnd.mediamonitor"); + data->AddString("add_on", APP_SIGNATURE); return status; } @@ -71,19 +56,23 @@ void CoverView::Pulse() { Song song; + // No song playing if (fMediaPlayer->CurrentItem(&song, false) == false) { if (fCurrentSong.InitCheck()) { fCurrentSong = song; delete fCover; fCover = NULL; - Invalidate(); + + SetInactive(true); } + // New song } else if (song != fCurrentSong) { fCurrentSong = song; if (fCover != NULL) delete fCover; fCover = song.Cover(); - Invalidate(); + + SetInactive(false); } } @@ -92,7 +81,6 @@ void CoverView::Draw(BRect updateRect) { BView::Draw(updateRect); - if (fCover != NULL && fCover->IsValid()) DrawBitmap(fCover, Bounds()); } diff --git a/src/CoverView.h b/src/CoverView.h index 5f57e1c..c39f9a0 100644 --- a/src/CoverView.h +++ b/src/CoverView.h @@ -5,15 +5,14 @@ #ifndef COVERVIEW_H #define COVERVIEW_H -#include - +#include "ReplicantView.h" #include "Song.h" class BDragger; class MediaPlayer; -class CoverView : public BView { +class CoverView : public ReplicantView { public: CoverView(BRect frame); CoverView(BMessage* archive); @@ -28,10 +27,7 @@ public: private: void _Init(BRect frame); - MediaPlayer* fMediaPlayer; Song fCurrentSong; - - BDragger* fDragger; BBitmap* fCover; }; diff --git a/src/LyricsView.cpp b/src/LyricsView.cpp index 2e7fc69..6bb680d 100644 --- a/src/LyricsView.cpp +++ b/src/LyricsView.cpp @@ -5,9 +5,7 @@ #include "LyricsView.h" -#include #include -#include #include #include #include @@ -35,7 +33,7 @@ LyricsTextView::Archive(BMessage* data, bool deep) const { status_t status = BTextView::Archive(data, deep); data->AddString("class", "LyricsTextView"); - data->AddString("add_on", "application/x-vnd.mediamonitor"); + data->AddString("add_on", APP_SIGNATURE); return status; } @@ -65,14 +63,8 @@ LyricsTextView::MouseDown(BPoint where) LyricsView::LyricsView(BRect frame) : - BView(frame, "Lyrics", B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_TRANSPARENT_BACKGROUND | B_PULSE_NEEDED) + ReplicantView(frame, "Lyrics", B_FOLLOW_LEFT) { - BRect dragRect(0, 0, 10, frame.Height()); - fDragger = new BDragger(dragRect, this, - B_FOLLOW_LEFT | B_FOLLOW_BOTTOM, B_WILL_DRAW); - fDragger->SetViewColor(B_TRANSPARENT_COLOR); - AddChild(fDragger); - BRect textRect(0, 0, Bounds().Width(), Bounds().Height() - 10); fTextView = new LyricsTextView(textRect, "lyricsText", textRect, B_FOLLOW_ALL, B_WILL_DRAW); @@ -85,41 +77,28 @@ LyricsView::LyricsView(BRect frame) AddChild(fScrollView); fAutoScroll = false; - fTransparentInactivity = true; - fTransparentDragger = false; fFgColor = ui_color(B_PANEL_TEXT_COLOR); fBgColor = ui_color(B_PANEL_BACKGROUND_COLOR); - fMediaPlayer = new MediaPlayer(0); - _Init(frame); } LyricsView::LyricsView(BMessage* data) : - BView(data) + ReplicantView(data) { fAutoScroll = false; - fTransparentInactivity = true; - fTransparentDragger = false; fFgColor = ui_color(B_PANEL_TEXT_COLOR); fBgColor = ui_color(B_PANEL_BACKGROUND_COLOR); fTextView = dynamic_cast(FindView("lyricsText")); fScrollView = dynamic_cast(FindView("scrollView")); - fDragger = dynamic_cast(FindView("_dragger_")); data->FindColor("background_color", &fBgColor); data->FindColor("foreground_color", &fFgColor); data->FindBool("autoscroll", &fAutoScroll); - data->FindBool("transparent_inactivity", &fTransparentInactivity); - data->FindBool("transparent_dragger", &fTransparentDragger); - - BMessage mediaplayer; - data->FindMessage("mediaplayer", &mediaplayer); - fMediaPlayer = new MediaPlayer(&mediaplayer); _Init(Frame()); } @@ -128,20 +107,14 @@ LyricsView::LyricsView(BMessage* data) status_t LyricsView::Archive(BMessage* data, bool deep) const { - status_t status = BView::Archive(data, deep); + status_t status = ReplicantView::Archive(data, deep); - BMessage mediaPlayer; - fMediaPlayer->Archive(&mediaPlayer); - data->AddMessage("mediaplayer", &mediaPlayer); data->AddColor("background_color", fBgColor); data->AddColor("foreground_color", fFgColor); data->AddBool("autoscroll", fAutoScroll); - data->AddBool("transparent_inactivity", fTransparentInactivity); - data->AddBool("transparent_dragger", fTransparentDragger); - data->AddString("class", "LyricsView"); - data->AddString("add_on", "application/x-vnd.mediamonitor"); + data->AddString("add_on", APP_SIGNATURE); return status; } @@ -179,19 +152,8 @@ LyricsView::MessageReceived(BMessage* msg) case LYRICS_AUTO_SCROLL: fAutoScroll = !fAutoScroll; break; - case LYRICS_TRANSPARENTLY_INACTIVE: - case LYRICS_TRANSPARENTLY_DRAG: { - if (msg->what == LYRICS_TRANSPARENTLY_INACTIVE) - fTransparentInactivity = !fTransparentInactivity; - if (msg->what == LYRICS_TRANSPARENTLY_DRAG) - fTransparentDragger = !fTransparentDragger; - - if (fCurrentSong.InitCheck() == false) - _ClearText(); - break; - } default: - BView::MessageReceived(msg); + ReplicantView::MessageReceived(msg); break; } } @@ -201,19 +163,22 @@ void LyricsView::Pulse() { Song song; + // No song if (fMediaPlayer->CurrentItem(&song, fAutoScroll) == false) { if (fCurrentSong.InitCheck()) { fCurrentSong = song; - _ClearText(); + SetInactive(true); } return; } + // New song if (song != fCurrentSong) { fCurrentSong = song; BString lyrics; song.Lyrics(&lyrics); - _SetText(lyrics.String()); + fTextView->SetText(lyrics.String()); + SetInactive(false); } if (fAutoScroll) { @@ -224,59 +189,66 @@ LyricsView::Pulse() } -void -LyricsView::MouseDown(BPoint where) +BPopUpMenu* +LyricsView::RightClickPopUp(BPopUpMenu* menu) { - uint32 buttons = 0; - Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); + if (menu == NULL) + menu = new BPopUpMenu("rightClickPopUp"); - if (buttons & B_SECONDARY_MOUSE_BUTTON) - _RightClickPopUp()->Go(ConvertToScreen(where), true, false, true); + BMenuItem* copy = + new BMenuItem("Copy", new BMessage(B_COPY), 'C', B_COMMAND_KEY); + + int32 start = -1, end = -1; + fTextView->GetSelection(&start, &end); + + copy->SetEnabled(start >= 0 && end > 0); + copy->SetTarget(fTextView); + menu->AddItem(copy); + + BMenuItem* selectAll = new BMenuItem("Select all", + new BMessage(B_SELECT_ALL), 'A', B_COMMAND_KEY); + selectAll->SetTarget(fTextView); + menu->AddItem(selectAll); + + menu->AddSeparatorItem(); + + BMenuItem* autoScroll = new BMenuItem("Auto-scroll", + new BMessage(LYRICS_AUTO_SCROLL)); + autoScroll->SetMarked(fAutoScroll); + autoScroll->SetTarget(this); + menu->AddItem(autoScroll); + + return ReplicantView::RightClickPopUp(menu); +} + + +void +LyricsView::SetInactive(bool inactive) +{ + if (inactive && fTransparentInactivity && !fScrollView->IsHidden()) + fScrollView->Hide(); + else if (!inactive && fScrollView->IsHidden()) + fScrollView->Show(); + + if (inactive) { + fTextView->SetText("No lyrics to display!"); + fTextView->SetAlignment(B_ALIGN_CENTER); + } else + fTextView->SetAlignment(B_ALIGN_LEFT); + + ReplicantView::SetInactive(inactive); } void LyricsView::_Init(BRect frame) { - _ClearText(); + SetInactive(true); _UpdateColors(); Pulse(); } -void -LyricsView::_SetText(const char* text) -{ - if (fScrollView->IsHidden()) - fScrollView->Show(); - if (fDragger->IsHidden()) - fDragger->Show(); - - fTextView->SetText(text); - fTextView->SetAlignment(B_ALIGN_LEFT); -} - - -void -LyricsView::_ClearText() -{ - if (fTransparentInactivity) { - if (fScrollView->IsHidden()) - fScrollView->Hide(); - if (fDragger->IsHidden() && fTransparentDragger) - fDragger->Hide(); - } else { - if (fScrollView->IsHidden()) - fScrollView->Show(); - if (fDragger->IsHidden()) - fDragger->Show(); - } - - fTextView->SetText("No lyrics to display!"); - fTextView->SetAlignment(B_ALIGN_CENTER); -} - - void LyricsView::_UpdateColors() { @@ -299,53 +271,6 @@ LyricsView::_UpdateColors() } -BPopUpMenu* -LyricsView::_RightClickPopUp() -{ - BPopUpMenu* menu = new BPopUpMenu("rightClickPopUp"); - menu->SetRadioMode(false); - - BMenuItem* copy = - new BMenuItem("Copy", new BMessage(B_COPY), 'C', B_COMMAND_KEY); - - int32 start = -1, end = -1; - fTextView->GetSelection(&start, &end); - - copy->SetEnabled(start >= 0 && end > 0); - copy->SetTarget(fTextView); - menu->AddItem(copy); - - BMenuItem* selectAll = new BMenuItem("Select all", - new BMessage(B_SELECT_ALL), 'A', B_COMMAND_KEY); - selectAll->SetTarget(fTextView); - menu->AddItem(selectAll); - - menu->AddSeparatorItem(); - - BMenuItem* autoScroll = new BMenuItem("Auto-scroll", - new BMessage(LYRICS_AUTO_SCROLL)); - autoScroll->SetMarked(fAutoScroll); - autoScroll->SetTarget(this); - menu->AddItem(autoScroll); - - BMenu* hideMenu = new BMenu("Hide when inactive"); - menu->AddItem(hideMenu); - - BMenuItem* hideInactive = hideMenu->Superitem(); - hideInactive->SetMessage(new BMessage(LYRICS_TRANSPARENTLY_INACTIVE)); - hideInactive->SetMarked(fTransparentInactivity); - hideInactive->SetTarget(this); - - BMenuItem* hideDragger = new BMenuItem("… including the dragger", - new BMessage(LYRICS_TRANSPARENTLY_DRAG)); - hideDragger->SetMarked(fTransparentDragger); - hideDragger->SetTarget(this); - hideMenu->AddItem(hideDragger); - - return menu; -} - - float LyricsView::_GetPositionProportion() { diff --git a/src/LyricsView.h b/src/LyricsView.h index b80314e..8af07d9 100644 --- a/src/LyricsView.h +++ b/src/LyricsView.h @@ -7,6 +7,7 @@ #include +#include "ReplicantView.h" #include "Song.h" class BDragger; @@ -35,41 +36,34 @@ public: }; -class LyricsView : public BView { +class LyricsView : public ReplicantView { public: - LyricsView(BRect frame); - LyricsView(BMessage* archive); + LyricsView(BRect frame); + LyricsView(BMessage* archive); - virtual status_t Archive(BMessage* data, bool deep = true) const; - static LyricsView* Instantiate(BMessage* data); + virtual status_t Archive(BMessage* data, bool deep = true) const; + static LyricsView* Instantiate(BMessage* data); - virtual void MessageReceived(BMessage* msg); - virtual void Pulse(); + virtual void MessageReceived(BMessage* msg); + virtual void Pulse(); - virtual void MouseDown(BPoint where); + virtual BPopUpMenu* RightClickPopUp(BPopUpMenu* menu = NULL); + + virtual void SetInactive(bool inactive); private: - void _Init(BRect frame); + void _Init(BRect frame); - void _SetText(const char* text); - void _ClearText(); + void _UpdateColors(); - void _UpdateColors(); + float _GetPositionProportion(); - BPopUpMenu* _RightClickPopUp(); - - float _GetPositionProportion(); - - MediaPlayer* fMediaPlayer; Song fCurrentSong; LyricsTextView* fTextView; BScrollView* fScrollView; - BDragger* fDragger; bool fAutoScroll; - bool fTransparentInactivity; - bool fTransparentDragger; rgb_color fBgColor; rgb_color fFgColor; diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 726cc53..1f20c91 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -14,7 +14,7 @@ MainWindow::MainWindow() : - BWindow(BRect(BPoint(0,0),BSize(500.0, 500.0)), "MediaMonitor", + BWindow(BRect(BPoint(0,0),BSize(500.0, 500.0)), APP_NAME, B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS | B_QUIT_ON_WINDOW_CLOSE) { SetPulseRate(0); diff --git a/src/MediaPlayer.cpp b/src/MediaPlayer.cpp index e4ed23c..d4a9fa7 100644 --- a/src/MediaPlayer.cpp +++ b/src/MediaPlayer.cpp @@ -31,7 +31,7 @@ MediaPlayer::Archive(BMessage* data, bool deep) const status_t status = BArchivable::Archive(data, deep); data->AddInt32("_window", fWindowIndex); data->AddString("class", "MediaPlayer"); - data->AddString("add_on", "application/x-vnd.mediamonitor"); + data->AddString("add_on", APP_SIGNATURE); return status; } diff --git a/src/ReplicantView.cpp b/src/ReplicantView.cpp new file mode 100644 index 0000000..e4894e5 --- /dev/null +++ b/src/ReplicantView.cpp @@ -0,0 +1,148 @@ +/* + * Copyright 2022, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#include "ReplicantView.h" + +#include +#include +#include +#include + +#include "MediaPlayer.h" + + +ReplicantView::ReplicantView(BRect frame, const char* name, uint32 draggerPlacement) + : + BView(frame, name, B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_TRANSPARENT_BACKGROUND | B_PULSE_NEEDED) +{ + BRect dragRect(frame.Width() - 10, 0, frame.Width(), frame.Height()); + uint32 dragFollow = B_FOLLOW_RIGHT; + if (draggerPlacement & _VIEW_LEFT_) { + dragRect = BRect(0, 0, 10, frame.Height()); + dragFollow = B_FOLLOW_LEFT; + } + fDragger = new BDragger(dragRect, this, dragFollow | B_FOLLOW_BOTTOM, B_WILL_DRAW); + fDragger->SetViewColor(B_TRANSPARENT_COLOR); + AddChild(fDragger); + + fTransparentInactivity = true; + fTransparentDragger = false; + fInactive = true; + + fMediaPlayer = new MediaPlayer(0); +} + + +ReplicantView::ReplicantView(BMessage* data) + : + BView(data) +{ + BMessage mediaplayer; + data->FindMessage("mediaplayer", &mediaplayer); + fMediaPlayer = new MediaPlayer(&mediaplayer); + + fDragger = dynamic_cast(FindView("_dragger_")); + + fTransparentInactivity = data->GetBool("transparent_inactivity", true); + fTransparentDragger = data->GetBool("transparent_dragger", false); + fInactive = true; +} + + +status_t +ReplicantView::Archive(BMessage* data, bool deep) const +{ + status_t status = BView::Archive(data, deep); + + BMessage mediaPlayer; + fMediaPlayer->Archive(&mediaPlayer); + data->AddMessage("mediaplayer", &mediaPlayer); + data->AddBool("transparent_inactivity", fTransparentInactivity); + data->AddBool("transparent_dragger", fTransparentDragger); + + data->AddString("class", "ReplicantView"); + data->AddString("add_on", APP_SIGNATURE); + return status; +} + + +ReplicantView* +ReplicantView::Instantiate(BMessage* data) +{ + if (!validate_instantiation(data, "ReplicantView")) + return NULL; + return new ReplicantView(data); +} + + +void +ReplicantView::MessageReceived(BMessage* msg) +{ + switch (msg->what) + { + case REPL_TRANSPARENTLY_INACTIVE: + case REPL_TRANSPARENTLY_DRAG: { + if (msg->what == REPL_TRANSPARENTLY_INACTIVE) + fTransparentInactivity = !fTransparentInactivity; + if (msg->what == REPL_TRANSPARENTLY_DRAG) + fTransparentDragger = !fTransparentDragger; + SetInactive(fInactive); + break; + } + default: + BView::MessageReceived(msg); + break; + } +} + + +void +ReplicantView::MouseDown(BPoint where) +{ + uint32 buttons = 0; + Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); + if (buttons & B_SECONDARY_MOUSE_BUTTON) + RightClickPopUp()->Go(ConvertToScreen(where), true, false, true); +} + + +BPopUpMenu* +ReplicantView::RightClickPopUp(BPopUpMenu* menu) +{ + if (menu == NULL) + menu = new BPopUpMenu("rightClickPopUp"); + menu->SetRadioMode(false); + + BMenu* hideMenu = new BMenu("Hide when inactive"); + menu->AddItem(hideMenu); + + BMenuItem* hideInactive = hideMenu->Superitem(); + hideInactive->SetMessage(new BMessage(REPL_TRANSPARENTLY_INACTIVE)); + hideInactive->SetMarked(fTransparentInactivity); + hideInactive->SetTarget(this); + + BMenuItem* hideDragger = new BMenuItem("… including the dragger", + new BMessage(REPL_TRANSPARENTLY_DRAG)); + hideDragger->SetMarked(fTransparentDragger); + hideDragger->SetTarget(this); + hideMenu->AddItem(hideDragger); + + return menu; +} + + +void +ReplicantView::SetInactive(bool inactive) +{ + fInactive = inactive; + + if (inactive && fTransparentInactivity) + if (!fDragger->IsHidden() && fTransparentDragger) + fDragger->Hide(); + if (fDragger->IsHidden() && (!fTransparentDragger || !fTransparentInactivity)) + fDragger->Show(); + + Invalidate(); +} diff --git a/src/ReplicantView.h b/src/ReplicantView.h new file mode 100644 index 0000000..ee9715e --- /dev/null +++ b/src/ReplicantView.h @@ -0,0 +1,59 @@ +/* + * Copyright 2022, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ +#ifndef REPLICANTVIEW_H +#define REPLICANTVIEW_H + +#include + +class BDragger; +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' +}; + + +/* Base class for each replicant, containing features need for (most) of them: + - Dragger where you'd expect it + - Transparency/hiding during inactivity + - Right-click menu with common options + - Setting custom background color (by drag) + - MediaPlayer object */ +class ReplicantView : public BView { +public: + ReplicantView(BRect frame, const char* name, uint32 draggerPlacement); + ReplicantView(BMessage* archive); + + virtual status_t Archive(BMessage* data, bool deep = true) const; + static ReplicantView* Instantiate(BMessage* data); + + virtual void MessageReceived(BMessage* msg); + + virtual void MouseDown(BPoint where); + + // Populates the given menu with default items + virtual BPopUpMenu* RightClickPopUp(BPopUpMenu* menu = NULL); + + // Set inactivity state, re-render accordingly + virtual void SetInactive(bool inactive); + + MediaPlayer* fMediaPlayer; + BDragger* fDragger; + + bool fInactive; + bool fTransparentInactivity; + bool fTransparentDragger; +}; + +#endif // REPLICANTVIEW_H