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 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29 #include <FilePanel.h>
31 #include <MediaDefs.h>
32 #include <MediaRoster.h>
35 #include <Resources.h>
41 #include "EventQueue.h"
44 #include "SettingsWindow.h"
47 #undef B_TRANSLATION_CONTEXT
48 #define B_TRANSLATION_CONTEXT "MediaPlayer-Main"
51 static const char* kCurrentPlaylistFilename
= "MediaPlayer Current Playlist";
53 const char* kAppSig
= "application/x-vnd.Haiku-MediaPlayer";
60 BApplication(kAppSig
),
62 fSettingsWindow(NULL
),
66 fLastFilePanelFolder(),
68 fAudioWindowFrameSaved(false),
69 fLastSavedAudioWindowCreationTime(0)
71 fLastFilePanelFolder
= Settings::Default()->FilePanelFolder();
73 if (!BMediaRoster::IsRunning()) {
74 BAlert
* alert
= new BAlert("start_media_server",
75 B_TRANSLATE("It appears the media server is not running.\n"
76 "Would you like to start it ?"), B_TRANSLATE("Quit"),
77 B_TRANSLATE("Start media server"), NULL
,
78 B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
79 alert
->SetShortcut(0, B_ESCAPE
);
81 if (alert
->Go() == 0) {
82 PostMessage(B_QUIT_REQUESTED
);
86 launch_media_server();
93 delete fOpenFilePanel
;
94 delete fSaveFilePanel
;
99 MainApp::QuitRequested()
101 // Make sure we store the current playlist, if applicable.
102 for (int32 i
= 0; BWindow
* window
= WindowAt(i
); i
++) {
103 MainWin
* playerWindow
= dynamic_cast<MainWin
*>(window
);
104 if (playerWindow
== NULL
)
107 BAutolock
_(playerWindow
);
109 BMessage quitMessage
;
110 playerWindow
->GetQuitMessage(&quitMessage
);
112 // Store the playlist if there is one. If the user has multiple
113 // instances playing audio at the this time, the first instance wins.
114 BMessage playlistArchive
;
115 if (quitMessage
.FindMessage("playlist", &playlistArchive
) == B_OK
) {
116 _StoreCurrentPlaylist(&playlistArchive
);
121 // Note: This needs to be done here, SettingsWindow::QuitRequested()
122 // returns "false" always. (Standard BApplication quit procedure will
124 if (fSettingsWindow
&& fSettingsWindow
->Lock())
125 fSettingsWindow
->Quit();
126 fSettingsWindow
= NULL
;
128 // store the current file panel ref in the global settings
129 Settings::Default()->SetFilePanelFolder(fLastFilePanelFolder
);
131 return BApplication::QuitRequested();
136 MainApp::NewWindow(BMessage
* message
)
140 return new(std::nothrow
) MainWin(fPlayerCount
== 1, message
);
145 MainApp::PlayerCount() const
147 BAutolock
_(const_cast<MainApp
*>(this));
156 MainApp::ReadyToRun()
158 // make sure we have at least one window open
159 if (fPlayerCount
== 0) {
160 MainWin
* window
= NewWindow();
161 if (window
== NULL
) {
162 PostMessage(B_QUIT_REQUESTED
);
165 BMessage lastPlaylistArchive
;
166 if (_RestoreCurrentPlaylist(&lastPlaylistArchive
) == B_OK
) {
167 lastPlaylistArchive
.what
= M_OPEN_PREVIOUS_PLAYLIST
;
168 window
->PostMessage(&lastPlaylistArchive
);
173 // setup the settings window now, we need to have it
174 fSettingsWindow
= new SettingsWindow(BRect(150, 150, 450, 520));
175 fSettingsWindow
->Hide();
176 fSettingsWindow
->Show();
178 _InstallPlaylistMimeType();
183 MainApp::RefsReceived(BMessage
* message
)
185 // The user dropped a file (or files) on this app's icon,
186 // or double clicked a file that's handled by this app.
187 // Command line arguments are also redirected to here by
188 // ArgvReceived() but without MIME type check.
190 // If multiple refs are received in short succession we
191 // combine them into a single window/playlist. Tracker
192 // will send multiple messages when opening a multi-
193 // selection for example and we don't want to spawn large
194 // numbers of windows when someone just tries to open an
195 // album. We use half a second time and prolong it for
196 // each new ref received.
197 static bigtime_t sLastRefsReceived
= 0;
198 static MainWin
* sLastRefsWindow
= NULL
;
200 if (system_time() - sLastRefsReceived
< 500000) {
201 // Find the last opened window
202 for (int32 i
= CountWindows() - 1; i
>= 0; i
--) {
203 MainWin
* playerWindow
= dynamic_cast<MainWin
*>(WindowAt(i
));
204 if (playerWindow
== NULL
)
207 if (playerWindow
!= sLastRefsWindow
) {
208 // The window has changed since the last refs
209 sLastRefsReceived
= 0;
210 sLastRefsWindow
= NULL
;
214 message
->AddBool("append to playlist", true);
215 playerWindow
->PostMessage(message
);
216 sLastRefsReceived
= system_time();
221 sLastRefsWindow
= NewWindow(message
);
222 sLastRefsReceived
= system_time();
227 MainApp::ArgvReceived(int32 argc
, char** argv
)
229 char cwd
[B_PATH_NAME_LENGTH
];
230 getcwd(cwd
, sizeof(cwd
));
232 for (int i
= 1; i
< argc
; i
++) {
235 BMessage archivedUrl
;
236 url
.Archive(&archivedUrl
);
238 BMessage
msg(M_URL_RECEIVED
);
239 if (msg
.AddMessage("mediaplayer:url", &archivedUrl
) == B_OK
)
246 if (argv
[i
][0] != '/')
247 path
.SetTo(cwd
, argv
[i
]);
250 BEntry
entry(path
.Path(), true);
251 if (!entry
.Exists() || !entry
.IsFile())
254 BMessage
message(B_REFS_RECEIVED
);
256 if (entry
.GetRef(&ref
) == B_OK
&& message
.AddRef("refs", &ref
) == B_OK
)
257 RefsReceived(&message
);
263 MainApp::MessageReceived(BMessage
* message
)
265 switch (message
->what
) {
268 MainWin
* window
= NewWindow();
275 // store the window settings of this instance
276 MainWin
* window
= NULL
;
277 bool audioOnly
= false;
279 bigtime_t creationTime
;
280 if (message
->FindPointer("instance", (void**)&window
) == B_OK
281 && message
->FindBool("audio only", &audioOnly
) == B_OK
282 && message
->FindRect("window frame", &windowFrame
) == B_OK
283 && message
->FindInt64("creation time", &creationTime
) == B_OK
) {
284 if (audioOnly
&& (!fAudioWindowFrameSaved
285 || creationTime
< fLastSavedAudioWindowCreationTime
)) {
286 fAudioWindowFrameSaved
= true;
287 fLastSavedAudioWindowCreationTime
= creationTime
;
289 Settings::Default()->SetAudioPlayerWindowFrame(windowFrame
);
293 // Store the playlist if there is one. Since the app is doing
294 // this, it is "atomic". If the user has multiple instances
295 // playing audio at the same time, the last instance which is
297 BMessage playlistArchive
;
298 if (message
->FindMessage("playlist", &playlistArchive
) == B_OK
)
299 _StoreCurrentPlaylist(&playlistArchive
);
301 // quit if this was the last player window
303 if (fPlayerCount
== 0)
304 PostMessage(B_QUIT_REQUESTED
);
309 _ShowSettingsWindow();
312 case M_SHOW_OPEN_PANEL
:
313 _ShowOpenFilePanel(message
);
315 case M_SHOW_SAVE_PANEL
:
316 _ShowSaveFilePanel(message
);
319 case M_OPEN_PANEL_RESULT
:
320 _HandleOpenPanelResult(message
);
322 case M_SAVE_PANEL_RESULT
:
323 _HandleSavePanelResult(message
);
327 // The user canceled a file panel, but store at least the current
328 // file panel folder.
330 if (message
->FindInt32("old_what", (int32
*)&oldWhat
) != B_OK
)
332 if (oldWhat
== M_OPEN_PANEL_RESULT
&& fOpenFilePanel
!= NULL
)
333 fOpenFilePanel
->GetPanelDirectory(&fLastFilePanelFolder
);
334 else if (oldWhat
== M_SAVE_PANEL_RESULT
&& fSaveFilePanel
!= NULL
)
335 fSaveFilePanel
->GetPanelDirectory(&fLastFilePanelFolder
);
340 BApplication::MessageReceived(message
);
350 MainApp::_BroadcastMessage(const BMessage
& _message
)
352 for (int32 i
= 0; BWindow
* window
= WindowAt(i
); i
++) {
353 BMessage
message(_message
);
354 window
->PostMessage(&message
);
360 MainApp::_ShowSettingsWindow()
362 BAutolock
lock(fSettingsWindow
);
363 if (!lock
.IsLocked())
366 // If the window is already showing, don't jerk the workspaces around,
367 // just pull it to the current one.
368 uint32 workspace
= 1UL << (uint32
)current_workspace();
369 uint32 windowWorkspaces
= fSettingsWindow
->Workspaces();
370 if ((windowWorkspaces
& workspace
) == 0) {
371 // window in a different workspace, reopen in current
372 fSettingsWindow
->SetWorkspaces(workspace
);
375 if (fSettingsWindow
->IsHidden())
376 fSettingsWindow
->Show();
378 fSettingsWindow
->Activate();
382 // #pragma mark - file panels
386 MainApp::_ShowOpenFilePanel(const BMessage
* message
)
388 if (fOpenFilePanel
== NULL
) {
389 BMessenger
target(this);
390 fOpenFilePanel
= new BFilePanel(B_OPEN_PANEL
, &target
);
393 _ShowFilePanel(fOpenFilePanel
, M_OPEN_PANEL_RESULT
, message
,
394 B_TRANSLATE("Open"), B_TRANSLATE("Open"));
399 MainApp::_ShowSaveFilePanel(const BMessage
* message
)
401 if (fSaveFilePanel
== NULL
) {
402 BMessenger
target(this);
403 fSaveFilePanel
= new BFilePanel(B_SAVE_PANEL
, &target
);
406 _ShowFilePanel(fSaveFilePanel
, M_SAVE_PANEL_RESULT
, message
,
407 B_TRANSLATE("Save"), B_TRANSLATE("Save"));
412 MainApp::_ShowFilePanel(BFilePanel
* panel
, uint32 command
,
413 const BMessage
* message
, const char* defaultTitle
,
414 const char* defaultLabel
)
416 // printf("_ShowFilePanel()\n");
417 // message->PrintToStream();
419 BMessage
panelMessage(command
);
421 if (message
!= NULL
) {
422 BMessage targetMessage
;
423 if (message
->FindMessage("message", &targetMessage
) == B_OK
)
424 panelMessage
.AddMessage("message", &targetMessage
);
427 if (message
->FindMessenger("target", &target
) == B_OK
)
428 panelMessage
.AddMessenger("target", target
);
430 const char* panelTitle
;
431 if (message
->FindString("title", &panelTitle
) != B_OK
)
432 panelTitle
= defaultTitle
;
434 BString finalPanelTitle
= "MediaPlayer: ";
435 finalPanelTitle
<< panelTitle
;
436 BAutolock
lock(panel
->Window());
437 panel
->Window()->SetTitle(finalPanelTitle
.String());
439 const char* buttonLabel
;
440 if (message
->FindString("label", &buttonLabel
) != B_OK
)
441 buttonLabel
= defaultLabel
;
442 panel
->SetButtonLabel(B_DEFAULT_BUTTON
, buttonLabel
);
445 // panelMessage.PrintToStream();
446 panel
->SetMessage(&panelMessage
);
448 if (fLastFilePanelFolder
!= entry_ref()) {
449 panel
->SetPanelDirectory(&fLastFilePanelFolder
);
457 MainApp::_HandleOpenPanelResult(const BMessage
* message
)
459 _HandleFilePanelResult(fOpenFilePanel
, message
);
464 MainApp::_HandleSavePanelResult(const BMessage
* message
)
466 _HandleFilePanelResult(fSaveFilePanel
, message
);
471 MainApp::_HandleFilePanelResult(BFilePanel
* panel
, const BMessage
* message
)
473 // printf("_HandleFilePanelResult()\n");
474 // message->PrintToStream();
476 panel
->GetPanelDirectory(&fLastFilePanelFolder
);
478 BMessage targetMessage
;
479 if (message
->FindMessage("message", &targetMessage
) != B_OK
)
480 targetMessage
.what
= message
->what
;
483 if (message
->FindMessenger("target", &target
) != B_OK
) {
484 if (targetMessage
.what
== M_OPEN_PANEL_RESULT
485 || targetMessage
.what
== M_SAVE_PANEL_RESULT
) {
486 // prevent endless message cycle
489 // send result message to ourselves
490 target
= BMessenger(this);
493 // copy the important contents of the message
496 if (message
->FindRef("directory", &directory
) == B_OK
)
497 targetMessage
.AddRef("directory", &directory
);
499 if (message
->FindString("name", &name
) == B_OK
)
500 targetMessage
.AddString("name", name
);
503 for (int32 i
= 0; message
->FindRef("refs", i
, &ref
) == B_OK
; i
++)
504 targetMessage
.AddRef("refs", &ref
);
506 target
.SendMessage(&targetMessage
);
511 MainApp::_StoreCurrentPlaylist(const BMessage
* message
) const
514 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) != B_OK
515 || path
.Append(kCurrentPlaylistFilename
) != B_OK
) {
519 BFile
file(path
.Path(), B_WRITE_ONLY
| B_CREATE_FILE
| B_ERASE_FILE
);
520 if (file
.InitCheck() != B_OK
)
523 message
->Flatten(&file
);
528 MainApp::_RestoreCurrentPlaylist(BMessage
* message
) const
531 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) != B_OK
532 || path
.Append(kCurrentPlaylistFilename
) != B_OK
) {
536 BFile
file(path
.Path(), B_READ_ONLY
);
537 if (file
.InitCheck() != B_OK
)
540 return message
->Unflatten(&file
);
545 MainApp::_InstallPlaylistMimeType()
547 // install mime type of documents
548 BMimeType
mime(kBinaryPlaylistMimeString
);
549 status_t ret
= mime
.InitCheck();
551 fprintf(stderr
, "Could not init native document mime type (%s): %s.\n",
552 kBinaryPlaylistMimeString
, strerror(ret
));
556 if (mime
.IsInstalled() && !(modifiers() & B_SHIFT_KEY
)) {
557 // mime is already installed, and the user is not
558 // pressing the shift key to force a re-install
562 ret
= mime
.Install();
563 if (ret
!= B_OK
&& ret
!= B_FILE_EXISTS
) {
564 fprintf(stderr
, "Could not install native document mime type (%s): %s.\n",
565 kBinaryPlaylistMimeString
, strerror(ret
));
569 ret
= mime
.SetPreferredApp(kAppSig
);
571 fprintf(stderr
, "Could not set native document preferred app: %s\n",
576 ret
= mime
.SetShortDescription("MediaPlayer playlist");
578 fprintf(stderr
, "Could not set short description of mime type: %s\n",
581 ret
= mime
.SetLongDescription("MediaPlayer binary playlist file");
583 fprintf(stderr
, "Could not set long description of mime type: %s\n",
588 BMessage
message('extn');
589 message
.AddString("extensions", "playlist");
590 ret
= mime
.SetFileExtensions(&message
);
592 fprintf(stderr
, "Could not set extensions of mime type: %s\n",
597 char snifferRule
[32];
598 uint32 bigEndianMagic
= B_HOST_TO_BENDIAN_INT32(kPlaylistMagicBytes
);
599 sprintf(snifferRule
, "0.9 ('%4s')", (const char*)&bigEndianMagic
);
600 ret
= mime
.SetSnifferRule(snifferRule
);
603 BMimeType::CheckSnifferRule(snifferRule
, &parseError
);
604 fprintf(stderr
, "Could not set sniffer rule of mime type: %s\n",
605 parseError
.String());
609 BResources
* resources
= AppResources();
610 // does not need to be freed (belongs to BApplication base)
611 if (resources
!= NULL
) {
613 const void* iconData
= resources
->LoadResource('VICN', "PlaylistIcon",
615 if (iconData
!= NULL
&& size
> 0) {
616 if (mime
.SetIcon(reinterpret_cast<const uint8
*>(iconData
), size
)
618 fprintf(stderr
, "Could not set vector icon of mime type.\n");
621 fprintf(stderr
, "Could not find icon in app resources "
622 "(data: %p, size: %ld).\n", iconData
, size
);
625 fprintf(stderr
, "Could not find app resources.\n");
629 // #pragma mark - main
635 EventQueue::CreateDefault();
637 srand(system_time());
639 gMainApp
= new MainApp
;
643 EventQueue::DeleteDefault();