xref: /haiku/src/kits/interface/BMCPrivate.cpp (revision 37fedaf8494b34aad811abcc49e79aa32943f880)
1 /*
2  * Copyright 2001-2013 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus, superstippi@gmx.de
7  *		Marc Flerackers, mflerackers@androme.be
8  *		John Scipione, jcipione@gmail.com
9  */
10 
11 
12 #include <BMCPrivate.h>
13 
14 #include <algorithm>
15 
16 #include <ControlLook.h>
17 #include <LayoutUtils.h>
18 #include <MenuField.h>
19 #include <MenuItem.h>
20 #include <Message.h>
21 #include <MessageRunner.h>
22 #include <Window.h>
23 
24 
25 static const float kPopUpIndicatorWidth = 13.0f;
26 
27 
28 //	#pragma mark - _BMCFilter_
29 
30 
31 _BMCFilter_::_BMCFilter_(BMenuField* menuField, uint32 what)
32 	:
33 	BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, what),
34 	fMenuField(menuField)
35 {
36 }
37 
38 
39 _BMCFilter_::~_BMCFilter_()
40 {
41 }
42 
43 
44 filter_result
45 _BMCFilter_::Filter(BMessage* message, BHandler** handler)
46 {
47 	if (message->what == B_MOUSE_DOWN) {
48 		if (BView* view = dynamic_cast<BView*>(*handler)) {
49 			BPoint point;
50 			message->FindPoint("be:view_where", &point);
51 			view->ConvertToParent(&point);
52 			message->ReplacePoint("be:view_where", point);
53 			*handler = fMenuField;
54 		}
55 	}
56 
57 	return B_DISPATCH_MESSAGE;
58 }
59 
60 
61 //	#pragma mark - _BMCMenuBar_
62 
63 
64 _BMCMenuBar_::_BMCMenuBar_(BRect frame, bool fixedSize, BMenuField* menuField)
65 	:
66 	BMenuBar(frame, "_mc_mb_", B_FOLLOW_LEFT | B_FOLLOW_TOP, B_ITEMS_IN_ROW,
67 		!fixedSize),
68 	fMenuField(menuField),
69 	fFixedSize(fixedSize),
70 	fShowPopUpMarker(true)
71 {
72 	_Init();
73 }
74 
75 
76 _BMCMenuBar_::_BMCMenuBar_(BMenuField* menuField)
77 	:
78 	BMenuBar("_mc_mb_", B_ITEMS_IN_ROW),
79 	fMenuField(menuField),
80 	fFixedSize(true),
81 	fShowPopUpMarker(true)
82 {
83 	_Init();
84 }
85 
86 
87 _BMCMenuBar_::_BMCMenuBar_(BMessage* data)
88 	:
89 	BMenuBar(data),
90 	fMenuField(NULL),
91 	fFixedSize(true),
92 	fShowPopUpMarker(true)
93 {
94 	SetFlags(Flags() | B_FRAME_EVENTS);
95 
96 	bool resizeToFit;
97 	if (data->FindBool("_rsize_to_fit", &resizeToFit) == B_OK)
98 		fFixedSize = !resizeToFit;
99 }
100 
101 
102 _BMCMenuBar_::~_BMCMenuBar_()
103 {
104 }
105 
106 
107 //	#pragma mark - _BMCMenuBar_ public methods
108 
109 
110 BArchivable*
111 _BMCMenuBar_::Instantiate(BMessage* data)
112 {
113 	if (validate_instantiation(data, "_BMCMenuBar_"))
114 		return new _BMCMenuBar_(data);
115 
116 	return NULL;
117 }
118 
119 
120 void
121 _BMCMenuBar_::AttachedToWindow()
122 {
123 	fMenuField = static_cast<BMenuField*>(Parent());
124 
125 	// Don't cause the KeyMenuBar to change by being attached
126 	BMenuBar* menuBar = Window()->KeyMenuBar();
127 	BMenuBar::AttachedToWindow();
128 	Window()->SetKeyMenuBar(menuBar);
129 
130 	if (fFixedSize && (Flags() & B_SUPPORTS_LAYOUT) == 0)
131 		SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
132 
133 	if (Parent() != NULL)
134 		SetLowColor(Parent()->LowColor());
135 	else
136 		SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR));
137 
138 	fPreviousWidth = Bounds().Width();
139 }
140 
141 
142 void
143 _BMCMenuBar_::Draw(BRect updateRect)
144 {
145 	if (fFixedSize) {
146 		// Set the width of the menu bar because the menu bar bounds may have
147 		// been expanded by the selected menu item.
148 		ResizeTo(fMenuField->_MenuBarWidth(), Bounds().Height());
149 	} else {
150 		// For compatability with BeOS R5:
151 		//  - Set to the minimum of the menu bar width set by the menu frame
152 		//    and the selected menu item width.
153 		//  - Set the height to the preferred height ignoring the height of the
154 		//    menu field.
155 		float height;
156 		BMenuBar::GetPreferredSize(NULL, &height);
157 		ResizeTo(std::min(Bounds().Width(), fMenuField->_MenuBarWidth()),
158 			height);
159 	}
160 
161 	BRect rect(Bounds());
162 	rgb_color base = ui_color(B_MENU_BACKGROUND_COLOR);
163 	uint32 flags = 0;
164 	if (!IsEnabled())
165 		flags |= BControlLook::B_DISABLED;
166 	if (IsFocus())
167 		flags |= BControlLook::B_FOCUSED;
168 
169 	be_control_look->DrawMenuFieldBackground(this, rect,
170 		updateRect, base, fShowPopUpMarker, flags);
171 
172 	_DrawItems(updateRect);
173 }
174 
175 
176 void
177 _BMCMenuBar_::FrameResized(float width, float height)
178 {
179 	// we need to take care of cleaning up the parent menu field
180 	float diff = width - fPreviousWidth;
181 	fPreviousWidth = width;
182 
183 	if (Window() != NULL && diff != 0) {
184 		BRect dirty(fMenuField->Bounds());
185 		if (diff > 0) {
186 			// clean up the dirty right border of
187 			// the menu field when enlarging
188 			dirty.right = Frame().right + kVMargin;
189 			dirty.left = dirty.right - diff - kVMargin * 2;
190 			fMenuField->Invalidate(dirty);
191 		} else if (diff < 0) {
192 			// clean up the dirty right line of
193 			// the menu field when shrinking
194 			dirty.left = Frame().right - kVMargin;
195 			fMenuField->Invalidate(dirty);
196 		}
197 	}
198 
199 	BMenuBar::FrameResized(width, height);
200 }
201 
202 
203 void
204 _BMCMenuBar_::MakeFocus(bool focused)
205 {
206 	if (IsFocus() == focused)
207 		return;
208 
209 	BMenuBar::MakeFocus(focused);
210 }
211 
212 
213 void
214 _BMCMenuBar_::MessageReceived(BMessage* message)
215 {
216 	switch (message->what) {
217 		case 'TICK':
218 		{
219 			BMenuItem* item = ItemAt(0);
220 
221 			if (item && item->Submenu() &&  item->Submenu()->Window()) {
222 				BMessage message(B_KEY_DOWN);
223 
224 				message.AddInt8("byte", B_ESCAPE);
225 				message.AddInt8("key", B_ESCAPE);
226 				message.AddInt32("modifiers", 0);
227 				message.AddInt8("raw_char", B_ESCAPE);
228 
229 				Window()->PostMessage(&message, this, NULL);
230 			}
231 		}
232 		// fall through
233 		default:
234 			BMenuBar::MessageReceived(message);
235 			break;
236 	}
237 }
238 
239 
240 void
241 _BMCMenuBar_::SetMaxContentWidth(float width)
242 {
243 	float left;
244 	float right;
245 	GetItemMargins(&left, NULL, &right, NULL);
246 
247 	BMenuBar::SetMaxContentWidth(width - (left + right));
248 }
249 
250 
251 void
252 _BMCMenuBar_::SetEnabled(bool enabled)
253 {
254 	fMenuField->SetEnabled(enabled);
255 
256 	BMenuBar::SetEnabled(enabled);
257 }
258 
259 
260 BSize
261 _BMCMenuBar_::MinSize()
262 {
263 	BSize size;
264 	BMenuBar::GetPreferredSize(&size.width, &size.height);
265 	if (fShowPopUpMarker) {
266 		// account for popup indicator + a few pixels margin
267 		size.width += kPopUpIndicatorWidth;
268 	}
269 
270 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
271 }
272 
273 
274 BSize
275 _BMCMenuBar_::MaxSize()
276 {
277 	// The maximum width of a normal BMenuBar is unlimited, but we want it
278 	// limited.
279 	BSize size;
280 	BMenuBar::GetPreferredSize(&size.width, &size.height);
281 
282 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
283 }
284 
285 
286 //	#pragma mark - _BMCMenuBar_ private methods
287 
288 
289 void
290 _BMCMenuBar_::_Init()
291 {
292 	SetFlags(Flags() | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE);
293 	SetBorder(B_BORDER_CONTENTS);
294 
295 	float left, top, right, bottom;
296 	GetItemMargins(&left, &top, &right, &bottom);
297 
298 #if 0
299 	// TODO: Better fix would be to make BMenuItem draw text properly
300 	// centered
301 	font_height fontHeight;
302 	GetFontHeight(&fontHeight);
303 	top = ceilf((Bounds().Height() - ceilf(fontHeight.ascent)
304 		- ceilf(fontHeight.descent)) / 2) + 1;
305 	bottom = top - 1;
306 #else
307 	// TODO: Fix content location properly. This is just a quick fix to
308 	// make the BMenuField label and the super-item of the BMenuBar
309 	// align vertically.
310 	top++;
311 	bottom--;
312 #endif
313 
314 	if (be_control_look != NULL)
315 		left = right = be_control_look->DefaultLabelSpacing();
316 
317 	SetItemMargins(left, top,
318 		right + fShowPopUpMarker ? kPopUpIndicatorWidth : 0, bottom);
319 }
320