misc_gui.cpp

Go to the documentation of this file.
00001 /* $Id: misc_gui.cpp 14555 2008-11-02 11:20:15Z skidd13 $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "heightmap.h"
00008 #include "debug.h"
00009 #include "landscape.h"
00010 #include "newgrf.h"
00011 #include "newgrf_text.h"
00012 #include "saveload.h"
00013 #include "tile_map.h"
00014 #include "gui.h"
00015 #include "window_gui.h"
00016 #include "station_gui.h"
00017 #include "textbuf_gui.h"
00018 #include "viewport_func.h"
00019 #include "gfx_func.h"
00020 #include "station_func.h"
00021 #include "command_func.h"
00022 #include "company_func.h"
00023 #include "company_base.h"
00024 #include "town.h"
00025 #include "network/network.h"
00026 #include "variables.h"
00027 #include "cheat_func.h"
00028 #include "train.h"
00029 #include "tgp.h"
00030 #include "cargotype.h"
00031 #include "company_manager_face.h"
00032 #include "strings_func.h"
00033 #include "fileio_func.h"
00034 #include "fios.h"
00035 #include "tile_cmd.h"
00036 #include "zoom_func.h"
00037 #include "functions.h"
00038 #include "window_func.h"
00039 #include "date_func.h"
00040 #include "sound_func.h"
00041 #include "string_func.h"
00042 #include "company_gui.h"
00043 #include "settings_type.h"
00044 #include "newgrf_cargo.h"
00045 #include "rail_gui.h"
00046 #include "tilehighlight_func.h"
00047 #include "querystring_gui.h"
00048 #include "company_base.h"
00049 
00050 #include "table/sprites.h"
00051 #include "table/strings.h"
00052 
00053 /* Variables to display file lists */
00054 SaveLoadDialogMode _saveload_mode;
00055 
00056 
00057 static bool _fios_path_changed;
00058 static bool _savegame_sort_dirty;
00059 
00060 static const Widget _land_info_widgets[] = {
00061 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
00062 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_GREY,    11,   299,     0,    13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
00063 {      WWT_PANEL, RESIZE_BOTTOM,  COLOUR_GREY,     0,   299,    14,    99, 0x0,                            STR_NULL},
00064 {    WIDGETS_END},
00065 };
00066 
00067 static const WindowDesc _land_info_desc = {
00068   WDP_AUTO, WDP_AUTO, 300, 100, 300, 100,
00069   WC_LAND_INFO, WC_NONE,
00070   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00071   _land_info_widgets,
00072 };
00073 
00074 class LandInfoWindow : public Window {
00075   enum {
00076     LAND_INFO_CENTERED_LINES   = 12,                       
00077     LAND_INFO_MULTICENTER_LINE = LAND_INFO_CENTERED_LINES, 
00078     LAND_INFO_LINE_END,
00079 
00080     LAND_INFO_LINE_BUFF_SIZE = 512,
00081   };
00082 
00083 public:
00084   char landinfo_data[LAND_INFO_LINE_END][LAND_INFO_LINE_BUFF_SIZE];
00085 
00086   virtual void OnPaint()
00087   {
00088     this->DrawWidgets();
00089 
00090     uint y = 21;
00091     for (uint i = 0; i < LAND_INFO_CENTERED_LINES; i++) {
00092       if (StrEmpty(this->landinfo_data[i])) break;
00093 
00094       DoDrawStringCentered(150, y, this->landinfo_data[i], i == 0 ? TC_LIGHT_BLUE : TC_FROMSTRING);
00095       y += i == 0 ? 16 : 12;
00096     }
00097 
00098     y += 6;
00099 
00100     if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) {
00101       SetDParamStr(0, this->landinfo_data[LAND_INFO_MULTICENTER_LINE]);
00102       DrawStringMultiCenter(150, y, STR_JUST_RAW_STRING, this->width - 4);
00103     }
00104   }
00105 
00106   LandInfoWindow(TileIndex tile) : Window(&_land_info_desc) {
00107     Company *c = GetCompany(IsValidCompanyID(_local_company) ? _local_company : COMPANY_FIRST);
00108     Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority);
00109 
00110     Money old_money = c->money;
00111     c->money = INT64_MAX;
00112     CommandCost costclear = DoCommand(tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR);
00113     c->money = old_money;
00114 
00115     /* Because build_date is not set yet in every TileDesc, we make sure it is empty */
00116     TileDesc td;
00117     AcceptedCargo ac;
00118 
00119     td.build_date = INVALID_DATE;
00120 
00121     /* Most tiles have only one owner, but
00122      *  - drivethrough roadstops can be build on town owned roads (up to 2 owners) and
00123      *  - roads can have up to four owners (railroad, road, tram, 3rd-roadtype "highway").
00124      */
00125     td.owner_type[0] = STR_01A7_OWNER; // At least one owner is displayed, though it might be "N/A".
00126     td.owner_type[1] = STR_NULL;       // STR_NULL results in skipping the owner
00127     td.owner_type[2] = STR_NULL;
00128     td.owner_type[3] = STR_NULL;
00129     td.owner[0] = OWNER_NONE;
00130     td.owner[1] = OWNER_NONE;
00131     td.owner[2] = OWNER_NONE;
00132     td.owner[3] = OWNER_NONE;
00133 
00134     td.station_class = STR_NULL;
00135     td.station_name = STR_NULL;
00136 
00137     td.grf = NULL;
00138 
00139     GetAcceptedCargo(tile, ac);
00140     GetTileDesc(tile, &td);
00141 
00142     uint line_nr = 0;
00143 
00144     /* Tiletype */
00145     SetDParam(0, td.dparam[0]);
00146     GetString(this->landinfo_data[line_nr], td.str, lastof(this->landinfo_data[line_nr]));
00147     line_nr++;
00148 
00149     /* Up to four owners */
00150     for (uint i = 0; i < 4; i++) {
00151       if (td.owner_type[i] == STR_NULL) continue;
00152 
00153       SetDParam(0, STR_01A6_N_A);
00154       if (td.owner[i] != OWNER_NONE && td.owner[i] != OWNER_WATER) GetNameOfOwner(td.owner[i], tile);
00155       GetString(this->landinfo_data[line_nr], td.owner_type[i], lastof(this->landinfo_data[line_nr]));
00156       line_nr++;
00157     }
00158 
00159     /* Cost to clear */
00160     StringID str = STR_01A4_COST_TO_CLEAR_N_A;
00161     if (CmdSucceeded(costclear)) {
00162       SetDParam(0, costclear.GetCost());
00163       str = STR_01A5_COST_TO_CLEAR;
00164     }
00165     GetString(this->landinfo_data[line_nr], str, lastof(this->landinfo_data[line_nr]));
00166     line_nr++;
00167 
00168     /* Location */
00169     char tmp[16];
00170     snprintf(tmp, lengthof(tmp), "0x%.4X", tile);
00171     SetDParam(0, TileX(tile));
00172     SetDParam(1, TileY(tile));
00173     SetDParam(2, TileHeight(tile));
00174     SetDParamStr(3, tmp);
00175     GetString(this->landinfo_data[line_nr], STR_LANDINFO_COORDS, lastof(this->landinfo_data[line_nr]));
00176     line_nr++;
00177 
00178     /* Local authority */
00179     SetDParam(0, STR_01A9_NONE);
00180     if (t != NULL && t->IsValid()) {
00181       SetDParam(0, STR_TOWN);
00182       SetDParam(1, t->index);
00183     }
00184     GetString(this->landinfo_data[line_nr], STR_01A8_LOCAL_AUTHORITY, lastof(this->landinfo_data[line_nr]));
00185     line_nr++;
00186 
00187     /* Build date */
00188     if (td.build_date != INVALID_DATE) {
00189       SetDParam(0, td.build_date);
00190       GetString(this->landinfo_data[line_nr], STR_BUILD_DATE, lastof(this->landinfo_data[line_nr]));
00191       line_nr++;
00192     }
00193 
00194     /* Station class */
00195     if (td.station_class != STR_NULL) {
00196       SetDParam(0, td.station_class);
00197       GetString(this->landinfo_data[line_nr], STR_TILEDESC_STATION_CLASS, lastof(this->landinfo_data[line_nr]));
00198       line_nr++;
00199     }
00200 
00201     /* Station type name */
00202     if (td.station_name != STR_NULL) {
00203       SetDParam(0, td.station_name);
00204       GetString(this->landinfo_data[line_nr], STR_TILEDESC_STATION_TYPE, lastof(this->landinfo_data[line_nr]));
00205       line_nr++;
00206     }
00207 
00208     /* NewGRF name */
00209     if (td.grf != NULL) {
00210       SetDParamStr(0, td.grf);
00211       GetString(this->landinfo_data[line_nr], STR_TILEDESC_NEWGRF_NAME, lastof(this->landinfo_data[line_nr]));
00212       line_nr++;
00213     }
00214 
00215     assert(line_nr < LAND_INFO_CENTERED_LINES);
00216 
00217     /* Mark last line empty */
00218     this->landinfo_data[line_nr][0] = '\0';
00219 
00220     /* Cargo acceptance is displayed in a extra multiline */
00221     char *strp = GetString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE], STR_01CE_CARGO_ACCEPTED, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
00222     bool found = false;
00223 
00224     for (CargoID i = 0; i < NUM_CARGO; ++i) {
00225       if (ac[i] > 0) {
00226         /* Add a comma between each item. */
00227         if (found) {
00228           *strp++ = ',';
00229           *strp++ = ' ';
00230         }
00231         found = true;
00232 
00233         /* If the accepted value is less than 8, show it in 1/8:ths */
00234         if (ac[i] < 8) {
00235           SetDParam(0, ac[i]);
00236           SetDParam(1, GetCargo(i)->name);
00237           strp = GetString(strp, STR_01D1_8, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
00238         } else {
00239           strp = GetString(strp, GetCargo(i)->name, lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]));
00240         }
00241       }
00242     }
00243     if (!found) this->landinfo_data[LAND_INFO_MULTICENTER_LINE][0] = '\0';
00244 
00245     if (found) line_nr += 2;
00246 
00247     if (line_nr > 6) ResizeWindow(this, 0, 12 * (line_nr - 6));
00248 
00249     this->FindWindowPlacementAndResize(&_land_info_desc);
00250 
00251 #if defined(_DEBUG)
00252 # define LANDINFOD_LEVEL 0
00253 #else
00254 # define LANDINFOD_LEVEL 1
00255 #endif
00256     DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile));
00257     DEBUG(misc, LANDINFOD_LEVEL, "type_height  = %#x", _m[tile].type_height);
00258     DEBUG(misc, LANDINFOD_LEVEL, "m1           = %#x", _m[tile].m1);
00259     DEBUG(misc, LANDINFOD_LEVEL, "m2           = %#x", _m[tile].m2);
00260     DEBUG(misc, LANDINFOD_LEVEL, "m3           = %#x", _m[tile].m3);
00261     DEBUG(misc, LANDINFOD_LEVEL, "m4           = %#x", _m[tile].m4);
00262     DEBUG(misc, LANDINFOD_LEVEL, "m5           = %#x", _m[tile].m5);
00263     DEBUG(misc, LANDINFOD_LEVEL, "m6           = %#x", _m[tile].m6);
00264     DEBUG(misc, LANDINFOD_LEVEL, "m7           = %#x", _me[tile].m7);
00265 #undef LANDINFOD_LEVEL
00266   }
00267 };
00268 
00269 static void Place_LandInfo(TileIndex tile)
00270 {
00271   DeleteWindowById(WC_LAND_INFO, 0);
00272   new LandInfoWindow(tile);
00273 }
00274 
00275 void PlaceLandBlockInfo()
00276 {
00277   if (_cursor.sprite == SPR_CURSOR_QUERY) {
00278     ResetObjectToPlace();
00279   } else {
00280     _place_proc = Place_LandInfo;
00281     SetObjectToPlace(SPR_CURSOR_QUERY, PAL_NONE, VHM_RECT, WC_MAIN_TOOLBAR, 0);
00282   }
00283 }
00284 
00285 static const Widget _about_widgets[] = {
00286 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
00287 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_GREY,    11,   419,     0,    13, STR_015B_OPENTTD, STR_NULL},
00288 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,     0,   419,    14,   271, 0x0,              STR_NULL},
00289 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,     5,   414,    40,   245, STR_NULL,         STR_NULL},
00290 {    WIDGETS_END},
00291 };
00292 
00293 static const WindowDesc _about_desc = {
00294   WDP_CENTER, WDP_CENTER, 420, 272, 420, 272,
00295   WC_GAME_OPTIONS, WC_NONE,
00296   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00297   _about_widgets,
00298 };
00299 
00300 struct AboutWindow : public Window {
00301   int scroll_height;
00302   uint16 counter;
00303 
00304   AboutWindow() : Window(&_about_desc)
00305   {
00306     this->counter = 5;
00307     this->scroll_height = this->height - 40;
00308     this->FindWindowPlacementAndResize(&_about_desc);
00309   }
00310 
00311   virtual void OnPaint()
00312   {
00313     static const char *credits[] = {
00314       /*************************************************************************
00315        *                      maximum length of string which fits in window   -^*/
00316       "Original design by Chris Sawyer",
00317       "Original graphics by Simon Foster",
00318       "",
00319       "The OpenTTD team (in alphabetical order):",
00320       "  Jean-Francois Claeys (Belugas) - GUI, newindustries and more",
00321       "  Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles",
00322       "  Matthijs Kooijman (blathijs) - Pathfinder-guru, pool rework",
00323       "  Victor Fischer (Celestar) - Programming everywhere you need him to",
00324       "  Christoph Elsenhans (frosch) - General coding",
00325       "  Loïc Guilloux (glx) - General coding",
00326       "  Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)",
00327       "  Jonathan Coome (Maedhros) - High priest of the newGRF Temple",
00328       "  Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host",
00329       "  Owen Rudge (orudge) - Forum host, OS/2 port",
00330       "  Peter Nelson (peter1138) - Spiritual descendant from newGRF gods",
00331       "  Remko Bijker (Rubidium) - Lead coder and way more",
00332       "  Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker",
00333       "  Zdenek Sojka (SmatZ) - Bug finder and fixer",
00334       "",
00335       "Inactive Developers:",
00336       "  Tamás Faragó (Darkvater) - Ex-Lead coder",
00337       "  Christoph Mallon (Tron) - Programmer, code correctness police",
00338       "",
00339       "Retired Developers:",
00340       "  Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)",
00341       "  Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)",
00342       "  Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)",
00343       "  Patric Stout (TrueLight) - Programmer, webhoster (0.3 - pre0.6)",
00344       "",
00345       "Special thanks go out to:",
00346       "  Josef Drexler - For his great work on TTDPatch",
00347       "  Marcin Grzegorczyk - For his documentation of TTD internals",
00348       "  Petr Baudis (pasky) - Many patches, newGRF support",
00349       "  Stefan Meißner (sign_de) - For his work on the console",
00350       "  Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with",
00351       "  Cian Duffy (MYOB) - BeOS port / manual writing",
00352       "  Christian Rosentreter (tokai) - MorphOS / AmigaOS port",
00353       "  Richard Kempton (richK) - additional airports, initial TGP implementation",
00354       "",
00355       "  Michael Blunck - Pre-Signals and Semaphores © 2003",
00356       "  George - Canal/Lock graphics © 2003-2004",
00357       "  David Dallaston - Tram tracks",
00358       "  Marcin Grzegorczyk - Foundations for Tracks on Slopes",
00359       "  All Translators - Who made OpenTTD a truly international game",
00360       "  Bug Reporters - Without whom OpenTTD would still be full of bugs!",
00361       "",
00362       "",
00363       "And last but not least:",
00364       "  Chris Sawyer - For an amazing game!"
00365     };
00366 
00367     this->DrawWidgets();
00368 
00369     /* Show original copyright and revision version */
00370     DrawStringCentered(210, 17, STR_00B6_ORIGINAL_COPYRIGHT, TC_FROMSTRING);
00371     DrawStringCentered(210, 17 + 10, STR_00B7_VERSION, TC_FROMSTRING);
00372 
00373     int y = this->scroll_height;
00374 
00375     /* Show all scrolling credits */
00376     for (uint i = 0; i < lengthof(credits); i++) {
00377       if (y >= 50 && y < (this->height - 40)) {
00378         DoDrawString(credits[i], 10, y, TC_BLACK);
00379       }
00380       y += 10;
00381     }
00382 
00383     /* If the last text has scrolled start a new from the start */
00384     if (y < 50) this->scroll_height = this->height - 40;
00385 
00386     DoDrawStringCentered(210, this->height - 25, "Website: http://www.openttd.org", TC_BLACK);
00387     DrawStringCentered(210, this->height - 15, STR_00BA_COPYRIGHT_OPENTTD, TC_FROMSTRING);
00388   }
00389 
00390   virtual void OnTick()
00391   {
00392     if (--this->counter == 0) {
00393       this->counter = 5;
00394       this->scroll_height--;
00395       this->SetDirty();
00396     }
00397   }
00398 };
00399 
00400 void ShowAboutWindow()
00401 {
00402   DeleteWindowById(WC_GAME_OPTIONS, 0);
00403   new AboutWindow();
00404 }
00405 
00406 static const Widget _errmsg_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,   239,     0,    13, STR_00B2_MESSAGE, STR_NULL},
00409 {      WWT_PANEL,   RESIZE_BOTTOM,  COLOUR_RED,     0,   239,    14,    45, 0x0,              STR_NULL},
00410 {    WIDGETS_END},
00411 };
00412 
00413 static const Widget _errmsg_face_widgets[] = {
00414 {   WWT_CLOSEBOX,   RESIZE_NONE,    COLOUR_RED,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
00415 {    WWT_CAPTION,   RESIZE_NONE,    COLOUR_RED,    11,   333,     0,    13, STR_00B3_MESSAGE_FROM, STR_NULL},
00416 {      WWT_PANEL,   RESIZE_BOTTOM,  COLOUR_RED,     0,   333,    14,   136, 0x0,                   STR_NULL},
00417 {   WIDGETS_END},
00418 };
00419 
00420 struct ErrmsgWindow : public Window {
00421 private:
00422   uint duration;
00423   uint64 decode_params[20];
00424   StringID message_1;
00425   StringID message_2;
00426   bool show_company_manager_face;
00427 
00428   int y[2];
00429 
00430 public:
00431   ErrmsgWindow(Point pt, int width, int height, StringID msg1, StringID msg2, const Widget *widget, bool show_company_manager_face) :
00432       Window(pt.x, pt.y, width, height, WC_ERRMSG, widget),
00433       show_company_manager_face(show_company_manager_face)
00434   {
00435     this->duration = _settings_client.gui.errmsg_duration;
00436     CopyOutDParam(this->decode_params, 0, lengthof(this->decode_params));
00437     this->message_1 = msg1;
00438     this->message_2 = msg2;
00439     this->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET;
00440 
00441     SwitchToErrorRefStack();
00442     RewindTextRefStack();
00443 
00444     assert(msg2 != INVALID_STRING_ID);
00445 
00446     int h2 = 3 + GetStringHeight(msg2, width - 2); // msg2 is printed first
00447     int h1 = (msg1 == INVALID_STRING_ID) ? 0 : 3 + GetStringHeight(msg1, width - 2);
00448 
00449     SwitchToNormalRefStack();
00450 
00451     int h = 15 + h1 + h2;
00452     height = max<int>(height, h);
00453 
00454     if (msg1 == INVALID_STRING_ID) {
00455       /* only 1 line will be printed */
00456       y[1] = (height - 15) / 2 + 15 - 5;
00457     } else {
00458       int over = (height - h) / 4;
00459 
00460       y[1] = 15 + h2 / 2 + 1 - 5 + over;
00461       y[0] = height - 3 - h1 / 2 - 5 - over;
00462     }
00463 
00464     this->FindWindowPlacementAndResize(width, height);
00465   }
00466 
00467   virtual void OnPaint()
00468   {
00469     CopyInDParam(0, this->decode_params, lengthof(this->decode_params));
00470     this->DrawWidgets();
00471     CopyInDParam(0, this->decode_params, lengthof(this->decode_params));
00472 
00473     /* If the error message comes from a NewGRF, we must use the text ref. stack reserved for error messages.
00474      * If the message doesn't come from a NewGRF, it won't use the TTDP-style text ref. stack, so we won't hurt anything
00475      */
00476     SwitchToErrorRefStack();
00477     RewindTextRefStack();
00478 
00479     if (this->show_company_manager_face) {
00480       const Company *c = GetCompany((CompanyID)GetDParamX(this->decode_params, 2));
00481       DrawCompanyManagerFace(c->face, c->colour, 2, 16);
00482     }
00483 
00484     DrawStringMultiCenter(this->width - 120, y[1], this->message_2, this->width - 2);
00485     if (this->message_1 != INVALID_STRING_ID) DrawStringMultiCenter(this->width - 120, y[0], this->message_1, this->width - 2);
00486 
00487     /* Switch back to the normal text ref. stack for NewGRF texts */
00488     SwitchToNormalRefStack();
00489   }
00490 
00491   virtual void OnMouseLoop()
00492   {
00493     if (_right_button_down) delete this;
00494   }
00495 
00496   virtual void OnHundredthTick()
00497   {
00498     if (--this->duration == 0) delete this;
00499   }
00500 
00501   ~ErrmsgWindow()
00502   {
00503     SetRedErrorSquare(0);
00504     extern StringID _switch_mode_errorstr;
00505     _switch_mode_errorstr = INVALID_STRING_ID;
00506   }
00507 
00508   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
00509   {
00510     if (keycode != WKC_SPACE) return ES_NOT_HANDLED;
00511     delete this;
00512     return ES_HANDLED;
00513   }
00514 };
00515 
00516 void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y)
00517 {
00518   DeleteWindowById(WC_ERRMSG, 0);
00519 
00520   if (!_settings_client.gui.errmsg_duration) return;
00521 
00522   if (msg_2 == STR_NULL) msg_2 = STR_EMPTY;
00523 
00524   Point pt;
00525   const ViewPort *vp;
00526 
00527   if (msg_1 != STR_013B_OWNED_BY || GetDParam(2) >= 8) {
00528     if ((x | y) != 0) {
00529       pt = RemapCoords2(x, y);
00530       vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00531 
00532       /* move x pos to opposite corner */
00533       pt.x = UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left;
00534       pt.x = (pt.x < (_screen.width >> 1)) ? _screen.width - 260 : 20;
00535 
00536       /* move y pos to opposite corner */
00537       pt.y = UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top;
00538       pt.y = (pt.y < (_screen.height >> 1)) ? _screen.height - 80 : 100;
00539 
00540     } else {
00541       pt.x = (_screen.width - 240) >> 1;
00542       pt.y = (_screen.height - 46) >> 1;
00543     }
00544     new ErrmsgWindow(pt, 240, 46, msg_1, msg_2, _errmsg_widgets, false);
00545   } else {
00546     if ((x | y) != 0) {
00547       pt = RemapCoords2(x, y);
00548       vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
00549       pt.x = Clamp(UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left - (334 / 2),  0, _screen.width  - 334);
00550       pt.y = Clamp(UnScaleByZoom(pt.y - vp->virtual_top,  vp->zoom) + vp->top  - (137 / 2), 22, _screen.height - 137);
00551     } else {
00552       pt.x = (_screen.width  - 334) >> 1;
00553       pt.y = (_screen.height - 137) >> 1;
00554     }
00555     new ErrmsgWindow(pt, 334, 137, msg_1, msg_2, _errmsg_face_widgets, true);
00556   }
00557 }
00558 
00559 void ShowEstimatedCostOrIncome(Money cost, int x, int y)
00560 {
00561   StringID msg = STR_0805_ESTIMATED_COST;
00562 
00563   if (cost < 0) {
00564     cost = -cost;
00565     msg = STR_0807_ESTIMATED_INCOME;
00566   }
00567   SetDParam(0, cost);
00568   ShowErrorMessage(INVALID_STRING_ID, msg, x, y);
00569 }
00570 
00571 void ShowCostOrIncomeAnimation(int x, int y, int z, Money cost)
00572 {
00573   Point pt = RemapCoords(x,y,z);
00574   StringID msg = STR_0801_COST;
00575 
00576   if (cost < 0) {
00577     cost = -cost;
00578     msg = STR_0803_INCOME;
00579   }
00580   SetDParam(0, cost);
00581   AddTextEffect(msg, pt.x, pt.y, 0x250, TE_RISING);
00582 }
00583 
00584 void ShowFeederIncomeAnimation(int x, int y, int z, Money cost)
00585 {
00586   Point pt = RemapCoords(x,y,z);
00587 
00588   SetDParam(0, cost);
00589   AddTextEffect(STR_FEEDER, pt.x, pt.y, 0x250, TE_RISING);
00590 }
00591 
00592 TextEffectID ShowFillingPercent(int x, int y, int z, uint8 percent, StringID string)
00593 {
00594   Point pt = RemapCoords(x, y, z);
00595 
00596   assert(string != STR_NULL);
00597 
00598   SetDParam(0, percent);
00599   return AddTextEffect(string, pt.x, pt.y, 0xFFFF, TE_STATIC);
00600 }
00601 
00602 void UpdateFillingPercent(TextEffectID te_id, uint8 percent, StringID string)
00603 {
00604   assert(string != STR_NULL);
00605 
00606   SetDParam(0, percent);
00607   UpdateTextEffect(te_id, string);
00608 }
00609 
00610 void HideFillingPercent(TextEffectID *te_id)
00611 {
00612   if (*te_id == INVALID_TE_ID) return;
00613 
00614   RemoveTextEffect(*te_id);
00615   *te_id = INVALID_TE_ID;
00616 }
00617 
00618 static const Widget _tooltips_widgets[] = {
00619 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,     0,   199,     0,    31, 0x0, STR_NULL},
00620 {   WIDGETS_END},
00621 };
00622 
00623 struct TooltipsWindow : public Window
00624 {
00625   StringID string_id;
00626   byte paramcount;
00627   uint64 params[5];
00628   bool use_left_mouse_button;
00629 
00630   TooltipsWindow(int x, int y, int width, int height, const Widget *widget,
00631                  StringID str, uint paramcount, const uint64 params[], bool use_left_mouse_button) :
00632       Window(x, y, width, height, WC_TOOLTIPS, widget)
00633   {
00634     this->string_id = str;
00635     assert(sizeof(this->params[0]) == sizeof(params[0]));
00636     assert(paramcount <= lengthof(this->params));
00637     memcpy(this->params, params, sizeof(this->params[0]) * paramcount);
00638     this->paramcount = paramcount;
00639     this->use_left_mouse_button = use_left_mouse_button;
00640 
00641     this->flags4 &= ~WF_WHITE_BORDER_MASK; // remove white-border from tooltip
00642     this->widget[0].right = width;
00643     this->widget[0].bottom = height;
00644 
00645     FindWindowPlacementAndResize(width, height);
00646   }
00647 
00648   virtual void OnPaint()
00649   {
00650     GfxFillRect(0, 0, this->width - 1, this->height - 1, 0);
00651     GfxFillRect(1, 1, this->width - 2, this->height - 2, 0x44);
00652 
00653     for (uint arg = 0; arg < this->paramcount; arg++) {
00654       SetDParam(arg, this->params[arg]);
00655     }
00656     DrawStringMultiCenter((this->width >> 1), (this->height >> 1) - 5, this->string_id, this->width - 2);
00657   }
00658 
00659   virtual void OnMouseLoop()
00660   {
00661     /* We can show tooltips while dragging tools. These are shown as long as
00662      * we are dragging the tool. Normal tooltips work with rmb */
00663     if (this->use_left_mouse_button ? !_left_button_down : !_right_button_down) delete this;
00664   }
00665 };
00666 
00673 void GuiShowTooltips(StringID str, uint paramcount, const uint64 params[], bool use_left_mouse_button)
00674 {
00675   DeleteWindowById(WC_TOOLTIPS, 0);
00676 
00677   if (str == STR_NULL) return;
00678 
00679   for (uint i = 0; i != paramcount; i++) SetDParam(i, params[i]);
00680   char buffer[512];
00681   GetString(buffer, str, lastof(buffer));
00682 
00683   Dimension br = GetStringBoundingBox(buffer);
00684   br.width += 6; br.height += 4; // increase slightly to have some space around the box
00685 
00686   /* Cut tooltip length to 200 pixels max, wrap to new line if longer */
00687   if (br.width > 200) {
00688     br.height += ((br.width - 4) / 176) * 10;
00689     br.width = 200;
00690   }
00691 
00692   /* Correctly position the tooltip position, watch out for window and cursor size
00693    * Clamp value to below main toolbar and above statusbar. If tooltip would
00694    * go below window, flip it so it is shown above the cursor */
00695   int y = Clamp(_cursor.pos.y + _cursor.size.y + _cursor.offs.y + 5, 22, _screen.height - 12);
00696   if (y + br.height > _screen.height - 12) y = _cursor.pos.y + _cursor.offs.y - br.height - 5;
00697   int x = Clamp(_cursor.pos.x - (br.width >> 1), 0, _screen.width - br.width);
00698 
00699   new TooltipsWindow(x, y, br.width, br.height, _tooltips_widgets, str, paramcount, params, use_left_mouse_button);
00700 }
00701 
00702 
00703 static int DrawStationCoverageText(const AcceptedCargo cargo,
00704   int str_x, int str_y, StationCoverageType sct, bool supplies)
00705 {
00706   bool first = true;
00707 
00708   char string[512];
00709   char *b = InlineString(string, supplies ? STR_SUPPLIES : STR_000D_ACCEPTS);
00710 
00711   for (CargoID i = 0; i < NUM_CARGO; i++) {
00712     if (b >= lastof(string) - (1 + 2 * 4)) break; // ',' or ' ' and two calls to Utf8Encode()
00713     switch (sct) {
00714       case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00715       case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00716       case SCT_ALL: break;
00717       default: NOT_REACHED();
00718     }
00719     if (cargo[i] >= (supplies ? 1U : 8U)) {
00720       if (first) {
00721         first = false;
00722       } else {
00723         /* Add a comma if this is not the first item */
00724         *b++ = ',';
00725         *b++ = ' ';
00726       }
00727       b = InlineString(b, GetCargo(i)->name);
00728     }
00729   }
00730 
00731   /* If first is still true then no cargo is accepted */
00732   if (first) b = InlineString(b, STR_00D0_NOTHING);
00733 
00734   *b = '\0';
00735 
00736   /* Make sure we detect any buffer overflow */
00737   assert(b < endof(string));
00738 
00739   SetDParamStr(0, string);
00740   return DrawStringMultiLine(str_x, str_y, STR_JUST_RAW_STRING, 144);
00741 }
00742 
00752 int DrawStationCoverageAreaText(int sx, int sy, StationCoverageType sct, int rad, bool supplies)
00753 {
00754   TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
00755   AcceptedCargo cargo;
00756   if (tile < MapSize()) {
00757     if (supplies) {
00758       GetProductionAroundTiles(cargo, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad);
00759     } else {
00760       GetAcceptanceAroundTiles(cargo, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad);
00761     }
00762     return sy + DrawStationCoverageText(cargo, sx, sy, sct, supplies);
00763   }
00764 
00765   return sy;
00766 }
00767 
00768 void CheckRedrawStationCoverage(const Window *w)
00769 {
00770   if (_thd.dirty & 1) {
00771     _thd.dirty &= ~1;
00772     SetWindowDirty(w);
00773   }
00774 }
00775 
00776 /* Delete a character at the caret position in a text buf.
00777  * If backspace is set, delete the character before the caret,
00778  * else delete the character after it. */
00779 static void DelChar(Textbuf *tb, bool backspace)
00780 {
00781   WChar c;
00782   char *s = tb->buf + tb->caretpos;
00783 
00784   if (backspace) s = Utf8PrevChar(s);
00785 
00786   uint16 len = (uint16)Utf8Decode(&c, s);
00787   uint width = GetCharacterWidth(FS_NORMAL, c);
00788 
00789   tb->width  -= width;
00790   if (backspace) {
00791     tb->caretpos   -= len;
00792     tb->caretxoffs -= width;
00793   }
00794 
00795   /* Move the remaining characters over the marker */
00796   memmove(s, s + len, tb->size - (s - tb->buf) - len);
00797   tb->size -= len;
00798 }
00799 
00807 bool DeleteTextBufferChar(Textbuf *tb, int delmode)
00808 {
00809   if (delmode == WKC_BACKSPACE && tb->caretpos != 0) {
00810     DelChar(tb, true);
00811     return true;
00812   } else if (delmode == WKC_DELETE && tb->caretpos < tb->size - 1) {
00813     DelChar(tb, false);
00814     return true;
00815   }
00816 
00817   return false;
00818 }
00819 
00824 void DeleteTextBufferAll(Textbuf *tb)
00825 {
00826   memset(tb->buf, 0, tb->maxsize);
00827   tb->size = 1;
00828   tb->width = tb->caretpos = tb->caretxoffs = 0;
00829 }
00830 
00839 bool InsertTextBufferChar(Textbuf *tb, WChar key)
00840 {
00841   const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
00842   uint16 len = (uint16)Utf8CharLen(key);
00843   if (tb->size + len <= tb->maxsize && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) {
00844     memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->size - tb->caretpos);
00845     Utf8Encode(tb->buf + tb->caretpos, key);
00846     tb->size  += len;
00847     tb->width += charwidth;
00848 
00849     tb->caretpos   += len;
00850     tb->caretxoffs += charwidth;
00851     return true;
00852   }
00853   return false;
00854 }
00855 
00863 bool MoveTextBufferPos(Textbuf *tb, int navmode)
00864 {
00865   switch (navmode) {
00866     case WKC_LEFT:
00867       if (tb->caretpos != 0) {
00868         WChar c;
00869         const char *s = Utf8PrevChar(tb->buf + tb->caretpos);
00870         Utf8Decode(&c, s);
00871         tb->caretpos    = s - tb->buf; // -= (tb->buf + tb->caretpos - s)
00872         tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
00873 
00874         return true;
00875       }
00876       break;
00877 
00878     case WKC_RIGHT:
00879       if (tb->caretpos < tb->size - 1) {
00880         WChar c;
00881 
00882         tb->caretpos   += (uint16)Utf8Decode(&c, tb->buf + tb->caretpos);
00883         tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
00884 
00885         return true;
00886       }
00887       break;
00888 
00889     case WKC_HOME:
00890       tb->caretpos = 0;
00891       tb->caretxoffs = 0;
00892       return true;
00893 
00894     case WKC_END:
00895       tb->caretpos = tb->size - 1;
00896       tb->caretxoffs = tb->width;
00897       return true;
00898 
00899     default:
00900       break;
00901   }
00902 
00903   return false;
00904 }
00905 
00915 void InitializeTextBuffer(Textbuf *tb, char *buf, uint16 maxsize, uint16 maxwidth)
00916 {
00917   assert(maxsize != 0);
00918 
00919   tb->buf      = buf;
00920   tb->maxsize  = maxsize;
00921   tb->maxwidth = maxwidth;
00922   tb->caret    = true;
00923   UpdateTextBufferSize(tb);
00924 }
00925 
00932 void UpdateTextBufferSize(Textbuf *tb)
00933 {
00934   const char *buf = tb->buf;
00935 
00936   tb->width = 0;
00937   tb->size = 1; // terminating zero
00938 
00939   WChar c;
00940   while ((c = Utf8Consume(&buf)) != '\0') {
00941     tb->width += GetCharacterWidth(FS_NORMAL, c);
00942     tb->size += Utf8CharLen(c);
00943   }
00944 
00945   assert(tb->size <= tb->maxsize);
00946 
00947   tb->caretpos = tb->size - 1;
00948   tb->caretxoffs = tb->width;
00949 }
00950 
00951 bool HandleCaret(Textbuf *tb)
00952 {
00953   /* caret changed? */
00954   bool b = !!(_caret_timer & 0x20);
00955 
00956   if (b != tb->caret) {
00957     tb->caret = b;
00958     return true;
00959   }
00960   return false;
00961 }
00962 
00963 HandleEditBoxResult QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, Window::EventState &state)
00964 {
00965   state = Window::ES_HANDLED;
00966 
00967   switch (keycode) {
00968     case WKC_ESC: return HEBR_CANCEL;
00969 
00970     case WKC_RETURN: case WKC_NUM_ENTER: return HEBR_CONFIRM;
00971 
00972     case (WKC_CTRL | 'V'):
00973       if (InsertTextBufferClipboard(&this->text)) w->InvalidateWidget(wid);
00974       break;
00975 
00976     case (WKC_CTRL | 'U'):
00977       DeleteTextBufferAll(&this->text);
00978       w->InvalidateWidget(wid);
00979       break;
00980 
00981     case WKC_BACKSPACE: case WKC_DELETE:
00982       if (DeleteTextBufferChar(&this->text, keycode)) w->InvalidateWidget(wid);
00983       break;
00984 
00985     case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
00986       if (MoveTextBufferPos(&this->text, keycode)) w->InvalidateWidget(wid);
00987       break;
00988 
00989     default:
00990       if (IsValidChar(key, this->afilter)) {
00991         if (InsertTextBufferChar(&this->text, key)) w->InvalidateWidget(wid);
00992       } else { // key wasn't caught. Continue only if standard entry specified
00993         state = (this->afilter == CS_ALPHANUMERAL) ? Window::ES_HANDLED : Window::ES_NOT_HANDLED;
00994       }
00995   }
00996 
00997   return HEBR_EDITING;
00998 }
00999 
01000 void QueryString::HandleEditBox(Window *w, int wid)
01001 {
01002   if (HandleCaret(&this->text)) w->InvalidateWidget(wid);
01003 }
01004 
01005 void QueryString::DrawEditBox(Window *w, int wid)
01006 {
01007   const Widget *wi = &w->widget[wid];
01008 
01009   assert((wi->type & WWT_MASK) == WWT_EDITBOX);
01010 
01011   GfxFillRect(wi->left + 1, wi->top + 1, wi->right - 1, wi->bottom - 1, 215);
01012 
01013   DrawPixelInfo dpi;
01014   int delta;
01015 
01016   /* Limit the drawing of the string inside the widget boundaries */
01017   if (!FillDrawPixelInfo(&dpi,
01018       wi->left + 4,
01019       wi->top + 1,
01020       wi->right - wi->left - 4,
01021       wi->bottom - wi->top - 1)) {
01022     return;
01023   }
01024 
01025   DrawPixelInfo *old_dpi = _cur_dpi;
01026   _cur_dpi = &dpi;
01027 
01028   /* We will take the current widget length as maximum width, with a small
01029    * space reserved at the end for the caret to show */
01030   const Textbuf *tb = &this->text;
01031 
01032   delta = (wi->right - wi->left) - tb->width - 10;
01033   if (delta > 0) delta = 0;
01034 
01035   if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
01036 
01037   DoDrawString(tb->buf, delta, 0, TC_YELLOW);
01038   if (tb->caret) DoDrawString("_", tb->caretxoffs + delta, 0, TC_WHITE);
01039 
01040   _cur_dpi = old_dpi;
01041 }
01042 
01043 int QueryStringBaseWindow::HandleEditBoxKey(int wid, uint16 key, uint16 keycode, EventState &state)
01044 {
01045   return this->QueryString::HandleEditBoxKey(this, wid, key, keycode, state);
01046 }
01047 
01048 void QueryStringBaseWindow::HandleEditBox(int wid)
01049 {
01050   this->QueryString::HandleEditBox(this, wid);
01051 }
01052 
01053 <