1 /* 2 * Copyright 2001-2015, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * Caz <turok2@currantbun.com> 8 * Axel Dörfler, axeld@pinc-software.de 9 * Michael Lotz <mmlr@mlotz.ch> 10 * Wim van der Meer <WPJvanderMeer@gmail.com> 11 * Joseph Groover <looncraz@looncraz.net> 12 */ 13 14 15 /*! Global functions and variables for the Interface Kit */ 16 17 18 #include <InterfaceDefs.h> 19 20 #include <new> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include <Bitmap.h> 26 #include <Clipboard.h> 27 #include <ControlLook.h> 28 #include <Font.h> 29 #include <Menu.h> 30 #include <Point.h> 31 #include <Roster.h> 32 #include <Screen.h> 33 #include <ScrollBar.h> 34 #include <String.h> 35 #include <TextView.h> 36 #include <Window.h> 37 38 #include <ApplicationPrivate.h> 39 #include <AppServerLink.h> 40 #include <ColorConversion.h> 41 #include <DecorInfo.h> 42 #include <DefaultColors.h> 43 #include <DesktopLink.h> 44 #include <HaikuControlLook.h> 45 #include <InputServerTypes.h> 46 #include <input_globals.h> 47 #include <InterfacePrivate.h> 48 #include <MenuPrivate.h> 49 #include <pr_server.h> 50 #include <ServerProtocol.h> 51 #include <ServerReadOnlyMemory.h> 52 #include <truncate_string.h> 53 #include <utf8_functions.h> 54 #include <WidthBuffer.h> 55 #include <WindowInfo.h> 56 57 58 using namespace BPrivate; 59 60 // some other weird struct exported by BeOS, it's not initialized, though 61 struct general_ui_info { 62 rgb_color background_color; 63 rgb_color mark_color; 64 rgb_color highlight_color; 65 bool color_frame; 66 rgb_color window_frame_color; 67 }; 68 69 struct general_ui_info general_info; 70 71 menu_info *_menu_info_ptr_; 72 73 extern "C" const char B_NOTIFICATION_SENDER[] = "be:sender"; 74 75 static const rgb_color _kDefaultColors[kColorWhichCount] = { 76 {216, 216, 216, 255}, // B_PANEL_BACKGROUND_COLOR 77 {216, 216, 216, 255}, // B_MENU_BACKGROUND_COLOR 78 {255, 203, 0, 255}, // B_WINDOW_TAB_COLOR 79 {0, 0, 229, 255}, // B_KEYBOARD_NAVIGATION_COLOR 80 {51, 102, 152, 255}, // B_DESKTOP_COLOR 81 {153, 153, 153, 255}, // B_MENU_SELECTED_BACKGROUND_COLOR 82 {0, 0, 0, 255}, // B_MENU_ITEM_TEXT_COLOR 83 {0, 0, 0, 255}, // B_MENU_SELECTED_ITEM_TEXT_COLOR 84 {0, 0, 0, 255}, // B_MENU_SELECTED_BORDER_COLOR 85 {0, 0, 0, 255}, // B_PANEL_TEXT_COLOR 86 {255, 255, 255, 255}, // B_DOCUMENT_BACKGROUND_COLOR 87 {0, 0, 0, 255}, // B_DOCUMENT_TEXT_COLOR 88 {245, 245, 245, 255}, // B_CONTROL_BACKGROUND_COLOR 89 {0, 0, 0, 255}, // B_CONTROL_TEXT_COLOR 90 {172, 172, 172, 255}, // B_CONTROL_BORDER_COLOR 91 {102, 152, 203, 255}, // B_CONTROL_HIGHLIGHT_COLOR 92 {0, 0, 0, 255}, // B_NAVIGATION_PULSE_COLOR 93 {255, 255, 255, 255}, // B_SHINE_COLOR 94 {0, 0, 0, 255}, // B_SHADOW_COLOR 95 {255, 255, 216, 255}, // B_TOOLTIP_BACKGROUND_COLOR 96 {0, 0, 0, 255}, // B_TOOLTIP_TEXT_COLOR 97 {0, 0, 0, 255}, // B_WINDOW_TEXT_COLOR 98 {232, 232, 232, 255}, // B_WINDOW_INACTIVE_TAB_COLOR 99 {80, 80, 80, 255}, // B_WINDOW_INACTIVE_TEXT_COLOR 100 {224, 224, 224, 255}, // B_WINDOW_BORDER_COLOR 101 {232, 232, 232, 255}, // B_WINDOW_INACTIVE_BORDER_COLOR 102 {27, 82, 140, 255}, // B_CONTROL_MARK_COLOR 103 {255, 255, 255, 255}, // B_LIST_BACKGROUND_COLOR 104 {190, 190, 190, 255}, // B_LIST_SELECTED_BACKGROUND_COLOR 105 {0, 0, 0, 255}, // B_LIST_ITEM_TEXT_COLOR 106 {0, 0, 0, 255}, // B_LIST_SELECTED_ITEM_TEXT_COLOR 107 {216, 216, 216, 255}, // B_SCROLL_BAR_THUMB_COLOR 108 {51, 102, 187, 255}, // B_LINK_TEXT_COLOR 109 {102, 152, 203, 255}, // B_LINK_HOVER_COLOR 110 {145, 112, 155, 255}, // B_LINK_VISITED_COLOR 111 {121, 142, 203, 255}, // B_LINK_ACTIVE_COLOR 112 {50, 150, 255, 255}, // B_STATUS_BAR_COLOR 113 // 100... 114 {46, 204, 64, 255}, // B_SUCCESS_COLOR 115 {255, 65, 54, 255}, // B_FAILURE_COLOR 116 {} 117 }; 118 const rgb_color* BPrivate::kDefaultColors = &_kDefaultColors[0]; 119 120 121 static const char* kColorNames[kColorWhichCount] = { 122 "B_PANEL_BACKGROUND_COLOR", 123 "B_MENU_BACKGROUND_COLOR", 124 "B_WINDOW_TAB_COLOR", 125 "B_KEYBOARD_NAVIGATION_COLOR", 126 "B_DESKTOP_COLOR", 127 "B_MENU_SELECTED_BACKGROUND_COLOR", 128 "B_MENU_ITEM_TEXT_COLOR", 129 "B_MENU_SELECTED_ITEM_TEXT_COLOR", 130 "B_MENU_SELECTED_BORDER_COLOR", 131 "B_PANEL_TEXT_COLOR", 132 "B_DOCUMENT_BACKGROUND_COLOR", 133 "B_DOCUMENT_TEXT_COLOR", 134 "B_CONTROL_BACKGROUND_COLOR", 135 "B_CONTROL_TEXT_COLOR", 136 "B_CONTROL_BORDER_COLOR", 137 "B_CONTROL_HIGHLIGHT_COLOR", 138 "B_NAVIGATION_PULSE_COLOR", 139 "B_SHINE_COLOR", 140 "B_SHADOW_COLOR", 141 "B_TOOLTIP_BACKGROUND_COLOR", 142 "B_TOOLTIP_TEXT_COLOR", 143 "B_WINDOW_TEXT_COLOR", 144 "B_WINDOW_INACTIVE_TAB_COLOR", 145 "B_WINDOW_INACTIVE_TEXT_COLOR", 146 "B_WINDOW_BORDER_COLOR", 147 "B_WINDOW_INACTIVE_BORDER_COLOR", 148 "B_CONTROL_MARK_COLOR", 149 "B_LIST_BACKGROUND_COLOR", 150 "B_LIST_SELECTED_BACKGROUND_COLOR", 151 "B_LIST_ITEM_TEXT_COLOR", 152 "B_LIST_SELECTED_ITEM_TEXT_COLOR", 153 "B_SCROLL_BAR_THUMB_COLOR", 154 "B_LINK_TEXT_COLOR", 155 "B_LINK_HOVER_COLOR", 156 "B_LINK_VISITED_COLOR", 157 "B_LINK_ACTIVE_COLOR", 158 "B_STATUS_BAR_COLOR", 159 // 100... 160 "B_SUCCESS_COLOR", 161 "B_FAILURE_COLOR", 162 NULL 163 }; 164 165 static image_id sControlLookAddon = -1; 166 167 168 namespace BPrivate { 169 170 171 /*! Fills the \a width, \a height, and \a colorSpace parameters according 172 to the window screen's mode. 173 Returns \c true if the mode is known. 174 */ 175 bool 176 get_mode_parameter(uint32 mode, int32& width, int32& height, 177 uint32& colorSpace) 178 { 179 switch (mode) { 180 case B_8_BIT_640x480: 181 case B_8_BIT_800x600: 182 case B_8_BIT_1024x768: 183 case B_8_BIT_1152x900: 184 case B_8_BIT_1280x1024: 185 case B_8_BIT_1600x1200: 186 colorSpace = B_CMAP8; 187 break; 188 189 case B_15_BIT_640x480: 190 case B_15_BIT_800x600: 191 case B_15_BIT_1024x768: 192 case B_15_BIT_1152x900: 193 case B_15_BIT_1280x1024: 194 case B_15_BIT_1600x1200: 195 colorSpace = B_RGB15; 196 break; 197 198 case B_16_BIT_640x480: 199 case B_16_BIT_800x600: 200 case B_16_BIT_1024x768: 201 case B_16_BIT_1152x900: 202 case B_16_BIT_1280x1024: 203 case B_16_BIT_1600x1200: 204 colorSpace = B_RGB16; 205 break; 206 207 case B_32_BIT_640x480: 208 case B_32_BIT_800x600: 209 case B_32_BIT_1024x768: 210 case B_32_BIT_1152x900: 211 case B_32_BIT_1280x1024: 212 case B_32_BIT_1600x1200: 213 colorSpace = B_RGB32; 214 break; 215 216 default: 217 return false; 218 } 219 220 switch (mode) { 221 case B_8_BIT_640x480: 222 case B_15_BIT_640x480: 223 case B_16_BIT_640x480: 224 case B_32_BIT_640x480: 225 width = 640; height = 480; 226 break; 227 228 case B_8_BIT_800x600: 229 case B_15_BIT_800x600: 230 case B_16_BIT_800x600: 231 case B_32_BIT_800x600: 232 width = 800; height = 600; 233 break; 234 235 case B_8_BIT_1024x768: 236 case B_15_BIT_1024x768: 237 case B_16_BIT_1024x768: 238 case B_32_BIT_1024x768: 239 width = 1024; height = 768; 240 break; 241 242 case B_8_BIT_1152x900: 243 case B_15_BIT_1152x900: 244 case B_16_BIT_1152x900: 245 case B_32_BIT_1152x900: 246 width = 1152; height = 900; 247 break; 248 249 case B_8_BIT_1280x1024: 250 case B_15_BIT_1280x1024: 251 case B_16_BIT_1280x1024: 252 case B_32_BIT_1280x1024: 253 width = 1280; height = 1024; 254 break; 255 256 case B_8_BIT_1600x1200: 257 case B_15_BIT_1600x1200: 258 case B_16_BIT_1600x1200: 259 case B_32_BIT_1600x1200: 260 width = 1600; height = 1200; 261 break; 262 } 263 264 return true; 265 } 266 267 268 void 269 get_workspaces_layout(uint32* _columns, uint32* _rows) 270 { 271 int32 columns = 1; 272 int32 rows = 1; 273 274 BPrivate::AppServerLink link; 275 link.StartMessage(AS_GET_WORKSPACE_LAYOUT); 276 277 status_t status; 278 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 279 link.Read<int32>(&columns); 280 link.Read<int32>(&rows); 281 } 282 283 if (_columns != NULL) 284 *_columns = columns; 285 if (_rows != NULL) 286 *_rows = rows; 287 } 288 289 290 void 291 set_workspaces_layout(uint32 columns, uint32 rows) 292 { 293 if (columns < 1 || rows < 1) 294 return; 295 296 BPrivate::AppServerLink link; 297 link.StartMessage(AS_SET_WORKSPACE_LAYOUT); 298 link.Attach<int32>(columns); 299 link.Attach<int32>(rows); 300 link.Flush(); 301 } 302 303 304 } // namespace BPrivate 305 306 307 void 308 set_subpixel_antialiasing(bool subpix) 309 { 310 BPrivate::AppServerLink link; 311 312 link.StartMessage(AS_SET_SUBPIXEL_ANTIALIASING); 313 link.Attach<bool>(subpix); 314 link.Flush(); 315 } 316 317 318 status_t 319 get_subpixel_antialiasing(bool* subpix) 320 { 321 BPrivate::AppServerLink link; 322 323 link.StartMessage(AS_GET_SUBPIXEL_ANTIALIASING); 324 int32 status = B_ERROR; 325 if (link.FlushWithReply(status) != B_OK || status < B_OK) 326 return status; 327 link.Read<bool>(subpix); 328 return B_OK; 329 } 330 331 332 void 333 set_hinting_mode(uint8 hinting) 334 { 335 BPrivate::AppServerLink link; 336 337 link.StartMessage(AS_SET_HINTING); 338 link.Attach<uint8>(hinting); 339 link.Flush(); 340 } 341 342 343 status_t 344 get_hinting_mode(uint8* hinting) 345 { 346 BPrivate::AppServerLink link; 347 348 link.StartMessage(AS_GET_HINTING); 349 int32 status = B_ERROR; 350 if (link.FlushWithReply(status) != B_OK || status < B_OK) 351 return status; 352 link.Read<uint8>(hinting); 353 return B_OK; 354 } 355 356 357 void 358 set_average_weight(uint8 averageWeight) 359 { 360 BPrivate::AppServerLink link; 361 362 link.StartMessage(AS_SET_SUBPIXEL_AVERAGE_WEIGHT); 363 link.Attach<uint8>(averageWeight); 364 link.Flush(); 365 } 366 367 368 status_t 369 get_average_weight(uint8* averageWeight) 370 { 371 BPrivate::AppServerLink link; 372 373 link.StartMessage(AS_GET_SUBPIXEL_AVERAGE_WEIGHT); 374 int32 status = B_ERROR; 375 if (link.FlushWithReply(status) != B_OK || status < B_OK) 376 return status; 377 link.Read<uint8>(averageWeight); 378 return B_OK; 379 } 380 381 382 void 383 set_is_subpixel_ordering_regular(bool subpixelOrdering) 384 { 385 BPrivate::AppServerLink link; 386 387 link.StartMessage(AS_SET_SUBPIXEL_ORDERING); 388 link.Attach<bool>(subpixelOrdering); 389 link.Flush(); 390 } 391 392 393 status_t 394 get_is_subpixel_ordering_regular(bool* subpixelOrdering) 395 { 396 BPrivate::AppServerLink link; 397 398 link.StartMessage(AS_GET_SUBPIXEL_ORDERING); 399 int32 status = B_ERROR; 400 if (link.FlushWithReply(status) != B_OK || status < B_OK) 401 return status; 402 link.Read<bool>(subpixelOrdering); 403 return B_OK; 404 } 405 406 407 const color_map * 408 system_colors() 409 { 410 return BScreen(B_MAIN_SCREEN_ID).ColorMap(); 411 } 412 413 414 status_t 415 set_screen_space(int32 index, uint32 space, bool stick) 416 { 417 int32 width; 418 int32 height; 419 uint32 depth; 420 if (!BPrivate::get_mode_parameter(space, width, height, depth)) 421 return B_BAD_VALUE; 422 423 BScreen screen(B_MAIN_SCREEN_ID); 424 display_mode mode; 425 426 // TODO: What about refresh rate ? 427 // currently we get it from the current video mode, but 428 // this might be not so wise. 429 status_t status = screen.GetMode(index, &mode); 430 if (status < B_OK) 431 return status; 432 433 mode.virtual_width = width; 434 mode.virtual_height = height; 435 mode.space = depth; 436 437 return screen.SetMode(index, &mode, stick); 438 } 439 440 441 status_t 442 get_scroll_bar_info(scroll_bar_info *info) 443 { 444 if (info == NULL) 445 return B_BAD_VALUE; 446 447 BPrivate::AppServerLink link; 448 link.StartMessage(AS_GET_SCROLLBAR_INFO); 449 450 int32 code; 451 if (link.FlushWithReply(code) == B_OK 452 && code == B_OK) { 453 link.Read<scroll_bar_info>(info); 454 return B_OK; 455 } 456 457 return B_ERROR; 458 } 459 460 461 status_t 462 set_scroll_bar_info(scroll_bar_info *info) 463 { 464 if (info == NULL) 465 return B_BAD_VALUE; 466 467 BPrivate::AppServerLink link; 468 int32 code; 469 470 link.StartMessage(AS_SET_SCROLLBAR_INFO); 471 link.Attach<scroll_bar_info>(*info); 472 473 if (link.FlushWithReply(code) == B_OK 474 && code == B_OK) 475 return B_OK; 476 477 return B_ERROR; 478 } 479 480 481 status_t 482 get_mouse_type(int32 *type) 483 { 484 BMessage command(IS_GET_MOUSE_TYPE); 485 BMessage reply; 486 487 status_t err = _control_input_server_(&command, &reply); 488 if (err != B_OK) 489 return err; 490 return reply.FindInt32("mouse_type", type); 491 } 492 493 494 status_t 495 set_mouse_type(int32 type) 496 { 497 BMessage command(IS_SET_MOUSE_TYPE); 498 BMessage reply; 499 500 status_t err = command.AddInt32("mouse_type", type); 501 if (err != B_OK) 502 return err; 503 return _control_input_server_(&command, &reply); 504 } 505 506 507 status_t 508 get_mouse_map(mouse_map *map) 509 { 510 BMessage command(IS_GET_MOUSE_MAP); 511 BMessage reply; 512 const void *data = 0; 513 ssize_t count; 514 515 status_t err = _control_input_server_(&command, &reply); 516 if (err == B_OK) 517 err = reply.FindData("mousemap", B_RAW_TYPE, &data, &count); 518 if (err != B_OK) 519 return err; 520 521 memcpy(map, data, count); 522 523 return B_OK; 524 } 525 526 527 status_t 528 set_mouse_map(mouse_map *map) 529 { 530 BMessage command(IS_SET_MOUSE_MAP); 531 BMessage reply; 532 533 status_t err = command.AddData("mousemap", B_RAW_TYPE, map, 534 sizeof(mouse_map)); 535 if (err != B_OK) 536 return err; 537 return _control_input_server_(&command, &reply); 538 } 539 540 541 status_t 542 get_click_speed(bigtime_t *speed) 543 { 544 BMessage command(IS_GET_CLICK_SPEED); 545 BMessage reply; 546 547 status_t err = _control_input_server_(&command, &reply); 548 if (err != B_OK) 549 return err; 550 551 if (reply.FindInt64("speed", speed) != B_OK) 552 *speed = 500000; 553 554 return B_OK; 555 } 556 557 558 status_t 559 set_click_speed(bigtime_t speed) 560 { 561 BMessage command(IS_SET_CLICK_SPEED); 562 BMessage reply; 563 command.AddInt64("speed", speed); 564 return _control_input_server_(&command, &reply); 565 } 566 567 568 status_t 569 get_mouse_speed(int32 *speed) 570 { 571 BMessage command(IS_GET_MOUSE_SPEED); 572 BMessage reply; 573 574 status_t err = _control_input_server_(&command, &reply); 575 if (err != B_OK) 576 return err; 577 578 if (reply.FindInt32("speed", speed) != B_OK) 579 *speed = 65536; 580 581 return B_OK; 582 } 583 584 585 status_t 586 set_mouse_speed(int32 speed) 587 { 588 BMessage command(IS_SET_MOUSE_SPEED); 589 BMessage reply; 590 command.AddInt32("speed", speed); 591 return _control_input_server_(&command, &reply); 592 } 593 594 595 status_t 596 get_mouse_acceleration(int32 *speed) 597 { 598 BMessage command(IS_GET_MOUSE_ACCELERATION); 599 BMessage reply; 600 601 _control_input_server_(&command, &reply); 602 603 if (reply.FindInt32("speed", speed) != B_OK) 604 *speed = 65536; 605 606 return B_OK; 607 } 608 609 610 status_t 611 set_mouse_acceleration(int32 speed) 612 { 613 BMessage command(IS_SET_MOUSE_ACCELERATION); 614 BMessage reply; 615 command.AddInt32("speed", speed); 616 return _control_input_server_(&command, &reply); 617 } 618 619 620 status_t 621 get_key_repeat_rate(int32 *rate) 622 { 623 BMessage command(IS_GET_KEY_REPEAT_RATE); 624 BMessage reply; 625 626 _control_input_server_(&command, &reply); 627 628 if (reply.FindInt32("rate", rate) != B_OK) 629 *rate = 250000; 630 631 return B_OK; 632 } 633 634 635 status_t 636 set_key_repeat_rate(int32 rate) 637 { 638 BMessage command(IS_SET_KEY_REPEAT_RATE); 639 BMessage reply; 640 command.AddInt32("rate", rate); 641 return _control_input_server_(&command, &reply); 642 } 643 644 645 status_t 646 get_key_repeat_delay(bigtime_t *delay) 647 { 648 BMessage command(IS_GET_KEY_REPEAT_DELAY); 649 BMessage reply; 650 651 _control_input_server_(&command, &reply); 652 653 if (reply.FindInt64("delay", delay) != B_OK) 654 *delay = 200; 655 656 return B_OK; 657 } 658 659 660 status_t 661 set_key_repeat_delay(bigtime_t delay) 662 { 663 BMessage command(IS_SET_KEY_REPEAT_DELAY); 664 BMessage reply; 665 command.AddInt64("delay", delay); 666 return _control_input_server_(&command, &reply); 667 } 668 669 670 uint32 671 modifiers() 672 { 673 BMessage command(IS_GET_MODIFIERS); 674 BMessage reply; 675 int32 err, modifier; 676 677 _control_input_server_(&command, &reply); 678 679 if (reply.FindInt32("status", &err) != B_OK) 680 return 0; 681 682 if (reply.FindInt32("modifiers", &modifier) != B_OK) 683 return 0; 684 685 return modifier; 686 } 687 688 689 status_t 690 get_key_info(key_info *info) 691 { 692 BMessage command(IS_GET_KEY_INFO); 693 BMessage reply; 694 const void *data = 0; 695 int32 err; 696 ssize_t count; 697 698 _control_input_server_(&command, &reply); 699 700 if (reply.FindInt32("status", &err) != B_OK) 701 return B_ERROR; 702 703 if (reply.FindData("key_info", B_ANY_TYPE, &data, &count) != B_OK) 704 return B_ERROR; 705 706 memcpy(info, data, count); 707 return B_OK; 708 } 709 710 711 void 712 get_key_map(key_map **map, char **key_buffer) 713 { 714 _get_key_map(map, key_buffer, NULL); 715 } 716 717 718 void 719 _get_key_map(key_map **map, char **key_buffer, ssize_t *key_buffer_size) 720 { 721 BMessage command(IS_GET_KEY_MAP); 722 BMessage reply; 723 ssize_t map_count, key_count; 724 const void *map_array = 0, *key_array = 0; 725 if (key_buffer_size == NULL) 726 key_buffer_size = &key_count; 727 728 _control_input_server_(&command, &reply); 729 730 if (reply.FindData("keymap", B_ANY_TYPE, &map_array, &map_count) != B_OK) { 731 *map = 0; *key_buffer = 0; 732 return; 733 } 734 735 if (reply.FindData("key_buffer", B_ANY_TYPE, &key_array, key_buffer_size) 736 != B_OK) { 737 *map = 0; *key_buffer = 0; 738 return; 739 } 740 741 *map = (key_map *)malloc(map_count); 742 memcpy(*map, map_array, map_count); 743 *key_buffer = (char *)malloc(*key_buffer_size); 744 memcpy(*key_buffer, key_array, *key_buffer_size); 745 } 746 747 748 status_t 749 get_keyboard_id(uint16 *id) 750 { 751 BMessage command(IS_GET_KEYBOARD_ID); 752 BMessage reply; 753 uint16 kid; 754 755 _control_input_server_(&command, &reply); 756 757 status_t err = reply.FindInt16("id", (int16 *)&kid); 758 if (err != B_OK) 759 return err; 760 *id = kid; 761 762 return B_OK; 763 } 764 765 766 status_t 767 get_modifier_key(uint32 modifier, uint32 *key) 768 { 769 BMessage command(IS_GET_MODIFIER_KEY); 770 BMessage reply; 771 uint32 rkey; 772 773 command.AddInt32("modifier", modifier); 774 _control_input_server_(&command, &reply); 775 776 status_t err = reply.FindInt32("key", (int32 *) &rkey); 777 if (err != B_OK) 778 return err; 779 *key = rkey; 780 781 return B_OK; 782 } 783 784 785 void 786 set_modifier_key(uint32 modifier, uint32 key) 787 { 788 BMessage command(IS_SET_MODIFIER_KEY); 789 BMessage reply; 790 791 command.AddInt32("modifier", modifier); 792 command.AddInt32("key", key); 793 _control_input_server_(&command, &reply); 794 } 795 796 797 void 798 set_keyboard_locks(uint32 modifiers) 799 { 800 BMessage command(IS_SET_KEYBOARD_LOCKS); 801 BMessage reply; 802 803 command.AddInt32("locks", modifiers); 804 _control_input_server_(&command, &reply); 805 } 806 807 808 status_t 809 _restore_key_map_() 810 { 811 BMessage message(IS_RESTORE_KEY_MAP); 812 BMessage reply; 813 814 return _control_input_server_(&message, &reply); 815 } 816 817 818 rgb_color 819 keyboard_navigation_color() 820 { 821 // Queries the app_server 822 return ui_color(B_KEYBOARD_NAVIGATION_COLOR); 823 } 824 825 826 int32 827 count_workspaces() 828 { 829 uint32 columns; 830 uint32 rows; 831 BPrivate::get_workspaces_layout(&columns, &rows); 832 833 return columns * rows; 834 } 835 836 837 void 838 set_workspace_count(int32 count) 839 { 840 int32 squareRoot = (int32)sqrt(count); 841 842 int32 rows = 1; 843 for (int32 i = 2; i <= squareRoot; i++) { 844 if (count % i == 0) 845 rows = i; 846 } 847 848 int32 columns = count / rows; 849 850 BPrivate::set_workspaces_layout(columns, rows); 851 } 852 853 854 int32 855 current_workspace() 856 { 857 int32 index = 0; 858 859 BPrivate::AppServerLink link; 860 link.StartMessage(AS_CURRENT_WORKSPACE); 861 862 int32 status; 863 if (link.FlushWithReply(status) == B_OK && status == B_OK) 864 link.Read<int32>(&index); 865 866 return index; 867 } 868 869 870 void 871 activate_workspace(int32 workspace) 872 { 873 BPrivate::AppServerLink link; 874 link.StartMessage(AS_ACTIVATE_WORKSPACE); 875 link.Attach<int32>(workspace); 876 link.Attach<bool>(false); 877 link.Flush(); 878 } 879 880 881 bigtime_t 882 idle_time() 883 { 884 bigtime_t idletime = 0; 885 886 BPrivate::AppServerLink link; 887 link.StartMessage(AS_IDLE_TIME); 888 889 int32 code; 890 if (link.FlushWithReply(code) == B_OK && code == B_OK) 891 link.Read<int64>(&idletime); 892 893 return idletime; 894 } 895 896 897 void 898 run_select_printer_panel() 899 { 900 if (be_roster == NULL) 901 return; 902 903 // Launches the Printer prefs app via the Roster 904 be_roster->Launch(PRNT_SIGNATURE_TYPE); 905 } 906 907 908 void 909 run_add_printer_panel() 910 { 911 // Launches the Printer prefs app via the Roster and asks it to 912 // add a printer 913 run_select_printer_panel(); 914 915 BMessenger printerPanelMessenger(PRNT_SIGNATURE_TYPE); 916 printerPanelMessenger.SendMessage(PRINTERS_ADD_PRINTER); 917 } 918 919 920 void 921 run_be_about() 922 { 923 if (be_roster != NULL) 924 be_roster->Launch("application/x-vnd.Haiku-About"); 925 } 926 927 928 void 929 set_focus_follows_mouse(bool follow) 930 { 931 // obviously deprecated API 932 set_mouse_mode(B_FOCUS_FOLLOWS_MOUSE); 933 } 934 935 936 bool 937 focus_follows_mouse() 938 { 939 return mouse_mode() == B_FOCUS_FOLLOWS_MOUSE; 940 } 941 942 943 void 944 set_mouse_mode(mode_mouse mode) 945 { 946 BPrivate::AppServerLink link; 947 link.StartMessage(AS_SET_MOUSE_MODE); 948 link.Attach<mode_mouse>(mode); 949 link.Flush(); 950 } 951 952 953 mode_mouse 954 mouse_mode() 955 { 956 // Gets the mouse focus style, such as activate to click, 957 // focus to click, ... 958 mode_mouse mode = B_NORMAL_MOUSE; 959 960 BPrivate::AppServerLink link; 961 link.StartMessage(AS_GET_MOUSE_MODE); 962 963 int32 code; 964 if (link.FlushWithReply(code) == B_OK && code == B_OK) 965 link.Read<mode_mouse>(&mode); 966 967 return mode; 968 } 969 970 971 void 972 set_focus_follows_mouse_mode(mode_focus_follows_mouse mode) 973 { 974 BPrivate::AppServerLink link; 975 link.StartMessage(AS_SET_FOCUS_FOLLOWS_MOUSE_MODE); 976 link.Attach<mode_focus_follows_mouse>(mode); 977 link.Flush(); 978 } 979 980 981 mode_focus_follows_mouse 982 focus_follows_mouse_mode() 983 { 984 mode_focus_follows_mouse mode = B_NORMAL_FOCUS_FOLLOWS_MOUSE; 985 986 BPrivate::AppServerLink link; 987 link.StartMessage(AS_GET_FOCUS_FOLLOWS_MOUSE_MODE); 988 989 int32 code; 990 if (link.FlushWithReply(code) == B_OK && code == B_OK) 991 link.Read<mode_focus_follows_mouse>(&mode); 992 993 return mode; 994 } 995 996 997 status_t 998 get_mouse(BPoint* screenWhere, uint32* buttons) 999 { 1000 if (screenWhere == NULL && buttons == NULL) 1001 return B_BAD_VALUE; 1002 1003 BPrivate::AppServerLink link; 1004 link.StartMessage(AS_GET_CURSOR_POSITION); 1005 1006 int32 code; 1007 status_t ret = link.FlushWithReply(code); 1008 if (ret != B_OK) 1009 return ret; 1010 if (code != B_OK) 1011 return code; 1012 1013 if (screenWhere != NULL) 1014 ret = link.Read<BPoint>(screenWhere); 1015 else { 1016 BPoint dummy; 1017 ret = link.Read<BPoint>(&dummy); 1018 } 1019 if (ret != B_OK) 1020 return ret; 1021 1022 if (buttons != NULL) 1023 ret = link.Read<uint32>(buttons); 1024 else { 1025 uint32 dummy; 1026 ret = link.Read<uint32>(&dummy); 1027 } 1028 1029 return ret; 1030 } 1031 1032 1033 status_t 1034 get_mouse_bitmap(BBitmap** bitmap, BPoint* hotspot) 1035 { 1036 if (bitmap == NULL && hotspot == NULL) 1037 return B_BAD_VALUE; 1038 1039 BPrivate::AppServerLink link; 1040 link.StartMessage(AS_GET_CURSOR_BITMAP); 1041 1042 int32 code; 1043 status_t status = link.FlushWithReply(code); 1044 if (status != B_OK) 1045 return status; 1046 if (code != B_OK) 1047 return code; 1048 1049 uint32 size = 0; 1050 uint32 cursorWidth = 0; 1051 uint32 cursorHeight = 0; 1052 1053 // if link.Read() returns an error, the same error will be returned on 1054 // subsequent calls, so we'll check only the return value of the last call 1055 link.Read<uint32>(&size); 1056 link.Read<uint32>(&cursorWidth); 1057 link.Read<uint32>(&cursorHeight); 1058 if (hotspot == NULL) { 1059 BPoint dummy; 1060 link.Read<BPoint>(&dummy); 1061 } else 1062 link.Read<BPoint>(hotspot); 1063 1064 void* data = NULL; 1065 if (size > 0) 1066 data = malloc(size); 1067 if (data == NULL) 1068 return B_NO_MEMORY; 1069 1070 status = link.Read(data, size); 1071 if (status != B_OK) { 1072 free(data); 1073 return status; 1074 } 1075 1076 BBitmap* cursorBitmap = new (std::nothrow) BBitmap(BRect(0, 0, 1077 cursorWidth - 1, cursorHeight - 1), B_RGBA32); 1078 1079 if (cursorBitmap == NULL) { 1080 free(data); 1081 return B_NO_MEMORY; 1082 } 1083 status = cursorBitmap->InitCheck(); 1084 if (status == B_OK) 1085 cursorBitmap->SetBits(data, size, 0, B_RGBA32); 1086 1087 free(data); 1088 1089 if (status == B_OK && bitmap != NULL) 1090 *bitmap = cursorBitmap; 1091 else 1092 delete cursorBitmap; 1093 1094 return status; 1095 } 1096 1097 1098 void 1099 set_accept_first_click(bool acceptFirstClick) 1100 { 1101 BPrivate::AppServerLink link; 1102 link.StartMessage(AS_SET_ACCEPT_FIRST_CLICK); 1103 link.Attach<bool>(acceptFirstClick); 1104 link.Flush(); 1105 } 1106 1107 1108 bool 1109 accept_first_click() 1110 { 1111 // Gets the accept first click status 1112 bool acceptFirstClick = true; 1113 1114 BPrivate::AppServerLink link; 1115 link.StartMessage(AS_GET_ACCEPT_FIRST_CLICK); 1116 1117 int32 code; 1118 if (link.FlushWithReply(code) == B_OK && code == B_OK) 1119 link.Read<bool>(&acceptFirstClick); 1120 1121 return acceptFirstClick; 1122 } 1123 1124 1125 rgb_color 1126 ui_color(color_which which) 1127 { 1128 int32 index = color_which_to_index(which); 1129 if (index < 0 || index >= kColorWhichCount) { 1130 fprintf(stderr, "ui_color(): unknown color_which %d\n", which); 1131 return make_color(0, 0, 0); 1132 } 1133 1134 if (be_app != NULL) { 1135 server_read_only_memory* shared 1136 = BApplication::Private::ServerReadOnlyMemory(); 1137 if (shared != NULL) { 1138 // check for unset colors 1139 if (shared->colors[index] == B_TRANSPARENT_COLOR) 1140 shared->colors[index] = kDefaultColors[index]; 1141 1142 return shared->colors[index]; 1143 } 1144 } 1145 1146 return kDefaultColors[index]; 1147 } 1148 1149 1150 const char* 1151 ui_color_name(color_which which) 1152 { 1153 // Suppress warnings for B_NO_COLOR. 1154 if (which == B_NO_COLOR) 1155 return NULL; 1156 1157 int32 index = color_which_to_index(which); 1158 if (index < 0 || index >= kColorWhichCount) { 1159 fprintf(stderr, "ui_color_name(): unknown color_which %d\n", which); 1160 return NULL; 1161 } 1162 1163 return kColorNames[index]; 1164 } 1165 1166 1167 color_which 1168 which_ui_color(const char* name) 1169 { 1170 if (name == NULL) 1171 return B_NO_COLOR; 1172 1173 for (int32 index = 0; index < kColorWhichCount; ++index) { 1174 if (!strcmp(kColorNames[index], name)) 1175 return index_to_color_which(index); 1176 } 1177 1178 return B_NO_COLOR; 1179 } 1180 1181 1182 void 1183 set_ui_color(const color_which &which, const rgb_color &color) 1184 { 1185 int32 index = color_which_to_index(which); 1186 if (index < 0 || index >= kColorWhichCount) { 1187 fprintf(stderr, "set_ui_color(): unknown color_which %d\n", which); 1188 return; 1189 } 1190 1191 if (ui_color(which) == color) 1192 return; 1193 1194 BPrivate::DesktopLink link; 1195 link.StartMessage(AS_SET_UI_COLOR); 1196 link.Attach<color_which>(which); 1197 link.Attach<rgb_color>(color); 1198 link.Flush(); 1199 } 1200 1201 1202 void 1203 set_ui_colors(const BMessage* colors) 1204 { 1205 if (colors == NULL) 1206 return; 1207 1208 int32 count = 0; 1209 int32 index = 0; 1210 char* name = NULL; 1211 type_code type; 1212 rgb_color color; 1213 color_which which = B_NO_COLOR; 1214 1215 BPrivate::DesktopLink desktop; 1216 if (desktop.InitCheck() != B_OK) 1217 return; 1218 1219 desktop.StartMessage(AS_SET_UI_COLORS); 1220 desktop.Attach<bool>(false); 1221 1222 // Only colors with names that map to system colors will get through. 1223 while (colors->GetInfo(B_RGB_32_BIT_TYPE, index, &name, &type) == B_OK) { 1224 1225 which = which_ui_color(name); 1226 ++index; 1227 1228 if (which == B_NO_COLOR || colors->FindColor(name, &color) != B_OK) 1229 continue; 1230 1231 desktop.Attach<color_which>(which); 1232 desktop.Attach<rgb_color>(color); 1233 ++count; 1234 } 1235 1236 if (count == 0) 1237 return; 1238 1239 desktop.Attach<color_which>(B_NO_COLOR); 1240 desktop.Flush(); 1241 } 1242 1243 1244 rgb_color 1245 tint_color(rgb_color color, float tint) 1246 { 1247 rgb_color result; 1248 1249 #define LIGHTEN(x) ((uint8)(255.0f - (255.0f - x) * tint)) 1250 #define DARKEN(x) ((uint8)(x * (2 - tint))) 1251 1252 if (tint < 1.0f) { 1253 result.red = LIGHTEN(color.red); 1254 result.green = LIGHTEN(color.green); 1255 result.blue = LIGHTEN(color.blue); 1256 result.alpha = color.alpha; 1257 } else { 1258 result.red = DARKEN(color.red); 1259 result.green = DARKEN(color.green); 1260 result.blue = DARKEN(color.blue); 1261 result.alpha = color.alpha; 1262 } 1263 1264 #undef LIGHTEN 1265 #undef DARKEN 1266 1267 return result; 1268 } 1269 1270 1271 rgb_color shift_color(rgb_color color, float shift); 1272 1273 rgb_color 1274 shift_color(rgb_color color, float shift) 1275 { 1276 return tint_color(color, shift); 1277 } 1278 1279 1280 extern "C" status_t 1281 _init_interface_kit_() 1282 { 1283 status_t status = BPrivate::PaletteConverter::InitializeDefault(true); 1284 if (status < B_OK) 1285 return status; 1286 1287 // init global clipboard 1288 if (be_clipboard == NULL) 1289 be_clipboard = new BClipboard(NULL); 1290 1291 BString path; 1292 if (get_control_look(path) && path.Length() > 0) { 1293 BControlLook* (*instantiate)(image_id); 1294 1295 sControlLookAddon = load_add_on(path.String()); 1296 if (sControlLookAddon >= 0 1297 && get_image_symbol(sControlLookAddon, 1298 "instantiate_control_look", 1299 B_SYMBOL_TYPE_TEXT, (void **)&instantiate) == B_OK) { 1300 be_control_look = instantiate(sControlLookAddon); 1301 if (be_control_look == NULL) { 1302 unload_add_on(sControlLookAddon); 1303 sControlLookAddon = -1; 1304 } 1305 } 1306 } 1307 if (be_control_look == NULL) 1308 be_control_look = new HaikuControlLook(); 1309 1310 _init_global_fonts_(); 1311 1312 BPrivate::gWidthBuffer = new BPrivate::WidthBuffer; 1313 status = BPrivate::MenuPrivate::CreateBitmaps(); 1314 if (status != B_OK) 1315 return status; 1316 1317 _menu_info_ptr_ = &BMenu::sMenuInfo; 1318 1319 status = get_menu_info(&BMenu::sMenuInfo); 1320 if (status != B_OK) 1321 return status; 1322 1323 general_info.background_color = ui_color(B_PANEL_BACKGROUND_COLOR); 1324 general_info.mark_color = ui_color(B_CONTROL_MARK_COLOR); 1325 general_info.highlight_color = ui_color(B_CONTROL_HIGHLIGHT_COLOR); 1326 general_info.window_frame_color = ui_color(B_WINDOW_TAB_COLOR); 1327 general_info.color_frame = true; 1328 1329 // TODO: fill the other static members 1330 1331 return status; 1332 } 1333 1334 1335 extern "C" status_t 1336 _fini_interface_kit_() 1337 { 1338 BPrivate::MenuPrivate::DeleteBitmaps(); 1339 1340 delete BPrivate::gWidthBuffer; 1341 BPrivate::gWidthBuffer = NULL; 1342 1343 delete be_control_look; 1344 be_control_look = NULL; 1345 1346 // Note: if we ever want to support live switching, we cannot just unload 1347 // the old one since some thread might still be in a method of the object. 1348 // maybe locking/unlocking all loopers around would ensure proper exit. 1349 if (sControlLookAddon >= 0) 1350 unload_add_on(sControlLookAddon); 1351 sControlLookAddon = -1; 1352 1353 // TODO: Anything else? 1354 1355 return B_OK; 1356 } 1357 1358 1359 1360 namespace BPrivate { 1361 1362 1363 /*! \brief queries the server for the current decorator 1364 \param path BString into which to store current decorator's location 1365 \return boolean true/false 1366 */ 1367 bool 1368 get_decorator(BString& path) 1369 { 1370 BPrivate::AppServerLink link; 1371 link.StartMessage(AS_GET_DECORATOR); 1372 1373 int32 code; 1374 if (link.FlushWithReply(code) != B_OK || code != B_OK) 1375 return false; 1376 1377 return link.ReadString(path) == B_OK; 1378 } 1379 1380 1381 /*! \brief Private function which sets the window decorator for the system. 1382 \param path BString with the path to the decorator to set 1383 1384 Will return detailed error status via status_t 1385 */ 1386 status_t 1387 set_decorator(const BString& path) 1388 { 1389 BPrivate::AppServerLink link; 1390 1391 link.StartMessage(AS_SET_DECORATOR); 1392 1393 link.AttachString(path.String()); 1394 link.Flush(); 1395 1396 status_t error = B_OK; 1397 link.Read<status_t>(&error); 1398 1399 return error; 1400 } 1401 1402 1403 /*! \brief sets a window to preview a given decorator 1404 \param path path to any given decorator add-on 1405 \param window pointer to BWindow which will show decorator 1406 1407 Piggy-backs on BWindow::SetDecoratorSettings(...) 1408 */ 1409 status_t 1410 preview_decorator(const BString& path, BWindow* window) 1411 { 1412 if (window == NULL) 1413 return B_ERROR; 1414 1415 BMessage msg('prVu'); 1416 msg.AddString("preview", path.String()); 1417 1418 return window->SetDecoratorSettings(msg); 1419 } 1420 1421 1422 /*! \brief queries the server for the current ControlLook path 1423 \param path BString into which to store current ControlLook's add-on path 1424 \return boolean true/false 1425 */ 1426 bool 1427 get_control_look(BString& path) 1428 { 1429 BPrivate::AppServerLink link; 1430 link.StartMessage(AS_GET_CONTROL_LOOK); 1431 1432 int32 code; 1433 if (link.FlushWithReply(code) != B_OK || code != B_OK) 1434 return false; 1435 1436 return link.ReadString(path) == B_OK; 1437 } 1438 1439 1440 /*! \brief Private function which sets the ControlLook for the system. 1441 \param BString with the ControlLook add-on path to set 1442 1443 Will return detailed error status via status_t 1444 */ 1445 status_t 1446 set_control_look(const BString& path) 1447 { 1448 BPrivate::AppServerLink link; 1449 1450 link.StartMessage(AS_SET_CONTROL_LOOK); 1451 1452 link.AttachString(path.String()); 1453 1454 status_t error = B_OK; 1455 if (link.FlushWithReply(error) != B_OK) 1456 return B_ERROR; 1457 1458 return error; 1459 } 1460 1461 1462 status_t 1463 get_application_order(int32 workspace, team_id** _applications, 1464 int32* _count) 1465 { 1466 BPrivate::AppServerLink link; 1467 1468 link.StartMessage(AS_GET_APPLICATION_ORDER); 1469 link.Attach<int32>(workspace); 1470 1471 int32 code; 1472 status_t status = link.FlushWithReply(code); 1473 if (status != B_OK) 1474 return status; 1475 if (code != B_OK) 1476 return code; 1477 1478 int32 count; 1479 link.Read<int32>(&count); 1480 1481 *_applications = (team_id*)malloc(count * sizeof(team_id)); 1482 if (*_applications == NULL) 1483 return B_NO_MEMORY; 1484 1485 link.Read(*_applications, count * sizeof(team_id)); 1486 *_count = count; 1487 return B_OK; 1488 } 1489 1490 1491 status_t 1492 get_window_order(int32 workspace, int32** _tokens, int32* _count) 1493 { 1494 BPrivate::AppServerLink link; 1495 1496 link.StartMessage(AS_GET_WINDOW_ORDER); 1497 link.Attach<int32>(workspace); 1498 1499 int32 code; 1500 status_t status = link.FlushWithReply(code); 1501 if (status != B_OK) 1502 return status; 1503 if (code != B_OK) 1504 return code; 1505 1506 int32 count; 1507 link.Read<int32>(&count); 1508 1509 *_tokens = (int32*)malloc(count * sizeof(int32)); 1510 if (*_tokens == NULL) 1511 return B_NO_MEMORY; 1512 1513 link.Read(*_tokens, count * sizeof(int32)); 1514 *_count = count; 1515 return B_OK; 1516 } 1517 1518 1519 } // namespace BPrivate 1520 1521 // These methods were marked with "Danger, will Robinson!" in 1522 // the OpenTracker source, so we might not want to be compatible 1523 // here. 1524 // In any way, we would need to update Deskbar to use our 1525 // replacements, so we could as well just implement them... 1526 1527 void 1528 do_window_action(int32 windowToken, int32 action, BRect zoomRect, bool zoom) 1529 { 1530 BPrivate::AppServerLink link; 1531 1532 link.StartMessage(AS_WINDOW_ACTION); 1533 link.Attach<int32>(windowToken); 1534 link.Attach<int32>(action); 1535 // we don't have any zooming effect 1536 1537 link.Flush(); 1538 } 1539 1540 1541 client_window_info* 1542 get_window_info(int32 serverToken) 1543 { 1544 BPrivate::AppServerLink link; 1545 1546 link.StartMessage(AS_GET_WINDOW_INFO); 1547 link.Attach<int32>(serverToken); 1548 1549 int32 code; 1550 if (link.FlushWithReply(code) != B_OK || code != B_OK) 1551 return NULL; 1552 1553 int32 size; 1554 link.Read<int32>(&size); 1555 1556 client_window_info* info = (client_window_info*)malloc(size); 1557 if (info == NULL) 1558 return NULL; 1559 1560 link.Read(info, size); 1561 return info; 1562 } 1563 1564 1565 int32* 1566 get_token_list(team_id team, int32* _count) 1567 { 1568 BPrivate::AppServerLink link; 1569 1570 link.StartMessage(AS_GET_WINDOW_LIST); 1571 link.Attach<team_id>(team); 1572 1573 int32 code; 1574 if (link.FlushWithReply(code) != B_OK || code != B_OK) 1575 return NULL; 1576 1577 int32 count; 1578 link.Read<int32>(&count); 1579 1580 int32* tokens = (int32*)malloc(count * sizeof(int32)); 1581 if (tokens == NULL) 1582 return NULL; 1583 1584 link.Read(tokens, count * sizeof(int32)); 1585 *_count = count; 1586 return tokens; 1587 } 1588 1589 1590 void 1591 do_bring_to_front_team(BRect zoomRect, team_id team, bool zoom) 1592 { 1593 BPrivate::AppServerLink link; 1594 1595 link.StartMessage(AS_BRING_TEAM_TO_FRONT); 1596 link.Attach<team_id>(team); 1597 // we don't have any zooming effect 1598 1599 link.Flush(); 1600 } 1601 1602 1603 void 1604 do_minimize_team(BRect zoomRect, team_id team, bool zoom) 1605 { 1606 BPrivate::AppServerLink link; 1607 1608 link.StartMessage(AS_MINIMIZE_TEAM); 1609 link.Attach<team_id>(team); 1610 // we don't have any zooming effect 1611 1612 link.Flush(); 1613 } 1614 1615 1616 // #pragma mark - truncate string 1617 1618 1619 void 1620 truncate_string(BString& string, uint32 mode, float width, 1621 const float* escapementArray, float fontSize, float ellipsisWidth, 1622 int32 charCount) 1623 { 1624 // add a tiny amount to the width to make floating point inaccuracy 1625 // not drop chars that would actually fit exactly 1626 width += 1.f / 128; 1627 1628 switch (mode) { 1629 case B_TRUNCATE_BEGINNING: 1630 { 1631 float totalWidth = 0; 1632 for (int32 i = charCount - 1; i >= 0; i--) { 1633 float charWidth = escapementArray[i] * fontSize; 1634 if (totalWidth + charWidth > width) { 1635 // we need to truncate 1636 while (totalWidth + ellipsisWidth > width) { 1637 // remove chars until there's enough space for the 1638 // ellipsis 1639 if (++i == charCount) { 1640 // we've reached the end of the string and still 1641 // no space, so return an empty string 1642 string.Truncate(0); 1643 return; 1644 } 1645 1646 totalWidth -= escapementArray[i] * fontSize; 1647 } 1648 1649 string.RemoveChars(0, i + 1); 1650 string.PrependChars(B_UTF8_ELLIPSIS, 1); 1651 return; 1652 } 1653 1654 totalWidth += charWidth; 1655 } 1656 1657 break; 1658 } 1659 1660 case B_TRUNCATE_END: 1661 { 1662 float totalWidth = 0; 1663 for (int32 i = 0; i < charCount; i++) { 1664 float charWidth = escapementArray[i] * fontSize; 1665 if (totalWidth + charWidth > width) { 1666 // we need to truncate 1667 while (totalWidth + ellipsisWidth > width) { 1668 // remove chars until there's enough space for the 1669 // ellipsis 1670 if (i-- == 0) { 1671 // we've reached the start of the string and still 1672 // no space, so return an empty string 1673 string.Truncate(0); 1674 return; 1675 } 1676 1677 totalWidth -= escapementArray[i] * fontSize; 1678 } 1679 1680 string.RemoveChars(i, charCount - i); 1681 string.AppendChars(B_UTF8_ELLIPSIS, 1); 1682 return; 1683 } 1684 1685 totalWidth += charWidth; 1686 } 1687 1688 break; 1689 } 1690 1691 case B_TRUNCATE_MIDDLE: 1692 case B_TRUNCATE_SMART: 1693 { 1694 float leftWidth = 0; 1695 float rightWidth = 0; 1696 int32 leftIndex = 0; 1697 int32 rightIndex = charCount - 1; 1698 bool left = true; 1699 1700 for (int32 i = 0; i < charCount; i++) { 1701 float charWidth 1702 = escapementArray[left ? leftIndex : rightIndex] * fontSize; 1703 1704 if (leftWidth + rightWidth + charWidth > width) { 1705 // we need to truncate 1706 while (leftWidth + rightWidth + ellipsisWidth > width) { 1707 // remove chars until there's enough space for the 1708 // ellipsis 1709 if (leftIndex == 0 && rightIndex == charCount - 1) { 1710 // we've reached both ends of the string and still 1711 // no space, so return an empty string 1712 string.Truncate(0); 1713 return; 1714 } 1715 1716 if (leftIndex > 0 && (rightIndex == charCount - 1 1717 || leftWidth > rightWidth)) { 1718 // remove char on the left 1719 leftWidth -= escapementArray[--leftIndex] 1720 * fontSize; 1721 } else { 1722 // remove char on the right 1723 rightWidth -= escapementArray[++rightIndex] 1724 * fontSize; 1725 } 1726 } 1727 1728 string.RemoveChars(leftIndex, rightIndex + 1 - leftIndex); 1729 string.InsertChars(B_UTF8_ELLIPSIS, 1, leftIndex); 1730 return; 1731 } 1732 1733 if (left) { 1734 leftIndex++; 1735 leftWidth += charWidth; 1736 } else { 1737 rightIndex--; 1738 rightWidth += charWidth; 1739 } 1740 1741 left = rightWidth > leftWidth; 1742 } 1743 1744 break; 1745 } 1746 } 1747 1748 // we've run through without the need to truncate, leave the string as it is 1749 } 1750