# HG changeset patch # User glx # Date 1211210038 0 # Node ID 8cbdb511a6740905fd884c85e84e11fae4585ef9 # Parent 6c4314786d6807b0a464e0f354fd1d7bb482a6f0 (svn r13189) [NoAI] -Sync: with trunk r13055:13185. diff -r 6c4314786d68 -r 8cbdb511a674 projects/openttd_vs80.vcproj --- a/projects/openttd_vs80.vcproj Mon May 19 14:14:33 2008 +0000 +++ b/projects/openttd_vs80.vcproj Mon May 19 15:13:58 2008 +0000 @@ -1292,6 +1292,10 @@ > + + @@ -1420,6 +1424,10 @@ > + + @@ -1452,6 +1460,10 @@ > + + @@ -2212,10 +2224,6 @@ > - - @@ -2224,6 +2232,14 @@ > + + + + diff -r 6c4314786d68 -r 8cbdb511a674 projects/openttd_vs90.vcproj --- a/projects/openttd_vs90.vcproj Mon May 19 14:14:33 2008 +0000 +++ b/projects/openttd_vs90.vcproj Mon May 19 15:13:58 2008 +0000 @@ -1289,6 +1289,10 @@ > + + @@ -1417,6 +1421,10 @@ > + + @@ -1449,6 +1457,10 @@ > + + @@ -2209,10 +2221,6 @@ > - - @@ -2221,6 +2229,14 @@ > + + + + diff -r 6c4314786d68 -r 8cbdb511a674 source.list --- a/source.list Mon May 19 14:14:33 2008 +0000 +++ b/source.list Mon May 19 15:13:58 2008 +0000 @@ -92,7 +92,7 @@ thread_morphos.cpp #else thread_pthread.cpp - #endif + #end #end fiber_thread.cpp #end @@ -248,6 +248,7 @@ player_func.h player_gui.h player_type.h +querystring_gui.h queue.h rail.h rail_gui.h @@ -280,6 +281,7 @@ signs_type.h slope_func.h slope_type.h +sortlist_type.h sound_func.h sound_type.h sprite.h @@ -288,6 +290,7 @@ station_func.h station_gui.h station_type.h +statusbar_gui.h stdafx.h string_func.h string_type.h diff -r 6c4314786d68 -r 8cbdb511a674 src/ai/ai_gui.cpp --- a/src/ai/ai_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/ai/ai_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -55,7 +55,7 @@ static PlayerID ai_debug_player; int redraw_timer; - AIDebugWindow(const WindowDesc *desc, void *data, WindowNumber number) : Window(desc, data, number) + AIDebugWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number) { /* Disable the players who are not active or not an AI */ for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { @@ -74,7 +74,7 @@ virtual void OnPaint() { /* Draw standard stuff */ - DrawWindowWidgets(this); + this->DrawWidgets(); /* Check if the currently selected player is still active. */ if (ai_debug_player == INVALID_PLAYER || !GetPlayer(ai_debug_player)->is_active) { @@ -246,8 +246,7 @@ WDP_AUTO, WDP_AUTO, 299, 228, 299, 228, WC_PERFORMANCE_DETAIL, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, - _ai_debug_widgets, - NULL + _ai_debug_widgets }; void ShowAIDebugWindow() diff -r 6c4314786d68 -r 8cbdb511a674 src/aircraft_cmd.cpp --- a/src/aircraft_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/aircraft_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -447,7 +447,7 @@ } InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0); InvalidateWindow(WC_COMPANY, v->owner); if (IsLocalPlayer()) InvalidateAutoreplaceWindow(v->engine_type, v->group_id); //updates the replace Aircraft window @@ -462,11 +462,10 @@ static void DoDeleteAircraft(Vehicle *v) { DeleteWindowById(WC_VEHICLE_VIEW, v->index); - RebuildVehicleLists(); InvalidateWindow(WC_COMPANY, v->owner); DeleteDepotHighlightOfVehicle(v); DeleteVehicleChain(v); - InvalidateWindowClasses(WC_AIRCRAFT_LIST); + InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0); } /** Sell an aircraft. @@ -657,7 +656,7 @@ v->cargo_subtype = new_subtype; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0); } return cost; @@ -1404,7 +1403,7 @@ SetDParam(1, st->index); AddNewsItem(newsitem, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, DNC_NONE, + NS_ACCIDENT_VEHICLE, v->index, 0); @@ -1449,7 +1448,7 @@ /* show newsitem of celebrating citizens */ AddNewsItem( STR_A033_CITIZENS_CELEBRATE_FIRST, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, (v->owner == _local_player) ? NT_ARRIVAL_PLAYER : NT_ARRIVAL_OTHER, DNC_NONE, + (v->owner == _local_player) ? NS_ARRIVAL_PLAYER : NS_ARRIVAL_OTHER, v->index, 0); } diff -r 6c4314786d68 -r 8cbdb511a674 src/aircraft_gui.cpp --- a/src/aircraft_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/aircraft_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -13,6 +13,7 @@ #include "vehicle_func.h" #include "gfx_func.h" #include "order_func.h" +#include "window_gui.h" #include "table/sprites.h" #include "table/strings.h" diff -r 6c4314786d68 -r 8cbdb511a674 src/airport_gui.cpp --- a/src/airport_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/airport_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -65,53 +65,65 @@ BuildAirClick_Demolish, }; -static void BuildAirToolbWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - DrawWindowWidgets(w); - break; - - case WE_CLICK: - if (e->we.click.widget - 3 >= 0) - _build_air_button_proc[e->we.click.widget - 3](w); - break; - - case WE_KEYPRESS: { - switch (e->we.keypress.keycode) { - case '1': BuildAirClick_Airport(w); break; - case '2': BuildAirClick_Demolish(w); break; - default: return; - } - } break; +struct BuildAirToolbarWindow : Window { + BuildAirToolbarWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->FindWindowPlacementAndResize(desc); + if (_patches.link_terraform_toolbar) ShowTerraformToolbar(this); + } - case WE_PLACE_OBJ: - _place_proc(e->we.place.tile); - break; - - case WE_PLACE_DRAG: - VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method); - break; + ~BuildAirToolbarWindow() + { + if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0); + } - case WE_PLACE_MOUSEUP: - if (e->we.place.pt.x != -1 && e->we.place.select_proc == DDSP_DEMOLISH_AREA) { - GUIPlaceProcDragXY(e); - } - break; - - case WE_ABORT_PLACE_OBJ: - w->RaiseButtons(); + virtual void OnPaint() + { + this->DrawWidgets(); + } - w = FindWindowById(WC_BUILD_STATION, 0); - if (w != 0) - WP(w, def_d).close = true; - break; + virtual void OnClick(Point pt, int widget) + { + if (widget - 3 >= 0) { + _build_air_button_proc[widget - 3](this); + } + } - case WE_DESTROY: - if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0); - break; + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + switch (keycode) { + case '1': BuildAirClick_Airport(this); break; + case '2': BuildAirClick_Demolish(this); break; + default: return ES_NOT_HANDLED; + } + return ES_HANDLED; } -} + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + _place_proc(tile); + } + + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) + { + VpSelectTilesWithMethod(pt.x, pt.y, select_method); + } + + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) + { + if (pt.x != -1 && select_proc == DDSP_DEMOLISH_AREA) { + GUIPlaceProcDragXY(select_proc, start_tile, end_tile); + } + } + + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); + + delete FindWindowById(WC_BUILD_STATION, 0); + } +}; static const Widget _air_toolbar_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, @@ -128,7 +140,6 @@ WC_BUILD_TOOLBAR, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _air_toolbar_widgets, - BuildAirToolbWndProc }; void ShowBuildAirToolbar() @@ -136,114 +147,107 @@ if (!IsValidPlayer(_current_player)) return; DeleteWindowByClass(WC_BUILD_TOOLBAR); - Window *w = AllocateWindowDescFront(&_air_toolbar_desc, TRANSPORT_AIR); - if (_patches.link_terraform_toolbar) ShowTerraformToolbar(w); + AllocateWindowDescFront(&_air_toolbar_desc, TRANSPORT_AIR); } -enum { - BAW_BOTTOMPANEL = 10, - BAW_SMALL_AIRPORT, - BAW_CITY_AIRPORT, - BAW_HELIPORT, - BAW_METRO_AIRPORT, - BAW_STR_INTERNATIONAL_AIRPORT, - BAW_COMMUTER_AIRPORT, - BAW_HELIDEPOT, - BAW_STR_INTERCONTINENTAL_AIRPORT, - BAW_HELISTATION, - BAW_LAST_AIRPORT = BAW_HELISTATION, - BAW_AIRPORT_COUNT = BAW_LAST_AIRPORT - BAW_SMALL_AIRPORT + 1, - BAW_BTN_DONTHILIGHT = BAW_LAST_AIRPORT + 1, - BAW_BTN_DOHILIGHT, -}; - -static void BuildAirportPickerWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - w->SetWidgetLoweredState(BAW_BTN_DONTHILIGHT, !_station_show_coverage); - w->SetWidgetLoweredState(BAW_BTN_DOHILIGHT, _station_show_coverage); - w->LowerWidget(_selected_airport_type + BAW_SMALL_AIRPORT); - break; - - case WE_PAINT: { - int i; // airport enabling loop - uint32 avail_airports; - const AirportFTAClass *airport; - - if (WP(w, def_d).close) return; - - avail_airports = GetValidAirports(); - - w->RaiseWidget(_selected_airport_type + BAW_SMALL_AIRPORT); - if (!HasBit(avail_airports, 0) && _selected_airport_type == AT_SMALL) _selected_airport_type = AT_LARGE; - if (!HasBit(avail_airports, 1) && _selected_airport_type == AT_LARGE) _selected_airport_type = AT_SMALL; - w->LowerWidget(_selected_airport_type + BAW_SMALL_AIRPORT); - - /* 'Country Airport' starts at widget BAW_SMALL_AIRPORT, and if its bit is set, it is - * available, so take its opposite value to set the disabled state. - * There are 9 buildable airports - * XXX TODO : all airports should be held in arrays, with all relevant data. - * This should be part of newgrf-airports, i suppose - */ - for (i = 0; i < BAW_AIRPORT_COUNT; i++) w->SetWidgetDisabledState(i + BAW_SMALL_AIRPORT, !HasBit(avail_airports, i)); - - /* select default the coverage area to 'Off' (16) */ - airport = GetAirport(_selected_airport_type); - SetTileSelectSize(airport->size_x, airport->size_y); +class AirportPickerWindow : public PickerWindowBase { - int rad = _patches.modified_catchment ? airport->catchment : (uint)CA_UNMODIFIED; - - if (_station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad); - - DrawWindowWidgets(w); - /* strings such as 'Size' and 'Coverage Area' */ - int text_end = DrawStationCoverageAreaText(2, 206, SCT_ALL, rad, false); - text_end = DrawStationCoverageAreaText(2, text_end + 4, SCT_ALL, rad, true) + 4; - if (text_end != w->widget[BAW_BOTTOMPANEL].bottom) { - w->SetDirty(); - ResizeWindowForWidget(w, BAW_BOTTOMPANEL, 0, text_end - w->widget[BAW_BOTTOMPANEL].bottom); - w->SetDirty(); - } - break; - } + enum { + BAW_BOTTOMPANEL = 10, + BAW_SMALL_AIRPORT, + BAW_CITY_AIRPORT, + BAW_HELIPORT, + BAW_METRO_AIRPORT, + BAW_STR_INTERNATIONAL_AIRPORT, + BAW_COMMUTER_AIRPORT, + BAW_HELIDEPOT, + BAW_STR_INTERCONTINENTAL_AIRPORT, + BAW_HELISTATION, + BAW_LAST_AIRPORT = BAW_HELISTATION, + BAW_AIRPORT_COUNT = BAW_LAST_AIRPORT - BAW_SMALL_AIRPORT + 1, + BAW_BTN_DONTHILIGHT = BAW_LAST_AIRPORT + 1, + BAW_BTN_DOHILIGHT, + }; - case WE_CLICK: { - switch (e->we.click.widget) { - case BAW_SMALL_AIRPORT: case BAW_CITY_AIRPORT: case BAW_HELIPORT: case BAW_METRO_AIRPORT: - case BAW_STR_INTERNATIONAL_AIRPORT: case BAW_COMMUTER_AIRPORT: case BAW_HELIDEPOT: - case BAW_STR_INTERCONTINENTAL_AIRPORT: case BAW_HELISTATION: - w->RaiseWidget(_selected_airport_type + BAW_SMALL_AIRPORT); - _selected_airport_type = e->we.click.widget - BAW_SMALL_AIRPORT; - w->LowerWidget(_selected_airport_type + BAW_SMALL_AIRPORT); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; +public: - case BAW_BTN_DONTHILIGHT: case BAW_BTN_DOHILIGHT: - _station_show_coverage = (e->we.click.widget != BAW_BTN_DONTHILIGHT); - w->SetWidgetLoweredState(BAW_BTN_DONTHILIGHT, !_station_show_coverage); - w->SetWidgetLoweredState(BAW_BTN_DOHILIGHT, _station_show_coverage); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; - } - } break; + AirportPickerWindow(const WindowDesc *desc) : PickerWindowBase(desc) + { + this->SetWidgetLoweredState(BAW_BTN_DONTHILIGHT, !_station_show_coverage); + this->SetWidgetLoweredState(BAW_BTN_DOHILIGHT, _station_show_coverage); + this->LowerWidget(_selected_airport_type + BAW_SMALL_AIRPORT); - case WE_TICK: { - if (WP(w, def_d).close) { - delete w; - return; - } + this->FindWindowPlacementAndResize(desc); + } - CheckRedrawStationCoverage(w); - } break; + virtual void OnPaint() + { + int i; // airport enabling loop + uint32 avail_airports; + const AirportFTAClass *airport; - case WE_DESTROY: - if (!WP(w, def_d).close) ResetObjectToPlace(); - break; + avail_airports = GetValidAirports(); + + this->RaiseWidget(_selected_airport_type + BAW_SMALL_AIRPORT); + if (!HasBit(avail_airports, 0) && _selected_airport_type == AT_SMALL) _selected_airport_type = AT_LARGE; + if (!HasBit(avail_airports, 1) && _selected_airport_type == AT_LARGE) _selected_airport_type = AT_SMALL; + this->LowerWidget(_selected_airport_type + BAW_SMALL_AIRPORT); + + /* 'Country Airport' starts at widget BAW_SMALL_AIRPORT, and if its bit is set, it is + * available, so take its opposite value to set the disabled state. + * There are 9 buildable airports + * XXX TODO : all airports should be held in arrays, with all relevant data. + * This should be part of newgrf-airports, i suppose + */ + for (i = 0; i < BAW_AIRPORT_COUNT; i++) this->SetWidgetDisabledState(i + BAW_SMALL_AIRPORT, !HasBit(avail_airports, i)); + + /* select default the coverage area to 'Off' (16) */ + airport = GetAirport(_selected_airport_type); + SetTileSelectSize(airport->size_x, airport->size_y); + + int rad = _patches.modified_catchment ? airport->catchment : (uint)CA_UNMODIFIED; + + if (_station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad); + + this->DrawWidgets(); + /* strings such as 'Size' and 'Coverage Area' */ + int text_end = DrawStationCoverageAreaText(2, 206, SCT_ALL, rad, false); + text_end = DrawStationCoverageAreaText(2, text_end + 4, SCT_ALL, rad, true) + 4; + if (text_end != this->widget[BAW_BOTTOMPANEL].bottom) { + this->SetDirty(); + ResizeWindowForWidget(this, BAW_BOTTOMPANEL, 0, text_end - this->widget[BAW_BOTTOMPANEL].bottom); + this->SetDirty(); + } } -} + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case BAW_SMALL_AIRPORT: case BAW_CITY_AIRPORT: case BAW_HELIPORT: case BAW_METRO_AIRPORT: + case BAW_STR_INTERNATIONAL_AIRPORT: case BAW_COMMUTER_AIRPORT: case BAW_HELIDEPOT: + case BAW_STR_INTERCONTINENTAL_AIRPORT: case BAW_HELISTATION: + this->RaiseWidget(_selected_airport_type + BAW_SMALL_AIRPORT); + _selected_airport_type = widget - BAW_SMALL_AIRPORT; + this->LowerWidget(_selected_airport_type + BAW_SMALL_AIRPORT); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; + + case BAW_BTN_DONTHILIGHT: case BAW_BTN_DOHILIGHT: + _station_show_coverage = (widget != BAW_BTN_DONTHILIGHT); + this->SetWidgetLoweredState(BAW_BTN_DONTHILIGHT, !_station_show_coverage); + this->SetWidgetLoweredState(BAW_BTN_DOHILIGHT, _station_show_coverage); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; + } + } + + virtual void OnTick() + { + CheckRedrawStationCoverage(this); + } +}; static const Widget _build_airport_picker_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -277,12 +281,11 @@ WC_BUILD_STATION, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _build_airport_picker_widgets, - BuildAirportPickerWndProc }; static void ShowBuildAirportPicker() { - new Window(&_build_airport_desc); + new AirportPickerWindow(&_build_airport_desc); } void InitializeAirportGui() diff -r 6c4314786d68 -r 8cbdb511a674 src/autoreplace_cmd.cpp --- a/src/autoreplace_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/autoreplace_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -216,7 +216,7 @@ // copy/clone the orders DoCommand(0, (old_v->index << 16) | new_v->index, old_v->IsOrderListShared() ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER); new_v->cur_order_index = old_v->cur_order_index; - ChangeVehicleViewWindow(old_v, new_v); + ChangeVehicleViewWindow(old_v->index, new_v->index); new_v->profit_this_year = old_v->profit_this_year; new_v->profit_last_year = old_v->profit_last_year; new_v->service_interval = old_v->service_interval; @@ -323,7 +323,7 @@ if (v == NULL) { /* We sold all the wagons and the train is still not short enough */ SetDParam(0, front->unitnumber); - AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, front->index, 0); + AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NS_ADVICE, front->index, 0); return cost; } @@ -480,7 +480,7 @@ default: NOT_REACHED(); message = 0; break; } - AddNewsItem(message, NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0); + AddNewsItem(message, NS_ADVICE, v->index, 0); } } } diff -r 6c4314786d68 -r 8cbdb511a674 src/autoreplace_gui.cpp --- a/src/autoreplace_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/autoreplace_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -21,6 +21,7 @@ #include "widgets/dropdown_func.h" #include "engine_func.h" #include "engine_base.h" +#include "window_gui.h" #include "table/sprites.h" #include "table/strings.h" @@ -249,7 +250,7 @@ } public: - ReplaceVehicleWindow(const WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc, NULL, window_number) + ReplaceVehicleWindow(const WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc, vehicletype) { this->wagon_btnstate = true; // start with locomotives (all other vehicles will not read this bool) new (&this->list[0]) EngineList(); @@ -360,7 +361,7 @@ this->widget[RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN].data = _rail_types_list[sel_railtype]; } - DrawWindowWidgets(this); + this->DrawWidgets(); /* sets up the string for the vehicle that is being replaced to */ if (selected_id[0] != INVALID_ENGINE) { @@ -532,7 +533,6 @@ WC_REPLACE_VEHICLE, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _replace_vehicle_widgets, - NULL }; static const WindowDesc _replace_vehicle_desc = { @@ -540,7 +540,6 @@ WC_REPLACE_VEHICLE, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _replace_vehicle_widgets, - NULL }; RailType ReplaceVehicleWindow::sel_railtype = RAILTYPE_RAIL; diff -r 6c4314786d68 -r 8cbdb511a674 src/blitter/factory.hpp --- a/src/blitter/factory.hpp Mon May 19 14:14:33 2008 +0000 +++ b/src/blitter/factory.hpp Mon May 19 15:13:58 2008 +0000 @@ -56,7 +56,13 @@ name(NULL) {} - virtual ~BlitterFactoryBase() { if (this->name != NULL) GetBlitters().erase(this->name); free(this->name); } + virtual ~BlitterFactoryBase() + { + if (this->name == NULL) return; + GetBlitters().erase(this->name); + if (GetBlitters().empty()) delete &GetBlitters(); + free(this->name); + } /** * Find the requested blitter and return his class. diff -r 6c4314786d68 -r 8cbdb511a674 src/bridge_gui.cpp --- a/src/bridge_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/bridge_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -17,146 +17,329 @@ #include "viewport_func.h" #include "gfx_func.h" #include "tunnelbridge.h" +#include "sortlist_type.h" +#include "widgets/dropdown_func.h" #include "table/strings.h" -static struct BridgeData { - uint8 last_size; - uint count; - TileIndex start_tile; - TileIndex end_tile; - uint32 type; ///< Data type for the bridge. Bit 16,15 = transport type, 14..8 = road/rail pieces, 7..0 = type of bridge - BridgeType indexes[MAX_BRIDGES]; - Money costs[MAX_BRIDGES]; +/* Save the sorting during runtime */ +static Listing _bridge_sorting = {false, 0}; - BridgeData() - : last_size(4) - , count(0) - {}; -} _bridgedata; +/** + * Carriage for the data we need if we want to build a bridge + */ +struct BuildBridgeData { + BridgeType index; + const BridgeSpec *spec; + Money cost; +}; +typedef GUIList GUIBridgeList; + +/** Sort the bridges by their index */ +static int CDECL BridgeIndexSorter(const void *a, const void *b) +{ + const BuildBridgeData* ba = (BuildBridgeData*)a; + const BuildBridgeData* bb = (BuildBridgeData*)b; + int r = ba->index - bb->index; + + return (_bridge_sorting.order) ? -r : r; +} + +/** Sort the bridges by their price */ +static int CDECL BridgePriceSorter(const void *a, const void *b) +{ + const BuildBridgeData* ba = (BuildBridgeData*)a; + const BuildBridgeData* bb = (BuildBridgeData*)b; + int r = ba->cost - bb->cost; + + return (_bridge_sorting.order) ? -r : r; +} + +/** Sort the bridges by their maximum speed */ +static int CDECL BridgeSpeedSorter(const void *a, const void *b) +{ + const BuildBridgeData* ba = (BuildBridgeData*)a; + const BuildBridgeData* bb = (BuildBridgeData*)b; + int r = ba->spec->speed - bb->spec->speed; + + return (_bridge_sorting.order) ? -r : r; +} + +typedef int CDECL BridgeSortListingTypeFunction(const void*, const void*); + +/* Availible bridge sorting functions */ +static BridgeSortListingTypeFunction* const _bridge_sorter[] = { + &BridgeIndexSorter, + &BridgePriceSorter, + &BridgeSpeedSorter +}; + +/* Names of the sorting functions */ +static const StringID _bridge_sort_listing[] = { + STR_SORT_BY_NUMBER, + STR_ENGINE_SORT_COST, + STR_SORT_BY_MAX_SPEED, + INVALID_STRING_ID +}; + +/** + * Callback executed after a build Bridge CMD has been called + * + * @param scucess True if the build succeded + * @param tile The tile where the command has been executed + * @param p1 not used + * @param p2 not used + */ void CcBuildBridge(bool success, TileIndex tile, uint32 p1, uint32 p2) { if (success) SndPlayTileFx(SND_27_BLACKSMITH_ANVIL, tile); } -static void BuildBridge(Window *w, int i) -{ - delete w; - DoCommandP(_bridgedata.end_tile, _bridgedata.start_tile, - _bridgedata.type | _bridgedata.indexes[i], CcBuildBridge, - CMD_BUILD_BRIDGE | CMD_MSG(STR_5015_CAN_T_BUILD_BRIDGE_HERE)); -} - /* Names of the build bridge selection window */ enum BuildBridgeSelectionWidgets { BBSW_CLOSEBOX = 0, BBSW_CAPTION, + BBSW_DROPDOWN_ORDER, + BBSW_DROPDOWN_CRITERIA, BBSW_BRIDGE_LIST, BBSW_SCROLLBAR, BBSW_RESIZEBOX }; -static void BuildBridgeWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - w->resize.step_height = 22; - w->vscroll.count = _bridgedata.count; +class BuildBridgeWindow : public Window { +private: + /* The last size of the build bridge window + * is saved during runtime */ + static uint last_size; - if (_bridgedata.last_size <= 4) { - w->vscroll.cap = 4; - } else { - /* Resize the bridge selection window if we used a bigger one the last time */ - w->vscroll.cap = (w->vscroll.count > _bridgedata.last_size) ? _bridgedata.last_size : w->vscroll.count; - ResizeWindow(w, 0, (w->vscroll.cap - 4) * w->resize.step_height); - w->widget[BBSW_BRIDGE_LIST].data = (w->vscroll.cap << 8) + 1; - } - break; - - case WE_PAINT: { - DrawWindowWidgets(w); + TileIndex start_tile; + TileIndex end_tile; + uint32 type; + GUIBridgeList *bridges; - uint y = 15; - for (uint i = 0; (i < w->vscroll.cap) && ((i + w->vscroll.pos) < _bridgedata.count); i++) { - const BridgeSpec *b = GetBridgeSpec(_bridgedata.indexes[i + w->vscroll.pos]); + void BuildBridge(uint8 i) + { + DoCommandP(this->end_tile, this->start_tile, this->type | this->bridges->sort_list[i].index, + CcBuildBridge, CMD_BUILD_BRIDGE | CMD_MSG(STR_5015_CAN_T_BUILD_BRIDGE_HERE)); + } - SetDParam(2, _bridgedata.costs[i + w->vscroll.pos]); - SetDParam(1, b->speed * 10 / 16); - SetDParam(0, b->material); + /** Sort the builable bridges */ + void SortBridgeList() + { + /* Skip sorting if resort bit is not set */ + if (!(bridges->flags & VL_RESORT)) return; - DrawSprite(b->sprite, b->pal, 3, y); - DrawString(44, y, STR_500D, TC_FROMSTRING); - y += w->resize.step_height; - } - break; + qsort(this->bridges->sort_list, this->bridges->list_length, sizeof(this->bridges->sort_list[0]), _bridge_sorter[_bridge_sorting.criteria]); + + /* Display the current sort variant */ + this->widget[BBSW_DROPDOWN_CRITERIA].data = _bridge_sort_listing[this->bridges->sort_type]; + + bridges->flags &= ~VL_RESORT; + + /* Set the modified widgets dirty */ + this->InvalidateWidget(BBSW_DROPDOWN_CRITERIA); + this->InvalidateWidget(BBSW_BRIDGE_LIST); + } + +public: + BuildBridgeWindow(const WindowDesc *desc, TileIndex start, TileIndex end, uint32 br_type, GUIBridgeList *bl) : Window(desc), + start_tile(start), + end_tile(end), + type(br_type), + bridges(bl) + { + this->SortBridgeList(); + + /* Change the data, or the caption of the gui. Set it to road or rail, accordingly */ + this->widget[BBSW_CAPTION].data = (GB(this->type, 15, 2) == TRANSPORT_ROAD) ? STR_1803_SELECT_ROAD_BRIDGE : STR_100D_SELECT_RAIL_BRIDGE; + + this->resize.step_height = 22; + this->vscroll.count = bl->list_length; + + if (this->last_size <= 4) { + this->vscroll.cap = 4; + } else { + /* Resize the bridge selection window if we used a bigger one the last time */ + this->vscroll.cap = (this->vscroll.count > this->last_size) ? this->last_size : this->vscroll.count; + ResizeWindow(this, 0, (this->vscroll.cap - 4) * this->resize.step_height); + this->widget[BBSW_BRIDGE_LIST].data = (this->vscroll.cap << 8) + 1; } - case WE_KEYPRESS: { - const uint8 i = e->we.keypress.keycode - '1'; - if (i < 9 && i < _bridgedata.count) { - e->we.keypress.cont = false; - BuildBridge(w, i); - } + this->FindWindowPlacementAndResize(desc); + } - break; - } + ~BuildBridgeWindow() + { + free(this->bridges->sort_list); + delete bridges; + } - case WE_CLICK: - if (e->we.click.widget == BBSW_BRIDGE_LIST) { - uint ind = ((int)e->we.click.pt.y - 14) / w->resize.step_height; - if (ind < w->vscroll.cap) { - ind += w->vscroll.pos; - if (ind < _bridgedata.count) { - BuildBridge(w, ind); + virtual void OnPaint() + { + this->DrawWidgets(); + + this->DrawSortButtonState(BBSW_DROPDOWN_ORDER, (this->bridges->flags & VL_DESC) ? SBS_DOWN : SBS_UP); + + uint y = this->widget[BBSW_BRIDGE_LIST].top + 2; + + for (uint i = this->vscroll.pos; (i < (this->vscroll.cap + this->vscroll.pos)) && (i < this->bridges->list_length); i++) { + const BridgeSpec *b = this->bridges->sort_list[i].spec; + + SetDParam(2, this->bridges->sort_list[i].cost); + SetDParam(1, b->speed * 10 / 16); + SetDParam(0, b->material); + + DrawSprite(b->sprite, b->pal, 3, y); + DrawString(44, y, STR_500D, TC_FROMSTRING); + y += this->resize.step_height; + + } + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + const uint8 i = keycode - '1'; + if (i < 9 && i < this->bridges->list_length) { + /* Build the requested bridge */ + this->BuildBridge(i); + delete this; + return ES_HANDLED; + } + return ES_NOT_HANDLED; + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + default: break; + case BBSW_BRIDGE_LIST: { + uint i = ((int)pt.y - this->widget[BBSW_BRIDGE_LIST].top) / this->resize.step_height; + if (i < this->vscroll.cap) { + i += this->vscroll.pos; + if (i < this->bridges->list_length) { + this->BuildBridge(i); + delete this; } } - } - break; + } break; - case WE_RESIZE: - w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height; - w->widget[BBSW_BRIDGE_LIST].data = (w->vscroll.cap << 8) + 1; - SetVScrollCount(w, _bridgedata.count); + case BBSW_DROPDOWN_ORDER: + /* Revers the sort order */ + this->bridges->flags ^= VL_DESC; + _bridge_sorting.order = !_bridge_sorting.order; - _bridgedata.last_size = w->vscroll.cap; - break; + this->bridges->flags |= VL_RESORT; + this->SortBridgeList(); + break; + + case BBSW_DROPDOWN_CRITERIA: + ShowDropDownMenu(this, _bridge_sort_listing, bridges->sort_type, BBSW_DROPDOWN_CRITERIA, 0, 0); + break; + } } -} + + virtual void OnDropdownSelect(int widget, int index) + { + if (widget == BBSW_DROPDOWN_CRITERIA && this->bridges->sort_type != index) { + this->bridges->sort_type = index; + _bridge_sorting.criteria = index; + + this->bridges->flags |= VL_RESORT; + this->SortBridgeList(); + } + } + + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / (int)this->resize.step_height; + this->widget[BBSW_BRIDGE_LIST].data = (this->vscroll.cap << 8) + 1; + SetVScrollCount(this, this->bridges->list_length); + + this->last_size = this->vscroll.cap; + } +}; + +/* Set the default size of the Build Bridge Window */ +uint BuildBridgeWindow::last_size = 4; /* Widget definition for the rail bridge selection window */ static const Widget _build_bridge_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // BBSW_CLOSEBOX { WWT_CAPTION, RESIZE_NONE, 7, 11, 199, 0, 13, STR_100D_SELECT_RAIL_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS}, // BBSW_CAPTION -{ WWT_MATRIX, RESIZE_BOTTOM, 7, 0, 187, 14, 101, 0x401, STR_101F_BRIDGE_SELECTION_CLICK}, // BBSW_BRIDGE_LIST -{ WWT_SCROLLBAR, RESIZE_BOTTOM, 7, 188, 199, 14, 89, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // BBSW_SCROLLBAR -{ WWT_RESIZEBOX, RESIZE_TB, 7, 188, 199, 90, 101, 0x0, STR_RESIZE_BUTTON}, // BBSW_RESIZEBOX + +{ WWT_TEXTBTN, RESIZE_NONE, 7, 0, 80, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP}, // BBSW_DROPDOWN_ORDER +{ WWT_DROPDOWN, RESIZE_NONE, 7, 81, 199, 14, 25, 0x0, STR_SORT_CRITERIA_TIP}, // BBSW_DROPDOWN_CRITERIA + +{ WWT_MATRIX, RESIZE_BOTTOM, 7, 0, 187, 26, 113, 0x401, STR_101F_BRIDGE_SELECTION_CLICK}, // BBSW_BRIDGE_LIST +{ WWT_SCROLLBAR, RESIZE_BOTTOM, 7, 188, 199, 26, 101, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // BBSW_SCROLLBAR +{ WWT_RESIZEBOX, RESIZE_TB, 7, 188, 199, 102, 113, 0x0, STR_RESIZE_BUTTON}, // BBSW_RESIZEBOX { WIDGETS_END}, }; /* Window definition for the rail bridge selection window */ static const WindowDesc _build_bridge_desc = { - WDP_AUTO, WDP_AUTO, 200, 102, 200, 102, + WDP_AUTO, WDP_AUTO, 200, 114, 200, 114, WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE, _build_bridge_widgets, - BuildBridgeWndProc }; +/** + * Add a buildable bridge to the list. + * If the list is empty a new one is created. + * + * @param bl The list which we want to manage + * @param item The item to add + * @return The pointer to the list + */ +static GUIBridgeList *PushBridgeList(GUIBridgeList *bl, BuildBridgeData item) +{ + if (bl == NULL) { + /* Create the list if needed */ + bl = new GUIBridgeList(); + bl->flags |= VL_RESORT; + if (_bridge_sorting.order) bl->flags |= VL_DESC; + bl->list_length = 1; + bl->sort_type = _bridge_sorting.criteria; + } else { + /* Resize the list */ + bl->list_length++; + } + + bl->sort_list = ReallocT(bl->sort_list, bl->list_length); + + bl->sort_list[bl->list_length - 1] = item; + + return bl; +} + +/** + * Prepare the data for the build a bridge window. + * If we can't build a bridge under the given conditions + * show an error message. + * + * @parma start The start tile of the bridge + * @param end The end tile of the bridge + * @param transport_type The transport type + * @param bridge_type The bridge type + */ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transport_type, byte bridge_type) { DeleteWindowById(WC_BUILD_BRIDGE, 0); - _bridgedata.type = (transport_type << 15) | (bridge_type << 8); //prepare the parameter for use only once - _bridgedata.start_tile = start; - _bridgedata.end_tile = end; + /* Data type for the bridge. + * Bit 16,15 = transport type, + * 14..8 = road/rail pieces, + * 7..0 = type of bridge */ + uint32 type = (transport_type << 15) | (bridge_type << 8); /* only query bridge building possibility once, result is the same for all bridges! * returns CMD_ERROR on failure, and price on success */ StringID errmsg = INVALID_STRING_ID; - CommandCost ret = DoCommand(end, start, _bridgedata.type, DC_AUTO | DC_QUERY_COST, CMD_BUILD_BRIDGE); + CommandCost ret = DoCommand(end, start, type, DC_AUTO | DC_QUERY_COST, CMD_BUILD_BRIDGE); - uint8 j = 0; + GUIBridgeList *bl = NULL; if (CmdFailed(ret)) { errmsg = _error_message; } else { @@ -171,22 +354,19 @@ for (BridgeType brd_type = 0; brd_type != MAX_BRIDGES; brd_type++) { if (CheckBridge_Stuff(brd_type, bridge_len)) { /* bridge is accepted, add to list */ - const BridgeSpec *b = GetBridgeSpec(brd_type); + BuildBridgeData item; + item.index = brd_type; + item.spec = GetBridgeSpec(brd_type); /* Add to terraforming & bulldozing costs the cost of the * bridge itself (not computed with DC_QUERY_COST) */ - _bridgedata.costs[j] = ret.GetCost() + (((int64)tot_bridgedata_len * _price.build_bridge * b->price) >> 8); - _bridgedata.indexes[j] = brd_type; - j++; + item.cost = ret.GetCost() + (((int64)tot_bridgedata_len * _price.build_bridge * item.spec->price) >> 8); + bl = PushBridgeList(bl, item); } } - - _bridgedata.count = j; } - if (j != 0) { - Window *w = new Window(&_build_bridge_desc); - /* Change the data, or the caption of the gui. Set it to road or rail, accordingly */ - w->widget[BBSW_CAPTION].data = (transport_type == TRANSPORT_ROAD) ? STR_1803_SELECT_ROAD_BRIDGE : STR_100D_SELECT_RAIL_BRIDGE; + if (bl != NULL) { + new BuildBridgeWindow(&_build_bridge_desc, start, end, type, bl); } else { ShowErrorMessage(errmsg, STR_5015_CAN_T_BUILD_BRIDGE_HERE, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE); } diff -r 6c4314786d68 -r 8cbdb511a674 src/build_vehicle_gui.cpp --- a/src/build_vehicle_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/build_vehicle_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -28,27 +28,11 @@ #include "gfx_func.h" #include "widgets/dropdown_func.h" #include "string_func.h" +#include "window_gui.h" #include "table/sprites.h" #include "table/strings.h" -struct buildvehicle_d { - VehicleType vehicle_type; - union { - RailTypeByte railtype; - AirportFTAClass::Flags flags; - RoadTypes roadtypes; - } filter; - byte sel_index; ///< deprecated value, used for 'unified' ship and road - bool descending_sort_order; - byte sort_criteria; - bool regenerate_list; - EngineID sel_engine; - EngineID rename_engine; - EngineList eng_list; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(buildvehicle_d)); - enum BuildVehicleWidgets { BUILD_VEHICLE_WIDGET_CLOSEBOX = 0, BUILD_VEHICLE_WIDGET_CAPTION, @@ -78,52 +62,7 @@ { WIDGETS_END}, }; -/* Setup widget strings to fit the different types of vehicles */ -static void SetupWindowStrings(Window *w, VehicleType type) -{ - switch (type) { - default: NOT_REACHED(); - - case VEH_TRAIN: - w->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_JUST_STRING; - w->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_8843_TRAIN_VEHICLE_SELECTION; - w->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_881F_BUILD_VEHICLE; - w->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_8844_BUILD_THE_HIGHLIGHTED_TRAIN; - w->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_8820_RENAME; - w->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_8845_RENAME_TRAIN_VEHICLE_TYPE; - break; - - case VEH_ROAD: - w->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_9006_NEW_ROAD_VEHICLES; - w->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_9026_ROAD_VEHICLE_SELECTION; - w->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_9007_BUILD_VEHICLE; - w->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_9027_BUILD_THE_HIGHLIGHTED_ROAD; - w->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_9034_RENAME; - w->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9035_RENAME_ROAD_VEHICLE_TYPE; - break; - - case VEH_SHIP: - w->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_9808_NEW_SHIPS; - w->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_9825_SHIP_SELECTION_LIST_CLICK; - w->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_9809_BUILD_SHIP; - w->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_9826_BUILD_THE_HIGHLIGHTED_SHIP; - w->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_9836_RENAME; - w->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9837_RENAME_SHIP_TYPE; - break; - - case VEH_AIRCRAFT: - w->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_A005_NEW_AIRCRAFT; - w->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_A025_AIRCRAFT_SELECTION_LIST; - w->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_A006_BUILD_AIRCRAFT; - w->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT; - w->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_A037_RENAME; - w->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_A038_RENAME_AIRCRAFT_TYPE; - break; - } -} - static bool _internal_sort_order; // descending/ascending - static byte _last_sort_criteria[] = {0, 0, 0, 0}; static bool _last_sort_order[] = {false, false, false, false}; @@ -216,8 +155,8 @@ const RailVehicleInfo *rvi_a = RailVehInfo(*(const EngineID*)a); const RailVehicleInfo *rvi_b = RailVehInfo(*(const EngineID*)b); - int va = rvi_a->power << (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 1 : 0); - int vb = rvi_b->power << (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 1 : 0); + int va = rvi_a->power; + int vb = rvi_b->power; int r = va - vb; return _internal_sort_order ? -r : r; @@ -600,7 +539,7 @@ /* Max speed - Engine power */ SetDParam(0, GetEngineProperty(engine_number, 0x09, rvi->max_speed) * 10 / 16); - SetDParam(1, GetEngineProperty(engine_number, 0x0B, rvi->power) << multihead); + SetDParam(1, GetEngineProperty(engine_number, 0x0B, rvi->power)); DrawString(x, y, STR_PURCHASE_INFO_SPEED_POWER, TC_FROMSTRING); y += 10; @@ -613,7 +552,7 @@ /* Running cost */ if (rvi->running_cost_class != 0xFF) { - SetDParam(0, (GetEngineProperty(engine_number, 0x0D, rvi->running_cost) * GetPriceByIndex(rvi->running_cost_class) >> 8) << multihead); + SetDParam(0, GetEngineProperty(engine_number, 0x0D, rvi->running_cost) * GetPriceByIndex(rvi->running_cost_class) >> 8); DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING); y += 10; } @@ -785,143 +724,6 @@ return y; } -/* Figure out what train EngineIDs to put in the list */ -static void GenerateBuildTrainList(Window *w) -{ - EngineID sel_id = INVALID_ENGINE; - int num_engines = 0; - int num_wagons = 0; - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - bv->filter.railtype = (w->window_number <= VEH_END) ? RAILTYPE_END : GetRailType(w->window_number); - - bv->eng_list.clear(); - - /* Make list of all available train engines and wagons. - * Also check to see if the previously selected engine is still available, - * and if not, reset selection to INVALID_ENGINE. This could be the case - * when engines become obsolete and are removed */ - const Engine *e; - FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { - EngineID eid = e->index; - const RailVehicleInfo *rvi = &e->u.rail; - - if (bv->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, bv->filter.railtype)) continue; - if (!IsEngineBuildable(eid, VEH_TRAIN, _local_player)) continue; - - bv->eng_list.push_back(eid); - if (rvi->railveh_type != RAILVEH_WAGON) { - num_engines++; - } else { - num_wagons++; - } - - if (eid == bv->sel_engine) sel_id = eid; - } - - bv->sel_engine = sel_id; - - /* make engines first, and then wagons, sorted by ListPositionOfEngine() */ - _internal_sort_order = false; - EngList_Sort(&bv->eng_list, TrainEnginesThenWagonsSorter); - - /* and then sort engines */ - _internal_sort_order = bv->descending_sort_order; - EngList_SortPartial(&bv->eng_list, _sorter[0][bv->sort_criteria], 0, num_engines); - - /* and finally sort wagons */ - EngList_SortPartial(&bv->eng_list, _sorter[0][bv->sort_criteria], num_engines, num_wagons); -} - -/* Figure out what road vehicle EngineIDs to put in the list */ -static void GenerateBuildRoadVehList(Window *w) -{ - EngineID sel_id = INVALID_ENGINE; - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - bv->eng_list.clear(); - - const Engine *e; - FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { - EngineID eid = e->index; - if (!IsEngineBuildable(eid, VEH_ROAD, _local_player)) continue; - if (!HasBit(bv->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue; - bv->eng_list.push_back(eid); - - if (eid == bv->sel_engine) sel_id = eid; - } - bv->sel_engine = sel_id; -} - -/* Figure out what ship EngineIDs to put in the list */ -static void GenerateBuildShipList(Window *w) -{ - EngineID sel_id = INVALID_ENGINE; - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - bv->eng_list.clear(); - - const Engine *e; - FOR_ALL_ENGINES_OF_TYPE(e, VEH_SHIP) { - EngineID eid = e->index; - if (!IsEngineBuildable(eid, VEH_SHIP, _local_player)) continue; - bv->eng_list.push_back(eid); - - if (eid == bv->sel_engine) sel_id = eid; - } - bv->sel_engine = sel_id; -} - -/* Figure out what aircraft EngineIDs to put in the list */ -static void GenerateBuildAircraftList(Window *w) -{ - EngineID sel_id = INVALID_ENGINE; - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - bv->eng_list.clear(); - - /* Make list of all available planes. - * Also check to see if the previously selected plane is still available, - * and if not, reset selection to INVALID_ENGINE. This could be the case - * when planes become obsolete and are removed */ - const Engine *e; - FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) { - EngineID eid = e->index; - if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_player)) continue; - /* First VEH_END window_numbers are fake to allow a window open for all different types at once */ - if (w->window_number > VEH_END && !CanAircraftUseStation(eid, w->window_number)) continue; - - bv->eng_list.push_back(eid); - if (eid == bv->sel_engine) sel_id = eid; - } - - bv->sel_engine = sel_id; -} - -/* Generate the list of vehicles */ -static void GenerateBuildList(Window *w) -{ - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - switch (bv->vehicle_type) { - default: NOT_REACHED(); - case VEH_TRAIN: - GenerateBuildTrainList(w); - return; // trains should not reach the last sorting - case VEH_ROAD: - GenerateBuildRoadVehList(w); - break; - case VEH_SHIP: - GenerateBuildShipList(w); - break; - case VEH_AIRCRAFT: - GenerateBuildAircraftList(w); - break; - } - _internal_sort_order = bv->descending_sort_order; - EngList_Sort(&bv->eng_list, _sorter[bv->vehicle_type][bv->sort_criteria]); -} - static void DrawVehicleEngine(VehicleType type, int x, int y, EngineID engine, SpriteID pal) { switch (type) { @@ -990,199 +792,400 @@ } } -static void DrawBuildVehicleWindow(Window *w) -{ - const buildvehicle_d *bv = &WP(w, buildvehicle_d); - uint max = min(w->vscroll.pos + w->vscroll.cap, bv->eng_list.size()); - - w->SetWidgetDisabledState(BUILD_VEHICLE_WIDGET_BUILD, w->window_number <= VEH_END); - - SetVScrollCount(w, bv->eng_list.size()); - SetDParam(0, bv->filter.railtype + STR_881C_NEW_RAIL_VEHICLES); // This should only affect rail vehicles - /* Set text of sort by dropdown */ - w->widget[BUILD_VEHICLE_WIDGET_SORT_DROPDOWN].data = _sort_listing[bv->vehicle_type][bv->sort_criteria]; - - DrawWindowWidgets(w); - - DrawEngineList(bv->vehicle_type, w->widget[BUILD_VEHICLE_WIDGET_LIST].left + 2, w->widget[BUILD_VEHICLE_WIDGET_LIST].top + 1, bv->eng_list, w->vscroll.pos, max, bv->sel_engine, 0, DEFAULT_GROUP); +struct BuildVehicleWindow : Window { + VehicleType vehicle_type; + union { + RailTypeByte railtype; + AirportFTAClass::Flags flags; + RoadTypes roadtypes; + } filter; + byte sel_index; ///< deprecated value, used for 'unified' ship and road + bool descending_sort_order; + byte sort_criteria; + bool regenerate_list; + EngineID sel_engine; + EngineID rename_engine; + EngineList eng_list; - if (bv->sel_engine != INVALID_ENGINE) { - const Widget *wi = &w->widget[BUILD_VEHICLE_WIDGET_PANEL]; - int text_end = DrawVehiclePurchaseInfo(2, wi->top + 1, wi->right - wi->left - 2, bv->sel_engine); + BuildVehicleWindow(const WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc, tile == 0 ? (int)type : tile) + { + this->vehicle_type = type; + int vlh = GetVehicleListHeight(this->vehicle_type); - if (text_end > wi->bottom) { - w->SetDirty(); - ResizeWindowForWidget(w, BUILD_VEHICLE_WIDGET_PANEL, 0, text_end - wi->bottom); - w->SetDirty(); + ResizeWindow(this, 0, vlh - 14); + this->resize.step_height = vlh; + this->vscroll.cap = 1; + this->widget[BUILD_VEHICLE_WIDGET_LIST].data = 0x101; + + this->resize.width = this->width; + this->resize.height = this->height; + + this->caption_color = (tile != 0) ? GetTileOwner(tile) : _local_player; + + this->sel_engine = INVALID_ENGINE; + this->regenerate_list = false; + + this->sort_criteria = _last_sort_criteria[type]; + this->descending_sort_order = _last_sort_order[type]; + + switch (type) { + default: NOT_REACHED(); + case VEH_TRAIN: + this->filter.railtype = (tile == 0) ? RAILTYPE_END : GetRailType(tile); + break; + case VEH_ROAD: + this->filter.roadtypes = (tile == 0) ? ROADTYPES_ALL : GetRoadTypes(tile); + case VEH_SHIP: + break; + case VEH_AIRCRAFT: + this->filter.flags = + tile == 0 ? AirportFTAClass::ALL : GetStationByTile(tile)->Airport()->flags; + break; + } + this->SetupWindowStrings(type); + ResizeButtons(this, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME); + + this->GenerateBuildList(); // generate the list, since we need it in the next line + /* Select the first engine in the list as default when opening the window */ + if (this->eng_list.size() > 0) this->sel_engine = this->eng_list[0]; + + this->FindWindowPlacementAndResize(desc); + } + + /* Setup widget strings to fit the different types of vehicles */ + void SetupWindowStrings(VehicleType type) + { + switch (type) { + default: NOT_REACHED(); + + case VEH_TRAIN: + this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_JUST_STRING; + this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_8843_TRAIN_VEHICLE_SELECTION; + this->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_881F_BUILD_VEHICLE; + this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_8844_BUILD_THE_HIGHLIGHTED_TRAIN; + this->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_8820_RENAME; + this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_8845_RENAME_TRAIN_VEHICLE_TYPE; + break; + + case VEH_ROAD: + this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_9006_NEW_ROAD_VEHICLES; + this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_9026_ROAD_VEHICLE_SELECTION; + this->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_9007_BUILD_VEHICLE; + this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_9027_BUILD_THE_HIGHLIGHTED_ROAD; + this->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_9034_RENAME; + this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9035_RENAME_ROAD_VEHICLE_TYPE; + break; + + case VEH_SHIP: + this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_9808_NEW_SHIPS; + this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_9825_SHIP_SELECTION_LIST_CLICK; + this->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_9809_BUILD_SHIP; + this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_9826_BUILD_THE_HIGHLIGHTED_SHIP; + this->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_9836_RENAME; + this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9837_RENAME_SHIP_TYPE; + break; + + case VEH_AIRCRAFT: + this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_A005_NEW_AIRCRAFT; + this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_A025_AIRCRAFT_SELECTION_LIST; + this->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_A006_BUILD_AIRCRAFT; + this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT; + this->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_A037_RENAME; + this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_A038_RENAME_AIRCRAFT_TYPE; + break; } } - DrawSortButtonState(w, BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING, bv->descending_sort_order ? SBS_DOWN : SBS_UP); -} - -static void BuildVehicleClickEvent(Window *w, WindowEvent *e) -{ - buildvehicle_d *bv = &WP(w, buildvehicle_d); + /* Figure out what train EngineIDs to put in the list */ + void GenerateBuildTrainList() + { + EngineID sel_id = INVALID_ENGINE; + int num_engines = 0; + int num_wagons = 0; - switch (e->we.click.widget) { - case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING: - bv->descending_sort_order ^= true; - _last_sort_order[bv->vehicle_type] = bv->descending_sort_order; - bv->regenerate_list = true; - w->SetDirty(); - break; + this->filter.railtype = (this->window_number <= VEH_END) ? RAILTYPE_END : GetRailType(this->window_number); - case BUILD_VEHICLE_WIDGET_LIST: { - uint i = (e->we.click.pt.y - w->widget[BUILD_VEHICLE_WIDGET_LIST].top) / GetVehicleListHeight(bv->vehicle_type) + w->vscroll.pos; - size_t num_items = bv->eng_list.size(); - bv->sel_engine = (i < num_items) ? bv->eng_list[i] : INVALID_ENGINE; - w->SetDirty(); - break; + this->eng_list.clear(); + + /* Make list of all available train engines and wagons. + * Also check to see if the previously selected engine is still available, + * and if not, reset selection to INVALID_ENGINE. This could be the case + * when engines become obsolete and are removed */ + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { + EngineID eid = e->index; + const RailVehicleInfo *rvi = &e->u.rail; + + if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue; + if (!IsEngineBuildable(eid, VEH_TRAIN, _local_player)) continue; + + this->eng_list.push_back(eid); + if (rvi->railveh_type != RAILVEH_WAGON) { + num_engines++; + } else { + num_wagons++; + } + + if (eid == this->sel_engine) sel_id = eid; } - case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN: // Select sorting criteria dropdown menu - ShowDropDownMenu(w, _sort_listing[bv->vehicle_type], bv->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0); - break; + this->sel_engine = sel_id; - case BUILD_VEHICLE_WIDGET_BUILD: { - EngineID sel_eng = bv->sel_engine; - if (sel_eng != INVALID_ENGINE) { - switch (bv->vehicle_type) { - default: NOT_REACHED(); - case VEH_TRAIN: - DoCommandP(w->window_number, sel_eng, 0, (RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildLoco, - CMD_BUILD_RAIL_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE)); - break; - case VEH_ROAD: - DoCommandP(w->window_number, sel_eng, 0, CcBuildRoadVeh, CMD_BUILD_ROAD_VEH | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE)); - break; - case VEH_SHIP: - DoCommandP(w->window_number, sel_eng, 0, CcBuildShip, CMD_BUILD_SHIP | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP)); - break; - case VEH_AIRCRAFT: - DoCommandP(w->window_number, sel_eng, 0, CcBuildAircraft, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT)); - break; - } - } - break; + /* make engines first, and then wagons, sorted by ListPositionOfEngine() */ + _internal_sort_order = false; + EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter); + + /* and then sort engines */ + _internal_sort_order = this->descending_sort_order; + EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], 0, num_engines); + + /* and finally sort wagons */ + EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], num_engines, num_wagons); + } + + /* Figure out what road vehicle EngineIDs to put in the list */ + void GenerateBuildRoadVehList() + { + EngineID sel_id = INVALID_ENGINE; + + this->eng_list.clear(); + + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { + EngineID eid = e->index; + if (!IsEngineBuildable(eid, VEH_ROAD, _local_player)) continue; + if (!HasBit(this->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue; + this->eng_list.push_back(eid); + + if (eid == this->sel_engine) sel_id = eid; + } + this->sel_engine = sel_id; + } + + /* Figure out what ship EngineIDs to put in the list */ + void GenerateBuildShipList() + { + EngineID sel_id = INVALID_ENGINE; + this->eng_list.clear(); + + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_SHIP) { + EngineID eid = e->index; + if (!IsEngineBuildable(eid, VEH_SHIP, _local_player)) continue; + this->eng_list.push_back(eid); + + if (eid == this->sel_engine) sel_id = eid; + } + this->sel_engine = sel_id; + } + + /* Figure out what aircraft EngineIDs to put in the list */ + void GenerateBuildAircraftList() + { + EngineID sel_id = INVALID_ENGINE; + + this->eng_list.clear(); + + /* Make list of all available planes. + * Also check to see if the previously selected plane is still available, + * and if not, reset selection to INVALID_ENGINE. This could be the case + * when planes become obsolete and are removed */ + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) { + EngineID eid = e->index; + if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_player)) continue; + /* First VEH_END window_numbers are fake to allow a window open for all different types at once */ + if (this->window_number > VEH_END && !CanAircraftUseStation(eid, this->window_number)) continue; + + this->eng_list.push_back(eid); + if (eid == this->sel_engine) sel_id = eid; } - case BUILD_VEHICLE_WIDGET_RENAME: { - EngineID sel_eng = bv->sel_engine; - if (sel_eng != INVALID_ENGINE) { - StringID str = STR_NULL; + this->sel_engine = sel_id; + } - bv->rename_engine = sel_eng; - switch (bv->vehicle_type) { - default: NOT_REACHED(); - case VEH_TRAIN: str = STR_886A_RENAME_TRAIN_VEHICLE_TYPE; break; - case VEH_ROAD: str = STR_9036_RENAME_ROAD_VEHICLE_TYPE; break; - case VEH_SHIP: str = STR_9838_RENAME_SHIP_TYPE; break; - case VEH_AIRCRAFT: str = STR_A039_RENAME_AIRCRAFT_TYPE; break; + /* Generate the list of vehicles */ + void GenerateBuildList() + { + switch (this->vehicle_type) { + default: NOT_REACHED(); + case VEH_TRAIN: + this->GenerateBuildTrainList(); + return; // trains should not reach the last sorting + case VEH_ROAD: + this->GenerateBuildRoadVehList(); + break; + case VEH_SHIP: + this->GenerateBuildShipList(); + break; + case VEH_AIRCRAFT: + this->GenerateBuildAircraftList(); + break; + } + _internal_sort_order = this->descending_sort_order; + EngList_Sort(&this->eng_list, _sorter[this->vehicle_type][this->sort_criteria]); + } + + void OnClick(Point pt, int widget) + { + switch (widget) { + case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING: + this->descending_sort_order ^= true; + _last_sort_order[this->vehicle_type] = this->descending_sort_order; + this->regenerate_list = true; + this->SetDirty(); + break; + + case BUILD_VEHICLE_WIDGET_LIST: { + uint i = (pt.y - this->widget[BUILD_VEHICLE_WIDGET_LIST].top) / GetVehicleListHeight(this->vehicle_type) + this->vscroll.pos; + size_t num_items = this->eng_list.size(); + this->sel_engine = (i < num_items) ? this->eng_list[i] : INVALID_ENGINE; + this->SetDirty(); + break; + } + + case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN: // Select sorting criteria dropdown menu + ShowDropDownMenu(this, _sort_listing[this->vehicle_type], this->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0); + break; + + case BUILD_VEHICLE_WIDGET_BUILD: { + EngineID sel_eng = this->sel_engine; + if (sel_eng != INVALID_ENGINE) { + switch (this->vehicle_type) { + default: NOT_REACHED(); + case VEH_TRAIN: + DoCommandP(this->window_number, sel_eng, 0, (RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildLoco, + CMD_BUILD_RAIL_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE)); + break; + case VEH_ROAD: + DoCommandP(this->window_number, sel_eng, 0, CcBuildRoadVeh, CMD_BUILD_ROAD_VEH | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE)); + break; + case VEH_SHIP: + DoCommandP(this->window_number, sel_eng, 0, CcBuildShip, CMD_BUILD_SHIP | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP)); + break; + case VEH_AIRCRAFT: + DoCommandP(this->window_number, sel_eng, 0, CcBuildAircraft, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT)); + break; + } } - SetDParam(0, sel_eng); - ShowQueryString(STR_ENGINE_NAME, str, 31, 160, w, CS_ALPHANUMERAL); + break; } - break; + + case BUILD_VEHICLE_WIDGET_RENAME: { + EngineID sel_eng = this->sel_engine; + if (sel_eng != INVALID_ENGINE) { + StringID str = STR_NULL; + + this->rename_engine = sel_eng; + switch (this->vehicle_type) { + default: NOT_REACHED(); + case VEH_TRAIN: str = STR_886A_RENAME_TRAIN_VEHICLE_TYPE; break; + case VEH_ROAD: str = STR_9036_RENAME_ROAD_VEHICLE_TYPE; break; + case VEH_SHIP: str = STR_9838_RENAME_SHIP_TYPE; break; + case VEH_AIRCRAFT: str = STR_A039_RENAME_AIRCRAFT_TYPE; break; + } + SetDParam(0, sel_eng); + ShowQueryString(STR_ENGINE_NAME, str, 31, 160, this, CS_ALPHANUMERAL); + } + break; + } } } -} - -static void NewVehicleWndProc(Window *w, WindowEvent *e) -{ - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - switch (e->event) { - case WE_CREATE: { - bv->vehicle_type = *(VehicleType*)e->we.create.data; - int vlh = GetVehicleListHeight(bv->vehicle_type); - - ResizeWindow(w, 0, vlh - 14); - w->resize.step_height = vlh; - w->vscroll.cap = 1; - w->widget[BUILD_VEHICLE_WIDGET_LIST].data = 0x101; - - w->resize.width = w->width; - w->resize.height = w->height; - } break; - - case WE_INVALIDATE_DATA: - bv->regenerate_list = true; - w->SetDirty(); - break; - - case WE_DESTROY: - bv->eng_list.~EngineList(); // call destructor explicitly - break; - case WE_PAINT: - if (bv->regenerate_list) { - bv->regenerate_list = false; - GenerateBuildList(w); - } - DrawBuildVehicleWindow(w); - break; - - case WE_CLICK: - BuildVehicleClickEvent(w, e); - break; + virtual void OnInvalidateData(int data) + { + this->regenerate_list = true; + } - case WE_DOUBLE_CLICK: - if (e->we.click.widget == BUILD_VEHICLE_WIDGET_LIST) { - /* When double clicking, we want to buy a vehicle */ - e->we.click.widget = BUILD_VEHICLE_WIDGET_BUILD; - BuildVehicleClickEvent(w, e); - } - break; - - case WE_ON_EDIT_TEXT: { - if (!StrEmpty(e->we.edittext.str)) { - StringID str = STR_NULL; - _cmd_text = e->we.edittext.str; - switch (bv->vehicle_type) { - default: NOT_REACHED(); - case VEH_TRAIN: str = STR_886B_CAN_T_RENAME_TRAIN_VEHICLE; break; - case VEH_ROAD: str = STR_9037_CAN_T_RENAME_ROAD_VEHICLE; break; - case VEH_SHIP: str = STR_9839_CAN_T_RENAME_SHIP_TYPE; break; - case VEH_AIRCRAFT: str = STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE; break; - } - DoCommandP(0, bv->rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(str)); - } - break; + virtual void OnPaint() + { + if (this->regenerate_list) { + this->regenerate_list = false; + this->GenerateBuildList(); } - case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list - if (bv->sort_criteria != e->we.dropdown.index) { - bv->sort_criteria = e->we.dropdown.index; - _last_sort_criteria[bv->vehicle_type] = bv->sort_criteria; - bv->regenerate_list = true; - } - w->SetDirty(); - break; + uint max = min(this->vscroll.pos + this->vscroll.cap, this->eng_list.size()); - case WE_RESIZE: - if (e->we.sizing.diff.x != 0) ResizeButtons(w, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME); - if (e->we.sizing.diff.y == 0) break; + this->SetWidgetDisabledState(BUILD_VEHICLE_WIDGET_BUILD, this->window_number <= VEH_END); - w->vscroll.cap += e->we.sizing.diff.y / (int)GetVehicleListHeight(bv->vehicle_type); - w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1; - break; + SetVScrollCount(this, this->eng_list.size()); + SetDParam(0, this->filter.railtype + STR_881C_NEW_RAIL_VEHICLES); // This should only affect rail vehicles + + /* Set text of sort by dropdown */ + this->widget[BUILD_VEHICLE_WIDGET_SORT_DROPDOWN].data = _sort_listing[this->vehicle_type][this->sort_criteria]; + + this->DrawWidgets(); + + DrawEngineList(this->vehicle_type, this->widget[BUILD_VEHICLE_WIDGET_LIST].left + 2, this->widget[BUILD_VEHICLE_WIDGET_LIST].top + 1, this->eng_list, this->vscroll.pos, max, this->sel_engine, 0, DEFAULT_GROUP); + + if (this->sel_engine != INVALID_ENGINE) { + const Widget *wi = &this->widget[BUILD_VEHICLE_WIDGET_PANEL]; + int text_end = DrawVehiclePurchaseInfo(2, wi->top + 1, wi->right - wi->left - 2, this->sel_engine); + + if (text_end > wi->bottom) { + this->SetDirty(); + ResizeWindowForWidget(this, BUILD_VEHICLE_WIDGET_PANEL, 0, text_end - wi->bottom); + this->SetDirty(); + } + } + + this->DrawSortButtonState(BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP); } -} + + virtual void OnDoubleClick(Point pt, int widget) + { + if (widget == BUILD_VEHICLE_WIDGET_LIST) { + /* When double clicking, we want to buy a vehicle */ + this->OnClick(pt, BUILD_VEHICLE_WIDGET_BUILD); + } + } + + virtual void OnQueryTextFinished(char *str) + { + if (!StrEmpty(str)) { + StringID err_str = STR_NULL; + _cmd_text = str; + switch (this->vehicle_type) { + default: NOT_REACHED(); + case VEH_TRAIN: err_str = STR_886B_CAN_T_RENAME_TRAIN_VEHICLE; break; + case VEH_ROAD: err_str = STR_9037_CAN_T_RENAME_ROAD_VEHICLE; break; + case VEH_SHIP: err_str = STR_9839_CAN_T_RENAME_SHIP_TYPE; break; + case VEH_AIRCRAFT: err_str = STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE; break; + } + DoCommandP(0, this->rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(err_str)); + } + } + + virtual void OnDropdownSelect(int widget, int index) + { + if (this->sort_criteria != index) { + this->sort_criteria = index; + _last_sort_criteria[this->vehicle_type] = this->sort_criteria; + this->regenerate_list = true; + } + this->SetDirty(); + } + + virtual void OnResize(Point new_size, Point delta) + { + if (delta.x != 0) ResizeButtons(this, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME); + if (delta.y == 0) return; + + this->vscroll.cap += delta.y / (int)GetVehicleListHeight(this->vehicle_type); + this->widget[BUILD_VEHICLE_WIDGET_LIST].data = (this->vscroll.cap << 8) + 1; + } +}; static const WindowDesc _build_vehicle_desc = { WDP_AUTO, WDP_AUTO, 240, 174, 240, 256, WC_BUILD_VEHICLE, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _build_vehicle_widgets, - NewVehicleWndProc }; void ShowBuildVehicleWindow(TileIndex tile, VehicleType type) { - buildvehicle_d *bv; - Window *w; /* We want to be able to open both Available Train as Available Ships, * so if tile == 0 (Available XXX Window), use 'type' as unique number. * As it always is a low value, it won't collide with any real tile @@ -1193,39 +1196,5 @@ DeleteWindowById(WC_BUILD_VEHICLE, num); - w = AllocateWindowDescFront(&_build_vehicle_desc, num, &type); - - if (w == NULL) return; - - w->caption_color = (tile != 0) ? GetTileOwner(tile) : _local_player; - - bv = &WP(w, buildvehicle_d); - new (&bv->eng_list) EngineList(); - bv->sel_engine = INVALID_ENGINE; - - bv->regenerate_list = false; - - bv->sort_criteria = _last_sort_criteria[type]; - bv->descending_sort_order = _last_sort_order[type]; - - switch (type) { - default: NOT_REACHED(); - case VEH_TRAIN: - WP(w, buildvehicle_d).filter.railtype = (tile == 0) ? RAILTYPE_END : GetRailType(tile); - break; - case VEH_ROAD: - WP(w, buildvehicle_d).filter.roadtypes = (tile == 0) ? ROADTYPES_ALL : GetRoadTypes(tile); - case VEH_SHIP: - break; - case VEH_AIRCRAFT: - bv->filter.flags = - tile == 0 ? AirportFTAClass::ALL : GetStationByTile(tile)->Airport()->flags; - break; - } - SetupWindowStrings(w, type); - ResizeButtons(w, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME); - - GenerateBuildList(w); // generate the list, since we need it in the next line - /* Select the first engine in the list as default when opening the window */ - if (bv->eng_list.size() > 0) bv->sel_engine = bv->eng_list[0]; + new BuildVehicleWindow(&_build_vehicle_desc, tile, type); } diff -r 6c4314786d68 -r 8cbdb511a674 src/cheat_gui.cpp --- a/src/cheat_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/cheat_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -120,114 +120,117 @@ { WIDGETS_END}, }; -static void CheatsWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - int clk = WP(w, def_d).data_1; - - DrawWindowWidgets(w); - DrawStringMultiCenter(200, 25, STR_CHEATS_WARNING, w->width - 50); - - for (int i = 0, x = 0, y = 45; i != lengthof(_cheats_ui); i++) { - const CheatEntry *ce = &_cheats_ui[i]; - - DrawSprite((*ce->been_used) ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, PAL_NONE, x + 5, y + 2); - - switch (ce->type) { - case SLE_BOOL: { - bool on = (*(bool*)ce->variable); - - DrawFrameRect(x + 20, y + 1, x + 30 + 9, y + 9, on ? 6 : 4, on ? FR_LOWERED : FR_NONE); - SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); - } break; - - default: { - int32 val = (int32)ReadValue(ce->variable, ce->type); - char buf[512]; +struct CheatWindow : Window { + int clicked; - /* Draw [<][>] boxes for settings of an integer-type */ - DrawArrowButtons(x + 20, y, 3, clk - (i * 2), true, true); - - switch (ce->str) { - /* Display date for change date cheat */ - case STR_CHEAT_CHANGE_DATE: SetDParam(0, _date); break; - - /* Draw colored flag for change player cheat */ - case STR_CHEAT_CHANGE_PLAYER: - SetDParam(0, val); - GetString(buf, STR_CHEAT_CHANGE_PLAYER, lastof(buf)); - DrawPlayerIcon(_current_player, 60 + GetStringBoundingBox(buf).width, y + 2); - break; + CheatWindow(const WindowDesc *desc) : Window(desc) + { + } - /* Set correct string for switch climate cheat */ - case STR_CHEAT_SWITCH_CLIMATE: val += STR_TEMPERATE_LANDSCAPE; + virtual void OnPaint() + { + this->DrawWidgets(); + DrawStringMultiCenter(200, 25, STR_CHEATS_WARNING, width - 50); - /* Fallthrough */ - default: SetDParam(0, val); - } - } break; - } + for (int i = 0, x = 0, y = 45; i != lengthof(_cheats_ui); i++) { + const CheatEntry *ce = &_cheats_ui[i]; - DrawString(50, y + 1, ce->str, TC_FROMSTRING); + DrawSprite((*ce->been_used) ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, PAL_NONE, x + 5, y + 2); - y += 12; + switch (ce->type) { + case SLE_BOOL: { + bool on = (*(bool*)ce->variable); + + DrawFrameRect(x + 20, y + 1, x + 30 + 9, y + 9, on ? 6 : 4, on ? FR_LOWERED : FR_NONE); + SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); + } break; + + default: { + int32 val = (int32)ReadValue(ce->variable, ce->type); + char buf[512]; + + /* Draw [<][>] boxes for settings of an integer-type */ + DrawArrowButtons(x + 20, y, 3, clicked - (i * 2), true, true); + + switch (ce->str) { + /* Display date for change date cheat */ + case STR_CHEAT_CHANGE_DATE: SetDParam(0, _date); break; + + /* Draw colored flag for change player cheat */ + case STR_CHEAT_CHANGE_PLAYER: + SetDParam(0, val); + GetString(buf, STR_CHEAT_CHANGE_PLAYER, lastof(buf)); + DrawPlayerIcon(_current_player, 60 + GetStringBoundingBox(buf).width, y + 2); + break; + + /* Set correct string for switch climate cheat */ + case STR_CHEAT_SWITCH_CLIMATE: val += STR_TEMPERATE_LANDSCAPE; + + /* Fallthrough */ + default: SetDParam(0, val); + } + } break; } - break; + + DrawString(50, y + 1, ce->str, TC_FROMSTRING); + + y += 12; + } + } + + virtual void OnClick(Point pt, int widget) + { + uint btn = (pt.y - 46) / 12; + uint x = pt.x; + + /* Not clicking a button? */ + if (!IsInsideMM(x, 20, 40) || btn >= lengthof(_cheats_ui)) return; + + const CheatEntry *ce = &_cheats_ui[btn]; + int value = (int32)ReadValue(ce->variable, ce->type); + int oldvalue = value; + + *ce->been_used = true; + + switch (ce->type) { + case SLE_BOOL: + value ^= 1; + if (ce->proc != NULL) ce->proc(value, 0); + break; + + default: + /* Take whatever the function returns */ + value = ce->proc(value + ((x >= 30) ? 1 : -1), (x >= 30) ? 1 : -1); + + /* The first cheat (money), doesn't return a different value. */ + if (value != oldvalue || btn == 0) this->clicked = btn * 2 + 1 + ((x >= 30) ? 1 : 0); + break; } - case WE_CLICK: { - uint btn = (e->we.click.pt.y - 46) / 12; - uint x = e->we.click.pt.x; - - /* Not clicking a button? */ - if (!IsInsideMM(x, 20, 40) || btn >= lengthof(_cheats_ui)) break; - - const CheatEntry *ce = &_cheats_ui[btn]; - int value = (int32)ReadValue(ce->variable, ce->type); - int oldvalue = value; - - *ce->been_used = true; - - switch (ce->type) { - case SLE_BOOL: - value ^= 1; - if (ce->proc != NULL) ce->proc(value, 0); - break; + if (value != oldvalue) WriteValue(ce->variable, ce->type, (int64)value); - default: - /* Take whatever the function returns */ - value = ce->proc(value + ((x >= 30) ? 1 : -1), (x >= 30) ? 1 : -1); - - if (value != oldvalue) WP(w, def_d).data_1 = btn * 2 + 1 + ((x >= 30) ? 1 : 0); - break; - } - - if (value != oldvalue) WriteValue(ce->variable, ce->type, (int64)value); + flags4 |= 5 << WF_TIMEOUT_SHL; - w->flags4 |= 5 << WF_TIMEOUT_SHL; - - w->SetDirty(); - } break; + SetDirty(); + } - case WE_TIMEOUT: - WP(w, def_d).data_1 = 0; - w->SetDirty(); - break; + virtual void OnTimeout() + { + this->clicked = 0; + this->SetDirty(); } -} +}; static const WindowDesc _cheats_desc = { 240, 22, 400, 170, 400, 170, WC_CHEATS, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _cheat_widgets, - CheatsWndProc }; void ShowCheatWindow() { DeleteWindowById(WC_CHEATS, 0); - new Window(&_cheats_desc); + new CheatWindow(&_cheats_desc); } diff -r 6c4314786d68 -r 8cbdb511a674 src/console.cpp --- a/src/console.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/console.cpp Mon May 19 15:13:58 2008 +0000 @@ -77,137 +77,167 @@ static void IConsoleHistoryAdd(const char *cmd); static void IConsoleHistoryNavigate(int direction); -/* ** console window ** */ -static void IConsoleWndProc(Window *w, WindowEvent *e) +struct IConsoleWindow : Window { - static byte iconsole_scroll = ICON_BUFFER; + static byte scroll; - switch (e->event) { - case WE_PAINT: { - int i = iconsole_scroll; - int max = (w->height / ICON_LINE_HEIGHT) - 1; - int delta = 0; - GfxFillRect(w->left, w->top, w->width, w->height - 1, 0); - while ((i > 0) && (i > iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) { - DoDrawString(_iconsole_buffer[i], 5, - w->height - (iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]); - i--; - } - /* If the text is longer than the window, don't show the starting ']' */ - delta = w->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH; - if (delta > 0) { - DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _icolour_cmd); - delta = 0; - } + IConsoleWindow(const WindowDesc *desc) : Window(desc) + { + _iconsole_mode = ICONSOLE_OPENED; + SetBit(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll - DoDrawString(_iconsole_cmdline.buf, 10 + delta, w->height - ICON_LINE_HEIGHT, _icolour_cmd); + this->height = _screen.height / 3; + this->width = _screen.width; + } - if (_iconsole_cmdline.caret) - DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, w->height - ICON_LINE_HEIGHT, TC_WHITE); - break; + ~IConsoleWindow() + { + _iconsole_mode = ICONSOLE_CLOSED; + ClrBit(_no_scroll, SCROLL_CON); + } + + virtual void OnPaint() + { + int i = IConsoleWindow::scroll; + int max = (this->height / ICON_LINE_HEIGHT) - 1; + int delta = 0; + GfxFillRect(this->left, this->top, this->width, this->height - 1, 0); + while ((i > 0) && (i > IConsoleWindow::scroll - max) && (_iconsole_buffer[i] != NULL)) { + DoDrawString(_iconsole_buffer[i], 5, + this->height - (IConsoleWindow::scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]); + i--; } - case WE_MOUSELOOP: - if (HandleCaret(&_iconsole_cmdline)) w->SetDirty(); - break; - case WE_DESTROY: - _iconsole_mode = ICONSOLE_CLOSED; - break; - case WE_KEYPRESS: - e->we.keypress.cont = false; - switch (e->we.keypress.keycode) { - case WKC_UP: - IConsoleHistoryNavigate(+1); - w->SetDirty(); - break; - case WKC_DOWN: - IConsoleHistoryNavigate(-1); - w->SetDirty(); - break; - case WKC_SHIFT | WKC_PAGEUP: - if (iconsole_scroll - (w->height / ICON_LINE_HEIGHT) - 1 < 0) { - iconsole_scroll = 0; - } else { - iconsole_scroll -= (w->height / ICON_LINE_HEIGHT) - 1; - } - w->SetDirty(); - break; - case WKC_SHIFT | WKC_PAGEDOWN: - if (iconsole_scroll + (w->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) { - iconsole_scroll = ICON_BUFFER; - } else { - iconsole_scroll += (w->height / ICON_LINE_HEIGHT) - 1; - } - w->SetDirty(); - break; - case WKC_SHIFT | WKC_UP: - if (iconsole_scroll <= 0) { - iconsole_scroll = 0; - } else { - --iconsole_scroll; - } - w->SetDirty(); - break; - case WKC_SHIFT | WKC_DOWN: - if (iconsole_scroll >= ICON_BUFFER) { - iconsole_scroll = ICON_BUFFER; - } else { - ++iconsole_scroll; - } - w->SetDirty(); - break; - case WKC_BACKQUOTE: - IConsoleSwitch(); - break; - case WKC_RETURN: case WKC_NUM_ENTER: - IConsolePrintF(_icolour_cmd, "] %s", _iconsole_cmdline.buf); - IConsoleHistoryAdd(_iconsole_cmdline.buf); + /* If the text is longer than the window, don't show the starting ']' */ + delta = this->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH; + if (delta > 0) { + DoDrawString("]", 5, this->height - ICON_LINE_HEIGHT, _icolour_cmd); + delta = 0; + } - IConsoleCmdExec(_iconsole_cmdline.buf); - IConsoleClearCommand(); - break; - case WKC_CTRL | WKC_RETURN: - _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL; - IConsoleResize(w); - MarkWholeScreenDirty(); - break; - case (WKC_CTRL | 'V'): - if (InsertTextBufferClipboard(&_iconsole_cmdline)) { - IConsoleResetHistoryPos(); - w->SetDirty(); - } - break; - case (WKC_CTRL | 'L'): - IConsoleCmdExec("clear"); - break; - case (WKC_CTRL | 'U'): - DeleteTextBufferAll(&_iconsole_cmdline); - w->SetDirty(); - break; - case WKC_BACKSPACE: case WKC_DELETE: - if (DeleteTextBufferChar(&_iconsole_cmdline, e->we.keypress.keycode)) { - IConsoleResetHistoryPos(); - w->SetDirty(); - } - break; - case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: - if (MoveTextBufferPos(&_iconsole_cmdline, e->we.keypress.keycode)) { - IConsoleResetHistoryPos(); - w->SetDirty(); - } - break; - default: - if (IsValidChar(e->we.keypress.key, CS_ALPHANUMERAL)) { - iconsole_scroll = ICON_BUFFER; - InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.key); - IConsoleResetHistoryPos(); - w->SetDirty(); - } else { - e->we.keypress.cont = true; - } - break; + DoDrawString(_iconsole_cmdline.buf, 10 + delta, this->height - ICON_LINE_HEIGHT, _icolour_cmd); + + if (_iconsole_cmdline.caret) { + DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, this->height - ICON_LINE_HEIGHT, TC_WHITE); } } -} + + virtual void OnMouseLoop() + { + if (HandleCaret(&_iconsole_cmdline)) this->SetDirty(); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + switch (keycode) { + case WKC_UP: + IConsoleHistoryNavigate(+1); + this->SetDirty(); + break; + + case WKC_DOWN: + IConsoleHistoryNavigate(-1); + this->SetDirty(); + break; + + case WKC_SHIFT | WKC_PAGEUP: + if (IConsoleWindow::scroll - (this->height / ICON_LINE_HEIGHT) - 1 < 0) { + IConsoleWindow::scroll = 0; + } else { + IConsoleWindow::scroll -= (this->height / ICON_LINE_HEIGHT) - 1; + } + this->SetDirty(); + break; + + case WKC_SHIFT | WKC_PAGEDOWN: + if (IConsoleWindow::scroll + (this->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) { + IConsoleWindow::scroll = ICON_BUFFER; + } else { + IConsoleWindow::scroll += (this->height / ICON_LINE_HEIGHT) - 1; + } + this->SetDirty(); + break; + + case WKC_SHIFT | WKC_UP: + if (IConsoleWindow::scroll <= 0) { + IConsoleWindow::scroll = 0; + } else { + --IConsoleWindow::scroll; + } + this->SetDirty(); + break; + + case WKC_SHIFT | WKC_DOWN: + if (IConsoleWindow::scroll >= ICON_BUFFER) { + IConsoleWindow::scroll = ICON_BUFFER; + } else { + ++IConsoleWindow::scroll; + } + this->SetDirty(); + break; + + case WKC_BACKQUOTE: + IConsoleSwitch(); + break; + + case WKC_RETURN: case WKC_NUM_ENTER: + IConsolePrintF(_icolour_cmd, "] %s", _iconsole_cmdline.buf); + IConsoleHistoryAdd(_iconsole_cmdline.buf); + + IConsoleCmdExec(_iconsole_cmdline.buf); + IConsoleClearCommand(); + break; + + case WKC_CTRL | WKC_RETURN: + _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL; + IConsoleResize(this); + MarkWholeScreenDirty(); + break; + + case (WKC_CTRL | 'V'): + if (InsertTextBufferClipboard(&_iconsole_cmdline)) { + IConsoleResetHistoryPos(); + this->SetDirty(); + } + break; + + case (WKC_CTRL | 'L'): + IConsoleCmdExec("clear"); + break; + + case (WKC_CTRL | 'U'): + DeleteTextBufferAll(&_iconsole_cmdline); + this->SetDirty(); + break; + + case WKC_BACKSPACE: case WKC_DELETE: + if (DeleteTextBufferChar(&_iconsole_cmdline, keycode)) { + IConsoleResetHistoryPos(); + this->SetDirty(); + } + break; + + case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: + if (MoveTextBufferPos(&_iconsole_cmdline, keycode)) { + IConsoleResetHistoryPos(); + this->SetDirty(); + } + break; + + default: + if (IsValidChar(key, CS_ALPHANUMERAL)) { + IConsoleWindow::scroll = ICON_BUFFER; + InsertTextBufferChar(&_iconsole_cmdline, key); + IConsoleResetHistoryPos(); + this->SetDirty(); + } else { + return ES_NOT_HANDLED; + } + } + return ES_HANDLED; + } +}; + +byte IConsoleWindow::scroll = ICON_BUFFER; static const Widget _iconsole_window_widgets[] = { {WIDGETS_END} @@ -218,7 +248,6 @@ WC_CONSOLE, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _iconsole_window_widgets, - IConsoleWndProc, }; void IConsoleInit() @@ -317,17 +346,12 @@ void IConsoleSwitch() { switch (_iconsole_mode) { - case ICONSOLE_CLOSED: { - Window *w = new Window(&_iconsole_window_desc); - w->height = _screen.height / 3; - w->width = _screen.width; - _iconsole_mode = ICONSOLE_OPENED; - SetBit(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll - } break; + case ICONSOLE_CLOSED: + new IConsoleWindow(&_iconsole_window_desc); + break; + case ICONSOLE_OPENED: case ICONSOLE_FULL: DeleteWindowById(WC_CONSOLE, 0); - _iconsole_mode = ICONSOLE_CLOSED; - ClrBit(_no_scroll, SCROLL_CON); break; } diff -r 6c4314786d68 -r 8cbdb511a674 src/console_cmds.cpp --- a/src/console_cmds.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/console_cmds.cpp Mon May 19 15:13:58 2008 +0000 @@ -537,7 +537,11 @@ if (argc < 3) return false; - SEND_COMMAND(PACKET_CLIENT_RCON)(argv[1], argv[2]); + if (_network_server) { + IConsoleCmdExec(argv[2]); + } else { + SEND_COMMAND(PACKET_CLIENT_RCON)(argv[1], argv[2]); + } return true; } diff -r 6c4314786d68 -r 8cbdb511a674 src/currency.cpp --- a/src/currency.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/currency.cpp Mon May 19 15:13:58 2008 +0000 @@ -154,7 +154,7 @@ _currency_specs[_opt.currency].to_euro != CF_ISEURO && _cur_year >= _currency_specs[_opt.currency].to_euro) { _opt.currency = 2; // this is the index of euro above. - AddNewsItem(STR_EURO_INTRODUCE, NM_NORMAL, NF_NONE, NT_ECONOMY, DNC_NONE, 0, 0); + AddNewsItem(STR_EURO_INTRODUCE, NS_ECONOMY, 0, 0); } } diff -r 6c4314786d68 -r 8cbdb511a674 src/depot_gui.cpp --- a/src/depot_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/depot_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -25,6 +25,7 @@ #include "order_func.h" #include "depot_base.h" #include "tilehighlight_func.h" +#include "window_gui.h" #include "table/strings.h" #include "table/sprites.h" @@ -101,27 +102,11 @@ }; -struct depot_d { - VehicleID sel; - VehicleType type; - bool generate_list; - uint16 engine_list_length; - uint16 wagon_list_length; - uint16 engine_count; - uint16 wagon_count; - Vehicle **vehicle_list; - Vehicle **wagon_list; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(depot_d)); - -static void DepotWndProc(Window *w, WindowEvent *e); - static const WindowDesc _train_depot_desc = { WDP_AUTO, WDP_AUTO, 36, 27, 36, 27, WC_VEHICLE_DEPOT, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _depot_widgets, - DepotWndProc }; static const WindowDesc _road_depot_desc = { @@ -129,7 +114,6 @@ WC_VEHICLE_DEPOT, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _depot_widgets, - DepotWndProc }; static const WindowDesc _ship_depot_desc = { @@ -137,7 +121,6 @@ WC_VEHICLE_DEPOT, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _depot_widgets, - DepotWndProc }; static const WindowDesc _aircraft_depot_desc = { @@ -145,10 +128,10 @@ WC_VEHICLE_DEPOT, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _depot_widgets, - DepotWndProc }; extern int WagonLengthToPixels(int len); +extern void DepotSortList(Vehicle **v, uint16 length); /** * This is the Callback method after the cloning attempt of a vehicle @@ -166,247 +149,6 @@ ShowVehicleViewWindow(v); } -static void DepotSellAllConfirmationCallback(Window *w, bool confirmed) -{ - if (confirmed) { - TileIndex tile = w->window_number; - byte vehtype = WP(w, depot_d).type; - DoCommandP(tile, vehtype, 0, NULL, CMD_DEPOT_SELL_ALL_VEHICLES); - } -} - -const Sprite *GetAircraftSprite(EngineID engine); - -/** Draw a vehicle in the depot window in the box with the top left corner at x,y - * @param *w Window to draw in - * @param *v Vehicle to draw - * @param x Left side of the box to draw in - * @param y Top of the box to draw in - */ -static void DrawVehicleInDepot(Window *w, const Vehicle *v, int x, int y) -{ - byte diff_x = 0, diff_y = 0; - - int sprite_y = y + w->resize.step_height - GetVehicleListHeight(v->type); - - switch (v->type) { - case VEH_TRAIN: - DrawTrainImage(v, x + 21, sprite_y, WP(w, depot_d).sel, w->hscroll.cap + 4, w->hscroll.pos); - - /* Number of wagons relative to a standard length wagon (rounded up) */ - SetDParam(0, (v->u.rail.cached_total_length + 7) / 8); - DrawStringRightAligned(w->widget[DEPOT_WIDGET_MATRIX].right - 1, y + 4, STR_TINY_BLACK, TC_FROMSTRING); // Draw the counter - break; - - case VEH_ROAD: DrawRoadVehImage( v, x + 24, sprite_y, WP(w, depot_d).sel, 1); break; - case VEH_SHIP: DrawShipImage( v, x + 19, sprite_y - 1, WP(w, depot_d).sel); break; - case VEH_AIRCRAFT: { - const Sprite *spr = GetSprite(v->GetImage(DIR_W)); - DrawAircraftImage(v, x + 12, - y + max(spr->height + spr->y_offs - 14, 0), // tall sprites needs an y offset - WP(w, depot_d).sel); - } break; - default: NOT_REACHED(); - } - - if (w->resize.step_height == 14) { - /* VEH_TRAIN and VEH_ROAD, which are low */ - diff_x = 15; - } else { - /* VEH_SHIP and VEH_AIRCRAFT, which are tall */ - diff_y = 12; - } - - DrawSprite((v->vehstatus & VS_STOPPED) ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, x + diff_x, y + diff_y); - - SetDParam(0, v->unitnumber); - DrawString(x, y + 2, (uint16)(v->max_age - 366) >= v->age ? STR_00E2 : STR_00E3, TC_FROMSTRING); -} - -static void DrawDepotWindow(Window *w) -{ - Vehicle **vl = WP(w, depot_d).vehicle_list; - TileIndex tile = w->window_number; - int x, y, i, maxval; - uint16 hnum; - uint16 num = WP(w, depot_d).engine_count; - - /* Set the row and number of boxes in each row based on the number of boxes drawn in the matrix */ - uint16 rows_in_display = w->widget[DEPOT_WIDGET_MATRIX].data >> 8; - uint16 boxes_in_each_row = w->widget[DEPOT_WIDGET_MATRIX].data & 0xFF; - - /* setup disabled buttons */ - w->SetWidgetsDisabledState(!IsTileOwner(tile, _local_player), - DEPOT_WIDGET_STOP_ALL, - DEPOT_WIDGET_START_ALL, - DEPOT_WIDGET_SELL, - DEPOT_WIDGET_SELL_CHAIN, - DEPOT_WIDGET_SELL_ALL, - DEPOT_WIDGET_BUILD, - DEPOT_WIDGET_CLONE, - DEPOT_WIDGET_AUTOREPLACE, - WIDGET_LIST_END); - - /* determine amount of items for scroller */ - if (WP(w, depot_d).type == VEH_TRAIN) { - hnum = 8; - for (num = 0; num < WP(w, depot_d).engine_count; num++) { - const Vehicle *v = vl[num]; - hnum = max(hnum, v->u.rail.cached_total_length); - } - /* Always have 1 empty row, so people can change the setting of the train */ - SetVScrollCount(w, WP(w, depot_d).engine_count + WP(w, depot_d).wagon_count + 1); - SetHScrollCount(w, WagonLengthToPixels(hnum)); - } else { - SetVScrollCount(w, (num + w->hscroll.cap - 1) / w->hscroll.cap); - } - - /* locate the depot struct */ - if (WP(w, depot_d).type == VEH_AIRCRAFT) { - SetDParam(0, GetStationIndex(tile)); // Airport name - } else { - Depot *depot = GetDepotByTile(tile); - assert(depot != NULL); - - SetDParam(0, depot->town_index); - } - - DrawWindowWidgets(w); - - num = w->vscroll.pos * boxes_in_each_row; - maxval = min(WP(w, depot_d).engine_count, num + (rows_in_display * boxes_in_each_row)); - - for (x = 2, y = 15; num < maxval; y += w->resize.step_height, x = 2) { // Draw the rows - byte i; - - for (i = 0; i < boxes_in_each_row && num < maxval; i++, num++, x += w->resize.step_width) { - /* Draw all vehicles in the current row */ - const Vehicle *v = vl[num]; - DrawVehicleInDepot(w, v, x, y); - } - } - - maxval = min(WP(w, depot_d).engine_count + WP(w, depot_d).wagon_count, (w->vscroll.pos * boxes_in_each_row) + (rows_in_display * boxes_in_each_row)); - - /* draw the train wagons, that do not have an engine in front */ - for (; num < maxval; num++, y += 14) { - const Vehicle *v = WP(w, depot_d).wagon_list[num - WP(w, depot_d).engine_count]; - const Vehicle *u; - - DrawTrainImage(v, x + 50, y, WP(w, depot_d).sel, w->hscroll.cap - 29, 0); - DrawString(x, y + 2, STR_8816, TC_FROMSTRING); - - /*Draw the train counter */ - i = 0; - u = v; - do i++; while ((u = u->Next()) != NULL); // Determine length of train - SetDParam(0, i); // Set the counter - DrawStringRightAligned(w->widget[DEPOT_WIDGET_MATRIX].right - 1, y + 4, STR_TINY_BLACK, TC_FROMSTRING); // Draw the counter - } -} - -struct GetDepotVehiclePtData { - Vehicle *head; - Vehicle *wagon; -}; - -enum DepotGUIAction { - MODE_ERROR, - MODE_DRAG_VEHICLE, - MODE_SHOW_VEHICLE, - MODE_START_STOP, -}; - -static DepotGUIAction GetVehicleFromDepotWndPt(const Window *w, int x, int y, Vehicle **veh, GetDepotVehiclePtData *d) -{ - Vehicle **vl = WP(w, depot_d).vehicle_list; - uint xt, row, xm = 0, ym = 0; - int pos, skip = 0; - uint16 boxes_in_each_row = w->widget[DEPOT_WIDGET_MATRIX].data & 0xFF; - - if (WP(w, depot_d).type == VEH_TRAIN) { - xt = 0; - x -= 23; - } else { - xt = x / w->resize.step_width; - xm = x % w->resize.step_width; - if (xt >= w->hscroll.cap) return MODE_ERROR; - - ym = (y - 14) % w->resize.step_height; - } - - row = (y - 14) / w->resize.step_height; - if (row >= w->vscroll.cap) return MODE_ERROR; - - pos = ((row + w->vscroll.pos) * boxes_in_each_row) + xt; - - if (WP(w, depot_d).engine_count + WP(w, depot_d).wagon_count <= pos) { - if (WP(w, depot_d).type == VEH_TRAIN) { - d->head = NULL; - d->wagon = NULL; - return MODE_DRAG_VEHICLE; - } else { - return MODE_ERROR; // empty block, so no vehicle is selected - } - } - - if (WP(w, depot_d).engine_count > pos) { - *veh = vl[pos]; - skip = w->hscroll.pos; - } else { - vl = WP(w, depot_d).wagon_list; - pos -= WP(w, depot_d).engine_count; - *veh = vl[pos]; - /* free wagons don't have an initial loco. */ - x -= _traininfo_vehicle_width; - } - - switch (WP(w, depot_d).type) { - case VEH_TRAIN: { - Vehicle *v = *veh; - d->head = d->wagon = v; - - /* either pressed the flag or the number, but only when it's a loco */ - if (x < 0 && IsFrontEngine(v)) return (x >= -10) ? MODE_START_STOP : MODE_SHOW_VEHICLE; - - skip = (skip * 8) / _traininfo_vehicle_width; - x = (x * 8) / _traininfo_vehicle_width; - - /* Skip vehicles that are scrolled off the list */ - x += skip; - - /* find the vehicle in this row that was clicked */ - while (v != NULL && (x -= v->u.rail.cached_veh_length) >= 0) v = v->Next(); - - /* if an articulated part was selected, find its parent */ - while (v != NULL && IsArticulatedPart(v)) v = v->Previous(); - - d->wagon = v; - - return MODE_DRAG_VEHICLE; - } - break; - - case VEH_ROAD: - if (xm >= 24) return MODE_DRAG_VEHICLE; - if (xm <= 16) return MODE_SHOW_VEHICLE; - break; - - case VEH_SHIP: - if (xm >= 19) return MODE_DRAG_VEHICLE; - if (ym <= 10) return MODE_SHOW_VEHICLE; - break; - - case VEH_AIRCRAFT: - if (xm >= 12) return MODE_DRAG_VEHICLE; - if (ym <= 12) return MODE_SHOW_VEHICLE; - break; - - default: NOT_REACHED(); - } - return MODE_START_STOP; -} - static void TrainDepotMoveVehicle(Vehicle *wagon, VehicleID sel, Vehicle *head) { Vehicle *v; @@ -427,220 +169,6 @@ DoCommandP(v->tile, v->index + ((wagon == NULL ? INVALID_VEHICLE : wagon->index) << 16), _ctrl_pressed ? 1 : 0, NULL, CMD_MOVE_RAIL_VEHICLE | CMD_MSG(STR_8837_CAN_T_MOVE_VEHICLE)); } -static void DepotClick(Window *w, int x, int y) -{ - GetDepotVehiclePtData gdvp; - Vehicle *v = NULL; - DepotGUIAction mode = GetVehicleFromDepotWndPt(w, x, y, &v, &gdvp); - - /* share / copy orders */ - if (_thd.place_mode != VHM_NONE && mode != MODE_ERROR) { - _place_clicked_vehicle = (WP(w, depot_d).type == VEH_TRAIN ? gdvp.head : v); - return; - } - - if (WP(w, depot_d).type == VEH_TRAIN) v = gdvp.wagon; - - switch (mode) { - case MODE_ERROR: // invalid - return; - - case MODE_DRAG_VEHICLE: { // start dragging of vehicle - VehicleID sel = WP(w, depot_d).sel; - - if (WP(w, depot_d).type == VEH_TRAIN && sel != INVALID_VEHICLE) { - WP(w, depot_d).sel = INVALID_VEHICLE; - TrainDepotMoveVehicle(v, sel, gdvp.head); - } else if (v != NULL) { - int image = v->GetImage(DIR_W); - - WP(w, depot_d).sel = v->index; - w->SetDirty(); - SetObjectToPlaceWnd(image, GetVehiclePalette(v), VHM_DRAG, w); - _cursor.vehchain = _ctrl_pressed; - } - } - break; - - case MODE_SHOW_VEHICLE: // show info window - ShowVehicleViewWindow(v); - break; - - case MODE_START_STOP: { // click start/stop flag - uint command; - - switch (WP(w, depot_d).type) { - case VEH_TRAIN: command = CMD_START_STOP_TRAIN | CMD_MSG(STR_883B_CAN_T_STOP_START_TRAIN); break; - case VEH_ROAD: command = CMD_START_STOP_ROADVEH | CMD_MSG(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE); break; - case VEH_SHIP: command = CMD_START_STOP_SHIP | CMD_MSG(STR_9818_CAN_T_STOP_START_SHIP); break; - case VEH_AIRCRAFT: command = CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT); break; - default: NOT_REACHED(); command = 0; - } - DoCommandP(v->tile, v->index, 0, NULL, command); - } - break; - - default: NOT_REACHED(); - } -} - -/** - * Clones a vehicle - * @param *v is the original vehicle to clone - * @param *w is the window of the depot where the clone is build - */ -static void HandleCloneVehClick(const Vehicle *v, const Window *w) -{ - uint error_str; - - if (v == NULL) return; - - if (!v->IsPrimaryVehicle()) { - v = v->First(); - /* Do nothing when clicking on a train in depot with no loc attached */ - if (v->type == VEH_TRAIN && !IsFrontEngine(v)) return; - } - - switch (v->type) { - case VEH_TRAIN: error_str = CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE); break; - case VEH_ROAD: error_str = CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE); break; - case VEH_SHIP: error_str = CMD_MSG(STR_980D_CAN_T_BUILD_SHIP); break; - case VEH_AIRCRAFT: error_str = CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT); break; - default: return; - } - - DoCommandP(w->window_number, v->index, _ctrl_pressed ? 1 : 0, CcCloneVehicle, CMD_CLONE_VEHICLE | error_str); - - ResetObjectToPlace(); -} - -static void ClonePlaceObj(const Window *w) -{ - const Vehicle *v = CheckMouseOverVehicle(); - - if (v != NULL) HandleCloneVehClick(v, w); -} - -static void ResizeDepotButtons(Window *w) -{ - ResizeButtons(w, DEPOT_WIDGET_BUILD, DEPOT_WIDGET_LOCATION); - - if (WP(w, depot_d).type == VEH_TRAIN) { - /* Divide the size of DEPOT_WIDGET_SELL into two equally big buttons so DEPOT_WIDGET_SELL and DEPOT_WIDGET_SELL_CHAIN will get the same size. - * This way it will stay the same even if DEPOT_WIDGET_SELL_CHAIN is resized for some reason */ - w->widget[DEPOT_WIDGET_SELL_CHAIN].top = ((w->widget[DEPOT_WIDGET_SELL_CHAIN].bottom - w->widget[DEPOT_WIDGET_SELL].top) / 2) + w->widget[DEPOT_WIDGET_SELL].top; - w->widget[DEPOT_WIDGET_SELL].bottom = w->widget[DEPOT_WIDGET_SELL_CHAIN].top - 1; - } -} - -/* Function to set up vehicle specific sprites and strings - * Only use this if it's the same widget, that's used for more than one vehicle type and it needs different text/sprites - * Vehicle specific text/sprites, that's in a widget, that's only shown for one vehicle type (like sell whole train) is set in the widget array - */ -static void SetupStringsForDepotWindow(Window *w, VehicleType type) -{ - switch (type) { - default: NOT_REACHED(); - - case VEH_TRAIN: - w->widget[DEPOT_WIDGET_CAPTION].data = STR_8800_TRAIN_DEPOT; - w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_TRAIN_TIP; - w->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_DEPOT_TRAIN_TIP; - w->widget[DEPOT_WIDGET_SELL].tooltips = STR_8841_DRAG_TRAIN_VEHICLE_TO_HERE; - w->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_TRAIN_TIP; - w->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_883F_TRAINS_CLICK_ON_TRAIN_FOR; - - w->widget[DEPOT_WIDGET_BUILD].data = STR_8815_NEW_VEHICLES; - w->widget[DEPOT_WIDGET_BUILD].tooltips = STR_8840_BUILD_NEW_TRAIN_VEHICLE; - w->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_TRAIN; - w->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_TRAIN_DEPOT_INFO; - - w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_8842_CENTER_MAIN_VIEW_ON_TRAIN; - w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_TRAIN; - w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_TRAIN_TIP; - w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_TRAIN_TIP; - - /* Sprites */ - w->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_TRAIN; - w->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_TRAIN; - w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_TRAIN; - break; - - case VEH_ROAD: - w->widget[DEPOT_WIDGET_CAPTION].data = STR_9003_ROAD_VEHICLE_DEPOT; - w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_ROADVEH_TIP; - w->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_DEPOT_ROADVEH_TIP; - w->widget[DEPOT_WIDGET_SELL].tooltips = STR_9024_DRAG_ROAD_VEHICLE_TO_HERE; - w->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_ROADVEH_TIP; - w->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_9022_VEHICLES_CLICK_ON_VEHICLE; - - w->widget[DEPOT_WIDGET_BUILD].data = STR_9004_NEW_VEHICLES; - w->widget[DEPOT_WIDGET_BUILD].tooltips = STR_9023_BUILD_NEW_ROAD_VEHICLE; - w->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_ROAD_VEHICLE; - w->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_ROAD_VEHICLE_DEPOT_INFO; - - w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_9025_CENTER_MAIN_VIEW_ON_ROAD; - w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_LORRY; - w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_ROADVEH_TIP; - w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_ROADVEH_TIP; - - /* Sprites */ - w->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_ROADVEH; - w->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_ROADVEH; - w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_ROADVEH; - break; - - case VEH_SHIP: - w->widget[DEPOT_WIDGET_CAPTION].data = STR_9803_SHIP_DEPOT; - w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_SHIP_TIP; - w->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_DEPOT_SHIP_TIP; - w->widget[DEPOT_WIDGET_SELL].tooltips = STR_9821_DRAG_SHIP_TO_HERE_TO_SELL; - w->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_SHIP_TIP; - w->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_981F_SHIPS_CLICK_ON_SHIP_FOR; - - w->widget[DEPOT_WIDGET_BUILD].data = STR_9804_NEW_SHIPS; - w->widget[DEPOT_WIDGET_BUILD].tooltips = STR_9820_BUILD_NEW_SHIP; - w->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_SHIP; - w->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_SHIP_DEPOT_INFO; - - w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_9822_CENTER_MAIN_VIEW_ON_SHIP; - w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_SHIP; - w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_SHIP_TIP; - w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_SHIP_TIP; - - /* Sprites */ - w->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_SHIP; - w->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_SHIP; - w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_SHIP; - break; - - case VEH_AIRCRAFT: - w->widget[DEPOT_WIDGET_CAPTION].data = STR_A002_AIRCRAFT_HANGAR; - w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_HANGAR_TIP; - w->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_HANGAR_TIP; - w->widget[DEPOT_WIDGET_SELL].tooltips = STR_A023_DRAG_AIRCRAFT_TO_HERE_TO; - w->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_AIRCRAFT_TIP; - w->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_A021_AIRCRAFT_CLICK_ON_AIRCRAFT; - - w->widget[DEPOT_WIDGET_BUILD].data = STR_A003_NEW_AIRCRAFT; - w->widget[DEPOT_WIDGET_BUILD].tooltips = STR_A022_BUILD_NEW_AIRCRAFT; - w->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_AIRCRAFT; - w->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_AIRCRAFT_INFO_HANGAR_WINDOW; - - w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_A024_CENTER_MAIN_VIEW_ON_HANGAR; - w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_PLANE; - w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_AIRCRAFT_TIP; - w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_AIRCRAFT_TIP; - - /* Sprites */ - w->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_AIRCRAFT; - w->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_AIRCRAFT; - w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_AIRCRAFT; - break; - } -} - - /* Array to hold the block sizes * First part is the vehicle type, while the last is 0 = x, 1 = y */ uint _block_sizes[4][2]; @@ -707,296 +235,746 @@ ResizeDefaultWindowSize(VEH_AIRCRAFT); } -static void CreateDepotListWindow(Window *w, VehicleType type) -{ - WP(w, depot_d).type = type; - _backup_orders_tile = 0; - - assert(IsPlayerBuildableVehicleType(type)); // ensure that we make the call with a valid type - - /* Resize the window according to the vehicle type */ - - /* Set the number of blocks in each direction */ - w->vscroll.cap = _resize_cap[type][0]; - w->hscroll.cap = _resize_cap[type][1]; +static void DepotSellAllConfirmationCallback(Window *w, bool confirmed); +const Sprite *GetAircraftSprite(EngineID engine); - /* Set the block size */ - w->resize.step_width = _block_sizes[type][0]; - w->resize.step_height = _block_sizes[type][1]; +struct DepotWindow : Window { + VehicleID sel; + VehicleType type; + bool generate_list; + uint16 engine_list_length; + uint16 wagon_list_length; + uint16 engine_count; + uint16 wagon_count; + Vehicle **vehicle_list; + Vehicle **wagon_list; - /* Enlarge the window to fit with the selected number of blocks of the selected size */ - ResizeWindow(w, - _block_sizes[type][0] * w->hscroll.cap, - _block_sizes[type][1] * w->vscroll.cap); + DepotWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->sel = INVALID_VEHICLE; + this->vehicle_list = NULL; + this->wagon_list = NULL; + this->engine_count = 0; + this->wagon_count = 0; + this->generate_list = true; - if (type == VEH_TRAIN) { - /* Make space for the horizontal scrollbar vertically, and the unit - * number, flag, and length counter horizontally. */ - ResizeWindow(w, 36, 12); - /* substract the newly added space from the matrix since it was meant for the scrollbar */ - w->widget[DEPOT_WIDGET_MATRIX].bottom -= 12; + this->FindWindowPlacementAndResize(desc); } - /* Set the minimum window size to the current window size */ - w->resize.width = w->width; - w->resize.height = w->height; + ~DepotWindow() + { + DeleteWindowById(WC_BUILD_VEHICLE, this->window_number); + free((void*)this->vehicle_list); + free((void*)this->wagon_list); + } - SetupStringsForDepotWindow(w, type); + /** Draw a vehicle in the depot window in the box with the top left corner at x,y + * @param *w Window to draw in + * @param *v Vehicle to draw + * @param x Left side of the box to draw in + * @param y Top of the box to draw in + */ + void DrawVehicleInDepot(Window *w, const Vehicle *v, int x, int y) + { + byte diff_x = 0, diff_y = 0; - w->widget[DEPOT_WIDGET_MATRIX].data = - (w->vscroll.cap * 0x100) // number of rows to draw on the background - + (type == VEH_TRAIN ? 1 : w->hscroll.cap); // number of boxes in each row. Trains always have just one + int sprite_y = y + this->resize.step_height - GetVehicleListHeight(v->type); + + switch (v->type) { + case VEH_TRAIN: + DrawTrainImage(v, x + 21, sprite_y, this->sel, this->hscroll.cap + 4, this->hscroll.pos); + + /* Number of wagons relative to a standard length wagon (rounded up) */ + SetDParam(0, (v->u.rail.cached_total_length + 7) / 8); + DrawStringRightAligned(this->widget[DEPOT_WIDGET_MATRIX].right - 1, y + 4, STR_TINY_BLACK, TC_FROMSTRING); // Draw the counter + break; + + case VEH_ROAD: DrawRoadVehImage( v, x + 24, sprite_y, this->sel, 1); break; + case VEH_SHIP: DrawShipImage( v, x + 19, sprite_y - 1, this->sel); break; + case VEH_AIRCRAFT: { + const Sprite *spr = GetSprite(v->GetImage(DIR_W)); + DrawAircraftImage(v, x + 12, + y + max(spr->height + spr->y_offs - 14, 0), // tall sprites needs an y offset + this->sel); + } break; + default: NOT_REACHED(); + } + + if (this->resize.step_height == 14) { + /* VEH_TRAIN and VEH_ROAD, which are low */ + diff_x = 15; + } else { + /* VEH_SHIP and VEH_AIRCRAFT, which are tall */ + diff_y = 12; + } + + DrawSprite((v->vehstatus & VS_STOPPED) ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, x + diff_x, y + diff_y); + + SetDParam(0, v->unitnumber); + DrawString(x, y + 2, (uint16)(v->max_age - 366) >= v->age ? STR_00E2 : STR_00E3, TC_FROMSTRING); + } + + void DrawDepotWindow(Window *w) + { + Vehicle **vl = this->vehicle_list; + TileIndex tile = this->window_number; + int x, y, i, maxval; + uint16 hnum; + uint16 num = this->engine_count; + + /* Set the row and number of boxes in each row based on the number of boxes drawn in the matrix */ + uint16 rows_in_display = this->widget[DEPOT_WIDGET_MATRIX].data >> 8; + uint16 boxes_in_each_row = this->widget[DEPOT_WIDGET_MATRIX].data & 0xFF; + + /* setup disabled buttons */ + this->SetWidgetsDisabledState(!IsTileOwner(tile, _local_player), + DEPOT_WIDGET_STOP_ALL, + DEPOT_WIDGET_START_ALL, + DEPOT_WIDGET_SELL, + DEPOT_WIDGET_SELL_CHAIN, + DEPOT_WIDGET_SELL_ALL, + DEPOT_WIDGET_BUILD, + DEPOT_WIDGET_CLONE, + DEPOT_WIDGET_AUTOREPLACE, + WIDGET_LIST_END); + + /* determine amount of items for scroller */ + if (this->type == VEH_TRAIN) { + hnum = 8; + for (num = 0; num < this->engine_count; num++) { + const Vehicle *v = vl[num]; + hnum = max(hnum, v->u.rail.cached_total_length); + } + /* Always have 1 empty row, so people can change the setting of the train */ + SetVScrollCount(w, this->engine_count + this->wagon_count + 1); + SetHScrollCount(w, WagonLengthToPixels(hnum)); + } else { + SetVScrollCount(w, (num + this->hscroll.cap - 1) / this->hscroll.cap); + } + + /* locate the depot struct */ + if (this->type == VEH_AIRCRAFT) { + SetDParam(0, GetStationIndex(tile)); // Airport name + } else { + Depot *depot = GetDepotByTile(tile); + assert(depot != NULL); + + SetDParam(0, depot->town_index); + } + + w->DrawWidgets(); + + num = this->vscroll.pos * boxes_in_each_row; + maxval = min(this->engine_count, num + (rows_in_display * boxes_in_each_row)); + + for (x = 2, y = 15; num < maxval; y += this->resize.step_height, x = 2) { // Draw the rows + byte i; + + for (i = 0; i < boxes_in_each_row && num < maxval; i++, num++, x += this->resize.step_width) { + /* Draw all vehicles in the current row */ + const Vehicle *v = vl[num]; + DrawVehicleInDepot(w, v, x, y); + } + } + + maxval = min(this->engine_count + this->wagon_count, (this->vscroll.pos * boxes_in_each_row) + (rows_in_display * boxes_in_each_row)); + + /* draw the train wagons, that do not have an engine in front */ + for (; num < maxval; num++, y += 14) { + const Vehicle *v = this->wagon_list[num - this->engine_count]; + const Vehicle *u; + + DrawTrainImage(v, x + 50, y, this->sel, this->hscroll.cap - 29, 0); + DrawString(x, y + 2, STR_8816, TC_FROMSTRING); + + /*Draw the train counter */ + i = 0; + u = v; + do i++; while ((u = u->Next()) != NULL); // Determine length of train + SetDParam(0, i); // Set the counter + DrawStringRightAligned(this->widget[DEPOT_WIDGET_MATRIX].right - 1, y + 4, STR_TINY_BLACK, TC_FROMSTRING); // Draw the counter + } + } + + struct GetDepotVehiclePtData { + Vehicle *head; + Vehicle *wagon; + }; + + enum DepotGUIAction { + MODE_ERROR, + MODE_DRAG_VEHICLE, + MODE_SHOW_VEHICLE, + MODE_START_STOP, + }; + + DepotGUIAction GetVehicleFromDepotWndPt(int x, int y, Vehicle **veh, GetDepotVehiclePtData *d) const + { + Vehicle **vl = this->vehicle_list; + uint xt, row, xm = 0, ym = 0; + int pos, skip = 0; + uint16 boxes_in_each_row = this->widget[DEPOT_WIDGET_MATRIX].data & 0xFF; + + if (this->type == VEH_TRAIN) { + xt = 0; + x -= 23; + } else { + xt = x / this->resize.step_width; + xm = x % this->resize.step_width; + if (xt >= this->hscroll.cap) return MODE_ERROR; + + ym = (y - 14) % this->resize.step_height; + } + + row = (y - 14) / this->resize.step_height; + if (row >= this->vscroll.cap) return MODE_ERROR; + + pos = ((row + this->vscroll.pos) * boxes_in_each_row) + xt; + + if (this->engine_count + this->wagon_count <= pos) { + if (this->type == VEH_TRAIN) { + d->head = NULL; + d->wagon = NULL; + return MODE_DRAG_VEHICLE; + } else { + return MODE_ERROR; // empty block, so no vehicle is selected + } + } + + if (this->engine_count > pos) { + *veh = vl[pos]; + skip = this->hscroll.pos; + } else { + vl = this->wagon_list; + pos -= this->engine_count; + *veh = vl[pos]; + /* free wagons don't have an initial loco. */ + x -= _traininfo_vehicle_width; + } + + switch (this->type) { + case VEH_TRAIN: { + Vehicle *v = *veh; + d->head = d->wagon = v; + + /* either pressed the flag or the number, but only when it's a loco */ + if (x < 0 && IsFrontEngine(v)) return (x >= -10) ? MODE_START_STOP : MODE_SHOW_VEHICLE; + + skip = (skip * 8) / _traininfo_vehicle_width; + x = (x * 8) / _traininfo_vehicle_width; + + /* Skip vehicles that are scrolled off the list */ + x += skip; + + /* find the vehicle in this row that was clicked */ + while (v != NULL && (x -= v->u.rail.cached_veh_length) >= 0) v = v->Next(); + + /* if an articulated part was selected, find its parent */ + while (v != NULL && IsArticulatedPart(v)) v = v->Previous(); + + d->wagon = v; + + return MODE_DRAG_VEHICLE; + } + break; + + case VEH_ROAD: + if (xm >= 24) return MODE_DRAG_VEHICLE; + if (xm <= 16) return MODE_SHOW_VEHICLE; + break; + + case VEH_SHIP: + if (xm >= 19) return MODE_DRAG_VEHICLE; + if (ym <= 10) return MODE_SHOW_VEHICLE; + break; + + case VEH_AIRCRAFT: + if (xm >= 12) return MODE_DRAG_VEHICLE; + if (ym <= 12) return MODE_SHOW_VEHICLE; + break; + + default: NOT_REACHED(); + } + return MODE_START_STOP; + } + + void DepotClick(int x, int y) + { + GetDepotVehiclePtData gdvp = { NULL, NULL }; + Vehicle *v = NULL; + DepotGUIAction mode = this->GetVehicleFromDepotWndPt(x, y, &v, &gdvp); + + /* share / copy orders */ + if (_thd.place_mode != VHM_NONE && mode != MODE_ERROR) { + _place_clicked_vehicle = (this->type == VEH_TRAIN ? gdvp.head : v); + return; + } + + if (this->type == VEH_TRAIN) v = gdvp.wagon; + + switch (mode) { + case MODE_ERROR: // invalid + return; + + case MODE_DRAG_VEHICLE: { // start dragging of vehicle + VehicleID sel = this->sel; + + if (this->type == VEH_TRAIN && sel != INVALID_VEHICLE) { + this->sel = INVALID_VEHICLE; + TrainDepotMoveVehicle(v, sel, gdvp.head); + } else if (v != NULL) { + int image = v->GetImage(DIR_W); + + this->sel = v->index; + this->SetDirty(); + SetObjectToPlaceWnd(image, GetVehiclePalette(v), VHM_DRAG, this); + _cursor.vehchain = _ctrl_pressed; + } + } break; + + case MODE_SHOW_VEHICLE: // show info window + ShowVehicleViewWindow(v); + break; + + case MODE_START_STOP: { // click start/stop flag + uint command; + + switch (this->type) { + case VEH_TRAIN: command = CMD_START_STOP_TRAIN | CMD_MSG(STR_883B_CAN_T_STOP_START_TRAIN); break; + case VEH_ROAD: command = CMD_START_STOP_ROADVEH | CMD_MSG(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE); break; + case VEH_SHIP: command = CMD_START_STOP_SHIP | CMD_MSG(STR_9818_CAN_T_STOP_START_SHIP); break; + case VEH_AIRCRAFT: command = CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT); break; + default: NOT_REACHED(); command = 0; + } + DoCommandP(v->tile, v->index, 0, NULL, command); + } break; + + default: NOT_REACHED(); + } + } + + /** + * Clones a vehicle + * @param *v is the original vehicle to clone + * @param *w is the window of the depot where the clone is build + */ + void HandleCloneVehClick(const Vehicle *v, const Window *w) + { + uint error_str; + + if (v == NULL) return; + + if (!v->IsPrimaryVehicle()) { + v = v->First(); + /* Do nothing when clicking on a train in depot with no loc attached */ + if (v->type == VEH_TRAIN && !IsFrontEngine(v)) return; + } + + switch (v->type) { + case VEH_TRAIN: error_str = CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE); break; + case VEH_ROAD: error_str = CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE); break; + case VEH_SHIP: error_str = CMD_MSG(STR_980D_CAN_T_BUILD_SHIP); break; + case VEH_AIRCRAFT: error_str = CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT); break; + default: return; + } + + DoCommandP(this->window_number, v->index, _ctrl_pressed ? 1 : 0, CcCloneVehicle, CMD_CLONE_VEHICLE | error_str); + + ResetObjectToPlace(); + } + + void ResizeDepotButtons(Window *w) + { + ResizeButtons(w, DEPOT_WIDGET_BUILD, DEPOT_WIDGET_LOCATION); + + if (this->type == VEH_TRAIN) { + /* Divide the size of DEPOT_WIDGET_SELL into two equally big buttons so DEPOT_WIDGET_SELL and DEPOT_WIDGET_SELL_CHAIN will get the same size. + * This way it will stay the same even if DEPOT_WIDGET_SELL_CHAIN is resized for some reason */ + this->widget[DEPOT_WIDGET_SELL_CHAIN].top = ((this->widget[DEPOT_WIDGET_SELL_CHAIN].bottom - this->widget[DEPOT_WIDGET_SELL].top) / 2) + this->widget[DEPOT_WIDGET_SELL].top; + this->widget[DEPOT_WIDGET_SELL].bottom = this->widget[DEPOT_WIDGET_SELL_CHAIN].top - 1; + } + } + + /* Function to set up vehicle specific sprites and strings + * Only use this if it's the same widget, that's used for more than one vehicle type and it needs different text/sprites + * Vehicle specific text/sprites, that's in a widget, that's only shown for one vehicle type (like sell whole train) is set in the widget array + */ + void SetupStringsForDepotWindow(VehicleType type) + { + switch (type) { + default: NOT_REACHED(); + + case VEH_TRAIN: + this->widget[DEPOT_WIDGET_CAPTION].data = STR_8800_TRAIN_DEPOT; + this->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_TRAIN_TIP; + this->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_DEPOT_TRAIN_TIP; + this->widget[DEPOT_WIDGET_SELL].tooltips = STR_8841_DRAG_TRAIN_VEHICLE_TO_HERE; + this->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_TRAIN_TIP; + this->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_883F_TRAINS_CLICK_ON_TRAIN_FOR; + + this->widget[DEPOT_WIDGET_BUILD].data = STR_8815_NEW_VEHICLES; + this->widget[DEPOT_WIDGET_BUILD].tooltips = STR_8840_BUILD_NEW_TRAIN_VEHICLE; + this->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_TRAIN; + this->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_TRAIN_DEPOT_INFO; + + this->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_8842_CENTER_MAIN_VIEW_ON_TRAIN; + this->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_TRAIN; + this->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_TRAIN_TIP; + this->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_TRAIN_TIP; + + /* Sprites */ + this->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_TRAIN; + this->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_TRAIN; + this->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_TRAIN; + break; + + case VEH_ROAD: + this->widget[DEPOT_WIDGET_CAPTION].data = STR_9003_ROAD_VEHICLE_DEPOT; + this->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_ROADVEH_TIP; + this->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_DEPOT_ROADVEH_TIP; + this->widget[DEPOT_WIDGET_SELL].tooltips = STR_9024_DRAG_ROAD_VEHICLE_TO_HERE; + this->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_ROADVEH_TIP; + this->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_9022_VEHICLES_CLICK_ON_VEHICLE; + + this->widget[DEPOT_WIDGET_BUILD].data = STR_9004_NEW_VEHICLES; + this->widget[DEPOT_WIDGET_BUILD].tooltips = STR_9023_BUILD_NEW_ROAD_VEHICLE; + this->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_ROAD_VEHICLE; + this->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_ROAD_VEHICLE_DEPOT_INFO; + + this->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_9025_CENTER_MAIN_VIEW_ON_ROAD; + this->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_LORRY; + this->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_ROADVEH_TIP; + this->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_ROADVEH_TIP; + + /* Sprites */ + this->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_ROADVEH; + this->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_ROADVEH; + this->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_ROADVEH; + break; + + case VEH_SHIP: + this->widget[DEPOT_WIDGET_CAPTION].data = STR_9803_SHIP_DEPOT; + this->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_SHIP_TIP; + this->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_DEPOT_SHIP_TIP; + this->widget[DEPOT_WIDGET_SELL].tooltips = STR_9821_DRAG_SHIP_TO_HERE_TO_SELL; + this->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_SHIP_TIP; + this->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_981F_SHIPS_CLICK_ON_SHIP_FOR; + + this->widget[DEPOT_WIDGET_BUILD].data = STR_9804_NEW_SHIPS; + this->widget[DEPOT_WIDGET_BUILD].tooltips = STR_9820_BUILD_NEW_SHIP; + this->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_SHIP; + this->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_SHIP_DEPOT_INFO; + + this->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_9822_CENTER_MAIN_VIEW_ON_SHIP; + this->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_SHIP; + this->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_SHIP_TIP; + this->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_SHIP_TIP; + + /* Sprites */ + this->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_SHIP; + this->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_SHIP; + this->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_SHIP; + break; + + case VEH_AIRCRAFT: + this->widget[DEPOT_WIDGET_CAPTION].data = STR_A002_AIRCRAFT_HANGAR; + this->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_HANGAR_TIP; + this->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_HANGAR_TIP; + this->widget[DEPOT_WIDGET_SELL].tooltips = STR_A023_DRAG_AIRCRAFT_TO_HERE_TO; + this->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_AIRCRAFT_TIP; + this->widget[DEPOT_WIDGET_MATRIX].tooltips = STR_A021_AIRCRAFT_CLICK_ON_AIRCRAFT; + + this->widget[DEPOT_WIDGET_BUILD].data = STR_A003_NEW_AIRCRAFT; + this->widget[DEPOT_WIDGET_BUILD].tooltips = STR_A022_BUILD_NEW_AIRCRAFT; + this->widget[DEPOT_WIDGET_CLONE].data = STR_CLONE_AIRCRAFT; + this->widget[DEPOT_WIDGET_CLONE].tooltips = STR_CLONE_AIRCRAFT_INFO_HANGAR_WINDOW; + + this->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_A024_CENTER_MAIN_VIEW_ON_HANGAR; + this->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_PLANE; + this->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_AIRCRAFT_TIP; + this->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_AIRCRAFT_TIP; + + /* Sprites */ + this->widget[DEPOT_WIDGET_SELL].data = SPR_SELL_AIRCRAFT; + this->widget[DEPOT_WIDGET_SELL_ALL].data = SPR_SELL_ALL_AIRCRAFT; + this->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_AIRCRAFT; + break; + } + } + + void CreateDepotListWindow(VehicleType type) + { + this->type = type; + _backup_orders_tile = 0; + + assert(IsPlayerBuildableVehicleType(type)); // ensure that we make the call with a valid type + + /* Resize the window according to the vehicle type */ + + /* Set the number of blocks in each direction */ + this->vscroll.cap = _resize_cap[type][0]; + this->hscroll.cap = _resize_cap[type][1]; + + /* Set the block size */ + this->resize.step_width = _block_sizes[type][0]; + this->resize.step_height = _block_sizes[type][1]; + + /* Enlarge the window to fit with the selected number of blocks of the selected size */ + ResizeWindow(this, + _block_sizes[type][0] * this->hscroll.cap, + _block_sizes[type][1] * this->vscroll.cap); + + if (type == VEH_TRAIN) { + /* Make space for the horizontal scrollbar vertically, and the unit + * number, flag, and length counter horizontally. */ + ResizeWindow(this, 36, 12); + /* substract the newly added space from the matrix since it was meant for the scrollbar */ + this->widget[DEPOT_WIDGET_MATRIX].bottom -= 12; + } + + /* Set the minimum window size to the current window size */ + this->resize.width = this->width; + this->resize.height = this->height; + + this->SetupStringsForDepotWindow(type); + + this->widget[DEPOT_WIDGET_MATRIX].data = + (this->vscroll.cap * 0x100) // number of rows to draw on the background + + (type == VEH_TRAIN ? 1 : this->hscroll.cap); // number of boxes in each row. Trains always have just one - w->SetWidgetsHiddenState(type != VEH_TRAIN, - DEPOT_WIDGET_H_SCROLL, - DEPOT_WIDGET_SELL_CHAIN, - WIDGET_LIST_END); - - ResizeDepotButtons(w); -} - -void DepotSortList(Vehicle **v, uint16 length); - -static void DepotWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - WP(w, depot_d).sel = INVALID_VEHICLE; - WP(w, depot_d).vehicle_list = NULL; - WP(w, depot_d).wagon_list = NULL; - WP(w, depot_d).engine_count = 0; - WP(w, depot_d).wagon_count = 0; - WP(w, depot_d).generate_list = true; - break; - - case WE_INVALIDATE_DATA: - WP(w, depot_d).generate_list = true; - break; + this->SetWidgetsHiddenState(type != VEH_TRAIN, + DEPOT_WIDGET_H_SCROLL, + DEPOT_WIDGET_SELL_CHAIN, + WIDGET_LIST_END); - case WE_PAINT: - if (WP(w, depot_d).generate_list) { - /* Generate the vehicle list - * It's ok to use the wagon pointers for non-trains as they will be ignored */ - BuildDepotVehicleList(WP(w, depot_d).type, w->window_number, - &WP(w, depot_d).vehicle_list, &WP(w, depot_d).engine_list_length, &WP(w, depot_d).engine_count, - &WP(w, depot_d).wagon_list, &WP(w, depot_d).wagon_list_length, &WP(w, depot_d).wagon_count); - WP(w, depot_d).generate_list = false; - DepotSortList(WP(w, depot_d).vehicle_list, WP(w, depot_d).engine_count); -//#ifndef NDEBUG -#if 0 -/* We disabled this check for now, but will keep it to quickly make this test again later (if we change some code) */ - } else { - /* Here we got a piece of code, that only checks if we got a different number of vehicles in the depot list and the number of vehicles actually being in the depot. - * IF they aren't the same, then WE_INVALIDATE_DATA should have been called somewhere, but it wasn't and we got a bug - * Since this is a time consuming check and not nice to memory fragmentation, it may not stay for long, but it's a good way to check this - * We can turn it on/off by switching between #ifndef NDEBUG and #if 0 */ - Vehicle **engines = NULL, **wagons = NULL; - uint16 engine_count = 0, engine_length = 0; - uint16 wagon_count = 0, wagon_length = 0; - BuildDepotVehicleList(WP(w, depot_d).type, w->window_number, &engines, &engine_length, &engine_count, - &wagons, &wagon_length, &wagon_count); - - assert(engine_count == WP(w, depot_d).engine_count); - assert(wagon_count == WP(w, depot_d).wagon_count); - free((void*)engines); - free((void*)wagons); -#endif - } - DrawDepotWindow(w); - break; + ResizeDepotButtons(this); + } - case WE_CLICK: - switch (e->we.click.widget) { - case DEPOT_WIDGET_MATRIX: // List - DepotClick(w, e->we.click.pt.x, e->we.click.pt.y); - break; - - case DEPOT_WIDGET_BUILD: // Build vehicle - ResetObjectToPlace(); - ShowBuildVehicleWindow(w->window_number, WP(w, depot_d).type); - break; - - case DEPOT_WIDGET_CLONE: // Clone button - w->InvalidateWidget(DEPOT_WIDGET_CLONE); - w->ToggleWidgetLoweredState(DEPOT_WIDGET_CLONE); + virtual void OnInvalidateData(int data) + { + this->generate_list = true; + } - if (w->IsWidgetLowered(DEPOT_WIDGET_CLONE)) { - static const CursorID clone_icons[] = { - SPR_CURSOR_CLONE_TRAIN, SPR_CURSOR_CLONE_ROADVEH, - SPR_CURSOR_CLONE_SHIP, SPR_CURSOR_CLONE_AIRPLANE - }; + virtual void OnPaint() + { + if (this->generate_list) { + /* Generate the vehicle list + * It's ok to use the wagon pointers for non-trains as they will be ignored */ + BuildDepotVehicleList(this->type, this->window_number, + &this->vehicle_list, &this->engine_list_length, &this->engine_count, + &this->wagon_list, &this->wagon_list_length, &this->wagon_count); + this->generate_list = false; + DepotSortList(this->vehicle_list, this->engine_count); + } + DrawDepotWindow(this); + } - _place_clicked_vehicle = NULL; - SetObjectToPlaceWnd(clone_icons[WP(w, depot_d).type], PAL_NONE, VHM_RECT, w); - } else { - ResetObjectToPlace(); - } - break; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case DEPOT_WIDGET_MATRIX: // List + this->DepotClick(pt.x, pt.y); + break; - case DEPOT_WIDGET_LOCATION: - if (_ctrl_pressed) { - ShowExtraViewPortWindow(w->window_number); - } else { - ScrollMainWindowToTile(w->window_number); - } - break; + case DEPOT_WIDGET_BUILD: // Build vehicle + ResetObjectToPlace(); + ShowBuildVehicleWindow(this->window_number, this->type); + break; - case DEPOT_WIDGET_STOP_ALL: - case DEPOT_WIDGET_START_ALL: - DoCommandP(w->window_number, 0, WP(w, depot_d).type | (e->we.click.widget == DEPOT_WIDGET_START_ALL ? (1 << 5) : 0), NULL, CMD_MASS_START_STOP); + case DEPOT_WIDGET_CLONE: // Clone button + this->InvalidateWidget(DEPOT_WIDGET_CLONE); + this->ToggleWidgetLoweredState(DEPOT_WIDGET_CLONE); + + if (this->IsWidgetLowered(DEPOT_WIDGET_CLONE)) { + static const CursorID clone_icons[] = { + SPR_CURSOR_CLONE_TRAIN, SPR_CURSOR_CLONE_ROADVEH, + SPR_CURSOR_CLONE_SHIP, SPR_CURSOR_CLONE_AIRPLANE + }; + + _place_clicked_vehicle = NULL; + SetObjectToPlaceWnd(clone_icons[this->type], PAL_NONE, VHM_RECT, this); + } else { + ResetObjectToPlace(); + } break; - case DEPOT_WIDGET_SELL_ALL: - /* Only open the confimation window if there are anything to sell */ - if (WP(w, depot_d).engine_count != 0 || WP(w, depot_d).wagon_count != 0) { - static const StringID confirm_captions[] = { - STR_8800_TRAIN_DEPOT, - STR_9003_ROAD_VEHICLE_DEPOT, - STR_9803_SHIP_DEPOT, - STR_A002_AIRCRAFT_HANGAR - }; - TileIndex tile = w->window_number; - byte vehtype = WP(w, depot_d).type; - - SetDParam(0, (vehtype == VEH_AIRCRAFT) ? GetStationIndex(tile) : GetDepotByTile(tile)->town_index); - ShowQuery( - confirm_captions[vehtype], - STR_DEPOT_SELL_CONFIRMATION_TEXT, - w, - DepotSellAllConfirmationCallback - ); - } - break; - - case DEPOT_WIDGET_VEHICLE_LIST: - ShowVehicleListWindow(GetTileOwner(w->window_number), WP(w, depot_d).type, (TileIndex)w->window_number); - break; - - case DEPOT_WIDGET_AUTOREPLACE: - DoCommandP(w->window_number, WP(w, depot_d).type, 0, NULL, CMD_DEPOT_MASS_AUTOREPLACE); - break; - - } - break; - - case WE_PLACE_OBJ: { - ClonePlaceObj(w); - } break; - - case WE_ABORT_PLACE_OBJ: { - /* abort clone */ - w->RaiseWidget(DEPOT_WIDGET_CLONE); - w->InvalidateWidget(DEPOT_WIDGET_CLONE); - - /* abort drag & drop */ - WP(w, depot_d).sel = INVALID_VEHICLE; - w->InvalidateWidget(DEPOT_WIDGET_MATRIX); - } break; - - /* check if a vehicle in a depot was clicked.. */ - case WE_MOUSELOOP: { - const Vehicle *v = _place_clicked_vehicle; - - /* since OTTD checks all open depot windows, we will make sure that it triggers the one with a clicked clone button */ - if (v != NULL && w->IsWidgetLowered(DEPOT_WIDGET_CLONE)) { - _place_clicked_vehicle = NULL; - HandleCloneVehClick(v, w); - } - } break; - - case WE_DESTROY: - DeleteWindowById(WC_BUILD_VEHICLE, w->window_number); - free((void*)WP(w, depot_d).vehicle_list); - free((void*)WP(w, depot_d).wagon_list); - break; - - case WE_DRAGDROP: - switch (e->we.click.widget) { - case DEPOT_WIDGET_MATRIX: { - Vehicle *v; - VehicleID sel = WP(w, depot_d).sel; - - WP(w, depot_d).sel = INVALID_VEHICLE; - w->SetDirty(); + case DEPOT_WIDGET_LOCATION: + if (_ctrl_pressed) { + ShowExtraViewPortWindow(this->window_number); + } else { + ScrollMainWindowToTile(this->window_number); + } + break; - if (WP(w, depot_d).type == VEH_TRAIN) { - GetDepotVehiclePtData gdvp; - - if (GetVehicleFromDepotWndPt(w, e->we.dragdrop.pt.x, e->we.dragdrop.pt.y, &v, &gdvp) == MODE_DRAG_VEHICLE && - sel != INVALID_VEHICLE) { - if (gdvp.wagon != NULL && gdvp.wagon->index == sel && _ctrl_pressed) { - DoCommandP(GetVehicle(sel)->tile, GetVehicle(sel)->index, true, NULL, CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_9033_CAN_T_MAKE_VEHICLE_TURN)); - } else if (gdvp.wagon == NULL || gdvp.wagon->index != sel) { - TrainDepotMoveVehicle(gdvp.wagon, sel, gdvp.head); - } else if (gdvp.head != NULL && IsFrontEngine(gdvp.head)) { - ShowVehicleViewWindow(gdvp.head); - } - } - } else if (GetVehicleFromDepotWndPt(w, e->we.dragdrop.pt.x, e->we.dragdrop.pt.y, &v, NULL) == MODE_DRAG_VEHICLE && - v != NULL && - sel == v->index) { - ShowVehicleViewWindow(v); - } - } break; - - case DEPOT_WIDGET_SELL: case DEPOT_WIDGET_SELL_CHAIN: - if (!w->IsWidgetDisabled(DEPOT_WIDGET_SELL) && - WP(w, depot_d).sel != INVALID_VEHICLE) { - Vehicle *v; - uint command; - int sell_cmd; - bool is_engine; - - if (w->IsWidgetDisabled(e->we.click.widget)) return; - if (WP(w, depot_d).sel == INVALID_VEHICLE) return; - - w->HandleButtonClick(e->we.click.widget); - - v = GetVehicle(WP(w, depot_d).sel); - WP(w, depot_d).sel = INVALID_VEHICLE; - w->SetDirty(); + case DEPOT_WIDGET_STOP_ALL: + case DEPOT_WIDGET_START_ALL: + DoCommandP(this->window_number, 0, this->type | (widget == DEPOT_WIDGET_START_ALL ? (1 << 5) : 0), NULL, CMD_MASS_START_STOP); + break; - sell_cmd = (v->type == VEH_TRAIN && (e->we.click.widget == DEPOT_WIDGET_SELL_CHAIN || _ctrl_pressed)) ? 1 : 0; - - is_engine = (!(v->type == VEH_TRAIN && !IsFrontEngine(v))); - - if (is_engine) { - _backup_orders_tile = v->tile; - BackupVehicleOrders(v); - } + case DEPOT_WIDGET_SELL_ALL: + /* Only open the confimation window if there are anything to sell */ + if (this->engine_count != 0 || this->wagon_count != 0) { + static const StringID confirm_captions[] = { + STR_8800_TRAIN_DEPOT, + STR_9003_ROAD_VEHICLE_DEPOT, + STR_9803_SHIP_DEPOT, + STR_A002_AIRCRAFT_HANGAR + }; + TileIndex tile = this->window_number; + byte vehtype = this->type; - switch (v->type) { - case VEH_TRAIN: command = CMD_SELL_RAIL_WAGON | CMD_MSG(STR_8839_CAN_T_SELL_RAILROAD_VEHICLE); break; - case VEH_ROAD: command = CMD_SELL_ROAD_VEH | CMD_MSG(STR_9014_CAN_T_SELL_ROAD_VEHICLE); break; - case VEH_SHIP: command = CMD_SELL_SHIP | CMD_MSG(STR_980C_CAN_T_SELL_SHIP); break; - case VEH_AIRCRAFT: command = CMD_SELL_AIRCRAFT | CMD_MSG(STR_A01C_CAN_T_SELL_AIRCRAFT); break; - default: NOT_REACHED(); command = 0; + SetDParam(0, (vehtype == VEH_AIRCRAFT) ? GetStationIndex(tile) : GetDepotByTile(tile)->town_index); + ShowQuery( + confirm_captions[vehtype], + STR_DEPOT_SELL_CONFIRMATION_TEXT, + this, + DepotSellAllConfirmationCallback + ); + } + break; + + case DEPOT_WIDGET_VEHICLE_LIST: + ShowVehicleListWindow(GetTileOwner(this->window_number), this->type, (TileIndex)this->window_number); + break; + + case DEPOT_WIDGET_AUTOREPLACE: + DoCommandP(this->window_number, this->type, 0, NULL, CMD_DEPOT_MASS_AUTOREPLACE); + break; + + } + } + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + const Vehicle *v = CheckMouseOverVehicle(); + + if (v != NULL) HandleCloneVehClick(v, this); + } + + virtual void OnPlaceObjectAbort() + { + /* abort clone */ + this->RaiseWidget(DEPOT_WIDGET_CLONE); + this->InvalidateWidget(DEPOT_WIDGET_CLONE); + + /* abort drag & drop */ + this->sel = INVALID_VEHICLE; + this->InvalidateWidget(DEPOT_WIDGET_MATRIX); + }; + + /* check if a vehicle in a depot was clicked.. */ + virtual void OnMouseLoop() + { + const Vehicle *v = _place_clicked_vehicle; + + /* since OTTD checks all open depot windows, we will make sure that it triggers the one with a clicked clone button */ + if (v != NULL && this->IsWidgetLowered(DEPOT_WIDGET_CLONE)) { + _place_clicked_vehicle = NULL; + HandleCloneVehClick(v, this); + } + } + + virtual void OnDragDrop(Point pt, int widget) + { + switch (widget) { + case DEPOT_WIDGET_MATRIX: { + Vehicle *v; + VehicleID sel = this->sel; + + this->sel = INVALID_VEHICLE; + this->SetDirty(); + + if (this->type == VEH_TRAIN) { + GetDepotVehiclePtData gdvp = { NULL, NULL }; + + if (this->GetVehicleFromDepotWndPt(pt.x, pt.y, &v, &gdvp) == MODE_DRAG_VEHICLE && + sel != INVALID_VEHICLE) { + if (gdvp.wagon != NULL && gdvp.wagon->index == sel && _ctrl_pressed) { + DoCommandP(GetVehicle(sel)->tile, GetVehicle(sel)->index, true, NULL, CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_9033_CAN_T_MAKE_VEHICLE_TURN)); + } else if (gdvp.wagon == NULL || gdvp.wagon->index != sel) { + TrainDepotMoveVehicle(gdvp.wagon, sel, gdvp.head); + } else if (gdvp.head != NULL && IsFrontEngine(gdvp.head)) { + ShowVehicleViewWindow(gdvp.head); } - - if (!DoCommandP(v->tile, v->index, sell_cmd, NULL, command) && is_engine) _backup_orders_tile = 0; } - break; - default: - WP(w, depot_d).sel = INVALID_VEHICLE; - w->SetDirty(); - } - _cursor.vehchain = false; - break; + } else if (this->GetVehicleFromDepotWndPt(pt.x, pt.y, &v, NULL) == MODE_DRAG_VEHICLE && + v != NULL && + sel == v->index) { + ShowVehicleViewWindow(v); + } + } break; - case WE_RESIZE: - w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height; - w->hscroll.cap += e->we.sizing.diff.x / (int)w->resize.step_width; - w->widget[DEPOT_WIDGET_MATRIX].data = (w->vscroll.cap << 8) + (WP(w, depot_d).type == VEH_TRAIN ? 1 : w->hscroll.cap); - ResizeDepotButtons(w); - break; + case DEPOT_WIDGET_SELL: case DEPOT_WIDGET_SELL_CHAIN: + if (!this->IsWidgetDisabled(DEPOT_WIDGET_SELL) && + this->sel != INVALID_VEHICLE) { + Vehicle *v; + uint command; + int sell_cmd; + bool is_engine; - case WE_CTRL_CHANGED: - if (WP(w, depot_d).sel != INVALID_VEHICLE) { - _cursor.vehchain = _ctrl_pressed; - w->InvalidateWidget(DEPOT_WIDGET_MATRIX); - } - break; + if (this->IsWidgetDisabled(widget)) return; + if (this->sel == INVALID_VEHICLE) return; + + this->HandleButtonClick(widget); + + v = GetVehicle(this->sel); + this->sel = INVALID_VEHICLE; + this->SetDirty(); + + sell_cmd = (v->type == VEH_TRAIN && (widget == DEPOT_WIDGET_SELL_CHAIN || _ctrl_pressed)) ? 1 : 0; + + is_engine = (!(v->type == VEH_TRAIN && !IsFrontEngine(v))); + + if (is_engine) { + _backup_orders_tile = v->tile; + BackupVehicleOrders(v); + } + + switch (v->type) { + case VEH_TRAIN: command = CMD_SELL_RAIL_WAGON | CMD_MSG(STR_8839_CAN_T_SELL_RAILROAD_VEHICLE); break; + case VEH_ROAD: command = CMD_SELL_ROAD_VEH | CMD_MSG(STR_9014_CAN_T_SELL_ROAD_VEHICLE); break; + case VEH_SHIP: command = CMD_SELL_SHIP | CMD_MSG(STR_980C_CAN_T_SELL_SHIP); break; + case VEH_AIRCRAFT: command = CMD_SELL_AIRCRAFT | CMD_MSG(STR_A01C_CAN_T_SELL_AIRCRAFT); break; + default: NOT_REACHED(); command = 0; + } + + if (!DoCommandP(v->tile, v->index, sell_cmd, NULL, command) && is_engine) _backup_orders_tile = 0; + } + break; + default: + this->sel = INVALID_VEHICLE; + this->SetDirty(); + } + _cursor.vehchain = false; + } + + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / (int)this->resize.step_height; + this->hscroll.cap += delta.x / (int)this->resize.step_width; + this->widget[DEPOT_WIDGET_MATRIX].data = (this->vscroll.cap << 8) + (this->type == VEH_TRAIN ? 1 : this->hscroll.cap); + ResizeDepotButtons(this); + } + + virtual EventState OnCTRLStateChange() + { + if (this->sel != INVALID_VEHICLE) { + _cursor.vehchain = _ctrl_pressed; + this->InvalidateWidget(DEPOT_WIDGET_MATRIX); + } + + return ES_HANDLED; + } +}; + +static void DepotSellAllConfirmationCallback(Window *win, bool confirmed) +{ + if (confirmed) { + DepotWindow *w = (DepotWindow*)win; + TileIndex tile = w->window_number; + byte vehtype = w->type; + DoCommandP(tile, vehtype, 0, NULL, CMD_DEPOT_SELL_ALL_VEHICLES); } } @@ -1006,24 +984,24 @@ */ void ShowDepotWindow(TileIndex tile, VehicleType type) { - Window *w; + DepotWindow *w; switch (type) { default: NOT_REACHED(); case VEH_TRAIN: - w = AllocateWindowDescFront(&_train_depot_desc, tile); break; + w = AllocateWindowDescFront(&_train_depot_desc, tile); break; case VEH_ROAD: - w = AllocateWindowDescFront(&_road_depot_desc, tile); break; + w = AllocateWindowDescFront(&_road_depot_desc, tile); break; case VEH_SHIP: - w = AllocateWindowDescFront(&_ship_depot_desc, tile); break; + w = AllocateWindowDescFront(&_ship_depot_desc, tile); break; case VEH_AIRCRAFT: - w = AllocateWindowDescFront(&_aircraft_depot_desc, tile); break; + w = AllocateWindowDescFront(&_aircraft_depot_desc, tile); break; } - if (w != NULL) { - w->caption_color = GetTileOwner(tile); - CreateDepotListWindow(w, type); - } + if (w == NULL) return; + + w->caption_color = GetTileOwner(tile); + w->CreateDepotListWindow(type); } /** Removes the highlight of a vehicle in a depot window @@ -1031,15 +1009,16 @@ */ void DeleteDepotHighlightOfVehicle(const Vehicle *v) { - Window *w; + DepotWindow *w; /* If we haven't got any vehicles on the mouse pointer, we haven't got any highlighted in any depots either - * If that is the case, we can skip looping though the windows and save time */ + * If that is the case, we can skip looping though the windows and save time + */ if (_special_mouse_mode != WSM_DRAGDROP) return; - w = FindWindowById(WC_VEHICLE_DEPOT, v->tile); + w = dynamic_cast(FindWindowById(WC_VEHICLE_DEPOT, v->tile)); if (w != NULL) { - WP(w, depot_d).sel = INVALID_VEHICLE; + w->sel = INVALID_VEHICLE; ResetObjectToPlace(); } } diff -r 6c4314786d68 -r 8cbdb511a674 src/disaster_cmd.cpp --- a/src/disaster_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/disaster_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -234,7 +234,7 @@ SetDParam(0, GetStationIndex(tile)); AddNewsItem(STR_B000_ZEPPELIN_DISASTER_AT, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, DNC_NONE, + NS_ACCIDENT_VEHICLE, v->index, 0); } @@ -367,7 +367,7 @@ u->vehstatus |= VS_CRASHED; AddNewsItem(STR_B001_ROAD_VEHICLE_DESTROYED, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, DNC_NONE, + NS_ACCIDENT_VEHICLE, u->index, 0); } @@ -442,7 +442,7 @@ DestructIndustry(i); SetDParam(0, i->town->index); - AddNewsItem(STR_B002_OIL_REFINERY_EXPLOSION, NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, DNC_NONE, i->xy, 0); + AddNewsItem(STR_B002_OIL_REFINERY_EXPLOSION, NS_ACCIDENT_TILE, i->xy, 0); SndPlayTileFx(SND_12_EXPLOSION, i->xy); } } else if (v->current_order.GetDestination() == 0) { @@ -515,7 +515,7 @@ DestructIndustry(i); SetDParam(0, i->town->index); - AddNewsItem(STR_B003_FACTORY_DESTROYED_IN_SUSPICIOUS, NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, DNC_NONE, i->xy, 0); + AddNewsItem(STR_B003_FACTORY_DESTROYED_IN_SUSPICIOUS, NS_ACCIDENT_TILE, i->xy, 0); SndPlayTileFx(SND_12_EXPLOSION, i->xy); } } else if (v->current_order.GetDestination() == 0) { @@ -600,7 +600,7 @@ t = ClosestTownFromTile(v->dest_tile, (uint)-1); SetDParam(0, t->index); AddNewsItem(STR_B004_UFO_LANDS_NEAR, - NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, DNC_NONE, + NS_ACCIDENT_TILE, v->tile, 0); @@ -978,7 +978,7 @@ if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CAN_SUBSIDENCE) && --index < 0) { SetDParam(0, i->town->index); AddNewsItem(STR_B005_COAL_MINE_SUBSIDENCE_LEAVES, - NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, DNC_NONE, i->xy + TileDiffXY(1, 1), 0); + NS_ACCIDENT_TILE, i->xy + TileDiffXY(1, 1), 0); { TileIndex tile = i->xy; diff -r 6c4314786d68 -r 8cbdb511a674 src/dock_gui.cpp --- a/src/dock_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/dock_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -125,80 +125,85 @@ BuildDocksClick_Buoy }; -static void BuildDocksToolbWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - DrawWindowWidgets(w); - w->SetWidgetsDisabledState(!CanBuildVehicleInfrastructure(VEH_SHIP), 7, 8, 9, WIDGET_LIST_END); - break; - - case WE_CLICK: - if (e->we.click.widget - 3 >= 0 && e->we.click.widget != 5) _build_docks_button_proc[e->we.click.widget - 3](w); - break; - - case WE_KEYPRESS: - switch (e->we.keypress.keycode) { - case '1': BuildDocksClick_Canal(w); break; - case '2': BuildDocksClick_Lock(w); break; - case '3': BuildDocksClick_Demolish(w); break; - case '4': BuildDocksClick_Depot(w); break; - case '5': BuildDocksClick_Dock(w); break; - case '6': BuildDocksClick_Buoy(w); break; - default: return; - } - break; - - case WE_PLACE_OBJ: - _place_proc(e->we.place.tile); - break; - - case WE_PLACE_DRAG: { - VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method); - return; +struct BuildDocksToolbarWindow : Window { + BuildDocksToolbarWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->FindWindowPlacementAndResize(desc); + if (_patches.link_terraform_toolbar) ShowTerraformToolbar(this); } - case WE_PLACE_MOUSEUP: - if (e->we.place.pt.x != -1) { - switch (e->we.place.select_proc) { + ~BuildDocksToolbarWindow() + { + if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0); + } + + virtual void OnPaint() + { + this->SetWidgetsDisabledState(!CanBuildVehicleInfrastructure(VEH_SHIP), 7, 8, 9, WIDGET_LIST_END); + this->DrawWidgets(); + } + + virtual void OnClick(Point pt, int widget) + { + if (widget - 3 >= 0 && widget != 5) _build_docks_button_proc[widget - 3](this); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + switch (keycode) { + case '1': BuildDocksClick_Canal(this); break; + case '2': BuildDocksClick_Lock(this); break; + case '3': BuildDocksClick_Demolish(this); break; + case '4': BuildDocksClick_Depot(this); break; + case '5': BuildDocksClick_Dock(this); break; + case '6': BuildDocksClick_Buoy(this); break; + default: return ES_NOT_HANDLED; + } + return ES_HANDLED; + } + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + _place_proc(tile); + } + + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) + { + VpSelectTilesWithMethod(pt.x, pt.y, select_method); + } + + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) + { + if (pt.x != -1) { + switch (select_proc) { case DDSP_DEMOLISH_AREA: - GUIPlaceProcDragXY(e); + GUIPlaceProcDragXY(select_proc, start_tile, end_tile); break; case DDSP_CREATE_WATER: - DoCommandP(e->we.place.tile, e->we.place.starttile, 0, CcBuildCanal, CMD_BUILD_CANAL | CMD_MSG(STR_CANT_BUILD_CANALS)); + DoCommandP(end_tile, start_tile, 0, CcBuildCanal, CMD_BUILD_CANAL | CMD_MSG(STR_CANT_BUILD_CANALS)); break; + default: break; } } - break; - - case WE_ABORT_PLACE_OBJ: - w->RaiseButtons(); - - w = FindWindowById(WC_BUILD_STATION, 0); - if (w != NULL) WP(w, def_d).close = true; + } - w = FindWindowById(WC_BUILD_DEPOT, 0); - if (w != NULL) WP(w, def_d).close = true; - break; + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); - case WE_PLACE_PRESIZE: { - TileIndex tile_from; - TileIndex tile_to; + delete FindWindowById(WC_BUILD_STATION, 0); + delete FindWindowById(WC_BUILD_DEPOT, 0); + } - tile_from = e->we.place.tile; - + virtual void OnPlacePresize(Point pt, TileIndex tile_from) + { DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile_from, NULL)); - tile_to = (dir != INVALID_DIAGDIR ? TileAddByDiagDir(tile_from, ReverseDiagDir(dir)) : tile_from); + TileIndex tile_to = (dir != INVALID_DIAGDIR ? TileAddByDiagDir(tile_from, ReverseDiagDir(dir)) : tile_from); VpSetPresizeRange(tile_from, tile_to); - } break; - - case WE_DESTROY: - if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0); - break; } -} +}; static const Widget _build_docks_toolb_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -221,7 +226,6 @@ WC_BUILD_TOOLBAR, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _build_docks_toolb_widgets, - BuildDocksToolbWndProc }; void ShowBuildDocksToolbar() @@ -229,20 +233,32 @@ if (!IsValidPlayer(_current_player)) return; DeleteWindowByClass(WC_BUILD_TOOLBAR); - Window *w = AllocateWindowDescFront(&_build_docks_toolbar_desc, TRANSPORT_WATER); - if (_patches.link_terraform_toolbar) ShowTerraformToolbar(w); + AllocateWindowDescFront(&_build_docks_toolbar_desc, TRANSPORT_WATER); } -static void BuildDockStationWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: w->LowerWidget(_station_show_coverage + 3); break; +struct BuildDocksStationWindow : public PickerWindowBase { +private: + enum BuildDockStationWidgets { + BDSW_CLOSE, + BDSW_CAPTION, + BDSW_BACKGROUND, + BDSW_LT_OFF, + BDSW_LT_ON, + BDSW_INFO, + }; - case WE_PAINT: { +public: + BuildDocksStationWindow(const WindowDesc *desc) : PickerWindowBase(desc) + { + this->LowerWidget(_station_show_coverage + BDSW_LT_OFF); + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { int rad = (_patches.modified_catchment) ? CA_DOCK : CA_UNMODIFIED; - if (WP(w, def_d).close) return; - DrawWindowWidgets(w); + this->DrawWidgets(); if (_station_show_coverage) { SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad); @@ -252,50 +268,40 @@ int text_end = DrawStationCoverageAreaText(4, 50, SCT_ALL, rad, false); text_end = DrawStationCoverageAreaText(4, text_end + 4, SCT_ALL, rad, true) + 4; - if (text_end != w->widget[2].bottom) { - w->SetDirty(); - ResizeWindowForWidget(w, 2, 0, text_end - w->widget[2].bottom); - w->SetDirty(); + if (text_end != this->widget[BDSW_BACKGROUND].bottom) { + this->SetDirty(); + ResizeWindowForWidget(this, 2, 0, text_end - this->widget[BDSW_BACKGROUND].bottom); + this->SetDirty(); } - - break; } - case WE_CLICK: - switch (e->we.click.widget) { - case 3: - case 4: - w->RaiseWidget(_station_show_coverage + 3); - _station_show_coverage = (e->we.click.widget != 3); - w->LowerWidget(_station_show_coverage + 3); + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case BDSW_LT_OFF: + case BDSW_LT_ON: + this->RaiseWidget(_station_show_coverage + BDSW_LT_OFF); + _station_show_coverage = (widget != BDSW_LT_OFF); + this->LowerWidget(_station_show_coverage + BDSW_LT_OFF); SndPlayFx(SND_15_BEEP); - w->SetDirty(); + this->SetDirty(); break; } - break; - - case WE_TICK: - if (WP(w, def_d).close) { - delete w; - return; - } + } - CheckRedrawStationCoverage(w); - break; - - case WE_DESTROY: - if (!WP(w, def_d).close) ResetObjectToPlace(); - break; + virtual void OnTick() + { + CheckRedrawStationCoverage(this); } -} +}; static const Widget _build_dock_station_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 147, 0, 13, STR_3068_DOCK, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 14, 74, 0x0, STR_NULL}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 14, 73, 30, 40, STR_02DB_OFF, STR_3065_DON_T_HIGHLIGHT_COVERAGE}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 74, 133, 30, 40, STR_02DA_ON, STR_3064_HIGHLIGHT_COVERAGE_AREA}, -{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 17, 30, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL}, +{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // BDSW_CLOSE +{ WWT_CAPTION, RESIZE_NONE, 7, 11, 147, 0, 13, STR_3068_DOCK, STR_018C_WINDOW_TITLE_DRAG_THIS}, // BDSW_CAPTION +{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 14, 74, 0x0, STR_NULL}, // BDSW_BACKGROUND +{ WWT_TEXTBTN, RESIZE_NONE, 14, 14, 73, 30, 40, STR_02DB_OFF, STR_3065_DON_T_HIGHLIGHT_COVERAGE}, // BDSW_LT_OFF +{ WWT_TEXTBTN, RESIZE_NONE, 14, 74, 133, 30, 40, STR_02DA_ON, STR_3064_HIGHLIGHT_COVERAGE_AREA}, // BDSW_LT_ON +{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 17, 30, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL}, // BDSW_INFO { WIDGETS_END}, }; @@ -304,67 +310,72 @@ WC_BUILD_STATION, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _build_dock_station_widgets, - BuildDockStationWndProc }; static void ShowBuildDockStationPicker() { - new Window(&_build_dock_station_desc); + new BuildDocksStationWindow(&_build_dock_station_desc); } -static void UpdateDocksDirection() -{ - if (_ship_depot_direction != AXIS_X) { - SetTileSelectSize(1, 2); - } else { - SetTileSelectSize(2, 1); +struct BuildDocksDepotWindow : public PickerWindowBase { +private: + enum BuildDockDepotWidgets { + BDDW_CLOSE, + BDDW_CAPTION, + BDDW_BACKGROUND, + BDDW_X, + BDDW_Y, + }; + + static void UpdateDocksDirection() + { + if (_ship_depot_direction != AXIS_X) { + SetTileSelectSize(1, 2); + } else { + SetTileSelectSize(2, 1); + } } -} -static void BuildDocksDepotWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: w->LowerWidget(_ship_depot_direction + 3); break; +public: + BuildDocksDepotWindow(const WindowDesc *desc) : PickerWindowBase(desc) + { + this->LowerWidget(_ship_depot_direction + BDDW_X); + UpdateDocksDirection(); + this->FindWindowPlacementAndResize(desc); + } - case WE_PAINT: - DrawWindowWidgets(w); + virtual void OnPaint() + { + this->DrawWidgets(); DrawShipDepotSprite(67, 35, 0); DrawShipDepotSprite(35, 51, 1); DrawShipDepotSprite(135, 35, 2); DrawShipDepotSprite(167, 51, 3); - return; + } - case WE_CLICK: { - switch (e->we.click.widget) { - case 3: - case 4: - w->RaiseWidget(_ship_depot_direction + 3); - _ship_depot_direction = (e->we.click.widget == 3 ? AXIS_X : AXIS_Y); - w->LowerWidget(_ship_depot_direction + 3); - SndPlayFx(SND_15_BEEP); - UpdateDocksDirection(); - w->SetDirty(); - break; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case BDDW_X: + case BDDW_Y: + this->RaiseWidget(_ship_depot_direction + BDDW_X); + _ship_depot_direction = (widget == BDDW_X ? AXIS_X : AXIS_Y); + this->LowerWidget(_ship_depot_direction + BDDW_X); + SndPlayFx(SND_15_BEEP); + UpdateDocksDirection(); + this->SetDirty(); + break; } - } break; - - case WE_TICK: - if (WP(w, def_d).close) delete w; - break; - - case WE_DESTROY: - if (!WP(w, def_d).close) ResetObjectToPlace(); - break; } -} +}; static const Widget _build_docks_depot_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 203, 0, 13, STR_3800_SHIP_DEPOT_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_PANEL, RESIZE_NONE, 7, 0, 203, 14, 85, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 14, 3, 100, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION}, -{ WWT_PANEL, RESIZE_NONE, 14, 103, 200, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION}, +{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // BDDW_CLOSE +{ WWT_CAPTION, RESIZE_NONE, 7, 11, 203, 0, 13, STR_3800_SHIP_DEPOT_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS}, // BDDW_CAPTION +{ WWT_PANEL, RESIZE_NONE, 7, 0, 203, 14, 85, 0x0, STR_NULL}, // BDDW_BACKGROUND +{ WWT_PANEL, RESIZE_NONE, 14, 3, 100, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION}, // BDDW_X +{ WWT_PANEL, RESIZE_NONE, 14, 103, 200, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION}, // BDDW_Y { WIDGETS_END}, }; @@ -373,14 +384,12 @@ WC_BUILD_DEPOT, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _build_docks_depot_widgets, - BuildDocksDepotWndProc }; static void ShowBuildDocksDepotPicker() { - new Window(&_build_docks_depot_desc); - UpdateDocksDirection(); + new BuildDocksDepotWindow(&_build_docks_depot_desc); } diff -r 6c4314786d68 -r 8cbdb511a674 src/driver.h --- a/src/driver.h Mon May 19 14:14:33 2008 +0000 +++ b/src/driver.h Mon May 19 15:13:58 2008 +0000 @@ -43,7 +43,7 @@ static Drivers &GetDrivers() { - static Drivers s_drivers; + static Drivers &s_drivers = *new Drivers(); return s_drivers; } @@ -71,7 +71,14 @@ */ virtual ~DriverFactoryBase() { if (this->name == NULL) return; - GetDrivers().erase(this->name); + + /* Prefix the name with driver type to make it unique */ + char buf[32]; + strecpy(buf, GetDriverTypeName(type), lastof(buf)); + strecpy(buf + 5, this->name, lastof(buf)); + + GetDrivers().erase(buf); + if (GetDrivers().empty()) delete &GetDrivers(); free(this->name); } diff -r 6c4314786d68 -r 8cbdb511a674 src/economy.cpp --- a/src/economy.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/economy.cpp Mon May 19 15:13:58 2008 +0000 @@ -29,7 +29,6 @@ #include "newgrf_station.h" #include "unmovable.h" #include "cargotype.h" -#include "player_face.h" #include "group.h" #include "strings_func.h" #include "tile_cmd.h" @@ -528,8 +527,10 @@ switch (p->quarters_of_bankrupcy) { case 2: - AddNewsItem((StringID)(owner | NB_BTROUBLE), - NM_CALLBACK, NF_NONE, NT_COMPANY_INFO, DNC_BANKRUPCY, 0, 0); + SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE); + SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED); + SetDParam(2, owner); + AddNewsItem(STR_02B6, NS_COMPANY_TROUBLE, 0, owner); for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { AI_Event(i, new AIEventCompanyInTrouble(owner)); } @@ -538,8 +539,10 @@ /* XXX - In multiplayer, should we ask other players if it wants to take over when it is a human company? -- TrueLight */ if (IsHumanPlayer(owner)) { - AddNewsItem((StringID)(owner | NB_BTROUBLE), - NM_CALLBACK, NF_NONE, NT_COMPANY_INFO, DNC_BANKRUPCY, 0, 0); + SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE); + SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED); + SetDParam(2, owner); + AddNewsItem(STR_02B6, NS_COMPANY_TROUBLE, 0, owner); break; } @@ -559,8 +562,10 @@ DeletePlayerWindows(owner); /* Show bankrupt news */ - SetDParam(0, p->index); - AddNewsItem((StringID)(owner | NB_BBANKRUPT), NM_CALLBACK, NF_NONE, NT_COMPANY_INFO, DNC_BANKRUPCY, 0, 0); + SetDParam(0, STR_705C_BANKRUPT); + SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY); + SetDParam(2, p->index); + AddNewsItem(STR_02B6, NS_COMPANY_BANKRUPT, 0, owner); if (IsHumanPlayer(owner)) { /* XXX - If we are in offline mode, leave the player playing. Eg. there @@ -591,102 +596,6 @@ } } -void DrawNewsBankrupcy(Window *w, const NewsItem *ni) -{ - DrawNewsBorder(w); - - Player *p = GetPlayer((PlayerID)GB(ni->string_id, 0, 4)); - DrawPlayerFace(p->face, p->player_color, 2, 23); - GfxFillRect(3, 23, 3 + 91, 23 + 118, PALETTE_TO_STRUCT_GREY | (1 << USE_COLORTABLE)); - - SetDParam(0, p->index); - - DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94); - - switch (ni->string_id & 0xF0) { - case NB_BTROUBLE: - DrawStringCentered(w->width >> 1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, TC_FROMSTRING); - - SetDParam(0, p->index); - - DrawStringMultiCenter( - ((w->width - 101) >> 1) + 98, - 90, - STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED, - w->width - 101); - break; - - case NB_BMERGER: - DrawStringCentered(w->width >> 1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, TC_FROMSTRING); - SetDParam(0, ni->params[0]); - SetDParam(1, p->index); - SetDParam(2, ni->params[1]); - DrawStringMultiCenter( - ((w->width - 101) >> 1) + 98, - 90, - ni->params[1] == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR, - w->width - 101); - break; - - case NB_BBANKRUPT: - DrawStringCentered(w->width >> 1, 1, STR_705C_BANKRUPT, TC_FROMSTRING); - SetDParam(0, ni->params[0]); - DrawStringMultiCenter( - ((w->width - 101) >> 1) + 98, - 90, - STR_705D_HAS_BEEN_CLOSED_DOWN_BY, - w->width - 101); - break; - - case NB_BNEWCOMPANY: - DrawStringCentered(w->width >> 1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, TC_FROMSTRING); - SetDParam(0, p->index); - SetDParam(1, ni->params[0]); - DrawStringMultiCenter( - ((w->width - 101) >> 1) + 98, - 90, - STR_705F_STARTS_CONSTRUCTION_NEAR, - w->width - 101); - break; - - default: - NOT_REACHED(); - } -} - -StringID GetNewsStringBankrupcy(const NewsItem *ni) -{ - const Player *p = GetPlayer((PlayerID)GB(ni->string_id, 0, 4)); - - switch (ni->string_id & 0xF0) { - case NB_BTROUBLE: - SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE); - SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED); - SetDParam(2, p->index); - return STR_02B6; - case NB_BMERGER: - SetDParam(0, STR_7059_TRANSPORT_COMPANY_MERGER); - SetDParam(1, ni->params[1] == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR); - SetDParam(2, ni->params[0]); - SetDParam(3, p->index); - SetDParam(4, ni->params[1]); - return STR_02B6; - case NB_BBANKRUPT: - SetDParam(0, STR_705C_BANKRUPT); - SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY); - SetDParam(2, ni->params[0]); - return STR_02B6; - case NB_BNEWCOMPANY: - SetDParam(0, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED); - SetDParam(1, STR_705F_STARTS_CONSTRUCTION_NEAR); - SetDParam(2, p->index); - SetDParam(3, ni->params[0]); - return STR_02B6; - default: - NOT_REACHED(); - } -} - static void PlayersGenStatistics() { Station *st; @@ -808,10 +717,10 @@ if (--_economy.fluct == 0) { _economy.fluct = -(int)GB(Random(), 0, 2); - AddNewsItem(STR_7073_WORLD_RECESSION_FINANCIAL, NM_NORMAL, NF_NONE, NT_ECONOMY, DNC_NONE, 0, 0); + AddNewsItem(STR_7073_WORLD_RECESSION_FINANCIAL, NS_ECONOMY, 0, 0); } else if (_economy.fluct == -12) { _economy.fluct = GB(Random(), 0, 8) + 312; - AddNewsItem(STR_7074_RECESSION_OVER_UPTURN_IN, NM_NORMAL, NF_NONE, NT_ECONOMY, DNC_NONE, 0, 0); + AddNewsItem(STR_7074_RECESSION_OVER_UPTURN_IN, NS_ECONOMY, 0, 0); } } @@ -1151,7 +1060,7 @@ if (s->age == 12-1) { pair = SetupSubsidyDecodeParam(s, 1); - AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NM_NORMAL, NF_TILE | NF_TILE2, NT_SUBSIDIES, DNC_NONE, pair.a, pair.b); + AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, pair.a, pair.b); s->cargo_type = CT_INVALID; modified = true; for (PlayerID i = PLAYER_FIRST; i != MAX_PLAYERS; i++) { @@ -1161,7 +1070,7 @@ st = GetStation(s->to); if (st->owner == _local_player) { pair = SetupSubsidyDecodeParam(s, 1); - AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NM_NORMAL, NF_TILE | NF_TILE2, NT_SUBSIDIES, DNC_NONE, pair.a, pair.b); + AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, pair.a, pair.b); } s->cargo_type = CT_INVALID; modified = true; @@ -1203,7 +1112,7 @@ if (!CheckSubsidyDuplicate(s)) { s->age = 0; pair = SetupSubsidyDecodeParam(s, 0); - AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NM_NORMAL, NF_TILE | NF_TILE2, NT_SUBSIDIES, DNC_NONE, pair.a, pair.b); + AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, pair.a, pair.b); for (PlayerID i = PLAYER_FIRST; i != MAX_PLAYERS; i++) { AI_Event(i, new AIEventSubsidyOffer(s - _subsidies)); } @@ -1423,7 +1332,7 @@ SetDParam(0, _current_player); AddNewsItem( STR_2031_SERVICE_SUBSIDY_AWARDED + _opt.diff.subsidy_multiplier, - NM_NORMAL, NF_TILE | NF_TILE2, NT_SUBSIDIES, DNC_NONE, + NS_SUBSIDIES, pair.a, pair.b ); for (PlayerID i = PLAYER_FIRST; i != MAX_PLAYERS; i++) { @@ -1864,9 +1773,12 @@ Money value; PlayerID pi = p->index; - SetDParam(0, p->index); - SetDParam(1, p->bankrupt_value); - AddNewsItem((StringID)(_current_player | NB_BMERGER), NM_CALLBACK, NF_NONE, NT_COMPANY_INFO, DNC_BANKRUPCY, 0, 0); + SetDParam(0, STR_7059_TRANSPORT_COMPANY_MERGER); + SetDParam(1, p->bankrupt_value == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR); + SetDParam(2, p->index); + SetDParam(3, _current_player); + SetDParam(4, p->bankrupt_value); + AddNewsItem(STR_02B6, NS_COMPANY_MERGER, 0, _current_player); for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { AI_Event(i, new AIEventCompanyMerger(pi, _current_player)); } @@ -1894,7 +1806,10 @@ if (!IsHumanPlayer(p->index)) AI_PlayerDied(p->index); DeletePlayerWindows(pi); - RebuildVehicleLists(); //Updates the open windows to add the newly acquired vehicles to the lists + InvalidateWindowClassesData(WC_TRAINS_LIST, 0); + InvalidateWindowClassesData(WC_SHIPS_LIST, 0); + InvalidateWindowClassesData(WC_ROADVEH_LIST, 0); + InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0); } extern int GetAmountOwnedBy(const Player *p, PlayerID owner); diff -r 6c4314786d68 -r 8cbdb511a674 src/elrail.cpp --- a/src/elrail.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/elrail.cpp Mon May 19 15:13:58 2008 +0000 @@ -104,7 +104,7 @@ if (override != NULL && (IsTunnel(t) || GetTunnelBridgeLength(t, GetOtherBridgeEnd(t)) > 0)) { *override = 1 << GetTunnelBridgeDirection(t); } - return AxisToTrackBits(DiagDirToAxis(GetTunnelBridgeDirection(t))); + return DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)); case MP_ROAD: if (!IsLevelCrossing(t)) return TRACK_BIT_NONE; diff -r 6c4314786d68 -r 8cbdb511a674 src/engine.cpp --- a/src/engine.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/engine.cpp Mon May 19 15:13:58 2008 +0000 @@ -28,6 +28,7 @@ #include "oldpool_func.h" #include "ai/ai.h" #include "core/alloc_func.hpp" +#include "vehicle_func.h" #include "map" #include "table/strings.h" @@ -121,7 +122,11 @@ */ void EngList_Sort(EngineList *el, EngList_SortTypeFunction compare) { - qsort(&((*el)[0]), el->size(), sizeof(EngineID), compare); + size_t size = el->size(); + /* out-of-bounds access at the next line for size == 0 (even with operator[] at some systems) + * generally, do not sort if there are less than 2 items */ + if (size < 2) return; + qsort(&((*el)[0]), size, sizeof(EngineID), compare); // MorphOS doesn't know vector::at(int) ... } /** Sort selected range of items (on indices @ ) @@ -134,9 +139,49 @@ { assert(begin <= (uint)el->size()); assert(begin + num_items <= (uint)el->size()); + if (num_items < 2) return; qsort(&((*el)[begin]), num_items, sizeof(EngineID), compare); } + +/** Sets cached values in Player::num_vehicles and Group::num_vehicles + */ +void SetCachedEngineCounts() +{ + uint engines = GetEnginePoolSize(); + + /* Set up the engine count for all players */ + Player *p; + FOR_ALL_PLAYERS(p) { + free(p->num_engines); + p->num_engines = CallocT(engines); + } + + /* Recalculate */ + Group *g; + FOR_ALL_GROUPS(g) { + free(g->num_engines); + g->num_engines = CallocT(engines); + } + + const Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (!IsEngineCountable(v)) continue; + + assert(v->engine_type < engines); + + GetPlayer(v->owner)->num_engines[v->engine_type]++; + + if (v->group_id == DEFAULT_GROUP) continue; + + g = GetGroup(v->group_id); + assert(v->type == g->vehicle_type); + assert(v->owner == g->owner); + + g->num_engines[v->engine_type]++; + } +} + void SetupEngines() { _Engine_pool.CleanPool(); @@ -364,6 +409,8 @@ return e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON; } +StringID GetEngineCategoryName(EngineID engine); + static void NewVehicleAvailable(Engine *e) { Vehicle *v; @@ -416,7 +463,10 @@ if (p->is_active) SetBit(p->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD); } } - AddNewsItem(index, NM_CALLBACK, NF_NONE, NT_NEW_VEHICLES, DNC_VEHICLEAVAIL, 0, 0); + + SetDParam(0, GetEngineCategoryName(index)); + SetDParam(1, index); + AddNewsItem(STR_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NS_NEW_VEHICLES, index, 0); } void EnginesMonthlyLoop() diff -r 6c4314786d68 -r 8cbdb511a674 src/engine_func.h --- a/src/engine_func.h Mon May 19 14:14:33 2008 +0000 +++ b/src/engine_func.h Mon May 19 15:13:58 2008 +0000 @@ -27,6 +27,7 @@ bool IsEngineBuildable(EngineID engine, VehicleType type, PlayerID player); CargoID GetEngineCargoType(EngineID engine); +void SetCachedEngineCounts(); typedef int CDECL EngList_SortTypeFunction(const void*, const void*); ///< argument type for EngList_Sort() void EngList_Sort(EngineList *el, EngList_SortTypeFunction compare); ///< qsort of the engine list diff -r 6c4314786d68 -r 8cbdb511a674 src/engine_gui.cpp --- a/src/engine_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/engine_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -19,7 +19,7 @@ #include "table/strings.h" #include "table/sprites.h" -static StringID GetEngineCategoryName(EngineID engine) +StringID GetEngineCategoryName(EngineID engine) { switch (GetEngine(engine)->type) { default: NOT_REACHED(); @@ -66,55 +66,53 @@ { DrawAircraftEngine, DrawAircraftEngineInfo }, }; -static void EnginePreviewWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - EngineID engine = w->window_number; - const DrawEngineInfo* dei; - int width; +struct EnginePreviewWindow : Window { + EnginePreviewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + } - DrawWindowWidgets(w); + virtual void OnPaint() + { + this->DrawWidgets(); + EngineID engine = this->window_number; SetDParam(0, GetEngineCategoryName(engine)); DrawStringMultiCenter(150, 44, STR_8101_WE_HAVE_JUST_DESIGNED_A, 296); SetDParam(0, engine); - DrawStringCentered(w->width >> 1, 80, STR_ENGINE_NAME, TC_BLACK); + DrawStringCentered(this->width >> 1, 80, STR_ENGINE_NAME, TC_BLACK); - dei = &_draw_engine_list[GetEngine(engine)->type]; + const DrawEngineInfo *dei = &_draw_engine_list[GetEngine(engine)->type]; - width = w->width; + int width = this->width; dei->engine_proc(width >> 1, 100, engine, 0); dei->info_proc(engine, width >> 1, 130, width - 52); - break; } - case WE_CLICK: - switch (e->we.click.widget) { + virtual void OnClick(Point pt, int widget) + { + switch (widget) { case 4: - DoCommandP(0, w->window_number, 0, NULL, CMD_WANT_ENGINE_PREVIEW); + DoCommandP(0, this->window_number, 0, NULL, CMD_WANT_ENGINE_PREVIEW); /* Fallthrough */ case 3: - delete w; + delete this; break; } - break; } -} +}; static const WindowDesc _engine_preview_desc = { WDP_CENTER, WDP_CENTER, 300, 192, 300, 192, WC_ENGINE_PREVIEW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _engine_preview_widgets, - EnginePreviewWndProc }; void ShowEnginePreviewWindow(EngineID engine) { - AllocateWindowDescFront(&_engine_preview_desc, engine); + AllocateWindowDescFront(&_engine_preview_desc, engine); } static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw) @@ -174,20 +172,9 @@ DrawStringMultiCenter(x, y, STR_982E_COST_MAX_SPEED_CAPACITY, maxw); } - -StringID GetNewsStringNewVehicleAvail(const NewsItem *ni) -{ - EngineID engine = ni->string_id; - SetDParam(0, GetEngineCategoryName(engine)); - SetDParam(1, engine); - return STR_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE; -} - void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni) { - DrawNewsBorder(w); - - EngineID engine = ni->string_id; + EngineID engine = ni->data_a; const DrawEngineInfo *dei = &_draw_engine_list[GetEngine(engine)->type]; SetDParam(0, GetEngineCategoryName(engine)); diff -r 6c4314786d68 -r 8cbdb511a674 src/genworld_gui.cpp --- a/src/genworld_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/genworld_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -253,7 +253,7 @@ char name[64]; glwp_modes mode; - GenerateLandscapeWindow(const WindowDesc *desc, void *data = NULL, WindowNumber number = 0) : QueryStringBaseWindow(desc, NULL, number) + GenerateLandscapeWindow(const WindowDesc *desc, WindowNumber number = 0) : QueryStringBaseWindow(desc, number) { this->LowerWidget(_opt_newgame.landscape + GLAND_TEMPERATE); @@ -315,7 +315,7 @@ SetDParam(2, 1 << _patches_newgame.map_y); // GLAND_MAPSIZE_Y_PULLDOWN SetDParam(3, _patches_newgame.snow_line_height); // GLAND_SNOW_LEVEL_TEXT - DrawWindowWidgets(this); + this->DrawWidgets(); this->DrawEditBox(GLAND_RANDOM_EDITBOX); @@ -470,17 +470,17 @@ this->HandleEditBox(GLAND_RANDOM_EDITBOX); } - virtual bool OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { - bool cont; - this->HandleEditBoxKey(GLAND_RANDOM_EDITBOX, key, keycode, cont); + EventState state; + this->HandleEditBoxKey(GLAND_RANDOM_EDITBOX, key, keycode, state); /* the seed is unsigned, therefore atoi cannot be used. * As 2^32 - 1 (MAX_UVALUE(uint32)) is a 'magic' value * (use random seed) it should not be possible to be * entered into the input field; the generate seed * button can be used instead. */ _patches_newgame.generation_seed = minu(strtoul(this->edit_str_buf, NULL, sizeof(this->edit_str_buf) - 1), MAX_UVALUE(uint32) - 1); - return cont; + return state; } virtual void OnDropdownSelect(int widget, int index) @@ -554,7 +554,6 @@ WC_GENERATE_LANDSCAPE, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _generate_landscape_widgets, - NULL, }; static const WindowDesc _heightmap_load_desc = { @@ -562,7 +561,6 @@ WC_GENERATE_LANDSCAPE, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS, _heightmap_load_widgets, - NULL, }; static void _ShowGenerateLandscape(glwp_modes mode) @@ -644,7 +642,7 @@ { uint widget_id; - CreateScenarioWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, NULL, window_number) + CreateScenarioWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) { this->LowerWidget(_opt_newgame.landscape + CSCEN_TEMPERATE); } @@ -667,7 +665,7 @@ SetDParam(2, 1 << _patches_newgame.map_y); // CSCEN_MAPSIZE_Y_PULLDOWN SetDParam(3, _patches_newgame.se_flat_world_height); // CSCEN_FLAT_LAND_HEIGHT_TEXT - DrawWindowWidgets(this); + this->DrawWidgets(); } virtual void OnClick(Point pt, int widget) @@ -801,7 +799,6 @@ WC_GENERATE_LANDSCAPE, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS, _create_scenario_widgets, - NULL, }; void ShowCreateScenario() @@ -811,13 +808,20 @@ } -static const Widget _show_terrain_progress_widgets[] = { -{ WWT_CAPTION, RESIZE_NONE, 14, 0, 180, 0, 13, STR_GENERATION_WORLD, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_PANEL, RESIZE_NONE, 14, 0, 180, 14, 96, 0x0, STR_NULL}, -{ WWT_TEXTBTN, RESIZE_NONE, 15, 20, 161, 74, 85, STR_GENERATION_ABORT, STR_NULL}, // Abort button +static const Widget _generate_progress_widgets[] = { +{ WWT_CAPTION, RESIZE_NONE, 14, 0, 180, 0, 13, STR_GENERATION_WORLD, STR_018C_WINDOW_TITLE_DRAG_THIS}, // GPWW_CAPTION +{ WWT_PANEL, RESIZE_NONE, 14, 0, 180, 14, 96, 0x0, STR_NULL}, // GPWW_BACKGROUND +{ WWT_TEXTBTN, RESIZE_NONE, 15, 20, 161, 74, 85, STR_GENERATION_ABORT, STR_NULL}, // GPWW_ABORT { WIDGETS_END}, }; +static const WindowDesc _generate_progress_desc = { + WDP_CENTER, WDP_CENTER, 181, 97, 181, 97, + WC_GENERATE_PROGRESS_WINDOW, WC_NONE, + WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, + _generate_progress_widgets, +}; + struct tp_info { uint percent; StringID cls; @@ -837,51 +841,52 @@ } } -static void ShowTerrainProgressProc(Window* w, WindowEvent* e) -{ - switch (e->event) { - case WE_CLICK: - switch (e->we.click.widget) { - case 2: - if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE); - ShowQuery( - STR_GENERATION_ABORT_CAPTION, - STR_GENERATION_ABORT_MESSAGE, - w, - AbortGeneratingWorldCallback - ); - break; - } - break; - - case WE_PAINT: - DrawWindowWidgets(w); +struct GenerateProgressWindow : public Window { +private: + enum GenerationProgressWindowWidgets { + GPWW_CAPTION, + GPWW_BACKGROUND, + GPWW_ABORT, + }; - /* Draw the % complete with a bar and a text */ - DrawFrameRect(19, 20, (w->width - 18), 37, 14, FR_BORDERONLY); - DrawFrameRect(20, 21, (int)((w->width - 40) * _tp.percent / 100) + 20, 36, 10, FR_NONE); - SetDParam(0, _tp.percent); - DrawStringCentered(90, 25, STR_PROGRESS, TC_FROMSTRING); - - /* Tell which class we are generating */ - DrawStringCentered(90, 46, _tp.cls, TC_FROMSTRING); +public: + GenerateProgressWindow() : Window(&_generate_progress_desc) {}; - /* And say where we are in that class */ - SetDParam(0, _tp.current); - SetDParam(1, _tp.total); - DrawStringCentered(90, 58, STR_GENERATION_PROGRESS, TC_FROMSTRING); - - w->SetDirty(); - break; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case GPWW_ABORT: + if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE); + ShowQuery( + STR_GENERATION_ABORT_CAPTION, + STR_GENERATION_ABORT_MESSAGE, + this, + AbortGeneratingWorldCallback + ); + break; + } } -} -static const WindowDesc _show_terrain_progress_desc = { - WDP_CENTER, WDP_CENTER, 181, 97, 181, 97, - WC_GENERATE_PROGRESS_WINDOW, WC_NONE, - WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, - _show_terrain_progress_widgets, - ShowTerrainProgressProc + virtual void OnPaint() + { + this->DrawWidgets(); + + /* Draw the % complete with a bar and a text */ + DrawFrameRect(19, 20, (this->width - 18), 37, 14, FR_BORDERONLY); + DrawFrameRect(20, 21, (int)((this->width - 40) * _tp.percent / 100) + 20, 36, 10, FR_NONE); + SetDParam(0, _tp.percent); + DrawStringCentered(90, 25, STR_PROGRESS, TC_FROMSTRING); + + /* Tell which class we are generating */ + DrawStringCentered(90, 46, _tp.cls, TC_FROMSTRING); + + /* And say where we are in that class */ + SetDParam(0, _tp.current); + SetDParam(1, _tp.total); + DrawStringCentered(90, 58, STR_GENERATION_PROGRESS, TC_FROMSTRING); + + this->SetDirty(); + } }; /** @@ -901,7 +906,8 @@ */ void ShowGenerateWorldProgress() { - AllocateWindowDescFront(&_show_terrain_progress_desc, 0); + if (BringWindowToFrontById(WC_GENERATE_PROGRESS_WINDOW, 0)) return; + new GenerateProgressWindow(); } static void _SetGeneratingWorldProgress(gwp_class cls, uint progress, uint total) diff -r 6c4314786d68 -r 8cbdb511a674 src/graph_gui.cpp --- a/src/graph_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/graph_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -24,295 +24,62 @@ static uint _legend_excluded_players; static uint _legend_excluded_cargo; -/************************/ -/* GENERIC GRAPH DRAWER */ -/************************/ - -enum { - GRAPH_MAX_DATASETS = 32, - GRAPH_AXIS_LABEL_COLOUR = TC_BLACK, - GRAPH_AXIS_LINE_COLOUR = 215, - - GRAPH_X_POSITION_BEGINNING = 44, ///< Start the graph 44 pixels from gw->left - GRAPH_X_POSITION_SEPARATION = 22, ///< There are 22 pixels between each X value - - GRAPH_NUM_LINES_Y = 9, ///< How many horizontal lines to draw. - /* 9 is convenient as that means the distance between them is the height of the graph / 8, - * which is the same - * as height >> 3. */ -}; - /* Apparently these don't play well with enums. */ static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX); // Value used for a datapoint that shouldn't be drawn. static const uint INVALID_DATAPOINT_POS = UINT_MAX; // Used to determine if the previous point was drawn. -struct GraphDrawer { - uint excluded_data; ///< bitmask of the datasets that shouldn't be displayed. - byte num_dataset; - byte num_on_x_axis; - bool has_negative_values; - byte num_vert_lines; - - /* The starting month and year that values are plotted against. If month is - * 0xFF, use x_values_start and x_values_increment below instead. */ - byte month; - Year year; - - /* These values are used if the graph is being plotted against values - * rather than the dates specified by month and year. */ - uint16 x_values_start; - uint16 x_values_increment; - - int left, top; ///< Where to start drawing the graph, in pixels. - uint height; ///< The height of the graph in pixels. - StringID format_str_y_axis; - byte colors[GRAPH_MAX_DATASETS]; - OverflowSafeInt64 cost[GRAPH_MAX_DATASETS][24]; ///< last 2 years -}; - -static void DrawGraph(const GraphDrawer *gw) -{ - uint x, y; ///< Reused whenever x and y coordinates are needed. - OverflowSafeInt64 highest_value; ///< Highest value to be drawn. - int x_axis_offset; ///< Distance from the top of the graph to the x axis. - - /* the colors and cost array of GraphDrawer must accomodate - * both values for cargo and players. So if any are higher, quit */ - assert(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_PLAYERS); - assert(gw->num_vert_lines > 0); - - byte grid_colour = _colour_gradient[14][4]; - - /* The coordinates of the opposite edges of the graph. */ - int bottom = gw->top + gw->height - 1; - int right = gw->left + GRAPH_X_POSITION_BEGINNING + gw->num_vert_lines * GRAPH_X_POSITION_SEPARATION - 1; - - /* Draw the vertical grid lines. */ - - /* Don't draw the first line, as that's where the axis will be. */ - x = gw->left + GRAPH_X_POSITION_BEGINNING + GRAPH_X_POSITION_SEPARATION; - - for (int i = 0; i < gw->num_vert_lines; i++) { - GfxFillRect(x, gw->top, x, bottom, grid_colour); - x += GRAPH_X_POSITION_SEPARATION; - } - - /* Draw the horizontal grid lines. */ - x = gw->left + GRAPH_X_POSITION_BEGINNING; - y = gw->height + gw->top; - - for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) { - GfxFillRect(x, y, right, y, grid_colour); - y -= (gw->height / (GRAPH_NUM_LINES_Y - 1)); - } - - /* Draw the y axis. */ - GfxFillRect(x, gw->top, x, bottom, GRAPH_AXIS_LINE_COLOUR); - - /* Find the distance from the top of the graph to the x axis. */ - x_axis_offset = gw->height; - - /* The graph is currently symmetrical about the x axis. */ - if (gw->has_negative_values) x_axis_offset /= 2; - - /* Draw the x axis. */ - y = x_axis_offset + gw->top; - GfxFillRect(x, y, right, y, GRAPH_AXIS_LINE_COLOUR); - - /* Find the largest value that will be drawn. */ - if (gw->num_on_x_axis == 0) - return; - - assert(gw->num_on_x_axis > 0); - assert(gw->num_dataset > 0); - - /* Start of with a value of twice the height of the graph in pixels. It's a - * bit arbitrary, but it makes the cargo payment graph look a little nicer, - * and prevents division by zero when calculating where the datapoint - * should be drawn. */ - highest_value = x_axis_offset * 2; - - for (int i = 0; i < gw->num_dataset; i++) { - if (!HasBit(gw->excluded_data, i)) { - for (int j = 0; j < gw->num_on_x_axis; j++) { - OverflowSafeInt64 datapoint = gw->cost[i][j]; - - if (datapoint != INVALID_DATAPOINT) { - /* For now, if the graph has negative values the scaling is - * symmetrical about the x axis, so take the absolute value - * of each data point. */ - highest_value = max(highest_value, abs(datapoint)); - } - } - } - } - - /* Round up highest_value so that it will divide cleanly into the number of - * axis labels used. */ - int round_val = highest_value % (GRAPH_NUM_LINES_Y - 1); - if (round_val != 0) highest_value += (GRAPH_NUM_LINES_Y - 1 - round_val); - - /* draw text strings on the y axis */ - int64 y_label = highest_value; - int64 y_label_separation = highest_value / (GRAPH_NUM_LINES_Y - 1); - - /* If there are negative values, the graph goes from highest_value to - * -highest_value, not highest_value to 0. */ - if (gw->has_negative_values) y_label_separation *= 2; - - x = gw->left + GRAPH_X_POSITION_BEGINNING + 1; - y = gw->top - 3; - - for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) { - SetDParam(0, gw->format_str_y_axis); - SetDParam(1, y_label); - DrawStringRightAligned(x, y, STR_0170, GRAPH_AXIS_LABEL_COLOUR); - - y_label -= y_label_separation; - y += (gw->height / (GRAPH_NUM_LINES_Y - 1)); - } - - /* draw strings on the x axis */ - if (gw->month != 0xFF) { - x = gw->left + GRAPH_X_POSITION_BEGINNING; - y = gw->top + gw->height + 1; - byte month = gw->month; - Year year = gw->year; - for (int i = 0; i < gw->num_on_x_axis; i++) { - SetDParam(0, month + STR_0162_JAN); - SetDParam(1, month + STR_0162_JAN + 2); - SetDParam(2, year); - DrawString(x, y, month == 0 ? STR_016F : STR_016E, GRAPH_AXIS_LABEL_COLOUR); - - month += 3; - if (month >= 12) { - month = 0; - year++; - } - x += GRAPH_X_POSITION_SEPARATION; - } - } else { - /* Draw the label under the data point rather than on the grid line. */ - x = gw->left + GRAPH_X_POSITION_BEGINNING + (GRAPH_X_POSITION_SEPARATION / 2) + 1; - y = gw->top + gw->height + 1; - uint16 label = gw->x_values_start; - - for (int i = 0; i < gw->num_on_x_axis; i++) { - SetDParam(0, label); - DrawStringCentered(x, y, STR_01CB, GRAPH_AXIS_LABEL_COLOUR); - - label += gw->x_values_increment; - x += GRAPH_X_POSITION_SEPARATION; - } - } - - /* draw lines and dots */ - for (int i = 0; i < gw->num_dataset; i++) { - if (!HasBit(gw->excluded_data, i)) { - /* Centre the dot between the grid lines. */ - x = gw->left + GRAPH_X_POSITION_BEGINNING + (GRAPH_X_POSITION_SEPARATION / 2); - - byte color = gw->colors[i]; - uint prev_x = INVALID_DATAPOINT_POS; - uint prev_y = INVALID_DATAPOINT_POS; - - for (int j = 0; j < gw->num_on_x_axis; j++) { - OverflowSafeInt64 datapoint = gw->cost[i][j]; - - if (datapoint != INVALID_DATAPOINT) { - /* - * Check whether we need to reduce the 'accuracy' of the - * datapoint value and the highest value to splut overflows. - * And when 'drawing' 'one million' or 'one million and one' - * there is no significant difference, so the least - * significant bits can just be removed. - * - * If there are more bits needed than would fit in a 32 bits - * integer, so at about 31 bits because of the sign bit, the - * least significant bits are removed. - */ - int mult_range = FindLastBit(x_axis_offset) + FindLastBit(abs(datapoint)); - int reduce_range = max(mult_range - 31, 0); - - /* Handle negative values differently (don't shift sign) */ - if (datapoint < 0) { - datapoint = -(abs(datapoint) >> reduce_range); - } else { - datapoint >>= reduce_range; - } - - y = gw->top + x_axis_offset - (x_axis_offset * datapoint) / (highest_value >> reduce_range); - - /* Draw the point. */ - GfxFillRect(x - 1, y - 1, x + 1, y + 1, color); - - /* Draw the line connected to the previous point. */ - if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, color); - - prev_x = x; - prev_y = y; - } else { - prev_x = INVALID_DATAPOINT_POS; - prev_y = INVALID_DATAPOINT_POS; - } - - x += GRAPH_X_POSITION_SEPARATION; - } - } - } -} - /****************/ /* GRAPH LEGEND */ /****************/ -static void GraphLegendWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - for (uint i = 3; i < w->widget_count; i++) { - if (!HasBit(_legend_excluded_players, i - 3)) w->LowerWidget(i); - } - break; - - case WE_PAINT: { - const Player *p; - - FOR_ALL_PLAYERS(p) { - if (p->is_active) continue; - - SetBit(_legend_excluded_players, p->index); - w->RaiseWidget(p->index + 3); - } - - DrawWindowWidgets(w); - - FOR_ALL_PLAYERS(p) { - if (!p->is_active) continue; - - DrawPlayerIcon(p->index, 4, 18 + p->index * 12); - - SetDParam(0, p->index); - SetDParam(1, p->index); - DrawString(21, 17 + p->index * 12, STR_7021, HasBit(_legend_excluded_players, p->index) ? TC_BLACK : TC_WHITE); - } - break; +struct GraphLegendWindow : Window { + GraphLegendWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + for (uint i = 3; i < this->widget_count; i++) { + if (!HasBit(_legend_excluded_players, i - 3)) this->LowerWidget(i); } - case WE_CLICK: - if (!IsInsideMM(e->we.click.widget, 3, 11)) return; + this->FindWindowPlacementAndResize(desc); + } - ToggleBit(_legend_excluded_players, e->we.click.widget - 3); - w->ToggleWidgetLoweredState(e->we.click.widget); - w->SetDirty(); - InvalidateWindow(WC_INCOME_GRAPH, 0); - InvalidateWindow(WC_OPERATING_PROFIT, 0); - InvalidateWindow(WC_DELIVERED_CARGO, 0); - InvalidateWindow(WC_PERFORMANCE_HISTORY, 0); - InvalidateWindow(WC_COMPANY_VALUE, 0); - break; + virtual void OnPaint() + { + const Player *p; + + FOR_ALL_PLAYERS(p) { + if (p->is_active) continue; + + SetBit(_legend_excluded_players, p->index); + this->RaiseWidget(p->index + 3); + } + + this->DrawWidgets(); + + FOR_ALL_PLAYERS(p) { + if (!p->is_active) continue; + + DrawPlayerIcon(p->index, 4, 18 + p->index * 12); + + SetDParam(0, p->index); + SetDParam(1, p->index); + DrawString(21, 17 + p->index * 12, STR_7021, HasBit(_legend_excluded_players, p->index) ? TC_BLACK : TC_WHITE); + } } -} + + virtual void OnClick(Point pt, int widget) + { + if (!IsInsideMM(widget, 3, 11)) return; + + ToggleBit(_legend_excluded_players, widget - 3); + this->ToggleWidgetLoweredState(widget); + this->SetDirty(); + InvalidateWindow(WC_INCOME_GRAPH, 0); + InvalidateWindow(WC_OPERATING_PROFIT, 0); + InvalidateWindow(WC_DELIVERED_CARGO, 0); + InvalidateWindow(WC_PERFORMANCE_HISTORY, 0); + InvalidateWindow(WC_COMPANY_VALUE, 0); + } +}; static const Widget _graph_legend_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -334,90 +101,332 @@ WC_GRAPH_LEGEND, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _graph_legend_widgets, - GraphLegendWndProc }; static void ShowGraphLegend() { - AllocateWindowDescFront(&_graph_legend_desc, 0); + AllocateWindowDescFront(&_graph_legend_desc, 0); } +/******************/ +/* BASE OF GRAPHS */ +/*****************/ + +struct BaseGraphWindow : Window { +protected: + enum { + GRAPH_MAX_DATASETS = 32, + GRAPH_AXIS_LABEL_COLOUR = TC_BLACK, + GRAPH_AXIS_LINE_COLOUR = 215, + + GRAPH_X_POSITION_BEGINNING = 44, ///< Start the graph 44 pixels from gd_left + GRAPH_X_POSITION_SEPARATION = 22, ///< There are 22 pixels between each X value + + GRAPH_NUM_LINES_Y = 9, ///< How many horizontal lines to draw. + /* 9 is convenient as that means the distance between them is the gd_height of the graph / 8, + * which is the same + * as height >> 3. */ + }; + + uint excluded_data; ///< bitmask of the datasets that shouldn't be displayed. + byte num_dataset; + byte num_on_x_axis; + bool has_negative_values; + byte num_vert_lines; + + /* The starting month and year that values are plotted against. If month is + * 0xFF, use x_values_start and x_values_increment below instead. */ + byte month; + Year year; + + /* These values are used if the graph is being plotted against values + * rather than the dates specified by month and year. */ + uint16 x_values_start; + uint16 x_values_increment; + + int gd_left, gd_top; ///< Where to start drawing the graph, in pixels. + uint gd_height; ///< The height of the graph in pixels. + StringID format_str_y_axis; + byte colors[GRAPH_MAX_DATASETS]; + OverflowSafeInt64 cost[GRAPH_MAX_DATASETS][24]; ///< last 2 years + + void DrawGraph() const + { + uint x, y; ///< Reused whenever x and y coordinates are needed. + OverflowSafeInt64 highest_value; ///< Highest value to be drawn. + int x_axis_offset; ///< Distance from the top of the graph to the x axis. + + /* the colors and cost array of GraphDrawer must accomodate + * both values for cargo and players. So if any are higher, quit */ + assert(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_PLAYERS); + assert(this->num_vert_lines > 0); + + byte grid_colour = _colour_gradient[14][4]; + + /* The coordinates of the opposite edges of the graph. */ + int bottom = this->gd_top + this->gd_height - 1; + int right = this->gd_left + GRAPH_X_POSITION_BEGINNING + this->num_vert_lines * GRAPH_X_POSITION_SEPARATION - 1; + + /* Draw the vertical grid lines. */ + + /* Don't draw the first line, as that's where the axis will be. */ + x = this->gd_left + GRAPH_X_POSITION_BEGINNING + GRAPH_X_POSITION_SEPARATION; + + for (int i = 0; i < this->num_vert_lines; i++) { + GfxFillRect(x, this->gd_top, x, bottom, grid_colour); + x += GRAPH_X_POSITION_SEPARATION; + } + + /* Draw the horizontal grid lines. */ + x = this->gd_left + GRAPH_X_POSITION_BEGINNING; + y = this->gd_height + this->gd_top; + + for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) { + GfxFillRect(x, y, right, y, grid_colour); + y -= (this->gd_height / (GRAPH_NUM_LINES_Y - 1)); + } + + /* Draw the y axis. */ + GfxFillRect(x, this->gd_top, x, bottom, GRAPH_AXIS_LINE_COLOUR); + + /* Find the distance from the gd_top of the graph to the x axis. */ + x_axis_offset = this->gd_height; + + /* The graph is currently symmetrical about the x axis. */ + if (this->has_negative_values) x_axis_offset /= 2; + + /* Draw the x axis. */ + y = x_axis_offset + this->gd_top; + GfxFillRect(x, y, right, y, GRAPH_AXIS_LINE_COLOUR); + + /* Find the largest value that will be drawn. */ + if (this->num_on_x_axis == 0) + return; + + assert(this->num_on_x_axis > 0); + assert(this->num_dataset > 0); + + /* Start of with a value of twice the gd_height of the graph in pixels. It's a + * bit arbitrary, but it makes the cargo payment graph look a little nicer, + * and prevents division by zero when calculating where the datapoint + * should be drawn. */ + highest_value = x_axis_offset * 2; + + for (int i = 0; i < this->num_dataset; i++) { + if (!HasBit(this->excluded_data, i)) { + for (int j = 0; j < this->num_on_x_axis; j++) { + OverflowSafeInt64 datapoint = this->cost[i][j]; + + if (datapoint != INVALID_DATAPOINT) { + /* For now, if the graph has negative values the scaling is + * symmetrical about the x axis, so take the absolute value + * of each data point. */ + highest_value = max(highest_value, abs(datapoint)); + } + } + } + } + + /* Round up highest_value so that it will divide cleanly into the number of + * axis labels used. */ + int round_val = highest_value % (GRAPH_NUM_LINES_Y - 1); + if (round_val != 0) highest_value += (GRAPH_NUM_LINES_Y - 1 - round_val); + + /* draw text strings on the y axis */ + int64 y_label = highest_value; + int64 y_label_separation = highest_value / (GRAPH_NUM_LINES_Y - 1); + + /* If there are negative values, the graph goes from highest_value to + * -highest_value, not highest_value to 0. */ + if (this->has_negative_values) y_label_separation *= 2; + + x = this->gd_left + GRAPH_X_POSITION_BEGINNING + 1; + y = this->gd_top - 3; + + for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) { + SetDParam(0, this->format_str_y_axis); + SetDParam(1, y_label); + DrawStringRightAligned(x, y, STR_0170, GRAPH_AXIS_LABEL_COLOUR); + + y_label -= y_label_separation; + y += (this->gd_height / (GRAPH_NUM_LINES_Y - 1)); + } + + /* draw strings on the x axis */ + if (this->month != 0xFF) { + x = this->gd_left + GRAPH_X_POSITION_BEGINNING; + y = this->gd_top + this->gd_height + 1; + byte month = this->month; + Year year = this->year; + for (int i = 0; i < this->num_on_x_axis; i++) { + SetDParam(0, month + STR_0162_JAN); + SetDParam(1, month + STR_0162_JAN + 2); + SetDParam(2, year); + DrawString(x, y, month == 0 ? STR_016F : STR_016E, GRAPH_AXIS_LABEL_COLOUR); + + month += 3; + if (month >= 12) { + month = 0; + year++; + } + x += GRAPH_X_POSITION_SEPARATION; + } + } else { + /* Draw the label under the data point rather than on the grid line. */ + x = this->gd_left + GRAPH_X_POSITION_BEGINNING + (GRAPH_X_POSITION_SEPARATION / 2) + 1; + y = this->gd_top + this->gd_height + 1; + uint16 label = this->x_values_start; + + for (int i = 0; i < this->num_on_x_axis; i++) { + SetDParam(0, label); + DrawStringCentered(x, y, STR_01CB, GRAPH_AXIS_LABEL_COLOUR); + + label += this->x_values_increment; + x += GRAPH_X_POSITION_SEPARATION; + } + } + + /* draw lines and dots */ + for (int i = 0; i < this->num_dataset; i++) { + if (!HasBit(this->excluded_data, i)) { + /* Centre the dot between the grid lines. */ + x = this->gd_left + GRAPH_X_POSITION_BEGINNING + (GRAPH_X_POSITION_SEPARATION / 2); + + byte color = this->colors[i]; + uint prev_x = INVALID_DATAPOINT_POS; + uint prev_y = INVALID_DATAPOINT_POS; + + for (int j = 0; j < this->num_on_x_axis; j++) { + OverflowSafeInt64 datapoint = this->cost[i][j]; + + if (datapoint != INVALID_DATAPOINT) { + /* + * Check whether we need to reduce the 'accuracy' of the + * datapoint value and the highest value to splut overflows. + * And when 'drawing' 'one million' or 'one million and one' + * there is no significant difference, so the least + * significant bits can just be removed. + * + * If there are more bits needed than would fit in a 32 bits + * integer, so at about 31 bits because of the sign bit, the + * least significant bits are removed. + */ + int mult_range = FindLastBit(x_axis_offset) + FindLastBit(abs(datapoint)); + int reduce_range = max(mult_range - 31, 0); + + /* Handle negative values differently (don't shift sign) */ + if (datapoint < 0) { + datapoint = -(abs(datapoint) >> reduce_range); + } else { + datapoint >>= reduce_range; + } + + y = this->gd_top + x_axis_offset - (x_axis_offset * datapoint) / (highest_value >> reduce_range); + + /* Draw the point. */ + GfxFillRect(x - 1, y - 1, x + 1, y + 1, color); + + /* Draw the line connected to the previous point. */ + if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, color); + + prev_x = x; + prev_y = y; + } else { + prev_x = INVALID_DATAPOINT_POS; + prev_y = INVALID_DATAPOINT_POS; + } + + x += GRAPH_X_POSITION_SEPARATION; + } + } + } + } + + + BaseGraphWindow(const WindowDesc *desc, WindowNumber window_number, int left, + int top, int height, bool has_negative_values, StringID format_str_y_axis) : + Window(desc, window_number), has_negative_values(has_negative_values), + gd_left(left), gd_top(top), gd_height(height), format_str_y_axis(format_str_y_axis) + { + InvalidateWindow(WC_GRAPH_LEGEND, 0); + } + +public: + virtual void OnPaint() + { + this->DrawWidgets(); + + uint excluded_players = _legend_excluded_players; + + /* Exclude the players which aren't valid */ + const Player* p; + FOR_ALL_PLAYERS(p) { + if (!p->is_active) SetBit(excluded_players, p->index); + } + this->excluded_data = excluded_players; + this->num_vert_lines = 24; + + byte nums = 0; + FOR_ALL_PLAYERS(p) { + if (p->is_active) nums = max(nums, p->num_valid_stat_ent); + } + this->num_on_x_axis = min(nums, 24); + + int mo = (_cur_month / 3 - nums) * 3; + int yr = _cur_year; + while (mo < 0) { + yr--; + mo += 12; + } + + this->year = yr; + this->month = mo; + + int numd = 0; + FOR_ALL_PLAYERS(p) { + if (p->is_active) { + this->colors[numd] = _colour_gradient[p->player_color][6]; + for (int j = this->num_on_x_axis, i = 0; --j >= 0;) { + this->cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : GetGraphData(p, j); + i++; + } + } + numd++; + } + + this->num_dataset = numd; + + this->DrawGraph(); + } + + virtual OverflowSafeInt64 GetGraphData(const Player *p, int j) + { + return INVALID_DATAPOINT; + } + + virtual void OnClick(Point pt, int widget) + { + /* Clicked on legend? */ + if (widget == 2) ShowGraphLegend(); + } +}; + /********************/ /* OPERATING PROFIT */ /********************/ -static void SetupGraphDrawerForPlayers(GraphDrawer *gd) -{ - const Player* p; - uint excluded_players = _legend_excluded_players; - byte nums; - int mo, yr; - - /* Exclude the players which aren't valid */ - FOR_ALL_PLAYERS(p) { - if (!p->is_active) SetBit(excluded_players, p->index); - } - gd->excluded_data = excluded_players; - gd->num_vert_lines = 24; - - nums = 0; - FOR_ALL_PLAYERS(p) { - if (p->is_active) nums = max(nums, p->num_valid_stat_ent); - } - gd->num_on_x_axis = min(nums, 24); - - mo = (_cur_month / 3 - nums) * 3; - yr = _cur_year; - while (mo < 0) { - yr--; - mo += 12; +struct OperatingProfitGraphWindow : BaseGraphWindow { + OperatingProfitGraphWindow(const WindowDesc *desc, WindowNumber window_number) : + BaseGraphWindow(desc, window_number, 2, 18, 136, true, STR_CURRCOMPACT) + { + this->FindWindowPlacementAndResize(desc); } - gd->year = yr; - gd->month = mo; -} - -static void OperatingProfitWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - GraphDrawer gd; - const Player* p; - - DrawWindowWidgets(w); - - gd.left = 2; - gd.top = 18; - gd.height = 136; - gd.has_negative_values = true; - gd.format_str_y_axis = STR_CURRCOMPACT; - - SetupGraphDrawerForPlayers(&gd); - - int numd = 0; - FOR_ALL_PLAYERS(p) { - if (p->is_active) { - gd.colors[numd] = _colour_gradient[p->player_color][6]; - for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { - gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (p->old_economy[j].income + p->old_economy[j].expenses); - i++; - } - } - numd++; - } - - gd.num_dataset = numd; - - DrawGraph(&gd); - break; - } - - case WE_CLICK: - /* Clicked on legend? */ - if (e->we.click.widget == 2) ShowGraphLegend(); - break; + virtual OverflowSafeInt64 GetGraphData(const Player *p, int j) + { + return p->old_economy[j].income + p->old_economy[j].expenses; } -} +}; static const Widget _operating_profit_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -432,15 +441,12 @@ WC_OPERATING_PROFIT, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _operating_profit_widgets, - OperatingProfitWndProc }; void ShowOperatingProfitGraph() { - if (AllocateWindowDescFront(&_operating_profit_desc, 0)) { - InvalidateWindow(WC_GRAPH_LEGEND, 0); - } + AllocateWindowDescFront(&_operating_profit_desc, 0); } @@ -448,45 +454,18 @@ /* INCOME GRAPH */ /****************/ -static void IncomeGraphWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - GraphDrawer gd; - const Player* p; - - DrawWindowWidgets(w); - - gd.left = 2; - gd.top = 18; - gd.height = 104; - gd.has_negative_values = false; - gd.format_str_y_axis = STR_CURRCOMPACT; - SetupGraphDrawerForPlayers(&gd); +struct IncomeGraphWindow : BaseGraphWindow { + IncomeGraphWindow(const WindowDesc *desc, WindowNumber window_number) : + BaseGraphWindow(desc, window_number, 2, 18, 104, false, STR_CURRCOMPACT) + { + this->FindWindowPlacementAndResize(desc); + } - int numd = 0; - FOR_ALL_PLAYERS(p) { - if (p->is_active) { - gd.colors[numd] = _colour_gradient[p->player_color][6]; - for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { - gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].income; - i++; - } - } - numd++; - } - - gd.num_dataset = numd; - - DrawGraph(&gd); - break; - } - - case WE_CLICK: - if (e->we.click.widget == 2) ShowGraphLegend(); - break; + virtual OverflowSafeInt64 GetGraphData(const Player *p, int j) + { + return p->old_economy[j].income; } -} +}; static const Widget _income_graph_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -501,59 +480,29 @@ WC_INCOME_GRAPH, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _income_graph_widgets, - IncomeGraphWndProc }; void ShowIncomeGraph() { - if (AllocateWindowDescFront(&_income_graph_desc, 0)) { - InvalidateWindow(WC_GRAPH_LEGEND, 0); - } + AllocateWindowDescFront(&_income_graph_desc, 0); } /*******************/ /* DELIVERED CARGO */ /*******************/ -static void DeliveredCargoGraphWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - GraphDrawer gd; - const Player* p; - - DrawWindowWidgets(w); - - gd.left = 2; - gd.top = 18; - gd.height = 104; - gd.has_negative_values = false; - gd.format_str_y_axis = STR_7024; - SetupGraphDrawerForPlayers(&gd); +struct DeliveredCargoGraphWindow : BaseGraphWindow { + DeliveredCargoGraphWindow(const WindowDesc *desc, WindowNumber window_number) : + BaseGraphWindow(desc, window_number, 2, 18, 104, false, STR_7024) + { + this->FindWindowPlacementAndResize(desc); + } - int numd = 0; - FOR_ALL_PLAYERS(p) { - if (p->is_active) { - gd.colors[numd] = _colour_gradient[p->player_color][6]; - for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { - gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (OverflowSafeInt64)p->old_economy[j].delivered_cargo; - i++; - } - } - numd++; - } - - gd.num_dataset = numd; - - DrawGraph(&gd); - break; - } - - case WE_CLICK: - if (e->we.click.widget == 2) ShowGraphLegend(); - break; + virtual OverflowSafeInt64 GetGraphData(const Player *p, int j) + { + return p->old_economy[j].delivered_cargo; } -} +}; static const Widget _delivered_cargo_graph_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -568,60 +517,35 @@ WC_DELIVERED_CARGO, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _delivered_cargo_graph_widgets, - DeliveredCargoGraphWndProc }; void ShowDeliveredCargoGraph() { - if (AllocateWindowDescFront(&_delivered_cargo_graph_desc, 0)) { - InvalidateWindow(WC_GRAPH_LEGEND, 0); - } + AllocateWindowDescFront(&_delivered_cargo_graph_desc, 0); } /***********************/ /* PERFORMANCE HISTORY */ /***********************/ -static void PerformanceHistoryWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - GraphDrawer gd; - const Player* p; - - DrawWindowWidgets(w); - - gd.left = 2; - gd.top = 18; - gd.height = 200; - gd.has_negative_values = false; - gd.format_str_y_axis = STR_7024; - SetupGraphDrawerForPlayers(&gd); +struct PerformanceHistoryGraphWindow : BaseGraphWindow { + PerformanceHistoryGraphWindow(const WindowDesc *desc, WindowNumber window_number) : + BaseGraphWindow(desc, window_number, 2, 18, 200, false, STR_7024) + { + this->FindWindowPlacementAndResize(desc); + } - int numd = 0; - FOR_ALL_PLAYERS(p) { - if (p->is_active) { - gd.colors[numd] = _colour_gradient[p->player_color][6]; - for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { - gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (OverflowSafeInt64)p->old_economy[j].performance_history; - i++; - } - } - numd++; - } + virtual OverflowSafeInt64 GetGraphData(const Player *p, int j) + { + return p->old_economy[j].performance_history; + } - gd.num_dataset = numd; - - DrawGraph(&gd); - break; - } - - case WE_CLICK: - if (e->we.click.widget == 2) ShowGraphLegend(); - if (e->we.click.widget == 3) ShowPerformanceRatingDetail(); - break; + virtual void OnClick(Point pt, int widget) + { + if (widget == 3) ShowPerformanceRatingDetail(); + this->BaseGraphWindow::OnClick(pt, widget); } -} +}; static const Widget _performance_history_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -637,59 +561,29 @@ WC_PERFORMANCE_HISTORY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _performance_history_widgets, - PerformanceHistoryWndProc }; void ShowPerformanceHistoryGraph() { - if (AllocateWindowDescFront(&_performance_history_desc, 0)) { - InvalidateWindow(WC_GRAPH_LEGEND, 0); - } + AllocateWindowDescFront(&_performance_history_desc, 0); } /*****************/ /* COMPANY VALUE */ /*****************/ -static void CompanyValueGraphWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - GraphDrawer gd; - const Player* p; - - DrawWindowWidgets(w); - - gd.left = 2; - gd.top = 18; - gd.height = 200; - gd.has_negative_values = false; - gd.format_str_y_axis = STR_CURRCOMPACT; - SetupGraphDrawerForPlayers(&gd); +struct CompanyValueGraphWindow : BaseGraphWindow { + CompanyValueGraphWindow(const WindowDesc *desc, WindowNumber window_number) : + BaseGraphWindow(desc, window_number, 2, 18, 200, false, STR_CURRCOMPACT) + { + this->FindWindowPlacementAndResize(desc); + } - int numd = 0; - FOR_ALL_PLAYERS(p) { - if (p->is_active) { - gd.colors[numd] = _colour_gradient[p->player_color][6]; - for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { - gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].company_value; - i++; - } - } - numd++; - } - - gd.num_dataset = numd; - - DrawGraph(&gd); - break; - } - - case WE_CLICK: - if (e->we.click.widget == 2) ShowGraphLegend(); - break; + virtual OverflowSafeInt64 GetGraphData(const Player *p, int j) + { + return p->old_economy[j].company_value; } -} +}; static const Widget _company_value_graph_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -704,90 +598,117 @@ WC_COMPANY_VALUE, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _company_value_graph_widgets, - CompanyValueGraphWndProc }; void ShowCompanyValueGraph() { - if (AllocateWindowDescFront(&_company_value_graph_desc, 0)) { - InvalidateWindow(WC_GRAPH_LEGEND, 0); - } + AllocateWindowDescFront(&_company_value_graph_desc, 0); } /*****************/ /* PAYMENT RATES */ /*****************/ -static void CargoPaymentRatesWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - GraphDrawer gd; - - DrawWindowWidgets(w); - - int x = 495; - int y = 24; - - gd.excluded_data = _legend_excluded_cargo; - gd.left = 2; - gd.top = 24; - gd.height = w->height - 38; - gd.has_negative_values = false; - gd.format_str_y_axis = STR_CURRCOMPACT; - gd.num_on_x_axis = 20; - gd.num_vert_lines = 20; - gd.month = 0xFF; - gd.x_values_start = 10; - gd.x_values_increment = 10; - - uint i = 0; - for (CargoID c = 0; c < NUM_CARGO; c++) { - const CargoSpec *cs = GetCargo(c); - if (!cs->IsValid()) continue; - - /* Only draw labels for widgets that exist. If the widget doesn't - * exist then the local player has used the climate cheat or - * changed the NewGRF configuration with this window open. */ - if (i + 3 < w->widget_count) { - /* Since the buttons have no text, no images, - * both the text and the colored box have to be manually painted. - * clk_dif will move one pixel down and one pixel to the right - * when the button is clicked */ - byte clk_dif = w->IsWidgetLowered(i + 3) ? 1 : 0; - - GfxFillRect(x + clk_dif, y + clk_dif, x + 8 + clk_dif, y + 5 + clk_dif, 0); - GfxFillRect(x + 1 + clk_dif, y + 1 + clk_dif, x + 7 + clk_dif, y + 4 + clk_dif, cs->legend_colour); - SetDParam(0, cs->name); - DrawString(x + 14 + clk_dif, y + clk_dif, STR_7065, TC_FROMSTRING); - y += 8; - } - - gd.colors[i] = cs->legend_colour; - for (uint j = 0; j != 20; j++) { - gd.cost[i][j] = GetTransportedGoodsIncome(10, 20, j * 6 + 6, c); - } - - i++; - } - gd.num_dataset = i; - - DrawGraph(&gd); - - DrawString(2 + 46, 24 + gd.height + 7, STR_7062_DAYS_IN_TRANSIT, TC_FROMSTRING); - DrawString(2 + 84, 24 - 9, STR_7063_PAYMENT_FOR_DELIVERING, TC_FROMSTRING); - break; +struct PaymentRatesGraphWindow : BaseGraphWindow { + PaymentRatesGraphWindow(const WindowDesc *desc, WindowNumber window_number) : + BaseGraphWindow(desc, window_number, 2, 24, 200, false, STR_CURRCOMPACT) + { + uint num_active = 0; + for (CargoID c = 0; c < NUM_CARGO; c++) { + if (GetCargo(c)->IsValid()) num_active++; } - case WE_CLICK: - if (e->we.click.widget >= 3) { - ToggleBit(_legend_excluded_cargo, e->we.click.widget - 3); - w->ToggleWidgetLoweredState(e->we.click.widget); - w->SetDirty(); + /* Resize the window to fit the cargo types */ + ResizeWindow(this, 0, max(num_active, 12U) * 8); + + /* Add widgets for each cargo type */ + this->widget_count += num_active; + this->widget = ReallocT(this->widget, this->widget_count + 1); + this->widget[this->widget_count].type = WWT_LAST; + + /* Set the properties of each widget */ + for (uint i = 0; i != num_active; i++) { + Widget *wi = &this->widget[3 + i]; + wi->type = WWT_PANEL; + wi->display_flags = RESIZE_NONE; + wi->color = 12; + wi->left = 493; + wi->right = 562; + wi->top = 24 + i * 8; + wi->bottom = wi->top + 7; + wi->data = 0; + wi->tooltips = STR_7064_TOGGLE_GRAPH_FOR_CARGO; + + if (!HasBit(_legend_excluded_cargo, i)) this->LowerWidget(i + 3); + } + + this->SetDirty(); + + this->gd_height = this->height - 38; + this->num_on_x_axis = 20; + this->num_vert_lines = 20; + this->month = 0xFF; + this->x_values_start = 10; + this->x_values_increment = 10; + + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { + this->DrawWidgets(); + + this->excluded_data = _legend_excluded_cargo; + + int x = 495; + int y = 24; + + uint i = 0; + for (CargoID c = 0; c < NUM_CARGO; c++) { + const CargoSpec *cs = GetCargo(c); + if (!cs->IsValid()) continue; + + /* Only draw labels for widgets that exist. If the widget doesn't + * exist then the local player has used the climate cheat or + * changed the NewGRF configuration with this window open. */ + if (i + 3 < this->widget_count) { + /* Since the buttons have no text, no images, + * both the text and the colored box have to be manually painted. + * clk_dif will move one pixel down and one pixel to the right + * when the button is clicked */ + byte clk_dif = this->IsWidgetLowered(i + 3) ? 1 : 0; + + GfxFillRect(x + clk_dif, y + clk_dif, x + 8 + clk_dif, y + 5 + clk_dif, 0); + GfxFillRect(x + 1 + clk_dif, y + 1 + clk_dif, x + 7 + clk_dif, y + 4 + clk_dif, cs->legend_colour); + SetDParam(0, cs->name); + DrawString(x + 14 + clk_dif, y + clk_dif, STR_7065, TC_FROMSTRING); + y += 8; } - break; + + this->colors[i] = cs->legend_colour; + for (uint j = 0; j != 20; j++) { + this->cost[i][j] = GetTransportedGoodsIncome(10, 20, j * 6 + 6, c); + } + + i++; + } + this->num_dataset = i; + + this->DrawGraph(); + + DrawString(2 + 46, 24 + this->gd_height + 7, STR_7062_DAYS_IN_TRANSIT, TC_FROMSTRING); + DrawString(2 + 84, 24 - 9, STR_7063_PAYMENT_FOR_DELIVERING, TC_FROMSTRING); } -} + + virtual void OnClick(Point pt, int widget) + { + if (widget >= 3) { + ToggleBit(_legend_excluded_cargo, widget - 3); + this->ToggleWidgetLoweredState(widget); + this->SetDirty(); + } + } +}; static const Widget _cargo_payment_rates_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -801,46 +722,12 @@ WC_PAYMENT_RATES, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _cargo_payment_rates_widgets, - CargoPaymentRatesWndProc }; void ShowCargoPaymentRates() { - Window *w = AllocateWindowDescFront(&_cargo_payment_rates_desc, 0); - if (w == NULL) return; - - /* Count the number of active cargo types */ - uint num_active = 0; - for (CargoID c = 0; c < NUM_CARGO; c++) { - if (GetCargo(c)->IsValid()) num_active++; - } - - /* Resize the window to fit the cargo types */ - ResizeWindow(w, 0, max(num_active, 12U) * 8); - - /* Add widgets for each cargo type */ - w->widget_count += num_active; - w->widget = ReallocT(w->widget, w->widget_count + 1); - w->widget[w->widget_count].type = WWT_LAST; - - /* Set the properties of each widget */ - for (uint i = 0; i != num_active; i++) { - Widget *wi = &w->widget[3 + i]; - wi->type = WWT_PANEL; - wi->display_flags = RESIZE_NONE; - wi->color = 12; - wi->left = 493; - wi->right = 562; - wi->top = 24 + i * 8; - wi->bottom = wi->top + 7; - wi->data = 0; - wi->tooltips = STR_7064_TOGGLE_GRAPH_FOR_CARGO; - - if (!HasBit(_legend_excluded_cargo, i)) w->LowerWidget(i + 3); - } - - w->SetDirty(); + AllocateWindowDescFront(&_cargo_payment_rates_desc, 0); } /************************/ @@ -879,35 +766,35 @@ return p2->old_economy[1].performance_history - p1->old_economy[1].performance_history; } -static void CompanyLeagueWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - const Player* plist[MAX_PLAYERS]; - const Player* p; - - DrawWindowWidgets(w); - - uint pl_num = 0; - FOR_ALL_PLAYERS(p) if (p->is_active) plist[pl_num++] = p; +struct CompanyLeagueWindow : Window { + CompanyLeagueWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + } - qsort((void*)plist, pl_num, sizeof(*plist), PerfHistComp); + virtual void OnPaint() + { + const Player *plist[MAX_PLAYERS]; + const Player *p; - for (uint i = 0; i != pl_num; i++) { - p = plist[i]; - SetDParam(0, i + STR_01AC_1ST); - SetDParam(1, p->index); - SetDParam(2, p->index); - SetDParam(3, GetPerformanceTitleFromValue(p->old_economy[1].performance_history)); + this->DrawWidgets(); - DrawString(2, 15 + i * 10, i == 0 ? STR_7054 : STR_7055, TC_FROMSTRING); - DrawPlayerIcon(p->index, 27, 16 + i * 10); - } + uint pl_num = 0; + FOR_ALL_PLAYERS(p) if (p->is_active) plist[pl_num++] = p; - break; + qsort((void*)plist, pl_num, sizeof(*plist), PerfHistComp); + + for (uint i = 0; i != pl_num; i++) { + p = plist[i]; + SetDParam(0, i + STR_01AC_1ST); + SetDParam(1, p->index); + SetDParam(2, p->index); + SetDParam(3, GetPerformanceTitleFromValue(p->old_economy[1].performance_history)); + + DrawString(2, 15 + i * 10, i == 0 ? STR_7054 : STR_7055, TC_FROMSTRING); + DrawPlayerIcon(p->index, 27, 16 + i * 10); } } -} +}; static const Widget _company_league_widgets[] = { @@ -923,208 +810,204 @@ WC_COMPANY_LEAGUE, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _company_league_widgets, - CompanyLeagueWndProc }; void ShowCompanyLeagueTable() { - AllocateWindowDescFront(&_company_league_desc, 0); + AllocateWindowDescFront(&_company_league_desc, 0); } /*****************************/ /* PERFORMANCE RATING DETAIL */ /*****************************/ -static void PerformanceRatingDetailWndProc(Window *w, WindowEvent *e) -{ - static PlayerID _performance_rating_detail_player = INVALID_PLAYER; - - switch (e->event) { - case WE_PAINT: { - byte x; - uint16 y = 14; - int total_score = 0; - int color_done, color_notdone; - - /* Draw standard stuff */ - DrawWindowWidgets(w); - - /* Check if the currently selected player is still active. */ - if (_performance_rating_detail_player == INVALID_PLAYER || !GetPlayer(_performance_rating_detail_player)->is_active) { - if (_performance_rating_detail_player != INVALID_PLAYER) { - /* Raise and disable the widget for the previous selection. */ - w->RaiseWidget(_performance_rating_detail_player + 13); - w->DisableWidget(_performance_rating_detail_player + 13); - w->SetDirty(); - - _performance_rating_detail_player = INVALID_PLAYER; - } - - for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { - if (GetPlayer(i)->is_active) { - /* Lower the widget corresponding to this player. */ - w->LowerWidget(i + 13); - w->SetDirty(); +struct PerformanceRatingDetailWindow : Window { + static PlayerID player; + int timeout; - _performance_rating_detail_player = i; - break; - } - } - } - - /* If there are no active players, don't display anything else. */ - if (_performance_rating_detail_player == INVALID_PLAYER) break; + PerformanceRatingDetailWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + /* Disable the players who are not active */ + for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { + this->SetWidgetDisabledState(i + 13, !GetPlayer(i)->is_active); + } - /* Paint the player icons */ - for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { - if (!GetPlayer(i)->is_active) { - /* Check if we have the player as an active player */ - if (!w->IsWidgetDisabled(i + 13)) { - /* Bah, player gone :( */ - w->DisableWidget(i + 13); + this->UpdatePlayerStats(); - /* We need a repaint */ - w->SetDirty(); - } - continue; - } + if (player != INVALID_PLAYER) this->LowerWidget(player + 13); - /* Check if we have the player marked as inactive */ - if (w->IsWidgetDisabled(i + 13)) { - /* New player! Yippie :p */ - w->EnableWidget(i + 13); - /* We need a repaint */ - w->SetDirty(); - } + this->FindWindowPlacementAndResize(desc); + } - x = (i == _performance_rating_detail_player) ? 1 : 0; - DrawPlayerIcon(i, i * 37 + 13 + x, 16 + x); + void UpdatePlayerStats() + { + /* Update all player stats with the current data + * (this is because _score_info is not saved to a savegame) */ + Player *p; + FOR_ALL_PLAYERS(p) { + if (p->is_active) UpdateCompanyRatingAndValue(p, false); + } + + this->timeout = DAY_TICKS * 5; + + } + + virtual void OnPaint() + { + byte x; + uint16 y = 14; + int total_score = 0; + int color_done, color_notdone; + + /* Draw standard stuff */ + this->DrawWidgets(); + + /* Check if the currently selected player is still active. */ + if (player == INVALID_PLAYER || !GetPlayer(player)->is_active) { + if (player != INVALID_PLAYER) { + /* Raise and disable the widget for the previous selection. */ + this->RaiseWidget(player + 13); + this->DisableWidget(player + 13); + this->SetDirty(); + + player = INVALID_PLAYER; } - /* The colors used to show how the progress is going */ - color_done = _colour_gradient[COLOUR_GREEN][4]; - color_notdone = _colour_gradient[COLOUR_RED][4]; - - /* Draw all the score parts */ - for (ScoreID i = SCORE_BEGIN; i < SCORE_END; i++) { - int val = _score_part[_performance_rating_detail_player][i]; - int needed = _score_info[i].needed; - int score = _score_info[i].score; - - y += 20; - /* SCORE_TOTAL has his own rulez ;) */ - if (i == SCORE_TOTAL) { - needed = total_score; - score = SCORE_MAX; - } else { - total_score += score; - } - - DrawString(7, y, STR_PERFORMANCE_DETAIL_VEHICLES + i, TC_FROMSTRING); - - /* Draw the score */ - SetDParam(0, score); - DrawStringRightAligned(107, y, SET_PERFORMANCE_DETAIL_INT, TC_FROMSTRING); - - /* Calculate the %-bar */ - x = Clamp(val, 0, needed) * 50 / needed; - - /* SCORE_LOAN is inversed */ - if (val < 0 && i == SCORE_LOAN) x = 0; + for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { + if (GetPlayer(i)->is_active) { + /* Lower the widget corresponding to this player. */ + this->LowerWidget(i + 13); + this->SetDirty(); - /* Draw the bar */ - if (x != 0) GfxFillRect(112, y - 2, 112 + x, y + 10, color_done); - if (x != 50) GfxFillRect(112 + x, y - 2, 112 + 50, y + 10, color_notdone); - - /* Calculate the % */ - x = Clamp(val, 0, needed) * 100 / needed; - - /* SCORE_LOAN is inversed */ - if (val < 0 && i == SCORE_LOAN) x = 0; - - /* Draw it */ - SetDParam(0, x); - DrawStringCentered(137, y, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING); - - /* SCORE_LOAN is inversed */ - if (i == SCORE_LOAN) val = needed - val; - - /* Draw the amount we have against what is needed - * For some of them it is in currency format */ - SetDParam(0, val); - SetDParam(1, needed); - switch (i) { - case SCORE_MIN_PROFIT: - case SCORE_MIN_INCOME: - case SCORE_MAX_INCOME: - case SCORE_MONEY: - case SCORE_LOAN: - DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, TC_FROMSTRING); - break; - default: - DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_INT, TC_FROMSTRING); + player = i; + break; } } - - break; } - case WE_CLICK: - /* Check which button is clicked */ - if (IsInsideMM(e->we.click.widget, 13, 21)) { - /* Is it no on disable? */ - if (!w->IsWidgetDisabled(e->we.click.widget)) { - w->RaiseWidget(_performance_rating_detail_player + 13); - _performance_rating_detail_player = (PlayerID)(e->we.click.widget - 13); - w->LowerWidget(_performance_rating_detail_player + 13); - w->SetDirty(); + /* If there are no active players, don't display anything else. */ + if (player == INVALID_PLAYER) return; + + /* Paint the player icons */ + for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { + if (!GetPlayer(i)->is_active) { + /* Check if we have the player as an active player */ + if (!this->IsWidgetDisabled(i + 13)) { + /* Bah, player gone :( */ + this->DisableWidget(i + 13); + + /* We need a repaint */ + this->SetDirty(); } - } - break; - - case WE_CREATE: { - Player *p2; - - /* Disable the players who are not active */ - for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { - w->SetWidgetDisabledState(i + 13, !GetPlayer(i)->is_active); - } - /* Update all player stats with the current data - * (this is because _score_info is not saved to a savegame) */ - FOR_ALL_PLAYERS(p2) { - if (p2->is_active) UpdateCompanyRatingAndValue(p2, false); + continue; } - w->custom[0] = DAY_TICKS; - w->custom[1] = 5; + /* Check if we have the player marked as inactive */ + if (this->IsWidgetDisabled(i + 13)) { + /* New player! Yippie :p */ + this->EnableWidget(i + 13); + /* We need a repaint */ + this->SetDirty(); + } - if (_performance_rating_detail_player != INVALID_PLAYER) w->LowerWidget(_performance_rating_detail_player + 13); - w->SetDirty(); - - break; + x = (i == player) ? 1 : 0; + DrawPlayerIcon(i, i * 37 + 13 + x, 16 + x); } - case WE_TICK: - if (_pause_game != 0) break; + /* The colors used to show how the progress is going */ + color_done = _colour_gradient[COLOUR_GREEN][4]; + color_notdone = _colour_gradient[COLOUR_RED][4]; - /* Update the player score every 5 days */ - if (--w->custom[0] == 0) { - w->custom[0] = DAY_TICKS; - if (--w->custom[1] == 0) { - Player *p2; + /* Draw all the score parts */ + for (ScoreID i = SCORE_BEGIN; i < SCORE_END; i++) { + int val = _score_part[player][i]; + int needed = _score_info[i].needed; + int score = _score_info[i].score; - w->custom[1] = 5; - FOR_ALL_PLAYERS(p2) { - /* Skip if player is not active */ - if (p2->is_active) UpdateCompanyRatingAndValue(p2, false); - } - w->SetDirty(); - } + y += 20; + /* SCORE_TOTAL has his own rulez ;) */ + if (i == SCORE_TOTAL) { + needed = total_score; + score = SCORE_MAX; + } else { + total_score += score; } - break; + DrawString(7, y, STR_PERFORMANCE_DETAIL_VEHICLES + i, TC_FROMSTRING); + + /* Draw the score */ + SetDParam(0, score); + DrawStringRightAligned(107, y, SET_PERFORMANCE_DETAIL_INT, TC_FROMSTRING); + + /* Calculate the %-bar */ + x = Clamp(val, 0, needed) * 50 / needed; + + /* SCORE_LOAN is inversed */ + if (val < 0 && i == SCORE_LOAN) x = 0; + + /* Draw the bar */ + if (x != 0) GfxFillRect(112, y - 2, 112 + x, y + 10, color_done); + if (x != 50) GfxFillRect(112 + x, y - 2, 112 + 50, y + 10, color_notdone); + + /* Calculate the % */ + x = Clamp(val, 0, needed) * 100 / needed; + + /* SCORE_LOAN is inversed */ + if (val < 0 && i == SCORE_LOAN) x = 0; + + /* Draw it */ + SetDParam(0, x); + DrawStringCentered(137, y, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING); + + /* SCORE_LOAN is inversed */ + if (i == SCORE_LOAN) val = needed - val; + + /* Draw the amount we have against what is needed + * For some of them it is in currency format */ + SetDParam(0, val); + SetDParam(1, needed); + switch (i) { + case SCORE_MIN_PROFIT: + case SCORE_MIN_INCOME: + case SCORE_MAX_INCOME: + case SCORE_MONEY: + case SCORE_LOAN: + DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, TC_FROMSTRING); + break; + default: + DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_INT, TC_FROMSTRING); + } + } } -} + + virtual void OnClick(Point pt, int widget) + { + /* Check which button is clicked */ + if (IsInsideMM(widget, 13, 21)) { + /* Is it no on disable? */ + if (!this->IsWidgetDisabled(widget)) { + this->RaiseWidget(player + 13); + player = (PlayerID)(widget - 13); + this->LowerWidget(player + 13); + this->SetDirty(); + } + } + } + + virtual void OnTick() + { + if (_pause_game != 0) return; + + /* Update the player score every 5 days */ + if (--this->timeout == 0) { + this->UpdatePlayerStats(); + this->SetDirty(); + } + } +}; + +PlayerID PerformanceRatingDetailWindow::player = INVALID_PLAYER; + static const Widget _performance_rating_detail_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -1158,10 +1041,9 @@ WC_PERFORMANCE_DETAIL, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _performance_rating_detail_widgets, - PerformanceRatingDetailWndProc }; void ShowPerformanceRatingDetail() { - AllocateWindowDescFront(&_performance_rating_detail_desc, 0); + AllocateWindowDescFront(&_performance_rating_detail_desc, 0); } diff -r 6c4314786d68 -r 8cbdb511a674 src/group_gui.cpp --- a/src/group_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/group_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -187,9 +187,10 @@ VehicleID vehicle_sel; GUIGroupList groups; - VehicleGroupWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, NULL, window_number) + VehicleGroupWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) { const PlayerID owner = (PlayerID)GB(this->window_number, 0, 8); + this->vehicle_type = (VehicleType)GB(this->window_number, 11, 5); this->caption_color = owner; this->hscroll.cap = 224; @@ -222,8 +223,6 @@ case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break; } - this->vehicle_type = (VehicleType)GB(this->window_number, 11, 5); - this->vehicles.sort_list = NULL; this->vehicles.sort_type = this->sorting->criteria; this->vehicles.flags = VL_REBUILD | (this->sorting->order ? VL_DESC : VL_NONE); @@ -287,8 +286,8 @@ virtual void OnInvalidateData(int data) { - this->vehicles.flags |= VL_REBUILD; - this->groups.flags |= VL_REBUILD; + this->vehicles.flags |= (data == 0 ? VL_REBUILD : VL_RESORT); + this->groups.flags |= (data == 0 ? VL_REBUILD : VL_RESORT); if (!(IsAllGroupID(this->group_sel) || IsDefaultGroupID(this->group_sel) || IsValidGroupID(this->group_sel))) { this->group_sel = ALL_GROUP; HideDropDownMenu(this); @@ -404,7 +403,7 @@ /* Set text of sort by dropdown */ this->widget[GRP_WIDGET_SORT_BY_DROPDOWN].data = _vehicle_sort_listing[this->vehicles.sort_type]; - DrawWindowWidgets(this); + this->DrawWidgets(); /* Draw Matrix Group * The selected group is drawn in white */ @@ -452,7 +451,7 @@ DrawStringRightAligned(187, y1 + 1, STR_GROUP_TINY_NUM, (this->group_sel == g->index) ? TC_WHITE : TC_BLACK); } - DrawSortButtonState(this, GRP_WIDGET_SORT_BY_ORDER, this->vehicles.flags & VL_DESC ? SBS_DOWN : SBS_UP); + this->DrawSortButtonState(GRP_WIDGET_SORT_BY_ORDER, this->vehicles.flags & VL_DESC ? SBS_DOWN : SBS_UP); /* Draw Matrix Vehicle according to the vehicle list built before */ max = min(this->vscroll2.pos + this->vscroll2.cap, this->vehicles.list_length); @@ -758,7 +757,6 @@ WC_TRAINS_LIST, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _group_widgets, - NULL }; void ShowPlayerGroup(PlayerID player, VehicleType vehicle_type) diff -r 6c4314786d68 -r 8cbdb511a674 src/gui.h --- a/src/gui.h Mon May 19 14:14:33 2008 +0000 +++ b/src/gui.h Mon May 19 15:13:58 2008 +0000 @@ -52,7 +52,6 @@ void PlaceLandBlockInfo(); void ShowAboutWindow(); void ShowBuildTreesToolbar(); -void ShowBuildTreesScenToolbar(); void ShowTownDirectory(); void ShowIndustryDirectory(); void ShowSubsidiesList(); diff -r 6c4314786d68 -r 8cbdb511a674 src/industry.h --- a/src/industry.h Mon May 19 14:14:33 2008 +0000 +++ b/src/industry.h Mon May 19 15:13:58 2008 +0000 @@ -354,9 +354,6 @@ #define FOR_ALL_INDUSTRIES_FROM(i, start) for (i = GetIndustry(start); i != NULL; i = (i->index + 1U < GetIndustryPoolSize()) ? GetIndustry(i->index + 1U) : NULL) if (i->IsValid()) #define FOR_ALL_INDUSTRIES(i) FOR_ALL_INDUSTRIES_FROM(i, 0) -extern const Industry **_industry_sort; -extern bool _industry_sort_dirty; - static const uint8 IT_INVALID = 255; #endif /* INDUSTRY_H */ diff -r 6c4314786d68 -r 8cbdb511a674 src/industry_cmd.cpp --- a/src/industry_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/industry_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -55,9 +55,6 @@ int _total_industries; //general counter uint16 _industry_counts[NUM_INDUSTRYTYPES]; // Number of industries per type ingame -const Industry **_industry_sort; -bool _industry_sort_dirty; - IndustrySpec _industry_specs[NUM_INDUSTRYTYPES]; IndustryTileSpec _industry_tile_specs[NUM_INDUSTRYTILES]; @@ -168,12 +165,11 @@ } END_TILE_LOOP(tile_cur, 42, 42, this->xy - TileDiff(21, 21)) } - _industry_sort_dirty = true; DecIndustryTypeCount(this->type); DeleteSubsidyWithIndustry(this->index); DeleteWindowById(WC_INDUSTRY_VIEW, this->index); - InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); + InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0); this->xy = 0; } @@ -1556,8 +1552,7 @@ if (GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_PLANT_ON_BUILT) { for (j = 0; j != 50; j++) PlantRandomFarmField(i); } - _industry_sort_dirty = true; - InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); + InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0); } /** Helper function for Build/Fund an industry @@ -1651,7 +1646,7 @@ SetDParam(1, ind->town->index); } AddNewsItem(indspec->new_industry_text, - NM_THIN, NF_VIEWPORT | NF_TILE, NT_OPENCLOSE, DNC_NONE, ind->xy, 0); + NS_OPENCLOSE, ind->xy, 0); break; } } @@ -1870,7 +1865,7 @@ SetDParam(1, ind->town->index); } AddNewsItem(ind_spc->new_industry_text, - NM_THIN, NF_VIEWPORT | NF_TILE, NT_OPENCLOSE, DNC_NONE, ind->xy, 0); + NS_OPENCLOSE, ind->xy, 0); } /** @@ -2002,12 +1997,12 @@ */ static void ReportNewsProductionChangeIndustry(Industry *ind, CargoID type, int percent) { - NewsType nt; + NewsSubtype ns; switch (WhoCanServiceIndustry(ind)) { - case 0: nt = NT_INDUSTRY_NOBODY; break; - case 1: nt = NT_INDUSTRY_OTHER; break; - case 2: nt = NT_INDUSTRY_PLAYER; break; + case 0: ns = NS_INDUSTRY_NOBODY; break; + case 1: ns = NS_INDUSTRY_OTHER; break; + case 2: ns = NS_INDUSTRY_PLAYER; break; default: NOT_REACHED(); break; } SetDParam(2, abs(percent)); @@ -2015,7 +2010,7 @@ SetDParam(1, ind->index); AddNewsItem( percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN, - NM_THIN, NF_VIEWPORT | NF_TILE, nt, DNC_NONE, + ns, ind->xy + TileDiffXY(1, 1), 0 ); } @@ -2183,15 +2178,15 @@ } if (!suppress_message && str != STR_NULL) { - NewsType nt; + NewsSubtype ns; /* Compute news category */ if (closeit) { - nt = NT_OPENCLOSE; + ns = NS_OPENCLOSE; } else { switch (WhoCanServiceIndustry(i)) { - case 0: nt = NT_INDUSTRY_NOBODY; break; - case 1: nt = NT_INDUSTRY_OTHER; break; - case 2: nt = NT_INDUSTRY_PLAYER; break; + case 0: ns = NS_INDUSTRY_NOBODY; break; + case 1: ns = NS_INDUSTRY_OTHER; break; + case 2: ns = NS_INDUSTRY_PLAYER; break; default: NOT_REACHED(); break; } } @@ -2209,7 +2204,7 @@ } /* and report the news to the user */ AddNewsItem(str, - NM_THIN, NF_VIEWPORT | NF_TILE, nt, DNC_NONE, + ns, i->xy + TileDiffXY(1, 1), 0); } } @@ -2240,8 +2235,7 @@ _current_player = old_player; /* production-change */ - _industry_sort_dirty = true; - InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); + InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 1); } @@ -2251,7 +2245,6 @@ _Industry_pool.AddBlockToPool(); ResetIndustryCounts(); - _industry_sort_dirty = true; _industry_sound_tile = 0; } diff -r 6c4314786d68 -r 8cbdb511a674 src/industry_gui.cpp --- a/src/industry_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/industry_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -26,6 +26,7 @@ #include "settings_type.h" #include "tilehighlight_func.h" #include "string_func.h" +#include "sortlist_type.h" #include "table/strings.h" #include "table/sprites.h" @@ -90,7 +91,6 @@ WC_BUILD_INDUSTRY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE, _build_industry_widgets, - NULL, }; class BuildIndustryWindow : public Window { @@ -103,14 +103,60 @@ StringID text[NUM_INDUSTRYTYPES + 1]; ///< Text coming from CBM_IND_FUND_MORE_TEXT (if ever) bool enabled[NUM_INDUSTRYTYPES + 1]; ///< availability state, coming from CBID_INDUSTRY_AVAILABLE (if ever) -public: - BuildIndustryWindow() : Window(&_build_industry_desc) + void SetupArrays() { IndustryType ind; const IndustrySpec *indsp; + this->count = 0; + + for (uint i = 0; i < lengthof(this->index); i++) { + this->index[i] = INVALID_INDUSTRYTYPE; + this->text[i] = STR_NULL; + this->enabled[i] = false; + } + + if (_game_mode == GM_EDITOR) { // give room for the Many Random "button" + this->index[this->count] = INVALID_INDUSTRYTYPE; + this->count++; + this->timer_enabled = false; + } + /* Fill the arrays with industries. + * The tests performed after the enabled allow to load the industries + * In the same way they are inserted by grf (if any) + */ + for (ind = 0; ind < NUM_INDUSTRYTYPES; ind++) { + indsp = GetIndustrySpec(ind); + if (indsp->enabled){ + /* Rule is that editor mode loads all industries. + * In game mode, all non raw industries are loaded too + * and raw ones are loaded only when setting allows it */ + if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _patches.raw_industry_construction == 0) { + /* Unselect if the industry is no longer in the list */ + if (this->selected_type == ind) this->selected_index = -1; + continue; + } + this->index[this->count] = ind; + this->enabled[this->count] = (_game_mode == GM_EDITOR) || CheckIfCallBackAllowsAvailability(ind, IACT_USERCREATION); + /* Keep the selection to the correct line */ + if (this->selected_type == ind) this->selected_index = this->count; + this->count++; + } + } + + /* first indutry type is selected if the current selection is invalid. + * I'll be damned if there are none available ;) */ + if (this->selected_index == -1) { + this->selected_index = 0; + this->selected_type = this->index[0]; + } + } + +public: + BuildIndustryWindow() : Window(&_build_industry_desc) + { /* Shorten the window to the equivalant of the additionnal purchase - * info coming from the callback. SO it will only be available to tis full + * info coming from the callback. SO it will only be available to its full * height when newindistries are loaded */ if (!_loaded_newgrf_features.has_newindustries) { this->widget[DPIW_INFOPANEL].bottom -= 44; @@ -123,45 +169,15 @@ this->timer_enabled = _loaded_newgrf_features.has_newindustries; - /* Initialize structures */ - this->count = 0; - - for (uint i = 0; i < lengthof(this->index); i++) { - this->index[i] = 0xFF; - this->text[i] = STR_NULL; - this->enabled[i] = false; - } - this->vscroll.cap = 8; // rows in grid, same in scroller this->resize.step_height = 13; - if (_game_mode == GM_EDITOR) { // give room for the Many Random "button" - this->index[this->count] = INVALID_INDUSTRYTYPE; - this->count++; - this->timer_enabled = false; - } + this->selected_index = -1; + this->selected_type = INVALID_INDUSTRYTYPE; - /* Fill the _fund_gui structure with industries. - * The tests performed after the enabled allow to load the industries - * In the same way they are inserted by grf (if any) - */ - for (ind = 0; ind < NUM_INDUSTRYTYPES; ind++) { - indsp = GetIndustrySpec(ind); - if (indsp->enabled){ - /* Rule is that editor mode loads all industries. - * In game mode, all non raw industries are loaded too - * and raw ones are loaded only when setting allows it */ - if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _patches.raw_industry_construction == 0) continue; - this->index[this->count] = ind; - this->enabled[this->count] = (_game_mode == GM_EDITOR) || CheckIfCallBackAllowsAvailability(ind, IACT_USERCREATION); - this->count++; - } - } + /* Initialize arrays */ + this->SetupArrays(); - /* first indutry type is selected. - * I'll be damned if there are none available ;) */ - this->selected_index = 0; - this->selected_type = this->index[0]; this->callback_timer = DAY_TICKS; this->FindWindowPlacementAndResize(&_build_industry_desc); @@ -188,7 +204,7 @@ SetVScrollCount(this, this->count); - DrawWindowWidgets(this); + this->DrawWidgets(); /* and now with the matrix painting */ for (byte i = 0; i < this->vscroll.cap && ((i + this->vscroll.pos) < this->count); i++) { @@ -392,6 +408,12 @@ { this->RaiseButtons(); } + + virtual void OnInvalidateData(int data = 0) + { + this->SetupArrays(); + this->SetDirty(); + } }; void ShowBuildIndustryWindow() @@ -440,7 +462,7 @@ byte production_offset_y; ///< The offset of the production texts/buttons public: - IndustryViewWindow(const WindowDesc *desc, void *data, WindowNumber window_number) : Window(desc, data, window_number) + IndustryViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) { this->flags4 |= WF_DISABLE_VP_SCROLL; this->editbox_line = 0; @@ -459,7 +481,7 @@ bool has_accept = false; SetDParam(0, this->window_number); - DrawWindowWidgets(this); + this->DrawWidgets(); if (HasBit(ind->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_flags, CBM_IND_PRODUCTION_256_TICKS)) { for (byte j = 0; j < lengthof(i->accepts_cargo); j++) { @@ -542,7 +564,7 @@ return; } - DrawWindowViewport(this); + this->DrawViewport(); } virtual void OnClick(Point pt, int widget) @@ -657,7 +679,6 @@ WC_INDUSTRY_VIEW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _industry_view_widgets, - NULL }; void ShowIndustryViewWindow(int industry) @@ -675,7 +696,7 @@ IDW_SORTBYPROD, IDW_SORTBYTRANSPORT, IDW_SPACER, - IDW_INDUSRTY_LIST, + IDW_INDUSTRY_LIST, IDW_SCROLLBAR, IDW_RESIZE, }; @@ -696,12 +717,9 @@ { WIDGETS_END}, }; -static uint _num_industry_sort; - static char _bufcache[96]; static const Industry* _last_industry; - -static byte _industry_sort_order; +static int _internal_sort_order; static int CDECL GeneralIndustrySorter(const void *a, const void *b) { @@ -709,7 +727,7 @@ const Industry* j = *(const Industry**)b; int r; - switch (_industry_sort_order >> 1) { + switch (_internal_sort_order >> 1) { default: NOT_REACHED(); case 0: /* Sort by Name (handled later) */ r = 0; @@ -776,138 +794,171 @@ r = strcmp(buf1, _bufcache); } - if (_industry_sort_order & 1) r = -r; + if (_internal_sort_order & 1) r = -r; return r; } +typedef GUIList GUIIndustryList; + /** - * Makes a sorted industry list. - * When there are no industries, the list has to be made. This so when one - * starts a new game without industries after playing a game with industries - * the list is not populated with invalid industries from the previous game. + * Rebuild industries list if the VL_REBUILD flag is set + * + * @param sl pointer to industry list */ -static void MakeSortedIndustryList() +static void BuildIndustriesList(GUIIndustryList *sl) { - const Industry* i; - int n = 0; + uint n = 0; + const Industry *i; + + if (!(sl->flags & VL_REBUILD)) return; /* Create array for sorting */ - _industry_sort = ReallocT(_industry_sort, GetMaxIndustryIndex() + 1); + const Industry **industry_sort = MallocT(GetMaxIndustryIndex() + 1); - /* Don't attempt a sort if there are no industries */ - if (GetNumIndustries() != 0) { - FOR_ALL_INDUSTRIES(i) _industry_sort[n++] = i; - qsort((void*)_industry_sort, n, sizeof(_industry_sort[0]), GeneralIndustrySorter); - } + DEBUG(misc, 3, "Building industry list"); - _num_industry_sort = n; - _last_industry = NULL; // used for "cache" + FOR_ALL_INDUSTRIES(i) industry_sort[n++] = i; - DEBUG(misc, 3, "Resorting industries list"); + free((void*)sl->sort_list); + sl->sort_list = MallocT(n); + sl->list_length = n; + + for (uint i = 0; i < n; ++i) sl->sort_list[i] = industry_sort[i]; + + sl->flags &= ~VL_REBUILD; + sl->flags |= VL_RESORT; + free((void*)industry_sort); } -static void IndustryDirectoryWndProc(Window *w, WindowEvent *e) +/** + * Sort industry list if the VL_RESORT flag is set + * + * @param sl pointer to industry list + */ +static void SortIndustriesList(GUIIndustryList *sl) { - switch (e->event) { - case WE_PAINT: { - if (_industry_sort_dirty) { - _industry_sort_dirty = false; - MakeSortedIndustryList(); + if (!(sl->flags & VL_RESORT)) return; + + _internal_sort_order = (sl->sort_type << 1) | (sl->flags & VL_DESC); + _last_industry = NULL; // used for "cache" in namesorting + qsort((void*)sl->sort_list, sl->list_length, sizeof(sl->sort_list[0]), &GeneralIndustrySorter); + + sl->flags &= ~VL_RESORT; +} + +/** + * The list of industries. + */ +struct IndustryDirectoryWindow : public Window, public GUIIndustryList { + static Listing industry_sort; + + IndustryDirectoryWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number) + { + this->vscroll.cap = 16; + this->resize.height = this->height - 6 * 10; // minimum 10 items + this->resize.step_height = 10; + this->FindWindowPlacementAndResize(desc); + + this->sort_list = NULL; + this->flags = VL_REBUILD; + this->sort_type = industry_sort.criteria; + if (industry_sort.order) this->flags |= VL_DESC; + } + + virtual void OnPaint() + { + BuildIndustriesList(this); + SortIndustriesList(this); + + SetVScrollCount(this, this->list_length); + + this->DrawWidgets(); + this->DrawSortButtonState(IDW_SORTBYNAME + this->sort_type, this->flags & VL_DESC ? SBS_DOWN : SBS_UP); + + int max = min(this->vscroll.pos + this->vscroll.cap, this->list_length); + int y = 28; // start of the list-widget + + for (int n = this->vscroll.pos; n < max; ++n) { + const Industry* i = this->sort_list[n]; + const IndustrySpec *indsp = GetIndustrySpec(i->type); + byte p = 0; + + /* Industry name */ + SetDParam(p++, i->index); + + /* Industry productions */ + for (byte j = 0; j < lengthof(i->produced_cargo); j++) { + if (i->produced_cargo[j] == CT_INVALID) continue; + SetDParam(p++, i->produced_cargo[j]); + SetDParam(p++, i->last_month_production[j]); + SetDParam(p++, GetCargoSuffix(j + 3, CST_DIR, (Industry*)i, i->type, indsp)); } - SetVScrollCount(w, _num_industry_sort); - - DrawWindowWidgets(w); - DrawSortButtonState(w, IDW_SORTBYNAME + (_industry_sort_order >> 1), _industry_sort_order & 1 ? SBS_DOWN : SBS_UP); - - uint pos = w->vscroll.pos; - int n = 0; - - while (pos < _num_industry_sort) { - const Industry* i = _industry_sort[pos]; - const IndustrySpec *indsp = GetIndustrySpec(i->type); - byte p = 0; - - /* Industry name */ - SetDParam(p++, i->index); - - /* Industry productions */ - for (byte j = 0; j < lengthof(i->produced_cargo); j++) { - if (i->produced_cargo[j] == CT_INVALID) continue; - SetDParam(p++, i->produced_cargo[j]); - SetDParam(p++, i->last_month_production[j]); - SetDParam(p++, GetCargoSuffix(j + 3, CST_DIR, (Industry*)i, i->type, indsp)); - } - - /* Transported productions */ - for (byte j = 0; j < lengthof(i->produced_cargo); j++) { - if (i->produced_cargo[j] == CT_INVALID) continue; - SetDParam(p++, i->last_month_pct_transported[j] * 100 >> 8); - } - - /* Drawing the right string */ - StringID str = STR_INDUSTRYDIR_ITEM_NOPROD; - if (p != 1) str = (p == 5) ? STR_INDUSTRYDIR_ITEM : STR_INDUSTRYDIR_ITEM_TWO; - DrawStringTruncated(4, 28 + n * 10, str, TC_FROMSTRING, w->widget[IDW_INDUSRTY_LIST].right - 4); - - pos++; - if (++n == w->vscroll.cap) break; + /* Transported productions */ + for (byte j = 0; j < lengthof(i->produced_cargo); j++) { + if (i->produced_cargo[j] == CT_INVALID) continue; + SetDParam(p++, i->last_month_pct_transported[j] * 100 >> 8); } - } break; - case WE_CLICK: - switch (e->we.click.widget) { - case IDW_SORTBYNAME: { - _industry_sort_order = _industry_sort_order == 0 ? 1 : 0; - _industry_sort_dirty = true; - w->SetDirty(); - } break; - - case IDW_SORTBYTYPE: { - _industry_sort_order = _industry_sort_order == 2 ? 3 : 2; - _industry_sort_dirty = true; - w->SetDirty(); - } break; - - case IDW_SORTBYPROD: { - _industry_sort_order = _industry_sort_order == 4 ? 5 : 4; - _industry_sort_dirty = true; - w->SetDirty(); - } break; - - case IDW_SORTBYTRANSPORT: { - _industry_sort_order = _industry_sort_order == 6 ? 7 : 6; - _industry_sort_dirty = true; - w->SetDirty(); - } break; + /* Drawing the right string */ + StringID str = STR_INDUSTRYDIR_ITEM_NOPROD; + if (p != 1) str = (p == 5) ? STR_INDUSTRYDIR_ITEM : STR_INDUSTRYDIR_ITEM_TWO; + DrawStringTruncated(4, y, str, TC_FROMSTRING, this->widget[IDW_INDUSTRY_LIST].right - 4); - case IDW_INDUSRTY_LIST: { - int y = (e->we.click.pt.y - 28) / 10; - uint16 p; + y += 10; + } + } - if (!IsInsideMM(y, 0, w->vscroll.cap)) return; - p = y + w->vscroll.pos; - if (p < _num_industry_sort) { - if (_ctrl_pressed) { - ShowExtraViewPortWindow(_industry_sort[p]->xy); - } else { - ScrollMainWindowToTile(_industry_sort[p]->xy); - } + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case IDW_SORTBYNAME: + case IDW_SORTBYTYPE: + case IDW_SORTBYPROD: + case IDW_SORTBYTRANSPORT: + if (this->sort_type == (widget - IDW_SORTBYNAME)) { + this->flags ^= VL_DESC; + } else { + this->sort_type = widget - IDW_SORTBYNAME; + this->flags &= ~VL_DESC; + } + industry_sort.criteria = this->sort_type; + industry_sort.order = HasBit(this->flags, 0); + this->flags |= VL_RESORT; + this->SetDirty(); + break; + + case IDW_INDUSTRY_LIST: { + int y = (pt.y - 28) / 10; + uint16 p; + + if (!IsInsideMM(y, 0, this->vscroll.cap)) return; + p = y + this->vscroll.pos; + if (p < this->list_length) { + if (_ctrl_pressed) { + ShowExtraViewPortWindow(this->sort_list[p]->xy); + } else { + ScrollMainWindowToTile(this->sort_list[p]->xy); } - } break; - } - break; + } + } break; + } + } - case WE_100_TICKS: - w->SetDirty(); - break; + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / 10; + } - case WE_RESIZE: - w->vscroll.cap += e->we.sizing.diff.y / 10; - break; + virtual void OnInvalidateData(int data) + { + this->flags |= (data == 0 ? VL_REBUILD : VL_RESORT); + this->InvalidateWidget(IDW_INDUSTRY_LIST); } -} +}; + +Listing IndustryDirectoryWindow::industry_sort = {0, 0}; /** Window definition of the industy directory gui */ static const WindowDesc _industry_directory_desc = { @@ -915,17 +966,9 @@ WC_INDUSTRY_DIRECTORY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _industry_directory_widgets, - IndustryDirectoryWndProc }; void ShowIndustryDirectory() { - Window *w = AllocateWindowDescFront(&_industry_directory_desc, 0); - - if (w != NULL) { - w->vscroll.cap = 16; - w->resize.height = w->height - 6 * 10; // minimum 10 items - w->resize.step_height = 10; - w->SetDirty(); - } + AllocateWindowDescFront(&_industry_directory_desc, 0); } diff -r 6c4314786d68 -r 8cbdb511a674 src/intro_gui.cpp --- a/src/intro_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/intro_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -53,87 +53,91 @@ 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_PATCHES_OPTIONS, - SGI_GRF_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_PATCHES_OPTIONS, + SGI_GRF_SETTINGS, + SGI_EXIT, + }; -static void SelectGameWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: w->LowerWidget(_opt_newgame.landscape + 8); break; +public: + SelectGameWindow(const WindowDesc *desc) : Window(desc) + { + this->LowerWidget(_opt_newgame.landscape + SGI_TEMPERATE_LANDSCAPE); + this->FindWindowPlacementAndResize(desc); + } - case WE_PAINT: - w->SetWidgetLoweredState(SGI_TEMPERATE_LANDSCAPE, _opt_newgame.landscape == LT_TEMPERATE); - w->SetWidgetLoweredState(SGI_ARCTIC_LANDSCAPE, _opt_newgame.landscape == LT_ARCTIC); - w->SetWidgetLoweredState(SGI_TROPIC_LANDSCAPE, _opt_newgame.landscape == LT_TROPIC); - w->SetWidgetLoweredState(SGI_TOYLAND_LANDSCAPE, _opt_newgame.landscape == LT_TOYLAND); - SetDParam(0, STR_6801_EASY + _opt_newgame.diff_level); - DrawWindowWidgets(w); - break; + virtual void OnPaint() + { + this->SetWidgetLoweredState(SGI_TEMPERATE_LANDSCAPE, _opt_newgame.landscape == LT_TEMPERATE); + this->SetWidgetLoweredState(SGI_ARCTIC_LANDSCAPE, _opt_newgame.landscape == LT_ARCTIC); + this->SetWidgetLoweredState(SGI_TROPIC_LANDSCAPE, _opt_newgame.landscape == LT_TROPIC); + this->SetWidgetLoweredState(SGI_TOYLAND_LANDSCAPE, _opt_newgame.landscape == LT_TOYLAND); + SetDParam(0, STR_6801_EASY + _opt_newgame.diff_level); + this->DrawWidgets(); + } - case WE_CLICK: + virtual void OnClick(Point pt, int widget) + { #ifdef ENABLE_NETWORK - /* Do not create a network server when you (just) have closed one of the game - * creation/load windows for the network server. */ - if (SGI_GENERATE_GAME <= e->we.click.widget && e->we.click.widget <= SGI_EDIT_SCENARIO) _is_network_server = false; + /* Do not create a network server when you (just) have closed one of the game + * creation/load windows for the network server. */ + if (IsInsideMM(widget, SGI_GENERATE_GAME, SGI_EDIT_SCENARIO + 1)) _is_network_server = false; #endif /* ENABLE_NETWORK */ - switch (e->we.click.widget) { - case SGI_GENERATE_GAME: ShowGenerateLandscape(); break; - case SGI_LOAD_GAME: ShowSaveLoadDialog(SLD_LOAD_GAME); break; - case SGI_PLAY_SCENARIO: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break; - case SGI_PLAY_HEIGHTMAP: ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP); break; - case SGI_EDIT_SCENARIO: StartScenarioEditor(); break; - - case SGI_PLAY_NETWORK: - if (!_network_available) { - ShowErrorMessage(INVALID_STRING_ID, STR_NETWORK_ERR_NOTAVAILABLE, 0, 0); - } else { - ShowNetworkGameWindow(); - } - break; + switch (widget) { + case SGI_GENERATE_GAME: ShowGenerateLandscape(); break; + case SGI_LOAD_GAME: ShowSaveLoadDialog(SLD_LOAD_GAME); break; + case SGI_PLAY_SCENARIO: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break; + case SGI_PLAY_HEIGHTMAP: ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP); break; + case SGI_EDIT_SCENARIO: StartScenarioEditor(); break; - case SGI_TEMPERATE_LANDSCAPE: case SGI_ARCTIC_LANDSCAPE: - case SGI_TROPIC_LANDSCAPE: case SGI_TOYLAND_LANDSCAPE: - w->RaiseWidget(_opt_newgame.landscape + SGI_TEMPERATE_LANDSCAPE); - SetNewLandscapeType(e->we.click.widget - SGI_TEMPERATE_LANDSCAPE); - break; + case SGI_PLAY_NETWORK: + if (!_network_available) { + ShowErrorMessage(INVALID_STRING_ID, STR_NETWORK_ERR_NOTAVAILABLE, 0, 0); + } else { + ShowNetworkGameWindow(); + } + break; - case SGI_OPTIONS: ShowGameOptions(); break; - case SGI_DIFFICULTIES: ShowGameDifficulty(); break; - case SGI_PATCHES_OPTIONS: ShowPatchesSelection(); break; - case SGI_GRF_SETTINGS: ShowNewGRFSettings(true, true, false, &_grfconfig_newgame); break; - case SGI_EXIT: HandleExitGameRequest(); break; - } - break; + case SGI_TEMPERATE_LANDSCAPE: case SGI_ARCTIC_LANDSCAPE: + case SGI_TROPIC_LANDSCAPE: case SGI_TOYLAND_LANDSCAPE: + this->RaiseWidget(_opt_newgame.landscape + SGI_TEMPERATE_LANDSCAPE); + SetNewLandscapeType(widget - SGI_TEMPERATE_LANDSCAPE); + break; + + case SGI_OPTIONS: ShowGameOptions(); break; + case SGI_DIFFICULTIES: ShowGameDifficulty(); break; + case SGI_PATCHES_OPTIONS: ShowPatchesSelection(); break; + case SGI_GRF_SETTINGS: ShowNewGRFSettings(true, true, false, &_grfconfig_newgame); break; + case SGI_EXIT: HandleExitGameRequest(); break; + } } -} +}; static const WindowDesc _select_game_desc = { WDP_CENTER, WDP_CENTER, 336, 195, 336, 195, WC_SELECT_GAME, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _select_game_widgets, - SelectGameWndProc }; void ShowSelectGameWindow() { - new Window(&_select_game_desc); + new SelectGameWindow(&_select_game_desc); } static void AskExitGameCallback(Window *w, bool confirmed) diff -r 6c4314786d68 -r 8cbdb511a674 src/main_gui.cpp --- a/src/main_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/main_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -29,6 +29,7 @@ #include "player_gui.h" #include "settings_type.h" #include "toolbar_gui.h" +#include "statusbar_gui.h" #include "ai/ai_gui.hpp" #include "variables.h" #include "tilehighlight_func.h" @@ -215,188 +216,194 @@ extern void UpdateAllStationVirtCoord(); -static void MainWindowWndProc(Window *w, WindowEvent *e) +struct MainWindow : Window { - switch (e->event) { - case WE_PAINT: - DrawWindowViewport(w); - if (_game_mode == GM_MENU) { - int off_x = _screen.width / 2; - - DrawSprite(SPR_OTTD_O, PAL_NONE, off_x - 120, 50); - DrawSprite(SPR_OTTD_P, PAL_NONE, off_x - 86, 50); - DrawSprite(SPR_OTTD_E, PAL_NONE, off_x - 53, 50); - DrawSprite(SPR_OTTD_N, PAL_NONE, off_x - 22, 50); - - DrawSprite(SPR_OTTD_T, PAL_NONE, off_x + 34, 50); - DrawSprite(SPR_OTTD_T, PAL_NONE, off_x + 65, 50); - DrawSprite(SPR_OTTD_D, PAL_NONE, off_x + 96, 50); - } - break; + MainWindow(int width, int height) : Window(0, 0, width, height, WC_MAIN_WINDOW, NULL) + { + InitializeWindowViewport(this, 0, 0, width, height, TileXY(32, 32), ZOOM_LVL_VIEWPORT); + } - case WE_KEYPRESS: - switch (e->we.keypress.keycode) { - case 'Q' | WKC_CTRL: - case 'Q' | WKC_META: - HandleExitGameRequest(); - break; - } + virtual void OnPaint() + { + this->DrawViewport(); + if (_game_mode == GM_MENU) { + int off_x = _screen.width / 2; - /* Disable all key shortcuts, except quit shortcuts when - * generating the world, otherwise they create threading - * problem during the generating, resulting in random - * assertions that are hard to trigger and debug */ - if (IsGeneratingWorld()) break; + DrawSprite(SPR_OTTD_O, PAL_NONE, off_x - 120, 50); + DrawSprite(SPR_OTTD_P, PAL_NONE, off_x - 86, 50); + DrawSprite(SPR_OTTD_E, PAL_NONE, off_x - 53, 50); + DrawSprite(SPR_OTTD_N, PAL_NONE, off_x - 22, 50); - if (e->we.keypress.keycode == WKC_BACKQUOTE) { - IConsoleSwitch(); - e->we.keypress.cont = false; - break; - } + DrawSprite(SPR_OTTD_T, PAL_NONE, off_x + 34, 50); + DrawSprite(SPR_OTTD_T, PAL_NONE, off_x + 65, 50); + DrawSprite(SPR_OTTD_D, PAL_NONE, off_x + 96, 50); + } + } - if (e->we.keypress.keycode == ('B' | WKC_CTRL)) { - e->we.keypress.cont = false; - extern bool _draw_bounding_boxes; - _draw_bounding_boxes = !_draw_bounding_boxes; - MarkWholeScreenDirty(); + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + switch (keycode) { + case 'Q' | WKC_CTRL: + case 'Q' | WKC_META: + HandleExitGameRequest(); + return ES_HANDLED; + } + + /* Disable all key shortcuts, except quit shortcuts when + * generating the world, otherwise they create threading + * problem during the generating, resulting in random + * assertions that are hard to trigger and debug */ + if (IsGeneratingWorld()) return ES_NOT_HANDLED; + + if (keycode == WKC_BACKQUOTE) { + IConsoleSwitch(); + return ES_HANDLED; + } + + if (keycode == ('B' | WKC_CTRL)) { + extern bool _draw_bounding_boxes; + _draw_bounding_boxes = !_draw_bounding_boxes; + MarkWholeScreenDirty(); + return ES_HANDLED; + } + + if (_game_mode == GM_MENU) return ES_NOT_HANDLED; + + switch (keycode) { + case 'C': + case 'Z': { + Point pt = GetTileBelowCursor(); + if (pt.x != -1) { + if (keycode == 'Z') MaxZoomInOut(ZOOM_IN, this); + ScrollMainWindowTo(pt.x, pt.y); + } break; } - if (_game_mode == GM_MENU) break; - - switch (e->we.keypress.keycode) { - case 'C': - case 'Z': { - Point pt = GetTileBelowCursor(); - if (pt.x != -1) { - if (e->we.keypress.keycode == 'Z') MaxZoomInOut(ZOOM_IN, w); - ScrollMainWindowTo(pt.x, pt.y); - } - break; - } - - case WKC_ESC: ResetObjectToPlace(); break; - case WKC_DELETE: DeleteNonVitalWindows(); break; - case WKC_DELETE | WKC_SHIFT: DeleteAllNonVitalWindows(); break; - case 'R' | WKC_CTRL: MarkWholeScreenDirty(); break; + case WKC_ESC: ResetObjectToPlace(); break; + case WKC_DELETE: DeleteNonVitalWindows(); break; + case WKC_DELETE | WKC_SHIFT: DeleteAllNonVitalWindows(); break; + case 'R' | WKC_CTRL: MarkWholeScreenDirty(); break; #if defined(_DEBUG) - case '0' | WKC_ALT: // Crash the game - *(byte*)0 = 0; - break; + case '0' | WKC_ALT: // Crash the game + *(byte*)0 = 0; + break; - case '1' | WKC_ALT: // Gimme money - /* Server can not cheat in advertise mode either! */ - if (!_networking || !_network_server || !_network_advertise) - DoCommandP(0, 10000000, 0, NULL, CMD_MONEY_CHEAT); - break; + case '1' | WKC_ALT: // Gimme money + /* Server can not cheat in advertise mode either! */ + if (!_networking || !_network_server || !_network_advertise) + DoCommandP(0, 10000000, 0, NULL, CMD_MONEY_CHEAT); + break; - case '2' | WKC_ALT: // Update the coordinates of all station signs - UpdateAllStationVirtCoord(); - break; + case '2' | WKC_ALT: // Update the coordinates of all station signs + UpdateAllStationVirtCoord(); + break; #endif - case '1' | WKC_CTRL: - case '2' | WKC_CTRL: - case '3' | WKC_CTRL: - case '4' | WKC_CTRL: - case '5' | WKC_CTRL: - case '6' | WKC_CTRL: - case '7' | WKC_CTRL: - case '8' | WKC_CTRL: - case '9' | WKC_CTRL: - /* Transparency toggle hot keys */ - ToggleTransparency((TransparencyOption)(e->we.keypress.keycode - ('1' | WKC_CTRL))); - MarkWholeScreenDirty(); - break; + case '1' | WKC_CTRL: + case '2' | WKC_CTRL: + case '3' | WKC_CTRL: + case '4' | WKC_CTRL: + case '5' | WKC_CTRL: + case '6' | WKC_CTRL: + case '7' | WKC_CTRL: + case '8' | WKC_CTRL: + case '9' | WKC_CTRL: + /* Transparency toggle hot keys */ + ToggleTransparency((TransparencyOption)(keycode - ('1' | WKC_CTRL))); + MarkWholeScreenDirty(); + break; - case '1' | WKC_CTRL | WKC_SHIFT: - case '2' | WKC_CTRL | WKC_SHIFT: - case '3' | WKC_CTRL | WKC_SHIFT: - case '4' | WKC_CTRL | WKC_SHIFT: - case '5' | WKC_CTRL | WKC_SHIFT: - case '6' | WKC_CTRL | WKC_SHIFT: - case '7' | WKC_CTRL | WKC_SHIFT: - case '8' | WKC_CTRL | WKC_SHIFT: - /* Invisibility toggle hot keys */ - ToggleInvisibilityWithTransparency((TransparencyOption)(e->we.keypress.keycode - ('1' | WKC_CTRL | WKC_SHIFT))); - MarkWholeScreenDirty(); - break; + case '1' | WKC_CTRL | WKC_SHIFT: + case '2' | WKC_CTRL | WKC_SHIFT: + case '3' | WKC_CTRL | WKC_SHIFT: + case '4' | WKC_CTRL | WKC_SHIFT: + case '5' | WKC_CTRL | WKC_SHIFT: + case '6' | WKC_CTRL | WKC_SHIFT: + case '7' | WKC_CTRL | WKC_SHIFT: + case '8' | WKC_CTRL | WKC_SHIFT: + /* Invisibility toggle hot keys */ + ToggleInvisibilityWithTransparency((TransparencyOption)(keycode - ('1' | WKC_CTRL | WKC_SHIFT))); + MarkWholeScreenDirty(); + break; - case 'X' | WKC_CTRL: - ShowTransparencyToolbar(); - break; + case 'X' | WKC_CTRL: + ShowTransparencyToolbar(); + break; - case 'X': - ResetRestoreAllTransparency(); - break; + case 'X': + ResetRestoreAllTransparency(); + break; #ifdef ENABLE_NETWORK - case WKC_RETURN: case 'T': // smart chat; send to team if any, otherwise to all - if (_networking) { - const NetworkClientInfo *cio = NetworkFindClientInfoFromIndex(_network_own_client_index); - bool teamchat = false; - - if (cio == NULL) break; + case WKC_RETURN: case 'T': // smart chat; send to team if any, otherwise to all + if (_networking) { + const NetworkClientInfo *cio = NetworkFindClientInfoFromIndex(_network_own_client_index); + bool teamchat = false; - /* Only players actually playing can speak to team. Eg spectators cannot */ - if (_patches.prefer_teamchat && IsValidPlayer(cio->client_playas)) { - const NetworkClientInfo *ci; - FOR_ALL_ACTIVE_CLIENT_INFOS(ci) { - if (ci->client_playas == cio->client_playas && ci != cio) { - teamchat = true; - break; - } + if (cio == NULL) break; + + /* Only players actually playing can speak to team. Eg spectators cannot */ + if (_patches.prefer_teamchat && IsValidPlayer(cio->client_playas)) { + const NetworkClientInfo *ci; + FOR_ALL_ACTIVE_CLIENT_INFOS(ci) { + if (ci->client_playas == cio->client_playas && ci != cio) { + teamchat = true; + break; } } - - ShowNetworkChatQueryWindow(teamchat ? DESTTYPE_TEAM : DESTTYPE_BROADCAST, cio->client_playas); } - break; - case WKC_SHIFT | WKC_RETURN: case WKC_SHIFT | 'T': // send text message to all players - if (_networking) ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0); - break; + ShowNetworkChatQueryWindow(teamchat ? DESTTYPE_TEAM : DESTTYPE_BROADCAST, cio->client_playas); + } + break; - case WKC_CTRL | WKC_RETURN: case WKC_CTRL | 'T': // send text to all team mates - if (_networking) { - const NetworkClientInfo *cio = NetworkFindClientInfoFromIndex(_network_own_client_index); - if (cio == NULL) break; + case WKC_SHIFT | WKC_RETURN: case WKC_SHIFT | 'T': // send text message to all players + if (_networking) ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0); + break; - ShowNetworkChatQueryWindow(DESTTYPE_TEAM, cio->client_playas); - } - break; + case WKC_CTRL | WKC_RETURN: case WKC_CTRL | 'T': // send text to all team mates + if (_networking) { + const NetworkClientInfo *cio = NetworkFindClientInfoFromIndex(_network_own_client_index); + if (cio == NULL) break; + + ShowNetworkChatQueryWindow(DESTTYPE_TEAM, cio->client_playas); + } + break; #endif - default: return; - } - e->we.keypress.cont = false; - break; - - case WE_SCROLL: { - ViewPort *vp = IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y); - - if (vp == NULL) { - _cursor.fix_at = false; - _scrolling_viewport = false; - } + default: return ES_NOT_HANDLED; + } + return ES_HANDLED; + } - w->viewport->scrollpos_x += ScaleByZoom(e->we.scroll.delta.x, vp->zoom); - w->viewport->scrollpos_y += ScaleByZoom(e->we.scroll.delta.y, vp->zoom); - w->viewport->dest_scrollpos_x = w->viewport->scrollpos_x; - w->viewport->dest_scrollpos_y = w->viewport->scrollpos_y; - } break; + virtual void OnScroll(Point delta) + { + ViewPort *vp = IsPtInWindowViewport(this, _cursor.pos.x, _cursor.pos.y); - case WE_MOUSEWHEEL: - ZoomInOrOutToCursorWindow(e->we.wheel.wheel < 0, w); - break; + if (vp == NULL) { + _cursor.fix_at = false; + _scrolling_viewport = false; + } - case WE_INVALIDATE_DATA: - /* Forward the message to the appropiate toolbar (ingame or scenario editor) */ - InvalidateWindowData(WC_MAIN_TOOLBAR, 0, e->we.invalidate.data); - break; + this->viewport->scrollpos_x += ScaleByZoom(delta.x, vp->zoom); + this->viewport->scrollpos_y += ScaleByZoom(delta.y, vp->zoom); + this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x; + this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y; + }; + + virtual void OnMouseWheel(int wheel) + { + ZoomInOrOutToCursorWindow(wheel < 0, this); } -} + + virtual void OnInvalidateData(int data) + { + /* Forward the message to the appropiate toolbar (ingame or scenario editor) */ + InvalidateWindowData(WC_MAIN_TOOLBAR, 0, data); + } +}; void ShowSelectGameWindow(); @@ -410,11 +417,7 @@ memcpy(_colour_gradient[i], b + 0xC6, sizeof(_colour_gradient[i])); } - int width = _screen.width; - int height = _screen.height; - - Window *w = new Window(0, 0, width, height, MainWindowWndProc, WC_MAIN_WINDOW, NULL); - InitializeWindowViewport(w, 0, 0, width, height, TileXY(32, 32), ZOOM_LVL_VIEWPORT); + new MainWindow(_screen.width, _screen.height); /* XXX: these are not done */ switch (_game_mode) { @@ -430,12 +433,9 @@ } } -extern void ShowStatusBar(); - void ShowVitalWindows() { - Window *w = AllocateToolbar(); - DoZoomInOutWindow(ZOOM_NONE, w); + AllocateToolbar(); /* Status bad only for normal games */ if (_game_mode == GM_EDITOR) return; diff -r 6c4314786d68 -r 8cbdb511a674 src/misc.cpp --- a/src/misc.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/misc.cpp Mon May 19 15:13:58 2008 +0000 @@ -28,6 +28,7 @@ #include "animated_tile_func.h" #include "settings_type.h" #include "tilehighlight_func.h" +#include "core/bitmath_func.hpp" #include "table/strings.h" #include "table/sprites.h" diff -r 6c4314786d68 -r 8cbdb511a674 src/misc_gui.cpp --- a/src/misc_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/misc_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -69,7 +69,6 @@ WC_LAND_INFO, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _land_info_widgets, - NULL }; class LandInfoWindow : public Window { @@ -83,7 +82,7 @@ virtual void OnPaint() { - DrawWindowWidgets(this); + this->DrawWidgets(); DoDrawStringCentered(140, 16, this->landinfo_data[0], TC_LIGHT_BLUE); DoDrawStringCentered(140, 27, this->landinfo_data[1], TC_FROMSTRING); @@ -206,107 +205,6 @@ } } -struct scroller_d { - int height; - uint16 counter; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(scroller_d)); - -static const char *credits[] = { - /************************************************************************* - * maximum length of string which fits in window -^*/ - "Original design by Chris Sawyer", - "Original graphics by Simon Foster", - "", - "The OpenTTD team (in alphabetical order):", - " Jean-Francois Claeys (Belugas) - GUI, newindustries and more", - " Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles", - " Matthijs Kooijman (blathijs) - Pathfinder-guru, pool rework", - " Loïc Guilloux (glx) - General coding", - " Christoph Elsenhans (frosch) - General coding", - " Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)", - " Jonathan Coome (Maedhros) - High priest of the newGRF Temple", - " Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host", - " Owen Rudge (orudge) - Forum host, OS/2 port", - " Peter Nelson (peter1138) - Spiritual descendant from newGRF gods", - " Remko Bijker (Rubidium) - Lead coder and way more", - " Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker", - " Zdenek Sojka (SmatZ) - Bug finder and fixer", - "", - "Inactive Developers:", - " Victor Fischer (Celestar) - Programming everywhere you need him to", - " Tamás Faragó (Darkvater) - Ex-Lead coder", - " Christoph Mallon (Tron) - Programmer, code correctness police", - "", - "Retired Developers:", - " Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)", - " Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)", - " Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)", - " Patric Stout (TrueLight) - Programmer, webhoster (0.3 - pre0.6)", - "", - "Special thanks go out to:", - " Josef Drexler - For his great work on TTDPatch", - " Marcin Grzegorczyk - For his documentation of TTD internals", - " Petr Baudis (pasky) - Many patches, newGRF support", - " Stefan Meißner (sign_de) - For his work on the console", - " Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with", - " Cian Duffy (MYOB) - BeOS port / manual writing", - " Christian Rosentreter (tokai) - MorphOS / AmigaOS port", - " Richard Kempton (richK) - additional airports, initial TGP implementation", - "", - " Michael Blunck - Pre-Signals and Semaphores © 2003", - " George - Canal/Lock graphics © 2003-2004", - " David Dallaston - Tram tracks", - " Marcin Grzegorczyk - Foundations for Tracks on Slopes", - " All Translators - Who made OpenTTD a truly international game", - " Bug Reporters - Without whom OpenTTD would still be full of bugs!", - "", - "", - "And last but not least:", - " Chris Sawyer - For an amazing game!" -}; - -static void AboutWindowProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: // Set up window counter and start position of scroller - WP(w, scroller_d).counter = 5; - WP(w, scroller_d).height = w->height - 40; - break; - - case WE_PAINT: { - int y = WP(w, scroller_d).height; - DrawWindowWidgets(w); - - /* Show original copyright and revision version */ - DrawStringCentered(210, 17, STR_00B6_ORIGINAL_COPYRIGHT, TC_FROMSTRING); - DrawStringCentered(210, 17 + 10, STR_00B7_VERSION, TC_FROMSTRING); - - /* Show all scrolling credits */ - for (uint i = 0; i < lengthof(credits); i++) { - if (y >= 50 && y < (w->height - 40)) { - DoDrawString(credits[i], 10, y, TC_BLACK); - } - y += 10; - } - - /* If the last text has scrolled start anew from the start */ - if (y < 50) WP(w, scroller_d).height = w->height - 40; - - DoDrawStringCentered(210, w->height - 25, "Website: http://www.openttd.org", TC_BLACK); - DrawStringCentered(210, w->height - 15, STR_00BA_COPYRIGHT_OPENTTD, TC_FROMSTRING); - } break; - - case WE_TICK: // Timer to scroll the text and adjust the new top - if (--WP(w, scroller_d).counter == 0) { - WP(w, scroller_d).counter = 5; - WP(w, scroller_d).height--; - w->SetDirty(); - } - break; - } -} - static const Widget _about_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, { WWT_CAPTION, RESIZE_NONE, 14, 11, 419, 0, 13, STR_015B_OPENTTD, STR_NULL}, @@ -320,21 +218,114 @@ WC_GAME_OPTIONS, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _about_widgets, - AboutWindowProc }; +struct AboutWindow : public Window { + int scroll_height; + uint16 counter; + + AboutWindow() : Window(&_about_desc) + { + this->counter = 5; + this->scroll_height = this->height - 40; + this->FindWindowPlacementAndResize(&_about_desc); + } + + virtual void OnPaint() + { + static const char *credits[] = { + /************************************************************************* + * maximum length of string which fits in window -^*/ + "Original design by Chris Sawyer", + "Original graphics by Simon Foster", + "", + "The OpenTTD team (in alphabetical order):", + " Jean-Francois Claeys (Belugas) - GUI, newindustries and more", + " Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles", + " Matthijs Kooijman (blathijs) - Pathfinder-guru, pool rework", + " Loïc Guilloux (glx) - General coding", + " Christoph Elsenhans (frosch) - General coding", + " Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)", + " Jonathan Coome (Maedhros) - High priest of the newGRF Temple", + " Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host", + " Owen Rudge (orudge) - Forum host, OS/2 port", + " Peter Nelson (peter1138) - Spiritual descendant from newGRF gods", + " Remko Bijker (Rubidium) - Lead coder and way more", + " Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker", + " Zdenek Sojka (SmatZ) - Bug finder and fixer", + "", + "Inactive Developers:", + " Victor Fischer (Celestar) - Programming everywhere you need him to", + " Tamás Faragó (Darkvater) - Ex-Lead coder", + " Christoph Mallon (Tron) - Programmer, code correctness police", + "", + "Retired Developers:", + " Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)", + " Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)", + " Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)", + " Patric Stout (TrueLight) - Programmer, webhoster (0.3 - pre0.6)", + "", + "Special thanks go out to:", + " Josef Drexler - For his great work on TTDPatch", + " Marcin Grzegorczyk - For his documentation of TTD internals", + " Petr Baudis (pasky) - Many patches, newGRF support", + " Stefan Meißner (sign_de) - For his work on the console", + " Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with", + " Cian Duffy (MYOB) - BeOS port / manual writing", + " Christian Rosentreter (tokai) - MorphOS / AmigaOS port", + " Richard Kempton (richK) - additional airports, initial TGP implementation", + "", + " Michael Blunck - Pre-Signals and Semaphores © 2003", + " George - Canal/Lock graphics © 2003-2004", + " David Dallaston - Tram tracks", + " Marcin Grzegorczyk - Foundations for Tracks on Slopes", + " All Translators - Who made OpenTTD a truly international game", + " Bug Reporters - Without whom OpenTTD would still be full of bugs!", + "", + "", + "And last but not least:", + " Chris Sawyer - For an amazing game!" + }; + + this->DrawWidgets(); + + /* Show original copyright and revision version */ + DrawStringCentered(210, 17, STR_00B6_ORIGINAL_COPYRIGHT, TC_FROMSTRING); + DrawStringCentered(210, 17 + 10, STR_00B7_VERSION, TC_FROMSTRING); + + int y = this->scroll_height; + + /* Show all scrolling credits */ + for (uint i = 0; i < lengthof(credits); i++) { + if (y >= 50 && y < (this->height - 40)) { + DoDrawString(credits[i], 10, y, TC_BLACK); + } + y += 10; + } + + /* If the last text has scrolled start a new from the start */ + if (y < 50) this->scroll_height = this->height - 40; + + DoDrawStringCentered(210, this->height - 25, "Website: http://www.openttd.org", TC_BLACK); + DrawStringCentered(210, this->height - 15, STR_00BA_COPYRIGHT_OPENTTD, TC_FROMSTRING); + } + + virtual void OnTick() + { + if (--this->counter == 0) { + this->counter = 5; + this->scroll_height--; + this->SetDirty(); + } + } +}; void ShowAboutWindow() { DeleteWindowById(WC_GAME_OPTIONS, 0); - new Window(&_about_desc); + new AboutWindow(); } -static uint64 _errmsg_decode_params[20]; -static StringID _errmsg_message_1, _errmsg_message_2; -static uint _errmsg_duration; - - static const Widget _errmsg_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, { WWT_CAPTION, RESIZE_NONE, 4, 11, 239, 0, 13, STR_00B2_MESSAGE, STR_NULL}, @@ -349,96 +340,96 @@ { WIDGETS_END}, }; -static void ErrmsgWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - CopyInDParam(0, _errmsg_decode_params, lengthof(_errmsg_decode_params)); - DrawWindowWidgets(w); - CopyInDParam(0, _errmsg_decode_params, lengthof(_errmsg_decode_params)); - - /* If the error message comes from a NewGRF, we must use the text ref. stack reserved for error messages. - * If the message doesn't come from a NewGRF, it won't use the TTDP-style text ref. stack, so we won't hurt anything - */ - SwitchToErrorRefStack(); - RewindTextRefStack(); - - if (!IsWindowOfPrototype(w, _errmsg_face_widgets)) { - DrawStringMultiCenter( - 120, - (_errmsg_message_1 == INVALID_STRING_ID ? 25 : 15), - _errmsg_message_2, - w->width - 2); - if (_errmsg_message_1 != INVALID_STRING_ID) { - DrawStringMultiCenter( - 120, - 30, - _errmsg_message_1, - w->width - 2); - } - } else { - const Player *p = GetPlayer((PlayerID)GetDParamX(_errmsg_decode_params,2)); - DrawPlayerFace(p->face, p->player_color, 2, 16); +struct ErrmsgWindow : public Window { +private: + uint duration; + uint64 decode_params[20]; + StringID message_1; + StringID message_2; + bool show_player_face; - DrawStringMultiCenter( - 214, - (_errmsg_message_1 == INVALID_STRING_ID ? 65 : 45), - _errmsg_message_2, - w->width - 2); - if (_errmsg_message_1 != INVALID_STRING_ID) { - DrawStringMultiCenter( - 214, - 90, - _errmsg_message_1, - w->width - 2); - } - } - - /* Switch back to the normal text ref. stack for NewGRF texts */ - SwitchToNormalRefStack(); - break; +public: + ErrmsgWindow(Point pt, int width, int height, StringID msg1, StringID msg2, const Widget *widget, bool show_player_face) : + Window(pt.x, pt.y, width, height, WC_ERRMSG, widget), + show_player_face(show_player_face) + { + this->duration = _patches.errmsg_duration; + CopyOutDParam(this->decode_params, 0, lengthof(this->decode_params)); + this->message_1 = msg1; + this->message_2 = msg2; + this->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET; + this->FindWindowPlacementAndResize(width, height); + } - case WE_MOUSELOOP: - if (_right_button_down) delete w; - break; - - case WE_100_TICKS: - if (--_errmsg_duration == 0) delete w; - break; + virtual void OnPaint() + { + static int y[][3] = { + {15, 25, 30}, // _errmsg_widgets + {45, 65, 90}, // _errmsg_face_widgets + }; - case WE_DESTROY: - SetRedErrorSquare(0); - extern StringID _switch_mode_errorstr; - _switch_mode_errorstr = INVALID_STRING_ID; - break; + CopyInDParam(0, this->decode_params, lengthof(this->decode_params)); + this->DrawWidgets(); + CopyInDParam(0, this->decode_params, lengthof(this->decode_params)); - case WE_KEYPRESS: - if (e->we.keypress.keycode == WKC_SPACE) { - /* Don't continue. */ - e->we.keypress.cont = false; - delete w; - } - break; + /* If the error message comes from a NewGRF, we must use the text ref. stack reserved for error messages. + * If the message doesn't come from a NewGRF, it won't use the TTDP-style text ref. stack, so we won't hurt anything + */ + SwitchToErrorRefStack(); + RewindTextRefStack(); + + if (this->show_player_face) { + const Player *p = GetPlayer((PlayerID)GetDParamX(this->decode_params, 2)); + DrawPlayerFace(p->face, p->player_color, 2, 16); + } + + byte j = (this->message_1 == INVALID_STRING_ID) ? 1 : 0; + DrawStringMultiCenter(this->width - 120, y[this->show_player_face][j], this->message_2, this->width - 2); + if (j == 0) { + DrawStringMultiCenter(this->width - 120, y[this->show_player_face][2], this->message_1, this->width - 2); + } + + /* Switch back to the normal text ref. stack for NewGRF texts */ + SwitchToNormalRefStack(); } -} + + virtual void OnMouseLoop() + { + if (_right_button_down) delete this; + } + + virtual void OnHundredthTick() + { + if (--this->duration == 0) delete this; + } + + ~ErrmsgWindow() + { + SetRedErrorSquare(0); + extern StringID _switch_mode_errorstr; + _switch_mode_errorstr = INVALID_STRING_ID; + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + if (keycode != WKC_SPACE) return ES_NOT_HANDLED; + delete this; + return ES_HANDLED; + } +}; void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y) { DeleteWindowById(WC_ERRMSG, 0); - if (msg_2 == STR_NULL) msg_2 = STR_EMPTY; + if (!_patches.errmsg_duration) return; - _errmsg_message_1 = msg_1; - _errmsg_message_2 = msg_2; - CopyOutDParam(_errmsg_decode_params, 0, lengthof(_errmsg_decode_params)); - _errmsg_duration = _patches.errmsg_duration; - if (!_errmsg_duration) return; + if (msg_2 == STR_NULL) msg_2 = STR_EMPTY; Point pt; const ViewPort *vp; - Window *w; - if (_errmsg_message_1 != STR_013B_OWNED_BY || GetDParamX(_errmsg_decode_params,2) >= 8) { + if (msg_1 != STR_013B_OWNED_BY || GetDParam(2) >= 8) { if ((x | y) != 0) { pt = RemapCoords2(x, y); vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport; @@ -455,7 +446,7 @@ pt.x = (_screen.width - 240) >> 1; pt.y = (_screen.height - 46) >> 1; } - w = new Window(pt.x, pt.y, 240, 46, ErrmsgWndProc, WC_ERRMSG, _errmsg_widgets); + new ErrmsgWindow(pt, 240, 46, msg_1, msg_2, _errmsg_widgets, false); } else { if ((x | y) != 0) { pt = RemapCoords2(x, y); @@ -466,13 +457,10 @@ pt.x = (_screen.width - 334) >> 1; pt.y = (_screen.height - 137) >> 1; } - w = new Window(pt.x, pt.y, 334, 137, ErrmsgWndProc, WC_ERRMSG, _errmsg_face_widgets); + new ErrmsgWindow(pt, 334, 137, msg_1, msg_2, _errmsg_face_widgets, true); } - - w->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET; } - void ShowEstimatedCostOrIncome(Money cost, int x, int y) { StringID msg = STR_0805_ESTIMATED_COST; @@ -542,7 +530,7 @@ TooltipsWindow(int x, int y, int width, int height, const Widget *widget, StringID str, uint paramcount, const uint64 params[]) : - Window(x, y, width, height, NULL, WC_TOOLTIPS, widget) + Window(x, y, width, height, WC_TOOLTIPS, widget) { this->string_id = str; assert(sizeof(this->params[0]) == sizeof(params[0])); @@ -893,9 +881,9 @@ return false; } -int QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, bool &cont) +int QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, Window::EventState &state) { - cont = false; + state = Window::ES_HANDLED; switch (keycode) { case WKC_ESC: return 2; @@ -923,7 +911,7 @@ if (IsValidChar(key, this->afilter)) { if (InsertTextBufferChar(&this->text, key)) w->InvalidateWidget(wid); } else { // key wasn't caught. Continue only if standard entry specified - cont = (this->afilter == CS_ALPHANUMERAL); + state = (this->afilter == CS_ALPHANUMERAL) ? Window::ES_HANDLED : Window::ES_NOT_HANDLED; } } @@ -973,9 +961,9 @@ _cur_dpi = old_dpi; } -int QueryStringBaseWindow::HandleEditBoxKey(int wid, uint16 key, uint16 keycode, bool &cont) +int QueryStringBaseWindow::HandleEditBoxKey(int wid, uint16 key, uint16 keycode, EventState &state) { - return this->QueryString::HandleEditBoxKey(this, wid, key, keycode, cont); + return this->QueryString::HandleEditBoxKey(this, wid, key, keycode, state); } void QueryStringBaseWindow::HandleEditBox(int wid) @@ -997,10 +985,9 @@ struct QueryStringWindow : public QueryStringBaseWindow { - Window *parent; - - QueryStringWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(desc), parent(parent) + QueryStringWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(desc) { + this->parent = parent; SetBit(_no_scroll, SCROLL_EDIT); this->FindWindowPlacementAndResize(desc); @@ -1009,7 +996,7 @@ virtual void OnPaint() { SetDParam(0, this->caption); - DrawWindowWidgets(this); + this->DrawWidgets(); this->DrawEditBox(QUERY_STR_WIDGET_TEXT); } @@ -1024,6 +1011,7 @@ } else { HandleOnEditText(this->text.buf); } + this->handled = true; } } @@ -1048,15 +1036,15 @@ this->HandleEditBox(QUERY_STR_WIDGET_TEXT); } - virtual bool OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { - bool cont; - switch (this->HandleEditBoxKey(QUERY_STR_WIDGET_TEXT, key, keycode, cont)) { + EventState state; + switch (this->HandleEditBoxKey(QUERY_STR_WIDGET_TEXT, key, keycode, state)) { case 1: this->OnOk(); // Enter pressed, confirms change /* FALL THROUGH */ case 2: delete this; break; // ESC pressed, closes window, abandons changes } - return cont; + return state; } ~QueryStringWindow() @@ -1084,17 +1072,16 @@ WC_QUERY_STRING, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _query_string_widgets, - NULL }; /** Show a query popup window with a textbox in it. * @param str StringID for the text shown in the textbox * @param caption StringID of text shown in caption of querywindow * @param maxlen maximum length in characters allowed. If bit 12 is set we - * will not check the resulting string against to original string to return success + * will not check the resulting string against to original string to return success * @param maxwidth maximum width in pixels allowed * @param parent pointer to a Window that will handle the events (ok/cancel) of this - * window. If NULL, results are handled by global function HandleOnEditText + * window. If NULL, results are handled by global function HandleOnEditText * @param afilter filters out unwanted character input */ void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, Window *parent, CharSetFilter afilter) { @@ -1163,7 +1150,7 @@ virtual void OnPaint() { CopyInDParam(0, this->params, lengthof(this->params)); - DrawWindowWidgets(this); + this->DrawWidgets(); CopyInDParam(0, this->params, lengthof(this->params)); DrawStringMultiCenter(this->width / 2, (this->height / 2) - 10, this->message, this->width - 2); @@ -1184,7 +1171,7 @@ } } - virtual bool OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { /* ESC closes the window, Enter confirms the action */ switch (keycode) { @@ -1197,9 +1184,9 @@ /* Fallthrough */ case WKC_ESC: delete this; - return false; + return ES_HANDLED; } - return true; + return ES_NOT_HANDLED; } }; @@ -1218,7 +1205,6 @@ WC_CONFIRM_POPUP_QUERY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_DEF_WIDGET | WDF_MODAL, _query_widgets, - NULL }; /** Show a modal confirmation window with standard 'yes' and 'no' buttons @@ -1429,7 +1415,7 @@ int y; SetVScrollCount(this, _fios_num); - DrawWindowWidgets(this); + this->DrawWidgets(); DrawFiosTexts(this->width); if (_savegame_sort_dirty) { @@ -1438,7 +1424,7 @@ } GfxFillRect(this->widget[7].left + 1, this->widget[7].top + 1, this->widget[7].right, this->widget[7].bottom, 0xD7); - DrawSortButtonState(this, _savegame_sort_order & SORT_BY_NAME ? 2 : 3, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP); + this->DrawSortButtonState(_savegame_sort_order & SORT_BY_NAME ? 2 : 3, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP); y = this->widget[7].top + 1; for (pos = this->vscroll.pos; pos < _fios_num; pos++) { @@ -1533,20 +1519,20 @@ } } - virtual bool OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { if (keycode == WKC_ESC) { delete this; - return false; + return ES_HANDLED; } - bool cont = true; + EventState state = ES_NOT_HANDLED; if ((_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) && - this->HandleEditBoxKey(10, key, keycode, cont) == 1) { // Press Enter + this->HandleEditBoxKey(10, key, keycode, state) == 1) { // Press Enter this->HandleButtonClick(12); } - return cont; + return state; } virtual void OnTimeout() @@ -1599,7 +1585,6 @@ WC_SAVELOAD, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _load_dialog_widgets, - NULL, }; static const WindowDesc _save_dialog_desc = { @@ -1607,7 +1592,6 @@ WC_SAVELOAD, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _save_dialog_widgets, - NULL, }; /** These values are used to convert the file/operations mode into a corresponding file type. diff -r 6c4314786d68 -r 8cbdb511a674 src/music/dmusic.cpp --- a/src/music/dmusic.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/music/dmusic.cpp Mon May 19 15:13:58 2008 +0000 @@ -5,6 +5,9 @@ #ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT #include "../stdafx.h" +#ifdef WIN32_LEAN_AND_MEAN + #undef WIN32_LEAN_AND_MEAN // Don't exclude rarely-used stuff from Windows headers +#endif #include "../debug.h" #include "../win32.h" #include "dmusic.h" diff -r 6c4314786d68 -r 8cbdb511a674 src/music_gui.cpp --- a/src/music_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/music_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -193,18 +193,40 @@ } } -static void MusicTrackSelectionWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { +struct MusicTrackSelectionWindow : public Window { +private: + enum MusicTrackSelectionWidgets { + MTSW_CLOSE, + MTSW_CAPTION, + MTSW_BACKGROUND, + MTSW_LIST_LEFT, + MTSW_LIST_RIGHT, + MTSW_ALL, + MTSW_OLD, + MTSW_NEW, + MTSW_EZY, + MTSW_CUSTOM1, + MTSW_CUSTOM2, + MTSW_CLEAR, + MTSW_SAVE, + }; + +public: + MusicTrackSelectionWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number) + { + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { const byte* p; uint i; int y; - w->SetWidgetDisabledState(11, msf.playlist <= 3); - w->LowerWidget(3); - w->LowerWidget(4); - DrawWindowWidgets(w); + this->SetWidgetDisabledState(MTSW_CLEAR, msf.playlist <= 3); + this->LowerWidget(MTSW_LIST_LEFT); + this->LowerWidget(MTSW_LIST_RIGHT); + this->DrawWidgets(); GfxFillRect(3, 23, 3 + 177, 23 + 191, 0); GfxFillRect(251, 23, 251 + 177, 23 + 191, 0); @@ -238,88 +260,88 @@ DrawString(252, y, (i < 10) ? STR_01EC_0 : STR_01ED, TC_FROMSTRING); y += 6; } - break; } - case WE_CLICK: - switch (e->we.click.widget) { - case 3: { // add to playlist - int y = (e->we.click.pt.y - 23) / 6; - uint i; - byte *p; - - if (msf.playlist < 4) return; - if (!IsInsideMM(y, 0, NUM_SONGS_AVAILABLE)) return; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case MTSW_LIST_LEFT: { // add to playlist + int y = (pt.y - 23) / 6; + uint i; + byte *p; - p = _playlists[msf.playlist]; - for (i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) { - if (p[i] == 0) { - p[i] = y + 1; - p[i + 1] = 0; - w->SetDirty(); - SelectSongToPlay(); - break; + if (msf.playlist < 4) return; + if (!IsInsideMM(y, 0, NUM_SONGS_AVAILABLE)) return; + + p = _playlists[msf.playlist]; + for (i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) { + if (p[i] == 0) { + p[i] = y + 1; + p[i + 1] = 0; + this->SetDirty(); + SelectSongToPlay(); + break; + } } - } - } break; + } break; - case 4: { // remove from playlist - int y = (e->we.click.pt.y - 23) / 6; - uint i; - byte *p; + case MTSW_LIST_RIGHT: { // remove from playlist + int y = (pt.y - 23) / 6; + uint i; + byte *p; - if (msf.playlist < 4) return; - if (!IsInsideMM(y, 0, NUM_SONGS_AVAILABLE)) return; + if (msf.playlist < 4) return; + if (!IsInsideMM(y, 0, NUM_SONGS_AVAILABLE)) return; - p = _playlists[msf.playlist]; - for (i = y; i != NUM_SONGS_PLAYLIST - 1; i++) { - p[i] = p[i + 1]; + p = _playlists[msf.playlist]; + for (i = y; i != NUM_SONGS_PLAYLIST - 1; i++) { + p[i] = p[i + 1]; } - w->SetDirty(); - SelectSongToPlay(); - } break; + this->SetDirty(); + SelectSongToPlay(); + } break; - case 11: // clear - _playlists[msf.playlist][0] = 0; - w->SetDirty(); - StopMusic(); - SelectSongToPlay(); - break; + case MTSW_CLEAR: // clear + _playlists[msf.playlist][0] = 0; + this->SetDirty(); + StopMusic(); + SelectSongToPlay(); + break; #if 0 - case 12: // save - ShowInfo("MusicTrackSelectionWndProc:save not implemented"); - break; + case MTSW_SAVE: // save + ShowInfo("MusicTrackSelectionWndProc:save not implemented"); + break; #endif - case 5: case 6: case 7: case 8: case 9: case 10: /* set playlist */ - msf.playlist = e->we.click.widget - 5; - w->SetDirty(); - InvalidateWindow(WC_MUSIC_WINDOW, 0); - StopMusic(); - SelectSongToPlay(); - break; + case MTSW_ALL: case MTSW_OLD: case MTSW_NEW: + case MTSW_EZY: case MTSW_CUSTOM1: case MTSW_CUSTOM2: // set playlist + msf.playlist = widget - MTSW_ALL; + this->SetDirty(); + InvalidateWindow(WC_MUSIC_WINDOW, 0); + StopMusic(); + SelectSongToPlay(); + break; } - break; } -} +}; static const Widget _music_track_selection_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 14, 11, 431, 0, 13, STR_01EB_MUSIC_PROGRAM_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_PANEL, RESIZE_NONE, 14, 0, 431, 14, 217, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 14, 2, 181, 22, 215, 0x0, STR_01FA_CLICK_ON_MUSIC_TRACK_TO}, -{ WWT_PANEL, RESIZE_NONE, 14, 250, 429, 22, 215, 0x0, STR_CLICK_ON_TRACK_TO_REMOVE}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 44, 51, 0x0, STR_01F3_SELECT_ALL_TRACKS_PROGRAM}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 52, 59, 0x0, STR_01F4_SELECT_OLD_STYLE_MUSIC}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 60, 67, 0x0, STR_01F5_SELECT_NEW_STYLE_MUSIC}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 68, 75, 0x0, STR_0330_SELECT_EZY_STREET_STYLE}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 76, 83, 0x0, STR_01F6_SELECT_CUSTOM_1_USER_DEFINED}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 84, 91, 0x0, STR_01F7_SELECT_CUSTOM_2_USER_DEFINED}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 108, 115, 0x0, STR_01F8_CLEAR_CURRENT_PROGRAM_CUSTOM1}, +{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // MTSW_CLOSE +{ WWT_CAPTION, RESIZE_NONE, 14, 11, 431, 0, 13, STR_01EB_MUSIC_PROGRAM_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS}, // MTSW_CAPTION +{ WWT_PANEL, RESIZE_NONE, 14, 0, 431, 14, 217, 0x0, STR_NULL}, // MTSW_BACKGROUND +{ WWT_PANEL, RESIZE_NONE, 14, 2, 181, 22, 215, 0x0, STR_01FA_CLICK_ON_MUSIC_TRACK_TO}, // MTSW_LIST_LEFT +{ WWT_PANEL, RESIZE_NONE, 14, 250, 429, 22, 215, 0x0, STR_CLICK_ON_TRACK_TO_REMOVE}, // MTSW_LIST_RIGHT +{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 44, 51, 0x0, STR_01F3_SELECT_ALL_TRACKS_PROGRAM}, // MTSW_ALL +{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 52, 59, 0x0, STR_01F4_SELECT_OLD_STYLE_MUSIC}, // MTSW_OLD +{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 60, 67, 0x0, STR_01F5_SELECT_NEW_STYLE_MUSIC}, // MTSW_NEW +{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 68, 75, 0x0, STR_0330_SELECT_EZY_STREET_STYLE}, // MTSW_EZY +{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 76, 83, 0x0, STR_01F6_SELECT_CUSTOM_1_USER_DEFINED}, // MTSW_CUSTOM1 +{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 84, 91, 0x0, STR_01F7_SELECT_CUSTOM_2_USER_DEFINED}, // MTSW_CUSTOM2 +{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 108, 115, 0x0, STR_01F8_CLEAR_CURRENT_PROGRAM_CUSTOM1}, // MTSW_CLEAR #if 0 -{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 124, 131, 0x0, STR_01F9_SAVE_MUSIC_SETTINGS}, +{ WWT_PUSHBTN, RESIZE_NONE, 14, 186, 245, 124, 131, 0x0, STR_01F9_SAVE_MUSIC_SETTINGS}, // MTSW_SAVE #endif { WIDGETS_END}, }; @@ -329,24 +351,50 @@ WC_MUSIC_TRACK_SELECTION, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _music_track_selection_widgets, - MusicTrackSelectionWndProc }; static void ShowMusicTrackSelection() { - AllocateWindowDescFront(&_music_track_selection_desc, 0); + AllocateWindowDescFront(&_music_track_selection_desc, 0); } -static void MusicWindowWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { +struct MusicWindow : public Window { +private: + enum MusicWidgets { + MW_CLOSE, + MW_CAPTION, + MW_PREV, + MW_NEXT, + MW_STOP, + MW_PLAY, + MW_SLIDERS, + MW_GAUGE, + MW_BACKGROUND, + MW_INFO, + MW_SHUFFLE, + MW_PROGRAMME, + MW_ALL, + MW_OLD, + MW_NEW, + MW_EZY, + MW_CUSTOM1, + MW_CUSTOM2, + }; + +public: + MusicWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number) + { + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { uint i; StringID str; - w->RaiseWidget(7); - w->RaiseWidget(9); - DrawWindowWidgets(w); + this->RaiseWidget(MW_GAUGE); + this->RaiseWidget(MW_INFO); + this->DrawWidgets(); GfxFillRect(187, 16, 200, 33, 0); @@ -403,92 +451,99 @@ DrawFrameRect( 214 + msf.effect_vol / 2, 22, 217 + msf.effect_vol / 2, 28, 14, FR_NONE ); - } break; - - case WE_CLICK: - switch (e->we.click.widget) { - case 2: // skip to prev - if (!_song_is_active) - return; - SkipToPrevSong(); - break; - case 3: // skip to next - if (!_song_is_active) - return; - SkipToNextSong(); - break; - case 4: // stop playing - msf.playing = false; - break; - case 5: // start playing - msf.playing = true; - break; - case 6: { // volume sliders - byte *vol, new_vol; - int x = e->we.click.pt.x - 88; - - if (x < 0) return; - - vol = &msf.music_vol; - if (x >= 106) { - vol = &msf.effect_vol; - x -= 106; - } - - new_vol = min(max(x - 21, 0) * 2, 127); - if (new_vol != *vol) { - *vol = new_vol; - if (vol == &msf.music_vol) - MusicVolumeChanged(new_vol); - w->SetDirty(); - } - - _left_button_clicked = false; - } break; - case 10: //toggle shuffle - msf.shuffle ^= 1; - StopMusic(); - SelectSongToPlay(); - break; - case 11: //show track selection - ShowMusicTrackSelection(); - break; - case 12: case 13: case 14: case 15: case 16: case 17: // playlist - msf.playlist = e->we.click.widget - 12; - w->SetDirty(); - InvalidateWindow(WC_MUSIC_TRACK_SELECTION, 0); - StopMusic(); - SelectSongToPlay(); - break; - } - break; - - case WE_TICK: - InvalidateWindowWidget(WC_MUSIC_WINDOW, 0, 7); - break; } -} + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case MW_PREV: // skip to prev + if (!_song_is_active) return; + SkipToPrevSong(); + break; + + case MW_NEXT: // skip to next + if (!_song_is_active) return; + SkipToNextSong(); + break; + + case MW_STOP: // stop playing + msf.playing = false; + break; + + case MW_PLAY: // start playing + msf.playing = true; + break; + + case MW_SLIDERS: { // volume sliders + byte *vol, new_vol; + int x = pt.x - 88; + + if (x < 0) return; + + vol = &msf.music_vol; + if (x >= 106) { + vol = &msf.effect_vol; + x -= 106; + } + + new_vol = min(max(x - 21, 0) * 2, 127); + if (new_vol != *vol) { + *vol = new_vol; + if (vol == &msf.music_vol) MusicVolumeChanged(new_vol); + this->SetDirty(); + } + + _left_button_clicked = false; + } break; + + case MW_SHUFFLE: //toggle shuffle + msf.shuffle ^= 1; + StopMusic(); + SelectSongToPlay(); + break; + + case MW_PROGRAMME: //show track selection + ShowMusicTrackSelection(); + break; + + case MW_ALL: case MW_OLD: case MW_NEW: + case MW_EZY: case MW_CUSTOM1: case MW_CUSTOM2: // playlist + msf.playlist = widget - MW_ALL; + this->SetDirty(); + InvalidateWindow(WC_MUSIC_TRACK_SELECTION, 0); + StopMusic(); + SelectSongToPlay(); + break; + } + } + +#if 0 + virtual void OnTick() + { + this->InvalidateWidget(MW_GAUGE); + } +#endif +}; static const Widget _music_window_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 14, 11, 299, 0, 13, STR_01D2_JAZZ_JUKEBOX, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 0, 21, 14, 35, SPR_IMG_SKIP_TO_PREV, STR_01DE_SKIP_TO_PREVIOUS_TRACK}, -{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 22, 43, 14, 35, SPR_IMG_SKIP_TO_NEXT, STR_01DF_SKIP_TO_NEXT_TRACK_IN_SELECTION}, -{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 44, 65, 14, 35, SPR_IMG_STOP_MUSIC, STR_01E0_STOP_PLAYING_MUSIC}, -{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 66, 87, 14, 35, SPR_IMG_PLAY_MUSIC, STR_01E1_START_PLAYING_MUSIC}, -{ WWT_PANEL, RESIZE_NONE, 14, 88, 299, 14, 35, 0x0, STR_01E2_DRAG_SLIDERS_TO_SET_MUSIC}, -{ WWT_PANEL, RESIZE_NONE, 14, 186, 201, 15, 34, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 14, 0, 299, 36, 57, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 14, 59, 240, 45, 53, 0x0, STR_NULL}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 6, 55, 42, 49, 0x0, STR_01FB_TOGGLE_PROGRAM_SHUFFLE}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 244, 293, 42, 49, 0x0, STR_01FC_SHOW_MUSIC_TRACK_SELECTION}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 0, 49, 58, 65, 0x0, STR_01F3_SELECT_ALL_TRACKS_PROGRAM}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 50, 99, 58, 65, 0x0, STR_01F4_SELECT_OLD_STYLE_MUSIC}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 100, 149, 58, 65, 0x0, STR_01F5_SELECT_NEW_STYLE_MUSIC}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 150, 199, 58, 65, 0x0, STR_0330_SELECT_EZY_STREET_STYLE}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 200, 249, 58, 65, 0x0, STR_01F6_SELECT_CUSTOM_1_USER_DEFINED}, -{ WWT_PUSHBTN, RESIZE_NONE, 14, 250, 299, 58, 65, 0x0, STR_01F7_SELECT_CUSTOM_2_USER_DEFINED}, +{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // MW_CLOSE +{ WWT_CAPTION, RESIZE_NONE, 14, 11, 299, 0, 13, STR_01D2_JAZZ_JUKEBOX, STR_018C_WINDOW_TITLE_DRAG_THIS}, // MW_CAPTION +{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 0, 21, 14, 35, SPR_IMG_SKIP_TO_PREV, STR_01DE_SKIP_TO_PREVIOUS_TRACK}, // MW_PREV +{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 22, 43, 14, 35, SPR_IMG_SKIP_TO_NEXT, STR_01DF_SKIP_TO_NEXT_TRACK_IN_SELECTION}, // MW_NEXT +{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 44, 65, 14, 35, SPR_IMG_STOP_MUSIC, STR_01E0_STOP_PLAYING_MUSIC}, // MW_STOP +{ WWT_PUSHIMGBTN, RESIZE_NONE, 14, 66, 87, 14, 35, SPR_IMG_PLAY_MUSIC, STR_01E1_START_PLAYING_MUSIC}, // MW_PLAY +{ WWT_PANEL, RESIZE_NONE, 14, 88, 299, 14, 35, 0x0, STR_01E2_DRAG_SLIDERS_TO_SET_MUSIC}, // MW_SLIDERS +{ WWT_PANEL, RESIZE_NONE, 14, 186, 201, 15, 34, 0x0, STR_NULL}, // MW_GAUGE +{ WWT_PANEL, RESIZE_NONE, 14, 0, 299, 36, 57, 0x0, STR_NULL}, // MW_BACKGROUND +{ WWT_PANEL, RESIZE_NONE, 14, 59, 240, 45, 53, 0x0, STR_NULL}, // MW_INFO +{ WWT_PUSHBTN, RESIZE_NONE, 14, 6, 55, 42, 49, 0x0, STR_01FB_TOGGLE_PROGRAM_SHUFFLE}, // MW_SHUFFLE +{ WWT_PUSHBTN, RESIZE_NONE, 14, 244, 293, 42, 49, 0x0, STR_01FC_SHOW_MUSIC_TRACK_SELECTION}, // MW_PROGRAMME +{ WWT_PUSHBTN, RESIZE_NONE, 14, 0, 49, 58, 65, 0x0, STR_01F3_SELECT_ALL_TRACKS_PROGRAM}, // MW_ALL +{ WWT_PUSHBTN, RESIZE_NONE, 14, 50, 99, 58, 65, 0x0, STR_01F4_SELECT_OLD_STYLE_MUSIC}, // MW_OLD +{ WWT_PUSHBTN, RESIZE_NONE, 14, 100, 149, 58, 65, 0x0, STR_01F5_SELECT_NEW_STYLE_MUSIC}, // MW_NEW +{ WWT_PUSHBTN, RESIZE_NONE, 14, 150, 199, 58, 65, 0x0, STR_0330_SELECT_EZY_STREET_STYLE}, // MW_EZY +{ WWT_PUSHBTN, RESIZE_NONE, 14, 200, 249, 58, 65, 0x0, STR_01F6_SELECT_CUSTOM_1_USER_DEFINED}, // MW_CUSTOM1 +{ WWT_PUSHBTN, RESIZE_NONE, 14, 250, 299, 58, 65, 0x0, STR_01F7_SELECT_CUSTOM_2_USER_DEFINED}, // MW_CUSTOM2 { WIDGETS_END}, }; @@ -497,10 +552,9 @@ WC_MUSIC_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _music_window_widgets, - MusicWindowWndProc }; void ShowMusicWindow() { - AllocateWindowDescFront(&_music_window_desc, 0); + AllocateWindowDescFront(&_music_window_desc, 0); } diff -r 6c4314786d68 -r 8cbdb511a674 src/network/network_gui.cpp --- a/src/network/network_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/network/network_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -30,6 +30,7 @@ #include "../settings_type.h" #include "../widgets/dropdown_func.h" #include "../querystring_gui.h" +#include "../sortlist_type.h" #include "table/strings.h" #include "../table/sprites.h" @@ -318,7 +319,7 @@ SetDParam(0, 0x00); SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]); - DrawWindowWidgets(this); + this->DrawWidgets(); /* Edit box to set player name */ this->DrawEditBox(NGWW_PLAYER); @@ -327,9 +328,9 @@ /* Sort based on widgets: name, clients, compatibility */ switch (this->servers.sort_type) { - case NGWW_NAME - NGWW_NAME: DrawSortButtonState(this, NGWW_NAME, arrow); break; - case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(this, NGWW_CLIENTS, arrow); break; - case NGWW_INFO - NGWW_NAME: DrawSortButtonState(this, NGWW_INFO, arrow); break; + case NGWW_NAME - NGWW_NAME: this->DrawSortButtonState(NGWW_NAME, arrow); break; + case NGWW_CLIENTS - NGWW_NAME: this->DrawSortButtonState(NGWW_CLIENTS, arrow); break; + case NGWW_INFO - NGWW_NAME: this->DrawSortButtonState(NGWW_INFO, arrow); break; } uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; @@ -542,9 +543,9 @@ this->SetDirty(); } - virtual bool OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { - bool cont = true; + EventState state = ES_NOT_HANDLED; if (this->field != NGWW_PLAYER) { if (this->server != NULL) { if (keycode == WKC_DELETE) { // Press 'delete' to remove servers @@ -553,10 +554,10 @@ this->server = NULL; } } - return cont; + return state; } - if (this->HandleEditBoxKey(NGWW_PLAYER, keycode, key, cont) == 1) return cont; // enter pressed + if (this->HandleEditBoxKey(NGWW_PLAYER, keycode, key, state) == 1) return state; // enter pressed /* The name is only allowed when it starts with a letter! */ if (StrEmpty(this->edit_str_buf) && this->edit_str_buf[0] != ' ') { @@ -564,7 +565,7 @@ } else { ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name)); } - return cont; + return state; } virtual void OnQueryTextFinished(char *str) @@ -641,7 +642,6 @@ WC_NETWORK_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _network_game_window_widgets, - NULL, }; void ShowNetworkGameWindow() @@ -727,7 +727,7 @@ SetDParam(3, _network_game_info.companies_max); SetDParam(4, _network_game_info.spectators_max); SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); - DrawWindowWidgets(this); + this->DrawWidgets(); /* editbox to set game name */ this->DrawEditBox(NSSW_GAMENAME); @@ -891,16 +891,16 @@ if (this->field == NSSW_GAMENAME) this->HandleEditBox(NSSW_GAMENAME); } - virtual bool OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { - bool cont = true; + EventState state = ES_NOT_HANDLED; if (this->field == NSSW_GAMENAME) { - if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, cont) == 1) return cont; // enter pressed + if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, state) == 1) return state; // enter pressed ttd_strlcpy(_network_server_name, this->text.buf, sizeof(_network_server_name)); } - return cont; + return state; } virtual void OnQueryTextFinished(char *str) @@ -976,7 +976,6 @@ WC_NETWORK_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _network_start_server_window_widgets, - NULL, }; static void ShowNetworkStartServerWindow() @@ -1036,7 +1035,7 @@ /* Draw window widgets */ SetDParamStr(0, gi->server_name); - DrawWindowWidgets(this); + this->DrawWidgets(); /* Draw company list */ pos = this->vscroll.pos; @@ -1190,7 +1189,6 @@ WC_NETWORK_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _network_lobby_window_widgets, - NULL, }; /* Show the networklobbywindow with the selected server @@ -1217,14 +1215,6 @@ // Max 10 actions per client #define MAX_CLIENTLIST_ACTION 10 -// Some standard bullshit.. defines variables ;) -static void ClientListWndProc(Window *w, WindowEvent *e); -static void ClientListPopupWndProc(Window *w, WindowEvent *e); -static byte _selected_clientlist_item = 255; -static byte _selected_clientlist_y = 0; -static char _clientlist_action[MAX_CLIENTLIST_ACTION][50]; -static ClientList_Action_Proc *_clientlist_proc[MAX_CLIENTLIST_ACTION]; - enum { CLNWND_OFFSET = 16, CLNWND_ROWSIZE = 10 @@ -1244,12 +1234,11 @@ { WIDGETS_END}, }; -static WindowDesc _client_list_desc = { +static const WindowDesc _client_list_desc = { WDP_AUTO, WDP_AUTO, 250, 1, 250, 1, WC_CLIENT_LIST, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _client_list_widgets, - ClientListWndProc }; // Finds the Xth client-info that is active @@ -1321,267 +1310,271 @@ -/** - * An action is clicked! What do we do? - */ -static void HandleClientListPopupClick(byte index, byte clientno) -{ - /* A click on the Popup of the ClientList.. handle the command */ - if (index < MAX_CLIENTLIST_ACTION && _clientlist_proc[index] != NULL) { - _clientlist_proc[index](clientno); - } -} +struct NetworkClientListPopupWindow : Window { + int sel_index; + int client_no; + char action[MAX_CLIENTLIST_ACTION][50]; + ClientList_Action_Proc *proc[MAX_CLIENTLIST_ACTION]; -/** - * Finds the amount of clients and set the height correct - */ -static bool CheckClientListHeight(Window *w) -{ - int num = 0; - const NetworkClientInfo *ci; + NetworkClientListPopupWindow(int x, int y, const Widget *widgets, int client_no) : + Window(x, y, 150, 100, WC_TOOLBAR_MENU, widgets), + sel_index(0), client_no(client_no) + { + /* + * Fill the actions this client has. + * Watch is, max 50 chars long! + */ - /* Should be replaced with a loop through all clients */ - FOR_ALL_ACTIVE_CLIENT_INFOS(ci) { - num++; + const NetworkClientInfo *ci = NetworkFindClientInfo(client_no); + + int i = 0; + if (_network_own_client_index != ci->client_index) { + GetString(this->action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, lastof(this->action[i])); + this->proc[i++] = &ClientList_SpeakToClient; + } + + if (IsValidPlayer(ci->client_playas) || ci->client_playas == PLAYER_SPECTATOR) { + GetString(this->action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, lastof(this->action[i])); + this->proc[i++] = &ClientList_SpeakToCompany; + } + GetString(this->action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, lastof(this->action[i])); + this->proc[i++] = &ClientList_SpeakToAll; + + if (_network_own_client_index != ci->client_index) { + /* We are no spectator and the player we want to give money to is no spectator and money gifts are allowed */ + if (IsValidPlayer(_network_playas) && IsValidPlayer(ci->client_playas) && _patches.give_money) { + GetString(this->action[i], STR_NETWORK_CLIENTLIST_GIVE_MONEY, lastof(this->action[i])); + this->proc[i++] = &ClientList_GiveMoney; + } + } + + /* A server can kick clients (but not himself) */ + if (_network_server && _network_own_client_index != ci->client_index) { + GetString(this->action[i], STR_NETWORK_CLIENTLIST_KICK, lastof(this->action[i])); + this->proc[i++] = &ClientList_Kick; + + sprintf(this->action[i],"Ban"); // XXX GetString? + this->proc[i++] = &ClientList_Ban; + } + + if (i == 0) { + GetString(this->action[i], STR_NETWORK_CLIENTLIST_NONE, lastof(this->action[i])); + this->proc[i++] = &ClientList_None; + } + + /* Calculate the height */ + int h = ClientListPopupHeight(); + + /* Allocate the popup */ + this->widget[0].bottom = this->widget[0].top + h; + this->widget[0].right = this->widget[0].left + 150; + + this->flags4 &= ~WF_WHITE_BORDER_MASK; + + this->FindWindowPlacementAndResize(150, h + 1); } - num *= CLNWND_ROWSIZE; - - /* If height is changed */ - if (w->height != CLNWND_OFFSET + num + 1) { - // XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1) - w->SetDirty(); - w->widget[3].bottom = w->widget[3].top + num + 2; - w->height = CLNWND_OFFSET + num + 1; - w->SetDirty(); - return false; - } - return true; -} - -/** - * Finds the amount of actions in the popup and set the height correct - */ -static uint ClientListPopupHeight() -{ - int num = 0; - - // Find the amount of actions - for (int i = 0; i < MAX_CLIENTLIST_ACTION; i++) { - if (_clientlist_action[i][0] == '\0') continue; - if (_clientlist_proc[i] == NULL) continue; - num++; + /** + * An action is clicked! What do we do? + */ + void HandleClientListPopupClick(byte index) + { + /* A click on the Popup of the ClientList.. handle the command */ + if (index < MAX_CLIENTLIST_ACTION && this->proc[index] != NULL) { + this->proc[index](this->client_no); + } } - num *= CLNWND_ROWSIZE; + /** + * Finds the amount of actions in the popup and set the height correct + */ + uint ClientListPopupHeight() + { + int num = 0; - return num + 1; -} + // Find the amount of actions + for (int i = 0; i < MAX_CLIENTLIST_ACTION; i++) { + if (this->action[i][0] == '\0') continue; + if (this->proc[i] == NULL) continue; + num++; + } + + num *= CLNWND_ROWSIZE; + + return num + 1; + } + + + virtual void OnPaint() + { + this->DrawWidgets(); + + /* Draw the actions */ + int sel = this->sel_index; + int y = 1; + for (int i = 0; i < MAX_CLIENTLIST_ACTION; i++, y += CLNWND_ROWSIZE) { + if (this->action[i][0] == '\0') continue; + if (this->proc[i] == NULL) continue; + + TextColour colour; + if (sel-- == 0) { // Selected item, highlight it + GfxFillRect(1, y, 150 - 2, y + CLNWND_ROWSIZE - 1, 0); + colour = TC_WHITE; + } else { + colour = TC_BLACK; + } + + DoDrawString(this->action[i], 4, y, colour); + } + } + + virtual void OnMouseLoop() + { + /* We selected an action */ + int index = (_cursor.pos.y - this->top) / CLNWND_ROWSIZE; + + if (_left_button_down) { + if (index == -1 || index == this->sel_index) return; + + this->sel_index = index; + this->SetDirty(); + } else { + if (index >= 0 && _cursor.pos.y >= this->top) { + HandleClientListPopupClick(index); + } + + DeleteWindowById(WC_TOOLBAR_MENU, 0); + } + } +}; /** * Show the popup (action list) */ -static Window *PopupClientList(Window *w, int client_no, int x, int y) +static void PopupClientList(int client_no, int x, int y) { - int i; - const NetworkClientInfo *ci; DeleteWindowById(WC_TOOLBAR_MENU, 0); - /* Clean the current actions */ - for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) { - _clientlist_action[i][0] = '\0'; - _clientlist_proc[i] = NULL; - } - - /* - * Fill the actions this client has. - * Watch is, max 50 chars long! - */ - - ci = NetworkFindClientInfo(client_no); - if (ci == NULL) return NULL; - - i = 0; - if (_network_own_client_index != ci->client_index) { - GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, lastof(_clientlist_action[i])); - _clientlist_proc[i++] = &ClientList_SpeakToClient; - } - - if (IsValidPlayer(ci->client_playas) || ci->client_playas == PLAYER_SPECTATOR) { - GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, lastof(_clientlist_action[i])); - _clientlist_proc[i++] = &ClientList_SpeakToCompany; - } - GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, lastof(_clientlist_action[i])); - _clientlist_proc[i++] = &ClientList_SpeakToAll; - - if (_network_own_client_index != ci->client_index) { - /* We are no spectator and the player we want to give money to is no spectator and money gifts are allowed */ - if (IsValidPlayer(_network_playas) && IsValidPlayer(ci->client_playas) && _patches.give_money) { - GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_GIVE_MONEY, lastof(_clientlist_action[i])); - _clientlist_proc[i++] = &ClientList_GiveMoney; - } - } - - /* A server can kick clients (but not himself) */ - if (_network_server && _network_own_client_index != ci->client_index) { - GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_KICK, lastof(_clientlist_action[i])); - _clientlist_proc[i++] = &ClientList_Kick; - - sprintf(_clientlist_action[i],"Ban"); // XXX GetString? - _clientlist_proc[i++] = &ClientList_Ban; - } - - if (i == 0) { - GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_NONE, lastof(_clientlist_action[i])); - _clientlist_proc[i++] = &ClientList_None; - } - - /* Calculate the height */ - int h = ClientListPopupHeight(); + if (NetworkFindClientInfo(client_no) == NULL) return; - /* Allocate the popup */ - w = new Window(x, y, 150, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets); - w->widget[0].bottom = w->widget[0].top + h; - w->widget[0].right = w->widget[0].left + 150; - - w->flags4 &= ~WF_WHITE_BORDER_MASK; - WP(w, menu_d).item_count = 0; - // Save our client - WP(w, menu_d).main_button = client_no; - WP(w, menu_d).sel_index = 0; - - return w; -} - -/** Main handle for the client popup list - * uses menu_d WP macro */ -static void ClientListPopupWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - DrawWindowWidgets(w); - - /* Draw the actions */ - int sel = WP(w, menu_d).sel_index; - int y = 1; - for (int i = 0; i < MAX_CLIENTLIST_ACTION; i++, y += CLNWND_ROWSIZE) { - if (_clientlist_action[i][0] == '\0') continue; - if (_clientlist_proc[i] == NULL) continue; - - TextColour colour; - if (sel-- == 0) { // Selected item, highlight it - GfxFillRect(1, y, 150 - 2, y + CLNWND_ROWSIZE - 1, 0); - colour = TC_WHITE; - } else { - colour = TC_BLACK; - } - - DoDrawString(_clientlist_action[i], 4, y, colour); - } - } break; - - case WE_MOUSELOOP: { - /* We selected an action */ - int index = (_cursor.pos.y - w->top) / CLNWND_ROWSIZE; - - if (_left_button_down) { - if (index == -1 || index == WP(w, menu_d).sel_index) return; - - WP(w, menu_d).sel_index = index; - w->SetDirty(); - } else { - if (index >= 0 && _cursor.pos.y >= w->top) { - HandleClientListPopupClick(index, WP(w, menu_d).main_button); - } - - DeleteWindowById(WC_TOOLBAR_MENU, 0); - } - } break; - } + new NetworkClientListPopupWindow(x, y, _client_list_popup_widgets, client_no); } /** * Main handle for clientlist */ -static void ClientListWndProc(Window *w, WindowEvent *e) +struct NetworkClientListWindow : Window { - switch (e->event) { - case WE_PAINT: { - NetworkClientInfo *ci; - int i = 0; - - /* Check if we need to reset the height */ - if (!CheckClientListHeight(w)) break; - - DrawWindowWidgets(w); - - int y = CLNWND_OFFSET; - - FOR_ALL_ACTIVE_CLIENT_INFOS(ci) { - TextColour colour; - if (_selected_clientlist_item == i++) { // Selected item, highlight it - GfxFillRect(1, y, 248, y + CLNWND_ROWSIZE - 1, 0); - colour = TC_WHITE; - } else { - colour = TC_BLACK; - } - - if (ci->client_index == NETWORK_SERVER_INDEX) { - DrawString(4, y, STR_NETWORK_SERVER, colour); - } else { - DrawString(4, y, STR_NETWORK_CLIENT, colour); - } + byte selected_item; + byte selected_y; - /* Filter out spectators */ - if (IsValidPlayer(ci->client_playas)) DrawPlayerIcon(ci->client_playas, 64, y + 1); - - DoDrawString(ci->client_name, 81, y, colour); - - y += CLNWND_ROWSIZE; - } - } break; - - case WE_CLICK: - /* Show the popup with option */ - if (_selected_clientlist_item != 255) { - PopupClientList(w, _selected_clientlist_item, e->we.click.pt.x + w->left, e->we.click.pt.y + w->top); - } - break; + NetworkClientListWindow(const WindowDesc *desc, WindowNumber window_number) : + Window(desc, window_number), + selected_item(0), + selected_y(255) + { + this->FindWindowPlacementAndResize(desc); + } - case WE_MOUSEOVER: - /* -1 means we left the current window */ - if (e->we.mouseover.pt.y == -1) { - _selected_clientlist_y = 0; - _selected_clientlist_item = 255; - w->SetDirty(); - break; - } - /* It did not change.. no update! */ - if (e->we.mouseover.pt.y == _selected_clientlist_y) break; + /** + * Finds the amount of clients and set the height correct + */ + bool CheckClientListHeight() + { + int num = 0; + const NetworkClientInfo *ci; - /* Find the new selected item (if any) */ - _selected_clientlist_y = e->we.mouseover.pt.y; - if (e->we.mouseover.pt.y > CLNWND_OFFSET) { - _selected_clientlist_item = (e->we.mouseover.pt.y - CLNWND_OFFSET) / CLNWND_ROWSIZE; + /* Should be replaced with a loop through all clients */ + FOR_ALL_ACTIVE_CLIENT_INFOS(ci) { + num++; + } + + num *= CLNWND_ROWSIZE; + + /* If height is changed */ + if (this->height != CLNWND_OFFSET + num + 1) { + // XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1) + this->SetDirty(); + this->widget[3].bottom = this->widget[3].top + num + 2; + this->height = CLNWND_OFFSET + num + 1; + this->SetDirty(); + return false; + } + return true; + } + + virtual void OnPaint() + { + NetworkClientInfo *ci; + int i = 0; + + /* Check if we need to reset the height */ + if (!this->CheckClientListHeight()) return; + + this->DrawWidgets(); + + int y = CLNWND_OFFSET; + + FOR_ALL_ACTIVE_CLIENT_INFOS(ci) { + TextColour colour; + if (this->selected_item == i++) { // Selected item, highlight it + GfxFillRect(1, y, 248, y + CLNWND_ROWSIZE - 1, 0); + colour = TC_WHITE; } else { - _selected_clientlist_item = 255; + colour = TC_BLACK; } - /* Repaint */ - w->SetDirty(); - break; + if (ci->client_index == NETWORK_SERVER_INDEX) { + DrawString(4, y, STR_NETWORK_SERVER, colour); + } else { + DrawString(4, y, STR_NETWORK_CLIENT, colour); + } - case WE_DESTROY: case WE_CREATE: - /* When created or destroyed, data is reset */ - _selected_clientlist_item = 255; - _selected_clientlist_y = 0; - break; + /* Filter out spectators */ + if (IsValidPlayer(ci->client_playas)) DrawPlayerIcon(ci->client_playas, 64, y + 1); + + DoDrawString(ci->client_name, 81, y, colour); + + y += CLNWND_ROWSIZE; + } } -} + + virtual void OnClick(Point pt, int widget) + { + /* Show the popup with option */ + if (this->selected_item != 255) { + PopupClientList(this->selected_item, pt.x + this->left, pt.y + this->top); + } + } + + virtual void OnMouseOver(Point pt, int widget) + { + /* -1 means we left the current window */ + if (pt.y == -1) { + this->selected_y = 0; + this->selected_item = 255; + this->SetDirty(); + return; + } + /* It did not change.. no update! */ + if (pt.y == this->selected_y) return; + + /* Find the new selected item (if any) */ + this->selected_y = pt.y; + if (pt.y > CLNWND_OFFSET) { + this->selected_item = (pt.y - CLNWND_OFFSET) / CLNWND_ROWSIZE; + } else { + this->selected_item = 255; + } + + /* Repaint */ + this->SetDirty(); + } +}; void ShowClientList() { - AllocateWindowDescFront(&_client_list_desc, 0); + AllocateWindowDescFront(&_client_list_desc, 0); } @@ -1601,56 +1594,60 @@ ShowQueryString(STR_EMPTY, caption, 20, 180, FindWindowById(WC_NETWORK_STATUS_WINDOW, 0), CS_ALPHANUMERAL); } - -static void NetworkJoinStatusWindowWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - uint8 progress; // used for progress bar - DrawWindowWidgets(w); +struct NetworkJoinStatusWindow : Window { + NetworkJoinStatusWindow(const WindowDesc *desc) : Window(desc) + { + this->parent = FindWindowById(WC_NETWORK_WINDOW, 0); + } - DrawStringCentered(125, 35, STR_NETWORK_CONNECTING_1 + _network_join_status, TC_GREY); - switch (_network_join_status) { - case NETWORK_JOIN_STATUS_CONNECTING: case NETWORK_JOIN_STATUS_AUTHORIZING: - case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO: - progress = 10; // first two stages 10% - break; - case NETWORK_JOIN_STATUS_WAITING: - SetDParam(0, _network_join_waiting); - DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_WAITING, TC_GREY); - progress = 15; // third stage is 15% - break; - case NETWORK_JOIN_STATUS_DOWNLOADING: - SetDParam(0, _network_join_kbytes); - SetDParam(1, _network_join_kbytes_total); - DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_DOWNLOADING, TC_GREY); - /* Fallthrough */ - default: /* Waiting is 15%, so the resting receivement of map is maximum 70% */ - progress = 15 + _network_join_kbytes * (100 - 15) / _network_join_kbytes_total; - } + virtual void OnPaint() + { + uint8 progress; // used for progress bar + this->DrawWidgets(); - /* Draw nice progress bar :) */ - DrawFrameRect(20, 18, (int)((w->width - 20) * progress / 100), 28, 10, FR_NONE); - } break; + DrawStringCentered(125, 35, STR_NETWORK_CONNECTING_1 + _network_join_status, TC_GREY); + switch (_network_join_status) { + case NETWORK_JOIN_STATUS_CONNECTING: case NETWORK_JOIN_STATUS_AUTHORIZING: + case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO: + progress = 10; // first two stages 10% + break; + case NETWORK_JOIN_STATUS_WAITING: + SetDParam(0, _network_join_waiting); + DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_WAITING, TC_GREY); + progress = 15; // third stage is 15% + break; + case NETWORK_JOIN_STATUS_DOWNLOADING: + SetDParam(0, _network_join_kbytes); + SetDParam(1, _network_join_kbytes_total); + DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_DOWNLOADING, TC_GREY); + /* Fallthrough */ + default: /* Waiting is 15%, so the resting receivement of map is maximum 70% */ + progress = 15 + _network_join_kbytes * (100 - 15) / _network_join_kbytes_total; + } - case WE_CLICK: - if (e->we.click.widget == 2) { //Disconnect button - NetworkDisconnect(); - SwitchMode(SM_MENU); - ShowNetworkGameWindow(); - } - break; + /* Draw nice progress bar :) */ + DrawFrameRect(20, 18, (int)((this->width - 20) * progress / 100), 28, 10, FR_NONE); + } - case WE_ON_EDIT_TEXT: - if (StrEmpty(e->we.edittext.str)) { - NetworkDisconnect(); - ShowNetworkGameWindow(); - } else { - SEND_COMMAND(PACKET_CLIENT_PASSWORD)(pw_type, e->we.edittext.str); - } - break; + virtual void OnClick(Point pt, int widget) + { + if (widget == 2) { //Disconnect button + NetworkDisconnect(); + SwitchMode(SM_MENU); + ShowNetworkGameWindow(); + } } -} + + virtual void OnQueryTextFinished(char *str) + { + if (StrEmpty(str)) { + NetworkDisconnect(); + ShowNetworkGameWindow(); + } else { + SEND_COMMAND(PACKET_CLIENT_PASSWORD)(pw_type, str); + } + } +}; static const Widget _network_join_status_window_widget[] = { { WWT_CAPTION, RESIZE_NONE, 14, 0, 249, 0, 13, STR_NETWORK_CONNECTING, STR_018C_WINDOW_TITLE_DRAG_THIS}, @@ -1664,15 +1661,12 @@ WC_NETWORK_STATUS_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_MODAL, _network_join_status_window_widget, - NetworkJoinStatusWindowWndProc, }; void ShowJoinStatusWindow() { DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); - Window *w = new Window(&_network_join_status_window_desc); - /* Parent the status window to the lobby */ - if (w != NULL) w->parent = FindWindowById(WC_NETWORK_WINDOW, 0); + new NetworkJoinStatusWindow(&_network_join_status_window_desc); } static void SendChat(const char *buf, DestType type, int dest) @@ -1853,7 +1847,7 @@ STR_NETWORK_CHAT_CLIENT_CAPTION }; - DrawWindowWidgets(this); + this->DrawWidgets(); assert((uint)this->dtype < lengthof(chat_captions)); DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK); @@ -1879,21 +1873,21 @@ this->HandleEditBox(2); } - virtual bool OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { - bool cont = true; + EventState state = ES_NOT_HANDLED; if (keycode == WKC_TAB) { ChatTabCompletion(); } else { _chat_tab_completion_active = false; - switch (this->HandleEditBoxKey(2, key, keycode, cont)) { + switch (this->HandleEditBoxKey(2, key, keycode, state)) { case 1: /* Return */ SendChat(this->text.buf, this->dtype, this->dest); /* FALLTHROUGH */ case 2: /* Escape */ delete this; break; } } - return cont; + return state; } }; @@ -1910,7 +1904,6 @@ WC_SEND_NETWORK_MSG, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET, _chat_window_widgets, - NULL }; void ShowNetworkChatQueryWindow(DestType type, int dest) @@ -1932,8 +1925,9 @@ }; struct NetworkCompanyPasswordWindow : public QueryStringBaseWindow { - NetworkCompanyPasswordWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) + NetworkCompanyPasswordWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(desc) { + this->parent = parent; this->afilter = CS_ALPHANUMERAL; InitializeTextBuffer(&this->text, this->edit_str_buf, min(lengthof(_network_default_company_pass), lengthof(this->edit_str_buf)), 0); @@ -1954,7 +1948,7 @@ virtual void OnPaint() { - DrawWindowWidgets(this); + this->DrawWidgets(); this->DrawEditBox(4); } @@ -1985,10 +1979,10 @@ this->HandleEditBox(4); } - virtual bool OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { - bool cont; - switch (this->HandleEditBoxKey(4, key, keycode, cont)) { + EventState state; + switch (this->HandleEditBoxKey(4, key, keycode, state)) { case 1: // Return this->OnOk(); /* FALL THROUGH */ @@ -1997,7 +1991,7 @@ delete this; break; } - return cont; + return state; } }; @@ -2018,14 +2012,13 @@ WC_COMPANY_PASSWORD_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON, _ncp_window_widgets, - NULL }; -void ShowNetworkCompanyPasswordWindow() +void ShowNetworkCompanyPasswordWindow(Window *parent) { DeleteWindowById(WC_COMPANY_PASSWORD_WINDOW, 0); - new NetworkCompanyPasswordWindow(&_ncp_window_desc); + new NetworkCompanyPasswordWindow(&_ncp_window_desc, parent); } #endif /* ENABLE_NETWORK */ diff -r 6c4314786d68 -r 8cbdb511a674 src/network/network_gui.h --- a/src/network/network_gui.h Mon May 19 14:14:33 2008 +0000 +++ b/src/network/network_gui.h Mon May 19 15:13:58 2008 +0000 @@ -5,6 +5,8 @@ #ifndef NETWORK_GUI_H #define NETWORK_GUI_H +#include "../window_type.h" + #ifdef ENABLE_NETWORK #include "network_data.h" @@ -15,7 +17,7 @@ void ShowJoinStatusWindow(); void ShowNetworkGameWindow(); void ShowClientList(); -void ShowNetworkCompanyPasswordWindow(); +void ShowNetworkCompanyPasswordWindow(Window *parent); #else /* ENABLE_NETWORK */ /* Network function stubs when networking is disabled */ @@ -23,7 +25,7 @@ static inline void ShowNetworkChatQueryWindow(byte desttype, int dest) {} static inline void ShowClientList() {} static inline void ShowNetworkGameWindow() {} -static inline void ShowNetworkCompanyPasswordWindow() {} +static inline void ShowNetworkCompanyPasswordWindow(Window *parent) {} #endif /* ENABLE_NETWORK */ diff -r 6c4314786d68 -r 8cbdb511a674 src/newgrf.cpp --- a/src/newgrf.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/newgrf.cpp Mon May 19 15:13:58 2008 +0000 @@ -466,22 +466,14 @@ rvi->max_speed = speed; } break; - case 0x0B: { // Power - uint16 power = grf_load_word(&buf); - - if (rvi->railveh_type == RAILVEH_MULTIHEAD) power /= 2; - - rvi->power = power; - dewagonize(power, e); - } break; - - case 0x0D: { // Running cost factor - uint8 runcostfact = grf_load_byte(&buf); - - if (rvi->railveh_type == RAILVEH_MULTIHEAD) runcostfact /= 2; - - rvi->running_cost = runcostfact; - } break; + case 0x0B: // Power + rvi->power = grf_load_word(&buf); + dewagonize(rvi->power, e); + break; + + case 0x0D: // Running cost factor + rvi->running_cost = grf_load_byte(&buf); + break; case 0x0E: { // Running cost base uint32 base = grf_load_dword(&buf); @@ -513,18 +505,8 @@ uint8 dual = grf_load_byte(&buf); if (dual != 0) { - if (rvi->railveh_type != RAILVEH_MULTIHEAD) { - // adjust power and running cost if needed - rvi->power /= 2; - rvi->running_cost /= 2; - } rvi->railveh_type = RAILVEH_MULTIHEAD; } else { - if (rvi->railveh_type == RAILVEH_MULTIHEAD) { - // adjust power and running cost if needed - rvi->power *= 2; - rvi->running_cost *= 2; - } rvi->railveh_type = rvi->power == 0 ? RAILVEH_WAGON : RAILVEH_SINGLEHEAD; } diff -r 6c4314786d68 -r 8cbdb511a674 src/newgrf_gui.cpp --- a/src/newgrf_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/newgrf_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -109,121 +109,126 @@ } -/* Dialogue for adding NewGRF files to the selection */ -struct newgrf_add_d { +/** + * Window for adding NewGRF files + */ +struct NewGRFAddWindow : public Window { + /* Names of the add a newgrf window widgets */ + enum AddNewGRFWindowWidgets { + ANGRFW_CLOSEBOX = 0, + ANGRFW_CAPTION, + ANGRFW_BACKGROUND, + ANGRFW_GRF_LIST, + ANGRFW_SCROLLBAR, + ANGRFW_GRF_INFO, + ANGRFW_ADD, + ANGRFW_RESCAN, + ANGRFW_RESIZE, + }; + GRFConfig **list; const GRFConfig *sel; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(newgrf_add_d)); - -/* Names of the add a newgrf window widgets */ -enum AddNewGRFWindowWidgets { - ANGRFW_CLOSEBOX = 0, - ANGRFW_CAPTION, - ANGRFW_BACKGROUND, - ANGRFW_GRF_LIST, - ANGRFW_SCROLLBAR, - ANGRFW_GRF_INFO, - ANGRFW_ADD, - ANGRFW_RESCAN, - ANGRFW_RESIZE, -}; -static void NewGRFAddDlgWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - const GRFConfig *c; - const Widget *wl = &w->widget[ANGRFW_GRF_LIST]; - int n = 0; - - /* Count the number of GRFs */ - for (c = _all_grfs; c != NULL; c = c->next) n++; - - w->vscroll.cap = (wl->bottom - wl->top) / 10; - SetVScrollCount(w, n); + NewGRFAddWindow(const WindowDesc *desc, GRFConfig **list) : Window(desc, 0) + { + this->list = list; + this->resize.step_height = 10; - w->SetWidgetDisabledState(ANGRFW_ADD, WP(w, newgrf_add_d).sel == NULL || WP(w, newgrf_add_d).sel->IsOpenTTDBaseGRF()); - DrawWindowWidgets(w); - - GfxFillRect(wl->left + 1, wl->top + 1, wl->right, wl->bottom, 0xD7); + this->FindWindowPlacementAndResize(desc); + } - uint y = wl->top + 1; - for (c = _all_grfs, n = 0; c != NULL && n < (w->vscroll.pos + w->vscroll.cap); c = c->next, n++) { - if (n >= w->vscroll.pos) { - bool h = c == WP(w, newgrf_add_d).sel; - const char *text = (c->name != NULL && !StrEmpty(c->name)) ? c->name : c->filename; + virtual void OnPaint() + { + const GRFConfig *c; + const Widget *wl = &this->widget[ANGRFW_GRF_LIST]; + int n = 0; - /* Draw selection background */ - if (h) GfxFillRect(3, y, w->width - 15, y + 9, 156); - DoDrawStringTruncated(text, 4, y, h ? TC_WHITE : TC_ORANGE, w->width - 18); - y += 10; - } + /* Count the number of GRFs */ + for (c = _all_grfs; c != NULL; c = c->next) n++; + + this->vscroll.cap = (wl->bottom - wl->top) / 10; + SetVScrollCount(this, n); + + this->SetWidgetDisabledState(ANGRFW_ADD, this->sel == NULL || this->sel->IsOpenTTDBaseGRF()); + this->DrawWidgets(); + + GfxFillRect(wl->left + 1, wl->top + 1, wl->right, wl->bottom, 0xD7); + + uint y = wl->top + 1; + for (c = _all_grfs, n = 0; c != NULL && n < (this->vscroll.pos + this->vscroll.cap); c = c->next, n++) { + if (n >= this->vscroll.pos) { + bool h = c == this->sel; + const char *text = (c->name != NULL && !StrEmpty(c->name)) ? c->name : c->filename; + + /* Draw selection background */ + if (h) GfxFillRect(3, y, this->width - 15, y + 9, 156); + DoDrawStringTruncated(text, 4, y, h ? TC_WHITE : TC_ORANGE, this->width - 18); + y += 10; + } + } + + if (this->sel != NULL) { + const Widget *wi = &this->widget[ANGRFW_GRF_INFO]; + ShowNewGRFInfo(this->sel, wi->left + 2, wi->top + 2, wi->right - wi->left - 2, wi->bottom, false); + } + } + + virtual void OnDoubleClick(Point pt, int widget) + { + if (widget == ANGRFW_GRF_LIST) this->OnClick(pt, ANGRFW_ADD); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case ANGRFW_GRF_LIST: { + /* Get row... */ + const GRFConfig *c; + uint i = (pt.y - this->widget[ANGRFW_GRF_LIST].top) / 10 + this->vscroll.pos; + + for (c = _all_grfs; c != NULL && i > 0; c = c->next, i--) {} + this->sel = c; + this->SetDirty(); + break; } - if (WP(w, newgrf_add_d).sel != NULL) { - const Widget *wi = &w->widget[ANGRFW_GRF_INFO]; - ShowNewGRFInfo(WP(w, newgrf_add_d).sel, wi->left + 2, wi->top + 2, wi->right - wi->left - 2, wi->bottom, false); - } - break; - } - - case WE_DOUBLE_CLICK: - if (e->we.click.widget != ANGRFW_GRF_LIST) break; - e->we.click.widget = ANGRFW_ADD; - /* Fall through */ - - case WE_CLICK: - switch (e->we.click.widget) { - case ANGRFW_GRF_LIST: { - /* Get row... */ - const GRFConfig *c; - uint i = (e->we.click.pt.y - w->widget[ANGRFW_GRF_LIST].top) / 10 + w->vscroll.pos; - - for (c = _all_grfs; c != NULL && i > 0; c = c->next, i--) {} - WP(w, newgrf_add_d).sel = c; - w->SetDirty(); - break; - } - - case ANGRFW_ADD: // Add selection to list - if (WP(w, newgrf_add_d).sel != NULL) { - const GRFConfig *src = WP(w, newgrf_add_d).sel; - GRFConfig **list; + case ANGRFW_ADD: // Add selection to list + if (this->sel != NULL) { + const GRFConfig *src = this->sel; + GRFConfig **list; - /* Find last entry in the list, checking for duplicate grfid on the way */ - for (list = WP(w, newgrf_add_d).list; *list != NULL; list = &(*list)->next) { - if ((*list)->grfid == src->grfid) { - ShowErrorMessage(INVALID_STRING_ID, STR_NEWGRF_DUPLICATE_GRFID, 0, 0); - return; - } + /* Find last entry in the list, checking for duplicate grfid on the way */ + for (list = this->list; *list != NULL; list = &(*list)->next) { + if ((*list)->grfid == src->grfid) { + ShowErrorMessage(INVALID_STRING_ID, STR_NEWGRF_DUPLICATE_GRFID, 0, 0); + return; } - - /* Copy GRF details from scanned list */ - GRFConfig *c = CallocT(1); - *c = *src; - c->filename = strdup(src->filename); - if (src->name != NULL) c->name = strdup(src->name); - if (src->info != NULL) c->info = strdup(src->info); - c->next = NULL; + } - /* Append GRF config to configuration list */ - *list = c; - - DeleteWindowByClass(WC_SAVELOAD); - InvalidateWindowData(WC_GAME_OPTIONS, 0); - } - break; + /* Copy GRF details from scanned list */ + GRFConfig *c = CallocT(1); + *c = *src; + c->filename = strdup(src->filename); + if (src->name != NULL) c->name = strdup(src->name); + if (src->info != NULL) c->info = strdup(src->info); + c->next = NULL; - case ANGRFW_RESCAN: // Rescan list - WP(w, newgrf_add_d).sel = NULL; - ScanNewGRFFiles(); - w->SetDirty(); - break; - } - break; + /* Append GRF config to configuration list */ + *list = c; + + DeleteWindowByClass(WC_SAVELOAD); + InvalidateWindowData(WC_GAME_OPTIONS, 0); + } + break; + + case ANGRFW_RESCAN: // Rescan list + this->sel = NULL; + ScanNewGRFFiles(); + this->SetDirty(); + break; + } } -} +}; /* Widget definition for the add a newgrf window */ static const Widget _newgrf_add_dlg_widgets[] = { @@ -245,303 +250,282 @@ WC_SAVELOAD, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _newgrf_add_dlg_widgets, - NewGRFAddDlgWndProc, }; -/* 'NewGRF Settings' dialogue */ -struct newgrf_d { +static void NewGRFConfirmationCallback(Window *w, bool confirmed); + +/** + * Window for showing NewGRF files + */ +struct NewGRFWindow : public Window { + /* Names of the manage newgrfs window widgets */ + enum ShowNewGRFStateWidgets { + SNGRFS_CLOSEBOX = 0, + SNGRFS_CAPTION, + SNGRFS_BACKGROUND, + SNGRFS_ADD, + SNGRFS_REMOVE, + SNGRFS_MOVE_UP, + SNGRFS_MOVE_DOWN, + SNGRFS_FILE_LIST, + SNGRFS_SCROLLBAR, + SNGRFS_NEWGRF_INFO, + SNGRFS_SET_PARAMETERS, + SNGRFS_APPLY_CHANGES, + SNGRFS_RESIZE, + }; + GRFConfig **orig_list; ///< grf list the window is shown with - GRFConfig **list; ///< temporary grf list to which changes are made + GRFConfig *list; ///< temporary grf list to which changes are made GRFConfig *sel; ///< selected grf item bool editable; ///< is the window editable bool show_params; ///< are the grf-parameters shown in the info-panel bool execute; ///< on pressing 'apply changes' are grf changes applied immediately, or only list is updated -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(newgrf_d)); - - -/* Names of the manage newgrfs window widgets */ -enum ShowNewGRFStateWidgets { - SNGRFS_CLOSEBOX = 0, - SNGRFS_CAPTION, - SNGRFS_BACKGROUND, - SNGRFS_ADD, - SNGRFS_REMOVE, - SNGRFS_MOVE_UP, - SNGRFS_MOVE_DOWN, - SNGRFS_FILE_LIST, - SNGRFS_SCROLLBAR, - SNGRFS_NEWGRF_INFO, - SNGRFS_SET_PARAMETERS, - SNGRFS_APPLY_CHANGES, - SNGRFS_RESIZE, -}; - -static void SetupNewGRFState(Window *w) -{ - bool disable_all = WP(w, newgrf_d).sel == NULL || !WP(w, newgrf_d).editable; - - w->SetWidgetDisabledState(SNGRFS_ADD, !WP(w, newgrf_d).editable); - w->SetWidgetsDisabledState(disable_all, - SNGRFS_REMOVE, - SNGRFS_MOVE_UP, - SNGRFS_MOVE_DOWN, - WIDGET_LIST_END - ); - w->SetWidgetDisabledState(SNGRFS_SET_PARAMETERS, !WP(w, newgrf_d).show_params || disable_all); - - if (!disable_all) { - /* All widgets are now enabled, so disable widgets we can't use */ - if (WP(w, newgrf_d).sel == *WP(w, newgrf_d).list) w->DisableWidget(SNGRFS_MOVE_UP); - if (WP(w, newgrf_d).sel->next == NULL) w->DisableWidget(SNGRFS_MOVE_DOWN); - if (WP(w, newgrf_d).sel->IsOpenTTDBaseGRF()) w->DisableWidget(SNGRFS_REMOVE); - } -} - - -static void SetupNewGRFWindow(Window *w) -{ - const GRFConfig *c; - int i; - - for (c = *WP(w, newgrf_d).list, i = 0; c != NULL; c = c->next, i++) {} - - w->vscroll.cap = (w->widget[SNGRFS_FILE_LIST].bottom - w->widget[SNGRFS_FILE_LIST].top) / 14 + 1; - SetVScrollCount(w, i); - w->SetWidgetDisabledState(SNGRFS_APPLY_CHANGES, !WP(w, newgrf_d).editable); -} - - -/** Callback function for the newgrf 'apply changes' confirmation window - * @param w Window which is calling this callback - * @param confirmed boolean value, true when yes was clicked, false otherwise - */ -static void NewGRFConfirmationCallback(Window *w, bool confirmed) -{ - if (confirmed) { - newgrf_d *nd = &WP(w, newgrf_d); - GRFConfig *c; - int i = 0; - CopyGRFConfigList(nd->orig_list, *nd->list, false); - ReloadNewGRFData(); - - /* Show new, updated list */ - for (c = *nd->list; c != NULL && c != nd->sel; c = c->next, i++) {} - CopyGRFConfigList(nd->list, *nd->orig_list, false); - for (c = *nd->list; c != NULL && i > 0; c = c->next, i--) {} - nd->sel = c; - - w->SetDirty(); - } -} - - -static void NewGRFWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - const GRFConfig *c; - int i, y; - - SetupNewGRFState(w); - - DrawWindowWidgets(w); - - /* Draw NewGRF list */ - y = w->widget[SNGRFS_FILE_LIST].top; - for (c = *WP(w, newgrf_d).list, i = 0; c != NULL; c = c->next, i++) { - if (i >= w->vscroll.pos && i < w->vscroll.pos + w->vscroll.cap) { - const char *text = (c->name != NULL && !StrEmpty(c->name)) ? c->name : c->filename; - SpriteID pal; - byte txtoffset; + NewGRFWindow(const WindowDesc *desc, bool editable, bool show_params, bool exec_changes, GRFConfig **config) : Window(desc, 0) + { + this->resize.step_height = 14; + this->sel = NULL; + this->list = NULL; + this->orig_list = config; + this->editable = editable; + this->execute = exec_changes; + this->show_params = show_params; - /* Pick a colour */ - switch (c->status) { - case GCS_NOT_FOUND: - case GCS_DISABLED: - pal = PALETTE_TO_RED; - break; - case GCS_ACTIVATED: - pal = PALETTE_TO_GREEN; - break; - default: - pal = PALETTE_TO_BLUE; - break; - } + CopyGRFConfigList(&this->list, *config, false); - /* Do not show a "not-failure" colour when it actually failed to load */ - if (pal != PALETTE_TO_RED) { - if (HasBit(c->flags, GCF_STATIC)) { - pal = PALETTE_TO_GREY; - } else if (HasBit(c->flags, GCF_COMPATIBLE)) { - pal = PALETTE_TO_ORANGE; - } - } + this->FindWindowPlacementAndResize(desc); + this->SetupNewGRFWindow(); + } - DrawSprite(SPR_SQUARE, pal, 5, y + 2); - if (c->error != NULL) DrawSprite(SPR_WARNING_SIGN, 0, 20, y + 2); - txtoffset = c->error != NULL ? 35 : 25; - DoDrawStringTruncated(text, txtoffset, y + 3, WP(w, newgrf_d).sel == c ? TC_WHITE : TC_BLACK, w->width - txtoffset - 10); - y += 14; - } - } - - if (WP(w, newgrf_d).sel != NULL) { - /* Draw NewGRF file info */ - const Widget *wi = &w->widget[SNGRFS_NEWGRF_INFO]; - ShowNewGRFInfo(WP(w, newgrf_d).sel, wi->left + 2, wi->top + 2, wi->right - wi->left - 2, wi->bottom, WP(w, newgrf_d).show_params); - } - - break; + ~NewGRFWindow() + { + if (!this->execute) { + CopyGRFConfigList(this->orig_list, this->list, true); + ResetGRFConfig(false); + ReloadNewGRFData(); } - case WE_INVALIDATE_DATA: - SetupNewGRFWindow(w); - break; - - case WE_CLICK: - switch (e->we.click.widget) { - case SNGRFS_ADD: { // Add GRF - GRFConfig **list = WP(w, newgrf_d).list; - Window *w; - - DeleteWindowByClass(WC_SAVELOAD); - w = new Window(&_newgrf_add_dlg_desc); - w->resize.step_height = 10; - - WP(w, newgrf_add_d).list = list; - break; - } - - case SNGRFS_REMOVE: { // Remove GRF - GRFConfig **pc, *c, *newsel; - - /* Choose the next GRF file to be the selected file */ - newsel = WP(w, newgrf_d).sel->next; + /* Remove the temporary copy of grf-list used in window */ + ClearGRFConfigList(&this->list); + } - for (pc = WP(w, newgrf_d).list; (c = *pc) != NULL; pc = &c->next) { - /* If the new selection is empty (i.e. we're deleting the last item - * in the list, pick the file just before the selected file */ - if (newsel == NULL && c->next == WP(w, newgrf_d).sel) newsel = c; - - if (c == WP(w, newgrf_d).sel) { - *pc = c->next; - free(c); - break; - } - } + void SetupNewGRFWindow() + { + const GRFConfig *c; + int i; - WP(w, newgrf_d).sel = newsel; - SetupNewGRFWindow(w); - w->SetDirty(); - break; - } - - case SNGRFS_MOVE_UP: { // Move GRF up - GRFConfig **pc, *c; - if (WP(w, newgrf_d).sel == NULL) break; + for (c = this->list, i = 0; c != NULL; c = c->next, i++) {} - for (pc = WP(w, newgrf_d).list; (c = *pc) != NULL; pc = &c->next) { - if (c->next == WP(w, newgrf_d).sel) { - c->next = WP(w, newgrf_d).sel->next; - WP(w, newgrf_d).sel->next = c; - *pc = WP(w, newgrf_d).sel; - break; - } - } - w->SetDirty(); - break; + this->vscroll.cap = (this->widget[SNGRFS_FILE_LIST].bottom - this->widget[SNGRFS_FILE_LIST].top) / 14 + 1; + SetVScrollCount(this, i); + + this->SetWidgetDisabledState(SNGRFS_ADD, !this->editable); + this->SetWidgetDisabledState(SNGRFS_APPLY_CHANGES, !this->editable); + } + + virtual void OnPaint() + { + bool disable_all = this->sel == NULL || !this->editable; + + this->SetWidgetsDisabledState(disable_all, + SNGRFS_REMOVE, + SNGRFS_MOVE_UP, + SNGRFS_MOVE_DOWN, + WIDGET_LIST_END + ); + this->SetWidgetDisabledState(SNGRFS_SET_PARAMETERS, !this->show_params || disable_all); + + if (!disable_all) { + /* All widgets are now enabled, so disable widgets we can't use */ + if (this->sel == this->list) this->DisableWidget(SNGRFS_MOVE_UP); + if (this->sel->next == NULL) this->DisableWidget(SNGRFS_MOVE_DOWN); + if (this->sel->IsOpenTTDBaseGRF()) this->DisableWidget(SNGRFS_REMOVE); + } + + this->DrawWidgets(); + + /* Draw NewGRF list */ + int y = this->widget[SNGRFS_FILE_LIST].top; + int i = 0; + for (const GRFConfig *c = this->list; c != NULL; c = c->next, i++) { + if (i >= this->vscroll.pos && i < this->vscroll.pos + this->vscroll.cap) { + const char *text = (c->name != NULL && !StrEmpty(c->name)) ? c->name : c->filename; + SpriteID pal; + byte txtoffset; + + /* Pick a colour */ + switch (c->status) { + case GCS_NOT_FOUND: + case GCS_DISABLED: + pal = PALETTE_TO_RED; + break; + case GCS_ACTIVATED: + pal = PALETTE_TO_GREEN; + break; + default: + pal = PALETTE_TO_BLUE; + break; } - case SNGRFS_MOVE_DOWN: { // Move GRF down - GRFConfig **pc, *c; - if (WP(w, newgrf_d).sel == NULL) break; - - for (pc = WP(w, newgrf_d).list; (c = *pc) != NULL; pc = &c->next) { - if (c == WP(w, newgrf_d).sel) { - *pc = c->next; - c->next = c->next->next; - (*pc)->next = c; - break; - } + /* Do not show a "not-failure" colour when it actually failed to load */ + if (pal != PALETTE_TO_RED) { + if (HasBit(c->flags, GCF_STATIC)) { + pal = PALETTE_TO_GREY; + } else if (HasBit(c->flags, GCF_COMPATIBLE)) { + pal = PALETTE_TO_ORANGE; } - w->SetDirty(); - break; - } - - case SNGRFS_FILE_LIST: { // Select a GRF - GRFConfig *c; - uint i = (e->we.click.pt.y - w->widget[SNGRFS_FILE_LIST].top) / 14 + w->vscroll.pos; - - for (c = *WP(w, newgrf_d).list; c != NULL && i > 0; c = c->next, i--) {} - WP(w, newgrf_d).sel = c; - - w->SetDirty(); - break; } - case SNGRFS_APPLY_CHANGES: // Apply changes made to GRF list - if (WP(w, newgrf_d).execute) { - ShowQuery( - STR_POPUP_CAUTION_CAPTION, - STR_NEWGRF_CONFIRMATION_TEXT, - w, - NewGRFConfirmationCallback - ); - } else { - CopyGRFConfigList(WP(w, newgrf_d).orig_list, *WP(w, newgrf_d).list, true); - ResetGRFConfig(false); - ReloadNewGRFData(); - } - break; - - case SNGRFS_SET_PARAMETERS: { // Edit parameters - char buff[512]; - if (WP(w, newgrf_d).sel == NULL) break; - - GRFBuildParamList(buff, WP(w, newgrf_d).sel, lastof(buff)); - ShowQueryString(BindCString(buff), STR_NEWGRF_PARAMETER_QUERY, 63, 250, w, CS_ALPHANUMERAL); - break; - } + DrawSprite(SPR_SQUARE, pal, 5, y + 2); + if (c->error != NULL) DrawSprite(SPR_WARNING_SIGN, 0, 20, y + 2); + txtoffset = c->error != NULL ? 35 : 25; + DoDrawStringTruncated(text, txtoffset, y + 3, this->sel == c ? TC_WHITE : TC_BLACK, this->width - txtoffset - 10); + y += 14; } - break; + } - case WE_ON_EDIT_TEXT: - if (e->we.edittext.str != NULL) { - /* Parse our new "int list" */ - GRFConfig *c = WP(w, newgrf_d).sel; - c->num_params = parse_intlist(e->we.edittext.str, (int*)c->param, lengthof(c->param)); - - /* parse_intlist returns -1 on error */ - if (c->num_params == (byte)-1) c->num_params = 0; - - w->SetDirty(); - } - break; + if (this->sel != NULL) { + /* Draw NewGRF file info */ + const Widget *wi = &this->widget[SNGRFS_NEWGRF_INFO]; + ShowNewGRFInfo(this->sel, wi->left + 2, wi->top + 2, wi->right - wi->left - 2, wi->bottom, this->show_params); + } + } - case WE_DESTROY: - if (!WP(w, newgrf_d).execute) { - CopyGRFConfigList(WP(w, newgrf_d).orig_list, *WP(w, newgrf_d).list, true); - ResetGRFConfig(false); - ReloadNewGRFData(); - } - /* Remove the temporary copy of grf-list used in window */ - ClearGRFConfigList(WP(w, newgrf_d).list); - break; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case SNGRFS_ADD: // Add GRF + DeleteWindowByClass(WC_SAVELOAD); + new NewGRFAddWindow(&_newgrf_add_dlg_desc, &this->list); + break; - case WE_RESIZE: - if (e->we.sizing.diff.x != 0) { - ResizeButtons(w, SNGRFS_ADD, SNGRFS_MOVE_DOWN); - ResizeButtons(w, SNGRFS_SET_PARAMETERS, SNGRFS_APPLY_CHANGES); + case SNGRFS_REMOVE: { // Remove GRF + GRFConfig **pc, *c, *newsel; + + /* Choose the next GRF file to be the selected file */ + newsel = this->sel->next; + + for (pc = &this->list; (c = *pc) != NULL; pc = &c->next) { + /* If the new selection is empty (i.e. we're deleting the last item + * in the list, pick the file just before the selected file */ + if (newsel == NULL && c->next == this->sel) newsel = c; + + if (c == this->sel) { + *pc = c->next; + free(c); + break; + } + } + + this->sel = newsel; + this->SetupNewGRFWindow(); + this->SetDirty(); + break; } - w->vscroll.cap += e->we.sizing.diff.y / 14; - w->widget[SNGRFS_FILE_LIST].data = (w->vscroll.cap << 8) + 1; - SetupNewGRFWindow(w); - break; + + case SNGRFS_MOVE_UP: { // Move GRF up + GRFConfig **pc, *c; + if (this->sel == NULL) break; + + for (pc = &this->list; (c = *pc) != NULL; pc = &c->next) { + if (c->next == this->sel) { + c->next = this->sel->next; + this->sel->next = c; + *pc = this->sel; + break; + } + } + this->SetDirty(); + break; + } + + case SNGRFS_MOVE_DOWN: { // Move GRF down + GRFConfig **pc, *c; + if (this->sel == NULL) break; + + for (pc = &this->list; (c = *pc) != NULL; pc = &c->next) { + if (c == this->sel) { + *pc = c->next; + c->next = c->next->next; + (*pc)->next = c; + break; + } + } + this->SetDirty(); + break; + } + + case SNGRFS_FILE_LIST: { // Select a GRF + GRFConfig *c; + uint i = (pt.y - this->widget[SNGRFS_FILE_LIST].top) / 14 + this->vscroll.pos; + + for (c = this->list; c != NULL && i > 0; c = c->next, i--) {} + this->sel = c; + + this->SetDirty(); + break; + } + + case SNGRFS_APPLY_CHANGES: // Apply changes made to GRF list + if (this->execute) { + ShowQuery( + STR_POPUP_CAUTION_CAPTION, + STR_NEWGRF_CONFIRMATION_TEXT, + this, + NewGRFConfirmationCallback + ); + } else { + CopyGRFConfigList(this->orig_list, this->list, true); + ResetGRFConfig(false); + ReloadNewGRFData(); + } + break; + + case SNGRFS_SET_PARAMETERS: { // Edit parameters + if (this->sel == NULL) break; + + char buff[512]; + GRFBuildParamList(buff, this->sel, lastof(buff)); + ShowQueryString(BindCString(buff), STR_NEWGRF_PARAMETER_QUERY, 63, 250, this, CS_ALPHANUMERAL); + break; + } + } } -} + + virtual void OnQueryTextFinished(char *str) + { + if (str == NULL) return; + + /* Parse our new "int list" */ + GRFConfig *c = this->sel; + c->num_params = parse_intlist(str, (int*)c->param, lengthof(c->param)); + + /* parse_intlist returns -1 on error */ + if (c->num_params == (byte)-1) c->num_params = 0; + + this->SetDirty(); + } + + virtual void OnResize(Point new_size, Point delta) + { + if (delta.x != 0) { + ResizeButtons(this, SNGRFS_ADD, SNGRFS_MOVE_DOWN); + ResizeButtons(this, SNGRFS_SET_PARAMETERS, SNGRFS_APPLY_CHANGES); + } + + this->vscroll.cap += delta.y / 14; + this->widget[SNGRFS_FILE_LIST].data = (this->vscroll.cap << 8) + 1; + + this->SetupNewGRFWindow(); + } + + virtual void OnInvalidateData(int data = 0) + { + this->SetupNewGRFWindow(); + } +}; /* Widget definition of the manage newgrfs window */ static const Widget _newgrf_widgets[] = { @@ -567,9 +551,33 @@ WC_GAME_OPTIONS, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _newgrf_widgets, - NewGRFWndProc, }; +/** Callback function for the newgrf 'apply changes' confirmation window + * @param w Window which is calling this callback + * @param confirmed boolean value, true when yes was clicked, false otherwise + */ +static void NewGRFConfirmationCallback(Window *w, bool confirmed) +{ + if (confirmed) { + NewGRFWindow *nw = dynamic_cast(w); + GRFConfig *c; + int i = 0; + + CopyGRFConfigList(nw->orig_list, nw->list, false); + ReloadNewGRFData(); + + /* Show new, updated list */ + for (c = nw->list; c != NULL && c != nw->sel; c = c->next, i++) {} + CopyGRFConfigList(&nw->list, *nw->orig_list, false); + for (c = nw->list; c != NULL && i > 0; c = c->next, i--) {} + nw->sel = c; + + w->SetDirty(); + } +} + + /** Setup the NewGRF gui * @param editable allow the user to make changes to the grfconfig in the window @@ -579,23 +587,6 @@ * @param config pointer to a linked-list of grfconfig's that will be shown */ void ShowNewGRFSettings(bool editable, bool show_params, bool exec_changes, GRFConfig **config) { - static GRFConfig *local = NULL; - Window *w; - DeleteWindowByClass(WC_GAME_OPTIONS); - w = new Window(&_newgrf_desc); - if (w == NULL) return; - - w->resize.step_height = 14; - CopyGRFConfigList(&local, *config, false); - - /* Clear selections */ - WP(w, newgrf_d).sel = NULL; - WP(w, newgrf_d).list = &local; - WP(w, newgrf_d).orig_list = config; - WP(w, newgrf_d).editable = editable; - WP(w, newgrf_d).execute = exec_changes; - WP(w, newgrf_d).show_params = show_params; - - SetupNewGRFWindow(w); + new NewGRFWindow(&_newgrf_desc, editable, show_params, exec_changes, config); } diff -r 6c4314786d68 -r 8cbdb511a674 src/news_func.h --- a/src/news_func.h Mon May 19 14:14:33 2008 +0000 +++ b/src/news_func.h Mon May 19 15:13:58 2008 +0000 @@ -8,9 +8,8 @@ #include "news_type.h" #include "vehicle_type.h" -void AddNewsItem(StringID string, NewsMode mode, NewsFlag flag, NewsType type, NewsCallback callback, uint data_a, uint data_b); +void AddNewsItem(StringID string, NewsSubtype subtype, uint data_a, uint data_b); void NewsLoop(); -void DrawNewsBorder(const Window *w); void InitNewsItemStructs(); extern NewsItem _statusbar_news_item; diff -r 6c4314786d68 -r 8cbdb511a674 src/news_gui.cpp --- a/src/news_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/news_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -18,6 +18,8 @@ #include "string_func.h" #include "widgets/dropdown_func.h" #include "map_func.h" +#include "statusbar_gui.h" +#include "player_face.h" #include "table/sprites.h" #include "table/strings.h" @@ -60,12 +62,6 @@ static NewsID _oldest_news = 0; ///< points to first item in fifo queue static NewsID _latest_news = INVALID_NEWS; ///< points to last item in fifo queue -struct news_d { - uint16 chat_height; - NewsItem *ni; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(news_d)); - /** Forced news item. * Users can force an item by accessing the history or "last message". * If the message being shown was forced by the user, its index is stored in @@ -73,23 +69,107 @@ static NewsID _forced_news = INVALID_NEWS; static uint _total_news = 0; ///< Number of news items in FIFO queue @see _news_items - -void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni); -void DrawNewsBankrupcy(Window *w, const NewsItem *ni); static void MoveToNextItem(); -StringID GetNewsStringNewVehicleAvail(const NewsItem *ni); -StringID GetNewsStringBankrupcy(const NewsItem *ni); -static DrawNewsCallbackProc * const _draw_news_callback[] = { - DrawNewsNewVehicleAvail, ///< DNC_VEHICLEAVAIL - DrawNewsBankrupcy, ///< DNC_BANKRUPCY +typedef void DrawNewsCallbackProc(struct Window *w, const NewsItem *ni); +void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni); + +static void DrawNewsBankrupcy(Window *w, const NewsItem *ni) +{ + Player *p = GetPlayer((PlayerID)(ni->data_b)); + DrawPlayerFace(p->face, p->player_color, 2, 23); + GfxFillRect(3, 23, 3 + 91, 23 + 118, PALETTE_TO_STRUCT_GREY | (1 << USE_COLORTABLE)); + + SetDParam(0, p->index); + + DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94); + + switch (ni->subtype) { + case NS_COMPANY_TROUBLE: + DrawStringCentered(w->width >> 1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, TC_FROMSTRING); + + SetDParam(0, p->index); + + DrawStringMultiCenter( + ((w->width - 101) >> 1) + 98, + 90, + STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED, + w->width - 101); + break; + + case NS_COMPANY_MERGER: + DrawStringCentered(w->width >> 1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, TC_FROMSTRING); + SetDParam(0, ni->params[2]); + SetDParam(1, p->index); + SetDParam(2, ni->params[4]); + DrawStringMultiCenter( + ((w->width - 101) >> 1) + 98, + 90, + ni->params[4] == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR, + w->width - 101); + break; + + case NS_COMPANY_BANKRUPT: + DrawStringCentered(w->width >> 1, 1, STR_705C_BANKRUPT, TC_FROMSTRING); + SetDParam(0, p->index); + DrawStringMultiCenter( + ((w->width - 101) >> 1) + 98, + 90, + STR_705D_HAS_BEEN_CLOSED_DOWN_BY, + w->width - 101); + break; + + case NS_COMPANY_NEW: + DrawStringCentered(w->width >> 1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, TC_FROMSTRING); + SetDParam(0, p->index); + SetDParam(1, ni->params[3]); + DrawStringMultiCenter( + ((w->width - 101) >> 1) + 98, + 90, + STR_705F_STARTS_CONSTRUCTION_NEAR, + w->width - 101); + break; + + default: + NOT_REACHED(); + } +} + + +/** + * Data common to all news items of a given subtype (structure) + */ +struct NewsSubtypeData { + NewsType type; ///< News category @see NewsType + NewsMode display_mode; ///< Display mode value @see NewsMode + NewsFlag flags; ///< Initial NewsFlags bits @see NewsFlag + DrawNewsCallbackProc *callback; ///< Call-back function }; -extern GetNewsStringCallbackProc * const _get_news_string_callback[]; -GetNewsStringCallbackProc * const _get_news_string_callback[] = { - GetNewsStringNewVehicleAvail, ///< DNC_VEHICLEAVAIL - GetNewsStringBankrupcy, ///< DNC_BANKRUPCY +/** + * Data common to all news items of a given subtype (actual data) + */ +static const struct NewsSubtypeData _news_subtype_data[NS_END] = { + /* type, display_mode, flags, callback */ + { NT_ARRIVAL_PLAYER, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ARRIVAL_PLAYER + { NT_ARRIVAL_OTHER, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ARRIVAL_OTHER + { NT_ACCIDENT, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_ACCIDENT_TILE + { NT_ACCIDENT, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ACCIDENT_VEHICLE + { NT_COMPANY_INFO, NM_CALLBACK, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_TROUBLE + { NT_COMPANY_INFO, NM_CALLBACK, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_MERGER + { NT_COMPANY_INFO, NM_CALLBACK, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_BANKRUPT + { NT_COMPANY_INFO, NM_CALLBACK, NF_TILE, DrawNewsBankrupcy }, ///< NS_COMPANY_NEW + { NT_OPENCLOSE, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_OPENCLOSE + { NT_ECONOMY, NM_NORMAL, NF_NONE, NULL }, ///< NS_ECONOMY + { NT_INDUSTRY_PLAYER, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_PLAYER + { NT_INDUSTRY_OTHER, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_OTHER + { NT_INDUSTRY_NOBODY, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_NOBODY + { NT_ADVICE, NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ADVICE + { NT_NEW_VEHICLES, NM_CALLBACK, NF_NONE, DrawNewsNewVehicleAvail }, ///< NS_NEW_VEHICLES + { NT_ACCEPTANCE, NM_SMALL, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_ACCEPTANCE + { NT_SUBSIDIES, NM_NORMAL, NF_TILE|NF_TILE2, NULL }, ///< NS_SUBSIDIES + { NT_GENERAL, NM_NORMAL, NF_TILE, NULL }, ///< NS_GENERAL }; /** Initialize the news-items data structures */ @@ -105,148 +185,154 @@ _total_news = 0; } -void DrawNewsBorder(const Window *w) -{ - int left = 0; - int right = w->width - 1; - int top = 0; - int bottom = w->height - 1; - - GfxFillRect(left, top, right, bottom, 0xF); - - GfxFillRect(left, top, left, bottom, 0xD7); - GfxFillRect(right, top, right, bottom, 0xD7); - GfxFillRect(left, top, right, top, 0xD7); - GfxFillRect(left, bottom, right, bottom, 0xD7); - - DrawString(left + 2, top + 1, STR_00C6, TC_FROMSTRING); -} - -static void NewsWindowProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: { // If chatbar is open at creation time, we need to go above it - const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0); - WP(w, news_d).chat_height = (w1 != NULL) ? w1->height : 0; - break; - } - - case WE_PAINT: { - const NewsItem *ni = WP(w, news_d).ni; - - switch (ni->display_mode) { - case NM_NORMAL: - case NM_THIN: { - DrawNewsBorder(w); - - DrawString(2, 1, STR_00C6, TC_FROMSTRING); - - SetDParam(0, ni->date); - DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING); - - if (!(ni->flags & NF_VIEWPORT)) { - CopyInDParam(0, ni->params, lengthof(ni->params)); - DrawStringMultiCenter(215, ni->display_mode == NM_NORMAL ? 76 : 56, - ni->string_id, w->width - 4); - } else { - /* Back up transparency options to draw news view */ - TransparencyOptionBits to_backup = _transparency_opt; - _transparency_opt = 0; - DrawWindowViewport(w); - _transparency_opt = to_backup; +struct NewsWindow : Window { + uint16 chat_height; + NewsItem *ni; - /* Shade the viewport into gray, or color*/ - ViewPort *vp = w->viewport; - GfxFillRect(vp->left - w->left, vp->top - w->top, - vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1, - (ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE) - ); + NewsWindow(const WindowDesc *desc, NewsItem *ni) : Window(desc), ni(ni) + { + const Window *w = FindWindowById(WC_SEND_NETWORK_MSG, 0); + this->chat_height = (w != NULL) ? w->height : 0; - CopyInDParam(0, ni->params, lengthof(ni->params)); - DrawStringMultiCenter(w->width / 2, 20, ni->string_id, w->width - 4); - } - break; - } + this->ni = &_news_items[_forced_news == INVALID_NEWS ? _current_news : _forced_news]; + this->flags4 |= WF_DISABLE_VP_SCROLL; - case NM_CALLBACK: - _draw_news_callback[ni->callback](w, ni); - break; + this->FindWindowPlacementAndResize(desc); + } - default: - DrawWindowWidgets(w); - if (!(ni->flags & NF_VIEWPORT)) { - CopyInDParam(0, ni->params, lengthof(ni->params)); - DrawStringMultiCenter(140, 38, ni->string_id, 276); + void DrawNewsBorder() + { + int left = 0; + int right = this->width - 1; + int top = 0; + int bottom = this->height - 1; + + GfxFillRect(left, top, right, bottom, 0xF); + + GfxFillRect(left, top, left, bottom, 0xD7); + GfxFillRect(right, top, right, bottom, 0xD7); + GfxFillRect(left, top, right, top, 0xD7); + GfxFillRect(left, bottom, right, bottom, 0xD7); + + DrawString(left + 2, top + 1, STR_00C6, TC_FROMSTRING); + } + + virtual void OnPaint() + { + const NewsMode display_mode = _news_subtype_data[this->ni->subtype].display_mode; + + switch (display_mode) { + case NM_NORMAL: + case NM_THIN: { + this->DrawNewsBorder(); + + DrawString(2, 1, STR_00C6, TC_FROMSTRING); + + SetDParam(0, this->ni->date); + DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING); + + if (!(this->ni->flags & NF_VIEWPORT)) { + CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); + DrawStringMultiCenter(215, display_mode == NM_NORMAL ? 76 : 56, + this->ni->string_id, this->width - 4); + } else { + /* Back up transparency options to draw news view */ + TransparencyOptionBits to_backup = _transparency_opt; + _transparency_opt = 0; + this->DrawViewport(); + _transparency_opt = to_backup; + + /* Shade the viewport into gray, or color*/ + ViewPort *vp = this->viewport; + GfxFillRect(vp->left - this->left, vp->top - this->top, + vp->left - this->left + vp->width - 1, vp->top - this->top + vp->height - 1, + (this->ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE) + ); + + CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); + DrawStringMultiCenter(this->width / 2, 20, this->ni->string_id, this->width - 4); + } + break; + } + + case NM_CALLBACK: + this->DrawNewsBorder(); + (_news_subtype_data[this->ni->subtype].callback)(this, ni); + break; + + default: + this->DrawWidgets(); + if (!(this->ni->flags & NF_VIEWPORT)) { + CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); + DrawStringMultiCenter(140, 38, this->ni->string_id, 276); + } else { + this->DrawViewport(); + CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); + DrawStringMultiCenter(this->width / 2, this->height - 16, this->ni->string_id, this->width - 4); + } + break; + } + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 1: + this->ni->duration = 0; + delete this; + _forced_news = INVALID_NEWS; + break; + + case 0: + if (this->ni->flags & NF_VEHICLE) { + Vehicle *v = GetVehicle(this->ni->data_a); + ScrollMainWindowTo(v->x_pos, v->y_pos); + } else if (this->ni->flags & NF_TILE) { + if (_ctrl_pressed) { + ShowExtraViewPortWindow(this->ni->data_a); + if (this->ni->flags & NF_TILE2) { + ShowExtraViewPortWindow(this->ni->data_b); + } } else { - DrawWindowViewport(w); - CopyInDParam(0, ni->params, lengthof(ni->params)); - DrawStringMultiCenter(w->width / 2, w->height - 16, ni->string_id, w->width - 4); - } - break; - } - break; - } - - case WE_CLICK: - switch (e->we.click.widget) { - case 1: { - NewsItem *ni = WP(w, news_d).ni; - delete w; - ni->duration = 0; - _forced_news = INVALID_NEWS; - break; - } - - case 0: { - NewsItem *ni = WP(w, news_d).ni; - if (ni->flags & NF_VEHICLE) { - Vehicle *v = GetVehicle(ni->data_a); - ScrollMainWindowTo(v->x_pos, v->y_pos); - } else if (ni->flags & NF_TILE) { - if (_ctrl_pressed) { - ShowExtraViewPortWindow(ni->data_a); - if (ni->flags & NF_TILE2) { - ShowExtraViewPortWindow(ni->data_b); - } - } else { - if (!ScrollMainWindowToTile(ni->data_a) && ni->flags & NF_TILE2) { - ScrollMainWindowToTile(ni->data_b); - } + if (!ScrollMainWindowToTile(this->ni->data_a) && this->ni->flags & NF_TILE2) { + ScrollMainWindowToTile(this->ni->data_b); } } - break; } - } - break; - - case WE_KEYPRESS: - if (e->we.keypress.keycode == WKC_SPACE) { - /* Don't continue. */ - e->we.keypress.cont = false; - delete w; - } - break; - - case WE_INVALIDATE_DATA: // The chatbar has notified us that is was either created or closed - WP(w, news_d).chat_height = e->we.invalidate.data; - break; - - case WE_TICK: { // Scroll up newsmessages from the bottom in steps of 4 pixels - int y = max(w->top - 4, _screen.height - w->height - 12 - WP(w, news_d).chat_height); - if (y == w->top) return; - - if (w->viewport != NULL) { - w->viewport->top += y - w->top; - } - - int diff = Delta(w->top, y); - w->top = y; - - SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height); - break; + break; } } -} + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + if (keycode == WKC_SPACE) { + /* Don't continue. */ + delete this; + return ES_HANDLED; + } + return ES_NOT_HANDLED; + } + + virtual void OnInvalidateData(int data) + { + /* The chatbar has notified us that is was either created or closed */ + this->chat_height = data; + } + + virtual void OnTick() + { + /* Scroll up newsmessages from the bottom in steps of 4 pixels */ + int y = max(this->top - 4, _screen.height - this->height - 12 - this->chat_height); + if (y == this->top) return; + + if (this->viewport != NULL) this->viewport->top += y - this->top; + + int diff = Delta(this->top, y); + this->top = y; + + SetDirtyBlocks(this->left, this->top - diff, this->left + this->width, this->top + this->height); + } +}; /** * Return the correct index in the pseudo-fifo @@ -270,29 +356,14 @@ /** * Add a new newsitem to be shown. - * @param string String to display, can have special values based on parameter \a display_mode - * @param display_mode, any of the NewsMode enums (NM_) - * @param flags any of the NewsFlag enums (NF_) - * @param type news category, any of the NewsType enums (NT_) - * @param callback news callback function, any of the NewsCallback enums (DNC_) + * @param string String to display + * @param subtype news category, any of the NewsSubtype enums (NS_) * @param data_a news-specific value based on news type * @param data_b news-specific value based on news type * - * @note If the display mode is NM_CALLBACK, special news is shown and parameter - * \a string has a special meaning. - * - For DNC_TRAINAVAIL, DNC_ROADAVAIL, DNC_SHIPAVAIL, DNC_AIRCRAFTAVAIL messages: StringID is - * the index of the engine that is shown - * - * - For DNC_BANKRUPCY: bytes 0-3 of StringID contains the player that is in trouble, - * and 4-7 contains what kind of bankrupcy message is shown. - * @see NewsBankrupcy - * - * @see NewsMode - * @see NewsFlag - * @see NewsType - * @see NewsCallback + * @see NewsSubype */ -void AddNewsItem(StringID string, NewsMode display_mode, NewsFlag flags, NewsType type, NewsCallback callback, uint data_a, uint data_b) +void AddNewsItem(StringID string, NewsSubtype subtype, uint data_a, uint data_b) { if (_game_mode == GM_MENU) return; @@ -323,14 +394,12 @@ memset(ni, 0, sizeof(*ni)); ni->string_id = string; - ni->display_mode = display_mode; - ni->flags = flags; + ni->subtype = subtype; + ni->flags = _news_subtype_data[subtype].flags; /* show this news message in color? */ if (_cur_year >= _patches.colored_news_year) ni->flags |= NF_INCOLOR; - ni->type = type; - ni->callback = callback; ni->data_a = data_a; ni->data_b = data_b; ni->date = _date; @@ -376,7 +445,6 @@ WC_NEWS_WINDOW, WC_NONE, WDF_DEF_WIDGET, _news_type13_widgets, - NewsWindowProc }; static const Widget _news_type2_widgets[] = { @@ -390,7 +458,6 @@ WC_NEWS_WINDOW, WC_NONE, WDF_DEF_WIDGET, _news_type2_widgets, - NewsWindowProc }; static const Widget _news_type0_widgets[] = { @@ -406,7 +473,6 @@ WC_NEWS_WINDOW, WC_NONE, WDF_DEF_WIDGET, _news_type0_widgets, - NewsWindowProc }; @@ -416,16 +482,16 @@ ni->flags &= ~NF_FORCE_BIG; ni->duration = 555; - SoundFx sound = _news_type_data[ni->type].sound; + SoundFx sound = _news_type_data[_news_subtype_data[ni->subtype].type].sound; if (sound != 0) SndPlayFx(sound); int top = _screen.height; Window *w; - switch (ni->display_mode) { + switch (_news_subtype_data[ni->subtype].display_mode) { case NM_NORMAL: case NM_CALLBACK: _news_type13_desc.top = top; - w = new Window(&_news_type13_desc); + w = new NewsWindow(&_news_type13_desc, ni); if (ni->flags & NF_VIEWPORT) { InitializeWindowViewport(w, 2, 58, 426, 110, ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); @@ -434,7 +500,7 @@ case NM_THIN: _news_type2_desc.top = top; - w = new Window(&_news_type2_desc); + w = new NewsWindow(&_news_type2_desc, ni); if (ni->flags & NF_VIEWPORT) { InitializeWindowViewport(w, 2, 58, 426, 70, ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); @@ -443,7 +509,7 @@ default: _news_type0_desc.top = top; - w = new Window(&_news_type0_desc); + w = new NewsWindow(&_news_type0_desc, ni); if (ni->flags & NF_VIEWPORT) { InitializeWindowViewport(w, 3, 17, 274, 47, ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); @@ -453,9 +519,6 @@ /*DEBUG(misc, 0, " cur %3d, old %2d, lat %3d, for %3d, tot %2d", _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/ - - WP(w, news_d).ni = &_news_items[_forced_news == INVALID_NEWS ? _current_news : _forced_news]; - w->flags4 |= WF_DISABLE_VP_SCROLL; } /** Show news item in the ticker */ @@ -464,8 +527,7 @@ if (_news_ticker_sound) SndPlayFx(SND_16_MORSE); _statusbar_news_item = *ni; - Window *w = FindWindowById(WC_STATUS_BAR, 0); - if (w != NULL) WP(w, def_d).data_1 = 360; + InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_TICKER); } @@ -482,8 +544,7 @@ /* Ticker message * Check if the status bar message is still being displayed? */ - const Window *w = FindWindowById(WC_STATUS_BAR, 0); - if (w != NULL && WP(w, const def_d).data_1 > -1280) return false; + if (IsNewsTickerShown()) return false; /* Newspaper message, decrement duration counter */ if (ni->duration != 0) ni->duration--; @@ -502,21 +563,16 @@ if (_current_news != _latest_news) { _current_news = (_current_news == INVALID_NEWS) ? _oldest_news : IncreaseIndex(_current_news); NewsItem *ni = &_news_items[_current_news]; + const NewsType type = _news_subtype_data[ni->subtype].type; /* check the date, don't show too old items */ - if (_date - _news_type_data[ni->type].age > ni->date) return; + if (_date - _news_type_data[type].age > ni->date) return; - switch (_news_type_data[ni->type].display) { + switch (_news_type_data[type].display) { default: NOT_REACHED(); - case ND_OFF: { // Off - show nothing only a small reminder in the status bar - Window *w = FindWindowById(WC_STATUS_BAR, 0); - - if (w != NULL) { - WP(w, def_d).data_2 = 91; - w->SetDirty(); - } + case ND_OFF: // Off - show nothing only a small reminder in the status bar + InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_REMINDER); break; - } case ND_SUMMARY: // Summary - show ticker, but if forced big, cascade to full if (!(ni->flags & NF_FORCE_BIG)) { @@ -607,12 +663,8 @@ char buffer[512], buffer2[512]; StringID str; - if (ni->display_mode == NM_CALLBACK) { - str = _get_news_string_callback[ni->callback](ni); - } else { - CopyInDParam(0, ni->params, lengthof(ni->params)); - str = ni->string_id; - } + CopyInDParam(0, ni->params, lengthof(ni->params)); + str = ni->string_id; GetString(buffer, str, lastof(buffer)); /* Copy the just gotten string to another buffer to remove any formatting @@ -642,45 +694,56 @@ } -static void MessageHistoryWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - int y = 19; - - SetVScrollCount(w, _total_news); - DrawWindowWidgets(w); - - if (_total_news == 0) break; - NewsID show = min(_total_news, w->vscroll.cap); - - for (NewsID p = w->vscroll.pos; p < w->vscroll.pos + show; p++) { - /* get news in correct order */ - const NewsItem *ni = &_news_items[getNews(p)]; - - SetDParam(0, ni->date); - DrawString(4, y, STR_SHORT_DATE, TC_WHITE); +struct MessageHistoryWindow : Window { + MessageHistoryWindow(const WindowDesc *desc) : Window(desc) + { + this->vscroll.cap = 10; + this->vscroll.count = _total_news; + this->resize.step_height = 12; + this->resize.height = this->height - 12 * 6; // minimum of 4 items in the list, each item 12 high + this->resize.step_width = 1; + this->resize.width = 200; // can't make window any smaller than 200 pixel - DrawNewsString(82, y, TC_WHITE, ni, w->width - 95); - y += 12; - } - break; - } + this->FindWindowPlacementAndResize(desc); + } - case WE_CLICK: - if (e->we.click.widget == 3) { - int y = (e->we.click.pt.y - 19) / 12; - NewsID p = getNews(y + w->vscroll.pos); + virtual void OnPaint() + { + int y = 19; - if (p != INVALID_NEWS) ShowNewsMessage(p); - } - break; + SetVScrollCount(this, _total_news); + this->DrawWidgets(); - case WE_RESIZE: - w->vscroll.cap += e->we.sizing.diff.y / 12; - break; + if (_total_news == 0) return; + NewsID show = min(_total_news, this->vscroll.cap); + + for (NewsID p = this->vscroll.pos; p < this->vscroll.pos + show; p++) { + /* get news in correct order */ + const NewsItem *ni = &_news_items[getNews(p)]; + + SetDParam(0, ni->date); + DrawString(4, y, STR_SHORT_DATE, TC_WHITE); + + DrawNewsString(82, y, TC_WHITE, ni, this->width - 95); + y += 12; + } } -} + + virtual void OnClick(Point pt, int widget) + { + if (widget == 3) { + int y = (pt.y - 19) / 12; + NewsID p = getNews(y + this->vscroll.pos); + + if (p != INVALID_NEWS) ShowNewsMessage(p); + } + } + + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / 12; + } +}; static const Widget _message_history_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -697,24 +760,13 @@ 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, - MessageHistoryWndProc }; /** Display window with news messages history */ void ShowMessageHistory() { DeleteWindowById(WC_MESSAGE_HISTORY, 0); - Window *w = new Window(&_message_history_desc); - - if (w == NULL) return; - - w->vscroll.cap = 10; - w->vscroll.count = _total_news; - w->resize.step_height = 12; - w->resize.height = w->height - 12 * 6; // minimum of 4 items in the list, each item 12 high - w->resize.step_width = 1; - w->resize.width = 200; // can't make window any smaller than 200 pixel - w->SetDirty(); + new MessageHistoryWindow(&_message_history_desc); } @@ -725,101 +777,96 @@ WIDGET_NEWSOPT_START_OPTION = 8, ///< First widget that is part of a group [<] .. [.] }; -/** - * Setup the disabled/enabled buttons in the message window - * If the value is 'off' disable the [<] widget, and enable the [>] one - * Same-wise for all the others. Starting value of 4 is the first widget - * group. These are grouped as [<][>] .. [<][>], etc. - * @param w Window been used - * @param value to set in the widget - * @param element index of the group of widget to set - */ -static void SetMessageButtonStates(Window *w, byte value, int element) -{ - element *= NB_WIDG_PER_SETTING; - - w->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION, value == 0); - w->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION + 2, value == 2); -} - -/** - * Event handler of the Message Options window - * @param w window pointer - * @param e event been triggered - */ -static void MessageOptionsWndProc(Window *w, WindowEvent *e) -{ - static const StringID message_opt[] = {STR_OFF, STR_SUMMARY, STR_FULL, INVALID_STRING_ID}; - - /* WP(w, def_d).data_1 stores state of the ALL on/off/summary button */ - switch (e->event) { - case WE_CREATE: { - NewsDisplay all_val; - - /* Set up the initial disabled buttons in the case of 'off' or 'full' */ - all_val = _news_type_data[0].display; - for (int i = 0; i < NT_END; i++) { - SetMessageButtonStates(w, _news_type_data[i].display, i); - /* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */ - if (_news_type_data[i].display != all_val) all_val = ND_OFF; - } - /* If all values are the same value, the ALL-button will take over this value */ - WP(w, def_d).data_1 = all_val; - break; - } - - case WE_PAINT: - if (_news_ticker_sound) w->LowerWidget(WIDGET_NEWSOPT_SOUNDTICKER); +static const StringID _message_opt[] = {STR_OFF, STR_SUMMARY, STR_FULL, INVALID_STRING_ID}; - w->widget[WIDGET_NEWSOPT_DROP_SUMMARY].data = message_opt[WP(w, def_d).data_1]; - DrawWindowWidgets(w); - - /* Draw the string of each setting on each button. */ - for (int i = 0, y = 26; i < NT_END; i++, y += 12) { - /* 51 comes from 13 + 89 (left and right of the button)+1, shiefted by one as to get division, - * which will give centered position */ - DrawStringCentered(51, y + 1, message_opt[_news_type_data[i].display], TC_BLACK); - } - break; - - case WE_CLICK: - switch (e->we.click.widget) { - case WIDGET_NEWSOPT_DROP_SUMMARY: // Dropdown menu for all settings - ShowDropDownMenu(w, message_opt, WP(w, def_d).data_1, WIDGET_NEWSOPT_DROP_SUMMARY, 0, 0); - break; - - case WIDGET_NEWSOPT_SOUNDTICKER: // Change ticker sound on/off - _news_ticker_sound ^= 1; - w->ToggleWidgetLoweredState(e->we.click.widget); - w->InvalidateWidget(e->we.click.widget); - break; +struct MessageOptionsWindow : Window { + int state; - default: { // Clicked on the [<] .. [>] widgets - int wid = e->we.click.widget - WIDGET_NEWSOPT_START_OPTION; - if (wid >= 0 && wid < (NB_WIDG_PER_SETTING * NT_END)) { - int element = wid / NB_WIDG_PER_SETTING; - byte val = (_news_type_data[element].display + ((wid % NB_WIDG_PER_SETTING) ? 1 : -1)) % 3; + MessageOptionsWindow(const WindowDesc *desc) : Window(desc) + { + NewsDisplay all_val; - SetMessageButtonStates(w, val, element); - _news_type_data[element].display = (NewsDisplay)val; - w->SetDirty(); - } - break; + /* Set up the initial disabled buttons in the case of 'off' or 'full' */ + all_val = _news_type_data[0].display; + for (int i = 0; i < NT_END; i++) { + this->SetMessageButtonStates(_news_type_data[i].display, i); + /* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */ + if (_news_type_data[i].display != all_val) all_val = ND_OFF; + } + /* If all values are the same value, the ALL-button will take over this value */ + this->state = all_val; + } + + /** + * Setup the disabled/enabled buttons in the message window + * If the value is 'off' disable the [<] widget, and enable the [>] one + * Same-wise for all the others. Starting value of 4 is the first widget + * group. These are grouped as [<][>] .. [<][>], etc. + * @param value to set in the widget + * @param element index of the group of widget to set + */ + void SetMessageButtonStates(byte value, int element) + { + element *= NB_WIDG_PER_SETTING; + + this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION, value == 0); + this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION + 2, value == 2); + } + + virtual void OnPaint() + { + if (_news_ticker_sound) this->LowerWidget(WIDGET_NEWSOPT_SOUNDTICKER); + + this->widget[WIDGET_NEWSOPT_DROP_SUMMARY].data = _message_opt[this->state]; + this->DrawWidgets(); + + /* Draw the string of each setting on each button. */ + for (int i = 0, y = 26; i < NT_END; i++, y += 12) { + /* 51 comes from 13 + 89 (left and right of the button)+1, shiefted by one as to get division, + * which will give centered position */ + DrawStringCentered(51, y + 1, _message_opt[_news_type_data[i].display], TC_BLACK); + } + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case WIDGET_NEWSOPT_DROP_SUMMARY: // Dropdown menu for all settings + ShowDropDownMenu(this, _message_opt, this->state, WIDGET_NEWSOPT_DROP_SUMMARY, 0, 0); + break; + + case WIDGET_NEWSOPT_SOUNDTICKER: // Change ticker sound on/off + _news_ticker_sound ^= 1; + this->ToggleWidgetLoweredState(widget); + this->InvalidateWidget(widget); + break; + + default: { // Clicked on the [<] .. [>] widgets + int wid = widget - WIDGET_NEWSOPT_START_OPTION; + if (wid >= 0 && wid < (NB_WIDG_PER_SETTING * NT_END)) { + int element = wid / NB_WIDG_PER_SETTING; + byte val = (_news_type_data[element].display + ((wid % NB_WIDG_PER_SETTING) ? 1 : -1)) % 3; + + this->SetMessageButtonStates(val, element); + _news_type_data[element].display = (NewsDisplay)val; + this->SetDirty(); } + break; } - break; - - case WE_DROPDOWN_SELECT: // Select all settings for newsmessages - WP(w, def_d).data_1 = e->we.dropdown.index; + } + } - for (int i = 0; i < NT_END; i++) { - SetMessageButtonStates(w, e->we.dropdown.index, i); - _news_type_data[i].display = (NewsDisplay)e->we.dropdown.index; - } - w->SetDirty(); - break; + virtual void OnDropdownSelect(int widget, int index) + { + this->state = index; + + for (int i = 0; i < NT_END; i++) { + this->SetMessageButtonStates(index, i); + _news_type_data[i].display = (NewsDisplay)index; + } + this->SetDirty(); } -} +}; /* @@ -920,13 +967,12 @@ WC_GAME_OPTIONS, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _message_options_widgets, - MessageOptionsWndProc }; void ShowMessageOptions() { DeleteWindowById(WC_GAME_OPTIONS, 0); - new Window(&_message_options_desc); + new MessageOptionsWindow(&_message_options_desc); } @@ -961,8 +1007,8 @@ * We also need an update of the current, forced and visible (open window) * news's as this shifting could change the items they were pointing to */ if (_total_news != 0) { - Window *w = FindWindowById(WC_NEWS_WINDOW, 0); - NewsID visible_news = (w != NULL) ? (NewsID)(WP(w, news_d).ni - _news_items) : INVALID_NEWS; + NewsWindow *w = dynamic_cast(FindWindowById(WC_NEWS_WINDOW, 0)); + NewsID visible_news = (w != NULL) ? (NewsID)(w->ni - _news_items) : INVALID_NEWS; for (NewsID i = n;; i = DecreaseIndex(i)) { _news_items[i] = _news_items[DecreaseIndex(i)]; @@ -970,7 +1016,7 @@ if (i != _latest_news) { if (i == _current_news) _current_news = IncreaseIndex(_current_news); if (i == _forced_news) _forced_news = IncreaseIndex(_forced_news); - if (i == visible_news) WP(w, news_d).ni = &_news_items[IncreaseIndex(visible_news)]; + if (i == visible_news) w->ni = &_news_items[IncreaseIndex(visible_news)]; } if (i == _oldest_news) break; diff -r 6c4314786d68 -r 8cbdb511a674 src/news_type.h --- a/src/news_type.h Mon May 19 14:14:33 2008 +0000 +++ b/src/news_type.h Mon May 19 15:13:58 2008 +0000 @@ -5,7 +5,6 @@ #ifndef NEWS_TYPE_H #define NEWS_TYPE_H -#include "window_type.h" #include "date_type.h" #include "strings_type.h" #include "sound_type.h" @@ -32,6 +31,31 @@ }; /** + * News subtypes. + */ +enum NewsSubtype { + NS_ARRIVAL_PLAYER, ///< NT_ARRIVAL_PLAYER + NS_ARRIVAL_OTHER, ///< NT_ARRIVAL_OTHER + NS_ACCIDENT_TILE, ///< NT_ACCIDENT (tile) + NS_ACCIDENT_VEHICLE, ///< NT_ACCIDENT (vehicle) + NS_COMPANY_TROUBLE, ///< NT_COMPANY_INFO (trouble) + NS_COMPANY_MERGER, ///< NT_COMPANY_INFO (merger) + NS_COMPANY_BANKRUPT, ///< NT_COMPANY_INFO (bankrupt) + NS_COMPANY_NEW, ///< NT_COMPANY_INFO (new company) + NS_OPENCLOSE, ///< NT_OPENCLOSE + NS_ECONOMY, ///< NT_ECONOMY + NS_INDUSTRY_PLAYER, ///< NT_INDUSTRY_PLAYER + NS_INDUSTRY_OTHER, ///< NT_INDUSTRY_OTHER + NS_INDUSTRY_NOBODY, ///< NT_INDUSTRY_NOBODY + NS_ADVICE, ///< NT_ADVICE + NS_NEW_VEHICLES, ///< NT_NEW_VEHICLES + NS_ACCEPTANCE, ///< NT_ACCEPTANCE + NS_SUBSIDIES, ///< NT_SUBSIDIES + NS_GENERAL, ///< NT_GENERAL + NS_END, ///< end-of-array marker +}; + +/** * News mode. */ enum NewsMode { @@ -58,26 +82,6 @@ /** - * Special news items - */ -enum NewsCallback { - DNC_VEHICLEAVAIL = 0, ///< Show new vehicle available message. StringID is EngineID - DNC_BANKRUPCY = 1, ///< Show bankrupcy message. StringID is PlayerID (0-3) and NewsBankrupcy (4-7) - DNC_NONE = 0xFF, ///< No news callback. -}; - -/** - * Kinds of bankrupcy - * @note These flags are or'd with player index - */ -enum NewsBankrupcy { - NB_BTROUBLE = (1 << 4), ///< Company is in trouble (warning) - NB_BMERGER = (2 << 4), ///< Company has been bought by another company - NB_BBANKRUPT = (3 << 4), ///< Company has gone bankrupt - NB_BNEWCOMPANY = (4 << 4), ///< A new company has been started -}; - -/** * News display options */ enum NewsDisplay { @@ -97,22 +101,16 @@ }; struct NewsItem { - StringID string_id; ///< Message text (sometimes also used for storing other info) + StringID string_id; ///< Message text uint16 duration; ///< Remaining time for showing this news message Date date; ///< Date of the news + NewsSubtype subtype; ///< News subtype @see NewsSubtype NewsFlag flags; ///< NewsFlags bits @see NewsFlag - NewsMode display_mode; ///< Display mode value @see NewsMode - NewsType type; ///< News category @see NewsType - NewsCallback callback; ///< Call-back function - uint data_a; ///< Reference to tile or vehicle - uint data_b; ///< Reference to second tile or vehicle + uint data_a; ///< Custom data 1 (usually tile or vehicle) + uint data_b; ///< Custom data 2 uint64 params[10]; }; -typedef bool ValidationProc(uint data_a, uint data_b); -typedef void DrawNewsCallbackProc(Window *w, const NewsItem *ni); -typedef StringID GetNewsStringCallbackProc(const NewsItem *ni); - #endif /* NEWS_TYPE_H */ diff -r 6c4314786d68 -r 8cbdb511a674 src/oldloader.cpp --- a/src/oldloader.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/oldloader.cpp Mon May 19 15:13:58 2008 +0000 @@ -1516,7 +1516,13 @@ * clear it for ourselves and let OTTD's rebuild PBS itself */ _m[i].m4 &= 0xF; /* Only keep the lower four bits; upper four is PBS */ break; - default: break; + + case MP_WATER: + if (GetWaterClass(i) == 3) MakeRiver(i, Random()); + break; + + default: + break; } } diff -r 6c4314786d68 -r 8cbdb511a674 src/openttd.cpp --- a/src/openttd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/openttd.cpp Mon May 19 15:13:58 2008 +0000 @@ -290,7 +290,6 @@ { /* Dynamic stuff needs to be initialized somewhere... */ _town_sort = NULL; - _industry_sort = NULL; _industry_mngr.ResetMapping(); _industile_mngr.ResetMapping(); } @@ -327,7 +326,6 @@ _Engine_pool.CleanPool(); free((void*)_town_sort); - free((void*)_industry_sort); free(_config_file); @@ -1344,42 +1342,17 @@ UpdateAllTownVirtCoords(); UpdateAllWaypointSigns(); - /* Recalculate */ - Group *g; - FOR_ALL_GROUPS(g) { - g->num_engines = CallocT(GetEnginePoolSize()); - - const Vehicle *v; - FOR_ALL_VEHICLES(v) { - if (!IsEngineCountable(v)) continue; - - if (v->group_id != g->index || v->type != g->vehicle_type || v->owner != g->owner) continue; - - g->num_engines[v->engine_type]++; + Player *p; + FOR_ALL_PLAYERS(p) { + /* For each player, verify (while loading a scenario) that the inauguration date is the current year and set it + * accordingly if it is not the case. No need to set it on players that are not been used already, + * thus the MIN_YEAR (which is really nothing more than Zero, initialized value) test */ + if (_file_to_saveload.filetype == FT_SCENARIO && p->inaugurated_year != MIN_YEAR) { + p->inaugurated_year = _cur_year; } } - /* Set up the engine count for all players */ - Player *players[MAX_PLAYERS]; - const Vehicle *v; - - for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { - players[i] = GetPlayer(i); - - /* For each player, verify (while loading a scenario) that the inauguration date is the current year and set it - * accordingly if it is not the case. No need to set it on players that are not been used already, - * thus the MIN_YEAR (which is really nothing more than Zero, initialized value) test */ - if (_file_to_saveload.filetype == FT_SCENARIO && players[i]->inaugurated_year != MIN_YEAR) - players[i]->inaugurated_year = _cur_year; - - free(players[i]->num_engines); - players[i]->num_engines = CallocT(GetEnginePoolSize()); - } - - FOR_ALL_VEHICLES(v) { - if (!IsEngineCountable(v)) continue; - players[v->owner]->num_engines[v->engine_type]++; - } + SetCachedEngineCounts(); return true; } @@ -2434,9 +2407,7 @@ /* Move river flag and update canals to use water class */ if (IsTileType(t, MP_WATER)) { - if (_m[t].m5 == 2) { - MakeRiver(t, Random()); - } else { + if (GetWaterClass(t) != WATER_CLASS_RIVER) { if (IsWater(t)) { Owner o = GetTileOwner(t); if (o == OWNER_WATER) { @@ -2574,11 +2545,14 @@ ResetVehiclePosHash(); AfterLoadVehicles(false); StartupEngines(); + SetCachedEngineCounts(); /* update station and waypoint graphics */ AfterLoadWaypoints(); AfterLoadStations(); /* Check and update house and town values */ UpdateHousesAndTowns(); + /* Update livery selection windows */ + for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) InvalidateWindowData(WC_PLAYER_COLOR, i, _loaded_newgrf_features.has_2CC); /* redraw the whole screen */ MarkWholeScreenDirty(); } diff -r 6c4314786d68 -r 8cbdb511a674 src/order_cmd.cpp --- a/src/order_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/order_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -546,7 +546,7 @@ } /* Make sure to rebuild the whole list */ - RebuildVehicleLists(); + InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); } return CommandCost(); @@ -561,7 +561,7 @@ if (flags & DC_EXEC) { DeleteVehicleOrders(dst); InvalidateVehicleOrder(dst); - RebuildVehicleLists(); + InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); } return CommandCost(); } @@ -673,7 +673,7 @@ cur_order_id++; } - RebuildVehicleLists(); + InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); } return CommandCost(); @@ -811,7 +811,7 @@ } /* Make sure to rebuild the whole list */ - RebuildVehicleLists(); + InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); } return CommandCost(); @@ -1075,7 +1075,7 @@ InvalidateVehicleOrder(dst); InvalidateVehicleOrder(src); - RebuildVehicleLists(); + InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); } } break; @@ -1134,7 +1134,7 @@ InvalidateVehicleOrder(dst); - RebuildVehicleLists(); + InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); } } break; @@ -1444,7 +1444,7 @@ SetDParam(0, v->unitnumber); AddNewsItem( message, - NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, + NS_ADVICE, v->index, 0 ); diff -r 6c4314786d68 -r 8cbdb511a674 src/order_gui.cpp --- a/src/order_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/order_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -58,60 +58,6 @@ ORDER_WIDGET_RESIZE, }; -/** Under what reason are we using the PlaceObject functionality? */ -enum OrderPlaceObjectState { - OPOS_GOTO, - OPOS_CONDITIONAL, -}; - -struct order_d { - int sel; - OrderPlaceObjectState goto_type; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(order_d)); - -/** - * Return the memorised selected order. - * - * @param w current window - * @return the memorised order if it is a vaild one - * else return the number of orders - */ -static int OrderGetSel(const Window *w) -{ - const Vehicle *v = GetVehicle(w->window_number); - int num = WP(w, order_d).sel; - - return (num >= 0 && num < v->num_orders) ? num : v->num_orders; -} - -/** - * Calculate the selected order. - * The calculation is based on the relative (to the window) y click position and - * the position of the scrollbar. - * - * @param w current window - * @param y Y-value of the click relative to the window origin - * @param v current vehicle - * @return the new selected order if the order is valid else return that - * an invalid one has been selected. - */ -static int GetOrderFromOrderWndPt(Window *w, int y, const Vehicle *v) -{ - /* - * Calculation description: - * 15 = 14 (w->widget[ORDER_WIDGET_ORDER_LIST].top) + 1 (frame-line) - * 10 = order text hight - */ - int sel = (y - w->widget[ORDER_WIDGET_ORDER_LIST].top - 1) / 10; - - if ((uint)sel >= w->vscroll.cap) return INVALID_ORDER; - - sel += w->vscroll.pos; - - return (sel <= v->num_orders && sel >= 0) ? sel : INVALID_ORDER; -} - /** Order load types that could be given to station orders. */ static const StringID _station_load_types[][5] = { { @@ -312,121 +258,6 @@ } -static void DrawOrdersWindow(Window *w) -{ - const Vehicle *v = GetVehicle(w->window_number); - bool shared_orders = v->IsOrderListShared(); - - SetVScrollCount(w, v->num_orders + 1); - - int sel = OrderGetSel(w); - const Order *order = GetVehicleOrder(v, sel); - - if (v->owner == _local_player) { - /* Set the strings for the dropdown boxes. */ - w->widget[ORDER_WIDGET_NON_STOP].data = _order_non_stop_drowdown[order == NULL ? 0 : order->GetNonStopType()]; - w->widget[ORDER_WIDGET_FULL_LOAD].data = _order_full_load_drowdown[order == NULL ? 0 : order->GetLoadType()]; - w->widget[ORDER_WIDGET_UNLOAD].data = _order_unload_drowdown[order == NULL ? 0 : order->GetUnloadType()]; - w->widget[ORDER_WIDGET_COND_VARIABLE].data = _order_conditional_variable[order == NULL ? 0 : order->GetConditionVariable()]; - w->widget[ORDER_WIDGET_COND_COMPARATOR].data = _order_conditional_condition[order == NULL ? 0 : order->GetConditionComparator()]; - - /* skip */ - w->SetWidgetDisabledState(ORDER_WIDGET_SKIP, v->num_orders <= 1); - - /* delete */ - w->SetWidgetDisabledState(ORDER_WIDGET_DELETE, - (uint)v->num_orders + ((shared_orders || v->num_orders != 0) ? 1 : 0) <= (uint)WP(w, order_d).sel); - - /* non-stop only for trains */ - w->SetWidgetDisabledState(ORDER_WIDGET_NON_STOP, (v->type != VEH_TRAIN && v->type != VEH_ROAD) || order == NULL); - w->SetWidgetDisabledState(ORDER_WIDGET_FULL_LOAD, order == NULL || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) != 0); // full load - w->SetWidgetDisabledState(ORDER_WIDGET_UNLOAD, order == NULL || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) != 0); // unload - /* Disable list of vehicles with the same shared orders if there is no list */ - w->SetWidgetDisabledState(ORDER_WIDGET_SHARED_ORDER_LIST, !shared_orders || v->orders == NULL); - w->SetWidgetDisabledState(ORDER_WIDGET_REFIT, order == NULL); // Refit - w->SetWidgetDisabledState(ORDER_WIDGET_SERVICE, order == NULL); // Refit - w->HideWidget(ORDER_WIDGET_REFIT); // Refit - w->HideWidget(ORDER_WIDGET_SERVICE); // Service - - w->HideWidget(ORDER_WIDGET_COND_VARIABLE); - w->HideWidget(ORDER_WIDGET_COND_COMPARATOR); - w->HideWidget(ORDER_WIDGET_COND_VALUE); - } - - w->ShowWidget(ORDER_WIDGET_NON_STOP); - w->ShowWidget(ORDER_WIDGET_UNLOAD); - w->ShowWidget(ORDER_WIDGET_FULL_LOAD); - - if (order != NULL) { - switch (order->GetType()) { - case OT_GOTO_STATION: - if (!GetStation(order->GetDestination())->IsBuoy()) break; - /* Fall-through */ - - case OT_GOTO_WAYPOINT: - w->DisableWidget(ORDER_WIDGET_FULL_LOAD); - w->DisableWidget(ORDER_WIDGET_UNLOAD); - break; - - case OT_GOTO_DEPOT: - w->DisableWidget(ORDER_WIDGET_FULL_LOAD); - - /* Remove unload and replace it with refit */ - w->HideWidget(ORDER_WIDGET_UNLOAD); - w->ShowWidget(ORDER_WIDGET_REFIT); - w->HideWidget(ORDER_WIDGET_FULL_LOAD); - w->ShowWidget(ORDER_WIDGET_SERVICE); - break; - - case OT_CONDITIONAL: { - w->HideWidget(ORDER_WIDGET_NON_STOP); - w->HideWidget(ORDER_WIDGET_UNLOAD); - w->HideWidget(ORDER_WIDGET_FULL_LOAD); - w->ShowWidget(ORDER_WIDGET_COND_VARIABLE); - w->ShowWidget(ORDER_WIDGET_COND_COMPARATOR); - w->ShowWidget(ORDER_WIDGET_COND_VALUE); - - OrderConditionVariable ocv = order->GetConditionVariable(); - w->SetWidgetDisabledState(ORDER_WIDGET_COND_COMPARATOR, ocv == OCV_UNCONDITIONALLY); - w->SetWidgetDisabledState(ORDER_WIDGET_COND_VALUE, ocv == OCV_REQUIRES_SERVICE || ocv == OCV_UNCONDITIONALLY); - - uint value = order->GetConditionValue(); - if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value); - SetDParam(1, value); - } break; - - default: // every other orders - w->DisableWidget(ORDER_WIDGET_NON_STOP); - w->DisableWidget(ORDER_WIDGET_FULL_LOAD); - w->DisableWidget(ORDER_WIDGET_UNLOAD); - } - } - - SetDParam(0, v->index); - DrawWindowWidgets(w); - - int y = 15; - - int i = w->vscroll.pos; - order = GetVehicleOrder(v, i); - StringID str; - while (order != NULL) { - /* Don't draw anything if it extends past the end of the window. */ - if (i - w->vscroll.pos >= w->vscroll.cap) break; - - DrawOrderString(v, order, i, y, i == WP(w, order_d).sel, false); - y += 10; - - i++; - order = order->next; - } - - if (i - w->vscroll.pos < w->vscroll.cap) { - str = shared_orders ? STR_END_OF_SHARED_ORDERS : STR_882A_END_OF_ORDERS; - DrawString(2, y, str, (i == WP(w, order_d).sel) ? TC_WHITE : TC_BLACK); - } -} - static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile) { Order order; @@ -510,548 +341,689 @@ return order; } -static bool HandleOrderVehClick(const Vehicle *v, const Vehicle *u, Window *w) -{ - if (u->type != v->type) return false; +struct OrdersWindow : public Window { +private: + /** Under what reason are we using the PlaceObject functionality? */ + enum OrderPlaceObjectState { + OPOS_GOTO, + OPOS_CONDITIONAL, + }; - if (!u->IsPrimaryVehicle()) { - u = u->First(); - if (!u->IsPrimaryVehicle()) return false; - } + int selected_order; + OrderPlaceObjectState goto_type; + const Vehicle *vehicle; - /* v is vehicle getting orders. Only copy/clone orders if vehicle doesn't have any orders yet - * obviously if you press CTRL on a non-empty orders vehicle you know what you are doing */ - if (v->num_orders != 0 && _ctrl_pressed == 0) return false; - - if (DoCommandP(v->tile, v->index | (u->index << 16), _ctrl_pressed ? CO_SHARE : CO_COPY, NULL, - _ctrl_pressed ? CMD_CLONE_ORDER | CMD_MSG(STR_CANT_SHARE_ORDER_LIST) : CMD_CLONE_ORDER | CMD_MSG(STR_CANT_COPY_ORDER_LIST))) { - WP(w, order_d).sel = -1; - ResetObjectToPlace(); + /** + * Return the memorised selected order. + * @return the memorised order if it is a vaild one + * else return the number of orders + */ + int OrderGetSel() + { + int num = this->selected_order; + return (num >= 0 && num < vehicle->num_orders) ? num : vehicle->num_orders; } - return true; -} - -static void OrdersPlaceObj(const Vehicle *v, TileIndex tile, Window *w) -{ - /* check if we're clicking on a vehicle first.. clone orders in that case. */ - const Vehicle *u = CheckMouseOverVehicle(); - if (u != NULL && HandleOrderVehClick(v, u, w)) return; - - const Order cmd = GetOrderCmdFromTile(v, tile); - if (!cmd.IsValid()) return; - - if (DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), cmd.Pack(), NULL, CMD_INSERT_ORDER | CMD_MSG(STR_8833_CAN_T_INSERT_NEW_ORDER))) { - if (WP(w, order_d).sel != -1) WP(w, order_d).sel++; - ResetObjectToPlace(); - } -} + /** + * Calculate the selected order. + * The calculation is based on the relative (to the window) y click position and + * the position of the scrollbar. + * + * @param y Y-value of the click relative to the window origin + * @param v current vehicle + * @return the new selected order if the order is valid else return that + * an invalid one has been selected. + */ + int GetOrderFromPt(int y) + { + /* + * Calculation description: + * 15 = 14 (w->widget[ORDER_WIDGET_ORDER_LIST].top) + 1 (frame-line) + * 10 = order text hight + */ + int sel = (y - this->widget[ORDER_WIDGET_ORDER_LIST].top - 1) / 10; -/** - * Handle the click on the goto button. - * - * @param w current window - * @param v current vehicle - */ -static void OrderClick_Goto(Window *w, const Vehicle *v, int i) -{ - w->InvalidateWidget(ORDER_WIDGET_GOTO); - w->ToggleWidgetLoweredState(ORDER_WIDGET_GOTO); - if (w->IsWidgetLowered(ORDER_WIDGET_GOTO)) { - _place_clicked_vehicle = NULL; - SetObjectToPlaceWnd(ANIMCURSOR_PICKSTATION, PAL_NONE, VHM_RECT, w); - WP(w, order_d).goto_type = OPOS_GOTO; - } else { - ResetObjectToPlace(); - } -} + if ((uint)sel >= this->vscroll.cap) return INVALID_ORDER; -/** - * Handle the click on the full load button. - * - * @param w current window - * @param v current vehicle - * @param load_type the way to load. - */ -static void OrderClick_FullLoad(Window *w, const Vehicle *v, int load_type) -{ - VehicleOrderID sel_ord = OrderGetSel(w); - const Order *order = GetVehicleOrder(v, sel_ord); + sel += this->vscroll.pos; - if (order->GetLoadType() == load_type) return; - - if (load_type < 0) { - switch (order->GetLoadType()) { - case OLF_LOAD_IF_POSSIBLE: load_type = OLFB_FULL_LOAD; break; - case OLFB_FULL_LOAD: load_type = OLF_FULL_LOAD_ANY; break; - case OLF_FULL_LOAD_ANY: load_type = OLFB_NO_LOAD; break; - case OLFB_NO_LOAD: load_type = OLF_LOAD_IF_POSSIBLE; break; - default: NOT_REACHED(); - } + return (sel <= vehicle->num_orders && sel >= 0) ? sel : INVALID_ORDER; } - DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_LOAD | (load_type << 4), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); -} - -/** - * Handle the click on the service. - * - * @param w current window - * @param v current vehicle - */ -static void OrderClick_Service(Window *w, const Vehicle *v, int i) -{ - DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), MOF_DEPOT_ACTION, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); -} - -/** - * Handle the click on the service in nearest depot button. - * - * @param w current window - * @param v current vehicle - */ -static void OrderClick_NearestDepot(Window *w, const Vehicle *v, int i) -{ - Order order; - order.next = NULL; - order.index = 0; - order.MakeGoToDepot(0, ODTFB_PART_OF_ORDERS); - order.SetDepotActionType(ODATFB_NEAREST_DEPOT); - - DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), order.Pack(), NULL, CMD_INSERT_ORDER | CMD_MSG(STR_8833_CAN_T_INSERT_NEW_ORDER)); -} -/** - * Handle the click on the conditional order button. - * - * @param w current window - * @param v current vehicle - */ -static void OrderClick_Conditional(Window *w, const Vehicle *v, int i) -{ - w->InvalidateWidget(ORDER_WIDGET_GOTO); - w->LowerWidget(ORDER_WIDGET_GOTO); - SetObjectToPlaceWnd(ANIMCURSOR_PICKSTATION, PAL_NONE, VHM_RECT, w); - WP(w, order_d).goto_type = OPOS_CONDITIONAL; -} + bool HandleOrderVehClick(const Vehicle *u) + { + if (u->type != this->vehicle->type) return false; -/** - * Handle the click on the unload button. - * - * @param w current window - * @param v current vehicle - */ -static void OrderClick_Unload(Window *w, const Vehicle *v, int unload_type) -{ - VehicleOrderID sel_ord = OrderGetSel(w); - const Order *order = GetVehicleOrder(v, sel_ord); + if (!u->IsPrimaryVehicle()) { + u = u->First(); + if (!u->IsPrimaryVehicle()) return false; + } - if (order->GetUnloadType() == unload_type) return; + /* v is vehicle getting orders. Only copy/clone orders if vehicle doesn't have any orders yet + * obviously if you press CTRL on a non-empty orders vehicle you know what you are doing */ + if (this->vehicle->num_orders != 0 && _ctrl_pressed == 0) return false; - if (unload_type < 0) { - switch (order->GetUnloadType()) { - case OUF_UNLOAD_IF_POSSIBLE: unload_type = OUFB_UNLOAD; break; - case OUFB_UNLOAD: unload_type = OUFB_TRANSFER; break; - case OUFB_TRANSFER: unload_type = OUFB_NO_UNLOAD; break; - case OUFB_NO_UNLOAD: unload_type = OUF_UNLOAD_IF_POSSIBLE; break; - default: NOT_REACHED(); + if (DoCommandP(this->vehicle->tile, this->vehicle->index | (u->index << 16), _ctrl_pressed ? CO_SHARE : CO_COPY, NULL, + _ctrl_pressed ? CMD_CLONE_ORDER | CMD_MSG(STR_CANT_SHARE_ORDER_LIST) : CMD_CLONE_ORDER | CMD_MSG(STR_CANT_COPY_ORDER_LIST))) { + this->selected_order = -1; + ResetObjectToPlace(); + } + + return true; + } + + /** + * Handle the click on the goto button. + * + * @param w current window + */ + static void OrderClick_Goto(OrdersWindow *w, int i) + { + w->InvalidateWidget(ORDER_WIDGET_GOTO); + w->ToggleWidgetLoweredState(ORDER_WIDGET_GOTO); + if (w->IsWidgetLowered(ORDER_WIDGET_GOTO)) { + _place_clicked_vehicle = NULL; + SetObjectToPlaceWnd(ANIMCURSOR_PICKSTATION, PAL_NONE, VHM_RECT, w); + w->goto_type = OPOS_GOTO; + } else { + ResetObjectToPlace(); } } - DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_UNLOAD | (unload_type << 4), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); -} + /** + * Handle the click on the full load button. + * + * @param w current window + * @param load_type the way to load. + */ + static void OrderClick_FullLoad(OrdersWindow *w, int load_type) + { + VehicleOrderID sel_ord = w->OrderGetSel(); + const Order *order = GetVehicleOrder(w->vehicle, sel_ord); -/** - * Handle the click on the nonstop button. - * - * @param w current window - * @param v current vehicle - * @param non_stop what non-stop type to use; -1 to use the 'next' one. - */ -static void OrderClick_Nonstop(Window *w, const Vehicle *v, int non_stop) -{ - VehicleOrderID sel_ord = OrderGetSel(w); - const Order *order = GetVehicleOrder(v, sel_ord); + if (order == NULL || order->GetLoadType() == load_type) return; - if (order->GetNonStopType() == non_stop) return; - - /* Keypress if negative, so 'toggle' to the next */ - if (non_stop < 0) { - non_stop = (order->GetNonStopType() + 1) % ONSF_END; + if (load_type < 0) { + switch (order->GetLoadType()) { + case OLF_LOAD_IF_POSSIBLE: load_type = OLFB_FULL_LOAD; break; + case OLFB_FULL_LOAD: load_type = OLF_FULL_LOAD_ANY; break; + case OLF_FULL_LOAD_ANY: load_type = OLFB_NO_LOAD; break; + case OLFB_NO_LOAD: load_type = OLF_LOAD_IF_POSSIBLE; break; + default: NOT_REACHED(); + } + } + DoCommandP(w->vehicle->tile, w->vehicle->index + (sel_ord << 16), MOF_LOAD | (load_type << 4), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); } - DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_NON_STOP | non_stop << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); -} - -/** - * Handle the click on the transfer button. - * - * @param w current window - * @param v current vehicle - */ -static void OrderClick_Transfer(Window *w, const Vehicle *v, int i) -{ - VehicleOrderID sel_ord = OrderGetSel(w); - const Order *order = GetVehicleOrder(v, sel_ord); - - DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_UNLOAD | ((order->GetUnloadType() & ~OUFB_NO_UNLOAD) ^ OUFB_TRANSFER) << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); -} - -/** - * Handle the click on the skip button. - * If ctrl is pressed skip to selected order. - * Else skip to current order + 1 - * - * @param w current window - * @param v current vehicle - */ -static void OrderClick_Skip(Window *w, const Vehicle *v, int i) -{ - /* Don't skip when there's nothing to skip */ - if (_ctrl_pressed && v->cur_order_index == OrderGetSel(w)) return; - - DoCommandP(v->tile, v->index, _ctrl_pressed ? OrderGetSel(w) : ((v->cur_order_index + 1) % v->num_orders), - NULL, CMD_SKIP_TO_ORDER | CMD_MSG(_ctrl_pressed ? STR_CAN_T_SKIP_TO_ORDER : STR_CAN_T_SKIP_ORDER)); -} - -/** - * Handle the click on the unload button. - * - * @param w current window - * @param v current vehicle - */ -static void OrderClick_Delete(Window *w, const Vehicle *v, int i) -{ - DoCommandP(v->tile, v->index, OrderGetSel(w), NULL, CMD_DELETE_ORDER | CMD_MSG(STR_8834_CAN_T_DELETE_THIS_ORDER)); -} - -/** - * Handle the click on the refit button. - * If ctrl is pressed cancel refitting. - * Else show the refit window. - * - * @param w current window - * @param v current vehicle - */ -static void OrderClick_Refit(Window *w, const Vehicle *v, int i) -{ - if (_ctrl_pressed) { - /* Cancel refitting */ - DoCommandP(v->tile, v->index, (WP(w, order_d).sel << 16) | (CT_NO_REFIT << 8) | CT_NO_REFIT, NULL, CMD_ORDER_REFIT); - } else { - ShowVehicleRefitWindow(v, WP(w, order_d).sel); + /** + * Handle the click on the service. + * + * @param w current window + */ + static void OrderClick_Service(OrdersWindow *w, int i) + { + DoCommandP(w->vehicle->tile, w->vehicle->index + (w->OrderGetSel() << 16), MOF_DEPOT_ACTION, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); } -} - -typedef void OnButtonVehClick(Window *w, const Vehicle *v, int i); - -/** - * Keycode function mapping. - * - * @see _order_keycodes[] - * @note Keep them allways in sync with _order_keycodes[]! - */ -static OnButtonVehClick* const _order_button_proc[] = { - OrderClick_Skip, - OrderClick_Delete, - OrderClick_Nonstop, - OrderClick_Goto, - OrderClick_FullLoad, - OrderClick_Unload, - OrderClick_Transfer, - OrderClick_Service, -}; -static const uint16 _order_keycodes[] = { - 'D', //skip order - 'F', //delete order - 'G', //non-stop - 'H', //goto order - 'J', //full load - 'K' //unload -}; - -static void OrdersWndProc(Window *w, WindowEvent *e) -{ - const Vehicle *v = GetVehicle(w->window_number); - - switch (e->event) { - case WE_CREATE: - if (_patches.timetabling) { - w->widget[ORDER_WIDGET_CAPTION].right -= 61; - } else { - w->HideWidget(ORDER_WIDGET_TIMETABLE_VIEW); - } - - break; - - case WE_PAINT: - DrawOrdersWindow(w); - break; - - case WE_CLICK: - if (w->widget[e->we.click.widget].type != WWT_DROPDOWN) HideDropDownMenu(w); - switch (e->we.click.widget) { - case ORDER_WIDGET_ORDER_LIST: { - ResetObjectToPlace(); - - int sel = GetOrderFromOrderWndPt(w, e->we.click.pt.y, v); - - if (sel == INVALID_ORDER) { - /* This was a click on an empty part of the orders window, so - * deselect the currently selected order. */ - WP(w, order_d).sel = -1; - w->SetDirty(); - return; - } + /** + * Handle the click on the service in nearest depot button. + * + * @param w current window + */ + static void OrderClick_NearestDepot(OrdersWindow *w, int i) + { + Order order; + order.next = NULL; + order.index = 0; + order.MakeGoToDepot(0, ODTFB_PART_OF_ORDERS); + order.SetDepotActionType(ODATFB_NEAREST_DEPOT); - if (_ctrl_pressed && sel < v->num_orders) { - const Order *ord = GetVehicleOrder(v, sel); - TileIndex xy; - - switch (ord->GetType()) { - case OT_GOTO_STATION: xy = GetStation(ord->GetDestination())->xy ; break; - case OT_GOTO_DEPOT: xy = (v->type == VEH_AIRCRAFT) ? GetStation(ord->GetDestination())->xy : GetDepot(ord->GetDestination())->xy; break; - case OT_GOTO_WAYPOINT: xy = GetWaypoint(ord->GetDestination())->xy; break; - default: xy = 0; break; - } - - if (xy != 0) ScrollMainWindowToTile(xy); - return; - } else { - if (sel == WP(w, order_d).sel) { - /* Deselect clicked order */ - WP(w, order_d).sel = -1; - } else { - /* Select clicked order */ - WP(w, order_d).sel = sel; + DoCommandP(w->vehicle->tile, w->vehicle->index + (w->OrderGetSel() << 16), order.Pack(), NULL, CMD_INSERT_ORDER | CMD_MSG(STR_8833_CAN_T_INSERT_NEW_ORDER)); + } - if (v->owner == _local_player) { - /* Activate drag and drop */ - SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, VHM_DRAG, w); - } - } - } - - w->SetDirty(); - } break; + /** + * Handle the click on the conditional order button. + * + * @param w current window + */ + static void OrderClick_Conditional(OrdersWindow *w, int i) + { + w->InvalidateWidget(ORDER_WIDGET_GOTO); + w->LowerWidget(ORDER_WIDGET_GOTO); + SetObjectToPlaceWnd(ANIMCURSOR_PICKSTATION, PAL_NONE, VHM_RECT, w); + w->goto_type = OPOS_CONDITIONAL; + } - case ORDER_WIDGET_SKIP: - OrderClick_Skip(w, v, 0); - break; + /** + * Handle the click on the unload button. + * + * @param w current window + */ + static void OrderClick_Unload(OrdersWindow *w, int unload_type) + { + VehicleOrderID sel_ord = w->OrderGetSel(); + const Order *order = GetVehicleOrder(w->vehicle, sel_ord); - case ORDER_WIDGET_DELETE: - OrderClick_Delete(w, v, 0); - break; + if (order == NULL || order->GetUnloadType() == unload_type) return; - case ORDER_WIDGET_NON_STOP: { - const Order *o = GetVehicleOrder(v, OrderGetSel(w)); - ShowDropDownMenu(w, _order_non_stop_drowdown, o->GetNonStopType(), ORDER_WIDGET_NON_STOP, 0, o->IsType(OT_GOTO_STATION) ? 0 : (o->IsType(OT_GOTO_WAYPOINT) ? 3 : 12)); - } break; + if (unload_type < 0) { + switch (order->GetUnloadType()) { + case OUF_UNLOAD_IF_POSSIBLE: unload_type = OUFB_UNLOAD; break; + case OUFB_UNLOAD: unload_type = OUFB_TRANSFER; break; + case OUFB_TRANSFER: unload_type = OUFB_NO_UNLOAD; break; + case OUFB_NO_UNLOAD: unload_type = OUF_UNLOAD_IF_POSSIBLE; break; + default: NOT_REACHED(); + } + } - case ORDER_WIDGET_GOTO: - OrderClick_Goto(w, v, 0); + DoCommandP(w->vehicle->tile, w->vehicle->index + (sel_ord << 16), MOF_UNLOAD | (unload_type << 4), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); + } + + /** + * Handle the click on the nonstop button. + * + * @param w current window + * @param non_stop what non-stop type to use; -1 to use the 'next' one. + */ + static void OrderClick_Nonstop(OrdersWindow *w, int non_stop) + { + VehicleOrderID sel_ord = w->OrderGetSel(); + const Order *order = GetVehicleOrder(w->vehicle, sel_ord); + + if (order == NULL || order->GetNonStopType() == non_stop) return; + + /* Keypress if negative, so 'toggle' to the next */ + if (non_stop < 0) { + non_stop = (order->GetNonStopType() + 1) % ONSF_END; + } + + DoCommandP(w->vehicle->tile, w->vehicle->index + (sel_ord << 16), MOF_NON_STOP | non_stop << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); + } + + /** + * Handle the click on the transfer button. + * + * @param w current window + */ + static void OrderClick_Transfer(OrdersWindow *w, int i) + { + VehicleOrderID sel_ord = w->OrderGetSel(); + const Order *order = GetVehicleOrder(w->vehicle, sel_ord); + + if (order == NULL) return; + + DoCommandP(w->vehicle->tile, w->vehicle->index + (sel_ord << 16), MOF_UNLOAD | ((order->GetUnloadType() & ~OUFB_NO_UNLOAD) ^ OUFB_TRANSFER) << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); + } + + /** + * Handle the click on the skip button. + * If ctrl is pressed skip to selected order. + * Else skip to current order + 1 + * + * @param w current window + */ + static void OrderClick_Skip(OrdersWindow *w, int i) + { + /* Don't skip when there's nothing to skip */ + if (_ctrl_pressed && w->vehicle->cur_order_index == w->OrderGetSel()) return; + + DoCommandP(w->vehicle->tile, w->vehicle->index, _ctrl_pressed ? w->OrderGetSel() : ((w->vehicle->cur_order_index + 1) % w->vehicle->num_orders), + NULL, CMD_SKIP_TO_ORDER | CMD_MSG(_ctrl_pressed ? STR_CAN_T_SKIP_TO_ORDER : STR_CAN_T_SKIP_ORDER)); + } + + /** + * Handle the click on the unload button. + * + * @param w current window + */ + static void OrderClick_Delete(OrdersWindow *w, int i) + { + DoCommandP(w->vehicle->tile, w->vehicle->index, w->OrderGetSel(), NULL, CMD_DELETE_ORDER | CMD_MSG(STR_8834_CAN_T_DELETE_THIS_ORDER)); + } + + /** + * Handle the click on the refit button. + * If ctrl is pressed cancel refitting. + * Else show the refit window. + * + * @param w current window + */ + static void OrderClick_Refit(OrdersWindow *w, int i) + { + if (_ctrl_pressed) { + /* Cancel refitting */ + DoCommandP(w->vehicle->tile, w->vehicle->index, (w->OrderGetSel() << 16) | (CT_NO_REFIT << 8) | CT_NO_REFIT, NULL, CMD_ORDER_REFIT); + } else { + ShowVehicleRefitWindow(w->vehicle, w->OrderGetSel()); + } + } + typedef void Handler(OrdersWindow*, int); + struct KeyToEvent { + uint16 keycode; + Handler *proc; + }; + +public: + OrdersWindow(const WindowDesc *desc, const Vehicle *v) : Window(desc, v->index) + { + this->caption_color = v->owner; + this->vscroll.cap = 6; + this->resize.step_height = 10; + this->selected_order = -1; + this->vehicle = v; + if (_patches.timetabling) { + this->widget[ORDER_WIDGET_CAPTION].right -= 61; + } else { + this->HideWidget(ORDER_WIDGET_TIMETABLE_VIEW); + } + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { + bool shared_orders = this->vehicle->IsOrderListShared(); + + SetVScrollCount(this, this->vehicle->num_orders + 1); + + int sel = OrderGetSel(); + const Order *order = GetVehicleOrder(this->vehicle, sel); + + if (this->vehicle->owner == _local_player) { + /* Set the strings for the dropdown boxes. */ + this->widget[ORDER_WIDGET_NON_STOP].data = _order_non_stop_drowdown[order == NULL ? 0 : order->GetNonStopType()]; + this->widget[ORDER_WIDGET_FULL_LOAD].data = _order_full_load_drowdown[order == NULL ? 0 : order->GetLoadType()]; + this->widget[ORDER_WIDGET_UNLOAD].data = _order_unload_drowdown[order == NULL ? 0 : order->GetUnloadType()]; + this->widget[ORDER_WIDGET_COND_VARIABLE].data = _order_conditional_variable[order == NULL ? 0 : order->GetConditionVariable()]; + this->widget[ORDER_WIDGET_COND_COMPARATOR].data = _order_conditional_condition[order == NULL ? 0 : order->GetConditionComparator()]; + + /* skip */ + this->SetWidgetDisabledState(ORDER_WIDGET_SKIP, this->vehicle->num_orders <= 1); + + /* delete */ + this->SetWidgetDisabledState(ORDER_WIDGET_DELETE, + (uint)this->vehicle->num_orders + ((shared_orders || this->vehicle->num_orders != 0) ? 1 : 0) <= (uint)this->selected_order); + + /* non-stop only for trains */ + this->SetWidgetDisabledState(ORDER_WIDGET_NON_STOP, (this->vehicle->type != VEH_TRAIN && this->vehicle->type != VEH_ROAD) || order == NULL); + this->SetWidgetDisabledState(ORDER_WIDGET_FULL_LOAD, order == NULL || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) != 0); // full load + this->SetWidgetDisabledState(ORDER_WIDGET_UNLOAD, order == NULL || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) != 0); // unload + /* Disable list of vehicles with the same shared orders if there is no list */ + this->SetWidgetDisabledState(ORDER_WIDGET_SHARED_ORDER_LIST, !shared_orders || this->vehicle->orders == NULL); + this->SetWidgetDisabledState(ORDER_WIDGET_REFIT, order == NULL); // Refit + this->SetWidgetDisabledState(ORDER_WIDGET_SERVICE, order == NULL); // Refit + this->HideWidget(ORDER_WIDGET_REFIT); // Refit + this->HideWidget(ORDER_WIDGET_SERVICE); // Service + + this->HideWidget(ORDER_WIDGET_COND_VARIABLE); + this->HideWidget(ORDER_WIDGET_COND_COMPARATOR); + this->HideWidget(ORDER_WIDGET_COND_VALUE); + } + + this->ShowWidget(ORDER_WIDGET_NON_STOP); + this->ShowWidget(ORDER_WIDGET_UNLOAD); + this->ShowWidget(ORDER_WIDGET_FULL_LOAD); + + if (order != NULL) { + switch (order->GetType()) { + case OT_GOTO_STATION: + if (!GetStation(order->GetDestination())->IsBuoy()) break; + /* Fall-through */ + + case OT_GOTO_WAYPOINT: + this->DisableWidget(ORDER_WIDGET_FULL_LOAD); + this->DisableWidget(ORDER_WIDGET_UNLOAD); break; - case ORDER_WIDGET_GOTO_DROPDOWN: - ShowDropDownMenu(w, v->type == VEH_AIRCRAFT ? _order_goto_dropdown_aircraft : _order_goto_dropdown, 0, ORDER_WIDGET_GOTO, 0, 0, w->widget[ORDER_WIDGET_GOTO_DROPDOWN].right - w->widget[ORDER_WIDGET_GOTO].left); - break; - - case ORDER_WIDGET_FULL_LOAD: - ShowDropDownMenu(w, _order_full_load_drowdown, GetVehicleOrder(v, OrderGetSel(w))->GetLoadType(), ORDER_WIDGET_FULL_LOAD, 0, 2); - break; - - case ORDER_WIDGET_UNLOAD: - ShowDropDownMenu(w, _order_unload_drowdown, GetVehicleOrder(v, OrderGetSel(w))->GetUnloadType(), ORDER_WIDGET_UNLOAD, 0, 8); - break; - - case ORDER_WIDGET_REFIT: - OrderClick_Refit(w, v, 0); - break; - - case ORDER_WIDGET_SERVICE: - OrderClick_Service(w, v, 0); - break; - - case ORDER_WIDGET_TIMETABLE_VIEW: - ShowTimetableWindow(v); - break; - - case ORDER_WIDGET_COND_VARIABLE: - ShowDropDownMenu(w, _order_conditional_variable, GetVehicleOrder(v, OrderGetSel(w))->GetConditionVariable(), ORDER_WIDGET_COND_VARIABLE, 0, 0); - break; - - case ORDER_WIDGET_COND_COMPARATOR: { - const Order *o = GetVehicleOrder(v, OrderGetSel(w)); - ShowDropDownMenu(w, _order_conditional_condition, o->GetConditionComparator(), ORDER_WIDGET_COND_COMPARATOR, 0, (o->GetConditionVariable() == OCV_REQUIRES_SERVICE) ? 0x3F : 0xC0); - } break; - - case ORDER_WIDGET_COND_VALUE: { - const Order *order = GetVehicleOrder(v, OrderGetSel(w)); - uint value = order->GetConditionValue(); - if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value); - SetDParam(0, value); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_ORDER_CONDITIONAL_VALUE_CAPT, 5, 100, w, CS_NUMERAL); - } break; - - case ORDER_WIDGET_SHARED_ORDER_LIST: - ShowVehicleListWindow(v); - break; - } - break; + case OT_GOTO_DEPOT: + this->DisableWidget(ORDER_WIDGET_FULL_LOAD); - case WE_ON_EDIT_TEXT: - if (!StrEmpty(e->we.edittext.str)) { - VehicleOrderID sel = OrderGetSel(w); - uint value = atoi(e->we.edittext.str); - - switch (GetVehicleOrder(v, sel)->GetConditionVariable()) { - case OCV_MAX_SPEED: - value = ConvertDisplaySpeedToSpeed(value); - break; - - case OCV_RELIABILITY: - case OCV_LOAD_PERCENTAGE: - value = Clamp(value, 0, 100); - - default: - break; - } - DoCommandP(v->tile, v->index + (sel << 16), MOF_COND_VALUE | Clamp(value, 0, 2047) << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); - } - break; - - case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list - switch (e->we.dropdown.button) { - case ORDER_WIDGET_NON_STOP: - OrderClick_Nonstop(w, v, e->we.dropdown.index); - break; - - case ORDER_WIDGET_FULL_LOAD: - OrderClick_FullLoad(w, v, e->we.dropdown.index); - break; - - case ORDER_WIDGET_UNLOAD: - OrderClick_Unload(w, v, e->we.dropdown.index); - break; - - case ORDER_WIDGET_GOTO: - switch (e->we.dropdown.index) { - case 0: - w->ToggleWidgetLoweredState(ORDER_WIDGET_GOTO); - OrderClick_Goto(w, v, 0); - break; - - case 1: OrderClick_NearestDepot(w, v, 0); break; - case 2: OrderClick_Conditional(w, v, 0); break; - default: NOT_REACHED(); - } - break; - - case ORDER_WIDGET_COND_VARIABLE: - DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), MOF_COND_VARIABLE | e->we.dropdown.index << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); + /* Remove unload and replace it with refit */ + this->HideWidget(ORDER_WIDGET_UNLOAD); + this->ShowWidget(ORDER_WIDGET_REFIT); + this->HideWidget(ORDER_WIDGET_FULL_LOAD); + this->ShowWidget(ORDER_WIDGET_SERVICE); break; - case ORDER_WIDGET_COND_COMPARATOR: - DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), MOF_COND_COMPARATOR | e->we.dropdown.index << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); - break; - } - break; + case OT_CONDITIONAL: { + this->HideWidget(ORDER_WIDGET_NON_STOP); + this->HideWidget(ORDER_WIDGET_UNLOAD); + this->HideWidget(ORDER_WIDGET_FULL_LOAD); + this->ShowWidget(ORDER_WIDGET_COND_VARIABLE); + this->ShowWidget(ORDER_WIDGET_COND_COMPARATOR); + this->ShowWidget(ORDER_WIDGET_COND_VALUE); - case WE_DRAGDROP: - switch (e->we.click.widget) { - case ORDER_WIDGET_ORDER_LIST: { - int from_order = OrderGetSel(w); - int to_order = GetOrderFromOrderWndPt(w, e->we.dragdrop.pt.y, v); + OrderConditionVariable ocv = order->GetConditionVariable(); + this->SetWidgetDisabledState(ORDER_WIDGET_COND_COMPARATOR, ocv == OCV_UNCONDITIONALLY); + this->SetWidgetDisabledState(ORDER_WIDGET_COND_VALUE, ocv == OCV_REQUIRES_SERVICE || ocv == OCV_UNCONDITIONALLY); - if (!(from_order == to_order || from_order == INVALID_ORDER || from_order > v->num_orders || to_order == INVALID_ORDER || to_order > v->num_orders) && - DoCommandP(v->tile, v->index, from_order | (to_order << 16), NULL, CMD_MOVE_ORDER | CMD_MSG(STR_CAN_T_MOVE_THIS_ORDER))) { - WP(w, order_d).sel = -1; + uint value = order->GetConditionValue(); + if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value); + SetDParam(1, value); + } break; + + default: // every other orders + this->DisableWidget(ORDER_WIDGET_NON_STOP); + this->DisableWidget(ORDER_WIDGET_FULL_LOAD); + this->DisableWidget(ORDER_WIDGET_UNLOAD); + } + } + + SetDParam(0, this->vehicle->index); + this->DrawWidgets(); + + int y = 15; + + int i = this->vscroll.pos; + order = GetVehicleOrder(this->vehicle, i); + StringID str; + while (order != NULL) { + /* Don't draw anything if it extends past the end of the window. */ + if (i - this->vscroll.pos >= this->vscroll.cap) break; + + DrawOrderString(this->vehicle, order, i, y, i == this->selected_order, false); + y += 10; + + i++; + order = order->next; + } + + if (i - this->vscroll.pos < this->vscroll.cap) { + str = shared_orders ? STR_END_OF_SHARED_ORDERS : STR_882A_END_OF_ORDERS; + DrawString(2, y, str, (i == this->selected_order) ? TC_WHITE : TC_BLACK); + } + } + + virtual void OnClick(Point pt, int widget) + { + if (this->widget[widget].type != WWT_DROPDOWN) HideDropDownMenu(this); + switch (widget) { + case ORDER_WIDGET_ORDER_LIST: { + ResetObjectToPlace(); + + int sel = this->GetOrderFromPt(pt.y); + + if (sel == INVALID_ORDER) { + /* This was a click on an empty part of the orders window, so + * deselect the currently selected order. */ + this->selected_order = -1; + this->SetDirty(); + return; + } + + if (_ctrl_pressed && sel < this->vehicle->num_orders) { + const Order *ord = GetVehicleOrder(this->vehicle, sel); + TileIndex xy; + + switch (ord->GetType()) { + case OT_GOTO_STATION: xy = GetStation(ord->GetDestination())->xy ; break; + case OT_GOTO_DEPOT: xy = (this->vehicle->type == VEH_AIRCRAFT) ? GetStation(ord->GetDestination())->xy : GetDepot(ord->GetDestination())->xy; break; + case OT_GOTO_WAYPOINT: xy = GetWaypoint(ord->GetDestination())->xy; break; + default: xy = 0; break; } - } break; + if (xy != 0) ScrollMainWindowToTile(xy); + return; + } else { + if (sel == this->selected_order) { + /* Deselect clicked order */ + this->selected_order = -1; + } else { + /* Select clicked order */ + this->selected_order = sel; - case ORDER_WIDGET_DELETE: - OrderClick_Delete(w, v, 0); + if (this->vehicle->owner == _local_player) { + /* Activate drag and drop */ + SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, VHM_DRAG, this); + } + } + } + + this->SetDirty(); + } break; + + case ORDER_WIDGET_SKIP: + OrderClick_Skip(this, 0); + break; + + case ORDER_WIDGET_DELETE: + OrderClick_Delete(this, 0); + break; + + case ORDER_WIDGET_NON_STOP: { + const Order *o = GetVehicleOrder(this->vehicle, this->OrderGetSel()); + ShowDropDownMenu(this, _order_non_stop_drowdown, o->GetNonStopType(), ORDER_WIDGET_NON_STOP, 0, o->IsType(OT_GOTO_STATION) ? 0 : (o->IsType(OT_GOTO_WAYPOINT) ? 3 : 12)); + } break; + + case ORDER_WIDGET_GOTO: + OrderClick_Goto(this, 0); + break; + + case ORDER_WIDGET_GOTO_DROPDOWN: + ShowDropDownMenu(this, this->vehicle->type == VEH_AIRCRAFT ? _order_goto_dropdown_aircraft : _order_goto_dropdown, 0, ORDER_WIDGET_GOTO, 0, 0, this->widget[ORDER_WIDGET_GOTO_DROPDOWN].right - this->widget[ORDER_WIDGET_GOTO].left); + break; + + case ORDER_WIDGET_FULL_LOAD: + ShowDropDownMenu(this, _order_full_load_drowdown, GetVehicleOrder(this->vehicle, this->OrderGetSel())->GetLoadType(), ORDER_WIDGET_FULL_LOAD, 0, 2); + break; + + case ORDER_WIDGET_UNLOAD: + ShowDropDownMenu(this, _order_unload_drowdown, GetVehicleOrder(this->vehicle, this->OrderGetSel())->GetUnloadType(), ORDER_WIDGET_UNLOAD, 0, 8); + break; + + case ORDER_WIDGET_REFIT: + OrderClick_Refit(this, 0); + break; + + case ORDER_WIDGET_SERVICE: + OrderClick_Service(this, 0); + break; + + case ORDER_WIDGET_TIMETABLE_VIEW: + ShowTimetableWindow(this->vehicle); + break; + + case ORDER_WIDGET_COND_VARIABLE: + ShowDropDownMenu(this, _order_conditional_variable, GetVehicleOrder(this->vehicle, this->OrderGetSel())->GetConditionVariable(), ORDER_WIDGET_COND_VARIABLE, 0, 0); + break; + + case ORDER_WIDGET_COND_COMPARATOR: { + const Order *o = GetVehicleOrder(this->vehicle, this->OrderGetSel()); + ShowDropDownMenu(this, _order_conditional_condition, o->GetConditionComparator(), ORDER_WIDGET_COND_COMPARATOR, 0, (o->GetConditionVariable() == OCV_REQUIRES_SERVICE) ? 0x3F : 0xC0); + } break; + + case ORDER_WIDGET_COND_VALUE: { + const Order *order = GetVehicleOrder(this->vehicle, this->OrderGetSel()); + uint value = order->GetConditionValue(); + if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value); + SetDParam(0, value); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_ORDER_CONDITIONAL_VALUE_CAPT, 5, 100, this, CS_NUMERAL); + } break; + + case ORDER_WIDGET_SHARED_ORDER_LIST: + ShowVehicleListWindow(this->vehicle); + break; + } + } + + virtual void OnQueryTextFinished(char *str) + { + if (!StrEmpty(str)) { + VehicleOrderID sel = this->OrderGetSel(); + uint value = atoi(str); + + switch (GetVehicleOrder(this->vehicle, sel)->GetConditionVariable()) { + case OCV_MAX_SPEED: + value = ConvertDisplaySpeedToSpeed(value); + break; + + case OCV_RELIABILITY: + case OCV_LOAD_PERCENTAGE: + value = Clamp(value, 0, 100); + + default: break; } - - ResetObjectToPlace(); - break; + DoCommandP(this->vehicle->tile, this->vehicle->index + (sel << 16), MOF_COND_VALUE | Clamp(value, 0, 2047) << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); + } + } - case WE_KEYPRESS: - if (v->owner != _local_player) break; + virtual void OnDropdownSelect(int widget, int index) + { + switch (widget) { + case ORDER_WIDGET_NON_STOP: + OrderClick_Nonstop(this, index); + break; - for (uint i = 0; i < lengthof(_order_keycodes); i++) { - if (e->we.keypress.keycode == _order_keycodes[i]) { - e->we.keypress.cont = false; - /* see if the button is disabled */ - if (!w->IsWidgetDisabled(i + ORDER_WIDGET_SKIP)) _order_button_proc[i](w, v, -1); - break; + case ORDER_WIDGET_FULL_LOAD: + OrderClick_FullLoad(this, index); + break; + + case ORDER_WIDGET_UNLOAD: + OrderClick_Unload(this, index); + break; + + case ORDER_WIDGET_GOTO: + switch (index) { + case 0: + this->ToggleWidgetLoweredState(ORDER_WIDGET_GOTO); + OrderClick_Goto(this, 0); + break; + + case 1: OrderClick_NearestDepot(this, 0); break; + case 2: OrderClick_Conditional(this, 0); break; + default: NOT_REACHED(); + } + break; + + case ORDER_WIDGET_COND_VARIABLE: + DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 16), MOF_COND_VARIABLE | index << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); + break; + + case ORDER_WIDGET_COND_COMPARATOR: + DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 16), MOF_COND_COMPARATOR | index << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); + break; + } + } + + virtual void OnDragDrop(Point pt, int widget) + { + switch (widget) { + case ORDER_WIDGET_ORDER_LIST: { + int from_order = this->OrderGetSel(); + int to_order = this->GetOrderFromPt(pt.y); + + if (!(from_order == to_order || from_order == INVALID_ORDER || from_order > this->vehicle->num_orders || to_order == INVALID_ORDER || to_order > this->vehicle->num_orders) && + DoCommandP(this->vehicle->tile, this->vehicle->index, from_order | (to_order << 16), NULL, CMD_MOVE_ORDER | CMD_MSG(STR_CAN_T_MOVE_THIS_ORDER))) { + this->selected_order = -1; + } + } break; + + case ORDER_WIDGET_DELETE: + OrderClick_Delete(this, 0); + break; + } + + ResetObjectToPlace(); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + static const KeyToEvent keytoevent[] = { + {'D', OrderClick_Skip}, + {'F', OrderClick_Delete}, + {'G', OrderClick_Goto}, + {'H', OrderClick_Nonstop}, + {'J', OrderClick_FullLoad}, + {'K', OrderClick_Unload}, + //{'?', OrderClick_Transfer}, + //('?', OrderClick_Service}, + }; + + if (this->vehicle->owner != _local_player) return ES_NOT_HANDLED; + + for (uint i = 0; i < lengthof(keytoevent); i++) { + if (keycode == keytoevent[i].keycode) { + keytoevent[i].proc(this, -1); + return ES_HANDLED; + } + } + return ES_NOT_HANDLED; + } + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + if (this->goto_type == OPOS_GOTO) { + /* check if we're clicking on a vehicle first.. clone orders in that case. */ + const Vehicle *v = CheckMouseOverVehicle(); + if (v != NULL && this->HandleOrderVehClick(v)) return; + + const Order cmd = GetOrderCmdFromTile(this->vehicle, tile); + if (!cmd.IsValid()) return; + + if (DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 16), cmd.Pack(), NULL, CMD_INSERT_ORDER | CMD_MSG(STR_8833_CAN_T_INSERT_NEW_ORDER))) { + if (this->selected_order != -1) this->selected_order++; + ResetObjectToPlace(); + } + } + } + + virtual void OnPlaceObjectAbort() + { + if (this->goto_type == OPOS_CONDITIONAL) { + this->goto_type = OPOS_GOTO; + if (_cursor.pos.x >= (this->left + this->widget[ORDER_WIDGET_ORDER_LIST].left) && + _cursor.pos.y >= (this->top + this->widget[ORDER_WIDGET_ORDER_LIST].top) && + _cursor.pos.x <= (this->left + this->widget[ORDER_WIDGET_ORDER_LIST].right) && + _cursor.pos.y <= (this->top + this->widget[ORDER_WIDGET_ORDER_LIST].bottom)) { + int order_id = this->GetOrderFromPt(_cursor.pos.y - this->top); + if (order_id != INVALID_ORDER) { + Order order; + order.next = NULL; + order.index = 0; + order.MakeConditional(order_id); + + DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 16), order.Pack(), NULL, CMD_INSERT_ORDER | CMD_MSG(STR_8833_CAN_T_INSERT_NEW_ORDER)); } } - break; - - case WE_PLACE_OBJ: - if (WP(w, order_d).goto_type == OPOS_GOTO) { - OrdersPlaceObj(GetVehicle(w->window_number), e->we.place.tile, w); - } - break; - - case WE_ABORT_PLACE_OBJ: - if (WP(w, order_d).goto_type == OPOS_CONDITIONAL) { - WP(w, order_d).goto_type = OPOS_GOTO; - if (_cursor.pos.x >= (w->left + w->widget[ORDER_WIDGET_ORDER_LIST].left) && - _cursor.pos.y >= (w->top + w->widget[ORDER_WIDGET_ORDER_LIST].top) && - _cursor.pos.x <= (w->left + w->widget[ORDER_WIDGET_ORDER_LIST].right) && - _cursor.pos.y <= (w->top + w->widget[ORDER_WIDGET_ORDER_LIST].bottom)) { - int order_id = GetOrderFromOrderWndPt(w, _cursor.pos.y - w->top, v); - if (order_id != INVALID_ORDER) { - Order order; - order.next = NULL; - order.index = 0; - order.MakeConditional(order_id); - - DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), order.Pack(), NULL, CMD_INSERT_ORDER | CMD_MSG(STR_8833_CAN_T_INSERT_NEW_ORDER)); - } - } - } - w->RaiseWidget(ORDER_WIDGET_GOTO); - w->InvalidateWidget(ORDER_WIDGET_GOTO); - break; + } + this->RaiseWidget(ORDER_WIDGET_GOTO); + this->InvalidateWidget(ORDER_WIDGET_GOTO); + } - /* check if a vehicle in a depot was clicked.. */ - case WE_MOUSELOOP: - v = _place_clicked_vehicle; - /* - * Check if we clicked on a vehicle - * and if the GOTO button of this window is pressed - * This is because of all open order windows WE_MOUSELOOP is called - * and if you have 3 windows open, and this check is not done - * the order is copied to the last open window instead of the - * one where GOTO is enabled - */ - if (v != NULL && w->IsWidgetLowered(ORDER_WIDGET_GOTO)) { - _place_clicked_vehicle = NULL; - HandleOrderVehClick(GetVehicle(w->window_number), v, w); + virtual void OnMouseLoop() + { + const Vehicle *v = _place_clicked_vehicle; + /* + * Check if we clicked on a vehicle + * and if the GOTO button of this window is pressed + * This is because of all open order windows WE_MOUSELOOP is called + * and if you have 3 windows open, and this check is not done + * the order is copied to the last open window instead of the + * one where GOTO is enabled + */ + if (v != NULL && this->IsWidgetLowered(ORDER_WIDGET_GOTO)) { + _place_clicked_vehicle = NULL; + this->HandleOrderVehClick(v); + } + } + + virtual void OnResize(Point new_size, Point delta) + { + /* Update the scroll + matrix */ + this->vscroll.cap = (this->widget[ORDER_WIDGET_ORDER_LIST].bottom - this->widget[ORDER_WIDGET_ORDER_LIST].top) / 10; + } + + virtual void OnTimeout() + { + /* unclick all buttons except for the 'goto' button (ORDER_WIDGET_GOTO), which is 'persistent' */ + for (uint i = 0; i < this->widget_count; i++) { + if (this->IsWidgetLowered(i) && i != ORDER_WIDGET_GOTO) { + this->RaiseWidget(i); + this->InvalidateWidget(i); } - break; - - case WE_RESIZE: - /* Update the scroll + matrix */ - w->vscroll.cap = (w->widget[ORDER_WIDGET_ORDER_LIST].bottom - w->widget[ORDER_WIDGET_ORDER_LIST].top) / 10; - break; - - case WE_TIMEOUT: // handle button unclick ourselves... - /* unclick all buttons except for the 'goto' button (ORDER_WIDGET_GOTO), which is 'persistent' */ - for (uint i = 0; i < w->widget_count; i++) { - if (w->IsWidgetLowered(i) && i != ORDER_WIDGET_GOTO) { - w->RaiseWidget(i); - w->InvalidateWidget(i); - } - } - break; + } } -} +}; /** * Widget definition for player train orders @@ -1091,7 +1063,6 @@ WC_VEHICLE_ORDERS, WC_VEHICLE_VIEW, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE, _orders_train_widgets, - OrdersWndProc }; /** @@ -1132,7 +1103,6 @@ WC_VEHICLE_ORDERS, WC_VEHICLE_VIEW, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE, _orders_widgets, - OrdersWndProc }; /** @@ -1173,27 +1143,18 @@ WC_VEHICLE_ORDERS, WC_VEHICLE_VIEW, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _other_orders_widgets, - OrdersWndProc }; void ShowOrdersWindow(const Vehicle *v) { - Window *w; VehicleID veh = v->index; DeleteWindowById(WC_VEHICLE_ORDERS, veh); DeleteWindowById(WC_VEHICLE_DETAILS, veh); if (v->owner != _local_player) { - w = AllocateWindowDescFront(&_other_orders_desc, veh); + new OrdersWindow(&_other_orders_desc, v); } else { - w = AllocateWindowDescFront((v->type == VEH_TRAIN || v->type == VEH_ROAD) ? &_orders_train_desc : &_orders_desc, veh); - } - - if (w != NULL) { - w->caption_color = v->owner; - w->vscroll.cap = 6; - w->resize.step_height = 10; - WP(w, order_d).sel = -1; + new OrdersWindow((v->type == VEH_TRAIN || v->type == VEH_ROAD) ? &_orders_train_desc : &_orders_desc, v); } } diff -r 6c4314786d68 -r 8cbdb511a674 src/osk_gui.cpp --- a/src/osk_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/osk_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -42,6 +42,7 @@ static byte _keystate = KEYS_NONE; struct OskWindow : public Window { + StringID caption; ///< the caption for this window. QueryString *qs; ///< text-input int text_btn; ///< widget number of parent's text field int ok_btn; ///< widget number of parent's ok button (=0 when ok shouldn't be passed on) @@ -54,7 +55,7 @@ this->parent = parent; assert(parent != NULL); - if (parent->widget[button].data != 0) parent->caption = parent->widget[button].data; + this->caption = (parent->widget[button].data != STR_NULL) ? parent->widget[button].data : parent->caption; this->qs = parent; this->text_btn = button; @@ -96,8 +97,8 @@ this->ChangeOskDiabledState(shift); - SetDParam(0, this->qs->caption); - DrawWindowWidgets(this); + SetDParam(0, this->caption); + this->DrawWidgets(); for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) { DrawCharCentered(_keyboard[shift][i], @@ -129,6 +130,8 @@ return; } + bool delete_this = false; + switch (widget) { case OSK_WIDGET_BACKSPACE: if (DeleteTextBufferChar(&this->qs->text, WKC_BACKSPACE)) this->InvalidateWidget(OSK_WIDGET_TEXT); @@ -169,25 +172,29 @@ /* pass information by simulating a button press on parent window */ if (this->ok_btn != 0) { this->parent->OnClick(pt, this->ok_btn); + /* Window gets deleted when the parent window removes itself. */ + return; } } - delete this; + delete_this = true; break; case OSK_WIDGET_CANCEL: if (this->cancel_btn != 0) { // pass a cancel event to the parent window this->parent->OnClick(pt, this->cancel_btn); /* Window gets deleted when the parent window removes itself. */ + return; } else { // or reset to original string strcpy(qs->text.buf, this->orig_str_buf); UpdateTextBufferSize(&qs->text); MoveTextBufferPos(&qs->text, WKC_END); - delete this; + delete_this = true; } break; } /* make sure that the parent window's textbox also gets updated */ if (this->parent != NULL) this->parent->InvalidateWidget(this->text_btn); + if (delete_this) delete this; } virtual void OnMouseLoop() @@ -276,12 +283,11 @@ { WIDGETS_END}, }; -WindowDesc _osk_desc = { +static const WindowDesc _osk_desc = { WDP_CENTER, WDP_CENTER, 256, 140, 256, 140, WC_OSK, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _osk_widgets, - NULL }; /** @@ -339,7 +345,7 @@ } /** - * Show the osk associated with a given textbox + * Show the on-screen keyboard (osk) associated with a given textbox * @param parent pointer to the Window where this keyboard originated from * @param q querystr_d pointer to the query string of the parent, which is * shared for both windows diff -r 6c4314786d68 -r 8cbdb511a674 src/player_face.h --- a/src/player_face.h Mon May 19 14:14:33 2008 +0000 +++ b/src/player_face.h Mon May 19 15:13:58 2008 +0000 @@ -230,6 +230,5 @@ void DrawPlayerFace(PlayerFace face, int color, int x, int y); PlayerFace ConvertFromOldPlayerFace(uint32 face); bool IsValidPlayerFace(PlayerFace pf); -void DrawFaceStringLabel(const Window *w, byte widget_index, StringID str, uint8 val, bool is_bool_widget); #endif /* PLAYER_FACE_H */ diff -r 6c4314786d68 -r 8cbdb511a674 src/player_gui.cpp --- a/src/player_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/player_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -35,34 +35,21 @@ #include "table/sprites.h" #include "table/strings.h" -/* player face selection window */ -struct facesel_d { - PlayerFace face; // player face bits - bool advanced; // advance player face selection window -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(facesel_d)); - -struct highscore_d { - uint32 background_img; - int8 rank; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(highscore_d)); - enum { FIRST_GUI_CALL = INT_MAX, ///< default value to specify thuis is the first call of the resizable gui }; static void DoShowPlayerFinances(PlayerID player, bool show_small, bool show_stickied, int top = FIRST_GUI_CALL, int left = FIRST_GUI_CALL); -static void DoSelectPlayerFace(PlayerID player, bool show_big, int top = FIRST_GUI_CALL, int left = FIRST_GUI_CALL); +static void DoSelectPlayerFace(Window *parent, bool show_big, int top = FIRST_GUI_CALL, int left = FIRST_GUI_CALL); -static void DrawPlayerEconomyStats(const Player *p, byte mode) +static void DrawPlayerEconomyStats(const Player *p, bool small) { int x, y, i, j, year; const Money (*tbl)[EXPENSES_END]; Money sum, cost; StringID str; - if (!(mode & 1)) { // normal sized economics window (mode&1) is minimized status + if (!small) { // normal sized economics window /* draw categories */ DrawStringCenterUnderline(61, 15, STR_700F_EXPENDITURE_INCOME, TC_FROMSTRING); for (i = 0; i != EXPENSES_END; i++) @@ -161,73 +148,88 @@ { WIDGETS_END}, }; - -static void PlayerFinancesWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - PlayerID player = (PlayerID)w->window_number; - const Player *p = GetPlayer(player); - - /* Recheck the size of the window as it might need to be resized due to the local player changing */ - int new_height = ((player != _local_player) ? 0 : 12) + ((WP(w, def_d).data_1 != 0) ? 48 : 74 + 10 * EXPENSES_END); - if (w->height != new_height) { - /* Make window dirty before and after resizing */ - w->SetDirty(); - w->height = new_height; - w->SetDirty(); - - w->SetWidgetHiddenState(PFW_WIDGET_INCREASE_LOAN, player != _local_player); - w->SetWidgetHiddenState(PFW_WIDGET_REPAY_LOAN, player != _local_player); - } - - /* Borrow button only shows when there is any more money to loan */ - w->SetWidgetDisabledState(PFW_WIDGET_INCREASE_LOAN, p->current_loan == _economy.max_loan); +struct PlayerFinancesWindow : Window { + bool small; - /* Repay button only shows when there is any more money to repay */ - w->SetWidgetDisabledState(PFW_WIDGET_REPAY_LOAN, player != _local_player || p->current_loan == 0); - - SetDParam(0, p->index); - SetDParam(1, p->index); - SetDParam(2, LOAN_INTERVAL); - DrawWindowWidgets(w); - - DrawPlayerEconomyStats(p, (byte)WP(w, def_d).data_1); - } break; + PlayerFinancesWindow(const WindowDesc *desc, PlayerID player, bool show_small, + bool show_stickied, int top, int left) : + Window(desc, player), + small(show_small) + { + this->caption_color = this->window_number; - case WE_CLICK: - switch (e->we.click.widget) { - case PFW_WIDGET_TOGGLE_SIZE: {/* toggle size */ - byte mode = (byte)WP(w, def_d).data_1; - bool stickied = !!(w->flags4 & WF_STICKY); - int oldtop = w->top; ///< current top position of the window before closing it - int oldleft = w->left; ///< current left position of the window before closing it - PlayerID player = (PlayerID)w->window_number; + if (show_stickied) this->flags4 |= WF_STICKY; - delete w; - /* Open up the (toggled size) Finance window at the same position as the previous */ - DoShowPlayerFinances(player, !HasBit(mode, 0), stickied, oldtop, oldleft); - } + /* Check if repositioning from default is required */ + if (top != FIRST_GUI_CALL && left != FIRST_GUI_CALL) { + this->top = top; + this->left = left; + } + } + + virtual void OnPaint() + { + PlayerID player = (PlayerID)this->window_number; + const Player *p = GetPlayer(player); + + /* Recheck the size of the window as it might need to be resized due to the local player changing */ + int new_height = ((player != _local_player) ? 0 : 12) + ((this->small != 0) ? 48 : 74 + 10 * EXPENSES_END); + if (this->height != new_height) { + /* Make window dirty before and after resizing */ + this->SetDirty(); + this->height = new_height; + this->SetDirty(); + + this->SetWidgetHiddenState(PFW_WIDGET_INCREASE_LOAN, player != _local_player); + this->SetWidgetHiddenState(PFW_WIDGET_REPAY_LOAN, player != _local_player); + } + + /* Borrow button only shows when there is any more money to loan */ + this->SetWidgetDisabledState(PFW_WIDGET_INCREASE_LOAN, p->current_loan == _economy.max_loan); + + /* Repay button only shows when there is any more money to repay */ + this->SetWidgetDisabledState(PFW_WIDGET_REPAY_LOAN, player != _local_player || p->current_loan == 0); + + SetDParam(0, p->index); + SetDParam(1, p->index); + SetDParam(2, LOAN_INTERVAL); + this->DrawWidgets(); + + DrawPlayerEconomyStats(p, this->small); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case PFW_WIDGET_TOGGLE_SIZE: {/* toggle size */ + bool new_mode = !this->small; + bool stickied = !!(this->flags4 & WF_STICKY); + int oldtop = this->top; ///< current top position of the window before closing it + int oldleft = this->left; ///< current left position of the window before closing it + PlayerID player = (PlayerID)this->window_number; + + delete this; + /* Open up the (toggled size) Finance window at the same position as the previous */ + DoShowPlayerFinances(player, new_mode, stickied, oldtop, oldleft); + } + break; + + case PFW_WIDGET_INCREASE_LOAN: /* increase loan */ + DoCommandP(0, 0, _ctrl_pressed, NULL, CMD_INCREASE_LOAN | CMD_MSG(STR_702C_CAN_T_BORROW_ANY_MORE_MONEY)); break; - case PFW_WIDGET_INCREASE_LOAN: /* increase loan */ - DoCommandP(0, 0, _ctrl_pressed, NULL, CMD_INCREASE_LOAN | CMD_MSG(STR_702C_CAN_T_BORROW_ANY_MORE_MONEY)); - break; - - case PFW_WIDGET_REPAY_LOAN: /* repay loan */ - DoCommandP(0, 0, _ctrl_pressed, NULL, CMD_DECREASE_LOAN | CMD_MSG(STR_702F_CAN_T_REPAY_LOAN)); - break; - } - break; + case PFW_WIDGET_REPAY_LOAN: /* repay loan */ + DoCommandP(0, 0, _ctrl_pressed, NULL, CMD_DECREASE_LOAN | CMD_MSG(STR_702F_CAN_T_REPAY_LOAN)); + break; + } } -} +}; static const WindowDesc _player_finances_desc = { WDP_AUTO, WDP_AUTO, 407, 86 + 10 * EXPENSES_END, 407, 86 + 10 * EXPENSES_END, WC_FINANCES, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON, _player_finances_widgets, - PlayerFinancesWndProc }; static const WindowDesc _player_finances_small_desc = { @@ -235,7 +237,6 @@ WC_FINANCES, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON, _player_finances_small_widgets, - PlayerFinancesWndProc }; /** @@ -253,19 +254,8 @@ { if (!IsValidPlayer(player)) return; - Window *w = AllocateWindowDescFront(show_small ? &_player_finances_small_desc : &_player_finances_desc, player); - if (w != NULL) { - w->caption_color = w->window_number; - WP(w, def_d).data_1 = show_small; - - if (show_stickied) w->flags4 |= WF_STICKY; - - /* Check if repositioning from default is required */ - if (top != FIRST_GUI_CALL && left != FIRST_GUI_CALL) { - w->top = top; - w->left = left; - } - } + if (BringWindowToFrontById(WC_FINANCES, player)) return; + new PlayerFinancesWindow(show_small ? &_player_finances_small_desc : &_player_finances_desc, player, show_small, show_stickied, top, left); } void ShowPlayerFinances(PlayerID player) @@ -294,7 +284,7 @@ }; /* Association of liveries to livery classes */ -static const LiveryClass livery_class[LS_END] = { +static const LiveryClass _livery_class[LS_END] = { LC_OTHER, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_ROAD, LC_ROAD, @@ -303,34 +293,6 @@ LC_ROAD, LC_ROAD, }; -/* Number of liveries in each class, used to determine the height of the livery window */ -static const byte livery_height[] = { - 1, - 13, - 4, - 2, - 3, -}; - -struct livery_d { - uint32 sel; - LiveryClass livery_class; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(livery_d)); - - -enum PlayerLiveryWindowWidgets { - PLW_WIDGET_CLASS_GENERAL = 2, - PLW_WIDGET_CLASS_RAIL, - PLW_WIDGET_CLASS_ROAD, - PLW_WIDGET_CLASS_SHIP, - PLW_WIDGET_CLASS_AIRCRAFT, - - PLW_WIDGET_PRI_COL_DROPDOWN = 9, - PLW_WIDGET_SEC_COL_DROPDOWN, - PLW_WIDGET_MATRIX, -}; - class DropDownListColourItem : public DropDownListItem { public: DropDownListColourItem(int result, bool masked) : DropDownListItem(result, masked) {} @@ -354,168 +316,211 @@ } }; -static void ShowColourDropDownMenu(Window *w, uint32 widget) -{ - uint32 used_colours = 0; - const Livery *livery; - LiveryScheme scheme; +struct SelectPlayerLiveryWindow : public Window { +private: + uint32 sel; + LiveryClass livery_class; - /* Disallow other player colours for the primary colour */ - if (HasBit(WP(w, livery_d).sel, LS_DEFAULT) && widget == PLW_WIDGET_PRI_COL_DROPDOWN) { - const Player *p; - FOR_ALL_PLAYERS(p) { - if (p->is_active && p->index != _local_player) SetBit(used_colours, p->player_color); + enum PlayerLiveryWindowWidgets { + PLW_WIDGET_CLOSE, + PLW_WIDGET_CAPTION, + PLW_WIDGET_CLASS_GENERAL, + PLW_WIDGET_CLASS_RAIL, + PLW_WIDGET_CLASS_ROAD, + PLW_WIDGET_CLASS_SHIP, + PLW_WIDGET_CLASS_AIRCRAFT, + PLW_WIDGET_SPACER_CLASS, + PLW_WIDGET_SPACER_DROPDOWN, + PLW_WIDGET_PRI_COL_DROPDOWN, + PLW_WIDGET_SEC_COL_DROPDOWN, + PLW_WIDGET_MATRIX, + }; + + void ShowColourDropDownMenu(uint32 widget) + { + uint32 used_colours = 0; + const Livery *livery; + LiveryScheme scheme; + + /* Disallow other player colours for the primary colour */ + if (HasBit(this->sel, LS_DEFAULT) && widget == PLW_WIDGET_PRI_COL_DROPDOWN) { + const Player *p; + FOR_ALL_PLAYERS(p) { + if (p->is_active && p->index != _local_player) SetBit(used_colours, p->player_color); + } + } + + /* Get the first selected livery to use as the default dropdown item */ + for (scheme = LS_BEGIN; scheme < LS_END; scheme++) { + if (HasBit(this->sel, scheme)) break; + } + if (scheme == LS_END) scheme = LS_DEFAULT; + livery = &GetPlayer((PlayerID)this->window_number)->livery[scheme]; + + DropDownList *list = new DropDownList(); + for (uint i = 0; i < lengthof(_colour_dropdown); i++) { + list->push_back(new DropDownListColourItem(i, HasBit(used_colours, i))); + } + + ShowDropDownList(this, list, widget == PLW_WIDGET_PRI_COL_DROPDOWN ? livery->colour1 : livery->colour2, widget); + } + +public: + SelectPlayerLiveryWindow(const WindowDesc *desc, PlayerID player) : Window(desc, player) + { + this->caption_color = player; + this->livery_class = LC_OTHER; + this->sel = 1; + this->LowerWidget(PLW_WIDGET_CLASS_GENERAL); + this->OnInvalidateData(_loaded_newgrf_features.has_2CC); + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { + const Player *p = GetPlayer((PlayerID)this->window_number); + LiveryScheme scheme = LS_DEFAULT; + int y = 51; + + /* Disable dropdown controls if no scheme is selected */ + this->SetWidgetDisabledState(PLW_WIDGET_PRI_COL_DROPDOWN, this->sel == 0); + this->SetWidgetDisabledState(PLW_WIDGET_SEC_COL_DROPDOWN, this->sel == 0); + + if (this->sel != 0) { + for (scheme = LS_BEGIN; scheme < LS_END; scheme++) { + if (HasBit(this->sel, scheme)) break; + } + if (scheme == LS_END) scheme = LS_DEFAULT; + } + + SetDParam(0, STR_00D1_DARK_BLUE + p->livery[scheme].colour1); + SetDParam(1, STR_00D1_DARK_BLUE + p->livery[scheme].colour2); + + this->DrawWidgets(); + + for (scheme = LS_DEFAULT; scheme < LS_END; scheme++) { + if (_livery_class[scheme] == this->livery_class) { + bool sel = HasBit(this->sel, scheme) != 0; + + if (scheme != LS_DEFAULT) { + DrawSprite(p->livery[scheme].in_use ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, PAL_NONE, 2, y); + } + + DrawString(15, y, STR_LIVERY_DEFAULT + scheme, sel ? TC_WHITE : TC_BLACK); + + DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOR(p->livery[scheme].colour1), 152, y); + DrawString(165, y, STR_00D1_DARK_BLUE + p->livery[scheme].colour1, sel ? TC_WHITE : TC_GOLD); + + if (!this->IsWidgetHidden(PLW_WIDGET_SEC_COL_DROPDOWN)) { + DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOR(p->livery[scheme].colour2), 277, y); + DrawString(290, y, STR_00D1_DARK_BLUE + p->livery[scheme].colour2, sel ? TC_WHITE : TC_GOLD); + } + + y += 14; + } } } - /* Get the first selected livery to use as the default dropdown item */ - for (scheme = LS_BEGIN; scheme < LS_END; scheme++) { - if (HasBit(WP(w, livery_d).sel, scheme)) break; - } - if (scheme == LS_END) scheme = LS_DEFAULT; - livery = &GetPlayer((PlayerID)w->window_number)->livery[scheme]; - - DropDownList *list = new DropDownList(); - for (uint i = 0; i < lengthof(_colour_dropdown); i++) { - list->push_back(new DropDownListColourItem(i, HasBit(used_colours, i))); - } - - ShowDropDownList(w, list, widget == PLW_WIDGET_PRI_COL_DROPDOWN ? livery->colour1 : livery->colour2, widget); -} + virtual void OnClick(Point pt, int widget) + { + /* Number of liveries in each class, used to determine the height of the livery window */ + static const byte livery_height[] = { + 1, + 13, + 4, + 2, + 3, + }; -static void SelectPlayerLiveryWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - w->LowerWidget(WP(w, livery_d).livery_class + PLW_WIDGET_CLASS_GENERAL); - if (!_loaded_newgrf_features.has_2CC) { - w->HideWidget(PLW_WIDGET_SEC_COL_DROPDOWN); - } - break; + switch (widget) { + /* Livery Class buttons */ + case PLW_WIDGET_CLASS_GENERAL: + case PLW_WIDGET_CLASS_RAIL: + case PLW_WIDGET_CLASS_ROAD: + case PLW_WIDGET_CLASS_SHIP: + case PLW_WIDGET_CLASS_AIRCRAFT: { + LiveryScheme scheme; - case WE_PAINT: { - const Player *p = GetPlayer((PlayerID)w->window_number); - LiveryScheme scheme = LS_DEFAULT; - int y = 51; + this->RaiseWidget(this->livery_class + PLW_WIDGET_CLASS_GENERAL); + this->livery_class = (LiveryClass)(widget - PLW_WIDGET_CLASS_GENERAL); + this->sel = 0; + this->LowerWidget(this->livery_class + PLW_WIDGET_CLASS_GENERAL); - /* Disable dropdown controls if no scheme is selected */ - w->SetWidgetDisabledState(PLW_WIDGET_PRI_COL_DROPDOWN, (WP(w, livery_d).sel == 0)); - w->SetWidgetDisabledState(PLW_WIDGET_SEC_COL_DROPDOWN, (WP(w, livery_d).sel == 0)); - - if (!(WP(w, livery_d).sel == 0)) { - for (scheme = LS_BEGIN; scheme < LS_END; scheme++) { - if (HasBit(WP(w, livery_d).sel, scheme)) break; + /* Select the first item in the list */ + for (scheme = LS_DEFAULT; scheme < LS_END; scheme++) { + if (_livery_class[scheme] == this->livery_class) { + this->sel = 1 << scheme; + break; + } } - if (scheme == LS_END) scheme = LS_DEFAULT; + this->height = 49 + livery_height[this->livery_class] * 14; + this->widget[PLW_WIDGET_MATRIX].bottom = this->height - 1; + this->widget[PLW_WIDGET_MATRIX].data = livery_height[this->livery_class] << 8 | 1; + MarkWholeScreenDirty(); + break; } - SetDParam(0, STR_00D1_DARK_BLUE + p->livery[scheme].colour1); - SetDParam(1, STR_00D1_DARK_BLUE + p->livery[scheme].colour2); - - DrawWindowWidgets(w); - - for (scheme = LS_DEFAULT; scheme < LS_END; scheme++) { - if (livery_class[scheme] == WP(w, livery_d).livery_class) { - bool sel = HasBit(WP(w, livery_d).sel, scheme) != 0; + case PLW_WIDGET_PRI_COL_DROPDOWN: /* First colour dropdown */ + ShowColourDropDownMenu(PLW_WIDGET_PRI_COL_DROPDOWN); + break; - if (scheme != LS_DEFAULT) { - DrawSprite(p->livery[scheme].in_use ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, PAL_NONE, 2, y); - } - - DrawString(15, y, STR_LIVERY_DEFAULT + scheme, sel ? TC_WHITE : TC_BLACK); + case PLW_WIDGET_SEC_COL_DROPDOWN: /* Second colour dropdown */ + ShowColourDropDownMenu(PLW_WIDGET_SEC_COL_DROPDOWN); + break; - DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOR(p->livery[scheme].colour1), 152, y); - DrawString(165, y, STR_00D1_DARK_BLUE + p->livery[scheme].colour1, sel ? TC_WHITE : TC_GOLD); + case PLW_WIDGET_MATRIX: { + LiveryScheme scheme; + LiveryScheme j = (LiveryScheme)((pt.y - 48) / 14); - if (_loaded_newgrf_features.has_2CC) { - DrawSprite(SPR_SQUARE, GENERAL_SPRITE_COLOR(p->livery[scheme].colour2), 277, y); - DrawString(290, y, STR_00D1_DARK_BLUE + p->livery[scheme].colour2, sel ? TC_WHITE : TC_GOLD); - } - - y += 14; + for (scheme = LS_BEGIN; scheme <= j; scheme++) { + if (_livery_class[scheme] != this->livery_class) j++; + if (scheme >= LS_END) return; } - } - break; - } - - case WE_CLICK: { - switch (e->we.click.widget) { - /* Livery Class buttons */ - case PLW_WIDGET_CLASS_GENERAL: - case PLW_WIDGET_CLASS_RAIL: - case PLW_WIDGET_CLASS_ROAD: - case PLW_WIDGET_CLASS_SHIP: - case PLW_WIDGET_CLASS_AIRCRAFT: { - LiveryScheme scheme; + if (j >= LS_END) return; - w->RaiseWidget(WP(w, livery_d).livery_class + PLW_WIDGET_CLASS_GENERAL); - WP(w, livery_d).livery_class = (LiveryClass)(e->we.click.widget - PLW_WIDGET_CLASS_GENERAL); - WP(w, livery_d).sel = 0; - w->LowerWidget(WP(w, livery_d).livery_class + PLW_WIDGET_CLASS_GENERAL); - - /* Select the first item in the list */ - for (scheme = LS_DEFAULT; scheme < LS_END; scheme++) { - if (livery_class[scheme] == WP(w, livery_d).livery_class) { - WP(w, livery_d).sel = 1 << scheme; - break; - } - } - w->height = 49 + livery_height[WP(w, livery_d).livery_class] * 14; - w->widget[PLW_WIDGET_MATRIX].bottom = w->height - 1; - w->widget[PLW_WIDGET_MATRIX].data = livery_height[WP(w, livery_d).livery_class] << 8 | 1; - MarkWholeScreenDirty(); - break; + /* If clicking on the left edge, toggle using the livery */ + if (pt.x < 10) { + DoCommandP(0, j | (2 << 8), !GetPlayer((PlayerID)this->window_number)->livery[j].in_use, NULL, CMD_SET_PLAYER_COLOR); } - case PLW_WIDGET_PRI_COL_DROPDOWN: /* First colour dropdown */ - ShowColourDropDownMenu(w, PLW_WIDGET_PRI_COL_DROPDOWN); - break; - - case PLW_WIDGET_SEC_COL_DROPDOWN: /* Second colour dropdown */ - ShowColourDropDownMenu(w, PLW_WIDGET_SEC_COL_DROPDOWN); - break; - - case PLW_WIDGET_MATRIX: { - LiveryScheme scheme; - LiveryScheme j = (LiveryScheme)((e->we.click.pt.y - 48) / 14); - - for (scheme = LS_BEGIN; scheme <= j; scheme++) { - if (livery_class[scheme] != WP(w, livery_d).livery_class) j++; - if (scheme >= LS_END) return; - } - if (j >= LS_END) return; - - /* If clicking on the left edge, toggle using the livery */ - if (e->we.click.pt.x < 10) { - DoCommandP(0, j | (2 << 8), !GetPlayer((PlayerID)w->window_number)->livery[j].in_use, NULL, CMD_SET_PLAYER_COLOR); - } - - if (_ctrl_pressed) { - ToggleBit(WP(w, livery_d).sel, j); - } else { - WP(w, livery_d).sel = 1 << j; - } - w->SetDirty(); - break; + if (_ctrl_pressed) { + ToggleBit(this->sel, j); + } else { + this->sel = 1 << j; } + this->SetDirty(); + break; } - break; - } - - case WE_DROPDOWN_SELECT: { - LiveryScheme scheme; - - for (scheme = LS_DEFAULT; scheme < LS_END; scheme++) { - if (HasBit(WP(w, livery_d).sel, scheme)) { - DoCommandP(0, scheme | (e->we.dropdown.button == PLW_WIDGET_PRI_COL_DROPDOWN ? 0 : 256), e->we.dropdown.index, NULL, CMD_SET_PLAYER_COLOR); - } - } - break; } } -} -static const Widget _select_player_livery_2cc_widgets[] = { + virtual void OnDropdownSelect(int widget, int index) + { + for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) { + if (HasBit(this->sel, scheme)) { + DoCommandP(0, scheme | (widget == PLW_WIDGET_PRI_COL_DROPDOWN ? 0 : 256), index, NULL, CMD_SET_PLAYER_COLOR); + } + } + } + + virtual void OnInvalidateData(int data = 0) + { + static bool has2cc = true; + + if (has2cc == !!data) return; + + has2cc = !!data; + + int r = this->widget[has2cc ? PLW_WIDGET_SEC_COL_DROPDOWN : PLW_WIDGET_PRI_COL_DROPDOWN].right; + this->SetWidgetHiddenState(PLW_WIDGET_SEC_COL_DROPDOWN, !has2cc); + this->widget[PLW_WIDGET_CAPTION].right = r; + this->widget[PLW_WIDGET_SPACER_CLASS].right = r; + this->widget[PLW_WIDGET_MATRIX].right = r; + this->width = r + 1; + } +}; + +static const Widget _select_player_livery_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, { WWT_CAPTION, RESIZE_NONE, 14, 11, 399, 0, 13, STR_7007_NEW_COLOR_SCHEME, STR_018C_WINDOW_TITLE_DRAG_THIS }, { WWT_IMGBTN, RESIZE_NONE, 14, 0, 21, 14, 35, SPR_IMG_COMPANY_GENERAL, STR_LIVERY_GENERAL_TIP }, @@ -531,37 +536,11 @@ { WIDGETS_END }, }; -static const WindowDesc _select_player_livery_2cc_desc = { +static const WindowDesc _select_player_livery_desc = { WDP_AUTO, WDP_AUTO, 400, 49 + 1 * 14, 400, 49 + 1 * 14, WC_PLAYER_COLOR, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, - _select_player_livery_2cc_widgets, - SelectPlayerLiveryWndProc -}; - - -static const Widget _select_player_livery_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, -{ WWT_CAPTION, RESIZE_NONE, 14, 11, 274, 0, 13, STR_7007_NEW_COLOR_SCHEME, STR_018C_WINDOW_TITLE_DRAG_THIS }, -{ WWT_IMGBTN, RESIZE_NONE, 14, 0, 21, 14, 35, SPR_IMG_COMPANY_GENERAL, STR_LIVERY_GENERAL_TIP }, -{ WWT_IMGBTN, RESIZE_NONE, 14, 22, 43, 14, 35, SPR_IMG_TRAINLIST, STR_LIVERY_TRAIN_TIP }, -{ WWT_IMGBTN, RESIZE_NONE, 14, 44, 65, 14, 35, SPR_IMG_TRUCKLIST, STR_LIVERY_ROADVEH_TIP }, -{ WWT_IMGBTN, RESIZE_NONE, 14, 66, 87, 14, 35, SPR_IMG_SHIPLIST, STR_LIVERY_SHIP_TIP }, -{ WWT_IMGBTN, RESIZE_NONE, 14, 88, 109, 14, 35, SPR_IMG_AIRPLANESLIST, STR_LIVERY_AIRCRAFT_TIP }, -{ WWT_PANEL, RESIZE_NONE, 14, 110, 274, 14, 35, 0x0, STR_NULL }, -{ WWT_PANEL, RESIZE_NONE, 14, 0, 149, 36, 47, 0x0, STR_NULL }, -{ WWT_DROPDOWN, RESIZE_NONE, 14, 150, 274, 36, 47, STR_02BD, STR_LIVERY_PRIMARY_TIP }, -{ WWT_DROPDOWN, RESIZE_NONE, 14, 275, 275, 36, 47, STR_02E1, STR_LIVERY_SECONDARY_TIP }, -{ WWT_MATRIX, RESIZE_NONE, 14, 0, 274, 48, 48 + 1 * 14, (1 << 8) | 1, STR_LIVERY_PANEL_TIP }, -{ WIDGETS_END }, -}; - -static const WindowDesc _select_player_livery_desc = { - WDP_AUTO, WDP_AUTO, 275, 49 + 1 * 14, 275, 49 + 1 * 14, - WC_PLAYER_COLOR, WC_NONE, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _select_player_livery_widgets, - SelectPlayerLiveryWndProc }; /** @@ -608,62 +587,6 @@ } } -/** - * Names of the widgets. Keep them in the same order as in the widget array. - * Do not change the order of the widgets from PFW_WIDGET_HAS_MOUSTACHE_EARRING to PFW_WIDGET_GLASSES_R, - * this order is needed for the WE_CLICK event of DrawFaceStringLabel(). - */ -enum PlayerFaceWindowWidgets { - PFW_WIDGET_CLOSEBOX = 0, - PFW_WIDGET_CAPTION, - PFW_WIDGET_TOGGLE_LARGE_SMALL, - PFW_WIDGET_SELECT_FACE, - PFW_WIDGET_CANCEL, - PFW_WIDGET_ACCEPT, - PFW_WIDGET_MALE, - PFW_WIDGET_FEMALE, - PFW_WIDGET_RANDOM_NEW_FACE, - PFW_WIDGET_TOGGLE_LARGE_SMALL_BUTTON, - /* from here is the advanced player face selection window */ - PFW_WIDGET_LOAD, - PFW_WIDGET_FACECODE, - PFW_WIDGET_SAVE, - PFW_WIDGET_ETHNICITY_EUR, - PFW_WIDGET_ETHNICITY_AFR, - PFW_WIDGET_HAS_MOUSTACHE_EARRING, - PFW_WIDGET_HAS_GLASSES, - PFW_WIDGET_EYECOLOUR_L, - PFW_WIDGET_EYECOLOUR, - PFW_WIDGET_EYECOLOUR_R, - PFW_WIDGET_CHIN_L, - PFW_WIDGET_CHIN, - PFW_WIDGET_CHIN_R, - PFW_WIDGET_EYEBROWS_L, - PFW_WIDGET_EYEBROWS, - PFW_WIDGET_EYEBROWS_R, - PFW_WIDGET_LIPS_MOUSTACHE_L, - PFW_WIDGET_LIPS_MOUSTACHE, - PFW_WIDGET_LIPS_MOUSTACHE_R, - PFW_WIDGET_NOSE_L, - PFW_WIDGET_NOSE, - PFW_WIDGET_NOSE_R, - PFW_WIDGET_HAIR_L, - PFW_WIDGET_HAIR, - PFW_WIDGET_HAIR_R, - PFW_WIDGET_JACKET_L, - PFW_WIDGET_JACKET, - PFW_WIDGET_JACKET_R, - PFW_WIDGET_COLLAR_L, - PFW_WIDGET_COLLAR, - PFW_WIDGET_COLLAR_R, - PFW_WIDGET_TIE_EARRING_L, - PFW_WIDGET_TIE_EARRING, - PFW_WIDGET_TIE_EARRING_R, - PFW_WIDGET_GLASSES_L, - PFW_WIDGET_GLASSES, - PFW_WIDGET_GLASSES_R, -}; - /** Widget description for the normal/simple player face selection dialog */ static const Widget _select_player_face_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // PFW_WIDGET_CLOSEBOX @@ -731,267 +654,352 @@ { WIDGETS_END}, }; -/** - * Draw dynamic a label to the left of the button and a value in the button - * - * @param w Window on which the widget is located - * @param widget_index index of this widget in the window - * @param str the label which will be draw - * @param val the value which will be draw - * @param is_bool_widget is it a bool button - */ -void DrawFaceStringLabel(const Window *w, byte widget_index, StringID str, uint8 val, bool is_bool_widget) +class SelectPlayerFaceWindow : public Window { - /* Write the label in gold (0x2) to the left of the button. */ - DrawStringRightAligned(w->widget[widget_index].left - (is_bool_widget ? 5 : 14), w->widget[widget_index].top + 1, str, TC_GOLD); - - if (!w->IsWidgetDisabled(widget_index)) { - if (is_bool_widget) { - /* if it a bool button write yes or no */ - str = (val != 0) ? STR_FACE_YES : STR_FACE_NO; - } else { - /* else write the value + 1 */ - SetDParam(0, val + 1); - str = STR_JUST_INT; - } - - /* Draw the value/bool in white (0xC). If the button clicked adds 1px to x and y text coordinates (IsWindowWidgetLowered()). */ - DrawStringCentered(w->widget[widget_index].left + (w->widget[widget_index].right - w->widget[widget_index].left) / 2 + - w->IsWidgetLowered(widget_index), w->widget[widget_index].top + 1 + w->IsWidgetLowered(widget_index), str, TC_WHITE); - } -} - -/** - * Player face selection window event definition - * - * @param w window pointer - * @param e event been triggered - */ -static void SelectPlayerFaceWndProc(Window *w, WindowEvent *e) -{ - PlayerFace *pf = &WP(w, facesel_d).face; // pointer to the player face bits - GenderEthnicity ge = (GenderEthnicity)GB(*pf, _pf_info[PFV_GEN_ETHN].offset, _pf_info[PFV_GEN_ETHN].length); // get the gender and ethnicity - bool is_female = HasBit(ge, GENDER_FEMALE); // get the gender: 0 == male and 1 == female - bool is_moust_male = !is_female && GetPlayerFaceBits(*pf, PFV_HAS_MOUSTACHE, ge) != 0; // is a male face with moustache - - switch (e->event) { - case WE_PAINT: - /* lower the non-selected gender button */ - w->SetWidgetLoweredState(PFW_WIDGET_MALE, !is_female); - w->SetWidgetLoweredState(PFW_WIDGET_FEMALE, is_female); + PlayerFace face; // player face bits + bool advanced; // advance player face selection window - /* advanced player face selection window */ - if (WP(w, facesel_d).advanced) { - /* lower the non-selected ethnicity button */ - w->SetWidgetLoweredState(PFW_WIDGET_ETHNICITY_EUR, !HasBit(ge, ETHNICITY_BLACK)); - w->SetWidgetLoweredState(PFW_WIDGET_ETHNICITY_AFR, HasBit(ge, ETHNICITY_BLACK)); - - - /* Disable dynamically the widgets which PlayerFaceVariable has less than 2 options - * (or in other words you haven't any choice). - * If the widgets depend on a HAS-variable and this is false the widgets will be disabled, too. */ - - /* Eye colour buttons */ - w->SetWidgetsDisabledState(_pf_info[PFV_EYE_COLOUR].valid_values[ge] < 2, - PFW_WIDGET_EYECOLOUR, PFW_WIDGET_EYECOLOUR_L, PFW_WIDGET_EYECOLOUR_R, WIDGET_LIST_END); - - /* Chin buttons */ - w->SetWidgetsDisabledState(_pf_info[PFV_CHIN].valid_values[ge] < 2, - PFW_WIDGET_CHIN, PFW_WIDGET_CHIN_L, PFW_WIDGET_CHIN_R, WIDGET_LIST_END); - - /* Eyebrows buttons */ - w->SetWidgetsDisabledState(_pf_info[PFV_EYEBROWS].valid_values[ge] < 2, - PFW_WIDGET_EYEBROWS, PFW_WIDGET_EYEBROWS_L, PFW_WIDGET_EYEBROWS_R, WIDGET_LIST_END); + GenderEthnicity ge; + bool is_female; + bool is_moust_male; - /* Lips or (if it a male face with a moustache) moustache buttons */ - w->SetWidgetsDisabledState(_pf_info[is_moust_male ? PFV_MOUSTACHE : PFV_LIPS].valid_values[ge] < 2, - PFW_WIDGET_LIPS_MOUSTACHE, PFW_WIDGET_LIPS_MOUSTACHE_L, PFW_WIDGET_LIPS_MOUSTACHE_R, WIDGET_LIST_END); - - /* Nose buttons | male faces with moustache haven't any nose options */ - w->SetWidgetsDisabledState(_pf_info[PFV_NOSE].valid_values[ge] < 2 || is_moust_male, - PFW_WIDGET_NOSE, PFW_WIDGET_NOSE_L, PFW_WIDGET_NOSE_R, WIDGET_LIST_END); - - /* Hair buttons */ - w->SetWidgetsDisabledState(_pf_info[PFV_HAIR].valid_values[ge] < 2, - PFW_WIDGET_HAIR, PFW_WIDGET_HAIR_L, PFW_WIDGET_HAIR_R, WIDGET_LIST_END); + /** + * Names of the widgets. Keep them in the same order as in the widget array. + * Do not change the order of the widgets from PFW_WIDGET_HAS_MOUSTACHE_EARRING to PFW_WIDGET_GLASSES_R, + * this order is needed for the WE_CLICK event of DrawFaceStringLabel(). + */ + enum PlayerFaceWindowWidgets { + PFW_WIDGET_CLOSEBOX = 0, + PFW_WIDGET_CAPTION, + PFW_WIDGET_TOGGLE_LARGE_SMALL, + PFW_WIDGET_SELECT_FACE, + PFW_WIDGET_CANCEL, + PFW_WIDGET_ACCEPT, + PFW_WIDGET_MALE, + PFW_WIDGET_FEMALE, + PFW_WIDGET_RANDOM_NEW_FACE, + PFW_WIDGET_TOGGLE_LARGE_SMALL_BUTTON, + /* from here is the advanced player face selection window */ + PFW_WIDGET_LOAD, + PFW_WIDGET_FACECODE, + PFW_WIDGET_SAVE, + PFW_WIDGET_ETHNICITY_EUR, + PFW_WIDGET_ETHNICITY_AFR, + PFW_WIDGET_HAS_MOUSTACHE_EARRING, + PFW_WIDGET_HAS_GLASSES, + PFW_WIDGET_EYECOLOUR_L, + PFW_WIDGET_EYECOLOUR, + PFW_WIDGET_EYECOLOUR_R, + PFW_WIDGET_CHIN_L, + PFW_WIDGET_CHIN, + PFW_WIDGET_CHIN_R, + PFW_WIDGET_EYEBROWS_L, + PFW_WIDGET_EYEBROWS, + PFW_WIDGET_EYEBROWS_R, + PFW_WIDGET_LIPS_MOUSTACHE_L, + PFW_WIDGET_LIPS_MOUSTACHE, + PFW_WIDGET_LIPS_MOUSTACHE_R, + PFW_WIDGET_NOSE_L, + PFW_WIDGET_NOSE, + PFW_WIDGET_NOSE_R, + PFW_WIDGET_HAIR_L, + PFW_WIDGET_HAIR, + PFW_WIDGET_HAIR_R, + PFW_WIDGET_JACKET_L, + PFW_WIDGET_JACKET, + PFW_WIDGET_JACKET_R, + PFW_WIDGET_COLLAR_L, + PFW_WIDGET_COLLAR, + PFW_WIDGET_COLLAR_R, + PFW_WIDGET_TIE_EARRING_L, + PFW_WIDGET_TIE_EARRING, + PFW_WIDGET_TIE_EARRING_R, + PFW_WIDGET_GLASSES_L, + PFW_WIDGET_GLASSES, + PFW_WIDGET_GLASSES_R, + }; + /** + * Draw dynamic a label to the left of the button and a value in the button + * + * @param widget_index index of this widget in the window + * @param str the label which will be draw + * @param val the value which will be draw + * @param is_bool_widget is it a bool button + */ + void DrawFaceStringLabel(byte widget_index, StringID str, uint8 val, bool is_bool_widget) + { + /* Write the label in gold (0x2) to the left of the button. */ + DrawStringRightAligned(this->widget[widget_index].left - (is_bool_widget ? 5 : 14), this->widget[widget_index].top + 1, str, TC_GOLD); - /* Jacket buttons */ - w->SetWidgetsDisabledState(_pf_info[PFV_JACKET].valid_values[ge] < 2, - PFW_WIDGET_JACKET, PFW_WIDGET_JACKET_L, PFW_WIDGET_JACKET_R, WIDGET_LIST_END); - - /* Collar buttons */ - w->SetWidgetsDisabledState(_pf_info[PFV_COLLAR].valid_values[ge] < 2, - PFW_WIDGET_COLLAR, PFW_WIDGET_COLLAR_L, PFW_WIDGET_COLLAR_R, WIDGET_LIST_END); - - /* Tie/earring buttons | female faces without earring haven't any earring options */ - w->SetWidgetsDisabledState(_pf_info[PFV_TIE_EARRING].valid_values[ge] < 2 || - (is_female && GetPlayerFaceBits(*pf, PFV_HAS_TIE_EARRING, ge) == 0), - PFW_WIDGET_TIE_EARRING, PFW_WIDGET_TIE_EARRING_L, PFW_WIDGET_TIE_EARRING_R, WIDGET_LIST_END); - - /* Glasses buttons | faces without glasses haven't any glasses options */ - w->SetWidgetsDisabledState(_pf_info[PFV_GLASSES].valid_values[ge] < 2 || GetPlayerFaceBits(*pf, PFV_HAS_GLASSES, ge) == 0, - PFW_WIDGET_GLASSES, PFW_WIDGET_GLASSES_L, PFW_WIDGET_GLASSES_R, WIDGET_LIST_END); + if (!this->IsWidgetDisabled(widget_index)) { + if (is_bool_widget) { + /* if it a bool button write yes or no */ + str = (val != 0) ? STR_FACE_YES : STR_FACE_NO; + } else { + /* else write the value + 1 */ + SetDParam(0, val + 1); + str = STR_JUST_INT; } - DrawWindowWidgets(w); - - /* Draw dynamic button value and labels for the advanced player face selection window */ - if (WP(w, facesel_d).advanced) { - if (is_female) { - /* Only for female faces */ - DrawFaceStringLabel(w, PFW_WIDGET_HAS_MOUSTACHE_EARRING, STR_FACE_EARRING, GetPlayerFaceBits(*pf, PFV_HAS_TIE_EARRING, ge), true ); - DrawFaceStringLabel(w, PFW_WIDGET_TIE_EARRING, STR_FACE_EARRING, GetPlayerFaceBits(*pf, PFV_TIE_EARRING, ge), false); - } else { - /* Only for male faces */ - DrawFaceStringLabel(w, PFW_WIDGET_HAS_MOUSTACHE_EARRING, STR_FACE_MOUSTACHE, GetPlayerFaceBits(*pf, PFV_HAS_MOUSTACHE, ge), true ); - DrawFaceStringLabel(w, PFW_WIDGET_TIE_EARRING, STR_FACE_TIE, GetPlayerFaceBits(*pf, PFV_TIE_EARRING, ge), false); - } - if (is_moust_male) { - /* Only for male faces with moustache */ - DrawFaceStringLabel(w, PFW_WIDGET_LIPS_MOUSTACHE, STR_FACE_MOUSTACHE, GetPlayerFaceBits(*pf, PFV_MOUSTACHE, ge), false); - } else { - /* Only for female faces or male faces without moustache */ - DrawFaceStringLabel(w, PFW_WIDGET_LIPS_MOUSTACHE, STR_FACE_LIPS, GetPlayerFaceBits(*pf, PFV_LIPS, ge), false); - } - /* For all faces */ - DrawFaceStringLabel(w, PFW_WIDGET_HAS_GLASSES, STR_FACE_GLASSES, GetPlayerFaceBits(*pf, PFV_HAS_GLASSES, ge), true ); - DrawFaceStringLabel(w, PFW_WIDGET_HAIR, STR_FACE_HAIR, GetPlayerFaceBits(*pf, PFV_HAIR, ge), false); - DrawFaceStringLabel(w, PFW_WIDGET_EYEBROWS, STR_FACE_EYEBROWS, GetPlayerFaceBits(*pf, PFV_EYEBROWS, ge), false); - DrawFaceStringLabel(w, PFW_WIDGET_EYECOLOUR, STR_FACE_EYECOLOUR, GetPlayerFaceBits(*pf, PFV_EYE_COLOUR, ge), false); - DrawFaceStringLabel(w, PFW_WIDGET_GLASSES, STR_FACE_GLASSES, GetPlayerFaceBits(*pf, PFV_GLASSES, ge), false); - DrawFaceStringLabel(w, PFW_WIDGET_NOSE, STR_FACE_NOSE, GetPlayerFaceBits(*pf, PFV_NOSE, ge), false); - DrawFaceStringLabel(w, PFW_WIDGET_CHIN, STR_FACE_CHIN, GetPlayerFaceBits(*pf, PFV_CHIN, ge), false); - DrawFaceStringLabel(w, PFW_WIDGET_JACKET, STR_FACE_JACKET, GetPlayerFaceBits(*pf, PFV_JACKET, ge), false); - DrawFaceStringLabel(w, PFW_WIDGET_COLLAR, STR_FACE_COLLAR, GetPlayerFaceBits(*pf, PFV_COLLAR, ge), false); - } - - /* Draw the player face picture */ - DrawPlayerFace(*pf, GetPlayer((PlayerID)w->window_number)->player_color, 2, 16); - break; - - case WE_CLICK: - switch (e->we.click.widget) { - /* Toggle size, advanced/simple face selection */ - case PFW_WIDGET_TOGGLE_LARGE_SMALL: - case PFW_WIDGET_TOGGLE_LARGE_SMALL_BUTTON: { - int oldtop = w->top; ///< current top position of the window before closing it - int oldleft = w->left; ///< current top position of the window before closing it - - DoCommandP(0, 0, *pf, NULL, CMD_SET_PLAYER_FACE); - delete w; - /* Open up the (toggled size) Face selection window at the same position as the previous */ - DoSelectPlayerFace((PlayerID)w->window_number, !WP(w, facesel_d).advanced, oldtop, oldleft); - } break; - - /* Cancel button */ - case PFW_WIDGET_CANCEL: - delete w; - break; - - /* OK button */ - case PFW_WIDGET_ACCEPT: - DoCommandP(0, 0, *pf, NULL, CMD_SET_PLAYER_FACE); - delete w; - break; - - /* Load button */ - case PFW_WIDGET_LOAD: - *pf = _player_face; - ScaleAllPlayerFaceBits(*pf); - ShowErrorMessage(INVALID_STRING_ID, STR_FACE_LOAD_DONE, 0, 0); - w->SetDirty(); - break; - - /* 'Player face number' button, view and/or set player face number */ - case PFW_WIDGET_FACECODE: - SetDParam(0, *pf); - ShowQueryString(STR_JUST_INT, STR_FACE_FACECODE_CAPTION, 10 + 1, 0, w, CS_NUMERAL); - break; + /* Draw the value/bool in white (0xC). If the button clicked adds 1px to x and y text coordinates (IsWindowWidgetLowered()). */ + DrawStringCentered(this->widget[widget_index].left + (this->widget[widget_index].right - this->widget[widget_index].left) / 2 + + this->IsWidgetLowered(widget_index), this->widget[widget_index].top + 1 + this->IsWidgetLowered(widget_index), str, TC_WHITE); + } + } - /* Save button */ - case PFW_WIDGET_SAVE: - _player_face = *pf; - ShowErrorMessage(INVALID_STRING_ID, STR_FACE_SAVE_DONE, 0, 0); - break; - - /* Toggle gender (male/female) button */ - case PFW_WIDGET_MALE: - case PFW_WIDGET_FEMALE: - SetPlayerFaceBits(*pf, PFV_GENDER, ge, e->we.click.widget - PFW_WIDGET_MALE); - ScaleAllPlayerFaceBits(*pf); - w->SetDirty(); - break; - - /* Randomize face button */ - case PFW_WIDGET_RANDOM_NEW_FACE: - RandomPlayerFaceBits(*pf, ge, WP(w, facesel_d).advanced); - w->SetDirty(); - break; - - /* Toggle ethnicity (european/african) button */ - case PFW_WIDGET_ETHNICITY_EUR: - case PFW_WIDGET_ETHNICITY_AFR: - SetPlayerFaceBits(*pf, PFV_ETHNICITY, ge, e->we.click.widget - PFW_WIDGET_ETHNICITY_EUR); - ScaleAllPlayerFaceBits(*pf); - w->SetDirty(); - break; - - default: - /* For all buttons from PFW_WIDGET_HAS_MOUSTACHE_EARRING to PFW_WIDGET_GLASSES_R is the same function. - * Therefor is this combined function. - * First it checks which PlayerFaceVariable will be change and then - * a: invert the value for boolean variables - * or b: it checks inside of IncreasePlayerFaceBits() if a left (_L) butten is pressed and then decrease else increase the variable */ - if (WP(w, facesel_d).advanced && e->we.click.widget >= PFW_WIDGET_HAS_MOUSTACHE_EARRING && e->we.click.widget <= PFW_WIDGET_GLASSES_R) { - PlayerFaceVariable pfv; // which PlayerFaceVariable shall be edited + void UpdateData() + { + this->ge = (GenderEthnicity)GB(this->face, _pf_info[PFV_GEN_ETHN].offset, _pf_info[PFV_GEN_ETHN].length); // get the gender and ethnicity + this->is_female = HasBit(this->ge, GENDER_FEMALE); // get the gender: 0 == male and 1 == female + this->is_moust_male = !is_female && GetPlayerFaceBits(this->face, PFV_HAS_MOUSTACHE, this->ge) != 0; // is a male face with moustache + } - if (e->we.click.widget < PFW_WIDGET_EYECOLOUR_L) { // Bool buttons - switch (e->we.click.widget - PFW_WIDGET_HAS_MOUSTACHE_EARRING) { - default: NOT_REACHED(); - case 0: pfv = is_female ? PFV_HAS_TIE_EARRING : PFV_HAS_MOUSTACHE; break; // Has earring/moustache button - case 1: pfv = PFV_HAS_GLASSES; break; // Has glasses button - } - SetPlayerFaceBits(*pf, pfv, ge, !GetPlayerFaceBits(*pf, pfv, ge)); - ScaleAllPlayerFaceBits(*pf); +public: + SelectPlayerFaceWindow(const WindowDesc *desc, Window *parent, bool advanced) : Window(desc, parent->window_number) + { + this->parent = parent; + this->FindWindowPlacementAndResize(desc); + this->caption_color = this->window_number; + this->face = GetPlayer((PlayerID)this->window_number)->face; + this->advanced = advanced; - } else { // Value buttons - switch ((e->we.click.widget - PFW_WIDGET_EYECOLOUR_L) / 3) { - default: NOT_REACHED(); - case 0: pfv = PFV_EYE_COLOUR; break; // Eye colour buttons - case 1: pfv = PFV_CHIN; break; // Chin buttons - case 2: pfv = PFV_EYEBROWS; break; // Eyebrows buttons - case 3: pfv = is_moust_male ? PFV_MOUSTACHE : PFV_LIPS; break; // Moustache or lips buttons - case 4: pfv = PFV_NOSE; break; // Nose buttons - case 5: pfv = PFV_HAIR; break; // Hair buttons - case 6: pfv = PFV_JACKET; break; // Jacket buttons - case 7: pfv = PFV_COLLAR; break; // Collar buttons - case 8: pfv = PFV_TIE_EARRING; break; // Tie/earring buttons - case 9: pfv = PFV_GLASSES; break; // Glasses buttons - } - /* 0 == left (_L), 1 == middle or 2 == right (_R) - button click */ - IncreasePlayerFaceBits(*pf, pfv, ge, (((e->we.click.widget - PFW_WIDGET_EYECOLOUR_L) % 3) != 0) ? 1 : -1); - } + this->UpdateData(); - w->SetDirty(); - } - break; - } - break; + /* Check if repositioning from default is required */ + if (top != FIRST_GUI_CALL && left != FIRST_GUI_CALL) { + this->top = top; + this->left = left; + } + } - case WE_ON_EDIT_TEXT: - if (e->we.edittext.str == NULL) break; - /* Set a new player face number */ - if (!StrEmpty(e->we.edittext.str)) { - *pf = strtoul(e->we.edittext.str, NULL, 10); - ScaleAllPlayerFaceBits(*pf); - ShowErrorMessage(INVALID_STRING_ID, STR_FACE_FACECODE_SET, 0, 0); - w->SetDirty(); + virtual void OnPaint() + { + /* lower the non-selected gender button */ + this->SetWidgetLoweredState(PFW_WIDGET_MALE, !this->is_female); + this->SetWidgetLoweredState(PFW_WIDGET_FEMALE, this->is_female); + + /* advanced player face selection window */ + if (this->advanced) { + /* lower the non-selected ethnicity button */ + this->SetWidgetLoweredState(PFW_WIDGET_ETHNICITY_EUR, !HasBit(this->ge, ETHNICITY_BLACK)); + this->SetWidgetLoweredState(PFW_WIDGET_ETHNICITY_AFR, HasBit(this->ge, ETHNICITY_BLACK)); + + + /* Disable dynamically the widgets which PlayerFaceVariable has less than 2 options + * (or in other words you haven't any choice). + * If the widgets depend on a HAS-variable and this is false the widgets will be disabled, too. */ + + /* Eye colour buttons */ + this->SetWidgetsDisabledState(_pf_info[PFV_EYE_COLOUR].valid_values[this->ge] < 2, + PFW_WIDGET_EYECOLOUR, PFW_WIDGET_EYECOLOUR_L, PFW_WIDGET_EYECOLOUR_R, WIDGET_LIST_END); + + /* Chin buttons */ + this->SetWidgetsDisabledState(_pf_info[PFV_CHIN].valid_values[this->ge] < 2, + PFW_WIDGET_CHIN, PFW_WIDGET_CHIN_L, PFW_WIDGET_CHIN_R, WIDGET_LIST_END); + + /* Eyebrows buttons */ + this->SetWidgetsDisabledState(_pf_info[PFV_EYEBROWS].valid_values[this->ge] < 2, + PFW_WIDGET_EYEBROWS, PFW_WIDGET_EYEBROWS_L, PFW_WIDGET_EYEBROWS_R, WIDGET_LIST_END); + + /* Lips or (if it a male face with a moustache) moustache buttons */ + this->SetWidgetsDisabledState(_pf_info[this->is_moust_male ? PFV_MOUSTACHE : PFV_LIPS].valid_values[this->ge] < 2, + PFW_WIDGET_LIPS_MOUSTACHE, PFW_WIDGET_LIPS_MOUSTACHE_L, PFW_WIDGET_LIPS_MOUSTACHE_R, WIDGET_LIST_END); + + /* Nose buttons | male faces with moustache haven't any nose options */ + this->SetWidgetsDisabledState(_pf_info[PFV_NOSE].valid_values[this->ge] < 2 || this->is_moust_male, + PFW_WIDGET_NOSE, PFW_WIDGET_NOSE_L, PFW_WIDGET_NOSE_R, WIDGET_LIST_END); + + /* Hair buttons */ + this->SetWidgetsDisabledState(_pf_info[PFV_HAIR].valid_values[this->ge] < 2, + PFW_WIDGET_HAIR, PFW_WIDGET_HAIR_L, PFW_WIDGET_HAIR_R, WIDGET_LIST_END); + + /* Jacket buttons */ + this->SetWidgetsDisabledState(_pf_info[PFV_JACKET].valid_values[this->ge] < 2, + PFW_WIDGET_JACKET, PFW_WIDGET_JACKET_L, PFW_WIDGET_JACKET_R, WIDGET_LIST_END); + + /* Collar buttons */ + this->SetWidgetsDisabledState(_pf_info[PFV_COLLAR].valid_values[this->ge] < 2, + PFW_WIDGET_COLLAR, PFW_WIDGET_COLLAR_L, PFW_WIDGET_COLLAR_R, WIDGET_LIST_END); + + /* Tie/earring buttons | female faces without earring haven't any earring options */ + this->SetWidgetsDisabledState(_pf_info[PFV_TIE_EARRING].valid_values[this->ge] < 2 || + (this->is_female && GetPlayerFaceBits(this->face, PFV_HAS_TIE_EARRING, this->ge) == 0), + PFW_WIDGET_TIE_EARRING, PFW_WIDGET_TIE_EARRING_L, PFW_WIDGET_TIE_EARRING_R, WIDGET_LIST_END); + + /* Glasses buttons | faces without glasses haven't any glasses options */ + this->SetWidgetsDisabledState(_pf_info[PFV_GLASSES].valid_values[this->ge] < 2 || GetPlayerFaceBits(this->face, PFV_HAS_GLASSES, this->ge) == 0, + PFW_WIDGET_GLASSES, PFW_WIDGET_GLASSES_L, PFW_WIDGET_GLASSES_R, WIDGET_LIST_END); + } + + this->DrawWidgets(); + + /* Draw dynamic button value and labels for the advanced player face selection window */ + if (this->advanced) { + if (this->is_female) { + /* Only for female faces */ + this->DrawFaceStringLabel(PFW_WIDGET_HAS_MOUSTACHE_EARRING, STR_FACE_EARRING, GetPlayerFaceBits(this->face, PFV_HAS_TIE_EARRING, this->ge), true ); + this->DrawFaceStringLabel(PFW_WIDGET_TIE_EARRING, STR_FACE_EARRING, GetPlayerFaceBits(this->face, PFV_TIE_EARRING, this->ge), false); } else { - ShowErrorMessage(INVALID_STRING_ID, STR_FACE_FACECODE_ERR, 0, 0); + /* Only for male faces */ + this->DrawFaceStringLabel(PFW_WIDGET_HAS_MOUSTACHE_EARRING, STR_FACE_MOUSTACHE, GetPlayerFaceBits(this->face, PFV_HAS_MOUSTACHE, this->ge), true ); + this->DrawFaceStringLabel(PFW_WIDGET_TIE_EARRING, STR_FACE_TIE, GetPlayerFaceBits(this->face, PFV_TIE_EARRING, this->ge), false); } - break; + if (this->is_moust_male) { + /* Only for male faces with moustache */ + this->DrawFaceStringLabel(PFW_WIDGET_LIPS_MOUSTACHE, STR_FACE_MOUSTACHE, GetPlayerFaceBits(this->face, PFV_MOUSTACHE, this->ge), false); + } else { + /* Only for female faces or male faces without moustache */ + this->DrawFaceStringLabel(PFW_WIDGET_LIPS_MOUSTACHE, STR_FACE_LIPS, GetPlayerFaceBits(this->face, PFV_LIPS, this->ge), false); + } + /* For all faces */ + this->DrawFaceStringLabel(PFW_WIDGET_HAS_GLASSES, STR_FACE_GLASSES, GetPlayerFaceBits(this->face, PFV_HAS_GLASSES, this->ge), true ); + this->DrawFaceStringLabel(PFW_WIDGET_HAIR, STR_FACE_HAIR, GetPlayerFaceBits(this->face, PFV_HAIR, this->ge), false); + this->DrawFaceStringLabel(PFW_WIDGET_EYEBROWS, STR_FACE_EYEBROWS, GetPlayerFaceBits(this->face, PFV_EYEBROWS, this->ge), false); + this->DrawFaceStringLabel(PFW_WIDGET_EYECOLOUR, STR_FACE_EYECOLOUR, GetPlayerFaceBits(this->face, PFV_EYE_COLOUR, this->ge), false); + this->DrawFaceStringLabel(PFW_WIDGET_GLASSES, STR_FACE_GLASSES, GetPlayerFaceBits(this->face, PFV_GLASSES, this->ge), false); + this->DrawFaceStringLabel(PFW_WIDGET_NOSE, STR_FACE_NOSE, GetPlayerFaceBits(this->face, PFV_NOSE, this->ge), false); + this->DrawFaceStringLabel(PFW_WIDGET_CHIN, STR_FACE_CHIN, GetPlayerFaceBits(this->face, PFV_CHIN, this->ge), false); + this->DrawFaceStringLabel(PFW_WIDGET_JACKET, STR_FACE_JACKET, GetPlayerFaceBits(this->face, PFV_JACKET, this->ge), false); + this->DrawFaceStringLabel(PFW_WIDGET_COLLAR, STR_FACE_COLLAR, GetPlayerFaceBits(this->face, PFV_COLLAR, this->ge), false); + } + + /* Draw the player face picture */ + DrawPlayerFace(this->face, GetPlayer((PlayerID)this->window_number)->player_color, 2, 16); } -} + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + /* Toggle size, advanced/simple face selection */ + case PFW_WIDGET_TOGGLE_LARGE_SMALL: + case PFW_WIDGET_TOGGLE_LARGE_SMALL_BUTTON: { + DoCommandP(0, 0, this->face, NULL, CMD_SET_PLAYER_FACE); + + /* Backup some data before deletion */ + int oldtop = this->top; ///< current top position of the window before closing it + int oldleft = this->left; ///< current top position of the window before closing it + bool adv = !this->advanced; + Window *parent = this->parent; + + delete this; + + /* Open up the (toggled size) Face selection window at the same position as the previous */ + DoSelectPlayerFace(parent, adv, oldtop, oldleft); + } break; + + + /* OK button */ + case PFW_WIDGET_ACCEPT: + DoCommandP(0, 0, this->face, NULL, CMD_SET_PLAYER_FACE); + /* Fall-Through */ + + /* Cancel button */ + case PFW_WIDGET_CANCEL: + delete this; + break; + + /* Load button */ + case PFW_WIDGET_LOAD: + this->face = _player_face; + ScaleAllPlayerFaceBits(this->face); + ShowErrorMessage(INVALID_STRING_ID, STR_FACE_LOAD_DONE, 0, 0); + this->UpdateData(); + this->SetDirty(); + break; + + /* 'Player face number' button, view and/or set player face number */ + case PFW_WIDGET_FACECODE: + SetDParam(0, this->face); + ShowQueryString(STR_JUST_INT, STR_FACE_FACECODE_CAPTION, 10 + 1, 0, this, CS_NUMERAL); + break; + + /* Save button */ + case PFW_WIDGET_SAVE: + _player_face = this->face; + ShowErrorMessage(INVALID_STRING_ID, STR_FACE_SAVE_DONE, 0, 0); + break; + + /* Toggle gender (male/female) button */ + case PFW_WIDGET_MALE: + case PFW_WIDGET_FEMALE: + SetPlayerFaceBits(this->face, PFV_GENDER, this->ge, widget - PFW_WIDGET_MALE); + ScaleAllPlayerFaceBits(this->face); + this->UpdateData(); + this->SetDirty(); + break; + + /* Randomize face button */ + case PFW_WIDGET_RANDOM_NEW_FACE: + RandomPlayerFaceBits(this->face, this->ge, this->advanced); + this->UpdateData(); + this->SetDirty(); + break; + + /* Toggle ethnicity (european/african) button */ + case PFW_WIDGET_ETHNICITY_EUR: + case PFW_WIDGET_ETHNICITY_AFR: + SetPlayerFaceBits(this->face, PFV_ETHNICITY, this->ge, widget - PFW_WIDGET_ETHNICITY_EUR); + ScaleAllPlayerFaceBits(this->face); + this->UpdateData(); + this->SetDirty(); + break; + + default: + /* For all buttons from PFW_WIDGET_HAS_MOUSTACHE_EARRING to PFW_WIDGET_GLASSES_R is the same function. + * Therefor is this combined function. + * First it checks which PlayerFaceVariable will be change and then + * a: invert the value for boolean variables + * or b: it checks inside of IncreasePlayerFaceBits() if a left (_L) butten is pressed and then decrease else increase the variable */ + if (this->advanced && widget >= PFW_WIDGET_HAS_MOUSTACHE_EARRING && widget <= PFW_WIDGET_GLASSES_R) { + PlayerFaceVariable pfv; // which PlayerFaceVariable shall be edited + + if (widget < PFW_WIDGET_EYECOLOUR_L) { // Bool buttons + switch (widget - PFW_WIDGET_HAS_MOUSTACHE_EARRING) { + default: NOT_REACHED(); + case 0: pfv = this->is_female ? PFV_HAS_TIE_EARRING : PFV_HAS_MOUSTACHE; break; // Has earring/moustache button + case 1: pfv = PFV_HAS_GLASSES; break; // Has glasses button + } + SetPlayerFaceBits(this->face, pfv, this->ge, !GetPlayerFaceBits(this->face, pfv, this->ge)); + ScaleAllPlayerFaceBits(this->face); + } else { // Value buttons + switch ((widget - PFW_WIDGET_EYECOLOUR_L) / 3) { + default: NOT_REACHED(); + case 0: pfv = PFV_EYE_COLOUR; break; // Eye colour buttons + case 1: pfv = PFV_CHIN; break; // Chin buttons + case 2: pfv = PFV_EYEBROWS; break; // Eyebrows buttons + case 3: pfv = this->is_moust_male ? PFV_MOUSTACHE : PFV_LIPS; break; // Moustache or lips buttons + case 4: pfv = PFV_NOSE; break; // Nose buttons + case 5: pfv = PFV_HAIR; break; // Hair buttons + case 6: pfv = PFV_JACKET; break; // Jacket buttons + case 7: pfv = PFV_COLLAR; break; // Collar buttons + case 8: pfv = PFV_TIE_EARRING; break; // Tie/earring buttons + case 9: pfv = PFV_GLASSES; break; // Glasses buttons + } + /* 0 == left (_L), 1 == middle or 2 == right (_R) - button click */ + IncreasePlayerFaceBits(this->face, pfv, this->ge, (((widget - PFW_WIDGET_EYECOLOUR_L) % 3) != 0) ? 1 : -1); + } + this->UpdateData(); + this->SetDirty(); + } + break; + } + } + + virtual void OnQueryTextFinished(char *str) + { + if (str == NULL) return; + /* Set a new player face number */ + if (!StrEmpty(str)) { + this->face = strtoul(str, NULL, 10); + ScaleAllPlayerFaceBits(this->face); + ShowErrorMessage(INVALID_STRING_ID, STR_FACE_FACECODE_SET, 0, 0); + this->UpdateData(); + this->SetDirty(); + } else { + ShowErrorMessage(INVALID_STRING_ID, STR_FACE_FACECODE_ERR, 0, 0); + } + } +}; /** normal/simple player face selection window description */ static const WindowDesc _select_player_face_desc = { @@ -999,7 +1007,6 @@ WC_PLAYER_FACE, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _select_player_face_widgets, - SelectPlayerFaceWndProc }; /** advanced player face selection window description */ @@ -1008,7 +1015,6 @@ WC_PLAYER_FACE, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _select_player_face_adv_widgets, - SelectPlayerFaceWndProc }; /** @@ -1021,23 +1027,12 @@ * * @pre is player a valid player */ -static void DoSelectPlayerFace(PlayerID player, bool adv, int top, int left) +static void DoSelectPlayerFace(Window *parent, bool adv, int top, int left) { - if (!IsValidPlayer(player)) return; - - Window *w = AllocateWindowDescFront(adv ? &_select_player_face_adv_desc : &_select_player_face_desc, player); // simple or advanced window + if (!IsValidPlayer((PlayerID)parent->window_number)) return; - if (w != NULL) { - w->caption_color = w->window_number; - WP(w, facesel_d).face = GetPlayer((PlayerID)w->window_number)->face; - WP(w, facesel_d).advanced = adv; - - /* Check if repositioning from default is required */ - if (top != FIRST_GUI_CALL && left != FIRST_GUI_CALL) { - w->top = top; - w->left = left; - } - } + if (BringWindowToFrontById(WC_PLAYER_FACE, parent->window_number)) return; + new SelectPlayerFaceWindow(adv ? &_select_player_face_adv_desc : &_select_player_face_desc, parent, adv); // simple or advanced window } @@ -1165,238 +1160,232 @@ * @param w window pointer * @param e event been triggered */ -static void PlayerCompanyWndProc(Window *w, WindowEvent *e) +struct PlayerCompanyWindow : Window { - switch (e->event) { - case WE_PAINT: { - const Player *p = GetPlayer((PlayerID)w->window_number); - bool local = w->window_number == _local_player; - - w->SetWidgetHiddenState(PCW_WIDGET_NEW_FACE, !local); - w->SetWidgetHiddenState(PCW_WIDGET_COLOR_SCHEME, !local); - w->SetWidgetHiddenState(PCW_WIDGET_PRESIDENT_NAME, !local); - w->SetWidgetHiddenState(PCW_WIDGET_COMPANY_NAME, !local); - w->widget[PCW_WIDGET_BUILD_VIEW_HQ].data = (local && p->location_of_house == 0) ? STR_706F_BUILD_HQ : STR_7072_VIEW_HQ; - if (local && p->location_of_house != 0) w->widget[PCW_WIDGET_BUILD_VIEW_HQ].type = WWT_PUSHTXTBTN; //HQ is already built. - w->SetWidgetDisabledState(PCW_WIDGET_BUILD_VIEW_HQ, !local && p->location_of_house == 0); - w->SetWidgetHiddenState(PCW_WIDGET_RELOCATE_HQ, !local || p->location_of_house == 0); - w->SetWidgetHiddenState(PCW_WIDGET_BUY_SHARE, local); - w->SetWidgetHiddenState(PCW_WIDGET_SELL_SHARE, local); - w->SetWidgetHiddenState(PCW_WIDGET_COMPANY_PASSWORD, !local || !_networking); - - if (!local) { - if (_patches.allow_shares) { // Shares are allowed - /* If all shares are owned by someone (none by nobody), disable buy button */ - w->SetWidgetDisabledState(PCW_WIDGET_BUY_SHARE, GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 0 || - /* Only 25% left to buy. If the player is human, disable buying it up.. TODO issues! */ - (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 1 && !p->is_ai) || - /* Spectators cannot do anything of course */ - _local_player == PLAYER_SPECTATOR); + PlayerCompanyWindowWidgets query_widget; - /* If the player doesn't own any shares, disable sell button */ - w->SetWidgetDisabledState(PCW_WIDGET_SELL_SHARE, (GetAmountOwnedBy(p, _local_player) == 0) || - /* Spectators cannot do anything of course */ - _local_player == PLAYER_SPECTATOR); - } else { // Shares are not allowed, disable buy/sell buttons - w->DisableWidget(PCW_WIDGET_BUY_SHARE); - w->DisableWidget(PCW_WIDGET_SELL_SHARE); - } - } - - SetDParam(0, p->index); - SetDParam(1, p->index); - - DrawWindowWidgets(w); - - /* Player face */ - DrawPlayerFace(p->face, p->player_color, 2, 16); + PlayerCompanyWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->caption_color = this->window_number; + } - /* "xxx (Manager)" */ - SetDParam(0, p->index); - DrawStringMultiCenter(48, 141, STR_7037_PRESIDENT, 94); - - /* "Inaugurated:" */ - SetDParam(0, p->inaugurated_year); - DrawString(110, 23, STR_7038_INAUGURATED, TC_FROMSTRING); + virtual void OnPaint() + { + const Player *p = GetPlayer((PlayerID)this->window_number); + bool local = this->window_number == _local_player; - /* "Colour scheme:" */ - DrawString(110, 43, STR_7006_COLOR_SCHEME, TC_FROMSTRING); - /* Draw company-colour bus */ - DrawSprite(SPR_VEH_BUS_SW_VIEW, PLAYER_SPRITE_COLOR(p->index), 215, 44); - - /* "Vehicles:" */ - DrawPlayerVehiclesAmount((PlayerID)w->window_number); + this->SetWidgetHiddenState(PCW_WIDGET_NEW_FACE, !local); + this->SetWidgetHiddenState(PCW_WIDGET_COLOR_SCHEME, !local); + this->SetWidgetHiddenState(PCW_WIDGET_PRESIDENT_NAME, !local); + this->SetWidgetHiddenState(PCW_WIDGET_COMPANY_NAME, !local); + this->widget[PCW_WIDGET_BUILD_VIEW_HQ].data = (local && p->location_of_house == 0) ? STR_706F_BUILD_HQ : STR_7072_VIEW_HQ; + if (local && p->location_of_house != 0) this->widget[PCW_WIDGET_BUILD_VIEW_HQ].type = WWT_PUSHTXTBTN; //HQ is already built. + this->SetWidgetDisabledState(PCW_WIDGET_BUILD_VIEW_HQ, !local && p->location_of_house == 0); + this->SetWidgetHiddenState(PCW_WIDGET_RELOCATE_HQ, !local || p->location_of_house == 0); + this->SetWidgetHiddenState(PCW_WIDGET_BUY_SHARE, local); + this->SetWidgetHiddenState(PCW_WIDGET_SELL_SHARE, local); + this->SetWidgetHiddenState(PCW_WIDGET_COMPANY_PASSWORD, !local || !_networking); - /* "Company value:" */ - SetDParam(0, CalculateCompanyValue(p)); - DrawString(110, 106, STR_7076_COMPANY_VALUE, TC_FROMSTRING); + if (!local) { + if (_patches.allow_shares) { // Shares are allowed + /* If all shares are owned by someone (none by nobody), disable buy button */ + this->SetWidgetDisabledState(PCW_WIDGET_BUY_SHARE, GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 0 || + /* Only 25% left to buy. If the player is human, disable buying it up.. TODO issues! */ + (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 1 && !p->is_ai) || + /* Spectators cannot do anything of course */ + _local_player == PLAYER_SPECTATOR); - /* Shares list */ - DrawCompanyOwnerText(p); - - break; + /* If the player doesn't own any shares, disable sell button */ + this->SetWidgetDisabledState(PCW_WIDGET_SELL_SHARE, (GetAmountOwnedBy(p, _local_player) == 0) || + /* Spectators cannot do anything of course */ + _local_player == PLAYER_SPECTATOR); + } else { // Shares are not allowed, disable buy/sell buttons + this->DisableWidget(PCW_WIDGET_BUY_SHARE); + this->DisableWidget(PCW_WIDGET_SELL_SHARE); + } } - case WE_CLICK: - switch (e->we.click.widget) { - case PCW_WIDGET_NEW_FACE: DoSelectPlayerFace((PlayerID)w->window_number, false); break; - - case PCW_WIDGET_COLOR_SCHEME: { - Window *wf = AllocateWindowDescFront(_loaded_newgrf_features.has_2CC ? &_select_player_livery_2cc_desc : &_select_player_livery_desc, w->window_number); - if (wf != NULL) { - wf->caption_color = wf->window_number; - WP(wf, livery_d).livery_class = LC_OTHER; - WP(wf, livery_d).sel = 1; - wf->LowerWidget(2); - } - break; - } - - case PCW_WIDGET_PRESIDENT_NAME: { - const Player *p = GetPlayer((PlayerID)w->window_number); - WP(w, def_d).byte_1 = 0; - SetDParam(0, p->index); - ShowQueryString(STR_PLAYER_NAME, STR_700B_PRESIDENT_S_NAME, 31, 94, w, CS_ALPHANUMERAL); - break; - } - - case PCW_WIDGET_COMPANY_NAME: { - Player *p = GetPlayer((PlayerID)w->window_number); - WP(w, def_d).byte_1 = 1; - SetDParam(0, p->index); - ShowQueryString(STR_COMPANY_NAME, STR_700A_COMPANY_NAME, 31, 150, w, CS_ALPHANUMERAL); - break; - } + SetDParam(0, p->index); + SetDParam(1, p->index); - case PCW_WIDGET_BUILD_VIEW_HQ: { - TileIndex tile = GetPlayer((PlayerID)w->window_number)->location_of_house; - if (tile == 0) { - if ((byte)w->window_number != _local_player) - return; - SetObjectToPlaceWnd(SPR_CURSOR_HQ, PAL_NONE, VHM_RECT, w); - SetTileSelectSize(2, 2); - w->LowerWidget(PCW_WIDGET_BUILD_VIEW_HQ); - w->InvalidateWidget(PCW_WIDGET_BUILD_VIEW_HQ); + this->DrawWidgets(); + + /* Player face */ + DrawPlayerFace(p->face, p->player_color, 2, 16); + + /* "xxx (Manager)" */ + SetDParam(0, p->index); + DrawStringMultiCenter(48, 141, STR_7037_PRESIDENT, 94); + + /* "Inaugurated:" */ + SetDParam(0, p->inaugurated_year); + DrawString(110, 23, STR_7038_INAUGURATED, TC_FROMSTRING); + + /* "Colour scheme:" */ + DrawString(110, 43, STR_7006_COLOR_SCHEME, TC_FROMSTRING); + /* Draw company-colour bus */ + DrawSprite(SPR_VEH_BUS_SW_VIEW, PLAYER_SPRITE_COLOR(p->index), 215, 44); + + /* "Vehicles:" */ + DrawPlayerVehiclesAmount((PlayerID)this->window_number); + + /* "Company value:" */ + SetDParam(0, CalculateCompanyValue(p)); + DrawString(110, 106, STR_7076_COMPANY_VALUE, TC_FROMSTRING); + + /* Shares list */ + DrawCompanyOwnerText(p); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case PCW_WIDGET_NEW_FACE: DoSelectPlayerFace(this, false); break; + + case PCW_WIDGET_COLOR_SCHEME: + if (BringWindowToFrontById(WC_PLAYER_COLOR, this->window_number)) break; + new SelectPlayerLiveryWindow(&_select_player_livery_desc, (PlayerID)this->window_number); + break; + + case PCW_WIDGET_PRESIDENT_NAME: + this->query_widget = PCW_WIDGET_PRESIDENT_NAME; + SetDParam(0, this->window_number); + ShowQueryString(STR_PLAYER_NAME, STR_700B_PRESIDENT_S_NAME, 31, 94, this, CS_ALPHANUMERAL); + break; + + case PCW_WIDGET_COMPANY_NAME: + this->query_widget = PCW_WIDGET_COMPANY_NAME; + SetDParam(0, this->window_number); + ShowQueryString(STR_COMPANY_NAME, STR_700A_COMPANY_NAME, 31, 150, this, CS_ALPHANUMERAL); + break; + + case PCW_WIDGET_BUILD_VIEW_HQ: { + TileIndex tile = GetPlayer((PlayerID)this->window_number)->location_of_house; + if (tile == 0) { + if ((byte)this->window_number != _local_player) return; + SetObjectToPlaceWnd(SPR_CURSOR_HQ, PAL_NONE, VHM_RECT, this); + SetTileSelectSize(2, 2); + this->LowerWidget(PCW_WIDGET_BUILD_VIEW_HQ); + this->InvalidateWidget(PCW_WIDGET_BUILD_VIEW_HQ); + } else { + if (_ctrl_pressed) { + ShowExtraViewPortWindow(tile); } else { - if (_ctrl_pressed) { - ShowExtraViewPortWindow(tile); - } else { - ScrollMainWindowToTile(tile); - } + ScrollMainWindowToTile(tile); } - break; } + break; + } - case PCW_WIDGET_RELOCATE_HQ: - SetObjectToPlaceWnd(SPR_CURSOR_HQ, PAL_NONE, VHM_RECT, w); - SetTileSelectSize(2, 2); - w->LowerWidget(PCW_WIDGET_RELOCATE_HQ); - w->InvalidateWidget(PCW_WIDGET_RELOCATE_HQ); - break; + case PCW_WIDGET_RELOCATE_HQ: + SetObjectToPlaceWnd(SPR_CURSOR_HQ, PAL_NONE, VHM_RECT, this); + SetTileSelectSize(2, 2); + this->LowerWidget(PCW_WIDGET_RELOCATE_HQ); + this->InvalidateWidget(PCW_WIDGET_RELOCATE_HQ); + break; - case PCW_WIDGET_BUY_SHARE: - DoCommandP(0, w->window_number, 0, NULL, CMD_BUY_SHARE_IN_COMPANY | CMD_MSG(STR_707B_CAN_T_BUY_25_SHARE_IN_THIS)); - break; + case PCW_WIDGET_BUY_SHARE: + DoCommandP(0, this->window_number, 0, NULL, CMD_BUY_SHARE_IN_COMPANY | CMD_MSG(STR_707B_CAN_T_BUY_25_SHARE_IN_THIS)); + break; - case PCW_WIDGET_SELL_SHARE: - DoCommandP(0, w->window_number, 0, NULL, CMD_SELL_SHARE_IN_COMPANY | CMD_MSG(STR_707C_CAN_T_SELL_25_SHARE_IN)); - break; + case PCW_WIDGET_SELL_SHARE: + DoCommandP(0, this->window_number, 0, NULL, CMD_SELL_SHARE_IN_COMPANY | CMD_MSG(STR_707C_CAN_T_SELL_25_SHARE_IN)); + break; #ifdef ENABLE_NETWORK - case PCW_WIDGET_COMPANY_PASSWORD: - if (w->window_number == _local_player) ShowNetworkCompanyPasswordWindow(); - break; + case PCW_WIDGET_COMPANY_PASSWORD: + if (this->window_number == _local_player) ShowNetworkCompanyPasswordWindow(this); + break; #endif /* ENABLE_NETWORK */ - } - break; - - case WE_TICK: - /* redraw the window every now and then */ - if ((++w->vscroll.pos & 0x1F) == 0) w->SetDirty(); - break; - - case WE_PLACE_OBJ: - if (DoCommandP(e->we.place.tile, 0, 0, NULL, CMD_BUILD_COMPANY_HQ | CMD_NO_WATER | CMD_MSG(STR_7071_CAN_T_BUILD_COMPANY_HEADQUARTERS))) - ResetObjectToPlace(); - w->widget[PCW_WIDGET_BUILD_VIEW_HQ].type = WWT_PUSHTXTBTN; // this button can now behave as a normal push button - w->RaiseButtons(); - break; + } + } - case WE_ABORT_PLACE_OBJ: - w->RaiseButtons(); - break; - - case WE_DESTROY: - DeleteWindowById(WC_PLAYER_FACE, w->window_number); - if (w->window_number == _local_player) DeleteWindowById(WC_COMPANY_PASSWORD_WINDOW, 0); - break; - - case WE_ON_EDIT_TEXT: - if (StrEmpty(e->we.edittext.str)) break; + virtual void OnTick() + { + /* redraw the window every now and then */ + if ((++this->vscroll.pos & 0x1F) == 0) this->SetDirty(); + } - _cmd_text = e->we.edittext.str; - switch (WP(w, def_d).byte_1) { - case 0: /* Change president name */ - DoCommandP(0, 0, 0, NULL, CMD_CHANGE_PRESIDENT_NAME | CMD_MSG(STR_700D_CAN_T_CHANGE_PRESIDENT)); - break; - case 1: /* Change company name */ - DoCommandP(0, 0, 0, NULL, CMD_CHANGE_COMPANY_NAME | CMD_MSG(STR_700C_CAN_T_CHANGE_COMPANY_NAME)); - break; - } - break; + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + if (DoCommandP(tile, 0, 0, NULL, CMD_BUILD_COMPANY_HQ | CMD_NO_WATER | CMD_MSG(STR_7071_CAN_T_BUILD_COMPANY_HEADQUARTERS))) + ResetObjectToPlace(); + this->widget[PCW_WIDGET_BUILD_VIEW_HQ].type = WWT_PUSHTXTBTN; // this button can now behave as a normal push button + this->RaiseButtons(); } -} + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); + } + + virtual void OnQueryTextFinished(char *str) + { + if (StrEmpty(str)) return; + + _cmd_text = str; + switch (this->query_widget) { + default: NOT_REACHED(); + + case PCW_WIDGET_PRESIDENT_NAME: + DoCommandP(0, 0, 0, NULL, CMD_CHANGE_PRESIDENT_NAME | CMD_MSG(STR_700D_CAN_T_CHANGE_PRESIDENT)); + break; + + case PCW_WIDGET_COMPANY_NAME: + DoCommandP(0, 0, 0, NULL, CMD_CHANGE_COMPANY_NAME | CMD_MSG(STR_700C_CAN_T_CHANGE_COMPANY_NAME)); + break; + } + } +}; static const WindowDesc _player_company_desc = { WDP_AUTO, WDP_AUTO, 360, 170, 360, 170, WC_COMPANY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _player_company_widgets, - PlayerCompanyWndProc }; void ShowPlayerCompany(PlayerID player) { - Window *w; - if (!IsValidPlayer(player)) return; - w = AllocateWindowDescFront(&_player_company_desc, player); - if (w != NULL) w->caption_color = w->window_number; + AllocateWindowDescFront(&_player_company_desc, player); } -static void BuyCompanyWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - Player *p = GetPlayer((PlayerID)w->window_number); - SetDParam(0, STR_COMPANY_NAME); - SetDParam(1, p->index); - DrawWindowWidgets(w); - - DrawPlayerFace(p->face, p->player_color, 2, 16); +struct BuyCompanyWindow : Window { + BuyCompanyWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + } - SetDParam(0, p->index); - SetDParam(1, p->bankrupt_value); - DrawStringMultiCenter(214, 65, STR_705B_WE_ARE_LOOKING_FOR_A_TRANSPORT, 238); - } break; + virtual void OnPaint() + { + Player *p = GetPlayer((PlayerID)this->window_number); + SetDParam(0, STR_COMPANY_NAME); + SetDParam(1, p->index); + this->DrawWidgets(); - case WE_CLICK: - switch (e->we.click.widget) { - case 3: - delete w; - break; - case 4: { - DoCommandP(0, w->window_number, 0, NULL, CMD_BUY_COMPANY | CMD_MSG(STR_7060_CAN_T_BUY_COMPANY)); - break; - } - } - break; + DrawPlayerFace(p->face, p->player_color, 2, 16); + + SetDParam(0, p->index); + SetDParam(1, p->bankrupt_value); + DrawStringMultiCenter(214, 65, STR_705B_WE_ARE_LOOKING_FOR_A_TRANSPORT, 238); } -} + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 3: + delete this; + break; + + case 4: + DoCommandP(0, this->window_number, 0, NULL, CMD_BUY_COMPANY | CMD_MSG(STR_7060_CAN_T_BUY_COMPANY)); + break; + } + } +}; static const Widget _buy_company_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 5, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -1412,117 +1401,165 @@ WC_BUY_COMPANY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _buy_company_widgets, - BuyCompanyWndProc }; void ShowBuyCompanyDialog(uint player) { - AllocateWindowDescFront(&_buy_company_desc, player); + AllocateWindowDescFront(&_buy_company_desc, player); } /********** HIGHSCORE and ENDGAME windows */ -/* Always draw a maximized window and within there the centered background */ -static void SetupHighScoreEndWindow(Window *w, uint *x, uint *y) -{ - uint i; - /* resize window to "full-screen" */ - w->width = _screen.width; - w->height = _screen.height; - w->widget[0].right = w->width - 1; - w->widget[0].bottom = w->height - 1; +extern StringID EndGameGetPerformanceTitleFromValue(uint value); - DrawWindowWidgets(w); - /* Center Highscore/Endscreen background */ - *x = max(0, (_screen.width / 2) - (640 / 2)); - *y = max(0, (_screen.height / 2) - (480 / 2)); - for (i = 0; i < 10; i++) // the image is split into 10 50px high parts - DrawSprite(WP(w, highscore_d).background_img + i, PAL_NONE, *x, *y + (i * 50)); -} +struct EndGameHighScoreBaseWindow : Window +{ + uint32 background_img; + int8 rank; -extern StringID EndGameGetPerformanceTitleFromValue(uint value); + EndGameHighScoreBaseWindow(const WindowDesc *desc) : Window(desc) + { + } + + /* Always draw a maximized window and within there the centered background */ + void SetupHighScoreEndWindow(uint *x, uint *y) + { + /* resize window to "full-screen" */ + this->width = _screen.width; + this->height = _screen.height; + this->widget[0].right = this->width - 1; + this->widget[0].bottom = this->height - 1; + + this->DrawWidgets(); + + /* Center Highscore/Endscreen background */ + *x = max(0, (_screen.width / 2) - (640 / 2)); + *y = max(0, (_screen.height / 2) - (480 / 2)); + for (uint i = 0; i < 10; i++) { // the image is split into 10 50px high parts + DrawSprite(this->background_img + i, PAL_NONE, *x, *y + (i * 50)); + } + } + + virtual void OnClick(Point pt, int widget) + { + delete this; + } +}; /** End game window shown at the end of the game */ -static void EndGameWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - const Player *p; - uint x, y; - - SetupHighScoreEndWindow(w, &x, &y); - - if (!IsValidPlayer(_local_player)) break; +struct EndGameWindow : EndGameHighScoreBaseWindow { + EndGameWindow(const WindowDesc *desc) : EndGameHighScoreBaseWindow(desc) + { + /* Pause in single-player to have a look at the highscore at your own leisure */ + if (!_networking) DoCommandP(0, 1, 0, NULL, CMD_PAUSE); - p = GetPlayer(_local_player); - /* We need to get performance from last year because the image is shown - * at the start of the new year when these things have already been copied */ - if (WP(w, highscore_d).background_img == SPR_TYCOON_IMG2_BEGIN) { // Tycoon of the century \o/ - SetDParam(0, p->index); - SetDParam(1, p->index); - SetDParam(2, EndGameGetPerformanceTitleFromValue(p->old_economy[0].performance_history)); - DrawStringMultiCenter(x + (640 / 2), y + 107, STR_021C_OF_ACHIEVES_STATUS, 640); - } else { - SetDParam(0, p->index); - SetDParam(1, EndGameGetPerformanceTitleFromValue(p->old_economy[0].performance_history)); - DrawStringMultiCenter(x + (640 / 2), y + 157, STR_021B_ACHIEVES_STATUS, 640); + this->background_img = SPR_TYCOON_IMG1_BEGIN; + + if (_local_player != PLAYER_SPECTATOR) { + const Player *p = GetPlayer(_local_player); + if (p->old_economy[0].performance_history == SCORE_MAX) { + this->background_img = SPR_TYCOON_IMG2_BEGIN; } - } break; - - case WE_CLICK: /* Close the window (and show the highscore window) */ - delete w; - break; + } - case WE_DESTROY: /* Show the highscore window when this one is closed */ - if (!_networking) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // unpause - ShowHighscoreTable(w->window_number, WP(w, highscore_d).rank); - break; + /* In a network game show the endscores of the custom difficulty 'network' which is the last one + * as well as generate a TOP5 of that game, and not an all-time top5. */ + if (_networking) { + this->window_number = lengthof(_highscore_table) - 1; + this->rank = SaveHighScoreValueNetwork(); + } else { + /* in single player _local player is always valid */ + const Player *p = GetPlayer(_local_player); + this->window_number = _opt.diff_level; + this->rank = SaveHighScoreValue(p); + } + + MarkWholeScreenDirty(); } -} -static void HighScoreWndProc(Window *w, WindowEvent *e) + ~EndGameWindow() + { + if (!_networking) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // unpause + ShowHighscoreTable(this->window_number, this->rank); + } + + virtual void OnPaint() + { + const Player *p; + uint x, y; + + this->SetupHighScoreEndWindow(&x, &y); + + if (!IsValidPlayer(_local_player)) return; + + p = GetPlayer(_local_player); + /* We need to get performance from last year because the image is shown + * at the start of the new year when these things have already been copied */ + if (this->background_img == SPR_TYCOON_IMG2_BEGIN) { // Tycoon of the century \o/ + SetDParam(0, p->index); + SetDParam(1, p->index); + SetDParam(2, EndGameGetPerformanceTitleFromValue(p->old_economy[0].performance_history)); + DrawStringMultiCenter(x + (640 / 2), y + 107, STR_021C_OF_ACHIEVES_STATUS, 640); + } else { + SetDParam(0, p->index); + SetDParam(1, EndGameGetPerformanceTitleFromValue(p->old_economy[0].performance_history)); + DrawStringMultiCenter(x + (640 / 2), y + 157, STR_021B_ACHIEVES_STATUS, 640); + } + } +}; + +struct HighScoreWindow : EndGameHighScoreBaseWindow { - switch (e->event) { - case WE_PAINT: { - const HighScore *hs = _highscore_table[w->window_number]; - uint x, y; - uint8 i; - - SetupHighScoreEndWindow(w, &x, &y); - - SetDParam(0, _patches.ending_year); - SetDParam(1, w->window_number + STR_6801_EASY); - DrawStringMultiCenter(x + (640 / 2), y + 62, !_networking ? STR_0211_TOP_COMPANIES_WHO_REACHED : STR_TOP_COMPANIES_NETWORK_GAME, 500); - - /* Draw Highscore peepz */ - for (i = 0; i < lengthof(_highscore_table[0]); i++) { - SetDParam(0, i + 1); - DrawString(x + 40, y + 140 + (i * 55), STR_0212, TC_BLACK); + HighScoreWindow(const WindowDesc *desc, int difficulty, int8 ranking) : EndGameHighScoreBaseWindow(desc) + { + /* pause game to show the chart */ + if (!_networking) DoCommandP(0, 1, 0, NULL, CMD_PAUSE); - if (hs[i].company[0] != '\0') { - TextColour colour = (WP(w, highscore_d).rank == (int8)i) ? TC_RED : TC_BLACK; // draw new highscore in red - - DoDrawString(hs[i].company, x + 71, y + 140 + (i * 55), colour); - SetDParam(0, hs[i].title); - SetDParam(1, hs[i].score); - DrawString(x + 71, y + 160 + (i * 55), STR_HIGHSCORE_STATS, colour); - } - } - } break; + /* Close all always on-top windows to get a clean screen */ + if (_game_mode != GM_MENU) HideVitalWindows(); - case WE_CLICK: /* Onclick to close window, and in destroy event handle the rest */ - delete w; - break; + MarkWholeScreenDirty(); + this->window_number = difficulty; // show highscore chart for difficulty... + this->background_img = SPR_HIGHSCORE_CHART_BEGIN; // which background to show + this->rank = ranking; + } - case WE_DESTROY: /* Get back all the hidden windows */ - if (_game_mode != GM_MENU) ShowVitalWindows(); + ~HighScoreWindow() + { + if (_game_mode != GM_MENU) ShowVitalWindows(); - if (!_networking) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // unpause - break; + if (!_networking) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // unpause + } + + virtual void OnPaint() + { + const HighScore *hs = _highscore_table[this->window_number]; + uint x, y; + + this->SetupHighScoreEndWindow(&x, &y); + + SetDParam(0, _patches.ending_year); + SetDParam(1, this->window_number + STR_6801_EASY); + DrawStringMultiCenter(x + (640 / 2), y + 62, !_networking ? STR_0211_TOP_COMPANIES_WHO_REACHED : STR_TOP_COMPANIES_NETWORK_GAME, 500); + + /* Draw Highscore peepz */ + for (uint8 i = 0; i < lengthof(_highscore_table[0]); i++) { + SetDParam(0, i + 1); + DrawString(x + 40, y + 140 + (i * 55), STR_0212, TC_BLACK); + + if (hs[i].company[0] != '\0') { + TextColour colour = (this->rank == i) ? TC_RED : TC_BLACK; // draw new highscore in red + + DoDrawString(hs[i].company, x + 71, y + 140 + (i * 55), colour); + SetDParam(0, hs[i].title); + SetDParam(1, hs[i].score); + DrawString(x + 71, y + 160 + (i * 55), STR_HIGHSCORE_STATS, colour); + } } -} + } +}; static const Widget _highscore_widgets[] = { { WWT_PANEL, RESIZE_NONE, 16, 0, 640, 0, 480, 0x0, STR_NULL}, @@ -1534,7 +1571,6 @@ WC_HIGHSCORE, WC_NONE, 0, _highscore_widgets, - HighScoreWndProc }; static const WindowDesc _endgame_desc = { @@ -1542,7 +1578,6 @@ WC_ENDSCREEN, WC_NONE, 0, _highscore_widgets, - EndGameWndProc }; /** Show the highscore table for a given difficulty. When called from @@ -1550,61 +1585,18 @@ * and is thus highlighted */ void ShowHighscoreTable(int difficulty, int8 ranking) { - Window *w; - - /* pause game to show the chart */ - if (!_networking) DoCommandP(0, 1, 0, NULL, CMD_PAUSE); - - /* Close all always on-top windows to get a clean screen */ - if (_game_mode != GM_MENU) HideVitalWindows(); - DeleteWindowByClass(WC_HIGHSCORE); - w = new Window(&_highscore_desc); - - if (w != NULL) { - MarkWholeScreenDirty(); - w->window_number = difficulty; // show highscore chart for difficulty... - WP(w, highscore_d).background_img = SPR_HIGHSCORE_CHART_BEGIN; // which background to show - WP(w, highscore_d).rank = ranking; - } + new HighScoreWindow(&_highscore_desc, difficulty, ranking); } /** Show the endgame victory screen in 2050. Update the new highscore * if it was high enough */ void ShowEndGameChart() { - Window *w; - /* Dedicated server doesn't need the highscore window */ if (_network_dedicated) return; - /* Pause in single-player to have a look at the highscore at your own leisure */ - if (!_networking) DoCommandP(0, 1, 0, NULL, CMD_PAUSE); HideVitalWindows(); DeleteWindowByClass(WC_ENDSCREEN); - w = new Window(&_endgame_desc); - - if (w != NULL) { - MarkWholeScreenDirty(); - - WP(w, highscore_d).background_img = SPR_TYCOON_IMG1_BEGIN; - - if (_local_player != PLAYER_SPECTATOR) { - const Player *p = GetPlayer(_local_player); - if (p->old_economy[0].performance_history == SCORE_MAX) - WP(w, highscore_d).background_img = SPR_TYCOON_IMG2_BEGIN; - } - - /* In a network game show the endscores of the custom difficulty 'network' which is the last one - * as well as generate a TOP5 of that game, and not an all-time top5. */ - if (_networking) { - w->window_number = lengthof(_highscore_table) - 1; - WP(w, highscore_d).rank = SaveHighScoreValueNetwork(); - } else { - /* in single player _local player is always valid */ - const Player *p = GetPlayer(_local_player); - w->window_number = _opt.diff_level; - WP(w, highscore_d).rank = SaveHighScoreValue(p); - } - } + new EndGameWindow(&_endgame_desc); } diff -r 6c4314786d68 -r 8cbdb511a674 src/players.cpp --- a/src/players.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/players.cpp Mon May 19 15:13:58 2008 +0000 @@ -355,8 +355,11 @@ MarkWholeScreenDirty(); if (!IsHumanPlayer(p->index)) { - SetDParam(0, t->index); - AddNewsItem((StringID)(p->index | NB_BNEWCOMPANY), NM_CALLBACK, NF_TILE, NT_COMPANY_INFO, DNC_BANKRUPCY, p->last_build_coordinate, 0); + SetDParam(0, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED); + SetDParam(1, STR_705F_STARTS_CONSTRUCTION_NEAR); + SetDParam(2, p->index); + SetDParam(3, t->index); + AddNewsItem(STR_02B6, NS_COMPANY_NEW, p->last_build_coordinate, p->index); } for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { if (i != p->index) AI_Event(i, new AIEventCompanyNew(p->index)); @@ -930,8 +933,10 @@ DeletePlayerWindows(p->index); /* Show the bankrupt news */ - SetDParam(0, p->index); - AddNewsItem((StringID)(p->index | NB_BBANKRUPT), NM_CALLBACK, NF_NONE, NT_COMPANY_INFO, DNC_BANKRUPCY, 0, 0); + SetDParam(0, STR_705C_BANKRUPT); + SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY); + SetDParam(2, p->index); + AddNewsItem(STR_02B6, NS_COMPANY_BANKRUPT, 0, p->index); /* Remove the company */ ChangeOwnershipOfPlayerItems(p->index, PLAYER_SPECTATOR); diff -r 6c4314786d68 -r 8cbdb511a674 src/querystring_gui.h --- a/src/querystring_gui.h Mon May 19 14:14:33 2008 +0000 +++ b/src/querystring_gui.h Mon May 19 15:13:58 2008 +0000 @@ -17,20 +17,20 @@ void DrawEditBox(Window *w, int wid); void HandleEditBox(Window *w, int wid); - int HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, bool &cont); + int HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, Window::EventState &state); }; struct QueryStringBaseWindow : public Window, public QueryString { char edit_str_buf[64]; char orig_str_buf[64]; - QueryStringBaseWindow(const WindowDesc *desc, void *data = NULL, WindowNumber window_number = 0) : Window(desc, data, window_number) + QueryStringBaseWindow(const WindowDesc *desc, WindowNumber window_number = 0) : Window(desc, window_number) { } void DrawEditBox(int wid); void HandleEditBox(int wid); - int HandleEditBoxKey(int wid, uint16 key, uint16 keycode, bool &cont); + int HandleEditBoxKey(int wid, uint16 key, uint16 keycode, EventState &state); }; void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok); diff -r 6c4314786d68 -r 8cbdb511a674 src/rail_cmd.cpp --- a/src/rail_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/rail_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -777,7 +777,7 @@ d->town_index = ClosestTownFromTile(tile, (uint)-1)->index; AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_player); - YapfNotifyTrackLayoutChange(tile, TrackdirToTrack(DiagdirToDiagTrackdir(dir))); + YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir)); } return cost.AddCost(_price.build_train_depot); @@ -1246,7 +1246,7 @@ case RAIL_TILE_WAYPOINT: if (flags & DC_EXEC) { /* notify YAPF about the track layout change */ - YapfNotifyTrackLayoutChange(tile, AxisToTrack(GetWaypointAxis(tile))); + YapfNotifyTrackLayoutChange(tile, GetRailWaypointTrack(tile)); } cost.AddCost(RailConvertCost(type, totype)); break; @@ -1254,7 +1254,7 @@ case RAIL_TILE_DEPOT: if (flags & DC_EXEC) { /* notify YAPF about the track layout change */ - YapfNotifyTrackLayoutChange(tile, AxisToTrack(DiagDirToAxis(GetRailDepotDirection(tile)))); + YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile)); /* Update build vehicle window related to this depot */ InvalidateWindowData(WC_VEHICLE_DEPOT, tile); @@ -1295,7 +1295,7 @@ VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); VehicleFromPos(endtile, NULL, &UpdateTrainPowerProc); - Track track = AxisToTrack(DiagDirToAxis(GetTunnelBridgeDirection(tile))); + Track track = DiagDirToDiagTrack(GetTunnelBridgeDirection(tile)); YapfNotifyTrackLayoutChange(tile, track); YapfNotifyTrackLayoutChange(endtile, track); @@ -1344,7 +1344,7 @@ DoClearSquare(tile); delete GetDepotByTile(tile); AddSideToSignalBuffer(tile, dir, owner); - YapfNotifyTrackLayoutChange(tile, TrackdirToTrack(DiagdirToDiagTrackdir(dir))); + YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir)); } return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_train_depot); @@ -2149,7 +2149,7 @@ if (side != INVALID_DIAGDIR && side != dir) break; - trackbits = AxisToTrackBits(DiagDirToAxis(dir)); + trackbits = DiagDirToDiagTrackBits(dir); break; } diff -r 6c4314786d68 -r 8cbdb511a674 src/rail_gui.cpp --- a/src/rail_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/rail_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -42,25 +42,25 @@ #include "table/sprites.h" #include "table/strings.h" -static RailType _cur_railtype; -static bool _remove_button_clicked; -static DiagDirection _build_depot_direction; -static byte _waypoint_count = 1; -static byte _cur_waypoint_type; -static bool _convert_signal_button; ///< convert signal button in the signal GUI pressed -static SignalVariant _cur_signal_variant; ///< set the signal variant (for signal GUI) -static SignalType _cur_signal_type; ///< set the signal type (for signal GUI) +static RailType _cur_railtype; ///< Rail type of the current build-rail toolbar. +static bool _remove_button_clicked; ///< Flag whether 'remove' toggle-button is currently enabled +static DiagDirection _build_depot_direction; ///< Currently selected depot direction +static byte _waypoint_count = 1; ///< Number of waypoint types +static byte _cur_waypoint_type; ///< Currently selected waypoint type +static bool _convert_signal_button; ///< convert signal button in the signal GUI pressed +static SignalVariant _cur_signal_variant; ///< set the signal variant (for signal GUI) +static SignalType _cur_signal_type; ///< set the signal type (for signal GUI) static struct { - byte orientation; - byte numtracks; - byte platlength; - bool dragdrop; + byte orientation; ///< Currently selected rail station orientation + byte numtracks; ///< Currently selected number of tracks in station (if not \c dragdrop ) + byte platlength; ///< Currently selected platform length of station (if not \c dragdrop ) + bool dragdrop; ///< Use drag & drop to place a station - bool newstations; - StationClassIDByte station_class; - byte station_type; - byte station_count; + bool newstations; ///< Are custom station definitions available? + StationClassIDByte station_class; ///< Currently selected custom station class (if newstations is \c true ) + byte station_type; ///< Station type within the currently selected custom station class (if newstations is \c true ) + byte station_count; ///< Number of custom stations (if newstations is \c true ) } _railstation; @@ -111,6 +111,12 @@ VpStartPlaceSizing(tile, VPM_RAILDIRS, DDSP_PLACE_AUTORAIL); } +/** + * Try to add an additional rail-track at the entrance of a depot + * @param tile Tile to use for adding the rail-track + * @param extra Track to add + * @see CcRailDepot() + */ static void PlaceExtraDepotRail(TileIndex tile, uint16 extra) { if (GetRailTileType(tile) != RAIL_TILE_NORMAL) return; @@ -119,10 +125,11 @@ DoCommandP(tile, _cur_railtype, extra & 0xFF, NULL, CMD_BUILD_SINGLE_RAIL | CMD_NO_WATER); } +/** Additional pieces of track to add at the entrance of a depot. */ static const uint16 _place_depot_extra[12] = { - 0x0604, 0x2102, 0x1202, 0x0505, - 0x2400, 0x2801, 0x1800, 0x1401, - 0x2203, 0x0904, 0x0A05, 0x1103, + 0x0604, 0x2102, 0x1202, 0x0505, // First additional track for directions 0..3 + 0x2400, 0x2801, 0x1800, 0x1401, // Second additional track + 0x2203, 0x0904, 0x0A05, 0x1103, // Third additional track }; @@ -235,6 +242,7 @@ VpStartPlaceSizing(tile, VPM_X_OR_Y, DDSP_BUILD_BRIDGE); } +/** Command callback for building a tunnel */ void CcBuildRailTunnel(bool success, TileIndex tile, uint32 p1, uint32 p2) { if (success) { @@ -285,7 +293,7 @@ }; -/** Toogles state of the Remove button of Build rail toolbar +/** Toggles state of the Remove button of Build rail toolbar * @param w window the button belongs to */ static void ToggleRailButton_Remove(Window *w) @@ -316,36 +324,71 @@ } +/** + * The "rail N"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_N(Window *w) { HandlePlacePushButton(w, RTW_BUILD_NS, GetRailTypeInfo(_cur_railtype)->cursor.rail_ns, VHM_RECT, PlaceRail_N); } +/** + * The "rail NE"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_NE(Window *w) { HandlePlacePushButton(w, RTW_BUILD_X, GetRailTypeInfo(_cur_railtype)->cursor.rail_swne, VHM_RECT, PlaceRail_NE); } +/** + * The "rail E"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_E(Window *w) { HandlePlacePushButton(w, RTW_BUILD_EW, GetRailTypeInfo(_cur_railtype)->cursor.rail_ew, VHM_RECT, PlaceRail_E); } +/** + * The "rail NW"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_NW(Window *w) { HandlePlacePushButton(w, RTW_BUILD_Y, GetRailTypeInfo(_cur_railtype)->cursor.rail_nwse, VHM_RECT, PlaceRail_NW); } +/** + * The "auto-rail"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_AutoRail(Window *w) { HandlePlacePushButton(w, RTW_AUTORAIL, GetRailTypeInfo(_cur_railtype)->cursor.autorail, VHM_RAIL, PlaceRail_AutoRail); } +/** + * The "demolish"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_Demolish(Window *w) { HandlePlacePushButton(w, RTW_DEMOLISH, ANIMCURSOR_DEMOLISH, VHM_RECT, PlaceProc_DemolishArea); } +/** + * The "build depot"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_Depot(Window *w) { if (HandlePlacePushButton(w, RTW_BUILD_DEPOT, GetRailTypeInfo(_cur_railtype)->cursor.depot, VHM_RECT, PlaceRail_Depot)) { @@ -353,6 +396,12 @@ } } +/** + * The "build waypoint"-button click proc of the build-rail toolbar. + * If there are newGRF waypoints, also open a window to pick the waypoint type. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_Waypoint(Window *w) { _waypoint_count = GetNumCustomStations(STAT_CLASS_WAYP); @@ -362,12 +411,22 @@ } } +/** + * The "build station"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_Station(Window *w) { if (HandlePlacePushButton(w, RTW_BUILD_STATION, SPR_CURSOR_RAIL_STATION, VHM_RECT, PlaceRail_Station)) ShowStationBuilder(); } -/** The "build signal"-button proc from BuildRailToolbWndProc() (start ShowSignalBuilder() and/or HandleAutoSignalPlacement()) */ +/** + * The "build signal"-button click proc of the build-rail toolbar. + * Start ShowSignalBuilder() and/or HandleAutoSignalPlacement(). + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_AutoSignals(Window *w) { if (_patches.enable_signal_gui != _ctrl_pressed) { @@ -377,16 +436,31 @@ } } +/** + * The "build bridge"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_Bridge(Window *w) { HandlePlacePushButton(w, RTW_BUILD_BRIDGE, SPR_CURSOR_BRIDGE, VHM_RECT, PlaceRail_Bridge); } +/** + * The "build tunnel"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_Tunnel(Window *w) { HandlePlacePushButton(w, RTW_BUILD_TUNNEL, GetRailTypeInfo(_cur_railtype)->cursor.tunnel, VHM_SPECIAL, PlaceRail_Tunnel); } +/** + * The "remove"-button click proc of the build-rail toolbar. + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_Remove(Window *w) { if (w->IsWidgetDisabled(RTW_REMOVE)) return; @@ -416,6 +490,12 @@ } } +/** + * The "convert-rail"-button click proc of the build-rail toolbar. + * Switches to 'convert-rail' mode + * @param w Build-rail toolbar window + * @see BuildRailToolbWndProc() + */ static void BuildRailClick_Convert(Window *w) { HandlePlacePushButton(w, RTW_CONVERT_RAIL, GetRailTypeInfo(_cur_railtype)->cursor.convert, VHM_RECT, PlaceRail_ConvertRail); @@ -526,90 +606,105 @@ }; -static void UpdateRemoveWidgetStatus(Window *w, int clicked_widget) -{ - switch (clicked_widget) { - case RTW_REMOVE: - /* If it is the removal button that has been clicked, do nothing, - * as it is up to the other buttons to drive removal status */ - return; - break; - case RTW_BUILD_NS: - case RTW_BUILD_X: - case RTW_BUILD_EW: - case RTW_BUILD_Y: - case RTW_AUTORAIL: - case RTW_BUILD_WAYPOINT: - case RTW_BUILD_STATION: - case RTW_BUILD_SIGNALS: - /* Removal button is enabled only if the rail/signal/waypoint/station - * button is still lowered. Once raised, it has to be disabled */ - w->SetWidgetDisabledState(RTW_REMOVE, !w->IsWidgetLowered(clicked_widget)); - break; - - default: - /* When any other buttons than rail/signal/waypoint/station, raise and - * disable the removal button */ - w->DisableWidget(RTW_REMOVE); - w->RaiseWidget(RTW_REMOVE); - break; - } -} +/** + * Based on the widget clicked, update the status of the 'remove' button. + * @param w Rail toolbar window + * @param clicked_widget Widget clicked in the toolbar + */ +struct BuildRailToolbarWindow : Window { + BuildRailToolbarWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->DisableWidget(RTW_REMOVE); -/** - * Railway toolbar window event definition - * - * @param w window pointer - * @param e event been triggered - */ -static void BuildRailToolbWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: w->DisableWidget(RTW_REMOVE); break; - - case WE_PAINT: DrawWindowWidgets(w); break; + this->FindWindowPlacementAndResize(desc); + if (_patches.link_terraform_toolbar) ShowTerraformToolbar(this); + } - case WE_CLICK: - if (e->we.click.widget >= RTW_BUILD_NS) { - _remove_button_clicked = false; - _build_railroad_button_proc[e->we.click.widget - RTW_BUILD_NS](w); + ~BuildRailToolbarWindow() + { + if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0); + } + + void UpdateRemoveWidgetStatus(int clicked_widget) + { + switch (clicked_widget) { + case RTW_REMOVE: + /* If it is the removal button that has been clicked, do nothing, + * as it is up to the other buttons to drive removal status */ + return; + break; + case RTW_BUILD_NS: + case RTW_BUILD_X: + case RTW_BUILD_EW: + case RTW_BUILD_Y: + case RTW_AUTORAIL: + case RTW_BUILD_WAYPOINT: + case RTW_BUILD_STATION: + case RTW_BUILD_SIGNALS: + /* Removal button is enabled only if the rail/signal/waypoint/station + * button is still lowered. Once raised, it has to be disabled */ + this->SetWidgetDisabledState(RTW_REMOVE, !this->IsWidgetLowered(clicked_widget)); + break; + + default: + /* When any other buttons than rail/signal/waypoint/station, raise and + * disable the removal button */ + this->DisableWidget(RTW_REMOVE); + this->RaiseWidget(RTW_REMOVE); + break; } - UpdateRemoveWidgetStatus(w, e->we.click.widget); - if (_ctrl_pressed) RailToolbar_CtrlChanged(w); - break; + } - case WE_KEYPRESS: + virtual void OnPaint() + { + this->DrawWidgets(); + } + + virtual void OnClick(Point pt, int widget) + { + if (widget >= RTW_BUILD_NS) { + _remove_button_clicked = false; + _build_railroad_button_proc[widget - RTW_BUILD_NS](this); + } + this->UpdateRemoveWidgetStatus(widget); + if (_ctrl_pressed) RailToolbar_CtrlChanged(this); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + EventState state = ES_NOT_HANDLED; for (uint8 i = 0; i != lengthof(_rail_keycodes); i++) { - if (e->we.keypress.keycode == _rail_keycodes[i]) { - e->we.keypress.cont = false; + if (keycode == _rail_keycodes[i]) { _remove_button_clicked = false; - _build_railroad_button_proc[i](w); - UpdateRemoveWidgetStatus(w, i + RTW_BUILD_NS); - if (_ctrl_pressed) RailToolbar_CtrlChanged(w); + _build_railroad_button_proc[i](this); + this->UpdateRemoveWidgetStatus(i + RTW_BUILD_NS); + if (_ctrl_pressed) RailToolbar_CtrlChanged(this); + state = ES_HANDLED; break; } } MarkTileDirty(_thd.pos.x, _thd.pos.y); // redraw tile selection - break; - - case WE_PLACE_OBJ: - _place_proc(e->we.place.tile); - return; - - case WE_PLACE_DRAG: { - /* no dragging if you have pressed the convert button */ - if (FindWindowById(WC_BUILD_SIGNAL, 0) != NULL && _convert_signal_button && w->IsWidgetLowered(RTW_BUILD_SIGNALS)) return; - - VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method); - return; + return state; } - case WE_PLACE_MOUSEUP: - if (e->we.place.pt.x != -1) { - TileIndex start_tile = e->we.place.starttile; - TileIndex end_tile = e->we.place.tile; + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + _place_proc(tile); + } - switch (e->we.place.select_proc) { + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) + { + /* no dragging if you have pressed the convert button */ + if (FindWindowById(WC_BUILD_SIGNAL, 0) != NULL && _convert_signal_button && this->IsWidgetLowered(RTW_BUILD_SIGNALS)) return; + + VpSelectTilesWithMethod(pt.x, pt.y, select_method); + } + + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) + { + if (pt.x != -1) { + switch (select_proc) { + default: NOT_REACHED(); case DDSP_BUILD_BRIDGE: ResetObjectToPlace(); ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_RAIL, _cur_railtype); @@ -624,7 +719,7 @@ break; case DDSP_DEMOLISH_AREA: - GUIPlaceProcDragXY(e); + GUIPlaceProcDragXY(select_proc, start_tile, end_tile); break; case DDSP_CONVERT_RAIL: @@ -642,42 +737,36 @@ case DDSP_PLACE_RAIL_NE: case DDSP_PLACE_RAIL_NW: - DoRailroadTrack(e->we.place.select_proc == DDSP_PLACE_RAIL_NE ? TRACK_X : TRACK_Y); + DoRailroadTrack(select_proc == DDSP_PLACE_RAIL_NE ? TRACK_X : TRACK_Y); break; } } - break; - - case WE_ABORT_PLACE_OBJ: - w->RaiseButtons(); - w->DisableWidget(RTW_REMOVE); - w->InvalidateWidget(RTW_REMOVE); + } - w = FindWindowById(WC_BUILD_SIGNAL, 0); - if (w != NULL) WP(w, def_d).close = true; - w = FindWindowById(WC_BUILD_STATION, 0); - if (w != NULL) WP(w, def_d).close = true; - w = FindWindowById(WC_BUILD_DEPOT, 0); - if (w != NULL) WP(w, def_d).close = true; - break; + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); + this->DisableWidget(RTW_REMOVE); + this->InvalidateWidget(RTW_REMOVE); - case WE_PLACE_PRESIZE: { - TileIndex tile = e->we.place.tile; + delete FindWindowById(WC_BUILD_SIGNAL, 0); + delete FindWindowById(WC_BUILD_STATION, 0); + delete FindWindowById(WC_BUILD_DEPOT, 0); + } + virtual void OnPlacePresize(Point pt, TileIndex tile) + { DoCommand(tile, 0, 0, DC_AUTO, CMD_BUILD_TUNNEL); VpSetPresizeRange(tile, _build_tunnel_endtile == 0 ? tile : _build_tunnel_endtile); - } break; - - case WE_DESTROY: - if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0); - break; + } - case WE_CTRL_CHANGED: + virtual EventState OnCTRLStateChange() + { /* do not toggle Remove button by Ctrl when placing station */ - if (!w->IsWidgetLowered(RTW_BUILD_STATION) && RailToolbar_CtrlChanged(w)) e->we.ctrl.cont = false; - break; + if (!this->IsWidgetLowered(RTW_BUILD_STATION) && RailToolbar_CtrlChanged(this)) return ES_HANDLED; + return ES_NOT_HANDLED; } -} +}; /** Widget definition for the rail toolbar */ static const Widget _build_rail_widgets[] = { @@ -712,7 +801,6 @@ WC_BUILD_TOOLBAR, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _build_rail_widgets, - BuildRailToolbWndProc }; @@ -736,66 +824,39 @@ w->widget[RTW_BUILD_TUNNEL].data = rti->gui_sprites.build_tunnel; } +/** + * Open the build rail toolbar window for a specific rail type. + * The window may be opened in the 'normal' way by clicking at the rail icon in + * the main toolbar, or by means of selecting one of the functions of the + * toolbar. In the latter case, the corresponding widget is also selected. + * + * If the terraform toolbar is linked to the toolbar, that window is also opened. + * + * @param railtype Rail type to open the window for + * @param button Widget clicked (\c -1 means no button clicked) + */ void ShowBuildRailToolbar(RailType railtype, int button) { - Window *w; + BuildRailToolbarWindow *w; if (!IsValidPlayer(_current_player)) return; if (!ValParamRailtype(railtype)) return; // don't recreate the window if we're clicking on a button and the window exists. - if (button < 0 || !(w = FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_RAIL))) { + if (button < 0 || !(w = dynamic_cast(FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_RAIL)))) { DeleteWindowByClass(WC_BUILD_TOOLBAR); _cur_railtype = railtype; - w = AllocateWindowDescFront(&_build_rail_desc, TRANSPORT_RAIL); + w = AllocateWindowDescFront(&_build_rail_desc, TRANSPORT_RAIL); SetupRailToolbar(railtype, w); } _remove_button_clicked = false; if (w != NULL && button >= RTW_CLOSEBOX) { _build_railroad_button_proc[button](w); - UpdateRemoveWidgetStatus(w, button + RTW_BUILD_NS); + w->UpdateRemoveWidgetStatus(button + RTW_BUILD_NS); } - if (_patches.link_terraform_toolbar) ShowTerraformToolbar(w); } -/** Enum referring to the widgets of the rail stations window */ -enum BuildRailStationWidgets { - BRSW_CLOSEBOX = 0, - BRSW_CAPTION, - BRSW_BACKGROUND, - - BRSW_PLATFORM_DIR_X, - BRSW_PLATFORM_DIR_Y, - - BRSW_PLATFORM_NUM_BEGIN = BRSW_PLATFORM_DIR_Y, - BRSW_PLATFORM_NUM_1, - BRSW_PLATFORM_NUM_2, - BRSW_PLATFORM_NUM_3, - BRSW_PLATFORM_NUM_4, - BRSW_PLATFORM_NUM_5, - BRSW_PLATFORM_NUM_6, - BRSW_PLATFORM_NUM_7, - - BRSW_PLATFORM_LEN_BEGIN = BRSW_PLATFORM_NUM_7, - BRSW_PLATFORM_LEN_1, - BRSW_PLATFORM_LEN_2, - BRSW_PLATFORM_LEN_3, - BRSW_PLATFORM_LEN_4, - BRSW_PLATFORM_LEN_5, - BRSW_PLATFORM_LEN_6, - BRSW_PLATFORM_LEN_7, - - BRSW_PLATFORM_DRAG_N_DROP, - - BRSW_HIGHLIGHT_OFF, - BRSW_HIGHLIGHT_ON, - - BRSW_NEWST_DROPDOWN, - BRSW_NEWST_LIST, - BRSW_NEWST_SCROLL -}; - /* TODO: For custom stations, respect their allowed platforms/lengths bitmasks! * --pasky */ @@ -819,64 +880,124 @@ CMD_BUILD_RAILROAD_STATION | CMD_NO_WATER | CMD_MSG(STR_100F_CAN_T_BUILD_RAILROAD_STATION)); } -/* Check if the currently selected station size is allowed */ -static void CheckSelectedSize(Window *w, const StationSpec *statspec) -{ - if (statspec == NULL || _railstation.dragdrop) return; +struct BuildRailStationWindow : public PickerWindowBase { +private: + /** Enum referring to the widgets of the rail stations window */ + enum BuildRailStationWidgets { + BRSW_CLOSEBOX = 0, + BRSW_CAPTION, + BRSW_BACKGROUND, - if (HasBit(statspec->disallowed_platforms, _railstation.numtracks - 1)) { - w->RaiseWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); - _railstation.numtracks = 1; - while (HasBit(statspec->disallowed_platforms, _railstation.numtracks - 1)) { - _railstation.numtracks++; + BRSW_PLATFORM_DIR_X, + BRSW_PLATFORM_DIR_Y, + + BRSW_PLATFORM_NUM_BEGIN = BRSW_PLATFORM_DIR_Y, + BRSW_PLATFORM_NUM_1, + BRSW_PLATFORM_NUM_2, + BRSW_PLATFORM_NUM_3, + BRSW_PLATFORM_NUM_4, + BRSW_PLATFORM_NUM_5, + BRSW_PLATFORM_NUM_6, + BRSW_PLATFORM_NUM_7, + + BRSW_PLATFORM_LEN_BEGIN = BRSW_PLATFORM_NUM_7, + BRSW_PLATFORM_LEN_1, + BRSW_PLATFORM_LEN_2, + BRSW_PLATFORM_LEN_3, + BRSW_PLATFORM_LEN_4, + BRSW_PLATFORM_LEN_5, + BRSW_PLATFORM_LEN_6, + BRSW_PLATFORM_LEN_7, + + BRSW_PLATFORM_DRAG_N_DROP, + + BRSW_HIGHLIGHT_OFF, + BRSW_HIGHLIGHT_ON, + + BRSW_NEWST_DROPDOWN, + BRSW_NEWST_LIST, + BRSW_NEWST_SCROLL + }; + + /** + * Verify whether the currently selected station size is allowed after selecting a new station class/type. + * If not, change the station size variables ( _railstation.numtracks and _railstation.platlength ). + * @param statspec Specification of the new station class/type + */ + void CheckSelectedSize(const StationSpec *statspec) + { + if (statspec == NULL || _railstation.dragdrop) return; + + /* If current number of tracks is not allowed, make it as big as possible (which is always less than currently selected) */ + if (HasBit(statspec->disallowed_platforms, _railstation.numtracks - 1)) { + this->RaiseWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); + _railstation.numtracks = 1; + while (HasBit(statspec->disallowed_platforms, _railstation.numtracks - 1)) { + _railstation.numtracks++; + } + this->LowerWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); } - w->LowerWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); + + if (HasBit(statspec->disallowed_lengths, _railstation.platlength - 1)) { + this->RaiseWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); + _railstation.platlength = 1; + while (HasBit(statspec->disallowed_lengths, _railstation.platlength - 1)) { + _railstation.platlength++; + } + this->LowerWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); + } } - if (HasBit(statspec->disallowed_lengths, _railstation.platlength - 1)) { - w->RaiseWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); - _railstation.platlength = 1; - while (HasBit(statspec->disallowed_lengths, _railstation.platlength - 1)) { - _railstation.platlength++; + /** Build a dropdown list of available station classes */ + static DropDownList *BuildStationClassDropDown() + { + DropDownList *list = new DropDownList(); + + for (uint i = 0; i < GetNumStationClasses(); i++) { + if (i == STAT_CLASS_WAYP) continue; + list->push_back(new DropDownListStringItem(GetStationClassName((StationClassID)i), i, false)); } - w->LowerWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); + + return list; } -} +/** + * Window event handler of station build window. + * @param w Staion build window + * @param e Window event to handle + */ -static DropDownList *BuildStationClassDropDown() -{ - DropDownList *list = new DropDownList(); +public: + BuildRailStationWindow(const WindowDesc *desc, bool newstation) : PickerWindowBase(desc) + { + this->LowerWidget(_railstation.orientation + BRSW_PLATFORM_DIR_X); + if (_railstation.dragdrop) { + this->LowerWidget(BRSW_PLATFORM_DRAG_N_DROP); + } else { + this->LowerWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); + this->LowerWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); + } + this->SetWidgetLoweredState(BRSW_HIGHLIGHT_OFF, !_station_show_coverage); + this->SetWidgetLoweredState(BRSW_HIGHLIGHT_ON, _station_show_coverage); - for (uint i = 0; i < GetNumStationClasses(); i++) { - if (i == STAT_CLASS_WAYP) continue; - list->push_back(new DropDownListStringItem(GetStationClassName((StationClassID)i), i, false)); + this->FindWindowPlacementAndResize(desc); + + _railstation.newstations = newstation; + + if (newstation) { + _railstation.station_count = GetNumCustomStations(_railstation.station_class); + + this->vscroll.count = _railstation.station_count; + this->vscroll.cap = 5; + this->vscroll.pos = Clamp(_railstation.station_type - 2, 0, this->vscroll.count - this->vscroll.cap); + } } - return list; -} - -static void StationBuildWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - w->LowerWidget(_railstation.orientation + BRSW_PLATFORM_DIR_X); - if (_railstation.dragdrop) { - w->LowerWidget(BRSW_PLATFORM_DRAG_N_DROP); - } else { - w->LowerWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); - w->LowerWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); - } - w->SetWidgetLoweredState(BRSW_HIGHLIGHT_OFF, !_station_show_coverage); - w->SetWidgetLoweredState(BRSW_HIGHLIGHT_ON, _station_show_coverage); - break; - - case WE_PAINT: { + virtual void OnPaint() + { bool newstations = _railstation.newstations; DrawPixelInfo tmp_dpi, *old_dpi; const StationSpec *statspec = newstations ? GetCustomStationSpec(_railstation.station_class, _railstation.station_type) : NULL; - if (WP(w, def_d).close) return; - if (_railstation.dragdrop) { SetTileSelectSize(1, 1); } else { @@ -895,16 +1016,16 @@ for (uint bits = 0; bits < 7; bits++) { bool disable = bits >= _patches.station_spread; if (statspec == NULL) { - w->SetWidgetDisabledState(bits + BRSW_PLATFORM_NUM_1, disable); - w->SetWidgetDisabledState(bits + BRSW_PLATFORM_LEN_1, disable); + this->SetWidgetDisabledState(bits + BRSW_PLATFORM_NUM_1, disable); + this->SetWidgetDisabledState(bits + BRSW_PLATFORM_LEN_1, disable); } else { - w->SetWidgetDisabledState(bits + BRSW_PLATFORM_NUM_1, HasBit(statspec->disallowed_platforms, bits) || disable); - w->SetWidgetDisabledState(bits + BRSW_PLATFORM_LEN_1, HasBit(statspec->disallowed_lengths, bits) || disable); + this->SetWidgetDisabledState(bits + BRSW_PLATFORM_NUM_1, HasBit(statspec->disallowed_platforms, bits) || disable); + this->SetWidgetDisabledState(bits + BRSW_PLATFORM_LEN_1, HasBit(statspec->disallowed_lengths, bits) || disable); } } SetDParam(0, GetStationClassName(_railstation.station_class)); - DrawWindowWidgets(w); + this->DrawWidgets(); int y_offset = newstations ? 90 : 0; @@ -935,16 +1056,16 @@ int text_end = DrawStationCoverageAreaText(2, 166 + y_offset, SCT_ALL, rad, false); text_end = DrawStationCoverageAreaText(2, text_end + 4, SCT_ALL, rad, true) + 4; - if (text_end != w->widget[BRSW_BACKGROUND].bottom) { - w->SetDirty(); - ResizeWindowForWidget(w, BRSW_BACKGROUND, 0, text_end - w->widget[BRSW_BACKGROUND].bottom); - w->SetDirty(); + if (text_end != this->widget[BRSW_BACKGROUND].bottom) { + this->SetDirty(); + ResizeWindowForWidget(this, BRSW_BACKGROUND, 0, text_end - this->widget[BRSW_BACKGROUND].bottom); + this->SetDirty(); } if (newstations) { uint y = 35; - for (uint16 i = w->vscroll.pos; i < _railstation.station_count && i < (uint)(w->vscroll.pos + w->vscroll.cap); i++) { + for (uint16 i = this->vscroll.pos; i < _railstation.station_count && i < (uint)(this->vscroll.pos + this->vscroll.cap); i++) { const StationSpec *statspec = GetCustomStationSpec(_railstation.station_class, i); if (statspec != NULL && statspec->name != 0) { @@ -960,181 +1081,175 @@ y += 14; } } - } break; - - case WE_CLICK: { - switch (e->we.click.widget) { - case BRSW_PLATFORM_DIR_X: - case BRSW_PLATFORM_DIR_Y: - w->RaiseWidget(_railstation.orientation + BRSW_PLATFORM_DIR_X); - _railstation.orientation = e->we.click.widget - BRSW_PLATFORM_DIR_X; - w->LowerWidget(_railstation.orientation + BRSW_PLATFORM_DIR_X); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; + } - case BRSW_PLATFORM_NUM_1: - case BRSW_PLATFORM_NUM_2: - case BRSW_PLATFORM_NUM_3: - case BRSW_PLATFORM_NUM_4: - case BRSW_PLATFORM_NUM_5: - case BRSW_PLATFORM_NUM_6: - case BRSW_PLATFORM_NUM_7: { - w->RaiseWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); - w->RaiseWidget(BRSW_PLATFORM_DRAG_N_DROP); + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case BRSW_PLATFORM_DIR_X: + case BRSW_PLATFORM_DIR_Y: + this->RaiseWidget(_railstation.orientation + BRSW_PLATFORM_DIR_X); + _railstation.orientation = widget - BRSW_PLATFORM_DIR_X; + this->LowerWidget(_railstation.orientation + BRSW_PLATFORM_DIR_X); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; - _railstation.numtracks = e->we.click.widget - BRSW_PLATFORM_NUM_BEGIN; - _railstation.dragdrop = false; + case BRSW_PLATFORM_NUM_1: + case BRSW_PLATFORM_NUM_2: + case BRSW_PLATFORM_NUM_3: + case BRSW_PLATFORM_NUM_4: + case BRSW_PLATFORM_NUM_5: + case BRSW_PLATFORM_NUM_6: + case BRSW_PLATFORM_NUM_7: { + this->RaiseWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); + this->RaiseWidget(BRSW_PLATFORM_DRAG_N_DROP); - const StationSpec *statspec = _railstation.newstations ? GetCustomStationSpec(_railstation.station_class, _railstation.station_type) : NULL; - if (statspec != NULL && HasBit(statspec->disallowed_lengths, _railstation.platlength - 1)) { - /* The previously selected number of platforms in invalid */ - for (uint i = 0; i < 7; i++) { - if (!HasBit(statspec->disallowed_lengths, i)) { - w->RaiseWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); - _railstation.platlength = i + 1; - break; + _railstation.numtracks = widget - BRSW_PLATFORM_NUM_BEGIN; + _railstation.dragdrop = false; + + const StationSpec *statspec = _railstation.newstations ? GetCustomStationSpec(_railstation.station_class, _railstation.station_type) : NULL; + if (statspec != NULL && HasBit(statspec->disallowed_lengths, _railstation.platlength - 1)) { + /* The previously selected number of platforms in invalid */ + for (uint i = 0; i < 7; i++) { + if (!HasBit(statspec->disallowed_lengths, i)) { + this->RaiseWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); + _railstation.platlength = i + 1; + break; + } } } - } - - w->LowerWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); - w->LowerWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; - } - case BRSW_PLATFORM_LEN_1: - case BRSW_PLATFORM_LEN_2: - case BRSW_PLATFORM_LEN_3: - case BRSW_PLATFORM_LEN_4: - case BRSW_PLATFORM_LEN_5: - case BRSW_PLATFORM_LEN_6: - case BRSW_PLATFORM_LEN_7: { - w->RaiseWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); - w->RaiseWidget(BRSW_PLATFORM_DRAG_N_DROP); - - _railstation.platlength = e->we.click.widget - BRSW_PLATFORM_LEN_BEGIN; - _railstation.dragdrop = false; - - const StationSpec *statspec = _railstation.newstations ? GetCustomStationSpec(_railstation.station_class, _railstation.station_type) : NULL; - if (statspec != NULL && HasBit(statspec->disallowed_platforms, _railstation.numtracks - 1)) { - /* The previously selected number of tracks in invalid */ - for (uint i = 0; i < 7; i++) { - if (!HasBit(statspec->disallowed_platforms, i)) { - w->RaiseWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); - _railstation.numtracks = i + 1; - break; - } - } + this->LowerWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); + this->LowerWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; } - w->LowerWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); - w->LowerWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; - } + case BRSW_PLATFORM_LEN_1: + case BRSW_PLATFORM_LEN_2: + case BRSW_PLATFORM_LEN_3: + case BRSW_PLATFORM_LEN_4: + case BRSW_PLATFORM_LEN_5: + case BRSW_PLATFORM_LEN_6: + case BRSW_PLATFORM_LEN_7: { + this->RaiseWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); + this->RaiseWidget(BRSW_PLATFORM_DRAG_N_DROP); - case BRSW_PLATFORM_DRAG_N_DROP: { - _railstation.dragdrop ^= true; - w->ToggleWidgetLoweredState(BRSW_PLATFORM_DRAG_N_DROP); + _railstation.platlength = widget - BRSW_PLATFORM_LEN_BEGIN; + _railstation.dragdrop = false; - /* get the first allowed length/number of platforms */ - const StationSpec *statspec = _railstation.newstations ? GetCustomStationSpec(_railstation.station_class, _railstation.station_type) : NULL; - if (statspec != NULL && HasBit(statspec->disallowed_lengths, _railstation.platlength - 1)) { - for (uint i = 0; i < 7; i++) { - if (!HasBit(statspec->disallowed_lengths, i)) { - w->RaiseWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); - _railstation.platlength = i + 1; - break; + const StationSpec *statspec = _railstation.newstations ? GetCustomStationSpec(_railstation.station_class, _railstation.station_type) : NULL; + if (statspec != NULL && HasBit(statspec->disallowed_platforms, _railstation.numtracks - 1)) { + /* The previously selected number of tracks in invalid */ + for (uint i = 0; i < 7; i++) { + if (!HasBit(statspec->disallowed_platforms, i)) { + this->RaiseWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); + _railstation.numtracks = i + 1; + break; + } } } - } - if (statspec != NULL && HasBit(statspec->disallowed_platforms, _railstation.numtracks - 1)) { - for (uint i = 0; i < 7; i++) { - if (!HasBit(statspec->disallowed_platforms, i)) { - w->RaiseWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); - _railstation.numtracks = i + 1; - break; - } - } + + this->LowerWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); + this->LowerWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; } - w->SetWidgetLoweredState(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN, !_railstation.dragdrop); - w->SetWidgetLoweredState(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN, !_railstation.dragdrop); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - } break; - - case BRSW_HIGHLIGHT_OFF: - case BRSW_HIGHLIGHT_ON: - _station_show_coverage = (e->we.click.widget != BRSW_HIGHLIGHT_OFF); - w->SetWidgetLoweredState(BRSW_HIGHLIGHT_OFF, !_station_show_coverage); - w->SetWidgetLoweredState(BRSW_HIGHLIGHT_ON, _station_show_coverage); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; - - case BRSW_NEWST_DROPDOWN: - ShowDropDownList(w, BuildStationClassDropDown(), _railstation.station_class, BRSW_NEWST_DROPDOWN); - break; - - case BRSW_NEWST_LIST: { - const StationSpec *statspec; - int y = (e->we.click.pt.y - 32) / 14; + case BRSW_PLATFORM_DRAG_N_DROP: { + _railstation.dragdrop ^= true; + this->ToggleWidgetLoweredState(BRSW_PLATFORM_DRAG_N_DROP); - if (y >= w->vscroll.cap) return; - y += w->vscroll.pos; - if (y >= _railstation.station_count) return; - - /* Check station availability callback */ - statspec = GetCustomStationSpec(_railstation.station_class, y); - if (statspec != NULL && - HasBit(statspec->callbackmask, CBM_STATION_AVAIL) && - GB(GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE), 0, 8) == 0) return; - - _railstation.station_type = y; + /* get the first allowed length/number of platforms */ + const StationSpec *statspec = _railstation.newstations ? GetCustomStationSpec(_railstation.station_class, _railstation.station_type) : NULL; + if (statspec != NULL && HasBit(statspec->disallowed_lengths, _railstation.platlength - 1)) { + for (uint i = 0; i < 7; i++) { + if (!HasBit(statspec->disallowed_lengths, i)) { + this->RaiseWidget(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN); + _railstation.platlength = i + 1; + break; + } + } + } + if (statspec != NULL && HasBit(statspec->disallowed_platforms, _railstation.numtracks - 1)) { + for (uint i = 0; i < 7; i++) { + if (!HasBit(statspec->disallowed_platforms, i)) { + this->RaiseWidget(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN); + _railstation.numtracks = i + 1; + break; + } + } + } - CheckSelectedSize(w, statspec); + this->SetWidgetLoweredState(_railstation.numtracks + BRSW_PLATFORM_NUM_BEGIN, !_railstation.dragdrop); + this->SetWidgetLoweredState(_railstation.platlength + BRSW_PLATFORM_LEN_BEGIN, !_railstation.dragdrop); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + } break; - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; + case BRSW_HIGHLIGHT_OFF: + case BRSW_HIGHLIGHT_ON: + _station_show_coverage = (widget != BRSW_HIGHLIGHT_OFF); + this->SetWidgetLoweredState(BRSW_HIGHLIGHT_OFF, !_station_show_coverage); + this->SetWidgetLoweredState(BRSW_HIGHLIGHT_ON, _station_show_coverage); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; + + case BRSW_NEWST_DROPDOWN: + ShowDropDownList(this, BuildStationClassDropDown(), _railstation.station_class, BRSW_NEWST_DROPDOWN); + break; + + case BRSW_NEWST_LIST: { + const StationSpec *statspec; + int y = (pt.y - 32) / 14; + + if (y >= this->vscroll.cap) return; + y += this->vscroll.pos; + if (y >= _railstation.station_count) return; + + /* Check station availability callback */ + statspec = GetCustomStationSpec(_railstation.station_class, y); + if (statspec != NULL && + HasBit(statspec->callbackmask, CBM_STATION_AVAIL) && + GB(GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE), 0, 8) == 0) return; + + _railstation.station_type = y; + + this->CheckSelectedSize(statspec); + + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; + } } - } - } break; + } - case WE_DROPDOWN_SELECT: - if (_railstation.station_class != e->we.dropdown.index) { - _railstation.station_class = (StationClassID)e->we.dropdown.index; + virtual void OnDropdownSelect(int widget, int index) + { + if (_railstation.station_class != index) { + _railstation.station_class = (StationClassID)index; _railstation.station_type = 0; _railstation.station_count = GetNumCustomStations(_railstation.station_class); - CheckSelectedSize(w, GetCustomStationSpec(_railstation.station_class, _railstation.station_type)); + this->CheckSelectedSize(GetCustomStationSpec(_railstation.station_class, _railstation.station_type)); - w->vscroll.count = _railstation.station_count; - w->vscroll.pos = _railstation.station_type; + this->vscroll.count = _railstation.station_count; + this->vscroll.pos = _railstation.station_type; } SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; + this->SetDirty(); + } - case WE_TICK: - if (WP(w, def_d).close) { - delete w; - return; - } - CheckRedrawStationCoverage(w); - break; - - case WE_DESTROY: - if (!WP(w, def_d).close) ResetObjectToPlace(); - break; + virtual void OnTick() + { + CheckRedrawStationCoverage(this); } -} +}; /** Widget definition of the standard build rail station window */ static const Widget _station_builder_widgets[] = { @@ -1201,36 +1316,29 @@ { WIDGETS_END}, }; +/** High level window description of the default station-build window */ static const WindowDesc _station_builder_desc = { WDP_AUTO, WDP_AUTO, 148, 200, 148, 200, WC_BUILD_STATION, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _station_builder_widgets, - StationBuildWndProc }; +/** High level window description of the newGRF station-build window */ static const WindowDesc _newstation_builder_desc = { WDP_AUTO, WDP_AUTO, 148, 290, 148, 290, WC_BUILD_STATION, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _newstation_builder_widgets, - StationBuildWndProc }; +/** Open station build window */ static void ShowStationBuilder() { - Window *w; if (GetNumStationClasses() <= 2 && GetNumCustomStations(STAT_CLASS_DFLT) == 1) { - w = new Window(&_station_builder_desc); - _railstation.newstations = false; + new BuildRailStationWindow(&_station_builder_desc, false); } else { - w = new Window(&_newstation_builder_desc); - _railstation.newstations = true; - _railstation.station_count = GetNumCustomStations(_railstation.station_class); - - w->vscroll.count = _railstation.station_count; - w->vscroll.cap = 5; - w->vscroll.pos = Clamp(_railstation.station_type - 2, 0, w->vscroll.count - w->vscroll.cap); + new BuildRailStationWindow(&_newstation_builder_desc, true); } } @@ -1252,108 +1360,99 @@ BSW_DRAG_SIGNALS_DENSITY_INCREASE, }; -/** - * Draw dynamic a signal-sprite in a button in the signal GUI - * Draw the sprite +1px to the right and down if the button is lowered and change the sprite to sprite + 1 (red to green light) - * - * @param w Window on which the widget is located - * @param widget_index index of this widget in the window - * @param image the sprite to draw - * @param xrel the relativ x value of the sprite in the grf - * @param xsize the width of the sprite - */ -static void DrawSignalSprite(const Window *w, byte widget_index, SpriteID image, int8 xrel, uint8 xsize) -{ - DrawSprite(image + w->IsWidgetLowered(widget_index), PAL_NONE, - w->widget[widget_index].left + (w->widget[widget_index].right - w->widget[widget_index].left) / 2 - xrel - xsize / 2 + - w->IsWidgetLowered(widget_index), w->widget[widget_index].bottom - 3 + w->IsWidgetLowered(widget_index)); -} - -/** - * Signal selection window event definition - * - * @param w window pointer - * @param e event been triggered - */ -static void SignalBuildWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - w->LowerWidget((_cur_signal_variant == SIG_ELECTRIC ? BSW_ELECTRIC_NORM : BSW_SEMAPHORE_NORM) + _cur_signal_type); - - w->SetWidgetLoweredState(BSW_CONVERT, _convert_signal_button); - - w->SetWidgetDisabledState(BSW_DRAG_SIGNALS_DENSITY_DECREASE, _patches.drag_signals_density == 1); - w->SetWidgetDisabledState(BSW_DRAG_SIGNALS_DENSITY_INCREASE, _patches.drag_signals_density == 20); - - DrawWindowWidgets(w); - - /* The 'hardcoded' off sets are needed because they are reused sprites. */ - DrawSignalSprite(w, BSW_SEMAPHORE_NORM, SPR_IMG_SIGNAL_SEMAPHORE_NORM, 0, 12); // xsize of sprite + 1 == 9 - DrawSignalSprite(w, BSW_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, -1, 13); // xsize of sprite + 1 == 10 - DrawSignalSprite(w, BSW_SEMAPHORE_EXIT, SPR_IMG_SIGNAL_SEMAPHORE_EXIT, 0, 12); // xsize of sprite + 1 == 9 - DrawSignalSprite(w, BSW_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_COMBO, 0, 12); // xsize of sprite + 1 == 9 - DrawSignalSprite(w, BSW_ELECTRIC_NORM, SPR_IMG_SIGNAL_ELECTRIC_NORM, -1, 4); - DrawSignalSprite(w, BSW_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_ENTRY, -2, 6); - DrawSignalSprite(w, BSW_ELECTRIC_EXIT, SPR_IMG_SIGNAL_ELECTRIC_EXIT, -2, 6); - DrawSignalSprite(w, BSW_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_COMBO, -2, 6); +struct BuildSignalWindow : public PickerWindowBase { +private: + /** + * Draw dynamic a signal-sprite in a button in the signal GUI + * Draw the sprite +1px to the right and down if the button is lowered and change the sprite to sprite + 1 (red to green light) + * + * @param widget_index index of this widget in the window + * @param image the sprite to draw + * @param xrel the relativ x value of the sprite in the grf + * @param xsize the width of the sprite + */ + void DrawSignalSprite(byte widget_index, SpriteID image, int8 xrel, uint8 xsize) + { + DrawSprite(image + this->IsWidgetLowered(widget_index), PAL_NONE, + this->widget[widget_index].left + (this->widget[widget_index].right - this->widget[widget_index].left) / 2 - xrel - xsize / 2 + + this->IsWidgetLowered(widget_index), this->widget[widget_index].bottom - 3 + this->IsWidgetLowered(widget_index)); + } - /* Draw dragging signal density value in the BSW_DRAG_SIGNALS_DENSITY widget */ - SetDParam(0, _patches.drag_signals_density); - DrawStringCentered(w->widget[BSW_DRAG_SIGNALS_DENSITY].left + (w->widget[BSW_DRAG_SIGNALS_DENSITY].right - - w->widget[BSW_DRAG_SIGNALS_DENSITY].left) / 2 + 1, - w->widget[BSW_DRAG_SIGNALS_DENSITY].top + 2, STR_JUST_INT, TC_ORANGE); - break; - - case WE_CLICK: - switch (e->we.click.widget) { - case BSW_SEMAPHORE_NORM: - case BSW_SEMAPHORE_ENTRY: - case BSW_SEMAPHORE_EXIT: - case BSW_SEMAPHORE_COMBO: - case BSW_ELECTRIC_NORM: - case BSW_ELECTRIC_ENTRY: - case BSW_ELECTRIC_EXIT: - case BSW_ELECTRIC_COMBO: - w->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? BSW_ELECTRIC_NORM : BSW_SEMAPHORE_NORM) + _cur_signal_type); - - _cur_signal_type = (SignalType)((uint)((e->we.click.widget - BSW_SEMAPHORE_NORM) % (SIGTYPE_COMBO + 1))); - _cur_signal_variant = e->we.click.widget >= BSW_ELECTRIC_NORM ? SIG_ELECTRIC : SIG_SEMAPHORE; - break; - - case BSW_CONVERT: - _convert_signal_button = !_convert_signal_button; - break; +public: + BuildSignalWindow(const WindowDesc *desc) : PickerWindowBase(desc) + { + this->FindWindowPlacementAndResize(desc); + }; - case BSW_DRAG_SIGNALS_DENSITY_DECREASE: - if (_patches.drag_signals_density > 1) { - _patches.drag_signals_density--; - SetWindowDirty(FindWindowById(WC_GAME_OPTIONS, 0)); - } - break; - - case BSW_DRAG_SIGNALS_DENSITY_INCREASE: - if (_patches.drag_signals_density < 20) { - _patches.drag_signals_density++; - SetWindowDirty(FindWindowById(WC_GAME_OPTIONS, 0)); - } - break; + virtual void OnPaint() + { + this->LowerWidget((_cur_signal_variant == SIG_ELECTRIC ? BSW_ELECTRIC_NORM : BSW_SEMAPHORE_NORM) + _cur_signal_type); - default: break; - } - - w->SetDirty(); - break; + this->SetWidgetLoweredState(BSW_CONVERT, _convert_signal_button); - case WE_TICK: - if (WP(w, def_d).close) delete w; - return; + this->SetWidgetDisabledState(BSW_DRAG_SIGNALS_DENSITY_DECREASE, _patches.drag_signals_density == 1); + this->SetWidgetDisabledState(BSW_DRAG_SIGNALS_DENSITY_INCREASE, _patches.drag_signals_density == 20); - case WE_DESTROY: - if (!WP(w, def_d).close) ResetObjectToPlace(); - break; + this->DrawWidgets(); + + /* The 'hardcoded' off sets are needed because they are reused sprites. */ + this->DrawSignalSprite(BSW_SEMAPHORE_NORM, SPR_IMG_SIGNAL_SEMAPHORE_NORM, 0, 12); // xsize of sprite + 1 == 9 + this->DrawSignalSprite(BSW_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, -1, 13); // xsize of sprite + 1 == 10 + this->DrawSignalSprite(BSW_SEMAPHORE_EXIT, SPR_IMG_SIGNAL_SEMAPHORE_EXIT, 0, 12); // xsize of sprite + 1 == 9 + this->DrawSignalSprite(BSW_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_COMBO, 0, 12); // xsize of sprite + 1 == 9 + this->DrawSignalSprite(BSW_ELECTRIC_NORM, SPR_IMG_SIGNAL_ELECTRIC_NORM, -1, 4); + this->DrawSignalSprite(BSW_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_ENTRY, -2, 6); + this->DrawSignalSprite(BSW_ELECTRIC_EXIT, SPR_IMG_SIGNAL_ELECTRIC_EXIT, -2, 6); + this->DrawSignalSprite(BSW_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_COMBO, -2, 6); + + /* Draw dragging signal density value in the BSW_DRAG_SIGNALS_DENSITY widget */ + SetDParam(0, _patches.drag_signals_density); + DrawStringCentered(this->widget[BSW_DRAG_SIGNALS_DENSITY].left + (this->widget[BSW_DRAG_SIGNALS_DENSITY].right - + this->widget[BSW_DRAG_SIGNALS_DENSITY].left) / 2 + 1, + this->widget[BSW_DRAG_SIGNALS_DENSITY].top + 2, STR_JUST_INT, TC_ORANGE); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case BSW_SEMAPHORE_NORM: + case BSW_SEMAPHORE_ENTRY: + case BSW_SEMAPHORE_EXIT: + case BSW_SEMAPHORE_COMBO: + case BSW_ELECTRIC_NORM: + case BSW_ELECTRIC_ENTRY: + case BSW_ELECTRIC_EXIT: + case BSW_ELECTRIC_COMBO: + this->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? BSW_ELECTRIC_NORM : BSW_SEMAPHORE_NORM) + _cur_signal_type); + + _cur_signal_type = (SignalType)((uint)((widget - BSW_SEMAPHORE_NORM) % (SIGTYPE_COMBO + 1))); + _cur_signal_variant = widget >= BSW_ELECTRIC_NORM ? SIG_ELECTRIC : SIG_SEMAPHORE; + break; + + case BSW_CONVERT: + _convert_signal_button = !_convert_signal_button; + break; + + case BSW_DRAG_SIGNALS_DENSITY_DECREASE: + if (_patches.drag_signals_density > 1) { + _patches.drag_signals_density--; + SetWindowDirty(FindWindowById(WC_GAME_OPTIONS, 0)); + } + break; + + case BSW_DRAG_SIGNALS_DENSITY_INCREASE: + if (_patches.drag_signals_density < 20) { + _patches.drag_signals_density++; + SetWindowDirty(FindWindowById(WC_GAME_OPTIONS, 0)); + } + break; + + default: break; } -} + + this->SetDirty(); + } +}; /** Widget definition of the build signal window */ static const Widget _signal_builder_widgets[] = { @@ -1384,7 +1483,6 @@ WC_BUILD_SIGNAL, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _signal_builder_widgets, - SignalBuildWndProc }; /** @@ -1392,59 +1490,55 @@ */ static void ShowSignalBuilder() { - new Window(&_signal_builder_desc); + new BuildSignalWindow(&_signal_builder_desc); } -/** Enum referring to the widgets of the build rail depot window */ -enum BuildRailDepotWidgets { - BRDW_CLOSEBOX = 0, - BRDW_CAPTION, - BRDW_BACKGROUND, - BRDW_DEPOT_NE, - BRDW_DEPOT_SE, - BRDW_DEPOT_SW, - BRDW_DEPOT_NW, -}; +struct BuildRailDepotWindow : public PickerWindowBase { +private: + /** Enum referring to the widgets of the build rail depot window */ + enum BuildRailDepotWidgets { + BRDW_CLOSEBOX = 0, + BRDW_CAPTION, + BRDW_BACKGROUND, + BRDW_DEPOT_NE, + BRDW_DEPOT_SE, + BRDW_DEPOT_SW, + BRDW_DEPOT_NW, + }; -static void BuildTrainDepotWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: w->LowerWidget(_build_depot_direction + BRDW_DEPOT_NE); break; +public: + BuildRailDepotWindow(const WindowDesc *desc) : PickerWindowBase(desc) + { + this->LowerWidget(_build_depot_direction + BRDW_DEPOT_NE); + this->FindWindowPlacementAndResize(desc); + } - case WE_PAINT: { - DrawWindowWidgets(w); + virtual void OnPaint() + { + this->DrawWidgets(); DrawTrainDepotSprite(70, 17, DIAGDIR_NE, _cur_railtype); DrawTrainDepotSprite(70, 69, DIAGDIR_SE, _cur_railtype); DrawTrainDepotSprite( 2, 69, DIAGDIR_SW, _cur_railtype); DrawTrainDepotSprite( 2, 17, DIAGDIR_NW, _cur_railtype); - break; - } + } - case WE_CLICK: - switch (e->we.click.widget) { + virtual void OnClick(Point pt, int widget) + { + switch (widget) { case BRDW_DEPOT_NE: case BRDW_DEPOT_SE: case BRDW_DEPOT_SW: case BRDW_DEPOT_NW: - w->RaiseWidget(_build_depot_direction + BRDW_DEPOT_NE); - _build_depot_direction = (DiagDirection)(e->we.click.widget - BRDW_DEPOT_NE); - w->LowerWidget(_build_depot_direction + BRDW_DEPOT_NE); + this->RaiseWidget(_build_depot_direction + BRDW_DEPOT_NE); + _build_depot_direction = (DiagDirection)(widget - BRDW_DEPOT_NE); + this->LowerWidget(_build_depot_direction + BRDW_DEPOT_NE); SndPlayFx(SND_15_BEEP); - w->SetDirty(); + this->SetDirty(); break; } - break; - - case WE_TICK: - if (WP(w, def_d).close) delete w; - return; - - case WE_DESTROY: - if (!WP(w, def_d).close) ResetObjectToPlace(); - break; } -} +}; /** Widget definition of the build rail depot window */ static const Widget _build_depot_widgets[] = { @@ -1463,44 +1557,51 @@ WC_BUILD_DEPOT, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _build_depot_widgets, - BuildTrainDepotWndProc }; static void ShowBuildTrainDepotPicker() { - new Window(&_build_depot_desc); + new BuildRailDepotWindow(&_build_depot_desc); } -/** Enum referring to the widgets of the build NewGRF rail waypoint window */ -enum BuildRailWaypointWidgets { - BRWW_CLOSEBOX = 0, - BRWW_CAPTION, - BRWW_BACKGROUND, - BRWW_WAYPOINT_1, - BRWW_WAYPOINT_2, - BRWW_WAYPOINT_3, - BRWW_WAYPOINT_4, - BRWW_WAYPOINT_5, - BRWW_SCROLL, -}; +struct BuildRailWaypointWindow : PickerWindowBase { +private: + /** Enum referring to the widgets of the build NewGRF rail waypoint window */ + enum BuildRailWaypointWidgets { + BRWW_CLOSEBOX = 0, + BRWW_CAPTION, + BRWW_BACKGROUND, + BRWW_WAYPOINT_1, + BRWW_WAYPOINT_2, + BRWW_WAYPOINT_3, + BRWW_WAYPOINT_4, + BRWW_WAYPOINT_5, + BRWW_SCROLL, + }; -static void BuildWaypointWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { +public: + BuildRailWaypointWindow(const WindowDesc *desc) : PickerWindowBase(desc) + { + this->hscroll.cap = 5; + this->hscroll.count = _waypoint_count; + this->FindWindowPlacementAndResize(desc); + }; + + virtual void OnPaint() + { uint i; - for (i = 0; i < w->hscroll.cap; i++) { - w->SetWidgetLoweredState(i + BRWW_WAYPOINT_1, (w->hscroll.pos + i) == _cur_waypoint_type); + for (i = 0; i < this->hscroll.cap; i++) { + this->SetWidgetLoweredState(i + BRWW_WAYPOINT_1, (this->hscroll.pos + i) == _cur_waypoint_type); } - DrawWindowWidgets(w); + this->DrawWidgets(); - for (i = 0; i < w->hscroll.cap; i++) { - if (w->hscroll.pos + i < w->hscroll.count) { - const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, w->hscroll.pos + i); + for (i = 0; i < this->hscroll.cap; i++) { + if (this->hscroll.pos + i < this->hscroll.count) { + const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, this->hscroll.pos + i); - DrawWaypointSprite(2 + i * 68, 25, w->hscroll.pos + i, _cur_railtype); + DrawWaypointSprite(2 + i * 68, 25, this->hscroll.pos + i, _cur_railtype); if (statspec != NULL && HasBit(statspec->callbackmask, CBM_STATION_AVAIL) && @@ -1509,16 +1610,17 @@ } } } - break; } - case WE_CLICK: { - switch (e->we.click.widget) { + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { case BRWW_WAYPOINT_1: case BRWW_WAYPOINT_2: case BRWW_WAYPOINT_3: case BRWW_WAYPOINT_4: case BRWW_WAYPOINT_5: { - byte type = e->we.click.widget - BRWW_WAYPOINT_1 + w->hscroll.pos; + byte type = widget - BRWW_WAYPOINT_1 + this->hscroll.pos; /* Check station availability callback */ const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, type); @@ -1528,22 +1630,12 @@ _cur_waypoint_type = type; SndPlayFx(SND_15_BEEP); - w->SetDirty(); + this->SetDirty(); break; } } - break; } - - case WE_TICK: - if (WP(w, def_d).close) delete w; - break; - - case WE_DESTROY: - if (!WP(w, def_d).close) ResetObjectToPlace(); - break; - } -} +}; /** Widget definition for the build NewGRF rail waypoint window */ static const Widget _build_waypoint_widgets[] = { @@ -1566,17 +1658,16 @@ WC_BUILD_DEPOT, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _build_waypoint_widgets, - BuildWaypointWndProc }; static void ShowBuildWaypointPicker() { - Window *w = new Window(&_build_waypoint_desc); - w->hscroll.cap = 5; - w->hscroll.count = _waypoint_count; + new BuildRailWaypointWindow(&_build_waypoint_desc); } - +/** + * Initialize rail building GUI settings + */ void InitializeRailGui() { _build_depot_direction = DIAGDIR_NW; @@ -1585,6 +1676,10 @@ _railstation.dragdrop = true; } +/** + * Re-initialize rail-build toolbar after toggling support for electric trains + * @param disable Boolean whether electric trains are disabled (removed from the game) + */ void ReinitGuiAfterToggleElrail(bool disable) { extern RailType _last_built_railtype; @@ -1600,6 +1695,7 @@ MarkWholeScreenDirty(); } +/** Set the initial (default) railtype to use */ static void SetDefaultRailGui() { if (_local_player == PLAYER_SPECTATOR || !IsValidPlayer(_local_player)) return; diff -r 6c4314786d68 -r 8cbdb511a674 src/rail_map.h --- a/src/rail_map.h Mon May 19 14:14:33 2008 +0000 +++ b/src/rail_map.h Mon May 19 15:13:58 2008 +0000 @@ -168,6 +168,17 @@ return (DiagDirection)GB(_m[t].m5, 0, 2); } +/** + * Returns the track of a depot, ignoring direction + * @pre IsRailDepotTile(t) + * @param t the tile to get the depot track from + * @return the track of the depot + */ +static inline Track GetRailDepotTrack(TileIndex t) +{ + return DiagDirToDiagTrack(GetRailDepotDirection(t)); +} + /** * Returns the axis of the waypoint diff -r 6c4314786d68 -r 8cbdb511a674 src/road_cmd.cpp --- a/src/road_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/road_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -43,6 +43,37 @@ #include "table/sprites.h" #include "table/strings.h" + +bool RoadVehiclesAreBuilt() +{ + const Vehicle* v; + + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_ROAD) return true; + } + return false; +} + +/** + * Change the side of the road vehicles drive on (server only). + * @param tile unused + * @param flags operation to perform + * @param p1 the side of the road; 0 = left side and 1 = right side + * @param p2 unused + */ +CommandCost CmdSetRoadDriveSide(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) +{ + /* Check boundaries and you can only change this if NO vehicles have been built yet, + * except in the intro-menu where of course it's always possible to do so. */ + if (p1 > 1 || (_game_mode != GM_MENU && RoadVehiclesAreBuilt())) return CMD_ERROR; + + if (flags & DC_EXEC) { + _opt_ptr->road_side = p1; + InvalidateWindow(WC_GAME_OPTIONS, 0); + } + return CommandCost(); +} + #define M(x) (1 << (x)) /* Level crossings may only be built on these slopes */ static const uint32 VALID_LEVEL_CROSSING_SLOPES = (M(SLOPE_SEN) | M(SLOPE_ENW) | M(SLOPE_NWS) | M(SLOPE_NS) | M(SLOPE_WSE) | M(SLOPE_EW) | M(SLOPE_FLAT)); @@ -1390,7 +1421,7 @@ if (side != INVALID_DIAGDIR && side != dir) break; - trackdirbits = TrackBitsToTrackdirBits(AxisToTrackBits(DiagDirToAxis(dir))); + trackdirbits = TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir)); break; } } diff -r 6c4314786d68 -r 8cbdb511a674 src/road_gui.cpp --- a/src/road_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/road_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -400,220 +400,222 @@ 'R', }; -/** - * Update the remove button lowered state of the road toolbar - * - * @param w The toolbar window - * @param clicked_widget The widget which the player clicked just now - */ -static void UpdateOptionWidgetStatus(Window *w, RoadToolbarWidgets clicked_widget) -{ - /* The remove and the one way button state is driven - * by the other buttons so they don't act on themselfs. - * Both are only valid if they are able to apply as options. */ - switch (clicked_widget) { - case RTW_REMOVE: - w->RaiseWidget(RTW_ONE_WAY); - w->InvalidateWidget(RTW_ONE_WAY); - break; - - case RTW_ONE_WAY: - w->RaiseWidget(RTW_REMOVE); - w->InvalidateWidget(RTW_REMOVE); - break; - - case RTW_BUS_STATION: - case RTW_TRUCK_STATION: - w->DisableWidget(RTW_ONE_WAY); - w->SetWidgetDisabledState(RTW_REMOVE, !w->IsWidgetLowered(clicked_widget)); - break; +struct BuildRoadToolbarWindow : Window { + BuildRoadToolbarWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->SetWidgetsDisabledState(true, + RTW_REMOVE, + RTW_ONE_WAY, + WIDGET_LIST_END); - case RTW_ROAD_X: - case RTW_ROAD_Y: - case RTW_AUTOROAD: - w->SetWidgetsDisabledState(!w->IsWidgetLowered(clicked_widget), - RTW_REMOVE, - RTW_ONE_WAY, - WIDGET_LIST_END); - break; - - default: - /* When any other buttons than road/station, raise and - * disable the removal button */ - w->SetWidgetsDisabledState(true, - RTW_REMOVE, - RTW_ONE_WAY, - WIDGET_LIST_END); - w->SetWidgetsLoweredState (false, - RTW_REMOVE, - RTW_ONE_WAY, - WIDGET_LIST_END); - break; + this->FindWindowPlacementAndResize(desc); + if (_patches.link_terraform_toolbar) ShowTerraformToolbar(this); } -} -static void BuildRoadToolbWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - w->SetWidgetsDisabledState(true, - RTW_REMOVE, - RTW_ONE_WAY, - WIDGET_LIST_END); - break; + ~BuildRoadToolbarWindow() + { + if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0); + } - case WE_PAINT: - w->SetWidgetsDisabledState(!CanBuildVehicleInfrastructure(VEH_ROAD), - RTW_DEPOT, - RTW_BUS_STATION, - RTW_TRUCK_STATION, - WIDGET_LIST_END); - DrawWindowWidgets(w); - break; + /** + * Update the remove button lowered state of the road toolbar + * + * @param clicked_widget The widget which the player clicked just now + */ + void UpdateOptionWidgetStatus(RoadToolbarWidgets clicked_widget) + { + /* The remove and the one way button state is driven + * by the other buttons so they don't act on themselfs. + * Both are only valid if they are able to apply as options. */ + switch (clicked_widget) { + case RTW_REMOVE: + this->RaiseWidget(RTW_ONE_WAY); + this->InvalidateWidget(RTW_ONE_WAY); + break; - case WE_CLICK: - if (e->we.click.widget >= RTW_ROAD_X) { + case RTW_ONE_WAY: + this->RaiseWidget(RTW_REMOVE); + this->InvalidateWidget(RTW_REMOVE); + break; + + case RTW_BUS_STATION: + case RTW_TRUCK_STATION: + this->DisableWidget(RTW_ONE_WAY); + this->SetWidgetDisabledState(RTW_REMOVE, !this->IsWidgetLowered(clicked_widget)); + break; + + case RTW_ROAD_X: + case RTW_ROAD_Y: + case RTW_AUTOROAD: + this->SetWidgetsDisabledState(!this->IsWidgetLowered(clicked_widget), + RTW_REMOVE, + RTW_ONE_WAY, + WIDGET_LIST_END); + break; + + default: + /* When any other buttons than road/station, raise and + * disable the removal button */ + this->SetWidgetsDisabledState(true, + RTW_REMOVE, + RTW_ONE_WAY, + WIDGET_LIST_END); + this->SetWidgetsLoweredState (false, + RTW_REMOVE, + RTW_ONE_WAY, + WIDGET_LIST_END); + break; + } + } + + virtual void OnPaint() + { + this->SetWidgetsDisabledState(!CanBuildVehicleInfrastructure(VEH_ROAD), + RTW_DEPOT, + RTW_BUS_STATION, + RTW_TRUCK_STATION, + WIDGET_LIST_END); + this->DrawWidgets(); + } + + virtual void OnClick(Point pt, int widget) + { + if (widget >= RTW_ROAD_X) { + _remove_button_clicked = false; + _one_way_button_clicked = false; + _build_road_button_proc[widget - RTW_ROAD_X](this); + } + this->UpdateOptionWidgetStatus((RoadToolbarWidgets)widget); + if (_ctrl_pressed) RoadToolbar_CtrlChanged(this); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + EventState state = ES_NOT_HANDLED; + for (uint i = 0; i != lengthof(_road_keycodes); i++) { + if (keycode == _road_keycodes[i]) { _remove_button_clicked = false; _one_way_button_clicked = false; - _build_road_button_proc[e->we.click.widget - RTW_ROAD_X](w); + _build_road_button_proc[i](this); + this->UpdateOptionWidgetStatus((RoadToolbarWidgets)(i + RTW_ROAD_X)); + if (_ctrl_pressed) RoadToolbar_CtrlChanged(this); + state = ES_HANDLED; + break; } - UpdateOptionWidgetStatus(w, (RoadToolbarWidgets)e->we.click.widget); - if (_ctrl_pressed) RoadToolbar_CtrlChanged(w); - break; - - case WE_KEYPRESS: - for (uint i = 0; i != lengthof(_road_keycodes); i++) { - if (e->we.keypress.keycode == _road_keycodes[i]) { - e->we.keypress.cont = false; - _remove_button_clicked = false; - _one_way_button_clicked = false; - _build_road_button_proc[i](w); - UpdateOptionWidgetStatus(w, (RoadToolbarWidgets)(i + RTW_ROAD_X)); - if (_ctrl_pressed) RoadToolbar_CtrlChanged(w); - break; - } - } - MarkTileDirty(_thd.pos.x, _thd.pos.y); // redraw tile selection - break; - - case WE_PLACE_OBJ: - _remove_button_clicked = w->IsWidgetLowered(RTW_REMOVE); - _one_way_button_clicked = w->IsWidgetLowered(RTW_ONE_WAY); - _place_proc(e->we.place.tile); - break; + } + MarkTileDirty(_thd.pos.x, _thd.pos.y); // redraw tile selection + return state; + } - case WE_ABORT_PLACE_OBJ: - w->RaiseButtons(); - w->SetWidgetsDisabledState(true, - RTW_REMOVE, - RTW_ONE_WAY, - WIDGET_LIST_END); - w->InvalidateWidget(RTW_REMOVE); - w->InvalidateWidget(RTW_ONE_WAY); + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + _remove_button_clicked = this->IsWidgetLowered(RTW_REMOVE); + _one_way_button_clicked = this->IsWidgetLowered(RTW_ONE_WAY); + _place_proc(tile); + } - w = FindWindowById(WC_BUS_STATION, 0); - if (w != NULL) WP(w, def_d).close = true; - w = FindWindowById(WC_TRUCK_STATION, 0); - if (w != NULL) WP(w, def_d).close = true; - w = FindWindowById(WC_BUILD_DEPOT, 0); - if (w != NULL) WP(w, def_d).close = true; - break; + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); + this->SetWidgetsDisabledState(true, + RTW_REMOVE, + RTW_ONE_WAY, + WIDGET_LIST_END); + this->InvalidateWidget(RTW_REMOVE); + this->InvalidateWidget(RTW_ONE_WAY); - case WE_PLACE_DRAG: - /* Here we update the end tile flags - * of the road placement actions. - * At first we reset the end halfroad - * bits and if needed we set them again. */ - switch (e->we.place.select_proc) { - case DDSP_PLACE_ROAD_X_DIR: - _place_road_flag &= ~RF_END_HALFROAD_X; - if (e->we.place.pt.x & 8) _place_road_flag |= RF_END_HALFROAD_X; - break; + delete FindWindowById(WC_BUS_STATION, 0); + delete FindWindowById(WC_TRUCK_STATION, 0); + delete FindWindowById(WC_BUILD_DEPOT, 0); + } - case DDSP_PLACE_ROAD_Y_DIR: - _place_road_flag &= ~RF_END_HALFROAD_Y; - if (e->we.place.pt.y & 8) _place_road_flag |= RF_END_HALFROAD_Y; + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) + { + /* Here we update the end tile flags + * of the road placement actions. + * At first we reset the end halfroad + * bits and if needed we set them again. */ + switch (select_proc) { + case DDSP_PLACE_ROAD_X_DIR: + _place_road_flag &= ~RF_END_HALFROAD_X; + if (pt.x & 8) _place_road_flag |= RF_END_HALFROAD_X; + break; + + case DDSP_PLACE_ROAD_Y_DIR: + _place_road_flag &= ~RF_END_HALFROAD_Y; + if (pt.y & 8) _place_road_flag |= RF_END_HALFROAD_Y; + break; + + case DDSP_PLACE_AUTOROAD: + _place_road_flag &= ~(RF_END_HALFROAD_Y | RF_END_HALFROAD_X); + if (pt.y & 8) _place_road_flag |= RF_END_HALFROAD_Y; + if (pt.x & 8) _place_road_flag |= RF_END_HALFROAD_X; + + /* For autoroad we need to update the + * direction of the road */ + if (_thd.size.x > _thd.size.y || (_thd.size.x == _thd.size.y && + ( (_tile_fract_coords.x < _tile_fract_coords.y && (_tile_fract_coords.x + _tile_fract_coords.y) < 16) || + (_tile_fract_coords.x > _tile_fract_coords.y && (_tile_fract_coords.x + _tile_fract_coords.y) > 16) ))) { + /* Set dir = X */ + _place_road_flag &= ~RF_DIR_Y; + } else { + /* Set dir = Y */ + _place_road_flag |= RF_DIR_Y; + } + + break; + + default: + break; + } + + VpSelectTilesWithMethod(pt.x, pt.y, select_method); + } + + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) + { + if (pt.x != -1) { + switch (select_proc) { + default: NOT_REACHED(); + case DDSP_BUILD_BRIDGE: + ResetObjectToPlace(); + ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_ROAD, RoadTypeToRoadTypes(_cur_roadtype)); break; - case DDSP_PLACE_AUTOROAD: - _place_road_flag &= ~(RF_END_HALFROAD_Y | RF_END_HALFROAD_X); - if (e->we.place.pt.y & 8) _place_road_flag |= RF_END_HALFROAD_Y; - if (e->we.place.pt.x & 8) _place_road_flag |= RF_END_HALFROAD_X; - - /* For autoroad we need to update the - * direction of the road */ - if (_thd.size.x > _thd.size.y || (_thd.size.x == _thd.size.y && - ( (_tile_fract_coords.x < _tile_fract_coords.y && (_tile_fract_coords.x + _tile_fract_coords.y) < 16) || - (_tile_fract_coords.x > _tile_fract_coords.y && (_tile_fract_coords.x + _tile_fract_coords.y) > 16) ))) { - /* Set dir = X */ - _place_road_flag &= ~RF_DIR_Y; - } else { - /* Set dir = Y */ - _place_road_flag |= RF_DIR_Y; - } - + case DDSP_DEMOLISH_AREA: + GUIPlaceProcDragXY(select_proc, start_tile, end_tile); break; - default: + case DDSP_PLACE_ROAD_X_DIR: + case DDSP_PLACE_ROAD_Y_DIR: + case DDSP_PLACE_AUTOROAD: + /* Flag description: + * Use the first three bits (0x07) if dir == Y + * else use the last 2 bits (X dir has + * not the 3rd bit set) */ + _place_road_flag = (RoadFlags)((_place_road_flag & RF_DIR_Y) ? (_place_road_flag & 0x07) : (_place_road_flag >> 3)); + + DoCommandP(end_tile, start_tile, _place_road_flag | (_cur_roadtype << 3) | (_one_way_button_clicked << 5), CcPlaySound1D, + (_ctrl_pressed || _remove_button_clicked) ? + CMD_REMOVE_LONG_ROAD | CMD_NO_WATER | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_road) : + CMD_BUILD_LONG_ROAD | CMD_NO_WATER | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_road)); break; } - - VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method); - return; - - case WE_PLACE_MOUSEUP: - if (e->we.place.pt.x != -1) { - TileIndex start_tile = e->we.place.starttile; - TileIndex end_tile = e->we.place.tile; - - switch (e->we.place.select_proc) { - default: NOT_REACHED(); - case DDSP_BUILD_BRIDGE: - ResetObjectToPlace(); - ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_ROAD, RoadTypeToRoadTypes(_cur_roadtype)); - break; - - case DDSP_DEMOLISH_AREA: - GUIPlaceProcDragXY(e); - break; + } + } - case DDSP_PLACE_ROAD_X_DIR: - case DDSP_PLACE_ROAD_Y_DIR: - case DDSP_PLACE_AUTOROAD: - /* Flag description: - * Use the first three bits (0x07) if dir == Y - * else use the last 2 bits (X dir has - * not the 3rd bit set) */ - _place_road_flag = (RoadFlags)((_place_road_flag & RF_DIR_Y) ? (_place_road_flag & 0x07) : (_place_road_flag >> 3)); - - DoCommandP(end_tile, start_tile, _place_road_flag | (_cur_roadtype << 3) | (_one_way_button_clicked << 5), CcPlaySound1D, - (_ctrl_pressed || _remove_button_clicked) ? - CMD_REMOVE_LONG_ROAD | CMD_NO_WATER | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_road) : - CMD_BUILD_LONG_ROAD | CMD_NO_WATER | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_road)); - break; - } - } - break; + virtual void OnPlacePresize(Point pt, TileIndex tile) + { + DoCommand(tile, 0x200 | RoadTypeToRoadTypes(_cur_roadtype), 0, DC_AUTO, CMD_BUILD_TUNNEL); + VpSetPresizeRange(tile, _build_tunnel_endtile == 0 ? tile : _build_tunnel_endtile); + } - case WE_PLACE_PRESIZE: { - TileIndex tile = e->we.place.tile; - - DoCommand(tile, 0x200 | RoadTypeToRoadTypes(_cur_roadtype), 0, DC_AUTO, CMD_BUILD_TUNNEL); - VpSetPresizeRange(tile, _build_tunnel_endtile == 0 ? tile : _build_tunnel_endtile); - } break; - - case WE_DESTROY: - if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0); - break; - - case WE_CTRL_CHANGED: - if (RoadToolbar_CtrlChanged(w)) e->we.ctrl.cont = false; - break; - - default: - break; + virtual EventState OnCTRLStateChange() + { + if (RoadToolbar_CtrlChanged(this)) return ES_HANDLED; + return ES_NOT_HANDLED; } -} +}; /** Widget definition of the build road toolbar */ static const Widget _build_road_widgets[] = { @@ -641,7 +643,6 @@ WC_BUILD_TOOLBAR, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _build_road_widgets, - BuildRoadToolbWndProc }; /** Widget definition of the build tram toolbar */ @@ -670,7 +671,6 @@ WC_BUILD_TOOLBAR, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _build_tramway_widgets, - BuildRoadToolbWndProc }; void ShowBuildRoadToolbar(RoadType roadtype) @@ -679,8 +679,7 @@ _cur_roadtype = roadtype; DeleteWindowByClass(WC_BUILD_TOOLBAR); - Window *w = AllocateWindowDescFront(roadtype == ROADTYPE_ROAD ? &_build_road_desc : &_build_tramway_desc, TRANSPORT_ROAD); - if (_patches.link_terraform_toolbar) ShowTerraformToolbar(w); + AllocateWindowDescFront(roadtype == ROADTYPE_ROAD ? &_build_road_desc : &_build_tramway_desc, TRANSPORT_ROAD); } /** Widget definition of the build road toolbar in the scenario editor */ @@ -708,72 +707,67 @@ WC_SCEN_BUILD_ROAD, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _build_road_scen_widgets, - BuildRoadToolbWndProc }; void ShowBuildRoadScenToolbar() { _cur_roadtype = ROADTYPE_ROAD; - AllocateWindowDescFront(&_build_road_scen_desc, 0); + AllocateWindowDescFront(&_build_road_scen_desc, 0); } -/** Enum referring to the widgets of the build road depot window */ -enum BuildRoadDepotWidgets { - BRDW_CLOSEBOX = 0, - BRDW_CAPTION, - BRDW_BACKGROUND, - BRDW_DEPOT_NE, - BRDW_DEPOT_SE, - BRDW_DEPOT_SW, - BRDW_DEPOT_NW, -}; - -static void BuildRoadDepotWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - w->LowerWidget(_road_depot_orientation + BRDW_DEPOT_NE); - break; - - case WE_PAINT: - DrawWindowWidgets(w); - - DrawRoadDepotSprite(70, 17, DIAGDIR_NE, _cur_roadtype); - DrawRoadDepotSprite(70, 69, DIAGDIR_SE, _cur_roadtype); - DrawRoadDepotSprite( 2, 69, DIAGDIR_SW, _cur_roadtype); - DrawRoadDepotSprite( 2, 17, DIAGDIR_NW, _cur_roadtype); - break; +struct BuildRoadDepotWindow : public PickerWindowBase { +private: + /** Enum referring to the widgets of the build road depot window */ + enum BuildRoadDepotWidgets { + BRDW_CLOSEBOX = 0, + BRDW_CAPTION, + BRDW_BACKGROUND, + BRDW_DEPOT_NE, + BRDW_DEPOT_SE, + BRDW_DEPOT_SW, + BRDW_DEPOT_NW, + }; - case WE_CLICK: - switch (e->we.click.widget) { - case BRDW_DEPOT_NW: - case BRDW_DEPOT_NE: - case BRDW_DEPOT_SW: - case BRDW_DEPOT_SE: - w->RaiseWidget(_road_depot_orientation + BRDW_DEPOT_NE); - _road_depot_orientation = (DiagDirection)(e->we.click.widget - BRDW_DEPOT_NE); - w->LowerWidget(_road_depot_orientation + BRDW_DEPOT_NE); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; +public: + BuildRoadDepotWindow(const WindowDesc *desc) : PickerWindowBase(desc) + { + this->LowerWidget(_road_depot_orientation + BRDW_DEPOT_NE); + if ( _cur_roadtype == ROADTYPE_TRAM) { + this->widget[BRDW_CAPTION].data = STR_TRAM_DEPOT_ORIENTATION; + for (int i = BRDW_DEPOT_NE; i <= BRDW_DEPOT_NW; i++) this->widget[i].tooltips = STR_SELECT_TRAM_VEHICLE_DEPOT; + } + this->FindWindowPlacementAndResize(desc); + } - default: - break; - } - break; + virtual void OnPaint() + { + this->DrawWidgets(); - case WE_TICK: - if (WP(w, def_d).close) delete w; - break; + DrawRoadDepotSprite(70, 17, DIAGDIR_NE, _cur_roadtype); + DrawRoadDepotSprite(70, 69, DIAGDIR_SE, _cur_roadtype); + DrawRoadDepotSprite( 2, 69, DIAGDIR_SW, _cur_roadtype); + DrawRoadDepotSprite( 2, 17, DIAGDIR_NW, _cur_roadtype); + } - case WE_DESTROY: - if (!WP(w, def_d).close) ResetObjectToPlace(); - break; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case BRDW_DEPOT_NW: + case BRDW_DEPOT_NE: + case BRDW_DEPOT_SW: + case BRDW_DEPOT_SE: + this->RaiseWidget(_road_depot_orientation + BRDW_DEPOT_NE); + _road_depot_orientation = (DiagDirection)(widget - BRDW_DEPOT_NE); + this->LowerWidget(_road_depot_orientation + BRDW_DEPOT_NE); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; - default: - break; + default: + break; + } } -} +}; /** Widget definition of the build road depot window */ static const Widget _build_road_depot_widgets[] = { @@ -787,155 +781,128 @@ { WIDGETS_END}, }; -/** Widget definition of the build tram depot window */ -static const Widget _build_tram_depot_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // BRDW_CLOSEBOX -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 139, 0, 13, STR_TRAM_DEPOT_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS}, // BRDW_CAPTION -{ WWT_PANEL, RESIZE_NONE, 7, 0, 139, 14, 121, 0x0, STR_NULL}, // BRDW_BACKGROUND -{ WWT_PANEL, RESIZE_NONE, 14, 71, 136, 17, 66, 0x0, STR_SELECT_TRAM_VEHICLE_DEPOT}, // BRDW_DEPOT_NE -{ WWT_PANEL, RESIZE_NONE, 14, 71, 136, 69, 118, 0x0, STR_SELECT_TRAM_VEHICLE_DEPOT}, // BRDW_DEPOT_SE -{ WWT_PANEL, RESIZE_NONE, 14, 3, 68, 69, 118, 0x0, STR_SELECT_TRAM_VEHICLE_DEPOT}, // BRDW_DEPOT_SW -{ WWT_PANEL, RESIZE_NONE, 14, 3, 68, 17, 66, 0x0, STR_SELECT_TRAM_VEHICLE_DEPOT}, // BRDW_DEPOT_NW -{ WIDGETS_END}, -}; - static const WindowDesc _build_road_depot_desc = { WDP_AUTO, WDP_AUTO, 140, 122, 140, 122, WC_BUILD_DEPOT, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _build_road_depot_widgets, - BuildRoadDepotWndProc -}; - -static const WindowDesc _build_tram_depot_desc = { - WDP_AUTO, WDP_AUTO, 140, 122, 140, 122, - WC_BUILD_DEPOT, WC_BUILD_TOOLBAR, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, - _build_tram_depot_widgets, - BuildRoadDepotWndProc }; static void ShowRoadDepotPicker() { - new Window(_cur_roadtype == ROADTYPE_ROAD ? &_build_road_depot_desc : &_build_tram_depot_desc); + new BuildRoadDepotWindow(&_build_road_depot_desc); } -/** Enum referring to the widgets of the build road station window */ -enum BuildRoadStationWidgets { - BRSW_CLOSEBOX = 0, - BRSW_CAPTION, - BRSW_BACKGROUND, - BRSW_STATION_NE, - BRSW_STATION_SE, - BRSW_STATION_SW, - BRSW_STATION_NW, - BRSW_STATION_X, - BRSW_STATION_Y, - BRSW_LT_OFF, - BRSW_LT_ON, - BRSW_INFO, -}; - -static void RoadStationPickerWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - /* Trams don't have non-drivethrough stations */ - if (_cur_roadtype == ROADTYPE_TRAM && _road_station_picker_orientation < DIAGDIR_END) { - _road_station_picker_orientation = DIAGDIR_END; - } - w->SetWidgetsDisabledState(_cur_roadtype == ROADTYPE_TRAM, - BRSW_STATION_NE, - BRSW_STATION_SE, - BRSW_STATION_SW, - BRSW_STATION_NW, - WIDGET_LIST_END); - - w->LowerWidget(_road_station_picker_orientation + BRSW_STATION_NE); - w->LowerWidget(_station_show_coverage + BRSW_LT_OFF); - break; - - case WE_PAINT: { - if (WP(w, def_d).close) return; - - DrawWindowWidgets(w); - - if (_station_show_coverage) { - int rad = _patches.modified_catchment ? CA_TRUCK /* = CA_BUS */ : CA_UNMODIFIED; - SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad); - } else { - SetTileSelectSize(1, 1); - } - - StationType st = (w->window_class == WC_BUS_STATION) ? STATION_BUS : STATION_TRUCK; - - StationPickerDrawSprite(103, 35, st, INVALID_RAILTYPE, ROADTYPE_ROAD, 0); - StationPickerDrawSprite(103, 85, st, INVALID_RAILTYPE, ROADTYPE_ROAD, 1); - StationPickerDrawSprite( 35, 85, st, INVALID_RAILTYPE, ROADTYPE_ROAD, 2); - StationPickerDrawSprite( 35, 35, st, INVALID_RAILTYPE, ROADTYPE_ROAD, 3); - - StationPickerDrawSprite(171, 35, st, INVALID_RAILTYPE, _cur_roadtype, 4); - StationPickerDrawSprite(171, 85, st, INVALID_RAILTYPE, _cur_roadtype, 5); +struct BuildRoadStationWindow : public PickerWindowBase { +private: + /** Enum referring to the widgets of the build road station window */ + enum BuildRoadStationWidgets { + BRSW_CLOSEBOX = 0, + BRSW_CAPTION, + BRSW_BACKGROUND, + BRSW_STATION_NE, + BRSW_STATION_SE, + BRSW_STATION_SW, + BRSW_STATION_NW, + BRSW_STATION_X, + BRSW_STATION_Y, + BRSW_LT_OFF, + BRSW_LT_ON, + BRSW_INFO, + }; - int text_end = DrawStationCoverageAreaText(2, 146, - (w->window_class == WC_BUS_STATION) ? SCT_PASSENGERS_ONLY : SCT_NON_PASSENGERS_ONLY, - 3, false); - text_end = DrawStationCoverageAreaText(2, text_end + 4, - (w->window_class == WC_BUS_STATION) ? SCT_PASSENGERS_ONLY : SCT_NON_PASSENGERS_ONLY, - 3, true) + 4; - if (text_end > w->widget[BRSW_BACKGROUND].bottom) { - w->SetDirty(); - ResizeWindowForWidget(w, BRSW_BACKGROUND, 0, text_end - w->widget[BRSW_BACKGROUND].bottom); - w->SetDirty(); - } - } break; - - case WE_CLICK: - switch (e->we.click.widget) { - case BRSW_STATION_NE: - case BRSW_STATION_SE: - case BRSW_STATION_SW: - case BRSW_STATION_NW: - case BRSW_STATION_X: - case BRSW_STATION_Y: - w->RaiseWidget(_road_station_picker_orientation + BRSW_STATION_NE); - _road_station_picker_orientation = (DiagDirection)(e->we.click.widget - BRSW_STATION_NE); - w->LowerWidget(_road_station_picker_orientation + BRSW_STATION_NE); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; +public: + BuildRoadStationWindow(const WindowDesc *desc, RoadStopType rs) : PickerWindowBase(desc) + { + /* Trams don't have non-drivethrough stations */ + if (_cur_roadtype == ROADTYPE_TRAM && _road_station_picker_orientation < DIAGDIR_END) { + _road_station_picker_orientation = DIAGDIR_END; + } + this->SetWidgetsDisabledState(_cur_roadtype == ROADTYPE_TRAM, + BRSW_STATION_NE, + BRSW_STATION_SE, + BRSW_STATION_SW, + BRSW_STATION_NW, + WIDGET_LIST_END); - case BRSW_LT_OFF: - case BRSW_LT_ON: - w->RaiseWidget(_station_show_coverage + BRSW_LT_OFF); - _station_show_coverage = (e->we.click.widget != BRSW_LT_OFF); - w->LowerWidget(_station_show_coverage + BRSW_LT_OFF); - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - break; - - default: - break; - } - break; + this->window_class = (rs == ROADSTOP_BUS) ? WC_BUS_STATION : WC_TRUCK_STATION; + this->widget[BRSW_CAPTION].data = _road_type_infos[_cur_roadtype].picker_title[rs]; + for (uint i = BRSW_STATION_NE; i < BRSW_LT_OFF; i++) this->widget[i].tooltips = _road_type_infos[_cur_roadtype].picker_tooltip[rs]; - case WE_TICK: - if (WP(w, def_d).close) { - delete w; - return; - } + this->LowerWidget(_road_station_picker_orientation + BRSW_STATION_NE); + this->LowerWidget(_station_show_coverage + BRSW_LT_OFF); + this->FindWindowPlacementAndResize(desc); + } - CheckRedrawStationCoverage(w); - break; + virtual void OnPaint() + { + this->DrawWidgets(); - case WE_DESTROY: - if (!WP(w, def_d).close) ResetObjectToPlace(); - break; + if (_station_show_coverage) { + int rad = _patches.modified_catchment ? CA_TRUCK /* = CA_BUS */ : CA_UNMODIFIED; + SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad); + } else { + SetTileSelectSize(1, 1); + } - default: - break; + StationType st = (this->window_class == WC_BUS_STATION) ? STATION_BUS : STATION_TRUCK; + + StationPickerDrawSprite(103, 35, st, INVALID_RAILTYPE, ROADTYPE_ROAD, 0); + StationPickerDrawSprite(103, 85, st, INVALID_RAILTYPE, ROADTYPE_ROAD, 1); + StationPickerDrawSprite( 35, 85, st, INVALID_RAILTYPE, ROADTYPE_ROAD, 2); + StationPickerDrawSprite( 35, 35, st, INVALID_RAILTYPE, ROADTYPE_ROAD, 3); + + StationPickerDrawSprite(171, 35, st, INVALID_RAILTYPE, _cur_roadtype, 4); + StationPickerDrawSprite(171, 85, st, INVALID_RAILTYPE, _cur_roadtype, 5); + + int text_end = DrawStationCoverageAreaText(2, 146, + (this->window_class == WC_BUS_STATION) ? SCT_PASSENGERS_ONLY : SCT_NON_PASSENGERS_ONLY, + 3, false); + text_end = DrawStationCoverageAreaText(2, text_end + 4, + (this->window_class == WC_BUS_STATION) ? SCT_PASSENGERS_ONLY : SCT_NON_PASSENGERS_ONLY, + 3, true) + 4; + if (text_end > this->widget[BRSW_BACKGROUND].bottom) { + this->SetDirty(); + ResizeWindowForWidget(this, BRSW_BACKGROUND, 0, text_end - this->widget[BRSW_BACKGROUND].bottom); + this->SetDirty(); + } } -} + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case BRSW_STATION_NE: + case BRSW_STATION_SE: + case BRSW_STATION_SW: + case BRSW_STATION_NW: + case BRSW_STATION_X: + case BRSW_STATION_Y: + this->RaiseWidget(_road_station_picker_orientation + BRSW_STATION_NE); + _road_station_picker_orientation = (DiagDirection)(widget - BRSW_STATION_NE); + this->LowerWidget(_road_station_picker_orientation + BRSW_STATION_NE); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; + + case BRSW_LT_OFF: + case BRSW_LT_ON: + this->RaiseWidget(_station_show_coverage + BRSW_LT_OFF); + _station_show_coverage = (widget != BRSW_LT_OFF); + this->LowerWidget(_station_show_coverage + BRSW_LT_OFF); + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + break; + + default: + break; + } + } + + virtual void OnTick() + { + CheckRedrawStationCoverage(this); + } +}; /** Widget definition of the build raod station window */ static const Widget _rv_station_picker_widgets[] = { @@ -961,17 +928,11 @@ WC_BUS_STATION, WC_BUILD_TOOLBAR, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _rv_station_picker_widgets, - RoadStationPickerWndProc }; static void ShowRVStationPicker(RoadStopType rs) { - Window *w = new Window(&_rv_station_picker_desc); - if (w == NULL) return; - - w->window_class = (rs == ROADSTOP_BUS) ? WC_BUS_STATION : WC_TRUCK_STATION; - w->widget[BRSW_CAPTION].data = _road_type_infos[_cur_roadtype].picker_title[rs]; - for (uint i = BRSW_STATION_NE; i < BRSW_LT_OFF; i++) w->widget[i].tooltips = _road_type_infos[_cur_roadtype].picker_tooltip[rs]; + new BuildRoadStationWindow(&_rv_station_picker_desc, rs); } void InitializeRoadGui() diff -r 6c4314786d68 -r 8cbdb511a674 src/roadveh_cmd.cpp --- a/src/roadveh_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/roadveh_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -281,7 +281,7 @@ VehiclePositionChanged(v); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_ROADVEH_LIST, 0); InvalidateWindow(WC_COMPANY, v->owner); if (IsLocalPlayer()) InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Road window @@ -382,7 +382,7 @@ if (flags & DC_EXEC) { // Invalidate depot InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_ROADVEH_LIST, 0); InvalidateWindow(WC_COMPANY, v->owner); DeleteWindowById(WC_VEHICLE_VIEW, v->index); DeleteDepotHighlightOfVehicle(v); @@ -575,7 +575,7 @@ DeleteWindowById(WC_VEHICLE_VIEW, v->index); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_ROADVEH_LIST, 0); InvalidateWindow(WC_COMPANY, v->owner); if (IsTileType(v->tile, MP_STATION)) ClearCrashedStation(v); @@ -667,7 +667,7 @@ AddNewsItem( (pass == 1) ? STR_9031_ROAD_VEHICLE_CRASH_DRIVER : STR_9032_ROAD_VEHICLE_CRASH_DIE, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, DNC_NONE, + NS_ACCIDENT_VEHICLE, v->index, 0 ); @@ -833,7 +833,7 @@ SetDParam(0, st->index); AddNewsItem( v->u.road.roadtype == ROADTYPE_ROAD ? STR_902F_CITIZENS_CELEBRATE_FIRST : STR_CITIZENS_CELEBRATE_FIRST_PASSENGER_TRAM, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, (v->owner == _local_player) ? NT_ARRIVAL_PLAYER : NT_ARRIVAL_OTHER, DNC_NONE, + (v->owner == _local_player) ? NS_ARRIVAL_PLAYER : NS_ARRIVAL_OTHER, v->index, 0); } @@ -844,7 +844,7 @@ SetDParam(0, st->index); AddNewsItem( v->u.road.roadtype == ROADTYPE_ROAD ? STR_9030_CITIZENS_CELEBRATE_FIRST : STR_CITIZENS_CELEBRATE_FIRST_CARGO_TRAM, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, (v->owner == _local_player) ? NT_ARRIVAL_PLAYER : NT_ARRIVAL_OTHER, DNC_NONE, + (v->owner == _local_player) ? NS_ARRIVAL_PLAYER : NS_ARRIVAL_OTHER, v->index, 0 ); @@ -973,7 +973,7 @@ /* Check if vehicle is in a road stop, depot, tunnel or bridge or not on a straight road */ if (v->u.road.state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)(v->u.road.state & RVSB_TRACKDIR_MASK))) return; - od.trackdir = DiagdirToDiagTrackdir(DirToDiagDir(v->direction)); + od.trackdir = DiagDirToDiagTrackdir(DirToDiagDir(v->direction)); /* Are the current and the next tile suitable for overtaking? * - Does the track continue along od.trackdir @@ -1138,7 +1138,7 @@ NPFFindStationOrTileData fstd; NPFFillWithOrderData(&fstd, v); - Trackdir trackdir = DiagdirToDiagTrackdir(enterdir); + Trackdir trackdir = DiagDirToDiagTrackdir(enterdir); //debug("Finding path. Enterdir: %d, Trackdir: %d", enterdir, trackdir); NPFFoundTargetData ftd = PerfNPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_ROAD, v->u.road.compatible_roadtypes, v->owner, INVALID_RAILTYPES); @@ -1328,7 +1328,7 @@ } if (diag_dir == INVALID_DIAGDIR) return INVALID_TRACKDIR; - dir = DiagdirToDiagTrackdir(diag_dir); + dir = DiagDirToDiagTrackdir(diag_dir); } else { if (already_reversed && prev->tile != tile) { /* @@ -2122,7 +2122,7 @@ v->cargo_subtype = new_subtype; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_ROADVEH_LIST, 0); } if (only_this) break; diff -r 6c4314786d68 -r 8cbdb511a674 src/saveload.cpp --- a/src/saveload.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/saveload.cpp Mon May 19 15:13:58 2008 +0000 @@ -30,6 +30,7 @@ #include "core/endian_func.hpp" #include "vehicle_base.h" #include "autoreplace_base.h" +#include "statusbar_gui.h" #include #include "table/strings.h" @@ -1493,7 +1494,7 @@ _fast_forward = 0; if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE); - InvalidateWindowData(WC_STATUS_BAR, 0, true); + InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START); _ts.saveinprogress = true; } @@ -1504,7 +1505,7 @@ _fast_forward = _ts.ff_state; if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE); - InvalidateWindowData(WC_STATUS_BAR, 0, false); + InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH); _ts.saveinprogress = false; } @@ -1533,7 +1534,7 @@ SaveFileDone(); } -static ThreadObject *save_thread; +static ThreadObject *_save_thread; /** We have written the whole game into memory, _Savegame_pool, now find * and appropiate compressor and start writing to file. @@ -1603,10 +1604,10 @@ void WaitTillSaved() { - if (save_thread == NULL) return; + if (_save_thread == NULL) return; - save_thread->Join(); - save_thread = NULL; + _save_thread->Join(); + _save_thread = NULL; } /** @@ -1678,7 +1679,7 @@ SaveFileStart(); if (_network_server || - (save_thread = ThreadObject::New(&SaveFileToDiskThread, NULL)) == NULL) { + (_save_thread = ThreadObject::New(&SaveFileToDiskThread, NULL)) == NULL) { if (!_network_server) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode..."); SaveOrLoadResult result = SaveFileToDisk(false); diff -r 6c4314786d68 -r 8cbdb511a674 src/settings.cpp --- a/src/settings.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/settings.cpp Mon May 19 15:13:58 2008 +0000 @@ -1141,6 +1141,12 @@ return 0; } +static int32 InvalidateBuildIndustryWindow(int32 p1) +{ + InvalidateWindowData(WC_BUILD_INDUSTRY, 0); + return 0; +} + static int32 CloseSignalGUI(int32 p1) { if (p1 == 0) { @@ -1482,7 +1488,7 @@ /***************************************************************************/ /* Economy section of the GUI-configure patches window */ SDT_BOOL(Patches, inflation, 0, 0, true, STR_CONFIG_PATCHES_INFLATION, NULL), - SDT_VAR(Patches, raw_industry_construction,SLE_UINT8,0,MS,0,0, 2, 0, STR_CONFIG_PATCHES_RAW_INDUSTRY_CONSTRUCTION_METHOD, NULL), + SDT_VAR(Patches, raw_industry_construction,SLE_UINT8,0,MS,0,0, 2, 0, STR_CONFIG_PATCHES_RAW_INDUSTRY_CONSTRUCTION_METHOD, InvalidateBuildIndustryWindow), SDT_BOOL(Patches, multiple_industry_per_town, 0, 0, false, STR_CONFIG_PATCHES_MULTIPINDTOWN, NULL), SDT_BOOL(Patches, same_industry_close, 0, 0, false, STR_CONFIG_PATCHES_SAMEINDCLOSE, NULL), SDT_BOOL(Patches, bribe, 0, 0, true, STR_CONFIG_PATCHES_BRIBE, NULL), diff -r 6c4314786d68 -r 8cbdb511a674 src/settings_gui.cpp --- a/src/settings_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/settings_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -20,7 +20,6 @@ #include "strings_func.h" #include "functions.h" #include "window_func.h" -#include "vehicle_base.h" #include "core/alloc_func.hpp" #include "string_func.h" #include "gfx_func.h" @@ -102,17 +101,6 @@ return i; } -static inline bool RoadVehiclesAreBuilt() -{ - const Vehicle* v; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_ROAD) return true; - } - return false; -} - - enum GameOptionsWidgets { GAMEOPT_CURRENCY_BTN = 4, GAMEOPT_DISTANCE_BTN = 6, @@ -151,199 +139,178 @@ ShowDropDownList(w, list, sel, GAMEOPT_TOWNNAME_BTN); } -/** - * Update/redraw the languages dropdown - * @param w the window the dropdown belongs to - */ -static void ShowLangDropdown(Window *w) -{ - typedef std::map LangList; - - /* Sort language names */ - LangList langs; - for (int i = 0; i < _dynlang.num; i++) langs[SPECSTR_LANGUAGE_START + i] = i; - - DropDownList *list = new DropDownList(); - for (LangList::iterator it = langs.begin(); it != langs.end(); it++) { - list->push_back(new DropDownListStringItem((*it).first, (*it).second, false)); - } - - ShowDropDownList(w, list, _dynlang.curr, GAMEOPT_LANG_BTN); -} - static void ShowCustCurrency(); -static void GameOptionsWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - int i; - StringID str = STR_02BE_DEFAULT; - - w->SetWidgetDisabledState(GAMEOPT_VEHICLENAME_SAVE, !(_vehicle_design_names & 1)); - if (!w->IsWidgetDisabled(GAMEOPT_VEHICLENAME_SAVE)) str = STR_02BF_CUSTOM; - SetDParam(0, str); - SetDParam(1, _currency_specs[_opt_ptr->currency].name); - SetDParam(2, STR_UNITS_IMPERIAL + _opt_ptr->units); - SetDParam(3, STR_02E9_DRIVE_ON_LEFT + _opt_ptr->road_side); - SetDParam(4, TownName(_opt_ptr->town_name)); - SetDParam(5, _autosave_dropdown[_opt_ptr->autosave]); - SetDParam(6, SPECSTR_LANGUAGE_START + _dynlang.curr); - i = GetCurRes(); - SetDParam(7, i == _num_resolutions ? STR_RES_OTHER : SPECSTR_RESOLUTION_START + i); - SetDParam(8, SPECSTR_SCREENSHOT_START + _cur_screenshot_format); - w->SetWidgetLoweredState(GAMEOPT_FULLSCREEN, _fullscreen); - - DrawWindowWidgets(w); - DrawString(20, 175, STR_OPTIONS_FULLSCREEN, TC_FROMSTRING); // fullscreen - } break; - - case WE_CLICK: - switch (e->we.click.widget) { - case GAMEOPT_CURRENCY_BTN: /* Setup currencies dropdown */ - ShowDropDownMenu(w, BuildCurrencyDropdown(), _opt_ptr->currency, GAMEOPT_CURRENCY_BTN, _game_mode == GM_MENU ? 0 : ~GetMaskOfAllowedCurrencies(), 0); - break; - - case GAMEOPT_DISTANCE_BTN: /* Setup distance unit dropdown */ - ShowDropDownMenu(w, _units_dropdown, _opt_ptr->units, GAMEOPT_DISTANCE_BTN, 0, 0); - break; - - case GAMEOPT_ROADSIDE_BTN: { /* Setup road-side dropdown */ - int i = 0; - - /* You can only change the drive side if you are in the menu or ingame with - * no vehicles present. In a networking game only the server can change it */ - if ((_game_mode != GM_MENU && RoadVehiclesAreBuilt()) || (_networking && !_network_server)) - i = (-1) ^ (1 << _opt_ptr->road_side); // disable the other value - - ShowDropDownMenu(w, _driveside_dropdown, _opt_ptr->road_side, GAMEOPT_ROADSIDE_BTN, i, 0); - } break; - - case GAMEOPT_TOWNNAME_BTN: /* Setup townname dropdown */ - ShowTownnameDropdown(w, _opt_ptr->town_name); - break; - - case GAMEOPT_AUTOSAVE_BTN: /* Setup autosave dropdown */ - ShowDropDownMenu(w, _autosave_dropdown, _opt_ptr->autosave, GAMEOPT_AUTOSAVE_BTN, 0, 0); - break; - - case GAMEOPT_VEHICLENAME_BTN: /* Setup customized vehicle-names dropdown */ - ShowDropDownMenu(w, _designnames_dropdown, (_vehicle_design_names & 1) ? 1 : 0, GAMEOPT_VEHICLENAME_BTN, (_vehicle_design_names & 2) ? 0 : 2, 0); - break; - - case GAMEOPT_VEHICLENAME_SAVE: /* Save customized vehicle-names to disk */ - break; // not implemented - - case GAMEOPT_LANG_BTN: /* Setup interface language dropdown */ - ShowLangDropdown(w); - break; - - case GAMEOPT_RESOLUTION_BTN: /* Setup resolution dropdown */ - ShowDropDownMenu(w, BuildDynamicDropdown(SPECSTR_RESOLUTION_START, _num_resolutions), GetCurRes(), GAMEOPT_RESOLUTION_BTN, 0, 0); - break; - - case GAMEOPT_FULLSCREEN: /* Click fullscreen on/off */ - /* try to toggle full-screen on/off */ - if (!ToggleFullScreen(!_fullscreen)) { - ShowErrorMessage(INVALID_STRING_ID, STR_FULLSCREEN_FAILED, 0, 0); - } - w->SetWidgetLoweredState(GAMEOPT_FULLSCREEN, _fullscreen); - w->SetDirty(); - break; - - case GAMEOPT_SCREENSHOT_BTN: /* Setup screenshot format dropdown */ - ShowDropDownMenu(w, BuildDynamicDropdown(SPECSTR_SCREENSHOT_START, _num_screenshot_formats), _cur_screenshot_format, GAMEOPT_SCREENSHOT_BTN, 0, 0); - break; - } - break; - - case WE_DROPDOWN_SELECT: - switch (e->we.dropdown.button) { - case GAMEOPT_VEHICLENAME_BTN: /* Vehicle design names */ - if (e->we.dropdown.index == 0) { - DeleteCustomEngineNames(); - MarkWholeScreenDirty(); - } else if (!(_vehicle_design_names & 1)) { - LoadCustomEngineNames(); - MarkWholeScreenDirty(); - } - break; - - case GAMEOPT_CURRENCY_BTN: /* Currency */ - if (e->we.dropdown.index == CUSTOM_CURRENCY_ID) ShowCustCurrency(); - _opt_ptr->currency = e->we.dropdown.index; - MarkWholeScreenDirty(); - break; - - case GAMEOPT_DISTANCE_BTN: /* Measuring units */ - _opt_ptr->units = e->we.dropdown.index; - MarkWholeScreenDirty(); - break; - - case GAMEOPT_ROADSIDE_BTN: /* Road side */ - if (_opt_ptr->road_side != e->we.dropdown.index) { // only change if setting changed - DoCommandP(0, e->we.dropdown.index, 0, NULL, CMD_SET_ROAD_DRIVE_SIDE | CMD_MSG(STR_00B4_CAN_T_DO_THIS)); - MarkWholeScreenDirty(); - } - break; - - case GAMEOPT_TOWNNAME_BTN: /* Town names */ - if (_game_mode == GM_MENU) { - _opt_ptr->town_name = e->we.dropdown.index; - InvalidateWindow(WC_GAME_OPTIONS, 0); - } - break; - - case GAMEOPT_AUTOSAVE_BTN: /* Autosave options */ - _opt.autosave = _opt_newgame.autosave = e->we.dropdown.index; - w->SetDirty(); - break; - - case GAMEOPT_LANG_BTN: /* Change interface language */ - ReadLanguagePack(e->we.dropdown.index); - CheckForMissingGlyphsInLoadedLanguagePack(); - UpdateAllStationVirtCoord(); - UpdateAllWaypointSigns(); - MarkWholeScreenDirty(); - break; - - case GAMEOPT_RESOLUTION_BTN: /* Change resolution */ - if (e->we.dropdown.index < _num_resolutions && ChangeResInGame(_resolutions[e->we.dropdown.index][0], _resolutions[e->we.dropdown.index][1])) - w->SetDirty(); - break; - - case GAMEOPT_SCREENSHOT_BTN: /* Change screenshot format */ - SetScreenshotFormat(e->we.dropdown.index); - w->SetDirty(); - break; - } - break; - - case WE_DESTROY: - DeleteWindowById(WC_CUSTOM_CURRENCY, 0); - break; +struct GameOptionsWindow : Window { + GameOptionsWindow(const WindowDesc *desc) : Window(desc) + { + this->FindWindowPlacementAndResize(desc); } -} + ~GameOptionsWindow() + { + DeleteWindowById(WC_CUSTOM_CURRENCY, 0); + } -/** Change the side of the road vehicles drive on (server only). - * @param tile unused - * @param flags operation to perform - * @param p1 the side of the road; 0 = left side and 1 = right side - * @param p2 unused - */ -CommandCost CmdSetRoadDriveSide(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - /* Check boundaries and you can only change this if NO vehicles have been built yet, - * except in the intro-menu where of course it's always possible to do so. */ - if (p1 > 1 || (_game_mode != GM_MENU && RoadVehiclesAreBuilt())) return CMD_ERROR; + virtual void OnPaint() + { + StringID str = STR_02BE_DEFAULT; - if (flags & DC_EXEC) { - _opt_ptr->road_side = p1; - InvalidateWindow(WC_GAME_OPTIONS, 0); + this->SetWidgetDisabledState(GAMEOPT_VEHICLENAME_SAVE, !(_vehicle_design_names & 1)); + if (!this->IsWidgetDisabled(GAMEOPT_VEHICLENAME_SAVE)) str = STR_02BF_CUSTOM; + SetDParam(0, str); + SetDParam(1, _currency_specs[_opt_ptr->currency].name); + SetDParam(2, STR_UNITS_IMPERIAL + _opt_ptr->units); + SetDParam(3, STR_02E9_DRIVE_ON_LEFT + _opt_ptr->road_side); + SetDParam(4, TownName(_opt_ptr->town_name)); + SetDParam(5, _autosave_dropdown[_opt_ptr->autosave]); + SetDParam(6, SPECSTR_LANGUAGE_START + _dynlang.curr); + int i = GetCurRes(); + SetDParam(7, i == _num_resolutions ? STR_RES_OTHER : SPECSTR_RESOLUTION_START + i); + SetDParam(8, SPECSTR_SCREENSHOT_START + _cur_screenshot_format); + this->SetWidgetLoweredState(GAMEOPT_FULLSCREEN, _fullscreen); + + this->DrawWidgets(); + DrawString(20, 175, STR_OPTIONS_FULLSCREEN, TC_FROMSTRING); // fullscreen } - return CommandCost(); -} + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case GAMEOPT_CURRENCY_BTN: // Setup currencies dropdown + ShowDropDownMenu(this, BuildCurrencyDropdown(), _opt_ptr->currency, GAMEOPT_CURRENCY_BTN, _game_mode == GM_MENU ? 0 : ~GetMaskOfAllowedCurrencies(), 0); + break; + + case GAMEOPT_DISTANCE_BTN: // Setup distance unit dropdown + ShowDropDownMenu(this, _units_dropdown, _opt_ptr->units, GAMEOPT_DISTANCE_BTN, 0, 0); + break; + + case GAMEOPT_ROADSIDE_BTN: { // Setup road-side dropdown + int i = 0; + extern bool RoadVehiclesAreBuilt(); + + /* You can only change the drive side if you are in the menu or ingame with + * no vehicles present. In a networking game only the server can change it */ + if ((_game_mode != GM_MENU && RoadVehiclesAreBuilt()) || (_networking && !_network_server)) { + i = (-1) ^ (1 << _opt_ptr->road_side); // disable the other value + } + + ShowDropDownMenu(this, _driveside_dropdown, _opt_ptr->road_side, GAMEOPT_ROADSIDE_BTN, i, 0); + } break; + + case GAMEOPT_TOWNNAME_BTN: // Setup townname dropdown + ShowTownnameDropdown(this, _opt_ptr->town_name); + break; + + case GAMEOPT_AUTOSAVE_BTN: // Setup autosave dropdown + ShowDropDownMenu(this, _autosave_dropdown, _opt_ptr->autosave, GAMEOPT_AUTOSAVE_BTN, 0, 0); + break; + + case GAMEOPT_VEHICLENAME_BTN: // Setup customized vehicle-names dropdown + ShowDropDownMenu(this, _designnames_dropdown, (_vehicle_design_names & 1) ? 1 : 0, GAMEOPT_VEHICLENAME_BTN, (_vehicle_design_names & 2) ? 0 : 2, 0); + break; + + case GAMEOPT_VEHICLENAME_SAVE: // Save customized vehicle-names to disk + break; // not implemented + + case GAMEOPT_LANG_BTN: { // Setup interface language dropdown + typedef std::map LangList; + + /* Sort language names */ + LangList langs; + for (int i = 0; i < _dynlang.num; i++) langs[SPECSTR_LANGUAGE_START + i] = i; + + DropDownList *list = new DropDownList(); + for (LangList::iterator it = langs.begin(); it != langs.end(); it++) { + list->push_back(new DropDownListStringItem((*it).first, (*it).second, false)); + } + + ShowDropDownList(this, list, _dynlang.curr, GAMEOPT_LANG_BTN); + } break; + + case GAMEOPT_RESOLUTION_BTN: // Setup resolution dropdown + ShowDropDownMenu(this, BuildDynamicDropdown(SPECSTR_RESOLUTION_START, _num_resolutions), GetCurRes(), GAMEOPT_RESOLUTION_BTN, 0, 0); + break; + + case GAMEOPT_FULLSCREEN: // Click fullscreen on/off + /* try to toggle full-screen on/off */ + if (!ToggleFullScreen(!_fullscreen)) { + ShowErrorMessage(INVALID_STRING_ID, STR_FULLSCREEN_FAILED, 0, 0); + } + this->SetWidgetLoweredState(GAMEOPT_FULLSCREEN, _fullscreen); + this->SetDirty(); + break; + + case GAMEOPT_SCREENSHOT_BTN: // Setup screenshot format dropdown + ShowDropDownMenu(this, BuildDynamicDropdown(SPECSTR_SCREENSHOT_START, _num_screenshot_formats), _cur_screenshot_format, GAMEOPT_SCREENSHOT_BTN, 0, 0); + break; + } + } + + virtual void OnDropdownSelect(int widget, int index) + { + switch (widget) { + case GAMEOPT_VEHICLENAME_BTN: // Vehicle design names + if (index == 0) { + DeleteCustomEngineNames(); + MarkWholeScreenDirty(); + } else if (!(_vehicle_design_names & 1)) { + LoadCustomEngineNames(); + MarkWholeScreenDirty(); + } + break; + + case GAMEOPT_CURRENCY_BTN: /* Currency */ + if (index == CUSTOM_CURRENCY_ID) ShowCustCurrency(); + _opt_ptr->currency = index; + MarkWholeScreenDirty(); + break; + + case GAMEOPT_DISTANCE_BTN: // Measuring units + _opt_ptr->units = index; + MarkWholeScreenDirty(); + break; + + case GAMEOPT_ROADSIDE_BTN: // Road side + if (_opt_ptr->road_side != index) { // only change if setting changed + DoCommandP(0, index, 0, NULL, CMD_SET_ROAD_DRIVE_SIDE | CMD_MSG(STR_00B4_CAN_T_DO_THIS)); + MarkWholeScreenDirty(); + } + break; + + case GAMEOPT_TOWNNAME_BTN: // Town names + if (_game_mode == GM_MENU) { + _opt_ptr->town_name = index; + InvalidateWindow(WC_GAME_OPTIONS, 0); + } + break; + + case GAMEOPT_AUTOSAVE_BTN: // Autosave options + _opt.autosave = _opt_newgame.autosave = index; + this->SetDirty(); + break; + + case GAMEOPT_LANG_BTN: // Change interface language + ReadLanguagePack(index); + CheckForMissingGlyphsInLoadedLanguagePack(); + UpdateAllStationVirtCoord(); + UpdateAllWaypointSigns(); + MarkWholeScreenDirty(); + break; + + case GAMEOPT_RESOLUTION_BTN: // Change resolution + if (index < _num_resolutions && ChangeResInGame(_resolutions[index][0], _resolutions[index][1])) { + this->SetDirty(); + } + break; + + case GAMEOPT_SCREENSHOT_BTN: // Change screenshot format + SetScreenshotFormat(index); + this->SetDirty(); + break; + } + } +}; static const Widget _game_options_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -382,14 +349,13 @@ WC_GAME_OPTIONS, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _game_options_widgets, - GameOptionsWndProc }; void ShowGameOptions() { DeleteWindowById(WC_GAME_OPTIONS, 0); - new Window(&_game_options_desc); + new GameOptionsWindow(&_game_options_desc); } struct GameSettingData { @@ -478,194 +444,6 @@ extern void StartupEconomy(); -enum { - GAMEDIFF_WND_TOP_OFFSET = 45, - GAMEDIFF_WND_ROWSIZE = 9 -}; - -/* Temporary holding place of values in the difficulty window until 'Save' is clicked */ -static GameOptions _opt_mod_temp; -// 0x383E = (1 << 13) | (1 << 12) | (1 << 11) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) -#define DIFF_INGAME_DISABLED_BUTTONS 0x383E - -#define NO_SETTINGS_BUTTON 0xFF - -/** Carriage for the game settings window data */ -struct difficulty_d { - bool clicked_increase; - uint8 clicked_button; - uint8 timeout; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(difficulty_d)); - -/* Names of the game difficulty settings window */ -enum GameDifficultyWidgets { - GDW_CLOSEBOX = 0, - GDW_CAPTION, - GDW_UPPER_BG, - GDW_LVL_EASY, - GDW_LVL_MEDIUM, - GDW_LVL_HARD, - GDW_LVL_CUSTOM, - GDW_HIGHSCORE, - GDW_SETTING_BG, - GDW_LOWER_BG, - GDW_ACCEPT, - GDW_CANCEL, -}; - -static void GameDifficultyWndProc(Window *w, WindowEvent *e) -{ - difficulty_d *diffic_d = &WP(w, difficulty_d); - switch (e->event) { - case WE_CREATE: - diffic_d->clicked_increase = false; - diffic_d->clicked_button = NO_SETTINGS_BUTTON; - diffic_d->timeout = 0; - /* Hide the closebox to make sure that the user aborts or confirms his changes */ - w->HideWidget(GDW_CLOSEBOX); - w->widget[GDW_CAPTION].left = 0; - /* Setup disabled buttons when creating window - * disable all other difficulty buttons during gameplay except for 'custom' */ - w->SetWidgetsDisabledState(_game_mode == GM_NORMAL, - GDW_LVL_EASY, - GDW_LVL_MEDIUM, - GDW_LVL_HARD, - GDW_LVL_CUSTOM, - WIDGET_LIST_END); - w->SetWidgetDisabledState(GDW_HIGHSCORE, _game_mode == GM_EDITOR || _networking); // highscore chart in multiplayer - w->SetWidgetDisabledState(GDW_ACCEPT, _networking && !_network_server); // Save-button in multiplayer (and if client) - w->LowerWidget(GDW_LVL_EASY + _opt_mod_temp.diff_level); - break; - - case WE_PAINT: { - DrawWindowWidgets(w); - - /* XXX - Disabled buttons in normal gameplay or during muliplayer as non server. - * Bitshifted for each button to see if that bit is set. If it is set, the - * button is disabled */ - uint32 disabled = 0; - if (_networking && !_network_server) { - disabled = MAX_UVALUE(uint32); // Disable all - } else if (_game_mode == GM_NORMAL) { - disabled = DIFF_INGAME_DISABLED_BUTTONS; - } - - int value; - int y = GAMEDIFF_WND_TOP_OFFSET; - for (uint i = 0; i != GAME_DIFFICULTY_NUM; i++) { - const GameSettingData *gsd = &_game_setting_info[i]; - value = ((GDType*)&_opt_mod_temp.diff)[i]; - - DrawArrowButtons(5, y, 3, - (diffic_d->clicked_button == i) ? 1 + !!diffic_d->clicked_increase : 0, - !(HasBit(disabled, i) || gsd->min == value), - !(HasBit(disabled, i) || gsd->max == value)); - - value += _game_setting_info[i].str; - if (i == 4) value *= 1000; // XXX - handle currency option - SetDParam(0, value); - DrawString(30, y, STR_6805_MAXIMUM_NO_COMPETITORS + i, TC_FROMSTRING); - - y += GAMEDIFF_WND_ROWSIZE + 2; // space items apart a bit - } - } break; - - case WE_CLICK: - switch (e->we.click.widget) { - case GDW_SETTING_BG: { /* Difficulty settings widget, decode click */ - /* Don't allow clients to make any changes */ - if (_networking && !_network_server) return; - - const int x = e->we.click.pt.x - 5; - if (!IsInsideMM(x, 0, 21)) // Button area - return; - - const int y = e->we.click.pt.y - GAMEDIFF_WND_TOP_OFFSET; - if (y < 0) return; - - /* Get button from Y coord. */ - const uint8 btn = y / (GAMEDIFF_WND_ROWSIZE + 2); - if (btn >= GAME_DIFFICULTY_NUM || y % (GAMEDIFF_WND_ROWSIZE + 2) >= 9) - return; - - /* Clicked disabled button? */ - if (_game_mode == GM_NORMAL && HasBit(DIFF_INGAME_DISABLED_BUTTONS, btn)) - return; - - diffic_d->timeout = 5; - - int16 val = ((GDType*)&_opt_mod_temp.diff)[btn]; - - const GameSettingData *info = &_game_setting_info[btn]; // get information about the difficulty setting - if (x >= 10) { - /* Increase button clicked */ - val = min(val + info->step, info->max); - diffic_d->clicked_increase = true; - } else { - /* Decrease button clicked */ - val -= info->step; - val = max(val, info->min); - diffic_d->clicked_increase = false; - } - diffic_d->clicked_button = btn; - - /* save value in temporary variable */ - ((GDType*)&_opt_mod_temp.diff)[btn] = val; - w->RaiseWidget(GDW_LVL_EASY + _opt_mod_temp.diff_level); - SetDifficultyLevel(3, &_opt_mod_temp); // set difficulty level to custom - w->LowerWidget(GDW_LVL_CUSTOM); - w->SetDirty(); - } break; - - case GDW_LVL_EASY: - case GDW_LVL_MEDIUM: - case GDW_LVL_HARD: - case GDW_LVL_CUSTOM: - /* temporarily change difficulty level */ - w->RaiseWidget(GDW_LVL_EASY + _opt_mod_temp.diff_level); - SetDifficultyLevel(e->we.click.widget - GDW_LVL_EASY, &_opt_mod_temp); - w->LowerWidget(GDW_LVL_EASY + _opt_mod_temp.diff_level); - w->SetDirty(); - break; - - case GDW_HIGHSCORE: // Highscore Table - ShowHighscoreTable(_opt_mod_temp.diff_level, -1); - break; - - case GDW_ACCEPT: { // Save button - save changes - GDType btn, val; - for (btn = 0; btn != GAME_DIFFICULTY_NUM; btn++) { - val = ((GDType*)&_opt_mod_temp.diff)[btn]; - /* if setting has changed, change it */ - if (val != ((GDType*)&_opt_ptr->diff)[btn]) - DoCommandP(0, btn, val, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); - } - DoCommandP(0, UINT_MAX, _opt_mod_temp.diff_level, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); - delete w; - /* If we are in the editor, we should reload the economy. - * This way when you load a game, the max loan and interest rate - * are loaded correctly. */ - if (_game_mode == GM_EDITOR) StartupEconomy(); - break; - } - - case GDW_CANCEL: // Cancel button - close window, abandon changes - delete w; - break; - } break; - - case WE_TICK: /* Handle the visual 'clicking' of the buttons */ - if (diffic_d->timeout != 0) { - diffic_d->timeout--; - if (diffic_d->timeout == 0) diffic_d->clicked_button = NO_SETTINGS_BUTTON; - w->SetDirty(); - } - break; - } -} -#undef DIFF_INGAME_DISABLED_BUTTONS - /* Widget definition for the game difficulty settings window */ static const Widget _game_difficulty_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 10, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // GDW_CLOSEBOX @@ -689,16 +467,198 @@ WC_GAME_OPTIONS, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _game_difficulty_widgets, - GameDifficultyWndProc +}; + +struct GameDifficultyWindow : public Window { +private: + bool clicked_increase; + uint8 clicked_button; + uint8 timeout; + + /* Temporary holding place of values in the difficulty window until 'Save' is clicked */ + GameOptions opt_mod_temp; + + enum { + GAMEDIFF_WND_TOP_OFFSET = 45, + GAMEDIFF_WND_ROWSIZE = 9, + // 0x383E = (1 << 13) | (1 << 12) | (1 << 11) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) + DIFF_INGAME_DISABLED_BUTTONS = 0x383E, + NO_SETTINGS_BUTTON = 0xFF, + }; + + /* Names of the game difficulty settings window */ + enum GameDifficultyWidgets { + GDW_CLOSEBOX = 0, + GDW_CAPTION, + GDW_UPPER_BG, + GDW_LVL_EASY, + GDW_LVL_MEDIUM, + GDW_LVL_HARD, + GDW_LVL_CUSTOM, + GDW_HIGHSCORE, + GDW_SETTING_BG, + GDW_LOWER_BG, + GDW_ACCEPT, + GDW_CANCEL, + }; + +public: + GameDifficultyWindow() : Window(&_game_difficulty_desc) + { + /* Copy current settings (ingame or in intro) to temporary holding place + * change that when setting stuff, copy back on clicking 'OK' */ + this->opt_mod_temp = *_opt_ptr; + this->clicked_increase = false; + this->clicked_button = NO_SETTINGS_BUTTON; + this->timeout = 0; + /* Hide the closebox to make sure that the user aborts or confirms his changes */ + this->HideWidget(GDW_CLOSEBOX); + this->widget[GDW_CAPTION].left = 0; + /* Setup disabled buttons when creating window + * disable all other difficulty buttons during gameplay except for 'custom' */ + this->SetWidgetsDisabledState(_game_mode == GM_NORMAL, + GDW_LVL_EASY, + GDW_LVL_MEDIUM, + GDW_LVL_HARD, + GDW_LVL_CUSTOM, + WIDGET_LIST_END); + this->SetWidgetDisabledState(GDW_HIGHSCORE, _game_mode == GM_EDITOR || _networking); // highscore chart in multiplayer + this->SetWidgetDisabledState(GDW_ACCEPT, _networking && !_network_server); // Save-button in multiplayer (and if client) + this->LowerWidget(GDW_LVL_EASY + this->opt_mod_temp.diff_level); + this->FindWindowPlacementAndResize(&_game_difficulty_desc); + } + + virtual void OnPaint() + { + this->DrawWidgets(); + + /* XXX - Disabled buttons in normal gameplay or during muliplayer as non server. + * Bitshifted for each button to see if that bit is set. If it is set, the + * button is disabled */ + uint32 disabled = 0; + if (_networking && !_network_server) { + disabled = MAX_UVALUE(uint32); // Disable all + } else if (_game_mode == GM_NORMAL) { + disabled = DIFF_INGAME_DISABLED_BUTTONS; + } + + int value; + int y = GAMEDIFF_WND_TOP_OFFSET; + for (uint i = 0; i != GAME_DIFFICULTY_NUM; i++) { + const GameSettingData *gsd = &_game_setting_info[i]; + value = ((GDType*)&this->opt_mod_temp.diff)[i]; + + DrawArrowButtons(5, y, 3, + (this->clicked_button == i) ? 1 + !!this->clicked_increase : 0, + !(HasBit(disabled, i) || gsd->min == value), + !(HasBit(disabled, i) || gsd->max == value)); + + value += _game_setting_info[i].str; + if (i == 4) value *= 1000; // XXX - handle currency option + SetDParam(0, value); + DrawString(30, y, STR_6805_MAXIMUM_NO_COMPETITORS + i, TC_FROMSTRING); + + y += GAMEDIFF_WND_ROWSIZE + 2; // space items apart a bit + } + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case GDW_SETTING_BG: { /* Difficulty settings widget, decode click */ + /* Don't allow clients to make any changes */ + if (_networking && !_network_server) return; + + const int x = pt.x - 5; + if (!IsInsideMM(x, 0, 21)) return; // Button area + + const int y = pt.y - GAMEDIFF_WND_TOP_OFFSET; + if (y < 0) return; + + /* Get button from Y coord. */ + const uint8 btn = y / (GAMEDIFF_WND_ROWSIZE + 2); + if (btn >= GAME_DIFFICULTY_NUM || y % (GAMEDIFF_WND_ROWSIZE + 2) >= 9) return; + + /* Clicked disabled button? */ + if (_game_mode == GM_NORMAL && HasBit((int)DIFF_INGAME_DISABLED_BUTTONS, btn)) return; + + this->timeout = 5; + + int16 val = ((GDType*)&this->opt_mod_temp.diff)[btn]; + + const GameSettingData *info = &_game_setting_info[btn]; // get information about the difficulty setting + if (x >= 10) { + /* Increase button clicked */ + val = min(val + info->step, info->max); + this->clicked_increase = true; + } else { + /* Decrease button clicked */ + val -= info->step; + val = max(val, info->min); + this->clicked_increase = false; + } + this->clicked_button = btn; + + /* save value in temporary variable */ + ((GDType*)&this->opt_mod_temp.diff)[btn] = val; + this->RaiseWidget(GDW_LVL_EASY + this->opt_mod_temp.diff_level); + SetDifficultyLevel(3, &this->opt_mod_temp); // set difficulty level to custom + this->LowerWidget(GDW_LVL_CUSTOM); + this->SetDirty(); + } break; + + case GDW_LVL_EASY: + case GDW_LVL_MEDIUM: + case GDW_LVL_HARD: + case GDW_LVL_CUSTOM: + /* temporarily change difficulty level */ + this->RaiseWidget(GDW_LVL_EASY + this->opt_mod_temp.diff_level); + SetDifficultyLevel(widget - GDW_LVL_EASY, &this->opt_mod_temp); + this->LowerWidget(GDW_LVL_EASY + this->opt_mod_temp.diff_level); + this->SetDirty(); + break; + + case GDW_HIGHSCORE: // Highscore Table + ShowHighscoreTable(this->opt_mod_temp.diff_level, -1); + break; + + case GDW_ACCEPT: { // Save button - save changes + GDType btn, val; + for (btn = 0; btn != GAME_DIFFICULTY_NUM; btn++) { + val = ((GDType*)&this->opt_mod_temp.diff)[btn]; + /* if setting has changed, change it */ + if (val != ((GDType*)&_opt_ptr->diff)[btn]) + DoCommandP(0, btn, val, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); + } + DoCommandP(0, UINT_MAX, this->opt_mod_temp.diff_level, NULL, CMD_CHANGE_DIFFICULTY_LEVEL); + delete this; + /* If we are in the editor, we should reload the economy. + * This way when you load a game, the max loan and interest rate + * are loaded correctly. */ + if (_game_mode == GM_EDITOR) StartupEconomy(); + break; + } + + case GDW_CANCEL: // Cancel button - close window, abandon changes + delete this; + break; + } + } + + virtual void OnTick() + { + if (this->timeout != 0) { + this->timeout--; + if (this->timeout == 0) this->clicked_button = NO_SETTINGS_BUTTON; + this->SetDirty(); + } + } }; void ShowGameDifficulty() { DeleteWindowById(WC_GAME_OPTIONS, 0); - /* Copy current settings (ingame or in intro) to temporary holding place - * change that when setting stuff, copy back on clicking 'OK' */ - _opt_mod_temp = *_opt_ptr; - new Window(&_game_difficulty_desc); + new GameDifficultyWindow(); } static const char *_patches_ui[] = { @@ -854,228 +814,232 @@ PATCHSEL_COMPETITORS }; -/** The main patches window. Shows a number of categories on top and - * a selection of patches in that category. - * Uses WP(w, def_d) macro - data_1, data_2, data_3 */ -static void PatchesSelectionWndProc(Window *w, WindowEvent *e) -{ +struct PatchesSelectionWindow : Window { static Patches *patches_ptr; - static int patches_max = 0; - - switch (e->event) { - case WE_CREATE: { - static bool first_time = true; - - patches_ptr = (_game_mode == GM_MENU) ? &_patches_newgame : &_patches; - - /* Build up the dynamic settings-array only once per OpenTTD session */ - if (first_time) { - PatchPage *page; - for (page = &_patches_page[0]; page != endof(_patches_page); page++) { - uint i; - - if (patches_max < page->num) patches_max = page->num; - - page->entries = MallocT(page->num); - for (i = 0; i != page->num; i++) { - uint index; - const SettingDesc *sd = GetPatchFromName(page->names[i], &index); - assert(sd != NULL); - - page->entries[i].setting = sd; - page->entries[i].index = index; - } - } - first_time = false; - } - - /* Resize the window to fit the largest patch tab */ - ResizeWindowForWidget(w, PATCHSEL_OPTIONSPANEL, 0, patches_max * 11); - - /* Recentre the window for the new size */ - w->top = w->top - (patches_max * 11) / 2; - - w->LowerWidget(4); - } break; + static int patches_max; - case WE_PAINT: { - int x, y; - const PatchPage *page = &_patches_page[WP(w, def_d).data_1]; - uint i; - - /* Set up selected category */ - DrawWindowWidgets(w); - - x = 5; - y = 47; - for (i = 0; i != page->num; i++) { - const SettingDesc *sd = page->entries[i].setting; - const SettingDescBase *sdb = &sd->desc; - const void *var = GetVariableAddress(patches_ptr, &sd->save); - bool editable = true; - bool disabled = false; - - // We do not allow changes of some items when we are a client in a networkgame - if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) editable = false; - if ((sdb->flags & SGF_NETWORK_ONLY) && !_networking) editable = false; - if ((sdb->flags & SGF_NO_NETWORK) && _networking) editable = false; + int page; + int entry; + int click; - if (sdb->cmd == SDT_BOOLX) { - static const int _bool_ctabs[2][2] = {{9, 4}, {7, 6}}; - /* Draw checkbox for boolean-value either on/off */ - bool on = (*(bool*)var); - - DrawFrameRect(x, y, x + 19, y + 8, _bool_ctabs[!!on][!!editable], on ? FR_LOWERED : FR_NONE); - SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); - } else { - int32 value; - - value = (int32)ReadValue(var, sd->save.conv); + PatchesSelectionWindow(const WindowDesc *desc) : Window(desc) + { + static bool first_time = true; - /* Draw [<][>] boxes for settings of an integer-type */ - DrawArrowButtons(x, y, 3, WP(w, def_d).data_2 - (i * 2), (editable && value != sdb->min), (editable && value != sdb->max)); + patches_ptr = (_game_mode == GM_MENU) ? &_patches_newgame : &_patches; - disabled = (value == 0) && (sdb->flags & SGF_0ISDISABLED); - if (disabled) { - SetDParam(0, STR_CONFIG_PATCHES_DISABLED); + /* Build up the dynamic settings-array only once per OpenTTD session */ + if (first_time) { + PatchPage *page; + for (page = &_patches_page[0]; page != endof(_patches_page); page++) { + uint i; + + if (patches_max < page->num) patches_max = page->num; + + page->entries = MallocT(page->num); + for (i = 0; i != page->num; i++) { + uint index; + const SettingDesc *sd = GetPatchFromName(page->names[i], &index); + assert(sd != NULL); + + page->entries[i].setting = sd; + page->entries[i].index = index; + } + } + first_time = false; + } + + /* Resize the window to fit the largest patch tab */ + ResizeWindowForWidget(this, PATCHSEL_OPTIONSPANEL, 0, patches_max * 11); + + /* Recentre the window for the new size */ + this->top = this->top - (patches_max * 11) / 2; + + this->LowerWidget(4); + + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { + int x, y; + const PatchPage *page = &_patches_page[this->page]; + uint i; + + /* Set up selected category */ + this->DrawWidgets(); + + x = 5; + y = 47; + for (i = 0; i != page->num; i++) { + const SettingDesc *sd = page->entries[i].setting; + const SettingDescBase *sdb = &sd->desc; + const void *var = GetVariableAddress(patches_ptr, &sd->save); + bool editable = true; + bool disabled = false; + + // We do not allow changes of some items when we are a client in a networkgame + if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) editable = false; + if ((sdb->flags & SGF_NETWORK_ONLY) && !_networking) editable = false; + if ((sdb->flags & SGF_NO_NETWORK) && _networking) editable = false; + + if (sdb->cmd == SDT_BOOLX) { + static const int _bool_ctabs[2][2] = {{9, 4}, {7, 6}}; + /* Draw checkbox for boolean-value either on/off */ + bool on = (*(bool*)var); + + DrawFrameRect(x, y, x + 19, y + 8, _bool_ctabs[!!on][!!editable], on ? FR_LOWERED : FR_NONE); + SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); + } else { + int32 value; + + value = (int32)ReadValue(var, sd->save.conv); + + /* Draw [<][>] boxes for settings of an integer-type */ + DrawArrowButtons(x, y, 3, this->click - (i * 2), (editable && value != sdb->min), (editable && value != sdb->max)); + + disabled = (value == 0) && (sdb->flags & SGF_0ISDISABLED); + if (disabled) { + SetDParam(0, STR_CONFIG_PATCHES_DISABLED); + } else { + if (sdb->flags & SGF_CURRENCY) { + SetDParam(0, STR_CONFIG_PATCHES_CURRENCY); + } else if (sdb->flags & SGF_MULTISTRING) { + SetDParam(0, sdb->str + value + 1); } else { - if (sdb->flags & SGF_CURRENCY) { - SetDParam(0, STR_CONFIG_PATCHES_CURRENCY); - } else if (sdb->flags & SGF_MULTISTRING) { - SetDParam(0, sdb->str + value + 1); + SetDParam(0, (sdb->flags & SGF_NOCOMMA) ? STR_CONFIG_PATCHES_INT32 : STR_7024); + } + SetDParam(1, value); + } + } + DrawString(30, y, (sdb->str) + disabled, TC_FROMSTRING); + y += 11; + } + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case PATCHSEL_OPTIONSPANEL: { + const PatchPage *page = &_patches_page[this->page]; + const SettingDesc *sd; + void *var; + int32 value; + int x, y; + byte btn; + + y = pt.y - 46 - 1; + if (y < 0) return; + + x = pt.x - 5; + if (x < 0) return; + + btn = y / 11; + if (y % 11 > 9) return; + if (btn >= page->num) return; + + sd = page->entries[btn].setting; + + /* return if action is only active in network, or only settable by server */ + if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) return; + if ((sd->desc.flags & SGF_NETWORK_ONLY) && !_networking) return; + if ((sd->desc.flags & SGF_NO_NETWORK) && _networking) return; + + var = GetVariableAddress(patches_ptr, &sd->save); + value = (int32)ReadValue(var, sd->save.conv); + + /* clicked on the icon on the left side. Either scroller or bool on/off */ + if (x < 21) { + const SettingDescBase *sdb = &sd->desc; + int32 oldvalue = value; + + switch (sdb->cmd) { + case SDT_BOOLX: value ^= 1; break; + case SDT_NUMX: { + /* Add a dynamic step-size to the scroller. In a maximum of + * 50-steps you should be able to get from min to max, + * unless specified otherwise in the 'interval' variable + * of the current patch. */ + uint32 step = (sdb->interval == 0) ? ((sdb->max - sdb->min) / 50) : sdb->interval; + if (step == 0) step = 1; + + // don't allow too fast scrolling + if ((this->flags4 & WF_TIMEOUT_MASK) > 2 << WF_TIMEOUT_SHL) { + _left_button_clicked = false; + return; + } + + /* Increase or decrease the value and clamp it to extremes */ + if (x >= 10) { + value += step; + if (value > sdb->max) value = sdb->max; } else { - SetDParam(0, (sdb->flags & SGF_NOCOMMA) ? STR_CONFIG_PATCHES_INT32 : STR_7024); + value -= step; + if (value < sdb->min) value = (sdb->flags & SGF_0ISDISABLED) ? 0 : sdb->min; } - SetDParam(1, value); + + /* Set up scroller timeout for numeric values */ + if (value != oldvalue && !(sd->desc.flags & SGF_MULTISTRING)) { + this->click = btn * 2 + 1 + ((x >= 10) ? 1 : 0); + this->flags4 |= 5 << WF_TIMEOUT_SHL; + _left_button_clicked = false; + } + } break; + default: NOT_REACHED(); + } + + if (value != oldvalue) { + SetPatchValue(page->entries[btn].index, patches_ptr, value); + this->SetDirty(); + } + } else { + /* only open editbox for types that its sensible for */ + if (sd->desc.cmd != SDT_BOOLX && !(sd->desc.flags & SGF_MULTISTRING)) { + /* Show the correct currency-translated value */ + if (sd->desc.flags & SGF_CURRENCY) value *= _currency->rate; + + this->entry = btn; + SetDParam(0, value); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_PATCHES_QUERY_CAPT, 10, 100, this, CS_NUMERAL); } } - DrawString(30, y, (sdb->str) + disabled, TC_FROMSTRING); - y += 11; - } - } break; - - case WE_CLICK: - switch (e->we.click.widget) { - case PATCHSEL_OPTIONSPANEL: { - const PatchPage *page = &_patches_page[WP(w, def_d).data_1]; - const SettingDesc *sd; - void *var; - int32 value; - int x, y; - byte btn; - - y = e->we.click.pt.y - 46 - 1; - if (y < 0) return; - - x = e->we.click.pt.x - 5; - if (x < 0) return; - - btn = y / 11; - if (y % 11 > 9) return; - if (btn >= page->num) return; - - sd = page->entries[btn].setting; - - /* return if action is only active in network, or only settable by server */ - if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) return; - if ((sd->desc.flags & SGF_NETWORK_ONLY) && !_networking) return; - if ((sd->desc.flags & SGF_NO_NETWORK) && _networking) return; - - var = GetVariableAddress(patches_ptr, &sd->save); - value = (int32)ReadValue(var, sd->save.conv); - - /* clicked on the icon on the left side. Either scroller or bool on/off */ - if (x < 21) { - const SettingDescBase *sdb = &sd->desc; - int32 oldvalue = value; - - switch (sdb->cmd) { - case SDT_BOOLX: value ^= 1; break; - case SDT_NUMX: { - /* Add a dynamic step-size to the scroller. In a maximum of - * 50-steps you should be able to get from min to max, - * unless specified otherwise in the 'interval' variable - * of the current patch. */ - uint32 step = (sdb->interval == 0) ? ((sdb->max - sdb->min) / 50) : sdb->interval; - if (step == 0) step = 1; - - // don't allow too fast scrolling - if ((w->flags4 & WF_TIMEOUT_MASK) > 2 << WF_TIMEOUT_SHL) { - _left_button_clicked = false; - return; - } + } break; - /* Increase or decrease the value and clamp it to extremes */ - if (x >= 10) { - value += step; - if (value > sdb->max) value = sdb->max; - } else { - value -= step; - if (value < sdb->min) value = (sdb->flags & SGF_0ISDISABLED) ? 0 : sdb->min; - } - - /* Set up scroller timeout for numeric values */ - if (value != oldvalue && !(sd->desc.flags & SGF_MULTISTRING)) { - WP(w, def_d).data_2 = btn * 2 + 1 + ((x >= 10) ? 1 : 0); - w->flags4 |= 5 << WF_TIMEOUT_SHL; - _left_button_clicked = false; - } - } break; - default: NOT_REACHED(); - } - - if (value != oldvalue) { - SetPatchValue(page->entries[btn].index, patches_ptr, value); - w->SetDirty(); - } - } else { - /* only open editbox for types that its sensible for */ - if (sd->desc.cmd != SDT_BOOLX && !(sd->desc.flags & SGF_MULTISTRING)) { - /* Show the correct currency-translated value */ - if (sd->desc.flags & SGF_CURRENCY) value *= _currency->rate; + case PATCHSEL_INTERFACE: case PATCHSEL_CONSTRUCTION: case PATCHSEL_VEHICLES: + case PATCHSEL_STATIONS: case PATCHSEL_ECONOMY: case PATCHSEL_COMPETITORS: + this->RaiseWidget(this->page + PATCHSEL_INTERFACE); + this->page = widget - PATCHSEL_INTERFACE; + this->LowerWidget(this->page + PATCHSEL_INTERFACE); + DeleteWindowById(WC_QUERY_STRING, 0); + this->SetDirty(); + break; + } + } - WP(w, def_d).data_3 = btn; - SetDParam(0, value); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_PATCHES_QUERY_CAPT, 10, 100, w, CS_NUMERAL); - } - } - } break; - - case PATCHSEL_INTERFACE: case PATCHSEL_CONSTRUCTION: case PATCHSEL_VEHICLES: - case PATCHSEL_STATIONS: case PATCHSEL_ECONOMY: case PATCHSEL_COMPETITORS: - w->RaiseWidget(WP(w, def_d).data_1 + PATCHSEL_INTERFACE); - WP(w, def_d).data_1 = e->we.click.widget - PATCHSEL_INTERFACE; - w->LowerWidget(WP(w, def_d).data_1 + PATCHSEL_INTERFACE); - DeleteWindowById(WC_QUERY_STRING, 0); - w->SetDirty(); - break; - } - break; + virtual void OnTimeout() + { + this->click = 0; + this->SetDirty(); + } - case WE_TIMEOUT: - WP(w, def_d).data_2 = 0; - w->SetDirty(); - break; - - case WE_ON_EDIT_TEXT: - if (!StrEmpty(e->we.edittext.str)) { - const PatchEntry *pe = &_patches_page[WP(w, def_d).data_1].entries[WP(w, def_d).data_3]; - const SettingDesc *sd = pe->setting; - int32 value = atoi(e->we.edittext.str); + virtual void OnQueryTextFinished(char *str) + { + if (!StrEmpty(str)) { + const PatchEntry *pe = &_patches_page[this->page].entries[this->entry]; + const SettingDesc *sd = pe->setting; + int32 value = atoi(str); - /* Save the correct currency-translated value */ - if (sd->desc.flags & SGF_CURRENCY) value /= _currency->rate; + /* Save the correct currency-translated value */ + if (sd->desc.flags & SGF_CURRENCY) value /= _currency->rate; - SetPatchValue(pe->index, patches_ptr, value); - w->SetDirty(); - } - break; + SetPatchValue(pe->index, patches_ptr, value); + this->SetDirty(); + } + } +}; - case WE_DESTROY: - DeleteWindowById(WC_QUERY_STRING, 0); - break; - } -} +Patches *PatchesSelectionWindow::patches_ptr = NULL; +int PatchesSelectionWindow::patches_max = 0; static const Widget _patches_selection_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 10, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -1097,13 +1061,12 @@ WC_GAME_OPTIONS, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _patches_selection_widgets, - PatchesSelectionWndProc, }; void ShowPatchesSelection() { DeleteWindowById(WC_GAME_OPTIONS, 0); - new Window(&_patches_selection_desc); + new PatchesSelectionWindow(&_patches_selection_desc); } @@ -1143,176 +1106,178 @@ CUSTCURR_TO_EURO, }; -static char _str_separator[2]; - -static void CustCurrencyWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - int x; - int y = 20; - int clk = WP(w, def_d).data_1; - DrawWindowWidgets(w); - - /* exchange rate */ - DrawArrowButtons(10, y, 3, GB(clk, 0, 2), true, true); - SetDParam(0, 1); - SetDParam(1, 1); - DrawString(35, y + 1, STR_CURRENCY_EXCHANGE_RATE, TC_FROMSTRING); - y += 12; - - /* separator */ - DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(clk, 2, 2) ? FR_LOWERED : FR_NONE); - x = DrawString(35, y + 1, STR_CURRENCY_SEPARATOR, TC_FROMSTRING); - DoDrawString(_str_separator, x + 4, y + 1, TC_ORANGE); - y += 12; - - /* prefix */ - DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(clk, 4, 2) ? FR_LOWERED : FR_NONE); - x = DrawString(35, y + 1, STR_CURRENCY_PREFIX, TC_FROMSTRING); - DoDrawString(_custom_currency.prefix, x + 4, y + 1, TC_ORANGE); - y += 12; - - /* suffix */ - DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(clk, 6, 2) ? FR_LOWERED : FR_NONE); - x = DrawString(35, y + 1, STR_CURRENCY_SUFFIX, TC_FROMSTRING); - DoDrawString(_custom_currency.suffix, x + 4, y + 1, TC_ORANGE); - y += 12; - - /* switch to euro */ - DrawArrowButtons(10, y, 3, GB(clk, 8, 2), true, true); - SetDParam(0, _custom_currency.to_euro); - DrawString(35, y + 1, (_custom_currency.to_euro != CF_NOEURO) ? STR_CURRENCY_SWITCH_TO_EURO : STR_CURRENCY_SWITCH_TO_EURO_NEVER, TC_FROMSTRING); - y += 12; +struct CustomCurrencyWindow : Window { + char separator[2]; + int click; + int query_widget; - /* Preview */ - y += 12; - SetDParam(0, 10000); - DrawString(35, y + 1, STR_CURRENCY_PREVIEW, TC_FROMSTRING); - } break; - - case WE_CLICK: { - int line = (e->we.click.pt.y - 20) / 12; - int len = 0; - int x = e->we.click.pt.x; - StringID str = 0; - CharSetFilter afilter = CS_ALPHANUMERAL; + CustomCurrencyWindow(const WindowDesc *desc) : Window(desc) + { + this->separator[0] = _custom_currency.separator; + this->separator[1] = '\0'; + this->FindWindowPlacementAndResize(desc); + } - switch (line) { - case CUSTCURR_EXCHANGERATE: - if (IsInsideMM(x, 10, 30)) { // clicked buttons - if (x < 20) { - if (_custom_currency.rate > 1) _custom_currency.rate--; - WP(w, def_d).data_1 = 1 << (line * 2 + 0); - } else { - if (_custom_currency.rate < 5000) _custom_currency.rate++; - WP(w, def_d).data_1 = 1 << (line * 2 + 1); - } - } else { // enter text - SetDParam(0, _custom_currency.rate); - str = STR_CONFIG_PATCHES_INT32; - len = 4; - afilter = CS_NUMERAL; - } - break; + virtual void OnPaint() + { + int x; + int y = 20; + this->DrawWidgets(); - case CUSTCURR_SEPARATOR: - if (IsInsideMM(x, 10, 30)) { // clicked button - WP(w, def_d).data_1 = 1 << (line * 2 + 1); - } - str = BindCString(_str_separator); - len = 1; - break; + /* exchange rate */ + DrawArrowButtons(10, y, 3, GB(this->click, 0, 2), true, true); + SetDParam(0, 1); + SetDParam(1, 1); + DrawString(35, y + 1, STR_CURRENCY_EXCHANGE_RATE, TC_FROMSTRING); + y += 12; - case CUSTCURR_PREFIX: - if (IsInsideMM(x, 10, 30)) { // clicked button - WP(w, def_d).data_1 = 1 << (line * 2 + 1); + /* separator */ + DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(this->click, 2, 2) ? FR_LOWERED : FR_NONE); + x = DrawString(35, y + 1, STR_CURRENCY_SEPARATOR, TC_FROMSTRING); + DoDrawString(this->separator, x + 4, y + 1, TC_ORANGE); + y += 12; + + /* prefix */ + DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(this->click, 4, 2) ? FR_LOWERED : FR_NONE); + x = DrawString(35, y + 1, STR_CURRENCY_PREFIX, TC_FROMSTRING); + DoDrawString(_custom_currency.prefix, x + 4, y + 1, TC_ORANGE); + y += 12; + + /* suffix */ + DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(this->click, 6, 2) ? FR_LOWERED : FR_NONE); + x = DrawString(35, y + 1, STR_CURRENCY_SUFFIX, TC_FROMSTRING); + DoDrawString(_custom_currency.suffix, x + 4, y + 1, TC_ORANGE); + y += 12; + + /* switch to euro */ + DrawArrowButtons(10, y, 3, GB(this->click, 8, 2), true, true); + SetDParam(0, _custom_currency.to_euro); + DrawString(35, y + 1, (_custom_currency.to_euro != CF_NOEURO) ? STR_CURRENCY_SWITCH_TO_EURO : STR_CURRENCY_SWITCH_TO_EURO_NEVER, TC_FROMSTRING); + y += 12; + + /* Preview */ + y += 12; + SetDParam(0, 10000); + DrawString(35, y + 1, STR_CURRENCY_PREVIEW, TC_FROMSTRING); + } + + virtual void OnClick(Point pt, int widget) + { + int line = (pt.y - 20) / 12; + int len = 0; + int x = pt.x; + StringID str = 0; + CharSetFilter afilter = CS_ALPHANUMERAL; + + switch (line) { + case CUSTCURR_EXCHANGERATE: + if (IsInsideMM(x, 10, 30)) { // clicked buttons + if (x < 20) { + if (_custom_currency.rate > 1) _custom_currency.rate--; + this->click = 1 << (line * 2 + 0); + } else { + if (_custom_currency.rate < 5000) _custom_currency.rate++; + this->click = 1 << (line * 2 + 1); } - str = BindCString(_custom_currency.prefix); - len = 12; - break; - - case CUSTCURR_SUFFIX: - if (IsInsideMM(x, 10, 30)) { // clicked button - WP(w, def_d).data_1 = 1 << (line * 2 + 1); - } - str = BindCString(_custom_currency.suffix); - len = 12; - break; - - case CUSTCURR_TO_EURO: - if (IsInsideMM(x, 10, 30)) { // clicked buttons - if (x < 20) { - _custom_currency.to_euro = (_custom_currency.to_euro <= 2000) ? - CF_NOEURO : _custom_currency.to_euro - 1; - WP(w, def_d).data_1 = 1 << (line * 2 + 0); - } else { - _custom_currency.to_euro = - Clamp(_custom_currency.to_euro + 1, 2000, MAX_YEAR); - WP(w, def_d).data_1 = 1 << (line * 2 + 1); - } - } else { // enter text - SetDParam(0, _custom_currency.to_euro); - str = STR_CONFIG_PATCHES_INT32; - len = 4; - afilter = CS_NUMERAL; - } - break; - } - - if (len != 0) { - WP(w, def_d).data_2 = line; - ShowQueryString(str, STR_CURRENCY_CHANGE_PARAMETER, len + 1, 250, w, afilter); - } - - w->flags4 |= 5 << WF_TIMEOUT_SHL; - w->SetDirty(); - } break; + } else { // enter text + SetDParam(0, _custom_currency.rate); + str = STR_CONFIG_PATCHES_INT32; + len = 4; + afilter = CS_NUMERAL; + } + break; - case WE_ON_EDIT_TEXT: { - if (e->we.edittext.str == NULL) break; - - const char *b = e->we.edittext.str; - - switch (WP(w, def_d).data_2) { - case CUSTCURR_EXCHANGERATE: - _custom_currency.rate = Clamp(atoi(b), 1, 5000); - break; - - case CUSTCURR_SEPARATOR: /* Thousands seperator */ - _custom_currency.separator = StrEmpty(b) ? ' ' : b[0]; - ttd_strlcpy(_str_separator, b, lengthof(_str_separator)); - break; - - case CUSTCURR_PREFIX: - ttd_strlcpy(_custom_currency.prefix, b, lengthof(_custom_currency.prefix)); - break; + case CUSTCURR_SEPARATOR: + if (IsInsideMM(x, 10, 30)) { // clicked button + this->click = 1 << (line * 2 + 1); + } + str = BindCString(this->separator); + len = 1; + break; - case CUSTCURR_SUFFIX: - ttd_strlcpy(_custom_currency.suffix, b, lengthof(_custom_currency.suffix)); - break; + case CUSTCURR_PREFIX: + if (IsInsideMM(x, 10, 30)) { // clicked button + this->click = 1 << (line * 2 + 1); + } + str = BindCString(_custom_currency.prefix); + len = 12; + break; - case CUSTCURR_TO_EURO: { /* Year to switch to euro */ - int val = atoi(b); + case CUSTCURR_SUFFIX: + if (IsInsideMM(x, 10, 30)) { // clicked button + this->click = 1 << (line * 2 + 1); + } + str = BindCString(_custom_currency.suffix); + len = 12; + break; - _custom_currency.to_euro = (val < 2000 ? CF_NOEURO : min(val, MAX_YEAR)); - break; + case CUSTCURR_TO_EURO: + if (IsInsideMM(x, 10, 30)) { // clicked buttons + if (x < 20) { + _custom_currency.to_euro = (_custom_currency.to_euro <= 2000) ? + CF_NOEURO : _custom_currency.to_euro - 1; + this->click = 1 << (line * 2 + 0); + } else { + _custom_currency.to_euro = + Clamp(_custom_currency.to_euro + 1, 2000, MAX_YEAR); + this->click = 1 << (line * 2 + 1); + } + } else { // enter text + SetDParam(0, _custom_currency.to_euro); + str = STR_CONFIG_PATCHES_INT32; + len = 4; + afilter = CS_NUMERAL; } + break; + } + + if (len != 0) { + this->query_widget = line; + ShowQueryString(str, STR_CURRENCY_CHANGE_PARAMETER, len + 1, 250, this, afilter); + } + + this->flags4 |= 5 << WF_TIMEOUT_SHL; + this->SetDirty(); + } + + virtual void OnQueryTextFinished(char *str) + { + if (str == NULL) return; + + switch (this->query_widget) { + case CUSTCURR_EXCHANGERATE: + _custom_currency.rate = Clamp(atoi(str), 1, 5000); + break; + + case CUSTCURR_SEPARATOR: /* Thousands seperator */ + _custom_currency.separator = StrEmpty(str) ? ' ' : str[0]; + ttd_strlcpy(this->separator, str, lengthof(this->separator)); + break; + + case CUSTCURR_PREFIX: + ttd_strlcpy(_custom_currency.prefix, str, lengthof(_custom_currency.prefix)); + break; + + case CUSTCURR_SUFFIX: + ttd_strlcpy(_custom_currency.suffix, str, lengthof(_custom_currency.suffix)); + break; + + case CUSTCURR_TO_EURO: { /* Year to switch to euro */ + int val = atoi(str); + + _custom_currency.to_euro = (val < 2000 ? CF_NOEURO : min(val, MAX_YEAR)); + break; } - MarkWholeScreenDirty(); - } break; + } + MarkWholeScreenDirty(); + } - case WE_TIMEOUT: - WP(w, def_d).data_1 = 0; - w->SetDirty(); - break; - - case WE_DESTROY: - DeleteWindowById(WC_QUERY_STRING, 0); - MarkWholeScreenDirty(); - break; + virtual void OnTimeout() + { + this->click = 0; + this->SetDirty(); } -} +}; static const Widget _cust_currency_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -1326,14 +1291,10 @@ WC_CUSTOM_CURRENCY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _cust_currency_widgets, - CustCurrencyWndProc, }; static void ShowCustCurrency() { - _str_separator[0] = _custom_currency.separator; - _str_separator[1] = '\0'; - DeleteWindowById(WC_CUSTOM_CURRENCY, 0); - new Window(&_cust_currency_desc); + new CustomCurrencyWindow(&_cust_currency_desc); } diff -r 6c4314786d68 -r 8cbdb511a674 src/ship_cmd.cpp --- a/src/ship_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/ship_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -350,7 +350,7 @@ SetDParam(0, st->index); AddNewsItem( STR_9833_CITIZENS_CELEBRATE_FIRST, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, (v->owner == _local_player) ? NT_ARRIVAL_PLAYER : NT_ARRIVAL_OTHER, DNC_NONE, + (v->owner == _local_player) ? NS_ARRIVAL_PLAYER : NS_ARRIVAL_OTHER, v->index, 0); } @@ -818,7 +818,7 @@ VehiclePositionChanged(v); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_SHIPS_LIST, 0); InvalidateWindow(WC_COMPANY, v->owner); if (IsLocalPlayer()) InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Ship window @@ -855,7 +855,7 @@ if (flags & DC_EXEC) { InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_SHIPS_LIST, 0); InvalidateWindow(WC_COMPANY, v->owner); DeleteWindowById(WC_VEHICLE_VIEW, v->index); DeleteDepotHighlightOfVehicle(v); @@ -1001,7 +1001,7 @@ v->cargo_subtype = new_subtype; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_SHIPS_LIST, 0); } return cost; diff -r 6c4314786d68 -r 8cbdb511a674 src/signal.cpp --- a/src/signal.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/signal.cpp Mon May 19 15:13:58 2008 +0000 @@ -468,12 +468,12 @@ * @return false iff presignal entry would be green (needed for trains leaving depot) * @pre IsValidPlayer(owner) */ -static bool UpdateSignalsInBuffer(Owner owner) +static SigSegState UpdateSignalsInBuffer(Owner owner) { assert(IsValidPlayer(owner)); bool first = true; // first block? - bool state = false; // value to return + SigSegState state = SIGSEG_FREE; // value to return TileIndex tile; DiagDirection dir; @@ -532,7 +532,10 @@ if (first) { first = false; - state = (flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN)) || (flags & SF_FULL); // true iff train CAN'T leave the depot + if ((flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN)) || (flags & SF_FULL)) { + /* SIGSEG_FREE is set by default */ + state = SIGSEG_FULL; + } } /* do not do anything when some buffer was full */ @@ -629,7 +632,7 @@ * @param owner owner whose signals we will update * @return false iff train can leave depot */ -bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner) +SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner) { assert(_globset.IsEmpty()); _globset.Add(tile, side); diff -r 6c4314786d68 -r 8cbdb511a674 src/signal_func.h --- a/src/signal_func.h Mon May 19 14:14:33 2008 +0000 +++ b/src/signal_func.h Mon May 19 15:13:58 2008 +0000 @@ -41,7 +41,13 @@ return _signal_on_track[track]; } -bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner); +/** State of the signal segment */ +enum SigSegState { + SIGSEG_FREE, ///< Free and has no pre-signal exits or at least one green exit + SIGSEG_FULL, ///< Occupied by a train +}; + +SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner); void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner); void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner); void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner); diff -r 6c4314786d68 -r 8cbdb511a674 src/signs_gui.cpp --- a/src/signs_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/signs_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -66,54 +66,63 @@ DEBUG(misc, 3, "Resorting global signs list"); } -static void SignListWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - if (_sign_sort_dirty) GlobalSortSignList(); - - SetVScrollCount(w, _num_sign_sort); - - SetDParam(0, w->vscroll.count); - DrawWindowWidgets(w); - - /* No signs? */ - int y = 16; // offset from top of widget - if (w->vscroll.count == 0) { - DrawString(2, y, STR_304A_NONE, TC_FROMSTRING); - return; - } - - /* Start drawing the signs */ - for (uint16 i = w->vscroll.pos; i < w->vscroll.cap + w->vscroll.pos && i < w->vscroll.count; i++) { - const Sign *si = _sign_sort[i]; - - if (si->owner != OWNER_NONE) DrawPlayerIcon(si->owner, 4, y + 1); +struct SignListWindow : Window { + SignListWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->vscroll.cap = 12; + this->resize.step_height = 10; + this->resize.height = this->height - 10 * 7; // minimum if 5 in the list - SetDParam(0, si->index); - DrawString(22, y, STR_SIGN_NAME, TC_YELLOW); - y += 10; - } - } break; - - case WE_CLICK: - if (e->we.click.widget == 3) { - uint32 id_v = (e->we.click.pt.y - 15) / 10; + this->FindWindowPlacementAndResize(desc); + } - if (id_v >= w->vscroll.cap) return; - id_v += w->vscroll.pos; - if (id_v >= w->vscroll.count) return; + virtual void OnPaint() + { + if (_sign_sort_dirty) GlobalSortSignList(); - const Sign *si = _sign_sort[id_v]; - ScrollMainWindowToTile(TileVirtXY(si->x, si->y)); - } - break; + SetVScrollCount(this, _num_sign_sort); - case WE_RESIZE: - w->vscroll.cap += e->we.sizing.diff.y / 10; - break; + SetDParam(0, this->vscroll.count); + this->DrawWidgets(); + + /* No signs? */ + int y = 16; // offset from top of widget + if (this->vscroll.count == 0) { + DrawString(2, y, STR_304A_NONE, TC_FROMSTRING); + return; + } + + /* Start drawing the signs */ + for (uint16 i = this->vscroll.pos; i < this->vscroll.cap + this->vscroll.pos && i < this->vscroll.count; i++) { + const Sign *si = _sign_sort[i]; + + if (si->owner != OWNER_NONE) DrawPlayerIcon(si->owner, 4, y + 1); + + SetDParam(0, si->index); + DrawString(22, y, STR_SIGN_NAME, TC_YELLOW); + y += 10; + } } -} + + virtual void OnClick(Point pt, int widget) + { + if (widget == 3) { + uint32 id_v = (pt.y - 15) / 10; + + if (id_v >= this->vscroll.cap) return; + id_v += this->vscroll.pos; + if (id_v >= this->vscroll.count) return; + + const Sign *si = _sign_sort[id_v]; + ScrollMainWindowToTile(TileVirtXY(si->x, si->y)); + } + } + + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / 10; + } +}; static const Widget _sign_list_widget[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -130,18 +139,12 @@ WC_SIGN_LIST, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE, _sign_list_widget, - SignListWndProc }; void ShowSignList() { - Window *w = AllocateWindowDescFront(&_sign_list_desc, 0); - if (w != NULL) { - w->vscroll.cap = 12; - w->resize.step_height = 10; - w->resize.height = w->height - 10 * 7; // minimum if 5 in the list - } + AllocateWindowDescFront(&_sign_list_desc, 0); } static void RenameSign(SignID index, const char *text) @@ -170,6 +173,7 @@ this->LowerWidget(QUERY_EDIT_SIGN_WIDGET_TEXT); UpdateSignEditWindow(si); + this->FindWindowPlacementAndResize(desc); } ~SignWindow() @@ -197,7 +201,7 @@ virtual void OnPaint() { SetDParam(0, this->caption); - DrawWindowWidgets(this); + this->DrawWidgets(); this->DrawEditBox(QUERY_EDIT_SIGN_WIDGET_TEXT); } @@ -255,10 +259,10 @@ } } - virtual bool OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { - bool cont = true; - switch (this->HandleEditBoxKey(QUERY_EDIT_SIGN_WIDGET_TEXT, key, keycode, cont)) { + EventState state = ES_NOT_HANDLED; + switch (this->HandleEditBoxKey(QUERY_EDIT_SIGN_WIDGET_TEXT, key, keycode, state)) { case 1: // Enter pressed, confirms change RenameSign(this->cur_sign, this->text.buf); /* FALL THROUGH */ @@ -267,7 +271,7 @@ delete this; break; } - return cont; + return state; } virtual void OnMouseLoop() @@ -295,7 +299,6 @@ WC_QUERY_STRING, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, _query_sign_edit_widgets, - NULL }; void ShowRenameSignWindow(const Sign *si) diff -r 6c4314786d68 -r 8cbdb511a674 src/smallmap_gui.cpp --- a/src/smallmap_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/smallmap_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -800,7 +800,7 @@ this->SetDirty(); } - SmallMapWindow(const WindowDesc *desc, void *data, int window_number) : Window(desc, data, window_number) + SmallMapWindow(const WindowDesc *desc, int window_number) : Window(desc, window_number) { /* Resize the window to fit industries list */ if (_industries_per_column > BASE_NB_PER_COLUMN) { @@ -847,7 +847,7 @@ /* draw the window */ SetDParam(0, STR_00E5_CONTOURS + _smallmap_type); - DrawWindowWidgets(this); + this->DrawWidgets(); tbl = _legend_table[_smallmap_type]; @@ -1078,7 +1078,6 @@ WC_SMALLMAP, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE, _smallmap_widgets, - NULL }; void ShowSmallMap() @@ -1103,88 +1102,127 @@ { WIDGETS_END}, }; -static void ExtraViewPortWndProc(Window *w, WindowEvent *e) +class ExtraViewportWindow : public Window { - switch (e->event) { - case WE_CREATE: // Disable zoom in button - /* New viewport start at (zero,zero) */ - InitializeWindowViewport(w, 3, 17, w->widget[4].right - w->widget[4].left - 1, w->widget[4].bottom - w->widget[4].top - 1, 0, ZOOM_LVL_VIEWPORT); - - w->DisableWidget(5); - break; - - case WE_PAINT: - /* set the number in the title bar */ - SetDParam(0, w->window_number + 1); - - DrawWindowWidgets(w); - DrawWindowViewport(w); - break; - - case WE_CLICK: - switch (e->we.click.widget) { - case 5: DoZoomInOutWindow(ZOOM_IN, w); break; - case 6: DoZoomInOutWindow(ZOOM_OUT, w); break; - - case 7: { // location button (move main view to same spot as this view) 'Paste Location' - Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0); - int x = w->viewport->scrollpos_x; // Where is the main looking at - int y = w->viewport->scrollpos_y; - - /* set this view to same location. Based on the center, adjusting for zoom */ - w2->viewport->dest_scrollpos_x = x - (w2->viewport->virtual_width - w->viewport->virtual_width) / 2; - w2->viewport->dest_scrollpos_y = y - (w2->viewport->virtual_height - w->viewport->virtual_height) / 2; - } break; + enum ExtraViewportWindowWidgets { + EVW_CLOSE, + EVW_CAPTION, + EVW_STICKY, + EVW_BACKGROUND, + EVW_VIEWPORT, + EVW_ZOOMIN, + EVW_ZOOMOUT, + EVW_MAIN_TO_VIEW, + EVW_VIEW_TO_MAIN, + EVW_SPACER1, + EVW_SPACER2, + EVW_RESIZE, + }; - case 8: { // inverse location button (move this view to same spot as main view) 'Copy Location' - const Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0); - int x = w2->viewport->scrollpos_x; - int y = w2->viewport->scrollpos_y; - - w->viewport->dest_scrollpos_x = x + (w2->viewport->virtual_width - w->viewport->virtual_width) / 2; - w->viewport->dest_scrollpos_y = y + (w2->viewport->virtual_height - w->viewport->virtual_height) / 2; - } break; - } - break; - - case WE_RESIZE: - w->viewport->width += e->we.sizing.diff.x; - w->viewport->height += e->we.sizing.diff.y; - w->viewport->virtual_width += e->we.sizing.diff.x; - w->viewport->virtual_height += e->we.sizing.diff.y; - break; - - case WE_SCROLL: { - ViewPort *vp = IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y); +public: + ExtraViewportWindow(const WindowDesc *desc, int window_number, TileIndex tile) : Window(desc, window_number) + { + /* New viewport start at (zero,zero) */ + InitializeWindowViewport(this, 3, 17, this->widget[EVW_VIEWPORT].right - this->widget[EVW_VIEWPORT].left - 1, this->widget[EVW_VIEWPORT].bottom - this->widget[EVW_VIEWPORT].top - 1, 0, ZOOM_LVL_VIEWPORT); - if (vp == NULL) { - _cursor.fix_at = false; - _scrolling_viewport = false; - } + this->DisableWidget(EVW_ZOOMIN); + this->FindWindowPlacementAndResize(desc); - w->viewport->scrollpos_x += ScaleByZoom(e->we.scroll.delta.x, vp->zoom); - w->viewport->scrollpos_y += ScaleByZoom(e->we.scroll.delta.y, vp->zoom); - w->viewport->dest_scrollpos_x = w->viewport->scrollpos_x; - w->viewport->dest_scrollpos_y = w->viewport->scrollpos_y; - } break; + Point pt; + if (tile == INVALID_TILE) { + /* the main window with the main view */ + const Window *w = FindWindowById(WC_MAIN_WINDOW, 0); - case WE_MOUSEWHEEL: - ZoomInOrOutToCursorWindow(e->we.wheel.wheel < 0, w); - break; + /* center on same place as main window (zoom is maximum, no adjustment needed) */ + pt.x = w->viewport->scrollpos_x + w->viewport->virtual_height / 2; + pt.y = w->viewport->scrollpos_y + w->viewport->virtual_height / 2; + } else { + pt = RemapCoords(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, TileHeight(tile)); + } - case WE_INVALIDATE_DATA: - /* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */ - HandleZoomMessage(w, w->viewport, 5, 6); - break; + this->viewport->scrollpos_x = pt.x - ((this->widget[EVW_VIEWPORT].right - this->widget[EVW_VIEWPORT].left) - 1) / 2; + this->viewport->scrollpos_y = pt.y - ((this->widget[EVW_VIEWPORT].bottom - this->widget[EVW_VIEWPORT].top) - 1) / 2; + this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x; + this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y; + } -} + + virtual void OnPaint() + { + /* set the number in the title bar */ + SetDParam(0, this->window_number + 1); + + this->DrawWidgets(); + this->DrawViewport(); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case EVW_ZOOMIN: DoZoomInOutWindow(ZOOM_IN, this); break; + case EVW_ZOOMOUT: DoZoomInOutWindow(ZOOM_OUT, this); break; + + case EVW_MAIN_TO_VIEW: { // location button (move main view to same spot as this view) 'Paste Location' + Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + int x = this->viewport->scrollpos_x; // Where is the main looking at + int y = this->viewport->scrollpos_y; + + /* set this view to same location. Based on the center, adjusting for zoom */ + w->viewport->dest_scrollpos_x = x - (w->viewport->virtual_width - this->viewport->virtual_width) / 2; + w->viewport->dest_scrollpos_y = y - (w->viewport->virtual_height - this->viewport->virtual_height) / 2; + } break; + + case EVW_VIEW_TO_MAIN: { // inverse location button (move this view to same spot as main view) 'Copy Location' + const Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + int x = w->viewport->scrollpos_x; + int y = w->viewport->scrollpos_y; + + this->viewport->dest_scrollpos_x = x + (w->viewport->virtual_width - this->viewport->virtual_width) / 2; + this->viewport->dest_scrollpos_y = y + (w->viewport->virtual_height - this->viewport->virtual_height) / 2; + } break; + } + } + + virtual void OnResize(Point new_size, Point delta) + { + this->viewport->width += delta.x; + this->viewport->height += delta.y; + this->viewport->virtual_width += delta.x; + this->viewport->virtual_height += delta.y; + } + + virtual void OnScroll(Point delta) + { + ViewPort *vp = IsPtInWindowViewport(this, _cursor.pos.x, _cursor.pos.y); + + if (vp == NULL) { + _cursor.fix_at = false; + _scrolling_viewport = false; + } + + this->viewport->scrollpos_x += ScaleByZoom(delta.x, vp->zoom); + this->viewport->scrollpos_y += ScaleByZoom(delta.y, vp->zoom); + this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x; + this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y; + } + + virtual void OnMouseWheel(int wheel) + { + ZoomInOrOutToCursorWindow(wheel < 0, this); + } + + virtual void OnInvalidateData(int data = 0) + { + /* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */ + HandleZoomMessage(this, this->viewport, EVW_ZOOMIN, EVW_ZOOMOUT); + } +}; static const WindowDesc _extra_view_port_desc = { WDP_AUTO, WDP_AUTO, 300, 68, 300, 268, WC_EXTRA_VIEW_PORT, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _extra_view_port_widgets, - ExtraViewPortWndProc }; void ShowExtraViewPortWindow(TileIndex tile) @@ -1194,26 +1232,7 @@ /* find next free window number for extra viewport */ while (FindWindowById(WC_EXTRA_VIEW_PORT, i) != NULL) i++; - Window *w = AllocateWindowDescFront(&_extra_view_port_desc, i); - if (w != NULL) { - Point pt; - - if (tile == INVALID_TILE) { - /* the main window with the main view */ - const Window *v = FindWindowById(WC_MAIN_WINDOW, 0); - - /* center on same place as main window (zoom is maximum, no adjustment needed) */ - pt.x = v->viewport->scrollpos_x + v->viewport->virtual_height / 2; - pt.y = v->viewport->scrollpos_y + v->viewport->virtual_height / 2; - } else { - pt = RemapCoords(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, TileHeight(tile)); - } - - w->viewport->scrollpos_x = pt.x - ((w->widget[4].right - w->widget[4].left) - 1) / 2; - w->viewport->scrollpos_y = pt.y - ((w->widget[4].bottom - w->widget[4].top) - 1) / 2; - w->viewport->dest_scrollpos_x = w->viewport->scrollpos_x; - w->viewport->dest_scrollpos_y = w->viewport->scrollpos_y; - } + new ExtraViewportWindow(&_extra_view_port_desc, i, tile); } bool ScrollMainWindowTo(int x, int y, bool instant) diff -r 6c4314786d68 -r 8cbdb511a674 src/sortlist_type.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sortlist_type.h Mon May 19 15:13:58 2008 +0000 @@ -0,0 +1,31 @@ +/* $Id$ */ + +/** @file sortlist_type.h Base types for having sorted lists in GUIs. */ + +#ifndef SORTLIST_TYPE_H +#define SORTLIST_TYPE_H + +enum SortListFlags { + VL_NONE = 0, ///< no sort + VL_DESC = 1 << 0, ///< sort descending or ascending + VL_RESORT = 1 << 1, ///< instruct the code to resort the list in the next loop + VL_REBUILD = 1 << 2, ///< create sort-listing to use for qsort and friends + VL_END = 1 << 3, +}; +DECLARE_ENUM_AS_BIT_SET(SortListFlags); + +struct Listing { + bool order; ///< Ascending/descending + byte criteria; ///< Sorting criteria +}; + +template +struct GUIList { + T* sort_list; ///< The items to sort. + SortListFlags flags; ///< used to control sorting/resorting/etc. + uint16 list_length; ///< length of the list being sorted + uint16 resort_timer; ///< resort list after a given amount of ticks if set + byte sort_type; ///< what criteria to sort on +}; + +#endif /* SORTLIST_TYPE_H */ diff -r 6c4314786d68 -r 8cbdb511a674 src/station.cpp --- a/src/station.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/station.cpp Mon May 19 15:13:58 2008 +0000 @@ -74,8 +74,7 @@ } MarkDirty(); - RebuildStationLists(); - InvalidateWindowClasses(WC_STATION_LIST); + InvalidateWindowData(WC_STATION_LIST, this->owner, 0); DeleteWindowById(WC_STATION_VIEW, index); diff -r 6c4314786d68 -r 8cbdb511a674 src/station_cmd.cpp --- a/src/station_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/station_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -409,7 +409,7 @@ } SetDParam(0, st->index); - AddNewsItem(msg, NM_SMALL, NF_VIEWPORT | NF_TILE, NT_ACCEPTANCE, DNC_NONE, st->xy, 0); + AddNewsItem(msg, NS_ACCEPTANCE, st->xy, 0); } /** @@ -650,8 +650,7 @@ { if (st->facilities == 0) { st->delete_ctr = 0; - RebuildStationLists(); - InvalidateWindow(WC_STATION_LIST, st->owner); + InvalidateWindowData(WC_STATION_LIST, st->owner, 0); } /* station remains but it probably lost some parts - station sign should stay in the station boundaries */ UpdateStationSignCoord(st); @@ -1051,8 +1050,7 @@ st->MarkTilesDirty(false); UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); - RebuildStationLists(); - InvalidateWindow(WC_STATION_LIST, st->owner); + InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_TRAINS); } @@ -1410,8 +1408,7 @@ UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); - RebuildStationLists(); - InvalidateWindow(WC_STATION_LIST, st->owner); + InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_ROADVEHS); } return cost; @@ -1723,8 +1720,7 @@ UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); - RebuildStationLists(); - InvalidateWindow(WC_STATION_LIST, st->owner); + InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES); } @@ -1820,8 +1816,7 @@ UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); - RebuildStationLists(); - InvalidateWindow(WC_STATION_LIST, st->owner); + InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS); } @@ -1979,8 +1974,7 @@ UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); - RebuildStationLists(); - InvalidateWindow(WC_STATION_LIST, st->owner); + InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS); } @@ -2637,7 +2631,7 @@ st->name = strdup(_cmd_text); UpdateStationVirtCoord(st); - ResortStationLists(); + InvalidateWindowData(WC_STATION_LIST, st->owner, 1); MarkWholeScreenDirty(); } @@ -2858,8 +2852,7 @@ SetTileOwner(tile, new_player); if (!IsBuoy(tile)) st->owner = new_player; // do not set st->owner for buoys - RebuildStationLists(); - InvalidateWindowClasses(WC_STATION_LIST); + InvalidateWindowClassesData(WC_STATION_LIST, 0); } else { if (IsDriveThroughStopTile(tile)) { /* Remove the drive-through road stop */ diff -r 6c4314786d68 -r 8cbdb511a674 src/station_gui.cpp --- a/src/station_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/station_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -168,39 +168,6 @@ typedef GUIList GUIStationList; /** - * Set the station sort flag for all station-list windows. - * @param sl_flag Sort list flag to set for all station-list windows - */ -static void SetStationListsFlag(SortListFlags sl_flag) -{ - Window *const *wz; - - FOR_ALL_WINDOWS(wz) { - Window *w = *wz; - if (w->window_class == WC_STATION_LIST) { - dynamic_cast(w)->flags |= sl_flag; - w->SetDirty(); - } - } -} - -/** - * Set the 'VL_REBUILD' flag for all station lists - */ -void RebuildStationLists() -{ - SetStationListsFlag(VL_REBUILD); -} - -/** - * Set the 'VL_RESORT' flag for all station lists - */ -void ResortStationLists() -{ - SetStationListsFlag(VL_RESORT); -} - -/** * Rebuild station list if the VL_REBUILD flag is set * * @param sl pointer to plstations_d (station list and flags) @@ -287,7 +254,7 @@ static byte facilities; static bool include_empty; - PlayerStationsWindow(const WindowDesc *desc, void *data, WindowNumber window_number) : Window(desc, data, window_number) + PlayerStationsWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) { this->caption_color = (byte)this->window_number; this->vscroll.cap = 12; @@ -369,10 +336,10 @@ /* Set text of sort by dropdown */ this->widget[SLW_SORTDROPBTN].data = _station_sort_listing[this->sort_type]; - DrawWindowWidgets(this); + this->DrawWidgets(); /* draw arrow pointing up/down for ascending/descending sorting */ - DrawSortButtonState(this, SLW_SORTBY, this->flags & VL_DESC ? SBS_DOWN : SBS_UP); + this->DrawSortButtonState(SLW_SORTBY, this->flags & VL_DESC ? SBS_DOWN : SBS_UP); int cg_ofst; int x = 89; @@ -436,8 +403,6 @@ virtual void OnClick(Point pt, int widget) { - PlayerID owner = (PlayerID)this->window_number; - switch (widget) { case SLW_LIST: { uint32 id_v = (pt.y - 41) / 10; @@ -450,7 +415,7 @@ const Station *st = this->sort_list[id_v]; /* do not check HasStationInUse - it is slow and may be invalid */ - assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy())); + assert(st->owner == (PlayerID)this->window_number || (st->owner == OWNER_NONE && !st->IsBuoy())); if (_ctrl_pressed) { ShowExtraViewPortWindow(st->xy); @@ -607,6 +572,11 @@ { this->vscroll.cap += delta.y / 10; } + + virtual void OnInvalidateData(int data) + { + this->flags |= (data == 0 ? VL_REBUILD : VL_RESORT); + } }; Listing PlayerStationsWindow::station_sort = {0, 0}; @@ -645,7 +615,6 @@ WC_STATION_LIST, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE, _player_stations_widgets, - NULL }; /** @@ -737,7 +706,7 @@ uint32 cargo; ///< Bitmask of cargo types to expand uint16 cargo_rows[NUM_CARGO]; ///< Header row for each cargo type - StationViewWindow(const WindowDesc *desc, void *data, WindowNumber window_number) : Window(desc, data, window_number) + StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) { PlayerID owner = GetStation(window_number)->owner; if (owner != OWNER_NONE) this->caption_color = owner; @@ -815,7 +784,7 @@ SetDParam(0, st->index); SetDParam(1, st->facilities); - DrawWindowWidgets(this); + this->DrawWidgets(); int x = 2; ///< coordinates used for printing waiting/accepted/rating of cargo int y = 15; @@ -1017,7 +986,6 @@ WC_STATION_VIEW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _station_view_widgets, - NULL }; /** diff -r 6c4314786d68 -r 8cbdb511a674 src/station_gui.h --- a/src/station_gui.h Mon May 19 14:14:33 2008 +0000 +++ b/src/station_gui.h Mon May 19 15:13:58 2008 +0000 @@ -48,10 +48,6 @@ SVW_RESIZE, ///< Resize button }; -/* sorter stuff */ -void RebuildStationLists(); -void ResortStationLists(); - enum StationCoverageType { SCT_PASSENGERS_ONLY, SCT_NON_PASSENGERS_ONLY, diff -r 6c4314786d68 -r 8cbdb511a674 src/statusbar_gui.cpp --- a/src/statusbar_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/statusbar_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -18,21 +18,15 @@ #include "window_gui.h" #include "variables.h" #include "window_func.h" +#include "statusbar_gui.h" #include "table/strings.h" #include "table/sprites.h" -extern GetNewsStringCallbackProc * const _get_news_string_callback[]; - static bool DrawScrollingStatusText(const NewsItem *ni, int pos, int width) { - StringID str; - if (ni->display_mode == NM_CALLBACK) { - str = _get_news_string_callback[ni->callback](ni); - } else { - CopyInDParam(0, ni->params, lengthof(ni->params)); - str = ni->string_id; - } + CopyInDParam(0, ni->params, lengthof(ni->params)); + StringID str = ni->string_id; char buf[512]; GetString(buf, str, lastof(buf)); @@ -69,80 +63,98 @@ return x > 0; } -static void StatusBarWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - const Player *p = (_local_player == PLAYER_SPECTATOR) ? NULL : GetPlayer(_local_player); - - DrawWindowWidgets(w); - SetDParam(0, _date); - DrawStringCentered(70, 1, (_pause_game || _patches.status_long_date) ? STR_00AF : STR_00AE, TC_FROMSTRING); - - if (p != NULL) { - /* Draw player money */ - SetDParam(0, p->player_money); - DrawStringCentered(w->widget[2].left + 70, 1, STR_0004, TC_FROMSTRING); - } +struct StatusBarWindow : Window { + bool saving; + int ticker_scroll; + int reminder_timeout; - /* Draw status bar */ - if (WP(w, def_d).data_3) { // true when saving is active - DrawStringCenteredTruncated(w->widget[1].left + 1, w->widget[1].right - 1, 1, STR_SAVING_GAME, TC_FROMSTRING); - } else if (_do_autosave) { - DrawStringCenteredTruncated(w->widget[1].left + 1, w->widget[1].right - 1, 1, STR_032F_AUTOSAVE, TC_FROMSTRING); - } else if (_pause_game) { - DrawStringCenteredTruncated(w->widget[1].left + 1, w->widget[1].right - 1, 1, STR_0319_PAUSED, TC_FROMSTRING); - } else if (WP(w, def_d).data_1 > -1280 && FindWindowById(WC_NEWS_WINDOW,0) == NULL && _statusbar_news_item.string_id != 0) { - /* Draw the scrolling news text */ - if (!DrawScrollingStatusText(&_statusbar_news_item, WP(w, def_d).data_1, w->widget[1].right - w->widget[1].left - 2)) { - WP(w, def_d).data_1 = -1280; - if (p != NULL) { - /* This is the default text */ - SetDParam(0, p->index); - DrawStringCenteredTruncated(w->widget[1].left + 1, w->widget[1].right - 1, 1, STR_02BA, TC_FROMSTRING); - } - } - } else { + StatusBarWindow(const WindowDesc *desc) : Window(desc) + { + CLRBITS(this->flags4, WF_WHITE_BORDER_MASK); + this->ticker_scroll = -1280; + + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { + const Player *p = (_local_player == PLAYER_SPECTATOR) ? NULL : GetPlayer(_local_player); + + this->DrawWidgets(); + SetDParam(0, _date); + DrawStringCentered(70, 1, (_pause_game || _patches.status_long_date) ? STR_00AF : STR_00AE, TC_FROMSTRING); + + if (p != NULL) { + /* Draw player money */ + SetDParam(0, p->player_money); + DrawStringCentered(this->widget[2].left + 70, 1, STR_0004, TC_FROMSTRING); + } + + /* Draw status bar */ + if (this->saving) { // true when saving is active + DrawStringCenteredTruncated(this->widget[1].left + 1, this->widget[1].right - 1, 1, STR_SAVING_GAME, TC_FROMSTRING); + } else if (_do_autosave) { + DrawStringCenteredTruncated(this->widget[1].left + 1, this->widget[1].right - 1, 1, STR_032F_AUTOSAVE, TC_FROMSTRING); + } else if (_pause_game) { + DrawStringCenteredTruncated(this->widget[1].left + 1, this->widget[1].right - 1, 1, STR_0319_PAUSED, TC_FROMSTRING); + } else if (this->ticker_scroll > -1280 && FindWindowById(WC_NEWS_WINDOW, 0) == NULL && _statusbar_news_item.string_id != 0) { + /* Draw the scrolling news text */ + if (!DrawScrollingStatusText(&_statusbar_news_item, this->ticker_scroll, this->widget[1].right - this->widget[1].left - 2)) { + this->ticker_scroll = -1280; if (p != NULL) { /* This is the default text */ SetDParam(0, p->index); - DrawStringCenteredTruncated(w->widget[1].left + 1, w->widget[1].right - 1, 1, STR_02BA, TC_FROMSTRING); + DrawStringCenteredTruncated(this->widget[1].left + 1, this->widget[1].right - 1, 1, STR_02BA, TC_FROMSTRING); } } - - if (WP(w, def_d).data_2 > 0) DrawSprite(SPR_BLOT, PALETTE_TO_RED, w->widget[1].right - 11, 2); - } break; - - case WE_INVALIDATE_DATA: - WP(w, def_d).data_3 = e->we.invalidate.data; - break; - - case WE_CLICK: - switch (e->we.click.widget) { - case 1: ShowLastNewsMessage(); break; - case 2: if (_local_player != PLAYER_SPECTATOR) ShowPlayerFinances(_local_player); break; - default: ResetObjectToPlace(); + } else { + if (p != NULL) { + /* This is the default text */ + SetDParam(0, p->index); + DrawStringCenteredTruncated(this->widget[1].left + 1, this->widget[1].right - 1, 1, STR_02BA, TC_FROMSTRING); } - break; + } - case WE_TICK: { - if (_pause_game) return; - - if (WP(w, def_d).data_1 > -1280) { // Scrolling text - WP(w, def_d).data_1 -= 2; - w->InvalidateWidget(1); - } + if (this->reminder_timeout > 0) DrawSprite(SPR_BLOT, PALETTE_TO_RED, this->widget[1].right - 11, 2); + } - if (WP(w, def_d).data_2 > 0) { // Red blot to show there are new unread newsmessages - WP(w, def_d).data_2 -= 2; - } else if (WP(w, def_d).data_2 < 0) { - WP(w, def_d).data_2 = 0; - w->InvalidateWidget(1); - } + virtual void OnInvalidateData(int data) + { + switch (data) { + default: NOT_REACHED(); + case SBI_SAVELOAD_START: this->saving = true; break; + case SBI_SAVELOAD_FINISH: this->saving = false; break; + case SBI_SHOW_TICKER: this->ticker_scroll = 360; break; + case SBI_SHOW_REMINDER: this->reminder_timeout = 91; break; + } + } - } break; + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 1: ShowLastNewsMessage(); break; + case 2: if (_local_player != PLAYER_SPECTATOR) ShowPlayerFinances(_local_player); break; + default: ResetObjectToPlace(); + } } -} + + virtual void OnTick() + { + if (_pause_game) return; + + if (this->ticker_scroll > -1280) { // Scrolling text + this->ticker_scroll -= 2; + this->InvalidateWidget(1); + } + + if (this->reminder_timeout > 0) { // Red blot to show there are new unread newsmessages + this->reminder_timeout -= 2; + } else if (this->reminder_timeout < 0) { + this->reminder_timeout = 0; + this->InvalidateWidget(1); + } + } +}; static const Widget _main_status_widgets[] = { { WWT_PANEL, RESIZE_NONE, 14, 0, 139, 0, 11, 0x0, STR_NULL}, @@ -156,15 +168,19 @@ WC_STATUS_BAR, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _main_status_widgets, - StatusBarWndProc }; +/** + * Checks whether the news ticker is currently being used. + */ +bool IsNewsTickerShown() +{ + const StatusBarWindow *w = dynamic_cast(FindWindowById(WC_STATUS_BAR, 0)); + return w != NULL && w->ticker_scroll > -1280; +} + void ShowStatusBar() { _main_status_desc.top = _screen.height - 12; - Window *w = new Window(&_main_status_desc); - if (w != NULL) { - CLRBITS(w->flags4, WF_WHITE_BORDER_MASK); - WP(w, def_d).data_1 = -1280; - } + new StatusBarWindow(&_main_status_desc); } diff -r 6c4314786d68 -r 8cbdb511a674 src/statusbar_gui.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/statusbar_gui.h Mon May 19 15:13:58 2008 +0000 @@ -0,0 +1,20 @@ +/* $Id$ */ + +/** @file statusbar_gui.h Functions, definitions and such used only by the GUI. */ + +#ifndef STATUSBAR_GUI_H +#define STATUSBAR_GUI_H + +enum StatusBarInvalidate +{ + SBI_SAVELOAD_START, + SBI_SAVELOAD_FINISH, + SBI_SHOW_TICKER, + SBI_SHOW_REMINDER, + SBI_END +}; + +bool IsNewsTickerShown(); +void ShowStatusBar(); + +#endif /* STATUSBAR_GUI_H */ diff -r 6c4314786d68 -r 8cbdb511a674 src/stdafx.h --- a/src/stdafx.h Mon May 19 14:14:33 2008 +0000 +++ b/src/stdafx.h Mon May 19 15:13:58 2008 +0000 @@ -145,6 +145,10 @@ #include // alloca() #endif +#if defined(WIN32) + #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#endif + /* Stuff for MSVC */ #if defined(_MSC_VER) #pragma once @@ -158,7 +162,6 @@ #endif #define _WIN32_IE_ 0x0401 // 4.01 (win98 and NT4SP5+) - #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #pragma warning(disable: 4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data #pragma warning(disable: 4761) // integral size mismatch in argument : conversion supplied #pragma warning(disable: 4200) // nonstandard extension used : zero-sized array in struct/union diff -r 6c4314786d68 -r 8cbdb511a674 src/subsidy_gui.cpp --- a/src/subsidy_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/subsidy_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -19,161 +19,149 @@ #include "table/strings.h" -static void HandleSubsidyClick(int y) -{ - const Subsidy *s; - uint num; - int offs; - TileIndex xy; +struct SubsidyListWindow : Window { + SubsidyListWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + } - if (y < 0) return; + virtual void OnClick(Point pt, int widget) + { + if (widget != 3) return; - num = 0; - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type != CT_INVALID && s->age < 12) { + int y = pt.y - 25; + + if (y < 0) return; + + uint num = 0; + for (const Subsidy *s = _subsidies; s != endof(_subsidies); s++) { + if (s->cargo_type != CT_INVALID && s->age < 12) { + y -= 10; + if (y < 0) this->HandleClick(s); + num++; + } + } + + if (num == 0) { y -= 10; - if (y < 0) goto handle_click; - num++; + if (y < 0) return; + } + + y -= 11; + if (y < 0) return; + + for (const Subsidy *s = _subsidies; s != endof(_subsidies); s++) { + if (s->cargo_type != CT_INVALID && s->age >= 12) { + y -= 10; + if (y < 0) this->HandleClick(s); + } } } - if (num == 0) { - y -= 10; - if (y < 0) return; - } - - y -= 11; - if (y < 0) return; - - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type != CT_INVALID && s->age >= 12) { - y -= 10; - if (y < 0) goto handle_click; - } - } - return; + void HandleClick(const Subsidy *s) + { + TownEffect te = GetCargo(s->cargo_type)->town_effect; + TileIndex xy; -handle_click: - - TownEffect te = GetCargo(s->cargo_type)->town_effect; - - /* determine from coordinate for subsidy and try to scroll to it */ - offs = s->from; - if (s->age >= 12) { - xy = GetStation(offs)->xy; - } else if (te == TE_PASSENGERS || te == TE_MAIL) { - xy = GetTown(offs)->xy; - } else { - xy = GetIndustry(offs)->xy; - } - - if (_ctrl_pressed || !ScrollMainWindowToTile(xy)) { - if (_ctrl_pressed) ShowExtraViewPortWindow(xy); - - /* otherwise determine to coordinate for subsidy and scroll to it */ - offs = s->to; + /* determine from coordinate for subsidy and try to scroll to it */ + uint offs = s->from; if (s->age >= 12) { xy = GetStation(offs)->xy; - } else if (te == TE_PASSENGERS || te == TE_MAIL || te == TE_GOODS || te == TE_FOOD) { + } else if (te == TE_PASSENGERS || te == TE_MAIL) { xy = GetTown(offs)->xy; } else { xy = GetIndustry(offs)->xy; } - if (_ctrl_pressed) { - ShowExtraViewPortWindow(xy); - } else { - ScrollMainWindowToTile(xy); - } - } -} - -static void DrawSubsidiesWindow(const Window *w) -{ - YearMonthDay ymd; - const Subsidy *s; - uint num; - int x; - int y; - - DrawWindowWidgets(w); - - ConvertDateToYMD(_date, &ymd); + if (_ctrl_pressed || !ScrollMainWindowToTile(xy)) { + if (_ctrl_pressed) ShowExtraViewPortWindow(xy); - int width = w->width - 13; // scroll bar = 11 + pixel each side - y = 15; - x = 1; - - /* Section for drawing the offered subisidies */ - DrawStringTruncated(x, y, STR_2026_SUBSIDIES_ON_OFFER_FOR, TC_FROMSTRING, width); - y += 10; - num = 0; + /* otherwise determine to coordinate for subsidy and scroll to it */ + offs = s->to; + if (s->age >= 12) { + xy = GetStation(offs)->xy; + } else if (te == TE_PASSENGERS || te == TE_MAIL || te == TE_GOODS || te == TE_FOOD) { + xy = GetTown(offs)->xy; + } else { + xy = GetIndustry(offs)->xy; + } - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type != CT_INVALID && s->age < 12) { - int x2; - - /* Displays the two offered towns */ - SetupSubsidyDecodeParam(s, 1); - x2 = DrawStringTruncated(x + 2, y, STR_2027_FROM_TO, TC_FROMSTRING, width); - - /* Displays the deadline before voiding the proposal */ - SetDParam(0, _date - ymd.day + 384 - s->age * 32); - DrawStringTruncated(x2, y, STR_2028_BY, TC_FROMSTRING, width - x2); - y += 10; - num++; + if (_ctrl_pressed) { + ShowExtraViewPortWindow(xy); + } else { + ScrollMainWindowToTile(xy); + } } } - if (num == 0) { - DrawStringTruncated(x + 2, y, STR_202A_NONE, TC_FROMSTRING, width - 2); - y += 10; - } - - /* Section for drawing the already granted subisidies */ - DrawStringTruncated(x, y + 1, STR_202B_SERVICES_ALREADY_SUBSIDISED, TC_FROMSTRING, width); - y += 10; - num = 0; - - for (s = _subsidies; s != endof(_subsidies); s++) { - if (s->cargo_type != CT_INVALID && s->age >= 12) { - int xt; - - SetupSubsidyDecodeParam(s, 1); - - PlayerID player = GetStation(s->to)->owner; - SetDParam(3, player); - - /* Displays the two connected stations */ - xt = DrawStringTruncated(x + 2, y, STR_202C_FROM_TO, TC_FROMSTRING, width - 2); + virtual void OnPaint() + { + YearMonthDay ymd; + const Subsidy *s; - /* Displays the date where the granted subsidy will end */ - if ((xt > 3) && (width - xt) > 9 ) { // do not draw if previous drawing failed or if it will overlap on scrollbar - SetDParam(0, _date - ymd.day + 768 - s->age * 32); - DrawStringTruncated(xt, y, STR_202D_UNTIL, TC_FROMSTRING, width - xt); - } - y += 10; - num++; - } - } - - if (num == 0) DrawStringTruncated(x + 2, y, STR_202A_NONE, TC_FROMSTRING, width - 2); -} + this->DrawWidgets(); -static void SubsidiesListWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: DrawSubsidiesWindow(w); break; + ConvertDateToYMD(_date, &ymd); - case WE_CLICK: - switch (e->we.click.widget) { - case 3: - HandleSubsidyClick(e->we.click.pt.y - 25); - break; + int width = this->width - 13; // scroll bar = 11 + pixel each side + int y = 15; + int x = 1; + + /* Section for drawing the offered subisidies */ + DrawStringTruncated(x, y, STR_2026_SUBSIDIES_ON_OFFER_FOR, TC_FROMSTRING, width); + y += 10; + uint num = 0; + + for (s = _subsidies; s != endof(_subsidies); s++) { + if (s->cargo_type != CT_INVALID && s->age < 12) { + int x2; + + /* Displays the two offered towns */ + SetupSubsidyDecodeParam(s, 1); + x2 = DrawStringTruncated(x + 2, y, STR_2027_FROM_TO, TC_FROMSTRING, width); + + /* Displays the deadline before voiding the proposal */ + SetDParam(0, _date - ymd.day + 384 - s->age * 32); + DrawStringTruncated(x2, y, STR_2028_BY, TC_FROMSTRING, width - x2); + y += 10; + num++; } - break; + } + + if (num == 0) { + DrawStringTruncated(x + 2, y, STR_202A_NONE, TC_FROMSTRING, width - 2); + y += 10; + } + + /* Section for drawing the already granted subisidies */ + DrawStringTruncated(x, y + 1, STR_202B_SERVICES_ALREADY_SUBSIDISED, TC_FROMSTRING, width); + y += 10; + num = 0; + + for (s = _subsidies; s != endof(_subsidies); s++) { + if (s->cargo_type != CT_INVALID && s->age >= 12) { + int xt; + + SetupSubsidyDecodeParam(s, 1); + + PlayerID player = GetStation(s->to)->owner; + SetDParam(3, player); + + /* Displays the two connected stations */ + xt = DrawStringTruncated(x + 2, y, STR_202C_FROM_TO, TC_FROMSTRING, width - 2); + + /* Displays the date where the granted subsidy will end */ + if ((xt > 3) && (width - xt) > 9 ) { // do not draw if previous drawing failed or if it will overlap on scrollbar + SetDParam(0, _date - ymd.day + 768 - s->age * 32); + DrawStringTruncated(xt, y, STR_202D_UNTIL, TC_FROMSTRING, width - xt); + } + y += 10; + num++; + } + } + + if (num == 0) DrawStringTruncated(x + 2, y, STR_202A_NONE, TC_FROMSTRING, width - 2); } -} +}; static const Widget _subsidies_list_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -191,11 +179,10 @@ WC_SUBSIDIES_LIST, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE, _subsidies_list_widgets, - SubsidiesListWndProc }; void ShowSubsidiesList() { - AllocateWindowDescFront(&_subsidies_list_desc, 0); + AllocateWindowDescFront(&_subsidies_list_desc, 0); } diff -r 6c4314786d68 -r 8cbdb511a674 src/table/engines.h --- a/src/table/engines.h Mon May 19 14:14:33 2008 +0000 +++ b/src/table/engines.h Mon May 19 15:13:58 2008 +0000 @@ -387,8 +387,8 @@ RVI( 0, G, 13, 112, 1000, 131, 120, RC_S, 0, 0 , 20, R, S), // 8 RVI( 1, G, 19, 128, 1200, 162, 140, RC_S, 0, 0 , 21, R, S), // 9 RVI( 0, G, 22, 144, 1600, 170, 130, RC_S, 0, 0 , 22, R, S), // 10 - RVI( 8, M, 11, 112, 600/2, 32, 85/2, RC_D, 38, CT_PASSENGERS , 10, R, D), // 11 - RVI(10, M, 14, 120, 700/2, 38, 70/2, RC_D, 40, CT_PASSENGERS , 11, R, D), // 12 + RVI( 8, M, 11, 112, 600, 32, 85, RC_D, 38, CT_PASSENGERS , 10, R, D), // 11 + RVI(10, M, 14, 120, 700, 38, 70, RC_D, 40, CT_PASSENGERS , 11, R, D), // 12 RVI( 4, G, 15, 128, 1250, 72, 95, RC_D, 0, 0 , 30, R, D), // 13 RVI( 5, G, 17, 144, 1750, 101, 120, RC_D, 0, 0 , 31, R, D), // 14 RVI( 4, G, 18, 160, 2580, 112, 140, RC_D, 0, 0 , 32, R, D), // 15 @@ -396,13 +396,13 @@ RVI(12, G, 16, 112, 2400, 120, 105, RC_D, 0, 0 , 34, R, D), // 17 RVI(13, G, 30, 112, 6600, 207, 155, RC_D, 0, 0 , 35, R, D), // 18 RVI(15, G, 18, 104, 1500, 110, 105, RC_D, 0, 0 , 29, R, D), // 19 - RVI(16, M, 35, 160, 3500/2, 95, 205/2, RC_D, 0, 0 , 45, R, D), // 20 + RVI(16, M, 35, 160, 3500, 95, 205, RC_D, 0, 0 , 45, R, D), // 20 RVI(18, G, 21, 104, 2200, 120, 145, RC_D, 0, 0 , 32, R, D), // 21 - RVI( 6, M, 20, 200, 4500/2, 70, 190/2, RC_D, 4, CT_MAIL , 50, R, D), // 22 + RVI( 6, M, 20, 200, 4500, 70, 190, RC_D, 4, CT_MAIL , 50, R, D), // 22 RVI(20, G, 26, 160, 3600, 84, 180, RC_E, 0, 0 , 40, C, E), // 23 RVI(20, G, 30, 176, 5000, 82, 205, RC_E, 0, 0 , 41, C, E), // 24 - RVI(21, M, 40, 240, 7000/2, 90, 240/2, RC_E, 0, 0 , 51, C, E), // 25 - RVI(23, M, 43, 264, 8000/2, 95, 250/2, RC_E, 0, 0 , 52, C, E), // 26 + RVI(21, M, 40, 240, 7000, 90, 240, RC_E, 0, 0 , 51, C, E), // 25 + RVI(23, M, 43, 264, 8000, 95, 250, RC_E, 0, 0 , 52, C, E), // 26 RVI(33, W, 247, 0, 0, 25, 0, RC_W, 40, CT_PASSENGERS , 0, R, A), // 27 RVI(35, W, 228, 0, 0, 21, 0, RC_W, 30, CT_MAIL , 0, R, A), // 28 RVI(34, W, 176, 0, 0, 18, 0, RC_W, 30, CT_COAL , 0, R, A), // 29 @@ -431,7 +431,7 @@ RVI(58, W, 193, 0, 0, 18, 0, RC_W, 25, CT_FIZZY_DRINKS, 0, R, A), // 52 RVI(59, W, 191, 0, 0, 18, 0, RC_W, 30, CT_PLASTIC , 0, R, A), // 53 RVI(25, G, 52, 304, 9000, 95, 230, RC_E, 0, 0 , 60, O, N), // 54 - RVI(26, M, 60, 336, 10000/2, 85, 240/2, RC_E, 25, CT_PASSENGERS , 62, O, N), // 55 + RVI(26, M, 60, 336, 10000, 85, 240, RC_E, 25, CT_PASSENGERS , 62, O, N), // 55 RVI(26, G, 53, 320, 5000, 95, 230, RC_E, 0, 0 , 63, O, N), // 56 RVI(60, W, 247, 0, 0, 25, 0, RC_W, 45, CT_PASSENGERS , 0, O, A), // 57 RVI(62, W, 228, 0, 0, 21, 0, RC_W, 35, CT_MAIL , 0, O, A), // 58 @@ -463,7 +463,7 @@ RVI(28, G, 70, 400, 10000, 105, 250, RC_E, 0, 0 , 70, L, V), // 84 RVI(29, G, 74, 448, 12000, 120, 253, RC_E, 0, 0 , 71, L, V), // 85 RVI(30, G, 82, 480, 15000, 130, 254, RC_E, 0, 0 , 72, L, V), // 86 - RVI(31, M, 95, 640, 20000/2, 150, 255/2, RC_E, 0, 0 , 73, L, V), // 87 + RVI(31, M, 95, 640, 20000, 150, 255, RC_E, 0, 0 , 73, L, V), // 87 RVI(28, G, 70, 480, 10000, 120, 250, RC_E, 0, 0 , 74, L, V), // 88 RVI(60, W, 247, 0, 0, 25, 0, RC_W, 47, CT_PASSENGERS , 0, L, A), // 89 RVI(62, W, 228, 0, 0, 21, 0, RC_W, 37, CT_MAIL , 0, L, A), // 90 diff -r 6c4314786d68 -r 8cbdb511a674 src/terraform_cmd.cpp --- a/src/terraform_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/terraform_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -301,7 +301,10 @@ return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE); } /* Check tiletype-specific things, and add extra-cost */ + const bool curr_gen = _generating_world; + if (_game_mode == GM_EDITOR) _generating_world = true; // used to create green terraformed land CommandCost cost = _tile_type_procs[GetTileType(tile)]->terraform_tile_proc(tile, flags | DC_AUTO, z_min * TILE_HEIGHT, tileh); + _generating_world = curr_gen; if (CmdFailed(cost)) { _terraform_err_tile = tile; return cost; diff -r 6c4314786d68 -r 8cbdb511a674 src/terraform_gui.cpp --- a/src/terraform_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/terraform_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -110,12 +110,9 @@ * allows for additional implements that are more local. For example X_Y drag * of convertrail which belongs in rail_gui.cpp and not terraform_gui.cpp **/ -bool GUIPlaceProcDragXY(const WindowEvent *e) +bool GUIPlaceProcDragXY(ViewportDragDropSelectionProcess proc, TileIndex start_tile, TileIndex end_tile) { - TileIndex start_tile = e->we.place.starttile; - TileIndex end_tile = e->we.place.tile; - - switch (e->we.place.select_proc) { + switch (proc) { case DDSP_DEMOLISH_AREA: DoCommandP(end_tile, start_tile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA)); break; @@ -232,56 +229,67 @@ TerraformClick_PlaceSign, }; -static void TerraformToolbWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - DrawWindowWidgets(w); - break; +struct TerraformToolbarWindow : Window { + TerraformToolbarWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->FindWindowPlacementAndResize(desc); + } - case WE_CLICK: - if (e->we.click.widget >= 4) _terraform_button_proc[e->we.click.widget - 4](w); - break; + ~TerraformToolbarWindow() + { + } - case WE_KEYPRESS: { - uint i; + virtual void OnPaint() + { + this->DrawWidgets(); + } - for (i = 0; i != lengthof(_terraform_keycodes); i++) { - if (e->we.keypress.keycode == _terraform_keycodes[i]) { - e->we.keypress.cont = false; - _terraform_button_proc[i](w); - break; + virtual void OnClick(Point pt, int widget) + { + if (widget >= 4) _terraform_button_proc[widget - 4](this); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + for (uint i = 0; i != lengthof(_terraform_keycodes); i++) { + if (keycode == _terraform_keycodes[i]) { + _terraform_button_proc[i](this); + return ES_HANDLED; } } - break; + return ES_NOT_HANDLED; } - case WE_PLACE_OBJ: - _place_proc(e->we.place.tile); - return; + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + _place_proc(tile); + } - case WE_PLACE_DRAG: - VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method); - break; + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) + { + VpSelectTilesWithMethod(pt.x, pt.y, select_method); + } - case WE_PLACE_MOUSEUP: - if (e->we.place.pt.x != -1) { - switch (e->we.place.select_proc) { + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) + { + if (pt.x != -1) { + switch (select_proc) { + default: NOT_REACHED(); case DDSP_DEMOLISH_AREA: case DDSP_RAISE_AND_LEVEL_AREA: case DDSP_LOWER_AND_LEVEL_AREA: case DDSP_LEVEL_AREA: - GUIPlaceProcDragXY(e); + GUIPlaceProcDragXY(select_proc, start_tile, end_tile); break; } } - break; + } - case WE_ABORT_PLACE_OBJ: - w->RaiseButtons(); - break; + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); } -} +}; static const Widget _terraform_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -305,13 +313,12 @@ WC_SCEN_LAND_GEN, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _terraform_widgets, - TerraformToolbWndProc }; void ShowTerraformToolbar(Window *link) { if (!IsValidPlayer(_current_player)) return; - Window *w = AllocateWindowDescFront(&_terraform_desc, 0); + Window *w = AllocateWindowDescFront(&_terraform_desc, 0); if (w != NULL && link != NULL) { /* Align the terraform toolbar under the main toolbar and put the linked * toolbar to left of it @@ -339,8 +346,6 @@ int sizex, sizey; uint h; - _generating_world = true; // used to create green terraformed land - if (_terraform_size == 1) { StringID msg = mode ? STR_0808_CAN_T_RAISE_LAND_HERE : STR_0809_CAN_T_LOWER_LAND_HERE; @@ -376,8 +381,6 @@ } } END_TILE_LOOP(tile2, sizex, sizey, tile) } - - _generating_world = false; } static void PlaceProc_RaiseBigLand(TileIndex tile) @@ -569,122 +572,128 @@ } } -static void ScenEditLandGenWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - /* XXX - lighthouse button is widget 11!! Don't forget when changing */ - w->widget[11].tooltips = (_opt.landscape == LT_TROPIC) ? STR_028F_DEFINE_DESERT_AREA : STR_028D_PLACE_LIGHTHOUSE; - break; - - case WE_PAINT: { - DrawWindowWidgets(w); - - int n = _terraform_size * _terraform_size; - const int8 *coords = &_multi_terraform_coords[0][0]; - - assert(n != 0); - do { - DrawSprite(SPR_WHITE_POINT, PAL_NONE, 88 + coords[0], 55 + coords[1]); - coords += 2; - } while (--n); - - if (w->IsWidgetLowered(5) || w->IsWidgetLowered(6)) // change area-size if raise/lower corner is selected - SetTileSelectSize(_terraform_size, _terraform_size); - - } break; +struct ScenarioEditorLandscapeGenerationWindow : Window { + ScenarioEditorLandscapeGenerationWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + /* XXX - lighthouse button is widget 11!! Don't forget when changing */ + this->widget[11].tooltips = (_opt.landscape == LT_TROPIC) ? STR_028F_DEFINE_DESERT_AREA : STR_028D_PLACE_LIGHTHOUSE; + this->FindWindowPlacementAndResize(desc); + } - case WE_KEYPRESS: - for (uint i = 0; i != lengthof(_editor_terraform_keycodes); i++) { - if (e->we.keypress.keycode == _editor_terraform_keycodes[i]) { - e->we.keypress.cont = false; - _editor_terraform_button_proc[i](w); - break; - } - } - break; + virtual void OnPaint() { + this->DrawWidgets(); - case WE_CLICK: - switch (e->we.click.widget) { - case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: - _editor_terraform_button_proc[e->we.click.widget - 4](w); - break; - case 13: case 14: { // Increase/Decrease terraform size - int size = (e->we.click.widget == 13) ? 1 : -1; - w->HandleButtonClick(e->we.click.widget); - size += _terraform_size; - - if (!IsInsideMM(size, 1, 8 + 1)) return; - _terraform_size = size; + int n = _terraform_size * _terraform_size; + const int8 *coords = &_multi_terraform_coords[0][0]; - SndPlayFx(SND_15_BEEP); - w->SetDirty(); - } break; - case 15: // gen random land - w->HandleButtonClick(15); - ShowCreateScenario(); - break; - case 16: // Reset landscape - ShowQuery( - STR_022C_RESET_LANDSCAPE, - STR_RESET_LANDSCAPE_CONFIRMATION_TEXT, - NULL, - ResetLandscapeConfirmationCallback); + assert(n != 0); + do { + DrawSprite(SPR_WHITE_POINT, PAL_NONE, 88 + coords[0], 55 + coords[1]); + coords += 2; + } while (--n); + + if (this->IsWidgetLowered(5) || this->IsWidgetLowered(6)) { // change area-size if raise/lower corner is selected + SetTileSelectSize(_terraform_size, _terraform_size); + } + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + for (uint i = 0; i != lengthof(_editor_terraform_keycodes); i++) { + if (keycode == _editor_terraform_keycodes[i]) { + _editor_terraform_button_proc[i](this); + return ES_HANDLED; + } + } + return ES_NOT_HANDLED; + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: + _editor_terraform_button_proc[widget - 4](this); + break; + case 13: case 14: { // Increase/Decrease terraform size + int size = (widget == 13) ? 1 : -1; + this->HandleButtonClick(widget); + size += _terraform_size; + + if (!IsInsideMM(size, 1, 8 + 1)) return; + _terraform_size = size; + + SndPlayFx(SND_15_BEEP); + this->SetDirty(); + } break; + case 15: // gen random land + this->HandleButtonClick(15); + ShowCreateScenario(); + break; + case 16: // Reset landscape + ShowQuery( + STR_022C_RESET_LANDSCAPE, + STR_RESET_LANDSCAPE_CONFIRMATION_TEXT, + NULL, + ResetLandscapeConfirmationCallback); + break; + } + } + + virtual void OnTimeout() + { + for (uint i = 0; i < this->widget_count; i++) { + if (this->IsWidgetLowered(i)) { + this->RaiseWidget(i); + this->InvalidateWidget(i); + } + if (i == 3) i = 12; + } + } + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + _place_proc(tile); + } + + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) + { + VpSelectTilesWithMethod(pt.x, pt.y, select_method); + } + + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) + { + if (pt.x != -1) { + switch (select_proc) { + default: NOT_REACHED(); + case DDSP_CREATE_ROCKS: + case DDSP_CREATE_DESERT: + case DDSP_CREATE_WATER: + case DDSP_CREATE_RIVER: + case DDSP_RAISE_AND_LEVEL_AREA: + case DDSP_LOWER_AND_LEVEL_AREA: + case DDSP_LEVEL_AREA: + case DDSP_DEMOLISH_AREA: + GUIPlaceProcDragXY(select_proc, start_tile, end_tile); break; } - break; - - case WE_TIMEOUT: - for (uint i = 0; i < w->widget_count; i++) { - if (w->IsWidgetLowered(i)) { - w->RaiseWidget(i); - w->InvalidateWidget(i); - } - if (i == 3) i = 12; - } - break; - - case WE_PLACE_OBJ: - _place_proc(e->we.place.tile); - break; - - case WE_PLACE_DRAG: - VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method); - break; + } + } - case WE_PLACE_MOUSEUP: - if (e->we.place.pt.x != -1) { - switch (e->we.place.select_proc) { - case DDSP_CREATE_ROCKS: - case DDSP_CREATE_DESERT: - case DDSP_CREATE_WATER: - case DDSP_CREATE_RIVER: - case DDSP_RAISE_AND_LEVEL_AREA: - case DDSP_LOWER_AND_LEVEL_AREA: - case DDSP_LEVEL_AREA: - case DDSP_DEMOLISH_AREA: - GUIPlaceProcDragXY(e); - break; - } - } - break; - - case WE_ABORT_PLACE_OBJ: - w->RaiseButtons(); - w->SetDirty(); - break; + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); + this->SetDirty(); } -} +}; static const WindowDesc _scen_edit_land_gen_desc = { WDP_AUTO, WDP_AUTO, 204, 103, 204, 103, WC_SCEN_LAND_GEN, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _scen_edit_land_gen_widgets, - ScenEditLandGenWndProc, }; void ShowEditorTerraformToolbar() { - AllocateWindowDescFront(&_scen_edit_land_gen_desc, 0); + AllocateWindowDescFront(&_scen_edit_land_gen_desc, 0); } diff -r 6c4314786d68 -r 8cbdb511a674 src/tilehighlight_func.h --- a/src/tilehighlight_func.h Mon May 19 14:14:33 2008 +0000 +++ b/src/tilehighlight_func.h Mon May 19 15:13:58 2008 +0000 @@ -12,7 +12,7 @@ typedef void PlaceProc(TileIndex tile); void PlaceProc_DemolishArea(TileIndex tile); -bool GUIPlaceProcDragXY(const WindowEvent *e); +bool GUIPlaceProcDragXY(ViewportDragDropSelectionProcess proc, TileIndex start_tile, TileIndex end_tile); bool HandlePlacePushButton(Window *w, int widget, CursorID cursor, ViewportHighlightMode mode, PlaceProc *placeproc); void SetObjectToPlaceWnd(CursorID icon, SpriteID pal, ViewportHighlightMode mode, Window *w); diff -r 6c4314786d68 -r 8cbdb511a674 src/timetable_gui.cpp --- a/src/timetable_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/timetable_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -36,27 +36,6 @@ TTV_RESIZE, }; -struct timetable_d { - int sel; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(timetable_d)); - -static int GetOrderFromTimetableWndPt(Window *w, int y, const Vehicle *v) -{ - /* - * Calculation description: - * 15 = 14 (w->widget[TTV_ORDER_VIEW].top) + 1 (frame-line) - * 10 = order text hight - */ - int sel = (y - 15) / 10; - - if ((uint)sel >= w->vscroll.cap) return INVALID_ORDER; - - sel += w->vscroll.pos; - - return (sel <= v->num_orders * 2 && sel >= 0) ? sel : INVALID_ORDER; -} - void SetTimetableParams(int param1, int param2, uint32 time) { if (_patches.timetable_in_ticks) { @@ -68,208 +47,229 @@ } } -static void DrawTimetableWindow(Window *w) -{ - const Vehicle *v = GetVehicle(w->window_number); - int selected = WP(w, timetable_d).sel; - - SetVScrollCount(w, v->num_orders * 2); +struct TimetableWindow : Window { + int sel_index; - if (v->owner == _local_player) { - if (selected == -1) { - w->DisableWidget(TTV_CHANGE_TIME); - w->DisableWidget(TTV_CLEAR_TIME); - } else if (selected % 2 == 1) { - w->EnableWidget(TTV_CHANGE_TIME); - w->EnableWidget(TTV_CLEAR_TIME); - } else { - const Order *order = GetVehicleOrder(v, (selected + 1) / 2); - bool disable = order == NULL || !order->IsType(OT_GOTO_STATION) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION); - - w->SetWidgetDisabledState(TTV_CHANGE_TIME, disable); - w->SetWidgetDisabledState(TTV_CLEAR_TIME, disable); - } - - w->EnableWidget(TTV_RESET_LATENESS); - w->EnableWidget(TTV_AUTOFILL); - } else { - w->DisableWidget(TTV_CHANGE_TIME); - w->DisableWidget(TTV_CLEAR_TIME); - w->DisableWidget(TTV_RESET_LATENESS); - w->DisableWidget(TTV_AUTOFILL); + TimetableWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->caption_color = GetVehicle(window_number)->owner; + this->vscroll.cap = 8; + this->resize.step_height = 10; + this->sel_index = -1; } - w->SetWidgetLoweredState(TTV_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)); - - SetDParam(0, v->index); - DrawWindowWidgets(w); - - int y = 15; - int i = w->vscroll.pos; - VehicleOrderID order_id = (i + 1) / 2; - bool final_order = false; - - const Order *order = GetVehicleOrder(v, order_id); - - while (order != NULL) { - /* Don't draw anything if it extends past the end of the window. */ - if (i - w->vscroll.pos >= w->vscroll.cap) break; + int GetOrderFromTimetableWndPt(int y, const Vehicle *v) + { + /* + * Calculation description: + * 15 = 14 (this->widget[TTV_ORDER_VIEW].top) + 1 (frame-line) + * 10 = order text hight + */ + int sel = (y - 15) / 10; - if (i % 2 == 0) { - DrawOrderString(v, order, order_id, y, i == selected, true); + if ((uint)sel >= this->vscroll.cap) return INVALID_ORDER; - order_id++; + sel += this->vscroll.pos; - if (order_id >= v->num_orders) { - order = GetVehicleOrder(v, 0); - final_order = true; + return (sel <= v->num_orders * 2 && sel >= 0) ? sel : INVALID_ORDER; + } + + void OnPaint() + { + const Vehicle *v = GetVehicle(this->window_number); + int selected = this->sel_index; + + SetVScrollCount(this, v->num_orders * 2); + + if (v->owner == _local_player) { + if (selected == -1) { + this->DisableWidget(TTV_CHANGE_TIME); + this->DisableWidget(TTV_CLEAR_TIME); + } else if (selected % 2 == 1) { + this->EnableWidget(TTV_CHANGE_TIME); + this->EnableWidget(TTV_CLEAR_TIME); } else { - order = order->next; - } - } else { - StringID string; + const Order *order = GetVehicleOrder(v, (selected + 1) / 2); + bool disable = order == NULL || !order->IsType(OT_GOTO_STATION) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION); - if (order->travel_time == 0) { - string = STR_TIMETABLE_TRAVEL_NOT_TIMETABLED; - } else { - SetTimetableParams(0, 1, order->travel_time); - string = STR_TIMETABLE_TRAVEL_FOR; + this->SetWidgetDisabledState(TTV_CHANGE_TIME, disable); + this->SetWidgetDisabledState(TTV_CLEAR_TIME, disable); } - DrawString(22, y, string, (i == selected) ? TC_WHITE : TC_BLACK); - - if (final_order) break; + this->EnableWidget(TTV_RESET_LATENESS); + this->EnableWidget(TTV_AUTOFILL); + } else { + this->DisableWidget(TTV_CHANGE_TIME); + this->DisableWidget(TTV_CLEAR_TIME); + this->DisableWidget(TTV_RESET_LATENESS); + this->DisableWidget(TTV_AUTOFILL); } - i++; - y += 10; - } - - y = w->widget[TTV_SUMMARY_PANEL].top + 1; + this->SetWidgetLoweredState(TTV_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)); - { - uint total_time = 0; - bool complete = true; + SetDParam(0, v->index); + this->DrawWidgets(); - for (const Order *order = GetVehicleOrder(v, 0); order != NULL; order = order->next) { - total_time += order->travel_time + order->wait_time; - if (order->travel_time == 0) complete = false; - if (order->wait_time == 0 && order->IsType(OT_GOTO_STATION) && !(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) complete = false; + int y = 15; + int i = this->vscroll.pos; + VehicleOrderID order_id = (i + 1) / 2; + bool final_order = false; + + const Order *order = GetVehicleOrder(v, order_id); + + while (order != NULL) { + /* Don't draw anything if it extends past the end of the window. */ + if (i - this->vscroll.pos >= this->vscroll.cap) break; + + if (i % 2 == 0) { + DrawOrderString(v, order, order_id, y, i == selected, true); + + order_id++; + + if (order_id >= v->num_orders) { + order = GetVehicleOrder(v, 0); + final_order = true; + } else { + order = order->next; + } + } else { + StringID string; + + if (order->travel_time == 0) { + string = STR_TIMETABLE_TRAVEL_NOT_TIMETABLED; + } else { + SetTimetableParams(0, 1, order->travel_time); + string = STR_TIMETABLE_TRAVEL_FOR; + } + + DrawString(22, y, string, (i == selected) ? TC_WHITE : TC_BLACK); + + if (final_order) break; + } + + i++; + y += 10; } - if (total_time != 0) { - SetTimetableParams(0, 1, total_time); - DrawString(2, y, complete ? STR_TIMETABLE_TOTAL_TIME : STR_TIMETABLE_TOTAL_TIME_INCOMPLETE, TC_BLACK); + y = this->widget[TTV_SUMMARY_PANEL].top + 1; + + { + uint total_time = 0; + bool complete = true; + + for (const Order *order = GetVehicleOrder(v, 0); order != NULL; order = order->next) { + total_time += order->travel_time + order->wait_time; + if (order->travel_time == 0) complete = false; + if (order->wait_time == 0 && order->IsType(OT_GOTO_STATION) && !(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) complete = false; + } + + if (total_time != 0) { + SetTimetableParams(0, 1, total_time); + DrawString(2, y, complete ? STR_TIMETABLE_TOTAL_TIME : STR_TIMETABLE_TOTAL_TIME_INCOMPLETE, TC_BLACK); + } + } + y += 10; + + if (v->lateness_counter == 0 || (!_patches.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) { + DrawString(2, y, STR_TIMETABLE_STATUS_ON_TIME, TC_BLACK); + } else { + SetTimetableParams(0, 1, abs(v->lateness_counter)); + DrawString(2, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE, TC_BLACK); } } - y += 10; - - if (v->lateness_counter == 0 || (!_patches.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) { - DrawString(2, y, STR_TIMETABLE_STATUS_ON_TIME, TC_BLACK); - } else { - SetTimetableParams(0, 1, abs(v->lateness_counter)); - DrawString(2, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE, TC_BLACK); - } -} - -static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected) -{ - uint order_number = (selected + 1) / 2; - uint is_journey = (selected % 2 == 1) ? 1 : 0; - - if (order_number >= v->num_orders) order_number = 0; - - return v->index | (order_number << 16) | (is_journey << 24); -} - -static void TimetableWndProc(Window *w, WindowEvent *we) -{ - switch (we->event) { - case WE_PAINT: - DrawTimetableWindow(w); - break; - - case WE_CLICK: { - const Vehicle *v = GetVehicle(w->window_number); - - switch (we->we.click.widget) { - case TTV_ORDER_VIEW: /* Order view button */ - ShowOrdersWindow(v); - break; - - case TTV_TIMETABLE_PANEL: { /* Main panel. */ - int selected = GetOrderFromTimetableWndPt(w, we->we.click.pt.y, v); - - if (selected == INVALID_ORDER || selected == WP(w, timetable_d).sel) { - /* Deselect clicked order */ - WP(w, timetable_d).sel = -1; - } else { - /* Select clicked order */ - WP(w, timetable_d).sel = selected; - } - } break; - - case TTV_CHANGE_TIME: { /* "Wait For" button. */ - int selected = WP(w, timetable_d).sel; - VehicleOrderID real = (selected + 1) / 2; - - if (real >= v->num_orders) real = 0; - const Order *order = GetVehicleOrder(v, real); - StringID current = STR_EMPTY; - - if (order != NULL) { - uint time = (selected % 2 == 1) ? order->travel_time : order->wait_time; - if (!_patches.timetable_in_ticks) time /= DAY_TICKS; - - if (time != 0) { - SetDParam(0, time); - current = STR_CONFIG_PATCHES_INT32; - } - } - - ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, 150, w, CS_NUMERAL); - } break; - - case TTV_CLEAR_TIME: { /* Clear waiting time button. */ - uint32 p1 = PackTimetableArgs(v, WP(w, timetable_d).sel); - DoCommandP(0, p1, 0, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); - } break; - - case TTV_RESET_LATENESS: /* Reset the vehicle's late counter. */ - DoCommandP(0, v->index, 0, NULL, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); - break; + static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected) + { + uint order_number = (selected + 1) / 2; + uint is_journey = (selected % 2 == 1) ? 1 : 0; - case TTV_AUTOFILL: /* Autofill the timetable. */ - DoCommandP(0, v->index, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE) ? 0 : 1, NULL, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); - break; - } - - w->SetDirty(); - } break; - - case WE_ON_EDIT_TEXT: { - if (we->we.edittext.str == NULL) break; - - const Vehicle *v = GetVehicle(w->window_number); - - uint32 p1 = PackTimetableArgs(v, WP(w, timetable_d).sel); + if (order_number >= v->num_orders) order_number = 0; - uint64 time = StrEmpty(we->we.edittext.str) ? 0 : strtoul(we->we.edittext.str, NULL, 10); - if (!_patches.timetable_in_ticks) time *= DAY_TICKS; - - uint32 p2 = minu(time, MAX_UVALUE(uint16)); + return v->index | (order_number << 16) | (is_journey << 24); + } - DoCommandP(0, p1, p2, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); - } break; + virtual void OnClick(Point pt, int widget) + { + const Vehicle *v = GetVehicle(this->window_number); - case WE_RESIZE: - /* Update the scroll + matrix */ - w->vscroll.cap = (w->widget[TTV_TIMETABLE_PANEL].bottom - w->widget[TTV_TIMETABLE_PANEL].top) / 10; - break; + switch (widget) { + case TTV_ORDER_VIEW: /* Order view button */ + ShowOrdersWindow(v); + break; + case TTV_TIMETABLE_PANEL: { /* Main panel. */ + int selected = GetOrderFromTimetableWndPt(pt.y, v); + + if (selected == INVALID_ORDER || selected == this->sel_index) { + /* Deselect clicked order */ + this->sel_index = -1; + } else { + /* Select clicked order */ + this->sel_index = selected; + } + } break; + + case TTV_CHANGE_TIME: { /* "Wait For" button. */ + int selected = this->sel_index; + VehicleOrderID real = (selected + 1) / 2; + + if (real >= v->num_orders) real = 0; + + const Order *order = GetVehicleOrder(v, real); + StringID current = STR_EMPTY; + + if (order != NULL) { + uint time = (selected % 2 == 1) ? order->travel_time : order->wait_time; + if (!_patches.timetable_in_ticks) time /= DAY_TICKS; + + if (time != 0) { + SetDParam(0, time); + current = STR_CONFIG_PATCHES_INT32; + } + } + + ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, 150, this, CS_NUMERAL); + } break; + + case TTV_CLEAR_TIME: { /* Clear waiting time button. */ + uint32 p1 = PackTimetableArgs(v, this->sel_index); + DoCommandP(0, p1, 0, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); + } break; + + case TTV_RESET_LATENESS: /* Reset the vehicle's late counter. */ + DoCommandP(0, v->index, 0, NULL, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); + break; + + case TTV_AUTOFILL: /* Autofill the timetable. */ + DoCommandP(0, v->index, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE) ? 0 : 1, NULL, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); + break; + } + + this->SetDirty(); } -} + + virtual void OnQueryTextFinished(char *str) + { + if (str == NULL) return; + + const Vehicle *v = GetVehicle(this->window_number); + + uint32 p1 = PackTimetableArgs(v, this->sel_index); + + uint64 time = StrEmpty(str) ? 0 : strtoul(str, NULL, 10); + if (!_patches.timetable_in_ticks) time *= DAY_TICKS; + + uint32 p2 = minu(time, MAX_UVALUE(uint16)); + + DoCommandP(0, p1, p2, NULL, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); + } + + virtual void OnResize(Point new_size, Point delta) + { + /* Update the scroll + matrix */ + this->vscroll.cap = (this->widget[TTV_TIMETABLE_PANEL].bottom - this->widget[TTV_TIMETABLE_PANEL].top) / 10; + } +}; static const Widget _timetable_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // TTV_WIDGET_CLOSEBOX @@ -298,17 +298,9 @@ WC_VEHICLE_TIMETABLE, WC_VEHICLE_VIEW, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _timetable_widgets, - TimetableWndProc }; void ShowTimetableWindow(const Vehicle *v) { - Window *w = AllocateWindowDescFront(&_timetable_desc, v->index); - - if (w != NULL) { - w->caption_color = v->owner; - w->vscroll.cap = 8; - w->resize.step_height = 10; - WP(w, timetable_d).sel = -1; - } + AllocateWindowDescFront(&_timetable_desc, v->index); } diff -r 6c4314786d68 -r 8cbdb511a674 src/toolbar_gui.cpp --- a/src/toolbar_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/toolbar_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -46,8 +46,8 @@ #include "table/strings.h" #include "table/sprites.h" -static Window *PopupMainToolbMenu(Window *w, uint16 parent_button, StringID base_string, byte item_count, byte disabled_mask); -static Window *PopupMainPlayerToolbMenu(Window *w, int main_button, int gray); +static void PopupMainToolbMenu(Window *parent, uint16 parent_button, StringID base_string, byte item_count, byte disabled_mask = 0, int sel_index = 0, int checked_items = 0); +static void PopupMainPlayerToolbMenu(Window *parent, int main_button, int gray); RailType _last_built_railtype; RoadType _last_built_roadtype; @@ -79,13 +79,13 @@ } /** - * In a window with menu_d custom extension, retrieve the menu item number from a position + * Retrieve the menu item number from a position * @param w Window holding the menu items * @param x X coordinate of the position * @param y Y coordinate of the position * @return Index number of the menu item, or \c -1 if no valid selection under position */ -static int GetMenuItemIndex(const Window *w) +static int GetMenuItemIndex(const Window *w, int item_count, int disabled_items) { int x = _cursor.pos.x; int y = _cursor.pos.y; @@ -93,8 +93,8 @@ if ((x -= w->left) >= 0 && x < w->width && (y -= w->top + 1) >= 0) { y /= 10; - if (y < WP(w, const menu_d).item_count && - !HasBit(WP(w, const menu_d).disabled_items, y)) { + if (y < item_count && + !HasBit(disabled_items, y)) { return y; } } @@ -123,9 +123,6 @@ static void ToolbarOptionsClick(Window *w) { uint16 x = 0; - - w = PopupMainToolbMenu(w, 2, STR_02C4_GAME_OPTIONS, 14, 0); - if (HasBit(_display_opt, DO_SHOW_TOWN_NAMES)) SetBit(x, 6); if (HasBit(_display_opt, DO_SHOW_STATION_NAMES)) SetBit(x, 7); if (HasBit(_display_opt, DO_SHOW_SIGNS)) SetBit(x, 8); @@ -134,7 +131,8 @@ if (HasBit(_display_opt, DO_FULL_DETAIL)) SetBit(x, 11); if (IsTransparencySet(TO_HOUSES)) SetBit(x, 12); if (IsTransparencySet(TO_SIGNS)) SetBit(x, 13); - WP(w, menu_d).checked_items = x; + + PopupMainToolbMenu(w, 2, STR_02C4_GAME_OPTIONS, 14, 0, 0, x); } static void MenuClickSettings(int index) @@ -162,12 +160,12 @@ static void ToolbarSaveClick(Window *w) { - PopupMainToolbMenu(w, 3, STR_015C_SAVE_GAME, 4, 0); + PopupMainToolbMenu(w, 3, STR_015C_SAVE_GAME, 4); } static void ToolbarScenSaveOrLoad(Window *w) { - PopupMainToolbMenu(w, 3, STR_0292_SAVE_SCENARIO, 6, 0); + PopupMainToolbMenu(w, 3, STR_0292_SAVE_SCENARIO, 6); } static void MenuClickSaveLoad(int index) @@ -194,7 +192,7 @@ static void ToolbarMapClick(Window *w) { - PopupMainToolbMenu(w, 4, STR_02DE_MAP_OF_WORLD, 3, 0); + PopupMainToolbMenu(w, 4, STR_02DE_MAP_OF_WORLD, 3); } static void MenuClickMap(int index) @@ -220,7 +218,7 @@ static void ToolbarTownClick(Window *w) { - PopupMainToolbMenu(w, 5, STR_02BB_TOWN_DIRECTORY, 1, 0); + PopupMainToolbMenu(w, 5, STR_02BB_TOWN_DIRECTORY, 1); } static void MenuClickTown(int index) @@ -232,7 +230,7 @@ static void ToolbarSubsidiesClick(Window *w) { - PopupMainToolbMenu(w, 6, STR_02DD_SUBSIDIES, 1, 0); + PopupMainToolbMenu(w, 6, STR_02DD_SUBSIDIES, 1); } static void MenuClickSubsidies(int index) @@ -285,7 +283,7 @@ static void ToolbarGraphsClick(Window *w) { - PopupMainToolbMenu(w, 10, STR_0154_OPERATING_PROFIT_GRAPH, 6, 0); + PopupMainToolbMenu(w, 10, STR_0154_OPERATING_PROFIT_GRAPH, 6); } static void MenuClickGraphs(int index) @@ -304,7 +302,7 @@ static void ToolbarLeagueClick(Window *w) { - PopupMainToolbMenu(w, 11, STR_015A_COMPANY_LEAGUE_TABLE, 2, 0); + PopupMainToolbMenu(w, 11, STR_015A_COMPANY_LEAGUE_TABLE, 2); } static void MenuClickLeague(int index) @@ -416,8 +414,7 @@ static void ToolbarBuildRailClick(Window *w) { const Player *p = GetPlayer(_local_player); - Window *w2 = PopupMainToolbMenu(w, 19, STR_1015_RAILROAD_CONSTRUCTION, RAILTYPE_END, ~p->avail_railtypes); - WP(w2, menu_d).sel_index = _last_built_railtype; + PopupMainToolbMenu(w, 19, STR_1015_RAILROAD_CONSTRUCTION, RAILTYPE_END, ~p->avail_railtypes, _last_built_railtype); } static void MenuClickBuildRail(int index) @@ -432,8 +429,7 @@ { const Player *p = GetPlayer(_local_player); /* The standard road button is *always* available */ - Window *w2 = PopupMainToolbMenu(w, 20, STR_180A_ROAD_CONSTRUCTION, 2, ~(p->avail_roadtypes | ROADTYPES_ROAD)); - WP(w2, menu_d).sel_index = _last_built_roadtype; + PopupMainToolbMenu(w, 20, STR_180A_ROAD_CONSTRUCTION, 2, ~(p->avail_roadtypes | ROADTYPES_ROAD), _last_built_roadtype); } static void MenuClickBuildRoad(int index) @@ -446,7 +442,7 @@ static void ToolbarBuildWaterClick(Window *w) { - PopupMainToolbMenu(w, 21, STR_9800_DOCK_CONSTRUCTION, 1, 0); + PopupMainToolbMenu(w, 21, STR_9800_DOCK_CONSTRUCTION, 1); } static void MenuClickBuildWater(int index) @@ -458,7 +454,7 @@ static void ToolbarBuildAirClick(Window *w) { - PopupMainToolbMenu(w, 22, STR_A01D_AIRPORT_CONSTRUCTION, 1, 0); + PopupMainToolbMenu(w, 22, STR_A01D_AIRPORT_CONSTRUCTION, 1); } static void MenuClickBuildAir(int index) @@ -470,7 +466,7 @@ static void ToolbarForestClick(Window *w) { - PopupMainToolbMenu(w, 23, STR_LANDSCAPING, 3, 0); + PopupMainToolbMenu(w, 23, STR_LANDSCAPING, 3); } static void MenuClickForest(int index) @@ -486,7 +482,7 @@ static void ToolbarMusicClick(Window *w) { - PopupMainToolbMenu(w, 24, STR_01D3_SOUND_MUSIC, 1, 0); + PopupMainToolbMenu(w, 24, STR_01D3_SOUND_MUSIC, 1); } static void MenuClickMusicWindow(int index) @@ -498,7 +494,7 @@ static void ToolbarNewspaperClick(Window *w) { - PopupMainToolbMenu(w, 25, STR_0200_LAST_MESSAGE_NEWS_REPORT, 3, 0); + PopupMainToolbMenu(w, 25, STR_0200_LAST_MESSAGE_NEWS_REPORT, 3); } static void MenuClickNewspaper(int index) @@ -514,7 +510,7 @@ static void ToolbarHelpClick(Window *w) { - PopupMainToolbMenu(w, 26, STR_02D5_LAND_BLOCK_INFO, 7, 0); + PopupMainToolbMenu(w, 26, STR_02D5_LAND_BLOCK_INFO, 7); } static void MenuClickSmallScreenshot() @@ -570,7 +566,7 @@ static void ToolbarScenMapTownDir(Window *w) { /* Scenario editor button, *hack*hack* use different button to activate */ - PopupMainToolbMenu(w, 8 | (17 << 8), STR_02DE_MAP_OF_WORLD, 4, 0); + PopupMainToolbMenu(w, 8 | (17 << 8), STR_02DE_MAP_OF_WORLD, 4); } static void ToolbarScenZoomIn(Window *w) @@ -623,7 +619,7 @@ { w->HandleButtonClick(15); SndPlayFx(SND_15_BEEP); - ShowBuildTreesScenToolbar(); + ShowBuildTreesToolbar(); } static void ToolbarScenPlaceSign(Window *w) @@ -672,137 +668,155 @@ ToolbarHelpClick, }; -static void MainToolbarWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - /* Draw brown-red toolbar bg. */ - GfxFillRect(0, 0, w->width - 1, w->height - 1, 0xB2); - GfxFillRect(0, 0, w->width - 1, w->height - 1, 0xB4 | (1 << PALETTE_MODIFIER_GREYOUT)); - - /* If spectator, disable all construction buttons - * ie : Build road, rail, ships, airports and landscaping - * Since enabled state is the default, just disable when needed */ - w->SetWidgetsDisabledState(_current_player == PLAYER_SPECTATOR, 19, 20, 21, 22, 23, WIDGET_LIST_END); - /* disable company list drop downs, if there are no companies */ - w->SetWidgetsDisabledState(ActivePlayerCount() == 0, 7, 8, 13, 14, 15, 16, WIDGET_LIST_END); - - w->SetWidgetDisabledState(19, !CanBuildVehicleInfrastructure(VEH_TRAIN)); - w->SetWidgetDisabledState(22, !CanBuildVehicleInfrastructure(VEH_AIRCRAFT)); - - DrawWindowWidgets(w); - break; - - case WE_CLICK: - if (_game_mode != GM_MENU && !w->IsWidgetDisabled(e->we.click.widget)) _toolbar_button_procs[e->we.click.widget](w); - break; +struct MainToolbarWindow : Window { + MainToolbarWindow(const WindowDesc *desc) : Window(desc) + { + this->SetWidgetDisabledState(0, _networking && !_network_server); // if not server, disable pause button + this->SetWidgetDisabledState(1, _networking); // if networking, disable fast-forward button - case WE_KEYPRESS: - switch (e->we.keypress.keycode) { - case WKC_F1: case WKC_PAUSE: ToolbarPauseClick(w); break; - case WKC_F2: ShowGameOptions(); break; - case WKC_F3: MenuClickSaveLoad(0); break; - case WKC_F4: ShowSmallMap(); break; - case WKC_F5: ShowTownDirectory(); break; - case WKC_F6: ShowSubsidiesList(); break; - case WKC_F7: ShowPlayerStations(_local_player); break; - case WKC_F8: ShowPlayerFinances(_local_player); break; - case WKC_F9: ShowPlayerCompany(_local_player); break; - case WKC_F10: ShowOperatingProfitGraph(); break; - case WKC_F11: ShowCompanyLeagueTable(); break; - case WKC_F12: ShowBuildIndustryWindow(); break; - case WKC_SHIFT | WKC_F1: ShowVehicleListWindow(_local_player, VEH_TRAIN); break; - case WKC_SHIFT | WKC_F2: ShowVehicleListWindow(_local_player, VEH_ROAD); break; - case WKC_SHIFT | WKC_F3: ShowVehicleListWindow(_local_player, VEH_SHIP); break; - case WKC_SHIFT | WKC_F4: ShowVehicleListWindow(_local_player, VEH_AIRCRAFT); break; - case WKC_NUM_PLUS: // Fall through - case WKC_EQUALS: // Fall through - case WKC_SHIFT | WKC_EQUALS: // Fall through - case WKC_SHIFT | WKC_F5: ToolbarZoomInClick(w); break; - case WKC_NUM_MINUS: // Fall through - case WKC_MINUS: // Fall through - case WKC_SHIFT | WKC_MINUS: // Fall through - case WKC_SHIFT | WKC_F6: ToolbarZoomOutClick(w); break; - case WKC_SHIFT | WKC_F7: if (CanBuildVehicleInfrastructure(VEH_TRAIN)) ShowBuildRailToolbar(_last_built_railtype, -1); break; - case WKC_SHIFT | WKC_F8: ShowBuildRoadToolbar(_last_built_roadtype); break; - case WKC_SHIFT | WKC_F9: ShowBuildDocksToolbar(); break; - case WKC_SHIFT | WKC_F10: if (CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) ShowBuildAirToolbar(); break; - case WKC_SHIFT | WKC_F11: ShowBuildTreesToolbar(); break; - case WKC_SHIFT | WKC_F12: ShowMusicWindow(); break; - case WKC_CTRL | 'S': MenuClickSmallScreenshot(); break; - case WKC_CTRL | 'G': MenuClickWorldScreenshot(); break; - case WKC_CTRL | WKC_ALT | 'C': if (!_networking) ShowCheatWindow(); break; - case 'A': if (CanBuildVehicleInfrastructure(VEH_TRAIN)) ShowBuildRailToolbar(_last_built_railtype, 4); break; // Invoke Autorail - case 'L': ShowTerraformToolbar(); break; - case 'M': ShowSmallMap(); break; - case 'V': ShowExtraViewPortWindow(); break; - default: return; - } - e->we.keypress.cont = false; - break; + CLRBITS(this->flags4, WF_WHITE_BORDER_MASK); - case WE_PLACE_OBJ: - _place_proc(e->we.place.tile); - break; + this->FindWindowPlacementAndResize(desc); + PositionMainToolbar(this); + DoZoomInOutWindow(ZOOM_NONE, this); + } - case WE_ABORT_PLACE_OBJ: - w->RaiseWidget(25); - w->SetDirty(); - break; + virtual void OnPaint() + { + /* Draw brown-red toolbar bg. */ + GfxFillRect(0, 0, this->width - 1, this->height - 1, 0xB2); + GfxFillRect(0, 0, this->width - 1, this->height - 1, 0xB4 | (1 << PALETTE_MODIFIER_GREYOUT)); - case WE_TICK: - if (w->IsWidgetLowered(0) != !!_pause_game) { - w->ToggleWidgetLoweredState(0); - w->InvalidateWidget(0); + /* If spectator, disable all construction buttons + * ie : Build road, rail, ships, airports and landscaping + * Since enabled state is the default, just disable when needed */ + this->SetWidgetsDisabledState(_current_player == PLAYER_SPECTATOR, 19, 20, 21, 22, 23, WIDGET_LIST_END); + /* disable company list drop downs, if there are no companies */ + this->SetWidgetsDisabledState(ActivePlayerCount() == 0, 7, 8, 13, 14, 15, 16, WIDGET_LIST_END); + + this->SetWidgetDisabledState(19, !CanBuildVehicleInfrastructure(VEH_TRAIN)); + this->SetWidgetDisabledState(22, !CanBuildVehicleInfrastructure(VEH_AIRCRAFT)); + + this->DrawWidgets(); + } + + virtual void OnClick(Point pt, int widget) + { + if (_game_mode != GM_MENU && !this->IsWidgetDisabled(widget)) _toolbar_button_procs[widget](this); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + switch (keycode) { + case WKC_F1: case WKC_PAUSE: ToolbarPauseClick(this); break; + case WKC_F2: ShowGameOptions(); break; + case WKC_F3: MenuClickSaveLoad(0); break; + case WKC_F4: ShowSmallMap(); break; + case WKC_F5: ShowTownDirectory(); break; + case WKC_F6: ShowSubsidiesList(); break; + case WKC_F7: ShowPlayerStations(_local_player); break; + case WKC_F8: ShowPlayerFinances(_local_player); break; + case WKC_F9: ShowPlayerCompany(_local_player); break; + case WKC_F10: ShowOperatingProfitGraph(); break; + case WKC_F11: ShowCompanyLeagueTable(); break; + case WKC_F12: ShowBuildIndustryWindow(); break; + case WKC_SHIFT | WKC_F1: ShowVehicleListWindow(_local_player, VEH_TRAIN); break; + case WKC_SHIFT | WKC_F2: ShowVehicleListWindow(_local_player, VEH_ROAD); break; + case WKC_SHIFT | WKC_F3: ShowVehicleListWindow(_local_player, VEH_SHIP); break; + case WKC_SHIFT | WKC_F4: ShowVehicleListWindow(_local_player, VEH_AIRCRAFT); break; + case WKC_NUM_PLUS: // Fall through + case WKC_EQUALS: // Fall through + case WKC_SHIFT | WKC_EQUALS: // Fall through + case WKC_SHIFT | WKC_F5: ToolbarZoomInClick(this); break; + case WKC_NUM_MINUS: // Fall through + case WKC_MINUS: // Fall through + case WKC_SHIFT | WKC_MINUS: // Fall through + case WKC_SHIFT | WKC_F6: ToolbarZoomOutClick(this); break; + case WKC_SHIFT | WKC_F7: if (CanBuildVehicleInfrastructure(VEH_TRAIN)) ShowBuildRailToolbar(_last_built_railtype, -1); break; + case WKC_SHIFT | WKC_F8: ShowBuildRoadToolbar(_last_built_roadtype); break; + case WKC_SHIFT | WKC_F9: ShowBuildDocksToolbar(); break; + case WKC_SHIFT | WKC_F10: if (CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) ShowBuildAirToolbar(); break; + case WKC_SHIFT | WKC_F11: ShowBuildTreesToolbar(); break; + case WKC_SHIFT | WKC_F12: ShowMusicWindow(); break; + case WKC_CTRL | 'S': MenuClickSmallScreenshot(); break; + case WKC_CTRL | 'G': MenuClickWorldScreenshot(); break; + case WKC_CTRL | WKC_ALT | 'C': if (!_networking) ShowCheatWindow(); break; + case 'A': if (CanBuildVehicleInfrastructure(VEH_TRAIN)) ShowBuildRailToolbar(_last_built_railtype, 4); break; // Invoke Autorail + case 'L': ShowTerraformToolbar(); break; + case 'M': ShowSmallMap(); break; + case 'V': ShowExtraViewPortWindow(); break; + default: return ES_NOT_HANDLED; + } + return ES_HANDLED; + } + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + _place_proc(tile); + } + + virtual void OnPlaceObjectAbort() + { + this->RaiseWidget(25); + this->SetDirty(); + } + + virtual void OnTick() + { + if (this->IsWidgetLowered(0) != !!_pause_game) { + this->ToggleWidgetLoweredState(0); + this->InvalidateWidget(0); + } + + if (this->IsWidgetLowered(1) != !!_fast_forward) { + this->ToggleWidgetLoweredState(1); + this->InvalidateWidget(1); + } + } + + virtual void OnResize(Point new_size, Point delta) + { + /* There are 27 buttons plus some spacings if the space allows it */ + uint button_width; + uint spacing; + if (this->width >= 27 * 22) { + button_width = 22; + spacing = this->width - (27 * button_width); + } else { + button_width = this->width / 27; + spacing = 0; + } + uint extra_spacing_at[] = { 4, 8, 13, 17, 19, 24, 0 }; + + for (uint i = 0, x = 0, j = 0; i < 27; i++) { + if (extra_spacing_at[j] == i) { + j++; + uint add = spacing / (lengthof(extra_spacing_at) - j); + spacing -= add; + x += add; } - if (w->IsWidgetLowered(1) != !!_fast_forward) { - w->ToggleWidgetLoweredState(1); - w->InvalidateWidget(1); - } - break; - - case WE_RESIZE: { - /* There are 27 buttons plus some spacings if the space allows it */ - uint button_width; - uint spacing; - if (w->width >= 27 * 22) { - button_width = 22; - spacing = w->width - (27 * button_width); - } else { - button_width = w->width / 27; - spacing = 0; - } - uint extra_spacing_at[] = { 4, 8, 13, 17, 19, 24, 0 }; + this->widget[i].left = x; + x += (spacing != 0) ? button_width : (this->width - x) / (27 - i); + this->widget[i].right = x - 1; + } + } - for (uint i = 0, x = 0, j = 0; i < 27; i++) { - if (extra_spacing_at[j] == i) { - j++; - uint add = spacing / (lengthof(extra_spacing_at) - j); - spacing -= add; - x += add; - } - - w->widget[i].left = x; - x += (spacing != 0) ? button_width : (w->width - x) / (27 - i); - w->widget[i].right = x - 1; + virtual void OnTimeout() + { + for (uint i = 2; i < this->widget_count; i++) { + if (this->IsWidgetLowered(i)) { + this->RaiseWidget(i); + this->InvalidateWidget(i); } - } break; + } + } - case WE_TIMEOUT: - for (uint i = 2; i < w->widget_count; i++) { - if (w->IsWidgetLowered(i)) { - w->RaiseWidget(i); - w->InvalidateWidget(i); - } - } - break; - - case WE_INVALIDATE_DATA: - if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) HandleZoomMessage(w, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, 17, 18); - break; + virtual void OnInvalidateData(int data) + { + if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) HandleZoomMessage(this, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, 17, 18); } -} +}; static const Widget _toolb_normal_widgets[] = { { WWT_IMGBTN, RESIZE_LEFT, 14, 0, 0, 0, 21, SPR_IMG_PAUSE, STR_0171_PAUSE_GAME}, @@ -846,7 +860,6 @@ WC_MAIN_TOOLBAR, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET, _toolb_normal_widgets, - MainToolbarWndProc }; @@ -882,163 +895,176 @@ ToolbarHelpClick, }; -static void ScenEditToolbarWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - w->SetWidgetDisabledState(6, _patches_newgame.starting_year <= MIN_YEAR); - w->SetWidgetDisabledState(7, _patches_newgame.starting_year >= MAX_YEAR); +struct ScenarioEditorToolbarWindow : Window { + ScenarioEditorToolbarWindow(const WindowDesc *desc) : Window(desc) + { + CLRBITS(this->flags4, WF_WHITE_BORDER_MASK); - /* Draw brown-red toolbar bg. */ - GfxFillRect(0, 0, w->width - 1, w->height - 1, 0xB2); - GfxFillRect(0, 0, w->width - 1, w->height - 1, 0xB4 | (1 << PALETTE_MODIFIER_GREYOUT)); - - DrawWindowWidgets(w); + this->FindWindowPlacementAndResize(desc); + PositionMainToolbar(this); + DoZoomInOutWindow(ZOOM_NONE, this); + } - SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); - DrawStringCenteredTruncated(w->widget[6].right, w->widget[7].left, 6, STR_00AF, TC_FROMSTRING); + virtual void OnPaint() + { + this->SetWidgetDisabledState(6, _patches_newgame.starting_year <= MIN_YEAR); + this->SetWidgetDisabledState(7, _patches_newgame.starting_year >= MAX_YEAR); - /* We hide this panel when the toolbar space gets too small */ - if (w->widget[4].left != w->widget[4].right) { - DrawStringCenteredTruncated(w->widget[4].left + 1, w->widget[4].right - 1, 1, STR_0221_OPENTTD, TC_FROMSTRING); - DrawStringCenteredTruncated(w->widget[4].left + 1, w->widget[4].right - 1, 11, STR_0222_SCENARIO_EDITOR, TC_FROMSTRING); + /* Draw brown-red toolbar bg. */ + GfxFillRect(0, 0, this->width - 1, this->height - 1, 0xB2); + GfxFillRect(0, 0, this->width - 1, this->height - 1, 0xB4 | (1 << PALETTE_MODIFIER_GREYOUT)); + + this->DrawWidgets(); + + SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); + DrawStringCenteredTruncated(this->widget[6].right, this->widget[7].left, 6, STR_00AF, TC_FROMSTRING); + + /* We hide this panel when the toolbar space gets too small */ + if (this->widget[4].left != this->widget[4].right) { + DrawStringCenteredTruncated(this->widget[4].left + 1, this->widget[4].right - 1, 1, STR_0221_OPENTTD, TC_FROMSTRING); + DrawStringCenteredTruncated(this->widget[4].left + 1, this->widget[4].right - 1, 11, STR_0222_SCENARIO_EDITOR, TC_FROMSTRING); + } + } + + virtual void OnClick(Point pt, int widget) + { + if (_game_mode == GM_MENU) return; + _scen_toolbar_button_procs[widget](this); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + switch (keycode) { + case WKC_F1: case WKC_PAUSE: ToolbarPauseClick(this); break; + case WKC_F2: ShowGameOptions(); break; + case WKC_F3: MenuClickSaveLoad(0); break; + case WKC_F4: ToolbarScenGenLand(this); break; + case WKC_F5: ToolbarScenGenTown(this); break; + case WKC_F6: ToolbarScenGenIndustry(this); break; + case WKC_F7: ToolbarScenBuildRoad(this); break; + case WKC_F8: ToolbarScenPlantTrees(this); break; + case WKC_F9: ToolbarScenPlaceSign(this); break; + case WKC_F10: ShowMusicWindow(); break; + case WKC_F11: PlaceLandBlockInfo(); break; + case WKC_CTRL | 'S': MenuClickSmallScreenshot(); break; + case WKC_CTRL | 'G': MenuClickWorldScreenshot(); break; + + /* those following are all fall through */ + case WKC_NUM_PLUS: + case WKC_EQUALS: + case WKC_SHIFT | WKC_EQUALS: + case WKC_SHIFT | WKC_F5: ToolbarZoomInClick(this); break; + + /* those following are all fall through */ + case WKC_NUM_MINUS: + case WKC_MINUS: + case WKC_SHIFT | WKC_MINUS: + case WKC_SHIFT | WKC_F6: ToolbarZoomOutClick(this); break; + + case 'L': ShowEditorTerraformToolbar(); break; + case 'M': ShowSmallMap(); break; + case 'V': ShowExtraViewPortWindow(); break; + default: return ES_NOT_HANDLED; + } + return ES_HANDLED; + } + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + _place_proc(tile); + } + + virtual void OnPlaceObjectAbort() + { + this->RaiseWidget(25); + this->SetDirty(); + } + + virtual void OnResize(Point new_size, Point delta) + { + /* There are 15 buttons plus some spacings if the space allows it. + * Furthermore there are two panels of which one is non - essential + * and that one can be removed is the space is too small. */ + uint buttons_width; + uint spacing; + + static int normal_min_width = (15 * 22) + (2 * 130); + static int one_less_panel_min_width = (15 * 22) + 130; + + if (this->width >= one_less_panel_min_width) { + buttons_width = 15 * 22; + spacing = this->width - ((this->width >= normal_min_width) ? normal_min_width : one_less_panel_min_width); + } else { + buttons_width = this->width - 130; + spacing = 0; + } + uint extra_spacing_at[] = { 3, 4, 7, 8, 10, 16, 0 }; + + /* Yes, it defines about 27 widgets for this toolbar */ + for (uint i = 0, x = 0, j = 0, b = 0; i < 27; i++) { + switch (i) { + case 4: + this->widget[i].left = x; + if (this->width < normal_min_width) { + this->widget[i].right = x; + j++; + continue; + } + + x += 130; + this->widget[i].right = x - 1; + break; + + case 5: { + int offset = x - this->widget[i].left; + this->widget[i + 1].left += offset; + this->widget[i + 1].right += offset; + this->widget[i + 2].left += offset; + this->widget[i + 2].right += offset; + this->widget[i].left = x; + x += 130; + this->widget[i].right = x - 1; + i += 2; + } break; + + default: + if (this->widget[i].bottom == 0) continue; + + this->widget[i].left = x; + x += buttons_width / (15 - b); + this->widget[i].right = x - 1; + buttons_width -= buttons_width / (15 - b); + b++; + break; } - break; - - case WE_CLICK: - if (_game_mode == GM_MENU) return; - _scen_toolbar_button_procs[e->we.click.widget](w); - break; - - case WE_KEYPRESS: - switch (e->we.keypress.keycode) { - case WKC_F1: case WKC_PAUSE: ToolbarPauseClick(w); break; - case WKC_F2: ShowGameOptions(); break; - case WKC_F3: MenuClickSaveLoad(0); break; - case WKC_F4: ToolbarScenGenLand(w); break; - case WKC_F5: ToolbarScenGenTown(w); break; - case WKC_F6: ToolbarScenGenIndustry(w); break; - case WKC_F7: ToolbarScenBuildRoad(w); break; - case WKC_F8: ToolbarScenPlantTrees(w); break; - case WKC_F9: ToolbarScenPlaceSign(w); break; - case WKC_F10: ShowMusicWindow(); break; - case WKC_F11: PlaceLandBlockInfo(); break; - case WKC_CTRL | 'S': MenuClickSmallScreenshot(); break; - case WKC_CTRL | 'G': MenuClickWorldScreenshot(); break; - - /* those following are all fall through */ - case WKC_NUM_PLUS: - case WKC_EQUALS: - case WKC_SHIFT | WKC_EQUALS: - case WKC_SHIFT | WKC_F5: ToolbarZoomInClick(w); break; - - /* those following are all fall through */ - case WKC_NUM_MINUS: - case WKC_MINUS: - case WKC_SHIFT | WKC_MINUS: - case WKC_SHIFT | WKC_F6: ToolbarZoomOutClick(w); break; - - case 'L': ShowEditorTerraformToolbar(); break; - case 'M': ShowSmallMap(); break; - case 'V': ShowExtraViewPortWindow(); break; - default: return; + if (extra_spacing_at[j] == i) { + j++; + uint add = spacing / (lengthof(extra_spacing_at) - j); + spacing -= add; + x += add; } - e->we.keypress.cont = false; - break; - - case WE_PLACE_OBJ: - _place_proc(e->we.place.tile); - break; - - case WE_ABORT_PLACE_OBJ: - w->RaiseWidget(25); - w->SetDirty(); - break; - - case WE_RESIZE: { - /* There are 15 buttons plus some spacings if the space allows it. - * Furthermore there are two panels of which one is non - essential - * and that one can be removed is the space is too small. */ - uint buttons_width; - uint spacing; - - static int normal_min_width = (15 * 22) + (2 * 130); - static int one_less_panel_min_width = (15 * 22) + 130; + } + } - if (w->width >= one_less_panel_min_width) { - buttons_width = 15 * 22; - spacing = w->width - ((w->width >= normal_min_width) ? normal_min_width : one_less_panel_min_width); - } else { - buttons_width = w->width - 130; - spacing = 0; - } - uint extra_spacing_at[] = { 3, 4, 7, 8, 10, 16, 0 }; - - /* Yes, it defines about 27 widgets for this toolbar */ - for (uint i = 0, x = 0, j = 0, b = 0; i < 27; i++) { - switch (i) { - case 4: - w->widget[i].left = x; - if (w->width < normal_min_width) { - w->widget[i].right = x; - j++; - continue; - } - - x += 130; - w->widget[i].right = x - 1; - break; - - case 5: { - int offset = x - w->widget[i].left; - w->widget[i + 1].left += offset; - w->widget[i + 1].right += offset; - w->widget[i + 2].left += offset; - w->widget[i + 2].right += offset; - w->widget[i].left = x; - x += 130; - w->widget[i].right = x - 1; - i += 2; - } break; + virtual void OnTick() + { + if (this->IsWidgetLowered(0) != !!_pause_game) { + this->ToggleWidgetLoweredState(0); + this->SetDirty(); + } - default: - if (w->widget[i].bottom == 0) continue; - - w->widget[i].left = x; - x += buttons_width / (15 - b); - w->widget[i].right = x - 1; - buttons_width -= buttons_width / (15 - b); - b++; - break; - } + if (this->IsWidgetLowered(1) != !!_fast_forward) { + this->ToggleWidgetLoweredState(1); + this->SetDirty(); + } + } - if (extra_spacing_at[j] == i) { - j++; - uint add = spacing / (lengthof(extra_spacing_at) - j); - spacing -= add; - x += add; - } - } - } break; - - case WE_TICK: - if (w->IsWidgetLowered(0) != !!_pause_game) { - w->ToggleWidgetLoweredState(0); - w->SetDirty(); - } - - if (w->IsWidgetLowered(1) != !!_fast_forward) { - w->ToggleWidgetLoweredState(1); - w->SetDirty(); - } - break; - - case WE_INVALIDATE_DATA: - HandleZoomMessage(w, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, 9, 10); - break; + virtual void OnInvalidateData(int data) + { + if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) HandleZoomMessage(this, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, 17, 18); } -} +}; static const Widget _toolb_scen_widgets[] = { { WWT_IMGBTN, RESIZE_LEFT, 14, 0, 0, 0, 21, SPR_IMG_PAUSE, STR_0171_PAUSE_GAME}, @@ -1082,7 +1108,6 @@ WC_MAIN_TOOLBAR, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _toolb_scen_widgets, - ScenEditToolbarWndProc }; /* --- Rendering/handling the drop down menus --- */ @@ -1119,71 +1144,77 @@ MenuClickHelp, /* 26 */ }; -static void MenuWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - w->widget[0].right = w->width - 1; - break; - - case WE_PAINT: { - byte count = WP(w, menu_d).item_count; - byte sel = WP(w, menu_d).sel_index; - uint16 chk = WP(w, menu_d).checked_items; - StringID string = WP(w, menu_d).string_id; - byte dis = WP(w, menu_d).disabled_items; - - DrawWindowWidgets(w); +struct ToolbarMenuWindow : Window { + int item_count; + int sel_index; + int main_button; + int action_id; + int checked_items; + int disabled_items; + StringID base_string; - int x = 1; - int y = 1; - - for (; count != 0; count--, string++, sel--) { - TextColour color = HasBit(dis, 0) ? TC_GREY : (sel == 0) ? TC_WHITE : TC_BLACK; - if (sel == 0) GfxFillRect(x, y, x + w->width - 3, y + 9, 0); - - if (HasBit(chk, 0)) DrawString(x + 2, y, STR_CHECKMARK, color); - DrawString(x + 2, y, string, color); + ToolbarMenuWindow(int x, int y, int width, int height, const Widget *widgets, int item_count, + int sel_index, int parent_button, StringID base_string, int checked_items, + int disabled_mask) : + Window(x, y, width, height, WC_TOOLBAR_MENU, widgets), + item_count(item_count), sel_index(sel_index), main_button(GB(parent_button, 0, 8)), + action_id((GB(parent_button, 8, 8) != 0) ? GB(parent_button, 8, 8) : parent_button), + checked_items(checked_items), disabled_items(disabled_items), base_string(base_string) + { + this->widget[0].bottom = item_count * 10 + 1; + this->widget[0].right = this->width - 1; + this->flags4 &= ~WF_WHITE_BORDER_MASK; - y += 10; - chk >>= 1; - dis >>= 1; - } - } break; + this->FindWindowPlacementAndResize(width, height); + } - case WE_DESTROY: { - Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0); - v->RaiseWidget(WP(w, menu_d).main_button); - v->SetDirty(); - return; + ~ToolbarMenuWindow() + { + Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); + w->RaiseWidget(this->main_button); + w->SetDirty(); + } + + virtual void OnPaint() + { + this->DrawWidgets(); + + for (int i = 0, x = 1, y = 1; i != this->item_count; i++, y += 10) { + TextColour color = HasBit(this->disabled_items, i) ? TC_GREY : (this->sel_index == i) ? TC_WHITE : TC_BLACK; + if (this->sel_index == i) GfxFillRect(x, y, x + this->width - 3, y + 9, 0); + + if (HasBit(this->checked_items, i)) DrawString(x + 2, y, STR_CHECKMARK, color); + DrawString(x + 2, y, this->base_string + i, color); + } + } + + virtual void OnMouseLoop() + { + int index = GetMenuItemIndex(this, this->item_count, this->disabled_items); + + if (_left_button_down) { + if (index == -1 || index == this->sel_index) return; + + this->sel_index = index; + this->SetDirty(); + } else { + if (index < 0) { + Window *w = FindWindowById(WC_MAIN_TOOLBAR,0); + if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) == this->main_button) { + index = this->sel_index; + } } - case WE_MOUSELOOP: { - int index = GetMenuItemIndex(w); - - if (_left_button_down) { - if (index == -1 || index == WP(w, menu_d).sel_index) return; + int action_id = this->action_id; + delete this; - WP(w, menu_d).sel_index = index; - w->SetDirty(); - } else { - if (index < 0) { - Window *w2 = FindWindowById(WC_MAIN_TOOLBAR,0); - if (GetWidgetFromPos(w2, _cursor.pos.x - w2->left, _cursor.pos.y - w2->top) == WP(w, menu_d).main_button) - index = WP(w, menu_d).sel_index; - } - - int action_id = WP(w, menu_d).action_id; - delete w; - - if (index >= 0) { - assert((uint)index <= lengthof(_menu_clicked_procs)); - _menu_clicked_procs[action_id](index); - } + if (index >= 0) { + assert((uint)index <= lengthof(_menu_clicked_procs)); + _menu_clicked_procs[action_id](index); } - } break; + } } -} +}; /* Dynamic widget length determined by toolbar-string length. * See PopupMainToolbMenu en MenuWndProc */ @@ -1222,7 +1253,7 @@ * aligned with the toolbar's right side. * Since the disable-mask is only 8 bits right now, these dropdowns are * restricted to 8 items max if any bits of disabled_mask are active. - * @param w Pointer to a window this dropdown menu belongs to. Has no effect + * @param parent Pointer to a window this dropdown menu belongs to. Has no effect * whatsoever, only graphically for positioning. * @param parent_button The widget identifier of the button that was clicked for * this dropdown. The created dropdown then knows what button to raise (button) on @@ -1236,12 +1267,15 @@ * consecutive indeces from the language file. XXX - fix? Use ingame-string tables? * @param item_count Number of strings in the list, see previous parameter * @param disabled_mask Bitmask of disabled strings in the list - * @return Return a pointer to the newly created dropdown window */ -static Window *PopupMainToolbMenu(Window *w, uint16 parent_button, StringID base_string, byte item_count, byte disabled_mask) + * @param sel_index The selected toolbar item + * @param check_items The items to have a checked mark in front of them. + * @return Return a pointer to the newly created dropdown window + */ +static void PopupMainToolbMenu(Window *parent, uint16 parent_button, StringID base_string, byte item_count, byte disabled_mask, int sel_index, int checked_items) { assert(disabled_mask == 0 || item_count <= 8); - w->LowerWidget(parent_button); - w->InvalidateWidget(parent_button); + parent->LowerWidget(parent_button); + parent->InvalidateWidget(parent_button); DeleteWindowById(WC_TOOLBAR_MENU, 0); @@ -1251,20 +1285,9 @@ Point pos = GetToolbarDropdownPos(parent_button, width, height); - w = new Window(pos.x, pos.y, width, height, MenuWndProc, WC_TOOLBAR_MENU, _menu_widgets); - w->widget[0].bottom = item_count * 10 + 1; - w->flags4 &= ~WF_WHITE_BORDER_MASK; - - WP(w, menu_d).item_count = item_count; - WP(w, menu_d).sel_index = 0; - WP(w, menu_d).main_button = GB(parent_button, 0, 8); - WP(w, menu_d).action_id = (GB(parent_button, 8, 8) != 0) ? GB(parent_button, 8, 8) : parent_button; - WP(w, menu_d).string_id = base_string; - WP(w, menu_d).checked_items = 0; - WP(w, menu_d).disabled_items = disabled_mask; + new ToolbarMenuWindow(pos.x, pos.y, width, height, _menu_widgets, item_count, sel_index, parent_button, base_string, checked_items, disabled_mask); SndPlayFx(SND_15_BEEP); - return w; } /* --- Rendering/drawing the player menu --- */ @@ -1280,170 +1303,169 @@ return -1; } -static void UpdatePlayerMenuHeight(Window *w) -{ - byte num = ActivePlayerCount(); - - /* Increase one to fit in PlayerList in the menu when in network */ - if (_networking && WP(w, menu_d).main_button == 9) num++; +struct ToolbarPlayerMenuWindow : Window { + int item_count; + int sel_index; + int main_button; + int action_id; + int gray_items; - if (WP(w, menu_d).item_count != num) { - WP(w, menu_d).item_count = num; - w->SetDirty(); - num = num * 10 + 2; - w->height = num; - w->widget[0].bottom = w->widget[0].top + num - 1; - w->top = GetToolbarDropdownPos(0, w->width, w->height).y; + ToolbarPlayerMenuWindow(int x, int y, int width, int height, const Widget *widgets, int main_button, int gray) : + Window(x, y, width, height, WC_TOOLBAR_MENU, widgets), + item_count(0), main_button(main_button), action_id(main_button), gray_items(gray) + { + this->flags4 &= ~WF_WHITE_BORDER_MASK; + this->sel_index = (_local_player != PLAYER_SPECTATOR) ? _local_player : GetPlayerIndexFromMenu(0); + if (_networking && main_button == 9) { + if (_local_player != PLAYER_SPECTATOR) { + this->sel_index++; + } else { + /* Select client list by default for spectators */ + this->sel_index = 0; + } + } + } + + ~ToolbarPlayerMenuWindow() + { + Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); + w->RaiseWidget(this->main_button); w->SetDirty(); } -} -static void PlayerMenuWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - UpdatePlayerMenuHeight(w); - DrawWindowWidgets(w); + void UpdatePlayerMenuHeight() + { + byte num = ActivePlayerCount(); - int x = 1; - int y = 1; - int sel = WP(w, menu_d).sel_index; - int chk = WP(w, menu_d).checked_items; // let this mean gray items. + /* Increase one to fit in PlayerList in the menu when in network */ + if (_networking && this->main_button == 9) num++; - /* 9 = playerlist */ - if (_networking && WP(w, menu_d).main_button == 9) { - if (sel == 0) { + if (this->item_count != num) { + this->item_count = num; + this->SetDirty(); + num = num * 10 + 2; + this->height = num; + this->widget[0].bottom = this->widget[0].top + num - 1; + this->top = GetToolbarDropdownPos(0, this->width, this->height).y; + this->SetDirty(); + } + } + + virtual void OnPaint() + { + this->UpdatePlayerMenuHeight(); + this->DrawWidgets(); + + int x = 1; + int y = 1; + int sel = this->sel_index; + int gray = this->gray_items; + + /* 9 = playerlist */ + if (_networking && this->main_button == 9) { + if (sel == 0) { + GfxFillRect(x, y, x + 238, y + 9, 0); + } + DrawString(x + 19, y, STR_NETWORK_CLIENT_LIST, TC_FROMSTRING); + y += 10; + sel--; + } + + const Player *p; + FOR_ALL_PLAYERS(p) { + if (p->is_active) { + if (p->index == sel) { GfxFillRect(x, y, x + 238, y + 9, 0); } - DrawString(x + 19, y, STR_NETWORK_CLIENT_LIST, TC_FROMSTRING); + + DrawPlayerIcon(p->index, x + 2, y + 1); + + SetDParam(0, p->index); + SetDParam(1, p->index); + + TextColour color = (p->index == sel) ? TC_WHITE : TC_BLACK; + if (gray & 1) color = TC_GREY; + DrawString(x + 19, y, STR_7021, color); + y += 10; - sel--; + } + gray >>= 1; + } + } + + virtual void OnMouseLoop() + { + int index = GetMenuItemIndex(this, this->item_count, 0); + + if (_left_button_down) { + this->UpdatePlayerMenuHeight(); + /* We have a new entry at the top of the list of menu 9 when networking + * so keep that in count */ + if (_networking && this->main_button == 9) { + if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1; + } else { + index = GetPlayerIndexFromMenu(index); } - const Player *p; - FOR_ALL_PLAYERS(p) { - if (p->is_active) { - if (p->index == sel) { - GfxFillRect(x, y, x + 238, y + 9, 0); - } - - DrawPlayerIcon(p->index, x + 2, y + 1); - - SetDParam(0, p->index); - SetDParam(1, p->index); - - TextColour color = (p->index == sel) ? TC_WHITE : TC_BLACK; - if (chk & 1) color = TC_GREY; - DrawString(x + 19, y, STR_7021, color); - - y += 10; - } - chk >>= 1; - } - } break; - - case WE_DESTROY: { - Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0); - v->RaiseWidget(WP(w, menu_d).main_button); - v->SetDirty(); - return; - } - - case WE_MOUSELOOP: { - int index = GetMenuItemIndex(w); + if (index == -1 || index == this->sel_index) return; - if (_left_button_down) { - UpdatePlayerMenuHeight(w); - /* We have a new entry at the top of the list of menu 9 when networking - * so keep that in count */ - if (_networking && WP(w, menu_d).main_button == 9) { - if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1; - } else { - index = GetPlayerIndexFromMenu(index); - } - - if (index == -1 || index == WP(w, menu_d).sel_index) return; - - WP(w, menu_d).sel_index = index; - w->SetDirty(); - } else { - int action_id = WP(w, menu_d).action_id; + this->sel_index = index; + this->SetDirty(); + } else { + int action_id = this->action_id; - /* We have a new entry at the top of the list of menu 9 when networking - * so keep that in count */ - if (_networking && WP(w, menu_d).main_button == 9) { - if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1; - } else { - index = GetPlayerIndexFromMenu(index); - } + /* We have a new entry at the top of the list of menu 9 when networking + * so keep that in count */ + if (_networking && this->main_button == 9) { + if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1; + } else { + index = GetPlayerIndexFromMenu(index); + } - if (index < 0) { - Window *w2 = FindWindowById(WC_MAIN_TOOLBAR,0); - if (GetWidgetFromPos(w2, _cursor.pos.x - w2->left, _cursor.pos.y - w2->top) == WP(w, menu_d).main_button) - index = WP(w, menu_d).sel_index; - } - - delete w; - - if (index >= 0) { - assert(index >= 0 && index < 30); - _menu_clicked_procs[action_id](index); + if (index < 0) { + Window *w = FindWindowById(WC_MAIN_TOOLBAR,0); + if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) == this->main_button) { + index = this->sel_index; } } - } break; + + delete this; + + if (index >= 0) { + assert(index >= 0 && index < 30); + _menu_clicked_procs[action_id](index); + } + } } -} +}; static const Widget _player_menu_widgets[] = { { WWT_PANEL, RESIZE_NONE, 14, 0, 240, 0, 81, 0x0, STR_NULL}, { WIDGETS_END}, }; -static Window *PopupMainPlayerToolbMenu(Window *w, int main_button, int gray) +static void PopupMainPlayerToolbMenu(Window *parent, int main_button, int gray) { - w->LowerWidget(main_button); - w->InvalidateWidget(main_button); + parent->LowerWidget(main_button); + parent->InvalidateWidget(main_button); DeleteWindowById(WC_TOOLBAR_MENU, 0); Point pos = GetToolbarDropdownPos(main_button, 241, 82); - w = new Window(pos.x, pos.y, 241, 82, PlayerMenuWndProc, WC_TOOLBAR_MENU, _player_menu_widgets); - w->flags4 &= ~WF_WHITE_BORDER_MASK; - WP(w, menu_d).item_count = 0; - WP(w, menu_d).sel_index = (_local_player != PLAYER_SPECTATOR) ? _local_player : GetPlayerIndexFromMenu(0); - if (_networking && main_button == 9) { - if (_local_player != PLAYER_SPECTATOR) { - WP(w, menu_d).sel_index++; - } else { - /* Select client list by default for spectators */ - WP(w, menu_d).sel_index = 0; - } - } - WP(w, menu_d).action_id = main_button; - WP(w, menu_d).main_button = main_button; - WP(w, menu_d).checked_items = gray; - WP(w, menu_d).disabled_items = 0; + new ToolbarPlayerMenuWindow(pos.x, pos.y, 241, 82, _player_menu_widgets, main_button, gray); SndPlayFx(SND_15_BEEP); - return w; } /* --- Allocating the toolbar --- */ -Window *AllocateToolbar() +void AllocateToolbar() { /* Clean old GUI values; railtype is (re)set by rail_gui.cpp */ _last_built_roadtype = ROADTYPE_ROAD; - Window *w = new Window((_game_mode != GM_EDITOR) ? &_toolb_normal_desc : &_toolb_scen_desc); - if (w == NULL) return NULL; - - CLRBITS(w->flags4, WF_WHITE_BORDER_MASK); - - w->SetWidgetDisabledState(0, _networking && !_network_server); // if not server, disable pause button - w->SetWidgetDisabledState(1, _networking); // if networking, disable fast-forward button - - /* 'w' is for sure a WC_MAIN_TOOLBAR */ - PositionMainToolbar(w); - - return w; + if (_game_mode == GM_EDITOR) { + new ScenarioEditorToolbarWindow(&_toolb_scen_desc);; + } else { + new MainToolbarWindow(&_toolb_normal_desc); + } } diff -r 6c4314786d68 -r 8cbdb511a674 src/toolbar_gui.h --- a/src/toolbar_gui.h Mon May 19 14:14:33 2008 +0000 +++ b/src/toolbar_gui.h Mon May 19 15:13:58 2008 +0000 @@ -5,8 +5,6 @@ #ifndef TOOLBAR_GUI_H #define TOOLBAR_GUI_H -#include "window_type.h" - -Window *AllocateToolbar(); +void AllocateToolbar(); #endif /*TOOLBAR_GUI_H*/ diff -r 6c4314786d68 -r 8cbdb511a674 src/town_cmd.cpp --- a/src/town_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/town_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -2152,7 +2152,7 @@ SetDParam(1, _current_player); AddNewsItem(STR_2055_TRAFFIC_CHAOS_IN_ROAD_REBUILDING, - NM_NORMAL, NF_TILE, NT_GENERAL, DNC_NONE, t->xy, 0); + NS_GENERAL, t->xy, 0); } static bool DoBuildStatueOfCompany(TileIndex tile, TownID town_id) diff -r 6c4314786d68 -r 8cbdb511a674 src/town_gui.cpp --- a/src/town_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/town_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -141,140 +141,134 @@ return -1; } -static void TownAuthorityWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - const Town *t = GetTown(w->window_number); - int numact; - uint buttons = GetMaskOfTownActions(&numact, _local_player, t); - - SetVScrollCount(w, numact + 1); - - if (WP(w, def_d).data_1 != -1 && !HasBit(buttons, WP(w, def_d).data_1)) - WP(w, def_d).data_1 = -1; - - w->SetWidgetDisabledState(6, WP(w, def_d).data_1 == -1); - - { - int y; - const Player *p; - int r; - StringID str; - - SetDParam(0, w->window_number); - DrawWindowWidgets(w); - - DrawString(2, 15, STR_2023_TRANSPORT_COMPANY_RATINGS, TC_FROMSTRING); +struct TownAuthorityWindow : Window { + int sel_index; - /* Draw list of players */ - y = 25; - FOR_ALL_PLAYERS(p) { - if (p->is_active && (HasBit(t->have_ratings, p->index) || t->exclusivity == p->index)) { - DrawPlayerIcon(p->index, 2, y); - - SetDParam(0, p->index); - SetDParam(1, p->index); + TownAuthorityWindow(const WindowDesc *desc, WindowNumber window_number) : + Window(desc, window_number), sel_index(-1) + { + this->vscroll.cap = 5; - r = t->ratings[p->index]; - (str = STR_3035_APPALLING, r <= RATING_APPALLING) || // Apalling - (str++, r <= RATING_VERYPOOR) || // Very Poor - (str++, r <= RATING_POOR) || // Poor - (str++, r <= RATING_MEDIOCRE) || // Mediocore - (str++, r <= RATING_GOOD) || // Good - (str++, r <= RATING_VERYGOOD) || // Very Good - (str++, r <= RATING_EXCELLENT) || // Excellent - (str++, true); // Outstanding + this->FindWindowPlacementAndResize(desc); + } - SetDParam(2, str); - if (t->exclusivity == p->index) { // red icon for player with exclusive rights - DrawSprite(SPR_BLOT, PALETTE_TO_RED, 18, y); - } + virtual void OnPaint() + { + const Town *t = GetTown(this->window_number); + int numact; + uint buttons = GetMaskOfTownActions(&numact, _local_player, t); - DrawString(28, y, STR_2024, TC_FROMSTRING); - y += 10; - } + SetVScrollCount(this, numact + 1); + + if (this->sel_index != -1 && !HasBit(buttons, this->sel_index)) { + this->sel_index = -1; + } + + this->SetWidgetDisabledState(6, this->sel_index == -1); + + SetDParam(0, this->window_number); + this->DrawWidgets(); + + DrawString(2, 15, STR_2023_TRANSPORT_COMPANY_RATINGS, TC_FROMSTRING); + + /* Draw list of players */ + int y = 25; + + const Player *p; + FOR_ALL_PLAYERS(p) { + if (p->is_active && (HasBit(t->have_ratings, p->index) || t->exclusivity == p->index)) { + DrawPlayerIcon(p->index, 2, y); + + SetDParam(0, p->index); + SetDParam(1, p->index); + + int r = t->ratings[p->index]; + StringID str; + (str = STR_3035_APPALLING, r <= RATING_APPALLING) || // Apalling + (str++, r <= RATING_VERYPOOR) || // Very Poor + (str++, r <= RATING_POOR) || // Poor + (str++, r <= RATING_MEDIOCRE) || // Mediocore + (str++, r <= RATING_GOOD) || // Good + (str++, r <= RATING_VERYGOOD) || // Very Good + (str++, r <= RATING_EXCELLENT) || // Excellent + (str++, true); // Outstanding + + SetDParam(2, str); + if (t->exclusivity == p->index) { // red icon for player with exclusive rights + DrawSprite(SPR_BLOT, PALETTE_TO_RED, 18, y); } + + DrawString(28, y, STR_2024, TC_FROMSTRING); + y += 10; + } + } + y = 107; + int pos = this->vscroll.pos; + + if (--pos < 0) { + DrawString(2, y, STR_2045_ACTIONS_AVAILABLE, TC_FROMSTRING); + y += 10; + } + + for (int i = 0; buttons; i++, buttons >>= 1) { + if (pos <= -5) break; ///< Draw only the 5 fitting lines + + if ((buttons & 1) && --pos < 0) { + DrawString(3, y, STR_2046_SMALL_ADVERTISING_CAMPAIGN + i, TC_ORANGE); + y += 10; + } + } + + if (this->sel_index != -1) { + SetDParam(1, (_price.build_industry >> 8) * _town_action_costs[this->sel_index]); + SetDParam(0, STR_2046_SMALL_ADVERTISING_CAMPAIGN + this->sel_index); + DrawStringMultiLine(2, 159, STR_204D_INITIATE_A_SMALL_LOCAL + this->sel_index, 313); + } + } + + virtual void OnDoubleClick(Point pt, int widget) { HandleClick(pt, widget, true); } + virtual void OnClick(Point pt, int widget) { HandleClick(pt, widget, false); } + + void HandleClick(Point pt, int widget, bool double_click) + { + switch (widget) { + case TWA_COMMAND_LIST: { + const Town *t = GetTown(this->window_number); + int y = (pt.y - 0x6B) / 10; + + if (!IsInsideMM(y, 0, 5)) return; + + y = GetNthSetBit(GetMaskOfTownActions(NULL, _local_player, t), y + this->vscroll.pos - 1); + if (y >= 0) { + this->sel_index = y; + this->SetDirty(); + } + /* Fall through to clicking in case we are double-clicked */ + if (!double_click || y < 0) break; } - /* Draw actions list */ - { - int y = 107, i; - int pos = w->vscroll.pos; - - if (--pos < 0) { - DrawString(2, y, STR_2045_ACTIONS_AVAILABLE, TC_FROMSTRING); - y += 10; - } - - for (i = 0; buttons; i++, buttons >>= 1) { - if (pos <= -5) break; ///< Draw only the 5 fitting lines - - if ((buttons & 1) && --pos < 0) { - DrawString(3, y, STR_2046_SMALL_ADVERTISING_CAMPAIGN + i, TC_ORANGE); - y += 10; - } - } - } - - { - int i = WP(w, def_d).data_1; - - if (i != -1) { - SetDParam(1, (_price.build_industry >> 8) * _town_action_costs[i]); - SetDParam(0, STR_2046_SMALL_ADVERTISING_CAMPAIGN + i); - DrawStringMultiLine(2, 159, STR_204D_INITIATE_A_SMALL_LOCAL + i, 313); - } - } + case TWA_EXECUTE: + DoCommandP(GetTown(this->window_number)->xy, this->window_number, this->sel_index, NULL, CMD_DO_TOWN_ACTION | CMD_MSG(STR_00B4_CAN_T_DO_THIS)); + break; + } + } - } break; - - case WE_DOUBLE_CLICK: - case WE_CLICK: - switch (e->we.click.widget) { - case TWA_COMMAND_LIST: { - const Town *t = GetTown(w->window_number); - int y = (e->we.click.pt.y - 0x6B) / 10; - - if (!IsInsideMM(y, 0, 5)) return; - - y = GetNthSetBit(GetMaskOfTownActions(NULL, _local_player, t), y + w->vscroll.pos - 1); - if (y >= 0) { - WP(w, def_d).data_1 = y; - w->SetDirty(); - } - /* Fall through to clicking in case we are double-clicked */ - if (e->event != WE_DOUBLE_CLICK || y < 0) break; - } - - case TWA_EXECUTE: - DoCommandP(GetTown(w->window_number)->xy, w->window_number, WP(w, def_d).data_1, NULL, CMD_DO_TOWN_ACTION | CMD_MSG(STR_00B4_CAN_T_DO_THIS)); - break; - } - break; - - case WE_100_TICKS: - w->SetDirty(); - break; + virtual void OnHundredthTick() + { + this->SetDirty(); } -} +}; static const WindowDesc _town_authority_desc = { WDP_AUTO, WDP_AUTO, 317, 222, 317, 222, WC_TOWN_AUTHORITY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _town_authority_widgets, - TownAuthorityWndProc }; static void ShowTownAuthorityWindow(uint town) { - Window *w = AllocateWindowDescFront(&_town_authority_desc, town); - - if (w != NULL) { - w->vscroll.cap = 5; - WP(w, def_d).data_1 = -1; - } + AllocateWindowDescFront(&_town_authority_desc, town); } @@ -288,87 +282,98 @@ TVW_DELETE, }; -static void TownViewWndProc(Window *w, WindowEvent *e) -{ - Town *t = GetTown(w->window_number); - - switch (e->event) { - case WE_CREATE: { - bool ingame = _game_mode != GM_EDITOR; - if (t->larger_town) w->widget[TVW_CAPTION].data = STR_CITY; - w->SetWidgetHiddenState(TVW_DELETE, ingame); // hide delete button on game mode - w->SetWidgetHiddenState(TVW_EXPAND, ingame); // hide expand button on game mode - w->SetWidgetHiddenState(TVW_SHOWAUTORITY, !ingame); // hide autority button on editor mode - - if (ingame) { - /* resize caption bar */ - w->widget[TVW_CAPTION].right = w->widget[TVW_STICKY].left -1; - /* move the rename from top on scenario to bottom in game */ - w->widget[TVW_CHANGENAME].top = w->widget[TVW_EXPAND].top; - w->widget[TVW_CHANGENAME].bottom = w->widget[TVW_EXPAND].bottom; - w->widget[TVW_CHANGENAME].right = w->widget[TVW_STICKY].right; - } - } break; - - case WE_PAINT: - /* disable renaming town in network games if you are not the server */ - w->SetWidgetDisabledState(TVW_CHANGENAME, _networking && !_network_server); - - SetDParam(0, t->index); - DrawWindowWidgets(w); - - SetDParam(0, t->population); - SetDParam(1, t->num_houses); - DrawString(2, 107, STR_2006_POPULATION, TC_FROMSTRING); - - SetDParam(0, t->act_pass); - SetDParam(1, t->max_pass); - DrawString(2, 117, STR_200D_PASSENGERS_LAST_MONTH_MAX, TC_FROMSTRING); +struct TownViewWindow : Window { + TownViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + const Town *t = GetTown(this->window_number); - SetDParam(0, t->act_mail); - SetDParam(1, t->max_mail); - DrawString(2, 127, STR_200E_MAIL_LAST_MONTH_MAX, TC_FROMSTRING); - - DrawWindowViewport(w); - break; - - case WE_CLICK: - switch (e->we.click.widget) { - case TVW_CENTERVIEW: /* scroll to location */ - if (_ctrl_pressed) { - ShowExtraViewPortWindow(t->xy); - } else { - ScrollMainWindowToTile(t->xy); - } - break; - - case TVW_SHOWAUTORITY: /* town authority */ - ShowTownAuthorityWindow(w->window_number); - break; + this->flags4 |= WF_DISABLE_VP_SCROLL; + InitializeWindowViewport(this, 3, 17, 254, 86, t->xy, ZOOM_LVL_TOWN); - case TVW_CHANGENAME: /* rename */ - SetDParam(0, w->window_number); - ShowQueryString(STR_TOWN, STR_2007_RENAME_TOWN, 31, 130, w, CS_ALPHANUMERAL); - break; - - case TVW_EXPAND: /* expand town - only available on Scenario editor */ - ExpandTown(t); - break; + bool ingame = _game_mode != GM_EDITOR; + if (t->larger_town) this->widget[TVW_CAPTION].data = STR_CITY; + this->SetWidgetHiddenState(TVW_DELETE, ingame); // hide delete button on game mode + this->SetWidgetHiddenState(TVW_EXPAND, ingame); // hide expand button on game mode + this->SetWidgetHiddenState(TVW_SHOWAUTORITY, !ingame); // hide autority button on editor mode - case TVW_DELETE: /* delete town - only available on Scenario editor */ - delete t; - break; - } break; + if (ingame) { + /* resize caption bar */ + this->widget[TVW_CAPTION].right = this->widget[TVW_STICKY].left -1; + /* move the rename from top on scenario to bottom in game */ + this->widget[TVW_CHANGENAME].top = this->widget[TVW_EXPAND].top; + this->widget[TVW_CHANGENAME].bottom = this->widget[TVW_EXPAND].bottom; + this->widget[TVW_CHANGENAME].right = this->widget[TVW_STICKY].right; + } - case WE_ON_EDIT_TEXT: - if (!StrEmpty(e->we.edittext.str)) { - _cmd_text = e->we.edittext.str; - DoCommandP(0, w->window_number, 0, NULL, - CMD_RENAME_TOWN | CMD_MSG(STR_2008_CAN_T_RENAME_TOWN)); - } - break; + this->FindWindowPlacementAndResize(desc); } -} + + virtual void OnPaint() + { + const Town *t = GetTown(this->window_number); + + /* disable renaming town in network games if you are not the server */ + this->SetWidgetDisabledState(TVW_CHANGENAME, _networking && !_network_server); + + SetDParam(0, t->index); + this->DrawWidgets(); + + SetDParam(0, t->population); + SetDParam(1, t->num_houses); + DrawString(2, 107, STR_2006_POPULATION, TC_FROMSTRING); + + SetDParam(0, t->act_pass); + SetDParam(1, t->max_pass); + DrawString(2, 117, STR_200D_PASSENGERS_LAST_MONTH_MAX, TC_FROMSTRING); + + SetDParam(0, t->act_mail); + SetDParam(1, t->max_mail); + DrawString(2, 127, STR_200E_MAIL_LAST_MONTH_MAX, TC_FROMSTRING); + + this->DrawViewport(); + } + + virtual void OnClick(Point pt, int widget) + { + Town *t = GetTown(this->window_number); + + switch (widget) { + case TVW_CENTERVIEW: /* scroll to location */ + if (_ctrl_pressed) { + ShowExtraViewPortWindow(t->xy); + } else { + ScrollMainWindowToTile(t->xy); + } + break; + + case TVW_SHOWAUTORITY: /* town authority */ + ShowTownAuthorityWindow(this->window_number); + break; + + case TVW_CHANGENAME: /* rename */ + SetDParam(0, this->window_number); + ShowQueryString(STR_TOWN, STR_2007_RENAME_TOWN, 31, 130, this, CS_ALPHANUMERAL); + break; + + case TVW_EXPAND: /* expand town - only available on Scenario editor */ + ExpandTown(t); + break; + + case TVW_DELETE: /* delete town - only available on Scenario editor */ + delete t; + break; + } + } + + virtual void OnQueryTextFinished(char *str) + { + if (!StrEmpty(str)) { + _cmd_text = str; + DoCommandP(0, this->window_number, 0, NULL, + CMD_RENAME_TOWN | CMD_MSG(STR_2008_CAN_T_RENAME_TOWN)); + } + } +}; static const Widget _town_view_widgets[] = { @@ -391,26 +396,13 @@ WC_TOWN_VIEW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON, _town_view_widgets, - TownViewWndProc }; void ShowTownViewWindow(TownID town) { - Window *w; - - w = AllocateWindowDescFront(&_town_view_desc, town); - - if (w != NULL) { - w->flags4 |= WF_DISABLE_VP_SCROLL; - InitializeWindowViewport(w, 3, 17, 254, 86, GetTown(town)->xy, ZOOM_LVL_TOWN); - } + AllocateWindowDescFront(&_town_view_desc, town); } -enum TownDirectoryWidget { - TDW_SORTNAME = 3, - TDW_SORTPOPULATION, - TDW_CENTERTOWN, -}; static const Widget _town_directory_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, { WWT_CAPTION, RESIZE_NONE, 13, 11, 195, 0, 13, STR_2000_TOWNS, STR_018C_WINDOW_TITLE_DRAG_THIS}, @@ -483,107 +475,117 @@ } -static void TownDirectoryWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - if (_town_sort_dirty) { - _town_sort_dirty = false; - MakeSortedTownList(); +struct TownDirectoryWindow : public Window { + enum TownDirectoryWidget { + TDW_SORTNAME = 3, + TDW_SORTPOPULATION, + TDW_CENTERTOWN, + }; + + TownDirectoryWindow(const WindowDesc *desc) : Window(desc, 0) + { + this->vscroll.cap = 16; + this->resize.step_height = 10; + this->resize.height = this->height - 10 * 6; // minimum of 10 items in the list, each item 10 high + + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { + if (_town_sort_dirty) { + _town_sort_dirty = false; + MakeSortedTownList(); + } + + SetVScrollCount(this, _num_town_sort); + + this->DrawWidgets(); + this->DrawSortButtonState((_town_sort_order <= 1) ? TDW_SORTNAME : TDW_SORTPOPULATION, _town_sort_order & 1 ? SBS_DOWN : SBS_UP); + + { + int n = 0; + uint16 i = this->vscroll.pos; + int y = 28; + + while (i < _num_town_sort) { + const Town* t = _town_sort[i]; + + assert(t->xy); + + SetDParam(0, t->index); + SetDParam(1, t->population); + DrawString(2, y, STR_2057, TC_FROMSTRING); + + y += 10; + i++; + if (++n == this->vscroll.cap) break; // max number of towns in 1 window } - SetVScrollCount(w, _num_town_sort); - - DrawWindowWidgets(w); - DrawSortButtonState(w, (_town_sort_order <= 1) ? TDW_SORTNAME : TDW_SORTPOPULATION, _town_sort_order & 1 ? SBS_DOWN : SBS_UP); - - { - int n = 0; - uint16 i = w->vscroll.pos; - int y = 28; - - while (i < _num_town_sort) { - const Town* t = _town_sort[i]; - - assert(t->xy); - - SetDParam(0, t->index); - SetDParam(1, t->population); - DrawString(2, y, STR_2057, TC_FROMSTRING); - - y += 10; - i++; - if (++n == w->vscroll.cap) break; // max number of towns in 1 window - } - SetDParam(0, GetWorldPopulation()); - DrawString(3, w->height - 12 + 2, STR_TOWN_POPULATION, TC_FROMSTRING); - } - } break; - - case WE_CLICK: - switch (e->we.click.widget) { - case TDW_SORTNAME: /* Sort by Name ascending/descending */ - _town_sort_order = (_town_sort_order == 0) ? 1 : 0; - _town_sort_dirty = true; - w->SetDirty(); - break; + SetDParam(0, GetWorldPopulation()); + DrawString(3, this->height - 12 + 2, STR_TOWN_POPULATION, TC_FROMSTRING); + } + } - case TDW_SORTPOPULATION: /* Sort by Population ascending/descending */ - _town_sort_order = (_town_sort_order == 2) ? 3 : 2; - _town_sort_dirty = true; - w->SetDirty(); - break; - - case TDW_CENTERTOWN: { /* Click on Town Matrix */ - const Town* t; - - uint16 id_v = (e->we.click.pt.y - 28) / 10; - - if (id_v >= w->vscroll.cap) return; // click out of bounds - - id_v += w->vscroll.pos; - - if (id_v >= _num_town_sort) return; // click out of town bounds + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case TDW_SORTNAME: /* Sort by Name ascending/descending */ + _town_sort_order = (_town_sort_order == 0) ? 1 : 0; + _town_sort_dirty = true; + this->SetDirty(); + break; - t = _town_sort[id_v]; - assert(t->xy); - if (_ctrl_pressed) { - ShowExtraViewPortWindow(t->xy); - } else { - ScrollMainWindowToTile(t->xy); - } - } break; + case TDW_SORTPOPULATION: /* Sort by Population ascending/descending */ + _town_sort_order = (_town_sort_order == 2) ? 3 : 2; + _town_sort_dirty = true; + this->SetDirty(); + break; + + case TDW_CENTERTOWN: { /* Click on Town Matrix */ + const Town* t; + + uint16 id_v = (pt.y - 28) / 10; + + if (id_v >= this->vscroll.cap) return; // click out of bounds + + id_v += this->vscroll.pos; + + if (id_v >= _num_town_sort) return; // click out of town bounds + + t = _town_sort[id_v]; + assert(t->xy); + if (_ctrl_pressed) { + ShowExtraViewPortWindow(t->xy); + } else { + ScrollMainWindowToTile(t->xy); + } + break; } - break; + } + } - case WE_100_TICKS: - w->SetDirty(); - break; + virtual void OnHundredthTick() + { + this->SetDirty(); + } - case WE_RESIZE: - w->vscroll.cap += e->we.sizing.diff.y / 10; - break; + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / 10; } -} +}; static const WindowDesc _town_directory_desc = { WDP_AUTO, WDP_AUTO, 208, 202, 208, 202, WC_TOWN_DIRECTORY, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _town_directory_widgets, - TownDirectoryWndProc }; - void ShowTownDirectory() { - Window *w = AllocateWindowDescFront(&_town_directory_desc, 0); - - if (w != NULL) { - w->vscroll.cap = 16; - w->resize.step_height = 10; - w->resize.height = w->height - 10 * 6; // minimum of 10 items in the list, each item 10 high - } + new TownDirectoryWindow(&_town_directory_desc); } void CcBuildTown(bool success, TileIndex tile, uint32 p1, uint32 p2) @@ -627,85 +629,89 @@ { WIDGETS_END}, }; -static void ScenEditTownGenWndProc(Window *w, WindowEvent *e) +struct ScenarioEditorTownGenerationWindow : Window { - switch (e->event) { - case WE_PAINT: - DrawWindowWidgets(w); - break; - - case WE_CREATE: - w->LowerWidget(_scengen_town_size + TSEW_SMALLTOWN); - break; - - case WE_CLICK: - switch (e->we.click.widget) { - case TSEW_NEWTOWN: - HandlePlacePushButton(w, TSEW_NEWTOWN, SPR_CURSOR_TOWN, VHM_RECT, PlaceProc_Town); - break; - - case TSEW_RANDOMTOWN: { - Town *t; - uint size = min(_scengen_town_size, (int)TSM_CITY); - TownSizeMode mode = _scengen_town_size > TSM_CITY ? TSM_CITY : TSM_FIXED; + ScenarioEditorTownGenerationWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + this->LowerWidget(_scengen_town_size + TSEW_SMALLTOWN); + } - w->HandleButtonClick(TSEW_RANDOMTOWN); - _generating_world = true; - t = CreateRandomTown(20, mode, size); - _generating_world = false; - - if (t == NULL) { - ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0); - } else { - ScrollMainWindowToTile(t->xy); - } - } break; + virtual void OnPaint() + { + this->DrawWidgets(); + } - case TSEW_MANYRANDOMTOWNS: - w->HandleButtonClick(TSEW_MANYRANDOMTOWNS); + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case TSEW_NEWTOWN: + HandlePlacePushButton(this, TSEW_NEWTOWN, SPR_CURSOR_TOWN, VHM_RECT, PlaceProc_Town); + break; - _generating_world = true; - if (!GenerateTowns()) ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0); - _generating_world = false; - break; + case TSEW_RANDOMTOWN: { + Town *t; + uint size = min(_scengen_town_size, (int)TSM_CITY); + TownSizeMode mode = _scengen_town_size > TSM_CITY ? TSM_CITY : TSM_FIXED; - case TSEW_SMALLTOWN: case TSEW_MEDIUMTOWN: case TSEW_LARGETOWN: case TSEW_CITY: - w->RaiseWidget(_scengen_town_size + TSEW_SMALLTOWN); - _scengen_town_size = e->we.click.widget - TSEW_SMALLTOWN; - w->LowerWidget(_scengen_town_size + TSEW_SMALLTOWN); - w->SetDirty(); - break; + this->HandleButtonClick(TSEW_RANDOMTOWN); + _generating_world = true; + t = CreateRandomTown(20, mode, size); + _generating_world = false; + + if (t == NULL) { + ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0); + } else { + ScrollMainWindowToTile(t->xy); + } } break; - case WE_TIMEOUT: - w->RaiseWidget(TSEW_RANDOMTOWN); - w->RaiseWidget(TSEW_MANYRANDOMTOWNS); - w->SetDirty(); - break; + case TSEW_MANYRANDOMTOWNS: + this->HandleButtonClick(TSEW_MANYRANDOMTOWNS); - case WE_PLACE_OBJ: - _place_proc(e->we.place.tile); - break; + _generating_world = true; + if (!GenerateTowns()) ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0); + _generating_world = false; + break; - case WE_ABORT_PLACE_OBJ: - w->RaiseButtons(); - w->LowerWidget(_scengen_town_size + TSEW_SMALLTOWN); - w->SetDirty(); - break; + case TSEW_SMALLTOWN: case TSEW_MEDIUMTOWN: case TSEW_LARGETOWN: case TSEW_CITY: + this->RaiseWidget(_scengen_town_size + TSEW_SMALLTOWN); + _scengen_town_size = widget - TSEW_SMALLTOWN; + this->LowerWidget(_scengen_town_size + TSEW_SMALLTOWN); + this->SetDirty(); + break; + } } -} + + virtual void OnTimeout() + { + this->RaiseWidget(TSEW_RANDOMTOWN); + this->RaiseWidget(TSEW_MANYRANDOMTOWNS); + this->SetDirty(); + } + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + _place_proc(tile); + } + + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); + this->LowerWidget(_scengen_town_size + TSEW_SMALLTOWN); + this->SetDirty(); + } +}; static const WindowDesc _scen_edit_town_gen_desc = { WDP_AUTO, WDP_AUTO, 160, 95, 160, 95, WC_SCEN_TOWN_GEN, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _scen_edit_town_gen_widgets, - ScenEditTownGenWndProc, }; void ShowBuildTownWindow() { if (_game_mode != GM_EDITOR && !IsValidPlayer(_current_player)) return; - AllocateWindowDescFront(&_scen_edit_town_gen_desc, 0); + AllocateWindowDescFront(&_scen_edit_town_gen_desc, 0); } diff -r 6c4314786d68 -r 8cbdb511a674 src/track_func.h --- a/src/track_func.h Mon May 19 14:14:33 2008 +0000 +++ b/src/track_func.h Mon May 19 15:13:58 2008 +0000 @@ -24,8 +24,6 @@ return (Track)a; } - - /** * Maps a Track to the corresponding TrackBits value * @param track the track to convert @@ -277,7 +275,7 @@ * Converts TrackBits to TrackdirBits while allowing both directions. * * @param bits The TrackBits - * @return The TrackDirBits containing of bits in both directions. + * @return The TrackdirBits containing of bits in both directions. */ static inline TrackdirBits TrackBitsToTrackdirBits(TrackBits bits) { @@ -436,13 +434,35 @@ } /** + * Maps a (4-way) direction to the diagonal track incidating with that diagdir + * + * @param diagdir The direction + * @return The resulting Track + */ +static inline Track DiagDirToDiagTrack(DiagDirection diagdir) +{ + return (Track)(diagdir & 1); +} + +/** + * Maps a (4-way) direction to the diagonal track bits incidating with that diagdir + * + * @param diagdir The direction + * @return The resulting TrackBits + */ +static inline TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir) +{ + return TrackToTrackBits(DiagDirToDiagTrack(diagdir)); +} + +/** * Maps a (4-way) direction to the diagonal trackdir that runs in that * direction. * * @param diagdir The direction * @return The resulting Trackdir direction */ -static inline Trackdir DiagdirToDiagTrackdir(DiagDirection diagdir) +static inline Trackdir DiagDirToDiagTrackdir(DiagDirection diagdir) { extern const Trackdir _dir_to_diag_trackdir[DIAGDIR_END]; return _dir_to_diag_trackdir[diagdir]; diff -r 6c4314786d68 -r 8cbdb511a674 src/train_cmd.cpp --- a/src/train_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/train_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -119,6 +119,9 @@ if (engine_has_power) { uint16 power = GetVehicleProperty(u, 0x0B, rvi_u->power); if (power != 0) { + /* Halve power for multiheaded parts */ + if (IsMultiheaded(u)) power /= 2; + total_power += power; /* Tractive effort in (tonnes * 1000 * 10 =) N */ max_te += (u->u.rail.cached_veh_weight * 10000 * GetVehicleProperty(u, 0x1F, rvi_u->tractive_effort)) / 256; @@ -801,7 +804,7 @@ } InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_TRAINS_LIST, 0); InvalidateWindow(WC_COMPANY, v->owner); if (IsLocalPlayer()) { InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Train window @@ -1270,7 +1273,7 @@ InvalidateWindow(WC_VEHICLE_DEPOT, dst_head->tile); } - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_TRAINS_LIST, 0); } return CommandCost(); @@ -1348,10 +1351,10 @@ if (flags & DC_EXEC) { if (v == first && IsFrontEngine(first)) { - delete FindWindowById(WC_VEHICLE_VIEW, first->index); + DeleteWindowById(WC_VEHICLE_VIEW, first->index); } InvalidateWindow(WC_VEHICLE_DEPOT, first->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_TRAINS_LIST, 0); } CommandCost cost(EXPENSES_NEW_VEHICLES); @@ -1762,7 +1765,7 @@ if (d <= 0) { leave->vehstatus &= ~VS_HIDDEN; // move it out of the depot - leave->u.rail.track = AxisToTrackBits(DiagDirToAxis(GetRailDepotDirection(leave->tile))); + leave->u.rail.track = TrackToTrackBits(GetRailDepotTrack(leave->tile)); for (int i = 0; i >= d; i--) TrainController(leave, NULL, false); // maybe move it, and maybe let another wagon leave } } else { @@ -1999,7 +2002,7 @@ v->cargo_subtype = new_subtype; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_TRAINS_LIST, 0); } } } @@ -2260,7 +2263,7 @@ v->load_unload_time_rem = 0; - if (UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner)) { + if (UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner) == SIGSEG_FULL) { InvalidateWindowClasses(WC_TRAINS_LIST); return true; } @@ -2448,7 +2451,7 @@ SetDParam(0, v->unitnumber); AddNewsItem( STR_TRAIN_IS_LOST, - NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, + NS_ADVICE, v->index, 0); } @@ -2646,7 +2649,7 @@ SetDParam(0, st->index); AddNewsItem( STR_8801_CITIZENS_CELEBRATE_FIRST, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, v->owner == _local_player ? NT_ARRIVAL_PLAYER : NT_ARRIVAL_OTHER, DNC_NONE, + v->owner == _local_player ? NS_ARRIVAL_PLAYER : NS_ARRIVAL_OTHER, v->index, 0 ); @@ -2804,7 +2807,7 @@ InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); } - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_TRAINS_LIST, 0); BEGIN_ENUM_WAGONS(v) v->vehstatus |= VS_CRASHED; @@ -2893,7 +2896,7 @@ AI_Event(v->owner, new AIEventVehicleCrash(v->index, v->tile)); SetDParam(0, tcc.num); AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, DNC_NONE, + NS_ACCIDENT_VEHICLE, v->index, 0 ); @@ -3152,7 +3155,7 @@ } } - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_TRAINS_LIST, 0); MarkSingleVehicleDirty(v); @@ -3502,6 +3505,9 @@ byte cost_factor = GetVehicleProperty(v, 0x0D, rvi->running_cost); if (cost_factor == 0) continue; + /* Halve running cost for multiheaded parts */ + if (IsMultiheaded(v)) cost_factor /= 2; + cost += cost_factor * GetPriceByIndex(rvi->running_cost_class); } while ((v = GetNextVehicle(v)) != NULL); @@ -3615,7 +3621,7 @@ SetDParam(0, v->unitnumber); AddNewsItem( STR_TRAIN_IS_UNPROFITABLE, - NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, + NS_ADVICE, v->index, 0); } diff -r 6c4314786d68 -r 8cbdb511a674 src/train_gui.cpp --- a/src/train_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/train_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -18,6 +18,7 @@ #include "order_func.h" #include "engine_func.h" #include "engine_base.h" +#include "window_func.h" #include "table/sprites.h" #include "table/strings.h" @@ -43,7 +44,7 @@ found = GetLastVehicleInChain(found); /* put the new wagon at the end of the loco. */ DoCommandP(0, _new_vehicle_id | (found->index << 16), 0, NULL, CMD_MOVE_RAIL_VEHICLE); - RebuildVehicleLists(); + InvalidateWindowClassesData(WC_TRAINS_LIST, 0); } } diff -r 6c4314786d68 -r 8cbdb511a674 src/transparency_gui.cpp --- a/src/transparency_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/transparency_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -17,82 +17,86 @@ TransparencyOptionBits _transparency_lock; TransparencyOptionBits _invisibility_opt; -enum TransparencyToolbarWidgets{ - TTW_WIDGET_SIGNS = 3, ///< Make signs background transparent - TTW_WIDGET_TREES, ///< Make trees transparent - TTW_WIDGET_HOUSES, ///< Make houses transparent - TTW_WIDGET_INDUSTRIES, ///< Make Industries transparent - TTW_WIDGET_BUILDINGS, ///< Make player buildings and structures transparent - TTW_WIDGET_BRIDGES, ///< Make bridges transparent - TTW_WIDGET_STRUCTURES, ///< Make unmovable structures transparent - TTW_WIDGET_CATENARY, ///< Make catenary transparent - TTW_WIDGET_LOADING, ///< Make loading indicators transparent - TTW_WIDGET_END, ///< End of toggle buttons - - /* Panel with buttons for invisibility */ - TTW_BUTTONS = 12, ///< Panel with 'invisibility' buttons -}; - -static void TransparencyToolbWndProc(Window *w, WindowEvent *e) +class TransparenciesWindow : public Window { - switch (e->event) { - case WE_PAINT: - /* must be sure that the widgets show the transparency variable changes - * also when we use shortcuts */ - for (uint i = TTW_WIDGET_SIGNS; i < TTW_WIDGET_END; i++) { - w->SetWidgetLoweredState(i, IsTransparencySet((TransparencyOption)(i - TTW_WIDGET_SIGNS))); - } - - DrawWindowWidgets(w); - for (uint i = TO_SIGNS; i < TO_END; i++) { - if (HasBit(_transparency_lock, i)) DrawSprite(SPR_LOCK, PAL_NONE, w->widget[TTW_WIDGET_SIGNS + i].left + 1, w->widget[TTW_WIDGET_SIGNS + i].top + 1); - } + enum TransparencyToolbarWidgets{ + TTW_WIDGET_SIGNS = 3, ///< Make signs background transparent + TTW_WIDGET_TREES, ///< Make trees transparent + TTW_WIDGET_HOUSES, ///< Make houses transparent + TTW_WIDGET_INDUSTRIES, ///< Make Industries transparent + TTW_WIDGET_BUILDINGS, ///< Make player buildings and structures transparent + TTW_WIDGET_BRIDGES, ///< Make bridges transparent + TTW_WIDGET_STRUCTURES, ///< Make unmovable structures transparent + TTW_WIDGET_CATENARY, ///< Make catenary transparent + TTW_WIDGET_LOADING, ///< Make loading indicators transparent + TTW_WIDGET_END, ///< End of toggle buttons - /* Do not draw button for invisible loading indicators */ - for (uint i = 0; i < 8; i++) { - if (i < TTW_WIDGET_BRIDGES - TTW_WIDGET_SIGNS) { - DrawFrameRect(i * 22, 38, i * 22 + 19, 46, true, HasBit(_invisibility_opt, i) ? FR_LOWERED : FR_NONE); - } else if (i == TTW_WIDGET_BRIDGES - TTW_WIDGET_SIGNS) { - DrawFrameRect(i * 22, 38, i * 22 + 41, 46, true, HasBit(_invisibility_opt, i) ? FR_LOWERED : FR_NONE); - } else { // i > TTW_WIDGET_BRIDGES - TTW_WIDGET_SIGNS - DrawFrameRect((i + 1) * 22, 38, (i + 1) * 22 + 19, 46, true, HasBit(_invisibility_opt, i) ? FR_LOWERED : FR_NONE); - } - } - - break; + /* Panel with buttons for invisibility */ + TTW_BUTTONS = 12, ///< Panel with 'invisibility' buttons + }; - case WE_CLICK: - if (e->we.click.widget >= TTW_WIDGET_SIGNS && e->we.click.widget < TTW_WIDGET_END) { - if (_ctrl_pressed) { - /* toggle the bit of the transparencies lock variable */ - ToggleTransparencyLock((TransparencyOption)(e->we.click.widget - TTW_WIDGET_SIGNS)); - w->SetDirty(); - } else { - /* toggle the bit of the transparencies variable and play a sound */ - ToggleTransparency((TransparencyOption)(e->we.click.widget - TTW_WIDGET_SIGNS)); - SndPlayFx(SND_15_BEEP); - MarkWholeScreenDirty(); - } - } else if (e->we.click.widget == TTW_BUTTONS) { - uint x = e->we.click.pt.x / 22; +public: + TransparenciesWindow(const WindowDesc *desc, int window_number) : Window(desc, window_number) + { + this->FindWindowPlacementAndResize(desc); + } - if (x > TTW_WIDGET_BRIDGES - TTW_WIDGET_SIGNS) x--; - if (x > TTW_WIDGET_CATENARY - TTW_WIDGET_SIGNS) break; - - ToggleInvisibility((TransparencyOption)x); - SndPlayFx(SND_15_BEEP); + virtual void OnPaint() + { + /* must be sure that the widgets show the transparency variable changes + * also when we use shortcuts */ + for (uint i = TTW_WIDGET_SIGNS; i < TTW_WIDGET_END; i++) { + this->SetWidgetLoweredState(i, IsTransparencySet((TransparencyOption)(i - TTW_WIDGET_SIGNS))); + } - /* Redraw whole screen only if transparency is set */ - if (IsTransparencySet((TransparencyOption)x)) { - MarkWholeScreenDirty(); - } else { - w->InvalidateWidget(TTW_BUTTONS); - } + this->DrawWidgets(); + for (uint i = TO_SIGNS; i < TO_END; i++) { + if (HasBit(_transparency_lock, i)) DrawSprite(SPR_LOCK, PAL_NONE, this->widget[TTW_WIDGET_SIGNS + i].left + 1, this->widget[TTW_WIDGET_SIGNS + i].top + 1); + } + + /* Do not draw button for invisible loading indicators */ + for (uint i = 0; i < 8; i++) { + if (i < TTW_WIDGET_BRIDGES - TTW_WIDGET_SIGNS) { + DrawFrameRect(i * 22, 38, i * 22 + 19, 46, true, HasBit(_invisibility_opt, i) ? FR_LOWERED : FR_NONE); + } else if (i == TTW_WIDGET_BRIDGES - TTW_WIDGET_SIGNS) { + DrawFrameRect(i * 22, 38, i * 22 + 41, 46, true, HasBit(_invisibility_opt, i) ? FR_LOWERED : FR_NONE); + } else { // i > TTW_WIDGET_BRIDGES - TTW_WIDGET_SIGNS + DrawFrameRect((i + 1) * 22, 38, (i + 1) * 22 + 19, 46, true, HasBit(_invisibility_opt, i) ? FR_LOWERED : FR_NONE); } + } + } - break; + virtual void OnClick(Point pt, int widget) + { + if (widget >= TTW_WIDGET_SIGNS && widget < TTW_WIDGET_END) { + if (_ctrl_pressed) { + /* toggle the bit of the transparencies lock variable */ + ToggleTransparencyLock((TransparencyOption)(widget - TTW_WIDGET_SIGNS)); + this->SetDirty(); + } else { + /* toggle the bit of the transparencies variable and play a sound */ + ToggleTransparency((TransparencyOption)(widget - TTW_WIDGET_SIGNS)); + SndPlayFx(SND_15_BEEP); + MarkWholeScreenDirty(); + } + } else if (widget == TTW_BUTTONS) { + uint x = pt.x / 22; + + if (x > TTW_WIDGET_BRIDGES - TTW_WIDGET_SIGNS) x--; + if (x > TTW_WIDGET_CATENARY - TTW_WIDGET_SIGNS) return; + + ToggleInvisibility((TransparencyOption)x); + SndPlayFx(SND_15_BEEP); + + /* Redraw whole screen only if transparency is set */ + if (IsTransparencySet((TransparencyOption)x)) { + MarkWholeScreenDirty(); + } else { + this->InvalidateWidget(TTW_BUTTONS); + } + } } -} +}; static const Widget _transparency_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -121,10 +125,9 @@ WC_TRANSPARENCY_TOOLBAR, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, _transparency_widgets, - TransparencyToolbWndProc }; void ShowTransparencyToolbar(void) { - AllocateWindowDescFront(&_transparency_desc, 0); + AllocateWindowDescFront(&_transparency_desc, 0); } diff -r 6c4314786d68 -r 8cbdb511a674 src/tree_gui.cpp --- a/src/tree_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/tree_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -16,178 +16,169 @@ #include "table/strings.h" #include "table/tree_land.h" -struct tree_d { - uint16 base; - uint16 count; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(tree_d)); - -static int _tree_to_plant; void PlaceTreesRandomly(); -static const PalSpriteID _tree_sprites[] = { - { 0x655, PAL_NONE }, { 0x663, PAL_NONE }, { 0x678, PAL_NONE }, { 0x62B, PAL_NONE }, - { 0x647, PAL_NONE }, { 0x639, PAL_NONE }, { 0x64E, PAL_NONE }, { 0x632, PAL_NONE }, - { 0x67F, PAL_NONE }, { 0x68D, PAL_NONE }, { 0x69B, PAL_NONE }, { 0x6A9, PAL_NONE }, - { 0x6AF, PAL_NONE }, { 0x6D2, PAL_NONE }, { 0x6D9, PAL_NONE }, { 0x6C4, PAL_NONE }, - { 0x6CB, PAL_NONE }, { 0x6B6, PAL_NONE }, { 0x6BD, PAL_NONE }, { 0x6E0, PAL_NONE }, - { 0x72E, PAL_NONE }, { 0x734, PAL_NONE }, { 0x74A, PAL_NONE }, { 0x74F, PAL_NONE }, - { 0x76B, PAL_NONE }, { 0x78F, PAL_NONE }, { 0x788, PAL_NONE }, { 0x77B, PAL_NONE }, - { 0x75F, PAL_NONE }, { 0x774, PAL_NONE }, { 0x720, PAL_NONE }, { 0x797, PAL_NONE }, - { 0x79E, PAL_NONE }, { 0x7A5, PALETTE_TO_GREEN }, { 0x7AC, PALETTE_TO_RED }, { 0x7B3, PAL_NONE }, - { 0x7BA, PAL_NONE }, { 0x7C1, PALETTE_TO_RED, }, { 0x7C8, PALETTE_TO_PALE_GREEN }, { 0x7CF, PALETTE_TO_YELLOW }, { 0x7D6, PALETTE_TO_RED } +class BuildTreesWindow : public Window +{ + uint16 base; + uint16 count; + int tree_to_plant; + + enum BuildTreesWidgets { + BTW_CLOSE, + BTW_CAPTION, + BTW_BACKGROUND, + BTW_TYPE_11, + BTW_TYPE_12, + BTW_TYPE_13, + BTW_TYPE_14, + BTW_TYPE_21, + BTW_TYPE_22, + BTW_TYPE_23, + BTW_TYPE_24, + BTW_TYPE_31, + BTW_TYPE_32, + BTW_TYPE_33, + BTW_TYPE_34, + BTW_TYPE_RANDOM, + BTW_MANY_RANDOM, + }; + +public: + BuildTreesWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + if (_game_mode != GM_EDITOR) { + this->HideWidget(BTW_MANY_RANDOM); + int offset = this->widget[BTW_MANY_RANDOM].bottom - this->widget[BTW_MANY_RANDOM].top; + this->height -= offset; + this->widget[BTW_BACKGROUND].bottom -= offset; + } + ResetObjectToPlace(); + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { + static const PalSpriteID tree_sprites[] = { + { 0x655, PAL_NONE }, { 0x663, PAL_NONE }, { 0x678, PAL_NONE }, { 0x62B, PAL_NONE }, + { 0x647, PAL_NONE }, { 0x639, PAL_NONE }, { 0x64E, PAL_NONE }, { 0x632, PAL_NONE }, + { 0x67F, PAL_NONE }, { 0x68D, PAL_NONE }, { 0x69B, PAL_NONE }, { 0x6A9, PAL_NONE }, + { 0x6AF, PAL_NONE }, { 0x6D2, PAL_NONE }, { 0x6D9, PAL_NONE }, { 0x6C4, PAL_NONE }, + { 0x6CB, PAL_NONE }, { 0x6B6, PAL_NONE }, { 0x6BD, PAL_NONE }, { 0x6E0, PAL_NONE }, + { 0x72E, PAL_NONE }, { 0x734, PAL_NONE }, { 0x74A, PAL_NONE }, { 0x74F, PAL_NONE }, + { 0x76B, PAL_NONE }, { 0x78F, PAL_NONE }, { 0x788, PAL_NONE }, { 0x77B, PAL_NONE }, + { 0x75F, PAL_NONE }, { 0x774, PAL_NONE }, { 0x720, PAL_NONE }, { 0x797, PAL_NONE }, + { 0x79E, PAL_NONE }, { 0x7A5, PALETTE_TO_GREEN }, { 0x7AC, PALETTE_TO_RED }, { 0x7B3, PAL_NONE }, + { 0x7BA, PAL_NONE }, { 0x7C1, PALETTE_TO_RED, }, { 0x7C8, PALETTE_TO_PALE_GREEN }, { 0x7CF, PALETTE_TO_YELLOW }, { 0x7D6, PALETTE_TO_RED } + }; + + this->DrawWidgets(); + + int i = this->base = _tree_base_by_landscape[_opt.landscape]; + int count = this->count = _tree_count_by_landscape[_opt.landscape]; + + int x = 18; + int y = 54; + do { + DrawSprite(tree_sprites[i].sprite, tree_sprites[i].pal, x, y); + x += 35; + if (!(++i & 3)) { + x -= 35 * 4; + y += 47; + } + } while (--count); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case BTW_TYPE_11: case BTW_TYPE_12: case BTW_TYPE_13: case BTW_TYPE_14: + case BTW_TYPE_21: case BTW_TYPE_22: case BTW_TYPE_23: case BTW_TYPE_24: + case BTW_TYPE_31: case BTW_TYPE_32: case BTW_TYPE_33: case BTW_TYPE_34: + if (widget - BTW_TYPE_11 >= this->count) break; + + if (HandlePlacePushButton(this, widget, SPR_CURSOR_TREE, VHM_RECT, NULL)) { + this->tree_to_plant = this->base + widget - BTW_TYPE_11; + } + break; + + case BTW_TYPE_RANDOM: // tree of random type. + if (HandlePlacePushButton(this, BTW_TYPE_RANDOM, SPR_CURSOR_TREE, VHM_RECT, NULL)) { + this->tree_to_plant = -1; + } + break; + + case BTW_MANY_RANDOM: // place trees randomly over the landscape + this->LowerWidget(BTW_MANY_RANDOM); + this->flags4 |= 5 << WF_TIMEOUT_SHL; + SndPlayFx(SND_15_BEEP); + PlaceTreesRandomly(); + MarkWholeScreenDirty(); + break; + } + } + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_PLANT_TREES); + VpSetPlaceSizingLimit(20); + } + + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) + { + VpSelectTilesWithMethod(pt.x, pt.y, select_method); + } + + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) + { + if (pt.x != -1 && select_proc == DDSP_PLANT_TREES) { + DoCommandP(end_tile, this->tree_to_plant, start_tile, NULL, + CMD_PLANT_TREE | CMD_MSG(STR_2805_CAN_T_PLANT_TREE_HERE)); + } + } + + virtual void OnTimeout() + { + this->RaiseWidget(BTW_MANY_RANDOM); + this->InvalidateWidget(BTW_MANY_RANDOM); + } + + virtual void OnPlaceObjectAbort() + { + this->RaiseButtons(); + } }; -static void BuildTreesWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - ResetObjectToPlace(); - break; - - case WE_PAINT: { - DrawWindowWidgets(w); - - int i = WP(w, tree_d).base = _tree_base_by_landscape[_opt.landscape]; - int count = WP(w, tree_d).count = _tree_count_by_landscape[_opt.landscape]; - - int x = 18; - int y = 54; - do { - DrawSprite(_tree_sprites[i].sprite, _tree_sprites[i].pal, x, y); - x += 35; - if (!(++i & 3)) { - x -= 35 * 4; - y += 47; - } - } while (--count); - } break; - - case WE_CLICK: { - int wid = e->we.click.widget; - - switch (wid) { - case 0: - ResetObjectToPlace(); - break; - - case 3: case 4: case 5: case 6: - case 7: case 8: case 9: case 10: - case 11:case 12: case 13: case 14: - if (wid - 3 >= WP(w, tree_d).count) break; - - if (HandlePlacePushButton(w, wid, SPR_CURSOR_TREE, VHM_RECT, NULL)) { - _tree_to_plant = WP(w, tree_d).base + wid - 3; - } - break; - - case 15: // tree of random type. - if (HandlePlacePushButton(w, 15, SPR_CURSOR_TREE, VHM_RECT, NULL)) { - _tree_to_plant = -1; - } - break; - - case 16: // place trees randomly over the landscape - w->LowerWidget(16); - w->flags4 |= 5 << WF_TIMEOUT_SHL; - SndPlayFx(SND_15_BEEP); - PlaceTreesRandomly(); - MarkWholeScreenDirty(); - break; - } - } break; - - case WE_PLACE_OBJ: - VpStartPlaceSizing(e->we.place.tile, VPM_X_AND_Y_LIMITED, DDSP_PLANT_TREES); - VpSetPlaceSizingLimit(20); - break; - - case WE_PLACE_DRAG: - VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method); - return; - - case WE_PLACE_MOUSEUP: - if (e->we.place.pt.x != -1 && e->we.place.select_proc == DDSP_PLANT_TREES) { - DoCommandP(e->we.place.tile, _tree_to_plant, e->we.place.starttile, NULL, - CMD_PLANT_TREE | CMD_MSG(STR_2805_CAN_T_PLANT_TREE_HERE)); - } - break; - - case WE_TIMEOUT: - w->RaiseWidget(16); - break; - - case WE_ABORT_PLACE_OBJ: - w->RaiseButtons(); - break; - } -} - static const Widget _build_trees_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 142, 0, 13, STR_2802_TREES, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_PANEL, RESIZE_NONE, 7, 0, 142, 14, 170, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 14, 2, 35, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 37, 70, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 72, 105, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 107, 140, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 2, 35, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 37, 70, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 72, 105, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 107, 140, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 2, 35, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 37, 70, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 72, 105, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 107, 140, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 157, 168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP}, +{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // BTW_CLOSE +{ WWT_CAPTION, RESIZE_NONE, 7, 11, 142, 0, 13, STR_2802_TREES, STR_018C_WINDOW_TITLE_DRAG_THIS}, // BTW_CAPTION +{ WWT_PANEL, RESIZE_NONE, 7, 0, 142, 14, 183, 0x0, STR_NULL}, // BTW_BACKGROUND +{ WWT_PANEL, RESIZE_NONE, 14, 2, 35, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_11 +{ WWT_PANEL, RESIZE_NONE, 14, 37, 70, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_12 +{ WWT_PANEL, RESIZE_NONE, 14, 72, 105, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_13 +{ WWT_PANEL, RESIZE_NONE, 14, 107, 140, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_14 +{ WWT_PANEL, RESIZE_NONE, 14, 2, 35, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_21 +{ WWT_PANEL, RESIZE_NONE, 14, 37, 70, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_22 +{ WWT_PANEL, RESIZE_NONE, 14, 72, 105, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_23 +{ WWT_PANEL, RESIZE_NONE, 14, 107, 140, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_24 +{ WWT_PANEL, RESIZE_NONE, 14, 2, 35, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_31 +{ WWT_PANEL, RESIZE_NONE, 14, 37, 70, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_32 +{ WWT_PANEL, RESIZE_NONE, 14, 72, 105, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_33 +{ WWT_PANEL, RESIZE_NONE, 14, 107, 140, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, // BTW_TYPE_34 +{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 157, 168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP}, // BTW_TYPE_RANDOM +{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 170, 181, STR_028A_RANDOM_TREES, STR_028B_PLANT_TREES_RANDOMLY_OVER}, // BTW_MANY_RANDOM { WIDGETS_END}, }; static const WindowDesc _build_trees_desc = { - 497, 22, 143, 171, 143, 171, - WC_BUILD_TREES, WC_SCEN_LAND_GEN, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, - _build_trees_widgets, - BuildTreesWndProc -}; - -static const Widget _build_trees_scen_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 142, 0, 13, STR_2802_TREES, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_PANEL, RESIZE_NONE, 7, 0, 142, 14, 183, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 14, 2, 35, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 37, 70, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 72, 105, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 107, 140, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 2, 35, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 37, 70, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 72, 105, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 107, 140, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 2, 35, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 37, 70, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 72, 105, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_PANEL, RESIZE_NONE, 14, 107, 140, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 157, 168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 170, 181, STR_028A_RANDOM_TREES, STR_028B_PLANT_TREES_RANDOMLY_OVER}, -{ WIDGETS_END}, -}; - -static const WindowDesc _build_trees_scen_desc = { WDP_AUTO, WDP_AUTO, 143, 184, 143, 184, WC_BUILD_TREES, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, - _build_trees_scen_widgets, - BuildTreesWndProc + _build_trees_widgets, }; - void ShowBuildTreesToolbar() { - if (!IsValidPlayer(_current_player)) return; - AllocateWindowDescFront(&_build_trees_desc, 0); + if (_game_mode != GM_EDITOR && !IsValidPlayer(_current_player)) return; + AllocateWindowDescFront(&_build_trees_desc, 0); } - -void ShowBuildTreesScenToolbar() -{ - AllocateWindowDescFront(&_build_trees_scen_desc, 0); -} diff -r 6c4314786d68 -r 8cbdb511a674 src/tunnelbridge_cmd.cpp --- a/src/tunnelbridge_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/tunnelbridge_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -494,7 +494,7 @@ delta = TileOffsByDiagDir(direction); DiagDirection tunnel_in_way_dir; - if (OtherAxis(DiagDirToAxis(direction)) == AXIS_X) { + if (DiagDirToAxis(direction) == AXIS_Y) { tunnel_in_way_dir = (TileX(start_tile) < (MapMaxX() / 2)) ? DIAGDIR_SW : DIAGDIR_NE; } else { tunnel_in_way_dir = (TileY(start_tile) < (MapMaxX() / 2)) ? DIAGDIR_SE : DIAGDIR_NW; @@ -558,7 +558,7 @@ MakeRailTunnel(start_tile, _current_player, direction, (RailType)GB(p1, 0, 4)); MakeRailTunnel(end_tile, _current_player, ReverseDiagDir(direction), (RailType)GB(p1, 0, 4)); AddSideToSignalBuffer(start_tile, INVALID_DIAGDIR, _current_player); - YapfNotifyTrackLayoutChange(start_tile, AxisToTrack(DiagDirToAxis(direction))); + YapfNotifyTrackLayoutChange(start_tile, DiagDirToDiagTrack(direction)); } else { MakeRoadTunnel(start_tile, _current_player, direction, (RoadTypes)GB(p1, 0, 3)); MakeRoadTunnel(end_tile, _current_player, ReverseDiagDir(direction), (RoadTypes)GB(p1, 0, 3)); @@ -623,7 +623,7 @@ AddSideToSignalBuffer(tile, ReverseDiagDir(dir), owner); AddSideToSignalBuffer(endtile, dir, owner); - Track track = AxisToTrack(DiagDirToAxis(dir)); + Track track = DiagDirToDiagTrack(dir); YapfNotifyTrackLayoutChange(tile, track); YapfNotifyTrackLayoutChange(endtile, track); } else { @@ -691,7 +691,7 @@ AddSideToSignalBuffer(tile, ReverseDiagDir(direction), owner); AddSideToSignalBuffer(endtile, direction, owner); - Track track = AxisToTrack(DiagDirToAxis(direction)); + Track track = DiagDirToDiagTrack(direction); YapfNotifyTrackLayoutChange(tile, track); YapfNotifyTrackLayoutChange(endtile, track); } @@ -1233,7 +1233,7 @@ DiagDirection dir = GetTunnelBridgeDirection(tile); if (side != INVALID_DIAGDIR && side != ReverseDiagDir(dir)) return 0; - return CombineTrackStatus(TrackBitsToTrackdirBits(AxisToTrackBits(DiagDirToAxis(dir))), TRACKDIR_BIT_NONE); + return CombineTrackStatus(TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir)), TRACKDIR_BIT_NONE); } static void ChangeTileOwner_TunnelBridge(TileIndex tile, PlayerID old_player, PlayerID new_player) diff -r 6c4314786d68 -r 8cbdb511a674 src/vehicle.cpp --- a/src/vehicle.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/vehicle.cpp Mon May 19 15:13:58 2008 +0000 @@ -591,11 +591,8 @@ delete this->Next(); } - Window *w = FindWindowById(WC_MAIN_WINDOW, 0); - if (w != NULL && w->viewport->follow_vehicle == this->index) { - ScrollMainWindowTo(this->x_pos, this->y_pos, true); // lock the main view on the vehicle's last position - w->viewport->follow_vehicle = INVALID_VEHICLE; - } + extern void StopGlobalFollowVehicle(const Vehicle *v); + StopGlobalFollowVehicle(this); } Vehicle::~Vehicle() @@ -953,7 +950,7 @@ SetDParam(0, _vehicle_type_names[v->type]); SetDParam(1, v->unitnumber); - AddNewsItem(msg, NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0); + AddNewsItem(msg, NS_ADVICE, v->index, 0); } void AgeVehicle(Vehicle *v) @@ -1553,7 +1550,7 @@ * @param color The string to show depending on if we are unloading or loading * @return A percentage of how full the Vehicle is. */ -uint8 CalcPercentVehicleFilled(Vehicle *v, StringID *color) +uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *color) { int count = 0; int max = 0; @@ -1652,7 +1649,7 @@ /* Notify the user that we stopped the vehicle */ SetDParam(0, _vehicle_type_names[v->type]); SetDParam(1, v->unitnumber); - AddNewsItem(STR_ORDER_REFIT_FAILED, NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0); + AddNewsItem(STR_ORDER_REFIT_FAILED, NS_ADVICE, v->index, 0); } } else if (v->owner == _local_player && cost.GetCost() != 0) { ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost()); @@ -1678,7 +1675,7 @@ } SetDParam(0, v->unitnumber); - AddNewsItem(string, NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0); + AddNewsItem(string, NS_ADVICE, v->index, 0); } AI_Event(v->owner, new AIEventVehicleWaitingInDepot(v->index)); } @@ -1738,7 +1735,7 @@ if (flags & DC_EXEC) { free(v->name); v->name = strdup(_cmd_text); - ResortVehicleLists(); + InvalidateWindowClassesData(WC_TRAINS_LIST, 1); MarkWholeScreenDirty(); } @@ -1869,34 +1866,34 @@ switch (v->type) { case VEH_TRAIN: if (v->u.rail.track == TRACK_BIT_DEPOT) // We'll assume the train is facing outwards - return DiagdirToDiagTrackdir(GetRailDepotDirection(v->tile)); // Train in depot + return DiagDirToDiagTrackdir(GetRailDepotDirection(v->tile)); // Train in depot if (v->u.rail.track == TRACK_BIT_WORMHOLE) // train in tunnel, so just use his direction and assume a diagonal track - return DiagdirToDiagTrackdir(DirToDiagDir(v->direction)); + return DiagDirToDiagTrackdir(DirToDiagDir(v->direction)); return TrackDirectionToTrackdir(FindFirstTrack(v->u.rail.track), v->direction); case VEH_SHIP: if (v->IsInDepot()) // We'll assume the ship is facing outwards - return DiagdirToDiagTrackdir(GetShipDepotDirection(v->tile)); + return DiagDirToDiagTrackdir(GetShipDepotDirection(v->tile)); return TrackDirectionToTrackdir(FindFirstTrack(v->u.ship.state), v->direction); case VEH_ROAD: if (v->IsInDepot()) // We'll assume the road vehicle is facing outwards - return DiagdirToDiagTrackdir(GetRoadDepotDirection(v->tile)); + return DiagDirToDiagTrackdir(GetRoadDepotDirection(v->tile)); if (IsStandardRoadStopTile(v->tile)) // We'll assume the road vehicle is facing outwards - return DiagdirToDiagTrackdir(GetRoadStopDir(v->tile)); // Road vehicle in a station - - if (IsDriveThroughStopTile(v->tile)) return DiagdirToDiagTrackdir(DirToDiagDir(v->direction)); + return DiagDirToDiagTrackdir(GetRoadStopDir(v->tile)); // Road vehicle in a station + + if (IsDriveThroughStopTile(v->tile)) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction)); /* If vehicle's state is a valid track direction (vehicle is not turning around) return it */ if (!IsReversingRoadTrackdir((Trackdir)v->u.road.state)) return (Trackdir)v->u.road.state; /* Vehicle is turning around, get the direction from vehicle's direction */ - return DiagdirToDiagTrackdir(DirToDiagDir(v->direction)); + return DiagDirToDiagTrackdir(DirToDiagDir(v->direction)); /* case VEH_AIRCRAFT: case VEH_EFFECT: case VEH_DISASTER: */ default: return INVALID_TRACKDIR; @@ -2759,7 +2756,11 @@ memcpy(dest, backup, sizeof(Vehicle)); /* We decreased the engine count when we sold the engines so we will increase it again. */ - if (IsEngineCountable(backup)) p->num_engines[backup->engine_type]++; + if (IsEngineCountable(backup)) { + p->num_engines[backup->engine_type]++; + if (IsValidGroupID(backup->group_id)) GetGroup(backup->group_id)->num_engines[backup->engine_type]++; + if (backup->IsPrimaryVehicle()) IncreaseGroupNumVehicle(backup->group_id); + } /* Update hash. */ Vehicle *dummy = dest; @@ -2806,11 +2807,11 @@ { if (!ContainsBackup()) return v; if (v != NULL) { - ChangeVehicleViewWindow(v, INVALID_VEHICLE); + ChangeVehicleViewWindow(v->index, INVALID_VEHICLE); DoCommand(0, v->index, 1, DC_EXEC, GetCmdSellVeh(v)); } v = RestoreBackupVehicle(this->vehicles, p); - ChangeVehicleViewWindow(INVALID_VEHICLE, v); + ChangeVehicleViewWindow(INVALID_VEHICLE, v->index); if (orders != NULL) RestoreVehicleOrdersBruteForce(v, orders); if (economy != NULL) economy->Restore(); /* If we stored cargo as well then we should restore it. */ diff -r 6c4314786d68 -r 8cbdb511a674 src/vehicle_func.h --- a/src/vehicle_func.h Mon May 19 14:14:33 2008 +0000 +++ b/src/vehicle_func.h Mon May 19 15:13:58 2008 +0000 @@ -32,7 +32,7 @@ void *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc); void CallVehicleTicks(); Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z); -uint8 CalcPercentVehicleFilled(Vehicle *v, StringID *color); +uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *color); void InitializeTrains(); byte VehicleRandomBits(); diff -r 6c4314786d68 -r 8cbdb511a674 src/vehicle_gui.cpp --- a/src/vehicle_gui.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/vehicle_gui.cpp Mon May 19 15:13:58 2008 +0000 @@ -40,20 +40,6 @@ #include "table/sprites.h" #include "table/strings.h" -struct vehicledetails_d { - byte tab; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehicledetails_d)); - -struct refit_d { - int sel; - struct RefitOption *cargo; - struct RefitList *list; - uint length; - VehicleOrderID order; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(refit_d)); - Sorting _sorting; static bool _internal_sort_order; // descending/ascending @@ -100,47 +86,6 @@ INVALID_STRING_ID }; -/** - * Set sort list flag for all vehicle list windows - * @param sl_flag Sort list flag to set - */ -static void SetVehicleListsFlag(SortListFlags sl_flag) -{ - Window* const *wz; - - FOR_ALL_WINDOWS(wz) { - Window *w = *wz; - - switch (w->window_class) { - case WC_TRAINS_LIST: - case WC_ROADVEH_LIST: - case WC_SHIPS_LIST: - case WC_AIRCRAFT_LIST: - dynamic_cast(w)->vehicles.flags |= sl_flag; - w->SetDirty(); - break; - - default: break; - } - } -} - -/** - * Rebuild all vehicle list windows - */ -void RebuildVehicleLists() -{ - SetVehicleListsFlag(VL_REBUILD); -} - -/** - * Resort all vehicle list windows - */ -void ResortVehicleLists() -{ - SetVehicleListsFlag(VL_RESORT); -} - void BuildVehicleList(VehicleListBase *vl, PlayerID owner, uint16 index, uint16 window_type) { if (!(vl->vehicles.flags & VL_REBUILD)) return; @@ -333,89 +278,141 @@ return selected; } -static void VehicleRefitWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - Vehicle *v = GetVehicle(w->window_number); +struct RefitWindow : public Window { + int sel; + RefitOption *cargo; + RefitList *list; + uint length; + VehicleOrderID order; - if (v->type == VEH_TRAIN) { - uint length = CountVehiclesInChain(v); + RefitWindow(const WindowDesc *desc, const Vehicle *v, VehicleOrderID order) : Window(desc, v->index) + { + this->caption_color = v->owner; + this->vscroll.cap = 8; + this->resize.step_height = 14; - if (length != WP(w, refit_d).length) { - /* Consist length has changed, so rebuild the refit list */ - free(WP(w, refit_d).list->items); - free(WP(w, refit_d).list); - WP(w, refit_d).list = BuildRefitList(v); - WP(w, refit_d).length = length; + this->order = order; + this->sel = -1; + this->list = BuildRefitList(v); + if (v->type == VEH_TRAIN) this->length = CountVehiclesInChain(v); + SetVScrollCount(this, this->list->num_lines); + + switch (v->type) { + case VEH_TRAIN: + this->widget[3].tooltips = STR_RAIL_SELECT_TYPE_OF_CARGO_FOR; + this->widget[6].data = STR_RAIL_REFIT_VEHICLE; + this->widget[6].tooltips = STR_RAIL_REFIT_TO_CARRY_HIGHLIGHTED; + break; + + case VEH_ROAD: + this->widget[3].tooltips = STR_ROAD_SELECT_TYPE_OF_CARGO_FOR; + this->widget[6].data = STR_REFIT_ROAD_VEHICLE; + this->widget[6].tooltips = STR_REFIT_ROAD_VEHICLE_TO_CARRY_HIGHLIGHTED; + break; + + case VEH_SHIP: + this->widget[3].tooltips = STR_983D_SELECT_TYPE_OF_CARGO_FOR; + this->widget[6].data = STR_983C_REFIT_SHIP; + this->widget[6].tooltips = STR_983E_REFIT_SHIP_TO_CARRY_HIGHLIGHTED; + break; + + case VEH_AIRCRAFT: + this->widget[3].tooltips = STR_A03E_SELECT_TYPE_OF_CARGO_FOR; + this->widget[6].data = STR_A03D_REFIT_AIRCRAFT; + this->widget[6].tooltips = STR_A03F_REFIT_AIRCRAFT_TO_CARRY; + break; + + default: NOT_REACHED(); + } + + this->FindWindowPlacementAndResize(desc); + } + + ~RefitWindow() + { + free(this->list->items); + free(this->list); + } + + virtual void OnPaint() + { + Vehicle *v = GetVehicle(this->window_number); + + if (v->type == VEH_TRAIN) { + uint length = CountVehiclesInChain(v); + + if (length != this->length) { + /* Consist length has changed, so rebuild the refit list */ + free(this->list->items); + free(this->list); + this->list = BuildRefitList(v); + this->length = length; + } + } + + SetVScrollCount(this, this->list->num_lines); + + SetDParam(0, v->index); + this->DrawWidgets(); + + this->cargo = DrawVehicleRefitWindow(this->list, this->sel, this->vscroll.pos, this->vscroll.cap, this->resize.step_height); + + if (this->cargo != NULL) { + CommandCost cost; + + cost = DoCommand(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8, + DC_QUERY_COST, GetCmdRefitVeh(v->type)); + + if (CmdSucceeded(cost)) { + SetDParam(0, this->cargo->cargo); + SetDParam(1, _returned_refit_capacity); + SetDParam(2, cost.GetCost()); + DrawString(2, this->widget[5].top + 1, STR_9840_NEW_CAPACITY_COST_OF_REFIT, TC_FROMSTRING); + } + } + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 3: { // listbox + int y = pt.y - this->widget[3].top; + if (y >= 0) { + this->sel = (y / (int)this->resize.step_height) + this->vscroll.pos; + this->SetDirty(); } + break; } - SetVScrollCount(w, WP(w, refit_d).list->num_lines); - - SetDParam(0, v->index); - DrawWindowWidgets(w); - - WP(w, refit_d).cargo = DrawVehicleRefitWindow(WP(w, refit_d).list, WP(w, refit_d).sel, w->vscroll.pos, w->vscroll.cap, w->resize.step_height); - - if (WP(w, refit_d).cargo != NULL) { - CommandCost cost; - - cost = DoCommand(v->tile, v->index, WP(w, refit_d).cargo->cargo | WP(w, refit_d).cargo->subtype << 8, - DC_QUERY_COST, GetCmdRefitVeh(GetVehicle(w->window_number)->type)); + case 6: // refit button + if (this->cargo != NULL) { + const Vehicle *v = GetVehicle(this->window_number); - if (CmdSucceeded(cost)) { - SetDParam(0, WP(w, refit_d).cargo->cargo); - SetDParam(1, _returned_refit_capacity); - SetDParam(2, cost.GetCost()); - DrawString(2, w->widget[5].top + 1, STR_9840_NEW_CAPACITY_COST_OF_REFIT, TC_FROMSTRING); - } - } - } break; + if (this->order == INVALID_VEH_ORDER_ID) { + int command = 0; - case WE_CLICK: - switch (e->we.click.widget) { - case 3: { // listbox - int y = e->we.click.pt.y - w->widget[3].top; - if (y >= 0) { - WP(w, refit_d).sel = (y / (int)w->resize.step_height) + w->vscroll.pos; - w->SetDirty(); + switch (v->type) { + default: NOT_REACHED(); + case VEH_TRAIN: command = CMD_REFIT_RAIL_VEHICLE | CMD_MSG(STR_RAIL_CAN_T_REFIT_VEHICLE); break; + case VEH_ROAD: command = CMD_REFIT_ROAD_VEH | CMD_MSG(STR_REFIT_ROAD_VEHICLE_CAN_T); break; + case VEH_SHIP: command = CMD_REFIT_SHIP | CMD_MSG(STR_9841_CAN_T_REFIT_SHIP); break; + case VEH_AIRCRAFT: command = CMD_REFIT_AIRCRAFT | CMD_MSG(STR_A042_CAN_T_REFIT_AIRCRAFT); break; + } + if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8, NULL, command)) delete this; + } else { + if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8 | this->order << 16, NULL, CMD_ORDER_REFIT)) delete this; } - } break; - case 6: // refit button - if (WP(w, refit_d).cargo != NULL) { - const Vehicle *v = GetVehicle(w->window_number); - - if (WP(w, refit_d).order == INVALID_VEH_ORDER_ID) { - int command = 0; + } + break; + } + } - switch (v->type) { - default: NOT_REACHED(); - case VEH_TRAIN: command = CMD_REFIT_RAIL_VEHICLE | CMD_MSG(STR_RAIL_CAN_T_REFIT_VEHICLE); break; - case VEH_ROAD: command = CMD_REFIT_ROAD_VEH | CMD_MSG(STR_REFIT_ROAD_VEHICLE_CAN_T); break; - case VEH_SHIP: command = CMD_REFIT_SHIP | CMD_MSG(STR_9841_CAN_T_REFIT_SHIP); break; - case VEH_AIRCRAFT: command = CMD_REFIT_AIRCRAFT | CMD_MSG(STR_A042_CAN_T_REFIT_AIRCRAFT); break; - } - if (DoCommandP(v->tile, v->index, WP(w, refit_d).cargo->cargo | WP(w, refit_d).cargo->subtype << 8, NULL, command)) delete w; - } else { - if (DoCommandP(v->tile, v->index, WP(w, refit_d).cargo->cargo | WP(w, refit_d).cargo->subtype << 8 | WP(w, refit_d).order << 16, NULL, CMD_ORDER_REFIT)) delete w; - } - } - break; - } - break; - - case WE_RESIZE: - w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height; - w->widget[3].data = (w->vscroll.cap << 8) + 1; - break; - - case WE_DESTROY: - free(WP(w, refit_d).list->items); - free(WP(w, refit_d).list); - break; + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / (int)this->resize.step_height; + this->widget[3].data = (this->vscroll.cap << 8) + 1; } -} +}; static const Widget _vehicle_refit_widgets[] = { @@ -435,7 +432,6 @@ WC_VEHICLE_REFIT, WC_VEHICLE_VIEW, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _vehicle_refit_widgets, - VehicleRefitWndProc, }; /** Show the refit window for a vehicle @@ -444,46 +440,8 @@ */ void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order) { - Window *w; - DeleteWindowById(WC_VEHICLE_REFIT, v->index); - - w = AllocateWindowDescFront(&_vehicle_refit_desc, v->index); - WP(w, refit_d).order = order; - - if (w != NULL) { - w->caption_color = v->owner; - w->vscroll.cap = 8; - w->resize.step_height = 14; - WP(w, refit_d).sel = -1; - WP(w, refit_d).list = BuildRefitList(v); - if (v->type == VEH_TRAIN) WP(w, refit_d).length = CountVehiclesInChain(v); - SetVScrollCount(w, WP(w, refit_d).list->num_lines); - - switch (v->type) { - case VEH_TRAIN: - w->widget[3].tooltips = STR_RAIL_SELECT_TYPE_OF_CARGO_FOR; - w->widget[6].data = STR_RAIL_REFIT_VEHICLE; - w->widget[6].tooltips = STR_RAIL_REFIT_TO_CARRY_HIGHLIGHTED; - break; - case VEH_ROAD: - w->widget[3].tooltips = STR_ROAD_SELECT_TYPE_OF_CARGO_FOR; - w->widget[6].data = STR_REFIT_ROAD_VEHICLE; - w->widget[6].tooltips = STR_REFIT_ROAD_VEHICLE_TO_CARRY_HIGHLIGHTED; - break; - case VEH_SHIP: - w->widget[3].tooltips = STR_983D_SELECT_TYPE_OF_CARGO_FOR; - w->widget[6].data = STR_983C_REFIT_SHIP; - w->widget[6].tooltips = STR_983E_REFIT_SHIP_TO_CARRY_HIGHLIGHTED; - break; - case VEH_AIRCRAFT: - w->widget[3].tooltips = STR_A03E_SELECT_TYPE_OF_CARGO_FOR; - w->widget[6].data = STR_A03D_REFIT_AIRCRAFT; - w->widget[6].tooltips = STR_A03F_REFIT_AIRCRAFT_TO_CARRY; - break; - default: NOT_REACHED(); - } - } + new RefitWindow(&_vehicle_refit_desc, v, order); } /** Display additional text from NewGRF in the purchase information window */ @@ -841,7 +799,7 @@ */ struct VehicleListWindow : public Window, public VehicleListBase { - VehicleListWindow(const WindowDesc *desc, void *data, WindowNumber window_number) : Window(desc, data, window_number) + VehicleListWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) { uint16 window_type = this->window_number & VLW_MASK; PlayerID player = (PlayerID)GB(this->window_number, 0, 8); @@ -1028,12 +986,12 @@ VLW_WIDGET_START_ALL, WIDGET_LIST_END); - DrawWindowWidgets(this); + this->DrawWidgets(); /* draw sorting criteria string */ DrawString(85, 15, _vehicle_sort_listing[this->vehicles.sort_type], TC_BLACK); /* draw arrow pointing up/down for ascending/descending sorting */ - DrawSortButtonState(this, VLW_WIDGET_SORT_ORDER, this->vehicles.flags & VL_DESC ? SBS_DOWN : SBS_UP); + this->DrawSortButtonState(VLW_WIDGET_SORT_ORDER, this->vehicles.flags & VL_DESC ? SBS_DOWN : SBS_UP); max = min(this->vscroll.pos + this->vscroll.cap, this->vehicles.list_length); for (i = this->vscroll.pos; i < max; ++i) { @@ -1187,6 +1145,11 @@ this->vscroll.cap += delta.y / (int)this->resize.step_height; this->widget[VLW_WIDGET_LIST].data = (this->vscroll.cap << 8) + 1; } + + virtual void OnInvalidateData(int data) + { + this->vehicles.flags |= (data == 0 ? VL_REBUILD : VL_RESORT); + } }; static const WindowDesc _player_vehicle_list_train_desc = { @@ -1194,7 +1157,6 @@ WC_TRAINS_LIST, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _vehicle_list_widgets, - NULL }; static const WindowDesc _player_vehicle_list_road_veh_desc = { @@ -1202,7 +1164,6 @@ WC_ROADVEH_LIST, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _vehicle_list_widgets, - NULL }; static const WindowDesc _player_vehicle_list_ship_desc = { @@ -1210,7 +1171,6 @@ WC_SHIPS_LIST, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _vehicle_list_widgets, - NULL }; static const WindowDesc _player_vehicle_list_aircraft_desc = { @@ -1218,7 +1178,6 @@ WC_AIRCRAFT_LIST, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _vehicle_list_widgets, - NULL }; static void ShowVehicleListWindowLocal(PlayerID player, uint16 VLW_flag, VehicleType vehicle_type, uint16 unique_number) @@ -1374,78 +1333,6 @@ }, }; -/** Initialize a newly created vehicle details window */ -void CreateVehicleDetailsWindow(Window *w) -{ - const Vehicle *v = GetVehicle(w->window_number); - - switch (v->type) { - case VEH_TRAIN: - ResizeWindow(w, 0, 39); - - w->vscroll.cap = 6; - w->height += 12; - w->resize.step_height = 14; - w->resize.height = w->height - 14 * 2; // Minimum of 4 wagons in the display - - w->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_8867_NAME_TRAIN; - w->widget[VLD_WIDGET_CAPTION].data = STR_8802_DETAILS; - break; - - case VEH_ROAD: { - w->widget[VLD_WIDGET_CAPTION].data = STR_900C_DETAILS; - w->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_902E_NAME_ROAD_VEHICLE; - - if (!RoadVehHasArticPart(v)) break; - - /* Draw the text under the vehicle instead of next to it, minus the - * height already allocated for the cargo of the first vehicle. */ - uint height_extension = 15 - 11; - - /* Add space for the cargo amount for each part. */ - for (const Vehicle *u = v; u != NULL; u = u->Next()) { - height_extension += 11; - } - - ResizeWindow(w, 0, height_extension); - } break; - - case VEH_SHIP: - w->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_982F_NAME_SHIP; - w->widget[VLD_WIDGET_CAPTION].data = STR_9811_DETAILS; - break; - - case VEH_AIRCRAFT: - ResizeWindow(w, 0, 11); - w->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_A032_NAME_AIRCRAFT; - w->widget[VLD_WIDGET_CAPTION].data = STR_A00C_DETAILS; - break; - default: NOT_REACHED(); - } - - if (v->type != VEH_TRAIN) { - w->vscroll.cap = 1; - w->widget[VLD_WIDGET_MIDDLE_DETAILS].right += 12; - } - - w->widget[VLD_WIDGET_MIDDLE_DETAILS].data = (w->vscroll.cap << 8) + 1; - w->caption_color = v->owner; - - WP(w, vehicledetails_d).tab = 0; -} - -/** Checks whether service interval is enabled for the vehicle. */ -static inline bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type) -{ - switch (vehicle_type) { - default: NOT_REACHED(); - case VEH_TRAIN: return _patches.servint_trains != 0; break; - case VEH_ROAD: return _patches.servint_roadveh != 0; break; - case VEH_SHIP: return _patches.servint_ships != 0; break; - case VEH_AIRCRAFT: return _patches.servint_aircraft != 0; break; - } - return false; // kill a compiler warning -} extern int GetTrainDetailsWndVScroll(VehicleID veh_id, byte det_tab); extern void DrawTrainDetails(const Vehicle *v, int x, int y, int vscroll_pos, uint16 vscroll_cap, byte det_tab); @@ -1453,201 +1340,267 @@ extern void DrawShipDetails(const Vehicle *v, int x, int y); extern void DrawAircraftDetails(const Vehicle *v, int x, int y); -/** -* Draw the details for the given vehicle at the position (x, y) of the Details windows -* -* @param v current vehicle -* @param x The x coordinate -* @param y The y coordinate -* @param vscroll_pos (train only) -* @param vscroll_cap (train only) -* @param det_tab (train only) -*/ -static inline void DrawVehicleDetails(const Vehicle *v, int x, int y, int vscroll_pos, uint vscroll_cap, byte det_tab) -{ - switch (v->type) { - case VEH_TRAIN: DrawTrainDetails(v, x, y, vscroll_pos, vscroll_cap, det_tab); break; - case VEH_ROAD: DrawRoadVehDetails(v, x, y); break; - case VEH_SHIP: DrawShipDetails(v, x, y); break; - case VEH_AIRCRAFT: DrawAircraftDetails(v, x, y); break; - default: NOT_REACHED(); - } -} - -/** Repaint vehicle details window. */ -static void DrawVehicleDetailsWindow(Window *w) -{ - const Vehicle *v = GetVehicle(w->window_number); - byte det_tab = WP(w, vehicledetails_d).tab; - - w->SetWidgetDisabledState(VLD_WIDGET_RENAME_VEHICLE, v->owner != _local_player); - - if (v->type == VEH_TRAIN) { - w->DisableWidget(det_tab + VLD_WIDGET_DETAILS_CARGO_CARRIED); - SetVScrollCount(w, GetTrainDetailsWndVScroll(v->index, det_tab)); - } +struct VehicleDetailsWindow : Window { + int tab; - w->SetWidgetsHiddenState(v->type != VEH_TRAIN, - VLD_WIDGET_SCROLLBAR, - VLD_WIDGET_DETAILS_CARGO_CARRIED, - VLD_WIDGET_DETAILS_TRAIN_VEHICLES, - VLD_WIDGET_DETAILS_CAPACITY_OF_EACH, - VLD_WIDGET_DETAILS_TOTAL_CARGO, - VLD_WIDGET_RESIZE, - WIDGET_LIST_END); - - /* Disable service-scroller when interval is set to disabled */ - w->SetWidgetsDisabledState(!IsVehicleServiceIntervalEnabled(v->type), - VLD_WIDGET_INCREASE_SERVICING_INTERVAL, - VLD_WIDGET_DECREASE_SERVICING_INTERVAL, - WIDGET_LIST_END); - - - SetDParam(0, v->index); - DrawWindowWidgets(w); + /** Initialize a newly created vehicle details window */ + VehicleDetailsWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + const Vehicle *v = GetVehicle(this->window_number); - /* Draw running cost */ - SetDParam(1, v->age / 366); - SetDParam(0, (v->age + 365 < v->max_age) ? STR_AGE : STR_AGE_RED); - SetDParam(2, v->max_age / 366); - SetDParam(3, v->GetDisplayRunningCost()); - DrawString(2, 15, _vehicle_translation_table[VST_VEHICLE_AGE_RUNNING_COST_YR][v->type], TC_FROMSTRING); + switch (v->type) { + case VEH_TRAIN: + ResizeWindow(this, 0, 39); - /* Draw max speed */ - switch (v->type) { - case VEH_TRAIN: - SetDParam(2, v->GetDisplayMaxSpeed()); - SetDParam(1, v->u.rail.cached_power); - SetDParam(0, v->u.rail.cached_weight); - SetDParam(3, v->u.rail.cached_max_te / 1000); - DrawString(2, 25, (_patches.realistic_acceleration && v->u.rail.railtype != RAILTYPE_MAGLEV) ? - STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE : - STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, TC_FROMSTRING); - break; + this->vscroll.cap = 6; + this->height += 12; + this->resize.step_height = 14; + this->resize.height = this->height - 14 * 2; // Minimum of 4 wagons in the display - case VEH_ROAD: - case VEH_SHIP: - case VEH_AIRCRAFT: - SetDParam(0, v->GetDisplayMaxSpeed()); - DrawString(2, 25, _vehicle_translation_table[VST_VEHICLE_MAX_SPEED][v->type], TC_FROMSTRING); - break; + this->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_8867_NAME_TRAIN; + this->widget[VLD_WIDGET_CAPTION].data = STR_8802_DETAILS; + break; - default: NOT_REACHED(); + case VEH_ROAD: { + this->widget[VLD_WIDGET_CAPTION].data = STR_900C_DETAILS; + this->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_902E_NAME_ROAD_VEHICLE; + + if (!RoadVehHasArticPart(v)) break; + + /* Draw the text under the vehicle instead of next to it, minus the + * height already allocated for the cargo of the first vehicle. */ + uint height_extension = 15 - 11; + + /* Add space for the cargo amount for each part. */ + for (const Vehicle *u = v; u != NULL; u = u->Next()) { + height_extension += 11; + } + + ResizeWindow(this, 0, height_extension); + } break; + + case VEH_SHIP: + this->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_982F_NAME_SHIP; + this->widget[VLD_WIDGET_CAPTION].data = STR_9811_DETAILS; + break; + + case VEH_AIRCRAFT: + ResizeWindow(this, 0, 11); + this->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_A032_NAME_AIRCRAFT; + this->widget[VLD_WIDGET_CAPTION].data = STR_A00C_DETAILS; + break; + default: NOT_REACHED(); + } + + if (v->type != VEH_TRAIN) { + this->vscroll.cap = 1; + this->widget[VLD_WIDGET_MIDDLE_DETAILS].right += 12; + } + + this->widget[VLD_WIDGET_MIDDLE_DETAILS].data = (this->vscroll.cap << 8) + 1; + this->caption_color = v->owner; + + this->tab = 0; } - /* Draw profit */ - SetDParam(0, v->GetDisplayProfitThisYear()); - SetDParam(1, v->GetDisplayProfitLastYear()); - DrawString(2, 35, _vehicle_translation_table[VST_VEHICLE_PROFIT_THIS_YEAR_LAST_YEAR][v->type], TC_FROMSTRING); - - /* Draw breakdown & reliability */ - SetDParam(0, v->reliability * 100 >> 16); - SetDParam(1, v->breakdowns_since_last_service); - DrawString(2, 45, _vehicle_translation_table[VST_VEHICLE_RELIABILITY_BREAKDOWNS][v->type], TC_FROMSTRING); - - /* Draw service interval text */ - SetDParam(0, v->service_interval); - SetDParam(1, v->date_of_last_service); - DrawString(13, w->height - (v->type != VEH_TRAIN ? 11 : 23), _patches.servint_ispercent ? STR_SERVICING_INTERVAL_PERCENT : STR_883C_SERVICING_INTERVAL_DAYS, TC_FROMSTRING); - - switch (v->type) { - case VEH_TRAIN: - DrawVehicleDetails(v, 2, 57, w->vscroll.pos, w->vscroll.cap, det_tab); - break; - - case VEH_ROAD: - case VEH_SHIP: - case VEH_AIRCRAFT: - DrawVehicleImage(v, 3, 57, INVALID_VEHICLE, 0, 0); - DrawVehicleDetails(v, 75, 57, w->vscroll.pos, w->vscroll.cap, det_tab); - break; - - default: NOT_REACHED(); + /** Checks whether service interval is enabled for the vehicle. */ + static bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type) + { + switch (vehicle_type) { + default: NOT_REACHED(); + case VEH_TRAIN: return _patches.servint_trains != 0; break; + case VEH_ROAD: return _patches.servint_roadveh != 0; break; + case VEH_SHIP: return _patches.servint_ships != 0; break; + case VEH_AIRCRAFT: return _patches.servint_aircraft != 0; break; + } + return false; // kill a compiler warning } -} - -/** Message strings for renaming vehicles indexed by vehicle type. */ -static const StringID _name_vehicle_title[] = { - STR_8865_NAME_TRAIN, - STR_902C_NAME_ROAD_VEHICLE, - STR_9831_NAME_SHIP, - STR_A030_NAME_AIRCRAFT -}; - -/** Message strings for error while renaming indexed by vehicle type. */ -static const StringID _name_vehicle_error[] = { - STR_8866_CAN_T_NAME_TRAIN, - STR_902D_CAN_T_NAME_ROAD_VEHICLE, - STR_9832_CAN_T_NAME_SHIP, - STR_A031_CAN_T_NAME_AIRCRAFT -}; - -/** Window event hook for vehicle details. */ -static void VehicleDetailsWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - CreateVehicleDetailsWindow(w); - break; - case WE_PAINT: - DrawVehicleDetailsWindow(w); - break; - - case WE_CLICK: { - switch (e->we.click.widget) { - case VLD_WIDGET_RENAME_VEHICLE: {// rename - const Vehicle *v = GetVehicle(w->window_number); - SetDParam(0, v->index); - ShowQueryString(STR_VEHICLE_NAME, _name_vehicle_title[v->type], 31, 150, w, CS_ALPHANUMERAL); - } break; - - case VLD_WIDGET_INCREASE_SERVICING_INTERVAL: // increase int - case VLD_WIDGET_DECREASE_SERVICING_INTERVAL: { // decrease int - int mod = _ctrl_pressed ? 5 : 10; - const Vehicle *v = GetVehicle(w->window_number); - - mod = (e->we.click.widget == VLD_WIDGET_DECREASE_SERVICING_INTERVAL) ? -mod : mod; - mod = GetServiceIntervalClamped(mod + v->service_interval); - if (mod == v->service_interval) return; - - DoCommandP(v->tile, v->index, mod, NULL, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING)); - } break; + /** + * Draw the details for the given vehicle at the position (x, y) of the Details windows + * + * @param v current vehicle + * @param x The x coordinate + * @param y The y coordinate + * @param vscroll_pos (train only) + * @param vscroll_cap (train only) + * @param det_tab (train only) + */ + static void DrawVehicleDetails(const Vehicle *v, int x, int y, int vscroll_pos, uint vscroll_cap, byte det_tab) + { + switch (v->type) { + case VEH_TRAIN: DrawTrainDetails(v, x, y, vscroll_pos, vscroll_cap, det_tab); break; + case VEH_ROAD: DrawRoadVehDetails(v, x, y); break; + case VEH_SHIP: DrawShipDetails(v, x, y); break; + case VEH_AIRCRAFT: DrawAircraftDetails(v, x, y); break; + default: NOT_REACHED(); + } + } - case VLD_WIDGET_DETAILS_CARGO_CARRIED: - case VLD_WIDGET_DETAILS_TRAIN_VEHICLES: - case VLD_WIDGET_DETAILS_CAPACITY_OF_EACH: - case VLD_WIDGET_DETAILS_TOTAL_CARGO: - w->SetWidgetsDisabledState(false, - VLD_WIDGET_DETAILS_CARGO_CARRIED, - VLD_WIDGET_DETAILS_TRAIN_VEHICLES, - VLD_WIDGET_DETAILS_CAPACITY_OF_EACH, - VLD_WIDGET_DETAILS_TOTAL_CARGO, - e->we.click.widget, - WIDGET_LIST_END); + /** Repaint vehicle details window. */ + virtual void OnPaint() + { + const Vehicle *v = GetVehicle(this->window_number); + byte det_tab = this->tab; - WP(w, vehicledetails_d).tab = e->we.click.widget - VLD_WIDGET_DETAILS_CARGO_CARRIED; - w->SetDirty(); - break; - } - } break; + this->SetWidgetDisabledState(VLD_WIDGET_RENAME_VEHICLE, v->owner != _local_player); - case WE_ON_EDIT_TEXT: - if (!StrEmpty(e->we.edittext.str)) { - _cmd_text = e->we.edittext.str; - DoCommandP(0, w->window_number, 0, NULL, CMD_NAME_VEHICLE | CMD_MSG(_name_vehicle_error[GetVehicle(w->window_number)->type])); - } - break; + if (v->type == VEH_TRAIN) { + this->DisableWidget(det_tab + VLD_WIDGET_DETAILS_CARGO_CARRIED); + SetVScrollCount(this, GetTrainDetailsWndVScroll(v->index, det_tab)); + } - case WE_RESIZE: - if (e->we.sizing.diff.x != 0) ResizeButtons(w, VLD_WIDGET_DETAILS_CARGO_CARRIED, VLD_WIDGET_DETAILS_TOTAL_CARGO); - if (e->we.sizing.diff.y == 0) break; + this->SetWidgetsHiddenState(v->type != VEH_TRAIN, + VLD_WIDGET_SCROLLBAR, + VLD_WIDGET_DETAILS_CARGO_CARRIED, + VLD_WIDGET_DETAILS_TRAIN_VEHICLES, + VLD_WIDGET_DETAILS_CAPACITY_OF_EACH, + VLD_WIDGET_DETAILS_TOTAL_CARGO, + VLD_WIDGET_RESIZE, + WIDGET_LIST_END); - w->vscroll.cap += e->we.sizing.diff.y / 14; - w->widget[VLD_WIDGET_MIDDLE_DETAILS].data = (w->vscroll.cap << 8) + 1; - break; + /* Disable service-scroller when interval is set to disabled */ + this->SetWidgetsDisabledState(!IsVehicleServiceIntervalEnabled(v->type), + VLD_WIDGET_INCREASE_SERVICING_INTERVAL, + VLD_WIDGET_DECREASE_SERVICING_INTERVAL, + WIDGET_LIST_END); + + + SetDParam(0, v->index); + this->DrawWidgets(); + + /* Draw running cost */ + SetDParam(1, v->age / 366); + SetDParam(0, (v->age + 365 < v->max_age) ? STR_AGE : STR_AGE_RED); + SetDParam(2, v->max_age / 366); + SetDParam(3, v->GetDisplayRunningCost()); + DrawString(2, 15, _vehicle_translation_table[VST_VEHICLE_AGE_RUNNING_COST_YR][v->type], TC_FROMSTRING); + + /* Draw max speed */ + switch (v->type) { + case VEH_TRAIN: + SetDParam(2, v->GetDisplayMaxSpeed()); + SetDParam(1, v->u.rail.cached_power); + SetDParam(0, v->u.rail.cached_weight); + SetDParam(3, v->u.rail.cached_max_te / 1000); + DrawString(2, 25, (_patches.realistic_acceleration && v->u.rail.railtype != RAILTYPE_MAGLEV) ? + STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE : + STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, TC_FROMSTRING); + break; + + case VEH_ROAD: + case VEH_SHIP: + case VEH_AIRCRAFT: + SetDParam(0, v->GetDisplayMaxSpeed()); + DrawString(2, 25, _vehicle_translation_table[VST_VEHICLE_MAX_SPEED][v->type], TC_FROMSTRING); + break; + + default: NOT_REACHED(); + } + + /* Draw profit */ + SetDParam(0, v->GetDisplayProfitThisYear()); + SetDParam(1, v->GetDisplayProfitLastYear()); + DrawString(2, 35, _vehicle_translation_table[VST_VEHICLE_PROFIT_THIS_YEAR_LAST_YEAR][v->type], TC_FROMSTRING); + + /* Draw breakdown & reliability */ + SetDParam(0, v->reliability * 100 >> 16); + SetDParam(1, v->breakdowns_since_last_service); + DrawString(2, 45, _vehicle_translation_table[VST_VEHICLE_RELIABILITY_BREAKDOWNS][v->type], TC_FROMSTRING); + + /* Draw service interval text */ + SetDParam(0, v->service_interval); + SetDParam(1, v->date_of_last_service); + DrawString(13, this->height - (v->type != VEH_TRAIN ? 11 : 23), _patches.servint_ispercent ? STR_SERVICING_INTERVAL_PERCENT : STR_883C_SERVICING_INTERVAL_DAYS, TC_FROMSTRING); + + switch (v->type) { + case VEH_TRAIN: + DrawVehicleDetails(v, 2, 57, this->vscroll.pos, this->vscroll.cap, det_tab); + break; + + case VEH_ROAD: + case VEH_SHIP: + case VEH_AIRCRAFT: + DrawVehicleImage(v, 3, 57, INVALID_VEHICLE, 0, 0); + DrawVehicleDetails(v, 75, 57, this->vscroll.pos, this->vscroll.cap, det_tab); + break; + + default: NOT_REACHED(); + } } -} + + virtual void OnClick(Point pt, int widget) + { + /** Message strings for renaming vehicles indexed by vehicle type. */ + static const StringID _name_vehicle_title[] = { + STR_8865_NAME_TRAIN, + STR_902C_NAME_ROAD_VEHICLE, + STR_9831_NAME_SHIP, + STR_A030_NAME_AIRCRAFT + }; + + switch (widget) { + case VLD_WIDGET_RENAME_VEHICLE: {// rename + const Vehicle *v = GetVehicle(this->window_number); + SetDParam(0, v->index); + ShowQueryString(STR_VEHICLE_NAME, _name_vehicle_title[v->type], 31, 150, this, CS_ALPHANUMERAL); + } break; + + case VLD_WIDGET_INCREASE_SERVICING_INTERVAL: // increase int + case VLD_WIDGET_DECREASE_SERVICING_INTERVAL: { // decrease int + int mod = _ctrl_pressed ? 5 : 10; + const Vehicle *v = GetVehicle(this->window_number); + + mod = (widget == VLD_WIDGET_DECREASE_SERVICING_INTERVAL) ? -mod : mod; + mod = GetServiceIntervalClamped(mod + v->service_interval); + if (mod == v->service_interval) return; + + DoCommandP(v->tile, v->index, mod, NULL, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING)); + } break; + + case VLD_WIDGET_DETAILS_CARGO_CARRIED: + case VLD_WIDGET_DETAILS_TRAIN_VEHICLES: + case VLD_WIDGET_DETAILS_CAPACITY_OF_EACH: + case VLD_WIDGET_DETAILS_TOTAL_CARGO: + this->SetWidgetsDisabledState(false, + VLD_WIDGET_DETAILS_CARGO_CARRIED, + VLD_WIDGET_DETAILS_TRAIN_VEHICLES, + VLD_WIDGET_DETAILS_CAPACITY_OF_EACH, + VLD_WIDGET_DETAILS_TOTAL_CARGO, + widget, + WIDGET_LIST_END); + + this->tab = widget - VLD_WIDGET_DETAILS_CARGO_CARRIED; + this->SetDirty(); + break; + } + } + + virtual void OnQueryTextFinished(char *str) + { + /** Message strings for error while renaming indexed by vehicle type. */ + static const StringID _name_vehicle_error[] = { + STR_8866_CAN_T_NAME_TRAIN, + STR_902D_CAN_T_NAME_ROAD_VEHICLE, + STR_9832_CAN_T_NAME_SHIP, + STR_A031_CAN_T_NAME_AIRCRAFT + }; + + if (!StrEmpty(str)) { + _cmd_text = str; + DoCommandP(0, this->window_number, 0, NULL, CMD_NAME_VEHICLE | CMD_MSG(_name_vehicle_error[GetVehicle(this->window_number)->type])); + } + } + + virtual void OnResize(Point new_size, Point delta) + { + if (delta.x != 0) ResizeButtons(this, VLD_WIDGET_DETAILS_CARGO_CARRIED, VLD_WIDGET_DETAILS_TOTAL_CARGO); + if (delta.y == 0) return; + + this->vscroll.cap += delta.y / 14; + this->widget[VLD_WIDGET_MIDDLE_DETAILS].data = (this->vscroll.cap << 8) + 1; + } +}; /** Vehicle details window descriptor. */ static const WindowDesc _vehicle_details_desc = { @@ -1655,7 +1608,6 @@ WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _vehicle_details_widgets, - VehicleDetailsWndProc }; /** Shows the vehicle details window of the given vehicle. */ @@ -1663,7 +1615,7 @@ { DeleteWindowById(WC_VEHICLE_ORDERS, v->index); DeleteWindowById(WC_VEHICLE_DETAILS, v->index); - AllocateWindowDescFront(&_vehicle_details_desc, v->index); + AllocateWindowDescFront(&_vehicle_details_desc, v->index); } @@ -1691,15 +1643,12 @@ }; -static void VehicleViewWndProc(Window *w, WindowEvent *e); - /** Vehicle view window descriptor for all vehicles but trains. */ static const WindowDesc _vehicle_view_desc = { WDP_AUTO, WDP_AUTO, 250, 116, 250, 116, WC_VEHICLE_VIEW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _vehicle_view_widgets, - VehicleViewWndProc }; /** Vehicle view window descriptor for trains. Only minimum_height and @@ -1710,7 +1659,6 @@ WC_VEHICLE_VIEW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _vehicle_view_widgets, - VehicleViewWndProc }; @@ -1736,300 +1684,6 @@ static const int VV_INITIAL_VIEWPORT_HEIGHT = 84; static const int VV_INITIAL_VIEWPORT_HEIGHT_TRAIN = 102; -/** Shows the vehicle view window of the given vehicle. */ -void ShowVehicleViewWindow(const Vehicle *v) -{ - Window *w = AllocateWindowDescFront((v->type == VEH_TRAIN) ? &_train_view_desc : &_vehicle_view_desc, v->index); - - if (w != NULL) { - w->caption_color = v->owner; - InitializeWindowViewport(w, VV_VIEWPORT_X, VV_VIEWPORT_Y, VV_INITIAL_VIEWPORT_WIDTH, - (v->type == VEH_TRAIN) ? VV_INITIAL_VIEWPORT_HEIGHT_TRAIN : VV_INITIAL_VIEWPORT_HEIGHT, - w->window_number | (1 << 31), _vehicle_view_zoom_levels[v->type]); - } -} - -/** Initialize a newly created vehicle view window */ -static void CreateVehicleViewWindow(Window *w) -{ - const Vehicle *v = GetVehicle(w->window_number); - - /* - * fill in data and tooltip codes for the widgets and - * move some of the buttons for trains - */ - switch (v->type) { - case VEH_TRAIN: - w->widget[VVW_WIDGET_CAPTION].data = STR_882E; - - w->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_8846_CURRENT_TRAIN_ACTION_CLICK; - - w->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_8848_CENTER_MAIN_VIEW_ON_TRAIN; - - w->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_TRAIN_TODEPOT; - w->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_8849_SEND_TRAIN_TO_DEPOT; - - w->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_RAIL_REFIT_VEHICLE_TO_CARRY; - - w->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_8847_SHOW_TRAIN_S_ORDERS; - - w->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_884C_SHOW_TRAIN_DETAILS; - - w->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_TRAIN; - w->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_TRAIN_INFO; - - w->widget[VVW_WIDGET_TURN_AROUND].tooltips = STR_884B_REVERSE_DIRECTION_OF_TRAIN; - - - /* due to more buttons we must modify the layout a bit for trains */ - w->widget[VVW_WIDGET_PANEL].bottom = 121; - w->widget[VVW_WIDGET_VIEWPORT].bottom = 119; - - w->widget[VVW_WIDGET_START_STOP_VEH].top = 122; - w->widget[VVW_WIDGET_START_STOP_VEH].bottom = 133; - - w->widget[VVW_WIDGET_REFIT_VEH].top = 68; - w->widget[VVW_WIDGET_REFIT_VEH].bottom = 85; - - w->widget[VVW_WIDGET_SHOW_ORDERS].top = 86; - w->widget[VVW_WIDGET_SHOW_ORDERS].bottom = 103; - - w->widget[VVW_WIDGET_SHOW_DETAILS].top = 104; - w->widget[VVW_WIDGET_SHOW_DETAILS].bottom = 121; - - w->widget[VVW_WIDGET_EMPTY_BOTTOM_RIGHT].top = 122; - w->widget[VVW_WIDGET_EMPTY_BOTTOM_RIGHT].bottom = 121; - - w->widget[VVW_WIDGET_RESIZE].top = 122; - w->widget[VVW_WIDGET_RESIZE].bottom = 133; - - w->widget[VVW_WIDGET_TURN_AROUND].top = 68; - w->widget[VVW_WIDGET_TURN_AROUND].bottom = 85; - break; - - case VEH_ROAD: - w->widget[VVW_WIDGET_CAPTION].data = STR_9002; - - w->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_901C_CURRENT_VEHICLE_ACTION; - - w->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_901E_CENTER_MAIN_VIEW_ON_VEHICLE; - - w->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_ROADVEH_TODEPOT; - w->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_901F_SEND_VEHICLE_TO_DEPOT; - - w->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_REFIT_ROAD_VEHICLE_TO_CARRY; - - w->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_901D_SHOW_VEHICLE_S_ORDERS; - - w->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_9021_SHOW_ROAD_VEHICLE_DETAILS; - - w->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_ROADVEH; - w->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_ROAD_VEHICLE_INFO; - - w->SetWidgetHiddenState(VVW_WIDGET_FORCE_PROCEED, true); - break; - - case VEH_SHIP: - w->widget[VVW_WIDGET_CAPTION].data = STR_980F; - - w->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_9827_CURRENT_SHIP_ACTION_CLICK; - - w->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_9829_CENTER_MAIN_VIEW_ON_SHIP; - - w->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_SHIP_TODEPOT; - w->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_982A_SEND_SHIP_TO_DEPOT; - - w->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_983A_REFIT_CARGO_SHIP_TO_CARRY; - - w->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_9828_SHOW_SHIP_S_ORDERS; - - w->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_982B_SHOW_SHIP_DETAILS; - - w->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_SHIP; - w->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_SHIP_INFO; - - w->SetWidgetsHiddenState(true, - VVW_WIDGET_TURN_AROUND, - VVW_WIDGET_FORCE_PROCEED, - WIDGET_LIST_END); - break; - - case VEH_AIRCRAFT: - w->widget[VVW_WIDGET_CAPTION].data = STR_A00A; - - w->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_A027_CURRENT_AIRCRAFT_ACTION; - - w->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_A029_CENTER_MAIN_VIEW_ON_AIRCRAFT; - - w->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_AIRCRAFT_TODEPOT; - w->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_A02A_SEND_AIRCRAFT_TO_HANGAR; - - w->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_A03B_REFIT_AIRCRAFT_TO_CARRY; - - w->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_A028_SHOW_AIRCRAFT_S_ORDERS; - - w->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_A02B_SHOW_AIRCRAFT_DETAILS; - - w->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_AIRCRAFT; - w->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_AIRCRAFT_INFO; - - w->SetWidgetsHiddenState(true, - VVW_WIDGET_TURN_AROUND, - VVW_WIDGET_FORCE_PROCEED, - WIDGET_LIST_END); - break; - - default: NOT_REACHED(); - } -} - -/** Checks whether the vehicle may be refitted at the moment.*/ -static bool IsVehicleRefitable(const Vehicle *v) -{ - /* Why is this so different for different vehicles? - * Does maybe work one solution for all? - */ - switch (v->type) { - case VEH_TRAIN: return false; - case VEH_ROAD: return EngInfo(v->engine_type)->refit_mask != 0 && v->IsStoppedInDepot(); - case VEH_SHIP: return ShipVehInfo(v->engine_type)->refittable && v->IsStoppedInDepot(); - case VEH_AIRCRAFT: return v->IsStoppedInDepot(); - default: NOT_REACHED(); - } -} - -/** Message strings for heading to depot indexed by vehicle type. */ -static const StringID _heading_for_depot_strings[] = { - STR_HEADING_FOR_TRAIN_DEPOT, - STR_HEADING_FOR_ROAD_DEPOT, - STR_HEADING_FOR_SHIP_DEPOT, - STR_HEADING_FOR_HANGAR, -}; - -/** Message strings for heading to depot and servicing indexed by vehicle type. */ -static const StringID _heading_for_depot_service_strings[] = { - STR_HEADING_FOR_TRAIN_DEPOT_SERVICE, - STR_HEADING_FOR_ROAD_DEPOT_SERVICE, - STR_HEADING_FOR_SHIP_DEPOT_SERVICE, - STR_HEADING_FOR_HANGAR_SERVICE, -}; - -/** Repaint vehicle view window. */ -static void DrawVehicleViewWindow(Window *w) -{ - const Vehicle *v = GetVehicle(w->window_number); - StringID str; - bool is_localplayer = v->owner == _local_player; - bool refitable_and_stopped_in_depot = IsVehicleRefitable(v); - - w->SetWidgetDisabledState(VVW_WIDGET_GOTO_DEPOT, !is_localplayer); - w->SetWidgetDisabledState(VVW_WIDGET_REFIT_VEH, - !refitable_and_stopped_in_depot || !is_localplayer); - w->SetWidgetDisabledState(VVW_WIDGET_CLONE_VEH, !is_localplayer); - - if (v->type == VEH_TRAIN) { - w->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !is_localplayer); - w->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !is_localplayer); - - /* Cargo refit button is disabled, until we know we can enable it below. */ - - if (is_localplayer) { - /* See if any vehicle can be refitted */ - for (const Vehicle *u = v; u != NULL; u = u->Next()) { - if (EngInfo(u->engine_type)->refit_mask != 0 || - (RailVehInfo(v->engine_type)->railveh_type != RAILVEH_WAGON && v->cargo_cap != 0)) { - w->EnableWidget(VVW_WIDGET_REFIT_VEH); - /* We have a refittable carriage, bail out */ - break; - } - } - } - } - - /* draw widgets & caption */ - SetDParam(0, v->index); - DrawWindowWidgets(w); - - if (v->vehstatus & VS_CRASHED) { - str = STR_8863_CRASHED; - } else if (v->type != VEH_AIRCRAFT && v->breakdown_ctr == 1) { // check for aircraft necessary? - str = STR_885C_BROKEN_DOWN; - } else if (v->vehstatus & VS_STOPPED) { - if (v->type == VEH_TRAIN) { - if (v->cur_speed == 0) { - if (v->u.rail.cached_power == 0) { - str = STR_TRAIN_NO_POWER; - } else { - str = STR_8861_STOPPED; - } - } else { - SetDParam(0, v->GetDisplaySpeed()); - str = STR_TRAIN_STOPPING + _patches.vehicle_speed; - } - } else { // no train - str = STR_8861_STOPPED; - } - } else { // vehicle is in a "normal" state, show current order - switch (v->current_order.GetType()) { - case OT_GOTO_STATION: { - SetDParam(0, v->current_order.GetDestination()); - SetDParam(1, v->GetDisplaySpeed()); - str = STR_HEADING_FOR_STATION + _patches.vehicle_speed; - } break; - - case OT_GOTO_DEPOT: { - if (v->type == VEH_AIRCRAFT) { - /* Aircrafts always go to a station, even if you say depot */ - SetDParam(0, v->current_order.GetDestination()); - SetDParam(1, v->GetDisplaySpeed()); - } else { - Depot *depot = GetDepot(v->current_order.GetDestination()); - SetDParam(0, depot->town_index); - SetDParam(1, v->GetDisplaySpeed()); - } - if ((v->current_order.GetDepotActionType() & ODATFB_HALT) && !(v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) { - str = _heading_for_depot_strings[v->type] + _patches.vehicle_speed; - } else { - str = _heading_for_depot_service_strings[v->type] + _patches.vehicle_speed; - } - } break; - - case OT_LOADING: - str = STR_882F_LOADING_UNLOADING; - break; - - case OT_GOTO_WAYPOINT: { - assert(v->type == VEH_TRAIN); - SetDParam(0, v->current_order.GetDestination()); - str = STR_HEADING_FOR_WAYPOINT + _patches.vehicle_speed; - SetDParam(1, v->GetDisplaySpeed()); - break; - } - - case OT_LEAVESTATION: - if (v->type != VEH_AIRCRAFT) { - str = STR_LEAVING; - break; - } - /* fall-through if aircraft. Does this even happen? */ - - default: - if (v->num_orders == 0) { - str = STR_NO_ORDERS + _patches.vehicle_speed; - SetDParam(0, v->GetDisplaySpeed()); - } else { - str = STR_EMPTY; - } - break; - } - } - - /* draw the flag plus orders */ - DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, 2, w->widget[VVW_WIDGET_START_STOP_VEH].top + 1); - DrawStringCenteredTruncated(w->widget[VVW_WIDGET_START_STOP_VEH].left + 8, w->widget[VVW_WIDGET_START_STOP_VEH].right, w->widget[VVW_WIDGET_START_STOP_VEH].top + 1, str, TC_FROMSTRING); - DrawWindowViewport(w); -} - /** Command indices for the _vehicle_command_translation_table. */ enum VehicleCommandTranslation { VCT_CMD_START_STOP = 0, @@ -2067,104 +1721,387 @@ }, }; -/** Window event hook for vehicle view. */ -static void VehicleViewWndProc(Window *w, WindowEvent *e) +/** Checks whether the vehicle may be refitted at the moment.*/ +static bool IsVehicleRefitable(const Vehicle *v) { - switch (e->event) { - case WE_CREATE: - CreateVehicleViewWindow(w); - break; - - case WE_PAINT: - DrawVehicleViewWindow(w); - break; + /* Why is this so different for different vehicles? + * Does maybe work one solution for all? + */ + switch (v->type) { + case VEH_TRAIN: return false; + case VEH_ROAD: return EngInfo(v->engine_type)->refit_mask != 0 && v->IsStoppedInDepot(); + case VEH_SHIP: return ShipVehInfo(v->engine_type)->refittable && v->IsStoppedInDepot(); + case VEH_AIRCRAFT: return v->IsStoppedInDepot(); + default: NOT_REACHED(); + } +} - case WE_CLICK: { - const Vehicle *v = GetVehicle(w->window_number); +struct VehicleViewWindow : Window { + VehicleViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number) + { + const Vehicle *v = GetVehicle(this->window_number); - switch (e->we.click.widget) { - case VVW_WIDGET_START_STOP_VEH: /* start stop */ - DoCommandP(v->tile, v->index, 0, NULL, - _vehicle_command_translation_table[VCT_CMD_START_STOP][v->type]); - break; - case VVW_WIDGET_CENTER_MAIN_VIEH: {/* center main view */ - const Window *mainwindow = FindWindowById(WC_MAIN_WINDOW, 0); - /* code to allow the main window to 'follow' the vehicle if the ctrl key is pressed */ - if (_ctrl_pressed && mainwindow->viewport->zoom == ZOOM_LVL_NORMAL) { - mainwindow->viewport->follow_vehicle = v->index; + this->caption_color = v->owner; + InitializeWindowViewport(this, VV_VIEWPORT_X, VV_VIEWPORT_Y, VV_INITIAL_VIEWPORT_WIDTH, + (v->type == VEH_TRAIN) ? VV_INITIAL_VIEWPORT_HEIGHT_TRAIN : VV_INITIAL_VIEWPORT_HEIGHT, + this->window_number | (1 << 31), _vehicle_view_zoom_levels[v->type]); + + /* + * fill in data and tooltip codes for the widgets and + * move some of the buttons for trains + */ + switch (v->type) { + case VEH_TRAIN: + this->widget[VVW_WIDGET_CAPTION].data = STR_882E; + + this->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_8846_CURRENT_TRAIN_ACTION_CLICK; + + this->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_8848_CENTER_MAIN_VIEW_ON_TRAIN; + + this->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_TRAIN_TODEPOT; + this->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_8849_SEND_TRAIN_TO_DEPOT; + + this->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_RAIL_REFIT_VEHICLE_TO_CARRY; + + this->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_8847_SHOW_TRAIN_S_ORDERS; + + this->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_884C_SHOW_TRAIN_DETAILS; + + this->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_TRAIN; + this->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_TRAIN_INFO; + + this->widget[VVW_WIDGET_TURN_AROUND].tooltips = STR_884B_REVERSE_DIRECTION_OF_TRAIN; + + + /* due to more buttons we must modify the layout a bit for trains */ + this->widget[VVW_WIDGET_PANEL].bottom = 121; + this->widget[VVW_WIDGET_VIEWPORT].bottom = 119; + + this->widget[VVW_WIDGET_START_STOP_VEH].top = 122; + this->widget[VVW_WIDGET_START_STOP_VEH].bottom = 133; + + this->widget[VVW_WIDGET_REFIT_VEH].top = 68; + this->widget[VVW_WIDGET_REFIT_VEH].bottom = 85; + + this->widget[VVW_WIDGET_SHOW_ORDERS].top = 86; + this->widget[VVW_WIDGET_SHOW_ORDERS].bottom = 103; + + this->widget[VVW_WIDGET_SHOW_DETAILS].top = 104; + this->widget[VVW_WIDGET_SHOW_DETAILS].bottom = 121; + + this->widget[VVW_WIDGET_EMPTY_BOTTOM_RIGHT].top = 122; + this->widget[VVW_WIDGET_EMPTY_BOTTOM_RIGHT].bottom = 121; + + this->widget[VVW_WIDGET_RESIZE].top = 122; + this->widget[VVW_WIDGET_RESIZE].bottom = 133; + + this->widget[VVW_WIDGET_TURN_AROUND].top = 68; + this->widget[VVW_WIDGET_TURN_AROUND].bottom = 85; + break; + + case VEH_ROAD: + this->widget[VVW_WIDGET_CAPTION].data = STR_9002; + + this->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_901C_CURRENT_VEHICLE_ACTION; + + this->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_901E_CENTER_MAIN_VIEW_ON_VEHICLE; + + this->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_ROADVEH_TODEPOT; + this->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_901F_SEND_VEHICLE_TO_DEPOT; + + this->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_REFIT_ROAD_VEHICLE_TO_CARRY; + + this->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_901D_SHOW_VEHICLE_S_ORDERS; + + this->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_9021_SHOW_ROAD_VEHICLE_DETAILS; + + this->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_ROADVEH; + this->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_ROAD_VEHICLE_INFO; + + this->SetWidgetHiddenState(VVW_WIDGET_FORCE_PROCEED, true); + break; + + case VEH_SHIP: + this->widget[VVW_WIDGET_CAPTION].data = STR_980F; + + this->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_9827_CURRENT_SHIP_ACTION_CLICK; + + this->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_9829_CENTER_MAIN_VIEW_ON_SHIP; + + this->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_SHIP_TODEPOT; + this->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_982A_SEND_SHIP_TO_DEPOT; + + this->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_983A_REFIT_CARGO_SHIP_TO_CARRY; + + this->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_9828_SHOW_SHIP_S_ORDERS; + + this->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_982B_SHOW_SHIP_DETAILS; + + this->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_SHIP; + this->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_SHIP_INFO; + + this->SetWidgetsHiddenState(true, + VVW_WIDGET_TURN_AROUND, + VVW_WIDGET_FORCE_PROCEED, + WIDGET_LIST_END); + break; + + case VEH_AIRCRAFT: + this->widget[VVW_WIDGET_CAPTION].data = STR_A00A; + + this->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_A027_CURRENT_AIRCRAFT_ACTION; + + this->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_A029_CENTER_MAIN_VIEW_ON_AIRCRAFT; + + this->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_AIRCRAFT_TODEPOT; + this->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_A02A_SEND_AIRCRAFT_TO_HANGAR; + + this->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_A03B_REFIT_AIRCRAFT_TO_CARRY; + + this->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_A028_SHOW_AIRCRAFT_S_ORDERS; + + this->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_A02B_SHOW_AIRCRAFT_DETAILS; + + this->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_AIRCRAFT; + this->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_AIRCRAFT_INFO; + + this->SetWidgetsHiddenState(true, + VVW_WIDGET_TURN_AROUND, + VVW_WIDGET_FORCE_PROCEED, + WIDGET_LIST_END); + break; + + default: NOT_REACHED(); + } + } + + ~VehicleViewWindow() + { + DeleteWindowById(WC_VEHICLE_ORDERS, this->window_number); + DeleteWindowById(WC_VEHICLE_REFIT, this->window_number); + DeleteWindowById(WC_VEHICLE_DETAILS, this->window_number); + DeleteWindowById(WC_VEHICLE_TIMETABLE, this->window_number); + } + + virtual void OnPaint() + { + /** Message strings for heading to depot indexed by vehicle type. */ + static const StringID _heading_for_depot_strings[] = { + STR_HEADING_FOR_TRAIN_DEPOT, + STR_HEADING_FOR_ROAD_DEPOT, + STR_HEADING_FOR_SHIP_DEPOT, + STR_HEADING_FOR_HANGAR, + }; + + /** Message strings for heading to depot and servicing indexed by vehicle type. */ + static const StringID _heading_for_depot_service_strings[] = { + STR_HEADING_FOR_TRAIN_DEPOT_SERVICE, + STR_HEADING_FOR_ROAD_DEPOT_SERVICE, + STR_HEADING_FOR_SHIP_DEPOT_SERVICE, + STR_HEADING_FOR_HANGAR_SERVICE, + }; + + const Vehicle *v = GetVehicle(this->window_number); + StringID str; + bool is_localplayer = v->owner == _local_player; + bool refitable_and_stopped_in_depot = IsVehicleRefitable(v); + + this->SetWidgetDisabledState(VVW_WIDGET_GOTO_DEPOT, !is_localplayer); + this->SetWidgetDisabledState(VVW_WIDGET_REFIT_VEH, + !refitable_and_stopped_in_depot || !is_localplayer); + this->SetWidgetDisabledState(VVW_WIDGET_CLONE_VEH, !is_localplayer); + + if (v->type == VEH_TRAIN) { + this->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !is_localplayer); + this->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !is_localplayer); + + /* Cargo refit button is disabled, until we know we can enable it below. */ + + if (is_localplayer) { + /* See if any vehicle can be refitted */ + for (const Vehicle *u = v; u != NULL; u = u->Next()) { + if (EngInfo(u->engine_type)->refit_mask != 0 || + (RailVehInfo(v->engine_type)->railveh_type != RAILVEH_WAGON && v->cargo_cap != 0)) { + this->EnableWidget(VVW_WIDGET_REFIT_VEH); + /* We have a refittable carriage, bail out */ + break; + } + } + } + } + + /* draw widgets & caption */ + SetDParam(0, v->index); + this->DrawWidgets(); + + if (v->vehstatus & VS_CRASHED) { + str = STR_8863_CRASHED; + } else if (v->type != VEH_AIRCRAFT && v->breakdown_ctr == 1) { // check for aircraft necessary? + str = STR_885C_BROKEN_DOWN; + } else if (v->vehstatus & VS_STOPPED) { + if (v->type == VEH_TRAIN) { + if (v->cur_speed == 0) { + if (v->u.rail.cached_power == 0) { + str = STR_TRAIN_NO_POWER; } else { - ScrollMainWindowTo(v->x_pos, v->y_pos); + str = STR_8861_STOPPED; + } + } else { + SetDParam(0, v->GetDisplaySpeed()); + str = STR_TRAIN_STOPPING + _patches.vehicle_speed; + } + } else { // no train + str = STR_8861_STOPPED; + } + } else { // vehicle is in a "normal" state, show current order + switch (v->current_order.GetType()) { + case OT_GOTO_STATION: { + SetDParam(0, v->current_order.GetDestination()); + SetDParam(1, v->GetDisplaySpeed()); + str = STR_HEADING_FOR_STATION + _patches.vehicle_speed; + } break; + + case OT_GOTO_DEPOT: { + if (v->type == VEH_AIRCRAFT) { + /* Aircrafts always go to a station, even if you say depot */ + SetDParam(0, v->current_order.GetDestination()); + SetDParam(1, v->GetDisplaySpeed()); + } else { + Depot *depot = GetDepot(v->current_order.GetDestination()); + SetDParam(0, depot->town_index); + SetDParam(1, v->GetDisplaySpeed()); + } + if ((v->current_order.GetDepotActionType() & ODATFB_HALT) && !(v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) { + str = _heading_for_depot_strings[v->type] + _patches.vehicle_speed; + } else { + str = _heading_for_depot_service_strings[v->type] + _patches.vehicle_speed; } } break; - case VVW_WIDGET_GOTO_DEPOT: /* goto hangar */ - DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, NULL, - _vehicle_command_translation_table[VCT_CMD_GOTO_DEPOT][v->type]); - break; - case VVW_WIDGET_REFIT_VEH: /* refit */ - ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID); - break; - case VVW_WIDGET_SHOW_ORDERS: /* show orders */ - if (_ctrl_pressed) { - ShowTimetableWindow(v); - } else { - ShowOrdersWindow(v); - } + case OT_LOADING: + str = STR_882F_LOADING_UNLOADING; break; - case VVW_WIDGET_SHOW_DETAILS: /* show details */ - ShowVehicleDetailsWindow(v); - break; - case VVW_WIDGET_CLONE_VEH: /* clone vehicle */ - DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneVehicle, - _vehicle_command_translation_table[VCT_CMD_CLONE_VEH][v->type]); + + case OT_GOTO_WAYPOINT: { + assert(v->type == VEH_TRAIN); + SetDParam(0, v->current_order.GetDestination()); + str = STR_HEADING_FOR_WAYPOINT + _patches.vehicle_speed; + SetDParam(1, v->GetDisplaySpeed()); break; - case VVW_WIDGET_TURN_AROUND: /* turn around */ - assert(v->type == VEH_TRAIN || v->type == VEH_ROAD); - DoCommandP(v->tile, v->index, 0, NULL, - _vehicle_command_translation_table[VCT_CMD_TURN_AROUND][v->type]); - break; - case VVW_WIDGET_FORCE_PROCEED: /* force proceed */ - assert(v->type == VEH_TRAIN); - DoCommandP(v->tile, v->index, 0, NULL, CMD_FORCE_TRAIN_PROCEED | CMD_MSG(STR_8862_CAN_T_MAKE_TRAIN_PASS_SIGNAL)); + } + + case OT_LEAVESTATION: + if (v->type != VEH_AIRCRAFT) { + str = STR_LEAVING; + break; + } + /* fall-through if aircraft. Does this even happen? */ + + default: + if (v->num_orders == 0) { + str = STR_NO_ORDERS + _patches.vehicle_speed; + SetDParam(0, v->GetDisplaySpeed()); + } else { + str = STR_EMPTY; + } break; } - } break; - - case WE_RESIZE: - w->viewport->width += e->we.sizing.diff.x; - w->viewport->height += e->we.sizing.diff.y; - w->viewport->virtual_width += e->we.sizing.diff.x; - w->viewport->virtual_height += e->we.sizing.diff.y; - break; - - case WE_DESTROY: - DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number); - DeleteWindowById(WC_VEHICLE_REFIT, w->window_number); - DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number); - DeleteWindowById(WC_VEHICLE_TIMETABLE, w->window_number); - break; + } - case WE_TICK: { - const Vehicle *v = GetVehicle(w->window_number); - bool veh_stopped = v->IsStoppedInDepot(); + /* draw the flag plus orders */ + DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, 2, this->widget[VVW_WIDGET_START_STOP_VEH].top + 1); + DrawStringCenteredTruncated(this->widget[VVW_WIDGET_START_STOP_VEH].left + 8, this->widget[VVW_WIDGET_START_STOP_VEH].right, this->widget[VVW_WIDGET_START_STOP_VEH].top + 1, str, TC_FROMSTRING); + this->DrawViewport(); + } - /* Widget VVW_WIDGET_GOTO_DEPOT must be hidden if the vehicle is already - * stopped in depot. - * Widget VVW_WIDGET_CLONE_VEH should then be shown, since cloning is - * allowed only while in depot and stopped. - * This sytem allows to have two buttons, on top of each other. - * The same system applies to widget VVW_WIDGET_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/ - if (veh_stopped != w->IsWidgetHidden(VVW_WIDGET_GOTO_DEPOT) || veh_stopped == w->IsWidgetHidden(VVW_WIDGET_CLONE_VEH)) { - w->SetWidgetHiddenState( VVW_WIDGET_GOTO_DEPOT, veh_stopped); // send to depot - w->SetWidgetHiddenState(VVW_WIDGET_CLONE_VEH, !veh_stopped); // clone - if (v->type == VEH_ROAD || v->type == VEH_TRAIN) { - w->SetWidgetHiddenState( VVW_WIDGET_REFIT_VEH, !veh_stopped); // refit - w->SetWidgetHiddenState(VVW_WIDGET_TURN_AROUND, veh_stopped); // force turn around + virtual void OnClick(Point pt, int widget) + { + const Vehicle *v = GetVehicle(this->window_number); + + switch (widget) { + case VVW_WIDGET_START_STOP_VEH: // start stop + DoCommandP(v->tile, v->index, 0, NULL, + _vehicle_command_translation_table[VCT_CMD_START_STOP][v->type]); + break; + case VVW_WIDGET_CENTER_MAIN_VIEH: {/* center main view */ + const Window *mainwindow = FindWindowById(WC_MAIN_WINDOW, 0); + /* code to allow the main window to 'follow' the vehicle if the ctrl key is pressed */ + if (_ctrl_pressed && mainwindow->viewport->zoom == ZOOM_LVL_NORMAL) { + mainwindow->viewport->follow_vehicle = v->index; + } else { + ScrollMainWindowTo(v->x_pos, v->y_pos); } - w->SetDirty(); + } break; + + case VVW_WIDGET_GOTO_DEPOT: // goto hangar + DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, NULL, + _vehicle_command_translation_table[VCT_CMD_GOTO_DEPOT][v->type]); + break; + case VVW_WIDGET_REFIT_VEH: // refit + ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID); + break; + case VVW_WIDGET_SHOW_ORDERS: // show orders + if (_ctrl_pressed) { + ShowTimetableWindow(v); + } else { + ShowOrdersWindow(v); + } + break; + case VVW_WIDGET_SHOW_DETAILS: // show details + ShowVehicleDetailsWindow(v); + break; + case VVW_WIDGET_CLONE_VEH: // clone vehicle + DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneVehicle, + _vehicle_command_translation_table[VCT_CMD_CLONE_VEH][v->type]); + break; + case VVW_WIDGET_TURN_AROUND: // turn around + assert(v->type == VEH_TRAIN || v->type == VEH_ROAD); + DoCommandP(v->tile, v->index, 0, NULL, + _vehicle_command_translation_table[VCT_CMD_TURN_AROUND][v->type]); + break; + case VVW_WIDGET_FORCE_PROCEED: // force proceed + assert(v->type == VEH_TRAIN); + DoCommandP(v->tile, v->index, 0, NULL, CMD_FORCE_TRAIN_PROCEED | CMD_MSG(STR_8862_CAN_T_MAKE_TRAIN_PASS_SIGNAL)); + break; + } + } + + virtual void OnResize(Point new_size, Point delta) + { + this->viewport->width += delta.x; + this->viewport->height += delta.y; + this->viewport->virtual_width += delta.x; + this->viewport->virtual_height += delta.y; + } + + virtual void OnTick() + { + const Vehicle *v = GetVehicle(this->window_number); + bool veh_stopped = v->IsStoppedInDepot(); + + /* Widget VVW_WIDGET_GOTO_DEPOT must be hidden if the vehicle is already + * stopped in depot. + * Widget VVW_WIDGET_CLONE_VEH should then be shown, since cloning is + * allowed only while in depot and stopped. + * This sytem allows to have two buttons, on top of each other. + * The same system applies to widget VVW_WIDGET_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/ + if (veh_stopped != this->IsWidgetHidden(VVW_WIDGET_GOTO_DEPOT) || veh_stopped == this->IsWidgetHidden(VVW_WIDGET_CLONE_VEH)) { + this->SetWidgetHiddenState( VVW_WIDGET_GOTO_DEPOT, veh_stopped); // send to depot + this->SetWidgetHiddenState(VVW_WIDGET_CLONE_VEH, !veh_stopped); // clone + if (v->type == VEH_ROAD || v->type == VEH_TRAIN) { + this->SetWidgetHiddenState( VVW_WIDGET_REFIT_VEH, !veh_stopped); // refit + this->SetWidgetHiddenState(VVW_WIDGET_TURN_AROUND, veh_stopped); // force turn around } - } break; + this->SetDirty(); + } } +}; + + +/** Shows the vehicle view window of the given vehicle. */ +void ShowVehicleViewWindow(const Vehicle *v) +{ + AllocateWindowDescFront((v->type == VEH_TRAIN) ? &_train_view_desc : &_vehicle_view_desc, v->index); } void DrawVehicleImage(const Vehicle *v, int x, int y, VehicleID selection, int count, int skip) @@ -2177,3 +2114,12 @@ default: NOT_REACHED(); } } + +void StopGlobalFollowVehicle(const Vehicle *v) +{ + Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + if (w != NULL && w->viewport->follow_vehicle == v->index) { + ScrollMainWindowTo(v->x_pos, v->y_pos, true); // lock the main view on the vehicle's last position + w->viewport->follow_vehicle = INVALID_VEHICLE; + } +} diff -r 6c4314786d68 -r 8cbdb511a674 src/vehicle_gui.h --- a/src/vehicle_gui.h Mon May 19 14:14:33 2008 +0000 +++ b/src/vehicle_gui.h Mon May 19 15:13:58 2008 +0000 @@ -5,12 +5,12 @@ #ifndef VEHICLE_GUI_H #define VEHICLE_GUI_H -#include "window_gui.h" +#include "sortlist_type.h" +#include "window_type.h" #include "vehicle_type.h" #include "order_type.h" #include "station_type.h" #include "engine_type.h" -#include "vehicle_base.h" void DrawVehicleProfitButton(const Vehicle *v, int x, int y); void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order); @@ -89,21 +89,6 @@ * For ease of use it can be called with both Vehicle pointers and VehicleIDs. */ void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index); -static inline void ChangeVehicleViewWindow(const Vehicle *from_v, VehicleID to_index) -{ - ChangeVehicleViewWindow(from_v->index, to_index); -} - -static inline void ChangeVehicleViewWindow(VehicleID from_index, const Vehicle *to_v) -{ - ChangeVehicleViewWindow(from_index, to_v->index); -} - -static inline void ChangeVehicleViewWindow(const Vehicle *from_v, const Vehicle *to_v) -{ - ChangeVehicleViewWindow(from_v->index, to_v->index); -} - static inline uint GetVehicleListHeight(VehicleType type) { return (type == VEH_TRAIN || type == VEH_ROAD) ? 14 : 24; @@ -148,8 +133,6 @@ extern Sorting _sorting; /* sorter stuff */ -void RebuildVehicleLists(); -void ResortVehicleLists(); void SortVehicleList(VehicleListBase *vl); void BuildVehicleList(VehicleListBase *vl, PlayerID owner, uint16 index, uint16 window_type); diff -r 6c4314786d68 -r 8cbdb511a674 src/viewport.cpp --- a/src/viewport.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/viewport.cpp Mon May 19 15:13:58 2008 +0000 @@ -47,6 +47,7 @@ #include "misc/smallvec.h" #include "window_func.h" #include "tilehighlight_func.h" +#include "window_gui.h" #include "table/sprites.h" #include "table/strings.h" @@ -1536,17 +1537,17 @@ ViewportDrawChk(vp, left, top, right, bottom); } -void DrawWindowViewport(const Window *w) +void Window::DrawViewport() const { DrawPixelInfo *dpi = _cur_dpi; - dpi->left += w->left; - dpi->top += w->top; - - ViewportDraw(w->viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height); - - dpi->left -= w->left; - dpi->top -= w->top; + dpi->left += this->left; + dpi->top += this->top; + + ViewportDraw(this->viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height); + + dpi->left -= this->left; + dpi->top -= this->top; } static inline void ClampViewportToMap(const ViewPort *vp, int &x, int &y) @@ -2660,7 +2661,10 @@ _thd.selend.y = y; } -/** while dragging */ +/** + * Handle the mouse while dragging for placement/resizing. + * @return Boolean whether search for a handler should continue + */ bool VpHandlePlaceSizingDrag() { if (_special_mouse_mode != WSM_SIZING) return true; @@ -2708,12 +2712,18 @@ void SetObjectToPlace(CursorID icon, SpriteID pal, ViewportHighlightMode mode, WindowClass window_class, WindowNumber window_num) { - Window *w; - /* undo clicking on button and drag & drop */ if (_thd.place_mode != VHM_NONE || _special_mouse_mode == WSM_DRAGDROP) { - w = FindWindowById(_thd.window_class, _thd.window_number); - if (w != NULL) w->OnPlaceObjectAbort(); + Window *w = FindWindowById(_thd.window_class, _thd.window_number); + if (w != NULL) { + /* Call the abort function, but set the window class to something + * that will never be used to avoid infinite loops. Setting it to + * the 'next' window class must not be done because recursion into + * this function might in some cases reset the newly set object to + * place or not properly reset the original selection. */ + _thd.window_class = WC_INVALID; + w->OnPlaceObjectAbort(); + } } SetTileSelectSize(1, 1); @@ -2734,10 +2744,12 @@ if (mode == VHM_SPECIAL) // special tools, like tunnels or docks start with presizing mode VpStartPreSizing(); - if ( (int)icon < 0) + if ((int)icon < 0) { SetAnimatedMouseCursor(_animcursors[~icon]); - else + } else { SetMouseCursor(icon, pal); + } + } void ResetObjectToPlace() diff -r 6c4314786d68 -r 8cbdb511a674 src/water_cmd.cpp --- a/src/water_cmd.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/water_cmd.cpp Mon May 19 15:13:58 2008 +0000 @@ -838,20 +838,21 @@ case VEH_TRAIN: if (IsFrontEngine(v)) pass += 4; // driver v->u.rail.crash_anim_pos = 4000; // max 4440, disappear pretty fast + InvalidateWindowClassesData(WC_TRAINS_LIST, 0); break; case VEH_ROAD: if (IsRoadVehFront(v)) pass += 1; // driver v->u.road.crashed_ctr = 2000; // max 2220, disappear pretty fast + InvalidateWindowClassesData(WC_ROADVEH_LIST, 0); break; case VEH_AIRCRAFT: pass += 2; // driver v->u.air.crashed_counter = 9000; // max 10000, disappear pretty fast + InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0); break; } - - RebuildVehicleLists(); } else { return; } @@ -861,7 +862,7 @@ SetDParam(0, pass); AddNewsItem(STR_B006_FLOOD_VEHICLE_DESTROYED, - NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, DNC_NONE, + NS_ACCIDENT_VEHICLE, v->index, 0); CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); @@ -1123,7 +1124,7 @@ switch (GetWaterTileType(tile)) { case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break; case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break; - case WATER_TILE_LOCK: ts = AxisToTrackBits(DiagDirToAxis(GetLockDirection(tile))); break; + case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break; case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break; default: return 0; } diff -r 6c4314786d68 -r 8cbdb511a674 src/widget.cpp --- a/src/widget.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/widget.cpp Mon May 19 15:13:58 2008 +0000 @@ -190,20 +190,20 @@ * Paint all widgets of a window. * @param w Window */ -void DrawWindowWidgets(const Window *w) +void Window::DrawWidgets() const { const DrawPixelInfo* dpi = _cur_dpi; - for (uint i = 0; i < w->widget_count; i++) { - const Widget *wi = &w->widget[i]; - bool clicked = w->IsWidgetLowered(i); + for (uint i = 0; i < this->widget_count; i++) { + const Widget *wi = &this->widget[i]; + bool clicked = this->IsWidgetLowered(i); Rect r; if (dpi->left > (r.right = wi->right) || dpi->left + dpi->width <= (r.left = wi->left) || dpi->top > (r.bottom = wi->bottom) || dpi->top + dpi->height <= (r.top = wi->top) || - w->IsWidgetHidden(i)) { + this->IsWidgetHidden(i)) { continue; } @@ -304,11 +304,11 @@ assert(r.right - r.left == 11); // To ensure the same sizes are used everywhere! /* draw up/down buttons */ - clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_UP); + clicked = ((this->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_UP); DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : FR_NONE); DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, TC_BLACK); - clicked = (((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_DOWN)); + clicked = (((this->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_DOWN)); DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, TC_BLACK); @@ -325,8 +325,8 @@ GfxFillRect(r.left + 7, r.top + 10, r.left + 7, r.bottom - 10, c1); GfxFillRect(r.left + 8, r.top + 10, r.left + 8, r.bottom - 10, c2); - Point pt = HandleScrollbarHittest(&w->vscroll, r.top, r.bottom); - DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_MIDDLE ? FR_LOWERED : FR_NONE); + Point pt = HandleScrollbarHittest(&this->vscroll, r.top, r.bottom); + DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (this->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_MIDDLE ? FR_LOWERED : FR_NONE); break; } @@ -335,11 +335,11 @@ assert(r.right - r.left == 11); // To ensure the same sizes are used everywhere! /* draw up/down buttons */ - clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_UP | WF_SCROLL2)); + clicked = ((this->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_UP | WF_SCROLL2)); DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : FR_NONE); DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, TC_BLACK); - clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_DOWN | WF_SCROLL2)); + clicked = ((this->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_DOWN | WF_SCROLL2)); DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, TC_BLACK); @@ -356,8 +356,8 @@ GfxFillRect(r.left + 7, r.top + 10, r.left + 7, r.bottom - 10, c1); GfxFillRect(r.left + 8, r.top + 10, r.left + 8, r.bottom - 10, c2); - Point pt = HandleScrollbarHittest(&w->vscroll2, r.top, r.bottom); - DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_MIDDLE | WF_SCROLL2) ? FR_LOWERED : FR_NONE); + Point pt = HandleScrollbarHittest(&this->vscroll2, r.top, r.bottom); + DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (this->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_MIDDLE | WF_SCROLL2) ? FR_LOWERED : FR_NONE); break; } @@ -366,11 +366,11 @@ assert(wi->data == 0); assert(r.bottom - r.top == 11); // To ensure the same sizes are used everywhere! - clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL)) == (WF_SCROLL_UP | WF_HSCROLL)); + clicked = ((this->flags4 & (WF_SCROLL_UP | WF_HSCROLL)) == (WF_SCROLL_UP | WF_HSCROLL)); DrawFrameRect(r.left, r.top, r.left + 9, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); DrawSprite(SPR_ARROW_LEFT, PAL_NONE, r.left + 1 + clicked, r.top + 1 + clicked); - clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL)) == (WF_SCROLL_DOWN | WF_HSCROLL)); + clicked = ((this->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL)) == (WF_SCROLL_DOWN | WF_HSCROLL)); DrawFrameRect(r.right - 9, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); DrawSprite(SPR_ARROW_RIGHT, PAL_NONE, r.right - 8 + clicked, r.top + 1 + clicked); @@ -388,8 +388,8 @@ GfxFillRect(r.left + 10, r.top + 8, r.right - 10, r.top + 8, c2); /* draw actual scrollbar */ - Point pt = HandleScrollbarHittest(&w->hscroll, r.left, r.right); - DrawFrameRect(pt.x, r.top, pt.y, r.bottom, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL)) == (WF_SCROLL_MIDDLE | WF_HSCROLL) ? FR_LOWERED : FR_NONE); + Point pt = HandleScrollbarHittest(&this->hscroll, r.left, r.right); + DrawFrameRect(pt.x, r.top, pt.y, r.bottom, wi->color, (this->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL)) == (WF_SCROLL_MIDDLE | WF_HSCROLL) ? FR_LOWERED : FR_NONE); break; } @@ -429,7 +429,7 @@ assert(wi->data == 0); assert(r.right - r.left == 11); // To ensure the same sizes are used everywhere! - clicked = !!(w->flags4 & WF_STICKY); + clicked = !!(this->flags4 & WF_STICKY); DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); DrawSprite((clicked) ? SPR_PIN_UP : SPR_PIN_DOWN, PAL_NONE, r.left + 2 + clicked, r.top + 3 + clicked); break; @@ -438,7 +438,7 @@ assert(wi->data == 0); assert(r.right - r.left == 11); // To ensure the same sizes are used everywhere! - clicked = !!(w->flags4 & WF_SIZING); + clicked = !!(this->flags4 & WF_SIZING); DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE); DrawSprite(SPR_WINDOW_RESIZE, PAL_NONE, r.left + 3 + clicked, r.top + 3 + clicked); break; @@ -457,10 +457,10 @@ case WWT_CAPTION: assert(r.bottom - r.top == 13); // To ensure the same sizes are used everywhere! DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_BORDERONLY); - DrawFrameRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, wi->color, (w->caption_color == 0xFF) ? FR_LOWERED | FR_DARKENED : FR_LOWERED | FR_DARKENED | FR_BORDERONLY); + DrawFrameRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, wi->color, (this->caption_color == 0xFF) ? FR_LOWERED | FR_DARKENED : FR_LOWERED | FR_DARKENED | FR_BORDERONLY); - if (w->caption_color != 0xFF) { - GfxFillRect(r.left + 2, r.top + 2, r.right - 2, r.bottom - 2, _colour_gradient[_player_colors[w->caption_color]][4]); + if (this->caption_color != 0xFF) { + GfxFillRect(r.left + 2, r.top + 2, r.right - 2, r.bottom - 2, _colour_gradient[_player_colors[this->caption_color]][4]); } DrawStringCenteredTruncated(r.left + 2, r.right - 2, r.top + 2, wi->data, 0x84); @@ -489,14 +489,14 @@ } } - if (w->IsWidgetDisabled(i)) { + if (this->IsWidgetDisabled(i)) { GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, _colour_gradient[wi->color & 0xF][2] | (1 << PALETTE_MODIFIER_GREYOUT)); } } - if (w->flags4 & WF_WHITE_BORDER_MASK) { - DrawFrameRect(0, 0, w->width - 1, w->height - 1, 0xF, FR_BORDERONLY); + if (this->flags4 & WF_WHITE_BORDER_MASK) { + DrawFrameRect(0, 0, this->width - 1, this->height - 1, 0xF, FR_BORDERONLY); } } @@ -597,10 +597,10 @@ * @param widget Sort button widget * @param state State of sort button */ -void DrawSortButtonState(const Window *w, int widget, SortButtonState state) +void Window::DrawSortButtonState(int widget, SortButtonState state) const { if (state == SBS_OFF) return; - int offset = w->IsWidgetLowered(widget) ? 1 : 0; - DoDrawString(state == SBS_DOWN ? DOWNARROW : UPARROW, w->widget[widget].right - 11 + offset, w->widget[widget].top + 1 + offset, TC_BLACK); + 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); } diff -r 6c4314786d68 -r 8cbdb511a674 src/widgets/dropdown.cpp --- a/src/widgets/dropdown.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/widgets/dropdown.cpp Mon May 19 15:13:58 2008 +0000 @@ -72,7 +72,7 @@ bool drag_mode; int scrolling; - DropdownWindow(int x, int y, int width, int height, const Widget *widget) : Window(x, y, width, height, NULL, WC_DROPDOWN_MENU, widget) + DropdownWindow(int x, int y, int width, int height, const Widget *widget) : Window(x, y, width, height, WC_DROPDOWN_MENU, widget) { } @@ -87,9 +87,9 @@ DeleteDropDownList(this->list); } - int GetDropDownItem() + bool GetDropDownItem(int &value) { - if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) < 0) return -1; + if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) < 0) return false; int y = _cursor.pos.y - this->top - 2; int width = this->widget[0].right - 3; @@ -105,19 +105,20 @@ int item_height = item->Height(width); if (y < item_height) { - if (item->masked || item->String() == STR_NULL) return -1; - return item->result; + if (item->masked || item->String() == STR_NULL) return false; + value = item->result; + return true; } y -= item_height; } - return -1; + return false; } virtual void OnPaint() { - DrawWindowWidgets(this); + this->DrawWidgets(); int x = 1; int y = 2; @@ -162,8 +163,8 @@ virtual void OnClick(Point pt, int widget) { if (widget != 0) return; - int item = GetDropDownItem(); - if (item >= 0) { + int item; + if (this->GetDropDownItem(item)) { this->click_delay = 4; this->selected_index = item; this->SetDirty(); @@ -197,11 +198,11 @@ } if (this->drag_mode) { - int item = GetDropDownItem(); + int item; if (!_left_button_clicked) { this->drag_mode = false; - if (item < 0) return; + if (!this->GetDropDownItem(item)) return; this->click_delay = 2; } else { if (_cursor.pos.y <= this->top + 2) { @@ -214,7 +215,7 @@ return; } - if (item < 0) return; + if (!this->GetDropDownItem(item)) return; } this->selected_index = item; diff -r 6c4314786d68 -r 8cbdb511a674 src/win32.cpp --- a/src/win32.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/win32.cpp Mon May 19 15:13:58 2008 +0000 @@ -61,7 +61,7 @@ for (;;) { FARPROC p; - while (*dll++ != '\0'); + while (*dll++ != '\0') { /* Nothing */ } if (*dll == '\0') break; #if defined(WINCE) p = GetProcAddress(lib, MB_TO_WIDE(dll)); @@ -796,7 +796,7 @@ fios->mtime = 0; snprintf(fios->name, lengthof(fios->name), "%c:", s[0] & 0xFF); ttd_strlcpy(fios->title, fios->name, lengthof(fios->title)); - while (*s++ != '\0'); + while (*s++ != '\0') { /* Nothing */ } } #endif } diff -r 6c4314786d68 -r 8cbdb511a674 src/window.cpp --- a/src/window.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/window.cpp Mon May 19 15:13:58 2008 +0000 @@ -48,221 +48,6 @@ byte _special_mouse_mode; -/** - * Call the window event handler for handling event \a e. - * This is a temporary helper functions that will be removed - * once all windows that still rely on WindowEvent and - * WindowEventCodes have been rewritten to use the 'OnXXX' - * event handlers. - * @param e Window event to handle - */ -void Window::HandleWindowEvent(WindowEvent *e) -{ - if (wndproc != NULL) wndproc(this, e); -} - -void Window::OnPaint() -{ - WindowEvent e; - e.event = WE_PAINT; - this->HandleWindowEvent(&e); -} - -bool Window::OnKeyPress(uint16 key, uint16 keycode) -{ - WindowEvent e; - e.event = WE_KEYPRESS; - e.we.keypress.key = key; - e.we.keypress.keycode = keycode; - e.we.keypress.cont = true; - this->HandleWindowEvent(&e); - - return e.we.keypress.cont; -} - -bool Window::OnCTRLStateChange() -{ - WindowEvent e; - e.event = WE_CTRL_CHANGED; - e.we.ctrl.cont = true; - this->HandleWindowEvent(&e); - - return e.we.ctrl.cont; -} - -void Window::OnClick(Point pt, int widget) -{ - WindowEvent e; - e.event = WE_CLICK; - e.we.click.pt = pt; - e.we.click.widget = widget; - this->HandleWindowEvent(&e); -} - -void Window::OnDoubleClick(Point pt, int widget) -{ - WindowEvent e; - e.event = WE_DOUBLE_CLICK; - e.we.click.pt = pt; - e.we.click.widget = widget; - this->HandleWindowEvent(&e); -} - -void Window::OnRightClick(Point pt, int widget) -{ - WindowEvent e; - e.event = WE_RCLICK; - e.we.click.pt = pt; - e.we.click.widget = widget; - this->HandleWindowEvent(&e); -} - -void Window::OnDragDrop(Point pt, int widget) -{ - WindowEvent e; - e.event = WE_DRAGDROP; - e.we.click.pt = pt; - e.we.click.widget = widget; - this->HandleWindowEvent(&e); -} - -void Window::OnScroll(Point delta) -{ - WindowEvent e; - e.event = WE_SCROLL; - e.we.scroll.delta = delta; - this->HandleWindowEvent(&e); -} - -void Window::OnMouseOver(Point pt, int widget) -{ - WindowEvent e; - e.event = WE_MOUSEOVER; - e.we.click.pt = pt; - e.we.click.widget = widget; - this->HandleWindowEvent(&e); -} - -void Window::OnMouseWheel(int wheel) -{ - WindowEvent e; - e.event = WE_MOUSEWHEEL; - e.we.wheel.wheel = wheel; - this->HandleWindowEvent(&e); -} - -void Window::OnMouseLoop() -{ - WindowEvent e; - e.event = WE_MOUSELOOP; - this->HandleWindowEvent(&e); -} - -void Window::OnTick() -{ - WindowEvent e; - e.event = WE_TICK; - this->HandleWindowEvent(&e); -} - -void Window::OnHundredthTick() -{ - WindowEvent e; - e.event = WE_100_TICKS; - this->HandleWindowEvent(&e); -} - -void Window::OnTimeout() -{ - WindowEvent e; - e.event = WE_TIMEOUT; - this->HandleWindowEvent(&e); -} - -void Window::OnResize(Point new_size, Point delta) -{ - WindowEvent e; - e.event = WE_RESIZE; - e.we.sizing.size = new_size; - e.we.sizing.diff = delta; - this->HandleWindowEvent(&e); -} - -void Window::OnDropdownSelect(int widget, int index) -{ - WindowEvent e; - e.event = WE_DROPDOWN_SELECT; - e.we.dropdown.button = widget; - e.we.dropdown.index = index; - this->HandleWindowEvent(&e); -} - -void Window::OnQueryTextFinished(char *str) -{ - WindowEvent e; - e.event = WE_ON_EDIT_TEXT; - e.we.edittext.str = str; - this->HandleWindowEvent(&e); -} - -void Window::OnInvalidateData(int data) -{ - WindowEvent e; - e.event = WE_INVALIDATE_DATA; - e.we.invalidate.data = data; - this->HandleWindowEvent(&e); -} - -void Window::OnPlaceObject(Point pt, TileIndex tile) -{ - WindowEvent e; - e.event = WE_PLACE_OBJ; - e.we.place.pt = pt; - e.we.place.tile = tile; - this->HandleWindowEvent(&e); -} - -void Window::OnPlaceObjectAbort() -{ - WindowEvent e; - e.event = WE_ABORT_PLACE_OBJ; - this->HandleWindowEvent(&e); -} - - -void Window::OnPlaceDrag(ViewportPlaceMethod select_method, byte select_proc, Point pt) -{ - WindowEvent e; - e.event = WE_PLACE_DRAG; - e.we.place.select_method = select_method; - e.we.place.select_proc = select_proc; - e.we.place.pt = pt; - this->HandleWindowEvent(&e); -} - -void Window::OnPlaceMouseUp(ViewportPlaceMethod select_method, byte select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) -{ - WindowEvent e; - e.event = WE_PLACE_MOUSEUP; - e.we.place.select_method = select_method; - e.we.place.select_proc = select_proc; - e.we.place.pt = pt; - e.we.place.tile = end_tile; - e.we.place.starttile = start_tile; - this->HandleWindowEvent(&e); -} - -void Window::OnPlacePresize(Point pt, TileIndex tile) -{ - WindowEvent e; - e.event = WE_PLACE_PRESIZE; - e.we.place.pt = pt; - e.we.place.tile = tile; - this->HandleWindowEvent(&e); -} - - - void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...) { va_list wdg_list; @@ -626,12 +411,13 @@ memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz); _last_z_window--; - /* Delete any children a window might have in a head-recursive manner */ - delete FindChildWindow(this); + /* Delete all children a window might have in a head-recursive manner */ + Window *child = FindChildWindow(this); + while (child != NULL) { + delete child; + child = FindChildWindow(this); + } - WindowEvent e; - e.event = WE_DESTROY; - this->HandleWindowEvent(&e); if (this->viewport != NULL) DeleteWindowViewport(this); this->SetDirty(); @@ -844,11 +630,6 @@ NOT_REACHED(); } -bool IsWindowOfPrototype(const Window *w, const Widget *widget) -{ - return (w->original_widget == widget); -} - /** * Assign widgets to a new window by initialising its widget pointers, and by * copying the widget array \a widget to \c w->widget to allow for resizable @@ -857,13 +638,10 @@ * @param widget pointer of widget array to fill the window with * * @post \c w->widget points to allocated memory and contains the copied widget array except for the terminating widget, - * \c w->original_widget points to the original widgets, * \c w->widget_count contains number of widgets in the allocated memory. */ static void AssignWidgetToWindow(Window *w, const Widget *widget) { - w->original_widget = widget; - if (widget != NULL) { uint index = 1; @@ -887,14 +665,13 @@ * @param y offset in pixels from the top of the screen * @param min_width minimum width in pixels of the window * @param min_height minimum height in pixels of the window - * @param *proc see WindowProc function to call when any messages/updates happen to the window * @param cls see WindowClass class of the window, used for identification and grouping * @param *widget see Widget pointer to the window layout and various elements * @param window_number number being assigned to the new window - * @param data the data to be given during the WE_CREATE message - * @return Window pointer of the newly created window */ + * @return Window pointer of the newly created window + */ void Window::Initialize(int x, int y, int min_width, int min_height, - WindowProc *proc, WindowClass cls, const Widget *widget, int window_number, void *data) + WindowClass cls, const Widget *widget, int window_number) { /* We have run out of windows, close one and use that as the place for our new one */ if (_last_z_window == endof(_z_windows)) { @@ -911,7 +688,6 @@ this->top = y; this->width = min_width; this->height = min_height; - this->wndproc = proc; AssignWidgetToWindow(this, widget); this->resize.width = min_width; this->resize.height = min_height; @@ -940,11 +716,6 @@ *wz = this; _last_z_window++; - - WindowEvent e; - e.event = WE_CREATE; - e.we.create.data = data; - this->HandleWindowEvent(&e); } /** @@ -1020,16 +791,13 @@ * @param y offset in pixels from the top of the screen * @param width width in pixels of the window * @param height height in pixels of the window - * @param *proc see WindowProc function to call when any messages/updates happen to the window * @param cls see WindowClass class of the window, used for identification and grouping * @param *widget see Widget pointer to the window layout and various elements * @return Window pointer of the newly created window */ -Window::Window(int x, int y, int width, int height, WindowProc *proc, WindowClass cls, const Widget *widget, void *data) +Window::Window(int x, int y, int width, int height, WindowClass cls, const Widget *widget) { - this->Initialize(x, y, width, height, proc, cls, widget, 0, data); - - if (proc != NULL) this->FindWindowPlacementAndResize(width, height); + this->Initialize(x, y, width, height, cls, widget, 0); } @@ -1215,17 +983,14 @@ * * @param *desc The pointer to the WindowDesc to be created * @param window_number the window number of the new window - * @param data arbitrary data that is send with the WE_CREATE message * * @return Window pointer of the newly created window */ -Window::Window(const WindowDesc *desc, void *data, WindowNumber window_number) +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->proc, desc->cls, desc->widgets, window_number, data); + this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->cls, desc->widgets, window_number); this->desc_flags = desc->flags; - - if (desc->proc != NULL) this->FindWindowPlacementAndResize(desc->default_width, desc->default_height); } /** Do a search for a window at specific coordinates. For this we start @@ -1860,8 +1625,7 @@ w->window_class != WC_COMPANY_PASSWORD_WINDOW) { continue; } - ; - if (!w->OnKeyPress(key, keycode)) return; + if (w->OnKeyPress(key, keycode) == Window::ES_HANDLED) return; } Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); @@ -1877,7 +1641,7 @@ /* Call the event, start with the uppermost window. */ for (Window* const *wz = _last_z_window; wz != _z_windows;) { Window *w = *--wz; - if (!w->OnCTRLStateChange()) break; + if (w->OnCTRLStateChange() == Window::ES_HANDLED) return; } } @@ -1970,7 +1734,7 @@ if (mousewheel != 0) { if (_patches.scrollwheel_scrolling == 0) { - /* Send WE_MOUSEWHEEL event to window */ + /* Send mousewheel event to window */ w->OnMouseWheel(mousewheel); } @@ -2400,3 +2164,13 @@ w->top = top; } } + +/** Destructor of the base class PickerWindowBase + * Main utility is to stop the base Window destructor from triggering + * a free while the child will already be free, in this case by the ResetObjectToPlace(). + */ +PickerWindowBase::~PickerWindowBase() +{ + this->window_class = WC_INVALID; // stop the ancestor from freeing the already (to be) child + ResetObjectToPlace(); +} diff -r 6c4314786d68 -r 8cbdb511a674 src/window_gui.h --- a/src/window_gui.h Mon May 19 14:14:33 2008 +0000 +++ b/src/window_gui.h Mon May 19 15:13:58 2008 +0000 @@ -20,8 +20,6 @@ */ static const int MAX_NUMBER_OF_WINDOWS = 25; -typedef void WindowProc(Window *w, WindowEvent *e); - /* How the resize system works: First, you need to add a WWT_RESIZEBOX to the widgets, and you need to add the flag WDF_RESIZABLE to the window. Now the window is ready @@ -106,109 +104,6 @@ void DrawFrameRect(int left, int top, int right, int bottom, int color, FrameFlags flags); /** - * Available window events - */ -enum WindowEventCodes { - WE_CREATE, ///< Initialize the Window - WE_DESTROY, ///< Prepare for deletion of the window - WE_PAINT, ///< Repaint the window contents - WE_KEYPRESS, ///< Key pressed - WE_CLICK, ///< Left mouse button click - WE_DOUBLE_CLICK, ///< Left mouse button double click - WE_RCLICK, ///< Right mouse button click - WE_MOUSEOVER, - WE_MOUSEWHEEL, - WE_MOUSELOOP, ///< Event for each mouse event in the game (at least once every game tick) - WE_TICK, ///< Regularly occurring event (every game tick) - WE_100_TICKS, ///< Regularly occurring event (every 100 game ticks, approximatelly 3 seconds) - WE_TIMEOUT, - WE_PLACE_OBJ, - WE_ABORT_PLACE_OBJ, - WE_ON_EDIT_TEXT, - WE_DRAGDROP, - WE_PLACE_DRAG, - WE_PLACE_MOUSEUP, - WE_PLACE_PRESIZE, - WE_DROPDOWN_SELECT, - WE_RESIZE, ///< Request to resize the window, @see WindowEvent.we.resize - WE_SCROLL, - WE_INVALIDATE_DATA, ///< Notification that data displayed by the window is obsolete - WE_CTRL_CHANGED, ///< CTRL key has changed state -}; - -/** - * Data structures for additional data associated with a window event - * @see WindowEventCodes - */ -struct WindowEvent { - byte event; - union { - struct { - void *data; - } create; - - struct { - Point pt; - int widget; - } click; - - struct { - Point pt; - TileIndex tile; - TileIndex starttile; - ViewportPlaceMethod select_method; - byte select_proc; - } place; - - struct { - Point pt; - int widget; - } dragdrop; - - struct { - Point size; - Point diff; - } sizing; - - struct { - char *str; - } edittext; - - struct { - int button; - int index; - } dropdown; - - struct { - Point pt; - int widget; - } mouseover; - - struct { - bool cont; ///< continue the search? (default true) - uint16 key; ///< 16-bit Unicode value of the key - uint16 keycode; ///< untranslated key (including shift-state) - } keypress; - - struct { - int data; - } invalidate; - - struct { - Point delta; ///< delta position against position of last call - } scroll; - - struct { - int wheel; ///< how much was 'wheel'd' - } wheel; - - struct { - bool cont; ///< continue the search? (default true) - } ctrl; - } we; -}; - -/** * High level window description */ struct WindowDesc { @@ -222,7 +117,6 @@ 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 - WindowProc *proc; ///< Window event handler function for the window }; /** @@ -249,8 +143,6 @@ WDP_ALIGN_TBL = -4, ///< Align the left side of the window with the left side of the main toolbar }; -#define WP(ptr, str) (*(str*)(ptr)->custom) - /** * Scrollbar data structure */ @@ -270,6 +162,12 @@ uint step_height; ///< Step-size of height resize changes }; +enum SortButtonState { + SBS_OFF, + SBS_DOWN, + SBS_UP, +}; + /** * Data structure for a window viewport */ @@ -281,23 +179,24 @@ int32 dest_scrollpos_y; }; - /** +/** * Data structure for an opened window */ struct Window : ZeroedMemoryAllocator { -private: - WindowProc *wndproc; ///< Event handler function for the window. Do not use directly, call HandleWindowEvent() instead. - void HandleWindowEvent(WindowEvent *e); + enum EventState { + ES_HANDLED, + ES_NOT_HANDLED, + }; protected: void Initialize(int x, int y, int min_width, int min_height, - WindowProc *proc, WindowClass cls, const Widget *widget, int window_number, void *data); + WindowClass cls, const Widget *widget, int window_number); void FindWindowPlacementAndResize(int def_width, int def_height); void FindWindowPlacementAndResize(const WindowDesc *desc); public: - Window(int x, int y, int width, int height, WindowProc *proc, WindowClass cls, const Widget *widget, void *data = NULL); - Window(const WindowDesc *desc, void *data = NULL, WindowNumber number = 0); + Window(int x, int y, int width, int height, WindowClass cls, const Widget *widget); + Window(const WindowDesc *desc, WindowNumber number = 0); virtual ~Window(); @@ -318,13 +217,11 @@ byte caption_color; ///< Background color of the window caption, contains PlayerID ViewportData *viewport; ///< Pointer to viewport data, if present - const Widget *original_widget; ///< Original widget layout, copied from WindowDesc Widget *widget; ///< Widgets of the window uint widget_count; ///< Number of widgets of the window uint32 desc_flags; ///< Window/widgets default flags setting, @see WindowDefaultFlag Window *parent; ///< Parent window - byte custom[WINDOW_CUSTOM_SIZE]; ///< Additional data depending on window type void HandleButtonClick(byte widget); @@ -348,6 +245,10 @@ void CDECL SetWidgetsLoweredState(bool lowered_stat, int widgets, ...); void InvalidateWidget(byte widget_index) const; + void DrawWidgets() const; + void DrawViewport() const; + void DrawSortButtonState(int widget, SortButtonState state) const; + void SetDirty() const; /*** Event handling ***/ @@ -355,24 +256,24 @@ /** * This window is currently being repainted. */ - virtual void OnPaint(); + virtual void OnPaint() {} /** * A key has been pressed. * @param key the Unicode value of the key. * @param keycode the untranslated key code including shift state. - * @return true if the key press has been handled and no other + * @return ES_HANDLED if the key press has been handled and no other * window should receive the event. */ - virtual bool OnKeyPress(uint16 key, uint16 keycode); + virtual EventState OnKeyPress(uint16 key, uint16 keycode) { return ES_NOT_HANDLED; } /** * The state of the control key has changed - * @return true if the change has been handled and no other + * @return ES_HANDLED if the change has been handled and no other * window should receive the event. */ - virtual bool OnCTRLStateChange(); + virtual EventState OnCTRLStateChange() { return ES_NOT_HANDLED; } /** @@ -380,34 +281,34 @@ * @param pt the point inside the window that has been clicked. * @param widget the clicked widget. */ - virtual void OnClick(Point pt, int widget); + virtual void OnClick(Point pt, int widget) {} /** * A double click with the left mouse button has been made on the window. * @param pt the point inside the window that has been clicked. * @param widget the clicked widget. */ - virtual void OnDoubleClick(Point pt, int widget); + virtual void OnDoubleClick(Point pt, int widget) {} /** * A click with the right mouse button has been made on the window. * @param pt the point inside the window that has been clicked. * @param widget the clicked widget. */ - virtual void OnRightClick(Point pt, int widget); + virtual void OnRightClick(Point pt, int widget) {} /** * A dragged 'object' has been released. * @param pt the point inside the window where the release took place. * @param widget the widget where the release took place. */ - virtual void OnDragDrop(Point pt, int widget); + virtual void OnDragDrop(Point pt, int widget) {} /** * Handle the request for (viewport) scrolling. * @param delta the amount the viewport must be scrolled. */ - virtual void OnScroll(Point delta); + virtual void OnScroll(Point delta) {} /** * The mouse is currently moving over the window or has just moved outside @@ -415,34 +316,34 @@ * @param pt the point inside the window that the mouse hovers over. * @param widget the widget the mouse hovers over. */ - virtual void OnMouseOver(Point pt, int widget); + virtual void OnMouseOver(Point pt, int widget) {} /** * The mouse wheel has been turned. * @param wheel the amount of movement of the mouse wheel. */ - virtual void OnMouseWheel(int wheel); + virtual void OnMouseWheel(int wheel) {} /** * Called for every mouse loop run, which is at least once per (game) tick. */ - virtual void OnMouseLoop(); + virtual void OnMouseLoop() {} /** * Called once per (game) tick. */ - virtual void OnTick(); + virtual void OnTick() {} /** * Called once every 100 (game) ticks. */ - virtual void OnHundredthTick(); + virtual void OnHundredthTick() {} /** * Called when this window's timeout has been reached. */ - virtual void OnTimeout(); + virtual void OnTimeout() {} /** @@ -450,27 +351,27 @@ * @param new_size the new size of the window. * @param delta the amount of which the window size changed. */ - virtual void OnResize(Point new_size, Point delta); + virtual void OnResize(Point new_size, Point delta) {} /** * A dropdown option associated to this window has been selected. * @param widget the widget (button) that the dropdown is associated with. * @param index the element in the dropdown that is selected. */ - virtual void OnDropdownSelect(int widget, int index); + virtual void OnDropdownSelect(int widget, int index) {} /** * The query window opened from this window has closed. * @param str the new value of the string or NULL if the window * was cancelled. */ - virtual void OnQueryTextFinished(char *str); + virtual void OnQueryTextFinished(char *str) {} /** * Some data on this window has become invalid. * @param data information about the changed data. */ - virtual void OnInvalidateData(int data = 0); + virtual void OnInvalidateData(int data = 0) {} /** @@ -479,12 +380,12 @@ * @param pt the exact point on the map that has been clicked. * @param tile the tile on the map that has been clicked. */ - virtual void OnPlaceObject(Point pt, TileIndex tile); + virtual void OnPlaceObject(Point pt, TileIndex tile) {} /** * The user cancelled a tile highlight mode that has been set. */ - virtual void OnPlaceObjectAbort(); + virtual void OnPlaceObjectAbort() {} /** @@ -494,7 +395,7 @@ * @param select_proc what will be created when the drag is over. * @param pt the exact point on the map where the mouse is. */ - virtual void OnPlaceDrag(ViewportPlaceMethod select_method, byte select_proc, Point pt); + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) {} /** * The user has dragged over the map when the tile highlight mode @@ -505,7 +406,7 @@ * @param start_tile the begin tile of the drag. * @param end_tile the end tile of the drag. */ - virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, byte select_proc, Point pt, TileIndex start_tile, TileIndex end_tile); + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) {} /** * The user moves over the map when a tile highlight mode has been set @@ -514,51 +415,20 @@ * @param pt the exact point on the map where the mouse is. * @param tile the tile on the map where the mouse is. */ - virtual void OnPlacePresize(Point pt, TileIndex tile); + virtual void OnPlacePresize(Point pt, TileIndex tile) {} /*** End of the event handling ***/ }; -struct menu_d { - byte item_count; ///< follow_vehicle - byte sel_index; ///< scrollpos_x - byte main_button; ///< scrollpos_y - byte action_id; - StringID string_id; ///< unk30 - uint16 checked_items; ///< unk32 - byte disabled_items; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(menu_d)); - -struct def_d { - int16 data_1, data_2, data_3; - int16 data_4, data_5; - bool close; - byte byte_1; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(def_d)); +/** + * Data structure for a window opened from a toolbar + */ +class PickerWindowBase : public Window { -enum SortListFlags { - VL_NONE = 0, ///< no sort - VL_DESC = 1 << 0, ///< sort descending or ascending - VL_RESORT = 1 << 1, ///< instruct the code to resort the list in the next loop - VL_REBUILD = 1 << 2, ///< create sort-listing to use for qsort and friends - VL_END = 1 << 3, -}; -DECLARE_ENUM_AS_BIT_SET(SortListFlags); +public: + PickerWindowBase(const WindowDesc *desc) : Window(desc) {}; // nothing special yet, just propagation -struct Listing { - bool order; ///< Ascending/descending - byte criteria; ///< Sorting criteria -}; - -template -struct GUIList { - T* sort_list; ///< The items to sort. - SortListFlags flags; ///< used to control sorting/resorting/etc. - uint16 list_length; ///< length of the list being sorted - uint16 resort_timer; ///< resort list after a given amount of ticks if set - byte sort_type; ///< what criteria to sort on + virtual ~PickerWindowBase(); }; /****************** THESE ARE NOT WIDGET TYPES!!!!! *******************/ @@ -632,8 +502,6 @@ Window *BringWindowToFrontById(WindowClass cls, WindowNumber number); Window *FindWindowFromPt(int x, int y); -bool IsWindowOfPrototype(const Window *w, const Widget *widget); - /** * Open a new window. * @param *desc The pointer to the WindowDesc to be created @@ -642,14 +510,12 @@ * @return see Window pointer of the newly created window */ template -Wcls *AllocateWindowDescFront(const WindowDesc *desc, int window_number, void *data = NULL) +Wcls *AllocateWindowDescFront(const WindowDesc *desc, int window_number) { if (BringWindowToFrontById(desc->cls, window_number)) return NULL; - return new Wcls(desc, data, window_number); + return new Wcls(desc, window_number); } -void DrawWindowViewport(const Window *w); - void RelocateAllWindows(int neww, int newh); /* misc_gui.cpp */ @@ -661,17 +527,6 @@ /* widget.cpp */ int GetWidgetFromPos(const Window *w, int x, int y); -void DrawWindowWidgets(const Window *w); - -enum SortButtonState { - SBS_OFF, - SBS_DOWN, - SBS_UP, -}; - -void DrawSortButtonState(const Window *w, int widget, SortButtonState state); - - /* window.cpp */ extern Window *_z_windows[]; diff -r 6c4314786d68 -r 8cbdb511a674 src/window_type.h --- a/src/window_type.h Mon May 19 14:14:33 2008 +0000 +++ b/src/window_type.h Mon May 19 15:13:58 2008 +0000 @@ -94,17 +94,11 @@ WC_BUILD_SIGNAL, WC_COMPANY_PASSWORD_WINDOW, WC_OSK, + + WC_INVALID = 0xFFFF }; struct Window; -struct WindowEvent; typedef int32 WindowNumber; -/** - * You cannot 100% reliably calculate the biggest custom struct as - * the number of pointers in it and alignment will have a huge impact. - * 96 is the largest window-size for 64-bit machines currently. - */ -#define WINDOW_CUSTOM_SIZE 96 - #endif /* WINDOW_TYPE_H */ diff -r 6c4314786d68 -r 8cbdb511a674 src/yapf/yapf_road.cpp --- a/src/yapf/yapf_road.cpp Mon May 19 14:14:33 2008 +0000 +++ b/src/yapf/yapf_road.cpp Mon May 19 15:13:58 2008 +0000 @@ -88,7 +88,7 @@ if (v->current_order.IsType(OT_GOTO_STATION) && tile == v->dest_tile) break; // stop if we have just entered the depot - if (IsRoadDepotTile(tile) && trackdir == DiagdirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) { + if (IsRoadDepotTile(tile) && trackdir == DiagDirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) { // next time we will reverse and leave the depot break; } @@ -264,7 +264,7 @@ // handle special case - when next tile is destination tile if (tile == v->dest_tile) { // choose diagonal trackdir reachable from enterdir - return (Trackdir)DiagdirToDiagTrackdir(enterdir); + return DiagDirToDiagTrackdir(enterdir); } // our source tile will be the next vehicle tile (should be the given one) TileIndex src_tile = tile;