xref: /haiku/src/kits/interface/InterfaceDefs.cpp (revision 55b40aa53a835472ec7952b138ae4256203d02e4)
1 /*
2  * Copyright 2001-2006, 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  */
10 
11 /**	Global functions and variables for the Interface Kit */
12 
13 #include "truncate_string.h"
14 #include "utf8_functions.h"
15 
16 #include <ApplicationPrivate.h>
17 #include <AppServerLink.h>
18 #include <DefaultColors.h>
19 #include <InputServerTypes.h>
20 #include <input_globals.h>
21 #include <ServerProtocol.h>
22 #include <ServerReadOnlyMemory.h>
23 #include <WidthBuffer.h>
24 #include <WindowInfo.h>
25 
26 #include <Font.h>
27 #include <InterfaceDefs.h>
28 #include <Menu.h>
29 #include <Roster.h>
30 #include <ScrollBar.h>
31 #include <Screen.h>
32 #include <String.h>
33 #include <TextView.h>
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 
40 // Private definitions not placed in public headers
41 void _init_global_fonts_();
42 extern "C" status_t _fini_interface_kit_();
43 
44 using namespace BPrivate;
45 
46 // some other weird struct exported by BeOS, it's not initialized, though
47 struct general_ui_info {
48 	rgb_color	background_color;
49 	rgb_color	mark_color;
50 	rgb_color	highlight_color;
51 	bool		color_frame;
52 	rgb_color	window_frame_color;
53 };
54 
55 struct general_ui_info general_info;
56 
57 menu_info *_menu_info_ptr_;
58 
59 extern "C" const char B_NOTIFICATION_SENDER[] = "be:sender";
60 
61 static const rgb_color _kDefaultColors[kNumColors] = {
62 	{216, 216, 216, 255},	// B_PANEL_BACKGROUND_COLOR
63 	{216, 216, 216, 255},	// B_MENU_BACKGROUND_COLOR
64 	{255, 203, 0, 255},		// B_WINDOW_TAB_COLOR
65 	{0, 0, 229, 255},		// B_KEYBOARD_NAVIGATION_COLOR
66 	{51, 102, 152, 255},	// B_DESKTOP_COLOR
67 	{153, 153, 153, 255},	// B_MENU_SELECTED_BACKGROUND_COLOR
68 	{0, 0, 0, 255},			// B_MENU_ITEM_TEXT_COLOR
69 	{0, 0, 0, 255},			// B_MENU_SELECTED_ITEM_TEXT_COLOR
70 	{0, 0, 0, 255},			// B_MENU_SELECTED_BORDER_COLOR
71 	{0, 0, 0, 255},			// B_PANEL_TEXT_COLOR
72 	{255, 255, 255, 255},	// B_DOCUMENT_BACKGROUND_COLOR
73 	{0, 0, 0, 255},			// B_DOCUMENT_TEXT_COLOR
74 	{245, 245, 245, 255},	// B_CONTROL_BACKGROUND_COLOR
75 	{0, 0, 0, 255},			// B_CONTROL_TEXT_COLOR
76 	{0, 0, 0, 255},			// B_CONTROL_BORDER_COLOR
77 	{102, 152, 203, 255},	// B_CONTROL_HIGHLIGHT_COLOR
78 	{0, 0, 0, 255},			// B_NAVIGATION_PULSE_COLOR
79 	{255, 255, 255, 255},	// B_SHINE_COLOR
80 	{0, 0, 0, 255},			// B_SHADOW_COLOR
81 	{255, 255, 0, 255},		// B_TOOLTIP_BACKGROUND_COLOR
82 	{0, 0, 0, 255},			// B_TOOLTIP_TEXT_COLOR
83 	{0, 0, 0, 255},			// B_WINDOW_TEXT_COLOR
84 	{232, 232, 232, 255},	// B_WINDOW_INACTIVE_TAB_COLOR
85 	{80, 80, 80, 255},		// B_WINDOW_INACTIVE_TEXT_COLOR
86 	// 100...
87 	{0, 255, 0, 255},		// B_SUCCESS_COLOR
88 	{255, 0, 0, 255},		// B_FAILURE_COLOR
89 	{}
90 };
91 const rgb_color* BPrivate::kDefaultColors = &_kDefaultColors[0];
92 
93 
94 static status_t
95 mode2parms(uint32 space, uint32 *out_space, int32 *width, int32 *height)
96 {
97 	status_t status = B_OK;
98 
99 	switch (space) {
100 		case B_8_BIT_640x480:
101 			*out_space = B_CMAP8;
102 			*width = 640; *height = 480;
103 			break;
104 		case B_8_BIT_800x600:
105 			*out_space = B_CMAP8;
106 			*width = 800; *height = 600;
107 			break;
108 		case B_8_BIT_1024x768:
109 			*out_space = B_CMAP8;
110 			*width = 1024; *height = 768;
111 			break;
112 		case B_8_BIT_1280x1024:
113 			*out_space = B_CMAP8;
114 			*width = 1280; *height = 1024;
115 			break;
116 		case B_8_BIT_1600x1200:
117 			*out_space = B_CMAP8;
118 			*width = 1600; *height = 1200;
119 			break;
120 		case B_16_BIT_640x480:
121 			*out_space = B_RGB16;
122 			*width = 640; *height = 480;
123 			break;
124 		case B_16_BIT_800x600:
125 			*out_space = B_RGB16;
126 			*width = 800; *height = 600;
127 			break;
128 		case B_16_BIT_1024x768:
129 			*out_space = B_RGB16;
130 			*width = 1024; *height = 768;
131 			break;
132 		case B_16_BIT_1280x1024:
133 			*out_space = B_RGB16;
134 			*width = 1280; *height = 1024;
135 			break;
136 		case B_16_BIT_1600x1200:
137 			*out_space = B_RGB16;
138 			*width = 1600; *height = 1200;
139 			break;
140 		case B_32_BIT_640x480:
141 			*out_space = B_RGB32;
142 			*width = 640; *height = 480;
143 			break;
144 		case B_32_BIT_800x600:
145 			*out_space = B_RGB32;
146 			*width = 800; *height = 600;
147 			break;
148 		case B_32_BIT_1024x768:
149 			*out_space = B_RGB32;
150 			*width = 1024; *height = 768;
151 			break;
152 		case B_32_BIT_1280x1024:
153 			*out_space = B_RGB32;
154 			*width = 1280; *height = 1024;
155 			break;
156 		case B_32_BIT_1600x1200:
157 			*out_space = B_RGB32;
158 			*width = 1600; *height = 1200;
159 			break;
160     		case B_8_BIT_1152x900:
161     			*out_space = B_CMAP8;
162     			*width = 1152; *height = 900;
163     			break;
164     		case B_16_BIT_1152x900:
165     			*out_space = B_RGB16;
166     			*width = 1152; *height = 900;
167     			break;
168     		case B_32_BIT_1152x900:
169     			*out_space = B_RGB32;
170     			*width = 1152; *height = 900;
171     			break;
172 		case B_15_BIT_640x480:
173 			*out_space = B_RGB15;
174 			*width = 640; *height = 480;
175 			break;
176 		case B_15_BIT_800x600:
177 			*out_space = B_RGB15;
178 			*width = 800; *height = 600;
179 			break;
180 		case B_15_BIT_1024x768:
181 			*out_space = B_RGB15;
182 			*width = 1024; *height = 768;
183 			break;
184 		case B_15_BIT_1280x1024:
185 			*out_space = B_RGB15;
186 			*width = 1280; *height = 1024;
187 			break;
188 		case B_15_BIT_1600x1200:
189 			*out_space = B_RGB15;
190 			*width = 1600; *height = 1200;
191 			break;
192     		case B_15_BIT_1152x900:
193     			*out_space = B_RGB15;
194     			*width = 1152; *height = 900;
195     			break;
196     		default:
197     			status = B_BAD_VALUE;
198     			break;
199 	}
200 
201 	return status;
202 }
203 
204 
205 _IMPEXP_BE const color_map *
206 system_colors()
207 {
208 	return BScreen(B_MAIN_SCREEN_ID).ColorMap();
209 }
210 
211 
212 _IMPEXP_BE status_t
213 set_screen_space(int32 index, uint32 space, bool stick)
214 {
215 	int32 width;
216 	int32 height;
217 	uint32 depth;
218 
219 	status_t status = mode2parms(space, &depth, &width, &height);
220 	if (status < B_OK)
221 		return status;
222 
223 	BScreen screen(B_MAIN_SCREEN_ID);
224 	display_mode mode;
225 
226 	// TODO: What about refresh rate ?
227 	// currently we get it from the current video mode, but
228 	// this might be not so wise.
229 	status = screen.GetMode(index, &mode);
230 	if (status < B_OK)
231 		return status;
232 
233 	mode.virtual_width = width;
234 	mode.virtual_height = height;
235 	mode.space = depth;
236 
237 	return screen.SetMode(index, &mode, stick);
238 }
239 
240 
241 _IMPEXP_BE status_t
242 get_scroll_bar_info(scroll_bar_info *info)
243 {
244 	if (info == NULL)
245 		return B_BAD_VALUE;
246 
247 	BPrivate::AppServerLink link;
248 	link.StartMessage(AS_GET_SCROLLBAR_INFO);
249 
250 	int32 code;
251 	if (link.FlushWithReply(code) == B_OK
252 		&& code == B_OK) {
253 		link.Read<scroll_bar_info>(info);
254 		return B_OK;
255 	}
256 
257 	return B_ERROR;
258 }
259 
260 
261 _IMPEXP_BE status_t
262 set_scroll_bar_info(scroll_bar_info *info)
263 {
264 	if (info == NULL)
265 		return B_BAD_VALUE;
266 
267 	BPrivate::AppServerLink link;
268 	int32 code;
269 
270 	link.StartMessage(AS_SET_SCROLLBAR_INFO);
271 	link.Attach<scroll_bar_info>(*info);
272 
273 	if (link.FlushWithReply(code) == B_OK
274 		&& code == B_OK)
275 		return B_OK;
276 
277 	return B_ERROR;
278 }
279 
280 
281 _IMPEXP_BE status_t
282 get_mouse_type(int32 *type)
283 {
284 	BMessage command(IS_GET_MOUSE_TYPE);
285 	BMessage reply;
286 
287 	_control_input_server_(&command, &reply);
288 
289 	if(reply.FindInt32("mouse_type", type) != B_OK)
290 		return B_ERROR;
291 
292 	return B_OK;
293 }
294 
295 
296 _IMPEXP_BE status_t
297 set_mouse_type(int32 type)
298 {
299 	BMessage command(IS_SET_MOUSE_TYPE);
300 	BMessage reply;
301 
302 	command.AddInt32("mouse_type", type);
303 	return _control_input_server_(&command, &reply);
304 }
305 
306 
307 _IMPEXP_BE status_t
308 get_mouse_map(mouse_map *map)
309 {
310 	BMessage command(IS_GET_MOUSE_MAP);
311 	BMessage reply;
312 	const void *data = 0;
313 	ssize_t count;
314 
315 	_control_input_server_(&command, &reply);
316 
317 	if (reply.FindData("mousemap", B_RAW_TYPE, &data, &count) != B_OK)
318 		return B_ERROR;
319 
320 	memcpy(map, data, count);
321 
322 	return B_OK;
323 }
324 
325 
326 _IMPEXP_BE status_t
327 set_mouse_map(mouse_map *map)
328 {
329 	BMessage command(IS_SET_MOUSE_MAP);
330 	BMessage reply;
331 
332 	command.AddData("mousemap", B_RAW_TYPE, map, sizeof(mouse_map));
333 	return _control_input_server_(&command, &reply);
334 }
335 
336 
337 _IMPEXP_BE status_t
338 get_click_speed(bigtime_t *speed)
339 {
340 	BMessage command(IS_GET_CLICK_SPEED);
341 	BMessage reply;
342 
343 	_control_input_server_(&command, &reply);
344 
345 	if (reply.FindInt64("speed", speed) != B_OK)
346 		*speed = 500000;
347 
348 	return B_OK;
349 }
350 
351 
352 _IMPEXP_BE status_t
353 set_click_speed(bigtime_t speed)
354 {
355 	BMessage command(IS_SET_CLICK_SPEED);
356 	BMessage reply;
357 	command.AddInt64("speed", speed);
358 	return _control_input_server_(&command, &reply);
359 }
360 
361 
362 _IMPEXP_BE status_t
363 get_mouse_speed(int32 *speed)
364 {
365 	BMessage command(IS_GET_MOUSE_SPEED);
366 	BMessage reply;
367 
368 	_control_input_server_(&command, &reply);
369 
370 	if (reply.FindInt32("speed", speed) != B_OK)
371 		*speed = 65536;
372 
373 	return B_OK;
374 }
375 
376 
377 _IMPEXP_BE status_t
378 set_mouse_speed(int32 speed)
379 {
380 	BMessage command(IS_SET_MOUSE_SPEED);
381 	BMessage reply;
382 	command.AddInt32("speed", speed);
383 	return _control_input_server_(&command, &reply);
384 }
385 
386 
387 _IMPEXP_BE status_t
388 get_mouse_acceleration(int32 *speed)
389 {
390 	BMessage command(IS_GET_MOUSE_ACCELERATION);
391 	BMessage reply;
392 
393 	_control_input_server_(&command, &reply);
394 
395 	if (reply.FindInt32("speed", speed) != B_OK)
396 		*speed = 65536;
397 
398 	return B_OK;
399 }
400 
401 
402 _IMPEXP_BE status_t
403 set_mouse_acceleration(int32 speed)
404 {
405 	BMessage command(IS_SET_MOUSE_ACCELERATION);
406 	BMessage reply;
407 	command.AddInt32("speed", speed);
408 	return _control_input_server_(&command, &reply);
409 }
410 
411 
412 _IMPEXP_BE status_t
413 get_key_repeat_rate(int32 *rate)
414 {
415 	BMessage command(IS_GET_KEY_REPEAT_RATE);
416 	BMessage reply;
417 
418 	_control_input_server_(&command, &reply);
419 
420 	if (reply.FindInt32("rate", rate) != B_OK)
421 		*rate = 250000;
422 
423 	return B_OK;
424 }
425 
426 
427 _IMPEXP_BE status_t
428 set_key_repeat_rate(int32 rate)
429 {
430 	BMessage command(IS_SET_KEY_REPEAT_RATE);
431 	BMessage reply;
432 	command.AddInt32("rate", rate);
433 	return _control_input_server_(&command, &reply);
434 }
435 
436 
437 _IMPEXP_BE status_t
438 get_key_repeat_delay(bigtime_t *delay)
439 {
440 	BMessage command(IS_GET_KEY_REPEAT_DELAY);
441 	BMessage reply;
442 
443 	_control_input_server_(&command, &reply);
444 
445 	if (reply.FindInt64("delay", delay) != B_OK)
446 		*delay = 200;
447 
448 	return B_OK;
449 }
450 
451 
452 _IMPEXP_BE status_t
453 set_key_repeat_delay(bigtime_t  delay)
454 {
455 	BMessage command(IS_SET_KEY_REPEAT_DELAY);
456 	BMessage reply;
457 	command.AddInt64("delay", delay);
458 	return _control_input_server_(&command, &reply);
459 }
460 
461 
462 _IMPEXP_BE uint32
463 modifiers()
464 {
465 	BMessage command(IS_GET_MODIFIERS);
466 	BMessage reply;
467 	int32 err, modifier;
468 
469 	_control_input_server_(&command, &reply);
470 
471 	if (reply.FindInt32("status", &err) != B_OK)
472 		return 0;
473 
474 	if (reply.FindInt32("modifiers", &modifier) != B_OK)
475 		return 0;
476 
477 	return modifier;
478 }
479 
480 
481 _IMPEXP_BE status_t
482 get_key_info(key_info *info)
483 {
484 	BMessage command(IS_GET_KEY_INFO);
485 	BMessage reply;
486 	const void *data = 0;
487 	int32 err;
488 	ssize_t count;
489 
490 	_control_input_server_(&command, &reply);
491 
492 	if (reply.FindInt32("status", &err) != B_OK)
493 		return B_ERROR;
494 
495 	if (reply.FindData("key_info", B_ANY_TYPE, &data, &count) != B_OK)
496 		return B_ERROR;
497 
498 	memcpy(info, data, count);
499 	return B_OK;
500 }
501 
502 
503 _IMPEXP_BE void
504 get_key_map(key_map **map, char **key_buffer)
505 {
506 	BMessage command(IS_GET_KEY_MAP);
507 	BMessage reply;
508 	ssize_t map_count, key_count;
509 	const void *map_array = 0, *key_array = 0;
510 
511 	_control_input_server_(&command, &reply);
512 
513 	if (reply.FindData("keymap", B_ANY_TYPE, &map_array, &map_count) != B_OK) {
514 		*map = 0; *key_buffer = 0;
515 		return;
516 	}
517 
518 	if (reply.FindData("key_buffer", B_ANY_TYPE, &key_array, &key_count) != B_OK) {
519 		*map = 0; *key_buffer = 0;
520 		return;
521 	}
522 
523 	*map = (key_map *)malloc(map_count);
524 	memcpy(*map, map_array, map_count);
525 	*key_buffer = (char *)malloc(key_count);
526 	memcpy(*key_buffer, key_array, key_count);
527 }
528 
529 
530 _IMPEXP_BE status_t
531 get_keyboard_id(uint16 *id)
532 {
533 	BMessage command(IS_GET_KEYBOARD_ID);
534 	BMessage reply;
535 	uint16 kid;
536 
537 	_control_input_server_(&command, &reply);
538 
539 	reply.FindInt16("id", (int16 *)&kid);
540 	*id = kid;
541 
542 	return B_OK;
543 }
544 
545 
546 _IMPEXP_BE void
547 set_modifier_key(uint32 modifier, uint32 key)
548 {
549 	BMessage command(IS_SET_MODIFIER_KEY);
550 	BMessage reply;
551 
552 	command.AddInt32("modifier", modifier);
553 	command.AddInt32("key", key);
554 	_control_input_server_(&command, &reply);
555 }
556 
557 
558 _IMPEXP_BE void
559 set_keyboard_locks(uint32 modifiers)
560 {
561 	BMessage command(IS_SET_KEYBOARD_LOCKS);
562 	BMessage reply;
563 
564 	command.AddInt32("locks", modifiers);
565 	_control_input_server_(&command, &reply);
566 }
567 
568 
569 _IMPEXP_BE status_t
570 _restore_key_map_()
571 {
572 	BMessage message(IS_RESTORE_KEY_MAP);
573 	BMessage reply;
574 
575 	return _control_input_server_(&message, &reply);
576 }
577 
578 
579 _IMPEXP_BE rgb_color
580 keyboard_navigation_color()
581 {
582 	// Queries the app_server
583 	return ui_color(B_KEYBOARD_NAVIGATION_COLOR);
584 }
585 
586 
587 _IMPEXP_BE int32
588 count_workspaces()
589 {
590 	int32 count = 1;
591 
592 	BPrivate::AppServerLink link;
593 	link.StartMessage(AS_COUNT_WORKSPACES);
594 
595 	status_t status;
596 	if (link.FlushWithReply(status) == B_OK && status == B_OK)
597 		link.Read<int32>(&count);
598 
599 	return count;
600 }
601 
602 
603 _IMPEXP_BE void
604 set_workspace_count(int32 count)
605 {
606 	BPrivate::AppServerLink link;
607 	link.StartMessage(AS_SET_WORKSPACE_COUNT);
608 	link.Attach<int32>(count);
609 	link.Flush();
610 }
611 
612 
613 _IMPEXP_BE int32
614 current_workspace()
615 {
616 	int32 index = 0;
617 
618 	BPrivate::AppServerLink link;
619 	link.StartMessage(AS_CURRENT_WORKSPACE);
620 
621 	int32 status;
622 	if (link.FlushWithReply(status) == B_OK && status == B_OK)
623 		link.Read<int32>(&index);
624 
625 	return index;
626 }
627 
628 
629 _IMPEXP_BE void
630 activate_workspace(int32 workspace)
631 {
632 	BPrivate::AppServerLink link;
633 	link.StartMessage(AS_ACTIVATE_WORKSPACE);
634 	link.Attach<int32>(workspace);
635 	link.Flush();
636 }
637 
638 
639 _IMPEXP_BE bigtime_t
640 idle_time()
641 {
642 	bigtime_t idletime = 0;
643 
644 	BPrivate::AppServerLink link;
645 	link.StartMessage(AS_IDLE_TIME);
646 
647 	int32 code;
648 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
649 		link.Read<int64>(&idletime);
650 
651 	return idletime;
652 }
653 
654 
655 _IMPEXP_BE void
656 run_select_printer_panel()
657 {
658 	// Launches the Printer prefs app via the Roster
659 	be_roster->Launch("application/x-vnd.Be-PRNT");
660 }
661 
662 
663 _IMPEXP_BE void
664 run_add_printer_panel()
665 {
666 	// Launches the Printer prefs app via the Roster and asks it to
667 	// add a printer
668 	// TODO: Implement
669 }
670 
671 
672 _IMPEXP_BE void
673 run_be_about()
674 {
675 	if (be_roster != NULL)
676 		be_roster->Launch("application/x-vnd.haiku-AboutHaiku");
677 }
678 
679 
680 _IMPEXP_BE void
681 set_focus_follows_mouse(bool follow)
682 {
683 	// obviously deprecated API
684 	set_mouse_mode(B_WARP_MOUSE);
685 }
686 
687 
688 _IMPEXP_BE bool
689 focus_follows_mouse()
690 {
691 	return mouse_mode() != B_NORMAL_MOUSE;
692 }
693 
694 
695 _IMPEXP_BE void
696 set_mouse_mode(mode_mouse mode)
697 {
698 	BPrivate::AppServerLink link;
699 	link.StartMessage(AS_SET_MOUSE_MODE);
700 	link.Attach<mode_mouse>(mode);
701 	link.Flush();
702 }
703 
704 
705 _IMPEXP_BE mode_mouse
706 mouse_mode()
707 {
708 	// Gets the focus-follows-mouse style, such as normal, B_WARP_MOUSE, etc.
709 	mode_mouse mode = B_NORMAL_MOUSE;
710 
711 	BPrivate::AppServerLink link;
712 	link.StartMessage(AS_GET_MOUSE_MODE);
713 
714 	int32 code;
715 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
716 		link.Read<mode_mouse>(&mode);
717 
718 	return mode;
719 }
720 
721 
722 _IMPEXP_BE rgb_color
723 ui_color(color_which which)
724 {
725 	int32 index = color_which_to_index(which);
726 	if (index < 0 || index >= kNumColors) {
727 		fprintf(stderr, "ui_color(): unknown color_which %d\n", which);
728 		return make_color(0, 0, 0);
729 	}
730 
731 	if (be_app) {
732 		server_read_only_memory* shared = BApplication::Private::ServerReadOnlyMemory();
733 		return shared->colors[index];
734 	}
735 
736 	return kDefaultColors[index];
737 }
738 
739 
740 _IMPEXP_BE void
741 set_ui_color(const color_which &which, const rgb_color &color)
742 {
743 	int32 index = color_which_to_index(which);
744 	if (index < 0 || index >= kNumColors) {
745 		fprintf(stderr, "set_ui_color(): unknown color_which %d\n", which);
746 		return;
747 	}
748 
749 	BPrivate::AppServerLink link;
750 	link.StartMessage(AS_SET_UI_COLOR);
751 	link.Attach<color_which>(which);
752 	link.Attach<rgb_color>(color);
753 	link.Flush();
754 }
755 
756 
757 _IMPEXP_BE rgb_color
758 tint_color(rgb_color color, float tint)
759 {
760 	rgb_color result;
761 
762 	#define LIGHTEN(x) ((uint8)(255.0f - (255.0f - x) * tint))
763 	#define DARKEN(x)  ((uint8)(x * (2 - tint)))
764 
765 	if (tint < 1.0f) {
766 		result.red   = LIGHTEN(color.red);
767 		result.green = LIGHTEN(color.green);
768 		result.blue  = LIGHTEN(color.blue);
769 		result.alpha = color.alpha;
770 	} else {
771 		result.red   = DARKEN(color.red);
772 		result.green = DARKEN(color.green);
773 		result.blue  = DARKEN(color.blue);
774 		result.alpha = color.alpha;
775 	}
776 
777 	#undef LIGHTEN
778 	#undef DARKEN
779 
780 	return result;
781 }
782 
783 
784 rgb_color shift_color(rgb_color color, float shift);
785 
786 rgb_color
787 shift_color(rgb_color color, float shift)
788 {
789 	return tint_color(color, shift);
790 }
791 
792 
793 extern "C" status_t
794 _init_interface_kit_()
795 {
796 	sem_id widthSem = create_sem(0, "BTextView WidthBuffer Sem");
797 	if (widthSem < 0)
798 		return widthSem;
799 	BTextView::sWidthSem = widthSem;
800 	BTextView::sWidthAtom = 0;
801 	BTextView::sWidths = new _BWidthBuffer_;
802 
803 	_init_global_fonts_();
804 
805 	_menu_info_ptr_ = &BMenu::sMenuInfo;
806 	status_t status = get_menu_info(&BMenu::sMenuInfo);
807 
808 	general_info.background_color = ui_color(B_PANEL_BACKGROUND_COLOR);
809 	general_info.mark_color.set_to(0, 0, 0);
810 	general_info.highlight_color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
811 	general_info.window_frame_color = ui_color(B_WINDOW_TAB_COLOR);
812 	general_info.color_frame = true;
813 
814 	// TODO: fill the other static members
815 
816 	return status;
817 }
818 
819 
820 extern "C" status_t
821 _fini_interface_kit_()
822 {
823 	//TODO: Implement ?
824 
825 	return B_OK;
826 }
827 
828 
829 //	#pragma mark -
830 
831 
832 /*!
833 	\brief private function used by Deskbar to set window decor
834 	Note, we don't have to be compatible here, and could just change
835 	the Deskbar not to use this anymore
836 	\param theme The theme to choose
837 
838 	- \c 0: BeOS
839 	- \c 1: AmigaOS
840 	- \c 2: Win95
841 	- \c 3: MacOS
842 */
843 void
844 __set_window_decor(int32 theme)
845 {
846 	BPrivate::AppServerLink link;
847 	link.StartMessage(AS_R5_SET_DECORATOR);
848 	link.Attach<int32>(theme);
849 	link.Flush();
850 }
851 
852 
853 namespace BPrivate {
854 
855 /*!
856 	\brief queries the server for the number of available decorators
857 	\return the number of available decorators
858 */
859 int32
860 count_decorators(void)
861 {
862 	BPrivate::AppServerLink link;
863 	link.StartMessage(AS_COUNT_DECORATORS);
864 
865 	int32 code;
866 	int32 count = -1;
867 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
868 		link.Read<int32>(&count);
869 
870 	return count;
871 }
872 
873 /*!
874 	\brief queries the server for the index of the current decorators
875 	\return the current decorator's index
876 
877 	If for some bizarre reason this function fails, it returns -1
878 */
879 int32
880 get_decorator(void)
881 {
882 	BPrivate::AppServerLink link;
883 	link.StartMessage(AS_GET_DECORATOR);
884 
885 	int32 code;
886 	int32 index = -1;
887 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
888 		link.Read<int32>(&index);
889 
890 	return index;
891 }
892 
893 
894 /*!
895 	\brief queries the server for the name of the decorator with a certain index
896 	\param index The index of the decorator to get the name for
897 	\param name BString to receive the name of the decorator
898 	\return B_OK if successful, B_ERROR if not
899 */
900 status_t
901 get_decorator_name(const int32 &index, BString &name)
902 {
903 	BPrivate::AppServerLink link;
904 	link.StartMessage(AS_GET_DECORATOR_NAME);
905 	link.Attach<int32>(index);
906 
907 	int32 code;
908 	if (link.FlushWithReply(code) == B_OK && code == B_OK) {
909 		char *string;
910 		if (link.ReadString(&string) == B_OK) {
911 			name = string;
912 			free(string);
913 			return B_OK;
914 		}
915 	}
916 
917 	return B_ERROR;
918 }
919 
920 /*!
921 	\brief asks the server to draw a decorator preview into a BBitmap
922 	\param index The index of the decorator to get the name for
923 	\param bitmap BBitmap to receive the preview
924 	\return B_OK if successful, B_ERROR if not.
925 
926 	This is currently unimplemented.
927 */
928 status_t
929 get_decorator_preview(const int32 &index, BBitmap *bitmap)
930 {
931 	// TODO: implement get_decorator_preview
932 	return B_ERROR;
933 }
934 
935 
936 /*!
937 	\brief Private function which sets the window decorator for the system.
938 	\param index Index of the decorator to set
939 
940 	If the index is invalid, this function and the server do nothing
941 */
942 status_t
943 set_decorator(const int32 &index)
944 {
945 	if (index < 0)
946 		return B_BAD_VALUE;
947 
948 	BPrivate::AppServerLink link;
949 
950 	link.StartMessage(AS_SET_DECORATOR);
951 	link.Attach<int32>(index);
952 	link.Flush();
953 
954 	return B_OK;
955 }
956 
957 }	// namespace BPrivate
958 
959 // These methods were marked with "Danger, will Robinson!" in
960 // the OpenTracker source, so we might not want to be compatible
961 // here.
962 // In any way, we would need to update Deskbar to use our
963 // replacements, so we could as well just implement them...
964 //
965 // They are defined (also the complete window_info structure) in
966 // src/apps/deskbar/WindowMenuItem.h
967 
968 struct window_info;
969 
970 void do_window_action(int32 window_id, int32 action,
971 		BRect zoomRect, bool zoom);
972 window_info	*get_window_info(int32 a_token);
973 int32 *get_token_list(team_id app, int32 *count);
974 void do_bring_to_front_team(BRect zoomRect, team_id app, bool zoom);
975 void do_minimize_team(BRect zoomRect, team_id team, bool zoom);
976 
977 
978 void
979 do_window_action(int32 windowToken, int32 action,
980 	BRect zoomRect, bool zoom)
981 {
982 	BPrivate::AppServerLink link;
983 
984 	link.StartMessage(AS_WINDOW_ACTION);
985 	link.Attach<int32>(windowToken);
986 	link.Attach<int32>(action);
987 		// we don't have any zooming effect
988 
989 	link.Flush();
990 }
991 
992 
993 window_info	*
994 get_window_info(int32 serverToken)
995 {
996 	BPrivate::AppServerLink link;
997 
998 	link.StartMessage(AS_GET_WINDOW_INFO);
999 	link.Attach<int32>(serverToken);
1000 
1001 	int32 code;
1002 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1003 		return NULL;
1004 
1005 	int32 size;
1006 	link.Read<int32>(&size);
1007 
1008 	client_window_info* info = (client_window_info*)malloc(size);
1009 	if (info == NULL)
1010 		return NULL;
1011 
1012 	link.Read(info, size);
1013 	return info;
1014 }
1015 
1016 
1017 int32 *
1018 get_token_list(team_id team, int32 *_count)
1019 {
1020 	BPrivate::AppServerLink link;
1021 
1022 	link.StartMessage(AS_GET_WINDOW_LIST);
1023 	link.Attach<team_id>(team);
1024 
1025 	int32 code;
1026 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1027 		return NULL;
1028 
1029 	int32 count;
1030 	link.Read<int32>(&count);
1031 
1032 	int32* tokens = (int32*)malloc(count * sizeof(int32));
1033 	if (tokens == NULL)
1034 		return NULL;
1035 
1036 	link.Read(tokens, count * sizeof(int32));
1037 	*_count = count;
1038 	return tokens;
1039 }
1040 
1041 
1042 void
1043 do_bring_to_front_team(BRect zoomRect, team_id team, bool zoom)
1044 {
1045 	BPrivate::AppServerLink link;
1046 
1047 	link.StartMessage(AS_BRING_TEAM_TO_FRONT);
1048 	link.Attach<team_id>(team);
1049 		// we don't have any zooming effect
1050 
1051 	link.Flush();
1052 }
1053 
1054 
1055 void
1056 do_minimize_team(BRect zoomRect, team_id team, bool zoom)
1057 {
1058 	BPrivate::AppServerLink link;
1059 
1060 	link.StartMessage(AS_MINIMIZE_TEAM);
1061 	link.Attach<team_id>(team);
1062 		// we don't have any zooming effect
1063 
1064 	link.Flush();
1065 }
1066 
1067 
1068 //	#pragma mark - truncate string
1069 
1070 
1071 static char*
1072 write_ellipsis(char* dst)
1073 {
1074 	strcpy(dst, B_UTF8_ELLIPSIS);
1075 	// The UTF-8 character spans over 3 bytes
1076 	return dst + 3;
1077 }
1078 
1079 
1080 bool
1081 truncate_end(const char* source, char* dest, uint32 numChars,
1082 	const float* escapementArray, float width, float ellipsisWidth, float size)
1083 {
1084 	float currentWidth = 0.0;
1085 	ellipsisWidth /= size;	// test if this is as accurate as escapementArray * size
1086 	width /= size;
1087 	uint32 lastFit = 0, c;
1088 
1089 	for (c = 0; c < numChars; c++) {
1090 		currentWidth += escapementArray[c];
1091 		if (currentWidth + ellipsisWidth <= width)
1092 			lastFit = c;
1093 
1094 		if (currentWidth > width)
1095 			break;
1096 	}
1097 
1098 	if (c == numChars) {
1099 		// string fits into width
1100 		return false;
1101 	}
1102 
1103 	if (c == 0) {
1104 		// there is no space for the ellipsis
1105 		strcpy(dest, "");
1106 		return true;
1107 	}
1108 
1109 	// copy string to destination
1110 
1111 	for (uint32 i = 0; i < lastFit + 1; i++) {
1112 		// copy one glyph
1113 		do {
1114 			*dest++ = *source++;
1115 		} while (IsInsideGlyph(*source));
1116 	}
1117 
1118 	// write ellipsis and terminate
1119 
1120 	dest = write_ellipsis(dest);
1121 	*dest = '\0';
1122 	return true;
1123 }
1124 
1125 
1126 static char*
1127 copy_from_end(const char* src, char* dst, uint32 numChars, uint32 length,
1128 	const float* escapementArray, float width, float ellipsisWidth, float size)
1129 {
1130 	const char* originalStart = src;
1131 	src += length - 1;
1132 	float currentWidth = 0.0;
1133 	for (int32 c = numChars - 1; c > 0; c--) {
1134 		currentWidth += escapementArray[c] * size;
1135 		if (currentWidth > width) {
1136 			// ups, we definitely don't fit. go back until the ellipsis fits
1137 			currentWidth += ellipsisWidth;
1138 			// go forward again until ellipsis fits (already beyond the target)
1139 			for (uint32 c2 = c; c2 < numChars; c2++) {
1140 //printf(" backward: %c (%ld) (%.1f - %.1f = %.1f)\n", *dst, c2, currentWidth, escapementArray[c2] * size, currentWidth - escapementArray[c2] * size);
1141 				currentWidth -= escapementArray[c2] * size;
1142 				do {
1143 					src++;
1144 				} while (IsInsideGlyph(*src));
1145 				// see if we went back enough
1146 				if (currentWidth <= width)
1147 					break;
1148 			}
1149 			break;
1150 		} else {
1151 			// go back one glyph
1152 			do {
1153 				src--;
1154 			} while (IsInsideGlyph(*src));
1155 		}
1156 	}
1157 	// copy from the end of the string
1158 	uint32 bytesToCopy = originalStart + length - src;
1159 	memcpy(dst, src, bytesToCopy);
1160 	dst += bytesToCopy;
1161 	return dst;
1162 }
1163 
1164 
1165 bool
1166 truncate_middle(const char* source, char* dest, uint32 numChars,
1167 	const float* escapementArray, float width, float ellipsisWidth, float size)
1168 {
1169 	// find visual center
1170 
1171 	ellipsisWidth /= size;	// test if this is as accurate as escapementArray * size
1172 	width /= size;
1173 
1174 	float halfWidth = (width - ellipsisWidth) / 2.0;
1175 	float leftWidth = 0.0, rightWidth = 0.0;
1176 	uint32 left, right;
1177 
1178 	// coming from left...
1179 
1180 	for (left = 0; left < numChars; left++) {
1181 		if (leftWidth + escapementArray[left] > halfWidth)
1182 			break;
1183 
1184 		leftWidth += escapementArray[left];
1185 	}
1186 
1187 	if (left == numChars) {
1188 		// string is smaller than half of the maximum width
1189 		return false;
1190 	}
1191 
1192 	// coming from right...
1193 
1194 	for (right = numChars; right-- > left; ) {
1195 		if (rightWidth + escapementArray[right] > halfWidth)
1196 			break;
1197 
1198 		rightWidth += escapementArray[right];
1199 	}
1200 
1201 	if (left >= right) {
1202 		// string is smaller than the maximum width
1203 		return false;
1204 	}
1205 
1206 	if (left == 0 || right >= numChars - 1) {
1207 		// there is no space for the ellipsis
1208 		strcpy(dest, "");
1209 		return true;
1210 	}
1211 
1212 	// see if the gap between left/right is smaller than the ellipsis
1213 
1214 	float totalWidth = rightWidth + leftWidth;
1215 
1216 	for (uint32 i = left; i < right; i++) {
1217 		totalWidth += escapementArray[i];
1218 		if (totalWidth > width)
1219 			break;
1220 	}
1221 
1222 	if (totalWidth <= width) {
1223 		// the whole string fits!
1224 		return false;
1225 	}
1226 
1227 	// The ellipsis now definitely fits, but let's
1228 	// see if we can add another character
1229 
1230 	totalWidth = ellipsisWidth + rightWidth + leftWidth;
1231 
1232 	if (left > numChars - right) {
1233 		// try right letter first
1234 		if (escapementArray[right] + totalWidth <= width)
1235 			right--;
1236 		else if (escapementArray[left] + totalWidth <= width)
1237 			left++;
1238 	} else {
1239 		// try left letter first
1240 		if (escapementArray[left] + totalWidth <= width)
1241 			left++;
1242 		else if (escapementArray[right] + totalWidth <= width)
1243 			right--;
1244 	}
1245 
1246 	// copy characters
1247 
1248 	for (uint32 i = 0; i < left; i++) {
1249 		// copy one glyph
1250 		do {
1251 			*dest++ = *source++;
1252 		} while (IsInsideGlyph(*source));
1253 	}
1254 
1255 	dest = write_ellipsis(dest);
1256 
1257 	for (uint32 i = left; i < numChars; i++) {
1258 		// copy one glyph
1259 		do {
1260 			if (i >= right)
1261 				*dest++ = *source++;
1262 			else
1263 				source++;
1264 		} while (IsInsideGlyph(*source));
1265 	}
1266 
1267 	// terminate
1268 	dest[0] = '\0';
1269 	return true;
1270 }
1271 
1272 
1273 // ToDo: put into BPrivate namespace
1274 void
1275 truncate_string(const char* string, uint32 mode, float width,
1276 	char* result, const float* escapementArray, float fontSize,
1277 	float ellipsisWidth, int32 length, int32 numChars)
1278 {
1279 	// ToDo: that's actually not correct: the string could be smaller than ellipsisWidth
1280 	if (string == NULL /*|| width < ellipsisWidth*/) {
1281 		// we don't have room for a single glyph
1282 		strcpy(result, "");
1283 		return;
1284 	}
1285 
1286 	// iterate over glyphs and copy source into result string
1287 	// one glyph at a time as long as we have room for the "…" yet
1288 	char* dest = result;
1289 	const char* source = string;
1290 	bool truncated = true;
1291 
1292 	switch (mode) {
1293 		case B_TRUNCATE_BEGINNING: {
1294 			dest = copy_from_end(source, dest, numChars, length,
1295 				escapementArray, width, ellipsisWidth, fontSize);
1296 			// "dst" points to the position behind the last glyph that
1297 			// was copied.
1298 			int32 dist = dest - result;
1299 			// we didn't terminate yet
1300 			*dest = 0;
1301 			if (dist < length) {
1302 				// TODO: Is there a smarter way?
1303 				char* temp = new char[dist + 4];
1304 				char* t = temp;
1305 				// append "…"
1306 				t = write_ellipsis(t);
1307 				// shuffle arround strings so that "…" is prepended
1308 				strcpy(t, result);
1309 				strcpy(result, temp);
1310 				delete[] temp;
1311 /*						char t = result[3];
1312 				memmove(&result[3], result, dist + 1);
1313 				write_ellipsis(result);
1314 				result[3] = t;*/
1315 			}
1316 			break;
1317 		}
1318 
1319 		case B_TRUNCATE_END:
1320 			truncated = truncate_end(source, dest, numChars, escapementArray,
1321 				width, ellipsisWidth, fontSize);
1322 			break;
1323 
1324 		case B_TRUNCATE_SMART:
1325 			// TODO: implement, though it was never implemented on R5
1326 			// FALL THROUGH (at least do something)
1327 		case B_TRUNCATE_MIDDLE:
1328 			truncated = truncate_middle(source, dest, numChars, escapementArray,
1329 				width, ellipsisWidth, fontSize);
1330 			break;
1331 	}
1332 
1333 	if (!truncated) {
1334 		// copy string to destination verbatim
1335 		strlcpy(dest, source, length + 1);
1336 	}
1337 }
1338 
1339