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 BView* parent = Parent(); 148 if (parent != NULL) { 149 // inherit the color from parent 150 rgb_color color = parent->ViewColor(); 151 if (color == B_TRANSPARENT_COLOR) 152 color = ui_color(B_PANEL_BACKGROUND_COLOR); 153 154 SetViewColor(color); 155 SetLowColor(color); 156 } 157 158 if (!Messenger().IsValid()) 159 SetTarget(Window()); 160 161 BView::AttachedToWindow(); 162 } 163 164 165 void 166 BControl::MessageReceived(BMessage *message) 167 { 168 if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) { 169 BMessage reply(B_REPLY); 170 bool handled = false; 171 172 BMessage specifier; 173 int32 index; 174 int32 form; 175 const char *property; 176 if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) { 177 if (strcmp(property, "Label") == 0) { 178 if (message->what == B_GET_PROPERTY) { 179 reply.AddString("result", fLabel); 180 handled = true; 181 } else { 182 // B_SET_PROPERTY 183 const char *label; 184 if (message->FindString("data", &label) == B_OK) { 185 SetLabel(label); 186 reply.AddInt32("error", B_OK); 187 handled = true; 188 } 189 } 190 } else if (strcmp(property, "Value") == 0) { 191 if (message->what == B_GET_PROPERTY) { 192 reply.AddInt32("result", fValue); 193 handled = true; 194 } else { 195 // B_SET_PROPERTY 196 int32 value; 197 if (message->FindInt32("data", &value) == B_OK) { 198 SetValue(value); 199 reply.AddInt32("error", B_OK); 200 handled = true; 201 } 202 } 203 } else if (strcmp(property, "Enabled") == 0) { 204 if (message->what == B_GET_PROPERTY) { 205 reply.AddBool("result", fEnabled); 206 handled = true; 207 } else { 208 // B_SET_PROPERTY 209 bool enabled; 210 if (message->FindBool("data", &enabled) == B_OK) { 211 SetEnabled(enabled); 212 reply.AddInt32("error", B_OK); 213 handled = true; 214 } 215 } 216 } 217 } 218 219 if (handled) { 220 message->SendReply(&reply); 221 return; 222 } 223 } 224 225 BView::MessageReceived(message); 226 } 227 228 229 void 230 BControl::MakeFocus(bool focused) 231 { 232 if (focused == IsFocus()) 233 return; 234 235 BView::MakeFocus(focused); 236 237 if (Window()) { 238 fFocusChanging = true; 239 Invalidate(Bounds()); 240 Flush(); 241 fFocusChanging = false; 242 } 243 } 244 245 246 void 247 BControl::KeyDown(const char *bytes, int32 numBytes) 248 { 249 if (*bytes == B_ENTER || *bytes == B_SPACE) { 250 if (!fEnabled) 251 return; 252 253 SetValue(Value() ? B_CONTROL_OFF : B_CONTROL_ON); 254 Invoke(); 255 } else 256 BView::KeyDown(bytes, numBytes); 257 } 258 259 260 void 261 BControl::MouseDown(BPoint point) 262 { 263 BView::MouseDown(point); 264 } 265 266 267 void 268 BControl::MouseUp(BPoint point) 269 { 270 BView::MouseUp(point); 271 } 272 273 274 void 275 BControl::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 276 { 277 BView::MouseMoved(point, transit, message); 278 } 279 280 281 void 282 BControl::DetachedFromWindow() 283 { 284 BView::DetachedFromWindow(); 285 } 286 287 288 void 289 BControl::SetLabel(const char *label) 290 { 291 if (label != NULL && !label[0]) 292 label = NULL; 293 294 // Has the label been changed? 295 if ((fLabel && label && !strcmp(fLabel, label)) 296 || ((fLabel == NULL || !fLabel[0]) && label == NULL)) 297 return; 298 299 free(fLabel); 300 fLabel = label ? strdup(label) : NULL; 301 302 InvalidateLayout(); 303 Invalidate(); 304 } 305 306 307 const char * 308 BControl::Label() const 309 { 310 return fLabel; 311 } 312 313 314 void 315 BControl::SetValue(int32 value) 316 { 317 if (value == fValue) 318 return; 319 320 fValue = value; 321 Invalidate(); 322 } 323 324 325 void 326 BControl::SetValueNoUpdate(int32 value) 327 { 328 fValue = value; 329 } 330 331 332 int32 333 BControl::Value() const 334 { 335 return fValue; 336 } 337 338 339 void 340 BControl::SetEnabled(bool enabled) 341 { 342 if (fEnabled == enabled) 343 return; 344 345 fEnabled = enabled; 346 347 if (fEnabled && fWantsNav) 348 SetFlags(Flags() | B_NAVIGABLE); 349 else if (!fEnabled && (Flags() & B_NAVIGABLE)) { 350 fWantsNav = true; 351 SetFlags(Flags() & ~B_NAVIGABLE); 352 } else 353 fWantsNav = false; 354 355 if (Window()) { 356 Invalidate(Bounds()); 357 Flush(); 358 } 359 } 360 361 362 bool 363 BControl::IsEnabled() const 364 { 365 return fEnabled; 366 } 367 368 369 void 370 BControl::GetPreferredSize(float *_width, float *_height) 371 { 372 BView::GetPreferredSize(_width, _height); 373 } 374 375 376 void 377 BControl::ResizeToPreferred() 378 { 379 BView::ResizeToPreferred(); 380 } 381 382 383 status_t 384 BControl::Invoke(BMessage *message) 385 { 386 bool notify = false; 387 uint32 kind = InvokeKind(¬ify); 388 389 if (!message && !notify) 390 message = Message(); 391 392 BMessage clone(kind); 393 394 if (!message) { 395 if (!IsWatched()) 396 return B_BAD_VALUE; 397 } else 398 clone = *message; 399 400 clone.AddInt64("when", (int64)system_time()); 401 clone.AddPointer("source", this); 402 clone.AddInt32("be:value", fValue); 403 clone.AddMessenger("be:sender", BMessenger(this)); 404 405 // ToDo: is this correct? If message == NULL (even if IsWatched()), we always return B_BAD_VALUE 406 status_t err; 407 if (message) 408 err = BInvoker::Invoke(&clone); 409 else 410 err = B_BAD_VALUE; 411 412 // TODO: asynchronous messaging 413 SendNotices(kind, &clone); 414 415 return err; 416 } 417 418 419 BHandler * 420 BControl::ResolveSpecifier(BMessage *message, int32 index, 421 BMessage *specifier, int32 what, const char *property) 422 { 423 BPropertyInfo propInfo(sPropertyList); 424 425 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK) 426 return this; 427 428 return BView::ResolveSpecifier(message, index, specifier, what, 429 property); 430 } 431 432 433 status_t 434 BControl::GetSupportedSuites(BMessage *message) 435 { 436 message->AddString("suites", "suite/vnd.Be-control"); 437 438 BPropertyInfo propInfo(sPropertyList); 439 message->AddFlat("messages", &propInfo); 440 441 return BView::GetSupportedSuites(message); 442 } 443 444 445 void 446 BControl::AllAttached() 447 { 448 BView::AllAttached(); 449 } 450 451 452 void 453 BControl::AllDetached() 454 { 455 BView::AllDetached(); 456 } 457 458 459 status_t 460 BControl::Perform(perform_code code, void* _data) 461 { 462 switch (code) { 463 case PERFORM_CODE_MIN_SIZE: 464 ((perform_data_min_size*)_data)->return_value 465 = BControl::MinSize(); 466 return B_OK; 467 case PERFORM_CODE_MAX_SIZE: 468 ((perform_data_max_size*)_data)->return_value 469 = BControl::MaxSize(); 470 return B_OK; 471 case PERFORM_CODE_PREFERRED_SIZE: 472 ((perform_data_preferred_size*)_data)->return_value 473 = BControl::PreferredSize(); 474 return B_OK; 475 case PERFORM_CODE_LAYOUT_ALIGNMENT: 476 ((perform_data_layout_alignment*)_data)->return_value 477 = BControl::LayoutAlignment(); 478 return B_OK; 479 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 480 ((perform_data_has_height_for_width*)_data)->return_value 481 = BControl::HasHeightForWidth(); 482 return B_OK; 483 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 484 { 485 perform_data_get_height_for_width* data 486 = (perform_data_get_height_for_width*)_data; 487 BControl::GetHeightForWidth(data->width, &data->min, &data->max, 488 &data->preferred); 489 return B_OK; 490 } 491 case PERFORM_CODE_SET_LAYOUT: 492 { 493 perform_data_set_layout* data = (perform_data_set_layout*)_data; 494 BControl::SetLayout(data->layout); 495 return B_OK; 496 } 497 case PERFORM_CODE_INVALIDATE_LAYOUT: 498 { 499 perform_data_invalidate_layout* data 500 = (perform_data_invalidate_layout*)_data; 501 BControl::InvalidateLayout(data->descendants); 502 return B_OK; 503 } 504 case PERFORM_CODE_DO_LAYOUT: 505 { 506 BControl::DoLayout(); 507 return B_OK; 508 } 509 } 510 511 return BView::Perform(code, _data); 512 } 513 514 515 bool 516 BControl::IsFocusChanging() const 517 { 518 return fFocusChanging; 519 } 520 521 522 bool 523 BControl::IsTracking() const 524 { 525 return fTracking; 526 } 527 528 529 void 530 BControl::SetTracking(bool state) 531 { 532 fTracking = state; 533 } 534 535 536 void BControl::_ReservedControl1() {} 537 void BControl::_ReservedControl2() {} 538 void BControl::_ReservedControl3() {} 539 void BControl::_ReservedControl4() {} 540 541 542 BControl & 543 BControl::operator=(const BControl &) 544 { 545 return *this; 546 } 547 548 549 void 550 BControl::InitData(BMessage *data) 551 { 552 fLabel = NULL; 553 SetLabel(B_EMPTY_STRING); 554 fValue = B_CONTROL_OFF; 555 fEnabled = true; 556 fFocusChanging = false; 557 fTracking = false; 558 fWantsNav = Flags() & B_NAVIGABLE; 559 560 if (data && data->HasString("_fname")) 561 SetFont(be_plain_font, B_FONT_FAMILY_AND_STYLE); 562 } 563 564