xref: /haiku/src/kits/interface/InterfaceDefs.cpp (revision 44d19f4d32b8f7e9c01f00294c87ca5cc2e057f7)
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