1/*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions, and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions, and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
32// ParameterWindow.cpp
33
34#include "ParameterWindow.h"
35// ParameterWindow
36#include "ParameterContainerView.h"
37
38// Application Kit
39#include <Message.h>
40#include <Messenger.h>
41// Interface Kit
42#include <Alert.h>
43#include <Menu.h>
44#include <MenuBar.h>
45#include <MenuItem.h>
46#include <Screen.h>
47#include <ScrollBar.h>
48// Media Kit
49#include <MediaRoster.h>
50#include <MediaTheme.h>
51#include <ParameterWeb.h>
52// Storage Kit
53#include <FilePanel.h>
54#include <Path.h>
55// Support Kit
56#include <String.h>
57// Locale Kit
58#undef B_CATALOG
59#define B_CATALOG (&sCatalog)
60#include <Catalog.h>
61
62#undef B_TRANSLATION_CONTEXT
63#define B_TRANSLATION_CONTEXT "ParameterWindow"
64
65__USE_CORTEX_NAMESPACE
66
67#include <Debug.h>
68#define D_ALLOC(x) //PRINT (x)
69#define D_HOOK(x) //PRINT (x)
70#define D_INTERNAL(x) //PRINT (x)
71#define D_MESSAGE(x) //PRINT (x)
72
73static BCatalog sCatalog("x-vnd.Cortex.ParameterView");
74
75// -------------------------------------------------------- //
76// ctor/dtor
77// -------------------------------------------------------- //
78
79ParameterWindow::ParameterWindow(
80	BPoint position,
81	live_node_info &nodeInfo,
82	BMessenger *notifyTarget)
83	: BWindow(BRect(position, position + BPoint(50.0, 50.0)),
84			  "parameters", B_DOCUMENT_WINDOW,
85			  B_WILL_ACCEPT_FIRST_CLICK | B_ASYNCHRONOUS_CONTROLS),
86	  m_node(nodeInfo.node),
87	  m_parameters(0),
88	  m_notifyTarget(0),
89	  m_zoomed(false),
90	  m_zooming(false) {
91	D_ALLOC(("ParameterWindow::ParameterWindow()\n"));
92
93	// add the nodes name to the title
94	{
95		BString title = B_TRANSLATE("%nodeinfo% parameters");
96		title.ReplaceFirst("%nodeinfo%", nodeInfo.name);
97		SetTitle(title);
98	}
99	// add the menu bar
100	BMenuBar *menuBar = new BMenuBar(Bounds(), "ParameterWindow MenuBar");
101
102	BMenu *menu = new BMenu(B_TRANSLATE("Window"));
103	menu->AddItem(new BMenuItem(B_TRANSLATE("Start control panel"),
104								new BMessage(M_START_CONTROL_PANEL),
105								'P', B_COMMAND_KEY | B_SHIFT_KEY));
106	menu->AddSeparatorItem();
107	menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
108								new BMessage(B_QUIT_REQUESTED),
109								'W', B_COMMAND_KEY));
110	menuBar->AddItem(menu);
111
112	// future Media Theme selection capabilities go here
113	menu = new BMenu(B_TRANSLATE("Themes"));
114	BMessage *message = new BMessage(M_THEME_SELECTED);
115	BMediaTheme *theme = BMediaTheme::PreferredTheme();
116	message->AddInt32("themeID", theme->ID());
117	BMenuItem *item = new BMenuItem(theme->Name(), message);
118	item->SetMarked(true);
119	menu->AddItem(item);
120	menuBar->AddItem(menu);
121	AddChild(menuBar);
122
123	_updateParameterView();
124	_init();
125
126	// start watching for parameter web changes
127	BMediaRoster *roster = BMediaRoster::CurrentRoster();
128	roster->StartWatching(this, nodeInfo.node, B_MEDIA_WILDCARD);
129
130	if (notifyTarget) {
131		m_notifyTarget = new BMessenger(*notifyTarget);
132	}
133}
134
135ParameterWindow::~ParameterWindow() {
136	D_ALLOC(("ParameterWindow::~ParameterWindow()\n"));
137
138	if (m_notifyTarget) {
139		delete m_notifyTarget;
140	}
141}
142
143// -------------------------------------------------------- //
144// BWindow impl
145// -------------------------------------------------------- //
146
147void ParameterWindow::FrameResized(
148	float width,
149	float height) {
150	D_HOOK(("ParameterWindow::FrameResized()\n"));
151
152	if (!m_zooming) {
153		m_zoomed = false;
154	}
155	else {
156		m_zooming = false;
157	}
158}
159
160void ParameterWindow::MessageReceived(
161	BMessage *message) {
162	D_MESSAGE(("ParameterWindow::MessageReceived()\n"));
163
164	switch (message->what) {
165		case M_START_CONTROL_PANEL: {
166			D_MESSAGE((" -> M_START_CONTROL_PANEL\n"));
167			status_t error = _startControlPanel();
168			if (error) {
169				BString s = B_TRANSLATE(
170					"Could not start control panel (%error%)");
171				s.ReplaceFirst("%error%", strerror(error));
172				BAlert *alert = new BAlert("", s.String(), B_TRANSLATE("OK"),
173					0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
174				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
175				alert->Go(0);
176			}
177			bool replace = false;
178			if ((message->FindBool("replace", &replace) == B_OK)
179			 && (replace == true)) {
180				PostMessage(B_QUIT_REQUESTED);
181			}
182			break;
183		}
184		case M_THEME_SELECTED: {
185			D_MESSAGE((" -> M_THEME_SELECTED\n"));
186			int32 themeID;
187			if (message->FindInt32("themeID", &themeID) != B_OK) {
188				return;
189			}
190			// not yet implemented
191			break;
192		}
193		case B_MEDIA_WEB_CHANGED: {
194			D_MESSAGE((" -> B_MEDIA_WEB_CHANGED\n"));
195			_updateParameterView();
196			_constrainToScreen();
197			break;
198		}
199		default: {
200			BWindow::MessageReceived(message);
201		}
202	}
203}
204
205bool ParameterWindow::QuitRequested() {
206	D_HOOK(("ParameterWindow::QuitRequested()\n"));
207
208	// stop watching the MediaRoster
209	BMediaRoster *roster = BMediaRoster::CurrentRoster();
210	if (roster)	{
211		roster->StopWatching(this, m_node, B_MEDIA_WILDCARD);
212	}
213
214	// tell the notification target to forget about us
215	if (m_notifyTarget && m_notifyTarget->IsValid()) {
216		BMessage message(M_CLOSED);
217		message.AddInt32("nodeID", m_node.node);
218		status_t error = m_notifyTarget->SendMessage(&message);
219		if (error) {
220			D_HOOK((" -> error sending message (%s) !\n", strerror(error)));
221		}
222	}
223
224	return true;
225}
226
227void ParameterWindow::Zoom(
228	BPoint origin,
229	float width,
230	float height) {
231	D_HOOK(("ParameterWindow::Zoom()\n"));
232
233	m_zooming = true;
234
235	BScreen screen(this);
236	if (!screen.Frame().Contains(Frame())) {
237		m_zoomed = false;
238	}
239
240	if (!m_zoomed) {
241		// resize to the ideal size
242		m_manualSize = Bounds();
243		ResizeTo(m_idealSize.Width(), m_idealSize.Height());
244		_constrainToScreen();
245		m_zoomed = true;
246	}
247	else {
248		// resize to the most recent manual size
249		ResizeTo(m_manualSize.Width(), m_manualSize.Height());
250		m_zoomed = false;
251	}
252}
253
254// -------------------------------------------------------- //
255// internal operations
256// -------------------------------------------------------- //
257
258void ParameterWindow::_init() {
259	D_INTERNAL(("ParameterWindow::_init()\n"));
260
261	// offset to a new position
262	_constrainToScreen();
263	m_manualSize = Bounds().OffsetToCopy(0.0, 0.0);
264
265	// add the hidden option to close this window when the
266	// control panel has been started successfully
267	BMessage *message = new BMessage(M_START_CONTROL_PANEL);
268	message->AddBool("replace", true);
269	AddShortcut('P', B_COMMAND_KEY | B_SHIFT_KEY | B_OPTION_KEY,
270				message);
271}
272
273void ParameterWindow::_updateParameterView(
274	BMediaTheme *theme) {
275	D_INTERNAL(("ParameterWindow::_updateParameterView()\n"));
276
277	// clear the old version
278	if (m_parameters) {
279		ParameterContainerView *view = dynamic_cast<ParameterContainerView *>(FindView("ParameterContainerView"));
280		RemoveChild(view);
281		delete m_parameters;
282		m_parameters = 0;
283		delete view;
284	}
285
286	// fetch ParameterWeb from the MediaRoster
287	BMediaRoster *roster = BMediaRoster::CurrentRoster();
288	if (roster) {
289		BParameterWeb *web;
290		status_t error = roster->GetParameterWebFor(m_node, &web);
291		if (!error && (web->CountParameters() || web->CountGroups())) {
292			// if no theme was specified, use the preferred theme
293			if (!theme) {
294				theme = BMediaTheme::PreferredTheme();
295			}
296			// acquire the view
297			m_parameters = BMediaTheme::ViewFor(web, 0, theme);
298			if (m_parameters) {
299				BMenuBar *menuBar = KeyMenuBar();
300				m_idealSize = m_parameters->Bounds();
301				m_idealSize.right += B_V_SCROLL_BAR_WIDTH;
302				m_idealSize.bottom += B_H_SCROLL_BAR_HEIGHT;
303				if (menuBar) {
304					m_parameters->MoveTo(0.0, menuBar->Bounds().bottom + 1.0);
305					m_idealSize.bottom += menuBar->Bounds().bottom + 1.0;
306				}
307			}
308		}
309	}
310
311	// limit min size to avoid funny-looking scrollbars
312	float hMin = B_V_SCROLL_BAR_WIDTH*6, vMin = B_H_SCROLL_BAR_HEIGHT*3;
313	// limit max size to full extents of the parameter view
314	float hMax = m_idealSize.Width(), vMax = m_idealSize.Height();
315	SetSizeLimits(hMin, hMax, vMin, vMax);
316
317	// adapt the window to the new dimensions
318	ResizeTo(m_idealSize.Width(), m_idealSize.Height());
319	m_zoomed = true;
320
321	if (m_parameters) {
322		BRect paramRect = m_parameters->Bounds();
323		AddChild(new ParameterContainerView(paramRect, m_parameters));
324	}
325}
326
327void ParameterWindow::_constrainToScreen() {
328	D_INTERNAL(("ParameterWindow::_constrainToScreen()\n"));
329
330	BScreen screen(this);
331	BRect screenRect = screen.Frame();
332	BRect windowRect = Frame();
333
334	// if the window is outside the screen rect
335	// move it to the default position
336	if (!screenRect.Intersects(windowRect)) {
337		windowRect.OffsetTo(screenRect.LeftTop());
338		MoveTo(windowRect.LeftTop());
339		windowRect = Frame();
340	}
341
342	// if the window is larger than the screen rect
343	// resize it to fit at each side
344	if (!screenRect.Contains(windowRect)) {
345		if (windowRect.left < screenRect.left) {
346			windowRect.left = screenRect.left + 5.0;
347			MoveTo(windowRect.LeftTop());
348			windowRect = Frame();
349		}
350		if (windowRect.top < screenRect.top) {
351			windowRect.top = screenRect.top + 5.0;
352			MoveTo(windowRect.LeftTop());
353			windowRect = Frame();
354		}
355		if (windowRect.right > screenRect.right) {
356			windowRect.right = screenRect.right - 5.0;
357		}
358		if (windowRect.bottom > screenRect.bottom) {
359			windowRect.bottom = screenRect.bottom - 5.0;
360		}
361		ResizeTo(windowRect.Width(), windowRect.Height());
362	}
363}
364
365status_t ParameterWindow::_startControlPanel() {
366	D_INTERNAL(("ParameterWindow::_startControlPanel()\n"));
367
368	// get roster instance
369	BMediaRoster *roster = BMediaRoster::CurrentRoster();
370	if (!roster) {
371		D_INTERNAL((" -> MediaRoster not available\n"));
372		return B_ERROR;
373	}
374
375	// try to StartControlPanel()
376	BMessenger messenger;
377	status_t error = roster->StartControlPanel(m_node, &messenger);
378	if (error) {
379		D_INTERNAL((" -> StartControlPanel() failed (%s)\n", strerror(error)));
380		return error;
381	}
382
383	// notify the notification target, if any
384	if (m_notifyTarget) {
385		BMessage message(M_CONTROL_PANEL_STARTED);
386		message.AddInt32("nodeID", m_node.node);
387		message.AddMessenger("messenger", messenger);
388		error = m_notifyTarget->SendMessage(&message);
389		if (error) {
390			D_INTERNAL((" -> failed to notify target\n"));
391		}
392	}
393
394	return B_OK;
395}
396
397// END -- ParameterWindow.cpp --
398