/*
* MainApp.cpp - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
* Copyright (C) 2008 Stephan Aßmus <superstippi@gmx.de> (MIT Ok)
*
* Released under the terms of the MIT license.
*/
#include "MainApp.h"
#include <Alert.h>
#include <Autolock.h>
#include <Catalog.h>
#include <Entry.h>
#include <FilePanel.h>
#include <Locale.h>
#include <MediaDefs.h>
#include <MediaRoster.h>
#include <MimeType.h>
#include <Path.h>
#include <Resources.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "EventQueue.h"
#include "Playlist.h"
#include "Settings.h"
#include "SettingsWindow.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "MediaPlayer-Main"
static const char* kCurrentPlaylistFilename = "MediaPlayer Current Playlist";
const char* kAppSig = "application/x-vnd.Haiku-MediaPlayer";
MainApp* gMainApp;
MainApp::MainApp()
:
BApplication(kAppSig),
fPlayerCount(0),
fSettingsWindow(NULL),
fOpenFilePanel(NULL),
fSaveFilePanel(NULL),
fLastFilePanelFolder(),
fAudioWindowFrameSaved(false),
fLastSavedAudioWindowCreationTime(0)
{
fLastFilePanelFolder = Settings::Default()->FilePanelFolder();
if (!BMediaRoster::IsRunning()) {
BAlert* alert = new BAlert("start_media_server",
B_TRANSLATE("It appears the media server is not running.\n"
"Would you like to start it ?"), B_TRANSLATE("Quit"),
B_TRANSLATE("Start media server"), NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0) {
PostMessage(B_QUIT_REQUESTED);
return;
}
launch_media_server();
}
}
MainApp::~MainApp()
{
delete fOpenFilePanel;
delete fSaveFilePanel;
}
bool
MainApp::QuitRequested()
{
// Make sure we store the current playlist, if applicable.
for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
MainWin* playerWindow = dynamic_cast<MainWin*>(window);
if (playerWindow == NULL)
continue;
BAutolock _(playerWindow);
BMessage quitMessage;
playerWindow->GetQuitMessage(&quitMessage);
// Store the playlist if there is one. If the user has multiple
// instances playing audio at the this time, the first instance wins.
BMessage playlistArchive;
if (quitMessage.FindMessage("playlist", &playlistArchive) == B_OK) {
_StoreCurrentPlaylist(&playlistArchive);
break;
}
}
// Note: This needs to be done here, SettingsWindow::QuitRequested()
// returns "false" always. (Standard BApplication quit procedure will
// hang otherwise.)
if (fSettingsWindow && fSettingsWindow->Lock())
fSettingsWindow->Quit();
fSettingsWindow = NULL;
// store the current file panel ref in the global settings
Settings::Default()->SetFilePanelFolder(fLastFilePanelFolder);
return BApplication::QuitRequested();
}
MainWin*
MainApp::NewWindow(BMessage* message)
{
BAutolock _(this);
fPlayerCount++;
return new(std::nothrow) MainWin(fPlayerCount == 1, message);
}
int32
MainApp::PlayerCount() const
{
BAutolock _(const_cast<MainApp*>(this));
return fPlayerCount;
}
// #pragma mark -
void
MainApp::ReadyToRun()
{
// make sure we have at least one window open
if (fPlayerCount == 0) {
MainWin* window = NewWindow();
if (window == NULL) {
PostMessage(B_QUIT_REQUESTED);
return;
}
BMessage lastPlaylistArchive;
if (_RestoreCurrentPlaylist(&lastPlaylistArchive) == B_OK) {
lastPlaylistArchive.what = M_OPEN_PREVIOUS_PLAYLIST;
window->PostMessage(&lastPlaylistArchive);
} else
window->Show();
}
// setup the settings window now, we need to have it
fSettingsWindow = new SettingsWindow(BRect(150, 150, 450, 520));
fSettingsWindow->Hide();
fSettingsWindow->Show();
_InstallPlaylistMimeType();
}
void
MainApp::RefsReceived(BMessage* message)
{
// The user dropped a file (or files) on this app's icon,
// or double clicked a file that's handled by this app.
// Command line arguments are also redirected to here by
// ArgvReceived() but without MIME type check.
// If multiple refs are received in short succession we
// combine them into a single window/playlist. Tracker
// will send multiple messages when opening a multi-
// selection for example and we don't want to spawn large
// numbers of windows when someone just tries to open an
// album. We use half a second time and prolong it for
// each new ref received.
static bigtime_t sLastRefsReceived = 0;
static MainWin* sLastRefsWindow = NULL;
if (system_time() - sLastRefsReceived < 500000) {
// Find the last opened window
for (int32 i = CountWindows() - 1; i >= 0; i--) {
MainWin* playerWindow = dynamic_cast<MainWin*>(WindowAt(i));
if (playerWindow == NULL)
continue;
if (playerWindow != sLastRefsWindow) {
// The window has changed since the last refs
sLastRefsReceived = 0;
sLastRefsWindow = NULL;
break;
}
message->AddBool("append to playlist", true);
playerWindow->PostMessage(message);
sLastRefsReceived = system_time();
return;
}
}
sLastRefsWindow = NewWindow(message);
sLastRefsReceived = system_time();
}
void
MainApp::ArgvReceived(int32 argc, char** argv)
{
char cwd[B_PATH_NAME_LENGTH];
getcwd(cwd, sizeof(cwd));
for (int i = 1; i < argc; i++) {
BUrl url(argv[i]);
if (url.IsValid()) {
BMessage archivedUrl;
url.Archive(&archivedUrl);
BMessage msg(M_URL_RECEIVED);
if (msg.AddMessage("mediaplayer:url", &archivedUrl) == B_OK)
RefsReceived(&msg);
continue;
}
BPath path;
if (argv[i][0] != '/')
path.SetTo(cwd, argv[i]);
else
path.SetTo(argv[i]);
BEntry entry(path.Path(), true);
if (!entry.Exists() || !entry.IsFile())
continue;
BMessage message(B_REFS_RECEIVED);
entry_ref ref;
if (entry.GetRef(&ref) == B_OK && message.AddRef("refs", &ref) == B_OK)
RefsReceived(&message);
}
}
void
MainApp::MessageReceived(BMessage* message)
{
switch (message->what) {
case M_NEW_PLAYER:
{
MainWin* window = NewWindow();
if (window != NULL)
window->Show();
break;
}
case M_PLAYER_QUIT:
{
// store the window settings of this instance
MainWin* window = NULL;
bool audioOnly = false;
BRect windowFrame;
bigtime_t creationTime;
if (message->FindPointer("instance", (void**)&window) == B_OK
&& message->FindBool("audio only", &audioOnly) == B_OK
&& message->FindRect("window frame", &windowFrame) == B_OK
&& message->FindInt64("creation time", &creationTime) == B_OK) {
if (audioOnly && (!fAudioWindowFrameSaved
|| creationTime < fLastSavedAudioWindowCreationTime)) {
fAudioWindowFrameSaved = true;
fLastSavedAudioWindowCreationTime = creationTime;
Settings::Default()->SetAudioPlayerWindowFrame(windowFrame);
}
}
// Store the playlist if there is one. Since the app is doing
// this, it is "atomic". If the user has multiple instances
// playing audio at the same time, the last instance which is
// quit wins.
BMessage playlistArchive;
if (message->FindMessage("playlist", &playlistArchive) == B_OK)
_StoreCurrentPlaylist(&playlistArchive);
// quit if this was the last player window
fPlayerCount--;
if (fPlayerCount == 0)
PostMessage(B_QUIT_REQUESTED);
break;
}
case M_SETTINGS:
_ShowSettingsWindow();
break;
case M_SHOW_OPEN_PANEL:
_ShowOpenFilePanel(message);
break;
case M_SHOW_SAVE_PANEL:
_ShowSaveFilePanel(message);
break;
case M_OPEN_PANEL_RESULT:
_HandleOpenPanelResult(message);
break;
case M_SAVE_PANEL_RESULT:
_HandleSavePanelResult(message);
break;
case B_CANCEL:
{
// The user canceled a file panel, but store at least the current
// file panel folder.
uint32 oldWhat;
if (message->FindInt32("old_what", (int32*)&oldWhat) != B_OK)
break;
if (oldWhat == M_OPEN_PANEL_RESULT && fOpenFilePanel != NULL)
fOpenFilePanel->GetPanelDirectory(&fLastFilePanelFolder);
else if (oldWhat == M_SAVE_PANEL_RESULT && fSaveFilePanel != NULL)
fSaveFilePanel->GetPanelDirectory(&fLastFilePanelFolder);
break;
}
default:
BApplication::MessageReceived(message);
break;
}
}
// #pragma mark -
void
MainApp::_BroadcastMessage(const BMessage& _message)
{
for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
BMessage message(_message);
window->PostMessage(&message);
}
}
void
MainApp::_ShowSettingsWindow()
{
BAutolock lock(fSettingsWindow);
if (!lock.IsLocked())
return;
// If the window is already showing, don't jerk the workspaces around,
// just pull it to the current one.
uint32 workspace = 1UL << (uint32)current_workspace();
uint32 windowWorkspaces = fSettingsWindow->Workspaces();
if ((windowWorkspaces & workspace) == 0) {
// window in a different workspace, reopen in current
fSettingsWindow->SetWorkspaces(workspace);
}
if (fSettingsWindow->IsHidden())
fSettingsWindow->Show();
else
fSettingsWindow->Activate();
}
// #pragma mark - file panels
void
MainApp::_ShowOpenFilePanel(const BMessage* message)
{
if (fOpenFilePanel == NULL) {
BMessenger target(this);
fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, &target);
}
_ShowFilePanel(fOpenFilePanel, M_OPEN_PANEL_RESULT, message,
B_TRANSLATE("Open"), B_TRANSLATE("Open"));
}
void
MainApp::_ShowSaveFilePanel(const BMessage* message)
{
if (fSaveFilePanel == NULL) {
BMessenger target(this);
fSaveFilePanel = new BFilePanel(B_SAVE_PANEL, &target);
}
_ShowFilePanel(fSaveFilePanel, M_SAVE_PANEL_RESULT, message,
B_TRANSLATE("Save"), B_TRANSLATE("Save"));
}
void
MainApp::_ShowFilePanel(BFilePanel* panel, uint32 command,
const BMessage* message, const char* defaultTitle,
const char* defaultLabel)
{
// printf("_ShowFilePanel()\n");
// message->PrintToStream();
BMessage panelMessage(command);
if (message != NULL) {
BMessage targetMessage;
if (message->FindMessage("message", &targetMessage) == B_OK)
panelMessage.AddMessage("message", &targetMessage);
BMessenger target;
if (message->FindMessenger("target", &target) == B_OK)
panelMessage.AddMessenger("target", target);
const char* panelTitle;
if (message->FindString("title", &panelTitle) != B_OK)
panelTitle = defaultTitle;
{
BString finalPanelTitle = "MediaPlayer: ";
finalPanelTitle << panelTitle;
BAutolock lock(panel->Window());
panel->Window()->SetTitle(finalPanelTitle.String());
}
const char* buttonLabel;
if (message->FindString("label", &buttonLabel) != B_OK)
buttonLabel = defaultLabel;
panel->SetButtonLabel(B_DEFAULT_BUTTON, buttonLabel);
}
// panelMessage.PrintToStream();
panel->SetMessage(&panelMessage);
if (fLastFilePanelFolder != entry_ref()) {
panel->SetPanelDirectory(&fLastFilePanelFolder);
}
panel->Show();
}
void
MainApp::_HandleOpenPanelResult(const BMessage* message)
{
_HandleFilePanelResult(fOpenFilePanel, message);
}
void
MainApp::_HandleSavePanelResult(const BMessage* message)
{
_HandleFilePanelResult(fSaveFilePanel, message);
}
void
MainApp::_HandleFilePanelResult(BFilePanel* panel, const BMessage* message)
{
// printf("_HandleFilePanelResult()\n");
// message->PrintToStream();
panel->GetPanelDirectory(&fLastFilePanelFolder);
BMessage targetMessage;
if (message->FindMessage("message", &targetMessage) != B_OK)
targetMessage.what = message->what;
BMessenger target;
if (message->FindMessenger("target", &target) != B_OK) {
if (targetMessage.what == M_OPEN_PANEL_RESULT
|| targetMessage.what == M_SAVE_PANEL_RESULT) {
// prevent endless message cycle
return;
}
// send result message to ourselves
target = BMessenger(this);
}
// copy the important contents of the message
// save panel
entry_ref directory;
if (message->FindRef("directory", &directory) == B_OK)
targetMessage.AddRef("directory", &directory);
const char* name;
if (message->FindString("name", &name) == B_OK)
targetMessage.AddString("name", name);
// open panel
entry_ref ref;
for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++)
targetMessage.AddRef("refs", &ref);
target.SendMessage(&targetMessage);
}
void
MainApp::_StoreCurrentPlaylist(const BMessage* message) const
{
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
|| path.Append(kCurrentPlaylistFilename) != B_OK) {
return;
}
BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
if (file.InitCheck() != B_OK)
return;
message->Flatten(&file);
}
status_t
MainApp::_RestoreCurrentPlaylist(BMessage* message) const
{
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
|| path.Append(kCurrentPlaylistFilename) != B_OK) {
return B_ERROR;
}
BFile file(path.Path(), B_READ_ONLY);
if (file.InitCheck() != B_OK)
return B_ERROR;
return message->Unflatten(&file);
}
void
MainApp::_InstallPlaylistMimeType()
{
// install mime type of documents
BMimeType mime(kBinaryPlaylistMimeString);
status_t ret = mime.InitCheck();
if (ret != B_OK) {
fprintf(stderr, "Could not init native document mime type (%s): %s.\n",
kBinaryPlaylistMimeString, strerror(ret));
return;
}
if (mime.IsInstalled() && !(modifiers() & B_SHIFT_KEY)) {
// mime is already installed, and the user is not
// pressing the shift key to force a re-install
return;
}
ret = mime.Install();
if (ret != B_OK && ret != B_FILE_EXISTS) {
fprintf(stderr, "Could not install native document mime type (%s): %s.\n",
kBinaryPlaylistMimeString, strerror(ret));
return;
}
// set preferred app
ret = mime.SetPreferredApp(kAppSig);
if (ret != B_OK) {
fprintf(stderr, "Could not set native document preferred app: %s\n",
strerror(ret));
}
// set descriptions
ret = mime.SetShortDescription("MediaPlayer playlist");
if (ret != B_OK) {
fprintf(stderr, "Could not set short description of mime type: %s\n",
strerror(ret));
}
ret = mime.SetLongDescription("MediaPlayer binary playlist file");
if (ret != B_OK) {
fprintf(stderr, "Could not set long description of mime type: %s\n",
strerror(ret));
}
// set extensions
BMessage message('extn');
message.AddString("extensions", "playlist");
ret = mime.SetFileExtensions(&message);
if (ret != B_OK) {
fprintf(stderr, "Could not set extensions of mime type: %s\n",
strerror(ret));
}
// set sniffer rule
char snifferRule[32];
uint32 bigEndianMagic = B_HOST_TO_BENDIAN_INT32(kPlaylistMagicBytes);
sprintf(snifferRule, "0.9 ('%4s')", (const char*)&bigEndianMagic);
ret = mime.SetSnifferRule(snifferRule);
if (ret != B_OK) {
BString parseError;
BMimeType::CheckSnifferRule(snifferRule, &parseError);
fprintf(stderr, "Could not set sniffer rule of mime type: %s\n",
parseError.String());
}
// set playlist icon
BResources* resources = AppResources();
// does not need to be freed (belongs to BApplication base)
if (resources != NULL) {
size_t size;
const void* iconData = resources->LoadResource('VICN', "PlaylistIcon",
&size);
if (iconData != NULL && size > 0) {
if (mime.SetIcon(reinterpret_cast<const uint8*>(iconData), size)
!= B_OK) {
fprintf(stderr, "Could not set vector icon of mime type.\n");
}
} else {
fprintf(stderr, "Could not find icon in app resources "
"(data: %p, size: %ld).\n", iconData, size);
}
} else
fprintf(stderr, "Could not find app resources.\n");
}
// #pragma mark - main
int
main()
{
EventQueue::CreateDefault();
srand(system_time());
gMainApp = new MainApp;
gMainApp->Run();
delete gMainApp;
EventQueue::DeleteDefault();
return 0;
}
↑ V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.