/* * Copyright 2001-2009, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: * DarkWyrm * Caz * Axel Dörfler, axeld@pinc-software.de */ /*! Global functions and variables for the Interface Kit */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace BPrivate; // some other weird struct exported by BeOS, it's not initialized, though struct general_ui_info { rgb_color background_color; rgb_color mark_color; rgb_color highlight_color; bool color_frame; rgb_color window_frame_color; }; struct general_ui_info general_info; menu_info *_menu_info_ptr_; extern "C" const char B_NOTIFICATION_SENDER[] = "be:sender"; static const rgb_color _kDefaultColors[kNumColors] = { {216, 216, 216, 255}, // B_PANEL_BACKGROUND_COLOR {216, 216, 216, 255}, // B_MENU_BACKGROUND_COLOR {255, 203, 0, 255}, // B_WINDOW_TAB_COLOR {0, 0, 229, 255}, // B_KEYBOARD_NAVIGATION_COLOR {51, 102, 152, 255}, // B_DESKTOP_COLOR {153, 153, 153, 255}, // B_MENU_SELECTED_BACKGROUND_COLOR {0, 0, 0, 255}, // B_MENU_ITEM_TEXT_COLOR {0, 0, 0, 255}, // B_MENU_SELECTED_ITEM_TEXT_COLOR {0, 0, 0, 255}, // B_MENU_SELECTED_BORDER_COLOR {0, 0, 0, 255}, // B_PANEL_TEXT_COLOR {255, 255, 255, 255}, // B_DOCUMENT_BACKGROUND_COLOR {0, 0, 0, 255}, // B_DOCUMENT_TEXT_COLOR {245, 245, 245, 255}, // B_CONTROL_BACKGROUND_COLOR {0, 0, 0, 255}, // B_CONTROL_TEXT_COLOR {0, 0, 0, 255}, // B_CONTROL_BORDER_COLOR {102, 152, 203, 255}, // B_CONTROL_HIGHLIGHT_COLOR {0, 0, 0, 255}, // B_NAVIGATION_PULSE_COLOR {255, 255, 255, 255}, // B_SHINE_COLOR {0, 0, 0, 255}, // B_SHADOW_COLOR {255, 255, 216, 255}, // B_TOOLTIP_BACKGROUND_COLOR {0, 0, 0, 255}, // B_TOOLTIP_TEXT_COLOR {0, 0, 0, 255}, // B_WINDOW_TEXT_COLOR {232, 232, 232, 255}, // B_WINDOW_INACTIVE_TAB_COLOR {80, 80, 80, 255}, // B_WINDOW_INACTIVE_TEXT_COLOR // 100... {0, 255, 0, 255}, // B_SUCCESS_COLOR {255, 0, 0, 255}, // B_FAILURE_COLOR {} }; const rgb_color* BPrivate::kDefaultColors = &_kDefaultColors[0]; namespace BPrivate { /*! Fills the \a width, \a height, and \a colorSpace parameters according to the window screen's mode. Returns \c true if the mode is known. */ bool get_mode_parameter(uint32 mode, int32& width, int32& height, uint32& colorSpace) { switch (mode) { case B_8_BIT_640x480: case B_8_BIT_800x600: case B_8_BIT_1024x768: case B_8_BIT_1152x900: case B_8_BIT_1280x1024: case B_8_BIT_1600x1200: colorSpace = B_CMAP8; break; case B_15_BIT_640x480: case B_15_BIT_800x600: case B_15_BIT_1024x768: case B_15_BIT_1152x900: case B_15_BIT_1280x1024: case B_15_BIT_1600x1200: colorSpace = B_RGB15; break; case B_16_BIT_640x480: case B_16_BIT_800x600: case B_16_BIT_1024x768: case B_16_BIT_1152x900: case B_16_BIT_1280x1024: case B_16_BIT_1600x1200: colorSpace = B_RGB16; break; case B_32_BIT_640x480: case B_32_BIT_800x600: case B_32_BIT_1024x768: case B_32_BIT_1152x900: case B_32_BIT_1280x1024: case B_32_BIT_1600x1200: colorSpace = B_RGB32; break; default: return false; } switch (mode) { case B_8_BIT_640x480: case B_15_BIT_640x480: case B_16_BIT_640x480: case B_32_BIT_640x480: width = 640; height = 480; break; case B_8_BIT_800x600: case B_15_BIT_800x600: case B_16_BIT_800x600: case B_32_BIT_800x600: width = 800; height = 600; break; case B_8_BIT_1024x768: case B_15_BIT_1024x768: case B_16_BIT_1024x768: case B_32_BIT_1024x768: width = 1024; height = 768; break; case B_8_BIT_1152x900: case B_15_BIT_1152x900: case B_16_BIT_1152x900: case B_32_BIT_1152x900: width = 1152; height = 900; break; case B_8_BIT_1280x1024: case B_15_BIT_1280x1024: case B_16_BIT_1280x1024: case B_32_BIT_1280x1024: width = 1280; height = 1024; break; case B_8_BIT_1600x1200: case B_15_BIT_1600x1200: case B_16_BIT_1600x1200: case B_32_BIT_1600x1200: width = 1600; height = 1200; break; } return true; } void get_workspaces_layout(uint32* _columns, uint32* _rows) { int32 columns = 1; int32 rows = 1; BPrivate::AppServerLink link; link.StartMessage(AS_GET_WORKSPACE_LAYOUT); status_t status; if (link.FlushWithReply(status) == B_OK && status == B_OK) { link.Read(&columns); link.Read(&rows); } if (_columns != NULL) *_columns = columns; if (_rows != NULL) *_rows = rows; } void set_workspaces_layout(uint32 columns, uint32 rows) { if (columns < 1 || rows < 1) return; BPrivate::AppServerLink link; link.StartMessage(AS_SET_WORKSPACE_LAYOUT); link.Attach(columns); link.Attach(rows); link.Flush(); } } // namespace BPrivate void set_subpixel_antialiasing(bool subpix) { BPrivate::AppServerLink link; link.StartMessage(AS_SET_SUBPIXEL_ANTIALIASING); link.Attach(subpix); link.Flush(); } status_t get_subpixel_antialiasing(bool* subpix) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_SUBPIXEL_ANTIALIASING); int32 status = B_ERROR; if (link.FlushWithReply(status) != B_OK || status < B_OK) return status; link.Read(subpix); return B_OK; } void set_hinting_mode(uint8 hinting) { BPrivate::AppServerLink link; link.StartMessage(AS_SET_HINTING); link.Attach(hinting); link.Flush(); } status_t get_hinting_mode(uint8* hinting) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_HINTING); int32 status = B_ERROR; if (link.FlushWithReply(status) != B_OK || status < B_OK) return status; link.Read(hinting); return B_OK; } void set_average_weight(uint8 averageWeight) { BPrivate::AppServerLink link; link.StartMessage(AS_SET_SUBPIXEL_AVERAGE_WEIGHT); link.Attach(averageWeight); link.Flush(); } status_t get_average_weight(uint8* averageWeight) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_SUBPIXEL_AVERAGE_WEIGHT); int32 status = B_ERROR; if (link.FlushWithReply(status) != B_OK || status < B_OK) return status; link.Read(averageWeight); return B_OK; } void set_is_subpixel_ordering_regular(bool subpixelOrdering) { BPrivate::AppServerLink link; link.StartMessage(AS_SET_SUBPIXEL_ORDERING); link.Attach(subpixelOrdering); link.Flush(); } status_t get_is_subpixel_ordering_regular(bool* subpixelOrdering) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_SUBPIXEL_ORDERING); int32 status = B_ERROR; if (link.FlushWithReply(status) != B_OK || status < B_OK) return status; link.Read(subpixelOrdering); return B_OK; } const color_map * system_colors() { return BScreen(B_MAIN_SCREEN_ID).ColorMap(); } status_t set_screen_space(int32 index, uint32 space, bool stick) { int32 width; int32 height; uint32 depth; if (!BPrivate::get_mode_parameter(space, width, height, depth)) return B_BAD_VALUE; BScreen screen(B_MAIN_SCREEN_ID); display_mode mode; // TODO: What about refresh rate ? // currently we get it from the current video mode, but // this might be not so wise. status_t status = screen.GetMode(index, &mode); if (status < B_OK) return status; mode.virtual_width = width; mode.virtual_height = height; mode.space = depth; return screen.SetMode(index, &mode, stick); } status_t get_scroll_bar_info(scroll_bar_info *info) { if (info == NULL) return B_BAD_VALUE; BPrivate::AppServerLink link; link.StartMessage(AS_GET_SCROLLBAR_INFO); int32 code; if (link.FlushWithReply(code) == B_OK && code == B_OK) { link.Read(info); return B_OK; } return B_ERROR; } status_t set_scroll_bar_info(scroll_bar_info *info) { if (info == NULL) return B_BAD_VALUE; BPrivate::AppServerLink link; int32 code; link.StartMessage(AS_SET_SCROLLBAR_INFO); link.Attach(*info); if (link.FlushWithReply(code) == B_OK && code == B_OK) return B_OK; return B_ERROR; } status_t get_mouse_type(int32 *type) { BMessage command(IS_GET_MOUSE_TYPE); BMessage reply; status_t err = _control_input_server_(&command, &reply); if (err != B_OK) return err; return reply.FindInt32("mouse_type", type); } status_t set_mouse_type(int32 type) { BMessage command(IS_SET_MOUSE_TYPE); BMessage reply; status_t err = command.AddInt32("mouse_type", type); if (err != B_OK) return err; return _control_input_server_(&command, &reply); } status_t get_mouse_map(mouse_map *map) { BMessage command(IS_GET_MOUSE_MAP); BMessage reply; const void *data = 0; ssize_t count; status_t err = _control_input_server_(&command, &reply); if (err == B_OK) err = reply.FindData("mousemap", B_RAW_TYPE, &data, &count); if (err != B_OK) return err; memcpy(map, data, count); return B_OK; } status_t set_mouse_map(mouse_map *map) { BMessage command(IS_SET_MOUSE_MAP); BMessage reply; status_t err = command.AddData("mousemap", B_RAW_TYPE, map, sizeof(mouse_map)); if (err != B_OK) return err; return _control_input_server_(&command, &reply); } status_t get_click_speed(bigtime_t *speed) { BMessage command(IS_GET_CLICK_SPEED); BMessage reply; status_t err = _control_input_server_(&command, &reply); if (err != B_OK) return err; if (reply.FindInt64("speed", speed) != B_OK) *speed = 500000; return B_OK; } status_t set_click_speed(bigtime_t speed) { BMessage command(IS_SET_CLICK_SPEED); BMessage reply; command.AddInt64("speed", speed); return _control_input_server_(&command, &reply); } status_t get_mouse_speed(int32 *speed) { BMessage command(IS_GET_MOUSE_SPEED); BMessage reply; status_t err = _control_input_server_(&command, &reply); if (err != B_OK) return err; if (reply.FindInt32("speed", speed) != B_OK) *speed = 65536; return B_OK; } status_t set_mouse_speed(int32 speed) { BMessage command(IS_SET_MOUSE_SPEED); BMessage reply; command.AddInt32("speed", speed); return _control_input_server_(&command, &reply); } status_t get_mouse_acceleration(int32 *speed) { BMessage command(IS_GET_MOUSE_ACCELERATION); BMessage reply; _control_input_server_(&command, &reply); if (reply.FindInt32("speed", speed) != B_OK) *speed = 65536; return B_OK; } status_t set_mouse_acceleration(int32 speed) { BMessage command(IS_SET_MOUSE_ACCELERATION); BMessage reply; command.AddInt32("speed", speed); return _control_input_server_(&command, &reply); } status_t get_key_repeat_rate(int32 *rate) { BMessage command(IS_GET_KEY_REPEAT_RATE); BMessage reply; _control_input_server_(&command, &reply); if (reply.FindInt32("rate", rate) != B_OK) *rate = 250000; return B_OK; } status_t set_key_repeat_rate(int32 rate) { BMessage command(IS_SET_KEY_REPEAT_RATE); BMessage reply; command.AddInt32("rate", rate); return _control_input_server_(&command, &reply); } status_t get_key_repeat_delay(bigtime_t *delay) { BMessage command(IS_GET_KEY_REPEAT_DELAY); BMessage reply; _control_input_server_(&command, &reply); if (reply.FindInt64("delay", delay) != B_OK) *delay = 200; return B_OK; } status_t set_key_repeat_delay(bigtime_t delay) { BMessage command(IS_SET_KEY_REPEAT_DELAY); BMessage reply; command.AddInt64("delay", delay); return _control_input_server_(&command, &reply); } uint32 modifiers() { BMessage command(IS_GET_MODIFIERS); BMessage reply; int32 err, modifier; _control_input_server_(&command, &reply); if (reply.FindInt32("status", &err) != B_OK) return 0; if (reply.FindInt32("modifiers", &modifier) != B_OK) return 0; return modifier; } status_t get_key_info(key_info *info) { BMessage command(IS_GET_KEY_INFO); BMessage reply; const void *data = 0; int32 err; ssize_t count; _control_input_server_(&command, &reply); if (reply.FindInt32("status", &err) != B_OK) return B_ERROR; if (reply.FindData("key_info", B_ANY_TYPE, &data, &count) != B_OK) return B_ERROR; memcpy(info, data, count); return B_OK; } void get_key_map(key_map **map, char **key_buffer) { _get_key_map(map, key_buffer, NULL); } void _get_key_map(key_map **map, char **key_buffer, ssize_t *key_buffer_size) { BMessage command(IS_GET_KEY_MAP); BMessage reply; ssize_t map_count, key_count; const void *map_array = 0, *key_array = 0; if (key_buffer_size == NULL) key_buffer_size = &key_count; _control_input_server_(&command, &reply); if (reply.FindData("keymap", B_ANY_TYPE, &map_array, &map_count) != B_OK) { *map = 0; *key_buffer = 0; return; } if (reply.FindData("key_buffer", B_ANY_TYPE, &key_array, key_buffer_size) != B_OK) { *map = 0; *key_buffer = 0; return; } *map = (key_map *)malloc(map_count); memcpy(*map, map_array, map_count); *key_buffer = (char *)malloc(*key_buffer_size); memcpy(*key_buffer, key_array, *key_buffer_size); } status_t get_keyboard_id(uint16 *id) { BMessage command(IS_GET_KEYBOARD_ID); BMessage reply; uint16 kid; _control_input_server_(&command, &reply); status_t err = reply.FindInt16("id", (int16 *)&kid); if (err != B_OK) return err; *id = kid; return B_OK; } status_t get_modifier_key(uint32 modifier, uint32 *key) { BMessage command(IS_GET_MODIFIER_KEY); BMessage reply; uint32 rkey; command.AddInt32("modifier", modifier); _control_input_server_(&command, &reply); status_t err = reply.FindInt32("key", (int32 *) &rkey); if (err != B_OK) return err; *key = rkey; return B_OK; } void set_modifier_key(uint32 modifier, uint32 key) { BMessage command(IS_SET_MODIFIER_KEY); BMessage reply; command.AddInt32("modifier", modifier); command.AddInt32("key", key); _control_input_server_(&command, &reply); } void set_keyboard_locks(uint32 modifiers) { BMessage command(IS_SET_KEYBOARD_LOCKS); BMessage reply; command.AddInt32("locks", modifiers); _control_input_server_(&command, &reply); } status_t _restore_key_map_() { BMessage message(IS_RESTORE_KEY_MAP); BMessage reply; return _control_input_server_(&message, &reply); } rgb_color keyboard_navigation_color() { // Queries the app_server return ui_color(B_KEYBOARD_NAVIGATION_COLOR); } int32 count_workspaces() { uint32 columns; uint32 rows; BPrivate::get_workspaces_layout(&columns, &rows); return columns * rows; } void set_workspace_count(int32 count) { int32 squareRoot = (int32)sqrt(count); int32 rows = 1; for (int32 i = 2; i <= squareRoot; i++) { if (count % i == 0) rows = i; } int32 columns = count / rows; BPrivate::set_workspaces_layout(columns, rows); } int32 current_workspace() { int32 index = 0; BPrivate::AppServerLink link; link.StartMessage(AS_CURRENT_WORKSPACE); int32 status; if (link.FlushWithReply(status) == B_OK && status == B_OK) link.Read(&index); return index; } void activate_workspace(int32 workspace) { BPrivate::AppServerLink link; link.StartMessage(AS_ACTIVATE_WORKSPACE); link.Attach(workspace); link.Flush(); } bigtime_t idle_time() { bigtime_t idletime = 0; BPrivate::AppServerLink link; link.StartMessage(AS_IDLE_TIME); int32 code; if (link.FlushWithReply(code) == B_OK && code == B_OK) link.Read(&idletime); return idletime; } void run_select_printer_panel() { if (be_roster == NULL) return; // Launches the Printer prefs app via the Roster be_roster->Launch(PRNT_SIGNATURE_TYPE); } void run_add_printer_panel() { // Launches the Printer prefs app via the Roster and asks it to // add a printer run_select_printer_panel(); BMessenger printerPanelMessenger(PRNT_SIGNATURE_TYPE); printerPanelMessenger.SendMessage(PRINTERS_ADD_PRINTER); } void run_be_about() { if (be_roster != NULL) be_roster->Launch("application/x-vnd.Haiku-About"); } void set_focus_follows_mouse(bool follow) { // obviously deprecated API set_mouse_mode(B_WARP_MOUSE); } bool focus_follows_mouse() { return mouse_mode() != B_NORMAL_MOUSE; } void set_mouse_mode(mode_mouse mode) { BPrivate::AppServerLink link; link.StartMessage(AS_SET_MOUSE_MODE); link.Attach(mode); link.Flush(); } mode_mouse mouse_mode() { // Gets the focus-follows-mouse style, such as normal, B_WARP_MOUSE, etc. mode_mouse mode = B_NORMAL_MOUSE; BPrivate::AppServerLink link; link.StartMessage(AS_GET_MOUSE_MODE); int32 code; if (link.FlushWithReply(code) == B_OK && code == B_OK) link.Read(&mode); return mode; } rgb_color ui_color(color_which which) { int32 index = color_which_to_index(which); if (index < 0 || index >= kNumColors) { fprintf(stderr, "ui_color(): unknown color_which %d\n", which); return make_color(0, 0, 0); } if (be_app) { server_read_only_memory* shared = BApplication::Private::ServerReadOnlyMemory(); return shared->colors[index]; } return kDefaultColors[index]; } void set_ui_color(const color_which &which, const rgb_color &color) { int32 index = color_which_to_index(which); if (index < 0 || index >= kNumColors) { fprintf(stderr, "set_ui_color(): unknown color_which %d\n", which); return; } BPrivate::AppServerLink link; link.StartMessage(AS_SET_UI_COLOR); link.Attach(which); link.Attach(color); link.Flush(); } rgb_color tint_color(rgb_color color, float tint) { rgb_color result; #define LIGHTEN(x) ((uint8)(255.0f - (255.0f - x) * tint)) #define DARKEN(x) ((uint8)(x * (2 - tint))) if (tint < 1.0f) { result.red = LIGHTEN(color.red); result.green = LIGHTEN(color.green); result.blue = LIGHTEN(color.blue); result.alpha = color.alpha; } else { result.red = DARKEN(color.red); result.green = DARKEN(color.green); result.blue = DARKEN(color.blue); result.alpha = color.alpha; } #undef LIGHTEN #undef DARKEN return result; } rgb_color shift_color(rgb_color color, float shift); rgb_color shift_color(rgb_color color, float shift) { return tint_color(color, shift); } extern "C" status_t _init_interface_kit_() { status_t status = BPrivate::PaletteConverter::InitializeDefault(true); if (status < B_OK) return status; // TODO: Could support different themes here in the future. be_control_look = new BControlLook(); _init_global_fonts_(); BPrivate::gWidthBuffer = new BPrivate::WidthBuffer; status = BPrivate::MenuPrivate::CreateBitmaps(); if (status != B_OK) return status; _menu_info_ptr_ = &BMenu::sMenuInfo; status = get_menu_info(&BMenu::sMenuInfo); if (status != B_OK) return status; general_info.background_color = ui_color(B_PANEL_BACKGROUND_COLOR); general_info.mark_color.set_to(0, 0, 0); general_info.highlight_color = ui_color(B_CONTROL_HIGHLIGHT_COLOR); general_info.window_frame_color = ui_color(B_WINDOW_TAB_COLOR); general_info.color_frame = true; // TODO: fill the other static members return status; } extern "C" status_t _fini_interface_kit_() { BPrivate::MenuPrivate::DeleteBitmaps(); delete BPrivate::gWidthBuffer; BPrivate::gWidthBuffer = NULL; delete be_control_look; be_control_look = NULL; // TODO: Anything else? return B_OK; } // #pragma mark - /*! \brief private function used by Deskbar to set window decor Note, we don't have to be compatible here, and could just change the Deskbar not to use this anymore \param theme The theme to choose - \c 0: BeOS - \c 1: AmigaOS - \c 2: Win95 - \c 3: MacOS */ void __set_window_decor(int32 theme) { BPrivate::AppServerLink link; link.StartMessage(AS_R5_SET_DECORATOR); link.Attach(theme); link.Flush(); } namespace BPrivate { /*! \brief queries the server for the number of available decorators \return the number of available decorators */ int32 count_decorators(void) { BPrivate::AppServerLink link; link.StartMessage(AS_COUNT_DECORATORS); int32 code; int32 count = -1; if (link.FlushWithReply(code) == B_OK && code == B_OK) link.Read(&count); return count; } /*! \brief queries the server for the index of the current decorators \return the current decorator's index If for some bizarre reason this function fails, it returns -1 */ int32 get_decorator(void) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_DECORATOR); int32 code; int32 index = -1; if (link.FlushWithReply(code) == B_OK && code == B_OK) link.Read(&index); return index; } /*! \brief queries the server for the name of the decorator with a certain index \param index The index of the decorator to get the name for \param name BString to receive the name of the decorator \return B_OK if successful, B_ERROR if not */ status_t get_decorator_name(const int32 &index, BString &name) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_DECORATOR_NAME); link.Attach(index); int32 code; if (link.FlushWithReply(code) == B_OK && code == B_OK) { char *string; if (link.ReadString(&string) == B_OK) { name = string; free(string); return B_OK; } } return B_ERROR; } /*! \brief asks the server to draw a decorator preview into a BBitmap \param index The index of the decorator to get the name for \param bitmap BBitmap to receive the preview \return B_OK if successful, B_ERROR if not. This is currently unimplemented. */ status_t get_decorator_preview(const int32 &index, BBitmap *bitmap) { // TODO: implement get_decorator_preview return B_ERROR; } /*! \brief Private function which sets the window decorator for the system. \param index Index of the decorator to set If the index is invalid, this function and the server do nothing */ status_t set_decorator(const int32 &index) { if (index < 0) return B_BAD_VALUE; BPrivate::AppServerLink link; link.StartMessage(AS_SET_DECORATOR); link.Attach(index); link.Flush(); return B_OK; } status_t get_application_order(int32 workspace, team_id** _applications, int32* _count) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_APPLICATION_ORDER); link.Attach(workspace); int32 code; status_t status = link.FlushWithReply(code); if (status != B_OK) return status; if (code != B_OK) return code; int32 count; link.Read(&count); *_applications = (team_id*)malloc(count * sizeof(team_id)); if (*_applications == NULL) return B_NO_MEMORY; link.Read(*_applications, count * sizeof(team_id)); *_count = count; return B_OK; } status_t get_window_order(int32 workspace, int32** _tokens, int32* _count) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_WINDOW_ORDER); link.Attach(workspace); int32 code; status_t status = link.FlushWithReply(code); if (status != B_OK) return status; if (code != B_OK) return code; int32 count; link.Read(&count); *_tokens = (int32*)malloc(count * sizeof(int32)); if (*_tokens == NULL) return B_NO_MEMORY; link.Read(*_tokens, count * sizeof(int32)); *_count = count; return B_OK; } } // namespace BPrivate // These methods were marked with "Danger, will Robinson!" in // the OpenTracker source, so we might not want to be compatible // here. // In any way, we would need to update Deskbar to use our // replacements, so we could as well just implement them... void do_window_action(int32 windowToken, int32 action, BRect zoomRect, bool zoom) { BPrivate::AppServerLink link; link.StartMessage(AS_WINDOW_ACTION); link.Attach(windowToken); link.Attach(action); // we don't have any zooming effect link.Flush(); } client_window_info* get_window_info(int32 serverToken) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_WINDOW_INFO); link.Attach(serverToken); int32 code; if (link.FlushWithReply(code) != B_OK || code != B_OK) return NULL; int32 size; link.Read(&size); client_window_info* info = (client_window_info*)malloc(size); if (info == NULL) return NULL; link.Read(info, size); return info; } int32* get_token_list(team_id team, int32* _count) { BPrivate::AppServerLink link; link.StartMessage(AS_GET_WINDOW_LIST); link.Attach(team); int32 code; if (link.FlushWithReply(code) != B_OK || code != B_OK) return NULL; int32 count; link.Read(&count); int32* tokens = (int32*)malloc(count * sizeof(int32)); if (tokens == NULL) return NULL; link.Read(tokens, count * sizeof(int32)); *_count = count; return tokens; } void do_bring_to_front_team(BRect zoomRect, team_id team, bool zoom) { BPrivate::AppServerLink link; link.StartMessage(AS_BRING_TEAM_TO_FRONT); link.Attach(team); // we don't have any zooming effect link.Flush(); } void do_minimize_team(BRect zoomRect, team_id team, bool zoom) { BPrivate::AppServerLink link; link.StartMessage(AS_MINIMIZE_TEAM); link.Attach(team); // we don't have any zooming effect link.Flush(); } // #pragma mark - truncate string static char* write_ellipsis(char* dst) { strcpy(dst, B_UTF8_ELLIPSIS); // The UTF-8 character spans over 3 bytes return dst + 3; } static bool optional_char_fits(float escapement, float fontSize, float gap) { const float size = escapement * fontSize; if (size <= gap || fabs(size - gap) <= 0.0001) return true; return false; } bool truncate_end(const char* source, char* dest, uint32 numChars, const float* escapementArray, float width, float ellipsisWidth, float size) { float currentWidth = 0.0; ellipsisWidth /= size; // test if this is as accurate as escapementArray * size width /= size; uint32 lastFit = 0, c; for (c = 0; c < numChars; c++) { currentWidth += escapementArray[c]; if (currentWidth + ellipsisWidth <= width) lastFit = c; if (currentWidth > width) break; } if (c == numChars) { // string fits into width return false; } if (c == 0) { // there is no space for the ellipsis strcpy(dest, ""); return true; } // copy string to destination for (uint32 i = 0; i < lastFit + 1; i++) { // copy one glyph do { *dest++ = *source++; } while (IsInsideGlyph(*source)); } // write ellipsis and terminate dest = write_ellipsis(dest); *dest = '\0'; return true; } static char* copy_from_end(const char* src, char* dst, uint32 numChars, uint32 length, const float* escapementArray, float width, float ellipsisWidth, float size) { const char* originalStart = src; src += length - 1; float currentWidth = 0.0; for (int32 c = numChars - 1; c > 0; c--) { currentWidth += escapementArray[c] * size; if (currentWidth > width) { // ups, we definitely don't fit. go back until the ellipsis fits currentWidth += ellipsisWidth; // go forward again until ellipsis fits (already beyond the target) for (uint32 c2 = c; c2 < numChars; c2++) { //printf(" backward: %c (%ld) (%.1f - %.1f = %.1f)\n", *dst, c2, currentWidth, escapementArray[c2] * size, currentWidth - escapementArray[c2] * size); currentWidth -= escapementArray[c2] * size; do { src++; } while (IsInsideGlyph(*src)); // see if we went back enough if (currentWidth <= width) break; } break; } else { // go back one glyph do { src--; } while (IsInsideGlyph(*src)); } } // copy from the end of the string uint32 bytesToCopy = originalStart + length - src; memcpy(dst, src, bytesToCopy); dst += bytesToCopy; return dst; } bool truncate_middle(const char* source, char* dest, uint32 numChars, const float* escapementArray, float width, float ellipsisWidth, float size) { float mid = (width - ellipsisWidth) / 2.0; uint32 left = 0; float leftWidth = 0.0; while (left < numChars && (leftWidth + (escapementArray[left] * size)) < mid) leftWidth += (escapementArray[left++] * size); if (left == numChars) return false; float rightWidth = 0.0; uint32 right = numChars; while (right > left && (rightWidth + (escapementArray[right - 1] * size)) < mid) rightWidth += (escapementArray[--right] * size); if (left >= right) return false; float stringWidth = leftWidth + rightWidth; for (uint32 i = left; i < right; ++i) stringWidth += (escapementArray[i] * size); if (stringWidth <= width) return false; // if there is no space for the ellipsis if (width < ellipsisWidth) { strcpy(dest, ""); return true; } // The ellipsis now definitely fits, but let's // see if we can add another character float gap = width - (leftWidth + ellipsisWidth + rightWidth); if (left > numChars - right) { // try right letter first if (optional_char_fits(escapementArray[right - 1], size, gap)) { right--; } else if (optional_char_fits(escapementArray[left], size, gap)) { left++; } } else { // try left letter first if (optional_char_fits(escapementArray[left], size, gap)) { left++; } else if (optional_char_fits(escapementArray[right - 1], size, gap)) { right--; } } // copy characters for (uint32 i = 0; i < left; i++) { // copy one glyph do { *dest++ = *source++; } while (IsInsideGlyph(*source)); } dest = write_ellipsis(dest); for (uint32 i = left; i < numChars; i++) { // copy one glyph do { if (i >= right) *dest++ = *source++; else source++; } while (IsInsideGlyph(*source)); } // terminate dest[0] = '\0'; return true; } // TODO: put into BPrivate namespace void truncate_string(const char* string, uint32 mode, float width, char* result, const float* escapementArray, float fontSize, float ellipsisWidth, int32 length, int32 numChars) { // TODO: that's actually not correct: the string could be smaller than ellipsisWidth if (string == NULL /*|| width < ellipsisWidth*/) { // we don't have room for a single glyph strcpy(result, ""); return; } // iterate over glyphs and copy source into result string // one glyph at a time as long as we have room for the "…" yet char* dest = result; const char* source = string; bool truncated = true; switch (mode) { case B_TRUNCATE_BEGINNING: { dest = copy_from_end(source, dest, numChars, length, escapementArray, width, ellipsisWidth, fontSize); // "dst" points to the position behind the last glyph that // was copied. int32 dist = dest - result; // we didn't terminate yet *dest = 0; if (dist < length) { // TODO: Is there a smarter way? char* temp = new char[dist + 4]; char* t = temp; // append "…" t = write_ellipsis(t); // shuffle arround strings so that "…" is prepended strcpy(t, result); strcpy(result, temp); delete[] temp; /* char t = result[3]; memmove(&result[3], result, dist + 1); write_ellipsis(result); result[3] = t;*/ } break; } case B_TRUNCATE_END: truncated = truncate_end(source, dest, numChars, escapementArray, width, ellipsisWidth, fontSize); break; case B_TRUNCATE_SMART: // TODO: implement, though it was never implemented on R5 // FALL THROUGH (at least do something) case B_TRUNCATE_MIDDLE: default: truncated = truncate_middle(source, dest, numChars, escapementArray, width, ellipsisWidth, fontSize); break; } if (!truncated) { // copy string to destination verbatim strlcpy(dest, source, length + 1); } }