1177548Scsjp/* 2177548Scsjp * Copyright 2001-2006, Haiku Inc. 3177548Scsjp * Distributed under the terms of the MIT License. 4177548Scsjp * 5230108Seadler * Authors: 6177548Scsjp * Marc Flerackers (mflerackers@androme.be) 7177548Scsjp */ 8177548Scsjp 9177548Scsjp/*! BControl is the base class for user-event handling objects. */ 10177548Scsjp 11177548Scsjp 12177548Scsjp#include <stdlib.h> 13177548Scsjp#include <string.h> 14177548Scsjp 15177548Scsjp#include <Control.h> 16177548Scsjp#include <PropertyInfo.h> 17177548Scsjp#include <Window.h> 18177548Scsjp 19177548Scsjp#include <binary_compatibility/Interface.h> 20177548Scsjp 21177548Scsjp 22177548Scsjpstatic property_info sPropertyList[] = { 23177548Scsjp { 24177548Scsjp "Enabled", 25177548Scsjp { B_GET_PROPERTY, B_SET_PROPERTY }, 26177548Scsjp { B_DIRECT_SPECIFIER }, 27177548Scsjp NULL, 0, 28177548Scsjp { B_BOOL_TYPE } 29177548Scsjp }, 30177548Scsjp { 31177548Scsjp "Label", 32177548Scsjp { B_GET_PROPERTY, B_SET_PROPERTY }, 33177548Scsjp { B_DIRECT_SPECIFIER }, 34177548Scsjp NULL, 0, 35177548Scsjp { B_STRING_TYPE } 36177548Scsjp }, 37177548Scsjp { 38177548Scsjp "Value", 39177548Scsjp { B_GET_PROPERTY, B_SET_PROPERTY }, 40177548Scsjp { B_DIRECT_SPECIFIER }, 41177548Scsjp NULL, 0, 42177548Scsjp { B_INT32_TYPE } 43177966Srwatson }, 44177548Scsjp {} 45180310Scsjp}; 46177548Scsjp 47177966Srwatson 48177548ScsjpBControl::BControl(BRect frame, const char *name, const char *label, 49177548Scsjp BMessage *message, uint32 resizingMode, uint32 flags) 50177548Scsjp : BView(frame, name, resizingMode, flags) 51177548Scsjp{ 52177548Scsjp InitData(NULL); 53177548Scsjp 54177548Scsjp SetLabel(label); 55177548Scsjp SetMessage(message); 56177548Scsjp} 57 58 59BControl::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 70BControl::~BControl() 71{ 72 free(fLabel); 73 SetMessage(NULL); 74} 75 76 77BControl::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 103BArchivable * 104BControl::Instantiate(BMessage *archive) 105{ 106 if (validate_instantiation(archive, "BControl")) 107 return new BControl(archive); 108 109 return NULL; 110} 111 112 113status_t 114BControl::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 134void 135BControl::WindowActivated(bool active) 136{ 137 BView::WindowActivated(active); 138 139 if (IsFocus()) 140 Invalidate(); 141} 142 143 144void 145BControl::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 168void 169BControl::DetachedFromWindow() 170{ 171 BView::DetachedFromWindow(); 172} 173 174 175void 176BControl::AllAttached() 177{ 178 BView::AllAttached(); 179} 180 181 182void 183BControl::AllDetached() 184{ 185 BView::AllDetached(); 186} 187 188 189void 190BControl::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 253void 254BControl::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 270void 271BControl::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 284void 285BControl::MouseDown(BPoint point) 286{ 287 BView::MouseDown(point); 288} 289 290 291void 292BControl::MouseUp(BPoint point) 293{ 294 BView::MouseUp(point); 295} 296 297 298void 299BControl::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 300{ 301 BView::MouseMoved(point, transit, message); 302} 303 304 305void 306BControl::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 324const char * 325BControl::Label() const 326{ 327 return fLabel; 328} 329 330 331void 332BControl::SetValue(int32 value) 333{ 334 if (value == fValue) 335 return; 336 337 fValue = value; 338 Invalidate(); 339} 340 341 342void 343BControl::SetValueNoUpdate(int32 value) 344{ 345 fValue = value; 346} 347 348 349int32 350BControl::Value() const 351{ 352 return fValue; 353} 354 355 356void 357BControl::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 379bool 380BControl::IsEnabled() const 381{ 382 return fEnabled; 383} 384 385 386void 387BControl::GetPreferredSize(float *_width, float *_height) 388{ 389 BView::GetPreferredSize(_width, _height); 390} 391 392 393void 394BControl::ResizeToPreferred() 395{ 396 BView::ResizeToPreferred(); 397} 398 399 400status_t 401BControl::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 436BHandler * 437BControl::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 450status_t 451BControl::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 462status_t 463BControl::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 518bool 519BControl::IsFocusChanging() const 520{ 521 return fFocusChanging; 522} 523 524 525bool 526BControl::IsTracking() const 527{ 528 return fTracking; 529} 530 531 532void 533BControl::SetTracking(bool state) 534{ 535 fTracking = state; 536} 537 538 539void BControl::_ReservedControl1() {} 540void BControl::_ReservedControl2() {} 541void BControl::_ReservedControl3() {} 542void BControl::_ReservedControl4() {} 543 544 545BControl & 546BControl::operator=(const BControl &) 547{ 548 return *this; 549} 550 551 552void 553BControl::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