tron@2186: /* $Id$ */ tron@2186: rubidium@10429: /** @file misc_gui.cpp GUIs for a number of misc windows. */ belugas@6527: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" truelight@4300: #include "heightmap.h" tron@1299: #include "debug.h" maedhros@6949: #include "landscape.h" Darkvater@5352: #include "newgrf.h" rubidium@8246: #include "newgrf_text.h" tron@2162: #include "saveload.h" rubidium@8604: #include "tile_map.h" truelight@0: #include "gui.h" rubidium@8603: #include "window_gui.h" rubidium@8603: #include "station_gui.h" rubidium@8603: #include "textbuf_gui.h" rubidium@8720: #include "viewport_func.h" rubidium@8720: #include "gfx_func.h" rubidium@9281: #include "station_func.h" rubidium@8612: #include "command_func.h" rubidium@8750: #include "player_func.h" rubidium@8750: #include "player_base.h" truelight@0: #include "town.h" rubidium@5720: #include "network/network.h" tron@2159: #include "variables.h" rubidium@10225: #include "cheat_func.h" celestar@3890: #include "train.h" truelight@4300: #include "tgp.h" peter1138@6417: #include "cargotype.h" rubidium@6516: #include "player_face.h" rubidium@8610: #include "strings_func.h" rubidium@7425: #include "fileio.h" Darkvater@4223: #include "fios.h" rubidium@8615: #include "tile_cmd.h" rubidium@8619: #include "zoom_func.h" rubidium@8627: #include "functions.h" rubidium@8627: #include "window_func.h" rubidium@8636: #include "date_func.h" rubidium@8653: #include "sound_func.h" rubidium@8710: #include "string_func.h" rubidium@8750: #include "player_gui.h" rubidium@8766: #include "settings_type.h" rubidium@9283: #include "newgrf_cargo.h" smatz@9305: #include "rail_gui.h" rubidium@10445: #include "tilehighlight_func.h" rubidium@10499: #include "querystring_gui.h" rubidium@8615: rubidium@8760: #include "table/sprites.h" rubidium@8760: #include "table/strings.h" rubidium@8760: Darkvater@4223: /* Variables to display file lists */ belugas@8645: SaveLoadDialogMode _saveload_mode; truelight@0: truelight@4300: darkvater@1596: static bool _fios_path_changed; tron@1594: static bool _savegame_sort_dirty; tron@1594: truelight@0: static const Widget _land_info_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 279, 0, 13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS}, smatz@11045: { WWT_PANEL, RESIZE_BOTTOM, 14, 0, 279, 14, 99, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _land_info_desc = { smatz@11045: WDP_AUTO, WDP_AUTO, 280, 100, 280, 100, rubidium@6144: WC_LAND_INFO, WC_NONE, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, truelight@0: _land_info_widgets, truelight@0: }; truelight@0: rubidium@10475: class LandInfoWindow : public Window { rubidium@10475: enum { frosch@10662: LAND_INFO_CENTERED_LINES = 9, ///< Up to 9 centered lines frosch@10662: LAND_INFO_MULTICENTER_LINE = LAND_INFO_CENTERED_LINES, ///< One multicenter line frosch@10662: LAND_INFO_LINE_END, frosch@10662: rubidium@10475: LAND_INFO_LINE_BUFF_SIZE = 512, rubidium@10475: }; truelight@193: rubidium@10475: public: frosch@10662: char landinfo_data[LAND_INFO_LINE_END][LAND_INFO_LINE_BUFF_SIZE]; truelight@193: smatz@10490: virtual void OnPaint() rubidium@10475: { rubidium@10595: this->DrawWidgets(); rubidium@5834: smatz@11045: uint y = 21; frosch@10662: for (uint i = 0; i < LAND_INFO_CENTERED_LINES; i++) { smatz@11045: if (StrEmpty(this->landinfo_data[i])) break; frosch@10662: frosch@10662: DoDrawStringCentered(140, y, this->landinfo_data[i], i == 0 ? TC_LIGHT_BLUE : TC_FROMSTRING); smatz@11045: y += i == 0 ? 16 : 12; frosch@10662: } frosch@10662: smatz@11045: y += 6; frosch@10662: rubidium@11157: if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) { rubidium@11157: SetDParamStr(0, this->landinfo_data[LAND_INFO_MULTICENTER_LINE]); rubidium@11157: DrawStringMultiCenter(140, y, STR_JUST_RAW_STRING, this->width - 4); rubidium@11157: } rubidium@5834: } rubidium@5834: rubidium@10475: LandInfoWindow(TileIndex tile) : Window(&_land_info_desc) { rubidium@11161: Player *p = GetPlayer(IsValidPlayerID(_local_player) ? _local_player : PLAYER_FIRST); rubidium@10775: Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority); rubidium@5834: rubidium@10475: Money old_money = p->player_money; rubidium@10475: p->player_money = INT64_MAX; rubidium@10475: CommandCost costclear = DoCommand(tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR); rubidium@10475: p->player_money = old_money; rubidium@5834: rubidium@10475: /* Because build_date is not set yet in every TileDesc, we make sure it is empty */ rubidium@10475: TileDesc td; rubidium@10475: AcceptedCargo ac; rubidium@10475: rubidium@10475: td.build_date = 0; frosch@10662: frosch@10662: /* Most tiles have only one owner, but frosch@10662: * - drivethrough roadstops can be build on town owned roads (up to 2 owners) and frosch@10662: * - roads can have up to four owners (railroad, road, tram, 3rd-roadtype "highway"). frosch@10662: */ frosch@10662: td.owner_type[0] = STR_01A7_OWNER; // At least one owner is displayed, though it might be "N/A". frosch@10662: td.owner_type[1] = STR_NULL; // STR_NULL results in skipping the owner frosch@10662: td.owner_type[2] = STR_NULL; frosch@10662: td.owner_type[3] = STR_NULL; frosch@10662: td.owner[0] = OWNER_NONE; frosch@10662: td.owner[1] = OWNER_NONE; frosch@10662: td.owner[2] = OWNER_NONE; frosch@10662: td.owner[3] = OWNER_NONE; frosch@10662: rubidium@10475: GetAcceptedCargo(tile, ac); rubidium@10475: GetTileDesc(tile, &td); rubidium@10475: frosch@10662: uint line_nr = 0; rubidium@10475: frosch@10662: /* Tiletype */ frosch@10662: SetDParam(0, td.dparam[0]); frosch@10662: GetString(this->landinfo_data[line_nr], td.str, lastof(this->landinfo_data[line_nr])); frosch@10662: line_nr++; rubidium@10475: frosch@10662: /* Up to four owners */ frosch@10662: for (uint i = 0; i < 4; i++) { frosch@10662: if (td.owner_type[i] == STR_NULL) continue; frosch@10662: frosch@10662: SetDParam(0, STR_01A6_N_A); frosch@10662: if (td.owner[i] != OWNER_NONE && td.owner[i] != OWNER_WATER) GetNameOfOwner(td.owner[i], tile); frosch@10662: GetString(this->landinfo_data[line_nr], td.owner_type[i], lastof(this->landinfo_data[line_nr])); frosch@10662: line_nr++; frosch@10662: } frosch@10662: frosch@10662: /* Cost to clear */ rubidium@10475: StringID str = STR_01A4_COST_TO_CLEAR_N_A; rubidium@10475: if (CmdSucceeded(costclear)) { rubidium@10475: SetDParam(0, costclear.GetCost()); rubidium@10475: str = STR_01A5_COST_TO_CLEAR; rubidium@10475: } frosch@10662: GetString(this->landinfo_data[line_nr], str, lastof(this->landinfo_data[line_nr])); frosch@10662: line_nr++; rubidium@10475: frosch@10662: /* Location */ rubidium@11157: char tmp[16]; rubidium@11157: snprintf(tmp, lengthof(tmp), "0x%.4X", tile); rubidium@10475: SetDParam(0, TileX(tile)); rubidium@10475: SetDParam(1, TileY(tile)); rubidium@10475: SetDParam(2, TileHeight(tile)); rubidium@11157: SetDParamStr(3, tmp); frosch@10662: GetString(this->landinfo_data[line_nr], STR_LANDINFO_COORDS, lastof(this->landinfo_data[line_nr])); frosch@10662: line_nr++; rubidium@10475: frosch@10662: /* Local authority */ rubidium@10475: SetDParam(0, STR_01A9_NONE); rubidium@10475: if (t != NULL && t->IsValid()) { rubidium@10475: SetDParam(0, STR_TOWN); rubidium@10475: SetDParam(1, t->index); rubidium@10475: } frosch@10662: GetString(this->landinfo_data[line_nr], STR_01A8_LOCAL_AUTHORITY, lastof(this->landinfo_data[line_nr])); frosch@10662: line_nr++; rubidium@10475: frosch@10662: /* Build date */ frosch@10662: if (td.build_date != 0) { frosch@10662: SetDParam(0, td.build_date); frosch@10662: GetString(this->landinfo_data[line_nr], STR_BUILD_DATE, lastof(this->landinfo_data[line_nr])); frosch@10662: line_nr++; frosch@10662: } frosch@10662: smatz@11045: /* Mark last line empty */ smatz@11045: this->landinfo_data[line_nr][0] = '\0'; frosch@10662: frosch@10662: /* Cargo acceptance is displayed in a extra multiline */ frosch@10662: char *strp = GetString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE], STR_01CE_CARGO_ACCEPTED, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])); rubidium@10475: bool found = false; rubidium@10475: rubidium@10475: for (CargoID i = 0; i < NUM_CARGO; ++i) { rubidium@10475: if (ac[i] > 0) { rubidium@10475: /* Add a comma between each item. */ rubidium@10475: if (found) { rubidium@10475: *strp++ = ','; rubidium@10475: *strp++ = ' '; rubidium@10475: } rubidium@10475: found = true; rubidium@10475: rubidium@10475: /* If the accepted value is less than 8, show it in 1/8:ths */ rubidium@10475: if (ac[i] < 8) { rubidium@10475: SetDParam(0, ac[i]); rubidium@10475: SetDParam(1, GetCargo(i)->name); frosch@10662: strp = GetString(strp, STR_01D1_8, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])); rubidium@10475: } else { frosch@10662: strp = GetString(strp, GetCargo(i)->name, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])); rubidium@10475: } rubidium@5834: } rubidium@5834: } frosch@10662: if (!found) this->landinfo_data[LAND_INFO_MULTICENTER_LINE][0] = '\0'; truelight@193: smatz@11045: if (found) line_nr += 2; smatz@11045: smatz@11045: if (line_nr > 6) ResizeWindow(this, 0, 12 * (line_nr - 6)); smatz@11045: smatz@11045: this->FindWindowPlacementAndResize(&_land_info_desc); smatz@11045: tron@4000: #if defined(_DEBUG) tron@4000: # define LANDINFOD_LEVEL 0 tron@4000: #else tron@4000: # define LANDINFOD_LEVEL 1 tron@4000: #endif rubidium@10475: DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile)); rubidium@10475: DEBUG(misc, LANDINFOD_LEVEL, "type_height = %#x", _m[tile].type_height); rubidium@10475: DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", _m[tile].m1); rubidium@10475: DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", _m[tile].m2); rubidium@10475: DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", _m[tile].m3); rubidium@10475: DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4); rubidium@10475: DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5); rubidium@10475: DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _m[tile].m6); rubidium@10475: DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7); tron@4000: #undef LANDINFOD_LEVEL rubidium@10475: } rubidium@10475: }; rubidium@10475: rubidium@10475: static void Place_LandInfo(TileIndex tile) rubidium@10475: { rubidium@10475: DeleteWindowById(WC_LAND_INFO, 0); rubidium@10475: new LandInfoWindow(tile); truelight@0: } truelight@0: rubidium@6573: void PlaceLandBlockInfo() truelight@0: { Darkvater@1914: if (_cursor.sprite == SPR_CURSOR_QUERY) { truelight@0: ResetObjectToPlace(); truelight@0: } else { truelight@0: _place_proc = Place_LandInfo; rubidium@8385: SetObjectToPlace(SPR_CURSOR_QUERY, PAL_NONE, VHM_RECT, WC_MAIN_TOOLBAR, 0); truelight@0: } truelight@0: } truelight@0: truelight@0: static const Widget _about_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 14, 11, 419, 0, 13, STR_015B_OPENTTD, STR_NULL}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 0, 419, 14, 271, 0x0, STR_NULL}, rubidium@4344: { WWT_FRAME, RESIZE_NONE, 14, 5, 414, 40, 245, STR_NULL, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _about_desc = { rubidium@7837: WDP_CENTER, WDP_CENTER, 420, 272, 420, 272, rubidium@6144: WC_GAME_OPTIONS, WC_NONE, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, truelight@0: _about_widgets, truelight@0: }; truelight@0: glx@10549: struct AboutWindow : public Window { glx@10549: int scroll_height; glx@10549: uint16 counter; glx@10549: glx@10549: AboutWindow() : Window(&_about_desc) glx@10549: { glx@10549: this->counter = 5; glx@10549: this->scroll_height = this->height - 40; glx@10549: this->FindWindowPlacementAndResize(&_about_desc); glx@10549: } glx@10549: glx@10549: virtual void OnPaint() glx@10549: { glx@10549: static const char *credits[] = { glx@10549: /************************************************************************* glx@10549: * maximum length of string which fits in window -^*/ glx@10549: "Original design by Chris Sawyer", glx@10549: "Original graphics by Simon Foster", glx@10549: "", glx@10549: "The OpenTTD team (in alphabetical order):", glx@10549: " Jean-Francois Claeys (Belugas) - GUI, newindustries and more", glx@10549: " Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles", glx@10549: " Matthijs Kooijman (blathijs) - Pathfinder-guru, pool rework", glx@10549: " Loïc Guilloux (glx) - General coding", glx@10549: " Christoph Elsenhans (frosch) - General coding", glx@10549: " Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)", glx@10549: " Jonathan Coome (Maedhros) - High priest of the newGRF Temple", glx@10549: " Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host", glx@10549: " Owen Rudge (orudge) - Forum host, OS/2 port", glx@10549: " Peter Nelson (peter1138) - Spiritual descendant from newGRF gods", glx@10549: " Remko Bijker (Rubidium) - Lead coder and way more", glx@10549: " Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker", glx@10549: " Zdenek Sojka (SmatZ) - Bug finder and fixer", glx@10549: "", glx@10549: "Inactive Developers:", glx@10549: " Victor Fischer (Celestar) - Programming everywhere you need him to", glx@10549: " Tamás Faragó (Darkvater) - Ex-Lead coder", glx@10549: " Christoph Mallon (Tron) - Programmer, code correctness police", glx@10549: "", glx@10549: "Retired Developers:", glx@10549: " Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)", glx@10549: " Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)", glx@10549: " Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)", glx@10549: " Patric Stout (TrueLight) - Programmer, webhoster (0.3 - pre0.6)", glx@10549: "", glx@10549: "Special thanks go out to:", glx@10549: " Josef Drexler - For his great work on TTDPatch", glx@10549: " Marcin Grzegorczyk - For his documentation of TTD internals", glx@10549: " Petr Baudis (pasky) - Many patches, newGRF support", glx@10549: " Stefan Meißner (sign_de) - For his work on the console", glx@10549: " Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with", glx@10549: " Cian Duffy (MYOB) - BeOS port / manual writing", glx@10549: " Christian Rosentreter (tokai) - MorphOS / AmigaOS port", glx@10549: " Richard Kempton (richK) - additional airports, initial TGP implementation", glx@10549: "", glx@10549: " Michael Blunck - Pre-Signals and Semaphores © 2003", glx@10549: " George - Canal/Lock graphics © 2003-2004", glx@10549: " David Dallaston - Tram tracks", glx@10549: " Marcin Grzegorczyk - Foundations for Tracks on Slopes", glx@10549: " All Translators - Who made OpenTTD a truly international game", glx@10549: " Bug Reporters - Without whom OpenTTD would still be full of bugs!", glx@10549: "", glx@10549: "", glx@10549: "And last but not least:", glx@10549: " Chris Sawyer - For an amazing game!" glx@10549: }; glx@10549: rubidium@10595: this->DrawWidgets(); glx@10549: glx@10549: /* Show original copyright and revision version */ glx@10549: DrawStringCentered(210, 17, STR_00B6_ORIGINAL_COPYRIGHT, TC_FROMSTRING); glx@10549: DrawStringCentered(210, 17 + 10, STR_00B7_VERSION, TC_FROMSTRING); glx@10549: glx@10549: int y = this->scroll_height; glx@10549: glx@10549: /* Show all scrolling credits */ glx@10549: for (uint i = 0; i < lengthof(credits); i++) { glx@10549: if (y >= 50 && y < (this->height - 40)) { glx@10549: DoDrawString(credits[i], 10, y, TC_BLACK); glx@10549: } glx@10549: y += 10; glx@10549: } glx@10549: glx@10549: /* If the last text has scrolled start a new from the start */ glx@10549: if (y < 50) this->scroll_height = this->height - 40; glx@10549: glx@10549: DoDrawStringCentered(210, this->height - 25, "Website: http://www.openttd.org", TC_BLACK); glx@10549: DrawStringCentered(210, this->height - 15, STR_00BA_COPYRIGHT_OPENTTD, TC_FROMSTRING); glx@10549: } glx@10549: glx@10549: virtual void OnTick() glx@10549: { glx@10549: if (--this->counter == 0) { glx@10549: this->counter = 5; glx@10549: this->scroll_height--; glx@10549: this->SetDirty(); glx@10549: } glx@10549: } glx@10549: }; truelight@0: rubidium@6573: void ShowAboutWindow() truelight@0: { truelight@0: DeleteWindowById(WC_GAME_OPTIONS, 0); glx@10549: new AboutWindow(); truelight@0: } truelight@0: truelight@0: static const Widget _errmsg_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 4, 11, 239, 0, 13, STR_00B2_MESSAGE, STR_NULL}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 4, 0, 239, 14, 45, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const Widget _errmsg_face_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 4, 11, 333, 0, 13, STR_00B3_MESSAGE_FROM, STR_NULL}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 4, 0, 333, 14, 136, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: glx@10587: struct ErrmsgWindow : public Window { glx@10587: private: glx@10587: uint duration; glx@10587: uint64 decode_params[20]; glx@10587: StringID message_1; glx@10587: StringID message_2; rubidium@10594: bool show_player_face; truelight@0: glx@10587: public: rubidium@10594: ErrmsgWindow(Point pt, int width, int height, StringID msg1, StringID msg2, const Widget *widget, bool show_player_face) : rubidium@10625: Window(pt.x, pt.y, width, height, WC_ERRMSG, widget), rubidium@10594: show_player_face(show_player_face) glx@10587: { rubidium@10775: this->duration = _settings_client.gui.errmsg_duration; glx@10587: CopyOutDParam(this->decode_params, 0, lengthof(this->decode_params)); glx@10587: this->message_1 = msg1; glx@10587: this->message_2 = msg2; glx@10587: this->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET; glx@10587: this->FindWindowPlacementAndResize(width, height); glx@10587: } tron@2639: glx@10587: virtual void OnPaint() glx@10587: { glx@10587: static int y[][3] = { glx@10587: {15, 25, 30}, // _errmsg_widgets glx@10587: {45, 65, 90}, // _errmsg_face_widgets glx@10587: }; rubidium@9234: glx@10587: CopyInDParam(0, this->decode_params, lengthof(this->decode_params)); rubidium@10595: this->DrawWidgets(); glx@10587: CopyInDParam(0, this->decode_params, lengthof(this->decode_params)); rubidium@9234: glx@10587: /* If the error message comes from a NewGRF, we must use the text ref. stack reserved for error messages. rubidium@10594: * 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 rubidium@10594: */ glx@10587: SwitchToErrorRefStack(); glx@10587: RewindTextRefStack(); glx@10587: rubidium@10594: if (this->show_player_face) { glx@10587: const Player *p = GetPlayer((PlayerID)GetDParamX(this->decode_params, 2)); glx@10587: DrawPlayerFace(p->face, p->player_color, 2, 16); glx@10587: } glx@10587: glx@10587: byte j = (this->message_1 == INVALID_STRING_ID) ? 1 : 0; rubidium@10594: DrawStringMultiCenter(this->width - 120, y[this->show_player_face][j], this->message_2, this->width - 2); glx@10587: if (j == 0) { rubidium@10594: DrawStringMultiCenter(this->width - 120, y[this->show_player_face][2], this->message_1, this->width - 2); glx@10587: } glx@10587: glx@10587: /* Switch back to the normal text ref. stack for NewGRF texts */ glx@10587: SwitchToNormalRefStack(); truelight@0: } glx@10587: glx@10587: virtual void OnMouseLoop() glx@10587: { glx@10587: if (_right_button_down) delete this; glx@10587: } glx@10587: glx@10587: virtual void OnHundredthTick() glx@10587: { glx@10587: if (--this->duration == 0) delete this; glx@10587: } glx@10587: glx@10587: ~ErrmsgWindow() glx@10587: { glx@10587: SetRedErrorSquare(0); glx@10587: extern StringID _switch_mode_errorstr; glx@10587: _switch_mode_errorstr = INVALID_STRING_ID; glx@10587: } glx@10587: rubidium@10607: virtual EventState OnKeyPress(uint16 key, uint16 keycode) glx@10587: { rubidium@10607: if (keycode != WKC_SPACE) return ES_NOT_HANDLED; glx@10587: delete this; rubidium@10607: return ES_HANDLED; glx@10587: } glx@10587: }; truelight@0: truelight@0: void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y) truelight@0: { truelight@0: DeleteWindowById(WC_ERRMSG, 0); truelight@0: rubidium@10775: if (!_settings_client.gui.errmsg_duration) return; truelight@0: glx@10587: if (msg_2 == STR_NULL) msg_2 = STR_EMPTY; truelight@193: smatz@9315: Point pt; smatz@9315: const ViewPort *vp; smatz@9315: glx@10587: if (msg_1 != STR_013B_OWNED_BY || GetDParam(2) >= 8) { rubidium@9234: if ((x | y) != 0) { truelight@0: pt = RemapCoords2(x, y); Darkvater@5120: vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport; truelight@0: belugas@6527: /* move x pos to opposite corner */ truelight@7122: pt.x = UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left; truelight@0: pt.x = (pt.x < (_screen.width >> 1)) ? _screen.width - 260 : 20; truelight@193: belugas@6527: /* move y pos to opposite corner */ truelight@7122: pt.y = UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top; truelight@0: pt.y = (pt.y < (_screen.height >> 1)) ? _screen.height - 80 : 100; truelight@193: truelight@0: } else { truelight@0: pt.x = (_screen.width - 240) >> 1; truelight@0: pt.y = (_screen.height - 46) >> 1; truelight@0: } rubidium@10594: new ErrmsgWindow(pt, 240, 46, msg_1, msg_2, _errmsg_widgets, false); truelight@0: } else { rubidium@9234: if ((x | y) != 0) { truelight@0: pt = RemapCoords2(x, y); Darkvater@5120: vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport; skidd13@8418: pt.x = Clamp(UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left - (334/2), 0, _screen.width - 334); skidd13@8418: pt.y = Clamp(UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top - (137/2), 22, _screen.height - 137); truelight@0: } else { truelight@0: pt.x = (_screen.width - 334) >> 1; truelight@0: pt.y = (_screen.height - 137) >> 1; truelight@0: } rubidium@10594: new ErrmsgWindow(pt, 334, 137, msg_1, msg_2, _errmsg_face_widgets, true); truelight@0: } truelight@0: } truelight@0: rubidium@7486: void ShowEstimatedCostOrIncome(Money cost, int x, int y) truelight@0: { tron@2498: StringID msg = STR_0805_ESTIMATED_COST; truelight@0: truelight@0: if (cost < 0) { truelight@0: cost = -cost; truelight@0: msg = STR_0807_ESTIMATED_INCOME; truelight@0: } rubidium@7498: SetDParam(0, cost); tron@2498: ShowErrorMessage(INVALID_STRING_ID, msg, x, y); truelight@0: } truelight@0: rubidium@7486: void ShowCostOrIncomeAnimation(int x, int y, int z, Money cost) truelight@0: { truelight@0: Point pt = RemapCoords(x,y,z); smatz@9315: StringID msg = STR_0801_COST; truelight@0: truelight@0: if (cost < 0) { truelight@0: cost = -cost; truelight@0: msg = STR_0803_INCOME; truelight@0: } rubidium@7498: SetDParam(0, cost); truelight@7494: AddTextEffect(msg, pt.x, pt.y, 0x250, TE_RISING); truelight@0: } truelight@0: rubidium@7486: void ShowFeederIncomeAnimation(int x, int y, int z, Money cost) celestar@1935: { celestar@1935: Point pt = RemapCoords(x,y,z); celestar@1935: rubidium@7498: SetDParam(0, cost); truelight@7494: AddTextEffect(STR_FEEDER, pt.x, pt.y, 0x250, TE_RISING); truelight@7494: } truelight@7494: truelight@7510: TextEffectID ShowFillingPercent(int x, int y, int z, uint8 percent, StringID string) truelight@7494: { truelight@7494: Point pt = RemapCoords(x, y, z); truelight@7494: truelight@7510: assert(string != STR_NULL); truelight@7510: truelight@7494: SetDParam(0, percent); truelight@7510: return AddTextEffect(string, pt.x, pt.y, 0xFFFF, TE_STATIC); truelight@7494: } truelight@7494: truelight@7510: void UpdateFillingPercent(TextEffectID te_id, uint8 percent, StringID string) truelight@7494: { truelight@7510: assert(string != STR_NULL); truelight@7510: truelight@7494: SetDParam(0, percent); truelight@7510: UpdateTextEffect(te_id, string); truelight@7494: } truelight@7494: truelight@7494: void HideFillingPercent(TextEffectID te_id) truelight@7494: { truelight@7494: if (te_id != INVALID_TE_ID) RemoveTextEffect(te_id); celestar@1935: } celestar@1935: tron@2275: static const Widget _tooltips_widgets[] = { truelight@867: { WWT_PANEL, RESIZE_NONE, 14, 0, 199, 0, 31, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: rubidium@10506: struct TooltipsWindow : public Window truelight@0: { rubidium@10506: StringID string_id; rubidium@10506: byte paramcount; rubidium@10506: uint64 params[5]; Darkvater@4834: rubidium@10506: TooltipsWindow(int x, int y, int width, int height, const Widget *widget, rubidium@10506: StringID str, uint paramcount, const uint64 params[]) : rubidium@10625: Window(x, y, width, height, WC_TOOLTIPS, widget) rubidium@10506: { rubidium@10506: this->string_id = str; rubidium@10506: assert(sizeof(this->params[0]) == sizeof(params[0])); rubidium@10506: memcpy(this->params, params, sizeof(this->params[0]) * paramcount); rubidium@10506: this->paramcount = paramcount; truelight@0: rubidium@10506: this->flags4 &= ~WF_WHITE_BORDER_MASK; // remove white-border from tooltip rubidium@10506: this->widget[0].right = width; rubidium@10506: this->widget[0].bottom = height; Darkvater@4834: rubidium@10506: FindWindowPlacementAndResize(width, height); truelight@0: } rubidium@10506: rubidium@10506: virtual void OnPaint() rubidium@10506: { rubidium@10506: GfxFillRect(0, 0, this->width - 1, this->height - 1, 0); rubidium@10506: GfxFillRect(1, 1, this->width - 2, this->height - 2, 0x44); rubidium@10506: rubidium@10506: for (uint arg = 0; arg < this->paramcount; arg++) { rubidium@10506: SetDParam(arg, this->params[arg]); rubidium@10506: } rubidium@10506: DrawStringMultiCenter((this->width >> 1), (this->height >> 1) - 5, this->string_id, this->width - 2); rubidium@10506: } rubidium@10506: rubidium@10506: virtual void OnMouseLoop() rubidium@10506: { rubidium@10506: /* We can show tooltips while dragging tools. These are shown as long as rubidium@10506: * we are dragging the tool. Normal tooltips work with rmb */ rubidium@10506: if (this->paramcount == 0 ) { rubidium@10506: if (!_right_button_down) delete this; rubidium@10506: } else { rubidium@10506: if (!_left_button_down) delete this; rubidium@10506: } rubidium@10506: } rubidium@10506: }; truelight@0: Darkvater@4834: /** Shows a tooltip belugas@6977: * @param str String to be displayed belugas@6977: * @param paramcount number of params to deal with belugas@6977: * @param params (optional) up to 5 pieces of additional information that may be belugas@6977: * added to a tooltip; currently only supports parameters of {NUM} (integer) */ rubidium@7502: void GuiShowTooltipsWithArgs(StringID str, uint paramcount, const uint64 params[]) truelight@0: { Darkvater@4884: DeleteWindowById(WC_TOOLTIPS, 0); truelight@0: Darkvater@4884: /* We only show measurement tooltips with patch setting on */ rubidium@10775: if (str == STR_NULL || (paramcount != 0 && !_settings_client.gui.measure_tooltip)) return; truelight@0: rubidium@9234: for (uint i = 0; i != paramcount; i++) SetDParam(i, params[i]); rubidium@9234: char buffer[512]; Darkvater@4912: GetString(buffer, str, lastof(buffer)); Darkvater@4834: rubidium@9234: Dimension br = GetStringBoundingBox(buffer); Darkvater@4834: br.width += 6; br.height += 4; // increase slightly to have some space around the box truelight@0: Darkvater@4558: /* Cut tooltip length to 200 pixels max, wrap to new line if longer */ Darkvater@4834: if (br.width > 200) { Darkvater@4834: br.height += ((br.width - 4) / 176) * 10; Darkvater@4834: br.width = 200; truelight@0: } truelight@0: Darkvater@4558: /* Correctly position the tooltip position, watch out for window and cursor size Darkvater@4558: * Clamp value to below main toolbar and above statusbar. If tooltip would Darkvater@4558: * go below window, flip it so it is shown above the cursor */ rubidium@9234: int y = Clamp(_cursor.pos.y + _cursor.size.y + _cursor.offs.y + 5, 22, _screen.height - 12); Darkvater@4834: if (y + br.height > _screen.height - 12) y = _cursor.pos.y + _cursor.offs.y - br.height - 5; rubidium@9234: int x = Clamp(_cursor.pos.x - (br.width >> 1), 0, _screen.width - br.width); truelight@0: rubidium@10506: new TooltipsWindow(x, y, br.width, br.height, _tooltips_widgets, str, paramcount, params); truelight@0: } truelight@0: truelight@0: rubidium@9342: static int DrawStationCoverageText(const AcceptedCargo cargo, rubidium@9342: int str_x, int str_y, StationCoverageType sct, bool supplies) truelight@0: { peter1138@5038: bool first = true; truelight@193: rubidium@11157: char string[512]; rubidium@11157: char *b = InlineString(string, supplies ? STR_SUPPLIES : STR_000D_ACCEPTS); truelight@0: rubidium@7737: for (CargoID i = 0; i < NUM_CARGO; i++) { rubidium@11157: if (b >= lastof(string) - (1 + 2 * 4)) break; // ',' or ' ' and two calls to Utf8Encode() rubidium@7737: switch (sct) { rubidium@7737: case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break; rubidium@7737: case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break; rubidium@7737: case SCT_ALL: break; rubidium@7737: default: NOT_REACHED(); rubidium@7737: } glx@10155: if (cargo[i] >= (supplies ? 1U : 8U)) { peter1138@5038: if (first) { peter1138@5038: first = false; peter1138@5038: } else { peter1138@5038: /* Add a comma if this is not the first item */ peter1138@5038: *b++ = ','; peter1138@5038: *b++ = ' '; peter1138@5038: } peter1138@6417: b = InlineString(b, GetCargo(i)->name); truelight@0: } truelight@0: } truelight@0: peter1138@5038: /* If first is still true then no cargo is accepted */ peter1138@5038: if (first) b = InlineString(b, STR_00D0_NOTHING); truelight@0: peter1138@5038: *b = '\0'; smatz@8537: smatz@8537: /* Make sure we detect any buffer overflow */ rubidium@11157: assert(b < endof(string)); smatz@8537: rubidium@11157: SetDParamStr(0, string); rubidium@11157: return DrawStringMultiLine(str_x, str_y, STR_JUST_RAW_STRING, 144); truelight@0: } truelight@0: rubidium@9342: /** rubidium@9342: * Calculates and draws the accepted or supplied cargo around the selected tile(s) rubidium@9342: * @param sx x position where the string is to be drawn rubidium@9342: * @param sy y position where the string is to be drawn rubidium@9342: * @param sct which type of cargo is to be displayed (passengers/non-passengers) rubidium@9342: * @param rad radius around selected tile(s) to be searched rubidium@9342: * @param supplies if supplied cargos should be drawn, else accepted cargos rubidium@9342: * @return Returns the y value below the string that was drawn rubidium@9342: */ rubidium@9342: int DrawStationCoverageAreaText(int sx, int sy, StationCoverageType sct, int rad, bool supplies) rubidium@7817: { peter1138@2873: TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y); rubidium@9342: AcceptedCargo cargo; peter1138@2873: if (tile < MapSize()) { rubidium@9342: if (supplies) { rubidium@9342: GetProductionAroundTiles(cargo, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad); rubidium@9342: } else { rubidium@9342: GetAcceptanceAroundTiles(cargo, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad); rubidium@9342: } rubidium@9342: return sy + DrawStationCoverageText(cargo, sx, sy, sct, supplies); truelight@0: } peter1138@8919: peter1138@8919: return sy; truelight@0: } truelight@0: belugas@4171: void CheckRedrawStationCoverage(const Window *w) truelight@0: { tron@2549: if (_thd.dirty & 1) { tron@2549: _thd.dirty &= ~1; truelight@0: SetWindowDirty(w); truelight@0: } truelight@0: } truelight@0: peter1138@5108: /* Delete a character at the caret position in a text buf. peter1138@5108: * If backspace is set, delete the character before the caret, peter1138@5108: * else delete the character after it. */ peter1138@5108: static void DelChar(Textbuf *tb, bool backspace) darkvater@911: { peter1138@5108: WChar c; Darkvater@6540: char *s = tb->buf + tb->caretpos; peter1138@5108: Darkvater@6540: if (backspace) s = Utf8PrevChar(s); peter1138@5108: rubidium@10751: uint16 len = (uint16)Utf8Decode(&c, s); rubidium@9234: uint width = GetCharacterWidth(FS_NORMAL, c); peter1138@5108: peter1138@5108: tb->width -= width; Darkvater@6540: if (backspace) { Darkvater@6540: tb->caretpos -= len; Darkvater@6540: tb->caretxoffs -= width; Darkvater@6540: } peter1138@5108: peter1138@5108: /* Move the remaining characters over the marker */ Darkvater@6540: memmove(s, s + len, tb->length - (s - tb->buf) - len + 1); peter1138@5108: tb->length -= len; Darkvater@1390: } darkvater@911: Darkvater@1390: /** Darkvater@1390: * Delete a character from a textbuffer, either with 'Delete' or 'Backspace' Darkvater@1390: * The character is delete from the position the caret is at belugas@6977: * @param tb Textbuf type to be changed belugas@6977: * @param delmode Type of deletion, either WKC_BACKSPACE or WKC_DELETE peter1138@7868: * @return Return true on successful change of Textbuf, or false otherwise Darkvater@1390: */ Darkvater@1390: bool DeleteTextBufferChar(Textbuf *tb, int delmode) Darkvater@1390: { Darkvater@1390: if (delmode == WKC_BACKSPACE && tb->caretpos != 0) { peter1138@5108: DelChar(tb, true); Darkvater@1390: return true; Darkvater@1390: } else if (delmode == WKC_DELETE && tb->caretpos < tb->length) { peter1138@5108: DelChar(tb, false); Darkvater@1390: return true; Darkvater@1390: } Darkvater@1390: Darkvater@1390: return false; Darkvater@1390: } Darkvater@1390: Darkvater@1390: /** Darkvater@1879: * Delete every character in the textbuffer belugas@6977: * @param tb Textbuf buffer to be emptied Darkvater@1879: */ Darkvater@1879: void DeleteTextBufferAll(Textbuf *tb) Darkvater@1879: { Darkvater@1879: memset(tb->buf, 0, tb->maxlength); Darkvater@1879: tb->length = tb->width = 0; Darkvater@1879: tb->caretpos = tb->caretxoffs = 0; Darkvater@1879: } Darkvater@1879: Darkvater@1879: /** Darkvater@3459: * Insert a character to a textbuffer. If maxwidth of the Textbuf is zero, Darkvater@3459: * we don't care about the visual-length but only about the physical Darkvater@3458: * length of the string belugas@6977: * @param tb Textbuf type to be changed Darkvater@1390: * @param key Character to be inserted peter1138@7868: * @return Return true on successful change of Textbuf, or false otherwise Darkvater@1390: */ peter1138@5108: bool InsertTextBufferChar(Textbuf *tb, WChar key) Darkvater@1390: { peter1138@3798: const byte charwidth = GetCharacterWidth(FS_NORMAL, key); rubidium@10751: uint16 len = (uint16)Utf8CharLen(key); peter1138@5108: if (tb->length < (tb->maxlength - len) && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) { peter1138@5108: memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->length - tb->caretpos + 1); peter1138@5108: Utf8Encode(tb->buf + tb->caretpos, key); peter1138@5108: tb->length += len; peter1138@5108: tb->width += charwidth; Darkvater@1390: peter1138@5108: tb->caretpos += len; Darkvater@1390: tb->caretxoffs += charwidth; Darkvater@1390: return true; Darkvater@1390: } Darkvater@1390: return false; Darkvater@1390: } Darkvater@1390: Darkvater@1390: /** Darkvater@1390: * Handle text navigation with arrow keys left/right. Darkvater@1390: * This defines where the caret will blink and the next characer interaction will occur belugas@6977: * @param tb Textbuf type where navigation occurs belugas@6977: * @param navmode Direction in which navigation occurs WKC_LEFT, WKC_RIGHT, WKC_END, WKC_HOME peter1138@7868: * @return Return true on successful change of Textbuf, or false otherwise Darkvater@1390: */ Darkvater@1390: bool MoveTextBufferPos(Textbuf *tb, int navmode) Darkvater@1390: { Darkvater@1390: switch (navmode) { rubidium@9234: case WKC_LEFT: rubidium@9234: if (tb->caretpos != 0) { rubidium@9234: WChar c; rubidium@9234: const char *s = Utf8PrevChar(tb->buf + tb->caretpos); rubidium@9234: Utf8Decode(&c, s); rubidium@9234: tb->caretpos = s - tb->buf; // -= (tb->buf + tb->caretpos - s) rubidium@9234: tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c); peter1138@5108: rubidium@9234: return true; rubidium@9234: } rubidium@9234: break; rubidium@9234: rubidium@9234: case WKC_RIGHT: rubidium@9234: if (tb->caretpos < tb->length) { rubidium@9234: WChar c; rubidium@9234: rubidium@10751: tb->caretpos += (uint16)Utf8Decode(&c, tb->buf + tb->caretpos); rubidium@9234: tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c); rubidium@9234: rubidium@9234: return true; rubidium@9234: } rubidium@9234: break; rubidium@9234: rubidium@9234: case WKC_HOME: rubidium@9234: tb->caretpos = 0; rubidium@9234: tb->caretxoffs = 0; Darkvater@1390: return true; peter1138@5108: rubidium@9234: case WKC_END: rubidium@9234: tb->caretpos = tb->length; rubidium@9234: tb->caretxoffs = tb->width; Darkvater@1390: return true; smatz@9315: smatz@9315: default: smatz@9315: break; Darkvater@1390: } Darkvater@1390: Darkvater@1390: return false; Darkvater@1390: } Darkvater@1390: Darkvater@1390: /** Darkvater@4948: * Initialize the textbuffer by supplying it the buffer to write into Darkvater@4948: * and the maximum length of this buffer belugas@6977: * @param tb Textbuf type which is getting initialized Darkvater@4948: * @param buf the buffer that will be holding the data for input Darkvater@4948: * @param maxlength maximum length in characters of this buffer Darkvater@4948: * @param maxwidth maximum length in pixels of this buffer. If reached, buffer Darkvater@4958: * cannot grow, even if maxlength would allow because there is space. A length Darkvater@4958: * of zero '0' means the buffer is only restricted by maxlength */ Darkvater@4948: void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16 maxwidth) Darkvater@4948: { Darkvater@4948: tb->buf = (char*)buf; Darkvater@4948: tb->maxlength = maxlength; Darkvater@4948: tb->maxwidth = maxwidth; Darkvater@4948: tb->caret = true; Darkvater@4948: UpdateTextBufferSize(tb); Darkvater@4948: } Darkvater@4948: Darkvater@4948: /** belugas@6977: * Update Textbuf type with its actual physical character and screenlength Darkvater@1390: * Get the count of characters in the string as well as the width in pixels. Darkvater@1390: * Useful when copying in a larger amount of text at once belugas@6977: * @param tb Textbuf type which length is calculated Darkvater@1390: */ Darkvater@1390: void UpdateTextBufferSize(Textbuf *tb) Darkvater@1390: { peter1138@5108: const char *buf = tb->buf; peter1138@5108: WChar c = Utf8Consume(&buf); tron@2630: Darkvater@1390: tb->width = 0; rubidium@5698: tb->length = 0; Darkvater@1390: peter1138@5108: for (; c != '\0' && tb->length < (tb->maxlength - 1); c = Utf8Consume(&buf)) { peter1138@5108: tb->width += GetCharacterWidth(FS_NORMAL, c); rubidium@5698: tb->length += Utf8CharLen(c); Darkvater@1390: } Darkvater@1390: Darkvater@1390: tb->caretpos = tb->length; Darkvater@1390: tb->caretxoffs = tb->width; darkvater@911: } darkvater@911: Darkvater@1390: bool HandleCaret(Textbuf *tb) Darkvater@1390: { Darkvater@1390: /* caret changed? */ Darkvater@1390: bool b = !!(_caret_timer & 0x20); Darkvater@1390: Darkvater@1390: if (b != tb->caret) { Darkvater@1390: tb->caret = b; Darkvater@1390: return true; Darkvater@1390: } Darkvater@1390: return false; Darkvater@1390: } Darkvater@1390: rubidium@10607: int QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, Window::EventState &state) truelight@0: { rubidium@10607: state = Window::ES_HANDLED; rubidium@10499: rubidium@10499: switch (keycode) { rubidium@10499: case WKC_ESC: return 2; rubidium@10499: rubidium@10499: case WKC_RETURN: case WKC_NUM_ENTER: return 1; rubidium@10499: rubidium@10499: case (WKC_CTRL | 'V'): rubidium@10499: if (InsertTextBufferClipboard(&this->text)) w->InvalidateWidget(wid); rubidium@10499: break; rubidium@10499: rubidium@10499: case (WKC_CTRL | 'U'): rubidium@10499: DeleteTextBufferAll(&this->text); rubidium@10499: w->InvalidateWidget(wid); rubidium@10499: break; rubidium@10499: rubidium@10499: case WKC_BACKSPACE: case WKC_DELETE: rubidium@10499: if (DeleteTextBufferChar(&this->text, keycode)) w->InvalidateWidget(wid); rubidium@10499: break; rubidium@10499: rubidium@10499: case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: rubidium@10499: if (MoveTextBufferPos(&this->text, keycode)) w->InvalidateWidget(wid); rubidium@10499: break; rubidium@10499: rubidium@10499: default: rubidium@10499: if (IsValidChar(key, this->afilter)) { rubidium@10499: if (InsertTextBufferChar(&this->text, key)) w->InvalidateWidget(wid); rubidium@10499: } else { // key wasn't caught. Continue only if standard entry specified rubidium@10607: state = (this->afilter == CS_ALPHANUMERAL) ? Window::ES_HANDLED : Window::ES_NOT_HANDLED; rubidium@10499: } rubidium@10499: } rubidium@10499: rubidium@10499: return 0; truelight@0: } truelight@0: rubidium@10499: void QueryString::HandleEditBox(Window *w, int wid) rubidium@10499: { rubidium@10499: if (HandleCaret(&this->text)) w->InvalidateWidget(wid); rubidium@10499: } rubidium@10499: rubidium@10499: void QueryString::DrawEditBox(Window *w, int wid) truelight@0: { Darkvater@4955: const Widget *wi = &w->widget[wid]; truelight@0: rubidium@9233: assert((wi->type & WWT_MASK) == WWT_EDITBOX); rubidium@9233: Darkvater@5890: GfxFillRect(wi->left + 1, wi->top + 1, wi->right - 1, wi->bottom - 1, 215); Darkvater@5890: smatz@9315: DrawPixelInfo dpi; smatz@9315: int delta; smatz@9315: Darkvater@4955: /* Limit the drawing of the string inside the widget boundaries */ Darkvater@4955: if (!FillDrawPixelInfo(&dpi, rubidium@9234: wi->left + 4, rubidium@9234: wi->top + 1, rubidium@9234: wi->right - wi->left - 4, rubidium@9234: wi->bottom - wi->top - 1)) { rubidium@9234: return; rubidium@9234: } Darkvater@4955: smatz@9315: DrawPixelInfo *old_dpi = _cur_dpi; Darkvater@4955: _cur_dpi = &dpi; Darkvater@4955: Darkvater@4955: /* We will take the current widget length as maximum width, with a small Darkvater@4955: * space reserved at the end for the caret to show */ rubidium@10499: const Textbuf *tb = &this->text; smatz@9315: Darkvater@4955: delta = (wi->right - wi->left) - tb->width - 10; Darkvater@4955: if (delta > 0) delta = 0; Darkvater@4955: Darkvater@4955: if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs; Darkvater@4955: belugas@8320: DoDrawString(tb->buf, delta, 0, TC_YELLOW); belugas@8320: if (tb->caret) DoDrawString("_", tb->caretxoffs + delta, 0, TC_WHITE); Darkvater@4955: Darkvater@4955: _cur_dpi = old_dpi; truelight@0: } truelight@0: rubidium@10607: int QueryStringBaseWindow::HandleEditBoxKey(int wid, uint16 key, uint16 keycode, EventState &state) rubidium@10499: { rubidium@10607: return this->QueryString::HandleEditBoxKey(this, wid, key, keycode, state); rubidium@10499: } rubidium@10499: rubidium@10499: void QueryStringBaseWindow::HandleEditBox(int wid) rubidium@10499: { rubidium@10499: this->QueryString::HandleEditBox(this, wid); rubidium@10499: } rubidium@10499: rubidium@10499: void QueryStringBaseWindow::DrawEditBox(int wid) rubidium@10499: { rubidium@10499: this->QueryString::DrawEditBox(this, wid); rubidium@10499: } rubidium@10499: Darkvater@5682: enum QueryStringWidgets { Darkvater@5682: QUERY_STR_WIDGET_TEXT = 3, Darkvater@5682: QUERY_STR_WIDGET_CANCEL, Darkvater@5682: QUERY_STR_WIDGET_OK Darkvater@5682: }; Darkvater@5682: Darkvater@5682: rubidium@10499: struct QueryStringWindow : public QueryStringBaseWindow truelight@0: { glx@10548: QueryStringWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(desc) rubidium@10499: { glx@10548: this->parent = parent; rubidium@10499: SetBit(_no_scroll, SCROLL_EDIT); truelight@0: rubidium@10499: this->FindWindowPlacementAndResize(desc); rubidium@10499: } truelight@0: rubidium@10499: virtual void OnPaint() rubidium@10499: { rubidium@10499: SetDParam(0, this->caption); rubidium@10595: this->DrawWidgets(); rubidium@10499: rubidium@10499: this->DrawEditBox(QUERY_STR_WIDGET_TEXT); rubidium@10499: } rubidium@10499: rubidium@10499: void OnOk() rubidium@10499: { rubidium@10499: if (this->orig == NULL || strcmp(this->text.buf, this->orig) != 0) { rubidium@10499: /* If the parent is NULL, the editbox is handled by general function rubidium@10499: * HandleOnEditText */ rubidium@10499: if (this->parent != NULL) { rubidium@10499: this->parent->OnQueryTextFinished(this->text.buf); rubidium@10499: } else { rubidium@10499: HandleOnEditText(this->text.buf); Darkvater@5682: } rubidium@10550: this->handled = true; rubidium@10499: } rubidium@10499: } Darkvater@5682: rubidium@10499: virtual void OnClick(Point pt, int widget) rubidium@10499: { rubidium@10499: switch (widget) { rubidium@10499: case QUERY_STR_WIDGET_TEXT: rubidium@10499: ShowOnScreenKeyboard(this, QUERY_STR_WIDGET_TEXT, QUERY_STR_WIDGET_CANCEL, QUERY_STR_WIDGET_OK); rubidium@10499: break; rubidium@10499: rubidium@10499: case QUERY_STR_WIDGET_OK: rubidium@10499: this->OnOk(); rubidium@10499: /* Fallthrough */ rubidium@10499: case QUERY_STR_WIDGET_CANCEL: rubidium@10499: delete this; rubidium@10499: break; rubidium@10499: } rubidium@10486: } rubidium@10499: rubidium@10499: virtual void OnMouseLoop() rubidium@10499: { rubidium@10499: this->HandleEditBox(QUERY_STR_WIDGET_TEXT); rubidium@10499: } rubidium@10499: rubidium@10607: virtual EventState OnKeyPress(uint16 key, uint16 keycode) rubidium@10499: { rubidium@10607: EventState state; rubidium@10607: switch (this->HandleEditBoxKey(QUERY_STR_WIDGET_TEXT, key, keycode, state)) { smatz@10760: default: NOT_REACHED(); smatz@10760: case 0: { smatz@10760: Window *osk = FindWindowById(WC_OSK, 0); smatz@10760: if (osk != NULL && osk->parent == this) osk->OnInvalidateData(); smatz@10760: } break; rubidium@10499: case 1: this->OnOk(); // Enter pressed, confirms change rubidium@10499: /* FALL THROUGH */ rubidium@10499: case 2: delete this; break; // ESC pressed, closes window, abandons changes rubidium@10499: } rubidium@10607: return state; rubidium@10499: } rubidium@10499: rubidium@10499: ~QueryStringWindow() rubidium@10499: { rubidium@10499: if (!this->handled && this->parent != NULL) { smatz@10754: Window *parent = this->parent; smatz@10754: this->parent = NULL; // so parent doesn't try to delete us again smatz@10754: parent->OnQueryTextFinished(NULL); rubidium@10499: } rubidium@10499: ClrBit(_no_scroll, SCROLL_EDIT); rubidium@10499: } rubidium@10499: }; truelight@0: truelight@0: static const Widget _query_string_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 14, 11, 259, 0, 13, STR_012D, STR_NULL}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 259, 14, 29, 0x0, STR_NULL}, rubidium@9233: { WWT_EDITBOX, RESIZE_NONE, 14, 2, 257, 16, 27, 0x0, STR_NULL}, rubidium@4344: { WWT_TEXTBTN, RESIZE_NONE, 14, 0, 129, 30, 41, STR_012E_CANCEL, STR_NULL}, rubidium@4344: { WWT_TEXTBTN, RESIZE_NONE, 14, 130, 259, 30, 41, STR_012F_OK, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _query_string_desc = { rubidium@7837: 190, 219, 260, 42, 260, 42, rubidium@6144: WC_QUERY_STRING, WC_NONE, belugas@8515: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, truelight@0: _query_string_widgets, truelight@0: }; truelight@0: Darkvater@5682: /** Show a query popup window with a textbox in it. Darkvater@5682: * @param str StringID for the text shown in the textbox Darkvater@5682: * @param caption StringID of text shown in caption of querywindow Darkvater@5682: * @param maxlen maximum length in characters allowed. If bit 12 is set we rubidium@10638: * will not check the resulting string against to original string to return success Darkvater@5682: * @param maxwidth maximum width in pixels allowed Darkvater@5682: * @param parent pointer to a Window that will handle the events (ok/cancel) of this rubidium@10638: * window. If NULL, results are handled by global function HandleOnEditText Darkvater@5682: * @param afilter filters out unwanted character input */ Darkvater@5682: void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, Window *parent, CharSetFilter afilter) truelight@0: { Darkvater@1390: uint realmaxlen = maxlen & ~0x1000; truelight@0: truelight@0: DeleteWindowById(WC_QUERY_STRING, 0); truelight@0: DeleteWindowById(WC_SAVELOAD, 0); truelight@0: rubidium@10499: QueryStringWindow *w = new QueryStringWindow(&_query_string_desc, parent); tron@1386: rubidium@10499: assert(realmaxlen < lengthof(w->edit_str_buf)); rubidium@10499: rubidium@10499: GetString(w->edit_str_buf, str, lastof(w->edit_str_buf)); rubidium@10499: w->edit_str_buf[realmaxlen - 1] = '\0'; truelight@596: truelight@596: if (maxlen & 0x1000) { rubidium@10499: w->orig = NULL; tron@1386: } else { rubidium@10499: strecpy(w->orig_str_buf, w->edit_str_buf, lastof(w->orig_str_buf)); rubidium@10499: w->orig = w->orig_str_buf; tron@1386: } truelight@0: rubidium@8493: w->LowerWidget(QUERY_STR_WIDGET_TEXT); rubidium@10499: w->caption = caption; rubidium@10499: w->afilter = afilter; rubidium@10499: InitializeTextBuffer(&w->text, w->edit_str_buf, realmaxlen, maxwidth); truelight@0: } truelight@0: Darkvater@5669: Darkvater@5669: enum QueryWidgets { Darkvater@5669: QUERY_WIDGET_CAPTION = 1, Darkvater@5669: QUERY_WIDGET_NO = 3, Darkvater@5669: QUERY_WIDGET_YES Darkvater@5669: }; Darkvater@5669: rubidium@10496: /** rubidium@10496: * Window used for asking the user a YES/NO question. rubidium@10496: */ rubidium@10496: struct QueryWindow : public Window { smatz@10647: QueryCallbackProc *proc; ///< callback function executed on closing of popup. Window* points to parent, bool is true if 'yes' clicked, false otherwise smatz@10647: uint64 params[10]; ///< local copy of _decode_parameters smatz@10647: StringID message; ///< message shown for query window truelight@4300: smatz@10647: QueryWindow(const WindowDesc *desc, StringID caption, StringID message, Window *parent, QueryCallbackProc *callback) : Window(desc) rubidium@10496: { rubidium@10496: if (parent == NULL) parent = FindWindowById(WC_MAIN_WINDOW, 0); rubidium@10496: this->parent = parent; rubidium@10496: this->left = parent->left + (parent->width / 2) - (this->width / 2); rubidium@10496: this->top = parent->top + (parent->height / 2) - (this->height / 2); truelight@4300: rubidium@10496: /* Create a backup of the variadic arguments to strings because it will be rubidium@10496: * overridden pretty often. We will copy these back for drawing */ rubidium@10496: CopyOutDParam(this->params, 0, lengthof(this->params)); rubidium@10496: this->widget[QUERY_WIDGET_CAPTION].data = caption; rubidium@10496: this->message = message; rubidium@10496: this->proc = callback; rubidium@10498: rubidium@10498: this->FindWindowPlacementAndResize(desc); rubidium@10496: } Darkvater@5669: rubidium@10496: ~QueryWindow() rubidium@10496: { rubidium@10496: if (this->proc != NULL) this->proc(this->parent, false); truelight@4300: } rubidium@10496: rubidium@10496: virtual void OnPaint() rubidium@10496: { rubidium@10496: CopyInDParam(0, this->params, lengthof(this->params)); rubidium@10595: this->DrawWidgets(); rubidium@10496: CopyInDParam(0, this->params, lengthof(this->params)); rubidium@10496: rubidium@10496: DrawStringMultiCenter(this->width / 2, (this->height / 2) - 10, this->message, this->width - 2); rubidium@10496: } rubidium@10496: rubidium@10496: virtual void OnClick(Point pt, int widget) rubidium@10496: { rubidium@10496: switch (widget) { smatz@10647: case QUERY_WIDGET_YES: { smatz@10647: /* in the Generate New World window, clicking 'Yes' causes smatz@10647: * DeleteNonVitalWindows() to be called - we shouldn't be in a window then */ smatz@10647: QueryCallbackProc *proc = this->proc; smatz@10647: Window *parent = this->parent; peter1138@10755: /* Prevent the destructor calling the callback function */ peter1138@10755: this->proc = NULL; smatz@10647: delete this; smatz@10647: if (proc != NULL) { smatz@10647: proc(parent, true); smatz@10647: proc = NULL; rubidium@10496: } smatz@10647: } break; rubidium@10496: case QUERY_WIDGET_NO: rubidium@10496: delete this; rubidium@10496: break; rubidium@10496: } rubidium@10496: } rubidium@10496: rubidium@10607: virtual EventState OnKeyPress(uint16 key, uint16 keycode) rubidium@10496: { rubidium@10496: /* ESC closes the window, Enter confirms the action */ rubidium@10496: switch (keycode) { rubidium@10496: case WKC_RETURN: rubidium@10496: case WKC_NUM_ENTER: rubidium@10496: if (this->proc != NULL) { rubidium@10496: this->proc(this->parent, true); rubidium@10496: this->proc = NULL; rubidium@10496: } rubidium@10496: /* Fallthrough */ rubidium@10496: case WKC_ESC: rubidium@10496: delete this; rubidium@10607: return ES_HANDLED; rubidium@10496: } rubidium@10607: return ES_NOT_HANDLED; rubidium@10496: } rubidium@10496: }; truelight@4300: Darkvater@5669: truelight@4300: static const Widget _query_widgets[] = { Darkvater@5669: { WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, Darkvater@5669: { WWT_CAPTION, RESIZE_NONE, 4, 11, 209, 0, 13, STR_NULL, STR_NULL}, Darkvater@5669: { WWT_PANEL, RESIZE_NONE, 4, 0, 209, 14, 81, 0x0, /*OVERRIDE*/STR_NULL}, Darkvater@5669: {WWT_PUSHTXTBTN, RESIZE_NONE, 3, 20, 90, 62, 73, STR_00C9_NO, STR_NULL}, Darkvater@5669: {WWT_PUSHTXTBTN, RESIZE_NONE, 3, 120, 190, 62, 73, STR_00C8_YES, STR_NULL}, Darkvater@5669: { WIDGETS_END }, truelight@4300: }; truelight@4300: truelight@4300: static const WindowDesc _query_desc = { rubidium@7837: WDP_CENTER, WDP_CENTER, 210, 82, 210, 82, rubidium@6144: WC_CONFIRM_POPUP_QUERY, WC_NONE, Darkvater@5669: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_DEF_WIDGET | WDF_MODAL, truelight@4300: _query_widgets, truelight@4300: }; truelight@4300: Darkvater@5669: /** Show a modal confirmation window with standard 'yes' and 'no' buttons Darkvater@5669: * The window is aligned to the centre of its parent. Darkvater@5669: * NOTE: You cannot use BindCString as parameter for this window! Darkvater@5669: * @param caption string shown as window caption Darkvater@5669: * @param message string that will be shown for the window Darkvater@5669: * @param parent pointer to parent window, if this pointer is NULL the parent becomes Darkvater@5669: * the main window WC_MAIN_WINDOW belugas@6977: * @param callback callback function pointer to set in the window descriptor*/ smatz@10647: void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallbackProc *callback) truelight@4300: { rubidium@10496: new QueryWindow(&_query_desc, caption, message, parent, callback); truelight@4300: } truelight@4300: truelight@4300: glx@5079: static const Widget _load_dialog_widgets[] = { glx@5079: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, glx@5079: { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 256, 0, 13, STR_NULL, STR_018C_WINDOW_TITLE_DRAG_THIS}, glx@5079: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP}, glx@5079: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP}, glx@5079: { WWT_PANEL, RESIZE_RIGHT, 14, 0, 256, 26, 47, 0x0, STR_NULL}, rubidium@7842: { WWT_PANEL, RESIZE_RB, 14, 0, 256, 48, 153, 0x0, STR_NULL}, glx@5079: { WWT_PUSHIMGBTN, RESIZE_LR, 14, 245, 256, 48, 59, SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON}, rubidium@7842: { WWT_INSET, RESIZE_RB, 14, 2, 243, 50, 151, 0x0, STR_400A_LIST_OF_DRIVES_DIRECTORIES}, rubidium@7842: { WWT_SCROLLBAR, RESIZE_LRB, 14, 245, 256, 60, 141, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, rubidium@7842: { WWT_RESIZEBOX, RESIZE_LRTB, 14, 245, 256, 142, 153, 0x0, STR_RESIZE_BUTTON}, truelight@4300: { WIDGETS_END}, truelight@4300: }; truelight@4300: truelight@0: static const Widget _save_dialog_widgets[] = { glx@5079: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, glx@5079: { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 256, 0, 13, STR_NULL, STR_018C_WINDOW_TITLE_DRAG_THIS}, glx@5079: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP}, glx@5079: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP}, glx@5079: { WWT_PANEL, RESIZE_RIGHT, 14, 0, 256, 26, 47, 0x0, STR_NULL}, rubidium@7842: { WWT_PANEL, RESIZE_RB, 14, 0, 256, 48, 151, 0x0, STR_NULL}, glx@5079: { WWT_PUSHIMGBTN, RESIZE_LR, 14, 245, 256, 48, 59, SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON}, rubidium@7842: { WWT_INSET, RESIZE_RB, 14, 2, 243, 50, 150, 0x0, STR_400A_LIST_OF_DRIVES_DIRECTORIES}, rubidium@7842: { WWT_SCROLLBAR, RESIZE_LRB, 14, 245, 256, 60, 151, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, rubidium@7842: { WWT_PANEL, RESIZE_RTB, 14, 0, 256, 152, 167, 0x0, STR_NULL}, rubidium@10229: { WWT_EDITBOX, RESIZE_RTB, 14, 2, 254, 154, 165, STR_SAVE_OSKTITLE, STR_400B_CURRENTLY_SELECTED_NAME}, rubidium@7842: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 127, 168, 179, STR_4003_DELETE, STR_400C_DELETE_THE_CURRENTLY_SELECTED}, rubidium@7842: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 128, 244, 168, 179, STR_4002_SAVE, STR_400D_SAVE_THE_CURRENT_GAME_USING}, rubidium@7842: { WWT_RESIZEBOX, RESIZE_LRTB, 14, 245, 256, 168, 179, 0x0, STR_RESIZE_BUTTON}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: belugas@6527: /* Colors for fios types */ belugas@8320: const TextColour _fios_colors[] = { belugas@8320: TC_LIGHT_BLUE, TC_DARK_GREEN, TC_DARK_GREEN, TC_ORANGE, TC_LIGHT_BROWN, belugas@8320: TC_ORANGE, TC_LIGHT_BROWN, TC_ORANGE, TC_ORANGE, TC_YELLOW belugas@8320: }; ludde@2261: rubidium@6573: void BuildFileList() truelight@0: { darkvater@1596: _fios_path_changed = true; truelight@0: FiosFreeSavegameList(); Darkvater@4223: tron@2549: switch (_saveload_mode) { tron@2549: case SLD_NEW_GAME: tron@2549: case SLD_LOAD_SCENARIO: tron@2549: case SLD_SAVE_SCENARIO: skidd13@10808: FiosGetScenarioList(_saveload_mode); break; truelight@4300: case SLD_LOAD_HEIGHTMAP: skidd13@10808: FiosGetHeightmapList(_saveload_mode); break; tron@2549: skidd13@10808: default: FiosGetSavegameList(_saveload_mode); break; tron@2549: } truelight@0: } truelight@0: Darkvater@2100: static void DrawFiosTexts(uint maxw) truelight@0: { darkvater@1596: static const char *path = NULL; darkvater@1596: static StringID str = STR_4006_UNABLE_TO_READ_DRIVE; darkvater@1596: static uint32 tot = 0; truelight@0: darkvater@1596: if (_fios_path_changed) { darkvater@1596: str = FiosGetDescText(&path, &tot); darkvater@1596: _fios_path_changed = false; darkvater@1596: } darkvater@1596: darkvater@1596: if (str != STR_4006_UNABLE_TO_READ_DRIVE) SetDParam(0, tot); belugas@8320: DrawString(2, 37, str, TC_FROMSTRING); belugas@8320: DoDrawStringTruncated(path, 2, 27, TC_BLACK, maxw); truelight@0: } truelight@0: rubidium@6573: static void MakeSortedSaveGameList() truelight@0: { tron@2631: uint sort_start = 0; tron@2631: uint sort_end = 0; tron@2631: belugas@4171: /* Directories are always above the files (FIOS_TYPE_DIR) belugas@4171: * Drives (A:\ (windows only) are always under the files (FIOS_TYPE_DRIVE) belugas@4171: * Only sort savegames/scenarios, not directories truelight@0: */ skidd13@10808: for (const FiosItem *item = _fios_items.Begin(); item != _fios_items.End(); item++) { skidd13@10808: switch (item->type) { tron@2631: case FIOS_TYPE_DIR: sort_start++; break; tron@2631: case FIOS_TYPE_PARENT: sort_start++; break; tron@2631: case FIOS_TYPE_DRIVE: sort_end++; break; peter1138@10307: default: break; tron@2631: } truelight@0: } truelight@0: skidd13@10808: uint s_amount = _fios_items.Length() - sort_start - sort_end; smatz@9315: if (s_amount > 0) { skidd13@10808: qsort(_fios_items.Get(sort_start), s_amount, sizeof(FiosItem), compare_FiosItems); smatz@9315: } truelight@0: } truelight@0: rubidium@6573: extern void StartupEngines(); darkvater@1131: rubidium@10499: struct SaveLoadWindow : public QueryStringBaseWindow { rubidium@10499: FiosItem o_dir; tron@4000: rubidium@10499: void GenerateFileName() rubidium@10499: { rubidium@10499: /* Check if we are not a spectator who wants to generate a name.. rubidium@10499: Let's use the name of player #0 for now. */ rubidium@11161: const Player *p = GetPlayer(IsValidPlayerID(_local_player) ? _local_player : PLAYER_FIRST); Darkvater@2100: rubidium@10499: SetDParam(0, p->index); rubidium@10499: SetDParam(1, _date); rubidium@10499: GetString(this->edit_str_buf, STR_4004, lastof(this->edit_str_buf)); rubidium@10499: SanitizeFilename(this->edit_str_buf); rubidium@10499: } rubidium@9234: rubidium@10499: SaveLoadWindow(const WindowDesc *desc, SaveLoadDialogMode mode) : QueryStringBaseWindow(desc) rubidium@10499: { rubidium@10499: static const StringID saveload_captions[] = { rubidium@10499: STR_4001_LOAD_GAME, rubidium@10499: STR_0298_LOAD_SCENARIO, rubidium@10499: STR_4000_SAVE_GAME, rubidium@10499: STR_0299_SAVE_SCENARIO, rubidium@10499: STR_LOAD_HEIGHTMAP, rubidium@10499: }; rubidium@9234: rubidium@10499: SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0); rubidium@10499: SetBit(_no_scroll, SCROLL_SAVE); rubidium@9234: rubidium@10499: /* Use an array to define what will be the current file type being handled rubidium@10499: * by current file mode */ rubidium@10499: switch (mode) { rubidium@10499: case SLD_SAVE_GAME: this->GenerateFileName(); break; rubidium@10499: case SLD_SAVE_SCENARIO: strcpy(this->edit_str_buf, "UNNAMED"); break; rubidium@10499: default: break; truelight@0: } Darkvater@2559: rubidium@10499: assert((uint)mode < lengthof(saveload_captions)); rubidium@9234: rubidium@10499: this->widget[1].data = saveload_captions[mode]; rubidium@10499: this->LowerWidget(7); rubidium@9234: rubidium@10499: this->afilter = CS_ALPHANUMERAL; rubidium@10499: InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 240); rubidium@10499: rubidium@10499: /* pause is only used in single-player, non-editor mode, non-menu mode. It rubidium@10499: * will be unpaused in the WE_DESTROY event handler. */ rubidium@10499: if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) { rubidium@10499: if (_pause_game >= 0) DoCommandP(0, 1, 0, NULL, CMD_PAUSE); rubidium@10499: } rubidium@10499: rubidium@10499: BuildFileList(); rubidium@10499: rubidium@10499: ResetObjectToPlace(); rubidium@10499: rubidium@10499: o_dir.type = FIOS_TYPE_DIRECT; rubidium@10499: switch (_saveload_mode) { rubidium@10499: case SLD_SAVE_GAME: rubidium@10499: case SLD_LOAD_GAME: rubidium@10499: FioGetDirectory(o_dir.name, lengthof(o_dir.name), SAVE_DIR); rubidium@10499: break; rubidium@10499: rubidium@10499: case SLD_SAVE_SCENARIO: rubidium@10499: case SLD_LOAD_SCENARIO: rubidium@10499: FioGetDirectory(o_dir.name, lengthof(o_dir.name), SCENARIO_DIR); rubidium@10499: break; rubidium@10499: rubidium@10499: case SLD_LOAD_HEIGHTMAP: rubidium@10499: FioGetDirectory(o_dir.name, lengthof(o_dir.name), HEIGHTMAP_DIR); rubidium@10499: break; rubidium@10499: rubidium@10499: default: rubidium@10499: ttd_strlcpy(o_dir.name, _personal_dir, lengthof(o_dir.name)); rubidium@10499: } rubidium@10499: rubidium@10499: this->vscroll.cap = 10; rubidium@10499: this->resize.step_width = 2; rubidium@10499: this->resize.step_height = 10; rubidium@10499: rubidium@10499: this->FindWindowPlacementAndResize(desc); rubidium@10499: } rubidium@10499: rubidium@10499: virtual ~SaveLoadWindow() rubidium@10499: { rubidium@10499: /* pause is only used in single-player, non-editor mode, non menu mode */ rubidium@10499: if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) { rubidium@10499: if (_pause_game >= 0) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); rubidium@10499: } rubidium@10499: FiosFreeSavegameList(); rubidium@10499: ClrBit(_no_scroll, SCROLL_SAVE); rubidium@10499: } rubidium@10499: rubidium@10499: virtual void OnPaint() rubidium@10499: { rubidium@10499: int y; rubidium@10499: skidd13@10808: SetVScrollCount(this, _fios_items.Length()); rubidium@10595: this->DrawWidgets(); rubidium@10499: DrawFiosTexts(this->width); rubidium@10499: rubidium@10499: if (_savegame_sort_dirty) { rubidium@10499: _savegame_sort_dirty = false; rubidium@10499: MakeSortedSaveGameList(); rubidium@10499: } rubidium@10499: rubidium@10499: GfxFillRect(this->widget[7].left + 1, this->widget[7].top + 1, this->widget[7].right, this->widget[7].bottom, 0xD7); rubidium@10595: this->DrawSortButtonState(_savegame_sort_order & SORT_BY_NAME ? 2 : 3, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP); rubidium@10499: rubidium@10499: y = this->widget[7].top + 1; skidd13@10808: for (uint pos = this->vscroll.pos; pos < _fios_items.Length(); pos++) { skidd13@10808: const FiosItem *item = _fios_items.Get(pos); rubidium@10499: rubidium@10499: DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], this->width - 18); rubidium@10499: y += 10; rubidium@10499: if (y >= this->vscroll.cap * 10 + this->widget[7].top + 1) break; rubidium@10499: } rubidium@10499: rubidium@10499: if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { rubidium@10499: this->DrawEditBox(10); rubidium@10499: } rubidium@10499: } rubidium@10499: rubidium@10499: virtual void OnClick(Point pt, int widget) rubidium@10499: { rubidium@10499: switch (widget) { rubidium@10499: case 2: // Sort save names by name rubidium@10499: _savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ? rubidium@10499: SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME; rubidium@10499: _savegame_sort_dirty = true; rubidium@10499: this->SetDirty(); rubidium@10499: break; rubidium@10499: rubidium@10499: case 3: // Sort save names by date rubidium@10499: _savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ? rubidium@10499: SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE; rubidium@10499: _savegame_sort_dirty = true; rubidium@10499: this->SetDirty(); rubidium@10499: break; rubidium@10499: rubidium@10499: case 6: // OpenTTD 'button', jumps to OpenTTD directory rubidium@10499: FiosBrowseTo(&o_dir); rubidium@10499: this->SetDirty(); rubidium@10499: BuildFileList(); rubidium@10499: break; rubidium@10499: rubidium@10499: case 7: { // Click the listbox rubidium@10499: int y = (pt.y - this->widget[widget].top - 1) / 10; rubidium@10499: rubidium@10499: if (y < 0 || (y += this->vscroll.pos) >= this->vscroll.count) return; rubidium@10499: skidd13@10808: const FiosItem *file = _fios_items.Get(y); rubidium@10499: skidd13@10808: char *name = FiosBrowseTo(file); rubidium@10499: if (name != NULL) { rubidium@10499: if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) { rubidium@10499: _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD; rubidium@10499: rubidium@10499: SetFiosType(file->type); rubidium@10499: ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); rubidium@10499: ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); rubidium@10499: rubidium@10499: delete this; rubidium@10499: } else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) { rubidium@10499: SetFiosType(file->type); rubidium@10499: ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); rubidium@10499: ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); rubidium@10499: rubidium@10499: delete this; rubidium@10499: ShowHeightmapLoad(); rubidium@9234: } else { rubidium@10499: /* SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox */ rubidium@10499: ttd_strlcpy(this->text.buf, file->title, this->text.maxlength); rubidium@10499: UpdateTextBufferSize(&this->text); rubidium@10499: this->InvalidateWidget(10); rubidium@9234: } rubidium@10499: } else { rubidium@10499: /* Changed directory, need repaint. */ rubidium@10499: this->SetDirty(); rubidium@10499: BuildFileList(); rubidium@9234: } rubidium@10499: break; tron@2255: } Darkvater@3288: rubidium@10499: case 10: // edit box rubidium@10499: ShowOnScreenKeyboard(this, widget, 0, 0); rubidium@10499: break; rubidium@9234: rubidium@10499: case 11: case 12: // Delete, Save game rubidium@10499: break; rubidium@10499: } rubidium@10499: } rubidium@9234: rubidium@10499: virtual void OnMouseLoop() rubidium@10499: { rubidium@10499: if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { rubidium@10499: this->HandleEditBox(10); rubidium@10499: } rubidium@10499: } rubidium@9234: rubidium@10607: virtual EventState OnKeyPress(uint16 key, uint16 keycode) rubidium@10499: { rubidium@10499: if (keycode == WKC_ESC) { rubidium@10499: delete this; rubidium@10607: return ES_HANDLED; rubidium@10499: } rubidium@9234: rubidium@10607: EventState state = ES_NOT_HANDLED; rubidium@10499: if ((_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) && rubidium@10607: this->HandleEditBoxKey(10, key, keycode, state) == 1) { // Press Enter rubidium@10499: this->HandleButtonClick(12); rubidium@10499: } rubidium@10499: rubidium@10607: return state; rubidium@10499: } rubidium@10499: rubidium@10499: virtual void OnTimeout() rubidium@10499: { rubidium@10499: /* This test protects against using widgets 11 and 12 which are only available rubidium@10499: * in those two saveload mode */ rubidium@10499: if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) return; rubidium@10499: rubidium@10499: if (this->IsWidgetLowered(11)) { // Delete button clicked rubidium@10499: if (!FiosDelete(this->text.buf)) { rubidium@10499: ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0); rubidium@10499: } else { rubidium@10499: BuildFileList(); rubidium@10499: /* Reset file name to current date on successful delete */ rubidium@10499: if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName(); rubidium@9234: } rubidium@9234: rubidium@10499: UpdateTextBufferSize(&this->text); rubidium@10499: this->SetDirty(); rubidium@10499: } else if (this->IsWidgetLowered(12)) { // Save button clicked rubidium@10499: _switch_mode = SM_SAVE; rubidium@10499: FiosMakeSavegameName(_file_to_saveload.name, this->text.buf, sizeof(_file_to_saveload.name)); rubidium@10499: rubidium@10499: /* In the editor set up the vehicle engines correctly (date might have changed) */ rubidium@10499: if (_game_mode == GM_EDITOR) StartupEngines(); rubidium@10499: } truelight@193: } rubidium@10499: rubidium@10499: virtual void OnResize(Point new_size, Point delta) rubidium@10499: { rubidium@10499: /* Widget 2 and 3 have to go with halve speed, make it so obiwan */ rubidium@10499: uint diff = delta.x / 2; rubidium@10499: this->widget[2].right += diff; rubidium@10499: this->widget[3].left += diff; rubidium@10499: this->widget[3].right += delta.x; rubidium@10499: rubidium@10499: /* Same for widget 11 and 12 in save-dialog */ rubidium@10499: if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { rubidium@10499: this->widget[11].right += diff; rubidium@10499: this->widget[12].left += diff; rubidium@10499: this->widget[12].right += delta.x; rubidium@10499: } rubidium@10499: rubidium@10499: this->vscroll.cap += delta.y / 10; rubidium@10499: } rubidium@10499: }; truelight@0: truelight@0: static const WindowDesc _load_dialog_desc = { rubidium@7842: WDP_CENTER, WDP_CENTER, 257, 154, 257, 294, rubidium@6144: WC_SAVELOAD, WC_NONE, belugas@8515: WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, glx@5079: _load_dialog_widgets, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _save_dialog_desc = { rubidium@7842: WDP_CENTER, WDP_CENTER, 257, 180, 257, 320, rubidium@6144: WC_SAVELOAD, WC_NONE, belugas@8515: WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, truelight@0: _save_dialog_widgets, truelight@0: }; truelight@0: belugas@8648: /** These values are used to convert the file/operations mode into a corresponding file type. belugas@8648: * So each entry, as expressed by the related comment, is based on the enum */ belugas@8648: static const FileType _file_modetotype[] = { belugas@8648: FT_SAVEGAME, ///< used for SLD_LOAD_GAME belugas@8648: FT_SCENARIO, ///< used for SLD_LOAD_SCENARIO belugas@8648: FT_SAVEGAME, ///< used for SLD_SAVE_GAME belugas@8648: FT_SCENARIO, ///< used for SLD_SAVE_SCENARIO belugas@8648: FT_HEIGHTMAP, ///< used for SLD_LOAD_HEIGHTMAP belugas@8648: FT_SAVEGAME, ///< SLD_NEW_GAME belugas@8648: }; belugas@8648: belugas@8645: void ShowSaveLoadDialog(SaveLoadDialogMode mode) truelight@0: { truelight@0: DeleteWindowById(WC_QUERY_STRING, 0); truelight@0: DeleteWindowById(WC_SAVELOAD, 0); truelight@0: rubidium@10499: const WindowDesc *sld; Darkvater@1397: switch (mode) { rubidium@10499: case SLD_SAVE_GAME: rubidium@10499: case SLD_SAVE_SCENARIO: rubidium@10499: sld = &_save_dialog_desc; break; rubidium@10499: default: rubidium@10499: sld = &_load_dialog_desc; break; Darkvater@1397: } truelight@0: rubidium@10499: _saveload_mode = mode; rubidium@10499: _file_to_saveload.filetype = _file_modetotype[mode]; truelight@0: rubidium@10499: new SaveLoadWindow(sld, mode); truelight@0: } truelight@0: rubidium@6573: void RedrawAutosave() truelight@0: { truelight@0: SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0)); truelight@0: } truelight@0: truelight@543: void SetFiosType(const byte fiostype) truelight@0: { truelight@0: switch (fiostype) { tron@2549: case FIOS_TYPE_FILE: tron@2549: case FIOS_TYPE_SCENARIO: tron@2549: _file_to_saveload.mode = SL_LOAD; tron@2549: break; tron@2549: tron@2549: case FIOS_TYPE_OLDFILE: tron@2549: case FIOS_TYPE_OLD_SCENARIO: tron@2549: _file_to_saveload.mode = SL_OLD_LOAD; tron@2549: break; tron@2549: truelight@4300: #ifdef WITH_PNG truelight@4300: case FIOS_TYPE_PNG: truelight@4300: _file_to_saveload.mode = SL_PNG; truelight@4300: break; truelight@4300: #endif /* WITH_PNG */ truelight@4300: truelight@4300: case FIOS_TYPE_BMP: truelight@4300: _file_to_saveload.mode = SL_BMP; truelight@4300: break; truelight@4300: tron@2549: default: tron@2549: _file_to_saveload.mode = SL_INVALID; tron@2549: break; truelight@0: } truelight@0: }