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