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