diff -r 878bd592e8df -r 65f668c56e62 src/core/math_func.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/core/math_func.cpp Fri Mar 20 22:45:43 2009 +0100 @@ -0,0 +1,41 @@ +/* $Id$ */ + +/** @file math_func.cpp Math functions. */ + +#include "../stdafx.h" +#include "math_func.hpp" + +/** + * Compute least common multiple (lcm) of arguments \a a and \a b, the smallest + * integer value that is a multiple of both \a a and \a b. + * @param a First number. + * @param b second number. + * @return Least common multiple of values \a a and \a b. + * + * @note This function only works for non-negative values of \a a and \a b. + */ +int LeastCommonMultiple(int a, int b) +{ + if (a == 0 || b == 0) return 0; // By definition. + if (a == 1 || a == b) return b; + if (b == 1) return a; + + return a * b / GreatestCommonDivisor(a, b); +} + +/** + * Compute greatest common divisor (gcd) of \a a and \a b. + * @param a First number. + * @param b second number. + * @return Greatest common divisor of \a a and \a b. + */ +int GreatestCommonDivisor(int a, int b) +{ + while (b != 0) { + int t = b; + b = a % b; + a = t; + } + return a; + +} diff -r 878bd592e8df -r 65f668c56e62 src/core/math_func.hpp --- a/src/core/math_func.hpp Wed Mar 18 17:55:47 2009 +0000 +++ b/src/core/math_func.hpp Fri Mar 20 22:45:43 2009 +0100 @@ -264,4 +264,7 @@ b = t; } +int LeastCommonMultiple(int a, int b); +int GreatestCommonDivisor(int a, int b); + #endif /* MATH_FUNC_HPP */ diff -r 878bd592e8df -r 65f668c56e62 src/group_gui.cpp --- a/src/group_gui.cpp Wed Mar 18 17:55:47 2009 +0000 +++ b/src/group_gui.cpp Fri Mar 20 22:45:43 2009 +0100 @@ -115,6 +115,55 @@ { WIDGETS_END}, }; +static const NWidgetPart _nested_group_widgets[] = { + NWidget(NWID_HORIZONTAL), // Window header + NWidget(WWT_CLOSEBOX, COLOUR_GREY, GRP_WIDGET_CLOSEBOX), + NWidget(WWT_CAPTION, COLOUR_GREY, GRP_WIDGET_CAPTION), SetMinimalSize(437, 14), + NWidget(WWT_STICKYBOX, COLOUR_GREY, GRP_WIDGET_STICKY), + EndContainer(), + + NWidget(NWID_HORIZONTAL), + /* left part */ + NWidget(NWID_VERTICAL), + + NWidget(WWT_PANEL, COLOUR_GREY, GRP_WIDGET_EMPTY_TOP_LEFT), SetMinimalSize(200, 12), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, GRP_WIDGET_ALL_VEHICLES), SetMinimalSize(200, 13), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, GRP_WIDGET_DEFAULT_VEHICLES), SetMinimalSize(200, 13), EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_GREY, GRP_WIDGET_LIST_GROUP), SetMinimalSize(188, 117), SetDataTip(0x701, STR_GROUPS_CLICK_ON_GROUP_FOR_TIP), SetResize(0, 1), + NWidget(WWT_SCROLL2BAR, COLOUR_GREY, GRP_WIDGET_LIST_GROUP_SCROLLBAR), SetMinimalSize(12, 117), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_CREATE_GROUP), SetMinimalSize(24, 25), SetDataTip(0x0, STR_GROUP_CREATE_TIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_DELETE_GROUP), SetMinimalSize(24, 25), SetDataTip(0x0, STR_GROUP_DELETE_TIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_RENAME_GROUP), SetMinimalSize(24, 25), SetDataTip(0x0, STR_GROUP_RENAME_TIP), + NWidget(WWT_PANEL, COLOUR_GREY, GRP_WIDGET_EMPTY1), SetMinimalSize(92, 25), SetFill(1, 0), EndContainer(), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_REPLACE_PROTECTION), SetMinimalSize(24, 25), SetDataTip(0x0, STR_GROUP_REPLACE_PROTECTION_TIP), + NWidget(WWT_PANEL, COLOUR_GREY, GRP_WIDGET_EMPTY2), SetMinimalSize(12, 25), EndContainer(), + EndContainer(), + EndContainer(), + /* right part */ + NWidget(NWID_VERTICAL), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, GRP_WIDGET_SORT_BY_ORDER), SetMinimalSize(81, 12), SetDataTip(STR_SORT_BY, STR_SORT_ORDER_TIP), + NWidget(WWT_DROPDOWN, COLOUR_GREY, GRP_WIDGET_SORT_BY_DROPDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_SORT_CRITERIA_TIP), + NWidget(WWT_PANEL, COLOUR_GREY, GRP_WIDGET_EMPTY_TOP_RIGHT), SetMinimalSize(12, 12), SetResize(1, 0), EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_GREY, GRP_WIDGET_LIST_VEHICLE), SetMinimalSize(248, 156), SetDataTip(0x701, STR_NULL), SetResize(1, 1), + NWidget(WWT_SCROLLBAR, COLOUR_GREY, GRP_WIDGET_LIST_VEHICLE_SCROLLBAR), SetMinimalSize(12, 156), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, GRP_WIDGET_AVAILABLE_VEHICLES), SetMinimalSize(106, 12), SetDataTip(0x0, STR_AVAILABLE_ENGINES_TIP), + NWidget(WWT_DROPDOWN, COLOUR_GREY, GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN), SetMinimalSize(118, 12), SetDataTip(STR_MANAGE_LIST, STR_MANAGE_LIST_TIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_STOP_ALL), SetMinimalSize(12, 12), SetDataTip(SPR_FLAG_VEH_STOPPED, STR_MASS_STOP_LIST_TIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_START_ALL), SetMinimalSize(12, 12), SetDataTip(SPR_FLAG_VEH_RUNNING, STR_MASS_START_LIST_TIP), + NWidget(WWT_PANEL, COLOUR_GREY, GRP_WIDGET_EMPTY_BOTTOM_RIGHT), SetMinimalSize(0, 12), SetResize(1, 0), EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_GREY, GRP_WIDGET_RESIZE), SetMinimalSize(12, 12), + EndContainer(), + EndContainer(), + EndContainer(), +}; class VehicleGroupWindow : public BaseVehicleListWindow { private: @@ -704,7 +753,7 @@ WDP_AUTO, WDP_AUTO, 460, 194, 460, 246, WC_INVALID, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, - _group_widgets + _group_widgets, _nested_group_widgets, lengthof(_nested_group_widgets) ); void ShowCompanyGroup(CompanyID company, VehicleType vehicle_type) diff -r 878bd592e8df -r 65f668c56e62 src/intro_gui.cpp --- a/src/intro_gui.cpp Wed Mar 18 17:55:47 2009 +0000 +++ b/src/intro_gui.cpp Fri Mar 20 22:45:43 2009 +0100 @@ -59,29 +59,28 @@ InvalidateWindowClasses(WC_SELECT_GAME); } +enum SelectGameIntroWidgets { + SGI_GENERATE_GAME = 2, + SGI_LOAD_GAME, + SGI_PLAY_SCENARIO, + SGI_PLAY_HEIGHTMAP, + SGI_EDIT_SCENARIO, + SGI_PLAY_NETWORK, + SGI_TEMPERATE_LANDSCAPE, + SGI_ARCTIC_LANDSCAPE, + SGI_TROPIC_LANDSCAPE, + SGI_TOYLAND_LANDSCAPE, + SGI_OPTIONS, + SGI_DIFFICULTIES, + SGI_SETTINGS_OPTIONS, + SGI_GRF_SETTINGS, + SGI_CONTENT_DOWNLOAD, + SGI_AI_SETTINGS, + SGI_EXIT, +}; + struct SelectGameWindow : public Window { -private: - enum SelectGameIntroWidgets { - SGI_GENERATE_GAME = 2, - SGI_LOAD_GAME, - SGI_PLAY_SCENARIO, - SGI_PLAY_HEIGHTMAP, - SGI_EDIT_SCENARIO, - SGI_PLAY_NETWORK, - SGI_TEMPERATE_LANDSCAPE, - SGI_ARCTIC_LANDSCAPE, - SGI_TROPIC_LANDSCAPE, - SGI_TOYLAND_LANDSCAPE, - SGI_OPTIONS, - SGI_DIFFICULTIES, - SGI_SETTINGS_OPTIONS, - SGI_GRF_SETTINGS, - SGI_CONTENT_DOWNLOAD, - SGI_AI_SETTINGS, - SGI_EXIT, - }; -public: SelectGameWindow(const WindowDesc *desc) : Window(desc) { this->LowerWidget(_settings_newgame.game_creation.landscape + SGI_TEMPERATE_LANDSCAPE); @@ -151,11 +150,121 @@ } }; +static const NWidgetPart _nested_select_game_widgets[] = { + NWidget(WWT_CAPTION, COLOUR_BROWN, 0), SetMinimalSize(336, 14), SetDataTip(STR_0307_OPENTTD), + NWidget(WWT_PANEL, COLOUR_BROWN, 1), + + NWidget(NWID_SPACER), SetMinimalSize(0, 8), + + /* 'generate game' and 'load game' buttons */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_GENERATE_GAME), SetMinimalSize(158, 12), + SetDataTip(STR_0140_NEW_GAME, STR_02FB_START_A_NEW_GAME), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_LOAD_GAME), SetMinimalSize(158, 12), + SetDataTip(STR_0141_LOAD_GAME, STR_02FC_LOAD_A_SAVED_GAME), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + EndContainer(), + + NWidget(NWID_SPACER), SetMinimalSize(0, 6), + + /* 'play scenario' and 'play heightmap' buttons */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_PLAY_SCENARIO), SetMinimalSize(158, 12), + SetDataTip(STR_029A_PLAY_SCENARIO, STR_0303_START_A_NEW_GAME_USING), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_PLAY_HEIGHTMAP), SetMinimalSize(158, 12), + SetDataTip(STR_PLAY_HEIGHTMAP, STR_PLAY_HEIGHTMAP_HINT), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + EndContainer(), + + NWidget(NWID_SPACER), SetMinimalSize(0, 6), + + /* 'edit scenario' and 'play multiplayer' buttons */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_EDIT_SCENARIO), SetMinimalSize(158, 12), + SetDataTip(STR_SCENARIO_EDITOR, STR_02FE_CREATE_A_CUSTOMIZED_GAME), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_PLAY_NETWORK), SetMinimalSize(158, 12), + SetDataTip(STR_MULTIPLAYER, STR_0300_SELECT_MULTIPLAYER_GAME), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + EndContainer(), + + NWidget(NWID_SPACER), SetMinimalSize(0, 7), + + /* climate selection buttons */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(2, 0), + NWidget(WWT_IMGBTN_2, COLOUR_ORANGE, SGI_TEMPERATE_LANDSCAPE), SetMinimalSize(77, 55), + SetDataTip(SPR_SELECT_TEMPERATE, STR_030E_SELECT_TEMPERATE_LANDSCAPE), + NWidget(NWID_SPACER), SetMinimalSize(3, 0), + NWidget(WWT_IMGBTN_2, COLOUR_ORANGE, SGI_ARCTIC_LANDSCAPE), SetMinimalSize(77, 55), + SetDataTip(SPR_SELECT_SUB_ARCTIC, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE), + NWidget(NWID_SPACER), SetMinimalSize(3, 0), + NWidget(WWT_IMGBTN_2, COLOUR_ORANGE, SGI_TROPIC_LANDSCAPE), SetMinimalSize(77, 55), + SetDataTip(SPR_SELECT_SUB_TROPICAL, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE), + NWidget(NWID_SPACER), SetMinimalSize(3, 0), + NWidget(WWT_IMGBTN_2, COLOUR_ORANGE, SGI_TOYLAND_LANDSCAPE), SetMinimalSize(77, 55), + SetDataTip(SPR_SELECT_TOYLAND, STR_0311_SELECT_TOYLAND_LANDSCAPE), + EndContainer(), + + NWidget(NWID_SPACER), SetMinimalSize(0, 7), + + /* 'game options' and 'difficulty options' buttons */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_OPTIONS), SetMinimalSize(158, 12), + SetDataTip(STR_0148_GAME_OPTIONS, STR_0301_DISPLAY_GAME_OPTIONS), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_DIFFICULTIES), SetMinimalSize(158, 12), + SetDataTip(STR_01FE_DIFFICULTY, STR_0302_DISPLAY_DIFFICULTY_OPTIONS), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + EndContainer(), + + NWidget(NWID_SPACER), SetMinimalSize(0, 6), + + /* 'advanced settings' and 'newgrf settings' buttons */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_SETTINGS_OPTIONS), SetMinimalSize(158, 12), + SetDataTip(STR_CONFIG_SETTING, STR_CONFIG_SETTING_TIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_GRF_SETTINGS), SetMinimalSize(158, 12), + SetDataTip(STR_NEWGRF_SETTINGS_BUTTON, STR_NEWGRF_SETTINGS_BUTTON_TIP), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + EndContainer(), + + NWidget(NWID_SPACER), SetMinimalSize(0, 6), + + /* 'online content' and 'ai settings' buttons */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_CONTENT_DOWNLOAD), SetMinimalSize(158, 12), + SetDataTip(STR_CONTENT_INTRO_BUTTON, STR_CONTENT_INTRO_BUTTON_TIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_AI_SETTINGS), SetMinimalSize(158, 12), + SetDataTip(STR_AI_SETTINGS_BUTTON, STR_AI_SETTINGS_BUTTON_TIP), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + EndContainer(), + + NWidget(NWID_SPACER), SetMinimalSize(0, 6), + + /* 'exit program' button */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(104, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, SGI_EXIT), SetMinimalSize(128, 12), + SetDataTip(STR_0304_QUIT, STR_0305_QUIT_OPENTTD), + NWidget(NWID_SPACER), SetMinimalSize(104, 0), + EndContainer(), + + NWidget(NWID_SPACER), SetMinimalSize(0, 8), + + EndContainer(), +}; + static const WindowDesc _select_game_desc( WDP_CENTER, WDP_CENTER, 336, 213, 336, 213, WC_SELECT_GAME, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, - _select_game_widgets + _select_game_widgets, + _nested_select_game_widgets, lengthof(_nested_select_game_widgets) ); void ShowSelectGameWindow() diff -r 878bd592e8df -r 65f668c56e62 src/network/network_gui.cpp --- a/src/network/network_gui.cpp Wed Mar 18 17:55:47 2009 +0000 +++ b/src/network/network_gui.cpp Fri Mar 20 22:45:43 2009 +0100 @@ -307,6 +307,22 @@ public: NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(NETWORK_NAME_LENGTH, desc) { + this->widget[NGWW_CLIENTS].left = this->widget[NGWW_NAME].right + 1; + this->widget[NGWW_MAPSIZE].left = this->widget[NGWW_NAME].right + 1; + this->widget[NGWW_DATE].left = this->widget[NGWW_NAME].right + 1; + this->widget[NGWW_YEARS].left = this->widget[NGWW_NAME].right + 1; + + this->widget[NGWW_CLIENTS].right = this->widget[NGWW_INFO].left - 1; + this->widget[NGWW_MAPSIZE].right = this->widget[NGWW_INFO].left - 1; + this->widget[NGWW_DATE].right = this->widget[NGWW_INFO].left - 1 - 20; + this->widget[NGWW_YEARS].right = this->widget[NGWW_INFO].left - 1 - 20; + + this->widget[NGWW_NAME].display_flags &= ~RESIZE_LRTB; + this->widget[NGWW_CLIENTS].display_flags &= ~RESIZE_LRTB; + this->widget[NGWW_MAPSIZE].display_flags &= ~RESIZE_LRTB; + this->widget[NGWW_DATE].display_flags &= ~RESIZE_LRTB; + this->widget[NGWW_YEARS].display_flags &= ~RESIZE_LRTB; + ttd_strlcpy(this->edit_str_buf, _settings_client.network.client_name, this->edit_str_size); this->afilter = CS_ALPHANUMERAL; InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 120); @@ -813,11 +829,115 @@ { WIDGETS_END}, }; +NWidgetPart _nested_network_game_widgets[] = { + /* TOP */ + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE, NGWW_CLOSE), + NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE, NGWW_CAPTION), SetMinimalSize(439, 14), SetDataTip(STR_NETWORK_MULTIPLAYER, STR_NULL), // XXX Add default caption tooltip! + EndContainer(), + NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NGWW_MAIN), + NWidget(NWID_SPACER), SetMinimalSize(0, 8), SetResize(1, 0), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(9, 0), + NWidget(NWID_VERTICAL), + NWidget(NWID_SPACER), SetMinimalSize(0,1), // Text is one pixel further down + NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NGWW_CONNECTION), SetMinimalSize(77, 13), SetDataTip(STR_NETWORK_CONNECTION, STR_NULL), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(4, 0), + NWidget(NWID_VERTICAL), + NWidget(WWT_DROPDOWNIN, COLOUR_LIGHT_BLUE, NGWW_CONN_BTN), SetMinimalSize(92, 12), + SetDataTip(STR_NETWORK_LAN_INTERNET_COMBO, STR_NETWORK_CONNECTION_TIP), + NWidget(NWID_SPACER), SetMinimalSize(0,2), // Text ends two pixels further down + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(108, 0), SetFill(1,0), SetResize(1,0), + NWidget(NWID_VERTICAL), + NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, NGWW_CLIENT), SetMinimalSize(151, 12), + SetDataTip(STR_NETWORK_PLAYER_NAME_OSKTITLE, STR_NETWORK_ENTER_NAME_TIP), + NWidget(NWID_SPACER), SetMinimalSize(0,2), // Text ends two pixels further down + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(9, 0), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 6), SetResize(1, 0), + NWidget(NWID_HORIZONTAL), + /* LEFT SIDE */ + NWidget(NWID_SPACER), SetMinimalSize(10, 0), SetResize(0, 1), + NWidget(NWID_VERTICAL), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_NAME), SetMinimalSize(61, 12), SetResize(1, 0), + SetDataTip(STR_NETWORK_GAME_NAME, STR_NETWORK_GAME_NAME_TIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_CLIENTS), SetMinimalSize(20, 12), + SetDataTip(STR_NETWORK_CLIENTS_CAPTION, STR_NETWORK_CLIENTS_CAPTION_TIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_MAPSIZE), SetMinimalSize(20, 12), + SetDataTip(STR_NETWORK_MAP_SIZE_CAPTION, STR_NETWORK_MAP_SIZE_CAPTION_TIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_DATE), SetMinimalSize(20, 12), SetDataTip(STR_NETWORK_DATE_CAPTION, STR_NETWORK_DATE_CAPTION_TIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_YEARS), SetMinimalSize(20, 12), SetDataTip(STR_NETWORK_YEARS_CAPTION, STR_NETWORK_YEARS_CAPTION_TIP), + NWidget(NWID_SPACER), SetMinimalSize(0, 0), SetFill(0, 0), SetResize(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_INFO), SetMinimalSize(40, 12), SetDataTip(STR_EMPTY, STR_NETWORK_INFO_ICONS_TIP), + EndContainer(), + NWidget(WWT_MATRIX, COLOUR_LIGHT_BLUE, NGWW_MATRIX), SetMinimalSize(181, 155), SetResize(1,1), + SetDataTip((11 << 8) + 1, STR_NETWORK_CLICK_GAME_TO_SELECT), + NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetResize(1, 0), + NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NGWW_LASTJOINED_LABEL), SetMinimalSize(181, 12), SetFill(1,0), + SetDataTip(STR_NETWORK_LAST_JOINED_SERVER, STR_NULL), SetResize(1, 0), + NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NGWW_LASTJOINED), SetMinimalSize(181, 14), SetFill(1,0), SetResize(1, 0), + SetDataTip(0x0, STR_NETWORK_CLICK_TO_SELECT_LAST), + EndContainer(), + EndContainer(), + NWidget(NWID_VERTICAL), + NWidget(WWT_SCROLLBAR, COLOUR_LIGHT_BLUE, NGWW_SCROLLBAR), SetMinimalSize(12, 167), + NWidget(NWID_SPACER), SetMinimalSize(0,26), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(7, 0), SetResize(0, 1), + /* RIGHT SIDE */ + NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NGWW_DETAILS), + NWidget(NWID_SPACER), SetMinimalSize(0, 155), SetResize(0, 1), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(120, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_NEWGRF), SetMinimalSize(106, 12), SetDataTip(STR_NEWGRF_SETTINGS_BUTTON, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 6), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_JOIN), SetMinimalSize(101, 12), SetDataTip(STR_NETWORK_JOIN_GAME, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(14, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_REFRESH), SetMinimalSize(106, 12), SetDataTip(STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 10), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(9, 0), SetResize(0, 1), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 9), SetResize(1, 0), + /* BOTTOM */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(NWID_VERTICAL), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_FIND), SetMinimalSize(101, 12), SetDataTip(STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP), + NWidget(NWID_SPACER), SetMinimalSize(7, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_ADD), SetMinimalSize(101, 12), SetDataTip(STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP), + NWidget(NWID_SPACER), SetMinimalSize(7, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_START), SetMinimalSize(101, 12), SetDataTip(STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP), + NWidget(NWID_SPACER), SetMinimalSize(7, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_CANCEL), SetMinimalSize(101, 12), SetDataTip(STR_012E_CANCEL, STR_NULL), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0,6), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(3, 0), SetResize(1, 0), + NWidget(NWID_VERTICAL), + NWidget(NWID_SPACER), SetMinimalSize(0, 6), + NWidget(WWT_RESIZEBOX, COLOUR_LIGHT_BLUE, NGWW_RESIZE), + EndContainer(), + EndContainer(), + EndContainer(), +}; + static const WindowDesc _network_game_window_desc( WDP_CENTER, WDP_CENTER, 450, 264, 780, 264, WC_NETWORK_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, - _network_game_window_widgets + _network_game_window_widgets, _nested_network_game_widgets, lengthof(_nested_network_game_widgets) ); void ShowNetworkGameWindow() diff -r 878bd592e8df -r 65f668c56e62 src/news_gui.cpp --- a/src/news_gui.cpp Wed Mar 18 17:55:47 2009 +0000 +++ b/src/news_gui.cpp Fri Mar 20 22:45:43 2009 +0100 @@ -353,11 +353,34 @@ { WIDGETS_END}, }; +static NWidgetPart _nested_news_type0_widgets[] = { + /* Caption + close box */ + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE, 1), SetMinimalSize(11, 14), SetDataTip(STR_00C5, STR_018B_CLOSE_WINDOW), + NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE, 2), SetMinimalSize(269, 14), SetDataTip(STR_012C_MESSAGE, STR_NULL), + EndContainer(), + + /* Main part */ + NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, 0), + NWidget(NWID_SPACER), SetMinimalSize(0, 2), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(2, 0), + + NWidget(WWT_INSET, COLOUR_LIGHT_BLUE, 3), SetMinimalSize(276, 49), + EndContainer(), + + NWidget(NWID_SPACER), SetMinimalSize(2, 0), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 22), + EndContainer(), +}; + static WindowDesc _news_type0_desc( WDP_CENTER, 476, 280, 87, 280, 87, WC_NEWS_WINDOW, WC_NONE, WDF_DEF_WIDGET, - _news_type0_widgets + _news_type0_widgets, + _nested_news_type0_widgets, lengthof(_nested_news_type0_widgets) ); @@ -773,11 +796,29 @@ { WIDGETS_END}, }; +static const NWidgetPart _nested_message_history[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_BROWN, 0), SetMinimalSize(11, 14), SetDataTip(STR_00C5, STR_018B_CLOSE_WINDOW), + NWidget(WWT_CAPTION, COLOUR_BROWN, 1), SetMinimalSize(377, 14), SetDataTip(STR_MESSAGE_HISTORY, STR_018C_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_STICKYBOX, COLOUR_BROWN, 2), SetMinimalSize(12, 14), SetDataTip(0x0, STR_STICKY_BUTTON), + EndContainer(), + + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_BROWN, 3), SetMinimalSize(388, 125), SetDataTip(0x0, STR_MESSAGE_HISTORY_TIP), SetResize(1, 1), + EndContainer(), + NWidget(NWID_VERTICAL), + NWidget(WWT_SCROLLBAR, COLOUR_BROWN, 4), SetMinimalSize(12, 114), SetDataTip(0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST), SetResize(0, 1), + NWidget(WWT_RESIZEBOX, COLOUR_BROWN, 5), SetMinimalSize(12, 12), SetDataTip(0x0, STR_RESIZE_BUTTON), + EndContainer(), + EndContainer(), +}; + static const WindowDesc _message_history_desc( 240, 22, 400, 140, 400, 140, WC_MESSAGE_HISTORY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, - _message_history_widgets + _message_history_widgets, + _nested_message_history, lengthof(_nested_message_history) ); /** Display window with news messages history */ diff -r 878bd592e8df -r 65f668c56e62 src/vehicle_gui.cpp --- a/src/vehicle_gui.cpp Wed Mar 18 17:55:47 2009 +0000 +++ b/src/vehicle_gui.cpp Fri Mar 20 22:45:43 2009 +0100 @@ -716,6 +716,45 @@ { WIDGETS_END}, }; +static const NWidgetPart _nested_vehicle_list[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY, VLW_WIDGET_CLOSEBOX), + NWidget(WWT_CAPTION, COLOUR_GREY, VLW_WIDGET_CAPTION), SetMinimalSize(237, 14), SetDataTip(0x0, STR_018C_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_STICKYBOX, COLOUR_GREY, VLW_WIDGET_STICKY), + EndContainer(), + + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLW_WIDGET_SORT_ORDER), SetMinimalSize(81, 12), SetDataTip(STR_SORT_BY, STR_SORT_ORDER_TIP), + NWidget(WWT_DROPDOWN, COLOUR_GREY, VLW_WIDGET_SORT_BY_PULLDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_SORT_CRITERIA_TIP), + NWidget(WWT_PANEL, COLOUR_GREY, VLW_WIDGET_EMPTY_TOP_RIGHT), SetMinimalSize(12, 12), SetResize(1, 0), + EndContainer(), + EndContainer(), + + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_GREY, VLW_WIDGET_LIST), SetMinimalSize(248, 156), SetResize(1,1), // vertical resize step size will be modified + NWidget(WWT_SCROLLBAR, COLOUR_GREY, VLW_WIDGET_SCROLLBAR), SetMinimalSize(12, 156), + EndContainer(), + + /* Widget to be shown for other companies hiding the following 6 widgets */ + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_GREY, VLW_WIDGET_OTHER_COMPANY_FILLER), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLW_WIDGET_AVAILABLE_VEHICLES), SetMinimalSize(106, 12), + SetDataTip(0x0, STR_AVAILABLE_ENGINES_TIP), + NWidget(WWT_DROPDOWN, COLOUR_GREY, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN), SetMinimalSize(118, 12), + SetDataTip(STR_MANAGE_LIST, STR_MANAGE_LIST_TIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VLW_WIDGET_STOP_ALL), SetMinimalSize(12, 12), + SetDataTip(SPR_FLAG_VEH_STOPPED, STR_MASS_STOP_LIST_TIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VLW_WIDGET_START_ALL), SetMinimalSize(12, 12), + SetDataTip(SPR_FLAG_VEH_RUNNING, STR_MASS_START_LIST_TIP), + NWidget(WWT_PANEL, COLOUR_GREY, VLW_WIDGET_EMPTY_BOTTOM_RIGHT), SetMinimalSize(0, 12), SetResize(1, 0), + EndContainer(), + EndContainer(), + EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_GREY, VLW_WIDGET_RESIZE), + EndContainer(), +}; + static void DrawSmallOrderList(const Vehicle *v, int x, int y) { const Order *order; @@ -1138,7 +1177,8 @@ WDP_AUTO, WDP_AUTO, 260, 194, 260, 246, WC_INVALID, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, - _vehicle_list_widgets + _vehicle_list_widgets, + _nested_vehicle_list, lengthof(_nested_vehicle_list) ); static void ShowVehicleListWindowLocal(CompanyID company, uint16 VLW_flag, VehicleType vehicle_type, uint16 unique_number) diff -r 878bd592e8df -r 65f668c56e62 src/widget.cpp --- a/src/widget.cpp Wed Mar 18 17:55:47 2009 +0000 +++ b/src/widget.cpp Fri Mar 20 22:45:43 2009 +0100 @@ -6,6 +6,7 @@ #include "company_func.h" #include "gfx_func.h" #include "window_gui.h" +#include "debug.h" #include "table/sprites.h" #include "table/strings.h" @@ -627,3 +628,861 @@ int offset = this->IsWidgetLowered(widget) ? 1 : 0; DoDrawString(state == SBS_DOWN ? DOWNARROW : UPARROW, this->widget[widget].right - 11 + offset, this->widget[widget].top + 1 + offset, TC_BLACK); } + + +/* == Nested widgets == */ + +/** + * Base class constructor. + * @param tp Nested widget type. + */ +NWidgetBase::NWidgetBase(byte tp) : ZeroedMemoryAllocator() +{ + this->type = tp; +} + +/* ~NWidgetContainer() takes care of #next and #prev data members. */ + +/** + * @fn int NWidgetBase::ComputeMinimalSize() + * @brief Compute minimal size needed by the widget. + * + * The minimal size of a widget is the smallest size that a widget needs to + * display itself properly. + * In addition, filling and resizing of the widget are computed. + * @return Biggest index in the widget array of all child widgets. + * + * @note After the computation, the results can be queried by accessing the data members of the widget. + */ + +/** + * @fn void NWidgetBase::AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl) + * @brief Assign minimal size and position to the widget. + * @param x Horizontal offset of the widget relative to the left edge of the window. + * @param y Vertical offset of the widget relative to the top edge of the window. + * @param given_width Width allocated to the widget. + * @param given_height Height allocated to the widget. + * @param allow_resize_x Horizontal resizing is allowed. + * @param allow_resize_y Vertical resizing is allowed. + * @param rtl Adapt for right-to-left languages (position contents of horizontal containers backwards). + */ + +/** + * @fn void NWidgetBase::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) + * @brief Store all child widgets with a valid index into the widget array. + * @param widgets Widget array to store the nested widgets in. + * @param length Length of the array. + * @param left_moving Left edge of the widget may move due to resizing (right edge if \a rtl). + * @param top_moving Top edge of the widget may move due to reisizing. + * @param rtl Adapt for right-to-left languages (position contents of horizontal containers backwards). + * + * @note When storing a nested widget, the function should check first that the type in the \a widgets array is #WWT_LAST. + * This is used to detect double widget allocations as well as holes in the widget array. + */ + +/** + * Constructor for resizable nested widgets. + * @param tp Nested widget type. + * @param fill_x Allow horizontal filling from initial size. + * @param fill_y Allow vertical filling from initial size. + */ +NWidgetResizeBase::NWidgetResizeBase(byte tp, bool fill_x, bool fill_y) : NWidgetBase(tp) +{ + this->fill_x = fill_x; + this->fill_y = fill_y; +} + +/** + * Set minimal size of the widget. + * @param min_x Horizontal minimal size of the widget. + * @param min_y Vertical minimal size of the widget. + */ +void NWidgetResizeBase::SetMinimalSize(int min_x, int min_y) +{ + assert(min_x >= 0 && min_y >= 0); + this->min_x = min_x; + this->min_y = min_y; +} + +/** + * Set the filling of the widget from initial size. + * @param fill_x Allow horizontal filling from initial size. + * @param fill_y Allow vertical filling from initial size. + */ +void NWidgetResizeBase::SetFill(bool fill_x, bool fill_y) +{ + this->fill_x = fill_x; + this->fill_y = fill_y; +} + +/** + * Set resize step of the widget. + * @param resize_x Resize step in horizontal direction, value \c 0 means no resize, otherwise the step size in pixels. + * @param resize_y Resize step in vertical direction, value \c 0 means no resize, otherwise the step size in pixels. + */ +void NWidgetResizeBase::SetResize(int resize_x, int resize_y) +{ + assert(resize_x >= 0 && resize_y >= 0); + this->resize_x = resize_x; + this->resize_y = resize_y; +} + +void NWidgetResizeBase::AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl) +{ + assert(x >= 0 && y >= 0); + assert(given_width >= 0 && given_height >= 0); + this->pos_x = x; + this->pos_y = y; + this->min_x = given_width; + this->min_y = given_height; + if (!allow_resize_x) this->resize_x = 0; + if (!allow_resize_y) this->resize_y = 0; +} + +/** + * Initialization of a 'real' widget. + * @param tp Type of the widget. + * @param colour Colour of the widget. + * @param fill_x Default horizontal filling. + * @param fill_y Default vertical filling. + * @param widget_data Data component of the widget. @see Widget::data + * @param tool_tip Tool tip of the widget. @see Widget::tootips + */ +NWidgetCore::NWidgetCore(byte tp, Colours colour, bool fill_x, bool fill_y, uint16 widget_data, StringID tool_tip) : NWidgetResizeBase(tp, fill_x, fill_y) +{ + this->colour = colour; + this->index = -1; + this->widget_data = widget_data; + this->tool_tip = tool_tip; +} + +/** + * Set index of the nested widget in the widget array. + * @param index Index to use. + */ +void NWidgetCore::SetIndex(int index) +{ + assert(index >= 0); + this->index = index; +} + +/** + * Set data and tool tip of the nested widget. + * @param widget_data Data to use. + * @param tool_tip Tool tip string to use. + */ +void NWidgetCore::SetDataTip(uint16 widget_data, StringID tool_tip) +{ + this->widget_data = widget_data; + this->tool_tip = tool_tip; +} + +int NWidgetCore::ComputeMinimalSize() +{ + /* All data is already at the right place. */ + return this->index; +} + +void NWidgetCore::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) +{ + if (this->index < 0) return; + + assert(this->index < length); + Widget *w = widgets + this->index; + assert(w->type == WWT_LAST); + + DisplayFlags flags = RESIZE_NONE; // resize flags. + /* Compute vertical resizing. */ + if (top_moving) { + flags |= RESIZE_TB; // Only 1 widget can resize in the widget array. + } else if(this->resize_y > 0) { + flags |= RESIZE_BOTTOM; + } + /* Compute horizontal resizing. */ + if (left_moving) { + flags |= RESIZE_LR; // Only 1 widget can resize in the widget array. + } else if (this->resize_x > 0) { + flags |= RESIZE_RIGHT; + } + + /* Copy nested widget data into its widget array entry. */ + w->type = (WidgetType)(this->type); // We have made some extensions, so we used a different type + w->display_flags = flags; + w->colour = this->colour; + w->left = this->pos_x; + w->right = this->pos_x + this->min_x - 1; + w->top = this->pos_y; + w->bottom = this->pos_y + this->min_y - 1; + w->data = this->widget_data; + w->tooltips = this->tool_tip; +} + +/** + * Constructor container baseclass. + * @param tp Type of the container. + */ +NWidgetContainer::NWidgetContainer(byte tp) : NWidgetBase(tp) +{ + this->head = NULL; + this->tail = NULL; +} + +NWidgetContainer::~NWidgetContainer() +{ + while (this->head != NULL) { + NWidgetBase *wid = this->head->next; + delete this->head; + this->head = wid; + } + this->tail = NULL; +} + +/** + * Append widget \a wid to container. + * @param wid Widget to append. + */ +void NWidgetContainer::Add(NWidgetBase *wid) +{ + assert(wid->next == NULL && wid->prev == NULL); + + if (this->head == NULL) { + this->head = wid; + this->tail = wid; + } else { + assert(this->tail != NULL); + assert(this->tail->next == NULL); + + this->tail->next = wid; + wid->prev = this->tail; + this->tail = wid; + } +} + +NWidgetHorizontal::NWidgetHorizontal() : NWidgetContainer(NWID_HORIZONTAL) +{ +} + +int NWidgetHorizontal::ComputeMinimalSize() +{ + int biggest_index = -1; + this->min_x = 0; // Sum of minimal size of all childs. + this->min_y = 0; // Biggest child. + this->fill_x = false; // true if at least one child allows fill_x. + this->fill_y = true; // true if all childs allow fill_y. + this->resize_x = 0; // smallest non-zero child widget resize step. + this->resize_y = 1; // smallest common child resize step + + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + int idx = child_wid->ComputeMinimalSize(); + biggest_index = max(biggest_index, idx); + + this->min_x += child_wid->min_x; + this->min_y = max(this->min_y, child_wid->min_y); + this->fill_x |= child_wid->fill_x; + this->fill_y &= child_wid->fill_y; + + if (child_wid->resize_x > 0) { + if (this->resize_x == 0 || this->resize_x > child_wid->resize_x) this->resize_x = child_wid->resize_x; + } + this->resize_y = LeastCommonMultiple(this->resize_y, child_wid->resize_y); + } + + return biggest_index; +} + +void NWidgetHorizontal::AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl) +{ + assert(x >= 0 && y >= 0); + assert(given_width >= 0 && given_height >= 0); + this->pos_x = x; + this->pos_y = y; + this->min_x = given_width; + this->min_y = given_height; + if (!allow_resize_x) this->resize_x = 0; + if (!allow_resize_y) this->resize_y = 0; + + /* Count number of childs that would like a piece of the pie. */ + int num_changing_childs = 0; // Number of childs that can change size. + NWidgetBase *child_wid; + for (child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + if (child_wid->fill_x) num_changing_childs++; + } + + /* Fill and position the child widgets. */ + int additional_length = given_width - this->min_x; // Additional width given to us. + int position = 0; // Place to put next child relative to origin of the container. + + allow_resize_x = (this->resize_x > 0); + child_wid = rtl ? this->tail : this->head; + while (child_wid != NULL) { + /* Decide about vertical filling of the child. */ + int child_height; // Height of the child widget. + int child_pos_y; // Vertical position of child relative to the top of the container. + if (child_wid->fill_y) { + child_height = given_height; + child_pos_y = 0; + } else { + child_height = child_wid->min_y; + child_pos_y = (given_height - child_height) / 2; + } + + /* Decide about horizontal filling of the child. */ + int child_width; // Width of the child widget. + if (child_wid->fill_x && num_changing_childs > 0) { + /* Hand out a piece of the pie while compensating for rounding errors. */ + int increment = additional_length / num_changing_childs; + additional_length -= increment; + num_changing_childs--; + + child_width = child_wid->min_x + increment; + } else { + child_width = child_wid->min_x; + } + + child_wid->AssignMinimalPosition(x + position, y + child_pos_y, child_width, child_height, allow_resize_x, (this->resize_y > 0), rtl); + position += child_width; + if (child_wid->resize_x > 0) allow_resize_x = false; // Widget array allows only one child resizing + + child_wid = rtl ? child_wid->prev : child_wid->next; + } +} + +void NWidgetHorizontal::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) +{ + NWidgetBase *child_wid = rtl ? this->tail : this->head; + while (child_wid != NULL) { + child_wid->StoreWidgets(widgets, length, left_moving, top_moving, rtl); + left_moving |= (child_wid->resize_x > 0); + + child_wid = rtl ? child_wid->prev : child_wid->next; + } +} + +NWidgetVertical::NWidgetVertical() : NWidgetContainer(NWID_VERTICAL) +{ +} + +int NWidgetVertical::ComputeMinimalSize() +{ + int biggest_index = -1; + this->min_x = 0; // Biggest child. + this->min_y = 0; // Sum of minimal size of all childs. + this->fill_x = true; // true if all childs allow fill_x. + this->fill_y = false; // true if at least one child allows fill_y. + this->resize_x = 1; // smallest common child resize step + this->resize_y = 0; // smallest non-zero child widget resize step. + + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + int idx = child_wid->ComputeMinimalSize(); + biggest_index = max(biggest_index, idx); + + this->min_y += child_wid->min_y; + this->min_x = max(this->min_x, child_wid->min_x); + this->fill_y |= child_wid->fill_y; + this->fill_x &= child_wid->fill_x; + + if (child_wid->resize_y > 0) { + if (this->resize_y == 0 || this->resize_y > child_wid->resize_y) this->resize_y = child_wid->resize_y; + } + this->resize_x = LeastCommonMultiple(this->resize_x, child_wid->resize_x); + } + + return biggest_index; +} + +void NWidgetVertical::AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl) +{ + assert(x >= 0 && y >= 0); + assert(given_width >= 0 && given_height >= 0); + this->pos_x = x; + this->pos_y = y; + this->min_x = given_width; + this->min_y = given_height; + if (!allow_resize_x) this->resize_x = 0; + if (!allow_resize_y) this->resize_y = 0; + + /* count number of childs that would like a piece of the pie. */ + int num_changing_childs = 0; // Number of childs that can change size. + NWidgetBase *child_wid; + for (child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + if (child_wid->fill_x) num_changing_childs++; + } + + /* Fill and position the child widgets. */ + int additional_length = given_height - this->min_y; // Additional width given to us. + int position = 0; // Place to put next child relative to origin of the container. + + allow_resize_y = (this->resize_y > 0); + for (child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + /* Decide about horizontal filling of the child. */ + int child_width; // Width of the child widget. + int child_pos_x; // Horizontal position of child relative to the left of the container. + if (child_wid->fill_x) { + child_width = given_width; + child_pos_x = 0; + } else { + child_width = child_wid->min_x; + child_pos_x = (given_width - child_width) / 2; + } + + /* Decide about vertical filling of the child. */ + int child_height; // Height of the child widget. + if (child_wid->fill_y && num_changing_childs > 0) { + /* Hand out a piece of the pie while compensating for rounding errors. */ + int increment = additional_length / num_changing_childs; + additional_length -= increment; + num_changing_childs--; + + child_height = child_wid->min_y + increment; + } else { + child_height = child_wid->min_y; + } + + child_wid->AssignMinimalPosition(x + child_pos_x, y + position, child_width, child_height, (this->resize_x > 0), allow_resize_y, rtl); + position += child_height; + if (child_wid->resize_y > 0) allow_resize_y = false; // Widget array allows only one child resizing + } +} + +void NWidgetVertical::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) +{ + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + child_wid->StoreWidgets(widgets, length, left_moving, top_moving, rtl); + top_moving |= (child_wid->resize_y > 0); + } +} + +/** + * Generic spacer widget. + * @param length Horizontal size of the spacer widget. + * @param height Vertical size of the spacer widget. + */ +NWidgetSpacer::NWidgetSpacer(int length, int height) : NWidgetResizeBase(NWID_SPACER, false, false) +{ + assert(NWID_SPACER <= (int)WWT_MASK); // Make sure we don't walk out of the allocated range for widgets + + this->SetMinimalSize(length, height); + this->SetResize(0, 0); +} + +int NWidgetSpacer::ComputeMinimalSize() +{ + /* No further computation needed. */ + return -1; +} + +void NWidgetSpacer::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) +{ + /* Spacer widgets are never stored in the widget array. */ +} + +/** + * Constructor parent nested widgets. + * @param tp Type of parent widget. + * @param colour Colour of the parent widget. + * @param index Index in the widget array used by the window system. + * @param child Child container widget (if supplied). If not supplied, a + * vertical container will be inserted while adding the first + * child widget. + */ +NWidgetBackground::NWidgetBackground(byte tp, Colours colour, int index, NWidgetContainer *child) : NWidgetCore(tp, colour, true, true, 0x0, STR_NULL) +{ + this->SetIndex(index); + assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME); + assert(index >= 0); + this->child = child; +} + +NWidgetBackground::~NWidgetBackground() +{ + if (this->child != NULL) delete this->child; +} + +/** + * Add a child to the parent. + * @param nwid Nested widget to add to the background widget. + * + * Unless a child container has been given in the constructor, a parent behaves as a vertical container. + * You can add several childs to it, and they are put underneath each other. + */ +void NWidgetBackground::Add(NWidgetBase *nwid) +{ + if (this->child == NULL) { + this->child = new NWidgetVertical(); + } + this->child->Add(nwid); +} + +int NWidgetBackground::ComputeMinimalSize() +{ + int biggest_index = this->index; + if (this->child != NULL) { + int idx = this->child->ComputeMinimalSize(); + biggest_index = max(biggest_index, idx); + + this->min_x = this->child->min_x; + this->min_y = this->child->min_y; + this->fill_x = this->child->fill_x; + this->fill_y = this->child->fill_y; + this->resize_x = this->child->resize_x; + this->resize_y = this->child->resize_y; + } + /* Otherwise, the program should have already set the above values. */ + + return biggest_index; +} + +void NWidgetBackground::AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl) +{ + assert(x >= 0 && y >= 0); + assert(given_width >= 0 && given_height >= 0); + this->pos_x = x; + this->pos_y = y; + this->min_x = given_width; + this->min_y = given_height; + if (!allow_resize_x) this->resize_x = 0; + if (!allow_resize_y) this->resize_y = 0; + + if (this->child != NULL) this->child->AssignMinimalPosition(x, y, given_width, given_height, (this->resize_x > 0), (this->resize_y > 0), rtl); +} + +void NWidgetBackground::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) +{ + NWidgetCore::StoreWidgets(widgets, length, left_moving, top_moving, rtl); + if (this->child != NULL) this->child->StoreWidgets(widgets, length, left_moving, top_moving, rtl); +} + +/** + * Nested leaf widget. + * @param tp Type of leaf widget. + * @param index Index in the widget array used by the window system. + * @param data Data of the widget. + * @param tip Tooltip of the widget. + */ +NWidgetLeaf::NWidgetLeaf(byte tp, Colours colour, int index, uint16 data, StringID tip) : NWidgetCore(tp, colour, true, true, data, tip) +{ + this->SetIndex(index); + this->SetMinimalSize(0, 0); + this->SetResize(0, 0); + + switch (tp) { + case WWT_EMPTY: + break; + + case WWT_PUSHBTN: + this->SetFill(false, false); + break; + + case WWT_IMGBTN: + case WWT_PUSHIMGBTN: + case WWT_IMGBTN_2: + this->SetFill(false, false); + break; + + case WWT_TEXTBTN: + case WWT_PUSHTXTBTN: + case WWT_TEXTBTN_2: + case WWT_LABEL: + case WWT_TEXT: + case WWT_MATRIX: + case WWT_EDITBOX: + this->SetFill(false, false); + break; + + case WWT_SCROLLBAR: + case WWT_SCROLL2BAR: + this->SetFill(false, true); + this->SetResize(0, 1); + this->min_x = 12; + this->SetDataTip(0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST); + break; + + case WWT_CAPTION: + this->SetFill(true, false); + this->SetResize(1, 0); + this->min_y = 14; + this->SetDataTip(0x0, STR_018C_WINDOW_TITLE_DRAG_THIS); + break; + + case WWT_HSCROLLBAR: + this->SetFill(true, false); + this->SetResize(1, 0); + this->min_y = 12; + this->SetDataTip(0x0, STR_HSCROLL_BAR_SCROLLS_LIST); + break; + + case WWT_STICKYBOX: + this->SetFill(false, false); + this->SetMinimalSize(12, 14); + this->SetDataTip(STR_NULL, STR_STICKY_BUTTON); + break; + + case WWT_RESIZEBOX: + this->SetFill(false, false); + this->SetMinimalSize(12, 12); + this->SetDataTip(STR_NULL, STR_RESIZE_BUTTON); + break; + + case WWT_CLOSEBOX: + this->SetFill(false, false); + this->SetMinimalSize(11, 14); + this->SetDataTip(STR_00C5, STR_018B_CLOSE_WINDOW); + break; + + case WWT_DROPDOWN: + case WWT_DROPDOWNIN: + this->SetFill(false, false); + this->min_y = 12; + break; + + default: + NOT_REACHED(); + } +} + +/** + * Intialize nested widget tree and convert to widget array. + * @param nwid Nested widget tree. + * @param rtl Direction of the language. + * @return Widget array with the converted widgets. + * @note Caller should release returned widget array with \c free(widgets). + */ +Widget *InitializeNWidgets(NWidgetBase *nwid, bool rtl) +{ + /* Initialize nested widgets. */ + int biggest_index = nwid->ComputeMinimalSize(); + nwid->AssignMinimalPosition(0, 0, nwid->min_x, nwid->min_y, (nwid->resize_x > 0), (nwid->resize_y > 0), rtl); + + /* Construct a local widget array and initialize all its types to #WWT_LAST. */ + Widget *widgets = MallocT(biggest_index + 2); + int i; + for (i = 0; i < biggest_index + 2; i++) { + widgets[i].type = WWT_LAST; + } + + /* Store nested widgets in the array. */ + nwid->StoreWidgets(widgets, biggest_index + 1, false, false, rtl); + + /* Check that all widgets are used. */ + for (i = 0; i < biggest_index + 2; i++) { + if (widgets[i].type == WWT_LAST) break; + } + assert(i == biggest_index + 1); + + /* Fill terminating widget */ + static const Widget last_widget = {WIDGETS_END}; + widgets[biggest_index + 1] = last_widget; + + return widgets; +} + +/** + * Compare two widget arrays with each other, and report differences. + * @param orig Pointer to original widget array. + * @param gen Pointer to generated widget array (from the nested widgets). + * @param report Report differences to 'misc' debug stream. + * @return Both widget arrays are equal. + */ +bool CompareWidgetArrays(const Widget *orig, const Widget *gen, bool report) +{ +#define CHECK(var, prn) \ + if (ow->var != gw->var) { \ + same = false; \ + if (report) DEBUG(misc, 1, "index %d, \"" #var "\" field: original " prn ", generated " prn, idx, ow->var, gw->var); \ + } +#define CHECK_COORD(var) \ + if (ow->var != gw->var) { \ + same = false; \ + if (report) DEBUG(misc, 1, "index %d, \"" #var "\" field: original %d, generated %d, (difference %d)", idx, ow->var, gw->var, ow->var - gw->var); \ + } + + int idx = 0; + bool same = true; + for(;;) { + const Widget *ow = orig + idx; + const Widget *gw = gen + idx; + + CHECK(type, "%d") + CHECK(display_flags, "0x%x") + CHECK(colour, "%d") + CHECK_COORD(left) + CHECK_COORD(right) + CHECK_COORD(top) + CHECK_COORD(bottom) + CHECK(data, "%u") + CHECK(tooltips, "%u") + + if (ow->type == WWT_LAST || gw->type == WWT_LAST) break; + idx++; + } + + return same; + +#undef CHECK +#undef CHECK_COORD +} + +/* == Conversion code from NWidgetPart array to NWidgetBase* tree == */ + +/** + * Construct a single nested widget in \a *dest from its parts. + * + * Construct a NWidgetBase object from a #NWidget function, and apply all + * settings that follow it, until encountering a #EndContainer, another + * #NWidget, or the end of the parts array. + * + * @param parts Array with parts of the nested widget. + * @param count Length of the \a parts array. + * @param dest Address of pointer to use for returning the composed widget. + * @return Number of widget part elements used to compose the widget. + */ +static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest) +{ + int num_used = 0; + + *dest = NULL; + + while (count > num_used) { + switch (parts->type) { + case NWID_SPACER: + if (*dest != NULL) return num_used; + *dest = new NWidgetSpacer(0, 0); + break; + + case NWID_HORIZONTAL: + if (*dest != NULL) return num_used; + *dest = new NWidgetHorizontal(); + break; + + case WWT_PANEL: + case WWT_INSET: + case WWT_FRAME: + if (*dest != NULL) return num_used; + *dest = new NWidgetBackground(parts->type, parts->d.widget.colour, parts->d.widget.index); + break; + + case NWID_VERTICAL: + if (*dest != NULL) return num_used; + *dest = new NWidgetVertical(); + break; + + case WPT_RESIZE: { + NWidgetResizeBase *nwrb = dynamic_cast(*dest); + if (nwrb != NULL) nwrb->SetResize(parts->d.xy.x, parts->d.xy.y); + break; + } + + case WPT_RESIZE_PTR: { + NWidgetResizeBase *nwrb = dynamic_cast(*dest); + if (nwrb != NULL) nwrb->SetResize(parts->d.xy_ptr->x, parts->d.xy_ptr->y); + break; + } + + case WPT_MINSIZE: { + NWidgetResizeBase *nwrb = dynamic_cast(*dest); + if (nwrb != NULL) nwrb->SetMinimalSize(parts->d.xy.x, parts->d.xy.y); + break; + } + + case WPT_MINSIZE_PTR: { + NWidgetResizeBase *nwrb = dynamic_cast(*dest); + if (nwrb != NULL) nwrb->SetMinimalSize(parts->d.xy_ptr->x, parts->d.xy_ptr->y); + break; + } + + case WPT_FILL: { + NWidgetResizeBase *nwrb = dynamic_cast(*dest); + if (nwrb != NULL) nwrb->SetFill(parts->d.xy.x, parts->d.xy.y); + break; + } + + case WPT_DATATIP: { + NWidgetCore *nwc = dynamic_cast(*dest); + if (nwc != NULL) { + nwc->widget_data = parts->d.data_tip.data; + nwc->tool_tip = parts->d.data_tip.tooltip; + } + break; + } + + case WPT_DATATIP_PTR: { + NWidgetCore *nwc = dynamic_cast(*dest); + if (nwc != NULL) { + nwc->widget_data = parts->d.datatip_ptr->data; + nwc->tool_tip = parts->d.datatip_ptr->tooltip; + } + break; + } + + case WPT_ENDCONTAINER: + return num_used; + + default: + if (*dest != NULL) return num_used; + assert((parts->type & WWT_MASK) < NWID_HORIZONTAL); + *dest = new NWidgetLeaf(parts->type, parts->d.widget.colour, parts->d.widget.index, 0x0, STR_NULL); + break; + } + num_used++; + parts++; + } + + return num_used; +} + +/** + * Build a nested widget tree by recursively filling containers with nested widgets read from their parts. + * @param parts Array with parts of the nested widgets. + * @param count Length of the \a parts array. + * @param parent Container to use for storing the child widgets. + * @return Number of widget part elements used to fill the container. + */ +static int MakeWidgetTree(const NWidgetPart *parts, int count, NWidgetBase *parent) +{ + /* Given parent must be either a #NWidgetContainer or a #NWidgetBackground object. */ + NWidgetContainer *nwid_cont = dynamic_cast(parent); + NWidgetBackground *nwid_parent = dynamic_cast(parent); + assert((nwid_cont != NULL && nwid_parent == NULL) || (nwid_cont == NULL && nwid_parent != NULL)); + + int total_used = 0; + while (true) { + NWidgetBase *sub_widget = NULL; + int num_used = MakeNWidget(parts, count - total_used, &sub_widget); + parts += num_used; + total_used += num_used; + + /* Break out of loop when end reached */ + if (sub_widget == NULL) break; + + /* Add sub_widget to parent container. */ + if (nwid_cont) nwid_cont->Add(sub_widget); + if (nwid_parent) nwid_parent->Add(sub_widget); + + /* If sub-widget is a container, recursively fill that container. */ + byte tp = sub_widget->type; + if (tp == NWID_HORIZONTAL || tp == NWID_VERTICAL || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET) { + int num_used = MakeWidgetTree(parts, count - total_used, sub_widget); + parts += num_used; + total_used += num_used; + } + } + + if (count == total_used) return total_used; // Reached the end of the array of parts? + + assert(total_used < count); + assert(parts->type == WPT_ENDCONTAINER); + return total_used + 1; // *parts is also 'used' +} + +/** + * Construct a nested widget tree from an array of parts. + * @param parts Array with parts of the widgets. + * @param count Length of the \a parts array. + * @return Root of the nested widget tree, a vertical container containing the entire GUI. + */ +NWidgetContainer *MakeNWidgets(const NWidgetPart *parts, int count) +{ + NWidgetContainer *cont = new NWidgetVertical(); + MakeWidgetTree(parts, count, cont); + return cont; +} diff -r 878bd592e8df -r 65f668c56e62 src/widget_type.h --- a/src/widget_type.h Wed Mar 18 17:55:47 2009 +0000 +++ b/src/widget_type.h Fri Mar 20 22:45:43 2009 +0100 @@ -123,4 +123,328 @@ StringID tooltips; ///< Tooltips that are shown when rightclicking on a widget }; +/** + * Types of nested widgets and nested widget parts. + * @note Extends #WidgetType. + */ +enum NewWidgetType { + NWID_HORIZONTAL = WWT_LAST + 1, ///< Horizontal container + NWID_VERTICAL, ///< Vertical container + NWID_SPACER, ///< Invisible widget that takes some space + + /* Nested widget part types */ + WPT_RESIZE, ///< Widget part for specifying resizing. + WPT_RESIZE_PTR, ///< Widget part for specifying resizing via a pointer. + WPT_MINSIZE, ///< Widget part for specifying minimal size. + WPT_MINSIZE_PTR, ///< Widget part for specifying minimal size via a pointer. + WPT_FILL, ///< Widget part for specifying fill. + WPT_DATATIP, ///< Widget part for specifying data and tooltip. + WPT_DATATIP_PTR, ///< Widget part for specifying data and tooltip via a pointer. + WPT_ENDCONTAINER, ///< Widget part to denote end of a container. +}; + + +/** + * Baseclass for nested widgets. + */ +class NWidgetBase: public ZeroedMemoryAllocator { +public: + NWidgetBase(byte tp); + + virtual int ComputeMinimalSize() = 0; + virtual void AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl) = 0; + + virtual void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) = 0; + + byte type; ///< Type of the widget / nested widget. + int min_x; ///< Minimal horizontal size. + int min_y; ///< Minimal vertical size. + bool fill_x; ///< Allow horizontal filling from initial size. + bool fill_y; ///< Allow vertical filling from initial size. + int resize_x; ///< Horizontal resize step (\c 0 means not resizable). + int resize_y; ///< Vertical resize step (\c 0 means not resizable). + + int pos_x; ///< Horizontal position of top-left corner of the widget in the window. + int pos_y; ///< Vertical position of top-left corner of the widget in the window. + + NWidgetBase *next; ///< Pointer to next widget in container. Managed by parent container widget. + NWidgetBase *prev; ///< Pointer to previous widget in container. Managed by parent container widget. +}; + +/** Base class for a resizable nested widget. */ +class NWidgetResizeBase: public NWidgetBase { +public: + NWidgetResizeBase(byte tp, bool fill_x, bool fill_y); + + void SetMinimalSize(int min_x, int min_y); + void SetFill(bool fill_x, bool fill_y); + void SetResize(int resize_x, int resize_y); + + void AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl); +}; + +/** Base class for a 'real' widget. */ +class NWidgetCore: public NWidgetResizeBase { +public: + NWidgetCore(byte tp, Colours colour, bool def_fill_x, bool def_fill_y, uint16 widget_data, StringID tool_tip); + + void SetIndex(int index); + void SetDataTip(uint16 widget_data, StringID tool_tip); + + int ComputeMinimalSize(); + void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); + + Colours colour; ///< Colour of this widget. + int index; ///< Index of the nested widget in the widget array of the window (\c -1 means 'not used'). + uint16 widget_data; ///< Data of the widget. @see Widget::data + StringID tool_tip; ///< Tooltip of the widget. @see Widget::tootips +}; + +/** Baseclass for container widgets. */ +class NWidgetContainer: public NWidgetBase { +public: + NWidgetContainer(byte tp); + ~NWidgetContainer(); + + void Add(NWidgetBase *wid); +protected: + NWidgetBase *head; ///< Pointer to first widget in container. + NWidgetBase *tail; ///< Pointer to last widget in container. +}; + +/** Horizontal container. */ +class NWidgetHorizontal: public NWidgetContainer { +public: + NWidgetHorizontal(); + + int ComputeMinimalSize(); + void AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl); + + void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); +}; + +/** Vertical container */ +class NWidgetVertical: public NWidgetContainer { +public: + NWidgetVertical(); + + int ComputeMinimalSize(); + void AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl); + + void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); +}; + + +/** Spacer widget */ +class NWidgetSpacer: public NWidgetResizeBase { +public: + NWidgetSpacer(int length, int height); + + int ComputeMinimalSize(); + void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); +}; + +/** Nested widget with a child. */ +class NWidgetBackground: public NWidgetCore { +public: + NWidgetBackground(byte tp, Colours colour, int index, NWidgetContainer *child = NULL); + ~NWidgetBackground(); + + void Add(NWidgetBase *nwid); + + int ComputeMinimalSize(); + void AssignMinimalPosition(int x, int y, int given_width, int given_height, bool allow_resize_x, bool allow_resize_y, bool rtl); + + void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); +private: + NWidgetContainer *child; ///< Child widget. +}; + +/** Leaf widget. */ +class NWidgetLeaf: public NWidgetCore { +public: + NWidgetLeaf(byte tp, Colours colour, int index, uint16 data, StringID tip); +}; + +Widget *InitializeNWidgets(NWidgetBase *nwid, bool rtl = false); +bool CompareWidgetArrays(const Widget *orig, const Widget *gen, bool report = true); + +/* == Nested widget parts == */ + +/** Widget part for storing data and tooltip information. */ +struct NWidgetPartDataTip { + uint16 data; ///< Data value of the widget. + StringID tooltip; ///< Tooltip of the widget. +}; + +/** Widget part for storing basic widget information. */ +struct NWidgetPartWidget { + Colours colour; ///< Widget colour. + int16 index; ///< Widget index in the widget array. +}; + +/** Partial widget specification to allow NWidgets to be written nested. */ +struct NWidgetPart { + byte type; ///< Type of the part. @see NWidgetPartType. + union { + Point xy; ///< Part with an x/y size. + Point *xy_ptr; ///< Part with a pointer to an x/y size. + NWidgetPartDataTip data_tip; ///< Part with a data/tooltip. + NWidgetPartDataTip *datatip_ptr; ///< Part with a pointer to data/tooltip. + NWidgetPartWidget widget; ///< Part with a start of a widget. + } d; +}; + +/** + * Widget part function for setting the resize step. + * @param dx Horizontal resize step. 0 means no horizontal resizing. + * @param dy Vertical resize step. 0 means no horizontal resizing. + */ +static inline NWidgetPart SetResize(int16 dx, int16 dy) +{ + NWidgetPart part; + + part.type = WPT_RESIZE; + part.d.xy.x = dx; + part.d.xy.y = dy; + + return part; +} + +/** + * Widget part function for using a pointer to set the resize step. + * @param ptr Pointer to horizontal and vertical resize step. + */ +static inline NWidgetPart SetResize(Point *ptr) +{ + NWidgetPart part; + + part.type = WPT_RESIZE_PTR; + part.d.xy_ptr = ptr; + + return part; +} + +/** + * Widget part function for setting the minimal size. + * @param dx Horizontal minimal size. + * @param dy Vertical minimal size. + */ +static inline NWidgetPart SetMinimalSize(int16 x, int16 y) +{ + NWidgetPart part; + + part.type = WPT_MINSIZE; + part.d.xy.x = x; + part.d.xy.y = y; + + return part; +} + +/** + * Widget part function for using a pointer to set the minimal size. + * @param ptr Pointer to horizontal and vertical minimal size. + */ +static inline NWidgetPart SetMinimalSize(Point *ptr) +{ + NWidgetPart part; + + part.type = WPT_MINSIZE_PTR; + part.d.xy_ptr = ptr; + + return part; +} + +/** + * Widget part function for setting filling. + * @param x_fill Allow horizontal filling from minimal size. + * @param y_fill Allow vertical filling from minimal size. + */ +static inline NWidgetPart SetFill(bool x_fill, bool y_fill) +{ + NWidgetPart part; + + part.type = WPT_FILL; + part.d.xy.x = x_fill; + part.d.xy.y = y_fill; + + return part; +} + +/** + * Widget part function for denoting the end of a container + * (horizontal, vertical, WWT_FRAME, WWT_INSET, or WWT_PANEL). + */ +static inline NWidgetPart EndContainer() +{ + NWidgetPart part; + + part.type = WPT_ENDCONTAINER; + + return part; +} + +/** Widget part function for setting the data and tooltip. + * @param data Data of the widget. + * @param tip Tooltip of the widget. + */ +static inline NWidgetPart SetDataTip(uint16 data = 0, StringID tip = 0) +{ + NWidgetPart part; + + part.type = WPT_DATATIP; + part.d.data_tip.data = data; + part.d.data_tip.tooltip = tip; + + return part; +} + +/** + * Widget part function for setting the data and tooltip via a pointer. + * @param ptr Pointer to the data and tooltip of the widget. + */ +static inline NWidgetPart SetDataTip(NWidgetPartDataTip *ptr) +{ + NWidgetPart part; + + part.type = WPT_DATATIP_PTR; + part.d.datatip_ptr = ptr; + + return part; +} + +/** + * Widget part function for starting a new 'real' widget. + * @param tp Type of the new nested widget. + * @param col Colour of the new widget. + * @param idx Index of the widget in the widget array. + * @note with #WWT_PANEL, #WWT_FRAME, #WWT_INSET, a new container is started. + * Child widgets must have a index bigger than the parent index. + */ +static inline NWidgetPart NWidget(byte tp, Colours col, int16 idx) +{ + NWidgetPart part; + + part.type = tp; + part.d.widget.colour = col; + part.d.widget.index = idx; + + return part; +} + +/** + * Widget part function for starting a new horizontal container, vertical container, or spacer widget. + * @param tp Type of the new nested widget, #NWID_HORIZONTAL, #NWID_VERTICAL, or #NWID_SPACER + */ +static inline NWidgetPart NWidget(byte tp) +{ + NWidgetPart part; + + part.type = tp; + + return part; +} + +NWidgetContainer *MakeNWidgets(const NWidgetPart *parts, int count); + #endif /* WIDGET_TYPE_H */ diff -r 878bd592e8df -r 65f668c56e62 src/window.cpp --- a/src/window.cpp Wed Mar 18 17:55:47 2009 +0000 +++ b/src/window.cpp Fri Mar 20 22:45:43 2009 +0100 @@ -54,7 +54,8 @@ /** Window description constructor. */ WindowDesc::WindowDesc(int16 left, int16 top, int16 min_width, int16 min_height, int16 def_width, int16 def_height, - WindowClass window_class, WindowClass parent_class, uint32 flags, const Widget *widgets) + WindowClass window_class, WindowClass parent_class, uint32 flags, const Widget *widgets, + const NWidgetPart *nwid_parts, int16 nwid_length) { this->left = left; this->top = top; @@ -66,8 +67,45 @@ this->parent_cls = parent_class; this->flags = flags; this->widgets = widgets; + this->nwid_parts = nwid_parts; + this->nwid_length = nwid_length; + this->new_widgets = NULL; } +/** Get widget array of the window description. */ +const Widget *WindowDesc::GetWidgets() const +{ + const bool rtl = false; // Direction of the language is left-to-right + + /* If nested widgets are present, convert them to a widget array. */ + if (this->nwid_parts != NULL && nwid_length > 0 && this->new_widgets == NULL) { + NWidgetContainer *nwid = MakeNWidgets(this->nwid_parts, this->nwid_length); + this->new_widgets = InitializeNWidgets(nwid, rtl); + + if (!rtl && this->widgets != NULL) { + /* There are two descriptions, compare them. + * Comparing only makes sense when using a left-to-right language. + */ + bool ok = CompareWidgetArrays(this->widgets, this->new_widgets, false); + if (ok) { + DEBUG(misc, 1, "Nested widgets are equal, min-size(%d, %d)", nwid->min_x, nwid->min_y); + } else { + DEBUG(misc, 0, "Nested widgets give different results"); + CompareWidgetArrays(this->widgets, this->new_widgets, true); + } + } + delete nwid; + } + + const Widget *wids = (this->widgets != NULL) ? this->widgets : this->new_widgets; + assert(wids != NULL); + return wids; +} + +WindowDesc::~WindowDesc() +{ + free(this->new_widgets); +} /** * Set the window that has the focus @@ -1159,7 +1197,7 @@ Window::Window(const WindowDesc *desc, WindowNumber window_number) { Point pt = LocalGetWindowPlacement(desc, window_number); - this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->cls, desc->widgets, window_number); + this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->cls, desc->GetWidgets(), window_number); this->desc_flags = desc->flags; } diff -r 878bd592e8df -r 65f668c56e62 src/window_gui.h --- a/src/window_gui.h Wed Mar 18 17:55:47 2009 +0000 +++ b/src/window_gui.h Fri Mar 20 22:45:43 2009 +0100 @@ -41,18 +41,26 @@ struct WindowDesc : ZeroedMemoryAllocator { WindowDesc(int16 left, int16 top, int16 min_width, int16 min_height, int16 def_width, int16 def_height, - WindowClass window_class, WindowClass parent_class, uint32 flags, const Widget *widgets); + WindowClass window_class, WindowClass parent_class, uint32 flags, const Widget *widgets, + const NWidgetPart *nwid_parts = NULL, int16 nwid_length = 0); - int16 left; ///< Prefered x position of left edge of the window, @see WindowDefaultPosition() - int16 top; ///< Prefered y position of the top of the window, @see WindowDefaultPosition() - int16 minimum_width; ///< Minimal width of the window - int16 minimum_height; ///< Minimal height of the window - int16 default_width; ///< Prefered initial width of the window - int16 default_height; ///< Prefered initial height of the window - WindowClass cls; ///< Class of the window, @see WindowClass - WindowClass parent_cls; ///< Class of the parent window, @see WindowClass - uint32 flags; ///< Flags, @see WindowDefaultFlags - const Widget *widgets; ///< List of widgets with their position and size for the window + ~WindowDesc(); + + int16 left; ///< Prefered x position of left edge of the window. @see WindowDefaultPosition() + int16 top; ///< Prefered y position of the top of the window. @see WindowDefaultPosition() + int16 minimum_width; ///< Minimal width of the window. + int16 minimum_height; ///< Minimal height of the window. + int16 default_width; ///< Prefered initial width of the window. + int16 default_height; ///< Prefered initial height of the window. + WindowClass cls; ///< Class of the window, @see WindowClass. + WindowClass parent_cls; ///< Class of the parent window. @see WindowClass + uint32 flags; ///< Flags. @see WindowDefaultFlags + const Widget *widgets; ///< List of widgets with their position and size for the window. + const NWidgetPart *nwid_parts; ///< Nested widget parts describing the window. + int16 nwid_length; ///< Length of the #nwid_parts array. + mutable Widget *new_widgets; ///< Widgets generated from #nwid_parts. + + const Widget *GetWidgets() const; }; /**