summaryrefslogtreecommitdiff
path: root/src/vehicle_gui.cpp
diff options
context:
space:
mode:
authorBernard Teo <bernard.14916@gmail.com>2019-01-11 16:50:38 +0800
committerPatric Stout <github@truebrain.nl>2020-12-21 23:15:53 +0100
commite59c400ca6e56605dd8660630246982f23a16b0a (patch)
tree43ba3c93dfa79fc5ed0e1719862c4ba773b58a9d /src/vehicle_gui.cpp
parent584df548f1bdfa27482aa995d151542abe4f0bca (diff)
downloadopenttd-e59c400ca6e56605dd8660630246982f23a16b0a.tar.xz
Feature: Option to group vehicle lists by shared orders
This applies to all kinds of vehicle lists, as well as the "vehicle groups" window.
Diffstat (limited to 'src/vehicle_gui.cpp')
-rw-r--r--src/vehicle_gui.cpp247
1 files changed, 209 insertions, 38 deletions
diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp
index 135da393a..a1b40ba92 100644
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -58,6 +58,11 @@ static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleValueSorter;
static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleLengthSorter;
static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimeToLiveSorter;
static BaseVehicleListWindow::VehicleIndividualSortFunction VehicleTimetableDelaySorter;
+static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupLengthSorter;
+static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitThisYearSorter;
+static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupTotalProfitLastYearSorter;
+static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupAverageProfitThisYearSorter;
+static BaseVehicleListWindow::VehicleGroupSortFunction VehicleGroupAverageProfitLastYearSorter;
/** Wrapper to convert a VehicleIndividualSortFunction to a VehicleGroupSortFunction */
template <BaseVehicleListWindow::VehicleIndividualSortFunction func>
@@ -99,6 +104,29 @@ const StringID BaseVehicleListWindow::vehicle_group_none_sorter_names[] = {
INVALID_STRING_ID
};
+BaseVehicleListWindow::VehicleGroupSortFunction * const BaseVehicleListWindow::vehicle_group_shared_orders_sorter_funcs[] = {
+ &VehicleGroupLengthSorter,
+ &VehicleGroupTotalProfitThisYearSorter,
+ &VehicleGroupTotalProfitLastYearSorter,
+ &VehicleGroupAverageProfitThisYearSorter,
+ &VehicleGroupAverageProfitLastYearSorter,
+};
+
+const StringID BaseVehicleListWindow::vehicle_group_shared_orders_sorter_names[] = {
+ STR_SORT_BY_NUM_VEHICLES,
+ STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR,
+ STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR,
+ STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR,
+ STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR,
+ INVALID_STRING_ID
+};
+
+const StringID BaseVehicleListWindow::vehicle_group_by_names[] = {
+ STR_GROUP_BY_NONE,
+ STR_GROUP_BY_SHARED_ORDERS,
+ INVALID_STRING_ID
+};
+
const StringID BaseVehicleListWindow::vehicle_depot_name[] = {
STR_VEHICLE_LIST_SEND_TRAIN_TO_DEPOT,
STR_VEHICLE_LIST_SEND_ROAD_VEHICLE_TO_DEPOT,
@@ -156,16 +184,50 @@ void BaseVehicleListWindow::BuildVehicleList()
GenerateVehicleSortList(&this->vehicles, this->vli);
- uint max_unitnumber = 0;
- for (auto it = this->vehicles.begin(); it != this->vehicles.end(); ++it) {
- this->vehgroups.emplace_back(it, it + 1, (*it)->GetDisplayProfitThisYear(), (*it)->GetDisplayProfitLastYear(), (*it)->age);
+ if (this->grouping == GB_NONE) {
+ uint max_unitnumber = 0;
+ for (auto it = this->vehicles.begin(); it != this->vehicles.end(); ++it) {
+ this->vehgroups.emplace_back(it, it + 1, (*it)->GetDisplayProfitThisYear(), (*it)->GetDisplayProfitLastYear(), (*it)->age);
+
+ max_unitnumber = max<uint>(max_unitnumber, (*it)->unitnumber);
+ }
+ this->unitnumber_digits = CountDigitsForAllocatingSpace(max_unitnumber);
+ } else {
+ /* Sort by the primary vehicle; we just want all vehicles that share the same orders to form a contiguous range. */
+ std::stable_sort(this->vehicles.begin(), this->vehicles.end(), [](const Vehicle * const &u, const Vehicle * const &v) {
+ return u->FirstShared() < v->FirstShared();
+ });
+
+ uint max_num_vehicles = 0;
+
+ VehicleList::const_iterator begin = this->vehicles.begin();
+ while (begin != this->vehicles.end()) {
+ VehicleList::const_iterator end = std::find_if_not(begin, this->vehicles.cend(), [first_shared = (*begin)->FirstShared()](const Vehicle * const &v) {
+ return v->FirstShared() == first_shared;
+ });
+
+ Money display_profit_this_year = 0;
+ Money display_profit_last_year = 0;
+ Date age = 0;
+ for (auto it = begin; it != end; ++it) {
+ const Vehicle * const v = (*it);
+ display_profit_this_year += v->GetDisplayProfitThisYear();
+ display_profit_last_year += v->GetDisplayProfitLastYear();
+ age = max<Date>(age, v->age);
+ }
- max_unitnumber = max<uint>(max_unitnumber, (*it)->unitnumber);
+ this->vehgroups.emplace_back(begin, end, display_profit_this_year, display_profit_last_year, age);
+
+ max_num_vehicles = max<uint>(max_num_vehicles, static_cast<uint>(end - begin));
+
+ begin = end;
+ }
+
+ this->unitnumber_digits = CountDigitsForAllocatingSpace(max_num_vehicles);
}
- this->unitnumber_digits = CountDigitsForAllocatingSpace(max_unitnumber);
this->vehgroups.RebuildDone();
- this->vscroll->SetCount(this->vehgroups.size());
+ this->vscroll->SetCount(static_cast<int>(this->vehgroups.size()));
}
/**
@@ -1114,6 +1176,36 @@ StringID GetCargoSubtypeText(const Vehicle *v)
return STR_EMPTY;
}
+/** Sort vehicle groups by the number of vehicles in the group */
+static bool VehicleGroupLengthSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
+{
+ return a.NumVehicles() < b.NumVehicles();
+}
+
+/** Sort vehicle groups by the total profit this year */
+static bool VehicleGroupTotalProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
+{
+ return a.display_profit_this_year < b.display_profit_this_year;
+}
+
+/** Sort vehicle groups by the total profit last year */
+static bool VehicleGroupTotalProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
+{
+ return a.display_profit_last_year < b.display_profit_last_year;
+}
+
+/** Sort vehicle groups by the average profit this year */
+static bool VehicleGroupAverageProfitThisYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
+{
+ return a.display_profit_this_year * static_cast<uint>(b.NumVehicles()) < b.display_profit_this_year * static_cast<uint>(a.NumVehicles());
+}
+
+/** Sort vehicle groups by the average profit last year */
+static bool VehicleGroupAverageProfitLastYearSorter(const GUIVehicleGroup &a, const GUIVehicleGroup &b)
+{
+ return a.display_profit_last_year * static_cast<uint>(b.NumVehicles()) < b.display_profit_last_year * static_cast<uint>(a.NumVehicles());
+}
+
/** Sort vehicles by their number */
static bool VehicleNumberSorter(const Vehicle * const &a, const Vehicle * const &b)
{
@@ -1290,6 +1382,12 @@ static const NWidgetPart _nested_vehicle_list[] = {
EndContainer(),
NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_VL_GROUP_ORDER), SetMinimalSize(81, 12), SetFill(0, 1), SetDataTip(STR_STATION_VIEW_GROUP, STR_TOOLTIP_GROUP_ORDER),
+ NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_GROUP_BY_PULLDOWN), SetMinimalSize(167, 12), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_GROUP_ORDER),
+ NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(),
+ EndContainer(),
+
+ NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VL_SORT_ORDER), SetMinimalSize(81, 12), SetFill(0, 1), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VL_SORT_BY_PULLDOWN), SetMinimalSize(167, 12), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetFill(1, 1), SetResize(1, 0),
@@ -1352,6 +1450,25 @@ static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, Veh
} while (oid != start);
}
+/** Draw small order list in the vehicle GUI, but without the little black arrow. This is used for shared order groups. */
+static void DrawSmallOrderList(const Order *order, int left, int right, int y)
+{
+ bool rtl = _current_text_dir == TD_RTL;
+ int l_offset = rtl ? 0 : ScaleGUITrad(6);
+ int r_offset = rtl ? ScaleGUITrad(6) : 0;
+ int i = 0;
+ while (order != nullptr) {
+ if (order->IsType(OT_GOTO_STATION)) {
+ SetDParam(0, order->GetDestination());
+ DrawString(left + l_offset, right - r_offset, y, STR_TINY_BLACK_STATION);
+
+ y += FONT_HEIGHT_SMALL;
+ if (++i == 4) break;
+ }
+ order = order->next;
+ }
+}
+
/**
* Draws an image of a vehicle chain
* @param v Front vehicle
@@ -1419,7 +1536,7 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int
int vehicle_button_x = rtl ? right - GetSpriteSize(SPR_PROFIT_LOT).width : left;
int y = r.top;
- uint max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->vehgroups.size());
+ uint max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), static_cast<uint>(this->vehgroups.size()));
for (uint i = this->vscroll->GetPosition(); i < max; ++i) {
const GUIVehicleGroup &vehgroup = this->vehgroups[i];
@@ -1430,31 +1547,54 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int
DrawVehicleProfitButton(vehgroup.age, vehgroup.display_profit_last_year, vehgroup.NumVehicles(), vehicle_button_x, y + FONT_HEIGHT_NORMAL + 3);
- const Vehicle *v = vehgroup.GetSingleVehicle();
+ switch (this->grouping) {
+ case GB_NONE: {
+ const Vehicle *v = vehgroup.GetSingleVehicle();
- DrawVehicleImage(v, image_left, image_right, y + FONT_HEIGHT_SMALL - 1, selected_vehicle, EIT_IN_LIST, 0);
+ DrawVehicleImage(v, image_left, image_right, y + FONT_HEIGHT_SMALL - 1, selected_vehicle, EIT_IN_LIST, 0);
- if (!v->name.empty()) {
- /* The vehicle got a name so we will print it */
- SetDParam(0, v->index);
- DrawString(text_left, text_right, y, STR_TINY_BLACK_VEHICLE);
- } else if (v->group_id != DEFAULT_GROUP) {
- /* The vehicle has no name, but is member of a group, so print group name */
- SetDParam(0, v->group_id);
- DrawString(text_left, text_right, y, STR_TINY_GROUP, TC_BLACK);
- }
+ if (!v->name.empty()) {
+ /* The vehicle got a name so we will print it */
+ SetDParam(0, v->index);
+ DrawString(text_left, text_right, y, STR_TINY_BLACK_VEHICLE);
+ } else if (v->group_id != DEFAULT_GROUP) {
+ /* The vehicle has no name, but is member of a group, so print group name */
+ SetDParam(0, v->group_id);
+ DrawString(text_left, text_right, y, STR_TINY_GROUP, TC_BLACK);
+ }
- if (show_orderlist) DrawSmallOrderList(v, orderlist_left, orderlist_right, y, v->cur_real_order_index);
+ if (show_orderlist) DrawSmallOrderList(v, orderlist_left, orderlist_right, y, v->cur_real_order_index);
- StringID str;
- if (v->IsChainInDepot()) {
- str = STR_BLUE_COMMA;
- } else {
- str = (v->age > v->max_age - DAYS_IN_LEAP_YEAR) ? STR_RED_COMMA : STR_BLACK_COMMA;
- }
+ StringID str;
+ if (v->IsChainInDepot()) {
+ str = STR_BLUE_COMMA;
+ } else {
+ str = (v->age > v->max_age - DAYS_IN_LEAP_YEAR) ? STR_RED_COMMA : STR_BLACK_COMMA;
+ }
- SetDParam(0, v->unitnumber);
- DrawString(left, right, y + 2, str);
+ SetDParam(0, v->unitnumber);
+ DrawString(left, right, y + 2, str);
+ break;
+ }
+
+ case GB_SHARED_ORDERS:
+
+ assert(vehgroup.NumVehicles() > 0);
+
+ for (int i = 0; i < static_cast<int>(vehgroup.NumVehicles()); ++i) {
+ if (image_left + 8 * i >= image_right) break; // Break if there is no more space to draw any more vehicles anyway.
+ DrawVehicleImage(vehgroup.vehicles_begin[i], image_left + 8 * i, image_right, y + FONT_HEIGHT_SMALL - 1, selected_vehicle, EIT_IN_LIST, 0);
+ }
+
+ if (show_orderlist) DrawSmallOrderList((vehgroup.vehicles_begin[0])->GetFirstOrder(), orderlist_left, orderlist_right, y);
+
+ SetDParam(0, vehgroup.NumVehicles());
+ DrawString(left, right, y + 2, STR_BLACK_COMMA);
+ break;
+
+ default:
+ NOT_REACHED();
+ }
y += line_height;
}
@@ -1472,7 +1612,7 @@ void BaseVehicleListWindow::UpdateSortingFromGrouping()
case VEH_AIRCRAFT: this->sorting = &_sorting[this->grouping].aircraft; break;
default: NOT_REACHED();
}
- this->vehgroups.SetSortFuncs(this->vehicle_group_none_sorter_funcs);
+ this->vehgroups.SetSortFuncs(this->GetVehicleSorterFuncs());
this->vehgroups.SetListing(*this->sorting);
this->vehgroups.ForceRebuild();
this->vehgroups.NeedResort();
@@ -1523,6 +1663,10 @@ public:
if (this->vli.type == VL_SHARED_ORDERS) {
this->GetWidget<NWidgetCore>(WID_VL_CAPTION)->widget_data = STR_VEHICLE_LIST_SHARED_ORDERS_LIST_CAPTION;
+ /* If we are in the shared orders window, then disable the group-by dropdown menu.
+ * Remove this when the group-by dropdown menu has another option apart from grouping by shared orders. */
+ this->SetWidgetDisabledState(WID_VL_GROUP_ORDER, true);
+ this->SetWidgetDisabledState(WID_VL_GROUP_BY_PULLDOWN, true);
} else {
this->GetWidget<NWidgetCore>(WID_VL_CAPTION)->widget_data = STR_VEHICLE_LIST_TRAIN_CAPTION + this->vli.vtype;
}
@@ -1656,8 +1800,11 @@ public:
WIDGET_LIST_END);
}
+ /* Set text of group by dropdown widget. */
+ this->GetWidget<NWidgetCore>(WID_VL_GROUP_BY_PULLDOWN)->widget_data = this->vehicle_group_by_names[this->grouping];
+
/* Set text of sort by dropdown widget. */
- this->GetWidget<NWidgetCore>(WID_VL_SORT_BY_PULLDOWN)->widget_data = this->vehicle_group_none_sorter_names[this->vehgroups.SortType()];
+ this->GetWidget<NWidgetCore>(WID_VL_SORT_BY_PULLDOWN)->widget_data = this->GetVehicleSorterNames()[this->vehgroups.SortType()];
this->DrawWidgets();
}
@@ -1670,8 +1817,12 @@ public:
this->SetDirty();
break;
- case WID_VL_SORT_BY_PULLDOWN:// Select sorting criteria dropdown menu
- ShowDropDownMenu(this, this->vehicle_group_none_sorter_names, this->vehgroups.SortType(), WID_VL_SORT_BY_PULLDOWN, 0,
+ case WID_VL_GROUP_BY_PULLDOWN: // Select sorting criteria dropdown menu
+ ShowDropDownMenu(this, this->vehicle_group_by_names, this->grouping, WID_VL_GROUP_BY_PULLDOWN, 0, 0);
+ return;
+
+ case WID_VL_SORT_BY_PULLDOWN: // Select sorting criteria dropdown menu
+ ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_VL_SORT_BY_PULLDOWN, 0,
(this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
return;
@@ -1680,15 +1831,29 @@ public:
if (id_v >= this->vehgroups.size()) return; // click out of list bound
const GUIVehicleGroup &vehgroup = this->vehgroups[id_v];
- const Vehicle *v = vehgroup.GetSingleVehicle();
-
- if (!VehicleClicked(v)) {
- if (_ctrl_pressed) {
- ShowCompanyGroupForVehicle(v);
- } else {
- ShowVehicleViewWindow(v);
+ switch (this->grouping) {
+ case GB_NONE: {
+ const Vehicle *v = vehgroup.GetSingleVehicle();
+ if (!VehicleClicked(v)) {
+ if (_ctrl_pressed) {
+ ShowCompanyGroupForVehicle(v);
+ } else {
+ ShowVehicleViewWindow(v);
+ }
+ }
+ break;
}
+
+ case GB_SHARED_ORDERS:
+ assert(vehgroup.NumVehicles() > 0);
+ /* We do not support VehicleClicked() here since the contextual action may only make sense for individual vehicles */
+
+ ShowVehicleListWindow(vehgroup.vehicles_begin[0]);
+ break;
+
+ default: NOT_REACHED();
}
+
break;
}
@@ -1711,9 +1876,14 @@ public:
void OnDropdownSelect(int widget, int index) override
{
switch (widget) {
+ case WID_VL_GROUP_BY_PULLDOWN:
+ this->UpdateVehicleGroupBy(static_cast<GroupBy>(index));
+ break;
+
case WID_VL_SORT_BY_PULLDOWN:
this->vehgroups.SetSortType(index);
break;
+
case WID_VL_MANAGE_VEHICLES_DROPDOWN:
assert(this->vehicles.size() != 0);
@@ -1729,6 +1899,7 @@ public:
default: NOT_REACHED();
}
break;
+
default: NOT_REACHED();
}
this->SetDirty();