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