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 41 _BMCItem_::_BMCItem_(BMessage* data) 42 : 43 BMenuItem(data) 44 { 45 } 46 47 48 /*static*/ BArchivable* 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 63 _BMCFilter_::_BMCFilter_(BMenuField* menuField, uint32 what) 64 : 65 BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, what), 66 fMenuField(menuField) 67 { 68 } 69 70 71 _BMCFilter_::~_BMCFilter_() 72 { 73 } 74 75 76 filter_result 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 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 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 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 134 _BMCMenuBar_::~_BMCMenuBar_() 135 { 136 } 137 138 139 // #pragma mark - _BMCMenuBar_ public methods 140 141 142 BArchivable* 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 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 180 _BMCMenuBar_::Draw(BRect updateRect) 181 { 182 if (fFixedSize) { 183 // Set the width of the menu bar because the menu bar bounds may have 184 // been expanded by the selected menu item. 185 ResizeTo(fMenuField->_MenuBarWidth(), Bounds().Height()); 186 } else { 187 // For compatability with BeOS R5: 188 // - Set to the minimum of the menu bar width set by the menu frame 189 // and the selected menu item width. 190 // - Set the height to the preferred height ignoring the height of the 191 // menu field. 192 float height; 193 BMenuBar::GetPreferredSize(NULL, &height); 194 ResizeTo(std::min(Bounds().Width(), fMenuField->_MenuBarWidth()), 195 height); 196 } 197 198 BRect rect(Bounds()); 199 rgb_color base = ui_color(B_MENU_BACKGROUND_COLOR); 200 uint32 flags = 0; 201 if (!IsEnabled()) 202 flags |= BControlLook::B_DISABLED; 203 if (IsFocus()) 204 flags |= BControlLook::B_FOCUSED; 205 206 be_control_look->DrawMenuFieldBackground(this, rect, 207 updateRect, base, fShowPopUpMarker, flags); 208 209 DrawItems(updateRect); 210 } 211 212 213 void 214 _BMCMenuBar_::FrameResized(float width, float height) 215 { 216 // we need to take care of cleaning up the parent menu field 217 float diff = width - fPreviousWidth; 218 fPreviousWidth = width; 219 220 if (Window() != NULL && diff != 0) { 221 BRect dirty(fMenuField->Bounds()); 222 if (diff > 0) { 223 // clean up the dirty right border of 224 // the menu field when enlarging 225 dirty.right = Frame().right + kVMargin; 226 dirty.left = dirty.right - diff - kVMargin * 2; 227 fMenuField->Invalidate(dirty); 228 } else if (diff < 0) { 229 // clean up the dirty right line of 230 // the menu field when shrinking 231 dirty.left = Frame().right - kVMargin; 232 fMenuField->Invalidate(dirty); 233 } 234 } 235 236 BMenuBar::FrameResized(width, height); 237 } 238 239 240 void 241 _BMCMenuBar_::MakeFocus(bool focused) 242 { 243 if (IsFocus() == focused) 244 return; 245 246 BMenuBar::MakeFocus(focused); 247 } 248 249 250 void 251 _BMCMenuBar_::MessageReceived(BMessage* message) 252 { 253 switch (message->what) { 254 case 'TICK': 255 { 256 BMenuItem* item = ItemAt(0); 257 258 if (item != NULL && item->Submenu() != NULL 259 && item->Submenu()->Window() != NULL) { 260 BMessage message(B_KEY_DOWN); 261 262 message.AddInt8("byte", B_ESCAPE); 263 message.AddInt8("key", B_ESCAPE); 264 message.AddInt32("modifiers", 0); 265 message.AddInt8("raw_char", B_ESCAPE); 266 267 Window()->PostMessage(&message, this, NULL); 268 } 269 } 270 // fall through 271 default: 272 BMenuBar::MessageReceived(message); 273 break; 274 } 275 } 276 277 278 void 279 _BMCMenuBar_::SetMaxContentWidth(float width) 280 { 281 float left; 282 float right; 283 GetItemMargins(&left, NULL, &right, NULL); 284 285 BMenuBar::SetMaxContentWidth(width - (left + right)); 286 } 287 288 289 void 290 _BMCMenuBar_::SetEnabled(bool enabled) 291 { 292 fMenuField->SetEnabled(enabled); 293 294 BMenuBar::SetEnabled(enabled); 295 } 296 297 298 BSize 299 _BMCMenuBar_::MinSize() 300 { 301 BSize size; 302 BMenuBar::GetPreferredSize(&size.width, &size.height); 303 if (fShowPopUpMarker) { 304 // account for popup indicator + a few pixels margin 305 size.width += kPopUpIndicatorWidth; 306 } 307 308 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 309 } 310 311 312 BSize 313 _BMCMenuBar_::MaxSize() 314 { 315 // The maximum width of a normal BMenuBar is unlimited, but we want it 316 // limited. 317 BSize size; 318 BMenuBar::GetPreferredSize(&size.width, &size.height); 319 320 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 321 } 322 323 324 // #pragma mark - _BMCMenuBar_ private methods 325 326 327 void 328 _BMCMenuBar_::_Init() 329 { 330 SetFlags(Flags() | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE); 331 SetBorder(B_BORDER_CONTENTS); 332 333 float left, top, right, bottom; 334 GetItemMargins(&left, &top, &right, &bottom); 335 336 #if 0 337 // TODO: Better fix would be to make BMenuItem draw text properly 338 // centered 339 font_height fontHeight; 340 GetFontHeight(&fontHeight); 341 top = ceilf((Bounds().Height() - ceilf(fontHeight.ascent) 342 - ceilf(fontHeight.descent)) / 2) + 1; 343 bottom = top - 1; 344 #else 345 // TODO: Fix content location properly. This is just a quick fix to 346 // make the BMenuField label and the super-item of the BMenuBar 347 // align vertically. 348 top++; 349 bottom--; 350 #endif 351 352 left = right = be_control_look->DefaultLabelSpacing(); 353 354 SetItemMargins(left, top, 355 right + (fShowPopUpMarker ? kPopUpIndicatorWidth : 0), bottom); 356 } 357