2 * MainApp.cpp - Media Player for the Haiku Operating System
4 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
5 * Copyright (C) 2008 Stephan Aßmus <superstippi@gmx.de> (MIT Ok)
7 * Released under the terms of the MIT license.
17 #include <FilePanel.h>
19 #include <MediaDefs.h>
20 #include <MediaRoster.h>
23 #include <Resources.h>
29 #include "EventQueue.h"
32 #include "SettingsWindow.h"
35 #undef B_TRANSLATION_CONTEXT
36 #define B_TRANSLATION_CONTEXT "MediaPlayer-Main"
39 static const char* kCurrentPlaylistFilename
= "MediaPlayer Current Playlist";
41 const char* kAppSig
= "application/x-vnd.Haiku-MediaPlayer";
48 BApplication(kAppSig
),
50 fSettingsWindow(NULL
),
54 fLastFilePanelFolder(),
56 fAudioWindowFrameSaved(false),
57 fLastSavedAudioWindowCreationTime(0)
59 fLastFilePanelFolder
= Settings::Default()->FilePanelFolder();
61 if (!BMediaRoster::IsRunning()) {
62 BAlert
* alert
= new BAlert("start_media_server",
63 B_TRANSLATE("It appears the media server is not running.\n"
64 "Would you like to start it ?"), B_TRANSLATE("Quit"),
65 B_TRANSLATE("Start media server"), NULL
,
66 B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
67 alert
->SetShortcut(0, B_ESCAPE
);
69 if (alert
->Go() == 0) {
70 PostMessage(B_QUIT_REQUESTED
);
74 launch_media_server();
81 delete fOpenFilePanel
;
82 delete fSaveFilePanel
;
87 MainApp::QuitRequested()
89 // Make sure we store the current playlist, if applicable.
90 for (int32 i
= 0; BWindow
* window
= WindowAt(i
); i
++) {
91 MainWin
* playerWindow
= dynamic_cast<MainWin
*>(window
);
92 if (playerWindow
== NULL
)
95 BAutolock
_(playerWindow
);
98 playerWindow
->GetQuitMessage(&quitMessage
);
100 // Store the playlist if there is one. If the user has multiple
101 // instances playing audio at the this time, the first instance wins.
102 BMessage playlistArchive
;
103 if (quitMessage
.FindMessage("playlist", &playlistArchive
) == B_OK
) {
104 _StoreCurrentPlaylist(&playlistArchive
);
109 // Note: This needs to be done here, SettingsWindow::QuitRequested()
110 // returns "false" always. (Standard BApplication quit procedure will
112 if (fSettingsWindow
&& fSettingsWindow
->Lock())
113 fSettingsWindow
->Quit();
114 fSettingsWindow
= NULL
;
116 // store the current file panel ref in the global settings
117 Settings::Default()->SetFilePanelFolder(fLastFilePanelFolder
);
119 return BApplication::QuitRequested();
124 MainApp::NewWindow(BMessage
* message
)
128 return new(std::nothrow
) MainWin(fPlayerCount
== 1, message
);
133 MainApp::PlayerCount() const
135 BAutolock
_(const_cast<MainApp
*>(this));
144 MainApp::ReadyToRun()
146 // make sure we have at least one window open
147 if (fPlayerCount
== 0) {
148 MainWin
* window
= NewWindow();
149 if (window
== NULL
) {
150 PostMessage(B_QUIT_REQUESTED
);
153 BMessage lastPlaylistArchive
;
154 if (_RestoreCurrentPlaylist(&lastPlaylistArchive
) == B_OK
) {
155 lastPlaylistArchive
.what
= M_OPEN_PREVIOUS_PLAYLIST
;
156 window
->PostMessage(&lastPlaylistArchive
);
161 // setup the settings window now, we need to have it
162 fSettingsWindow
= new SettingsWindow(BRect(150, 150, 450, 520));
163 fSettingsWindow
->Hide();
164 fSettingsWindow
->Show();
166 _InstallPlaylistMimeType();
171 MainApp::RefsReceived(BMessage
* message
)
173 // The user dropped a file (or files) on this app's icon,
174 // or double clicked a file that's handled by this app.
175 // Command line arguments are also redirected to here by
176 // ArgvReceived() but without MIME type check.
178 // If multiple refs are received in short succession we
179 // combine them into a single window/playlist. Tracker
180 // will send multiple messages when opening a multi-
181 // selection for example and we don't want to spawn large
182 // numbers of windows when someone just tries to open an
183 // album. We use half a second time and prolong it for
184 // each new ref received.
185 static bigtime_t sLastRefsReceived
= 0;
186 static MainWin
* sLastRefsWindow
= NULL
;
188 if (system_time() - sLastRefsReceived
< 500000) {
189 // Find the last opened window
190 for (int32 i
= CountWindows() - 1; i
>= 0; i
--) {
191 MainWin
* playerWindow
= dynamic_cast<MainWin
*>(WindowAt(i
));
192 if (playerWindow
== NULL
)
195 if (playerWindow
!= sLastRefsWindow
) {
196 // The window has changed since the last refs
197 sLastRefsReceived
= 0;
198 sLastRefsWindow
= NULL
;
202 message
->AddBool("append to playlist", true);
203 playerWindow
->PostMessage(message
);
204 sLastRefsReceived
= system_time();
209 sLastRefsWindow
= NewWindow(message
);
210 sLastRefsReceived
= system_time();
215 MainApp::ArgvReceived(int32 argc
, char** argv
)
217 char cwd
[B_PATH_NAME_LENGTH
];
218 getcwd(cwd
, sizeof(cwd
));
220 for (int i
= 1; i
< argc
; i
++) {
223 BMessage archivedUrl
;
224 url
.Archive(&archivedUrl
);
226 BMessage
msg(M_URL_RECEIVED
);
227 if (msg
.AddMessage("mediaplayer:url", &archivedUrl
) == B_OK
)
234 if (argv
[i
][0] != '/')
235 path
.SetTo(cwd
, argv
[i
]);
238 BEntry
entry(path
.Path(), true);
239 if (!entry
.Exists() || !entry
.IsFile())
242 BMessage
message(B_REFS_RECEIVED
);
244 if (entry
.GetRef(&ref
) == B_OK
&& message
.AddRef("refs", &ref
) == B_OK
)
245 RefsReceived(&message
);
251 MainApp::MessageReceived(BMessage
* message
)
253 switch (message
->what
) {
256 MainWin
* window
= NewWindow();
263 // store the window settings of this instance
264 MainWin
* window
= NULL
;
265 bool audioOnly
= false;
267 bigtime_t creationTime
;
268 if (message
->FindPointer("instance", (void**)&window
) == B_OK
269 && message
->FindBool("audio only", &audioOnly
) == B_OK
270 && message
->FindRect("window frame", &windowFrame
) == B_OK
271 && message
->FindInt64("creation time", &creationTime
) == B_OK
) {
272 if (audioOnly
&& (!fAudioWindowFrameSaved
273 || creationTime
< fLastSavedAudioWindowCreationTime
)) {
274 fAudioWindowFrameSaved
= true;
275 fLastSavedAudioWindowCreationTime
= creationTime
;
277 Settings::Default()->SetAudioPlayerWindowFrame(windowFrame
);
281 // Store the playlist if there is one. Since the app is doing
282 // this, it is "atomic". If the user has multiple instances
283 // playing audio at the same time, the last instance which is
285 BMessage playlistArchive
;
286 if (message
->FindMessage("playlist", &playlistArchive
) == B_OK
)
287 _StoreCurrentPlaylist(&playlistArchive
);
289 // quit if this was the last player window
291 if (fPlayerCount
== 0)
292 PostMessage(B_QUIT_REQUESTED
);
297 _ShowSettingsWindow();
300 case M_SHOW_OPEN_PANEL
:
301 _ShowOpenFilePanel(message
);
303 case M_SHOW_SAVE_PANEL
:
304 _ShowSaveFilePanel(message
);
307 case M_OPEN_PANEL_RESULT
:
308 _HandleOpenPanelResult(message
);
310 case M_SAVE_PANEL_RESULT
:
311 _HandleSavePanelResult(message
);
315 // The user canceled a file panel, but store at least the current
316 // file panel folder.
318 if (message
->FindInt32("old_what", (int32
*)&oldWhat
) != B_OK
)
320 if (oldWhat
== M_OPEN_PANEL_RESULT
&& fOpenFilePanel
!= NULL
)
321 fOpenFilePanel
->GetPanelDirectory(&fLastFilePanelFolder
);
322 else if (oldWhat
== M_SAVE_PANEL_RESULT
&& fSaveFilePanel
!= NULL
)
323 fSaveFilePanel
->GetPanelDirectory(&fLastFilePanelFolder
);
328 BApplication::MessageReceived(message
);
338 MainApp::_BroadcastMessage(const BMessage
& _message
)
340 for (int32 i
= 0; BWindow
* window
= WindowAt(i
); i
++) {
341 BMessage
message(_message
);
342 window
->PostMessage(&message
);
348 MainApp::_ShowSettingsWindow()
350 BAutolock
lock(fSettingsWindow
);
351 if (!lock
.IsLocked())
354 // If the window is already showing, don't jerk the workspaces around,
355 // just pull it to the current one.
356 uint32 workspace
= 1UL << (uint32
)current_workspace();
357 uint32 windowWorkspaces
= fSettingsWindow
->Workspaces();
358 if ((windowWorkspaces
& workspace
) == 0) {
359 // window in a different workspace, reopen in current
360 fSettingsWindow
->SetWorkspaces(workspace
);
363 if (fSettingsWindow
->IsHidden())
364 fSettingsWindow
->Show();
366 fSettingsWindow
->Activate();
370 // #pragma mark - file panels
374 MainApp::_ShowOpenFilePanel(const BMessage
* message
)
376 if (fOpenFilePanel
== NULL
) {
377 BMessenger
target(this);
378 fOpenFilePanel
= new BFilePanel(B_OPEN_PANEL
, &target
);
381 _ShowFilePanel(fOpenFilePanel
, M_OPEN_PANEL_RESULT
, message
,
382 B_TRANSLATE("Open"), B_TRANSLATE("Open"));
387 MainApp::_ShowSaveFilePanel(const BMessage
* message
)
389 if (fSaveFilePanel
== NULL
) {
390 BMessenger
target(this);
391 fSaveFilePanel
= new BFilePanel(B_SAVE_PANEL
, &target
);
394 _ShowFilePanel(fSaveFilePanel
, M_SAVE_PANEL_RESULT
, message
,
395 B_TRANSLATE("Save"), B_TRANSLATE("Save"));
400 MainApp::_ShowFilePanel(BFilePanel
* panel
, uint32 command
,
401 const BMessage
* message
, const char* defaultTitle
,
402 const char* defaultLabel
)
404 // printf("_ShowFilePanel()\n");
405 // message->PrintToStream();
407 BMessage
panelMessage(command
);
409 if (message
!= NULL
) {
410 BMessage targetMessage
;
411 if (message
->FindMessage("message", &targetMessage
) == B_OK
)
412 panelMessage
.AddMessage("message", &targetMessage
);
415 if (message
->FindMessenger("target", &target
) == B_OK
)
416 panelMessage
.AddMessenger("target", target
);
418 const char* panelTitle
;
419 if (message
->FindString("title", &panelTitle
) != B_OK
)
420 panelTitle
= defaultTitle
;
422 BString finalPanelTitle
= "MediaPlayer: ";
423 finalPanelTitle
<< panelTitle
;
424 BAutolock
lock(panel
->Window());
425 panel
->Window()->SetTitle(finalPanelTitle
.String());
427 const char* buttonLabel
;
428 if (message
->FindString("label", &buttonLabel
) != B_OK
)
429 buttonLabel
= defaultLabel
;
430 panel
->SetButtonLabel(B_DEFAULT_BUTTON
, buttonLabel
);
433 // panelMessage.PrintToStream();
434 panel
->SetMessage(&panelMessage
);
436 if (fLastFilePanelFolder
!= entry_ref()) {
437 panel
->SetPanelDirectory(&fLastFilePanelFolder
);
445 MainApp::_HandleOpenPanelResult(const BMessage
* message
)
447 _HandleFilePanelResult(fOpenFilePanel
, message
);
452 MainApp::_HandleSavePanelResult(const BMessage
* message
)
454 _HandleFilePanelResult(fSaveFilePanel
, message
);
459 MainApp::_HandleFilePanelResult(BFilePanel
* panel
, const BMessage
* message
)
461 // printf("_HandleFilePanelResult()\n");
462 // message->PrintToStream();
464 panel
->GetPanelDirectory(&fLastFilePanelFolder
);
466 BMessage targetMessage
;
467 if (message
->FindMessage("message", &targetMessage
) != B_OK
)
468 targetMessage
.what
= message
->what
;
471 if (message
->FindMessenger("target", &target
) != B_OK
) {
472 if (targetMessage
.what
== M_OPEN_PANEL_RESULT
473 || targetMessage
.what
== M_SAVE_PANEL_RESULT
) {
474 // prevent endless message cycle
477 // send result message to ourselves
478 target
= BMessenger(this);
481 // copy the important contents of the message
484 if (message
->FindRef("directory", &directory
) == B_OK
)
485 targetMessage
.AddRef("directory", &directory
);
487 if (message
->FindString("name", &name
) == B_OK
)
488 targetMessage
.AddString("name", name
);
491 for (int32 i
= 0; message
->FindRef("refs", i
, &ref
) == B_OK
; i
++)
492 targetMessage
.AddRef("refs", &ref
);
494 target
.SendMessage(&targetMessage
);
499 MainApp::_StoreCurrentPlaylist(const BMessage
* message
) const
502 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) != B_OK
503 || path
.Append(kCurrentPlaylistFilename
) != B_OK
) {
507 BFile
file(path
.Path(), B_WRITE_ONLY
| B_CREATE_FILE
| B_ERASE_FILE
);
508 if (file
.InitCheck() != B_OK
)
511 message
->Flatten(&file
);
516 MainApp::_RestoreCurrentPlaylist(BMessage
* message
) const
519 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) != B_OK
520 || path
.Append(kCurrentPlaylistFilename
) != B_OK
) {
524 BFile
file(path
.Path(), B_READ_ONLY
);
525 if (file
.InitCheck() != B_OK
)
528 return message
->Unflatten(&file
);
533 MainApp::_InstallPlaylistMimeType()
535 // install mime type of documents
536 BMimeType
mime(kBinaryPlaylistMimeString
);
537 status_t ret
= mime
.InitCheck();
539 fprintf(stderr
, "Could not init native document mime type (%s): %s.\n",
540 kBinaryPlaylistMimeString
, strerror(ret
));
544 if (mime
.IsInstalled() && !(modifiers() & B_SHIFT_KEY
)) {
545 // mime is already installed, and the user is not
546 // pressing the shift key to force a re-install
550 ret
= mime
.Install();
551 if (ret
!= B_OK
&& ret
!= B_FILE_EXISTS
) {
552 fprintf(stderr
, "Could not install native document mime type (%s): %s.\n",
553 kBinaryPlaylistMimeString
, strerror(ret
));
557 ret
= mime
.SetPreferredApp(kAppSig
);
559 fprintf(stderr
, "Could not set native document preferred app: %s\n",
564 ret
= mime
.SetShortDescription("MediaPlayer playlist");
566 fprintf(stderr
, "Could not set short description of mime type: %s\n",
569 ret
= mime
.SetLongDescription("MediaPlayer binary playlist file");
571 fprintf(stderr
, "Could not set long description of mime type: %s\n",
576 BMessage
message('extn');
577 message
.AddString("extensions", "playlist");
578 ret
= mime
.SetFileExtensions(&message
);
580 fprintf(stderr
, "Could not set extensions of mime type: %s\n",
585 char snifferRule
[32];
586 uint32 bigEndianMagic
= B_HOST_TO_BENDIAN_INT32(kPlaylistMagicBytes
);
587 sprintf(snifferRule
, "0.9 ('%4s')", (const char*)&bigEndianMagic
);
588 ret
= mime
.SetSnifferRule(snifferRule
);
591 BMimeType::CheckSnifferRule(snifferRule
, &parseError
);
592 fprintf(stderr
, "Could not set sniffer rule of mime type: %s\n",
593 parseError
.String());
597 BResources
* resources
= AppResources();
598 // does not need to be freed (belongs to BApplication base)
599 if (resources
!= NULL
) {
601 const void* iconData
= resources
->LoadResource('VICN', "PlaylistIcon",
603 if (iconData
!= NULL
&& size
> 0) {
604 if (mime
.SetIcon(reinterpret_cast<const uint8
*>(iconData
), size
)
606 fprintf(stderr
, "Could not set vector icon of mime type.\n");
609 fprintf(stderr
, "Could not find icon in app resources "
610 "(data: %p, size: %ld).\n", iconData
, size
);
613 fprintf(stderr
, "Could not find app resources.\n");
617 // #pragma mark - main
623 EventQueue::CreateDefault();
625 srand(system_time());
627 gMainApp
= new MainApp
;
631 EventQueue::DeleteDefault();