btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / mediaplayer / MainApp.cpp
blobaf37a1b47c75a66f3c283e12758f4c1e2f154a52
1 /*
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.
23 #include "MainApp.h"
25 #include <Alert.h>
26 #include <Autolock.h>
27 #include <Catalog.h>
28 #include <Entry.h>
29 #include <FilePanel.h>
30 #include <Locale.h>
31 #include <MediaDefs.h>
32 #include <MediaRoster.h>
33 #include <MimeType.h>
34 #include <Path.h>
35 #include <Resources.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
41 #include "EventQueue.h"
42 #include "Playlist.h"
43 #include "Settings.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";
55 MainApp* gMainApp;
58 MainApp::MainApp()
60 BApplication(kAppSig),
61 fPlayerCount(0),
62 fSettingsWindow(NULL),
64 fOpenFilePanel(NULL),
65 fSaveFilePanel(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);
83 return;
86 launch_media_server();
91 MainApp::~MainApp()
93 delete fOpenFilePanel;
94 delete fSaveFilePanel;
98 bool
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)
105 continue;
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);
117 break;
121 // Note: This needs to be done here, SettingsWindow::QuitRequested()
122 // returns "false" always. (Standard BApplication quit procedure will
123 // hang otherwise.)
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();
135 MainWin*
136 MainApp::NewWindow(BMessage* message)
138 BAutolock _(this);
139 fPlayerCount++;
140 return new(std::nothrow) MainWin(fPlayerCount == 1, message);
144 int32
145 MainApp::PlayerCount() const
147 BAutolock _(const_cast<MainApp*>(this));
148 return fPlayerCount;
152 // #pragma mark -
155 void
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);
163 return;
165 BMessage lastPlaylistArchive;
166 if (_RestoreCurrentPlaylist(&lastPlaylistArchive) == B_OK) {
167 lastPlaylistArchive.what = M_OPEN_PREVIOUS_PLAYLIST;
168 window->PostMessage(&lastPlaylistArchive);
169 } else
170 window->Show();
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();
182 void
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)
205 continue;
207 if (playerWindow != sLastRefsWindow) {
208 // The window has changed since the last refs
209 sLastRefsReceived = 0;
210 sLastRefsWindow = NULL;
211 break;
214 message->AddBool("append to playlist", true);
215 playerWindow->PostMessage(message);
216 sLastRefsReceived = system_time();
217 return;
221 sLastRefsWindow = NewWindow(message);
222 sLastRefsReceived = system_time();
226 void
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++) {
233 BUrl url(argv[i]);
234 if (url.IsValid()) {
235 BMessage archivedUrl;
236 url.Archive(&archivedUrl);
238 BMessage msg(M_URL_RECEIVED);
239 if (msg.AddMessage("mediaplayer:url", &archivedUrl) == B_OK)
240 RefsReceived(&msg);
242 continue;
245 BPath path;
246 if (argv[i][0] != '/')
247 path.SetTo(cwd, argv[i]);
248 else
249 path.SetTo(argv[i]);
250 BEntry entry(path.Path(), true);
251 if (!entry.Exists() || !entry.IsFile())
252 continue;
254 BMessage message(B_REFS_RECEIVED);
255 entry_ref ref;
256 if (entry.GetRef(&ref) == B_OK && message.AddRef("refs", &ref) == B_OK)
257 RefsReceived(&message);
262 void
263 MainApp::MessageReceived(BMessage* message)
265 switch (message->what) {
266 case M_NEW_PLAYER:
268 MainWin* window = NewWindow();
269 if (window != NULL)
270 window->Show();
271 break;
273 case M_PLAYER_QUIT:
275 // store the window settings of this instance
276 MainWin* window = NULL;
277 bool audioOnly = false;
278 BRect windowFrame;
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
296 // quit wins.
297 BMessage playlistArchive;
298 if (message->FindMessage("playlist", &playlistArchive) == B_OK)
299 _StoreCurrentPlaylist(&playlistArchive);
301 // quit if this was the last player window
302 fPlayerCount--;
303 if (fPlayerCount == 0)
304 PostMessage(B_QUIT_REQUESTED);
305 break;
308 case M_SETTINGS:
309 _ShowSettingsWindow();
310 break;
312 case M_SHOW_OPEN_PANEL:
313 _ShowOpenFilePanel(message);
314 break;
315 case M_SHOW_SAVE_PANEL:
316 _ShowSaveFilePanel(message);
317 break;
319 case M_OPEN_PANEL_RESULT:
320 _HandleOpenPanelResult(message);
321 break;
322 case M_SAVE_PANEL_RESULT:
323 _HandleSavePanelResult(message);
324 break;
325 case B_CANCEL:
327 // The user canceled a file panel, but store at least the current
328 // file panel folder.
329 uint32 oldWhat;
330 if (message->FindInt32("old_what", (int32*)&oldWhat) != B_OK)
331 break;
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);
336 break;
339 default:
340 BApplication::MessageReceived(message);
341 break;
346 // #pragma mark -
349 void
350 MainApp::_BroadcastMessage(const BMessage& _message)
352 for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
353 BMessage message(_message);
354 window->PostMessage(&message);
359 void
360 MainApp::_ShowSettingsWindow()
362 BAutolock lock(fSettingsWindow);
363 if (!lock.IsLocked())
364 return;
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();
377 else
378 fSettingsWindow->Activate();
382 // #pragma mark - file panels
385 void
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"));
398 void
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"));
411 void
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);
426 BMessenger target;
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);
452 panel->Show();
456 void
457 MainApp::_HandleOpenPanelResult(const BMessage* message)
459 _HandleFilePanelResult(fOpenFilePanel, message);
463 void
464 MainApp::_HandleSavePanelResult(const BMessage* message)
466 _HandleFilePanelResult(fSaveFilePanel, message);
470 void
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;
482 BMessenger target;
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
487 return;
489 // send result message to ourselves
490 target = BMessenger(this);
493 // copy the important contents of the message
494 // save panel
495 entry_ref directory;
496 if (message->FindRef("directory", &directory) == B_OK)
497 targetMessage.AddRef("directory", &directory);
498 const char* name;
499 if (message->FindString("name", &name) == B_OK)
500 targetMessage.AddString("name", name);
501 // open panel
502 entry_ref ref;
503 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++)
504 targetMessage.AddRef("refs", &ref);
506 target.SendMessage(&targetMessage);
510 void
511 MainApp::_StoreCurrentPlaylist(const BMessage* message) const
513 BPath path;
514 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
515 || path.Append(kCurrentPlaylistFilename) != B_OK) {
516 return;
519 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
520 if (file.InitCheck() != B_OK)
521 return;
523 message->Flatten(&file);
527 status_t
528 MainApp::_RestoreCurrentPlaylist(BMessage* message) const
530 BPath path;
531 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
532 || path.Append(kCurrentPlaylistFilename) != B_OK) {
533 return B_ERROR;
536 BFile file(path.Path(), B_READ_ONLY);
537 if (file.InitCheck() != B_OK)
538 return B_ERROR;
540 return message->Unflatten(&file);
544 void
545 MainApp::_InstallPlaylistMimeType()
547 // install mime type of documents
548 BMimeType mime(kBinaryPlaylistMimeString);
549 status_t ret = mime.InitCheck();
550 if (ret != B_OK) {
551 fprintf(stderr, "Could not init native document mime type (%s): %s.\n",
552 kBinaryPlaylistMimeString, strerror(ret));
553 return;
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
559 return;
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));
566 return;
568 // set preferred app
569 ret = mime.SetPreferredApp(kAppSig);
570 if (ret != B_OK) {
571 fprintf(stderr, "Could not set native document preferred app: %s\n",
572 strerror(ret));
575 // set descriptions
576 ret = mime.SetShortDescription("MediaPlayer playlist");
577 if (ret != B_OK) {
578 fprintf(stderr, "Could not set short description of mime type: %s\n",
579 strerror(ret));
581 ret = mime.SetLongDescription("MediaPlayer binary playlist file");
582 if (ret != B_OK) {
583 fprintf(stderr, "Could not set long description of mime type: %s\n",
584 strerror(ret));
587 // set extensions
588 BMessage message('extn');
589 message.AddString("extensions", "playlist");
590 ret = mime.SetFileExtensions(&message);
591 if (ret != B_OK) {
592 fprintf(stderr, "Could not set extensions of mime type: %s\n",
593 strerror(ret));
596 // set sniffer rule
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);
601 if (ret != B_OK) {
602 BString parseError;
603 BMimeType::CheckSnifferRule(snifferRule, &parseError);
604 fprintf(stderr, "Could not set sniffer rule of mime type: %s\n",
605 parseError.String());
608 // set playlist icon
609 BResources* resources = AppResources();
610 // does not need to be freed (belongs to BApplication base)
611 if (resources != NULL) {
612 size_t size;
613 const void* iconData = resources->LoadResource('VICN', "PlaylistIcon",
614 &size);
615 if (iconData != NULL && size > 0) {
616 if (mime.SetIcon(reinterpret_cast<const uint8*>(iconData), size)
617 != B_OK) {
618 fprintf(stderr, "Could not set vector icon of mime type.\n");
620 } else {
621 fprintf(stderr, "Could not find icon in app resources "
622 "(data: %p, size: %ld).\n", iconData, size);
624 } else
625 fprintf(stderr, "Could not find app resources.\n");
629 // #pragma mark - main
633 main()
635 EventQueue::CreateDefault();
637 srand(system_time());
639 gMainApp = new MainApp;
640 gMainApp->Run();
641 delete gMainApp;
643 EventQueue::DeleteDefault();
645 return 0;