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