xref: /haiku/src/apps/mediaplayer/MainApp.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
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 #include "MainApp.h"
22 
23 #include <Alert.h>
24 #include <Autolock.h>
25 #include <Entry.h>
26 #include <FilePanel.h>
27 #include <MediaRoster.h>
28 #include <Path.h>
29 #include <Roster.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 
35 #include "EventQueue.h"
36 #include "Settings.h"
37 #include "SettingsWindow.h"
38 
39 
40 MainApp* gMainApp;
41 const char* kAppSig = "application/x-vnd.Haiku-MediaPlayer";
42 
43 static const char* kMediaServerSig = "application/x-vnd.Be.media-server";
44 static const char* kMediaServerAddOnSig = "application/x-vnd.Be.addon-host";
45 
46 
47 MainApp::MainApp()
48 	: BApplication(kAppSig),
49 	  fPlayerCount(0),
50 	  fFirstWindow(NULL),
51 	  fSettingsWindow(NULL),
52 
53 	  fOpenFilePanel(NULL),
54 	  fSaveFilePanel(NULL),
55 	  fLastFilePanelFolder(),
56 
57 	  fMediaServerRunning(false),
58 	  fMediaAddOnServerRunning(false)
59 {
60 	mpSettings settings = Settings::CurrentSettings();
61 	fLastFilePanelFolder = settings.filePanelFolder;
62 }
63 
64 
65 MainApp::~MainApp()
66 {
67 	delete fOpenFilePanel;
68 	delete fSaveFilePanel;
69 }
70 
71 
72 bool
73 MainApp::QuitRequested()
74 {
75 	// Note: This needs to be done here, SettingsWindow::QuitRequested()
76 	// returns "false" always. (Standard BApplication quit procedure will
77 	// hang otherwise.)
78 	if (fSettingsWindow && fSettingsWindow->Lock())
79 		fSettingsWindow->Quit();
80 	fSettingsWindow = NULL;
81 
82 	// store the current file panel ref in the global settings
83 	mpSettings settings = Settings::CurrentSettings();
84 	settings.filePanelFolder = fLastFilePanelFolder;
85 	Settings::Default()->SaveSettings(settings);
86 
87 	return BApplication::QuitRequested();
88 }
89 
90 
91 BWindow*
92 MainApp::FirstWindow()
93 {
94 	BAutolock _(this);
95 	if (fFirstWindow != NULL)
96 		return fFirstWindow;
97 	return NewWindow();
98 }
99 
100 
101 BWindow*
102 MainApp::NewWindow()
103 {
104 	BAutolock _(this);
105 	fPlayerCount++;
106 	BWindow* window = new MainWin();
107 	if (fFirstWindow == NULL)
108 		fFirstWindow = window;
109 	return window;
110 }
111 
112 
113 int32
114 MainApp::PlayerCount() const
115 {
116 	BAutolock _(const_cast<MainApp*>(this));
117 	return fPlayerCount;
118 }
119 
120 
121 // #pragma mark -
122 
123 
124 void
125 MainApp::ReadyToRun()
126 {
127 	// Now tell the application roster, that we're interested
128 	// in getting notifications of apps being launched or quit.
129 	// In this way we are going to detect a media_server restart.
130 	be_roster->StartWatching(BMessenger(this, this),
131 		B_REQUEST_LAUNCHED | B_REQUEST_QUIT);
132 	// we will keep track of the status of media_server
133 	// and media_addon_server
134 	fMediaServerRunning = be_roster->IsRunning(kMediaServerSig);
135 	fMediaAddOnServerRunning = be_roster->IsRunning(kMediaServerAddOnSig);
136 
137 	if (!fMediaServerRunning || !fMediaAddOnServerRunning) {
138 		BAlert* alert = new BAlert("start_media_server",
139 			"It appears the Media Server is not running.\n"
140 			"Would you like to start it ?", "Quit", "Start Media Server", NULL,
141 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
142 		if (alert->Go() == 0) {
143 			PostMessage(B_QUIT_REQUESTED);
144 			return;
145 		}
146 
147 		launch_media_server();
148 
149 		fMediaServerRunning = be_roster->IsRunning(kMediaServerSig);
150 		fMediaAddOnServerRunning = be_roster->IsRunning(kMediaServerAddOnSig);
151 	}
152 
153 	// make sure we have at least one window open
154 	FirstWindow();
155 
156 	// setup the settings window now, we need to have it
157 	fSettingsWindow = new SettingsWindow(BRect(150, 150, 450, 520));
158 	fSettingsWindow->Hide();
159 	fSettingsWindow->Show();
160 }
161 
162 
163 void
164 MainApp::RefsReceived(BMessage* message)
165 {
166 	// The user dropped a file (or files) on this app's icon,
167 	// or double clicked a file that's handled by this app.
168 	// Command line arguments are also redirected to here by
169 	// ArgvReceived() but without MIME type check.
170 	// For each file we create a new window and send it a
171 	// B_REFS_RECEIVED message with a single file.
172 	// If IsLaunching() is true, we use fFirstWindow as first
173 	// window.
174 	printf("MainApp::RefsReceived\n");
175 
176 	BWindow* window = NewWindow();
177 	if (window)
178 		window->PostMessage(message);
179 }
180 
181 
182 void
183 MainApp::ArgvReceived(int32 argc, char **argv)
184 {
185 	char cwd[B_PATH_NAME_LENGTH];
186 	getcwd(cwd, sizeof(cwd));
187 
188 	BMessage m(B_REFS_RECEIVED);
189 
190 	for (int i = 1; i < argc; i++) {
191 		printf("MainApp::ArgvReceived %s\n", argv[i]);
192 		BPath path;
193 		if (argv[i][0] != '/')
194 			path.SetTo(cwd, argv[i]);
195 		else
196 			path.SetTo(argv[i]);
197 		BEntry entry(path.Path(), true);
198 		if (!entry.Exists() || !entry.IsFile())
199 			continue;
200 
201 		entry_ref ref;
202 		if (B_OK == entry.GetRef(&ref))
203 			m.AddRef("refs", &ref);
204 	}
205 
206 	if (m.HasRef("refs")) {
207 		printf("MainApp::ArgvReceived calling RefsReceived\n");
208 		RefsReceived(&m);
209 	}
210 }
211 
212 
213 void
214 MainApp::MessageReceived(BMessage* message)
215 {
216 	switch (message->what) {
217 		case M_PLAYER_QUIT:
218 			fPlayerCount--;
219 			if (fPlayerCount == 0)
220 				PostMessage(B_QUIT_REQUESTED);
221 			break;
222 
223 		case B_SOME_APP_LAUNCHED:
224 		case B_SOME_APP_QUIT:
225 		{
226 			const char* mimeSig;
227 			if (message->FindString("be:signature", &mimeSig) < B_OK)
228 				break;
229 
230 			bool isMediaServer = strcmp(mimeSig, kMediaServerSig) == 0;
231 			bool isAddonServer = strcmp(mimeSig, kMediaServerAddOnSig) == 0;
232 			if (!isMediaServer && !isAddonServer)
233 				break;
234 
235 			bool running = (message->what == B_SOME_APP_LAUNCHED);
236 			if (isMediaServer)
237 				fMediaServerRunning = running;
238 			if (isAddonServer)
239 				fMediaAddOnServerRunning = running;
240 
241 			if (!fMediaServerRunning && !fMediaAddOnServerRunning) {
242 				fprintf(stderr, "media server has quit.\n");
243 				// trigger closing of media nodes
244 				BMessage broadcast(M_MEDIA_SERVER_QUIT);
245 				_BroadcastMessage(broadcast);
246 			} else if (fMediaServerRunning && fMediaAddOnServerRunning) {
247 				fprintf(stderr, "media server has launched.\n");
248 				// HACK!
249 				// quit our now invalid instance of the media roster
250 				// so that before new nodes are created,
251 				// we get a new roster (it is a normal looper)
252 				// TODO: This functionality could become part of
253 				// BMediaRoster. It could detect the start/quit of
254 				// the servers like it is done here, and either quit
255 				// itself, or re-establish the connection, and send some
256 				// notification to the app... something along those lines.
257 				BMediaRoster* roster = BMediaRoster::CurrentRoster();
258 				if (roster) {
259 					roster->Lock();
260 					roster->Quit();
261 				}
262 				// give the servers some time to init...
263 				snooze(3000000);
264 				// trigger re-init of media nodes
265 				BMessage broadcast(M_MEDIA_SERVER_STARTED);
266 				_BroadcastMessage(broadcast);
267 			}
268 			break;
269 		}
270 		case M_SETTINGS:
271 			_ShowSettingsWindow();
272 			break;
273 
274 		case M_SHOW_OPEN_PANEL:
275 			_ShowOpenFilePanel(message);
276 			break;
277 		case M_SHOW_SAVE_PANEL:
278 			_ShowSaveFilePanel(message);
279 			break;
280 
281 		case M_OPEN_PANEL_RESULT:
282 			_HandleOpenPanelResult(message);
283 			break;
284 		case M_SAVE_PANEL_RESULT:
285 			_HandleSavePanelResult(message);
286 			break;
287 		case B_CANCEL: {
288 			// The user canceled a file panel, but store at least the current
289 			// file panel folder.
290 			uint32 oldWhat;
291 			if (message->FindInt32("old_what", (int32*)&oldWhat) != B_OK)
292 				break;
293 			if (oldWhat == M_OPEN_PANEL_RESULT && fOpenFilePanel != NULL)
294 				fOpenFilePanel->GetPanelDirectory(&fLastFilePanelFolder);
295 			else if (oldWhat == M_SAVE_PANEL_RESULT && fSaveFilePanel != NULL)
296 				fSaveFilePanel->GetPanelDirectory(&fLastFilePanelFolder);
297 			break;
298 		}
299 
300 		default:
301 			BApplication::MessageReceived(message);
302 			break;
303 	}
304 }
305 
306 
307 void
308 MainApp::AboutRequested()
309 {
310 	FirstWindow()->PostMessage(B_ABOUT_REQUESTED);
311 }
312 
313 
314 // #pragma mark -
315 
316 
317 void
318 MainApp::_BroadcastMessage(const BMessage& _message)
319 {
320 	for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
321 		BMessage message(_message);
322 		window->PostMessage(&message);
323 	}
324 }
325 
326 
327 void
328 MainApp::_ShowSettingsWindow()
329 {
330 	BAutolock lock(fSettingsWindow);
331 	if (!lock.IsLocked())
332 		return;
333 
334 	// If the window is already showing, don't jerk the workspaces around,
335 	// just pull it to the current one.
336 	uint32 workspace = 1UL << (uint32)current_workspace();
337 	uint32 windowWorkspaces = fSettingsWindow->Workspaces();
338 	if ((windowWorkspaces & workspace) == 0) {
339 		// window in a different workspace, reopen in current
340 		fSettingsWindow->SetWorkspaces(workspace);
341 	}
342 
343 	if (fSettingsWindow->IsHidden())
344 		fSettingsWindow->Show();
345 	else
346 		fSettingsWindow->Activate();
347 }
348 
349 
350 // #pragma mark - file panels
351 
352 
353 void
354 MainApp::_ShowOpenFilePanel(const BMessage* message)
355 {
356 	if (fOpenFilePanel == NULL) {
357 		BMessenger target(this);
358 		fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, &target);
359 	}
360 
361 	_ShowFilePanel(fOpenFilePanel, M_OPEN_PANEL_RESULT, message,
362 		"Open", "Open");
363 }
364 
365 
366 void
367 MainApp::_ShowSaveFilePanel(const BMessage* message)
368 {
369 	if (fSaveFilePanel == NULL) {
370 		BMessenger target(this);
371 		fSaveFilePanel = new BFilePanel(B_SAVE_PANEL, &target);
372 	}
373 
374 	_ShowFilePanel(fSaveFilePanel, M_SAVE_PANEL_RESULT, message,
375 		"Save", "Save");
376 }
377 
378 
379 void
380 MainApp::_ShowFilePanel(BFilePanel* panel, uint32 command,
381 	const BMessage* message, const char* defaultTitle,
382 	const char* defaultLabel)
383 {
384 //	printf("_ShowFilePanel()\n");
385 //	message->PrintToStream();
386 
387 	BMessage panelMessage(command);
388 
389 	if (message != NULL) {
390 		BMessage targetMessage;
391 		if (message->FindMessage("message", &targetMessage) == B_OK)
392 			panelMessage.AddMessage("message", &targetMessage);
393 
394 		BMessenger target;
395 		if (message->FindMessenger("target", &target) == B_OK)
396 			panelMessage.AddMessenger("target", target);
397 
398 		const char* panelTitle;
399 		if (message->FindString("title", &panelTitle) != B_OK)
400 			panelTitle = defaultTitle;
401 		{
402 			BString finalPanelTitle = "MediaPlayer: ";
403 			finalPanelTitle << panelTitle;
404 			BAutolock lock(panel->Window());
405 			panel->Window()->SetTitle(finalPanelTitle.String());
406 		}
407 		const char* buttonLabel;
408 		if (message->FindString("label", &buttonLabel) != B_OK)
409 			buttonLabel = defaultLabel;
410 		panel->SetButtonLabel(B_DEFAULT_BUTTON, buttonLabel);
411 	}
412 
413 //	panelMessage.PrintToStream();
414 	panel->SetMessage(&panelMessage);
415 
416 	if (fLastFilePanelFolder != entry_ref()) {
417 		panel->SetPanelDirectory(&fLastFilePanelFolder);
418 	}
419 
420 	panel->Show();
421 }
422 
423 
424 void
425 MainApp::_HandleOpenPanelResult(const BMessage* message)
426 {
427 	_HandleFilePanelResult(fOpenFilePanel, message);
428 }
429 
430 
431 void
432 MainApp::_HandleSavePanelResult(const BMessage* message)
433 {
434 	_HandleFilePanelResult(fSaveFilePanel, message);
435 }
436 
437 
438 void
439 MainApp::_HandleFilePanelResult(BFilePanel* panel, const BMessage* message)
440 {
441 //	printf("_HandleFilePanelResult()\n");
442 //	message->PrintToStream();
443 
444 	panel->GetPanelDirectory(&fLastFilePanelFolder);
445 
446 	BMessage targetMessage;
447 	if (message->FindMessage("message", &targetMessage) != B_OK)
448 		targetMessage.what = message->what;
449 
450 	BMessenger target;
451 	if (message->FindMessenger("target", &target) != B_OK) {
452 		if (targetMessage.what == M_OPEN_PANEL_RESULT
453 			|| targetMessage.what == M_SAVE_PANEL_RESULT) {
454 			// prevent endless message cycle
455 			return;
456 		}
457 		// send result message to ourselves
458 		target = BMessenger(this);
459 	}
460 
461 	// copy the important contents of the message
462 	// save panel
463 	entry_ref directory;
464 	if (message->FindRef("directory", &directory) == B_OK)
465 		targetMessage.AddRef("directory", &directory);
466 	const char* name;
467 	if (message->FindString("name", &name) == B_OK)
468 		targetMessage.AddString("name", name);
469 	// open panel
470 	entry_ref ref;
471 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++)
472 		targetMessage.AddRef("refs", &ref);
473 
474 	target.SendMessage(&targetMessage);
475 }
476 
477 
478 // #pragma mark - main
479 
480 
481 int
482 main()
483 {
484 	EventQueue::CreateDefault();
485 
486 	srand(system_time());
487 
488 	gMainApp = new MainApp;
489 	gMainApp->Run();
490 	delete gMainApp;
491 
492 	EventQueue::DeleteDefault();
493 
494 	return 0;
495 }
496