# group_gui.cpp

Go to the documentation of this file.

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