00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "openttd.h"
00014 #include "landscape.h"
00015 #include "newgrf_text.h"
00016 #include "saveload/saveload.h"
00017 #include "gui.h"
00018 #include "viewport_func.h"
00019 #include "gfx_func.h"
00020 #include "command_func.h"
00021 #include "company_func.h"
00022 #include "town.h"
00023 #include "network/network.h"
00024 #include "network/network_content.h"
00025 #include "company_base.h"
00026 #include "texteff.hpp"
00027 #include "cargotype.h"
00028 #include "company_manager_face.h"
00029 #include "strings_func.h"
00030 #include "fileio_func.h"
00031 #include "fios.h"
00032 #include "zoom_func.h"
00033 #include "window_func.h"
00034 #include "tilehighlight_func.h"
00035 #include "querystring_gui.h"
00036 #include "core/geometry_func.hpp"
00037
00038 #include "table/strings.h"
00039
00040
00047 bool GetClipboardContents(char *buffer, size_t buff_len);
00048
00049
00050
00051 SaveLoadDialogMode _saveload_mode;
00052
00053
00054 static bool _fios_path_changed;
00055 static bool _savegame_sort_dirty;
00056 int _caret_timer;
00057
00059 enum LandInfoWidgets {
00060 LIW_BACKGROUND,
00061 };
00062
00063 static const NWidgetPart _nested_land_info_widgets[] = {
00064 NWidget(NWID_HORIZONTAL),
00065 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00066 NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_LAND_AREA_INFORMATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00067 EndContainer(),
00068 NWidget(WWT_PANEL, COLOUR_GREY, LIW_BACKGROUND), EndContainer(),
00069 };
00070
00071 static const WindowDesc _land_info_desc(
00072 WDP_AUTO, 0, 0,
00073 WC_LAND_INFO, WC_NONE,
00074 0,
00075 _nested_land_info_widgets, lengthof(_nested_land_info_widgets)
00076 );
00077
00078 class LandInfoWindow : public Window {
00079 enum {
00080 LAND_INFO_CENTERED_LINES = 12,
00081 LAND_INFO_MULTICENTER_LINE = LAND_INFO_CENTERED_LINES,
00082 LAND_INFO_LINE_END,
00083
00084 LAND_INFO_LINE_BUFF_SIZE = 512,
00085 };
00086
00087 public:
00088 char landinfo_data[LAND_INFO_LINE_END][LAND_INFO_LINE_BUFF_SIZE];
00089
00090 virtual void OnPaint()
00091 {
00092 this->DrawWidgets();
00093 }
00094
00095 virtual void DrawWidget(const Rect &r, int widget) const
00096 {
00097 if (widget != LIW_BACKGROUND) return;
00098
00099 uint y = r.top + WD_TEXTPANEL_TOP;
00100 for (uint i = 0; i < LAND_INFO_CENTERED_LINES; i++) {
00101 if (StrEmpty(this->landinfo_data[i])) break;
00102
00103 DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, this->landinfo_data[i], i == 0 ? TC_LIGHT_BLUE : TC_FROMSTRING, SA_CENTER);
00104 y += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
00105 if (i == 0) y += 4;
00106 }
00107
00108 if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) {
00109 SetDParamStr(0, this->landinfo_data[LAND_INFO_MULTICENTER_LINE]);
00110 DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, r.bottom - WD_TEXTPANEL_BOTTOM, STR_JUST_RAW_STRING, TC_FROMSTRING, SA_CENTER);
00111 }
00112 }
00113
00114 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00115 {
00116 if (widget != LIW_BACKGROUND) return;
00117
00118 size->height = WD_TEXTPANEL_TOP + WD_TEXTPANEL_BOTTOM;
00119 for (uint i = 0; i < LAND_INFO_CENTERED_LINES; i++) {
00120 if (StrEmpty(this->landinfo_data[i])) break;
00121
00122 uint width = GetStringBoundingBox(this->landinfo_data[i]).width + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT;
00123 size->width = max(size->width, width);
00124
00125 size->height += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
00126 if (i == 0) size->height += 4;
00127 }
00128
00129 if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) {
00130 uint width = GetStringBoundingBox(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]).width + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT;
00131 size->width = max(size->width, min(300u, width));
00132 SetDParamStr(0, this->landinfo_data[LAND_INFO_MULTICENTER_LINE]);
00133 size->height += GetStringHeight(STR_JUST_RAW_STRING, size->width - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT);
00134 }
00135 }
00136
00137 LandInfoWindow(TileIndex tile) : Window() {
00138 Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority);
00139
00140
00141 TileDesc td;
00142
00143 td.build_date = INVALID_DATE;
00144
00145
00146
00147
00148
00149 td.owner_type[0] = STR_LAND_AREA_INFORMATION_OWNER;
00150 td.owner_type[1] = STR_NULL;
00151 td.owner_type[2] = STR_NULL;
00152 td.owner_type[3] = STR_NULL;
00153 td.owner[0] = OWNER_NONE;
00154 td.owner[1] = OWNER_NONE;
00155 td.owner[2] = OWNER_NONE;
00156 td.owner[3] = OWNER_NONE;
00157
00158 td.station_class = STR_NULL;
00159 td.station_name = STR_NULL;
00160
00161 td.grf = NULL;
00162
00163 CargoArray acceptance;
00164 AddAcceptedCargo(tile, acceptance, NULL);
00165 GetTileDesc(tile, &td);
00166
00167 uint line_nr = 0;
00168
00169
00170 SetDParam(0, td.dparam[0]);
00171 GetString(this->landinfo_data[line_nr], td.str, lastof(this->landinfo_data[line_nr]));
00172 line_nr++;
00173
00174
00175 for (uint i = 0; i < 4; i++) {
00176 if (td.owner_type[i] == STR_NULL) continue;
00177
00178 SetDParam(0, STR_LAND_AREA_INFORMATION_OWNER_N_A);
00179 if (td.owner[i] != OWNER_NONE && td.owner[i] != OWNER_WATER) GetNameOfOwner(td.owner[i], tile);
00180 GetString(this->landinfo_data[line_nr], td.owner_type[i], lastof(this->landinfo_data[line_nr]));
00181 line_nr++;
00182 }
00183
00184
00185 StringID str = STR_LAND_AREA_INFORMATION_COST_TO_CLEAR_N_A;
00186 Company *c = Company::GetIfValid(_local_company);
00187 if (c != NULL) {
00188 Money old_money = c->money;
00189 c->money = INT64_MAX;
00190 CommandCost costclear = DoCommand(tile, 0, 0, DC_NONE, CMD_LANDSCAPE_CLEAR);
00191 c->money = old_money;
00192 if (costclear.Succeeded()) {
00193 Money cost = costclear.GetCost();
00194 if (cost < 0) {
00195 cost = -cost;
00196 str = STR_LAND_AREA_INFORMATION_REVENUE_WHEN_CLEARED;
00197 } else {
00198 str = STR_LAND_AREA_INFORMATION_COST_TO_CLEAR;
00199 }
00200 SetDParam(0, cost);
00201 }
00202 }
00203 GetString(this->landinfo_data[line_nr], str, lastof(this->landinfo_data[line_nr]));
00204 line_nr++;
00205
00206
00207 char tmp[16];
00208 snprintf(tmp, lengthof(tmp), "0x%.4X", tile);
00209 SetDParam(0, TileX(tile));
00210 SetDParam(1, TileY(tile));
00211 SetDParam(2, TileHeight(tile));
00212 SetDParamStr(3, tmp);
00213 GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_LANDINFO_COORDS, lastof(this->landinfo_data[line_nr]));
00214 line_nr++;
00215
00216
00217 SetDParam(0, STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE);
00218 if (t != NULL) {
00219 SetDParam(0, STR_TOWN_NAME);
00220 SetDParam(1, t->index);
00221 }
00222 GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY, lastof(this->landinfo_data[line_nr]));
00223 line_nr++;
00224
00225
00226 if (td.build_date != INVALID_DATE) {
00227 SetDParam(0, td.build_date);
00228 GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_BUILD_DATE, lastof(this->landinfo_data[line_nr]));
00229 line_nr++;
00230 }
00231
00232
00233 if (td.station_class != STR_NULL) {
00234 SetDParam(0, td.station_class);
00235 GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_STATION_CLASS, lastof(this->landinfo_data[line_nr]));
00236 line_nr++;
00237 }
00238
00239
00240 if (td.station_name != STR_NULL) {
00241 SetDParam(0, td.station_name);
00242 GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_STATION_TYPE, lastof(this->landinfo_data[line_nr]));
00243 line_nr++;
00244 }
00245
00246
00247 if (td.grf != NULL) {
00248 SetDParamStr(0, td.grf);
00249 GetString(this->landinfo_data[line_nr], STR_LAND_AREA_INFORMATION_NEWGRF_NAME, lastof(this->landinfo_data[line_nr]));
00250 line_nr++;
00251 }
00252
00253 assert(line_nr < LAND_INFO_CENTERED_LINES);
00254
00255
00256 this->landinfo_data[line_nr][0] = '\0';
00257
00258
00259 char *strp = GetString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE], STR_LAND_AREA_INFORMATION_CARGO_ACCEPTED, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
00260 bool found = false;
00261
00262 for (CargoID i = 0; i < NUM_CARGO; ++i) {
00263 if (acceptance[i] > 0) {
00264
00265 if (found) {
00266 *strp++ = ',';
00267 *strp++ = ' ';
00268 }
00269 found = true;
00270
00271
00272 if (acceptance[i] < 8) {
00273 SetDParam(0, acceptance[i]);
00274 SetDParam(1, CargoSpec::Get(i)->name);
00275 strp = GetString(strp, STR_LAND_AREA_INFORMATION_CARGO_EIGHTS, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
00276 } else {
00277 strp = GetString(strp, CargoSpec::Get(i)->name, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
00278 }
00279 }
00280 }
00281 if (!found) this->landinfo_data[LAND_INFO_MULTICENTER_LINE][0] = '\0';
00282
00283 this->InitNested(&_land_info_desc);
00284
00285 #if defined(_DEBUG)
00286 # define LANDINFOD_LEVEL 0
00287 #else
00288 # define LANDINFOD_LEVEL 1
00289 #endif
00290 DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile));
00291 DEBUG(misc, LANDINFOD_LEVEL, "type_height = %#x", _m[tile].type_height);
00292 DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", _m[tile].m1);
00293 DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", _m[tile].m2);
00294 DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", _m[tile].m3);
00295 DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4);
00296 DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5);
00297 DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _m[tile].m6);
00298 DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7);
00299 #undef LANDINFOD_LEVEL
00300 }
00301 };
00302
00303 static void Place_LandInfo(TileIndex tile)
00304 {
00305 DeleteWindowById(WC_LAND_INFO, 0);
00306 new LandInfoWindow(tile);
00307 }
00308
00309 void PlaceLandBlockInfo()
00310 {
00311 if (_cursor.sprite == SPR_CURSOR_QUERY) {
00312 ResetObjectToPlace();
00313 } else {
00314 _place_proc = Place_LandInfo;
00315 SetObjectToPlace(SPR_CURSOR_QUERY, PAL_NONE, HT_RECT, WC_MAIN_TOOLBAR, 0);
00316 }
00317 }
00318
00320 enum AboutWidgets {
00321 AW_SCROLLING_TEXT,
00322 AW_WEBSITE,
00323 };
00324
00325 static const NWidgetPart _nested_about_widgets[] = {
00326 NWidget(NWID_HORIZONTAL),
00327 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00328 NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_ABOUT_OPENTTD, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00329 EndContainer(),
00330 NWidget(WWT_PANEL, COLOUR_GREY), SetPIP(4, 2, 4),
00331 NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_ABOUT_ORIGINAL_COPYRIGHT, STR_NULL),
00332 NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_ABOUT_VERSION, STR_NULL),
00333 NWidget(WWT_FRAME, COLOUR_GREY), SetPadding(0, 5, 1, 5),
00334 NWidget(WWT_EMPTY, INVALID_COLOUR, AW_SCROLLING_TEXT),
00335 EndContainer(),
00336 NWidget(WWT_LABEL, COLOUR_GREY, AW_WEBSITE), SetDataTip(STR_BLACK_RAW_STRING, STR_NULL),
00337 NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_ABOUT_COPYRIGHT_OPENTTD, STR_NULL),
00338 EndContainer(),
00339 };
00340
00341 static const WindowDesc _about_desc(
00342 WDP_CENTER, 0, 0,
00343 WC_GAME_OPTIONS, WC_NONE,
00344 0,
00345 _nested_about_widgets, lengthof(_nested_about_widgets)
00346 );
00347
00348 static const char * const _credits[] = {
00349 "Original design by Chris Sawyer",
00350 "Original graphics by Simon Foster",
00351 "",
00352 "The OpenTTD team (in alphabetical order):",
00353 " Albert Hofkamp (Alberth) - GUI expert",
00354 " Jean-Fran\xC3\xA7ois Claeys (Belugas) - GUI, newindustries and more",
00355 " Matthijs Kooijman (blathijs) - Pathfinder-guru, pool rework",
00356 " Christoph Elsenhans (frosch) - General coding",
00357 " Lo\xC3\xAF""c Guilloux (glx) - Windows Expert",
00358 " Michael Lutz (michi_cc) - Path based signals",
00359 " Owen Rudge (orudge) - Forum host, OS/2 port",
00360 " Peter Nelson (peter1138) - Spiritual descendant from NewGRF gods",
00361 " Remko Bijker (Rubidium) - Lead coder and way more",
00362 " Zden\xC4\x9Bk Sojka (SmatZ) - Bug finder and fixer",
00363 " Jos\xC3\xA9 Soler (Terkhen) - General coding",
00364 " Thijs Marinussen (Yexo) - AI Framework",
00365 "",
00366 "Inactive Developers:",
00367 " Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles",
00368 " Victor Fischer (Celestar) - Programming everywhere you need him to",
00369 " Tam\xC3\xA1s Farag\xC3\xB3 (Darkvater) - Ex-Lead coder",
00370 " Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)",
00371 " Jonathan Coome (Maedhros) - High priest of the NewGRF Temple",
00372 " Attila B\xC3\xA1n (MiHaMiX) - Developer WebTranslator 1 and 2",
00373 " Christoph Mallon (Tron) - Programmer, code correctness police",
00374 "",
00375 "Retired Developers:",
00376 " Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)",
00377 " Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)",
00378 " Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)",
00379 " Benedikt Br\xC3\xBCggemeier (skidd13) - Bug fixer and code reworker",
00380 " Patric Stout (TrueLight) - Programmer (0.3 - pre0.7), sys op (active)",
00381 "",
00382 "Special thanks go out to:",
00383 " Josef Drexler - For his great work on TTDPatch",
00384 " Marcin Grzegorczyk - For his documentation of TTD internals",
00385 " Petr Baudi\xC5\xA1 (pasky) - Many patches, newGRF support",
00386 " Stefan Mei\xC3\x9Fner (sign_de) - For his work on the console",
00387 " Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with",
00388 " Cian Duffy (MYOB) - BeOS port / manual writing",
00389 " Christian Rosentreter (tokai) - MorphOS / AmigaOS port",
00390 " Richard Kempton (richK) - additional airports, initial TGP implementation",
00391 "",
00392 " Alberto Demichelis - Squirrel scripting language \xC2\xA9 2003-2008",
00393 " L. Peter Deutsch - MD5 implementation \xC2\xA9 1999, 2000, 2002",
00394 " Michael Blunck - Pre-Signals and Semaphores \xC2\xA9 2003",
00395 " George - Canal/Lock graphics \xC2\xA9 2003-2004",
00396 " David Dallaston - Tram tracks",
00397 " Marcin Grzegorczyk - Foundations for Tracks on Slopes",
00398 " All Translators - Who made OpenTTD a truly international game",
00399 " Bug Reporters - Without whom OpenTTD would still be full of bugs!",
00400 "",
00401 "",
00402 "And last but not least:",
00403 " Chris Sawyer - For an amazing game!"
00404 };
00405
00406 struct AboutWindow : public Window {
00407 int text_position;
00408 byte counter;
00409 int line_height;
00410 static const int num_visible_lines = 19;
00411
00412 AboutWindow() : Window()
00413 {
00414 this->InitNested(&_about_desc);
00415
00416 this->counter = 5;
00417 this->text_position = this->GetWidget<NWidgetBase>(AW_SCROLLING_TEXT)->pos_y + this->GetWidget<NWidgetBase>(AW_SCROLLING_TEXT)->current_y;
00418 }
00419
00420 virtual void SetStringParameters(int widget) const
00421 {
00422 if (widget == AW_WEBSITE) SetDParamStr(0, "Website: http://www.openttd.org");
00423 }
00424
00425 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00426 {
00427 if (widget != AW_SCROLLING_TEXT) return;
00428
00429 this->line_height = FONT_HEIGHT_NORMAL;
00430
00431 Dimension d;
00432 d.height = this->line_height * num_visible_lines;
00433
00434 d.width = 0;
00435 for (uint i = 0; i < lengthof(_credits); i++) {
00436 d.width = max(d.width, GetStringBoundingBox(_credits[i]).width);
00437 }
00438 *size = maxdim(*size, d);
00439 }
00440
00441 virtual void OnPaint()
00442 {
00443 this->DrawWidgets();
00444 }
00445
00446 virtual void DrawWidget(const Rect &r, int widget) const
00447 {
00448 if (widget != AW_SCROLLING_TEXT) return;
00449
00450 int y = this->text_position;
00451
00452
00453 for (uint i = 0; i < lengthof(_credits); i++) {
00454 if (y >= r.top + 7 && y < r.bottom - this->line_height) {
00455 DrawString(r.left, r.right, y, _credits[i], TC_BLACK, SA_LEFT | SA_FORCE);
00456 }
00457 y += this->line_height;
00458 }
00459 }
00460
00461 virtual void OnTick()
00462 {
00463 if (--this->counter == 0) {
00464 this->counter = 5;
00465 this->text_position--;
00466
00467 if (this->text_position < (int)(this->GetWidget<NWidgetBase>(AW_SCROLLING_TEXT)->pos_y - lengthof(_credits) * this->line_height)) {
00468 this->text_position = this->GetWidget<NWidgetBase>(AW_SCROLLING_TEXT)->pos_y + this->GetWidget<NWidgetBase>(AW_SCROLLING_TEXT)->current_y;
00469 }
00470 this->SetDirty();
00471 }
00472 }
00473 };
00474
00475 void ShowAboutWindow()
00476 {
00477 DeleteWindowById(WC_GAME_OPTIONS, 0);
00478 new AboutWindow();
00479 }
00480
00482 enum ErrorMessageWidgets {
00483 EMW_CAPTION,
00484 EMW_FACE,
00485 EMW_MESSAGE,
00486 };
00487
00488 static const NWidgetPart _nested_errmsg_widgets[] = {
00489 NWidget(NWID_HORIZONTAL),
00490 NWidget(WWT_CLOSEBOX, COLOUR_RED),
00491 NWidget(WWT_CAPTION, COLOUR_RED, EMW_CAPTION), SetDataTip(STR_ERROR_MESSAGE_CAPTION, STR_NULL),
00492 EndContainer(),
00493 NWidget(WWT_PANEL, COLOUR_RED),
00494 NWidget(WWT_EMPTY, COLOUR_RED, EMW_MESSAGE), SetPadding(0, 2, 0, 2), SetMinimalSize(236, 32),
00495 EndContainer(),
00496 };
00497
00498 static const WindowDesc _errmsg_desc(
00499 WDP_MANUAL, 0, 0,
00500 WC_ERRMSG, WC_NONE,
00501 0,
00502 _nested_errmsg_widgets, lengthof(_nested_errmsg_widgets)
00503 );
00504
00505 static const NWidgetPart _nested_errmsg_face_widgets[] = {
00506 NWidget(NWID_HORIZONTAL),
00507 NWidget(WWT_CLOSEBOX, COLOUR_RED),
00508 NWidget(WWT_CAPTION, COLOUR_RED, EMW_CAPTION), SetDataTip(STR_ERROR_MESSAGE_CAPTION_OTHER_COMPANY, STR_NULL),
00509 EndContainer(),
00510 NWidget(WWT_PANEL, COLOUR_RED),
00511 NWidget(NWID_HORIZONTAL), SetPIP(2, 1, 2),
00512 NWidget(WWT_EMPTY, COLOUR_RED, EMW_FACE), SetMinimalSize(92, 119), SetFill(0, 1), SetPadding(2, 0, 1, 0),
00513 NWidget(WWT_EMPTY, COLOUR_RED, EMW_MESSAGE), SetFill(0, 1), SetMinimalSize(238, 123),
00514 EndContainer(),
00515 EndContainer(),
00516 };
00517
00518 static const WindowDesc _errmsg_face_desc(
00519 WDP_MANUAL, 0, 0,
00520 WC_ERRMSG, WC_NONE,
00521 0,
00522 _nested_errmsg_face_widgets, lengthof(_nested_errmsg_face_widgets)
00523 );
00524
00526 struct ErrmsgWindow : public Window {
00527 private:
00528 uint duration;
00529 uint64 decode_params[20];
00530 StringID summary_msg;
00531 StringID detailed_msg;
00532 uint height_summary;
00533 uint height_detailed;
00534 Point position;
00535
00536 public:
00537 ErrmsgWindow(Point pt, const WindowDesc *desc, StringID summary_msg, StringID detailed_msg, bool no_timeout) : Window()
00538 {
00539 this->position = pt;
00540 this->duration = no_timeout ? 0 : _settings_client.gui.errmsg_duration;
00541 CopyOutDParam(this->decode_params, 0, lengthof(this->decode_params));
00542 this->summary_msg = summary_msg;
00543 this->detailed_msg = detailed_msg;
00544
00545 assert(summary_msg != INVALID_STRING_ID);
00546
00547 this->InitNested(desc);
00548 }
00549
00550 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00551 {
00552 if (widget != EMW_MESSAGE) return;
00553
00554 CopyInDParam(0, this->decode_params, lengthof(this->decode_params));
00555
00556
00557
00558 SwitchToErrorRefStack();
00559 RewindTextRefStack();
00560
00561 int text_width = max(0, (int)size->width - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT);
00562 this->height_summary = GetStringHeight(this->summary_msg, text_width);
00563 this->height_detailed = (this->detailed_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->detailed_msg, text_width);
00564
00565 SwitchToNormalRefStack();
00566
00567 uint panel_height = WD_FRAMERECT_TOP + this->height_summary + WD_FRAMERECT_BOTTOM;
00568 if (this->detailed_msg != INVALID_STRING_ID) panel_height += this->height_detailed + WD_PAR_VSEP_WIDE;
00569
00570 size->height = max(size->height, panel_height);
00571 }
00572
00573 virtual Point OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
00574 {
00575
00576 if (this->position.x == 0 && this->position.y == 0) {
00577 Point pt = {(_screen.width - sm_width) >> 1, (_screen.height - sm_height) >> 1};
00578 return pt;
00579 }
00580
00581
00582
00583
00584 int scr_top = GetMainViewTop() + 20;
00585 int scr_bot = GetMainViewBottom() - 20;
00586
00587 Point pt = RemapCoords2(this->position.x, this->position.y);
00588 const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00589 if (this->detailed_msg != STR_ERROR_OWNED_BY || GetDParamX(this->decode_params, 2) >= MAX_COMPANIES) {
00590
00591 pt.x = UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left;
00592 pt.x = (pt.x < (_screen.width >> 1)) ? _screen.width - sm_width - 20 : 20;
00593
00594
00595 pt.y = UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top;
00596 pt.y = (pt.y < (_screen.height >> 1)) ? scr_bot - sm_height : scr_top;
00597 } else {
00598 pt.x = Clamp(UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left - (sm_width / 2), 0, _screen.width - sm_width);
00599 pt.y = Clamp(UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top - (sm_height / 2), scr_top, scr_bot - sm_height);
00600 }
00601 return pt;
00602 }
00603
00604 virtual void OnPaint()
00605 {
00606 this->DrawWidgets();
00607 }
00608
00609 virtual void SetStringParameters(int widget) const
00610 {
00611 if (widget == EMW_CAPTION) CopyInDParam(0, this->decode_params, lengthof(this->decode_params));
00612 }
00613
00614 virtual void DrawWidget(const Rect &r, int widget) const
00615 {
00616 switch (widget) {
00617 case EMW_FACE: {
00618 const Company *c = Company::Get((CompanyID)GetDParamX(this->decode_params, 2));
00619 DrawCompanyManagerFace(c->face, c->colour, r.left, r.top);
00620 break;
00621 }
00622
00623 case EMW_MESSAGE:
00624 CopyInDParam(0, this->decode_params, lengthof(this->decode_params));
00625 SwitchToErrorRefStack();
00626 RewindTextRefStack();
00627
00628 if (this->detailed_msg == INVALID_STRING_ID) {
00629 DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom - WD_FRAMERECT_BOTTOM,
00630 this->summary_msg, TC_FROMSTRING, SA_CENTER);
00631 } else {
00632 int extra = (r.bottom - r.top + 1 - this->height_summary - this->height_detailed - WD_PAR_VSEP_WIDE) / 2;
00633
00634 int top = r.top + WD_FRAMERECT_TOP;
00635 int bottom = top + this->height_summary + extra;
00636 DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, top, bottom, this->summary_msg, TC_FROMSTRING, SA_CENTER);
00637
00638 bottom = r.bottom - WD_FRAMERECT_BOTTOM;
00639 top = bottom - this->height_detailed - extra;
00640 DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, top, bottom, this->detailed_msg, TC_FROMSTRING, SA_CENTER);
00641 }
00642
00643 SwitchToNormalRefStack();
00644 break;
00645
00646 default:
00647 break;
00648 }
00649 }
00650
00651 virtual void OnMouseLoop()
00652 {
00653
00654 if (_right_button_down && this->duration != 0) delete this;
00655 }
00656
00657 virtual void OnHundredthTick()
00658 {
00659
00660 if (this->duration != 0) {
00661 this->duration--;
00662 if (this->duration == 0) delete this;
00663 }
00664 }
00665
00666 ~ErrmsgWindow()
00667 {
00668 SetRedErrorSquare(INVALID_TILE);
00669 extern StringID _switch_mode_errorstr;
00670 _switch_mode_errorstr = INVALID_STRING_ID;
00671 }
00672
00673 virtual EventState OnKeyPress(uint16 key, uint16 keycode)
00674 {
00675 if (keycode != WKC_SPACE) return ES_NOT_HANDLED;
00676 delete this;
00677 return ES_HANDLED;
00678 }
00679 };
00680
00689 void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, int x, int y, bool no_timeout)
00690 {
00691 DeleteWindowById(WC_ERRMSG, 0);
00692
00693 if (_settings_client.gui.errmsg_duration == 0 && !no_timeout) return;
00694
00695 if (summary_msg == STR_NULL) summary_msg = STR_EMPTY;
00696
00697 Point pt = {x, y};
00698 const WindowDesc *desc = (detailed_msg != STR_ERROR_OWNED_BY || GetDParam(2) >= MAX_COMPANIES) ? &_errmsg_desc : &_errmsg_face_desc;
00699 new ErrmsgWindow(pt, desc, summary_msg, detailed_msg, no_timeout);
00700 }
00701
00702 void ShowEstimatedCostOrIncome(Money cost, int x, int y)
00703 {
00704 StringID msg = STR_MESSAGE_ESTIMATED_COST;
00705
00706 if (cost < 0) {
00707 cost = -cost;
00708 msg = STR_MESSAGE_ESTIMATED_INCOME;
00709 }
00710 SetDParam(0, cost);
00711 ShowErrorMessage(msg, INVALID_STRING_ID, x, y);
00712 }
00713
00714 void ShowCostOrIncomeAnimation(int x, int y, int z, Money cost)
00715 {
00716 Point pt = RemapCoords(x, y, z);
00717 StringID msg = STR_INCOME_FLOAT_COST;
00718
00719 if (cost < 0) {
00720 cost = -cost;
00721 msg = STR_INCOME_FLOAT_INCOME;
00722 }
00723 SetDParam(0, cost);
00724 AddTextEffect(msg, pt.x, pt.y, DAY_TICKS, TE_RISING);
00725 }
00726
00727 void ShowFeederIncomeAnimation(int x, int y, int z, Money cost)
00728 {
00729 Point pt = RemapCoords(x, y, z);
00730
00731 SetDParam(0, cost);
00732 AddTextEffect(STR_FEEDER, pt.x, pt.y, DAY_TICKS, TE_RISING);
00733 }
00734
00735 TextEffectID ShowFillingPercent(int x, int y, int z, uint8 percent, StringID string)
00736 {
00737 Point pt = RemapCoords(x, y, z);
00738
00739 assert(string != STR_NULL);
00740
00741 SetDParam(0, percent);
00742 return AddTextEffect(string, pt.x, pt.y, 0, TE_STATIC);
00743 }
00744
00745 void UpdateFillingPercent(TextEffectID te_id, uint8 percent, StringID string)
00746 {
00747 assert(string != STR_NULL);
00748
00749 SetDParam(0, percent);
00750 UpdateTextEffect(te_id, string);
00751 }
00752
00753 void HideFillingPercent(TextEffectID *te_id)
00754 {
00755 if (*te_id == INVALID_TE_ID) return;
00756
00757 RemoveTextEffect(*te_id);
00758 *te_id = INVALID_TE_ID;
00759 }
00760
00761 static const NWidgetPart _nested_tooltips_widgets[] = {
00762 NWidget(WWT_PANEL, COLOUR_GREY, 0), SetMinimalSize(200, 32), EndContainer(),
00763 };
00764
00765 static const WindowDesc _tool_tips_desc(
00766 WDP_MANUAL, 0, 0,
00767 WC_TOOLTIPS, WC_NONE,
00768 0,
00769 _nested_tooltips_widgets, lengthof(_nested_tooltips_widgets)
00770 );
00771
00773 struct TooltipsWindow : public Window
00774 {
00775 StringID string_id;
00776 byte paramcount;
00777 uint64 params[5];
00778 bool use_left_mouse_button;
00779
00780 TooltipsWindow(StringID str, uint paramcount, const uint64 params[], bool use_left_mouse_button) : Window()
00781 {
00782 this->string_id = str;
00783 assert_compile(sizeof(this->params[0]) == sizeof(params[0]));
00784 assert(paramcount <= lengthof(this->params));
00785 memcpy(this->params, params, sizeof(this->params[0]) * paramcount);
00786 this->paramcount = paramcount;
00787 this->use_left_mouse_button = use_left_mouse_button;
00788
00789 this->InitNested(&_tool_tips_desc);
00790
00791 this->flags4 &= ~WF_WHITE_BORDER_MASK;
00792 }
00793
00794 virtual Point OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
00795 {
00796
00797
00798
00799 int scr_top = GetMainViewTop() + 2;
00800 int scr_bot = GetMainViewBottom() - 2;
00801
00802 Point pt;
00803
00804
00805
00806
00807 pt.y = Clamp(_cursor.pos.y + _cursor.size.y + _cursor.offs.y + 5, scr_top, scr_bot);
00808 if (pt.y + sm_height > scr_bot) pt.y = min(_cursor.pos.y + _cursor.offs.y - 5, scr_bot) - sm_height;
00809 pt.x = Clamp(_cursor.pos.x - (sm_width >> 1), 0, _screen.width - sm_width);
00810
00811 return pt;
00812 }
00813
00814 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00815 {
00816
00817 for (uint i = 0; i != this->paramcount; i++) SetDParam(i, this->params[i]);
00818
00819 size->width = min(GetStringBoundingBox(this->string_id).width, 194);
00820 size->height = GetStringHeight(this->string_id, size->width);
00821
00822
00823 size->width += 2 + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
00824 size->height += 2 + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
00825 }
00826
00827 virtual void DrawWidget(const Rect &r, int widget) const
00828 {
00829
00830 GfxFillRect(r.left, r.top, r.right, r.bottom, 0);
00831 GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, 0x44);
00832
00833 for (uint arg = 0; arg < this->paramcount; arg++) {
00834 SetDParam(arg, this->params[arg]);
00835 }
00836 DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom - WD_FRAMERECT_BOTTOM, this->string_id, TC_FROMSTRING, SA_CENTER);
00837 }
00838
00839
00840 virtual void OnPaint()
00841 {
00842 this->DrawWidgets();
00843 }
00844
00845 virtual void OnMouseLoop()
00846 {
00847
00848
00849 if (this->use_left_mouse_button ? !_left_button_down : !_right_button_down) delete this;
00850 }
00851 };
00852
00859 void GuiShowTooltips(StringID str, uint paramcount, const uint64 params[], bool use_left_mouse_button)
00860 {
00861 DeleteWindowById(WC_TOOLTIPS, 0);
00862
00863 if (str == STR_NULL) return;
00864
00865 new TooltipsWindow(str, paramcount, params, use_left_mouse_button);
00866 }
00867
00868
00869
00870
00871 static void DelChar(Textbuf *tb, bool backspace)
00872 {
00873 WChar c;
00874 char *s = tb->buf + tb->caretpos;
00875
00876 if (backspace) s = Utf8PrevChar(s);
00877
00878 uint16 len = (uint16)Utf8Decode(&c, s);
00879 uint width = GetCharacterWidth(FS_NORMAL, c);
00880
00881 tb->width -= width;
00882 if (backspace) {
00883 tb->caretpos -= len;
00884 tb->caretxoffs -= width;
00885 }
00886
00887
00888 memmove(s, s + len, tb->size - (s - tb->buf) - len);
00889 tb->size -= len;
00890 }
00891
00899 bool DeleteTextBufferChar(Textbuf *tb, int delmode)
00900 {
00901 if (delmode == WKC_BACKSPACE && tb->caretpos != 0) {
00902 DelChar(tb, true);
00903 return true;
00904 } else if (delmode == WKC_DELETE && tb->caretpos < tb->size - 1) {
00905 DelChar(tb, false);
00906 return true;
00907 }
00908
00909 return false;
00910 }
00911
00916 void DeleteTextBufferAll(Textbuf *tb)
00917 {
00918 memset(tb->buf, 0, tb->maxsize);
00919 tb->size = 1;
00920 tb->width = tb->caretpos = tb->caretxoffs = 0;
00921 }
00922
00931 bool InsertTextBufferChar(Textbuf *tb, WChar key)
00932 {
00933 const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
00934 uint16 len = (uint16)Utf8CharLen(key);
00935 if (tb->size + len <= tb->maxsize && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) {
00936 memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->size - tb->caretpos);
00937 Utf8Encode(tb->buf + tb->caretpos, key);
00938 tb->size += len;
00939 tb->width += charwidth;
00940
00941 tb->caretpos += len;
00942 tb->caretxoffs += charwidth;
00943 return true;
00944 }
00945 return false;
00946 }
00947
00955 bool InsertTextBufferClipboard(Textbuf *tb)
00956 {
00957 char utf8_buf[512];
00958
00959 if (!GetClipboardContents(utf8_buf, lengthof(utf8_buf))) return false;
00960
00961 uint16 width = 0, length = 0;
00962 WChar c;
00963 for (const char *ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
00964 if (!IsPrintable(c)) break;
00965
00966 byte len = Utf8CharLen(c);
00967 if (tb->size + length + len > tb->maxsize) break;
00968
00969 byte charwidth = GetCharacterWidth(FS_NORMAL, c);
00970 if (tb->maxwidth != 0 && width + tb->width + charwidth > tb->maxwidth) break;
00971
00972 width += charwidth;
00973 length += len;
00974 }
00975
00976 if (length == 0) return false;
00977
00978 memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->size - tb->caretpos);
00979 memcpy(tb->buf + tb->caretpos, utf8_buf, length);
00980 tb->width += width;
00981 tb->caretxoffs += width;
00982
00983 tb->size += length;
00984 tb->caretpos += length;
00985 assert(tb->size <= tb->maxsize);
00986 tb->buf[tb->size - 1] = '\0';
00987
00988 return true;
00989 }
00990
00998 bool MoveTextBufferPos(Textbuf *tb, int navmode)
00999 {
01000 switch (navmode) {
01001 case WKC_LEFT:
01002 if (tb->caretpos != 0) {
01003 WChar c;
01004 const char *s = Utf8PrevChar(tb->buf + tb->caretpos);
01005 Utf8Decode(&c, s);
01006 tb->caretpos = s - tb->buf;
01007 tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
01008
01009 return true;
01010 }
01011 break;
01012
01013 case WKC_RIGHT:
01014 if (tb->caretpos < tb->size - 1) {
01015 WChar c;
01016
01017 tb->caretpos += (uint16)Utf8Decode(&c, tb->buf + tb->caretpos);
01018 tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
01019
01020 return true;
01021 }
01022 break;
01023
01024 case WKC_HOME:
01025 tb->caretpos = 0;
01026 tb->caretxoffs = 0;
01027 return true;
01028
01029 case WKC_END:
01030 tb->caretpos = tb->size - 1;
01031 tb->caretxoffs = tb->width;
01032 return true;
01033
01034 default:
01035 break;
01036 }
01037
01038 return false;
01039 }
01040
01050 void InitializeTextBuffer(Textbuf *tb, char *buf, uint16 maxsize, uint16 maxwidth)
01051 {
01052 assert(maxsize != 0);
01053
01054 tb->buf = buf;
01055 tb->maxsize = maxsize;
01056 tb->maxwidth = maxwidth;
01057 tb->caret = true;
01058 UpdateTextBufferSize(tb);
01059 }
01060
01067 void UpdateTextBufferSize(Textbuf *tb)
01068 {
01069 const char *buf = tb->buf;
01070
01071 tb->width = 0;
01072 tb->size = 1;
01073
01074 WChar c;
01075 while ((c = Utf8Consume(&buf)) != '\0') {
01076 tb->width += GetCharacterWidth(FS_NORMAL, c);
01077 tb->size += Utf8CharLen(c);
01078 }
01079
01080 assert(tb->size <= tb->maxsize);
01081
01082 tb->caretpos = tb->size - 1;
01083 tb->caretxoffs = tb->width;
01084 }
01085
01086 bool HandleCaret(Textbuf *tb)
01087 {
01088
01089 bool b = !!(_caret_timer & 0x20);
01090
01091 if (b != tb->caret) {
01092 tb->caret = b;
01093 return true;
01094 }
01095 return false;
01096 }
01097
01098 bool QueryString::HasEditBoxFocus(const Window *w, int wid) const
01099 {
01100 if (w->IsWidgetGloballyFocused(wid)) return true;
01101 if (w->window_class != WC_OSK || _focused_window != w->parent) return false;
01102 return w->parent->nested_focus != NULL && w->parent->nested_focus->type == WWT_EDITBOX;
01103 }
01104
01105 HandleEditBoxResult QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, Window::EventState &state)
01106 {
01107 if (!QueryString::HasEditBoxFocus(w, wid)) return HEBR_NOT_FOCUSED;
01108
01109 state = Window::ES_HANDLED;
01110
01111 switch (keycode) {
01112 case WKC_ESC: return HEBR_CANCEL;
01113
01114 case WKC_RETURN: case WKC_NUM_ENTER: return HEBR_CONFIRM;
01115
01116 #ifdef WITH_COCOA
01117 case (WKC_META | 'V'):
01118 #endif
01119 case (WKC_CTRL | 'V'):
01120 if (InsertTextBufferClipboard(&this->text)) w->SetWidgetDirty(wid);
01121 break;
01122
01123 #ifdef WITH_COCOA
01124 case (WKC_META | 'U'):
01125 #endif
01126 case (WKC_CTRL | 'U'):
01127 DeleteTextBufferAll(&this->text);
01128 w->SetWidgetDirty(wid);
01129 break;
01130
01131 case WKC_BACKSPACE: case WKC_DELETE:
01132 if (DeleteTextBufferChar(&this->text, keycode)) w->SetWidgetDirty(wid);
01133 break;
01134
01135 case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
01136 if (MoveTextBufferPos(&this->text, keycode)) w->SetWidgetDirty(wid);
01137 break;
01138
01139 default:
01140 if (IsValidChar(key, this->afilter)) {
01141 if (InsertTextBufferChar(&this->text, key)) w->SetWidgetDirty(wid);
01142 } else {
01143 state = Window::ES_NOT_HANDLED;
01144 }
01145 }
01146
01147 return HEBR_EDITING;
01148 }
01149
01150 void QueryString::HandleEditBox(Window *w, int wid)
01151 {
01152 if (HasEditBoxFocus(w, wid) && HandleCaret(&this->text)) {
01153 w->SetWidgetDirty(wid);
01154
01155
01156 if (w->window_class != WC_OSK) {
01157 Window *w_osk = FindWindowById(WC_OSK, 0);
01158 if (w_osk != NULL && w_osk->parent == w) w_osk->InvalidateData();
01159 }
01160 }
01161 }
01162
01163 void QueryString::DrawEditBox(Window *w, int wid)
01164 {
01165 const NWidgetBase *wi = w->GetWidget<NWidgetBase>(wid);
01166
01167 assert((wi->type & WWT_MASK) == WWT_EDITBOX);
01168 int left = wi->pos_x;
01169 int right = wi->pos_x + wi->current_x - 1;
01170 int top = wi->pos_y;
01171 int bottom = wi->pos_y + wi->current_y - 1;
01172
01173 GfxFillRect(left + 1, top + 1, right - 1, bottom - 1, 215);
01174
01175
01176 DrawPixelInfo dpi;
01177 if (!FillDrawPixelInfo(&dpi, left + WD_FRAMERECT_LEFT, top + WD_FRAMERECT_TOP, right - left - WD_FRAMERECT_RIGHT, bottom - top - WD_FRAMERECT_BOTTOM)) return;
01178
01179 DrawPixelInfo *old_dpi = _cur_dpi;
01180 _cur_dpi = &dpi;
01181
01182
01183
01184 const Textbuf *tb = &this->text;
01185 int delta = min(0, (right - left) - tb->width - 10);
01186
01187 if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
01188
01189 DrawString(delta, tb->width, 0, tb->buf, TC_YELLOW);
01190 if (HasEditBoxFocus(w, wid) && tb->caret) {
01191 int caret_width = GetStringBoundingBox("_").width;
01192 DrawString(tb->caretxoffs + delta, tb->caretxoffs + delta + caret_width, 0, "_", TC_WHITE);
01193 }
01194
01195 _cur_dpi = old_dpi;
01196 }
01197
01198 HandleEditBoxResult QueryStringBaseWindow::HandleEditBoxKey(int wid, uint16 key, uint16 keycode, EventState &state)
01199 {
01200 return this->QueryString::HandleEditBoxKey(this, wid, key, keycode, state);
01201 }
01202
01203 void QueryStringBaseWindow::HandleEditBox(int wid)
01204 {
01205 this->QueryString::HandleEditBox(this, wid);
01206 }
01207
01208 void QueryStringBaseWindow::DrawEditBox(int wid)
01209 {
01210 this->QueryString::DrawEditBox(this, wid);
01211 }
01212
01213 void QueryStringBaseWindow::OnOpenOSKWindow(int wid)
01214 {
01215 ShowOnScreenKeyboard(this, wid, 0, 0);
01216 }
01217
01219 enum QueryStringWidgets {
01220 QUERY_STR_WIDGET_CAPTION,
01221 QUERY_STR_WIDGET_TEXT,
01222 QUERY_STR_WIDGET_DEFAULT,
01223 QUERY_STR_WIDGET_CANCEL,
01224 QUERY_STR_WIDGET_OK
01225 };
01226
01228 struct QueryStringWindow : public QueryStringBaseWindow
01229 {
01230 QueryStringFlags flags;
01231
01232 QueryStringWindow(StringID str, StringID caption, uint maxsize, uint maxwidth, const WindowDesc *desc, Window *parent, CharSetFilter afilter, QueryStringFlags flags) :
01233 QueryStringBaseWindow(maxsize)
01234 {
01235 GetString(this->edit_str_buf, str, &this->edit_str_buf[maxsize - 1]);
01236 this->edit_str_buf[maxsize - 1] = '\0';
01237
01238 if ((flags & QSF_ACCEPT_UNCHANGED) == 0) this->orig = strdup(this->edit_str_buf);
01239
01240 this->caption = caption;
01241 this->afilter = afilter;
01242 this->flags = flags;
01243 InitializeTextBuffer(&this->text, this->edit_str_buf, maxsize, maxwidth);
01244
01245 this->InitNested(desc);
01246
01247 this->parent = parent;
01248
01249 this->SetFocusedWidget(QUERY_STR_WIDGET_TEXT);
01250 this->LowerWidget(QUERY_STR_WIDGET_TEXT);
01251 }
01252
01253 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01254 {
01255 if (widget == QUERY_STR_WIDGET_DEFAULT && (this->flags & QSF_ENABLE_DEFAULT) == 0) {
01256
01257 fill->width = 0;
01258 resize->width = 0;
01259 size->width = 0;
01260 }
01261 }
01262
01263 virtual void OnPaint()
01264 {
01265 this->DrawWidgets();
01266
01267 this->DrawEditBox(QUERY_STR_WIDGET_TEXT);
01268 }
01269
01270 virtual void SetStringParameters(int widget) const
01271 {
01272 if (widget == QUERY_STR_WIDGET_CAPTION) SetDParam(0, this->caption);
01273 }
01274
01275 void OnOk()
01276 {
01277 if (this->orig == NULL || strcmp(this->text.buf, this->orig) != 0) {
01278
01279
01280 if (this->parent != NULL) {
01281 this->parent->OnQueryTextFinished(this->text.buf);
01282 } else {
01283 HandleOnEditText(this->text.buf);
01284 }
01285 this->handled = true;
01286 }
01287 }
01288
01289 virtual void OnClick(Point pt, int widget, int click_count)
01290 {
01291 switch (widget) {
01292 case QUERY_STR_WIDGET_DEFAULT:
01293 this->text.buf[0] = '\0';
01294
01295 case QUERY_STR_WIDGET_OK:
01296 this->OnOk();
01297
01298 case QUERY_STR_WIDGET_CANCEL:
01299 delete this;
01300 break;
01301 }
01302 }
01303
01304 virtual void OnMouseLoop()
01305 {
01306 this->HandleEditBox(QUERY_STR_WIDGET_TEXT);
01307 }
01308
01309 virtual EventState OnKeyPress(uint16 key, uint16 keycode)
01310 {
01311 EventState state = ES_NOT_HANDLED;
01312 switch (this->HandleEditBoxKey(QUERY_STR_WIDGET_TEXT, key, keycode, state)) {
01313 default: NOT_REACHED();
01314 case HEBR_EDITING: {
01315 Window *osk = FindWindowById(WC_OSK, 0);
01316 if (osk != NULL && osk->parent == this) osk->InvalidateData();
01317 } break;
01318 case HEBR_CONFIRM: this->OnOk();
01319
01320 case HEBR_CANCEL: delete this; break;
01321 case HEBR_NOT_FOCUSED: break;
01322 }
01323 return state;
01324 }
01325
01326 virtual void OnOpenOSKWindow(int wid)
01327 {
01328 ShowOnScreenKeyboard(this, wid, QUERY_STR_WIDGET_CANCEL, QUERY_STR_WIDGET_OK);
01329 }
01330
01331 ~QueryStringWindow()
01332 {
01333 if (!this->handled && this->parent != NULL) {
01334 Window *parent = this->parent;
01335 this->parent = NULL;
01336 parent->OnQueryTextFinished(NULL);
01337 }
01338 }
01339 };
01340
01341 static const NWidgetPart _nested_query_string_widgets[] = {
01342 NWidget(NWID_HORIZONTAL),
01343 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
01344 NWidget(WWT_CAPTION, COLOUR_GREY, QUERY_STR_WIDGET_CAPTION), SetDataTip(STR_WHITE_STRING, STR_NULL),
01345 EndContainer(),
01346 NWidget(WWT_PANEL, COLOUR_GREY),
01347 NWidget(WWT_EDITBOX, COLOUR_GREY, QUERY_STR_WIDGET_TEXT), SetMinimalSize(256, 12), SetFill(1, 1), SetPadding(2, 2, 2, 2),
01348 EndContainer(),
01349 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01350 NWidget(WWT_TEXTBTN, COLOUR_GREY, QUERY_STR_WIDGET_DEFAULT), SetMinimalSize(87, 12), SetFill(1, 1), SetDataTip(STR_BUTTON_DEFAULT, STR_NULL),
01351 NWidget(WWT_TEXTBTN, COLOUR_GREY, QUERY_STR_WIDGET_CANCEL), SetMinimalSize(86, 12), SetFill(1, 1), SetDataTip(STR_BUTTON_CANCEL, STR_NULL),
01352 NWidget(WWT_TEXTBTN, COLOUR_GREY, QUERY_STR_WIDGET_OK), SetMinimalSize(87, 12), SetFill(1, 1), SetDataTip(STR_BUTTON_OK, STR_NULL),
01353 EndContainer(),
01354 };
01355
01356 static const WindowDesc _query_string_desc(
01357 WDP_AUTO, 0, 0,
01358 WC_QUERY_STRING, WC_NONE,
01359 0,
01360 _nested_query_string_widgets, lengthof(_nested_query_string_widgets)
01361 );
01362
01373 void ShowQueryString(StringID str, StringID caption, uint maxsize, uint maxwidth, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
01374 {
01375 DeleteWindowById(WC_QUERY_STRING, 0);
01376 new QueryStringWindow(str, caption, maxsize, maxwidth, &_query_string_desc, parent, afilter, flags);
01377 }
01378
01379
01380 enum QueryWidgets {
01381 QUERY_WIDGET_CAPTION,
01382 QUERY_WIDGET_TEXT,
01383 QUERY_WIDGET_NO,
01384 QUERY_WIDGET_YES
01385 };
01386
01390 struct QueryWindow : public Window {
01391 QueryCallbackProc *proc;
01392 uint64 params[10];
01393 StringID message;
01394 StringID caption;
01395
01396 QueryWindow(const WindowDesc *desc, StringID caption, StringID message, Window *parent, QueryCallbackProc *callback) : Window()
01397 {
01398
01399
01400 CopyOutDParam(this->params, 0, lengthof(this->params));
01401 this->caption = caption;
01402 this->message = message;
01403 this->proc = callback;
01404
01405 this->InitNested(desc);
01406
01407 if (parent == NULL) parent = FindWindowById(WC_MAIN_WINDOW, 0);
01408 this->parent = parent;
01409 this->left = parent->left + (parent->width / 2) - (this->width / 2);
01410 this->top = parent->top + (parent->height / 2) - (this->height / 2);
01411 }
01412
01413 ~QueryWindow()
01414 {
01415 if (this->proc != NULL) this->proc(this->parent, false);
01416 }
01417
01418 virtual void SetStringParameters(int widget) const
01419 {
01420 switch (widget) {
01421 case QUERY_WIDGET_CAPTION:
01422 CopyInDParam(1, this->params, lengthof(this->params));
01423 SetDParam(0, this->caption);
01424 break;
01425
01426 case QUERY_WIDGET_TEXT:
01427 CopyInDParam(0, this->params, lengthof(this->params));
01428 break;
01429 }
01430 }
01431
01432 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01433 {
01434 if (widget != QUERY_WIDGET_TEXT) return;
01435
01436 Dimension d = GetStringMultiLineBoundingBox(this->message, *size);
01437 d.width += padding.width;
01438 d.height += padding.height;
01439 *size = d;
01440 }
01441
01442 virtual void DrawWidget(const Rect &r, int widget) const
01443 {
01444 if (widget != QUERY_WIDGET_TEXT) return;
01445
01446 DrawStringMultiLine(r.left, r.right, r.top, r.bottom, this->message, TC_FROMSTRING, SA_CENTER);
01447 }
01448
01449 virtual void OnPaint()
01450 {
01451 this->DrawWidgets();
01452 }
01453
01454 virtual void OnClick(Point pt, int widget, int click_count)
01455 {
01456 switch (widget) {
01457 case QUERY_WIDGET_YES: {
01458
01459
01460 QueryCallbackProc *proc = this->proc;
01461 Window *parent = this->parent;
01462
01463 this->proc = NULL;
01464 delete this;
01465 if (proc != NULL) {
01466 proc(parent, true);
01467 proc = NULL;
01468 }
01469 } break;
01470 case QUERY_WIDGET_NO:
01471 delete this;
01472 break;
01473 }
01474 }
01475
01476 virtual EventState OnKeyPress(uint16 key, uint16 keycode)
01477 {
01478
01479 switch (keycode) {
01480 case WKC_RETURN:
01481 case WKC_NUM_ENTER:
01482 if (this->proc != NULL) {
01483 this->proc(this->parent, true);
01484 this->proc = NULL;
01485 }
01486
01487 case WKC_ESC:
01488 delete this;
01489 return ES_HANDLED;
01490 }
01491 return ES_NOT_HANDLED;
01492 }
01493 };
01494
01495 static const NWidgetPart _nested_query_widgets[] = {
01496 NWidget(NWID_HORIZONTAL),
01497 NWidget(WWT_CLOSEBOX, COLOUR_RED),
01498 NWidget(WWT_CAPTION, COLOUR_RED, QUERY_WIDGET_CAPTION), SetDataTip(STR_JUST_STRING, STR_NULL),
01499 EndContainer(),
01500 NWidget(WWT_PANEL, COLOUR_RED), SetPIP(8, 15, 8),
01501 NWidget(WWT_TEXT, COLOUR_RED, QUERY_WIDGET_TEXT), SetMinimalSize(200, 12),
01502 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(20, 29, 20),
01503 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, QUERY_WIDGET_NO), SetMinimalSize(71, 12), SetDataTip(STR_QUIT_NO, STR_NULL),
01504 NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, QUERY_WIDGET_YES), SetMinimalSize(71, 12), SetDataTip(STR_QUIT_YES, STR_NULL),
01505 EndContainer(),
01506 EndContainer(),
01507 };
01508
01509 static const WindowDesc _query_desc(
01510 WDP_CENTER, 0, 0,
01511 WC_CONFIRM_POPUP_QUERY, WC_NONE,
01512 WDF_UNCLICK_BUTTONS | WDF_MODAL,
01513 _nested_query_widgets, lengthof(_nested_query_widgets)
01514 );
01515
01524 void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallbackProc *callback)
01525 {
01526 new QueryWindow(&_query_desc, caption, message, parent, callback);
01527 }
01528
01529
01530 enum SaveLoadWindowWidgets {
01531 SLWW_WINDOWTITLE,
01532 SLWW_SORT_BYNAME,
01533 SLWW_SORT_BYDATE,
01534 SLWW_BACKGROUND,
01535 SLWW_FILE_BACKGROUND,
01536 SLWW_HOME_BUTTON,
01537 SLWW_DRIVES_DIRECTORIES_LIST,
01538 SLWW_SCROLLBAR,
01539 SLWW_CONTENT_DOWNLOAD,
01540 SLWW_SAVE_OSK_TITLE,
01541 SLWW_DELETE_SELECTION,
01542 SLWW_SAVE_GAME,
01543 SLWW_CONTENT_DOWNLOAD_SEL,
01544 };
01545
01546 static const NWidgetPart _nested_load_dialog_widgets[] = {
01547 NWidget(NWID_HORIZONTAL),
01548 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
01549 NWidget(WWT_CAPTION, COLOUR_GREY, SLWW_WINDOWTITLE),
01550 EndContainer(),
01551 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01552 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SLWW_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
01553 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SLWW_SORT_BYDATE), SetDataTip(STR_SORT_BY_CAPTION_DATE, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
01554 EndContainer(),
01555 NWidget(WWT_PANEL, COLOUR_GREY, SLWW_BACKGROUND), SetFill(1, 0), SetResize(1, 0), EndContainer(),
01556 NWidget(WWT_PANEL, COLOUR_GREY, SLWW_FILE_BACKGROUND),
01557 NWidget(NWID_HORIZONTAL),
01558 NWidget(NWID_VERTICAL),
01559 NWidget(WWT_INSET, COLOUR_GREY, SLWW_DRIVES_DIRECTORIES_LIST), SetFill(1, 1), SetPadding(2, 1, 2, 2),
01560 SetDataTip(0x0, STR_SAVELOAD_LIST_TOOLTIP), SetResize(1, 10), EndContainer(),
01561 NWidget(NWID_SELECTION, INVALID_COLOUR, SLWW_CONTENT_DOWNLOAD_SEL),
01562 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SLWW_CONTENT_DOWNLOAD), SetResize(1, 0),
01563 SetDataTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT),
01564 EndContainer(),
01565 EndContainer(),
01566 NWidget(NWID_VERTICAL),
01567 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, SLWW_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
01568 NWidget(WWT_SCROLLBAR, COLOUR_GREY, SLWW_SCROLLBAR),
01569 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
01570 EndContainer(),
01571 EndContainer(),
01572 EndContainer(),
01573 };
01574
01575 static const NWidgetPart _nested_save_dialog_widgets[] = {
01576 NWidget(NWID_HORIZONTAL),
01577 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
01578 NWidget(WWT_CAPTION, COLOUR_GREY, SLWW_WINDOWTITLE),
01579 EndContainer(),
01580 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01581 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SLWW_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
01582 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SLWW_SORT_BYDATE), SetDataTip(STR_SORT_BY_CAPTION_DATE, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
01583 EndContainer(),
01584 NWidget(WWT_PANEL, COLOUR_GREY, SLWW_BACKGROUND), SetFill(1, 0), SetResize(1, 0), EndContainer(),
01585 NWidget(WWT_PANEL, COLOUR_GREY, SLWW_FILE_BACKGROUND),
01586 NWidget(NWID_HORIZONTAL),
01587 NWidget(WWT_INSET, COLOUR_GREY, SLWW_DRIVES_DIRECTORIES_LIST), SetPadding(2, 1, 0, 2),
01588 SetDataTip(0x0, STR_SAVELOAD_LIST_TOOLTIP), SetResize(1, 10), EndContainer(),
01589 NWidget(NWID_VERTICAL),
01590 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, SLWW_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
01591 NWidget(WWT_SCROLLBAR, COLOUR_GREY, SLWW_SCROLLBAR),
01592 EndContainer(),
01593 EndContainer(),
01594 NWidget(WWT_EDITBOX, COLOUR_GREY, SLWW_SAVE_OSK_TITLE), SetPadding(3, 2, 2, 2), SetFill(1, 0), SetResize(1, 0),
01595 SetDataTip(STR_SAVELOAD_OSKTITLE, STR_SAVELOAD_EDITBOX_TOOLTIP),
01596 EndContainer(),
01597 NWidget(NWID_HORIZONTAL),
01598 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SLWW_DELETE_SELECTION), SetDataTip(STR_SAVELOAD_DELETE_BUTTON, STR_SAVELOAD_DELETE_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01599 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SLWW_SAVE_GAME), SetDataTip(STR_SAVELOAD_SAVE_BUTTON, STR_SAVELOAD_SAVE_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01600 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
01601 EndContainer(),
01602 };
01603
01604
01605 const TextColour _fios_colours[] = {
01606 TC_LIGHT_BLUE, TC_DARK_GREEN, TC_DARK_GREEN, TC_ORANGE, TC_LIGHT_BROWN,
01607 TC_ORANGE, TC_LIGHT_BROWN, TC_ORANGE, TC_ORANGE, TC_YELLOW
01608 };
01609
01610 void BuildFileList()
01611 {
01612 _fios_path_changed = true;
01613 FiosFreeSavegameList();
01614
01615 switch (_saveload_mode) {
01616 case SLD_NEW_GAME:
01617 case SLD_LOAD_SCENARIO:
01618 case SLD_SAVE_SCENARIO:
01619 FiosGetScenarioList(_saveload_mode); break;
01620 case SLD_LOAD_HEIGHTMAP:
01621 FiosGetHeightmapList(_saveload_mode); break;
01622
01623 default: FiosGetSavegameList(_saveload_mode); break;
01624 }
01625 }
01626
01627 static void MakeSortedSaveGameList()
01628 {
01629 uint sort_start = 0;
01630 uint sort_end = 0;
01631
01632
01633
01634
01635
01636 for (const FiosItem *item = _fios_items.Begin(); item != _fios_items.End(); item++) {
01637 switch (item->type) {
01638 case FIOS_TYPE_DIR: sort_start++; break;
01639 case FIOS_TYPE_PARENT: sort_start++; break;
01640 case FIOS_TYPE_DRIVE: sort_end++; break;
01641 default: break;
01642 }
01643 }
01644
01645 uint s_amount = _fios_items.Length() - sort_start - sort_end;
01646 QSortT(_fios_items.Get(sort_start), s_amount, CompareFiosItems);
01647 }
01648
01649 extern void StartupEngines();
01650
01651 struct SaveLoadWindow : public QueryStringBaseWindow {
01652 private:
01653 FiosItem o_dir;
01654 public:
01655
01656 void GenerateFileName()
01657 {
01658 GenerateDefaultSaveName(this->edit_str_buf, &this->edit_str_buf[this->edit_str_size - 1]);
01659 }
01660
01661 SaveLoadWindow(const WindowDesc *desc, SaveLoadDialogMode mode) : QueryStringBaseWindow(64)
01662 {
01663 static const StringID saveload_captions[] = {
01664 STR_SAVELOAD_LOAD_CAPTION,
01665 STR_SAVELOAD_LOAD_SCENARIO,
01666 STR_SAVELOAD_SAVE_CAPTION,
01667 STR_SAVELOAD_SAVE_SCENARIO,
01668 STR_SAVELOAD_LOAD_HEIGHTMAP,
01669 };
01670 assert((uint)mode < lengthof(saveload_captions));
01671
01672
01673
01674 switch (mode) {
01675 case SLD_SAVE_GAME: this->GenerateFileName(); break;
01676 case SLD_SAVE_SCENARIO: strecpy(this->edit_str_buf, "UNNAMED", &this->edit_str_buf[edit_str_size - 1]); break;
01677 default: break;
01678 }
01679
01680 this->afilter = CS_ALPHANUMERAL;
01681 InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 240);
01682
01683 this->CreateNestedTree(desc);
01684 if (mode == SLD_LOAD_GAME) this->GetWidget<NWidgetStacked>(SLWW_CONTENT_DOWNLOAD_SEL)->SetDisplayedPlane(SZSP_HORIZONTAL);
01685 this->GetWidget<NWidgetCore>(SLWW_WINDOWTITLE)->widget_data = saveload_captions[mode];
01686
01687 this->FinishInitNested(desc, 0);
01688
01689 this->LowerWidget(SLWW_DRIVES_DIRECTORIES_LIST);
01690
01691
01692
01693 if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) {
01694 DoCommandP(0, PM_PAUSED_SAVELOAD, 1, CMD_PAUSE);
01695 }
01696 SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
01697
01698 BuildFileList();
01699
01700 ResetObjectToPlace();
01701
01702 o_dir.type = FIOS_TYPE_DIRECT;
01703 switch (_saveload_mode) {
01704 case SLD_SAVE_GAME:
01705 case SLD_LOAD_GAME:
01706 FioGetDirectory(o_dir.name, lengthof(o_dir.name), SAVE_DIR);
01707 break;
01708
01709 case SLD_SAVE_SCENARIO:
01710 case SLD_LOAD_SCENARIO:
01711 FioGetDirectory(o_dir.name, lengthof(o_dir.name), SCENARIO_DIR);
01712 break;
01713
01714 case SLD_LOAD_HEIGHTMAP:
01715 FioGetDirectory(o_dir.name, lengthof(o_dir.name), HEIGHTMAP_DIR);
01716 break;
01717
01718 default:
01719 strecpy(o_dir.name, _personal_dir, lastof(o_dir.name));
01720 }
01721
01722
01723 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01724 this->SetFocusedWidget(SLWW_SAVE_OSK_TITLE);
01725 }
01726 }
01727
01728 virtual ~SaveLoadWindow()
01729 {
01730
01731 if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) {
01732 DoCommandP(0, PM_PAUSED_SAVELOAD, 0, CMD_PAUSE);
01733 }
01734 FiosFreeSavegameList();
01735 }
01736
01737 virtual void DrawWidget(const Rect &r, int widget) const
01738 {
01739 switch (widget) {
01740 case SLWW_SORT_BYNAME:
01741 case SLWW_SORT_BYDATE:
01742 if (((_savegame_sort_order & SORT_BY_NAME) != 0) == (widget == SLWW_SORT_BYNAME)) {
01743 this->DrawSortButtonState(widget, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP);
01744 }
01745 break;
01746
01747 case SLWW_BACKGROUND: {
01748 static const char *path = NULL;
01749 static StringID str = STR_ERROR_UNABLE_TO_READ_DRIVE;
01750 static uint64 tot = 0;
01751
01752 if (_fios_path_changed) {
01753 str = FiosGetDescText(&path, &tot);
01754 _fios_path_changed = false;
01755 }
01756
01757 if (str != STR_ERROR_UNABLE_TO_READ_DRIVE) SetDParam(0, tot);
01758 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP, str);
01759 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, path, TC_BLACK);
01760 } break;
01761
01762 case SLWW_DRIVES_DIRECTORIES_LIST: {
01763 GfxFillRect(r.left + 1, r.top + 1, r.right, r.bottom, 0xD7);
01764
01765 uint y = r.top + WD_FRAMERECT_TOP;
01766 for (uint pos = this->vscroll.GetPosition(); pos < _fios_items.Length(); pos++) {
01767 const FiosItem *item = _fios_items.Get(pos);
01768
01769 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, item->title, _fios_colours[item->type]);
01770 y += this->resize.step_height;
01771 if (y >= this->vscroll.GetCapacity() * this->resize.step_height + r.top + WD_FRAMERECT_TOP) break;
01772 }
01773 } break;
01774 }
01775 }
01776
01777 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01778 {
01779 switch (widget) {
01780 case SLWW_BACKGROUND:
01781 size->height = 2 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
01782 break;
01783
01784 case SLWW_DRIVES_DIRECTORIES_LIST:
01785 resize->height = FONT_HEIGHT_NORMAL;
01786 size->height = resize->height * 10 + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
01787 break;
01788 }
01789 }
01790
01791 virtual void OnPaint()
01792 {
01793 if (_savegame_sort_dirty) {
01794 _savegame_sort_dirty = false;
01795 MakeSortedSaveGameList();
01796 }
01797
01798 this->vscroll.SetCount(_fios_items.Length());
01799 this->DrawWidgets();
01800
01801 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01802 this->DrawEditBox(SLWW_SAVE_OSK_TITLE);
01803 }
01804 }
01805
01806 virtual void OnClick(Point pt, int widget, int click_count)
01807 {
01808 switch (widget) {
01809 case SLWW_SORT_BYNAME:
01810 _savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ?
01811 SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME;
01812 _savegame_sort_dirty = true;
01813 this->SetDirty();
01814 break;
01815
01816 case SLWW_SORT_BYDATE:
01817 _savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ?
01818 SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE;
01819 _savegame_sort_dirty = true;
01820 this->SetDirty();
01821 break;
01822
01823 case SLWW_HOME_BUTTON:
01824 FiosBrowseTo(&o_dir);
01825 this->SetDirty();
01826 BuildFileList();
01827 break;
01828
01829 case SLWW_DRIVES_DIRECTORIES_LIST: {
01830 int y = (pt.y - this->GetWidget<NWidgetBase>(SLWW_DRIVES_DIRECTORIES_LIST)->pos_y - WD_FRAMERECT_TOP) / this->resize.step_height;
01831
01832 if (y < 0 || (y += this->vscroll.GetPosition()) >= this->vscroll.GetCount()) return;
01833
01834 const FiosItem *file = _fios_items.Get(y);
01835
01836 const char *name = FiosBrowseTo(file);
01837 if (name != NULL) {
01838 if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
01839 _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD;
01840
01841 SetFiosType(file->type);
01842 strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
01843 strecpy(_file_to_saveload.title, file->title, lastof(_file_to_saveload.title));
01844
01845 delete this;
01846 } else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
01847 SetFiosType(file->type);
01848 strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
01849 strecpy(_file_to_saveload.title, file->title, lastof(_file_to_saveload.title));
01850
01851 delete this;
01852 ShowHeightmapLoad();
01853 } else {
01854
01855 ttd_strlcpy(this->text.buf, file->title, this->text.maxsize);
01856 UpdateTextBufferSize(&this->text);
01857 this->SetWidgetDirty(SLWW_SAVE_OSK_TITLE);
01858 }
01859 } else {
01860
01861 this->SetDirty();
01862 BuildFileList();
01863 }
01864 break;
01865 }
01866
01867 case SLWW_CONTENT_DOWNLOAD:
01868 if (!_network_available) {
01869 ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, 0, 0);
01870 } else {
01871 #if defined(ENABLE_NETWORK)
01872 switch (_saveload_mode) {
01873 default: NOT_REACHED();
01874 case SLD_LOAD_SCENARIO: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_SCENARIO); break;
01875 case SLD_LOAD_HEIGHTMAP: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_HEIGHTMAP); break;
01876 }
01877 #endif
01878 }
01879 break;
01880
01881 case SLWW_DELETE_SELECTION: case SLWW_SAVE_GAME:
01882 break;
01883 }
01884 }
01885
01886 virtual void OnMouseLoop()
01887 {
01888 if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
01889 this->HandleEditBox(SLWW_SAVE_OSK_TITLE);
01890 }
01891 }
01892
01893 virtual EventState OnKeyPress(uint16 key, uint16 keycode)
01894 {
01895 if (keycode == WKC_ESC) {
01896 delete this;
01897 return ES_HANDLED;
01898 }
01899
01900 EventState state = ES_NOT_HANDLED;
01901 if ((_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) &&
01902 this->HandleEditBoxKey(SLWW_SAVE_OSK_TITLE, key, keycode, state) == HEBR_CONFIRM) {
01903 this->HandleButtonClick(SLWW_SAVE_GAME);
01904 }
01905
01906 return state;
01907 }
01908
01909 virtual void OnTimeout()
01910 {
01911
01912
01913 if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) return;
01914
01915 if (this->IsWidgetLowered(SLWW_DELETE_SELECTION)) {
01916 if (!FiosDelete(this->text.buf)) {
01917 ShowErrorMessage(STR_ERROR_UNABLE_TO_DELETE_FILE, INVALID_STRING_ID, 0, 0);
01918 } else {
01919 BuildFileList();
01920
01921 if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName();
01922 }
01923
01924 UpdateTextBufferSize(&this->text);
01925 this->SetDirty();
01926 } else if (this->IsWidgetLowered(SLWW_SAVE_GAME)) {
01927 _switch_mode = SM_SAVE;
01928 FiosMakeSavegameName(_file_to_saveload.name, this->text.buf, sizeof(_file_to_saveload.name));
01929
01930
01931 if (_game_mode == GM_EDITOR) StartupEngines();
01932 }
01933 }
01934
01935 virtual void OnResize()
01936 {
01937 this->vscroll.SetCapacityFromWidget(this, SLWW_DRIVES_DIRECTORIES_LIST);
01938 }
01939
01940 virtual void OnInvalidateData(int data)
01941 {
01942 BuildFileList();
01943 }
01944 };
01945
01946 static const WindowDesc _load_dialog_desc(
01947 WDP_CENTER, 257, 294,
01948 WC_SAVELOAD, WC_NONE,
01949 WDF_UNCLICK_BUTTONS,
01950 _nested_load_dialog_widgets, lengthof(_nested_load_dialog_widgets)
01951 );
01952
01953 static const WindowDesc _save_dialog_desc(
01954 WDP_CENTER, 257, 320,
01955 WC_SAVELOAD, WC_NONE,
01956 WDF_UNCLICK_BUTTONS,
01957 _nested_save_dialog_widgets, lengthof(_nested_save_dialog_widgets)
01958 );
01959
01962 static const FileType _file_modetotype[] = {
01963 FT_SAVEGAME,
01964 FT_SCENARIO,
01965 FT_SAVEGAME,
01966 FT_SCENARIO,
01967 FT_HEIGHTMAP,
01968 FT_SAVEGAME,
01969 };
01970
01971 void ShowSaveLoadDialog(SaveLoadDialogMode mode)
01972 {
01973 DeleteWindowById(WC_SAVELOAD, 0);
01974
01975 const WindowDesc *sld;
01976 switch (mode) {
01977 case SLD_SAVE_GAME:
01978 case SLD_SAVE_SCENARIO:
01979 sld = &_save_dialog_desc; break;
01980 default:
01981 sld = &_load_dialog_desc; break;
01982 }
01983
01984 _saveload_mode = mode;
01985 _file_to_saveload.filetype = _file_modetotype[mode];
01986
01987 new SaveLoadWindow(sld, mode);
01988 }
01989
01990 void RedrawAutosave()
01991 {
01992 SetWindowDirty(WC_STATUS_BAR, 0);
01993 }
01994
01995 void SetFiosType(const byte fiostype)
01996 {
01997 switch (fiostype) {
01998 case FIOS_TYPE_FILE:
01999 case FIOS_TYPE_SCENARIO:
02000 _file_to_saveload.mode = SL_LOAD;
02001 break;
02002
02003 case FIOS_TYPE_OLDFILE:
02004 case FIOS_TYPE_OLD_SCENARIO:
02005 _file_to_saveload.mode = SL_OLD_LOAD;
02006 break;
02007
02008 #ifdef WITH_PNG
02009 case FIOS_TYPE_PNG:
02010 _file_to_saveload.mode = SL_PNG;
02011 break;
02012 #endif
02013
02014 case FIOS_TYPE_BMP:
02015 _file_to_saveload.mode = SL_BMP;
02016 break;
02017
02018 default:
02019 _file_to_saveload.mode = SL_INVALID;
02020 break;
02021 }
02022 }