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
47static const uint32 kMsgOpenMediaSettings = 'mese';
48static const uint32 kMsgOpenSoundSettings = 'sose';
49static const uint32 kMsgOpenMediaPlayer = 'omep';
50static const uint32 kMsgToggleBeep = 'tdbp';
51static const uint32 kMsgVolumeWhich = 'svwh';
52
53static const char* kReplicantName = "MediaReplicant";
54	// R5 name needed, Media prefs manel removes by name
55
56static const char* kSettingsFile = "x-vnd.Haiku-desklink";
57
58
59class VolumeToolTip : public BToolTip {
60public:
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
103private:
104	BStringView*	fView;
105	int32			fWhich;
106};
107
108
109class MediaReplicant : public BView {
110public:
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
128private:
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
147MediaReplicant::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
157MediaReplicant::MediaReplicant(BMessage* message)
158	:
159	BView(message),
160	fVolumeSlider(NULL)
161{
162	_Init();
163}
164
165
166MediaReplicant::~MediaReplicant()
167{
168	delete fIcon;
169	_SaveSettings();
170}
171
172
173MediaReplicant*
174MediaReplicant::Instantiate(BMessage* data)
175{
176	if (!validate_instantiation(data, kReplicantName))
177		return NULL;
178
179	return new(std::nothrow) MediaReplicant(data);
180}
181
182
183status_t
184MediaReplicant::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
194void
195MediaReplicant::AttachedToWindow()
196{
197	BView* parent = Parent();
198	if (parent)
199		SetViewColor(parent->ViewColor());
200
201	BView::AttachedToWindow();
202}
203
204
205void
206MediaReplicant::Draw(BRect rect)
207{
208	SetDrawingMode(B_OP_OVER);
209	DrawBitmap(fIcon);
210}
211
212
213void
214MediaReplicant::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
267void
268MediaReplicant::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
337status_t
338MediaReplicant::_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
360status_t
361MediaReplicant::_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
378void
379MediaReplicant::_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
401void
402MediaReplicant::_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
426void
427MediaReplicant::_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
448void
449MediaReplicant::_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
464extern "C" BView*
465instantiate_deskbar_item(void)
466{
467	return new MediaReplicant(BRect(0, 0, 16, 16), kReplicantName);
468}
469
470