xref: /haiku/src/apps/mediaplayer/MainApp.cpp (revision d374a27286b8a52974a97dba0d5966ea026a665d)
1 /*
2  * MainApp.cpp - Media Player for the Haiku Operating System
3  *
4  * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
5  * Copyright (C) 2008 Stephan Aßmus <superstippi@gmx.de> (MIT Ok)
6  *
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.
10  *
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.
15  *
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.
19  *
20  */
21 
22 
23 #include "MainApp.h"
24 
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>
36 #include <Roster.h>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 
42 #include "EventQueue.h"
43 #include "Playlist.h"
44 #include "Settings.h"
45 #include "SettingsWindow.h"
46 
47 
48 #undef B_TRANSLATE_CONTEXT
49 #define B_TRANSLATE_CONTEXT "MediaPlayer-Main"
50 
51 
52 static const char* kCurrentPlaylistFilename = "MediaPlayer Current Playlist";
53 
54 const char* kAppSig = "application/x-vnd.Haiku-MediaPlayer";
55 
56 MainApp* gMainApp;
57 
58 static const char* kMediaServerSig = B_MEDIA_SERVER_SIGNATURE;
59 static const char* kMediaServerAddOnSig = "application/x-vnd.Be.addon-host";
60 
61 
62 MainApp::MainApp()
63 	:
64 	BApplication(kAppSig),
65 	fPlayerCount(0),
66 	fSettingsWindow(NULL),
67 
68 	fOpenFilePanel(NULL),
69 	fSaveFilePanel(NULL),
70 	fLastFilePanelFolder(),
71 
72 	fMediaServerRunning(false),
73 	fMediaAddOnServerRunning(false),
74 
75 	fAudioWindowFrameSaved(false),
76 	fLastSavedAudioWindowCreationTime(0)
77 {
78 	mpSettings settings = Settings::CurrentSettings();
79 	fLastFilePanelFolder = settings.filePanelFolder;
80 
81 	// Now tell the application roster, that we're interested
82 	// in getting notifications of apps being launched or quit.
83 	// In this way we are going to detect a media_server restart.
84 	be_roster->StartWatching(BMessenger(this, this),
85 		B_REQUEST_LAUNCHED | B_REQUEST_QUIT);
86 	// we will keep track of the status of media_server
87 	// and media_addon_server
88 	fMediaServerRunning = be_roster->IsRunning(kMediaServerSig);
89 	fMediaAddOnServerRunning = be_roster->IsRunning(kMediaServerAddOnSig);
90 
91 	if (!fMediaServerRunning || !fMediaAddOnServerRunning) {
92 		BAlert* alert = new BAlert("start_media_server",
93 			B_TRANSLATE("It appears the media server is not running.\n"
94 			"Would you like to start it ?"), B_TRANSLATE("Quit"),
95 			B_TRANSLATE("Start media server"), NULL,
96 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
97 		if (alert->Go() == 0) {
98 			PostMessage(B_QUIT_REQUESTED);
99 			return;
100 		}
101 
102 		launch_media_server();
103 
104 		fMediaServerRunning = be_roster->IsRunning(kMediaServerSig);
105 		fMediaAddOnServerRunning = be_roster->IsRunning(kMediaServerAddOnSig);
106 	}
107 }
108 
109 
110 MainApp::~MainApp()
111 {
112 	delete fOpenFilePanel;
113 	delete fSaveFilePanel;
114 }
115 
116 
117 bool
118 MainApp::QuitRequested()
119 {
120 	// Make sure we store the current playlist, if applicable.
121 	for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
122 		MainWin* playerWindow = dynamic_cast<MainWin*>(window);
123 		if (playerWindow == NULL)
124 			continue;
125 
126 		BAutolock _(playerWindow);
127 
128 		BMessage quitMessage;
129 		playerWindow->GetQuitMessage(&quitMessage);
130 
131 		// Store the playlist if there is one. If the user has multiple
132 		// instances playing audio at the this time, the first instance wins.
133 		BMessage playlistArchive;
134 		if (quitMessage.FindMessage("playlist", &playlistArchive) == B_OK) {
135 			_StoreCurrentPlaylist(&playlistArchive);
136 			break;
137 		}
138 	}
139 
140 	// Note: This needs to be done here, SettingsWindow::QuitRequested()
141 	// returns "false" always. (Standard BApplication quit procedure will
142 	// hang otherwise.)
143 	if (fSettingsWindow && fSettingsWindow->Lock())
144 		fSettingsWindow->Quit();
145 	fSettingsWindow = NULL;
146 
147 	// store the current file panel ref in the global settings
148 	mpSettings settings = Settings::CurrentSettings();
149 	settings.filePanelFolder = fLastFilePanelFolder;
150 	Settings::Default()->SaveSettings(settings);
151 
152 	return BApplication::QuitRequested();
153 }
154 
155 
156 MainWin*
157 MainApp::NewWindow(BMessage* message)
158 {
159 	BAutolock _(this);
160 	fPlayerCount++;
161 	return new(std::nothrow) MainWin(fPlayerCount == 1, message);
162 }
163 
164 
165 int32
166 MainApp::PlayerCount() const
167 {
168 	BAutolock _(const_cast<MainApp*>(this));
169 	return fPlayerCount;
170 }
171 
172 
173 // #pragma mark -
174 
175 
176 void
177 MainApp::ReadyToRun()
178 {
179 	// make sure we have at least one window open
180 	if (fPlayerCount == 0) {
181 		MainWin* window = NewWindow();
182 		if (window == NULL) {
183 			PostMessage(B_QUIT_REQUESTED);
184 			return;
185 		}
186 		BMessage lastPlaylistArchive;
187 		if (_RestoreCurrentPlaylist(&lastPlaylistArchive) == B_OK) {
188 			lastPlaylistArchive.what = M_OPEN_PREVIOUS_PLAYLIST;
189 			window->PostMessage(&lastPlaylistArchive);
190 		} else
191 			window->Show();
192 	}
193 
194 	// setup the settings window now, we need to have it
195 	fSettingsWindow = new SettingsWindow(BRect(150, 150, 450, 520));
196 	fSettingsWindow->Hide();
197 	fSettingsWindow->Show();
198 
199 	_InstallPlaylistMimeType();
200 }
201 
202 
203 void
204 MainApp::RefsReceived(BMessage* message)
205 {
206 	// The user dropped a file (or files) on this app's icon,
207 	// or double clicked a file that's handled by this app.
208 	// Command line arguments are also redirected to here by
209 	// ArgvReceived() but without MIME type check.
210 
211 	// If multiple refs are received in short succession we
212 	// combine them into a single window/playlist. Tracker
213 	// will send multiple messages when opening a multi-
214 	// selection for example and we don't want to spawn large
215 	// numbers of windows when someone just tries to open an
216 	// album. We use half a second time and prolong it for
217 	// each new ref received.
218 	static bigtime_t sLastRefsReceived = 0;
219 	static MainWin* sLastRefsWindow = NULL;
220 
221 	if (system_time() - sLastRefsReceived < 500000) {
222 		// Find the last opened window
223 		for (int32 i = CountWindows() - 1; i >= 0; i--) {
224 			MainWin* playerWindow = dynamic_cast<MainWin*>(WindowAt(i));
225 			if (playerWindow == NULL)
226 				continue;
227 
228 			if (playerWindow != sLastRefsWindow) {
229 				// The window has changed since the last refs
230 				sLastRefsReceived = 0;
231 				sLastRefsWindow = NULL;
232 				break;
233 			}
234 
235 			message->AddBool("append to playlist", true);
236 			playerWindow->PostMessage(message);
237 			sLastRefsReceived = system_time();
238 			return;
239 		}
240 	}
241 
242 	sLastRefsWindow = NewWindow(message);
243 	sLastRefsReceived = system_time();
244 }
245 
246 
247 void
248 MainApp::ArgvReceived(int32 argc, char** argv)
249 {
250 	char cwd[B_PATH_NAME_LENGTH];
251 	getcwd(cwd, sizeof(cwd));
252 
253 	BMessage message(B_REFS_RECEIVED);
254 
255 	for (int i = 1; i < argc; i++) {
256 		BPath path;
257 		if (argv[i][0] != '/')
258 			path.SetTo(cwd, argv[i]);
259 		else
260 			path.SetTo(argv[i]);
261 		BEntry entry(path.Path(), true);
262 		if (!entry.Exists() || !entry.IsFile())
263 			continue;
264 
265 		entry_ref ref;
266 		if (entry.GetRef(&ref) == B_OK)
267 			message.AddRef("refs", &ref);
268 	}
269 
270 	if (message.HasRef("refs"))
271 		RefsReceived(&message);
272 }
273 
274 
275 void
276 MainApp::MessageReceived(BMessage* message)
277 {
278 	switch (message->what) {
279 		case M_NEW_PLAYER:
280 		{
281 			MainWin* window = NewWindow();
282 			if (window != NULL)
283 				window->Show();
284 			break;
285 		}
286 		case M_PLAYER_QUIT:
287 		{
288 			// store the window settings of this instance
289 			MainWin* window = NULL;
290 			bool audioOnly = false;
291 			BRect windowFrame;
292 			bigtime_t creationTime;
293 			if (message->FindPointer("instance", (void**)&window) == B_OK
294 				&& message->FindBool("audio only", &audioOnly) == B_OK
295 				&& message->FindRect("window frame", &windowFrame) == B_OK
296 				&& message->FindInt64("creation time", &creationTime) == B_OK) {
297 				if (audioOnly) {
298 					if (!fAudioWindowFrameSaved
299 						|| creationTime < fLastSavedAudioWindowCreationTime) {
300 						fAudioWindowFrameSaved = true;
301 						fLastSavedAudioWindowCreationTime = creationTime;
302 						mpSettings settings
303 							= Settings::Default()->CurrentSettings();
304 						settings.audioPlayerWindowFrame = windowFrame;
305 						Settings::Default()->SaveSettings(settings);
306 					}
307 				}
308 			}
309 
310 			// Store the playlist if there is one. Since the app is doing
311 			// this, it is "atomic". If the user has multiple instances
312 			// playing audio at the same time, the last instance which is
313 			// quit wins.
314 			BMessage playlistArchive;
315 			if (message->FindMessage("playlist", &playlistArchive) == B_OK)
316 				_StoreCurrentPlaylist(&playlistArchive);
317 
318 			// quit if this was the last player window
319 			fPlayerCount--;
320 			if (fPlayerCount == 0)
321 				PostMessage(B_QUIT_REQUESTED);
322 			break;
323 		}
324 
325 		case B_SOME_APP_LAUNCHED:
326 		case B_SOME_APP_QUIT:
327 		{
328 			const char* mimeSig;
329 			if (message->FindString("be:signature", &mimeSig) < B_OK)
330 				break;
331 
332 			bool isMediaServer = strcmp(mimeSig, kMediaServerSig) == 0;
333 			bool isAddonServer = strcmp(mimeSig, kMediaServerAddOnSig) == 0;
334 			if (!isMediaServer && !isAddonServer)
335 				break;
336 
337 			bool running = (message->what == B_SOME_APP_LAUNCHED);
338 			if (isMediaServer)
339 				fMediaServerRunning = running;
340 			if (isAddonServer)
341 				fMediaAddOnServerRunning = running;
342 
343 			if (!fMediaServerRunning && !fMediaAddOnServerRunning) {
344 				fprintf(stderr, "media server has quit.\n");
345 				// trigger closing of media nodes
346 				BMessage broadcast(M_MEDIA_SERVER_QUIT);
347 				_BroadcastMessage(broadcast);
348 			} else if (fMediaServerRunning && fMediaAddOnServerRunning) {
349 				fprintf(stderr, "media server has launched.\n");
350 				// HACK!
351 				// quit our now invalid instance of the media roster
352 				// so that before new nodes are created,
353 				// we get a new roster (it is a normal looper)
354 				// TODO: This functionality could become part of
355 				// BMediaRoster. It could detect the start/quit of
356 				// the servers like it is done here, and either quit
357 				// itself, or re-establish the connection, and send some
358 				// notification to the app... something along those lines.
359 				BMediaRoster* roster = BMediaRoster::CurrentRoster();
360 				if (roster) {
361 					roster->Lock();
362 					roster->Quit();
363 				}
364 				// give the servers some time to init...
365 				snooze(3000000);
366 				// trigger re-init of media nodes
367 				BMessage broadcast(M_MEDIA_SERVER_STARTED);
368 				_BroadcastMessage(broadcast);
369 			}
370 			break;
371 		}
372 		case M_SETTINGS:
373 			_ShowSettingsWindow();
374 			break;
375 
376 		case M_SHOW_OPEN_PANEL:
377 			_ShowOpenFilePanel(message);
378 			break;
379 		case M_SHOW_SAVE_PANEL:
380 			_ShowSaveFilePanel(message);
381 			break;
382 
383 		case M_OPEN_PANEL_RESULT:
384 			_HandleOpenPanelResult(message);
385 			break;
386 		case M_SAVE_PANEL_RESULT:
387 			_HandleSavePanelResult(message);
388 			break;
389 		case B_CANCEL:
390 		{
391 			// The user canceled a file panel, but store at least the current
392 			// file panel folder.
393 			uint32 oldWhat;
394 			if (message->FindInt32("old_what", (int32*)&oldWhat) != B_OK)
395 				break;
396 			if (oldWhat == M_OPEN_PANEL_RESULT && fOpenFilePanel != NULL)
397 				fOpenFilePanel->GetPanelDirectory(&fLastFilePanelFolder);
398 			else if (oldWhat == M_SAVE_PANEL_RESULT && fSaveFilePanel != NULL)
399 				fSaveFilePanel->GetPanelDirectory(&fLastFilePanelFolder);
400 			break;
401 		}
402 
403 		default:
404 			BApplication::MessageReceived(message);
405 			break;
406 	}
407 }
408 
409 
410 void
411 MainApp::AboutRequested()
412 {
413 	const char* appName = B_TRANSLATE_APP_NAME("MediaPlayer");
414 	BString message = B_TRANSLATE("%app%\n\nWritten by Marcus Overhagen, "
415 		"Stephan Aßmus and Frederik Modéen");
416 	message.ReplaceFirst("%app%", appName);
417 	BAlert* alert = new BAlert(appName, message.String(),
418 		B_TRANSLATE("Thanks"));
419 	alert->SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
420 		// Make sure it is on top of any player windows that may have the
421 		// floating all window feel.
422 	alert->Go(NULL);
423 		// asynchronous mode
424 }
425 
426 
427 // #pragma mark -
428 
429 
430 void
431 MainApp::_BroadcastMessage(const BMessage& _message)
432 {
433 	for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
434 		BMessage message(_message);
435 		window->PostMessage(&message);
436 	}
437 }
438 
439 
440 void
441 MainApp::_ShowSettingsWindow()
442 {
443 	BAutolock lock(fSettingsWindow);
444 	if (!lock.IsLocked())
445 		return;
446 
447 	// If the window is already showing, don't jerk the workspaces around,
448 	// just pull it to the current one.
449 	uint32 workspace = 1UL << (uint32)current_workspace();
450 	uint32 windowWorkspaces = fSettingsWindow->Workspaces();
451 	if ((windowWorkspaces & workspace) == 0) {
452 		// window in a different workspace, reopen in current
453 		fSettingsWindow->SetWorkspaces(workspace);
454 	}
455 
456 	if (fSettingsWindow->IsHidden())
457 		fSettingsWindow->Show();
458 	else
459 		fSettingsWindow->Activate();
460 }
461 
462 
463 // #pragma mark - file panels
464 
465 
466 void
467 MainApp::_ShowOpenFilePanel(const BMessage* message)
468 {
469 	if (fOpenFilePanel == NULL) {
470 		BMessenger target(this);
471 		fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, &target);
472 	}
473 
474 	_ShowFilePanel(fOpenFilePanel, M_OPEN_PANEL_RESULT, message,
475 		B_TRANSLATE("Open"), B_TRANSLATE("Open"));
476 }
477 
478 
479 void
480 MainApp::_ShowSaveFilePanel(const BMessage* message)
481 {
482 	if (fSaveFilePanel == NULL) {
483 		BMessenger target(this);
484 		fSaveFilePanel = new BFilePanel(B_SAVE_PANEL, &target);
485 	}
486 
487 	_ShowFilePanel(fSaveFilePanel, M_SAVE_PANEL_RESULT, message,
488 		B_TRANSLATE("Save"), B_TRANSLATE("Save"));
489 }
490 
491 
492 void
493 MainApp::_ShowFilePanel(BFilePanel* panel, uint32 command,
494 	const BMessage* message, const char* defaultTitle,
495 	const char* defaultLabel)
496 {
497 //	printf("_ShowFilePanel()\n");
498 //	message->PrintToStream();
499 
500 	BMessage panelMessage(command);
501 
502 	if (message != NULL) {
503 		BMessage targetMessage;
504 		if (message->FindMessage("message", &targetMessage) == B_OK)
505 			panelMessage.AddMessage("message", &targetMessage);
506 
507 		BMessenger target;
508 		if (message->FindMessenger("target", &target) == B_OK)
509 			panelMessage.AddMessenger("target", target);
510 
511 		const char* panelTitle;
512 		if (message->FindString("title", &panelTitle) != B_OK)
513 			panelTitle = defaultTitle;
514 		{
515 			BString finalPanelTitle = "MediaPlayer: ";
516 			finalPanelTitle << panelTitle;
517 			BAutolock lock(panel->Window());
518 			panel->Window()->SetTitle(finalPanelTitle.String());
519 		}
520 		const char* buttonLabel;
521 		if (message->FindString("label", &buttonLabel) != B_OK)
522 			buttonLabel = defaultLabel;
523 		panel->SetButtonLabel(B_DEFAULT_BUTTON, buttonLabel);
524 	}
525 
526 //	panelMessage.PrintToStream();
527 	panel->SetMessage(&panelMessage);
528 
529 	if (fLastFilePanelFolder != entry_ref()) {
530 		panel->SetPanelDirectory(&fLastFilePanelFolder);
531 	}
532 
533 	panel->Show();
534 }
535 
536 
537 void
538 MainApp::_HandleOpenPanelResult(const BMessage* message)
539 {
540 	_HandleFilePanelResult(fOpenFilePanel, message);
541 }
542 
543 
544 void
545 MainApp::_HandleSavePanelResult(const BMessage* message)
546 {
547 	_HandleFilePanelResult(fSaveFilePanel, message);
548 }
549 
550 
551 void
552 MainApp::_HandleFilePanelResult(BFilePanel* panel, const BMessage* message)
553 {
554 //	printf("_HandleFilePanelResult()\n");
555 //	message->PrintToStream();
556 
557 	panel->GetPanelDirectory(&fLastFilePanelFolder);
558 
559 	BMessage targetMessage;
560 	if (message->FindMessage("message", &targetMessage) != B_OK)
561 		targetMessage.what = message->what;
562 
563 	BMessenger target;
564 	if (message->FindMessenger("target", &target) != B_OK) {
565 		if (targetMessage.what == M_OPEN_PANEL_RESULT
566 			|| targetMessage.what == M_SAVE_PANEL_RESULT) {
567 			// prevent endless message cycle
568 			return;
569 		}
570 		// send result message to ourselves
571 		target = BMessenger(this);
572 	}
573 
574 	// copy the important contents of the message
575 	// save panel
576 	entry_ref directory;
577 	if (message->FindRef("directory", &directory) == B_OK)
578 		targetMessage.AddRef("directory", &directory);
579 	const char* name;
580 	if (message->FindString("name", &name) == B_OK)
581 		targetMessage.AddString("name", name);
582 	// open panel
583 	entry_ref ref;
584 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++)
585 		targetMessage.AddRef("refs", &ref);
586 
587 	target.SendMessage(&targetMessage);
588 }
589 
590 
591 void
592 MainApp::_StoreCurrentPlaylist(const BMessage* message) const
593 {
594 	BPath path;
595 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
596 		|| path.Append(kCurrentPlaylistFilename) != B_OK) {
597 		return;
598 	}
599 
600 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
601 	if (file.InitCheck() != B_OK)
602 		return;
603 
604 	message->Flatten(&file);
605 }
606 
607 
608 status_t
609 MainApp::_RestoreCurrentPlaylist(BMessage* message) const
610 {
611 	BPath path;
612 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK
613 		|| path.Append(kCurrentPlaylistFilename) != B_OK) {
614 		return B_ERROR;
615 	}
616 
617 	BFile file(path.Path(), B_READ_ONLY);
618 	if (file.InitCheck() != B_OK)
619 		return B_ERROR;
620 
621 	return message->Unflatten(&file);
622 }
623 
624 
625 void
626 MainApp::_InstallPlaylistMimeType()
627 {
628 	// install mime type of documents
629 	BMimeType mime(kBinaryPlaylistMimeString);
630 	status_t ret = mime.InitCheck();
631 	if (ret != B_OK) {
632 		fprintf(stderr, "Could not init native document mime type (%s): %s.\n",
633 			kBinaryPlaylistMimeString, strerror(ret));
634 		return;
635 	}
636 
637 	if (mime.IsInstalled() && !(modifiers() & B_SHIFT_KEY)) {
638 		// mime is already installed, and the user is not
639 		// pressing the shift key to force a re-install
640 		return;
641 	}
642 
643 	ret = mime.Install();
644 	if (ret != B_OK && ret != B_FILE_EXISTS) {
645 		fprintf(stderr, "Could not install native document mime type (%s): %s.\n",
646 			kBinaryPlaylistMimeString, strerror(ret));
647 		return;
648 	}
649 	// set preferred app
650 	ret = mime.SetPreferredApp(kAppSig);
651 	if (ret != B_OK) {
652 		fprintf(stderr, "Could not set native document preferred app: %s\n",
653 			strerror(ret));
654 	}
655 
656 	// set descriptions
657 	ret = mime.SetShortDescription("MediaPlayer playlist");
658 	if (ret != B_OK) {
659 		fprintf(stderr, "Could not set short description of mime type: %s\n",
660 			strerror(ret));
661 	}
662 	ret = mime.SetLongDescription("MediaPlayer binary playlist file");
663 	if (ret != B_OK) {
664 		fprintf(stderr, "Could not set long description of mime type: %s\n",
665 			strerror(ret));
666 	}
667 
668 	// set extensions
669 	BMessage message('extn');
670 	message.AddString("extensions", "playlist");
671 	ret = mime.SetFileExtensions(&message);
672 	if (ret != B_OK) {
673 		fprintf(stderr, "Could not set extensions of mime type: %s\n",
674 			strerror(ret));
675 	}
676 
677 	// set sniffer rule
678 	char snifferRule[32];
679 	uint32 bigEndianMagic = B_HOST_TO_BENDIAN_INT32(kPlaylistMagicBytes);
680 	sprintf(snifferRule, "0.9 ('%4s')", (const char*)&bigEndianMagic);
681 	ret = mime.SetSnifferRule(snifferRule);
682 	if (ret != B_OK) {
683 		BString parseError;
684 		BMimeType::CheckSnifferRule(snifferRule, &parseError);
685 		fprintf(stderr, "Could not set sniffer rule of mime type: %s\n",
686 			parseError.String());
687 	}
688 
689 	// set playlist icon
690 	BResources* resources = AppResources();
691 		// does not need to be freed (belongs to BApplication base)
692 	if (resources != NULL) {
693 		size_t size;
694 		const void* iconData = resources->LoadResource('VICN', "PlaylistIcon",
695 			&size);
696 		if (iconData != NULL && size > 0) {
697 			if (mime.SetIcon(reinterpret_cast<const uint8*>(iconData), size)
698 				!= B_OK) {
699 				fprintf(stderr, "Could not set vector icon of mime type.\n");
700 			}
701 		} else {
702 			fprintf(stderr, "Could not find icon in app resources "
703 				"(data: %p, size: %ld).\n", iconData, size);
704 		}
705 	} else
706 		fprintf(stderr, "Could not find app resources.\n");
707 }
708 
709 
710 // #pragma mark - main
711 
712 
713 int
714 main()
715 {
716 	EventQueue::CreateDefault();
717 
718 	srand(system_time());
719 
720 	gMainApp = new MainApp;
721 	gMainApp->Run();
722 	delete gMainApp;
723 
724 	EventQueue::DeleteDefault();
725 
726 	return 0;
727 }
728