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
58__USE_CORTEX_NAMESPACE
59
60#include <Debug.h>
61#define D_ALLOC(x) //PRINT (x)
62#define D_HOOK(x) //PRINT (x)
63#define D_INTERNAL(x) //PRINT (x)
64#define D_MESSAGE(x) //PRINT (x)
65
66// -------------------------------------------------------- //
67// ctor/dtor
68// -------------------------------------------------------- //
69
70ParameterWindow::ParameterWindow(
71	BPoint position,
72	live_node_info &nodeInfo,
73	BMessenger *notifyTarget)
74	: BWindow(BRect(position, position + BPoint(50.0, 50.0)),
75			  "parameters", B_DOCUMENT_WINDOW,
76			  B_WILL_ACCEPT_FIRST_CLICK | B_ASYNCHRONOUS_CONTROLS),
77	  m_node(nodeInfo.node),
78	  m_parameters(0),
79	  m_notifyTarget(0),
80	  m_zoomed(false),
81	  m_zooming(false) {
82	D_ALLOC(("ParameterWindow::ParameterWindow()\n"));
83
84	// add the nodes name to the title
85	{
86		char* title = new char[strlen(nodeInfo.name) + strlen(" parameters") + 1];
87		sprintf(title, "%s parameters", nodeInfo.name);
88		SetTitle(title);
89		delete [] title;
90	}
91	// add the menu bar
92	BMenuBar *menuBar = new BMenuBar(Bounds(), "ParameterWindow MenuBar");
93
94	BMenu *menu = new BMenu("Window");
95	menu->AddItem(new BMenuItem("Start control panel",
96								new BMessage(M_START_CONTROL_PANEL),
97								'P', B_COMMAND_KEY | B_SHIFT_KEY));
98	menu->AddSeparatorItem();
99	menu->AddItem(new BMenuItem("Close",
100								new BMessage(B_QUIT_REQUESTED),
101								'W', B_COMMAND_KEY));
102	menuBar->AddItem(menu);
103
104	// future Media Theme selection capabilities go here
105	menu = new BMenu("Themes");
106	BMessage *message = new BMessage(M_THEME_SELECTED);
107	BMediaTheme *theme = BMediaTheme::PreferredTheme();
108	message->AddInt32("themeID", theme->ID());
109	BMenuItem *item = new BMenuItem(theme->Name(), message);
110	item->SetMarked(true);
111	menu->AddItem(item);
112	menuBar->AddItem(menu);
113	AddChild(menuBar);
114
115	_updateParameterView();
116	_init();
117
118	// start watching for parameter web changes
119	BMediaRoster *roster = BMediaRoster::CurrentRoster();
120	roster->StartWatching(this, nodeInfo.node, B_MEDIA_WILDCARD);
121
122	if (notifyTarget) {
123		m_notifyTarget = new BMessenger(*notifyTarget);
124	}
125}
126
127ParameterWindow::~ParameterWindow() {
128	D_ALLOC(("ParameterWindow::~ParameterWindow()\n"));
129
130	if (m_notifyTarget) {
131		delete m_notifyTarget;
132	}
133}
134
135// -------------------------------------------------------- //
136// BWindow impl
137// -------------------------------------------------------- //
138
139void ParameterWindow::FrameResized(
140	float width,
141	float height) {
142	D_HOOK(("ParameterWindow::FrameResized()\n"));
143
144	if (!m_zooming) {
145		m_zoomed = false;
146	}
147	else {
148		m_zooming = false;
149	}
150}
151
152void ParameterWindow::MessageReceived(
153	BMessage *message) {
154	D_MESSAGE(("ParameterWindow::MessageReceived()\n"));
155
156	switch (message->what) {
157		case M_START_CONTROL_PANEL: {
158			D_MESSAGE((" -> M_START_CONTROL_PANEL\n"));
159			status_t error = _startControlPanel();
160			if (error) {
161				BString s = "Could not start control panel";
162				s << " (" << strerror(error) << ")";
163				BAlert *alert = new BAlert("", s.String(), "OK", 0, 0,
164										   B_WIDTH_AS_USUAL, B_WARNING_ALERT);
165				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
166				alert->Go(0);
167			}
168			bool replace = false;
169			if ((message->FindBool("replace", &replace) == B_OK)
170			 && (replace == true)) {
171				PostMessage(B_QUIT_REQUESTED);
172			}
173			break;
174		}
175		case M_THEME_SELECTED: {
176			D_MESSAGE((" -> M_THEME_SELECTED\n"));
177			int32 themeID;
178			if (message->FindInt32("themeID", &themeID) != B_OK) {
179				return;
180			}
181			// not yet implemented
182			break;
183		}
184		case B_MEDIA_WEB_CHANGED: {
185			D_MESSAGE((" -> B_MEDIA_WEB_CHANGED\n"));
186			_updateParameterView();
187			_constrainToScreen();
188			break;
189		}
190		default: {
191			BWindow::MessageReceived(message);
192		}
193	}
194}
195
196bool ParameterWindow::QuitRequested() {
197	D_HOOK(("ParameterWindow::QuitRequested()\n"));
198
199	// stop watching the MediaRoster
200	BMediaRoster *roster = BMediaRoster::CurrentRoster();
201	if (roster)	{
202		roster->StopWatching(this, m_node, B_MEDIA_WILDCARD);
203	}
204
205	// tell the notification target to forget about us
206	if (m_notifyTarget && m_notifyTarget->IsValid()) {
207		BMessage message(M_CLOSED);
208		message.AddInt32("nodeID", m_node.node);
209		status_t error = m_notifyTarget->SendMessage(&message);
210		if (error) {
211			D_HOOK((" -> error sending message (%s) !\n", strerror(error)));
212		}
213	}
214
215	return true;
216}
217
218void ParameterWindow::Zoom(
219	BPoint origin,
220	float width,
221	float height) {
222	D_HOOK(("ParameterWindow::Zoom()\n"));
223
224	m_zooming = true;
225
226	BScreen screen(this);
227	if (!screen.Frame().Contains(Frame())) {
228		m_zoomed = false;
229	}
230
231	if (!m_zoomed) {
232		// resize to the ideal size
233		m_manualSize = Bounds();
234		ResizeTo(m_idealSize.Width(), m_idealSize.Height());
235		_constrainToScreen();
236		m_zoomed = true;
237	}
238	else {
239		// resize to the most recent manual size
240		ResizeTo(m_manualSize.Width(), m_manualSize.Height());
241		m_zoomed = false;
242	}
243}
244
245// -------------------------------------------------------- //
246// internal operations
247// -------------------------------------------------------- //
248
249void ParameterWindow::_init() {
250	D_INTERNAL(("ParameterWindow::_init()\n"));
251
252	// offset to a new position
253	_constrainToScreen();
254	m_manualSize = Bounds().OffsetToCopy(0.0, 0.0);
255
256	// add the hidden option to close this window when the
257	// control panel has been started successfully
258	BMessage *message = new BMessage(M_START_CONTROL_PANEL);
259	message->AddBool("replace", true);
260	AddShortcut('P', B_COMMAND_KEY | B_SHIFT_KEY | B_OPTION_KEY,
261				message);
262}
263
264void ParameterWindow::_updateParameterView(
265	BMediaTheme *theme) {
266	D_INTERNAL(("ParameterWindow::_updateParameterView()\n"));
267
268	// clear the old version
269	if (m_parameters) {
270		ParameterContainerView *view = dynamic_cast<ParameterContainerView *>(FindView("ParameterContainerView"));
271		RemoveChild(view);
272		delete m_parameters;
273		m_parameters = 0;
274		delete view;
275	}
276
277	// fetch ParameterWeb from the MediaRoster
278	BMediaRoster *roster = BMediaRoster::CurrentRoster();
279	if (roster) {
280		BParameterWeb *web;
281		status_t error = roster->GetParameterWebFor(m_node, &web);
282		if (!error && (web->CountParameters() || web->CountGroups())) {
283			// if no theme was specified, use the preferred theme
284			if (!theme) {
285				theme = BMediaTheme::PreferredTheme();
286			}
287			// acquire the view
288			m_parameters = BMediaTheme::ViewFor(web, 0, theme);
289			if (m_parameters) {
290				BMenuBar *menuBar = KeyMenuBar();
291				m_idealSize = m_parameters->Bounds();
292				m_idealSize.right += B_V_SCROLL_BAR_WIDTH;
293				m_idealSize.bottom += B_H_SCROLL_BAR_HEIGHT;
294				if (menuBar) {
295					m_parameters->MoveTo(0.0, menuBar->Bounds().bottom + 1.0);
296					m_idealSize.bottom += menuBar->Bounds().bottom + 1.0;
297				}
298			}
299		}
300	}
301
302	// limit min size to avoid funny-looking scrollbars
303	float hMin = B_V_SCROLL_BAR_WIDTH*6, vMin = B_H_SCROLL_BAR_HEIGHT*3;
304	// limit max size to full extents of the parameter view
305	float hMax = m_idealSize.Width(), vMax = m_idealSize.Height();
306	SetSizeLimits(hMin, hMax, vMin, vMax);
307
308	// adapt the window to the new dimensions
309	ResizeTo(m_idealSize.Width(), m_idealSize.Height());
310	m_zoomed = true;
311
312	if (m_parameters) {
313		BRect paramRect = m_parameters->Bounds();
314		AddChild(new ParameterContainerView(paramRect, m_parameters));
315	}
316}
317
318void ParameterWindow::_constrainToScreen() {
319	D_INTERNAL(("ParameterWindow::_constrainToScreen()\n"));
320
321	BScreen screen(this);
322	BRect screenRect = screen.Frame();
323	BRect windowRect = Frame();
324
325	// if the window is outside the screen rect
326	// move it to the default position
327	if (!screenRect.Intersects(windowRect)) {
328		windowRect.OffsetTo(screenRect.LeftTop());
329		MoveTo(windowRect.LeftTop());
330		windowRect = Frame();
331	}
332
333	// if the window is larger than the screen rect
334	// resize it to fit at each side
335	if (!screenRect.Contains(windowRect)) {
336		if (windowRect.left < screenRect.left) {
337			windowRect.left = screenRect.left + 5.0;
338			MoveTo(windowRect.LeftTop());
339			windowRect = Frame();
340		}
341		if (windowRect.top < screenRect.top) {
342			windowRect.top = screenRect.top + 5.0;
343			MoveTo(windowRect.LeftTop());
344			windowRect = Frame();
345		}
346		if (windowRect.right > screenRect.right) {
347			windowRect.right = screenRect.right - 5.0;
348		}
349		if (windowRect.bottom > screenRect.bottom) {
350			windowRect.bottom = screenRect.bottom - 5.0;
351		}
352		ResizeTo(windowRect.Width(), windowRect.Height());
353	}
354}
355
356status_t ParameterWindow::_startControlPanel() {
357	D_INTERNAL(("ParameterWindow::_startControlPanel()\n"));
358
359	// get roster instance
360	BMediaRoster *roster = BMediaRoster::CurrentRoster();
361	if (!roster) {
362		D_INTERNAL((" -> MediaRoster not available\n"));
363		return B_ERROR;
364	}
365
366	// try to StartControlPanel()
367	BMessenger messenger;
368	status_t error = roster->StartControlPanel(m_node, &messenger);
369	if (error) {
370		D_INTERNAL((" -> StartControlPanel() failed (%s)\n", strerror(error)));
371		return error;
372	}
373
374	// notify the notification target, if any
375	if (m_notifyTarget) {
376		BMessage message(M_CONTROL_PANEL_STARTED);
377		message.AddInt32("nodeID", m_node.node);
378		message.AddMessenger("messenger", messenger);
379		error = m_notifyTarget->SendMessage(&message);
380		if (error) {
381			D_INTERNAL((" -> failed to notify target\n"));
382		}
383	}
384
385	return B_OK;
386}
387
388// END -- ParameterWindow.cpp --
389