xref: /haiku/src/kits/interface/BMCPrivate.cpp (revision 04a0e9c7b68cbe3a43d38e2bca8e860fd80936fb)
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 
139 
140 void
141 _BMCMenuBar_::Draw(BRect updateRect)
142 {
143 	if (fFixedSize) {
144 		// Set the width of the menu bar because the menu bar bounds may have
145 		// been expanded by the selected menu item.
146 		ResizeTo(fMenuField->_MenuBarWidth(), Bounds().Height());
147 	} else {
148 		// For compatability with BeOS R5:
149 		//  - Set to the minimum of the menu bar width set by the menu frame
150 		//    and the selected menu item width.
151 		//  - Set the height to the preferred height ignoring the height of the
152 		//    menu field.
153 		float height;
154 		BMenuBar::GetPreferredSize(NULL, &height);
155 		ResizeTo(std::min(Bounds().Width(), fMenuField->_MenuBarWidth()),
156 			height);
157 	}
158 
159 	BRect rect(Bounds());
160 	rgb_color base = ui_color(B_MENU_BACKGROUND_COLOR);
161 	uint32 flags = 0;
162 	if (!IsEnabled())
163 		flags |= BControlLook::B_DISABLED;
164 	if (IsFocus())
165 		flags |= BControlLook::B_FOCUSED;
166 
167 	be_control_look->DrawMenuFieldBackground(this, rect,
168 		updateRect, base, fShowPopUpMarker, flags);
169 
170 	_DrawItems(updateRect);
171 }
172 
173 
174 void
175 _BMCMenuBar_::FrameResized(float width, float height)
176 {
177 	// we need to take care of resizing and cleaning up
178 	// the parent menu field
179 	float diff = width - fPreviousWidth;
180 	fPreviousWidth = width;
181 
182 	if (Window() != NULL && diff != 0) {
183 		BRect dirty(fMenuField->Bounds());
184 		if (diff > 0) {
185 			// clean up the dirty right border of
186 			// the menu field when enlarging
187 			dirty.right = Frame().right + kVMargin;
188 			dirty.left = dirty.left - diff - kVMargin * 2;
189 			fMenuField->Invalidate(dirty);
190 
191 			// clean up the arrow part
192 			dirty = Bounds();
193 			dirty.left = dirty.right - diff - kPopUpIndicatorWidth;
194 			Invalidate(dirty);
195 		} else if (diff < 0) {
196 			// clean up the dirty right line of
197 			// the menu field when shrinking
198 			dirty.left = Frame().right - kVMargin;
199 			dirty.right = dirty.left - diff + kVMargin * 2;
200 			fMenuField->Invalidate(dirty);
201 
202 			// clean up the arrow part
203 			dirty = Bounds();
204 			dirty.left = dirty.right - kPopUpIndicatorWidth;
205 			Invalidate(dirty);
206 		}
207 	}
208 
209 	BMenuBar::FrameResized(width, height);
210 }
211 
212 
213 void
214 _BMCMenuBar_::MakeFocus(bool focused)
215 {
216 	if (IsFocus() == focused)
217 		return;
218 
219 	BMenuBar::MakeFocus(focused);
220 }
221 
222 
223 void
224 _BMCMenuBar_::MessageReceived(BMessage* message)
225 {
226 	switch (message->what) {
227 		case 'TICK':
228 		{
229 			BMenuItem* item = ItemAt(0);
230 
231 			if (item && item->Submenu() &&  item->Submenu()->Window()) {
232 				BMessage message(B_KEY_DOWN);
233 
234 				message.AddInt8("byte", B_ESCAPE);
235 				message.AddInt8("key", B_ESCAPE);
236 				message.AddInt32("modifiers", 0);
237 				message.AddInt8("raw_char", B_ESCAPE);
238 
239 				Window()->PostMessage(&message, this, NULL);
240 			}
241 		}
242 		// fall through
243 		default:
244 			BMenuBar::MessageReceived(message);
245 			break;
246 	}
247 }
248 
249 
250 void
251 _BMCMenuBar_::SetMaxContentWidth(float width)
252 {
253 	float left;
254 	float right;
255 	GetItemMargins(&left, NULL, &right, NULL);
256 
257 	BMenuBar::SetMaxContentWidth(width - (left + right));
258 }
259 
260 
261 void
262 _BMCMenuBar_::SetEnabled(bool enabled)
263 {
264 	fMenuField->SetEnabled(enabled);
265 
266 	BMenuBar::SetEnabled(enabled);
267 }
268 
269 
270 BSize
271 _BMCMenuBar_::MinSize()
272 {
273 	BSize size;
274 	BMenuBar::GetPreferredSize(&size.width, &size.height);
275 
276 	if (fShowPopUpMarker) {
277 		// account for popup indicator + a few pixels margin
278 		size.width += kPopUpIndicatorWidth;
279 	}
280 
281 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
282 }
283 
284 
285 BSize
286 _BMCMenuBar_::MaxSize()
287 {
288 	// The maximum width of a normal BMenuBar is unlimited, but we want it
289 	// limited.
290 	BSize size;
291 	BMenuBar::GetPreferredSize(&size.width, &size.height);
292 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
293 }
294 
295 
296 //	#pragma mark - _BMCMenuBar_ private methods
297 
298 
299 void
300 _BMCMenuBar_::_Init()
301 {
302 	SetFlags(Flags() | B_FRAME_EVENTS);
303 	SetBorder(B_BORDER_CONTENTS);
304 
305 	float left, top, right, bottom;
306 	GetItemMargins(&left, &top, &right, &bottom);
307 
308 #if 0
309 	// TODO: Better fix would be to make BMenuItem draw text properly
310 	// centered
311 	font_height fontHeight;
312 	GetFontHeight(&fontHeight);
313 	top = ceilf((Bounds().Height() - ceilf(fontHeight.ascent)
314 		- ceilf(fontHeight.descent)) / 2) + 1;
315 	bottom = top - 1;
316 #else
317 	// TODO: Fix content location properly. This is just a quick fix to
318 	// make the BMenuField label and the super-item of the BMenuBar
319 	// align vertically.
320 	top++;
321 	bottom--;
322 #endif
323 
324 	if (be_control_look != NULL)
325 		left = right = be_control_look->DefaultLabelSpacing();
326 
327 	SetItemMargins(left, top,
328 		right + fShowPopUpMarker ? kPopUpIndicatorWidth : 0, bottom);
329 
330 	fPreviousWidth = Bounds().Width();
331 }
332