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 <MenuField.h> 14 #include <MenuItem.h> 15 #include <Message.h> 16 #include <Region.h> 17 #include <Window.h> 18 19 20 _BMCFilter_::_BMCFilter_(BMenuField *menuField, uint32 what) 21 : 22 BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, what), 23 fMenuField(menuField) 24 { 25 } 26 27 28 _BMCFilter_::~_BMCFilter_() 29 { 30 } 31 32 33 filter_result 34 _BMCFilter_::Filter(BMessage *message, BHandler **handler) 35 { 36 if (message->what == B_MOUSE_DOWN) { 37 if (BView *view = dynamic_cast<BView *>(*handler)) { 38 BPoint point; 39 message->FindPoint("be:view_where", &point); 40 view->ConvertToParent(&point); 41 message->ReplacePoint("be:view_where", point); 42 *handler = fMenuField; 43 } 44 } 45 46 return B_DISPATCH_MESSAGE; 47 } 48 49 50 _BMCFilter_ & 51 _BMCFilter_::operator=(const _BMCFilter_ &) 52 { 53 return *this; 54 } 55 56 57 _BMCMenuBar_::_BMCMenuBar_(BRect frame, bool fixed_size, BMenuField *menuField) 58 : BMenuBar(frame, "_mc_mb_", B_FOLLOW_LEFT | B_FOLLOW_TOP, B_ITEMS_IN_ROW, 59 !fixed_size) 60 { 61 SetFlags(Flags() | B_FRAME_EVENTS); 62 SetBorder(B_BORDER_CONTENTS); 63 64 fMenuField = menuField; 65 fFixedSize = fixed_size; 66 fRunner = NULL; 67 fShowPopUpMarker = true; 68 69 float left, top, right, bottom; 70 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 80 81 _BMCMenuBar_::_BMCMenuBar_(BMessage *data) 82 : BMenuBar(data) 83 { 84 SetFlags(Flags() | B_FRAME_EVENTS); 85 86 bool rsize_to_fit; 87 88 if (data->FindBool("_rsize_to_fit", &rsize_to_fit) == B_OK) 89 fFixedSize = !rsize_to_fit; 90 else 91 fFixedSize = true; 92 } 93 94 95 _BMCMenuBar_::~_BMCMenuBar_() 96 { 97 } 98 99 100 BArchivable * 101 _BMCMenuBar_::Instantiate(BMessage *data) 102 { 103 if (validate_instantiation(data, "_BMCMenuBar_")) 104 return new _BMCMenuBar_(data); 105 106 return NULL; 107 } 108 109 110 void 111 _BMCMenuBar_::AttachedToWindow() 112 { 113 fMenuField = static_cast<BMenuField *>(Parent()); 114 115 BMenuBar *menuBar = Window()->KeyMenuBar(); 116 BMenuBar::AttachedToWindow(); 117 Window()->SetKeyMenuBar(menuBar); 118 } 119 120 121 void 122 _BMCMenuBar_::Draw(BRect updateRect) 123 { 124 if (!fShowPopUpMarker) { 125 BMenuBar::Draw(updateRect); 126 return; 127 } 128 129 // draw the right side with the popup marker 130 131 // prevent the original BMenuBar's Draw from 132 // drawing in those parts 133 BRect bounds(Bounds()); 134 bounds.right -= 10.0; 135 bounds.bottom -= 1.0; 136 137 BRegion clipping(bounds); 138 ConstrainClippingRegion(&clipping); 139 140 BMenuBar::Draw(updateRect); 141 142 // restore clipping 143 ConstrainClippingRegion(NULL); 144 bounds.right += 10.0; 145 bounds.bottom += 1.0; 146 147 // prepare some colors 148 rgb_color normalNoTint = ui_color(B_MENU_BACKGROUND_COLOR); 149 rgb_color noTint = tint_color(normalNoTint, 0.74); 150 rgb_color darken4; 151 rgb_color normalDarken4; 152 rgb_color darken1; 153 rgb_color lighten1; 154 rgb_color lighten2; 155 156 if (IsEnabled()) { 157 darken4 = tint_color(noTint, B_DARKEN_4_TINT); 158 normalDarken4 = tint_color(normalNoTint, B_DARKEN_4_TINT); 159 darken1 = tint_color(noTint, B_DARKEN_1_TINT); 160 lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT); 161 lighten2 = tint_color(noTint, B_LIGHTEN_2_TINT); 162 } else { 163 darken4 = tint_color(noTint, B_DARKEN_2_TINT); 164 normalDarken4 = tint_color(normalNoTint, B_DARKEN_2_TINT); 165 darken1 = tint_color(noTint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); 166 lighten1 = tint_color(noTint, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0); 167 lighten2 = tint_color(noTint, B_LIGHTEN_1_TINT); 168 } 169 170 BRect r(bounds); 171 r.left = r.right - 10.0; 172 173 BeginLineArray(6); 174 // bottom below item text, darker then BMenuBar 175 // would normaly draw it 176 AddLine(BPoint(bounds.left, r.bottom), 177 BPoint(r.left - 1.0, r.bottom), normalDarken4); 178 179 // bottom below popup marker 180 AddLine(BPoint(r.left, r.bottom), 181 BPoint(r.right, r.bottom), darken4); 182 // right of popup marker 183 AddLine(BPoint(r.right, r.bottom - 1), 184 BPoint(r.right, r.top), darken4); 185 // top above popup marker 186 AddLine(BPoint(r.left, r.top), 187 BPoint(r.right - 2, r.top), lighten2); 188 189 r.top += 1; 190 r.bottom -= 1; 191 r.right -= 1; 192 193 // bottom below popup marker 194 AddLine(BPoint(r.left, r.bottom), 195 BPoint(r.right, r.bottom), darken1); 196 // right of popup marker 197 AddLine(BPoint(r.right, r.bottom - 1), 198 BPoint(r.right, r.top), darken1); 199 EndLineArray(); 200 201 r.bottom -= 1; 202 r.right -= 1; 203 SetHighColor(noTint); 204 FillRect(r); 205 206 // popup marker 207 BPoint center(roundf((r.left + r.right) / 2.0), 208 roundf((r.top + r.bottom) / 2.0)); 209 BPoint triangle[3]; 210 triangle[0] = center + BPoint(-2.5, -0.5); 211 triangle[1] = center + BPoint(2.5, -0.5); 212 triangle[2] = center + BPoint(0.0, 2.0); 213 214 uint32 flags = Flags(); 215 SetFlags(flags | B_SUBPIXEL_PRECISE); 216 217 SetHighColor(normalDarken4); 218 FillTriangle(triangle[0], triangle[1], triangle[2]); 219 220 SetFlags(flags); 221 } 222 223 224 void 225 _BMCMenuBar_::FrameResized(float width, float height) 226 { 227 // we need to take care of resizing and cleaning up 228 // the parent menu field 229 float diff = Frame().right - fMenuField->Bounds().right; 230 if (Window()) { 231 if (diff > 0) { 232 // clean up the dirty right border of 233 // the menu field when enlarging 234 BRect dirty(fMenuField->Bounds()); 235 dirty.left = dirty.right - 2; 236 fMenuField->Invalidate(dirty); 237 238 // clean up the arrow part 239 dirty = Bounds(); 240 dirty.right -= diff; 241 dirty.left = dirty.right - 12; 242 Invalidate(dirty); 243 244 } else if (diff < 0) { 245 // clean up the dirty right line of 246 // the menu field when shrinking 247 BRect dirty(fMenuField->Bounds()); 248 dirty.left = dirty.right + diff + 1; 249 dirty.right = dirty.left + 1; 250 fMenuField->Invalidate(dirty); 251 252 // clean up the arrow part 253 dirty = Bounds(); 254 dirty.left = dirty.right - 12; 255 Invalidate(dirty); 256 } 257 } 258 259 // we have been shrinked or enlarged and need to take 260 // of the size of the parent menu field as well 261 // NOTE: no worries about follow mode, we follow left and top 262 fMenuField->ResizeBy(diff + 2, 0.0); 263 BMenuBar::FrameResized(width, height); 264 } 265 266 267 void 268 _BMCMenuBar_::MessageReceived(BMessage *msg) 269 { 270 switch (msg->what) { 271 case 'TICK': 272 { 273 BMenuItem *item = ItemAt(0); 274 275 if (item && item->Submenu() && item->Submenu()->Window()) { 276 BMessage message(B_KEY_DOWN); 277 278 message.AddInt8("byte", B_ESCAPE); 279 message.AddInt8("key", B_ESCAPE); 280 message.AddInt32("modifiers", 0); 281 message.AddInt8("raw_char", B_ESCAPE); 282 283 Window()->PostMessage(&message, this, NULL); 284 } 285 } 286 // fall through 287 default: 288 BMenuBar::MessageReceived(msg); 289 break; 290 } 291 } 292 293 294 void 295 _BMCMenuBar_::MakeFocus(bool focused) 296 { 297 if (IsFocus() == focused) 298 return; 299 300 BMenuBar::MakeFocus(focused); 301 302 if (focused) { 303 BMessage message('TICK'); 304 //fRunner = new BMessageRunner(BMessenger(this, NULL, NULL), &message, 305 // 50000, -1); 306 } else if (fRunner) { 307 //delete fRunner; 308 fRunner = NULL; 309 } 310 311 if (focused) 312 return; 313 314 fMenuField->fSelected = false; 315 fMenuField->fTransition = true; 316 317 BRect bounds(fMenuField->Bounds()); 318 319 fMenuField->Invalidate(BRect(bounds.left, bounds.top, fMenuField->fDivider, 320 bounds.bottom)); 321 } 322 323 324 _BMCMenuBar_ 325 &_BMCMenuBar_::operator=(const _BMCMenuBar_ &) 326 { 327 return *this; 328 } 329