1/*
2 * Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include "AbstractButton.h"
7
8#include <View.h>
9
10
11// #pragma mark - AbstractButton
12
13
14AbstractButton::AbstractButton(button_policy policy, BMessage* message,
15	BMessenger target)
16	: View(BRect(0, 0, 0, 0)),
17	  BInvoker(message, target),
18	  fPolicy(policy),
19	  fSelected(false),
20	  fPressed(false),
21	  fPressedInBounds(false)
22{
23}
24
25
26void
27AbstractButton::SetPolicy(button_policy policy)
28{
29	fPolicy = policy;
30}
31
32
33button_policy
34AbstractButton::Policy() const
35{
36	return fPolicy;
37}
38
39
40void
41AbstractButton::SetSelected(bool selected)
42{
43	if (selected != fSelected) {
44		fSelected = selected;
45		Invalidate();
46
47		// check whether to notify the listeners depending on the button policy
48		bool notify = false;
49		switch (fPolicy) {
50			case BUTTON_POLICY_TOGGLE_ON_RELEASE:
51			case BUTTON_POLICY_SELECT_ON_RELEASE:
52				// always notify on selection changes
53				notify = true;
54				break;
55			case BUTTON_POLICY_INVOKE_ON_RELEASE:
56				// only notify when the user interaction has been finished
57				notify = !fPressed;
58				break;
59		}
60
61		if (notify) {
62			// notify synchronous listeners
63			_NotifyListeners();
64
65			// send the message
66			if (Message()) {
67				BMessage message(*Message());
68				message.AddBool("selected", IsSelected());
69				InvokeNotify(&message);
70			}
71		}
72	}
73}
74
75
76bool
77AbstractButton::IsSelected() const
78{
79	return fSelected;
80}
81
82
83void
84AbstractButton::MouseDown(BPoint where, uint32 buttons, int32 modifiers)
85{
86	if (fPressed)
87		return;
88
89	fPressed = true;
90	_PressedUpdate(where);
91}
92
93
94void
95AbstractButton::MouseUp(BPoint where, uint32 buttons, int32 modifiers)
96{
97	if (!fPressed || (buttons & B_PRIMARY_MOUSE_BUTTON))
98		return;
99
100	_PressedUpdate(where);
101	fPressed = false;
102	if (fPressedInBounds) {
103		fPressedInBounds = false;
104
105		// update selected state according to policy
106		bool selected = fSelected;
107		switch (fPolicy) {
108			case BUTTON_POLICY_TOGGLE_ON_RELEASE:
109				selected = !fSelected;
110				break;
111			case BUTTON_POLICY_SELECT_ON_RELEASE:
112				selected = true;
113				break;
114			case BUTTON_POLICY_INVOKE_ON_RELEASE:
115				selected = false;
116				break;
117		}
118
119		SetSelected(selected);
120	}
121	Invalidate();
122}
123
124
125void
126AbstractButton::MouseMoved(BPoint where, uint32 buttons, int32 modifiers)
127{
128	if (!fPressed)
129		return;
130
131	_PressedUpdate(where);
132}
133
134
135void
136AbstractButton::AddListener(Listener* listener)
137{
138	if (listener && !fListeners.HasItem(listener))
139		fListeners.AddItem(listener);
140}
141
142
143void
144AbstractButton::RemoveListener(Listener* listener)
145{
146	if (listener)
147		fListeners.RemoveItem(listener);
148}
149
150
151bool
152AbstractButton::IsPressed() const
153{
154	return (fPressed && fPressedInBounds);
155}
156
157
158void
159AbstractButton::_PressedUpdate(BPoint where)
160{
161	bool pressedInBounds = Bounds().Contains(where);
162	if (pressedInBounds == fPressedInBounds)
163		return;
164
165	fPressedInBounds = pressedInBounds;
166
167	// update the selected state according to the button policy
168	switch (fPolicy) {
169		case BUTTON_POLICY_TOGGLE_ON_RELEASE:
170		case BUTTON_POLICY_SELECT_ON_RELEASE:
171			// nothing to do
172			break;
173		case BUTTON_POLICY_INVOKE_ON_RELEASE:
174			SetSelected(fPressedInBounds);
175			break;
176	}
177
178	Invalidate();
179}
180
181
182void
183AbstractButton::_NotifyListeners()
184{
185	if (!fListeners.IsEmpty()) {
186		BList listeners(fListeners);
187		for (int32 i = 0; Listener* listener = (Listener*)listeners.ItemAt(i);
188			 i++) {
189			listener->SelectionChanged(this);
190		}
191	}
192}
193
194
195// #pragma mark - AbstractButton::Listener
196
197
198AbstractButton::Listener::~Listener()
199{
200}
201
202