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 : BMenuItem(data) 43 { 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 && item->Submenu() && item->Submenu()->Window()) { 259 BMessage message(B_KEY_DOWN); 260 261 message.AddInt8("byte", B_ESCAPE); 262 message.AddInt8("key", B_ESCAPE); 263 message.AddInt32("modifiers", 0); 264 message.AddInt8("raw_char", B_ESCAPE); 265 266 Window()->PostMessage(&message, this, NULL); 267 } 268 } 269 // fall through 270 default: 271 BMenuBar::MessageReceived(message); 272 break; 273 } 274 } 275 276 277 void 278 _BMCMenuBar_::SetMaxContentWidth(float width) 279 { 280 float left; 281 float right; 282 GetItemMargins(&left, NULL, &right, NULL); 283 284 BMenuBar::SetMaxContentWidth(width - (left + right)); 285 } 286 287 288 void 289 _BMCMenuBar_::SetEnabled(bool enabled) 290 { 291 fMenuField->SetEnabled(enabled); 292 293 BMenuBar::SetEnabled(enabled); 294 } 295 296 297 BSize 298 _BMCMenuBar_::MinSize() 299 { 300 BSize size; 301 BMenuBar::GetPreferredSize(&size.width, &size.height); 302 if (fShowPopUpMarker) { 303 // account for popup indicator + a few pixels margin 304 size.width += kPopUpIndicatorWidth; 305 } 306 307 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 308 } 309 310 311 BSize 312 _BMCMenuBar_::MaxSize() 313 { 314 // The maximum width of a normal BMenuBar is unlimited, but we want it 315 // limited. 316 BSize size; 317 BMenuBar::GetPreferredSize(&size.width, &size.height); 318 319 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 320 } 321 322 323 // #pragma mark - _BMCMenuBar_ private methods 324 325 326 void 327 _BMCMenuBar_::_Init() 328 { 329 SetFlags(Flags() | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE); 330 SetBorder(B_BORDER_CONTENTS); 331 332 float left, top, right, bottom; 333 GetItemMargins(&left, &top, &right, &bottom); 334 335 #if 0 336 // TODO: Better fix would be to make BMenuItem draw text properly 337 // centered 338 font_height fontHeight; 339 GetFontHeight(&fontHeight); 340 top = ceilf((Bounds().Height() - ceilf(fontHeight.ascent) 341 - ceilf(fontHeight.descent)) / 2) + 1; 342 bottom = top - 1; 343 #else 344 // TODO: Fix content location properly. This is just a quick fix to 345 // make the BMenuField label and the super-item of the BMenuBar 346 // align vertically. 347 top++; 348 bottom--; 349 #endif 350 351 if (be_control_look != NULL) 352 left = right = be_control_look->DefaultLabelSpacing(); 353 354 SetItemMargins(left, top, 355 right + fShowPopUpMarker ? kPopUpIndicatorWidth : 0, bottom); 356 } 357