1/*
2 * Copyright 2006-2007, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 */
8
9#include "MultipleManipulatorState.h"
10
11#include <stdio.h>
12
13#include <AppDefs.h>
14
15#include "Manipulator.h"
16#include "StateView.h"
17
18// constructor
19MultipleManipulatorState::MultipleManipulatorState(StateView* view)
20	: ViewState(view),
21	  fManipulators(24),
22	  fCurrentManipulator(NULL),
23	  fPreviousManipulator(NULL)
24{
25}
26
27// destructor
28MultipleManipulatorState::~MultipleManipulatorState()
29{
30	DeleteManipulators();
31}
32
33// #pragma mark -
34
35// Init
36void
37MultipleManipulatorState::Init()
38{
39}
40
41// Cleanup
42void
43MultipleManipulatorState::Cleanup()
44{
45}
46
47// #pragma mark -
48
49// Draw
50void
51MultipleManipulatorState::Draw(BView* into, BRect updateRect)
52{
53	int32 count = fManipulators.CountItems();
54	for (int32 i = 0; i < count; i++) {
55		Manipulator* manipulator =
56			(Manipulator*)fManipulators.ItemAtFast(i);
57		if (manipulator->Bounds().Intersects(updateRect))
58			manipulator->Draw(into, updateRect);
59	}
60}
61
62// MessageReceived
63bool
64MultipleManipulatorState::MessageReceived(BMessage* message,
65										  Command** _command)
66{
67	int32 count = fManipulators.CountItems();
68	for (int32 i = 0; i < count; i++) {
69		Manipulator* manipulator =
70			(Manipulator*)fManipulators.ItemAtFast(i);
71		if (manipulator->MessageReceived(message, _command))
72			return true;
73	}
74	return false;
75}
76
77// #pragma mark -
78
79// MouseDown
80void
81MultipleManipulatorState::MouseDown(BPoint where, uint32 buttons, uint32 clicks)
82{
83	if (buttons & B_SECONDARY_MOUSE_BUTTON) {
84		_ShowContextMenu(where);
85		return;
86	}
87
88	if (clicks == 2
89		&& fPreviousManipulator
90		&& fManipulators.HasItem(fPreviousManipulator)) {
91		// valid double click (onto the same, still existing manipulator)
92		if (fPreviousManipulator->TrackingBounds(fView).Contains(where)
93			&& fPreviousManipulator->DoubleClicked(where)) {
94			// TODO: eat the click here or wait for MouseUp?
95			fPreviousManipulator = NULL;
96			return;
97		}
98	}
99
100	int32 count = fManipulators.CountItems();
101	for (int32 i = count - 1; i >= 0; i--) {
102		Manipulator* manipulator =
103			(Manipulator*)fManipulators.ItemAtFast(i);
104		if (manipulator->MouseDown(where)) {
105			fCurrentManipulator = manipulator;
106			break;
107		}
108	}
109
110	fView->SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
111}
112
113// MouseMoved
114void
115MultipleManipulatorState::MouseMoved(BPoint where, uint32 transit,
116									 const BMessage* dragMessage)
117{
118	if (fCurrentManipulator) {
119		// the mouse is currently pressed
120		fCurrentManipulator->MouseMoved(where);
121
122	} else {
123		// the mouse is currently NOT pressed
124
125		// call MouseOver on all manipulators
126		// until one feels responsible
127		int32 count = fManipulators.CountItems();
128		bool updateCursor = true;
129		for (int32 i = 0; i < count; i++) {
130			Manipulator* manipulator =
131				(Manipulator*)fManipulators.ItemAtFast(i);
132			if (manipulator->TrackingBounds(fView).Contains(where)
133				&& manipulator->MouseOver(where)) {
134				updateCursor = false;
135				break;
136			}
137		}
138		if (updateCursor)
139			_UpdateCursor();
140	}
141}
142
143// MouseUp
144Command*
145MultipleManipulatorState::MouseUp()
146{
147	Command* command = NULL;
148	if (fCurrentManipulator) {
149		command = fCurrentManipulator->MouseUp();
150		fPreviousManipulator = fCurrentManipulator;
151		fCurrentManipulator = NULL;
152	}
153	return command;
154}
155
156// #pragma mark -
157
158// ModifiersChanged
159void
160MultipleManipulatorState::ModifiersChanged(uint32 modifiers)
161{
162	int32 count = fManipulators.CountItems();
163	for (int32 i = 0; i < count; i++) {
164		Manipulator* manipulator =
165			(Manipulator*)fManipulators.ItemAtFast(i);
166		manipulator->ModifiersChanged(modifiers);
167	}
168}
169
170// HandleKeyDown
171bool
172MultipleManipulatorState::HandleKeyDown(uint32 key, uint32 modifiers,
173										Command** _command)
174{
175	// TODO: somehow this looks suspicious, because it doesn't
176	// seem guaranteed that the manipulator having indicated to
177	// handle the key down handles the matching key up event...
178	// maybe there should be the concept of the "focused manipulator"
179	int32 count = fManipulators.CountItems();
180	for (int32 i = 0; i < count; i++) {
181		Manipulator* manipulator =
182			(Manipulator*)fManipulators.ItemAtFast(i);
183		if (manipulator->HandleKeyDown(key, modifiers, _command))
184			return true;
185	}
186	return false;
187}
188
189// HandleKeyUp
190bool
191MultipleManipulatorState::HandleKeyUp(uint32 key, uint32 modifiers,
192									  Command** _command)
193{
194	int32 count = fManipulators.CountItems();
195	for (int32 i = 0; i < count; i++) {
196		Manipulator* manipulator =
197			(Manipulator*)fManipulators.ItemAtFast(i);
198		if (manipulator->HandleKeyUp(key, modifiers, _command))
199			return true;
200	}
201	return false;
202}
203
204// UpdateCursor
205bool
206MultipleManipulatorState::UpdateCursor()
207{
208	if (fPreviousManipulator && fManipulators.HasItem(fPreviousManipulator))
209		return fPreviousManipulator->UpdateCursor();
210	return false;
211}
212
213// #pragma mark -
214
215// AddManipulator
216bool
217MultipleManipulatorState::AddManipulator(Manipulator* manipulator)
218{
219	if (!manipulator)
220		return false;
221
222	if (fManipulators.AddItem((void*)manipulator)) {
223		manipulator->AttachedToView(fView);
224		fView->Invalidate(manipulator->Bounds());
225		return true;
226	}
227	return false;
228}
229
230// RemoveManipulator
231Manipulator*
232MultipleManipulatorState::RemoveManipulator(int32 index)
233{
234	Manipulator* manipulator = (Manipulator*)fManipulators.RemoveItem(index);
235
236	if (manipulator == fCurrentManipulator)
237		fCurrentManipulator = NULL;
238
239	if (manipulator) {
240		fView->Invalidate(manipulator->Bounds());
241		manipulator->DetachedFromView(fView);
242	}
243
244	return manipulator;
245}
246
247// DeleteManipulators
248void
249MultipleManipulatorState::DeleteManipulators()
250{
251	BRect dirty(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN);
252
253	int32 count = fManipulators.CountItems();
254	for (int32 i = 0; i < count; i++) {
255		Manipulator* m = (Manipulator*)fManipulators.ItemAtFast(i);
256		dirty = dirty | m->Bounds();
257		m->DetachedFromView(fView);
258		delete m;
259	}
260	fManipulators.MakeEmpty();
261	fCurrentManipulator = NULL;
262	fPreviousManipulator = NULL;
263
264	fView->Invalidate(dirty);
265	_UpdateCursor();
266}
267
268// CountManipulators
269int32
270MultipleManipulatorState::CountManipulators() const
271{
272	return fManipulators.CountItems();
273}
274
275// ManipulatorAt
276Manipulator*
277MultipleManipulatorState::ManipulatorAt(int32 index) const
278{
279	return (Manipulator*)fManipulators.ItemAt(index);
280}
281
282// ManipulatorAtFast
283Manipulator*
284MultipleManipulatorState::ManipulatorAtFast(int32 index) const
285{
286	return (Manipulator*)fManipulators.ItemAtFast(index);
287}
288
289// #pragma mark -
290
291// _UpdateViewCursor
292void
293MultipleManipulatorState::_UpdateCursor()
294{
295	if (fCurrentManipulator)
296		fCurrentManipulator->UpdateCursor();
297	else
298		fView->SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
299}
300
301// _ShowContextMenu
302void
303MultipleManipulatorState::_ShowContextMenu(BPoint where)
304{
305	int32 count = fManipulators.CountItems();
306	for (int32 i = 0; i < count; i++) {
307		Manipulator* manipulator =
308			(Manipulator*)fManipulators.ItemAtFast(i);
309		if (manipulator->ShowContextMenu(where))
310			return;
311	}
312}
313
314
315