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