1 /* 2 * Copyright 2001-2006, Haiku Inc. 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 <stdio.h> 11 12 #include <BMCPrivate.h> 13 #include <LayoutUtils.h> 14 #include <MenuField.h> 15 #include <MenuItem.h> 16 #include <Message.h> 17 #include <MessageRunner.h> 18 #include <Region.h> 19 #include <Window.h> 20 21 22 _BMCFilter_::_BMCFilter_(BMenuField *menuField, uint32 what) 23 : 24 BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, what), 25 fMenuField(menuField) 26 { 27 } 28 29 30 _BMCFilter_::~_BMCFilter_() 31 { 32 } 33 34 35 filter_result 36 _BMCFilter_::Filter(BMessage *message, BHandler **handler) 37 { 38 if (message->what == B_MOUSE_DOWN) { 39 if (BView *view = dynamic_cast<BView *>(*handler)) { 40 BPoint point; 41 message->FindPoint("be:view_where", &point); 42 view->ConvertToParent(&point); 43 message->ReplacePoint("be:view_where", point); 44 *handler = fMenuField; 45 } 46 } 47 48 return B_DISPATCH_MESSAGE; 49 } 50 51 52 _BMCFilter_ & 53 _BMCFilter_::operator=(const _BMCFilter_ &) 54 { 55 return *this; 56 } 57 58 59 _BMCMenuBar_::_BMCMenuBar_(BRect frame, bool fixedSize, BMenuField *menuField) 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 SetFlags(Flags() | B_FRAME_EVENTS); 68 SetBorder(B_BORDER_CONTENTS); 69 70 float left, top, right, bottom; 71 GetItemMargins(&left, &top, &right, &bottom); 72 // give a bit more space to draw the small thumb 73 left -= 1; 74 right += 3; 75 SetItemMargins(left, top, right, bottom); 76 77 SetMaxContentWidth(frame.Width() - (left + right)); 78 79 fPreviousWidth = frame.Width(); 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 if (fFixedSize) 123 SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); 124 Window()->SetKeyMenuBar(menuBar); 125 } 126 127 128 void 129 _BMCMenuBar_::Draw(BRect updateRect) 130 { 131 if (!fShowPopUpMarker) { 132 BMenuBar::Draw(updateRect); 133 return; 134 } 135 136 // draw the right side with the popup marker 137 138 // prevent the original BMenuBar's Draw from 139 // drawing in those parts 140 BRect bounds(Bounds()); 141 bounds.right -= 10.0; 142 bounds.bottom -= 1.0; 143 144 BRegion clipping(bounds); 145 ConstrainClippingRegion(&clipping); 146 147 BMenuBar::Draw(updateRect); 148 149 // restore clipping 150 ConstrainClippingRegion(NULL); 151 bounds.right += 10.0; 152 bounds.bottom += 1.0; 153 154 // prepare some colors 155 rgb_color normalNoTint = LowColor(); 156 rgb_color noTint = tint_color(normalNoTint, 0.74); 157 rgb_color darken4; 158 rgb_color normalDarken4; 159 rgb_color darken1; 160 rgb_color lighten1; 161 rgb_color lighten2; 162 163 if (IsEnabled()) { 164 darken4 = tint_color(noTint, B_DARKEN_4_TINT); 165 normalDarken4 = tint_color(normalNoTint, B_DARKEN_4_TINT); 166 darken1 = tint_color(noTint, B_DARKEN_1_TINT); 167 lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT); 168 lighten2 = tint_color(noTint, B_LIGHTEN_2_TINT); 169 } else { 170 darken4 = tint_color(noTint, B_DARKEN_2_TINT); 171 normalDarken4 = tint_color(normalNoTint, B_DARKEN_2_TINT); 172 darken1 = tint_color(noTint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); 173 lighten1 = tint_color(noTint, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0); 174 lighten2 = tint_color(noTint, B_LIGHTEN_1_TINT); 175 } 176 177 BRect r(bounds); 178 r.left = r.right - 10.0; 179 180 BeginLineArray(6); 181 // bottom below item text, darker then BMenuBar 182 // would normaly draw it 183 AddLine(BPoint(bounds.left, r.bottom), 184 BPoint(r.left - 1.0, r.bottom), normalDarken4); 185 186 // bottom below popup marker 187 AddLine(BPoint(r.left, r.bottom), 188 BPoint(r.right, r.bottom), darken4); 189 // right of popup marker 190 AddLine(BPoint(r.right, r.bottom - 1), 191 BPoint(r.right, r.top), darken4); 192 // top above popup marker 193 AddLine(BPoint(r.left, r.top), 194 BPoint(r.right - 2, r.top), lighten2); 195 196 r.top += 1; 197 r.bottom -= 1; 198 r.right -= 1; 199 200 // bottom below popup marker 201 AddLine(BPoint(r.left, r.bottom), 202 BPoint(r.right, r.bottom), darken1); 203 // right of popup marker 204 AddLine(BPoint(r.right, r.bottom - 1), 205 BPoint(r.right, r.top), darken1); 206 EndLineArray(); 207 208 r.bottom -= 1; 209 r.right -= 1; 210 SetHighColor(noTint); 211 FillRect(r); 212 213 // popup marker 214 BPoint center(roundf((r.left + r.right) / 2.0), 215 roundf((r.top + r.bottom) / 2.0)); 216 BPoint triangle[3]; 217 triangle[0] = center + BPoint(-2.5, -0.5); 218 triangle[1] = center + BPoint(2.5, -0.5); 219 triangle[2] = center + BPoint(0.0, 2.0); 220 221 uint32 flags = Flags(); 222 SetFlags(flags | B_SUBPIXEL_PRECISE); 223 224 SetHighColor(normalDarken4); 225 FillTriangle(triangle[0], triangle[1], triangle[2]); 226 227 SetFlags(flags); 228 } 229 230 231 void 232 _BMCMenuBar_::FrameResized(float width, float height) 233 { 234 // we need to take care of resizing and cleaning up 235 // the parent menu field 236 float diff; 237 if (fFixedSize) 238 diff = width - fPreviousWidth; 239 else 240 diff = Frame().right - (fMenuField->Bounds().right - 2); 241 242 fPreviousWidth = width; 243 244 if (Window()) { 245 if (diff > 0) { 246 // clean up the dirty right border of 247 // the menu field when enlarging 248 BRect dirty(fMenuField->Bounds()); 249 dirty.left = dirty.right - diff - 2; 250 fMenuField->Invalidate(dirty); 251 252 // clean up the arrow part 253 dirty = Bounds(); 254 dirty.left = dirty.right - diff - 12; 255 Invalidate(dirty); 256 257 } else if (diff < 0) { 258 // clean up the dirty right line of 259 // the menu field when shrinking 260 BRect dirty(fMenuField->Bounds()); 261 dirty.left = dirty.right - 2; 262 fMenuField->Invalidate(dirty); 263 264 // clean up the arrow part 265 dirty = Bounds(); 266 dirty.left = dirty.right - 12; 267 Invalidate(dirty); 268 } 269 } 270 271 if (!fFixedSize && ResizingMode() == (B_FOLLOW_LEFT | B_FOLLOW_TOP)) { 272 // we have been shrinked or enlarged and need to take care 273 // of the size of the parent menu field as well 274 // NOTE: no worries about follow mode, we follow left and top 275 // in autosize mode 276 fMenuField->ResizeBy(diff, 0.0); 277 } 278 BMenuBar::FrameResized(width, height); 279 } 280 281 282 void 283 _BMCMenuBar_::MessageReceived(BMessage *msg) 284 { 285 switch (msg->what) { 286 case 'TICK': 287 { 288 BMenuItem *item = ItemAt(0); 289 290 if (item && item->Submenu() && item->Submenu()->Window()) { 291 BMessage message(B_KEY_DOWN); 292 293 message.AddInt8("byte", B_ESCAPE); 294 message.AddInt8("key", B_ESCAPE); 295 message.AddInt32("modifiers", 0); 296 message.AddInt8("raw_char", B_ESCAPE); 297 298 Window()->PostMessage(&message, this, NULL); 299 } 300 } 301 // fall through 302 default: 303 BMenuBar::MessageReceived(msg); 304 break; 305 } 306 } 307 308 309 void 310 _BMCMenuBar_::MakeFocus(bool focused) 311 { 312 if (IsFocus() == focused) 313 return; 314 315 BMenuBar::MakeFocus(focused); 316 317 if (focused) { 318 BMessage message('TICK'); 319 //fRunner = new BMessageRunner(BMessenger(this, NULL, NULL), &message, 320 // 50000, -1); 321 } else if (fRunner) { 322 //delete fRunner; 323 fRunner = NULL; 324 } 325 326 if (focused) 327 return; 328 329 fMenuField->fSelected = false; 330 fMenuField->fTransition = true; 331 332 BRect bounds(fMenuField->Bounds()); 333 334 fMenuField->Invalidate(BRect(bounds.left, bounds.top, fMenuField->fDivider, 335 bounds.bottom)); 336 } 337 338 339 BSize 340 _BMCMenuBar_::MaxSize() 341 { 342 // The maximum width of a normal BMenuBar is unlimited, but we want it 343 // limited. 344 BSize size; 345 BMenuBar::GetPreferredSize(&size.width, &size.height); 346 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 347 } 348 349 350 _BMCMenuBar_ 351 &_BMCMenuBar_::operator=(const _BMCMenuBar_ &) 352 { 353 return *this; 354 } 355