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