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 */ 8 9 /*! BControl is the base class for user-event handling objects. */ 10 11 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <Control.h> 16 #include <PropertyInfo.h> 17 #include <Window.h> 18 19 #include <binary_compatibility/Interface.h> 20 21 22 static property_info sPropertyList[] = { 23 { 24 "Enabled", 25 { B_GET_PROPERTY, B_SET_PROPERTY }, 26 { B_DIRECT_SPECIFIER }, 27 NULL, 0, 28 { B_BOOL_TYPE } 29 }, 30 { 31 "Label", 32 { B_GET_PROPERTY, B_SET_PROPERTY }, 33 { B_DIRECT_SPECIFIER }, 34 NULL, 0, 35 { B_STRING_TYPE } 36 }, 37 { 38 "Value", 39 { B_GET_PROPERTY, B_SET_PROPERTY }, 40 { B_DIRECT_SPECIFIER }, 41 NULL, 0, 42 { B_INT32_TYPE } 43 }, 44 {} 45 }; 46 47 48 BControl::BControl(BRect frame, const char *name, const char *label, 49 BMessage *message, uint32 resizingMode, uint32 flags) 50 : BView(frame, name, resizingMode, flags) 51 { 52 InitData(NULL); 53 54 SetLabel(label); 55 SetMessage(message); 56 } 57 58 59 BControl::BControl(const char *name, const char *label, BMessage *message, 60 uint32 flags) 61 : BView(name, flags) 62 { 63 InitData(NULL); 64 65 SetLabel(label); 66 SetMessage(message); 67 } 68 69 70 BControl::~BControl() 71 { 72 free(fLabel); 73 SetMessage(NULL); 74 } 75 76 77 BControl::BControl(BMessage *archive) 78 : BView(archive) 79 { 80 InitData(archive); 81 82 BMessage message; 83 if (archive->FindMessage("_msg", &message) == B_OK) 84 SetMessage(new BMessage(message)); 85 86 const char *label; 87 if (archive->FindString("_label", &label) == B_OK) 88 SetLabel(label); 89 90 int32 value; 91 if (archive->FindInt32("_val", &value) == B_OK) 92 SetValue(value); 93 94 bool toggle; 95 if (archive->FindBool("_disable", &toggle) == B_OK) 96 SetEnabled(!toggle); 97 98 if (archive->FindBool("be:wants_nav", &toggle) == B_OK) 99 fWantsNav = toggle; 100 } 101 102 103 BArchivable * 104 BControl::Instantiate(BMessage *archive) 105 { 106 if (validate_instantiation(archive, "BControl")) 107 return new BControl(archive); 108 109 return NULL; 110 } 111 112 113 status_t 114 BControl::Archive(BMessage *archive, bool deep) const 115 { 116 status_t status = BView::Archive(archive, deep); 117 118 if (status == B_OK && Message()) 119 status = archive->AddMessage("_msg", Message()); 120 121 if (status == B_OK && fLabel) 122 status = archive->AddString("_label", fLabel); 123 124 if (status == B_OK && fValue != B_CONTROL_OFF) 125 status = archive->AddInt32("_val", fValue); 126 127 if (status == B_OK && !fEnabled) 128 status = archive->AddBool("_disable", true); 129 130 return status; 131 } 132 133 134 void 135 BControl::WindowActivated(bool active) 136 { 137 BView::WindowActivated(active); 138 139 if (IsFocus()) 140 Invalidate(); 141 } 142 143 144 void 145 BControl::AttachedToWindow() 146 { 147 rgb_color color; 148 149 BView* parent = Parent(); 150 if (parent != NULL) { 151 // inherit the color from parent 152 color = parent->ViewColor(); 153 if (color == B_TRANSPARENT_COLOR) 154 color = ui_color(B_PANEL_BACKGROUND_COLOR); 155 } else 156 color = ui_color(B_PANEL_BACKGROUND_COLOR); 157 158 SetViewColor(color); 159 SetLowColor(color); 160 161 if (!Messenger().IsValid()) 162 SetTarget(Window()); 163 164 BView::AttachedToWindow(); 165 } 166 167 168 void 169 BControl::DetachedFromWindow() 170 { 171 BView::DetachedFromWindow(); 172 } 173 174 175 void 176 BControl::AllAttached() 177 { 178 BView::AllAttached(); 179 } 180 181 182 void 183 BControl::AllDetached() 184 { 185 BView::AllDetached(); 186 } 187 188 189 void 190 BControl::MessageReceived(BMessage *message) 191 { 192 if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) { 193 BMessage reply(B_REPLY); 194 bool handled = false; 195 196 BMessage specifier; 197 int32 index; 198 int32 form; 199 const char *property; 200 if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) { 201 if (strcmp(property, "Label") == 0) { 202 if (message->what == B_GET_PROPERTY) { 203 reply.AddString("result", fLabel); 204 handled = true; 205 } else { 206 // B_SET_PROPERTY 207 const char *label; 208 if (message->FindString("data", &label) == B_OK) { 209 SetLabel(label); 210 reply.AddInt32("error", B_OK); 211 handled = true; 212 } 213 } 214 } else if (strcmp(property, "Value") == 0) { 215 if (message->what == B_GET_PROPERTY) { 216 reply.AddInt32("result", fValue); 217 handled = true; 218 } else { 219 // B_SET_PROPERTY 220 int32 value; 221 if (message->FindInt32("data", &value) == B_OK) { 222 SetValue(value); 223 reply.AddInt32("error", B_OK); 224 handled = true; 225 } 226 } 227 } else if (strcmp(property, "Enabled") == 0) { 228 if (message->what == B_GET_PROPERTY) { 229 reply.AddBool("result", fEnabled); 230 handled = true; 231 } else { 232 // B_SET_PROPERTY 233 bool enabled; 234 if (message->FindBool("data", &enabled) == B_OK) { 235 SetEnabled(enabled); 236 reply.AddInt32("error", B_OK); 237 handled = true; 238 } 239 } 240 } 241 } 242 243 if (handled) { 244 message->SendReply(&reply); 245 return; 246 } 247 } 248 249 BView::MessageReceived(message); 250 } 251 252 253 void 254 BControl::MakeFocus(bool focused) 255 { 256 if (focused == IsFocus()) 257 return; 258 259 BView::MakeFocus(focused); 260 261 if (Window()) { 262 fFocusChanging = true; 263 Invalidate(Bounds()); 264 Flush(); 265 fFocusChanging = false; 266 } 267 } 268 269 270 void 271 BControl::KeyDown(const char *bytes, int32 numBytes) 272 { 273 if (*bytes == B_ENTER || *bytes == B_SPACE) { 274 if (!fEnabled) 275 return; 276 277 SetValue(Value() ? B_CONTROL_OFF : B_CONTROL_ON); 278 Invoke(); 279 } else 280 BView::KeyDown(bytes, numBytes); 281 } 282 283 284 void 285 BControl::MouseDown(BPoint point) 286 { 287 BView::MouseDown(point); 288 } 289 290 291 void 292 BControl::MouseUp(BPoint point) 293 { 294 BView::MouseUp(point); 295 } 296 297 298 void 299 BControl::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 300 { 301 BView::MouseMoved(point, transit, message); 302 } 303 304 305 void 306 BControl::SetLabel(const char *label) 307 { 308 if (label != NULL && !label[0]) 309 label = NULL; 310 311 // Has the label been changed? 312 if ((fLabel && label && !strcmp(fLabel, label)) 313 || ((fLabel == NULL || !fLabel[0]) && label == NULL)) 314 return; 315 316 free(fLabel); 317 fLabel = label ? strdup(label) : NULL; 318 319 InvalidateLayout(); 320 Invalidate(); 321 } 322 323 324 const char * 325 BControl::Label() const 326 { 327 return fLabel; 328 } 329 330 331 void 332 BControl::SetValue(int32 value) 333 { 334 if (value == fValue) 335 return; 336 337 fValue = value; 338 Invalidate(); 339 } 340 341 342 void 343 BControl::SetValueNoUpdate(int32 value) 344 { 345 fValue = value; 346 } 347 348 349 int32 350 BControl::Value() const 351 { 352 return fValue; 353 } 354 355 356 void 357 BControl::SetEnabled(bool enabled) 358 { 359 if (fEnabled == enabled) 360 return; 361 362 fEnabled = enabled; 363 364 if (fEnabled && fWantsNav) 365 SetFlags(Flags() | B_NAVIGABLE); 366 else if (!fEnabled && (Flags() & B_NAVIGABLE)) { 367 fWantsNav = true; 368 SetFlags(Flags() & ~B_NAVIGABLE); 369 } else 370 fWantsNav = false; 371 372 if (Window()) { 373 Invalidate(Bounds()); 374 Flush(); 375 } 376 } 377 378 379 bool 380 BControl::IsEnabled() const 381 { 382 return fEnabled; 383 } 384 385 386 void 387 BControl::GetPreferredSize(float *_width, float *_height) 388 { 389 BView::GetPreferredSize(_width, _height); 390 } 391 392 393 void 394 BControl::ResizeToPreferred() 395 { 396 BView::ResizeToPreferred(); 397 } 398 399 400 status_t 401 BControl::Invoke(BMessage *message) 402 { 403 bool notify = false; 404 uint32 kind = InvokeKind(¬ify); 405 406 if (!message && !notify) 407 message = Message(); 408 409 BMessage clone(kind); 410 411 if (!message) { 412 if (!IsWatched()) 413 return B_BAD_VALUE; 414 } else 415 clone = *message; 416 417 clone.AddInt64("when", (int64)system_time()); 418 clone.AddPointer("source", this); 419 clone.AddInt32("be:value", fValue); 420 clone.AddMessenger("be:sender", BMessenger(this)); 421 422 // ToDo: is this correct? If message == NULL (even if IsWatched()), we always return B_BAD_VALUE 423 status_t err; 424 if (message) 425 err = BInvoker::Invoke(&clone); 426 else 427 err = B_BAD_VALUE; 428 429 // TODO: asynchronous messaging 430 SendNotices(kind, &clone); 431 432 return err; 433 } 434 435 436 BHandler * 437 BControl::ResolveSpecifier(BMessage *message, int32 index, 438 BMessage *specifier, int32 what, const char *property) 439 { 440 BPropertyInfo propInfo(sPropertyList); 441 442 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK) 443 return this; 444 445 return BView::ResolveSpecifier(message, index, specifier, what, 446 property); 447 } 448 449 450 status_t 451 BControl::GetSupportedSuites(BMessage *message) 452 { 453 message->AddString("suites", "suite/vnd.Be-control"); 454 455 BPropertyInfo propInfo(sPropertyList); 456 message->AddFlat("messages", &propInfo); 457 458 return BView::GetSupportedSuites(message); 459 } 460 461 462 status_t 463 BControl::Perform(perform_code code, void* _data) 464 { 465 switch (code) { 466 case PERFORM_CODE_MIN_SIZE: 467 ((perform_data_min_size*)_data)->return_value 468 = BControl::MinSize(); 469 return B_OK; 470 case PERFORM_CODE_MAX_SIZE: 471 ((perform_data_max_size*)_data)->return_value 472 = BControl::MaxSize(); 473 return B_OK; 474 case PERFORM_CODE_PREFERRED_SIZE: 475 ((perform_data_preferred_size*)_data)->return_value 476 = BControl::PreferredSize(); 477 return B_OK; 478 case PERFORM_CODE_LAYOUT_ALIGNMENT: 479 ((perform_data_layout_alignment*)_data)->return_value 480 = BControl::LayoutAlignment(); 481 return B_OK; 482 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 483 ((perform_data_has_height_for_width*)_data)->return_value 484 = BControl::HasHeightForWidth(); 485 return B_OK; 486 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 487 { 488 perform_data_get_height_for_width* data 489 = (perform_data_get_height_for_width*)_data; 490 BControl::GetHeightForWidth(data->width, &data->min, &data->max, 491 &data->preferred); 492 return B_OK; 493 } 494 case PERFORM_CODE_SET_LAYOUT: 495 { 496 perform_data_set_layout* data = (perform_data_set_layout*)_data; 497 BControl::SetLayout(data->layout); 498 return B_OK; 499 } 500 case PERFORM_CODE_LAYOUT_INVALIDATED: 501 { 502 perform_data_layout_invalidated* data 503 = (perform_data_layout_invalidated*)_data; 504 BControl::LayoutInvalidated(data->descendants); 505 return B_OK; 506 } 507 case PERFORM_CODE_DO_LAYOUT: 508 { 509 BControl::DoLayout(); 510 return B_OK; 511 } 512 } 513 514 return BView::Perform(code, _data); 515 } 516 517 518 bool 519 BControl::IsFocusChanging() const 520 { 521 return fFocusChanging; 522 } 523 524 525 bool 526 BControl::IsTracking() const 527 { 528 return fTracking; 529 } 530 531 532 void 533 BControl::SetTracking(bool state) 534 { 535 fTracking = state; 536 } 537 538 539 void BControl::_ReservedControl1() {} 540 void BControl::_ReservedControl2() {} 541 void BControl::_ReservedControl3() {} 542 void BControl::_ReservedControl4() {} 543 544 545 BControl & 546 BControl::operator=(const BControl &) 547 { 548 return *this; 549 } 550 551 552 void 553 BControl::InitData(BMessage *data) 554 { 555 fLabel = NULL; 556 SetLabel(B_EMPTY_STRING); 557 fValue = B_CONTROL_OFF; 558 fEnabled = true; 559 fFocusChanging = false; 560 fTracking = false; 561 fWantsNav = Flags() & B_NAVIGABLE; 562 563 if (data && data->HasString("_fname")) 564 SetFont(be_plain_font, B_FONT_FAMILY_AND_STYLE); 565 } 566 567