station_gui.cpp

Go to the documentation of this file.
00001 /* $Id: station_gui.cpp 14268 2008-09-07 22:04:39Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "gui.h"
00009 #include "window_gui.h"
00010 #include "textbuf_gui.h"
00011 #include "station.h"
00012 #include "player_func.h"
00013 #include "economy_func.h"
00014 #include "town.h"
00015 #include "command_func.h"
00016 #include "variables.h"
00017 #include "vehicle_gui.h"
00018 #include "cargotype.h"
00019 #include "station_gui.h"
00020 #include "station.h"
00021 #include "strings_func.h"
00022 #include "core/alloc_func.hpp"
00023 #include "window_func.h"
00024 #include "viewport_func.h"
00025 #include "gfx_func.h"
00026 #include "widgets/dropdown_func.h"
00027 
00028 #include "table/strings.h"
00029 #include "table/sprites.h"
00030 
00031 typedef int CDECL StationSortListingTypeFunction(const void*, const void*);
00032 
00033 static StationSortListingTypeFunction StationNameSorter;
00034 static StationSortListingTypeFunction StationTypeSorter;
00035 static StationSortListingTypeFunction StationWaitingSorter;
00036 static StationSortListingTypeFunction StationRatingMaxSorter;
00037 
00038 bool _station_show_coverage;
00039 
00054 static void StationsWndShowStationRating(int x, int y, CargoID type, uint amount, byte rating)
00055 {
00056   static const uint units_full  = 576; 
00057   static const uint rating_full = 224; 
00058 
00059   const CargoSpec *cs = GetCargo(type);
00060   if (!cs->IsValid()) return;
00061 
00062   int colour = cs->rating_colour;
00063   uint w = (minu(amount, units_full) + 5) / 36;
00064 
00065   /* Draw total cargo (limited) on station (fits into 16 pixels) */
00066   if (w != 0) GfxFillRect(x, y, x + w - 1, y + 6, colour);
00067 
00068   /* Draw a one pixel-wide bar of additional cargo meter, useful
00069    * for stations with only a small amount (<=30) */
00070   if (w == 0) {
00071     uint rest = amount / 5;
00072     if (rest != 0) {
00073       w += x;
00074       GfxFillRect(w, y + 6 - rest, w, y + 6, colour);
00075     }
00076   }
00077 
00078   DrawString(x + 1, y, cs->abbrev, TC_BLACK);
00079 
00080   /* Draw green/red ratings bar (fits into 14 pixels) */
00081   y += 8;
00082   GfxFillRect(x + 1, y, x + 14, y, 0xB8);
00083   rating = minu(rating, rating_full) / 16;
00084   if (rating != 0) GfxFillRect(x + 1, y, x + rating, y, 0xD0);
00085 }
00086 
00087 const StringID _station_sort_listing[] = {
00088   STR_SORT_BY_DROPDOWN_NAME,
00089   STR_SORT_BY_FACILITY,
00090   STR_SORT_BY_WAITING,
00091   STR_SORT_BY_RATING_MAX,
00092   INVALID_STRING_ID
00093 };
00094 
00095 static char _bufcache[64];
00096 static const Station* _last_station;
00097 static int _internal_sort_order;
00098 
00099 static int CDECL StationNameSorter(const void *a, const void *b)
00100 {
00101   const Station* st1 = *(const Station**)a;
00102   const Station* st2 = *(const Station**)b;
00103   char buf1[64];
00104   int r;
00105 
00106   SetDParam(0, st1->index);
00107   GetString(buf1, STR_STATION, lastof(buf1));
00108 
00109   if (st2 != _last_station) {
00110     _last_station = st2;
00111     SetDParam(0, st2->index);
00112     GetString(_bufcache, STR_STATION, lastof(_bufcache));
00113   }
00114 
00115   r = strcmp(buf1, _bufcache); // sort by name
00116   return (_internal_sort_order & 1) ? -r : r;
00117 }
00118 
00119 static int CDECL StationTypeSorter(const void *a, const void *b)
00120 {
00121   const Station* st1 = *(const Station**)a;
00122   const Station* st2 = *(const Station**)b;
00123   return (_internal_sort_order & 1) ? st2->facilities - st1->facilities : st1->facilities - st2->facilities;
00124 }
00125 
00126 static const uint32 _cargo_filter_max = ~0;
00127 static uint32 _cargo_filter = _cargo_filter_max;
00128 
00129 static int CDECL StationWaitingSorter(const void *a, const void *b)
00130 {
00131   const Station* st1 = *(const Station**)a;
00132   const Station* st2 = *(const Station**)b;
00133   Money sum1 = 0, sum2 = 0;
00134 
00135   for (CargoID j = 0; j < NUM_CARGO; j++) {
00136     if (!HasBit(_cargo_filter, j)) continue;
00137     if (!st1->goods[j].cargo.Empty()) sum1 += GetTransportedGoodsIncome(st1->goods[j].cargo.Count(), 20, 50, j);
00138     if (!st2->goods[j].cargo.Empty()) sum2 += GetTransportedGoodsIncome(st2->goods[j].cargo.Count(), 20, 50, j);
00139   }
00140 
00141   return (_internal_sort_order & 1) ? ClampToI32(sum2 - sum1) : ClampToI32(sum1 - sum2);
00142 }
00143 
00152 static int CDECL StationRatingMaxSorter(const void *a, const void *b)
00153 {
00154   const Station* st1 = *(const Station**)a;
00155   const Station* st2 = *(const Station**)b;
00156   byte maxr1 = 0;
00157   byte maxr2 = 0;
00158 
00159   for (CargoID j = 0; j < NUM_CARGO; j++) {
00160     if (HasBit(st1->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr1 = max(maxr1, st1->goods[j].rating);
00161     if (HasBit(st2->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr2 = max(maxr2, st2->goods[j].rating);
00162   }
00163 
00164   return (_internal_sort_order & 1) ? maxr2 - maxr1 : maxr1 - maxr2;
00165 }
00166 
00168 enum StationListFlags {
00169   SL_ORDER   = 1 << 0, 
00170   SL_RESORT  = 1 << 1, 
00171   SL_REBUILD = 1 << 2, 
00172 };
00173 
00174 DECLARE_ENUM_AS_BIT_SET(StationListFlags);
00175 
00177 struct plstations_d {
00178   const Station** sort_list; 
00179   uint16 list_length;        
00180   uint16 resort_timer;       
00181   byte sort_type;            
00182   byte flags;                
00183 };
00184 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(plstations_d));
00185 
00189 void RebuildStationLists()
00190 {
00191   Window* const *wz;
00192 
00193   FOR_ALL_WINDOWS(wz) {
00194     Window *w = *wz;
00195     if (w->window_class == WC_STATION_LIST) {
00196       WP(w, plstations_d).flags |= SL_REBUILD;
00197       SetWindowDirty(w);
00198     }
00199   }
00200 }
00201 
00205 void ResortStationLists()
00206 {
00207   Window* const *wz;
00208 
00209   FOR_ALL_WINDOWS(wz) {
00210     Window *w = *wz;
00211     if (w->window_class == WC_STATION_LIST) {
00212       WP(w, plstations_d).flags |= SL_RESORT;
00213       SetWindowDirty(w);
00214     }
00215   }
00216 }
00217 
00227 static void BuildStationsList(plstations_d* sl, PlayerID owner, byte facilities, uint32 cargo_filter, bool include_empty)
00228 {
00229   uint n = 0;
00230   const Station *st;
00231 
00232   if (!(sl->flags & SL_REBUILD)) return;
00233 
00234   /* Create array for sorting */
00235   const Station** station_sort = MallocT<const Station*>(GetMaxStationIndex() + 1);
00236 
00237   DEBUG(misc, 3, "Building station list for player %d", owner);
00238 
00239   FOR_ALL_STATIONS(st) {
00240     if (st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy() && HasStationInUse(st->index, owner))) {
00241       if (facilities & st->facilities) { //only stations with selected facilities
00242         int num_waiting_cargo = 0;
00243         for (CargoID j = 0; j < NUM_CARGO; j++) {
00244           if (!st->goods[j].cargo.Empty()) {
00245             num_waiting_cargo++; //count number of waiting cargo
00246             if (HasBit(cargo_filter, j)) {
00247               station_sort[n++] = st;
00248               break;
00249             }
00250           }
00251         }
00252         /* stations without waiting cargo */
00253         if (num_waiting_cargo == 0 && include_empty) {
00254           station_sort[n++] = st;
00255         }
00256       }
00257     }
00258   }
00259 
00260   free((void*)sl->sort_list);
00261   sl->sort_list = MallocT<const Station*>(n);
00262   sl->list_length = n;
00263 
00264   for (uint i = 0; i < n; ++i) sl->sort_list[i] = station_sort[i];
00265 
00266   sl->flags &= ~SL_REBUILD;
00267   sl->flags |= SL_RESORT;
00268   free((void*)station_sort);
00269 }
00270 
00271 
00277 static void SortStationsList(plstations_d *sl)
00278 {
00279   static StationSortListingTypeFunction* const _station_sorter[] = {
00280     &StationNameSorter,
00281     &StationTypeSorter,
00282     &StationWaitingSorter,
00283     &StationRatingMaxSorter
00284   };
00285 
00286   if (!(sl->flags & SL_RESORT)) return;
00287 
00288   _internal_sort_order = sl->flags & SL_ORDER;
00289   _last_station = NULL; // used for "cache" in namesorting
00290   qsort((void*)sl->sort_list, sl->list_length, sizeof(sl->sort_list[0]), _station_sorter[sl->sort_type]);
00291 
00292   sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
00293   sl->flags &= ~SL_RESORT;
00294 }
00295 
00302 static void PlayerStationsWndProc(Window *w, WindowEvent *e)
00303 {
00304   const PlayerID owner = (PlayerID)w->window_number;
00305   static byte facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00306   static Listing station_sort = {0, 0};
00307   static bool include_empty = true;
00308 
00309   plstations_d *sl = &WP(w, plstations_d);
00310 
00311   switch (e->event) {
00312     case WE_CREATE:
00313       if (_cargo_filter == _cargo_filter_max) _cargo_filter = _cargo_mask;
00314 
00315       for (uint i = 0; i < 5; i++) {
00316         if (HasBit(facilities, i)) w->LowerWidget(i + SLW_TRAIN);
00317       }
00318       w->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00319       w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
00320       w->SetWidgetLoweredState(SLW_NOCARGOWAITING, include_empty);
00321 
00322       sl->sort_list = NULL;
00323       sl->flags = SL_REBUILD;
00324       sl->sort_type = station_sort.criteria;
00325       if (station_sort.order) sl->flags |= SL_ORDER;
00326 
00327       /* set up resort timer */
00328       sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
00329       break;
00330 
00331     case WE_PAINT: {
00332       BuildStationsList(sl, owner, facilities, _cargo_filter, include_empty);
00333       SortStationsList(sl);
00334 
00335       SetVScrollCount(w, sl->list_length);
00336 
00337       /* draw widgets, with player's name in the caption */
00338       SetDParam(0, owner);
00339       SetDParam(1, w->vscroll.count);
00340 
00341       /* Set text of sort by dropdown */
00342       w->widget[SLW_SORTDROPBTN].data = _station_sort_listing[sl->sort_type];
00343 
00344       DrawWindowWidgets(w);
00345 
00346       /* draw arrow pointing up/down for ascending/descending sorting */
00347       DrawSortButtonState(w, SLW_SORTBY, sl->flags & SL_ORDER ? SBS_DOWN : SBS_UP);
00348 
00349       int cg_ofst;
00350       int x = 89;
00351       int y = 14;
00352       int xb = 2; 
00353 
00354       uint i = 0;
00355       for (CargoID c = 0; c < NUM_CARGO; c++) {
00356         const CargoSpec *cs = GetCargo(c);
00357         if (!cs->IsValid()) continue;
00358 
00359         cg_ofst = HasBit(_cargo_filter, c) ? 2 : 1;
00360         GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, cs->rating_colour);
00361         DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, cs->abbrev, TC_BLACK);
00362         x += 14;
00363         i++;
00364       }
00365 
00366       x += 6;
00367       cg_ofst = w->IsWidgetLowered(SLW_NOCARGOWAITING) ? 2 : 1;
00368       DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, TC_BLACK);
00369       x += 14;
00370       cg_ofst = w->IsWidgetLowered(SLW_CARGOALL) ? 2 : 1;
00371       DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00372 
00373       cg_ofst = w->IsWidgetLowered(SLW_FACILALL) ? 2 : 1;
00374       DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00375 
00376       if (w->vscroll.count == 0) { // player has no stations
00377         DrawString(xb, 40, STR_304A_NONE, TC_FROMSTRING);
00378         return;
00379       }
00380 
00381       int max = min(w->vscroll.pos + w->vscroll.cap, sl->list_length);
00382       y = 40; // start of the list-widget
00383 
00384       for (int i = w->vscroll.pos; i < max; ++i) { // do until max number of stations of owner
00385         const Station *st = sl->sort_list[i];
00386         int x;
00387 
00388         assert(st->xy != 0);
00389 
00390         /* Do not do the complex check HasStationInUse here, it may be even false
00391          * when the order had been removed and the station list hasn't been removed yet */
00392         assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
00393 
00394         SetDParam(0, st->index);
00395         SetDParam(1, st->facilities);
00396         x = DrawString(xb, y, STR_3049_0, TC_FROMSTRING) + 5;
00397 
00398         /* show cargo waiting and station ratings */
00399         for (CargoID j = 0; j < NUM_CARGO; j++) {
00400           if (!st->goods[j].cargo.Empty()) {
00401             StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
00402             x += 20;
00403           }
00404         }
00405         y += 10;
00406       }
00407       break;
00408     }
00409 
00410     case WE_CLICK:
00411       switch (e->we.click.widget) {
00412         case SLW_LIST: {
00413           uint32 id_v = (e->we.click.pt.y - 41) / 10;
00414 
00415           if (id_v >= w->vscroll.cap) return; // click out of bounds
00416 
00417           id_v += w->vscroll.pos;
00418 
00419           if (id_v >= sl->list_length) return; // click out of list bound
00420 
00421           const Station *st = sl->sort_list[id_v];
00422           /* do not check HasStationInUse - it is slow and may be invalid */
00423           assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
00424           ScrollMainWindowToTile(st->xy);
00425           break;
00426         }
00427 
00428         case SLW_TRAIN:
00429         case SLW_TRUCK:
00430         case SLW_BUS:
00431         case SLW_AIRPLANE:
00432         case SLW_SHIP:
00433           if (_ctrl_pressed) {
00434             ToggleBit(facilities, e->we.click.widget - SLW_TRAIN);
00435             w->ToggleWidgetLoweredState(e->we.click.widget);
00436           } else {
00437             uint i;
00438             FOR_EACH_SET_BIT(i, facilities) {
00439               w->RaiseWidget(i + SLW_TRAIN);
00440             }
00441             SetBit(facilities, e->we.click.widget - SLW_TRAIN);
00442             w->LowerWidget(e->we.click.widget);
00443           }
00444           w->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00445           sl->flags |= SL_REBUILD;
00446           SetWindowDirty(w);
00447           break;
00448 
00449         case SLW_FACILALL:
00450           for (uint i = 0; i < 5; i++) {
00451             w->LowerWidget(i + SLW_TRAIN);
00452           }
00453           w->LowerWidget(SLW_FACILALL);
00454 
00455           facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00456           sl->flags |= SL_REBUILD;
00457           SetWindowDirty(w);
00458           break;
00459 
00460         case SLW_CARGOALL: {
00461           uint i = 0;
00462           for (CargoID c = 0; c < NUM_CARGO; c++) {
00463             if (!GetCargo(c)->IsValid()) continue;
00464             w->LowerWidget(i + SLW_CARGOSTART);
00465             i++;
00466           }
00467           w->LowerWidget(SLW_NOCARGOWAITING);
00468           w->LowerWidget(SLW_CARGOALL);
00469 
00470           _cargo_filter = _cargo_mask;
00471           include_empty = true;
00472           sl->flags |= SL_REBUILD;
00473           SetWindowDirty(w);
00474           break;
00475         }
00476 
00477         case SLW_SORTBY: // flip sorting method asc/desc
00478           sl->flags ^= SL_ORDER; //DESC-flag
00479           station_sort.order = HasBit(sl->flags, 0);
00480           sl->flags |= SL_RESORT;
00481           w->flags4 |= 5 << WF_TIMEOUT_SHL;
00482           w->LowerWidget(SLW_SORTBY);
00483           SetWindowDirty(w);
00484           break;
00485 
00486         case SLW_SORTDROPBTN: // select sorting criteria dropdown menu
00487           ShowDropDownMenu(w, _station_sort_listing, sl->sort_type, SLW_SORTDROPBTN, 0, 0);
00488           break;
00489 
00490         case SLW_NOCARGOWAITING:
00491           if (_ctrl_pressed) {
00492             include_empty = !include_empty;
00493             w->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
00494           } else {
00495             for (uint i = SLW_CARGOSTART; i < w->widget_count; i++) {
00496               w->RaiseWidget(i);
00497             }
00498 
00499             _cargo_filter = 0;
00500             include_empty = true;
00501 
00502             w->LowerWidget(SLW_NOCARGOWAITING);
00503           }
00504           sl->flags |= SL_REBUILD;
00505           w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
00506           SetWindowDirty(w);
00507           break;
00508 
00509         default:
00510           if (e->we.click.widget >= SLW_CARGOSTART) { // change cargo_filter
00511             /* Determine the selected cargo type */
00512             CargoID c;
00513             int i = 0;
00514             for (c = 0; c < NUM_CARGO; c++) {
00515               if (!GetCargo(c)->IsValid()) continue;
00516               if (e->we.click.widget - SLW_CARGOSTART == i) break;
00517               i++;
00518             }
00519 
00520             if (_ctrl_pressed) {
00521               ToggleBit(_cargo_filter, c);
00522               w->ToggleWidgetLoweredState(e->we.click.widget);
00523             } else {
00524               for (uint i = SLW_CARGOSTART; i < w->widget_count; i++) {
00525                 w->RaiseWidget(i);
00526               }
00527               w->RaiseWidget(SLW_NOCARGOWAITING);
00528 
00529               _cargo_filter = 0;
00530               include_empty = false;
00531 
00532               SetBit(_cargo_filter, c);
00533               w->LowerWidget(e->we.click.widget);
00534             }
00535             sl->flags |= SL_REBUILD;
00536             w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
00537             SetWindowDirty(w);
00538           }
00539           break;
00540       }
00541       break;
00542 
00543     case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
00544       if (sl->sort_type != e->we.dropdown.index) {
00545         /* value has changed -> resort */
00546         sl->sort_type = e->we.dropdown.index;
00547         station_sort.criteria = sl->sort_type;
00548         sl->flags |= SL_RESORT;
00549       }
00550       SetWindowDirty(w);
00551       break;
00552 
00553     case WE_TICK:
00554       if (_pause_game != 0) break;
00555       if (--sl->resort_timer == 0) {
00556         DEBUG(misc, 3, "Periodic rebuild station list player %d", owner);
00557         sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
00558         sl->flags |= SL_REBUILD;
00559         SetWindowDirty(w);
00560       }
00561       break;
00562 
00563     case WE_TIMEOUT:
00564       w->RaiseWidget(SLW_SORTBY);
00565       SetWindowDirty(w);
00566       break;
00567 
00568     case WE_RESIZE:
00569       w->vscroll.cap += e->we.sizing.diff.y / 10;
00570       break;
00571   }
00572 }
00573 
00574 static const Widget _player_stations_widgets[] = {
00575 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},            // SLW_CLOSEBOX
00576 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   345,     0,    13, STR_3048_STATIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
00577 {  WWT_STICKYBOX,     RESIZE_LR,    14,   346,   357,     0,    13, 0x0,               STR_STICKY_BUTTON},
00578 {      WWT_PANEL,     RESIZE_RB,    14,     0,   345,    37,   161, 0x0,               STR_3057_STATION_NAMES_CLICK_ON},  // SLW_LIST
00579 {  WWT_SCROLLBAR,    RESIZE_LRB,    14,   346,   357,    37,   149, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
00580 {  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   346,   357,   150,   161, 0x0,               STR_RESIZE_BUTTON},
00581 
00582 {    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,    13,    14,    24, STR_TRAIN,         STR_USE_CTRL_TO_SELECT_MORE},      // SLW_TRAIN
00583 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    14,    27,    14,    24, STR_LORRY,         STR_USE_CTRL_TO_SELECT_MORE},      // SLW_TRUCK
00584 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    28,    41,    14,    24, STR_BUS,           STR_USE_CTRL_TO_SELECT_MORE},      // SLW_BUS
00585 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    42,    55,    14,    24, STR_PLANE,         STR_USE_CTRL_TO_SELECT_MORE},      // SLW_AIRPLANE
00586 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    56,    69,    14,    24, STR_SHIP,          STR_USE_CTRL_TO_SELECT_MORE},      // SLW_SHIP
00587 {      WWT_PANEL,   RESIZE_NONE,    14,    70,    83,    14,    24, 0x0,               STR_SELECT_ALL_FACILITIES},        // SLW_FACILALL
00588 
00589 {      WWT_PANEL,   RESIZE_NONE,    14,    83,    88,    14,    24, 0x0,               STR_NULL},                         // SLW_PAN_BETWEEN
00590 {      WWT_PANEL,   RESIZE_NONE,    14,    89,   102,    14,    24, 0x0,               STR_NO_WAITING_CARGO},             // SLW_NOCARGOWAITING
00591 {      WWT_PANEL,   RESIZE_NONE,    14,   103,   116,    14,    24, 0x0,               STR_SELECT_ALL_TYPES},             // SLW_CARGOALL
00592 {      WWT_PANEL,  RESIZE_RIGHT,    14,   117,   357,    14,    24, 0x0,               STR_NULL},                         // SLW_PAN_RIGHT
00593 
00594 {    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,    80,    25,    36, STR_SORT_BY,       STR_SORT_ORDER_TIP},               // SLW_SORTBY
00595 {   WWT_DROPDOWN,   RESIZE_NONE,    14,    81,   243,    25,    36, 0x0,               STR_SORT_CRITERIA_TIP},            // SLW_SORTDROPBTN
00596 {      WWT_PANEL,  RESIZE_RIGHT,    14,   244,   357,    25,    36, 0x0,               STR_NULL},                         // SLW_PAN_SORT_RIGHT
00597 {   WIDGETS_END},
00598 };
00599 
00600 static const WindowDesc _player_stations_desc = {
00601   WDP_AUTO, WDP_AUTO, 358, 162, 358, 162,
00602   WC_STATION_LIST, WC_NONE,
00603   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00604   _player_stations_widgets,
00605   PlayerStationsWndProc
00606 };
00607 
00613 void ShowPlayerStations(PlayerID player)
00614 {
00615   if (!IsValidPlayer(player)) return;
00616 
00617   Window *w = AllocateWindowDescFront(&_player_stations_desc, player);
00618   if (w == NULL) return;
00619 
00620   w->caption_color = (byte)w->window_number;
00621   w->vscroll.cap = 12;
00622   w->resize.step_height = 10;
00623   w->resize.height = w->height - 10 * 7; // minimum if 5 in the list
00624 
00625   /* Add cargo filter buttons */
00626   uint num_active = 0;
00627   for (CargoID c = 0; c < NUM_CARGO; c++) {
00628     if (GetCargo(c)->IsValid()) num_active++;
00629   }
00630 
00631   w->widget_count += num_active;
00632   w->widget = ReallocT(w->widget, w->widget_count + 1);
00633   w->widget[w->widget_count].type = WWT_LAST;
00634 
00635   uint i = 0;
00636   for (CargoID c = 0; c < NUM_CARGO; c++) {
00637     if (!GetCargo(c)->IsValid()) continue;
00638 
00639     Widget *wi = &w->widget[SLW_CARGOSTART + i];
00640     wi->type     = WWT_PANEL;
00641     wi->display_flags = RESIZE_NONE;
00642     wi->color    = 14;
00643     wi->left     = 89 + i * 14;
00644     wi->right    = wi->left + 13;
00645     wi->top      = 14;
00646     wi->bottom   = 24;
00647     wi->data     = 0;
00648     wi->tooltips = STR_USE_CTRL_TO_SELECT_MORE;
00649 
00650     if (HasBit(_cargo_filter, c)) w->LowerWidget(SLW_CARGOSTART + i);
00651     i++;
00652   }
00653 
00654   w->widget[SLW_NOCARGOWAITING].left += num_active * 14;
00655   w->widget[SLW_NOCARGOWAITING].right += num_active * 14;
00656   w->widget[SLW_CARGOALL].left += num_active * 14;
00657   w->widget[SLW_CARGOALL].right += num_active * 14;
00658   w->widget[SLW_PAN_RIGHT].left += num_active * 14;
00659 
00660   if (num_active > 15) {
00661     /* Resize and fix the minimum width, if necessary */
00662     ResizeWindow(w, (num_active - 15) * 14, 0);
00663     w->resize.width = w->width;
00664   }
00665 }
00666 
00667 static const Widget _station_view_widgets[] = {
00668 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},                // SVW_CLOSEBOX
00669 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   236,     0,    13, STR_300A_0,        STR_018C_WINDOW_TITLE_DRAG_THIS},
00670 {  WWT_STICKYBOX,     RESIZE_LR,    14,   237,   248,     0,    13, 0x0,               STR_STICKY_BUTTON},
00671 {      WWT_PANEL,     RESIZE_RB,    14,     0,   236,    14,    65, 0x0,               STR_NULL},                             // SVW_WAITING
00672 {  WWT_SCROLLBAR,    RESIZE_LRB,    14,   237,   248,    14,    65, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
00673 {      WWT_PANEL,    RESIZE_RTB,    14,     0,   248,    66,    97, 0x0,               STR_NULL},                             // SVW_ACCEPTLIST / SVW_RATINGLIST
00674 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,    59,    98,   109, STR_00E4_LOCATION, STR_3053_CENTER_MAIN_VIEW_ON_STATION}, // SVW_LOCATION
00675 { WWT_PUSHTXTBTN,     RESIZE_TB,    14,    60,   120,    98,   109, STR_3032_RATINGS,  STR_3054_SHOW_STATION_RATINGS},        // SVW_RATINGS / SVW_ACCEPTS
00676 { WWT_PUSHTXTBTN,    RESIZE_RTB,    14,   121,   180,    98,   109, STR_0130_RENAME,   STR_3055_CHANGE_NAME_OF_STATION},      // SVW_RENAME
00677 { WWT_PUSHTXTBTN,   RESIZE_LRTB,    14,   181,   194,    98,   109, STR_TRAIN,         STR_SCHEDULED_TRAINS_TIP },            // SVW_TRAINS
00678 { WWT_PUSHTXTBTN,   RESIZE_LRTB,    14,   195,   208,    98,   109, STR_LORRY,         STR_SCHEDULED_ROAD_VEHICLES_TIP },     // SVW_ROADVEHS
00679 { WWT_PUSHTXTBTN,   RESIZE_LRTB,    14,   209,   222,    98,   109, STR_PLANE,         STR_SCHEDULED_AIRCRAFT_TIP },          // SVW_PLANES
00680 { WWT_PUSHTXTBTN,   RESIZE_LRTB,    14,   223,   236,    98,   109, STR_SHIP,          STR_SCHEDULED_SHIPS_TIP },             // SVW_SHIPS
00681 {  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   237,   248,    98,   109, 0x0,               STR_RESIZE_BUTTON},
00682 {   WIDGETS_END},
00683 };
00684 
00693 static void DrawCargoIcons(CargoID i, uint waiting, int x, int y, uint width)
00694 {
00695   uint num = min((waiting + 5) / 10, width / 10); // maximum is width / 10 icons so it won't overflow
00696   if (num == 0) return;
00697 
00698   const CargoSpec *cs = GetCargo(i);
00699   SpriteID sprite;
00700 
00701   if (cs->sprite == 0xFFFF) {
00702     /* A value of 0xFFFF indicates we should draw a custom icon */
00703     sprite = GetCustomCargoSprite(cs);
00704   } else {
00705     sprite = cs->sprite;
00706   }
00707 
00708   if (sprite == 0) sprite = SPR_CARGO_GOODS;
00709 
00710   do {
00711     DrawSprite(sprite, PAL_NONE, x, y);
00712     x += 10;
00713   } while (--num);
00714 }
00715 
00716 struct CargoData {
00717   CargoID cargo;
00718   StationID source;
00719   uint count;
00720 
00721   CargoData(CargoID cargo, StationID source, uint count) :
00722     cargo(cargo),
00723     source(source),
00724     count(count)
00725   { }
00726 };
00727 
00728 typedef std::list<CargoData> CargoDataList;
00729 
00735 static void DrawStationViewWindow(Window *w)
00736 {
00737   StationID station_id = w->window_number;
00738   const Station* st = GetStation(station_id);
00739   int x, y;     
00740   int pos;      
00741   StringID str;
00742   CargoDataList cargolist;
00743 
00744   /* count types of cargos waiting in station */
00745   for (CargoID i = 0; i < NUM_CARGO; i++) {
00746     if (!st->goods[i].cargo.Empty()) {
00747       /* Add an entry for total amount of cargo of this type waiting. */
00748       cargolist.push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
00749 
00750       /* Add an entry for each distinct cargo source. */
00751       const CargoList::List *packets = st->goods[i].cargo.Packets();
00752       for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
00753         const CargoPacket *cp = *it;
00754         if (cp->source != station_id) {
00755           bool added = false;
00756 
00757           /* Check if we already have this source in the list */
00758           for (CargoDataList::iterator jt = cargolist.begin(); jt != cargolist.end(); jt++) {
00759             CargoData *cd = &(*jt);
00760             if (cd->cargo == i && cd->source == cp->source) {
00761               cd->count += cp->count;
00762               added = true;
00763               break;
00764             }
00765           }
00766 
00767           if (!added) cargolist.push_back(CargoData(i, cp->source, cp->count));
00768         }
00769       }
00770     }
00771   }
00772   SetVScrollCount(w, cargolist.size() + 1); // update scrollbar
00773 
00774   /* disable some buttons */
00775   w->SetWidgetDisabledState(SVW_RENAME,   st->owner != _local_player);
00776   w->SetWidgetDisabledState(SVW_TRAINS,   !(st->facilities & FACIL_TRAIN));
00777   w->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
00778   w->SetWidgetDisabledState(SVW_PLANES,   !(st->facilities & FACIL_AIRPORT));
00779   w->SetWidgetDisabledState(SVW_SHIPS,    !(st->facilities & FACIL_DOCK));
00780 
00781   SetDParam(0, st->index);
00782   SetDParam(1, st->facilities);
00783   DrawWindowWidgets(w);
00784 
00785   x = 2;
00786   y = 15;
00787   pos = w->vscroll.pos;
00788 
00789   uint width = w->widget[SVW_WAITING].right - w->widget[SVW_WAITING].left - 4;
00790   int maxrows = w->vscroll.cap;
00791 
00792   if (--pos < 0) {
00793     str = STR_00D0_NOTHING;
00794     for (CargoID i = 0; i < NUM_CARGO; i++) {
00795       if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
00796     }
00797     SetDParam(0, str);
00798     DrawString(x, y, STR_0008_WAITING, TC_FROMSTRING);
00799     y += 10;
00800   }
00801 
00802   for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
00803     if (--pos < 0) {
00804       const CargoData *cd = &(*it);
00805       if (cd->source == INVALID_STATION) {
00806         /* Heading */
00807         DrawCargoIcons(cd->cargo, cd->count, x, y, width);
00808         SetDParam(0, cd->cargo);
00809         SetDParam(1, cd->count);
00810         DrawStringRightAligned(x + width, y, STR_0009, TC_FROMSTRING);
00811       } else {
00812         SetDParam(0, cd->cargo);
00813         SetDParam(1, cd->count);
00814         SetDParam(2, cd->source);
00815         DrawStringRightAlignedTruncated(x + width, y, STR_EN_ROUTE_FROM, TC_FROMSTRING, width);
00816       }
00817 
00818       y += 10;
00819     }
00820   }
00821 
00822   if (w->widget[SVW_ACCEPTS].data == STR_3032_RATINGS) { // small window with list of accepted cargo
00823     char *b = _userstring;
00824     bool first = true;
00825 
00826     b = InlineString(b, STR_000C_ACCEPTS);
00827 
00828     for (CargoID i = 0; i < NUM_CARGO; i++) {
00829       if (b >= lastof(_userstring) - (1 + 2 * 4)) break; // ',' or ' ' and two calls to Utf8Encode()
00830       if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
00831         if (first) {
00832           first = false;
00833         } else {
00834           /* Add a comma if this is not the first item */
00835           *b++ = ',';
00836           *b++ = ' ';
00837         }
00838         b = InlineString(b, GetCargo(i)->name);
00839       }
00840     }
00841 
00842     /* If first is still true then no cargo is accepted */
00843     if (first) b = InlineString(b, STR_00D0_NOTHING);
00844 
00845     *b = '\0';
00846 
00847     /* Make sure we detect any buffer overflow */
00848     assert(b < endof(_userstring));
00849 
00850     DrawStringMultiLine(2, w->widget[SVW_ACCEPTLIST].top + 1, STR_SPEC_USERSTRING, w->widget[SVW_ACCEPTLIST].right - w->widget[SVW_ACCEPTLIST].left);
00851   } else { // extended window with list of cargo ratings
00852     y = w->widget[SVW_RATINGLIST].top + 1;
00853 
00854     DrawString(2, y, STR_3034_LOCAL_RATING_OF_TRANSPORT, TC_FROMSTRING);
00855     y += 10;
00856 
00857     for (CargoID i = 0; i < NUM_CARGO; i++) {
00858       const CargoSpec *cs = GetCargo(i);
00859       if (!cs->IsValid()) continue;
00860 
00861       const GoodsEntry *ge = &st->goods[i];
00862       if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
00863 
00864       SetDParam(0, cs->name);
00865       SetDParam(2, ge->rating * 101 >> 8);
00866       SetDParam(1, STR_3035_APPALLING + (ge->rating >> 5));
00867       DrawString(8, y, STR_303D, TC_FROMSTRING);
00868       y += 10;
00869     }
00870   }
00871 }
00872 
00873 
00880 static void StationViewWndProc(Window *w, WindowEvent *e)
00881 {
00882   switch (e->event) {
00883     case WE_PAINT:
00884       DrawStationViewWindow(w);
00885       break;
00886 
00887     case WE_CLICK:
00888       switch (e->we.click.widget) {
00889         case SVW_LOCATION:
00890           ScrollMainWindowToTile(GetStation(w->window_number)->xy);
00891           break;
00892 
00893         case SVW_RATINGS:
00894           SetWindowDirty(w);
00895 
00896           if (w->widget[SVW_RATINGS].data == STR_3032_RATINGS) {
00897             /* Switch to ratings view */
00898             w->widget[SVW_RATINGS].data = STR_3033_ACCEPTS;
00899             w->widget[SVW_RATINGS].tooltips = STR_3056_SHOW_LIST_OF_ACCEPTED_CARGO;
00900             ResizeWindowForWidget(w, SVW_ACCEPTLIST, 0, 100);
00901           } else {
00902             /* Switch to accepts view */
00903             w->widget[SVW_RATINGS].data = STR_3032_RATINGS;
00904             w->widget[SVW_RATINGS].tooltips = STR_3054_SHOW_STATION_RATINGS;
00905             ResizeWindowForWidget(w, SVW_ACCEPTLIST, 0, -100);
00906           }
00907 
00908           SetWindowDirty(w);
00909           break;
00910 
00911         case SVW_RENAME:
00912           SetDParam(0, w->window_number);
00913           ShowQueryString(STR_STATION, STR_3030_RENAME_STATION_LOADING, 31, 180, w, CS_ALPHANUMERAL);
00914           break;
00915 
00916         case SVW_TRAINS: { // Show a list of scheduled trains to this station
00917           const Station *st = GetStation(w->window_number);
00918           ShowVehicleListWindow(st->owner, VEH_TRAIN, (StationID)w->window_number);
00919           break;
00920         }
00921 
00922         case SVW_ROADVEHS: { // Show a list of scheduled road-vehicles to this station
00923           const Station *st = GetStation(w->window_number);
00924           ShowVehicleListWindow(st->owner, VEH_ROAD, (StationID)w->window_number);
00925           break;
00926         }
00927 
00928         case SVW_PLANES: { // Show a list of scheduled aircraft to this station
00929           const Station *st = GetStation(w->window_number);
00930           /* Since oilrigs have no owners, show the scheduled aircraft of current player */
00931           PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
00932           ShowVehicleListWindow(owner, VEH_AIRCRAFT, (StationID)w->window_number);
00933           break;
00934         }
00935 
00936         case SVW_SHIPS: { // Show a list of scheduled ships to this station
00937           const Station *st = GetStation(w->window_number);
00938           /* Since oilrigs/bouys have no owners, show the scheduled ships of current player */
00939           PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
00940           ShowVehicleListWindow(owner, VEH_SHIP, (StationID)w->window_number);
00941           break;
00942         }
00943       }
00944       break;
00945 
00946     case WE_ON_EDIT_TEXT:
00947       if (e->we.edittext.str[0] != '\0') {
00948         _cmd_text = e->we.edittext.str;
00949         DoCommandP(0, w->window_number, 0, NULL,
00950           CMD_RENAME_STATION | CMD_MSG(STR_3031_CAN_T_RENAME_STATION));
00951       }
00952       break;
00953 
00954     case WE_DESTROY: {
00955       WindowNumber wno =
00956         (w->window_number << 16) | VLW_STATION_LIST | GetStation(w->window_number)->owner;
00957 
00958       DeleteWindowById(WC_TRAINS_LIST, wno | (VEH_TRAIN << 11));
00959       DeleteWindowById(WC_ROADVEH_LIST, wno | (VEH_ROAD << 11));
00960       DeleteWindowById(WC_SHIPS_LIST, wno | (VEH_SHIP << 11));
00961       DeleteWindowById(WC_AIRCRAFT_LIST, wno | (VEH_AIRCRAFT << 11));
00962       break;
00963     }
00964 
00965     case WE_RESIZE:
00966       if (e->we.sizing.diff.x != 0) ResizeButtons(w, SVW_LOCATION, SVW_RENAME);
00967       w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
00968       break;
00969   }
00970 }
00971 
00972 
00973 static const WindowDesc _station_view_desc = {
00974   WDP_AUTO, WDP_AUTO, 249, 110, 249, 110,
00975   WC_STATION_VIEW, WC_NONE,
00976   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00977   _station_view_widgets,
00978   StationViewWndProc
00979 };
00980 
00986 void ShowStationViewWindow(StationID station)
00987 {
00988   Window *w = AllocateWindowDescFront(&_station_view_desc, station);
00989   if (w == NULL) return;
00990 
00991   PlayerID owner = GetStation(w->window_number)->owner;
00992   if (owner != OWNER_NONE) w->caption_color = owner;
00993   w->vscroll.cap = 5;
00994   w->resize.step_height = 10;
00995 }

Generated on Mon Sep 22 20:34:18 2008 for openttd by  doxygen 1.5.6