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