00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "landscape.h"
00009 #include "newgrf_text.h"
00010 #include "saveload/saveload.h"
00011 #include "tile_map.h"
00012 #include "gui.h"
00013 #include "station_gui.h"
00014 #include "viewport_func.h"
00015 #include "gfx_func.h"
00016 #include "station_func.h"
00017 #include "command_func.h"
00018 #include "company_func.h"
00019 #include "town.h"
00020 #include "network/network.h"
00021 #include "variables.h"
00022 #include "company_base.h"
00023 #include "texteff.hpp"
00024 #include "cargotype.h"
00025 #include "company_manager_face.h"
00026 #include "strings_func.h"
00027 #include "fileio_func.h"
00028 #include "fios.h"
00029 #include "zoom_func.h"
00030 #include "window_func.h"
00031 #include "string_func.h"
00032 #include "newgrf_cargo.h"
00033 #include "tilehighlight_func.h"
00034 #include "querystring_gui.h"
00035
00036 #include "table/strings.h"
00037
00038
00039 SaveLoadDialogMode _saveload_mode;
00040
00041
00042 static bool _fios_path_changed;
00043 static bool _savegame_sort_dirty;
00044 int _caret_timer;
00045
00046 static const Widget _land_info_widgets[] = {
00047 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00048 { WWT_CAPTION, RESIZE_NONE, COLOUR_GREY, 11, 299, 0, 13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
00049 { WWT_PANEL, RESIZE_BOTTOM, COLOUR_GREY, 0, 299, 14, 99, 0x0, STR_NULL},
00050 { WIDGETS_END},
00051 };
00052
00053 static const WindowDesc _land_info_desc = {
00054 WDP_AUTO, WDP_AUTO, 300, 100, 300, 100,
00055 WC_LAND_INFO, WC_NONE,
00056 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00057 _land_info_widgets,
00058 };
00059
00060 class LandInfoWindow : public Window {
00061 enum {
00062 LAND_INFO_CENTERED_LINES = 12,
00063 LAND_INFO_MULTICENTER_LINE = LAND_INFO_CENTERED_LINES,
00064 LAND_INFO_LINE_END,
00065
00066 LAND_INFO_LINE_BUFF_SIZE = 512,
00067 };
00068
00069 public:
00070 char landinfo_data[LAND_INFO_LINE_END][LAND_INFO_LINE_BUFF_SIZE];
00071
00072 virtual void OnPaint()
00073 {
00074 this->DrawWidgets();
00075
00076 uint y = 21;
00077 for (uint i = 0; i < LAND_INFO_CENTERED_LINES; i++) {
00078 if (StrEmpty(this->landinfo_data[i])) break;
00079
00080 DoDrawStringCentered(150, y, this->landinfo_data[i], i == 0 ? TC_LIGHT_BLUE : TC_FROMSTRING);
00081 y += i == 0 ? 16 : 12;
00082 }
00083
00084 y += 6;
00085
00086 if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) {
00087 SetDParamStr(0, this->landinfo_data[LAND_INFO_MULTICENTER_LINE]);
00088 DrawStringMultiCenter(150, y, STR_JUST_RAW_STRING, this->width - 4);
00089 }
00090 }
00091
00092 LandInfoWindow(TileIndex tile) : Window(&_land_info_desc) {
00093 Company *c = GetCompany(IsValidCompanyID(_local_company) ? _local_company : COMPANY_FIRST);
00094 Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority);
00095
00096 Money old_money = c->money;
00097 c->money = INT64_MAX;
00098 CommandCost costclear = DoCommand(tile, 0, 0, DC_NONE, CMD_LANDSCAPE_CLEAR);
00099 c->money = old_money;
00100
00101
00102 TileDesc td;
00103 AcceptedCargo ac;
00104
00105 td.build_date = INVALID_DATE;
00106
00107
00108
00109
00110
00111 td.owner_type[0] = STR_01A7_OWNER;
00112 td.owner_type[1] = STR_NULL;
00113 td.owner_type[2] = STR_NULL;
00114 td.owner_type[3] = STR_NULL;
00115 td.owner[0] = OWNER_NONE;
00116 td.owner[1] = OWNER_NONE;
00117 td.owner[2] = OWNER_NONE;
00118 td.owner[3] = OWNER_NONE;
00119
00120 td.station_class = STR_NULL;
00121 td.station_name = STR_NULL;
00122
00123 td.grf = NULL;
00124
00125 GetAcceptedCargo(tile, ac);
00126 GetTileDesc(tile, &td);
00127
00128 uint line_nr = 0;
00129
00130
00131 SetDParam(0, td.dparam[0]);
00132 GetString(this->landinfo_data[line_nr], td.str, lastof(this->landinfo_data[line_nr]));
00133 line_nr++;
00134
00135
00136 for (uint i = 0; i < 4; i++) {
00137 if (td.owner_type[i] == STR_NULL) continue;
00138
00139 SetDParam(0, STR_01A6_N_A);
00140 if (td.owner[i] != OWNER_NONE && td.owner[i] != OWNER_WATER) GetNameOfOwner(td.owner[i], tile);
00141 GetString(this->landinfo_data[line_nr], td.owner_type[i], lastof(this->landinfo_data[line_nr]));
00142 line_nr++;
00143 }
00144
00145
00146 StringID str = STR_01A4_COST_TO_CLEAR_N_A;
00147 if (CmdSucceeded(costclear)) {
00148 Money cost = costclear.GetCost();
00149 if (cost < 0) {
00150 cost = -cost;
00151 str = STR_REVENUE_WHEN_CLEARED;
00152 } else {
00153 str = STR_01A5_COST_TO_CLEAR;
00154 }
00155 SetDParam(0, cost);
00156 }
00157 GetString(this->landinfo_data[line_nr], str, lastof(this->landinfo_data[line_nr]));
00158 line_nr++;
00159
00160
00161 char tmp[16];
00162 snprintf(tmp, lengthof(tmp), "0x%.4X", tile);
00163 SetDParam(0, TileX(tile));
00164 SetDParam(1, TileY(tile));
00165 SetDParam(2, TileHeight(tile));
00166 SetDParamStr(3, tmp);
00167 GetString(this->landinfo_data[line_nr], STR_LANDINFO_COORDS, lastof(this->landinfo_data[line_nr]));
00168 line_nr++;
00169
00170
00171 SetDParam(0, STR_01A9_NONE);
00172 if (t != NULL && t->IsValid()) {
00173 SetDParam(0, STR_TOWN);
00174 SetDParam(1, t->index);
00175 }
00176 GetString(this->landinfo_data[line_nr], STR_01A8_LOCAL_AUTHORITY, lastof(this->landinfo_data[line_nr]));
00177 line_nr++;
00178
00179
00180 if (td.build_date != INVALID_DATE) {
00181 SetDParam(0, td.build_date);
00182 GetString(this->landinfo_data[line_nr], STR_BUILD_DATE, lastof(this->landinfo_data[line_nr]));
00183 line_nr++;
00184 }
00185
00186
00187 if (td.station_class != STR_NULL) {
00188 SetDParam(0, td.station_class);
00189 GetString(this->landinfo_data[line_nr], STR_TILEDESC_STATION_CLASS, lastof(this->landinfo_data[line_nr]));
00190 line_nr++;
00191 }
00192
00193
00194 if (td.station_name != STR_NULL) {
00195 SetDParam(0, td.station_name);
00196 GetString(this->landinfo_data[line_nr], STR_TILEDESC_STATION_TYPE, lastof(this->landinfo_data[line_nr]));
00197 line_nr++;
00198 }
00199
00200
00201 if (td.grf != NULL) {
00202 SetDParamStr(0, td.grf);
00203 GetString(this->landinfo_data[line_nr], STR_TILEDESC_NEWGRF_NAME, lastof(this->landinfo_data[line_nr]));
00204 line_nr++;
00205 }
00206
00207 assert(line_nr < LAND_INFO_CENTERED_LINES);
00208
00209
00210 this->landinfo_data[line_nr][0] = '\0';
00211
00212
00213 char *strp = GetString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE], STR_01CE_CARGO_ACCEPTED, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
00214 bool found = false;
00215
00216 for (CargoID i = 0; i < NUM_CARGO; ++i) {
00217 if (ac[i] > 0) {
00218
00219 if (found) {
00220 *strp++ = ',';
00221 *strp++ = ' ';
00222 }
00223 found = true;
00224
00225
00226 if (ac[i] < 8) {
00227 SetDParam(0, ac[i]);
00228 SetDParam(1, GetCargo(i)->name);
00229 strp = GetString(strp, STR_01D1_8, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
00230 } else {
00231 strp = GetString(strp, GetCargo(i)->name, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
00232 }
00233 }
00234 }
00235 if (!found) this->landinfo_data[LAND_INFO_MULTICENTER_LINE][0] = '\0';
00236
00237 if (found) line_nr += 2;
00238
00239 if (line_nr > 6) ResizeWindow(this, 0, 12 * (line_nr - 6));
00240
00241 this->FindWindowPlacementAndResize(&_land_info_desc);
00242
00243 #if defined(_DEBUG)
00244 # define LANDINFOD_LEVEL 0
00245 #else
00246 # define LANDINFOD_LEVEL 1
00247 #endif
00248 DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile));
00249 DEBUG(misc, LANDINFOD_LEVEL, "type_height = %#x", _m[tile].type_height);
00250 DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", _m[tile].m1);
00251 DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", _m[tile].m2);
00252 DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", _m[tile].m3);
00253 DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4);
00254 DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5);
00255 DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _m[tile].m6);
00256 DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7);
00257 #undef LANDINFOD_LEVEL
00258 }
00259 };
00260
00261 static void Place_LandInfo(TileIndex tile)
00262 {
00263 DeleteWindowById(WC_LAND_INFO, 0);
00264 new LandInfoWindow(tile);
00265 }
00266
00267 void PlaceLandBlockInfo()
00268 {
00269 if (_cursor.sprite == SPR_CURSOR_QUERY) {
00270 ResetObjectToPlace();
00271 } else {
00272 _place_proc = Place_LandInfo;
00273 SetObjectToPlace(SPR_CURSOR_QUERY, PAL_NONE, VHM_RECT, WC_MAIN_TOOLBAR, 0);
00274 }
00275 }
00276
00277 static const Widget _about_widgets[] = {
00278 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00279 { WWT_CAPTION, RESIZE_NONE, COLOUR_GREY, 11, 419, 0, 13, STR_015B_OPENTTD, STR_NULL},
00280 { WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 0, 419, 14, 271, 0x0, STR_NULL},
00281 { WWT_FRAME, RESIZE_NONE, COLOUR_GREY, 5, 414, 40, 245, STR_NULL, STR_NULL},
00282 { WIDGETS_END},
00283 };
00284
00285 static const WindowDesc _about_desc = {
00286 WDP_CENTER, WDP_CENTER, 420, 272, 420, 272,
00287 WC_GAME_OPTIONS, WC_NONE,
00288 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00289 _about_widgets,
00290 };
00291
00292 struct AboutWindow : public Window {
00293 int scroll_height;
00294 uint16 counter;
00295
00296 AboutWindow() : Window(&_about_desc)
00297 {
00298 this->counter = 5;
00299 this->scroll_height = this->height - 40;
00300 this->FindWindowPlacementAndResize(&_about_desc);
00301 }
00302
00303 virtual void OnPaint()
00304 {
00305 static const char *credits[] = {
00306
00307
00308 "Original design by Chris Sawyer",
00309 "Original graphics by Simon Foster",
00310 "",
00311 "The OpenTTD team (in alphabetical order):",
00312 " Jean-Francois Claeys (Belugas) - GUI, newindustries and more",
00313 " Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles",
00314 " Matthijs Kooijman (blathijs) - Pathfinder-guru, pool rework",
00315 " Victor Fischer (Celestar) - Programming everywhere you need him to",
00316 " Christoph Elsenhans (frosch) - General coding",
00317 " Loïc Guilloux (glx) - General coding",
00318 " Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)",
00319 " Jonathan Coome (Maedhros) - High priest of the newGRF Temple",
00320 " Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host",
00321 " Owen Rudge (orudge) - Forum host, OS/2 port",
00322 " Peter Nelson (peter1138) - Spiritual descendant from newGRF gods",
00323 " Remko Bijker (Rubidium) - Lead coder and way more",
00324 " Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker",
00325 " Zdenek Sojka (SmatZ) - Bug finder and fixer",
00326 "",
00327 "Inactive Developers:",
00328 " Tamás Faragó (Darkvater) - Ex-Lead coder",
00329 " Christoph Mallon (Tron) - Programmer, code correctness police",
00330 "",
00331 "Retired Developers:",
00332 " Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)",
00333 " Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)",
00334 " Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)",
00335 " Patric Stout (TrueLight) - Programmer, webhoster (0.3 - pre0.6)",
00336 "",
00337 "Special thanks go out to:",
00338 " Josef Drexler - For his great work on TTDPatch",
00339 " Marcin Grzegorczyk - For his documentation of TTD internals",
00340 " Petr Baudis (pasky) - Many patches, newGRF support",
00341 " Stefan Meißner (sign_de) - For his work on the console",
00342 " Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with",
00343 " Cian Duffy (MYOB) - BeOS port / manual writing",
00344 " Christian Rosentreter (tokai) - MorphOS / AmigaOS port",
00345 " Richard Kempton (richK) - additional airports, initial TGP implementation",
00346 "",
00347 " Alberto Demichelis - Squirrel scripting language © 2003-2008",
00348 " Michael Blunck - Pre-Signals and Semaphores © 2003",
00349 " George - Canal/Lock graphics © 2003-2004",
00350 " David Dallaston - Tram tracks",
00351 " Marcin Grzegorczyk - Foundations for Tracks on Slopes",
00352 " All Translators - Who made OpenTTD a truly international game",
00353 " Bug Reporters - Without whom OpenTTD would still be full of bugs!",
00354 "",
00355 "",
00356 "And last but not least:",
00357 " Chris Sawyer - For an amazing game!"
00358 };
00359
00360 this->DrawWidgets();
00361
00362
00363 DrawStringCentered(210, 17, STR_00B6_ORIGINAL_COPYRIGHT, TC_FROMSTRING);
00364 DrawStringCentered(210, 17 + 10, STR_00B7_VERSION, TC_FROMSTRING);
00365
00366 int y = this->scroll_height;
00367
00368
00369 for (uint i = 0; i < lengthof(credits); i++) {
00370 if (y >= 50 && y < (this->height - 40)) {
00371 DoDrawString(credits[i], 10, y, TC_BLACK);
00372 }
00373 y += 10;
00374 }
00375
00376
00377 if (y < 50) this->scroll_height = this->height - 40;
00378
00379 DoDrawStringCentered(210, this->height - 25, "Website: http://www.openttd.org", TC_BLACK);
00380 DrawStringCentered(210, this->height - 15, STR_00BA_COPYRIGHT_OPENTTD, TC_FROMSTRING);
00381 }
00382
00383 virtual void OnTick()
00384 {
00385 if (--this->counter == 0) {
00386 this->counter = 5;
00387 this->scroll_height--;
00388 this->SetDirty();
00389 }
00390 }
00391 };
00392
00393 void ShowAboutWindow()
00394 {
00395 DeleteWindowById(WC_GAME_OPTIONS, 0);
00396 new AboutWindow();
00397 }
00398
00399 static const Widget _errmsg_widgets[] = {
00400 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_RED, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00401 { WWT_CAPTION, RESIZE_NONE, COLOUR_RED, 11, 239, 0, 13, STR_00B2_MESSAGE, STR_NULL},
00402 { WWT_PANEL, RESIZE_BOTTOM, COLOUR_RED, 0, 239, 14, 45, 0x0, STR_NULL},
00403 { WIDGETS_END},
00404 };
00405
00406 static const Widget _errmsg_face_widgets[] = {
00407 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_RED, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00408 { WWT_CAPTION, RESIZE_NONE, COLOUR_RED, 11, 333, 0, 13, STR_00B3_MESSAGE_FROM, STR_NULL},
00409 { WWT_PANEL, RESIZE_BOTTOM, COLOUR_RED, 0, 333, 14, 136, 0x0, STR_NULL},
00410 { WIDGETS_END},
00411 };
00412
00413 struct ErrmsgWindow : public Window {
00414 private:
00415 uint duration;
00416 uint64 decode_params[20];
00417 StringID message_1;
00418 StringID message_2;
00419 bool show_company_manager_face;
00420
00421 int y[2];
00422
00423 public:
00424 ErrmsgWindow(Point pt, int width, int height, StringID msg1, StringID msg2, const Widget *widget, bool show_company_manager_face) :
00425 Window(pt.x, pt.y, width, height, WC_ERRMSG, widget),
00426 show_company_manager_face(show_company_manager_face)
00427 {
00428 this->duration = _settings_client.gui.errmsg_duration;
00429 CopyOutDParam(this->decode_params, 0, lengthof(this->decode_params));
00430 this->message_1 = msg1;
00431 this->message_2 = msg2;
00432 this->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET;
00433
00434 SwitchToErrorRefStack();
00435 RewindTextRefStack();
00436
00437 assert(msg2 != INVALID_STRING_ID);
00438
00439 int h2 = 3 + GetStringHeight(msg2, width - 2);
00440 int h1 = (msg1 == INVALID_STRING_ID) ? 0 : 3 + GetStringHeight(msg1, width - 2);
00441
00442 SwitchToNormalRefStack();
00443
00444 int h = 15 + h1 + h2;
00445 height = max<int>(height, h);
00446
00447 if (msg1 == INVALID_STRING_ID) {
00448
00449 y[1] = (height - 15) / 2 + 15 - 5;
00450 } else {
00451 int over = (height - h) / 4;
00452
00453 y[1] = 15 + h2 / 2 + 1 - 5 + over;
00454 y[0] = height - 3 - h1 / 2 - 5 - over;
00455 }
00456
00457 this->FindWindowPlacementAndResize(width, height);
00458 }
00459
00460 virtual void OnPaint()
00461 {
00462 CopyInDParam(0, this->decode_params, lengthof(this->decode_params));
00463 this->DrawWidgets();
00464 CopyInDParam(0, this->decode_params, lengthof(this->decode_params));
00465
00466
00467
00468
00469 SwitchToErrorRefStack();
00470 RewindTextRefStack();
00471
00472 if (this->show_company_manager_face) {
00473 const Company *c = GetCompany((CompanyID)GetDParamX(this->decode_params, 2));
00474 DrawCompanyManagerFace(c->face, c->colour, 2, 16);
00475 }
00476
00477 DrawStringMultiCenter(this->width - 120, y[1], this->message_2, this->width - 2);
00478 if (this->message_1 != INVALID_STRING_ID) DrawStringMultiCenter(this->width - 120, y[0], this->message_1, this->width - 2);
00479
00480
00481 SwitchToNormalRefStack();
00482 }
00483
00484 virtual void OnMouseLoop()
00485 {
00486 if (_right_button_down) delete this;
00487 }
00488
00489 virtual void OnHundredthTick()
00490 {
00491 if (--this->duration == 0) delete this;
00492 }
00493
00494 ~ErrmsgWindow()
00495 {
00496 SetRedErrorSquare(INVALID_TILE);
00497 extern StringID _switch_mode_errorstr;
00498 _switch_mode_errorstr = INVALID_STRING_ID;
00499 }
00500
00501 virtual EventState OnKeyPress(uint16 key, uint16 keycode)
00502 {
00503 if (keycode != WKC_SPACE) return ES_NOT_HANDLED;
00504 delete this;
00505 return ES_HANDLED;
00506 }
00507 };
00508
00509 void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y)
00510 {
00511 DeleteWindowById(WC_ERRMSG, 0);
00512
00513 if (!_settings_client.gui.errmsg_duration) return;
00514
00515 if (msg_2 == STR_NULL) msg_2 = STR_EMPTY;
00516
00517 Point pt;
00518 const ViewPort *vp;
00519
00520 if (msg_1 != STR_013B_OWNED_BY || GetDParam(2) >= 8) {
00521 if ((x | y) != 0) {
00522 pt = RemapCoords2(x, y);
00523 vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00524
00525
00526 pt.x = UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left;
00527 pt.x = (pt.x < (_screen.width >> 1)) ? _screen.width - 260 : 20;
00528
00529
00530 pt.y = UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top;
00531 pt.y = (pt.y < (_screen.height >> 1)) ? _screen.height - 80 : 100;
00532
00533 } else {
00534 pt.x = (_screen.width - 240) >> 1;
00535 pt.y = (_screen.height - 46) >> 1;
00536 }
00537 new ErrmsgWindow(pt, 240, 46, msg_1, msg_2, _errmsg_widgets, false);
00538 } else {
00539 if ((x | y) != 0) {
00540 pt = RemapCoords2(x, y);
00541 vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00542 pt.x = Clamp(UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left - (334 / 2), 0, _screen.width - 334);
00543 pt.y = Clamp(UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top - (137 / 2), 22, _screen.height - 137);
00544 } else {
00545 pt.x = (_screen.width - 334) >> 1;
00546 pt.y = (_screen.height - 137) >> 1;
00547 }
00548 new ErrmsgWindow(pt, 334, 137, msg_1, msg_2, _errmsg_face_widgets, true);
00549 }
00550 }
00551
00552 void ShowEstimatedCostOrIncome(Money cost, int x, int y)
00553 {
00554 StringID msg = STR_0805_ESTIMATED_COST;
00555
00556 if (cost < 0) {
00557 cost = -cost;
00558 msg = STR_0807_ESTIMATED_INCOME;
00559 }
00560 SetDParam(0, cost);
00561 ShowErrorMessage(INVALID_STRING_ID, msg, x, y);
00562 }
00563
00564 void ShowCostOrIncomeAnimation(int x, int y, int z, Money cost)
00565 {
00566 Point pt = RemapCoords(x, y, z);
00567 StringID msg = STR_0801_COST;
00568
00569 if (cost < 0) {
00570 cost = -cost;
00571 msg = STR_0803_INCOME;
00572 }
00573 SetDParam(0, cost);
00574 AddTextEffect(msg, pt.x, pt.y, 0x250, TE_RISING);
00575 }
00576
00577 void ShowFeederIncomeAnimation(int x, int y, int z, Money cost)
00578 {
00579 Point pt = RemapCoords(x, y, z);
00580
00581 SetDParam(0, cost);
00582 AddTextEffect(STR_FEEDER, pt.x, pt.y, 0x250, TE_RISING);
00583 }
00584
00585 TextEffectID ShowFillingPercent(int x, int y, int z, uint8 percent, StringID string)
00586 {
00587 Point pt = RemapCoords(x, y, z);
00588
00589 assert(string != STR_NULL);
00590
00591 SetDParam(0, percent);
00592 return AddTextEffect(string, pt.x, pt.y, 0xFFFF, TE_STATIC);
00593 }
00594
00595 void UpdateFillingPercent(TextEffectID te_id, uint8 percent, StringID string)
00596 {
00597 assert(string != STR_NULL);
00598
00599 SetDParam(0, percent);
00600 UpdateTextEffect(te_id, string);
00601 }
00602
00603 void HideFillingPercent(TextEffectID *te_id)
00604 {
00605 if (*te_id == INVALID_TE_ID) return;
00606
00607 RemoveTextEffect(*te_id);
00608 *te_id = INVALID_TE_ID;
00609 }
00610
00611 static const Widget _tooltips_widgets[] = {
00612 { WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 0, 199, 0, 31, 0x0, STR_NULL},
00613 { WIDGETS_END},
00614 };
00615
00616 struct TooltipsWindow : public Window
00617 {
00618 StringID string_id;
00619 byte paramcount;
00620 uint64 params[5];
00621 bool use_left_mouse_button;
00622
00623 TooltipsWindow(int x, int y, int width, int height, const Widget *widget,
00624 StringID str, uint paramcount, const uint64 params[], bool use_left_mouse_button) :
00625 Window(x, y, width, height, WC_TOOLTIPS, widget)
00626 {
00627 this->string_id = str;
00628 assert(sizeof(this->params[0]) == sizeof(params[0]));
00629 assert(paramcount <= lengthof(this->params));
00630 memcpy(this->params, params, sizeof(this->params[0]) * paramcount);
00631 this->paramcount = paramcount;
00632 this->use_left_mouse_button = use_left_mouse_button;
00633
00634 this->flags4 &= ~WF_WHITE_BORDER_MASK;
00635 this->widget[0].right = width;
00636 this->widget[0].bottom = height;
00637
00638 FindWindowPlacementAndResize(width, height);
00639 }
00640
00641 virtual void OnPaint()
00642 {
00643 GfxFillRect(0, 0, this->width - 1, this->height - 1, 0);
00644 GfxFillRect(1, 1, this->width - 2, this->height - 2, 0x44);
00645
00646 for (uint arg = 0; arg < this->paramcount; arg++) {
00647 SetDParam(arg, this->params[arg]);
00648 }
00649 DrawStringMultiCenter((this->width >> 1), (this->height >> 1) - 5, this->string_id, this->width - 2);
00650 }
00651
00652 virtual void OnMouseLoop()
00653 {
00654
00655
00656 if (this->use_left_mouse_button ? !_left_button_down : !_right_button_down) delete this;
00657 }
00658 };
00659
00666 void GuiShowTooltips(StringID str, uint paramcount, const uint64 params[], bool use_left_mouse_button)
00667 {
00668 DeleteWindowById(WC_TOOLTIPS, 0);
00669
00670 if (str == STR_NULL) return;
00671
00672 for (uint i = 0; i != paramcount; i++) SetDParam(i, params[i]);
00673 char buffer[512];
00674 GetString(buffer, str, lastof(buffer));
00675
00676 Dimension br = GetStringBoundingBox(buffer);
00677 br.width += 6; br.height += 4;
00678
00679
00680 if (br.width > 200) {
00681 br.height += ((br.width - 4) / 176) * 10;
00682 br.width = 200;
00683 }
00684
00685
00686
00687
00688 int y = Clamp(_cursor.pos.y + _cursor.size.y + _cursor.offs.y + 5, 22, _screen.height - 12);
00689 if (y + br.height > _screen.height - 12) y = _cursor.pos.y + _cursor.offs.y - br.height - 5;
00690 int x = Clamp(_cursor.pos.x - (br.width >> 1), 0, _screen.width - br.width);
00691
00692 new TooltipsWindow(x, y, br.width, br.height, _tooltips_widgets, str, paramcount, params, use_left_mouse_button);
00693 }
00694
00695
00696 static int DrawStationCoverageText(const AcceptedCargo cargo,
00697 int str_x, int str_y, StationCoverageType sct, bool supplies)
00698 {
00699 bool first = true;
00700
00701 char string[512];
00702 char *b = InlineString(string, supplies ? STR_SUPPLIES : STR_000D_ACCEPTS);
00703
00704 for (CargoID i = 0; i < NUM_CARGO; i++) {
00705 if (b >= lastof(string) - (1 + 2 * 4)) break;
00706 switch (sct) {
00707 case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00708 case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00709 case SCT_ALL: break;
00710 default: NOT_REACHED();
00711 }
00712 if (cargo[i] >= (supplies ? 1U : 8U)) {
00713 if (first) {
00714 first = false;
00715 } else {
00716
00717 *b++ = ',';
00718 *b++ = ' ';
00719 }
00720 b = InlineString(b, GetCargo(i)->name);
00721 }
00722 }
00723
00724
00725 if (first) b = InlineString(b, STR_00D0_NOTHING);
00726
00727 *b = '\0';
00728
00729
00730 assert(b < endof(string));
00731
00732 SetDParamStr(0, string);
00733 return DrawStringMultiLine(str_x, str_y, STR_JUST_RAW_STRING, 144);
00734 }
00735
00745 int DrawStationCoverageAreaText(int sx, int sy, StationCoverageType sct, int rad, bool supplies)
00746 {
00747 TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
00748 AcceptedCargo cargo;
00749 if (tile < MapSize()) {
00750 if (supplies) {
00751 GetProductionAroundTiles(cargo, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad);
00752 } else {
00753 GetAcceptanceAroundTiles(cargo, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad);
00754 }
00755 return sy + DrawStationCoverageText(cargo, sx, sy, sct, supplies);
00756 }
00757
00758 return sy;
00759 }
00760
00761 void CheckRedrawStationCoverage(const Window *w)
00762 {
00763 if (_thd.dirty & 1) {
00764 _thd.dirty &= ~1;
00765 SetWindowDirty(w);
00766 }
00767 }
00768
00769
00770
00771
00772 static void DelChar(Textbuf *tb, bool backspace)
00773 {
00774 WChar c;
00775 char *s = tb->buf + tb->caretpos;
00776
00777 if (backspace) s = Utf8PrevChar(s);
00778
00779 uint16 len = (uint16)Utf8Decode(&c, s);
00780 uint width = GetCharacterWidth(FS_NORMAL, c);
00781
00782 tb->width -= width;
00783 if (backspace) {
00784 tb->caretpos -= len;
00785 tb->caretxoffs -= width;
00786 }
00787
00788
00789 memmove(s, s + len, tb->size - (s - tb->buf) - len);
00790 tb->size -= len;
00791 }
00792
00800 bool DeleteTextBufferChar(Textbuf *tb, int delmode)
00801 {
00802 if (delmode == WKC_BACKSPACE && tb->caretpos != 0) {
00803 DelChar(tb, true);
00804 return true;
00805 } else if (delmode == WKC_DELETE && tb->caretpos < tb->size - 1) {
00806 DelChar(tb, false);
00807 return true;
00808 }
00809
00810 return false;
00811 }
00812
00817 void DeleteTextBufferAll(Textbuf *tb)
00818 {
00819 memset(tb->buf, 0, tb->maxsize);
00820 tb->size = 1;
00821 tb->width = tb->caretpos = tb->caretxoffs = 0;
00822 }
00823
00832 bool InsertTextBufferChar(Textbuf *tb, WChar key)
00833 {
00834 const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
00835 uint16 len = (uint16)Utf8CharLen(key);
00836 if (tb->size + len <= tb->maxsize && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) {
00837 memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->size - tb->caretpos);
00838 Utf8Encode(tb->buf + tb->caretpos, key);
00839 tb->size += len;
00840 tb->width += charwidth;
00841
00842 tb->caretpos += len;
00843 tb->caretxoffs += charwidth;
00844 return true;
00845 }
00846 return false;
00847 }
00848
00856 bool MoveTextBufferPos(Textbuf *tb, int navmode)
00857 {
00858 switch (navmode) {
00859 case WKC_LEFT:
00860 if (tb->caretpos != 0) {
00861 WChar c;
00862 const char *s = Utf8PrevChar(tb->buf + tb->caretpos);
00863 Utf8Decode(&c, s);
00864 tb->caretpos = s - tb->buf;
00865 tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
00866
00867 return true;
00868 }
00869 break;
00870
00871 case WKC_RIGHT:
00872 if (tb->caretpos < tb->size - 1) {
00873 WChar c;
00874
00875 tb->caretpos += (uint16)Utf8Decode(&c, tb->buf + tb->caretpos);
00876 tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
00877
00878 return true;
00879 }
00880 break;
00881
00882 case WKC_HOME:
00883 tb->caretpos = 0;
00884 tb->caretxoffs = 0;
00885 return true;
00886
00887 case WKC_END:
00888 tb->caretpos = tb->size - 1;
00889 tb->caretxoffs = tb->width;
00890 return true;
00891
00892 default:
00893 break;
00894 }
00895
00896 return false;
00897 }
00898
00908 void InitializeTextBuffer(Textbuf *tb, char *buf, uint16 maxsize, uint16 maxwidth)
00909 {
00910 assert(maxsize != 0);
00911
00912 tb->buf = buf;
00913 tb->maxsize = maxsize;
00914 tb->maxwidth = maxwidth;
00915 tb->caret = true;
00916 UpdateTextBufferSize(tb);
00917 }
00918
00925 void UpdateTextBufferSize(Textbuf *tb)
00926 {
00927 const char *buf = tb->buf;
00928
00929 tb->width = 0;
00930 tb->size = 1;
00931
00932 WChar c;
00933 while ((c = Utf8Consume(&buf)) != '\0') {
00934 tb->width += GetCharacterWidth(FS_NORMAL, c);
00935 tb->size += Utf8CharLen(c);
00936 }
00937
00938 assert(tb->size <= tb->maxsize);
00939
00940 tb->caretpos = tb->size - 1;
00941 tb->caretxoffs = tb->width;
00942 }
00943
00944 bool HandleCaret(Textbuf *tb)
00945 {
00946
00947 bool b = !!(_caret_timer & 0x20);
00948
00949 if (b != tb->caret) {
00950 tb->caret = b;
00951 return true;
00952 }
00953 return false;
00954 }
00955
00956 bool QueryString::HasEditBoxFocus(const Window *w, int wid) const
00957 {
00958 return ((w->window_class == WC_OSK &&
00959 _focused_window == w->parent &&
00960 w->parent->focused_widget &&
00961 w->parent->focused_widget->type == WWT_EDITBOX) ||
00962 w->IsWidgetGloballyFocused(wid));
00963 }
00964
00965 HandleEditBoxResult QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, Window::EventState &state)
00966 {
00967 if (!QueryString::HasEditBoxFocus(w, wid)) return HEBR_NOT_FOCUSED;
00968
00969 state = Window::ES_HANDLED;
00970
00971 switch (keycode) {
00972 case WKC_ESC: return HEBR_CANCEL;
00973
00974 case WKC_RETURN: case WKC_NUM_ENTER: return HEBR_CONFIRM;
00975
00976 case (WKC_CTRL | 'V'):
00977 if (InsertTextBufferClipboard(&this->text)) w->InvalidateWidget(wid);
00978 break;
00979
00980 case (WKC_CTRL | 'U'):
00981 DeleteTextBufferAll(&this->text);
00982 w->InvalidateWidget(wid);
00983 break;
00984
00985 case WKC_BACKSPACE: case WKC_DELETE:
00986 if (DeleteTextBufferChar(&this->text, keycode)) w->InvalidateWidget(wid);
00987 break;
00988
00989 case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
00990 if (MoveTextBufferPos(&this->text, keycode)) w->InvalidateWidget(wid);
00991 break;
00992
00993 default:
00994 if (IsValidChar(key, this->afilter)) {
00995 if (InsertTextBufferChar(&this->text, key)) w->InvalidateWidget(wid);
00996 } else {
00997 state = (this->afilter == CS_ALPHANUMERAL) ? Window::ES_HANDLED : Window::ES_NOT_HANDLED;
00998 }
00999 }
01000
01001 return HEBR_EDITING;
01002 }
01003
01004 void QueryString::HandleEditBox(Window *w, int wid)
01005 {
01006 if (HasEditBoxFocus(w, wid) && HandleCaret(&this->text)) {
01007 w->InvalidateWidget(wid);
01008
01009
01010 if (w->window_class != WC_OSK) {
01011 Window *w_osk = FindWindowById(WC_OSK, 0);
01012 if (w_osk != NULL && w_osk->parent == w) w_osk->OnInvalidateData();
01013 }
01014 }
01015 }
01016
01017 void QueryString::DrawEditBox(Window *w, int wid)
01018 {
01019 const Widget *wi = &w->widget[wid];
01020
01021 assert((wi->type & WWT_MASK) == WWT_EDITBOX);
01022
01023 GfxFillRect(wi->left + 1, wi->top + 1, wi->right - 1, wi->bottom - 1, 215);
01024
01025 DrawPixelInfo dpi;
01026 int delta;
01027
01028
01029 if (!FillDrawPixelInfo(&dpi,
01030 wi->left + 4,
01031 wi->top + 1,
01032 wi->right - wi->left - 4,
01033 wi->bottom - wi->top - 1)) {
01034 return;
01035 }
01036
01037 DrawPixelInfo *old_dpi = _cur_dpi;
01038 _cur_dpi = &dpi;
01039
01040
01041
01042 const Textbuf *tb = &this->text;
01043
01044 delta = (wi->right - wi->left) - tb->width - 10;
01045 if (delta > 0) delta = 0;
01046
01047 if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
01048
01049 DoDrawString(tb->buf, delta, 0, TC_YELLOW);
01050 if (HasEditBoxFocus(w, wid) && tb->caret) DoDrawString("_", tb->caretxoffs + delta, 0, TC_WHITE);
01051
01052 _cur_dpi = old_dpi;
01053 }
01054
01055 HandleEditBoxResult QueryStringBaseWindow::HandleEditBoxKey(int wid, uint16 key, uint16 keycode, EventState &state)
01056 {
01057 return this->QueryString::HandleEditBoxKey(this, wid, key, keycode, state);
01058 }
01059
01060 void QueryStringBaseWindow::HandleEditBox(int wid)
01061 {
01062 this->QueryString::HandleEditBox(this, wid);
01063 }
01064
01065 void QueryStringBaseWindow::DrawEditBox(int wid)
01066 {
01067 this->QueryString::DrawEditBox(this, wid);
01068 }
01069
01070 void QueryStringBaseWindow::OnOpenOSKWindow(int wid)
01071 {
01072 ShowOnScreenKeyboard(this, wid, 0, 0);
01073 }
01074
01075 enum QueryStringWidgets {
01076 QUERY_STR_WIDGET_TEXT = 3,
01077 QUERY_STR_WIDGET_DEFAULT,
01078 QUERY_STR_WIDGET_CANCEL,
01079 QUERY_STR_WIDGET_OK
01080 };
01081
01082
01083 struct QueryStringWindow : public QueryStringBaseWindow
01084 {
01085 QueryStringWindow(uint16 size, const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(size, desc)
01086 {
01087 this->parent = parent;
01088 this->SetFocusedWidget(QUERY_STR_WIDGET_TEXT);
01089
01090 this->FindWindowPlacementAndResize(desc);
01091 }
01092
01093 virtual void OnPaint()
01094 {
01095 SetDParam(0, this->caption);
01096 this->DrawWidgets();
01097
01098 this->DrawEditBox(QUERY_STR_WIDGET_TEXT);
01099 }
01100
01101 void OnOk()
01102 {
01103 if (this->orig == NULL || strcmp(this->text.buf, this->orig) != 0) {
01104
01105
01106 if (this->parent != NULL) {
01107 this->parent->OnQueryTextFinished(this->text.buf);
01108 } else {
01109 HandleOnEditText(this->text.buf);
01110 }
01111 this->handled = true;
01112 }
01113 }
01114
01115 virtual void OnClick(Point pt, int widget)
01116 {
01117 switch (widget) {
01118 case QUERY_STR_WIDGET_DEFAULT:
01119 this->text.buf[0] = '\0';
01120
01121 case QUERY_STR_WIDGET_OK:
01122 this->OnOk();
01123
01124 case QUERY_STR_WIDGET_CANCEL:
01125 delete this;
01126 break;
01127 }
01128 }
01129
01130 virtual void OnMouseLoop()
01131 {
01132 this->HandleEditBox(QUERY_STR_WIDGET_TEXT);
01133 }
01134
01135 virtual EventState OnKeyPress(uint16 key, uint16 keycode)
01136 {
01137 EventState state;
01138 switch (this->HandleEditBoxKey(QUERY_STR_WIDGET_TEXT, key, keycode, state)) {
01139 default: NOT_REACHED();
01140 case HEBR_EDITING: {
01141 Window *osk = FindWindowById(WC_OSK, 0);
01142 if (osk != NULL && osk->parent == this) osk->OnInvalidateData();
01143 } break;
01144 case HEBR_CONFIRM: this->OnOk();
01145
01146 case HEBR_CANCEL: delete this; break;
01147 case HEBR_NOT_FOCUSED: break;
01148 }
01149 return state;
01150 }
01151
01152 virtual void OnOpenOSKWindow(int wid)
01153 {
01154 ShowOnScreenKeyboard(this, wid, QUERY_STR_WIDGET_CANCEL, QUERY_STR_WIDGET_OK);
01155 }
01156
01157 ~QueryStringWindow()
01158 {
01159 if (!this->handled && this->parent != NULL) {
01160 Window *parent = this->parent;
01161 this->parent = NULL;
01162 parent->OnQueryTextFinished(NULL);
01163 }
01164 }
01165 };
01166
01167 static const Widget _query_string_widgets[] = {
01168 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
01169 { WWT_CAPTION, RESIZE_NONE, COLOUR_GREY, 11, 259, 0, 13, STR_012D, STR_NULL},
01170 { WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 0, 259, 14, 29, 0x0, STR_NULL},
01171 { WWT_EDITBOX, RESIZE_NONE, COLOUR_GREY, 2, 257, 16, 27, 0x0, STR_NULL},
01172 { WWT_TEXTBTN, RESIZE_NONE, COLOUR_GREY, 0, 86, 30, 41, STR_DEFAULT, STR_NULL},
01173 { WWT_TEXTBTN, RESIZE_NONE, COLOUR_GREY, 87, 172, 30, 41, STR_012E_CANCEL, STR_NULL},
01174 { WWT_TEXTBTN, RESIZE_NONE, COLOUR_GREY, 173, 259, 30, 41, STR_012F_OK, STR_NULL},
01175 { WIDGETS_END},
01176 };
01177
01178 static const WindowDesc _query_string_desc = {
01179 190, 219, 260, 42, 260, 42,
01180 WC_QUERY_STRING, WC_NONE,
01181 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
01182 _query_string_widgets,
01183 };
01184
01195 void ShowQueryString(StringID str, StringID caption, uint maxsize, uint maxwidth, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
01196 {
01197 DeleteWindowById(WC_QUERY_STRING, 0);
01198
01199 QueryStringWindow *w = new QueryStringWindow(maxsize, &_query_string_desc, parent);
01200
01201 GetString(w->edit_str_buf, str, &w->edit_str_buf[maxsize - 1]);
01202 w->edit_str_buf[maxsize - 1] = '\0';
01203
01204 if ((flags & QSF_ACCEPT_UNCHANGED) == 0) w->orig = strdup(w->edit_str_buf);
01205
01206 if ((flags & QSF_ENABLE_DEFAULT) == 0) {
01207
01208 w->SetWidgetHiddenState(QUERY_STR_WIDGET_DEFAULT, true);
01209 w->widget[QUERY_STR_WIDGET_CANCEL].left = 0;
01210 w->widget[QUERY_STR_WIDGET_CANCEL].right = w->width / 2 - 1;
01211 w->widget[QUERY_STR_WIDGET_OK].left = w->width / 2;
01212 w->widget[QUERY_STR_WIDGET_OK].right = w->width - 1;
01213 }
01214
01215 w->LowerWidget(QUERY_STR_WIDGET_TEXT);
01216 w->caption = caption;
01217 w->afilter = afilter;
01218 InitializeTextBuffer(&w->text, w->edit_str_buf, maxsize, maxwidth);
01219 }
01220
01221
01222 enum QueryWidgets {
01223 QUERY_WIDGET_CAPTION = 1,
01224 QUERY_WIDGET_NO = 3,
01225 QUERY_WIDGET_YES
01226 };
01227
01231 struct QueryWindow : public Window {
01232 QueryCallbackProc *proc;
01233 uint64 params[10];
01234 StringID message;
01235
01236 QueryWindow(const WindowDesc *desc, StringID caption, StringID message, Window *parent, QueryCallbackProc *callback) : Window(desc)
01237 {
01238 if (parent == NULL) parent = FindWindowById(WC_MAIN_WINDOW, 0);
01239 this->parent = parent;
01240 this->left = parent->left + (parent->width / 2) - (this->width / 2);
01241 this->top = parent->top + (parent->height / 2) - (this->height / 2);
01242
01243
01244
01245 CopyOutDParam(this->params, 0, lengthof(this->params));
01246 this->widget[QUERY_WIDGET_CAPTION].data = caption;
01247 this->message = message;
01248 this->proc = callback;
01249
01250 this->FindWindowPlacementAndResize(desc);
01251 }
01252
01253 ~QueryWindow()
01254 {
01255 if (this->proc != NULL) this->proc(this->parent, false);
01256 }
01257
01258 virtual void OnPaint()
01259 {
01260 CopyInDParam(0, this->params, lengthof(this->params));
01261 this->DrawWidgets();
01262 CopyInDParam(0, this->params, lengthof(this->params));
01263
01264 DrawStringMultiCenter(this->width / 2, (this->height / 2) - 10, this->message, this->width - 2);
01265 }
01266
01267 virtual void OnClick(Point pt, int widget)
01268 {
01269 switch (widget) {
01270 case QUERY_WIDGET_YES: {
01271
01272
01273 QueryCallbackProc *proc = this->proc;
01274 Window *parent = this->parent;
01275
01276 this->proc = NULL;
01277 delete this;
01278 if (proc != NULL) {
01279 proc(parent, true);
01280 proc = NULL;
01281 }
01282 } break;
01283 case QUERY_WIDGET_NO:
01284 delete this;
01285 break;
01286 }
01287 }
01288
01289 virtual EventState OnKeyPress(uint16 key, uint16 keycode)
01290 {
01291
01292 switch (keycode) {
01293 case WKC_RETURN:
01294 case WKC_NUM_ENTER:
01295 if (this->proc != NULL) {
01296 this->proc(this->parent, true);
01297 this->proc = NULL;
01298 }
01299
01300 case WKC_ESC:
01301 delete this;
01302 return ES_HANDLED;
01303 }
01304 return ES_NOT_HANDLED;
01305 }
01306 };
01307
01308
01309 static const Widget _query_widgets[] = {
01310 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_RED, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
01311 { WWT_CAPTION, RESIZE_NONE, COLOUR_RED, 11, 209, 0, 13, STR_NULL, STR_NULL},
01312 { WWT_PANEL, RESIZE_NONE, COLOUR_RED, 0, 209, 14, 81, 0x0, STR_NULL},
01313 {WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_YELLOW, 20, 90, 62, 73, STR_00C9_NO, STR_NULL},
01314 {WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_YELLOW, 120, 190, 62, 73, STR_00C8_YES, STR_NULL},
01315 { WIDGETS_END },
01316 };
01317
01318 static const WindowDesc _query_desc = {
01319 WDP_CENTER, WDP_CENTER, 210, 82, 210, 82,
01320 WC_CONFIRM_POPUP_QUERY, WC_NONE,
01321 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_DEF_WIDGET | WDF_MODAL,
01322 _query_widgets,
01323 };
01324
01334 void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallbackProc *callback)
01335 {
01336 new QueryWindow(&_query_desc, caption, message, parent, callback);
01337 }
01338
01339
01340 static const Widget _load_dialog_widgets[] = {
01341 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
01342 { WWT_CAPTION, RESIZE_RIGHT, COLOUR_GREY, 11, 256, 0, 13, STR_NULL, STR_018C_WINDOW_TITLE_DRAG_THIS},
01343 { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_GREY, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
01344 { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_GREY, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP},
01345 { WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 0, 256, 26, 47, 0x0, STR_NULL},
01346 { WWT_PANEL, RESIZE_RB, COLOUR_GREY, 0, 256, 48, 153, 0x0, STR_NULL},
01347 { WWT_PUSHIMGBTN, RESIZE_LR, COLOUR_GREY, 245, 256, 48, 59, SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON},
01348 { WWT_INSET, RESIZE_RB, COLOUR_GREY, 2, 243, 50, 151, 0x0, STR_400A_LIST_OF_DRIVES_DIRECTORIES},
01349 { WWT_SCROLLBAR, RESIZE_LRB, COLOUR_GREY, 245, 256, 60, 141, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
01350 { WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_GREY, 245, 256, 142, 153, 0x0, STR_RESIZE_BUTTON},
01351 { WIDGETS_END},
01352 };
01353
01354 static const Widget _save_dialog_widgets[] = {
01355 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
01356 { WWT_CAPTION, RESIZE_RIGHT, COLOUR_GREY, 11, 256, 0, 13, STR_NULL, STR_018C_WINDOW_TITLE_DRAG_THIS},
01357 { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_GREY, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
01358 { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_GREY, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP},
01359 { WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 0, 256, 26, 47, 0x0, STR_NULL},
01360 { WWT_PANEL, RESIZE_RB, COLOUR_GREY, 0, 256, 48, 151, 0x0, STR_NULL},
01361 { WWT_PUSHIMGBTN, RESIZE_LR, COLOUR_GREY, 245, 256, 48, 59, SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON},
01362 { WWT_INSET, RESIZE_RB, COLOUR_GREY, 2, 243, 50, 150, 0x0, STR_400A_LIST_OF_DRIVES_DIRECTORIES},
01363 { WWT_SCROLLBAR, RESIZE_LRB, COLOUR_GREY, 245, 256, 60, 151, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
01364 { WWT_PANEL, RESIZE_RTB, COLOUR_GREY, 0, 256, 152, 167, 0x0, STR_NULL},
01365 { WWT_EDITBOX, RESIZE_RTB, COLOUR_GREY, 2, 254, 154, 165, STR_SAVE_OSKTITLE, STR_400B_CURRENTLY_SELECTED_NAME},
01366 { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_GREY, 0, 127, 168, 179, STR_4003_DELETE, STR_400C_DELETE_THE_CURRENTLY_SELECTED},
01367 { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_GREY, 128, 244, 168, 179, STR_4002_SAVE, STR_400D_SAVE_THE_CURRENT_GAME_USING},
01368 { WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_GREY, 245, 256, 168, 179, 0x0, STR_RESIZE_BUTTON},
01369 { WIDGETS_END},
01370 };
01371
01372
01373 const TextColour _fios_colours[] = {
01374 TC_LIGHT_BLUE, TC_DARK_GREEN, TC_DARK_GREEN, TC_ORANGE, TC_LIGHT_BROWN,
01375 TC_ORANGE, TC_LIGHT_BROWN, TC_ORANGE, TC_ORANGE, TC_YELLOW
01376 };
01377
01378 void BuildFileList()
01379 {
01380 _fios_path_changed = true;
01381 FiosFreeSavegameList();
01382
01383 switch (_saveload_mode) {
01384 case SLD_NEW_GAME:
01385 case SLD_LOAD_SCENARIO:
01386 case SLD_SAVE_SCENARIO:
01387 FiosGetScenarioList(_saveload_mode); break;
01388 case SLD_LOAD_HEIGHTMAP:
01389 FiosGetHeightmapList(_saveload_mode); break;
01390
01391 default: FiosGetSavegameList(_saveload_mode); break;
01392 }
01393 }
01394
01395 static void DrawFiosTexts(uint maxw)
01396 {
01397 static const char *path = NULL;
01398 static StringID str = STR_4006_UNABLE_TO_READ_DRIVE;
01399 static uint64 tot = 0;
01400
01401 if (_fios_path_changed) {
01402 str = FiosGetDescText(&path, &tot);
01403 _fios_path_changed = false;
01404 }
01405
01406 if (str != STR_4006_UNABLE_TO_READ_DRIVE) SetDParam(0, tot);
01407 DrawString(2, 37, str, TC_FROMSTRING);
01408 DoDrawStringTruncated(path, 2, 27, TC_BLACK, maxw);
01409 }
01410
01411 static void MakeSortedSaveGameList()
01412 {
01413 uint sort_start = 0;
01414 uint sort_end = 0;
01415
01416
01417
01418
01419
01420 for (const FiosItem *item = _fios_items.Begin(); item != _fios_items.End(); item++) {
01421 switch (item->type) {
01422 case FIOS_TYPE_DIR: sort_start++; break;
01423 case FIOS_TYPE_PARENT: sort_start++; break;
01424 case FIOS_TYPE_DRIVE: sort_end++; break;
01425 default: break;
01426 }
01427 }
01428
01429 uint s_amount = _fios_items.Length() - sort_start - sort_end;
01430 if (s_amount > 0) {
01431 qsort(_fios_items.Get(sort_start), s_amount, sizeof(FiosItem), compare_FiosItems);
01432 }
01433 }
01434
01435 extern void StartupEngines();
01436
01437 struct SaveLoadWindow : public QueryStringBaseWindow {
01438 private:
01439 enum SaveLoadWindowWidgets {
01440 SLWW_CLOSE = 0,
01441 SLWW_WINDOWTITLE,
01442 SLWW_SORT_BYNAME,
01443 SLWW_SORT_BYDATE,
01444 SLWW_HOME_BUTTON = 6,
01445 SLWW_DRIVES_DIRECTORIES_LIST,
01446 SLWW_SAVE_OSK_TITLE = 10,
01447 SLWW_DELETE_SELECTION,
01448 SLWW_SAVE_GAME,
01449 };
01450
01451 FiosItem o_dir;
01452 public:
01453
01454 void GenerateFileName()
01455 {
01456 GenerateDefaultSaveName(this->edit_str_buf, &this->edit_str_buf[this->edit_str_size - 1]);
01457 }
01458
01459 SaveLoadWindow(const WindowDesc *desc, SaveLoadDialogMode mode) : QueryStringBaseWindow(64, desc)
01460 {
01461 static const StringID saveload_captions[] = {
01462 STR_4001_LOAD_GAME,
01463 STR_0298_LOAD_SCENARIO,
01464 STR_4000_SAVE_GAME,
01465 STR_0299_SAVE_SCENARIO,
01466 STR_LOAD_HEIGHTMAP,
01467 };
01468
01469 SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0);
01470
01471
01472
01473 switch (mode) {
01474 case SLD_SAVE_GAME: this->GenerateFileName(); break;
01475 case SLD_SAVE_SCENARIO: strcpy(this->edit_str_buf, "UNNAMED"); break;
01476 default: break;
01477 }
01478
01479 assert((uint)mode < lengthof(saveload_captions));
01480
01481 this->widget[SLWW_WINDOWTITLE].data = saveload_captions[mode];
01482 this->LowerWidget(SLWW_DRIVES_DIRECTORIES_LIST);
01483
01484 this->afilter = CS_ALPHANUMERAL;
01485 InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 240);
01486
01487
01488
01489 if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) {
01490 if (_pause_game >= 0) DoCommandP(0, 1, 0, CMD_PAUSE);
01491 }
01492
01493 BuildFileList();
01494
01495 ResetObjectToPlace();
01496
01497 o_dir.type = FIOS_TYPE_DIRECT;
01498 switch (_saveload_mode) {
01499 case SLD_SAVE_GAME:
01500 case SLD_LOAD_GAME:
01501 FioGetDirectory(o_dir.name, lengthof(o_dir.name), SAVE_DIR);
01502 break;
01503
01504 case SLD_SAVE_SCENARIO:
01505 case SLD_LOAD_SCENARIO:
01506 FioGetDirectory(o_dir.name, lengthof(o_dir.name), SCENARIO_DIR);
01507 break;
01508
01509 case SLD_LOAD_HEIGHTMAP:
01510 FioGetDirectory(o_dir.name, lengthof(o_dir.name), HEIGHTMAP_DIR);
01511 break;
01512
01513 default:
01514 strecpy(o_dir.name, _personal_dir, lastof(o_dir.name));
01515 }
01516
01517
01518 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01519 this->SetFocusedWidget(SLWW_SAVE_OSK_TITLE);
01520 }
01521
01522 this->vscroll.cap = 10;
01523 this->resize.step_width = 2;
01524 this->resize.step_height = 10;
01525
01526 this->FindWindowPlacementAndResize(desc);
01527 }
01528
01529 virtual ~SaveLoadWindow()
01530 {
01531
01532 if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) {
01533 if (_pause_game >= 0) DoCommandP(0, 0, 0, CMD_PAUSE);
01534 }
01535 FiosFreeSavegameList();
01536 }
01537
01538 virtual void OnPaint()
01539 {
01540 int y;
01541
01542 SetVScrollCount(this, _fios_items.Length());
01543 this->DrawWidgets();
01544 DrawFiosTexts(this->width);
01545
01546 if (_savegame_sort_dirty) {
01547 _savegame_sort_dirty = false;
01548 MakeSortedSaveGameList();
01549 }
01550
01551 const Widget *widg = &this->widget[SLWW_DRIVES_DIRECTORIES_LIST];
01552 GfxFillRect(widg->left + 1, widg->top + 1, widg->right, widg->bottom, 0xD7);
01553 this->DrawSortButtonState(_savegame_sort_order & SORT_BY_NAME ? SLWW_SORT_BYNAME : SLWW_SORT_BYDATE, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP);
01554
01555 y = widg->top + 1;
01556 for (uint pos = this->vscroll.pos; pos < _fios_items.Length(); pos++) {
01557 const FiosItem *item = _fios_items.Get(pos);
01558
01559 DoDrawStringTruncated(item->title, 4, y, _fios_colours[item->type], this->width - 18);
01560 y += 10;
01561 if (y >= this->vscroll.cap * 10 + widg->top + 1) break;
01562 }
01563
01564 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01565 this->DrawEditBox(SLWW_SAVE_OSK_TITLE);
01566 }
01567 }
01568
01569 virtual void OnClick(Point pt, int widget)
01570 {
01571 switch (widget) {
01572 case SLWW_SORT_BYNAME:
01573 _savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ?
01574 SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME;
01575 _savegame_sort_dirty = true;
01576 this->SetDirty();
01577 break;
01578
01579 case SLWW_SORT_BYDATE:
01580 _savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ?
01581 SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE;
01582 _savegame_sort_dirty = true;
01583 this->SetDirty();
01584 break;
01585
01586 case SLWW_HOME_BUTTON:
01587 FiosBrowseTo(&o_dir);
01588 this->SetDirty();
01589 BuildFileList();
01590 break;
01591
01592 case SLWW_DRIVES_DIRECTORIES_LIST: {
01593 int y = (pt.y - this->widget[widget].top - 1) / 10;
01594
01595 if (y < 0 || (y += this->vscroll.pos) >= this->vscroll.count) return;
01596
01597 const FiosItem *file = _fios_items.Get(y);
01598
01599 const char *name = FiosBrowseTo(file);
01600 if (name != NULL) {
01601 if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
01602 _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD;
01603
01604 SetFiosType(file->type);
01605 strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
01606 strecpy(_file_to_saveload.title, file->title, lastof(_file_to_saveload.title));
01607
01608 delete this;
01609 } else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
01610 SetFiosType(file->type);
01611 strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
01612 strecpy(_file_to_saveload.title, file->title, lastof(_file_to_saveload.title));
01613
01614 delete this;
01615 ShowHeightmapLoad();
01616 } else {
01617
01618 ttd_strlcpy(this->text.buf, file->title, this->text.maxsize);
01619 UpdateTextBufferSize(&this->text);
01620 this->InvalidateWidget(SLWW_SAVE_OSK_TITLE);
01621 }
01622 } else {
01623
01624 this->SetDirty();
01625 BuildFileList();
01626 }
01627 break;
01628 }
01629
01630 case SLWW_DELETE_SELECTION: case SLWW_SAVE_GAME:
01631 break;
01632 }
01633 }
01634
01635 virtual void OnMouseLoop()
01636 {
01637 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01638 this->HandleEditBox(SLWW_SAVE_OSK_TITLE);
01639 }
01640 }
01641
01642 virtual EventState OnKeyPress(uint16 key, uint16 keycode)
01643 {
01644 if (keycode == WKC_ESC) {
01645 delete this;
01646 return ES_HANDLED;
01647 }
01648
01649 EventState state = ES_NOT_HANDLED;
01650 if ((_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) &&
01651 this->HandleEditBoxKey(SLWW_SAVE_OSK_TITLE, key, keycode, state) == HEBR_CONFIRM) {
01652 this->HandleButtonClick(SLWW_SAVE_GAME);
01653 }
01654
01655 return state;
01656 }
01657
01658 virtual void OnTimeout()
01659 {
01660
01661
01662 if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) return;
01663
01664 if (this->IsWidgetLowered(SLWW_DELETE_SELECTION)) {
01665 if (!FiosDelete(this->text.buf)) {
01666 ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0);
01667 } else {
01668 BuildFileList();
01669
01670 if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName();
01671 }
01672
01673 UpdateTextBufferSize(&this->text);
01674 this->SetDirty();
01675 } else if (this->IsWidgetLowered(SLWW_SAVE_GAME)) {
01676 _switch_mode = SM_SAVE;
01677 FiosMakeSavegameName(_file_to_saveload.name, this->text.buf, sizeof(_file_to_saveload.name));
01678
01679
01680 if (_game_mode == GM_EDITOR) StartupEngines();
01681 }
01682 }
01683
01684 virtual void OnResize(Point new_size, Point delta)
01685 {
01686
01687 uint diff = delta.x / 2;
01688 this->widget[SLWW_SORT_BYNAME].right += diff;
01689 this->widget[SLWW_SORT_BYDATE].left += diff;
01690 this->widget[SLWW_SORT_BYDATE].right += delta.x;
01691
01692
01693 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01694 this->widget[SLWW_DELETE_SELECTION].right += diff;
01695 this->widget[SLWW_SAVE_GAME].left += diff;
01696 this->widget[SLWW_SAVE_GAME].right += delta.x;
01697 }
01698
01699 this->vscroll.cap += delta.y / 10;
01700 }
01701 };
01702
01703 static const WindowDesc _load_dialog_desc = {
01704 WDP_CENTER, WDP_CENTER, 257, 154, 257, 294,
01705 WC_SAVELOAD, WC_NONE,
01706 WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
01707 _load_dialog_widgets,
01708 };
01709
01710 static const WindowDesc _save_dialog_desc = {
01711 WDP_CENTER, WDP_CENTER, 257, 180, 257, 320,
01712 WC_SAVELOAD, WC_NONE,
01713 WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
01714 _save_dialog_widgets,
01715 };
01716
01719 static const FileType _file_modetotype[] = {
01720 FT_SAVEGAME,
01721 FT_SCENARIO,
01722 FT_SAVEGAME,
01723 FT_SCENARIO,
01724 FT_HEIGHTMAP,
01725 FT_SAVEGAME,
01726 };
01727
01728 void ShowSaveLoadDialog(SaveLoadDialogMode mode)
01729 {
01730 DeleteWindowById(WC_SAVELOAD, 0);
01731
01732 const WindowDesc *sld;
01733 switch (mode) {
01734 case SLD_SAVE_GAME:
01735 case SLD_SAVE_SCENARIO:
01736 sld = &_save_dialog_desc; break;
01737 default:
01738 sld = &_load_dialog_desc; break;
01739 }
01740
01741 _saveload_mode = mode;
01742 _file_to_saveload.filetype = _file_modetotype[mode];
01743
01744 new SaveLoadWindow(sld, mode);
01745 }
01746
01747 void RedrawAutosave()
01748 {
01749 SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0));
01750 }
01751
01752 void SetFiosType(const byte fiostype)
01753 {
01754 switch (fiostype) {
01755 case FIOS_TYPE_FILE:
01756 case FIOS_TYPE_SCENARIO:
01757 _file_to_saveload.mode = SL_LOAD;
01758 break;
01759
01760 case FIOS_TYPE_OLDFILE:
01761 case FIOS_TYPE_OLD_SCENARIO:
01762 _file_to_saveload.mode = SL_OLD_LOAD;
01763 break;
01764
01765 #ifdef WITH_PNG
01766 case FIOS_TYPE_PNG:
01767 _file_to_saveload.mode = SL_PNG;
01768 break;
01769 #endif
01770
01771 case FIOS_TYPE_BMP:
01772 _file_to_saveload.mode = SL_BMP;
01773 break;
01774
01775 default:
01776 _file_to_saveload.mode = SL_INVALID;
01777 break;
01778 }
01779 }