1 /* 2 * Copyright 2001-2009 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Stephan Aßmus <superstippi@gmx.de> 8 */ 9 10 11 #include <BMCPrivate.h> 12 13 #include <stdio.h> 14 15 #include <ControlLook.h> 16 #include <LayoutUtils.h> 17 #include <MenuField.h> 18 #include <MenuItem.h> 19 #include <Message.h> 20 #include <MessageRunner.h> 21 #include <Region.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 _BMCMenuBar_::_BMCMenuBar_(BRect frame, bool fixedSize, BMenuField* menuField) 59 : 60 BMenuBar(frame, "_mc_mb_", B_FOLLOW_LEFT | B_FOLLOW_TOP, B_ITEMS_IN_ROW, 61 !fixedSize), 62 fMenuField(menuField), 63 fFixedSize(fixedSize), 64 fRunner(NULL), 65 fShowPopUpMarker(true) 66 { 67 _Init(true); 68 } 69 70 71 _BMCMenuBar_::_BMCMenuBar_(bool fixedSize, BMenuField* menuField) 72 : 73 BMenuBar("_mc_mb_", B_ITEMS_IN_ROW, !fixedSize), 74 fMenuField(menuField), 75 fFixedSize(fixedSize), 76 fRunner(NULL), 77 fShowPopUpMarker(true) 78 { 79 _Init(false); 80 } 81 82 83 _BMCMenuBar_::_BMCMenuBar_(BMessage* data) 84 : BMenuBar(data), 85 fMenuField(NULL), 86 fFixedSize(true), 87 fRunner(NULL), 88 fShowPopUpMarker(true) 89 { 90 SetFlags(Flags() | B_FRAME_EVENTS); 91 92 bool resizeToFit; 93 if (data->FindBool("_rsize_to_fit", &resizeToFit) == B_OK) 94 fFixedSize = !resizeToFit; 95 } 96 97 98 _BMCMenuBar_::~_BMCMenuBar_() 99 { 100 delete fRunner; 101 } 102 103 104 BArchivable* 105 _BMCMenuBar_::Instantiate(BMessage* data) 106 { 107 if (validate_instantiation(data, "_BMCMenuBar_")) 108 return new _BMCMenuBar_(data); 109 110 return NULL; 111 } 112 113 114 void 115 _BMCMenuBar_::AttachedToWindow() 116 { 117 fMenuField = static_cast<BMenuField*>(Parent()); 118 119 // Don't cause the KeyMenuBar to change by being attached 120 BMenuBar* menuBar = Window()->KeyMenuBar(); 121 BMenuBar::AttachedToWindow(); 122 Window()->SetKeyMenuBar(menuBar); 123 124 if (fFixedSize && (Flags() & B_SUPPORTS_LAYOUT) == 0) 125 SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); 126 127 if (Parent() != NULL) 128 SetLowColor(Parent()->LowColor()); 129 else 130 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 131 } 132 133 134 void 135 _BMCMenuBar_::Draw(BRect updateRect) 136 { 137 if (be_control_look != NULL) { 138 BRect rect(Bounds()); 139 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 140 uint32 flags = 0; 141 if (!IsEnabled()) 142 flags |= BControlLook::B_DISABLED; 143 if (IsFocus()) 144 flags |= BControlLook::B_FOCUSED; 145 be_control_look->DrawMenuFieldBackground(this, rect, 146 updateRect, base, fShowPopUpMarker, flags); 147 148 _DrawItems(updateRect); 149 150 return; 151 } 152 153 if (!fShowPopUpMarker) { 154 BMenuBar::Draw(updateRect); 155 return; 156 } 157 158 // draw the right side with the popup marker 159 160 // prevent the original BMenuBar's Draw from 161 // drawing in those parts 162 BRect bounds(Bounds()); 163 bounds.right -= 10.0; 164 bounds.bottom -= 1.0; 165 166 BRegion clipping(bounds); 167 ConstrainClippingRegion(&clipping); 168 169 BMenuBar::Draw(updateRect); 170 171 // restore clipping 172 ConstrainClippingRegion(NULL); 173 bounds.right += 10.0; 174 bounds.bottom += 1.0; 175 176 // prepare some colors 177 rgb_color normalNoTint = LowColor(); 178 rgb_color noTint = tint_color(normalNoTint, 0.74); 179 rgb_color darken4; 180 rgb_color normalDarken4; 181 rgb_color darken1; 182 rgb_color lighten1; 183 rgb_color lighten2; 184 185 if (IsEnabled()) { 186 darken4 = tint_color(noTint, B_DARKEN_4_TINT); 187 normalDarken4 = tint_color(normalNoTint, B_DARKEN_4_TINT); 188 darken1 = tint_color(noTint, B_DARKEN_1_TINT); 189 lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT); 190 lighten2 = tint_color(noTint, B_LIGHTEN_2_TINT); 191 } else { 192 darken4 = tint_color(noTint, B_DARKEN_2_TINT); 193 normalDarken4 = tint_color(normalNoTint, B_DARKEN_2_TINT); 194 darken1 = tint_color(noTint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); 195 lighten1 = tint_color(noTint, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0); 196 lighten2 = tint_color(noTint, B_LIGHTEN_1_TINT); 197 } 198 199 BRect r(bounds); 200 r.left = r.right - 10.0; 201 202 BeginLineArray(6); 203 // bottom below item text, darker then BMenuBar 204 // would normaly draw it 205 AddLine(BPoint(bounds.left, r.bottom), 206 BPoint(r.left - 1.0, r.bottom), normalDarken4); 207 208 // bottom below popup marker 209 AddLine(BPoint(r.left, r.bottom), 210 BPoint(r.right, r.bottom), darken4); 211 // right of popup marker 212 AddLine(BPoint(r.right, r.bottom - 1), 213 BPoint(r.right, r.top), darken4); 214 // top above popup marker 215 AddLine(BPoint(r.left, r.top), 216 BPoint(r.right - 2, r.top), lighten2); 217 218 r.top += 1; 219 r.bottom -= 1; 220 r.right -= 1; 221 222 // bottom below popup marker 223 AddLine(BPoint(r.left, r.bottom), 224 BPoint(r.right, r.bottom), darken1); 225 // right of popup marker 226 AddLine(BPoint(r.right, r.bottom - 1), 227 BPoint(r.right, r.top), darken1); 228 EndLineArray(); 229 230 r.bottom -= 1; 231 r.right -= 1; 232 SetHighColor(noTint); 233 FillRect(r); 234 235 // popup marker 236 BPoint center(roundf((r.left + r.right) / 2.0), 237 roundf((r.top + r.bottom) / 2.0)); 238 BPoint triangle[3]; 239 triangle[0] = center + BPoint(-2.5, -0.5); 240 triangle[1] = center + BPoint(2.5, -0.5); 241 triangle[2] = center + BPoint(0.0, 2.0); 242 243 uint32 flags = Flags(); 244 SetFlags(flags | B_SUBPIXEL_PRECISE); 245 246 SetHighColor(normalDarken4); 247 FillTriangle(triangle[0], triangle[1], triangle[2]); 248 249 SetFlags(flags); 250 } 251 252 253 void 254 _BMCMenuBar_::FrameResized(float width, float height) 255 { 256 // we need to take care of resizing and cleaning up 257 // the parent menu field 258 float diff = width - fPreviousWidth; 259 fPreviousWidth = width; 260 261 if (Window()) { 262 if (diff > 0) { 263 // clean up the dirty right border of 264 // the menu field when enlarging 265 BRect dirty(fMenuField->Bounds()); 266 dirty.right = Frame().right + 2; 267 dirty.left = dirty.left - diff - 4; 268 fMenuField->Invalidate(dirty); 269 270 // clean up the arrow part 271 dirty = Bounds(); 272 dirty.left = dirty.right - diff - 12; 273 Invalidate(dirty); 274 275 } else if (diff < 0) { 276 // clean up the dirty right line of 277 // the menu field when shrinking 278 BRect dirty(fMenuField->Bounds()); 279 dirty.left = Frame().right - 2; 280 dirty.right = dirty.left - diff + 4; 281 fMenuField->Invalidate(dirty); 282 283 // clean up the arrow part 284 dirty = Bounds(); 285 dirty.left = dirty.right - 12; 286 Invalidate(dirty); 287 } 288 } 289 290 if (!fFixedSize && ResizingMode() == (B_FOLLOW_LEFT | B_FOLLOW_TOP)) { 291 // we have been shrinked or enlarged and need to take care 292 // of the size of the parent menu field as well 293 // NOTE: no worries about follow mode, we follow left and top 294 // in autosize mode 295 diff = Frame().right + 2 - fMenuField->Bounds().right; 296 fMenuField->ResizeBy(diff, 0.0); 297 } 298 BMenuBar::FrameResized(width, height); 299 } 300 301 302 void 303 _BMCMenuBar_::MessageReceived(BMessage* msg) 304 { 305 switch (msg->what) { 306 case 'TICK': 307 { 308 BMenuItem* item = ItemAt(0); 309 310 if (item && item->Submenu() && item->Submenu()->Window()) { 311 BMessage message(B_KEY_DOWN); 312 313 message.AddInt8("byte", B_ESCAPE); 314 message.AddInt8("key", B_ESCAPE); 315 message.AddInt32("modifiers", 0); 316 message.AddInt8("raw_char", B_ESCAPE); 317 318 Window()->PostMessage(&message, this, NULL); 319 } 320 } 321 // fall through 322 default: 323 BMenuBar::MessageReceived(msg); 324 break; 325 } 326 } 327 328 329 void 330 _BMCMenuBar_::MakeFocus(bool focused) 331 { 332 if (IsFocus() == focused) 333 return; 334 335 BMenuBar::MakeFocus(focused); 336 337 if (focused) { 338 BMessage message('TICK'); 339 //fRunner = new BMessageRunner(BMessenger(this, NULL, NULL), &message, 340 // 50000, -1); 341 } else if (fRunner) { 342 //delete fRunner; 343 fRunner = NULL; 344 } 345 346 if (focused) 347 return; 348 349 fMenuField->fSelected = false; 350 fMenuField->fTransition = true; 351 352 BRect bounds(fMenuField->Bounds()); 353 354 fMenuField->Invalidate(BRect(bounds.left, bounds.top, fMenuField->fDivider, 355 bounds.bottom)); 356 } 357 358 359 BSize 360 _BMCMenuBar_::MinSize() 361 { 362 BSize size; 363 BMenuBar::GetPreferredSize(&size.width, &size.height); 364 365 if (fShowPopUpMarker) { 366 // account for popup indicator + a few pixels margin 367 size.width += 13.0; 368 } 369 370 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 371 } 372 373 374 BSize 375 _BMCMenuBar_::MaxSize() 376 { 377 // The maximum width of a normal BMenuBar is unlimited, but we want it 378 // limited. 379 BSize size; 380 BMenuBar::GetPreferredSize(&size.width, &size.height); 381 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 382 } 383 384 385 void 386 _BMCMenuBar_::_Init(bool setMaxContentWidth) 387 { 388 SetFlags(Flags() | B_FRAME_EVENTS); 389 SetBorder(B_BORDER_CONTENTS); 390 391 float left, top, right, bottom; 392 GetItemMargins(&left, &top, &right, &bottom); 393 394 #if 0 395 // TODO: Better fix would be to make BMenuItem draw text properly 396 // centered 397 font_height fontHeight; 398 GetFontHeight(&fontHeight); 399 top = ceilf((Bounds().Height() - ceilf(fontHeight.ascent) 400 - ceilf(fontHeight.descent)) / 2) + 1; 401 bottom = top - 1; 402 #else 403 // TODO: Fix content location properly. This is just a quick fix to 404 // make the BMenuField label and the super-item of the BMenuBar 405 // align vertically. 406 top++; 407 bottom--; 408 #endif 409 410 if (be_control_look) 411 left = right = be_control_look->DefaultLabelSpacing(); 412 413 SetItemMargins(left, top, right + fShowPopUpMarker ? 10 : 0, bottom); 414 415 fPreviousWidth = Bounds().Width(); 416 417 if (setMaxContentWidth) 418 SetMaxContentWidth(fPreviousWidth - (left + right)); 419 } 420 421