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 #include <BMCPrivate.h> 11 12 #include <stdio.h> 13 14 #include <ControlLook.h> 15 #include <LayoutUtils.h> 16 #include <MenuField.h> 17 #include <MenuItem.h> 18 #include <Message.h> 19 #include <MessageRunner.h> 20 #include <Region.h> 21 #include <Window.h> 22 23 24 _BMCFilter_::_BMCFilter_(BMenuField *menuField, uint32 what) 25 : 26 BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, what), 27 fMenuField(menuField) 28 { 29 } 30 31 32 _BMCFilter_::~_BMCFilter_() 33 { 34 } 35 36 37 filter_result 38 _BMCFilter_::Filter(BMessage *message, BHandler **handler) 39 { 40 if (message->what == B_MOUSE_DOWN) { 41 if (BView *view = dynamic_cast<BView *>(*handler)) { 42 BPoint point; 43 message->FindPoint("be:view_where", &point); 44 view->ConvertToParent(&point); 45 message->ReplacePoint("be:view_where", point); 46 *handler = fMenuField; 47 } 48 } 49 50 return B_DISPATCH_MESSAGE; 51 } 52 53 54 _BMCFilter_ & 55 _BMCFilter_::operator=(const _BMCFilter_ &) 56 { 57 return *this; 58 } 59 60 61 // #pragma mark - 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 fRunner(NULL), 71 fShowPopUpMarker(true) 72 { 73 _Init(true); 74 } 75 76 77 _BMCMenuBar_::_BMCMenuBar_(bool fixedSize, BMenuField* menuField) 78 : 79 BMenuBar("_mc_mb_", B_ITEMS_IN_ROW, !fixedSize), 80 fMenuField(menuField), 81 fFixedSize(fixedSize), 82 fRunner(NULL), 83 fShowPopUpMarker(true) 84 { 85 _Init(false); 86 } 87 88 89 _BMCMenuBar_::_BMCMenuBar_(BMessage *data) 90 : BMenuBar(data), 91 fMenuField(NULL), 92 fFixedSize(true), 93 fRunner(NULL), 94 fShowPopUpMarker(true) 95 { 96 SetFlags(Flags() | B_FRAME_EVENTS); 97 98 bool resizeToFit; 99 if (data->FindBool("_rsize_to_fit", &resizeToFit) == B_OK) 100 fFixedSize = !resizeToFit; 101 } 102 103 104 _BMCMenuBar_::~_BMCMenuBar_() 105 { 106 delete fRunner; 107 } 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_PANEL_BACKGROUND_COLOR)); 137 } 138 139 140 void 141 _BMCMenuBar_::Draw(BRect updateRect) 142 { 143 if (be_control_look != NULL) { 144 BRect rect(Bounds()); 145 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 146 uint32 flags = 0; 147 if (!IsEnabled()) 148 flags |= BControlLook::B_DISABLED; 149 if (IsFocus()) 150 flags |= BControlLook::B_FOCUSED; 151 be_control_look->DrawMenuFieldBackground(this, rect, 152 updateRect, base, fShowPopUpMarker, flags); 153 154 _DrawItems(updateRect); 155 156 return; 157 } 158 159 if (!fShowPopUpMarker) { 160 BMenuBar::Draw(updateRect); 161 return; 162 } 163 164 // draw the right side with the popup marker 165 166 // prevent the original BMenuBar's Draw from 167 // drawing in those parts 168 BRect bounds(Bounds()); 169 bounds.right -= 10.0; 170 bounds.bottom -= 1.0; 171 172 BRegion clipping(bounds); 173 ConstrainClippingRegion(&clipping); 174 175 BMenuBar::Draw(updateRect); 176 177 // restore clipping 178 ConstrainClippingRegion(NULL); 179 bounds.right += 10.0; 180 bounds.bottom += 1.0; 181 182 // prepare some colors 183 rgb_color normalNoTint = LowColor(); 184 rgb_color noTint = tint_color(normalNoTint, 0.74); 185 rgb_color darken4; 186 rgb_color normalDarken4; 187 rgb_color darken1; 188 rgb_color lighten1; 189 rgb_color lighten2; 190 191 if (IsEnabled()) { 192 darken4 = tint_color(noTint, B_DARKEN_4_TINT); 193 normalDarken4 = tint_color(normalNoTint, B_DARKEN_4_TINT); 194 darken1 = tint_color(noTint, B_DARKEN_1_TINT); 195 lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT); 196 lighten2 = tint_color(noTint, B_LIGHTEN_2_TINT); 197 } else { 198 darken4 = tint_color(noTint, B_DARKEN_2_TINT); 199 normalDarken4 = tint_color(normalNoTint, B_DARKEN_2_TINT); 200 darken1 = tint_color(noTint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); 201 lighten1 = tint_color(noTint, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0); 202 lighten2 = tint_color(noTint, B_LIGHTEN_1_TINT); 203 } 204 205 BRect r(bounds); 206 r.left = r.right - 10.0; 207 208 BeginLineArray(6); 209 // bottom below item text, darker then BMenuBar 210 // would normaly draw it 211 AddLine(BPoint(bounds.left, r.bottom), 212 BPoint(r.left - 1.0, r.bottom), normalDarken4); 213 214 // bottom below popup marker 215 AddLine(BPoint(r.left, r.bottom), 216 BPoint(r.right, r.bottom), darken4); 217 // right of popup marker 218 AddLine(BPoint(r.right, r.bottom - 1), 219 BPoint(r.right, r.top), darken4); 220 // top above popup marker 221 AddLine(BPoint(r.left, r.top), 222 BPoint(r.right - 2, r.top), lighten2); 223 224 r.top += 1; 225 r.bottom -= 1; 226 r.right -= 1; 227 228 // bottom below popup marker 229 AddLine(BPoint(r.left, r.bottom), 230 BPoint(r.right, r.bottom), darken1); 231 // right of popup marker 232 AddLine(BPoint(r.right, r.bottom - 1), 233 BPoint(r.right, r.top), darken1); 234 EndLineArray(); 235 236 r.bottom -= 1; 237 r.right -= 1; 238 SetHighColor(noTint); 239 FillRect(r); 240 241 // popup marker 242 BPoint center(roundf((r.left + r.right) / 2.0), 243 roundf((r.top + r.bottom) / 2.0)); 244 BPoint triangle[3]; 245 triangle[0] = center + BPoint(-2.5, -0.5); 246 triangle[1] = center + BPoint(2.5, -0.5); 247 triangle[2] = center + BPoint(0.0, 2.0); 248 249 uint32 flags = Flags(); 250 SetFlags(flags | B_SUBPIXEL_PRECISE); 251 252 SetHighColor(normalDarken4); 253 FillTriangle(triangle[0], triangle[1], triangle[2]); 254 255 SetFlags(flags); 256 } 257 258 259 void 260 _BMCMenuBar_::FrameResized(float width, float height) 261 { 262 // we need to take care of resizing and cleaning up 263 // the parent menu field 264 float diff = width - fPreviousWidth; 265 fPreviousWidth = width; 266 267 if (Window()) { 268 if (diff > 0) { 269 // clean up the dirty right border of 270 // the menu field when enlarging 271 BRect dirty(fMenuField->Bounds()); 272 dirty.right = Frame().right + 2; 273 dirty.left = dirty.left - diff - 4; 274 fMenuField->Invalidate(dirty); 275 276 // clean up the arrow part 277 dirty = Bounds(); 278 dirty.left = dirty.right - diff - 12; 279 Invalidate(dirty); 280 281 } else if (diff < 0) { 282 // clean up the dirty right line of 283 // the menu field when shrinking 284 BRect dirty(fMenuField->Bounds()); 285 dirty.left = Frame().right - 2; 286 dirty.right = dirty.left - diff + 4; 287 fMenuField->Invalidate(dirty); 288 289 // clean up the arrow part 290 dirty = Bounds(); 291 dirty.left = dirty.right - 12; 292 Invalidate(dirty); 293 } 294 } 295 296 if (!fFixedSize && ResizingMode() == (B_FOLLOW_LEFT | B_FOLLOW_TOP)) { 297 // we have been shrinked or enlarged and need to take care 298 // of the size of the parent menu field as well 299 // NOTE: no worries about follow mode, we follow left and top 300 // in autosize mode 301 diff = Frame().right + 2 - fMenuField->Bounds().right; 302 fMenuField->ResizeBy(diff, 0.0); 303 } 304 BMenuBar::FrameResized(width, height); 305 } 306 307 308 void 309 _BMCMenuBar_::MessageReceived(BMessage *msg) 310 { 311 switch (msg->what) { 312 case 'TICK': 313 { 314 BMenuItem *item = ItemAt(0); 315 316 if (item && item->Submenu() && item->Submenu()->Window()) { 317 BMessage message(B_KEY_DOWN); 318 319 message.AddInt8("byte", B_ESCAPE); 320 message.AddInt8("key", B_ESCAPE); 321 message.AddInt32("modifiers", 0); 322 message.AddInt8("raw_char", B_ESCAPE); 323 324 Window()->PostMessage(&message, this, NULL); 325 } 326 } 327 // fall through 328 default: 329 BMenuBar::MessageReceived(msg); 330 break; 331 } 332 } 333 334 335 void 336 _BMCMenuBar_::MakeFocus(bool focused) 337 { 338 if (IsFocus() == focused) 339 return; 340 341 BMenuBar::MakeFocus(focused); 342 343 if (focused) { 344 BMessage message('TICK'); 345 //fRunner = new BMessageRunner(BMessenger(this, NULL, NULL), &message, 346 // 50000, -1); 347 } else if (fRunner) { 348 //delete fRunner; 349 fRunner = NULL; 350 } 351 352 if (focused) 353 return; 354 355 fMenuField->fSelected = false; 356 fMenuField->fTransition = true; 357 358 BRect bounds(fMenuField->Bounds()); 359 360 fMenuField->Invalidate(BRect(bounds.left, bounds.top, fMenuField->fDivider, 361 bounds.bottom)); 362 } 363 364 365 BSize 366 _BMCMenuBar_::MaxSize() 367 { 368 // The maximum width of a normal BMenuBar is unlimited, but we want it 369 // limited. 370 BSize size; 371 BMenuBar::GetPreferredSize(&size.width, &size.height); 372 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 373 } 374 375 376 _BMCMenuBar_ 377 &_BMCMenuBar_::operator=(const _BMCMenuBar_ &) 378 { 379 return *this; 380 } 381 382 383 void 384 _BMCMenuBar_::_Init(bool setMaxContentWidth) 385 { 386 SetFlags(Flags() | B_FRAME_EVENTS); 387 SetBorder(B_BORDER_CONTENTS); 388 389 float left, top, right, bottom; 390 GetItemMargins(&left, &top, &right, &bottom); 391 392 #if 0 393 // TODO: Better fix would be to make BMenuItem draw text properly 394 // centered 395 font_height fontHeight; 396 GetFontHeight(&fontHeight); 397 top = ceilf((Bounds().Height() - ceilf(fontHeight.ascent) 398 - ceilf(fontHeight.descent)) / 2) + 1; 399 bottom = top - 1; 400 #else 401 // TODO: Fix content location properly. This is just a quick fix to 402 // make the BMenuField label and the super-item of the BMenuBar 403 // align vertically. 404 top++; 405 bottom--; 406 #endif 407 408 if (be_control_look) 409 left = right = be_control_look->DefaultLabelSpacing(); 410 411 SetItemMargins(left, top, right + fShowPopUpMarker ? 10 : 0, bottom); 412 413 fPreviousWidth = Bounds().Width(); 414 415 if (setMaxContentWidth) 416 SetMaxContentWidth(fPreviousWidth - (left + right)); 417 } 418 419