xref: /haiku/src/bin/desklink/MediaReplicant.cpp (revision 70449c90d95aeea01b25f686d51da4217fe41317)
1 /*
2  * Copyright 2003-2010, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jérôme Duval
7  *		François Revol
8  *		Marcus Overhagen
9  *		Jonas Sundström
10  *		Axel Dörfler, axeld@pinc-software.de.
11  *		Stephan Aßmus <superstippi@gmx.de>
12  */
13 
14 
15 //! Volume control, and media shortcuts in Deskbar
16 
17 
18 #include <new>
19 #include <stdio.h>
20 
21 #include <Alert.h>
22 #include <Bitmap.h>
23 #include <Catalog.h>
24 #include <Entry.h>
25 #include <File.h>
26 #include <FindDirectory.h>
27 #include <IconUtils.h>
28 #include <MenuItem.h>
29 #include <Path.h>
30 #include <PopUpMenu.h>
31 #include <Roster.h>
32 #include <String.h>
33 #include <StringView.h>
34 #include <ToolTip.h>
35 #include <ToolTipManager.h>
36 
37 #include "desklink.h"
38 #include "iconfile.h"
39 #include "MixerControl.h"
40 #include "VolumeWindow.h"
41 
42 
43 #undef B_TRANSLATION_CONTEXT
44 #define B_TRANSLATION_CONTEXT "MediaReplicant"
45 
46 
47 static const uint32 kMsgOpenMediaSettings = 'mese';
48 static const uint32 kMsgOpenSoundSettings = 'sose';
49 static const uint32 kMsgOpenMediaPlayer = 'omep';
50 static const uint32 kMsgToggleBeep = 'tdbp';
51 static const uint32 kMsgVolumeWhich = 'svwh';
52 
53 static const char* kReplicantName = "MediaReplicant";
54 	// R5 name needed, Media prefs manel removes by name
55 
56 static const char* kSettingsFile = "x-vnd.Haiku-desklink";
57 
58 
59 class VolumeToolTip : public BToolTip {
60 public:
61 	VolumeToolTip(int32 which = VOLUME_USE_MIXER)
62 		:
63 		fWhich(which)
64 	{
65 		fView = new BStringView("", "");
66 	}
67 
68 	virtual ~VolumeToolTip()
69 	{
70 		delete fView;
71 	}
72 
73 	virtual BView* View() const
74 	{
75 		return fView;
76 	}
77 
78 	virtual void AttachedToWindow()
79 	{
80 		Update();
81 	}
82 
83 	void SetWhich(int32 which)
84 	{
85 		fWhich = which;
86 	}
87 
88 	void Update()
89 	{
90 		if (!Lock())
91 			return;
92 
93 		MixerControl control;
94 		control.Connect(fWhich);
95 
96 		BString text;
97 		text.SetToFormat(B_TRANSLATE("%g dB"), control.Volume());
98 		fView->SetText(text.String());
99 
100 		Unlock();
101 	}
102 
103 private:
104 	BStringView*	fView;
105 	int32			fWhich;
106 };
107 
108 
109 class MediaReplicant : public BView {
110 public:
111 							MediaReplicant(BRect frame, const char* name,
112 								uint32 resizeMask = B_FOLLOW_ALL,
113 								uint32 flags = B_WILL_DRAW | B_NAVIGABLE);
114 							MediaReplicant(BMessage* archive);
115 
116 	virtual					~MediaReplicant();
117 
118 	// archiving overrides
119 	static	MediaReplicant*	Instantiate(BMessage* data);
120 	virtual	status_t		Archive(BMessage* data, bool deep = true) const;
121 
122 	// BView overrides
123 	virtual void			AttachedToWindow();
124 	virtual void			MouseDown(BPoint point);
125 	virtual void			Draw(BRect updateRect);
126 	virtual void			MessageReceived(BMessage* message);
127 
128 private:
129 			status_t		_LaunchByPath(const char* path);
130 			status_t		_LaunchBySignature(const char* signature);
131 			void			_Launch(const char* prettyName,
132 								const char* signature, directory_which base,
133 								const char* fileName);
134 			void			_LoadSettings();
135 			void			_SaveSettings();
136 			void			_Init();
137 
138 			BBitmap*		fIcon;
139 			VolumeWindow*	fVolumeSlider;
140 			bool 			fDontBeep;
141 				// don't beep on volume change
142 			int32 			fVolumeWhich;
143 				// which volume parameter to act on (Mixer/Phys.Output)
144 };
145 
146 
147 MediaReplicant::MediaReplicant(BRect frame, const char* name,
148 		uint32 resizeMask, uint32 flags)
149 	:
150 	BView(frame, name, resizeMask, flags),
151 	fVolumeSlider(NULL)
152 {
153 	_Init();
154 }
155 
156 
157 MediaReplicant::MediaReplicant(BMessage* message)
158 	:
159 	BView(message),
160 	fVolumeSlider(NULL)
161 {
162 	_Init();
163 }
164 
165 
166 MediaReplicant::~MediaReplicant()
167 {
168 	delete fIcon;
169 	_SaveSettings();
170 }
171 
172 
173 MediaReplicant*
174 MediaReplicant::Instantiate(BMessage* data)
175 {
176 	if (!validate_instantiation(data, kReplicantName))
177 		return NULL;
178 
179 	return new(std::nothrow) MediaReplicant(data);
180 }
181 
182 
183 status_t
184 MediaReplicant::Archive(BMessage* data, bool deep) const
185 {
186 	status_t status = BView::Archive(data, deep);
187 	if (status < B_OK)
188 		return status;
189 
190 	return data->AddString("add_on", kAppSignature);
191 }
192 
193 
194 void
195 MediaReplicant::AttachedToWindow()
196 {
197 	BView* parent = Parent();
198 	if (parent)
199 		SetViewColor(parent->ViewColor());
200 
201 	BView::AttachedToWindow();
202 }
203 
204 
205 void
206 MediaReplicant::Draw(BRect rect)
207 {
208 	SetDrawingMode(B_OP_OVER);
209 	DrawBitmap(fIcon);
210 }
211 
212 
213 void
214 MediaReplicant::MouseDown(BPoint point)
215 {
216 	int32 buttons = B_PRIMARY_MOUSE_BUTTON;
217 	if (Looper() != NULL && Looper()->CurrentMessage() != NULL)
218 		Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
219 
220 	BPoint where = ConvertToScreen(point);
221 
222 	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
223 		BPopUpMenu* menu = new BPopUpMenu("", false, false);
224 		menu->SetFont(be_plain_font);
225 
226 		menu->AddItem(new BMenuItem(
227 			B_TRANSLATE("Media preferences" B_UTF8_ELLIPSIS),
228 			new BMessage(kMsgOpenMediaSettings)));
229 		menu->AddItem(new BMenuItem(
230 			B_TRANSLATE("Sound preferences" B_UTF8_ELLIPSIS),
231 			new BMessage(kMsgOpenSoundSettings)));
232 
233 		menu->AddSeparatorItem();
234 
235 		menu->AddItem(new BMenuItem(B_TRANSLATE("Open MediaPlayer"),
236 			new BMessage(kMsgOpenMediaPlayer)));
237 
238 		menu->AddSeparatorItem();
239 
240 		BMenu* subMenu = new BMenu(B_TRANSLATE("Options"));
241 		menu->AddItem(subMenu);
242 
243 		BMenuItem* item = new BMenuItem(B_TRANSLATE("Control physical output"),
244 			new BMessage(kMsgVolumeWhich));
245 		item->SetMarked(fVolumeWhich == VOLUME_USE_PHYS_OUTPUT);
246 		subMenu->AddItem(item);
247 
248 		item = new BMenuItem(B_TRANSLATE("Beep"),
249 			new BMessage(kMsgToggleBeep));
250 		item->SetMarked(!fDontBeep);
251 		subMenu->AddItem(item);
252 
253 		menu->SetTargetForItems(this);
254 		subMenu->SetTargetForItems(this);
255 
256 		menu->Go(where, true, true, BRect(where - BPoint(4, 4),
257 			where + BPoint(4, 4)));
258 	} else {
259 		// Show VolumeWindow
260 		fVolumeSlider = new VolumeWindow(BRect(where.x, where.y,
261 			where.x + 207, where.y + 19), fDontBeep, fVolumeWhich);
262 		fVolumeSlider->Show();
263 	}
264 }
265 
266 
267 void
268 MediaReplicant::MessageReceived(BMessage* message)
269 {
270 	switch (message->what) {
271 		case kMsgOpenMediaPlayer:
272 			_Launch("MediaPlayer", "application/x-vnd.Haiku-MediaPlayer",
273 				B_SYSTEM_APPS_DIRECTORY, "MediaPlayer");
274 			break;
275 
276 		case kMsgOpenMediaSettings:
277 			_Launch("Media Preferences", "application/x-vnd.Haiku-Media",
278 				B_SYSTEM_PREFERENCES_DIRECTORY, "Media");
279 			break;
280 
281 		case kMsgOpenSoundSettings:
282 			_Launch("Sounds Preferences", "application/x-vnd.Haiku-Sounds",
283 				B_SYSTEM_PREFERENCES_DIRECTORY, "Sounds");
284 			break;
285 
286 		case kMsgToggleBeep:
287 		{
288 			BMenuItem* item;
289 			if (message->FindPointer("source", (void**)&item) != B_OK)
290 				return;
291 
292 			item->SetMarked(!item->IsMarked());
293 			fDontBeep = !item->IsMarked();
294 			break;
295 		}
296 
297 		case kMsgVolumeWhich:
298 		{
299 			BMenuItem* item;
300 			if (message->FindPointer("source", (void**)&item) != B_OK)
301 				return;
302 
303 			item->SetMarked(!item->IsMarked());
304 			fVolumeWhich = item->IsMarked()
305 				? VOLUME_USE_PHYS_OUTPUT : VOLUME_USE_MIXER;
306 
307 			if (VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip()))
308 				tip->SetWhich(fVolumeWhich);
309 			break;
310 		}
311 
312 		case B_MOUSE_WHEEL_CHANGED:
313 		{
314 			float deltaY;
315 			if (message->FindFloat("be:wheel_delta_y", &deltaY) == B_OK
316 				&& deltaY != 0.0) {
317 				MixerControl mixerControl;
318 				mixerControl.Connect(fVolumeWhich);
319 				mixerControl.ChangeVolumeBy(deltaY < 0 ? 6 : -6);
320 
321 				VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip());
322 				if (tip != NULL) {
323 					tip->Update();
324 					ShowToolTip(tip);
325 				}
326 			}
327 			break;
328 		}
329 
330 		default:
331 			BView::MessageReceived(message);
332 			break;
333 	}
334 }
335 
336 
337 status_t
338 MediaReplicant::_LaunchByPath(const char* path)
339 {
340 	entry_ref ref;
341 	status_t status = get_ref_for_path(path, &ref);
342 	if (status != B_OK)
343 		return status;
344 
345 	status = be_roster->Launch(&ref);
346 	if (status != B_ALREADY_RUNNING)
347 		return status;
348 
349 	// The application runs already, bring it to front
350 
351 	app_info appInfo;
352 	status = be_roster->GetAppInfo(&ref, &appInfo);
353 	if (status != B_OK)
354 		return status;
355 
356 	return be_roster->ActivateApp(appInfo.team);
357 }
358 
359 
360 status_t
361 MediaReplicant::_LaunchBySignature(const char* signature)
362 {
363 	status_t status = be_roster->Launch(signature);
364 	if (status != B_ALREADY_RUNNING)
365 		return status;
366 
367 	// The application runs already, bring it to front
368 
369 	app_info appInfo;
370 	status = be_roster->GetAppInfo(signature, &appInfo);
371 	if (status != B_OK)
372 		return status;
373 
374 	return be_roster->ActivateApp(appInfo.team);
375 }
376 
377 
378 void
379 MediaReplicant::_Launch(const char* prettyName, const char* signature,
380 	directory_which base, const char* fileName)
381 {
382 	BPath path;
383 	status_t status = find_directory(base, &path);
384 	if (status == B_OK)
385 		path.Append(fileName);
386 
387 	// launch the application
388 	if (_LaunchBySignature(signature) != B_OK
389 		&& _LaunchByPath(path.Path()) != B_OK) {
390 		BString message = B_TRANSLATE("Couldn't launch ");
391 		message << prettyName;
392 
393 		BAlert* alert = new BAlert(B_TRANSLATE("desklink"), message.String(),
394 			B_TRANSLATE("OK"));
395 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
396 		alert->Go();
397 	}
398 }
399 
400 
401 void
402 MediaReplicant::_LoadSettings()
403 {
404 	fDontBeep = false;
405 	fVolumeWhich = VOLUME_USE_MIXER;
406 
407 	BPath path;
408 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK)
409 		return;
410 
411 	path.Append(kSettingsFile);
412 
413 	BFile settings(path.Path(), B_READ_ONLY);
414 	if (settings.InitCheck() < B_OK)
415 		return;
416 
417 	BMessage msg;
418 	if (msg.Unflatten(&settings) < B_OK)
419 		return;
420 
421 	msg.FindInt32("volwhich", &fVolumeWhich);
422 	msg.FindBool("dontbeep", &fDontBeep);
423 }
424 
425 
426 void
427 MediaReplicant::_SaveSettings()
428 {
429 	BPath path;
430 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK)
431 		return;
432 
433 	path.Append(kSettingsFile);
434 
435 	BFile settings(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
436 	if (settings.InitCheck() < B_OK)
437 		return;
438 
439 	BMessage msg('CNFG');
440 	msg.AddInt32("volwhich", fVolumeWhich);
441 	msg.AddBool("dontbeep", fDontBeep);
442 
443 	ssize_t size = 0;
444 	msg.Flatten(&settings, &size);
445 }
446 
447 
448 void
449 MediaReplicant::_Init()
450 {
451 	fIcon = new BBitmap(BRect(0, 0, kSpeakerWidth - 1, kSpeakerHeight - 1),
452 		B_RGBA32);
453 	BIconUtils::GetVectorIcon(kSpeakerIcon, sizeof(kSpeakerIcon), fIcon);
454 
455 	_LoadSettings();
456 
457 	SetToolTip(new VolumeToolTip(fVolumeWhich));
458 }
459 
460 
461 //	#pragma mark -
462 
463 
464 extern "C" BView*
465 instantiate_deskbar_item(void)
466 {
467 	return new MediaReplicant(BRect(0, 0, 16, 16), kReplicantName);
468 }
469 
470