# group_gui.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    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 "group.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 "table/strings.h"
    00030 #include "table/sprites.h"
    00031
    00032 typedef GUIList<const Group*> GUIGroupList;
    00033
    00034 enum GroupListWidgets {
    00035   GRP_WIDGET_CAPTION,
    00036   GRP_WIDGET_SORT_BY_ORDER,
    00037   GRP_WIDGET_SORT_BY_DROPDOWN,
    00038   GRP_WIDGET_LIST_VEHICLE,
    00039   GRP_WIDGET_LIST_VEHICLE_SCROLLBAR,
    00040   GRP_WIDGET_AVAILABLE_VEHICLES,
    00041   GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
    00042   GRP_WIDGET_STOP_ALL,
    00043   GRP_WIDGET_START_ALL,
    00044
    00045   GRP_WIDGET_ALL_VEHICLES,
    00046   GRP_WIDGET_DEFAULT_VEHICLES,
    00047   GRP_WIDGET_LIST_GROUP,
    00048   GRP_WIDGET_LIST_GROUP_SCROLLBAR,
    00049   GRP_WIDGET_CREATE_GROUP,
    00050   GRP_WIDGET_DELETE_GROUP,
    00051   GRP_WIDGET_RENAME_GROUP,
    00052   GRP_WIDGET_REPLACE_PROTECTION,
    00053 };
    00054
    00055 static const NWidgetPart _nested_group_widgets[] = {
    00056   NWidget(NWID_HORIZONTAL), // Window header
    00057     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00058     NWidget(WWT_CAPTION, COLOUR_GREY, GRP_WIDGET_CAPTION),
    00059     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00060     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00061   EndContainer(),
    00062   NWidget(NWID_HORIZONTAL),
    00063     /* left part */
    00064     NWidget(NWID_VERTICAL),
    00065       NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetFill(1, 0), EndContainer(),
    00066       NWidget(WWT_PANEL, COLOUR_GREY, GRP_WIDGET_ALL_VEHICLES), SetMinimalSize(200, 13), SetFill(1, 0), EndContainer(),
    00067       NWidget(WWT_PANEL, COLOUR_GREY, GRP_WIDGET_DEFAULT_VEHICLES), SetMinimalSize(200, 13), SetFill(1, 0), EndContainer(),
    00068       NWidget(NWID_HORIZONTAL),
    00069         NWidget(WWT_MATRIX, COLOUR_GREY, GRP_WIDGET_LIST_GROUP), SetMinimalSize(188, 0), SetDataTip(0x701, STR_GROUPS_CLICK_ON_GROUP_FOR_TOOLTIP),
    00070             SetFill(1, 0), SetResize(0, 1), SetScrollbar(GRP_WIDGET_LIST_GROUP_SCROLLBAR),
    00071         NWidget(NWID_VSCROLLBAR, COLOUR_GREY, GRP_WIDGET_LIST_GROUP_SCROLLBAR),
    00072       EndContainer(),
    00073       NWidget(NWID_HORIZONTAL),
    00074         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_CREATE_GROUP), SetMinimalSize(24, 25), SetFill(0, 1),
    00075             SetDataTip(SPR_GROUP_CREATE_TRAIN, STR_GROUP_CREATE_TOOLTIP),
    00076         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_DELETE_GROUP), SetMinimalSize(24, 25), SetFill(0, 1),
    00077             SetDataTip(SPR_GROUP_DELETE_TRAIN, STR_GROUP_DELETE_TOOLTIP),
    00078         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_RENAME_GROUP), SetMinimalSize(24, 25), SetFill(0, 1),
    00079             SetDataTip(SPR_GROUP_RENAME_TRAIN, STR_GROUP_RENAME_TOOLTIP),
    00080         NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(92, 25), SetFill(1, 1), EndContainer(),
    00081         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_REPLACE_PROTECTION), SetMinimalSize(24, 25), SetFill(0, 1),
    00082             SetDataTip(SPR_GROUP_REPLACE_OFF_TRAIN, STR_GROUP_REPLACE_PROTECTION_TOOLTIP),
    00083         NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 25), SetFill(0, 1), EndContainer(),
    00084       EndContainer(),
    00085     EndContainer(),
    00086     /* right part */
    00087     NWidget(NWID_VERTICAL),
    00088       NWidget(NWID_HORIZONTAL),
    00089         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, GRP_WIDGET_SORT_BY_ORDER), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
    00090         NWidget(WWT_DROPDOWN, COLOUR_GREY, GRP_WIDGET_SORT_BY_DROPDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
    00091         NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetResize(1, 0), EndContainer(),
    00092       EndContainer(),
    00093       NWidget(NWID_HORIZONTAL),
    00094         NWidget(WWT_MATRIX, COLOUR_GREY, GRP_WIDGET_LIST_VEHICLE), SetMinimalSize(248, 0), SetDataTip(0x701, STR_NULL), SetResize(1, 1), SetFill(1, 0), SetScrollbar(GRP_WIDGET_LIST_VEHICLE_SCROLLBAR),
    00095         NWidget(NWID_VSCROLLBAR, COLOUR_GREY, GRP_WIDGET_LIST_VEHICLE_SCROLLBAR),
    00096       EndContainer(),
    00097       NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(1, 0), SetFill(1, 1), SetResize(1, 0), EndContainer(),
    00098       NWidget(NWID_HORIZONTAL),
    00099         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, GRP_WIDGET_AVAILABLE_VEHICLES), SetMinimalSize(106, 12), SetFill(0, 1),
    00100             SetDataTip(STR_BLACK_STRING, STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP),
    00101         NWidget(WWT_DROPDOWN, COLOUR_GREY, GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN), SetMinimalSize(118, 12), SetFill(0, 1),
    00102             SetDataTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP),
    00103         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_STOP_ALL), SetMinimalSize(12, 12), SetFill(0, 1),
    00104             SetDataTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP),
    00105         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, GRP_WIDGET_START_ALL), SetMinimalSize(12, 12), SetFill(0, 1),
    00106             SetDataTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP),
    00107         NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
    00108         NWidget(WWT_RESIZEBOX, COLOUR_GREY),
    00109       EndContainer(),
    00110     EndContainer(),
    00111   EndContainer(),
    00112 };
    00113
    00114 class VehicleGroupWindow : public BaseVehicleListWindow {
    00115 private:
    00116   VehicleID vehicle_sel;
    00117   GroupID group_rename;
    00118   GUIGroupList groups;
    00119   uint tiny_step_height;
    00120   Scrollbar *group_sb;
    00121
    00127   void BuildGroupList(Owner owner)
    00128   {
    00129     if (!this->groups.NeedRebuild()) return;
    00130
    00131     this->groups.Clear();
    00132
    00133     const Group *g;
    00134     FOR_ALL_GROUPS(g) {
    00135       if (g->owner == owner && g->vehicle_type == this->vli.vtype) {
    00136         *this->groups.Append() = g;
    00137       }
    00138     }
    00139
    00140     this->groups.Compact();
    00141     this->groups.RebuildDone();
    00142   }
    00143
    00145   static int CDECL GroupNameSorter(const Group * const *a, const Group * const *b)
    00146   {
    00147     static const Group *last_group[2] = { NULL, NULL };
    00148     static char         last_name[2][64] = { "", "" };
    00149
    00150     if (*a != last_group[0]) {
    00151       last_group[0] = *a;
    00152       SetDParam(0, (*a)->index);
    00153       GetString(last_name[0], STR_GROUP_NAME, lastof(last_name[0]));
    00154     }
    00155
    00156     if (*b != last_group[1]) {
    00157       last_group[1] = *b;
    00158       SetDParam(0, (*b)->index);
    00159       GetString(last_name[1], STR_GROUP_NAME, lastof(last_name[1]));
    00160     }
    00161
    00162     int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
    00163     if (r == 0) return (*a)->index - (*b)->index;
    00164     return r;
    00165   }
    00166
    00167 public:
    00168   VehicleGroupWindow(const WindowDesc *desc, WindowNumber window_number) : BaseVehicleListWindow(window_number)
    00169   {
    00170     this->CreateNestedTree(desc);
    00171
    00172     this->vscroll = this->GetScrollbar(GRP_WIDGET_LIST_VEHICLE_SCROLLBAR);
    00173     this->group_sb = this->GetScrollbar(GRP_WIDGET_LIST_GROUP_SCROLLBAR);
    00174
    00175     switch (this->vli.vtype) {
    00176       default: NOT_REACHED();
    00177       case VEH_TRAIN:    this->sorting = &_sorting.train;    break;
    00178       case VEH_ROAD:     this->sorting = &_sorting.roadveh;  break;
    00179       case VEH_SHIP:     this->sorting = &_sorting.ship;     break;
    00180       case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break;
    00181     }
    00182
    00183     this->vli.index = ALL_GROUP;
    00184     this->vehicle_sel = INVALID_VEHICLE;
    00185     this->group_rename = INVALID_GROUP;
    00186
    00187     this->vehicles.SetListing(*this->sorting);
    00188     this->vehicles.ForceRebuild();
    00189     this->vehicles.NeedResort();
    00190
    00191     this->BuildVehicleList();
    00192     this->SortVehicleList();
    00193
    00194     this->groups.ForceRebuild();
    00195     this->groups.NeedResort();
    00196     this->BuildGroupList(vli.company);
    00197     this->groups.Sort(&GroupNameSorter);
    00198
    00199     this->GetWidget<NWidgetCore>(GRP_WIDGET_CAPTION)->widget_data = STR_VEHICLE_LIST_TRAIN_CAPTION + this->vli.vtype;
    00200     this->GetWidget<NWidgetCore>(GRP_WIDGET_LIST_VEHICLE)->tool_tip = STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP + this->vli.vtype;
    00201
    00202     this->GetWidget<NWidgetCore>(GRP_WIDGET_CREATE_GROUP)->widget_data += this->vli.vtype;
    00203     this->GetWidget<NWidgetCore>(GRP_WIDGET_RENAME_GROUP)->widget_data += this->vli.vtype;
    00204     this->GetWidget<NWidgetCore>(GRP_WIDGET_DELETE_GROUP)->widget_data += this->vli.vtype;
    00205     this->GetWidget<NWidgetCore>(GRP_WIDGET_REPLACE_PROTECTION)->widget_data += this->vli.vtype;
    00206
    00207     this->FinishInitNested(desc, window_number);
    00208     this->owner = vli.company;
    00209   }
    00210
    00211   ~VehicleGroupWindow()
    00212   {
    00213     *this->sorting = this->vehicles.GetListing();
    00214   }
    00215
    00216   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00217   {
    00218     switch (widget) {
    00219       case GRP_WIDGET_LIST_GROUP:
    00220         this->tiny_step_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP;
    00221         resize->height = this->tiny_step_height;
    00222         /* Minimum height is the height of the list widget minus all and default vehicles and a bit for the bottom bar */
    00223         size->height =  4 * GetVehicleListHeight(this->vli.vtype, this->tiny_step_height) - (this->tiny_step_height > 25 ? 2 : 3) * this->tiny_step_height;
    00224         break;
    00225
    00226       case GRP_WIDGET_ALL_VEHICLES:
    00227       case GRP_WIDGET_DEFAULT_VEHICLES:
    00228         size->height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP;
    00229         size->width = max(GetStringBoundingBox(STR_GROUP_DEFAULT_TRAINS + this->vli.vtype).width, GetStringBoundingBox(STR_GROUP_ALL_TRAINS + this->vli.vtype).width);
    00230         size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + 8 + 8;
    00231         break;
    00232
    00233       case GRP_WIDGET_SORT_BY_ORDER: {
    00234         Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
    00235         d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better.
    00236         d.height += padding.height;
    00237         *size = maxdim(*size, d);
    00238         break;
    00239       }
    00240
    00241       case GRP_WIDGET_LIST_VEHICLE:
    00242         resize->height = GetVehicleListHeight(this->vli.vtype, FONT_HEIGHT_NORMAL + WD_MATRIX_TOP);
    00243         size->height = 4 * resize->height;
    00244         break;
    00245
    00246       case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
    00247         Dimension d = this->GetActionDropdownSize(true, true);
    00248         d.height += padding.height;
    00249         d.width  += padding.width;
    00250         *size = maxdim(*size, d);
    00251         break;
    00252       }
    00253     }
    00254   }
    00255
    00261   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00262   {
    00263     if (data == 0) {
    00264       /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
    00265       this->vehicles.ForceRebuild();
    00266       this->groups.ForceRebuild();
    00267     } else {
    00268       this->vehicles.ForceResort();
    00269       this->groups.ForceResort();
    00270     }
    00271
    00272     /* Process ID-invalidation in command-scope as well */
    00273     if (this->group_rename != INVALID_GROUP && !Group::IsValidID(this->group_rename)) {
    00274       DeleteWindowByClass(WC_QUERY_STRING);
    00275       this->group_rename = INVALID_GROUP;
    00276     }
    00277
    00278     if (!(IsAllGroupID(this->vli.index) || IsDefaultGroupID(this->vli.index) || Group::IsValidID(this->vli.index))) {
    00279       this->vli.index = ALL_GROUP;
    00280       HideDropDownMenu(this);
    00281     }
    00282     this->SetDirty();
    00283   }
    00284
    00285   virtual void SetStringParameters(int widget) const
    00286   {
    00287     switch (widget) {
    00288       case GRP_WIDGET_AVAILABLE_VEHICLES:
    00289         SetDParam(0, STR_VEHICLE_LIST_AVAILABLE_TRAINS + this->vli.vtype);
    00290         break;
    00291
    00292       case GRP_WIDGET_CAPTION:
    00293         /* If selected_group == DEFAULT_GROUP || ALL_GROUP, draw the standard caption
    00294          * We list all vehicles or ungrouped vehicles */
    00295         if (IsDefaultGroupID(this->vli.index) || IsAllGroupID(this->vli.index)) {
    00296           SetDParam(0, STR_COMPANY_NAME);
    00297           SetDParam(1, this->vli.company);
    00298           SetDParam(2, this->vehicles.Length());
    00299           SetDParam(3, this->vehicles.Length());
    00300         } else {
    00301           const Group *g = Group::Get(this->vli.index);
    00302
    00303           SetDParam(0, STR_GROUP_NAME);
    00304           SetDParam(1, g->index);
    00305           SetDParam(2, g->num_vehicle);
    00306           SetDParam(3, g->num_vehicle);
    00307         }
    00308         break;
    00309     }
    00310   }
    00311
    00312   virtual void OnPaint()
    00313   {
    00314     /* If we select the all vehicles, this->list will contain all vehicles of the owner
    00315      * else this->list will contain all vehicles which belong to the selected group */
    00316     this->BuildVehicleList();
    00317     this->SortVehicleList();
    00318
    00319     this->BuildGroupList(this->owner);
    00320     this->groups.Sort(&GroupNameSorter);
    00321
    00322     this->group_sb->SetCount(this->groups.Length());
    00323     this->vscroll->SetCount(this->vehicles.Length());
    00324
    00325     /* The drop down menu is out, *but* it may not be used, retract it. */
    00326     if (this->vehicles.Length() == 0 && this->IsWidgetLowered(GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN)) {
    00327       this->RaiseWidget(GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN);
    00328       HideDropDownMenu(this);
    00329     }
    00330
    00331     /* Disable all lists management button when the list is empty */
    00332     this->SetWidgetsDisabledState(this->vehicles.Length() == 0 || _local_company != this->vli.company,
    00333         GRP_WIDGET_STOP_ALL,
    00334         GRP_WIDGET_START_ALL,
    00335         GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
    00336         WIDGET_LIST_END);
    00337
    00338     /* Disable the group specific function when we select the default group or all vehicles */
    00339     this->SetWidgetsDisabledState(IsDefaultGroupID(this->vli.index) || IsAllGroupID(this->vli.index) || _local_company != this->vli.company,
    00340         GRP_WIDGET_DELETE_GROUP,
    00341         GRP_WIDGET_RENAME_GROUP,
    00342         GRP_WIDGET_REPLACE_PROTECTION,
    00343         WIDGET_LIST_END);
    00344
    00345     /* Disable remaining buttons for non-local companies
    00346      * Needed while changing _local_company, eg. by cheats
    00347      * All procedures (eg. move vehicle to another group)
    00348      *  verify, whether you are the owner of the vehicle,
    00349      *  so it doesn't have to be disabled
    00350      */
    00351     this->SetWidgetsDisabledState(_local_company != this->vli.company,
    00352         GRP_WIDGET_CREATE_GROUP,
    00353         GRP_WIDGET_AVAILABLE_VEHICLES,
    00354         WIDGET_LIST_END);
    00355
    00356     /* If not a default group and the group has replace protection, show an enabled replace sprite. */
    00357     uint16 protect_sprite = SPR_GROUP_REPLACE_OFF_TRAIN;
    00358     if (!IsDefaultGroupID(this->vli.index) && !IsAllGroupID(this->vli.index) && Group::Get(this->vli.index)->replace_protection) protect_sprite = SPR_GROUP_REPLACE_ON_TRAIN;
    00359     this->GetWidget<NWidgetCore>(GRP_WIDGET_REPLACE_PROTECTION)->widget_data = protect_sprite + this->vli.vtype;
    00360
    00361     /* Set text of sort by dropdown */
    00362     this->GetWidget<NWidgetCore>(GRP_WIDGET_SORT_BY_DROPDOWN)->widget_data = this->vehicle_sorter_names[this->vehicles.SortType()];
    00363
    00364     this->DrawWidgets();
    00365   }
    00366
    00367   virtual void DrawWidget(const Rect &r, int widget) const
    00368   {
    00369     switch (widget) {
    00370       case GRP_WIDGET_ALL_VEHICLES:
    00371         DrawString(r.left + WD_FRAMERECT_LEFT + 8, r.right - WD_FRAMERECT_RIGHT - 8, r.top + WD_FRAMERECT_TOP + 1,
    00372             STR_GROUP_ALL_TRAINS + this->vli.vtype, IsAllGroupID(this->vli.index) ? TC_WHITE : TC_BLACK);
    00373         break;
    00374
    00375       case GRP_WIDGET_DEFAULT_VEHICLES:
    00376         DrawString(r.left + WD_FRAMERECT_LEFT + 8, r.right - WD_FRAMERECT_RIGHT - 8, r.top + WD_FRAMERECT_TOP + 1,
    00377             STR_GROUP_DEFAULT_TRAINS + this->vli.vtype, IsDefaultGroupID(this->vli.index) ? TC_WHITE : TC_BLACK);
    00378         break;
    00379
    00380       case GRP_WIDGET_LIST_GROUP: {
    00381         int y1 = r.top + WD_FRAMERECT_TOP + 1;
    00382         int max = min(this->group_sb->GetPosition() + this->group_sb->GetCapacity(), this->groups.Length());
    00383         for (int i = this->group_sb->GetPosition(); i < max; ++i) {
    00384           const Group *g = this->groups[i];
    00385
    00386           assert(g->owner == this->owner);
    00387
    00388           /* draw the selected group in white, else we draw it in black */
    00389           SetDParam(0, g->index);
    00390           DrawString(r.left + WD_FRAMERECT_LEFT + 8, r.right - WD_FRAMERECT_RIGHT - 8, y1, STR_GROUP_NAME, (this->vli.index == g->index) ? TC_WHITE : TC_BLACK);
    00391
    00392           /* draw the number of vehicles of the group */
    00393           SetDParam(0, g->num_vehicle);
    00394           DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y1 + 1, STR_TINY_COMMA, (this->vli.index == g->index) ? TC_WHITE : TC_BLACK, SA_RIGHT);
    00395
    00396           y1 += this->tiny_step_height;
    00397         }
    00398         break;
    00399       }
    00400
    00401       case GRP_WIDGET_SORT_BY_ORDER:
    00402         this->DrawSortButtonState(GRP_WIDGET_SORT_BY_ORDER, this->vehicles.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
    00403         break;
    00404
    00405       case GRP_WIDGET_LIST_VEHICLE:
    00406         this->DrawVehicleListItems(this->vehicle_sel, this->resize.step_height, r);
    00407         break;
    00408     }
    00409   }
    00410
    00411   virtual void OnClick(Point pt, int widget, int click_count)
    00412   {
    00413     switch (widget) {
    00414       case GRP_WIDGET_SORT_BY_ORDER: // Flip sorting method ascending/descending
    00415         this->vehicles.ToggleSortOrder();
    00416         this->SetDirty();
    00417         break;
    00418
    00419       case GRP_WIDGET_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
    00420         ShowDropDownMenu(this, this->vehicle_sorter_names, this->vehicles.SortType(),  GRP_WIDGET_SORT_BY_DROPDOWN, 0, (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
    00421         return;
    00422
    00423       case GRP_WIDGET_ALL_VEHICLES: // All vehicles button
    00424         if (!IsAllGroupID(this->vli.index)) {
    00425           this->vli.index = ALL_GROUP;
    00426           this->vehicles.ForceRebuild();
    00427           this->SetDirty();
    00428         }
    00429         break;
    00430
    00431       case GRP_WIDGET_DEFAULT_VEHICLES: // Ungrouped vehicles button
    00432         if (!IsDefaultGroupID(this->vli.index)) {
    00433           this->vli.index = DEFAULT_GROUP;
    00434           this->vehicles.ForceRebuild();
    00435           this->SetDirty();
    00436         }
    00437         break;
    00438
    00439       case GRP_WIDGET_LIST_GROUP: { // Matrix Group
    00440         uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, GRP_WIDGET_LIST_GROUP, 0, this->tiny_step_height);
    00441         if (id_g >= this->groups.Length()) return;
    00442
    00443         this->vli.index = this->groups[id_g]->index;
    00444
    00445         this->vehicles.ForceRebuild();
    00446         this->SetDirty();
    00447         break;
    00448       }
    00449
    00450       case GRP_WIDGET_LIST_VEHICLE: { // Matrix Vehicle
    00451         uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, GRP_WIDGET_LIST_VEHICLE);
    00452         if (id_v >= this->vehicles.Length()) return; // click out of list bound
    00453
    00454         const Vehicle *v = this->vehicles[id_v];
    00455         if (VehicleClicked(v)) break;
    00456
    00457         this->vehicle_sel = v->index;
    00458
    00459         int image = v->GetImage(_current_text_dir == TD_RTL ? DIR_E : DIR_W);
    00460         SetObjectToPlaceWnd(image, GetVehiclePalette(v), HT_DRAG, this);
    00461         _cursor.vehchain = true;
    00462
    00463         this->SetDirty();
    00464         break;
    00465       }
    00466
    00467       case GRP_WIDGET_CREATE_GROUP: { // Create a new group
    00468         DoCommandP(0, this->vli.vtype, 0, CMD_CREATE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_CREATE), CcCreateGroup);
    00469         break;
    00470       }
    00471
    00472       case GRP_WIDGET_DELETE_GROUP: { // Delete the selected group
    00473         GroupID group = this->vli.index;
    00474         this->vli.index = ALL_GROUP;
    00475
    00476         DoCommandP(0, group, 0, CMD_DELETE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_DELETE));
    00477         break;
    00478       }
    00479
    00480       case GRP_WIDGET_RENAME_GROUP: // Rename the selected roup
    00481         this->ShowRenameGroupWindow(this->vli.index, false);
    00482         break;
    00483
    00484       case GRP_WIDGET_AVAILABLE_VEHICLES:
    00485         ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
    00486         break;
    00487
    00488       case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
    00489         DropDownList *list = this->BuildActionDropdownList(true, Group::IsValidID(this->vli.index));
    00490         ShowDropDownList(this, list, 0, GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN);
    00491         break;
    00492       }
    00493
    00494       case GRP_WIDGET_START_ALL:
    00495       case GRP_WIDGET_STOP_ALL: { // Start/stop all vehicles of the list
    00496         DoCommandP(0, (1 << 1) | (widget == GRP_WIDGET_START_ALL ? (1 << 0) : 0), this->vli.Pack(), CMD_MASS_START_STOP);
    00497         break;
    00498       }
    00499
    00500       case GRP_WIDGET_REPLACE_PROTECTION: {
    00501         const Group *g = Group::GetIfValid(this->vli.index);
    00502         if (g != NULL) {
    00503           DoCommandP(0, this->vli.index, !g->replace_protection, CMD_SET_GROUP_REPLACE_PROTECTION);
    00504         }
    00505         break;
    00506       }
    00507     }
    00508   }
    00509
    00510   virtual void OnDragDrop(Point pt, int widget)
    00511   {
    00512     switch (widget) {
    00513       case GRP_WIDGET_ALL_VEHICLES: // All vehicles
    00514       case GRP_WIDGET_DEFAULT_VEHICLES: // Ungrouped vehicles
    00515         DoCommandP(0, DEFAULT_GROUP, this->vehicle_sel, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE));
    00516
    00517         this->vehicle_sel = INVALID_VEHICLE;
    00518
    00519         this->SetDirty();
    00520         break;
    00521
    00522       case GRP_WIDGET_LIST_GROUP: { // Matrix group
    00523         const VehicleID vindex = this->vehicle_sel;
    00524         this->vehicle_sel = INVALID_VEHICLE;
    00525         this->SetDirty();
    00526
    00527         uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, GRP_WIDGET_LIST_GROUP, 0, this->tiny_step_height);
    00528         if (id_g >= this->groups.Length()) return;
    00529
    00530         DoCommandP(0, this->groups[id_g]->index, vindex, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE));
    00531         break;
    00532       }
    00533
    00534       case GRP_WIDGET_LIST_VEHICLE: { // Matrix vehicle
    00535         const VehicleID vindex = this->vehicle_sel;
    00536         this->vehicle_sel = INVALID_VEHICLE;
    00537         this->SetDirty();
    00538
    00539         uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, GRP_WIDGET_LIST_VEHICLE);
    00540         if (id_v >= this->vehicles.Length()) return; // click out of list bound
    00541
    00542         const Vehicle *v = this->vehicles[id_v];
    00543         if (!VehicleClicked(v) && vindex == v->index) {
    00544           ShowVehicleViewWindow(v);
    00545         }
    00546         break;
    00547       }
    00548     }
    00549     _cursor.vehchain = false;
    00550   }
    00551
    00552   virtual void OnQueryTextFinished(char *str)
    00553   {
    00554     if (str != NULL) DoCommandP(0, this->group_rename, 0, CMD_RENAME_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_RENAME), NULL, str);
    00555     this->group_rename = INVALID_GROUP;
    00556   }
    00557
    00558   virtual void OnResize()
    00559   {
    00560     NWidgetCore *nwi = this->GetWidget<NWidgetCore>(GRP_WIDGET_LIST_GROUP);
    00561     this->group_sb->SetCapacity(nwi->current_y / this->tiny_step_height);
    00562     nwi->widget_data = (this->group_sb->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
    00563
    00564     nwi = this->GetWidget<NWidgetCore>(GRP_WIDGET_LIST_VEHICLE);
    00565     this->vscroll->SetCapacityFromWidget(this, GRP_WIDGET_LIST_VEHICLE);
    00566     nwi->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
    00567   }
    00568
    00569   virtual void OnDropdownSelect(int widget, int index)
    00570   {
    00571     switch (widget) {
    00572       case GRP_WIDGET_SORT_BY_DROPDOWN:
    00573         this->vehicles.SetSortType(index);
    00574         break;
    00575
    00576       case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN:
    00577         assert(this->vehicles.Length() != 0);
    00578
    00579         switch (index) {
    00580           case ADI_REPLACE: // Replace window
    00581             ShowReplaceGroupVehicleWindow(this->vli.index, this->vli.vtype);
    00582             break;
    00583           case ADI_SERVICE: // Send for servicing
    00584           case ADI_DEPOT: { // Send to Depots
    00585             DoCommandP(0, DEPOT_MASS_SEND | (index == ADI_SERVICE ? DEPOT_SERVICE : 0U), this->vli.Pack(), GetCmdSendToDepot(this->vli.vtype));
    00586             break;
    00587           }
    00588
    00589           case ADI_ADD_SHARED: // Add shared Vehicles
    00590             assert(Group::IsValidID(this->vli.index));
    00591
    00592             DoCommandP(0, this->vli.index, this->vli.vtype, CMD_ADD_SHARED_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_SHARED_VEHICLE));
    00593             break;
    00594           case ADI_REMOVE_ALL: // Remove all Vehicles from the selected group
    00595             assert(Group::IsValidID(this->vli.index));
    00596
    00597             DoCommandP(0, this->vli.index, this->vli.vtype, CMD_REMOVE_ALL_VEHICLES_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_REMOVE_ALL_VEHICLES));
    00598             break;
    00599           default: NOT_REACHED();
    00600         }
    00601         break;
    00602
    00603       default: NOT_REACHED();
    00604     }
    00605
    00606     this->SetDirty();
    00607   }
    00608
    00609   virtual void OnTick()
    00610   {
    00611     if (_pause_mode != PM_UNPAUSED) return;
    00612     if (this->groups.NeedResort() || this->vehicles.NeedResort()) {
    00613       this->SetDirty();
    00614     }
    00615   }
    00616
    00617   virtual void OnPlaceObjectAbort()
    00618   {
    00619     /* abort drag & drop */
    00620     this->vehicle_sel = INVALID_VEHICLE;
    00621     this->SetWidgetDirty(GRP_WIDGET_LIST_VEHICLE);
    00622   }
    00623
    00624   void ShowRenameGroupWindow(GroupID group, bool empty)
    00625   {
    00626     assert(Group::IsValidID(group));
    00627     this->group_rename = group;
    00628     /* Show empty query for new groups */
    00629     StringID str = STR_EMPTY;
    00630     if (!empty) {
    00631       SetDParam(0, group);
    00632       str = STR_GROUP_NAME;
    00633     }
    00634     ShowQueryString(str, STR_GROUP_RENAME_CAPTION, MAX_LENGTH_GROUP_NAME_CHARS, MAX_LENGTH_GROUP_NAME_PIXELS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
    00635   }
    00636
    00642   void UnselectVehicle(VehicleID vehicle)
    00643   {
    00644     if (this->vehicle_sel == vehicle) ResetObjectToPlace();
    00645   }
    00646 };
    00647
    00648
    00649 static WindowDesc _other_group_desc(
    00650   WDP_AUTO, 460, 246,
    00651   WC_INVALID, WC_NONE,
    00652   WDF_UNCLICK_BUTTONS,
    00653   _nested_group_widgets, lengthof(_nested_group_widgets)
    00654 );
    00655
    00656 static const WindowDesc _train_group_desc(
    00657   WDP_AUTO, 525, 246,
    00658   WC_TRAINS_LIST, WC_NONE,
    00659   WDF_UNCLICK_BUTTONS,
    00660   _nested_group_widgets, lengthof(_nested_group_widgets)
    00661 );
    00662
    00663 void ShowCompanyGroup(CompanyID company, VehicleType vehicle_type)
    00664 {
    00665   if (!Company::IsValidID(company)) return;
    00666
    00667   WindowNumber num = VehicleListIdentifier(VL_GROUP_LIST, vehicle_type, company).Pack();
    00668   if (vehicle_type == VEH_TRAIN) {
    00669     AllocateWindowDescFront<VehicleGroupWindow>(&_train_group_desc, num);
    00670   } else {
    00671     _other_group_desc.cls = GetWindowClassForVehicleType(vehicle_type);
    00672     AllocateWindowDescFront<VehicleGroupWindow>(&_other_group_desc, num);
    00673   }
    00674 }
    00675
    00682 static inline VehicleGroupWindow *FindVehicleGroupWindow(VehicleType vt, Owner owner)
    00683 {
    00684   return (VehicleGroupWindow *)FindWindowById(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, owner).Pack());
    00685 }
    00686
    00695 void CcCreateGroup(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
    00696 {
    00697   if (result.Failed()) return;
    00698   assert(p1 <= VEH_AIRCRAFT);
    00699
    00700   VehicleGroupWindow *w = FindVehicleGroupWindow((VehicleType)p1, _current_company);
    00701   if (w != NULL) w->ShowRenameGroupWindow(_new_group_id, true);
    00702 }
    00703
    00708 void DeleteGroupHighlightOfVehicle(const Vehicle *v)
    00709 {
    00710   /* If we haven't got any vehicles on the mouse pointer, we haven't got any highlighted in any group windows either
    00711    * If that is the case, we can skip looping though the windows and save time
    00712    */
    00713   if (_special_mouse_mode != WSM_DRAGDROP) return;
    00714
    00715   VehicleGroupWindow *w = FindVehicleGroupWindow(v->type, v->owner);
    00716   if (w != NULL) w->UnselectVehicle(v->index);
    00717 }