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