# group_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: group_gui.cpp 25537 2013-06-30 14:36:31Z 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 "textbuf_gui.h"
    00014 #include "command_func.h"
    00015 #include "vehicle_gui.h"
    00016 #include "vehicle_base.h"
    00017 #include "string_func.h"
    00018 #include "strings_func.h"
    00019 #include "window_func.h"
    00020 #include "vehicle_func.h"
    00021 #include "autoreplace_gui.h"
    00022 #include "company_func.h"
    00023 #include "widgets/dropdown_func.h"
    00024 #include "tilehighlight_func.h"
    00025 #include "vehicle_gui_base.h"
    00026 #include "core/geometry_func.hpp"
    00027 #include "company_base.h"
    00028
    00029 #include "widgets/group_widget.h"
    00030
    00031 #include "table/sprites.h"
    00032
    00033 typedef GUIList<const Group*> GUIGroupList;
    00034
    00035 static const NWidgetPart _nested_group_widgets[] = {
    00036   NWidget(NWID_HORIZONTAL), // Window header
    00037     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00038     NWidget(WWT_CAPTION, COLOUR_GREY, WID_GL_CAPTION),
    00039     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00040     NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
    00041     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00042   EndContainer(),
    00043   NWidget(NWID_HORIZONTAL),
    00044     /* left part */
    00045     NWidget(NWID_VERTICAL),
    00046       NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetFill(1, 0), EndContainer(),
    00047       NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_ALL_VEHICLES), SetFill(1, 0), EndContainer(),
    00048       NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_DEFAULT_VEHICLES), SetFill(1, 0), EndContainer(),
    00049       NWidget(NWID_HORIZONTAL),
    00050         NWidget(WWT_MATRIX, COLOUR_GREY, WID_GL_LIST_GROUP), SetMatrixDataTip(1, 0, STR_GROUPS_CLICK_ON_GROUP_FOR_TOOLTIP),
    00051             SetFill(1, 0), SetResize(0, 1), SetScrollbar(WID_GL_LIST_GROUP_SCROLLBAR),
    00052         NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_GL_LIST_GROUP_SCROLLBAR),
    00053       EndContainer(),
    00054       NWidget(NWID_HORIZONTAL),
    00055         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_CREATE_GROUP), SetFill(0, 1),
    00056             SetDataTip(SPR_GROUP_CREATE_TRAIN, STR_GROUP_CREATE_TOOLTIP),
    00057         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_DELETE_GROUP), SetFill(0, 1),
    00058             SetDataTip(SPR_GROUP_DELETE_TRAIN, STR_GROUP_DELETE_TOOLTIP),
    00059         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_RENAME_GROUP), SetFill(0, 1),
    00060             SetDataTip(SPR_GROUP_RENAME_TRAIN, STR_GROUP_RENAME_TOOLTIP),
    00061         NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), EndContainer(),
    00062         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_REPLACE_PROTECTION), SetFill(0, 1),
    00063             SetDataTip(SPR_GROUP_REPLACE_OFF_TRAIN, STR_GROUP_REPLACE_PROTECTION_TOOLTIP),
    00064       EndContainer(),
    00065     EndContainer(),
    00066     /* right part */
    00067     NWidget(NWID_VERTICAL),
    00068       NWidget(NWID_HORIZONTAL),
    00069         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GL_SORT_BY_ORDER), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
    00070         NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_SORT_BY_DROPDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
    00071         NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetResize(1, 0), EndContainer(),
    00072       EndContainer(),
    00073       NWidget(NWID_HORIZONTAL),
    00074         NWidget(WWT_MATRIX, COLOUR_GREY, WID_GL_LIST_VEHICLE), SetMinimalSize(248, 0), SetMatrixDataTip(1, 0, STR_NULL), SetResize(1, 1), SetFill(1, 0), SetScrollbar(WID_GL_LIST_VEHICLE_SCROLLBAR),
    00075         NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_GL_LIST_VEHICLE_SCROLLBAR),
    00076       EndContainer(),
    00077       NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(1, 0), SetFill(1, 1), SetResize(1, 0), EndContainer(),
    00078       NWidget(NWID_HORIZONTAL),
    00079         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GL_AVAILABLE_VEHICLES), SetMinimalSize(106, 12), SetFill(0, 1),
    00080             SetDataTip(STR_BLACK_STRING, STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP),
    00081         NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
    00082         NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_MANAGE_VEHICLES_DROPDOWN), SetMinimalSize(118, 12), SetFill(0, 1),
    00083             SetDataTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP),
    00084         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_STOP_ALL), SetMinimalSize(12, 12), SetFill(0, 1),
    00085             SetDataTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP),
    00086         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_START_ALL), SetMinimalSize(12, 12), SetFill(0, 1),
    00087             SetDataTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP),
    00088         NWidget(WWT_RESIZEBOX, COLOUR_GREY),
    00089       EndContainer(),
    00090     EndContainer(),
    00091   EndContainer(),
    00092 };
    00093
    00094 class VehicleGroupWindow : public BaseVehicleListWindow {
    00095 private:
    00096   /* Columns in the group list */
    00097   enum ListColumns {
    00098     VGC_NAME,
    00099     VGC_PROTECT,
    00100     VGC_AUTOREPLACE,
    00101     VGC_PROFIT,
    00102     VGC_NUMBER,
    00103
    00104     VGC_END
    00105   };
    00106
    00107   VehicleID vehicle_sel;
    00108   GroupID group_rename;
    00109   GroupID group_over;
    00110   GUIGroupList groups;
    00111   uint tiny_step_height;
    00112   Scrollbar *group_sb;
    00113
    00114   Dimension column_size[VGC_END];
    00115
    00121   void BuildGroupList(Owner owner)
    00122   {
    00123     if (!this->groups.NeedRebuild()) return;
    00124
    00125     this->groups.Clear();
    00126
    00127     const Group *g;
    00128     FOR_ALL_GROUPS(g) {
    00129       if (g->owner == owner && g->vehicle_type == this->vli.vtype) {
    00130         *this->groups.Append() = g;
    00131       }
    00132     }
    00133
    00134     this->groups.Compact();
    00135     this->groups.RebuildDone();
    00136   }
    00137
    00139   static int CDECL GroupNameSorter(const Group * const *a, const Group * const *b)
    00140   {
    00141     static const Group *last_group[2] = { NULL, NULL };
    00142     static char         last_name[2][64] = { "", "" };
    00143
    00144     if (*a != last_group[0]) {
    00145       last_group[0] = *a;
    00146       SetDParam(0, (*a)->index);
    00147       GetString(last_name[0], STR_GROUP_NAME, lastof(last_name[0]));
    00148     }
    00149
    00150     if (*b != last_group[1]) {
    00151       last_group[1] = *b;
    00152       SetDParam(0, (*b)->index);
    00153       GetString(last_name[1], STR_GROUP_NAME, lastof(last_name[1]));
    00154     }
    00155
    00156     int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
    00157     if (r == 0) return (*a)->index - (*b)->index;
    00158     return r;
    00159   }
    00160
    00165   uint ComputeGroupInfoSize()
    00166   {
    00167     this->column_size[VGC_NAME] = maxdim(GetStringBoundingBox(STR_GROUP_DEFAULT_TRAINS + this->vli.vtype), GetStringBoundingBox(STR_GROUP_ALL_TRAINS + this->vli.vtype));
    00168     this->column_size[VGC_NAME].width = max(170u, this->column_size[VGC_NAME].width);
    00169     this->tiny_step_height = this->column_size[VGC_NAME].height;
    00170
    00171     this->column_size[VGC_PROTECT] = GetSpriteSize(SPR_GROUP_REPLACE_PROTECT);
    00172     this->tiny_step_height = max(this->tiny_step_height, this->column_size[VGC_PROTECT].height);
    00173
    00174     this->column_size[VGC_AUTOREPLACE] = GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE);
    00175     this->tiny_step_height = max(this->tiny_step_height, this->column_size[VGC_AUTOREPLACE].height);
    00176
    00177     this->column_size[VGC_PROFIT].width = 0;
    00178     this->column_size[VGC_PROFIT].height = 0;
    00179     static const SpriteID profit_sprites[] = {SPR_PROFIT_NA, SPR_PROFIT_NEGATIVE, SPR_PROFIT_SOME, SPR_PROFIT_LOT};
    00180     for (uint i = 0; i < lengthof(profit_sprites); i++) {
    00181       Dimension d = GetSpriteSize(profit_sprites[i]);
    00182       this->column_size[VGC_PROFIT] = maxdim(this->column_size[VGC_PROFIT], d);
    00183     }
    00184     this->tiny_step_height = max(this->tiny_step_height, this->column_size[VGC_PROFIT].height);
    00185
    00186     SetDParamMaxValue(0, GroupStatistics::Get(this->vli.company, ALL_GROUP, this->vli.vtype).num_vehicle, 3, FS_SMALL);
    00187     this->column_size[VGC_NUMBER] = GetStringBoundingBox(STR_TINY_COMMA);
    00188     this->tiny_step_height = max(this->tiny_step_height, this->column_size[VGC_NUMBER].height);
    00189
    00190     this->tiny_step_height += WD_MATRIX_TOP;
    00191
    00192     return WD_FRAMERECT_LEFT + 8 +
    00193       this->column_size[VGC_NAME].width + 8 +
    00194       this->column_size[VGC_PROTECT].width + 2 +
    00195       this->column_size[VGC_AUTOREPLACE].width + 2 +
    00196       this->column_size[VGC_PROFIT].width + 2 +
    00197       this->column_size[VGC_NUMBER].width + 2 +
    00198       WD_FRAMERECT_RIGHT;
    00199   }
    00200
    00209   void DrawGroupInfo(int y, int left, int right, GroupID g_id, bool protection = false) const
    00210   {
    00211     /* Highlight the group if a vehicle is dragged over it */
    00212     if (g_id == this->group_over) {
    00213       GfxFillRect(left + WD_FRAMERECT_LEFT, y + WD_FRAMERECT_TOP, right - WD_FRAMERECT_RIGHT, y + this->tiny_step_height - WD_FRAMERECT_BOTTOM - WD_MATRIX_TOP, _colour_gradient[COLOUR_GREY][7]);
    00214     }
    00215
    00216     if (g_id == NEW_GROUP) return;
    00217
    00218     /* draw the selected group in white, else we draw it in black */
    00219     TextColour colour = g_id == this->vli.index ? TC_WHITE : TC_BLACK;
    00220     const GroupStatistics &stats = GroupStatistics::Get(this->vli.company, g_id, this->vli.vtype);
    00221     bool rtl = _current_text_dir == TD_RTL;
    00222
    00223     /* draw group name */
    00224     StringID str;
    00225     if (IsAllGroupID(g_id)) {
    00226       str = STR_GROUP_ALL_TRAINS + this->vli.vtype;
    00227     } else if (IsDefaultGroupID(g_id)) {
    00228       str = STR_GROUP_DEFAULT_TRAINS + this->vli.vtype;
    00229     } else {
    00230       SetDParam(0, g_id);
    00231       str = STR_GROUP_NAME;
    00232     }
    00233     int x = rtl ? right - WD_FRAMERECT_RIGHT - 8 - this->column_size[VGC_NAME].width + 1 : left + WD_FRAMERECT_LEFT + 8;
    00234     DrawString(x, x + this->column_size[VGC_NAME].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NAME].height) / 2, str, colour);
    00235
    00236     /* draw autoreplace protection */
    00237     x = rtl ? x - 8 - this->column_size[VGC_PROTECT].width : x + 8 + this->column_size[VGC_NAME].width;
    00238     if (protection) DrawSprite(SPR_GROUP_REPLACE_PROTECT, PAL_NONE, x, y + (this->tiny_step_height - this->column_size[VGC_PROTECT].height) / 2);
    00239
    00240     /* draw autoreplace status */
    00241     x = rtl ? x - 2 - this->column_size[VGC_AUTOREPLACE].width : x + 2 + this->column_size[VGC_PROTECT].width;
    00242     if (stats.autoreplace_defined) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, stats.autoreplace_finished ? PALETTE_CRASH : PAL_NONE, x, y + (this->tiny_step_height - this->column_size[VGC_AUTOREPLACE].height) / 2);
    00243
    00244     /* draw the profit icon */
    00245     x = rtl ? x - 2 - this->column_size[VGC_PROFIT].width : x + 2 + this->column_size[VGC_AUTOREPLACE].width;
    00246     SpriteID spr;
    00247     if (stats.num_profit_vehicle == 0) {
    00248       spr = SPR_PROFIT_NA;
    00249     } else if (stats.profit_last_year < 0) {
    00250       spr = SPR_PROFIT_NEGATIVE;
    00251     } else if (stats.profit_last_year < 10000 * stats.num_profit_vehicle) { // TODO magic number
    00252       spr = SPR_PROFIT_SOME;
    00253     } else {
    00254       spr = SPR_PROFIT_LOT;
    00255     }
    00256     DrawSprite(spr, PAL_NONE, x, y + (this->tiny_step_height - this->column_size[VGC_PROFIT].height) / 2);
    00257
    00258     /* draw the number of vehicles of the group */
    00259     x = rtl ? x - 2 - this->column_size[VGC_NUMBER].width : x + 2 + this->column_size[VGC_PROFIT].width;
    00260     SetDParam(0, stats.num_vehicle);
    00261     DrawString(x, x + this->column_size[VGC_NUMBER].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NUMBER].height) / 2, STR_TINY_COMMA, colour, SA_RIGHT | SA_FORCE);
    00262   }
    00263
    00267   void DirtyHighlightedGroupWidget()
    00268   {
    00269     if (this->group_over == INVALID_GROUP) return;
    00270
    00271     if (IsAllGroupID(this->group_over)) {
    00272       this->SetWidgetDirty(WID_GL_ALL_VEHICLES);
    00273     } else if (IsDefaultGroupID(this->group_over)) {
    00274       this->SetWidgetDirty(WID_GL_DEFAULT_VEHICLES);
    00275     } else {
    00276       this->SetWidgetDirty(WID_GL_LIST_GROUP);
    00277     }
    00278   }
    00279
    00280 public:
    00281   VehicleGroupWindow(WindowDesc *desc, WindowNumber window_number) : BaseVehicleListWindow(desc, window_number)
    00282   {
    00283     this->CreateNestedTree();
    00284
    00285     this->vscroll = this->GetScrollbar(WID_GL_LIST_VEHICLE_SCROLLBAR);
    00286     this->group_sb = this->GetScrollbar(WID_GL_LIST_GROUP_SCROLLBAR);
    00287
    00288     switch (this->vli.vtype) {
    00289       default: NOT_REACHED();
    00290       case VEH_TRAIN:    this->sorting = &_sorting.train;    break;
    00291       case VEH_ROAD:     this->sorting = &_sorting.roadveh;  break;
    00292       case VEH_SHIP:     this->sorting = &_sorting.ship;     break;
    00293       case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break;
    00294     }
    00295
    00296     this->vli.index = ALL_GROUP;
    00297     this->vehicle_sel = INVALID_VEHICLE;
    00298     this->group_rename = INVALID_GROUP;
    00299     this->group_over = INVALID_GROUP;
    00300
    00301     this->vehicles.SetListing(*this->sorting);
    00302     this->vehicles.ForceRebuild();
    00303     this->vehicles.NeedResort();
    00304
    00305     this->BuildVehicleList();
    00306     this->SortVehicleList();
    00307
    00308     this->groups.ForceRebuild();
    00309     this->groups.NeedResort();
    00310     this->BuildGroupList(vli.company);
    00311     this->groups.Sort(&GroupNameSorter);
    00312
    00313     this->GetWidget<NWidgetCore>(WID_GL_CAPTION)->widget_data = STR_VEHICLE_LIST_TRAIN_CAPTION + this->vli.vtype;
    00314     this->GetWidget<NWidgetCore>(WID_GL_LIST_VEHICLE)->tool_tip = STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP + this->vli.vtype;
    00315
    00316     this->GetWidget<NWidgetCore>(WID_GL_CREATE_GROUP)->widget_data += this->vli.vtype;
    00317     this->GetWidget<NWidgetCore>(WID_GL_RENAME_GROUP)->widget_data += this->vli.vtype;
    00318     this->GetWidget<NWidgetCore>(WID_GL_DELETE_GROUP)->widget_data += this->vli.vtype;
    00319     this->GetWidget<NWidgetCore>(WID_GL_REPLACE_PROTECTION)->widget_data += this->vli.vtype;
    00320
    00321     this->FinishInitNested(window_number);
    00322     this->owner = vli.company;
    00323   }
    00324
    00325   ~VehicleGroupWindow()
    00326   {
    00327     *this->sorting = this->vehicles.GetListing();
    00328   }
    00329
    00330   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00331   {
    00332     switch (widget) {
    00333       case WID_GL_LIST_GROUP: {
    00334         size->width = this->ComputeGroupInfoSize();
    00335         resize->height = this->tiny_step_height;
    00336
    00337         /* Minimum height is the height of the list widget minus all and default vehicles... */
    00338         size->height =  4 * GetVehicleListHeight(this->vli.vtype, this->tiny_step_height) - 2 * this->tiny_step_height;
    00339
    00340         /* ... minus the buttons at the bottom ... */
    00341         uint max_icon_height = GetSpriteSize(this->GetWidget<NWidgetCore>(WID_GL_CREATE_GROUP)->widget_data).height;
    00342         max_icon_height = max(max_icon_height, GetSpriteSize(this->GetWidget<NWidgetCore>(WID_GL_RENAME_GROUP)->widget_data).height);
    00343         max_icon_height = max(max_icon_height, GetSpriteSize(this->GetWidget<NWidgetCore>(WID_GL_DELETE_GROUP)->widget_data).height);
    00344         max_icon_height = max(max_icon_height, GetSpriteSize(this->GetWidget<NWidgetCore>(WID_GL_REPLACE_PROTECTION)->widget_data).height);
    00345
    00346         /* Get a multiple of tiny_step_height of that amount */
    00347         size->height = Ceil(size->height - max_icon_height, tiny_step_height);
    00348         break;
    00349       }
    00350
    00351       case WID_GL_ALL_VEHICLES:
    00352       case WID_GL_DEFAULT_VEHICLES:
    00353         size->width = this->ComputeGroupInfoSize();
    00354         size->height = this->tiny_step_height;
    00355         break;
    00356
    00357       case WID_GL_SORT_BY_ORDER: {
    00358         Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
    00359         d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better.
    00360         d.height += padding.height;
    00361         *size = maxdim(*size, d);
    00362         break;
    00363       }
    00364
    00365       case WID_GL_LIST_VEHICLE:
    00366         this->ComputeGroupInfoSize();
    00367         resize->height = GetVehicleListHeight(this->vli.vtype, this->tiny_step_height);
    00368         size->height = 4 * resize->height;
    00369         break;
    00370
    00371       case WID_GL_MANAGE_VEHICLES_DROPDOWN: {
    00372         Dimension d = this->GetActionDropdownSize(true, true);
    00373         d.height += padding.height;
    00374         d.width  += padding.width;
    00375         *size = maxdim(*size, d);
    00376         break;
    00377       }
    00378     }
    00379   }
    00380
    00386   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00387   {
    00388     if (data == 0) {
    00389       /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
    00390       this->vehicles.ForceRebuild();
    00391       this->groups.ForceRebuild();
    00392     } else {
    00393       this->vehicles.ForceResort();
    00394       this->groups.ForceResort();
    00395     }
    00396
    00397     /* Process ID-invalidation in command-scope as well */
    00398     if (this->group_rename != INVALID_GROUP && !Group::IsValidID(this->group_rename)) {
    00399       DeleteWindowByClass(WC_QUERY_STRING);
    00400       this->group_rename = INVALID_GROUP;
    00401     }
    00402
    00403     if (!(IsAllGroupID(this->vli.index) || IsDefaultGroupID(this->vli.index) || Group::IsValidID(this->vli.index))) {
    00404       this->vli.index = ALL_GROUP;
    00405       HideDropDownMenu(this);
    00406     }
    00407     this->SetDirty();
    00408   }
    00409
    00410   virtual void SetStringParameters(int widget) const
    00411   {
    00412     switch (widget) {
    00413       case WID_GL_AVAILABLE_VEHICLES:
    00414         SetDParam(0, STR_VEHICLE_LIST_AVAILABLE_TRAINS + this->vli.vtype);
    00415         break;
    00416
    00417       case WID_GL_CAPTION:
    00418         /* If selected_group == DEFAULT_GROUP || ALL_GROUP, draw the standard caption
    00419          * We list all vehicles or ungrouped vehicles */
    00420         if (IsDefaultGroupID(this->vli.index) || IsAllGroupID(this->vli.index)) {
    00421           SetDParam(0, STR_COMPANY_NAME);
    00422           SetDParam(1, this->vli.company);
    00423           SetDParam(2, this->vehicles.Length());
    00424           SetDParam(3, this->vehicles.Length());
    00425         } else {
    00426           const Group *g = Group::Get(this->vli.index);
    00427
    00428           SetDParam(0, STR_GROUP_NAME);
    00429           SetDParam(1, g->index);
    00430           SetDParam(2, g->statistics.num_vehicle);
    00431           SetDParam(3, g->statistics.num_vehicle);
    00432         }
    00433         break;
    00434     }
    00435   }
    00436
    00437   virtual void OnPaint()
    00438   {
    00439     /* If we select the all vehicles, this->list will contain all vehicles of the owner
    00440      * else this->list will contain all vehicles which belong to the selected group */
    00441     this->BuildVehicleList();
    00442     this->SortVehicleList();
    00443
    00444     this->BuildGroupList(this->owner);
    00445     this->groups.Sort(&GroupNameSorter);
    00446
    00447     this->group_sb->SetCount(this->groups.Length());
    00448     this->vscroll->SetCount(this->vehicles.Length());
    00449
    00450     /* The drop down menu is out, *but* it may not be used, retract it. */
    00451     if (this->vehicles.Length() == 0 && this->IsWidgetLowered(WID_GL_MANAGE_VEHICLES_DROPDOWN)) {
    00452       this->RaiseWidget(WID_GL_MANAGE_VEHICLES_DROPDOWN);
    00453       HideDropDownMenu(this);
    00454     }
    00455
    00456     /* Disable all lists management button when the list is empty */
    00457     this->SetWidgetsDisabledState(this->vehicles.Length() == 0 || _local_company != this->vli.company,
    00458         WID_GL_STOP_ALL,
    00459         WID_GL_START_ALL,
    00460         WID_GL_MANAGE_VEHICLES_DROPDOWN,
    00461         WIDGET_LIST_END);
    00462
    00463     /* Disable the group specific function when we select the default group or all vehicles */
    00464     this->SetWidgetsDisabledState(IsDefaultGroupID(this->vli.index) || IsAllGroupID(this->vli.index) || _local_company != this->vli.company,
    00465         WID_GL_DELETE_GROUP,
    00466         WID_GL_RENAME_GROUP,
    00467         WID_GL_REPLACE_PROTECTION,
    00468         WIDGET_LIST_END);
    00469
    00470     /* Disable remaining buttons for non-local companies
    00471      * Needed while changing _local_company, eg. by cheats
    00472      * All procedures (eg. move vehicle to another group)
    00473      *  verify, whether you are the owner of the vehicle,
    00474      *  so it doesn't have to be disabled
    00475      */
    00476     this->SetWidgetsDisabledState(_local_company != this->vli.company,
    00477         WID_GL_CREATE_GROUP,
    00478         WID_GL_AVAILABLE_VEHICLES,
    00479         WIDGET_LIST_END);
    00480
    00481     /* If not a default group and the group has replace protection, show an enabled replace sprite. */
    00482     uint16 protect_sprite = SPR_GROUP_REPLACE_OFF_TRAIN;
    00483     if (!IsDefaultGroupID(this->vli.index) && !IsAllGroupID(this->vli.index) && Group::Get(this->vli.index)->replace_protection) protect_sprite = SPR_GROUP_REPLACE_ON_TRAIN;
    00484     this->GetWidget<NWidgetCore>(WID_GL_REPLACE_PROTECTION)->widget_data = protect_sprite + this->vli.vtype;
    00485
    00486     /* Set text of sort by dropdown */
    00487     this->GetWidget<NWidgetCore>(WID_GL_SORT_BY_DROPDOWN)->widget_data = this->vehicle_sorter_names[this->vehicles.SortType()];
    00488
    00489     this->DrawWidgets();
    00490   }
    00491
    00492   virtual void DrawWidget(const Rect &r, int widget) const
    00493   {
    00494     switch (widget) {
    00495       case WID_GL_ALL_VEHICLES:
    00496         DrawGroupInfo(r.top + WD_FRAMERECT_TOP, r.left, r.right, ALL_GROUP);
    00497         break;
    00498
    00499       case WID_GL_DEFAULT_VEHICLES:
    00500         DrawGroupInfo(r.top + WD_FRAMERECT_TOP, r.left, r.right, DEFAULT_GROUP);
    00501         break;
    00502
    00503       case WID_GL_LIST_GROUP: {
    00504         int y1 = r.top + WD_FRAMERECT_TOP;
    00505         int max = min(this->group_sb->GetPosition() + this->group_sb->GetCapacity(), this->groups.Length());
    00506         for (int i = this->group_sb->GetPosition(); i < max; ++i) {
    00507           const Group *g = this->groups[i];
    00508
    00509           assert(g->owner == this->owner);
    00510
    00511           DrawGroupInfo(y1, r.left, r.right, g->index, g->replace_protection);
    00512
    00513           y1 += this->tiny_step_height;
    00514         }
    00515         if ((uint)this->group_sb->GetPosition() + this->group_sb->GetCapacity() > this->groups.Length()) {
    00516           DrawGroupInfo(y1, r.left, r.right, NEW_GROUP);
    00517         }
    00518         break;
    00519       }
    00520
    00521       case WID_GL_SORT_BY_ORDER:
    00522         this->DrawSortButtonState(WID_GL_SORT_BY_ORDER, this->vehicles.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
    00523         break;
    00524
    00525       case WID_GL_LIST_VEHICLE:
    00526         this->DrawVehicleListItems(this->vehicle_sel, this->resize.step_height, r);
    00527         break;
    00528     }
    00529   }
    00530
    00531   virtual void OnClick(Point pt, int widget, int click_count)
    00532   {
    00533     switch (widget) {
    00534       case WID_GL_SORT_BY_ORDER: // Flip sorting method ascending/descending
    00535         this->vehicles.ToggleSortOrder();
    00536         this->SetDirty();
    00537         break;
    00538
    00539       case WID_GL_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
    00540         ShowDropDownMenu(this, this->vehicle_sorter_names, this->vehicles.SortType(),  WID_GL_SORT_BY_DROPDOWN, 0, (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
    00541         return;
    00542
    00543       case WID_GL_ALL_VEHICLES: // All vehicles button
    00544         if (!IsAllGroupID(this->vli.index)) {
    00545           this->vli.index = ALL_GROUP;
    00546           this->vehicles.ForceRebuild();
    00547           this->SetDirty();
    00548         }
    00549         break;
    00550
    00551       case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles button
    00552         if (!IsDefaultGroupID(this->vli.index)) {
    00553           this->vli.index = DEFAULT_GROUP;
    00554           this->vehicles.ForceRebuild();
    00555           this->SetDirty();
    00556         }
    00557         break;
    00558
    00559       case WID_GL_LIST_GROUP: { // Matrix Group
    00560         uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height);
    00561         if (id_g >= this->groups.Length()) return;
    00562
    00563         this->vli.index = this->groups[id_g]->index;
    00564
    00565         this->vehicles.ForceRebuild();
    00566         this->SetDirty();
    00567         break;
    00568       }
    00569
    00570       case WID_GL_LIST_VEHICLE: { // Matrix Vehicle
    00571         uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_VEHICLE);
    00572         if (id_v >= this->vehicles.Length()) return; // click out of list bound
    00573
    00574         const Vehicle *v = this->vehicles[id_v];
    00575         if (VehicleClicked(v)) break;
    00576
    00577         this->vehicle_sel = v->index;
    00578
    00579         int image = v->GetImage(_current_text_dir == TD_RTL ? DIR_E : DIR_W, EIT_IN_LIST);
    00580         SetObjectToPlaceWnd(image, GetVehiclePalette(v), HT_DRAG, this);
    00581         _cursor.vehchain = true;
    00582
    00583         this->SetDirty();
    00584         break;
    00585       }
    00586
    00587       case WID_GL_CREATE_GROUP: { // Create a new group
    00588         DoCommandP(0, this->vli.vtype, 0, CMD_CREATE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_CREATE), CcCreateGroup);
    00589         break;
    00590       }
    00591
    00592       case WID_GL_DELETE_GROUP: { // Delete the selected group
    00593         GroupID group = this->vli.index;
    00594         this->vli.index = ALL_GROUP;
    00595
    00596         DoCommandP(0, group, 0, CMD_DELETE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_DELETE));
    00597         break;
    00598       }
    00599
    00600       case WID_GL_RENAME_GROUP: // Rename the selected roup
    00601         this->ShowRenameGroupWindow(this->vli.index, false);
    00602         break;
    00603
    00604       case WID_GL_AVAILABLE_VEHICLES:
    00605         ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
    00606         break;
    00607
    00608       case WID_GL_MANAGE_VEHICLES_DROPDOWN: {
    00609         DropDownList *list = this->BuildActionDropdownList(true, Group::IsValidID(this->vli.index));
    00610         ShowDropDownList(this, list, 0, WID_GL_MANAGE_VEHICLES_DROPDOWN);
    00611         break;
    00612       }
    00613
    00614       case WID_GL_START_ALL:
    00615       case WID_GL_STOP_ALL: { // Start/stop all vehicles of the list
    00616         DoCommandP(0, (1 << 1) | (widget == WID_GL_START_ALL ? (1 << 0) : 0), this->vli.Pack(), CMD_MASS_START_STOP);
    00617         break;
    00618       }
    00619
    00620       case WID_GL_REPLACE_PROTECTION: {
    00621         const Group *g = Group::GetIfValid(this->vli.index);
    00622         if (g != NULL) {
    00623           DoCommandP(0, this->vli.index, !g->replace_protection, CMD_SET_GROUP_REPLACE_PROTECTION);
    00624         }
    00625         break;
    00626       }
    00627     }
    00628   }
    00629
    00630   virtual void OnDragDrop(Point pt, int widget)
    00631   {
    00632     switch (widget) {
    00633       case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles
    00634         DoCommandP(0, DEFAULT_GROUP, this->vehicle_sel | (_ctrl_pressed ? 1 << 31 : 0), CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE));
    00635
    00636         this->vehicle_sel = INVALID_VEHICLE;
    00637         this->group_over = INVALID_GROUP;
    00638
    00639         this->SetDirty();
    00640         break;
    00641
    00642       case WID_GL_LIST_GROUP: { // Matrix group
    00643         const VehicleID vindex = this->vehicle_sel;
    00644         this->vehicle_sel = INVALID_VEHICLE;
    00645         this->group_over = INVALID_GROUP;
    00646         this->SetDirty();
    00647
    00648         uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height);
    00649         GroupID new_g = id_g >= this->groups.Length() ? NEW_GROUP : this->groups[id_g]->index;
    00650
    00651         DoCommandP(0, new_g, vindex | (_ctrl_pressed ? 1 << 31 : 0), CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE), new_g == NEW_GROUP ? CcAddVehicleNewGroup : NULL);
    00652         break;
    00653       }
    00654
    00655       case WID_GL_LIST_VEHICLE: { // Matrix vehicle
    00656         const VehicleID vindex = this->vehicle_sel;
    00657         this->vehicle_sel = INVALID_VEHICLE;
    00658         this->group_over = INVALID_GROUP;
    00659         this->SetDirty();
    00660
    00661         uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_VEHICLE);
    00662         if (id_v >= this->vehicles.Length()) return; // click out of list bound
    00663
    00664         const Vehicle *v = this->vehicles[id_v];
    00665         if (!VehicleClicked(v) && vindex == v->index) {
    00666           ShowVehicleViewWindow(v);
    00667         }
    00668         break;
    00669       }
    00670     }
    00671     _cursor.vehchain = false;
    00672   }
    00673
    00674   virtual void OnQueryTextFinished(char *str)
    00675   {
    00676     if (str != NULL) DoCommandP(0, this->group_rename, 0, CMD_RENAME_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_RENAME), NULL, str);
    00677     this->group_rename = INVALID_GROUP;
    00678   }
    00679
    00680   virtual void OnResize()
    00681   {
    00682     this->group_sb->SetCapacityFromWidget(this, WID_GL_LIST_GROUP);
    00683     this->vscroll->SetCapacityFromWidget(this, WID_GL_LIST_VEHICLE);
    00684   }
    00685
    00686   virtual void OnDropdownSelect(int widget, int index)
    00687   {
    00688     switch (widget) {
    00689       case WID_GL_SORT_BY_DROPDOWN:
    00690         this->vehicles.SetSortType(index);
    00691         break;
    00692
    00693       case WID_GL_MANAGE_VEHICLES_DROPDOWN:
    00694         assert(this->vehicles.Length() != 0);
    00695
    00696         switch (index) {
    00697           case ADI_REPLACE: // Replace window
    00698             ShowReplaceGroupVehicleWindow(this->vli.index, this->vli.vtype);
    00699             break;
    00700           case ADI_SERVICE: // Send for servicing
    00701           case ADI_DEPOT: { // Send to Depots
    00702             DoCommandP(0, DEPOT_MASS_SEND | (index == ADI_SERVICE ? DEPOT_SERVICE : 0U), this->vli.Pack(), GetCmdSendToDepot(this->vli.vtype));
    00703             break;
    00704           }
    00705
    00706           case ADI_ADD_SHARED: // Add shared Vehicles
    00707             assert(Group::IsValidID(this->vli.index));
    00708
    00709             DoCommandP(0, this->vli.index, this->vli.vtype, CMD_ADD_SHARED_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_SHARED_VEHICLE));
    00710             break;
    00711           case ADI_REMOVE_ALL: // Remove all Vehicles from the selected group
    00712             assert(Group::IsValidID(this->vli.index));
    00713
    00714             DoCommandP(0, this->vli.index, 0, CMD_REMOVE_ALL_VEHICLES_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_REMOVE_ALL_VEHICLES));
    00715             break;
    00716           default: NOT_REACHED();
    00717         }
    00718         break;
    00719
    00720       default: NOT_REACHED();
    00721     }
    00722
    00723     this->SetDirty();
    00724   }
    00725
    00726   virtual void OnTick()
    00727   {
    00728     if (_pause_mode != PM_UNPAUSED) return;
    00729     if (this->groups.NeedResort() || this->vehicles.NeedResort()) {
    00730       this->SetDirty();
    00731     }
    00732   }
    00733
    00734   virtual void OnPlaceObjectAbort()
    00735   {
    00736     /* abort drag & drop */
    00737     this->vehicle_sel = INVALID_VEHICLE;
    00738     this->DirtyHighlightedGroupWidget();
    00739     this->group_over = INVALID_GROUP;
    00740     this->SetWidgetDirty(WID_GL_LIST_VEHICLE);
    00741   }
    00742
    00743   virtual void OnMouseDrag(Point pt, int widget)
    00744   {
    00745     if (this->vehicle_sel == INVALID_VEHICLE) return;
    00746
    00747     /* A vehicle is dragged over... */
    00748     GroupID new_group_over = INVALID_GROUP;
    00749     switch (widget) {
    00750       case WID_GL_DEFAULT_VEHICLES: // ... the 'default' group.
    00751         new_group_over = DEFAULT_GROUP;
    00752         break;
    00753
    00754       case WID_GL_LIST_GROUP: { // ... the list of custom groups.
    00755         uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height);
    00756         new_group_over = id_g >= this->groups.Length() ? NEW_GROUP : this->groups[id_g]->index;
    00757         break;
    00758       }
    00759     }
    00760
    00761     /* Do not highlight when dragging over the current group */
    00762     if (Vehicle::Get(vehicle_sel)->group_id == new_group_over) new_group_over = INVALID_GROUP;
    00763
    00764     /* Mark widgets as dirty if the group changed. */
    00765     if (new_group_over != this->group_over) {
    00766       this->DirtyHighlightedGroupWidget();
    00767       this->group_over = new_group_over;
    00768       this->DirtyHighlightedGroupWidget();
    00769     }
    00770   }
    00771
    00772   void ShowRenameGroupWindow(GroupID group, bool empty)
    00773   {
    00774     assert(Group::IsValidID(group));
    00775     this->group_rename = group;
    00776     /* Show empty query for new groups */
    00777     StringID str = STR_EMPTY;
    00778     if (!empty) {
    00779       SetDParam(0, group);
    00780       str = STR_GROUP_NAME;
    00781     }
    00782     ShowQueryString(str, STR_GROUP_RENAME_CAPTION, MAX_LENGTH_GROUP_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
    00783   }
    00784
    00790   void UnselectVehicle(VehicleID vehicle)
    00791   {
    00792     if (this->vehicle_sel == vehicle) ResetObjectToPlace();
    00793   }
    00794 };
    00795
    00796
    00797 static WindowDesc _other_group_desc(
    00798   WDP_AUTO, "list_groups", 460, 246,
    00799   WC_INVALID, WC_NONE,
    00800   0,
    00801   _nested_group_widgets, lengthof(_nested_group_widgets)
    00802 );
    00803
    00804 static WindowDesc _train_group_desc(
    00805   WDP_AUTO, "list_groups_train", 525, 246,
    00806   WC_TRAINS_LIST, WC_NONE,
    00807   0,
    00808   _nested_group_widgets, lengthof(_nested_group_widgets)
    00809 );
    00810
    00816 void ShowCompanyGroup(CompanyID company, VehicleType vehicle_type)
    00817 {
    00818   if (!Company::IsValidID(company)) return;
    00819
    00820   WindowNumber num = VehicleListIdentifier(VL_GROUP_LIST, vehicle_type, company).Pack();
    00821   if (vehicle_type == VEH_TRAIN) {
    00822     AllocateWindowDescFront<VehicleGroupWindow>(&_train_group_desc, num);
    00823   } else {
    00824     _other_group_desc.cls = GetWindowClassForVehicleType(vehicle_type);
    00825     AllocateWindowDescFront<VehicleGroupWindow>(&_other_group_desc, num);
    00826   }
    00827 }
    00828
    00835 static inline VehicleGroupWindow *FindVehicleGroupWindow(VehicleType vt, Owner owner)
    00836 {
    00837   return (VehicleGroupWindow *)FindWindowById(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, owner).Pack());
    00838 }
    00839
    00848 void CcCreateGroup(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
    00849 {
    00850   if (result.Failed()) return;
    00851   assert(p1 <= VEH_AIRCRAFT);
    00852
    00853   VehicleGroupWindow *w = FindVehicleGroupWindow((VehicleType)p1, _current_company);
    00854   if (w != NULL) w->ShowRenameGroupWindow(_new_group_id, true);
    00855 }
    00856
    00864 void CcAddVehicleNewGroup(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
    00865 {
    00866   if (result.Failed()) return;
    00867   assert(Vehicle::IsValidID(GB(p2, 0, 20)));
    00868
    00869   CcCreateGroup(result, 0, Vehicle::Get(GB(p2, 0, 20))->type, 0);
    00870 }
    00871
    00876 void DeleteGroupHighlightOfVehicle(const Vehicle *v)
    00877 {
    00878   /* If we haven't got any vehicles on the mouse pointer, we haven't got any highlighted in any group windows either
    00879    * If that is the case, we can skip looping though the windows and save time
    00880    */
    00881   if (_special_mouse_mode != WSM_DRAGDROP) return;
    00882
    00883   VehicleGroupWindow *w = FindVehicleGroupWindow(v->type, v->owner);
    00884   if (w != NULL) w->UnselectVehicle(v->index);
    00885 }