tron@2186: /* $Id$ */ tron@2186: belugas@6527: /** @file misc_gui.cpp */ 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" truelight@0: #include "station.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" 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@8615: rubidium@8760: #include "table/sprites.h" rubidium@8760: #include "table/strings.h" rubidium@8760: #include "table/tree_land.h" rubidium@8760: Darkvater@4223: /* Variables to display file lists */ Darkvater@4223: FiosItem *_fios_list; 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: rubidium@5834: enum { rubidium@5834: LAND_INFO_LINES = 7, rubidium@5834: LAND_INFO_LINE_BUFF_SIZE = 512, rubidium@5834: }; rubidium@5834: rubidium@5834: static char _landinfo_data[LAND_INFO_LINES][LAND_INFO_LINE_BUFF_SIZE]; truelight@0: truelight@0: static void LandInfoWndProc(Window *w, WindowEvent *e) truelight@0: { truelight@0: if (e->event == WE_PAINT) { truelight@0: DrawWindowWidgets(w); truelight@0: belugas@8320: DoDrawStringCentered(140, 16, _landinfo_data[0], TC_LIGHT_BLUE); belugas@8320: DoDrawStringCentered(140, 27, _landinfo_data[1], TC_FROMSTRING); belugas@8320: DoDrawStringCentered(140, 38, _landinfo_data[2], TC_FROMSTRING); belugas@8320: DoDrawStringCentered(140, 49, _landinfo_data[3], TC_FROMSTRING); belugas@8320: DoDrawStringCentered(140, 60, _landinfo_data[4], TC_FROMSTRING); Darkvater@6523: if (_landinfo_data[5][0] != '\0') DrawStringMultiCenter(140, 76, BindCString(_landinfo_data[5]), w->width - 4); belugas@8320: if (_landinfo_data[6][0] != '\0') DoDrawStringCentered(140, 71, _landinfo_data[6], TC_FROMSTRING); truelight@0: } truelight@0: } truelight@0: 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}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 279, 14, 92, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _land_info_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 280, 93, 280, 93, 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: LandInfoWndProc truelight@0: }; truelight@0: tron@1977: static void Place_LandInfo(TileIndex tile) truelight@0: { truelight@0: Player *p; truelight@0: Window *w; rubidium@5834: Town *t; rubidium@7449: Money old_money; rubidium@7446: CommandCost costclear; rubidium@5834: AcceptedCargo ac; rubidium@5834: TileDesc td; rubidium@5834: StringID str; truelight@0: truelight@0: DeleteWindowById(WC_LAND_INFO, 0); truelight@0: truelight@0: w = AllocateWindowDesc(&_land_info_desc); rubidium@5834: WP(w, void_d).data = &_landinfo_data; truelight@0: rubidium@5838: p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST); rubidium@5834: t = ClosestTownFromTile(tile, _patches.dist_local_authority); darkvater@3: rubidium@7448: old_money = p->player_money; rubidium@7486: p->player_money = INT64_MAX; rubidium@5834: costclear = DoCommand(tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR); rubidium@7448: p->player_money = old_money; truelight@193: rubidium@5834: /* Because build_date is not set yet in every TileDesc, we make sure it is empty */ rubidium@5834: td.build_date = 0; rubidium@5834: GetAcceptedCargo(tile, ac); rubidium@5834: GetTileDesc(tile, &td); truelight@193: rubidium@5834: SetDParam(0, td.dparam[0]); rubidium@5834: GetString(_landinfo_data[0], td.str, lastof(_landinfo_data[0])); rubidium@5834: rubidium@5834: SetDParam(0, STR_01A6_N_A); rubidium@5834: if (td.owner != OWNER_NONE && td.owner != OWNER_WATER) GetNameOfOwner(td.owner, tile); rubidium@5834: GetString(_landinfo_data[1], STR_01A7_OWNER, lastof(_landinfo_data[1])); rubidium@5834: rubidium@5834: str = STR_01A4_COST_TO_CLEAR_N_A; rubidium@7442: if (CmdSucceeded(costclear)) { rubidium@7498: SetDParam(0, costclear.GetCost()); rubidium@5834: str = STR_01A5_COST_TO_CLEAR; rubidium@5834: } rubidium@5834: GetString(_landinfo_data[2], str, lastof(_landinfo_data[2])); rubidium@5834: rubidium@5834: snprintf(_userstring, lengthof(_userstring), "0x%.4X", tile); rubidium@5834: SetDParam(0, TileX(tile)); rubidium@5834: SetDParam(1, TileY(tile)); truelight@7934: SetDParam(2, TileHeight(tile)); truelight@7934: SetDParam(3, STR_SPEC_USERSTRING); rubidium@5834: GetString(_landinfo_data[3], STR_LANDINFO_COORDS, lastof(_landinfo_data[3])); rubidium@5834: rubidium@5834: SetDParam(0, STR_01A9_NONE); rubidium@7882: if (t != NULL && t->IsValid()) { rubidium@5834: SetDParam(0, STR_TOWN); rubidium@5834: SetDParam(1, t->index); rubidium@5834: } rubidium@5834: GetString(_landinfo_data[4], STR_01A8_LOCAL_AUTHORITY, lastof(_landinfo_data[4])); rubidium@5834: rubidium@5834: { rubidium@5834: char *p = GetString(_landinfo_data[5], STR_01CE_CARGO_ACCEPTED, lastof(_landinfo_data[5])); rubidium@5834: bool found = false; rubidium@5834: peter1138@6676: for (CargoID i = 0; i < NUM_CARGO; ++i) { rubidium@5834: if (ac[i] > 0) { rubidium@5834: /* Add a comma between each item. */ rubidium@5834: if (found) { rubidium@5834: *p++ = ','; rubidium@5834: *p++ = ' '; rubidium@5834: } rubidium@5834: found = true; rubidium@5834: rubidium@5834: /* If the accepted value is less than 8, show it in 1/8:ths */ rubidium@5834: if (ac[i] < 8) { rubidium@5834: SetDParam(0, ac[i]); peter1138@6417: SetDParam(1, GetCargo(i)->name); rubidium@5834: p = GetString(p, STR_01D1_8, lastof(_landinfo_data[5])); rubidium@5834: } else { peter1138@6417: p = GetString(p, GetCargo(i)->name, lastof(_landinfo_data[5])); rubidium@5834: } rubidium@5834: } rubidium@5834: } rubidium@5834: rubidium@5834: if (!found) _landinfo_data[5][0] = '\0'; rubidium@5834: } rubidium@5834: rubidium@5834: if (td.build_date != 0) { rubidium@5834: SetDParam(0, td.build_date); rubidium@5838: GetString(_landinfo_data[6], STR_BUILD_DATE, lastof(_landinfo_data[6])); rubidium@5834: } else { rubidium@5834: _landinfo_data[6][0] = '\0'; rubidium@5834: } truelight@193: tron@4000: #if defined(_DEBUG) tron@4000: # define LANDINFOD_LEVEL 0 tron@4000: #else tron@4000: # define LANDINFOD_LEVEL 1 tron@4000: #endif Darkvater@5568: DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile)); Darkvater@5568: DEBUG(misc, LANDINFOD_LEVEL, "type_height = %#x", _m[tile].type_height); Darkvater@5568: DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", _m[tile].m1); Darkvater@5568: DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", _m[tile].m2); Darkvater@5568: DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", _m[tile].m3); Darkvater@5568: DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4); Darkvater@5568: DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5); belugas@5847: DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _m[tile].m6); maedhros@6658: DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7); tron@4000: #undef LANDINFOD_LEVEL 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: darkvater@859: static const char *credits[] = { darkvater@859: /************************************************************************* darkvater@859: * maximum length of string which fits in window -^*/ darkvater@859: "Original design by Chris Sawyer", darkvater@859: "Original graphics by Simon Foster", darkvater@859: "", darkvater@1124: "The OpenTTD team (in alphabetical order):", belugas@8555: " Jean-Francois Claeys (Belugas) - GUI, newindustries and more", belugas@8555: " Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles", belugas@8555: " Matthijs Kooijman (blathijs) - Pathfinder-guru, pool rework", belugas@8555: " Loïc Guilloux (glx) - General coding", belugas@9119: " Christoph Elsenhans (frosch) - General coding", Darkvater@5355: " Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)", belugas@6565: " Jonathan Coome (Maedhros) - High priest of the newGRF Temple", peter1138@5108: " Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host", truelight@7570: " Owen Rudge (orudge) - Forum host, OS/2 port", belugas@8680: " Peter Nelson (peter1138) - Spiritual descendant from newGRF gods", belugas@8555: " Remko Bijker (Rubidium) - Lead coder and way more", belugas@8555: " Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker", belugas@8555: " Zdenek Sojka (SmatZ) - Bug finder and fixer", belugas@8555: "", belugas@8555: "Inactive Developers:", belugas@8555: " Victor Fischer (Celestar) - Programming everywhere you need him to", belugas@8555: " Tamás Faragó (Darkvater) - Ex-Lead coder", darkvater@1126: " Christoph Mallon (Tron) - Programmer, code correctness police", darkvater@1125: "", darkvater@1125: "Retired Developers:", darkvater@1125: " Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)", darkvater@859: " Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)", Darkvater@1826: " Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)", truelight@8355: " Patric Stout (TrueLight) - Programmer, webhoster (0.3 - pre0.6)", darkvater@859: "", darkvater@859: "Special thanks go out to:", darkvater@859: " Josef Drexler - For his great work on TTDPatch", darkvater@859: " Marcin Grzegorczyk - For his documentation of TTD internals", belugas@8555: " Petr Baudis (pasky) - Many patches, newGRF support", peter1138@5108: " Stefan Meißner (sign_de) - For his work on the console", belugas@8555: " Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with", darkvater@1124: " Cian Duffy (MYOB) - BeOS port / manual writing", truelight@4133: " Christian Rosentreter (tokai) - MorphOS / AmigaOS port", Darkvater@5355: " Richard Kempton (richK) - additional airports, initial TGP implementation", darkvater@859: "", peter1138@5108: " Michael Blunck - Pre-Signals and Semaphores © 2003", peter1138@5108: " George - Canal/Lock graphics © 2003-2004", rubidium@7187: " David Dallaston - Tram tracks", darkvater@859: " Marcin Grzegorczyk - Foundations for Tracks on Slopes", darkvater@859: " All Translators - Who made OpenTTD a truly international game", darkvater@859: " Bug Reporters - Without whom OpenTTD would still be full of bugs!", darkvater@859: "", darkvater@859: "", darkvater@1124: "And last but not least:", darkvater@859: " Chris Sawyer - For an amazing game!" darkvater@859: }; truelight@0: truelight@0: static void AboutWindowProc(Window *w, WindowEvent *e) truelight@0: { tron@2639: switch (e->event) { belugas@6527: case WE_CREATE: // Set up window counter and start position of scroller skidd13@8549: WP(w, scroller_d).counter = 5; darkvater@998: WP(w, scroller_d).height = w->height - 40; darkvater@859: break; truelight@0: case WE_PAINT: { tron@959: uint i; darkvater@998: int y = WP(w, scroller_d).height; truelight@0: DrawWindowWidgets(w); truelight@0: belugas@6527: /* Show original copyright and revision version */ belugas@8320: DrawStringCentered(210, 17, STR_00B6_ORIGINAL_COPYRIGHT, TC_FROMSTRING); belugas@8320: DrawStringCentered(210, 17 + 10, STR_00B7_VERSION, TC_FROMSTRING); darkvater@859: belugas@6527: /* Show all scrolling credits */ darkvater@859: for (i = 0; i < lengthof(credits); i++) { darkvater@859: if (y >= 50 && y < (w->height - 40)) { belugas@8320: DoDrawString(credits[i], 10, y, TC_BLACK); darkvater@859: } darkvater@859: y += 10; darkvater@859: } darkvater@859: belugas@6527: /* If the last text has scrolled start anew from the start */ darkvater@998: if (y < 50) WP(w, scroller_d).height = w->height - 40; darkvater@859: belugas@8320: DoDrawStringCentered(210, w->height - 25, "Website: http://www.openttd.org", TC_BLACK); belugas@8320: DrawStringCentered(210, w->height - 15, STR_00BA_COPYRIGHT_OPENTTD, TC_FROMSTRING); rubidium@6988: } break; skidd13@8549: case WE_TICK: // Timer to scroll the text and adjust the new top skidd13@8549: if (--WP(w, scroller_d).counter == 0) { skidd13@8549: WP(w, scroller_d).counter = 5; darkvater@998: WP(w, scroller_d).height--; darkvater@859: SetWindowDirty(w); darkvater@859: } truelight@0: break; 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: AboutWindowProc truelight@0: }; truelight@0: truelight@0: rubidium@6573: void ShowAboutWindow() truelight@0: { truelight@0: DeleteWindowById(WC_GAME_OPTIONS, 0); truelight@0: AllocateWindowDesc(&_about_desc); truelight@0: } truelight@0: truelight@0: static int _tree_to_plant; truelight@0: peter1138@5919: static const PalSpriteID _tree_sprites[] = { peter1138@5919: { 0x655, PAL_NONE }, { 0x663, PAL_NONE }, { 0x678, PAL_NONE }, { 0x62B, PAL_NONE }, peter1138@5919: { 0x647, PAL_NONE }, { 0x639, PAL_NONE }, { 0x64E, PAL_NONE }, { 0x632, PAL_NONE }, peter1138@5919: { 0x67F, PAL_NONE }, { 0x68D, PAL_NONE }, { 0x69B, PAL_NONE }, { 0x6A9, PAL_NONE }, peter1138@5919: { 0x6AF, PAL_NONE }, { 0x6D2, PAL_NONE }, { 0x6D9, PAL_NONE }, { 0x6C4, PAL_NONE }, peter1138@5919: { 0x6CB, PAL_NONE }, { 0x6B6, PAL_NONE }, { 0x6BD, PAL_NONE }, { 0x6E0, PAL_NONE }, peter1138@5919: { 0x72E, PAL_NONE }, { 0x734, PAL_NONE }, { 0x74A, PAL_NONE }, { 0x74F, PAL_NONE }, peter1138@5919: { 0x76B, PAL_NONE }, { 0x78F, PAL_NONE }, { 0x788, PAL_NONE }, { 0x77B, PAL_NONE }, peter1138@5919: { 0x75F, PAL_NONE }, { 0x774, PAL_NONE }, { 0x720, PAL_NONE }, { 0x797, PAL_NONE }, peter1138@5919: { 0x79E, PAL_NONE }, { 0x7A5, PALETTE_TO_GREEN }, { 0x7AC, PALETTE_TO_RED }, { 0x7B3, PAL_NONE }, peter1138@5919: { 0x7BA, PAL_NONE }, { 0x7C1, PALETTE_TO_RED, }, { 0x7C8, PALETTE_TO_PALE_GREEN }, { 0x7CF, PALETTE_TO_YELLOW }, { 0x7D6, PALETTE_TO_RED } truelight@0: }; truelight@0: truelight@0: static void BuildTreesWndProc(Window *w, WindowEvent *e) truelight@0: { tron@2639: switch (e->event) { truelight@0: case WE_PAINT: { truelight@0: int x,y; truelight@0: int i, count; truelight@0: truelight@0: DrawWindowWidgets(w); truelight@0: rubidium@8578: WP(w, tree_d).base = i = _tree_base_by_landscape[_opt.landscape]; rubidium@8578: WP(w, tree_d).count = count = _tree_count_by_landscape[_opt.landscape]; truelight@0: truelight@0: x = 18; truelight@0: y = 54; truelight@0: do { peter1138@5919: DrawSprite(_tree_sprites[i].sprite, _tree_sprites[i].pal, x, y); truelight@0: x += 35; truelight@0: if (!(++i & 3)) { tron@2639: x -= 35 * 4; truelight@0: y += 47; truelight@0: } truelight@0: } while (--count); truelight@0: } break; truelight@0: truelight@0: case WE_CLICK: { belugas@4634: int wid = e->we.click.widget; truelight@0: tron@2639: switch (wid) { truelight@0: case 0: truelight@0: ResetObjectToPlace(); tron@2639: break; tron@2639: truelight@0: case 3: case 4: case 5: case 6: truelight@0: case 7: case 8: case 9: case 10: truelight@0: case 11:case 12: case 13: case 14: rubidium@8578: if (wid - 3 >= WP(w, tree_d).count) break; truelight@193: rubidium@8385: if (HandlePlacePushButton(w, wid, SPR_CURSOR_TREE, VHM_RECT, NULL)) rubidium@8578: _tree_to_plant = WP(w, tree_d).base + wid - 3; truelight@0: break; truelight@0: truelight@0: case 15: // tree of random type. rubidium@8385: if (HandlePlacePushButton(w, 15, SPR_CURSOR_TREE, VHM_RECT, NULL)) truelight@0: _tree_to_plant = -1; truelight@0: break; truelight@0: belugas@6527: case 16: // place trees randomly over the landscape rubidium@8493: w->LowerWidget(16); truelight@0: w->flags4 |= 5 << WF_TIMEOUT_SHL; tron@541: SndPlayFx(SND_15_BEEP); truelight@0: PlaceTreesRandomly(); truelight@0: MarkWholeScreenDirty(); truelight@0: break; truelight@0: } truelight@0: } break; truelight@193: truelight@0: case WE_PLACE_OBJ: maedhros@7166: VpStartPlaceSizing(e->we.place.tile, VPM_X_AND_Y_LIMITED, DDSP_PLANT_TREES); truelight@0: VpSetPlaceSizingLimit(20); truelight@0: break; truelight@0: truelight@0: case WE_PLACE_DRAG: maedhros@7165: VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method); truelight@0: return; truelight@0: truelight@0: case WE_PLACE_MOUSEUP: maedhros@7166: if (e->we.place.pt.x != -1 && e->we.place.select_proc == DDSP_PLANT_TREES) { belugas@4634: DoCommandP(e->we.place.tile, _tree_to_plant, e->we.place.starttile, NULL, rubidium@8017: CMD_PLANT_TREE | CMD_MSG(STR_2805_CAN_T_PLANT_TREE_HERE)); truelight@0: } truelight@0: break; truelight@0: truelight@0: case WE_TIMEOUT: rubidium@8493: w->RaiseWidget(16); truelight@0: break; truelight@0: truelight@0: case WE_ABORT_PLACE_OBJ: belugas@8528: w->RaiseButtons(); truelight@0: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static const Widget _build_trees_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 7, 11, 142, 0, 13, STR_2802_TREES, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 7, 0, 142, 14, 170, 0x0, STR_NULL}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 157, 168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _build_trees_desc = { rubidium@7837: 497, 22, 143, 171, 143, 171, dominik@606: WC_BUILD_TREES, WC_SCEN_LAND_GEN, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, truelight@0: _build_trees_widgets, truelight@0: BuildTreesWndProc truelight@0: }; truelight@0: truelight@0: static const Widget _build_trees_scen_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 7, 11, 142, 0, 13, STR_2802_TREES, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 7, 0, 142, 14, 183, 0x0, STR_NULL}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 16, 61, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 63, 108, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 2, 35, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 37, 70, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 72, 105, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 107, 140, 110, 155, 0x0, STR_280D_SELECT_TREE_TYPE_TO_PLANT}, rubidium@4344: { WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 157, 168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP}, rubidium@4344: { WWT_TEXTBTN, RESIZE_NONE, 14, 2, 140, 170, 181, STR_028A_RANDOM_TREES, STR_028B_PLANT_TREES_RANDOMLY_OVER}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _build_trees_scen_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 143, 184, 143, 184, rubidium@6144: WC_BUILD_TREES, WC_NONE, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, truelight@0: _build_trees_scen_widgets, truelight@0: BuildTreesWndProc truelight@0: }; truelight@0: truelight@193: rubidium@6573: void ShowBuildTreesToolbar() truelight@0: { Darkvater@5005: if (!IsValidPlayer(_current_player)) return; tron@1846: AllocateWindowDescFront(&_build_trees_desc, 0); truelight@0: } truelight@0: rubidium@6573: void ShowBuildTreesScenToolbar() truelight@0: { truelight@0: AllocateWindowDescFront(&_build_trees_scen_desc, 0); truelight@0: } truelight@0: rubidium@7498: static uint64 _errmsg_decode_params[20]; truelight@0: static StringID _errmsg_message_1, _errmsg_message_2; truelight@0: static uint _errmsg_duration; 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: truelight@0: static void ErrmsgWndProc(Window *w, WindowEvent *e) truelight@0: { tron@2639: switch (e->event) { truelight@0: case WE_PAINT: rubidium@7762: CopyInDParam(0, _errmsg_decode_params, lengthof(_errmsg_decode_params)); truelight@0: DrawWindowWidgets(w); rubidium@7762: CopyInDParam(0, _errmsg_decode_params, lengthof(_errmsg_decode_params)); rubidium@8246: rubidium@8246: /* If the error message comes from a NewGRF, we must use the text ref. stack reserved for error messages. rubidium@8246: * 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@8246: */ rubidium@8246: SwitchToErrorRefStack(); rubidium@8246: RewindTextRefStack(); rubidium@8246: truelight@867: if (!IsWindowOfPrototype(w, _errmsg_face_widgets)) { truelight@0: DrawStringMultiCenter( truelight@0: 120, truelight@193: (_errmsg_message_1 == INVALID_STRING_ID ? 25 : 15), truelight@0: _errmsg_message_2, Darkvater@6523: w->width - 2); truelight@0: if (_errmsg_message_1 != INVALID_STRING_ID) truelight@0: DrawStringMultiCenter( truelight@0: 120, truelight@0: 30, truelight@0: _errmsg_message_1, Darkvater@6523: w->width - 2); truelight@0: } else { rubidium@5838: const Player *p = GetPlayer((PlayerID)GetDParamX(_errmsg_decode_params,2)); truelight@0: DrawPlayerFace(p->face, p->player_color, 2, 16); truelight@0: truelight@0: DrawStringMultiCenter( truelight@0: 214, truelight@193: (_errmsg_message_1 == INVALID_STRING_ID ? 65 : 45), truelight@0: _errmsg_message_2, Darkvater@6523: w->width - 2); truelight@0: if (_errmsg_message_1 != INVALID_STRING_ID) truelight@0: DrawStringMultiCenter( truelight@0: 214, truelight@0: 90, truelight@0: _errmsg_message_1, Darkvater@6523: w->width - 2); truelight@0: } rubidium@8246: rubidium@8246: /* Switch back to the normal text ref. stack for NewGRF texts */ rubidium@8246: SwitchToNormalRefStack(); truelight@0: break; truelight@0: truelight@0: case WE_MOUSELOOP: tron@2639: if (_right_button_down) DeleteWindow(w); truelight@0: break; tron@2639: truelight@0: case WE_4: tron@2639: if (--_errmsg_duration == 0) DeleteWindow(w); truelight@0: break; tron@2639: tron@2639: case WE_DESTROY: tron@1860: SetRedErrorSquare(0); darkvater@172: _switch_mode_errorstr = INVALID_STRING_ID; truelight@0: break; truelight@0: truelight@0: case WE_KEYPRESS: belugas@4634: if (e->we.keypress.keycode == WKC_SPACE) { belugas@6527: /* Don't continue. */ belugas@4634: e->we.keypress.cont = false; truelight@0: DeleteWindow(w); truelight@0: } truelight@0: break; truelight@0: } truelight@0: } truelight@0: truelight@0: void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y) truelight@0: { truelight@0: Window *w; Darkvater@5120: const ViewPort *vp; truelight@0: Point pt; truelight@0: truelight@0: DeleteWindowById(WC_ERRMSG, 0); truelight@0: truelight@0: //assert(msg_2); truelight@0: if (msg_2 == 0) msg_2 = STR_EMPTY; truelight@0: truelight@0: _errmsg_message_1 = msg_1; truelight@0: _errmsg_message_2 = msg_2; rubidium@7762: CopyOutDParam(_errmsg_decode_params, 0, lengthof(_errmsg_decode_params)); truelight@0: _errmsg_duration = _patches.errmsg_duration; Darkvater@5120: if (!_errmsg_duration) return; truelight@193: tron@534: if (_errmsg_message_1 != STR_013B_OWNED_BY || GetDParamX(_errmsg_decode_params,2) >= 8) { truelight@0: truelight@0: 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: } truelight@0: w = AllocateWindow(pt.x, pt.y, 240, 46, ErrmsgWndProc, WC_ERRMSG, _errmsg_widgets); truelight@0: } else { truelight@0: 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: } truelight@0: w = AllocateWindow(pt.x, pt.y, 334, 137, ErrmsgWndProc, WC_ERRMSG, _errmsg_face_widgets); truelight@0: } truelight@0: truelight@0: w->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET; 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: { tron@2498: StringID msg; truelight@0: Point pt = RemapCoords(x,y,z); truelight@0: truelight@0: msg = STR_0801_COST; 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: truelight@0: truelight@0: static void TooltipsWndProc(Window *w, WindowEvent *e) truelight@0: { tron@2639: switch (e->event) { Darkvater@4834: case WE_PAINT: { Darkvater@4834: uint arg; tron@2639: GfxFillRect(0, 0, w->width - 1, w->height - 1, 0); tron@2639: GfxFillRect(1, 1, w->width - 2, w->height - 2, 0x44); Darkvater@4834: Darkvater@4834: for (arg = 0; arg < WP(w, tooltips_d).paramcount; arg++) { Darkvater@4834: SetDParam(arg, WP(w, tooltips_d).params[arg]); Darkvater@4834: } Darkvater@6523: DrawStringMultiCenter((w->width >> 1), (w->height >> 1) - 5, WP(w, tooltips_d).string_id, w->width - 2); tron@2639: break; Darkvater@4834: } truelight@0: tron@2639: case WE_MOUSELOOP: Darkvater@4834: /* We can show tooltips while dragging tools. These are shown as long as Darkvater@4834: * we are dragging the tool. Normal tooltips work with rmb */ Darkvater@4834: if (WP(w, tooltips_d).paramcount == 0 ) { Darkvater@4834: if (!_right_button_down) DeleteWindow(w); Darkvater@4834: } else { Darkvater@4834: if (!_left_button_down) DeleteWindow(w); Darkvater@4834: } Darkvater@4834: tron@2639: break; truelight@0: } truelight@0: } 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: { tron@1336: char buffer[512]; rubidium@8617: Dimension br; Darkvater@4884: Window *w; Darkvater@4834: uint i; Darkvater@4834: int x, y; truelight@0: Darkvater@4884: DeleteWindowById(WC_TOOLTIPS, 0); truelight@0: Darkvater@4884: /* We only show measurement tooltips with patch setting on */ Darkvater@4884: if (str == STR_NULL || (paramcount != 0 && !_patches.measure_tooltip)) return; truelight@0: Darkvater@4834: for (i = 0; i != paramcount; i++) SetDParam(i, params[i]); Darkvater@4912: GetString(buffer, str, lastof(buffer)); Darkvater@4834: Darkvater@4834: 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 */ skidd13@8418: 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; skidd13@8418: x = Clamp(_cursor.pos.x - (br.width >> 1), 0, _screen.width - br.width); truelight@0: Darkvater@4834: w = AllocateWindow(x, y, br.width, br.height, TooltipsWndProc, WC_TOOLTIPS, _tooltips_widgets); Darkvater@4834: Darkvater@4834: WP(w, tooltips_d).string_id = str; Darkvater@4834: assert(sizeof(WP(w, tooltips_d).params[0]) == sizeof(params[0])); Darkvater@4834: memcpy(WP(w, tooltips_d).params, params, sizeof(WP(w, tooltips_d).params[0]) * paramcount); Darkvater@4834: WP(w, tooltips_d).paramcount = paramcount; Darkvater@4834: Darkvater@4558: w->flags4 &= ~WF_WHITE_BORDER_MASK; // remove white-border from tooltip Darkvater@4834: w->widget[0].right = br.width; Darkvater@4834: w->widget[0].bottom = br.height; truelight@0: } truelight@0: truelight@0: peter1138@8919: static int DrawStationCoverageText(const AcceptedCargo accepts, rubidium@7737: int str_x, int str_y, StationCoverageType sct) truelight@0: { tron@1556: char *b = _userstring; peter1138@5038: bool first = true; truelight@193: tron@1553: b = InlineString(b, STR_000D_ACCEPTS); truelight@0: rubidium@7737: for (CargoID i = 0; i < NUM_CARGO; i++) { smatz@8537: if (b >= lastof(_userstring) - (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: } rubidium@7737: if (accepts[i] >= 8) { 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 */ smatz@8537: assert(b < endof(_userstring)); smatz@8537: peter1138@8919: return DrawStringMultiLine(str_x, str_y, STR_SPEC_USERSTRING, 144); truelight@0: } truelight@0: peter1138@8919: int DrawStationCoverageAreaText(int sx, int sy, StationCoverageType sct, int rad) rubidium@7817: { peter1138@2873: TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y); tron@2498: AcceptedCargo accepts; peter1138@2873: if (tile < MapSize()) { celestar@3421: GetAcceptanceAroundTiles(accepts, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad); peter1138@8919: return sy + DrawStationCoverageText(accepts, sx, sy, sct); 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: truelight@0: void SetVScrollCount(Window *w, int num) truelight@0: { truelight@0: w->vscroll.count = num; truelight@0: num -= w->vscroll.cap; truelight@0: if (num < 0) num = 0; truelight@0: if (num < w->vscroll.pos) w->vscroll.pos = num; truelight@0: } truelight@0: bjarni@842: void SetVScroll2Count(Window *w, int num) truelight@867: { bjarni@842: w->vscroll2.count = num; bjarni@842: num -= w->vscroll2.cap; bjarni@842: if (num < 0) num = 0; bjarni@842: if (num < w->vscroll2.pos) w->vscroll2.pos = num; bjarni@842: } bjarni@842: truelight@0: void SetHScrollCount(Window *w, int num) truelight@0: { truelight@0: w->hscroll.count = num; truelight@0: num -= w->hscroll.cap; truelight@0: if (num < 0) num = 0; truelight@0: if (num < w->hscroll.pos) w->hscroll.pos = num; 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; peter1138@5108: uint width; peter1138@5108: size_t len; Darkvater@6540: char *s = tb->buf + tb->caretpos; peter1138@5108: Darkvater@6540: if (backspace) s = Utf8PrevChar(s); peter1138@5108: Darkvater@6540: len = Utf8Decode(&c, s); peter1138@5108: 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); peter1138@5108: size_t len = 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) { Darkvater@1390: case WKC_LEFT: Darkvater@1390: if (tb->caretpos != 0) { peter1138@5108: WChar c; Darkvater@6540: const char *s = Utf8PrevChar(tb->buf + tb->caretpos); Darkvater@6540: Utf8Decode(&c, s); Darkvater@6540: tb->caretpos = s - tb->buf; // -= (tb->buf + tb->caretpos - s) peter1138@5108: tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c); peter1138@5108: Darkvater@1390: return true; Darkvater@1390: } Darkvater@1390: break; Darkvater@1390: case WKC_RIGHT: Darkvater@1390: if (tb->caretpos < tb->length) { peter1138@5108: WChar c; peter1138@5108: peter1138@5108: tb->caretpos += Utf8Decode(&c, tb->buf + tb->caretpos); peter1138@5108: tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c); peter1138@5108: Darkvater@1390: return true; Darkvater@1390: } Darkvater@1390: break; Darkvater@1390: case WKC_HOME: Darkvater@1390: tb->caretpos = 0; Darkvater@1390: tb->caretxoffs = 0; Darkvater@1390: return true; Darkvater@1390: case WKC_END: Darkvater@1390: tb->caretpos = tb->length; Darkvater@1390: tb->caretxoffs = tb->width; Darkvater@1390: return true; 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@4909: int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *e) truelight@0: { belugas@4634: e->we.keypress.cont = false; truelight@0: belugas@4634: switch (e->we.keypress.keycode) { Darkvater@1390: case WKC_ESC: return 2; Darkvater@1390: case WKC_RETURN: case WKC_NUM_ENTER: return 1; Darkvater@1390: case (WKC_CTRL | 'V'): Darkvater@2887: if (InsertTextBufferClipboard(&string->text)) glx@8524: w->InvalidateWidget(wid); Darkvater@1390: break; Darkvater@1879: case (WKC_CTRL | 'U'): Darkvater@2887: DeleteTextBufferAll(&string->text); glx@8524: w->InvalidateWidget(wid); Darkvater@1879: break; Darkvater@1390: case WKC_BACKSPACE: case WKC_DELETE: belugas@4634: if (DeleteTextBufferChar(&string->text, e->we.keypress.keycode)) glx@8524: w->InvalidateWidget(wid); Darkvater@1390: break; Darkvater@1390: case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: belugas@4634: if (MoveTextBufferPos(&string->text, e->we.keypress.keycode)) glx@8524: w->InvalidateWidget(wid); tron@4077: break; Darkvater@1390: default: peter1138@5108: if (IsValidChar(e->we.keypress.key, string->afilter)) { peter1138@5108: if (InsertTextBufferChar(&string->text, e->we.keypress.key)) { glx@8524: w->InvalidateWidget(wid); peter1138@5108: } truelight@4299: } else { // key wasn't caught. Continue only if standard entry specified Darkvater@4909: e->we.keypress.cont = (string->afilter == CS_ALPHANUMERAL); tron@4077: } truelight@0: } truelight@0: truelight@0: return 0; truelight@0: } truelight@0: 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: Darkvater@2887: void HandleEditBox(Window *w, querystr_d *string, int wid) truelight@0: { glx@8524: if (HandleCaret(&string->text)) w->InvalidateWidget(wid); truelight@0: } truelight@0: Darkvater@2887: void DrawEditBox(Window *w, querystr_d *string, int wid) truelight@0: { Darkvater@4955: DrawPixelInfo dpi, *old_dpi; Darkvater@4955: int delta; Darkvater@4955: const Widget *wi = &w->widget[wid]; Darkvater@2887: const Textbuf *tb = &string->text; truelight@0: Darkvater@5890: GfxFillRect(wi->left + 1, wi->top + 1, wi->right - 1, wi->bottom - 1, 215); Darkvater@5890: Darkvater@4955: /* Limit the drawing of the string inside the widget boundaries */ Darkvater@4955: if (!FillDrawPixelInfo(&dpi, Darkvater@5120: wi->left + 4, Darkvater@5120: wi->top + 1, Darkvater@5120: wi->right - wi->left - 4, Darkvater@5120: wi->bottom - wi->top - 1) Darkvater@4955: ) return; Darkvater@4955: Darkvater@4955: 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 */ 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: 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: truelight@0: static void QueryStringWndProc(Window *w, WindowEvent *e) truelight@0: { Darkvater@5682: querystr_d *qs = &WP(w, querystr_d); truelight@0: Darkvater@5682: switch (e->event) { Darkvater@5682: case WE_CREATE: skidd13@8427: SetBit(_no_scroll, SCROLL_EDIT); Darkvater@5682: break; tron@390: Darkvater@5682: case WE_PAINT: Darkvater@5682: SetDParam(0, qs->caption); Darkvater@5682: DrawWindowWidgets(w); truelight@598: Darkvater@5682: DrawEditBox(w, qs, QUERY_STR_WIDGET_TEXT); Darkvater@5682: break; Darkvater@5682: Darkvater@5682: case WE_CLICK: Darkvater@5682: switch (e->we.click.widget) { Darkvater@5682: case QUERY_STR_WIDGET_OK: Darkvater@5682: press_ok:; Darkvater@5682: if (qs->orig == NULL || strcmp(qs->text.buf, qs->orig) != 0) { Darkvater@5682: Window *parent = w->parent; Darkvater@5682: qs->handled = true; Darkvater@5682: Darkvater@5682: /* If the parent is NULL, the editbox is handled by general function Darkvater@5682: * HandleOnEditText */ Darkvater@5682: if (parent != NULL) { Darkvater@5682: WindowEvent e; Darkvater@5682: e.event = WE_ON_EDIT_TEXT; Darkvater@5682: e.we.edittext.str = qs->text.buf; Darkvater@5682: parent->wndproc(parent, &e); Darkvater@5682: } else { Darkvater@5682: HandleOnEditText(qs->text.buf); Darkvater@5682: } Darkvater@5682: } Darkvater@5682: /* Fallthrough */ Darkvater@5682: case QUERY_STR_WIDGET_CANCEL: Darkvater@5682: DeleteWindow(w); Darkvater@5682: break; truelight@0: } truelight@0: break; truelight@0: Darkvater@5682: case WE_MOUSELOOP: Darkvater@5682: HandleEditBox(w, qs, QUERY_STR_WIDGET_TEXT); truelight@0: break; truelight@0: Darkvater@5682: case WE_KEYPRESS: Darkvater@5682: switch (HandleEditBoxKey(w, qs, QUERY_STR_WIDGET_TEXT, e)) { belugas@6527: case 1: goto press_ok; // Enter pressed, confirms change belugas@6527: case 2: DeleteWindow(w); break; // ESC pressed, closes window, abandons changes Darkvater@5682: } Darkvater@5682: break; Darkvater@5682: belugas@6527: case WE_DESTROY: // Call cancellation of query, if we have not handled it before Darkvater@5682: if (!qs->handled && w->parent != NULL) { truelight@543: WindowEvent e; Darkvater@5682: Window *parent = w->parent; Darkvater@5682: Darkvater@5682: qs->handled = true; truelight@543: e.event = WE_ON_EDIT_TEXT_CANCEL; truelight@543: parent->wndproc(parent, &e); truelight@543: } skidd13@8425: ClrBit(_no_scroll, SCROLL_EDIT); Darkvater@5682: break; truelight@543: } truelight@0: } 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}, Darkvater@5682: { WWT_PANEL, 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: QueryStringWndProc truelight@0: }; truelight@0: tron@1386: static char _edit_str_buf[64]; 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 Darkvater@5682: * 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 Darkvater@5682: * 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@5682: static char orig_str_buf[lengthof(_edit_str_buf)]; truelight@0: Window *w; Darkvater@1390: uint realmaxlen = maxlen & ~0x1000; truelight@0: Darkvater@1390: assert(realmaxlen < lengthof(_edit_str_buf)); truelight@0: truelight@0: DeleteWindowById(WC_QUERY_STRING, 0); truelight@0: DeleteWindowById(WC_SAVELOAD, 0); truelight@0: tron@1386: w = AllocateWindowDesc(&_query_string_desc); Darkvater@5682: w->parent = parent; tron@1386: Darkvater@4912: GetString(_edit_str_buf, str, lastof(_edit_str_buf)); Darkvater@4949: _edit_str_buf[realmaxlen - 1] = '\0'; truelight@596: truelight@596: if (maxlen & 0x1000) { tron@1386: WP(w, querystr_d).orig = NULL; tron@1386: } else { Darkvater@5682: strecpy(orig_str_buf, _edit_str_buf, lastof(orig_str_buf)); Darkvater@5682: WP(w, querystr_d).orig = orig_str_buf; tron@1386: } truelight@0: rubidium@8493: w->LowerWidget(QUERY_STR_WIDGET_TEXT); Darkvater@1390: WP(w, querystr_d).caption = caption; truelight@4299: WP(w, querystr_d).afilter = afilter; Darkvater@4948: InitializeTextBuffer(&WP(w, querystr_d).text, _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: Darkvater@5669: rubidium@6574: struct query_d { Darkvater@5724: void (*proc)(Window*, bool); ///< callback function executed on closing of popup. Window* points to parent, bool is true if 'yes' clicked, false otherwise rubidium@7503: uint64 params[10]; ///< local copy of _decode_parameters Darkvater@5669: StringID message; ///< message shown for query window Darkvater@5669: bool calledback; ///< has callback been executed already (internal usage for WE_DESTROY event) rubidium@6574: }; Darkvater@5669: assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(query_d)); Darkvater@5669: Darkvater@5669: truelight@4300: static void QueryWndProc(Window *w, WindowEvent *e) truelight@4300: { Darkvater@5669: query_d *q = &WP(w, query_d); truelight@4300: Darkvater@5669: switch (e->event) { Darkvater@5669: case WE_PAINT: rubidium@7762: CopyInDParam(0, q->params, lengthof(q->params)); Darkvater@5669: DrawWindowWidgets(w); rubidium@7762: CopyInDParam(0, q->params, lengthof(q->params)); Darkvater@5669: Darkvater@6523: DrawStringMultiCenter(w->width / 2, (w->height / 2) - 10, q->message, w->width - 2); truelight@4300: break; truelight@4300: Darkvater@5669: case WE_CLICK: Darkvater@5669: switch (e->we.click.widget) { Darkvater@5669: case QUERY_WIDGET_YES: Darkvater@5669: q->calledback = true; Darkvater@5669: if (q->proc != NULL) q->proc(w->parent, true); Darkvater@5669: /* Fallthrough */ Darkvater@5669: case QUERY_WIDGET_NO: Darkvater@5669: DeleteWindow(w); Darkvater@5669: break; Darkvater@5669: } Darkvater@5669: break; truelight@4300: belugas@6527: case WE_KEYPRESS: // ESC closes the window, Enter confirms the action Darkvater@5669: switch (e->we.keypress.keycode) { Darkvater@5669: case WKC_RETURN: Darkvater@5669: case WKC_NUM_ENTER: Darkvater@5669: q->calledback = true; Darkvater@5669: if (q->proc != NULL) q->proc(w->parent, true); Darkvater@5669: /* Fallthrough */ Darkvater@5669: case WKC_ESC: Darkvater@5669: e->we.keypress.cont = false; Darkvater@5669: DeleteWindow(w); Darkvater@5669: break; Darkvater@5669: } Darkvater@5669: break; Darkvater@5669: belugas@6527: case WE_DESTROY: // Call callback function (if any) on window close if not yet called Darkvater@5680: if (!q->calledback && q->proc != NULL) { Darkvater@5680: q->calledback = true; Darkvater@5680: q->proc(w->parent, false); Darkvater@5680: } Darkvater@5669: break; truelight@4300: } truelight@4300: } 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: QueryWndProc 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*/ Darkvater@5669: void ShowQuery(StringID caption, StringID message, Window *parent, void (*callback)(Window*, bool)) truelight@4300: { Darkvater@5669: Window *w = AllocateWindowDesc(&_query_desc); Darkvater@5669: if (w == NULL) return; truelight@4300: Darkvater@5669: if (parent == NULL) parent = FindWindowById(WC_MAIN_WINDOW, 0); Darkvater@5669: w->parent = parent; Darkvater@5669: w->left = parent->left + (parent->width / 2) - (w->width / 2); Darkvater@5669: w->top = parent->top + (parent->height / 2) - (w->height / 2); truelight@4300: Darkvater@5669: /* Create a backup of the variadic arguments to strings because it will be Darkvater@5669: * overridden pretty often. We will copy these back for drawing */ rubidium@7762: CopyOutDParam(WP(w, query_d).params, 0, lengthof(WP(w, query_d).params)); Darkvater@5669: w->widget[QUERY_WIDGET_CAPTION].data = caption; Darkvater@5669: WP(w, query_d).message = message; Darkvater@5669: WP(w, query_d).proc = callback; Darkvater@5669: WP(w, query_d).calledback = false; 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@7842: { WWT_PANEL, RESIZE_RTB, 14, 2, 254, 154, 165, 0x0, 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: Darkvater@4223: _fios_list = FiosGetScenarioList(_saveload_mode); break; truelight@4300: case SLD_LOAD_HEIGHTMAP: truelight@4300: _fios_list = FiosGetHeightmapList(_saveload_mode); break; tron@2549: Darkvater@4223: default: _fios_list = 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: uint s_amount; tron@2631: int i; 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: */ tron@2631: for (i = 0; i < _fios_num; i++) { tron@2631: switch (_fios_list[i].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; tron@2631: } truelight@0: } truelight@0: truelight@0: s_amount = _fios_num - sort_start - sort_end; tron@1105: if (s_amount > 0) truelight@0: qsort(_fios_list + sort_start, s_amount, sizeof(FiosItem), compare_FiosItems); truelight@0: } truelight@0: rubidium@6573: static void GenerateFileName() tron@402: { truelight@635: /* Check if we are not a specatator who wants to generate a name.. truelight@635: Let's use the name of player #0 for now. */ rubidium@5838: const Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST); truelight@635: peter1138@7554: SetDParam(0, p->index); peter1138@7554: SetDParam(1, _date); Darkvater@4912: GetString(_edit_str_buf, STR_4004, lastof(_edit_str_buf)); rubidium@7371: SanitizeFilename(_edit_str_buf); tron@402: } tron@402: rubidium@6573: extern void StartupEngines(); darkvater@1131: truelight@0: static void SaveLoadDlgWndProc(Window *w, WindowEvent *e) truelight@0: { Darkvater@2559: static FiosItem o_dir; Darkvater@2559: Darkvater@2559: switch (e->event) { rubidium@7425: case WE_CREATE: // Set up OPENTTD button rubidium@7842: w->vscroll.cap = 10; rubidium@7842: w->resize.step_width = 2; rubidium@7842: w->resize.step_height = 10; rubidium@7842: Darkvater@2559: o_dir.type = FIOS_TYPE_DIRECT; Darkvater@2559: switch (_saveload_mode) { tron@2639: case SLD_SAVE_GAME: tron@2639: case SLD_LOAD_GAME: rubidium@7425: FioGetDirectory(o_dir.name, lengthof(o_dir.name), SAVE_DIR); Darkvater@2559: break; tron@2639: tron@2639: case SLD_SAVE_SCENARIO: tron@2639: case SLD_LOAD_SCENARIO: rubidium@7425: FioGetDirectory(o_dir.name, lengthof(o_dir.name), SCENARIO_DIR); Darkvater@2559: break; tron@2639: truelight@4300: case SLD_LOAD_HEIGHTMAP: rubidium@7425: FioGetDirectory(o_dir.name, lengthof(o_dir.name), HEIGHTMAP_DIR); truelight@4300: break; truelight@4300: Darkvater@2559: default: rubidium@7425: ttd_strlcpy(o_dir.name, _personal_dir, lengthof(o_dir.name)); Darkvater@2559: } Darkvater@2559: break; Darkvater@2559: truelight@0: case WE_PAINT: { tron@4000: int pos; tron@4000: int y; truelight@0: truelight@0: SetVScrollCount(w, _fios_num); truelight@0: DrawWindowWidgets(w); Darkvater@2100: DrawFiosTexts(w->width); truelight@0: truelight@0: if (_savegame_sort_dirty) { truelight@0: _savegame_sort_dirty = false; truelight@0: MakeSortedSaveGameList(); truelight@0: } truelight@0: Darkvater@2750: GfxFillRect(w->widget[7].left + 1, w->widget[7].top + 1, w->widget[7].right, w->widget[7].bottom, 0xD7); peter1138@8845: DrawSortButtonState(w, _savegame_sort_order & SORT_BY_NAME ? 2 : 3, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP); truelight@0: Darkvater@2750: y = w->widget[7].top + 1; tron@4000: for (pos = w->vscroll.pos; pos < _fios_num; pos++) { belugas@4171: const FiosItem *item = _fios_list + pos; tron@4000: Darkvater@2103: DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], w->width - 18); tron@2549: y += 10; Darkvater@2750: if (y >= w->vscroll.cap * 10 + w->widget[7].top + 1) break; truelight@0: } truelight@0: truelight@0: if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { rubidium@6987: DrawEditBox(w, &WP(w, querystr_d), 10); truelight@0: } truelight@0: break; truelight@0: } tron@4000: truelight@0: case WE_CLICK: belugas@4634: switch (e->we.click.widget) { belugas@6527: case 2: // Sort save names by name tron@2526: _savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ? tron@2526: SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME; truelight@0: _savegame_sort_dirty = true; truelight@0: SetWindowDirty(w); truelight@0: break; truelight@0: belugas@6527: case 3: // Sort save names by date tron@2526: _savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ? tron@2526: SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE; truelight@0: _savegame_sort_dirty = true; truelight@0: SetWindowDirty(w); truelight@0: break; truelight@0: belugas@6527: case 6: // OpenTTD 'button', jumps to OpenTTD directory Darkvater@2750: FiosBrowseTo(&o_dir); Darkvater@2750: SetWindowDirty(w); Darkvater@2750: BuildFileList(); Darkvater@2750: break; Darkvater@2750: belugas@6527: case 7: { // Click the listbox belugas@4634: int y = (e->we.click.pt.y - w->widget[e->we.click.widget].top - 1) / 10; truelight@0: char *name; truelight@0: const FiosItem *file; truelight@0: tron@2639: if (y < 0 || (y += w->vscroll.pos) >= w->vscroll.count) return; truelight@0: truelight@0: file = _fios_list + y; truelight@193: tron@2639: name = FiosBrowseTo(file); tron@2639: if (name != NULL) { Darkvater@2100: if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) { Darkvater@2100: _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD; Darkvater@2100: truelight@0: SetFiosType(file->type); Darkvater@2100: ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); Darkvater@2100: ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); Darkvater@2100: truelight@0: DeleteWindow(w); truelight@4300: } else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) { truelight@4300: SetFiosType(file->type); truelight@4300: ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); truelight@4300: ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title)); truelight@4300: truelight@4300: DeleteWindow(w); truelight@4300: ShowHeightmapLoad(); truelight@0: } else { belugas@6527: /* SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox */ Darkvater@3287: ttd_strlcpy(WP(w, querystr_d).text.buf, file->title, WP(w, querystr_d).text.maxlength); Darkvater@1390: UpdateTextBufferSize(&WP(w, querystr_d).text); glx@8524: w->InvalidateWidget(10); truelight@0: } truelight@0: } else { belugas@6527: /* Changed directory, need repaint. */ truelight@0: SetWindowDirty(w); truelight@0: BuildFileList(); truelight@0: } truelight@0: break; truelight@0: } Darkvater@2559: belugas@6527: case 11: case 12: // Delete, Save game truelight@0: break; truelight@0: } truelight@0: break; truelight@0: case WE_MOUSELOOP: rubidium@5230: if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { rubidium@5230: HandleEditBox(w, &WP(w, querystr_d), 10); rubidium@5230: } truelight@0: break; truelight@0: case WE_KEYPRESS: belugas@4634: if (e->we.keypress.keycode == WKC_ESC) { Darkvater@2165: DeleteWindow(w); Darkvater@2165: return; Darkvater@2165: } Darkvater@2165: Darkvater@2165: if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { belugas@6527: if (HandleEditBoxKey(w, &WP(w, querystr_d), 10, e) == 1) // Press Enter belugas@8532: w->HandleButtonClick(12); truelight@0: } truelight@0: break; truelight@0: case WE_TIMEOUT: belugas@5039: /* This test protects against using widgets 11 and 12 which are only available belugas@5039: * in those two saveload mode */ belugas@5039: if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) break; belugas@5039: rubidium@8493: if (w->IsWidgetLowered(11)) { // Delete button clicked rubidium@6987: if (!FiosDelete(WP(w, querystr_d).text.buf)) { tron@2255: ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0); Darkvater@3288: } else { Darkvater@3288: BuildFileList(); peter1138@7868: /* Reset file name to current date on successful delete */ Darkvater@3288: if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName(); tron@2255: } Darkvater@3288: Darkvater@3288: UpdateTextBufferSize(&WP(w, querystr_d).text); truelight@0: SetWindowDirty(w); rubidium@8493: } else if (w->IsWidgetLowered(12)) { // Save button clicked truelight@0: _switch_mode = SM_SAVE; rubidium@6987: FiosMakeSavegameName(_file_to_saveload.name, WP(w, querystr_d).text.buf, sizeof(_file_to_saveload.name)); darkvater@1131: darkvater@1131: /* In the editor set up the vehicle engines correctly (date might have changed) */ darkvater@1131: if (_game_mode == GM_EDITOR) StartupEngines(); truelight@0: } truelight@0: break; truelight@0: case WE_DESTROY: belugas@6527: /* pause is only used in single-player, non-editor mode, non menu mode */ tron@2952: if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) { peter1138@9167: if (_pause_game >= 0) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); tron@2952: } truelight@0: FiosFreeSavegameList(); skidd13@8425: ClrBit(_no_scroll, SCROLL_SAVE); truelight@0: break; truelight@867: case WE_RESIZE: { truelight@867: /* Widget 2 and 3 have to go with halve speed, make it so obiwan */ belugas@4634: uint diff = e->we.sizing.diff.x / 2; truelight@867: w->widget[2].right += diff; truelight@867: w->widget[3].left += diff; belugas@4634: w->widget[3].right += e->we.sizing.diff.x; truelight@867: Darkvater@2559: /* Same for widget 11 and 12 in save-dialog */ truelight@867: if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) { Darkvater@2559: w->widget[11].right += diff; Darkvater@2559: w->widget[12].left += diff; belugas@4634: w->widget[12].right += e->we.sizing.diff.x; truelight@867: } truelight@867: belugas@4634: w->vscroll.cap += e->we.sizing.diff.y / 10; truelight@867: } break; truelight@193: } truelight@0: } 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: SaveLoadDlgWndProc, 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: SaveLoadDlgWndProc, 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: { glx@5079: static const StringID saveload_captions[] = { glx@5079: STR_4001_LOAD_GAME, glx@5079: STR_0298_LOAD_SCENARIO, glx@5079: STR_4000_SAVE_GAME, glx@5079: STR_0299_SAVE_SCENARIO, maedhros@8920: STR_LOAD_HEIGHTMAP, glx@5079: }; glx@5079: truelight@0: Window *w; glx@5079: const WindowDesc *sld = &_save_dialog_desc; glx@5079: truelight@0: rubidium@8385: SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0); truelight@0: DeleteWindowById(WC_QUERY_STRING, 0); truelight@0: DeleteWindowById(WC_SAVELOAD, 0); truelight@0: truelight@0: _saveload_mode = mode; skidd13@8427: SetBit(_no_scroll, SCROLL_SAVE); Darkvater@1397: belugas@8648: /* Use an array to define what will be the current file type being handled belugas@8648: * by current file mode */ belugas@8648: _file_to_saveload.filetype = _file_modetotype[mode]; Darkvater@1397: switch (mode) { tron@2631: case SLD_SAVE_GAME: GenerateFileName(); break; tron@2631: case SLD_SAVE_SCENARIO: strcpy(_edit_str_buf, "UNNAMED"); break; glx@5079: default: sld = &_load_dialog_desc; break; Darkvater@1397: } truelight@0: glx@5079: assert((uint)mode < lengthof(saveload_captions)); glx@5079: w = AllocateWindowDesc(sld); glx@5079: w->widget[1].data = saveload_captions[mode]; rubidium@8493: w->LowerWidget(7); Darkvater@4948: Darkvater@4948: WP(w, querystr_d).afilter = CS_ALPHANUMERAL; Darkvater@4948: InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, lengthof(_edit_str_buf), 240); truelight@0: belugas@6527: /* pause is only used in single-player, non-editor mode, non-menu mode. It belugas@6527: * will be unpaused in the WE_DESTROY event handler. */ tron@2639: if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) { peter1138@9167: if (_pause_game >= 0) DoCommandP(0, 1, 0, NULL, CMD_PAUSE); tron@2639: } dominik@278: truelight@0: BuildFileList(); truelight@0: truelight@0: ResetObjectToPlace(); 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: } truelight@0: rubidium@9211: /** rubidium@9211: * The 'amount' to cheat with. rubidium@9211: * This variable is semantically a constant value, but because the cheat rubidium@9211: * code requires to be able to write to the variable it is not constified. rubidium@9211: */ rubidium@9211: static int32 _money_cheat_amount = 10000000; rubidium@9211: tron@410: static int32 ClickMoneyCheat(int32 p1, int32 p2) truelight@0: { rubidium@9211: DoCommandP(0, (uint32)(p2 * _money_cheat_amount), 0, NULL, CMD_MONEY_CHEAT); rubidium@9211: return _money_cheat_amount; truelight@0: } truelight@0: belugas@6527: /** belugas@6527: * @param p1 player to set to belugas@6527: * @param p2 is -1 or +1 (down/up) belugas@6527: */ tron@410: static int32 ClickChangePlayerCheat(int32 p1, int32 p2) truelight@0: { Darkvater@4850: while (IsValidPlayer((PlayerID)p1)) { tron@2639: if (_players[p1].is_active) { rubidium@5564: SetLocalPlayer((PlayerID)p1); rubidium@5564: dominik@138: MarkWholeScreenDirty(); truelight@0: return _local_player; truelight@0: } truelight@0: p1 += p2; truelight@0: } darkvater@155: darkvater@155: return _local_player; truelight@0: } truelight@0: belugas@6527: /** belugas@6527: * @param p1 -1 or +1 (down/up) belugas@6977: * @param p2 unused belugas@6527: */ tron@1095: static int32 ClickChangeClimateCheat(int32 p1, int32 p2) dominik@100: { tron@2639: if (p1 == -1) p1 = 3; tron@2639: if (p1 == 4) p1 = 0; dominik@100: _opt.landscape = p1; Darkvater@5352: ReloadNewGRFData(); dominik@100: return _opt.landscape; dominik@100: } dominik@100: rubidium@6573: extern void EnginesMonthlyLoop(); dominik@108: belugas@6527: /** belugas@6977: * @param p1 unused belugas@6527: * @param p2 1 (increase) or -1 (decrease) belugas@6527: */ tron@1095: static int32 ClickChangeDateCheat(int32 p1, int32 p2) dominik@108: { dominik@108: YearMonthDay ymd; rubidium@4288: ConvertDateToYMD(_date, &ymd); dominik@108: rubidium@4293: if ((ymd.year == MIN_YEAR && p2 == -1) || (ymd.year == MAX_YEAR && p2 == 1)) return _cur_year; dominik@108: rubidium@4288: SetDate(ConvertYMDToDate(_cur_year + p2, ymd.month, ymd.day)); dominik@108: EnginesMonthlyLoop(); dominik@108: SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0)); dominik@108: return _cur_year; dominik@108: } dominik@100: truelight@0: typedef int32 CheckButtonClick(int32, int32); truelight@0: rubidium@6574: struct CheatEntry { belugas@6527: VarType type; ///< type of selector belugas@6527: StringID str; ///< string with descriptive text belugas@6527: void *variable; ///< pointer to the variable belugas@6527: bool *been_used; ///< has this cheat been used before? belugas@6527: CheckButtonClick *proc;///< procedure rubidium@6574: }; truelight@0: truelight@0: static const CheatEntry _cheats_ui[] = { rubidium@9211: {SLE_INT32, STR_CHEAT_MONEY, &_money_cheat_amount, &_cheats.money.been_used, &ClickMoneyCheat }, rubidium@9211: {SLE_UINT8, STR_CHEAT_CHANGE_PLAYER, &_local_player, &_cheats.switch_player.been_used, &ClickChangePlayerCheat }, rubidium@9211: {SLE_BOOL, STR_CHEAT_EXTRA_DYNAMITE, &_cheats.magic_bulldozer.value, &_cheats.magic_bulldozer.been_used, NULL }, rubidium@9211: {SLE_BOOL, STR_CHEAT_CROSSINGTUNNELS, &_cheats.crossing_tunnels.value, &_cheats.crossing_tunnels.been_used, NULL }, rubidium@9211: {SLE_BOOL, STR_CHEAT_BUILD_IN_PAUSE, &_cheats.build_in_pause.value, &_cheats.build_in_pause.been_used, NULL }, rubidium@9211: {SLE_BOOL, STR_CHEAT_NO_JETCRASH, &_cheats.no_jetcrash.value, &_cheats.no_jetcrash.been_used, NULL }, rubidium@9211: {SLE_BOOL, STR_CHEAT_SETUP_PROD, &_cheats.setup_prod.value, &_cheats.setup_prod.been_used, NULL }, rubidium@9211: {SLE_UINT8, STR_CHEAT_SWITCH_CLIMATE, &_opt.landscape, &_cheats.switch_climate.been_used, &ClickChangeClimateCheat}, rubidium@9211: {SLE_INT32, STR_CHEAT_CHANGE_DATE, &_cur_year, &_cheats.change_date.been_used, &ClickChangeDateCheat }, truelight@0: }; truelight@0: truelight@0: truelight@0: static const Widget _cheat_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, 399, 0, 13, STR_CHEATS, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@4344: { WWT_PANEL, RESIZE_NONE, 14, 0, 399, 14, 169, 0x0, STR_NULL}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 399, 14, 169, 0x0, STR_CHEATS_TIP}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static void CheatsWndProc(Window *w, WindowEvent *e) truelight@0: { tron@2639: switch (e->event) { rubidium@9211: case WE_PAINT: { rubidium@9211: int clk = WP(w, def_d).data_1; tron@2639: rubidium@9211: DrawWindowWidgets(w); rubidium@9211: DrawStringMultiCenter(200, 25, STR_CHEATS_WARNING, w->width - 50); Darkvater@3110: rubidium@9211: for (int i = 0, x = 0, y = 45; i != lengthof(_cheats_ui); i++) { rubidium@9211: const CheatEntry *ce = &_cheats_ui[i]; rubidium@9211: rubidium@9211: DrawSprite((*ce->been_used) ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, PAL_NONE, x + 5, y + 2); rubidium@9211: rubidium@9211: switch (ce->type) { rubidium@9211: case SLE_BOOL: { rubidium@9211: bool on = (*(bool*)ce->variable); rubidium@9211: rubidium@9211: DrawFrameRect(x + 20, y + 1, x + 30 + 9, y + 9, on ? 6 : 4, on ? FR_LOWERED : FR_NONE); rubidium@9211: SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); rubidium@9211: } break; rubidium@9211: rubidium@9211: default: { rubidium@9211: int32 val = (int32)ReadValue(ce->variable, ce->type); rubidium@9211: char buf[512]; rubidium@9211: rubidium@9211: /* Draw [<][>] boxes for settings of an integer-type */ rubidium@9211: DrawArrowButtons(x + 20, y, 3, clk - (i * 2), true, true); rubidium@9211: rubidium@9211: switch (ce->str) { rubidium@9211: /* Display date for change date cheat */ rubidium@9211: case STR_CHEAT_CHANGE_DATE: SetDParam(0, _date); break; rubidium@9211: rubidium@9211: /* Draw colored flag for change player cheat */ rubidium@9211: case STR_CHEAT_CHANGE_PLAYER: rubidium@9211: SetDParam(0, val); rubidium@9211: GetString(buf, STR_CHEAT_CHANGE_PLAYER, lastof(buf)); rubidium@9211: DrawPlayerIcon(_current_player, 60 + GetStringBoundingBox(buf).width, y + 2); rubidium@9211: break; rubidium@9211: rubidium@9211: /* Set correct string for switch climate cheat */ rubidium@9211: case STR_CHEAT_SWITCH_CLIMATE: val += STR_TEMPERATE_LANDSCAPE; rubidium@9211: rubidium@9211: /* Fallthrough */ rubidium@9211: default: SetDParam(0, val); rubidium@9211: } rubidium@9211: } break; tron@2639: } truelight@193: rubidium@9211: DrawString(50, y + 1, ce->str, TC_FROMSTRING); rubidium@9211: rubidium@9211: y += 12; truelight@0: } rubidium@9211: break; truelight@0: } tron@2639: rubidium@9211: case WE_CLICK: { belugas@4634: uint btn = (e->we.click.pt.y - 46) / 12; belugas@4634: uint x = e->we.click.pt.x; truelight@0: rubidium@9211: /* Not clicking a button? */ skidd13@8450: if (!IsInsideMM(x, 20, 40) || btn >= lengthof(_cheats_ui)) break; truelight@0: rubidium@9211: const CheatEntry *ce = &_cheats_ui[btn]; rubidium@9211: int value = (int32)ReadValue(ce->variable, ce->type); rubidium@9211: int oldvalue = value; truelight@0: truelight@0: *ce->been_used = true; truelight@0: tron@2639: switch (ce->type) { rubidium@9211: case SLE_BOOL: rubidium@9211: value ^= 1; rubidium@9211: if (ce->proc != NULL) ce->proc(value, 0); rubidium@9211: break; truelight@0: rubidium@9211: default: rubidium@9211: /* Take whatever the function returns */ rubidium@9211: value = ce->proc(value + ((x >= 30) ? 1 : -1), (x >= 30) ? 1 : -1); truelight@0: rubidium@9211: if (value != oldvalue) WP(w, def_d).data_1 = btn * 2 + 1 + ((x >= 30) ? 1 : 0); rubidium@9211: break; truelight@0: } truelight@0: rubidium@9211: if (value != oldvalue) WriteValue(ce->variable, ce->type, (int64)value); truelight@0: truelight@0: w->flags4 |= 5 << WF_TIMEOUT_SHL; truelight@0: truelight@0: SetWindowDirty(w); rubidium@9211: } break; rubidium@9211: rubidium@9211: case WE_TIMEOUT: rubidium@9211: WP(w, def_d).data_1 = 0; rubidium@9211: SetWindowDirty(w); rubidium@9211: break; truelight@193: } truelight@0: } tron@2639: truelight@0: static const WindowDesc _cheats_desc = { rubidium@7837: 240, 22, 400, 170, 400, 170, rubidium@6144: WC_CHEATS, WC_NONE, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, truelight@0: _cheat_widgets, truelight@0: CheatsWndProc truelight@0: }; truelight@0: truelight@0: rubidium@6573: void ShowCheatWindow() truelight@0: { truelight@0: DeleteWindowById(WC_CHEATS, 0); Darkvater@5137: AllocateWindowDesc(&_cheats_desc); truelight@0: }