# newgrf_debug_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: newgrf_debug_gui.cpp 25946 2013-11-07 18:17:21Z frosch $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include <stdarg.h>
    00014 #include "window_gui.h"
    00015 #include "window_func.h"
    00016 #include "fileio_func.h"
    00017 #include "spritecache.h"
    00018 #include "string_func.h"
    00019 #include "strings_func.h"
    00020 #include "textbuf_gui.h"
    00021 #include "vehicle_gui.h"
    00022
    00023 #include "engine_base.h"
    00024 #include "industry.h"
    00025 #include "object_base.h"
    00026 #include "station_base.h"
    00027 #include "town.h"
    00028 #include "vehicle_base.h"
    00029 #include "train.h"
    00030 #include "roadveh.h"
    00031
    00032 #include "newgrf_airporttiles.h"
    00033 #include "newgrf_debug.h"
    00034 #include "newgrf_object.h"
    00035 #include "newgrf_spritegroup.h"
    00036 #include "newgrf_station.h"
    00037 #include "newgrf_town.h"
    00038 #include "newgrf_railtype.h"
    00039 #include "newgrf_industries.h"
    00040 #include "newgrf_industrytiles.h"
    00041
    00042 #include "widgets/newgrf_debug_widget.h"
    00043
    00044 #include "table/strings.h"
    00045
    00047 NewGrfDebugSpritePicker _newgrf_debug_sprite_picker = { SPM_NONE, NULL, 0, SmallVector<SpriteID, 256>() };
    00048
    00054 static inline uint GetFeatureIndex(uint window_number)
    00055 {
    00056   return GB(window_number, 0, 24);
    00057 }
    00058
    00066 static inline uint GetInspectWindowNumber(GrfSpecFeature feature, uint index)
    00067 {
    00068   assert((index >> 24) == 0);
    00069   return (feature << 24) | index;
    00070 }
    00071
    00076 enum NIType {
    00077   NIT_INT,
    00078   NIT_CARGO,
    00079 };
    00080
    00082 struct NIProperty {
    00083   const char *name;
    00084   ptrdiff_t offset;
    00085   byte read_size;
    00086   byte prop;
    00087   byte type;
    00088 };
    00089
    00090
    00095 struct NICallback {
    00096   const char *name;
    00097   ptrdiff_t offset;
    00098   byte read_size;
    00099   byte cb_bit;
    00100   uint16 cb_id;
    00101 };
    00103 static const int CBM_NO_BIT = UINT8_MAX;
    00104
    00106 struct NIVariable {
    00107   const char *name;
    00108   byte var;
    00109 };
    00110
    00112 class NIHelper {
    00113 public:
    00115   virtual ~NIHelper() {}
    00116
    00122   virtual bool IsInspectable(uint index) const = 0;
    00123
    00129   virtual uint GetParent(uint index) const = 0;
    00130
    00136   virtual const void *GetInstance(uint index) const = 0;
    00137
    00143   virtual const void *GetSpec(uint index) const = 0;
    00144
    00149   virtual void SetStringParameters(uint index) const = 0;
    00150
    00156   virtual uint32 GetGRFID(uint index) const = 0;
    00157
    00166   virtual uint Resolve(uint index, uint var, uint param, bool *avail) const = 0;
    00167
    00172   virtual bool PSAWithParameter() const
    00173   {
    00174     return false;
    00175   }
    00176
    00183   virtual uint GetPSASize(uint index, uint32 grfid) const
    00184   {
    00185     return 0;
    00186   }
    00187
    00194   virtual const int32 *GetPSAFirstPosition(uint index, uint32 grfid) const
    00195   {
    00196     return NULL;
    00197   }
    00198
    00199 protected:
    00205   void SetSimpleStringParameters(StringID string, uint32 index) const
    00206   {
    00207     SetDParam(0, string);
    00208     SetDParam(1, index);
    00209   }
    00210
    00211
    00218   void SetObjectAtStringParameters(StringID string, uint32 index, TileIndex tile) const
    00219   {
    00220     SetDParam(0, STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT);
    00221     SetDParam(1, string);
    00222     SetDParam(2, index);
    00223     SetDParam(3, tile);
    00224   }
    00225 };
    00226
    00227
    00229 struct NIFeature {
    00230   const NIProperty *properties;
    00231   const NICallback *callbacks;
    00232   const NIVariable *variables;
    00233   const NIHelper   *helper;
    00234 };
    00235
    00236 /* Load all the NewGRF debug data; externalised as it is just a huge bunch of tables. */
    00237 #include "table/newgrf_debug_data.h"
    00238
    00244 static inline GrfSpecFeature GetFeatureNum(uint window_number)
    00245 {
    00246   return (GrfSpecFeature)GB(window_number, 24, 8);
    00247 }
    00248
    00254 static inline const NIFeature *GetFeature(uint window_number)
    00255 {
    00256   GrfSpecFeature idx = GetFeatureNum(window_number);
    00257   return idx < GSF_FAKE_END ? _nifeatures[idx] : NULL;
    00258 }
    00259
    00266 static inline const NIHelper *GetFeatureHelper(uint window_number)
    00267 {
    00268   return GetFeature(window_number)->helper;
    00269 }
    00270
    00272 struct NewGRFInspectWindow : Window {
    00273   static const int LEFT_OFFSET   = 5;
    00274   static const int RIGHT_OFFSET  = 5;
    00275   static const int TOP_OFFSET    = 5;
    00276   static const int BOTTOM_OFFSET = 5;
    00277
    00279   static uint32 var60params[GSF_FAKE_END][0x20];
    00280
    00282   uint32 caller_grfid;
    00283
    00285   uint chain_index;
    00286
    00288   byte current_edit_param;
    00289
    00290   Scrollbar *vscroll;
    00291
    00297   static bool HasVariableParameter(uint variable)
    00298   {
    00299     return IsInsideBS(variable, 0x60, 0x20);
    00300   }
    00301
    00306   void SetCallerGRFID(uint32 grfid)
    00307   {
    00308     this->caller_grfid = grfid;
    00309     this->SetDirty();
    00310   }
    00311
    00315   bool HasChainIndex() const
    00316   {
    00317     GrfSpecFeature f = GetFeatureNum(this->window_number);
    00318     return f == GSF_TRAINS || f == GSF_ROADVEHICLES;
    00319   }
    00320
    00325   uint GetFeatureIndex() const
    00326   {
    00327     uint index = ::GetFeatureIndex(this->window_number);
    00328     if (this->chain_index > 0) {
    00329       assert(this->HasChainIndex());
    00330       const Vehicle *v = Vehicle::Get(index);
    00331       v = v->Move(this->chain_index);
    00332       if (v != NULL) index = v->index;
    00333     }
    00334     return index;
    00335   }
    00336
    00340   void ValidateChainIndex()
    00341   {
    00342     if (this->chain_index == 0) return;
    00343
    00344     assert(this->HasChainIndex());
    00345
    00346     const Vehicle *v = Vehicle::Get(::GetFeatureIndex(this->window_number));
    00347     v = v->Move(this->chain_index);
    00348     if (v == NULL) this->chain_index = 0;
    00349   }
    00350
    00351   NewGRFInspectWindow(WindowDesc *desc, WindowNumber wno) : Window(desc)
    00352   {
    00353     this->CreateNestedTree();
    00354     this->vscroll = this->GetScrollbar(WID_NGRFI_SCROLLBAR);
    00355     this->FinishInitNested(wno);
    00356
    00357     this->vscroll->SetCount(0);
    00358     this->SetWidgetDisabledState(WID_NGRFI_PARENT, GetFeatureHelper(this->window_number)->GetParent(this->GetFeatureIndex()) == UINT32_MAX);
    00359
    00360     this->OnInvalidateData(0, true);
    00361   }
    00362
    00363   virtual void SetStringParameters(int widget) const
    00364   {
    00365     if (widget != WID_NGRFI_CAPTION) return;
    00366
    00367     GetFeatureHelper(this->window_number)->SetStringParameters(this->GetFeatureIndex());
    00368   }
    00369
    00370   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00371   {
    00372     switch (widget) {
    00373       case WID_NGRFI_VEH_CHAIN: {
    00374         assert(this->HasChainIndex());
    00375         GrfSpecFeature f = GetFeatureNum(this->window_number);
    00376         size->height = max(size->height, GetVehicleImageCellSize((VehicleType)(VEH_TRAIN + (f - GSF_TRAINS)), EIT_IN_DEPOT).height + 2 + WD_BEVEL_TOP + WD_BEVEL_BOTTOM);
    00377         break;
    00378       }
    00379
    00380       case WID_NGRFI_MAINPANEL:
    00381         resize->height = max(11, FONT_HEIGHT_NORMAL + 1);
    00382         resize->width  = 1;
    00383
    00384         size->height = 5 * resize->height + TOP_OFFSET + BOTTOM_OFFSET;
    00385         break;
    00386     }
    00387   }
    00388
    00395   void WARN_FORMAT(4, 5) DrawString(const Rect &r, int offset, const char *format, ...) const
    00396   {
    00397     char buf[1024];
    00398
    00399     va_list va;
    00400     va_start(va, format);
    00401     vsnprintf(buf, lengthof(buf), format, va);
    00402     va_end(va);
    00403
    00404     offset -= this->vscroll->GetPosition();
    00405     if (offset < 0 || offset >= this->vscroll->GetCapacity()) return;
    00406
    00407 		::DrawString(r.left + LEFT_OFFSET, r.right - RIGHT_OFFSET, r.top + TOP_OFFSET + (offset * this->resize.step_height), buf, TC_BLACK);
    00408   }
    00409
    00410   virtual void DrawWidget(const Rect &r, int widget) const
    00411   {
    00412     switch (widget) {
    00413       case WID_NGRFI_VEH_CHAIN: {
    00414         const Vehicle *v = Vehicle::Get(this->GetFeatureIndex());
    00415         int total_width = 0;
    00416         int sel_start = 0;
    00417         int sel_end = 0;
    00418         for (const Vehicle *u = v->First(); u != NULL; u = u->Next()) {
    00419           if (u == v) sel_start = total_width;
    00420           switch (u->type) {
    00421             case VEH_TRAIN: total_width += Train      ::From(u)->GetDisplayImageWidth(); break;
    00422             case VEH_ROAD:  total_width += RoadVehicle::From(u)->GetDisplayImageWidth(); break;
    00423             default: NOT_REACHED();
    00424           }
    00425           if (u == v) sel_end = total_width;
    00426         }
    00427
    00428         int width = r.right + 1 - r.left - WD_BEVEL_LEFT - WD_BEVEL_RIGHT;
    00429         int skip = 0;
    00430         if (total_width > width) {
    00431           int sel_center = (sel_start + sel_end) / 2;
    00432           if (sel_center > width / 2) skip = min(total_width - width, sel_center - width / 2);
    00433         }
    00434
    00435         GrfSpecFeature f = GetFeatureNum(this->window_number);
    00436         int h = GetVehicleImageCellSize((VehicleType)(VEH_TRAIN + (f - GSF_TRAINS)), EIT_IN_DEPOT).height;
    00437         int y = (r.top + r.bottom - h) / 2;
    00438         DrawVehicleImage(v->First(), r.left + WD_BEVEL_LEFT, r.right - WD_BEVEL_RIGHT, y + 1, INVALID_VEHICLE, EIT_IN_DETAILS, skip);
    00439
    00440         /* Highlight the articulated part (this is different to the whole-vehicle highlighting of DrawVehicleImage */
    00441         if (_current_text_dir == TD_RTL) {
    00442           DrawFrameRect(r.right - sel_end   + skip, y, r.right - sel_start + skip, y + h, COLOUR_WHITE, FR_BORDERONLY);
    00443         } else {
    00444           DrawFrameRect(r.left  + sel_start - skip, y, r.left  + sel_end   - skip, y + h, COLOUR_WHITE, FR_BORDERONLY);
    00445         }
    00446         break;
    00447       }
    00448     }
    00449
    00450     if (widget != WID_NGRFI_MAINPANEL) return;
    00451
    00452     uint index = this->GetFeatureIndex();
    00453     const NIFeature *nif  = GetFeature(this->window_number);
    00454     const NIHelper *nih   = nif->helper;
    00455     const void *base      = nih->GetInstance(index);
    00456     const void *base_spec = nih->GetSpec(index);
    00457
    00458     uint i = 0;
    00459     if (nif->variables != NULL) {
    00460       this->DrawString(r, i++, "Variables:");
    00461       for (const NIVariable *niv = nif->variables; niv->name != NULL; niv++) {
    00462         bool avail = true;
    00463         uint param = HasVariableParameter(niv->var) ? NewGRFInspectWindow::var60params[GetFeatureNum(this->window_number)][niv->var - 0x60] : 0;
    00464         uint value = nih->Resolve(index, niv->var, param, &avail);
    00465
    00466         if (!avail) continue;
    00467
    00468         if (HasVariableParameter(niv->var)) {
    00469           this->DrawString(r, i++, "  %02x[%02x]: %08x (%s)", niv->var, param, value, niv->name);
    00470         } else {
    00471           this->DrawString(r, i++, "  %02x: %08x (%s)", niv->var, value, niv->name);
    00472         }
    00473       }
    00474     }
    00475
    00476     uint psa_size = nih->GetPSASize(index, this->caller_grfid);
    00477     const int32 *psa = nih->GetPSAFirstPosition(index, this->caller_grfid);
    00478     if (psa_size != 0 && psa != NULL) {
    00479       if (nih->PSAWithParameter()) {
    00480         this->DrawString(r, i++, "Persistent storage [%08X]:", BSWAP32(this->caller_grfid));
    00481       } else {
    00482         this->DrawString(r, i++, "Persistent storage:");
    00483       }
    00484       assert(psa_size % 4 == 0);
    00485       for (uint j = 0; j < psa_size; j += 4, psa += 4) {
    00486         this->DrawString(r, i++, "  %i: %i %i %i %i", j, psa[0], psa[1], psa[2], psa[3]);
    00487       }
    00488     }
    00489
    00490     if (nif->properties != NULL) {
    00491       this->DrawString(r, i++, "Properties:");
    00492       for (const NIProperty *nip = nif->properties; nip->name != NULL; nip++) {
    00493         const void *ptr = (const byte *)base + nip->offset;
    00494         uint value;
    00495         switch (nip->read_size) {
    00496           case 1: value = *(const uint8  *)ptr; break;
    00497           case 2: value = *(const uint16 *)ptr; break;
    00498           case 4: value = *(const uint32 *)ptr; break;
    00499           default: NOT_REACHED();
    00500         }
    00501
    00502         StringID string;
    00503         SetDParam(0, value);
    00504         switch (nip->type) {
    00505           case NIT_INT:
    00506             string = STR_JUST_INT;
    00507             break;
    00508
    00509           case NIT_CARGO:
    00510             string = value != INVALID_CARGO ? CargoSpec::Get(value)->name : STR_QUANTITY_N_A;
    00511             break;
    00512
    00513           default:
    00514             NOT_REACHED();
    00515         }
    00516
    00517         char buffer[64];
    00518         GetString(buffer, string, lastof(buffer));
    00519         this->DrawString(r, i++, "  %02x: %s (%s)", nip->prop, buffer, nip->name);
    00520       }
    00521     }
    00522
    00523     if (nif->callbacks != NULL) {
    00524       this->DrawString(r, i++, "Callbacks:");
    00525       for (const NICallback *nic = nif->callbacks; nic->name != NULL; nic++) {
    00526         if (nic->cb_bit != CBM_NO_BIT) {
    00527           const void *ptr = (const byte *)base_spec + nic->offset;
    00528           uint value;
    00529           switch (nic->read_size) {
    00530             case 1: value = *(const uint8  *)ptr; break;
    00531             case 2: value = *(const uint16 *)ptr; break;
    00532             case 4: value = *(const uint32 *)ptr; break;
    00533             default: NOT_REACHED();
    00534           }
    00535
    00536           if (!HasBit(value, nic->cb_bit)) continue;
    00537           this->DrawString(r, i++, "  %03x: %s", nic->cb_id, nic->name);
    00538         } else {
    00539           this->DrawString(r, i++, "  %03x: %s (unmasked)", nic->cb_id, nic->name);
    00540         }
    00541       }
    00542     }
    00543
    00544     /* Not nice and certainly a hack, but it beats duplicating
    00545      * this whole function just to count the actual number of
    00546      * elements. Especially because they need to be redrawn. */
    00547     const_cast<NewGRFInspectWindow*>(this)->vscroll->SetCount(i);
    00548   }
    00549
    00550   virtual void OnClick(Point pt, int widget, int click_count)
    00551   {
    00552     switch (widget) {
    00553       case WID_NGRFI_PARENT: {
    00554         const NIHelper *nih   = GetFeatureHelper(this->window_number);
    00555         uint index = nih->GetParent(this->GetFeatureIndex());
    00556 				::ShowNewGRFInspectWindow(GetFeatureNum(index), ::GetFeatureIndex(index), nih->GetGRFID(this->GetFeatureIndex()));
    00557         break;
    00558       }
    00559
    00560       case WID_NGRFI_VEH_PREV:
    00561         if (this->chain_index > 0) {
    00562           this->chain_index--;
    00563           this->InvalidateData();
    00564         }
    00565         break;
    00566
    00567       case WID_NGRFI_VEH_NEXT:
    00568         if (this->HasChainIndex()) {
    00569           uint index = this->GetFeatureIndex();
    00570           Vehicle *v = Vehicle::Get(index);
    00571           if (v != NULL && v->Next() != NULL) {
    00572             this->chain_index++;
    00573             this->InvalidateData();
    00574           }
    00575         }
    00576         break;
    00577
    00578       case WID_NGRFI_MAINPANEL: {
    00579         /* Does this feature have variables? */
    00580         const NIFeature *nif  = GetFeature(this->window_number);
    00581         if (nif->variables == NULL) return;
    00582
    00583         /* Get the line, make sure it's within the boundaries. */
    00584         int line = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NGRFI_MAINPANEL, TOP_OFFSET);
    00585         if (line == INT_MAX) return;
    00586
    00587         /* Find the variable related to the line */
    00588         for (const NIVariable *niv = nif->variables; niv->name != NULL; niv++, line--) {
    00589           if (line != 1) continue; // 1 because of the "Variables:" line
    00590
    00591           if (!HasVariableParameter(niv->var)) break;
    00592
    00593           this->current_edit_param = niv->var;
    00594           ShowQueryString(STR_EMPTY, STR_NEWGRF_INSPECT_QUERY_CAPTION, 9, this, CS_HEXADECIMAL, QSF_NONE);
    00595         }
    00596       }
    00597     }
    00598   }
    00599
    00600   virtual void OnQueryTextFinished(char *str)
    00601   {
    00602     if (StrEmpty(str)) return;
    00603
    00604     NewGRFInspectWindow::var60params[GetFeatureNum(this->window_number)][this->current_edit_param - 0x60] = strtol(str, NULL, 16);
    00605     this->SetDirty();
    00606   }
    00607
    00608   virtual void OnResize()
    00609   {
    00610     this->vscroll->SetCapacityFromWidget(this, WID_NGRFI_MAINPANEL, TOP_OFFSET + BOTTOM_OFFSET);
    00611   }
    00612
    00618   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00619   {
    00620     if (!gui_scope) return;
    00621     if (this->HasChainIndex()) {
    00622       this->ValidateChainIndex();
    00623       this->SetWidgetDisabledState(WID_NGRFI_VEH_PREV, this->chain_index == 0);
    00624       Vehicle *v = Vehicle::Get(this->GetFeatureIndex());
    00625       this->SetWidgetDisabledState(WID_NGRFI_VEH_NEXT, v == NULL || v->Next() == NULL);
    00626     }
    00627   }
    00628 };
    00629
    00630 /* static */ uint32 NewGRFInspectWindow::var60params[GSF_FAKE_END][0x20] = { {0} }; // Use spec to have 0s in whole array
    00631
    00632 static const NWidgetPart _nested_newgrf_inspect_chain_widgets[] = {
    00633   NWidget(NWID_HORIZONTAL),
    00634     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00635     NWidget(WWT_CAPTION, COLOUR_GREY, WID_NGRFI_CAPTION), SetDataTip(STR_NEWGRF_INSPECT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00636     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00637     NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
    00638     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00639   EndContainer(),
    00640   NWidget(WWT_PANEL, COLOUR_GREY),
    00641     NWidget(NWID_HORIZONTAL),
    00642       NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_NGRFI_VEH_PREV), SetDataTip(AWV_DECREASE, STR_NULL),
    00643       NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_NGRFI_VEH_NEXT), SetDataTip(AWV_INCREASE, STR_NULL),
    00644       NWidget(WWT_EMPTY, COLOUR_GREY, WID_NGRFI_VEH_CHAIN), SetFill(1, 0), SetResize(1, 0),
    00645     EndContainer(),
    00646   EndContainer(),
    00647   NWidget(NWID_HORIZONTAL),
    00648     NWidget(WWT_PANEL, COLOUR_GREY, WID_NGRFI_MAINPANEL), SetMinimalSize(300, 0), SetScrollbar(WID_NGRFI_SCROLLBAR), EndContainer(),
    00649     NWidget(NWID_VERTICAL),
    00650       NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_NGRFI_SCROLLBAR),
    00651       NWidget(WWT_RESIZEBOX, COLOUR_GREY),
    00652     EndContainer(),
    00653   EndContainer(),
    00654 };
    00655
    00656 static const NWidgetPart _nested_newgrf_inspect_widgets[] = {
    00657   NWidget(NWID_HORIZONTAL),
    00658     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00659     NWidget(WWT_CAPTION, COLOUR_GREY, WID_NGRFI_CAPTION), SetDataTip(STR_NEWGRF_INSPECT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00660     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NGRFI_PARENT), SetDataTip(STR_NEWGRF_INSPECT_PARENT_BUTTON, STR_NEWGRF_INSPECT_PARENT_TOOLTIP),
    00661     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00662     NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
    00663     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00664   EndContainer(),
    00665   NWidget(NWID_HORIZONTAL),
    00666     NWidget(WWT_PANEL, COLOUR_GREY, WID_NGRFI_MAINPANEL), SetMinimalSize(300, 0), SetScrollbar(WID_NGRFI_SCROLLBAR), EndContainer(),
    00667     NWidget(NWID_VERTICAL),
    00668       NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_NGRFI_SCROLLBAR),
    00669       NWidget(WWT_RESIZEBOX, COLOUR_GREY),
    00670     EndContainer(),
    00671   EndContainer(),
    00672 };
    00673
    00674 static WindowDesc _newgrf_inspect_chain_desc(
    00675   WDP_AUTO, "newgrf_inspect_chain", 400, 300,
    00676   WC_NEWGRF_INSPECT, WC_NONE,
    00677   0,
    00678   _nested_newgrf_inspect_chain_widgets, lengthof(_nested_newgrf_inspect_chain_widgets)
    00679 );
    00680
    00681 static WindowDesc _newgrf_inspect_desc(
    00682   WDP_AUTO, "newgrf_inspect", 400, 300,
    00683   WC_NEWGRF_INSPECT, WC_NONE,
    00684   0,
    00685   _nested_newgrf_inspect_widgets, lengthof(_nested_newgrf_inspect_widgets)
    00686 );
    00687
    00697 void ShowNewGRFInspectWindow(GrfSpecFeature feature, uint index, const uint32 grfid)
    00698 {
    00699   if (!IsNewGRFInspectable(feature, index)) return;
    00700
    00701   WindowNumber wno = GetInspectWindowNumber(feature, index);
    00702   NewGRFInspectWindow *w = AllocateWindowDescFront<NewGRFInspectWindow>(feature == GSF_TRAINS || feature == GSF_ROADVEHICLES ? &_newgrf_inspect_chain_desc : &_newgrf_inspect_desc, wno);
    00703   if (w == NULL) w = (NewGRFInspectWindow *)FindWindowById(WC_NEWGRF_INSPECT, wno);
    00704   w->SetCallerGRFID(grfid);
    00705 }
    00706
    00715 void InvalidateNewGRFInspectWindow(GrfSpecFeature feature, uint index)
    00716 {
    00717   if (feature == GSF_INVALID) return;
    00718
    00719   WindowNumber wno = GetInspectWindowNumber(feature, index);
    00720   InvalidateWindowData(WC_NEWGRF_INSPECT, wno);
    00721 }
    00722
    00731 void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
    00732 {
    00733   if (feature == GSF_INVALID) return;
    00734
    00735   WindowNumber wno = GetInspectWindowNumber(feature, index);
    00736   DeleteWindowById(WC_NEWGRF_INSPECT, wno);
    00737
    00738   /* Reinitialise the land information window to remove the "debug" sprite if needed.
    00739    * Note: Since we might be called from a command here, it is important to not execute
    00740    * the invalidation immediately. The landinfo window tests commands itself. */
    00741   InvalidateWindowData(WC_LAND_INFO, 0, 1);
    00742 }
    00743
    00753 bool IsNewGRFInspectable(GrfSpecFeature feature, uint index)
    00754 {
    00755   const NIFeature *nif = GetFeature(GetInspectWindowNumber(feature, index));
    00756   if (nif == NULL) return false;
    00757   return nif->helper->IsInspectable(index);
    00758 }
    00759
    00765 GrfSpecFeature GetGrfSpecFeature(TileIndex tile)
    00766 {
    00767   switch (GetTileType(tile)) {
    00768     default:              return GSF_INVALID;
    00769     case MP_RAILWAY:      return GSF_RAILTYPES;
    00770     case MP_ROAD:         return IsLevelCrossing(tile) ? GSF_RAILTYPES : GSF_INVALID;
    00771     case MP_HOUSE:        return GSF_HOUSES;
    00772     case MP_INDUSTRY:     return GSF_INDUSTRYTILES;
    00773     case MP_OBJECT:       return GSF_OBJECTS;
    00774
    00775     case MP_STATION:
    00776       switch (GetStationType(tile)) {
    00777         case STATION_RAIL:    return GSF_STATIONS;
    00778         case STATION_AIRPORT: return GSF_AIRPORTTILES;
    00779         default:              return GSF_INVALID;
    00780       }
    00781   }
    00782 }
    00783
    00789 GrfSpecFeature GetGrfSpecFeature(VehicleType type)
    00790 {
    00791   switch (type) {
    00792     case VEH_TRAIN:    return GSF_TRAINS;
    00793     case VEH_ROAD:     return GSF_ROADVEHICLES;
    00794     case VEH_SHIP:     return GSF_SHIPS;
    00795     case VEH_AIRCRAFT: return GSF_AIRCRAFT;
    00796     default:           return GSF_INVALID;
    00797   }
    00798 }
    00799
    00800
    00801
    00802 /**** Sprite Aligner ****/
    00803
    00805 struct SpriteAlignerWindow : Window {
    00806   SpriteID current_sprite;
    00807   Scrollbar *vscroll;
    00808
    00809   SpriteAlignerWindow(WindowDesc *desc, WindowNumber wno) : Window(desc)
    00810   {
    00811     this->CreateNestedTree();
    00812     this->vscroll = this->GetScrollbar(WID_SA_SCROLLBAR);
    00813     this->FinishInitNested(wno);
    00814
    00815     /* Oh yes, we assume there is at least one normal sprite! */
    00816     while (GetSpriteType(this->current_sprite) != ST_NORMAL) this->current_sprite++;
    00817   }
    00818
    00819   virtual void SetStringParameters(int widget) const
    00820   {
    00821     switch (widget) {
    00822       case WID_SA_CAPTION:
    00823         SetDParam(0, this->current_sprite);
    00824         SetDParamStr(1, FioGetFilename(GetOriginFileSlot(this->current_sprite)));
    00825         break;
    00826
    00827       case WID_SA_OFFSETS: {
    00828         const Sprite *spr = GetSprite(this->current_sprite, ST_NORMAL);
    00829         SetDParam(0, spr->x_offs / ZOOM_LVL_BASE);
    00830         SetDParam(1, spr->y_offs / ZOOM_LVL_BASE);
    00831         break;
    00832       }
    00833
    00834       default:
    00835         break;
    00836     }
    00837   }
    00838
    00839   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00840   {
    00841     if (widget != WID_SA_LIST) return;
    00842
    00843     resize->height = max(11, FONT_HEIGHT_NORMAL + 1);
    00844     resize->width  = 1;
    00845
    00846     /* Resize to about 200 pixels (for the preview) */
    00847     size->height = (1 + 200 / resize->height) * resize->height;
    00848   }
    00849
    00850   virtual void DrawWidget(const Rect &r, int widget) const
    00851   {
    00852     switch (widget) {
    00853       case WID_SA_SPRITE: {
    00854         /* Center the sprite ourselves */
    00855         const Sprite *spr = GetSprite(this->current_sprite, ST_NORMAL);
    00856         int width  = r.right  - r.left + 1;
    00857         int height = r.bottom - r.top  + 1;
    00858         int x = r.left - spr->x_offs / ZOOM_LVL_BASE + (width  - spr->width / ZOOM_LVL_BASE) / 2;
    00859         int y = r.top  - spr->y_offs / ZOOM_LVL_BASE + (height - spr->height / ZOOM_LVL_BASE) / 2;
    00860
    00861         /* And draw only the part within the sprite area */
    00862         SubSprite subspr = {
    00863           spr->x_offs + (spr->width  - width  * ZOOM_LVL_BASE) / 2 + 1,
    00864           spr->y_offs + (spr->height - height * ZOOM_LVL_BASE) / 2 + 1,
    00865           spr->x_offs + (spr->width  + width  * ZOOM_LVL_BASE) / 2 - 1,
    00866           spr->y_offs + (spr->height + height * ZOOM_LVL_BASE) / 2 - 1,
    00867         };
    00868
    00869         DrawSprite(this->current_sprite, PAL_NONE, x, y, &subspr, ZOOM_LVL_GUI);
    00870         break;
    00871       }
    00872
    00873       case WID_SA_LIST: {
    00874         const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget);
    00875         int step_size = nwid->resize_y;
    00876
    00877         SmallVector<SpriteID, 256> &list = _newgrf_debug_sprite_picker.sprites;
    00878         int max = min<int>(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), list.Length());
    00879
    00880         int y = r.top + WD_FRAMERECT_TOP;
    00881         for (int i = this->vscroll->GetPosition(); i < max; i++) {
    00882           SetDParam(0, list[i]);
    00883           DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_BLACK_COMMA, TC_FROMSTRING, SA_RIGHT | SA_FORCE);
    00884           y += step_size;
    00885         }
    00886         break;
    00887       }
    00888     }
    00889   }
    00890
    00891   virtual void OnClick(Point pt, int widget, int click_count)
    00892   {
    00893     switch (widget) {
    00894       case WID_SA_PREVIOUS:
    00895         do {
    00896           this->current_sprite = (this->current_sprite == 0 ? GetMaxSpriteID() :  this->current_sprite) - 1;
    00897         } while (GetSpriteType(this->current_sprite) != ST_NORMAL);
    00898         this->SetDirty();
    00899         break;
    00900
    00901       case WID_SA_GOTO:
    00902         ShowQueryString(STR_EMPTY, STR_SPRITE_ALIGNER_GOTO_CAPTION, 7, this, CS_NUMERAL, QSF_NONE);
    00903         break;
    00904
    00905       case WID_SA_NEXT:
    00906         do {
    00907           this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID();
    00908         } while (GetSpriteType(this->current_sprite) != ST_NORMAL);
    00909         this->SetDirty();
    00910         break;
    00911
    00912       case WID_SA_PICKER:
    00913         this->LowerWidget(WID_SA_PICKER);
    00914         _newgrf_debug_sprite_picker.mode = SPM_WAIT_CLICK;
    00915         this->SetDirty();
    00916         break;
    00917
    00918       case WID_SA_LIST: {
    00919         const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget);
    00920         int step_size = nwid->resize_y;
    00921
    00922         uint i = this->vscroll->GetPosition() + (pt.y - nwid->pos_y) / step_size;
    00923         if (i < _newgrf_debug_sprite_picker.sprites.Length()) {
    00924           SpriteID spr = _newgrf_debug_sprite_picker.sprites[i];
    00925           if (GetSpriteType(spr) == ST_NORMAL) this->current_sprite = spr;
    00926         }
    00927         this->SetDirty();
    00928         break;
    00929       }
    00930
    00931       case WID_SA_UP:
    00932       case WID_SA_DOWN:
    00933       case WID_SA_LEFT:
    00934       case WID_SA_RIGHT: {
    00935         /*
    00936          * Yes... this is a hack.
    00937          *
    00938          * No... I don't think it is useful to make this less of a hack.
    00939          *
    00940          * If you want to align sprites, you just need the number. Generally
    00941          * the sprite caches are big enough to not remove the sprite from the
    00942          * cache. If that's not the case, just let the NewGRF developer
    00943          * increase the cache size instead of storing thousands of offsets
    00944          * for the incredibly small chance that it's actually going to be
    00945          * used by someone and the sprite cache isn't big enough for that
    00946          * particular NewGRF developer.
    00947          */
    00948         Sprite *spr = const_cast<Sprite *>(GetSprite(this->current_sprite, ST_NORMAL));
    00949         switch (widget) {
    00950           case WID_SA_UP:    spr->y_offs -= ZOOM_LVL_BASE; break;
    00951           case WID_SA_DOWN:  spr->y_offs += ZOOM_LVL_BASE; break;
    00952           case WID_SA_LEFT:  spr->x_offs -= ZOOM_LVL_BASE; break;
    00953           case WID_SA_RIGHT: spr->x_offs += ZOOM_LVL_BASE; break;
    00954         }
    00955         /* Of course, we need to redraw the sprite, but where is it used?
    00956          * Everywhere is a safe bet. */
    00957         MarkWholeScreenDirty();
    00958         break;
    00959       }
    00960     }
    00961   }
    00962
    00963   virtual void OnQueryTextFinished(char *str)
    00964   {
    00965     if (StrEmpty(str)) return;
    00966
    00967     this->current_sprite = atoi(str);
    00968     if (this->current_sprite >= GetMaxSpriteID()) this->current_sprite = 0;
    00969     while (GetSpriteType(this->current_sprite) != ST_NORMAL) {
    00970       this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID();
    00971     }
    00972     this->SetDirty();
    00973   }
    00974
    00980   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00981   {
    00982     if (!gui_scope) return;
    00983     if (data == 1) {
    00984       /* Sprite picker finished */
    00985       this->RaiseWidget(WID_SA_PICKER);
    00986       this->vscroll->SetCount(_newgrf_debug_sprite_picker.sprites.Length());
    00987     }
    00988   }
    00989
    00990   virtual void OnResize()
    00991   {
    00992     this->vscroll->SetCapacityFromWidget(this, WID_SA_LIST);
    00993   }
    00994 };
    00995
    00996 static const NWidgetPart _nested_sprite_aligner_widgets[] = {
    00997   NWidget(NWID_HORIZONTAL),
    00998     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00999     NWidget(WWT_CAPTION, COLOUR_GREY, WID_SA_CAPTION), SetDataTip(STR_SPRITE_ALIGNER_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01000     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    01001     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    01002   EndContainer(),
    01003   NWidget(WWT_PANEL, COLOUR_GREY),
    01004     NWidget(NWID_HORIZONTAL), SetPIP(0, 0, 10),
    01005       NWidget(NWID_VERTICAL), SetPIP(10, 5, 10),
    01006         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 5, 10),
    01007           NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_PREVIOUS), SetDataTip(STR_SPRITE_ALIGNER_PREVIOUS_BUTTON, STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP), SetFill(1, 0),
    01008           NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_GOTO), SetDataTip(STR_SPRITE_ALIGNER_GOTO_BUTTON, STR_SPRITE_ALIGNER_GOTO_TOOLTIP), SetFill(1, 0),
    01009           NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SA_NEXT), SetDataTip(STR_SPRITE_ALIGNER_NEXT_BUTTON, STR_SPRITE_ALIGNER_NEXT_TOOLTIP), SetFill(1, 0),
    01010         EndContainer(),
    01011         NWidget(NWID_HORIZONTAL), SetPIP(10, 5, 10),
    01012           NWidget(NWID_SPACER), SetFill(1, 1),
    01013           NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SA_UP), SetDataTip(SPR_ARROW_UP, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0),
    01014           NWidget(NWID_SPACER), SetFill(1, 1),
    01015         EndContainer(),
    01016         NWidget(NWID_HORIZONTAL_LTR), SetPIP(10, 5, 10),
    01017           NWidget(NWID_VERTICAL),
    01018             NWidget(NWID_SPACER), SetFill(1, 1),
    01019             NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SA_LEFT), SetDataTip(SPR_ARROW_LEFT, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0),
    01020             NWidget(NWID_SPACER), SetFill(1, 1),
    01021           EndContainer(),
    01022           NWidget(WWT_PANEL, COLOUR_DARK_BLUE, WID_SA_SPRITE), SetDataTip(STR_NULL, STR_SPRITE_ALIGNER_SPRITE_TOOLTIP),
    01023           EndContainer(),
    01024           NWidget(NWID_VERTICAL),
    01025             NWidget(NWID_SPACER), SetFill(1, 1),
    01026             NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SA_RIGHT), SetDataTip(SPR_ARROW_RIGHT, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0),
    01027             NWidget(NWID_SPACER), SetFill(1, 1),
    01028           EndContainer(),
    01029         EndContainer(),
    01030         NWidget(NWID_HORIZONTAL), SetPIP(10, 5, 10),
    01031           NWidget(NWID_SPACER), SetFill(1, 1),
    01032           NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SA_DOWN), SetDataTip(SPR_ARROW_DOWN, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0),
    01033           NWidget(NWID_SPACER), SetFill(1, 1),
    01034         EndContainer(),
    01035         NWidget(NWID_HORIZONTAL), SetPIP(10, 5, 10),
    01036           NWidget(WWT_LABEL, COLOUR_GREY, WID_SA_OFFSETS), SetDataTip(STR_SPRITE_ALIGNER_OFFSETS, STR_NULL), SetFill(1, 0),
    01037         EndContainer(),
    01038       EndContainer(),
    01039       NWidget(NWID_VERTICAL), SetPIP(10, 5, 10),
    01040         NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SA_PICKER), SetDataTip(STR_SPRITE_ALIGNER_PICKER_BUTTON, STR_SPRITE_ALIGNER_PICKER_TOOLTIP), SetFill(1, 0),
    01041         NWidget(NWID_HORIZONTAL),
    01042           NWidget(WWT_MATRIX, COLOUR_GREY, WID_SA_LIST), SetResize(1, 1), SetMatrixDataTip(1, 0, STR_NULL), SetFill(1, 1), SetScrollbar(WID_SA_SCROLLBAR),
    01043           NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SA_SCROLLBAR),
    01044         EndContainer(),
    01045       EndContainer(),
    01046     EndContainer(),
    01047   EndContainer(),
    01048 };
    01049
    01050 static WindowDesc _sprite_aligner_desc(
    01051   WDP_AUTO, "sprite_aligner", 400, 300,
    01052   WC_SPRITE_ALIGNER, WC_NONE,
    01053   0,
    01054   _nested_sprite_aligner_widgets, lengthof(_nested_sprite_aligner_widgets)
    01055 );
    01056
    01060 void ShowSpriteAlignerWindow()
    01061 {
    01062   AllocateWindowDescFront<SpriteAlignerWindow>(&_sprite_aligner_desc, 0);
    01063 }