Changeset - r26613:35f34aec3be8
[Not reviewed]
master
0 9 0
Bernard Teo - 2 years ago 2022-11-26 17:03:21
btzy1996@hotmail.com
Feature: Contextual actions for vehicles grouped by shared orders (#8425)
9 files changed with 200 insertions and 19 deletions:
0 comments (0 inline, 0 general)
src/depot_gui.cpp
Show inline comments
 
@@ -24,8 +24,10 @@
 
#include "tilehighlight_func.h"
 
#include "window_gui.h"
 
#include "vehiclelist.h"
 
#include "vehicle_func.h"
 
#include "order_backup.h"
 
#include "zoom_func.h"
 
#include "error.h"
 
#include "depot_cmd.h"
 
#include "train_cmd.h"
 
#include "vehicle_cmd.h"
 
@@ -917,6 +919,49 @@ struct DepotWindow : Window {
 
		return true;
 
	}
 

	
 
	/**
 
	 * Clones a vehicle from a vehicle list.  If this doesn't make sense (because not all vehicles in the list have the same orders), then it displays an error.
 
	 * @return This always returns true, which indicates that the contextual action handled the mouse click.
 
	 *         Note that it's correct behaviour to always handle the click even though an error is displayed,
 
	 *         because users aren't going to expect the default action to be performed just because they overlooked that cloning doesn't make sense.
 
	 */
 
	bool OnVehicleSelect(VehicleList::const_iterator begin, VehicleList::const_iterator end) override
 
	{
 
		if (!_ctrl_pressed) {
 
			/* If CTRL is not pressed: If all the vehicles in this list have the same orders, then copy orders */
 
			if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
 
				return VehiclesHaveSameEngineList(v1, v2);
 
			})) {
 
				if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
 
					return VehiclesHaveSameOrderList(v1, v2);
 
				})) {
 
					OnVehicleSelect(*begin);
 
				} else {
 
					ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST, WL_INFO);
 
				}
 
			} else {
 
				ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_CLONE_VEHICLE_LIST, WL_INFO);
 
			}
 
		} else {
 
			/* If CTRL is pressed: If all the vehicles in this list share orders, then copy orders */
 
			if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
 
				return VehiclesHaveSameEngineList(v1, v2);
 
			})) {
 
				if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
 
					return v1->FirstShared() == v2->FirstShared();
 
				})) {
 
					OnVehicleSelect(*begin);
 
				} else {
 
					ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST, WL_INFO);
 
				}
 
			} else {
 
				ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_CLONE_VEHICLE_LIST, WL_INFO);
 
			}
 
		}
 

	
 
		return true;
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		/* abort clone */
src/group_gui.cpp
Show inline comments
 
@@ -884,14 +884,14 @@ public:
 
					}
 

	
 
					case GB_SHARED_ORDERS: {
 
						const Vehicle *v = vehgroup.vehicles_begin[0];
 
						/* We do not support VehicleClicked() here since the contextual action may only make sense for individual vehicles */
 

	
 
						if (vindex == v->index) {
 
							if (vehgroup.NumVehicles() == 1) {
 
								ShowVehicleViewWindow(v);
 
							} else {
 
								ShowVehicleListWindow(v);
 
						if (!VehicleClicked(vehgroup)) {
 
							const Vehicle* v = vehgroup.vehicles_begin[0];
 
							if (vindex == v->index) {
 
								if (vehgroup.NumVehicles() == 1) {
 
									ShowVehicleViewWindow(v);
 
								} else {
 
									ShowVehicleListWindow(v);
 
								}
 
							}
 
						}
 
						break;
src/lang/english.txt
Show inline comments
 
@@ -5029,6 +5029,8 @@ STR_ERROR_CAN_T_CHANGE_SERVICING        
 

	
 
STR_ERROR_VEHICLE_IS_DESTROYED                                  :{WHITE}... vehicle is destroyed
 

	
 
STR_ERROR_CAN_T_CLONE_VEHICLE_LIST                              :{WHITE}... not all vehicles are identical
 

	
 
STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL                          :{WHITE}No vehicles will be available at all
 
STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL_EXPLANATION              :{WHITE}Change your NewGRF configuration
 
STR_ERROR_NO_VEHICLES_AVAILABLE_YET                             :{WHITE}No vehicles are available yet
 
@@ -5055,6 +5057,8 @@ STR_ERROR_CAN_T_SKIP_TO_ORDER           
 
STR_ERROR_CAN_T_COPY_SHARE_ORDER                                :{WHITE}... vehicle can't go to all stations
 
STR_ERROR_CAN_T_ADD_ORDER                                       :{WHITE}... vehicle can't go to that station
 
STR_ERROR_CAN_T_ADD_ORDER_SHARED                                :{WHITE}... a vehicle sharing this order can't go to that station
 
STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST                         :{WHITE}... not all vehicles have the same orders
 
STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST                        :{WHITE}... not all vehicles are sharing orders
 

	
 
STR_ERROR_CAN_T_SHARE_ORDER_LIST                                :{WHITE}Can't share order list...
 
STR_ERROR_CAN_T_STOP_SHARING_ORDER_LIST                         :{WHITE}Can't stop sharing order list...
src/order_gui.cpp
Show inline comments
 
@@ -29,6 +29,9 @@
 
#include "aircraft.h"
 
#include "engine_func.h"
 
#include "vehicle_func.h"
 
#include "vehiclelist.h"
 
#include "vehicle_func.h"
 
#include "error.h"
 
#include "order_cmd.h"
 
#include "company_cmd.h"
 

	
 
@@ -1466,6 +1469,40 @@ public:
 
		return true;
 
	}
 

	
 
	/**
 
	 * Clones an order list from a vehicle list.  If this doesn't make sense (because not all vehicles in the list have the same orders), then it displays an error.
 
	 * @return This always returns true, which indicates that the contextual action handled the mouse click.
 
	 *         Note that it's correct behaviour to always handle the click even though an error is displayed,
 
	 *         because users aren't going to expect the default action to be performed just because they overlooked that cloning doesn't make sense.
 
	 */
 
	bool OnVehicleSelect(VehicleList::const_iterator begin, VehicleList::const_iterator end) override
 
	{
 
		bool share_order = _ctrl_pressed || this->goto_type == OPOS_SHARE;
 
		if (this->vehicle->GetNumOrders() != 0 && !share_order) return false;
 

	
 
		if (!share_order) {
 
			/* If CTRL is not pressed: If all the vehicles in this list have the same orders, then copy orders */
 
			if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
 
				return VehiclesHaveSameOrderList(v1, v2);
 
			})) {
 
				OnVehicleSelect(*begin);
 
			} else {
 
				ShowErrorMessage(STR_ERROR_CAN_T_COPY_ORDER_LIST, STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST, WL_INFO);
 
			}
 
		} else {
 
			/* If CTRL is pressed: If all the vehicles in this list share orders, then copy orders */
 
			if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
 
				return v1->FirstShared() == v2->FirstShared();
 
			})) {
 
				OnVehicleSelect(*begin);
 
			} else {
 
				ShowErrorMessage(STR_ERROR_CAN_T_SHARE_ORDER_LIST, STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST, WL_INFO);
 
			}
 
		}
 

	
 
		return true;
 
	}
 

	
 
	void OnPlaceObjectAbort() override
 
	{
 
		this->goto_type = OPOS_NONE;
src/vehicle.cpp
Show inline comments
 
@@ -3013,3 +3013,39 @@ uint32 Vehicle::GetDisplayMinPowerToWeig
 
	if (max_weight == 0) return 0;
 
	return GetGroundVehicleCache()->cached_power * 10u / max_weight;
 
}
 

	
 
/**
 
 * Checks if two vehicle chains have the same list of engines.
 
 * @param v1 First vehicle chain.
 
 * @param v1 Second vehicle chain.
 
 * @return True if same, false if different.
 
 */
 
bool VehiclesHaveSameEngineList(const Vehicle *v1, const Vehicle *v2)
 
{
 
	while (true) {
 
		if (v1 == nullptr && v2 == nullptr) return true;
 
		if (v1 == nullptr || v2 == nullptr) return false;
 
		if (v1->GetEngine() != v2->GetEngine()) return false;
 
		v1 = v1->GetNextVehicle();
 
		v2 = v2->GetNextVehicle();
 
	}
 
}
 

	
 
/**
 
 * Checks if two vehicles have the same list of orders.
 
 * @param v1 First vehicles.
 
 * @param v1 Second vehicles.
 
 * @return True if same, false if different.
 
 */
 
bool VehiclesHaveSameOrderList(const Vehicle *v1, const Vehicle *v2)
 
{
 
	const Order *o1 = v1->GetFirstOrder();
 
	const Order *o2 = v2->GetFirstOrder();
 
	while (true) {
 
		if (o1 == nullptr && o2 == nullptr) return true;
 
		if (o1 == nullptr || o2 == nullptr) return false;
 
		if (!o1->Equals(*o2)) return false;
 
		o1 = o1->next;
 
		o2 = o2->next;
 
	}
 
}
src/vehicle_func.h
Show inline comments
 
@@ -174,4 +174,7 @@ void GetVehicleSet(VehicleSet &set, Vehi
 

	
 
void CheckCargoCapacity(Vehicle *v);
 

	
 
bool VehiclesHaveSameEngineList(const Vehicle *v1, const Vehicle *v2);
 
bool VehiclesHaveSameOrderList(const Vehicle *v1, const Vehicle *v2);
 

	
 
#endif /* VEHICLE_FUNC_H */
src/vehicle_gui.cpp
Show inline comments
 
@@ -2018,16 +2018,16 @@ public:
 

	
 
					case GB_SHARED_ORDERS: {
 
						assert(vehgroup.NumVehicles() > 0);
 
						const Vehicle *v = vehgroup.vehicles_begin[0];
 
						/* We do not support VehicleClicked() here since the contextual action may only make sense for individual vehicles */
 

	
 
						if (_ctrl_pressed) {
 
							ShowOrdersWindow(v);
 
						} else {
 
							if (vehgroup.NumVehicles() == 1) {
 
								ShowVehicleViewWindow(v);
 
						if (!VehicleClicked(vehgroup)) {
 
							const Vehicle *v = vehgroup.vehicles_begin[0];
 
							if (_ctrl_pressed) {
 
								ShowOrdersWindow(v);
 
							} else {
 
								ShowVehicleListWindow(v);
 
								if (vehgroup.NumVehicles() == 1) {
 
									ShowVehicleViewWindow(v);
 
								} else {
 
									ShowVehicleListWindow(v);
 
								}
 
							}
 
						}
 
						break;
 
@@ -3288,6 +3288,33 @@ bool VehicleClicked(const Vehicle *v)
 
	return _thd.GetCallbackWnd()->OnVehicleSelect(v);
 
}
 

	
 
/**
 
 * Dispatch a "vehicle group selected" event if any window waits for it.
 
 * @param begin iterator to the start of the range of vehicles
 
 * @param end iterator to the end of the range of vehicles
 
 * @return did any window accept vehicle group selection?
 
 */
 
bool VehicleClicked(VehicleList::const_iterator begin, VehicleList::const_iterator end)
 
{
 
	assert(begin != end);
 
	if (!(_thd.place_mode & HT_VEHICLE)) return false;
 

	
 
	/* If there is only one vehicle in the group, act as if we clicked a single vehicle */
 
	if (begin + 1 == end) return _thd.GetCallbackWnd()->OnVehicleSelect(*begin);
 

	
 
	return _thd.GetCallbackWnd()->OnVehicleSelect(begin, end);
 
}
 

	
 
/**
 
 * Dispatch a "vehicle group selected" event if any window waits for it.
 
 * @param vehgroup the GUIVehicleGroup representing the vehicle group
 
 * @return did any window accept vehicle group selection?
 
 */
 
bool VehicleClicked(const GUIVehicleGroup &vehgroup)
 
{
 
	return VehicleClicked(vehgroup.vehicles_begin, vehgroup.vehicles_end);
 
}
 

	
 
void StopGlobalFollowVehicle(const Vehicle *v)
 
{
 
	Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
src/vehicle_gui.h
Show inline comments
 
@@ -12,6 +12,8 @@
 

	
 
#include "window_type.h"
 
#include "vehicle_type.h"
 
#include "vehicle_gui_base.h"
 
#include "vehiclelist.h"
 
#include "order_type.h"
 
#include "station_type.h"
 
#include "engine_type.h"
 
@@ -102,6 +104,8 @@ static inline WindowClass GetWindowClass
 
/* Unified window procedure */
 
void ShowVehicleViewWindow(const Vehicle *v);
 
bool VehicleClicked(const Vehicle *v);
 
bool VehicleClicked(VehicleList::const_iterator begin, VehicleList::const_iterator end);
 
bool VehicleClicked(const GUIVehicleGroup &vehgroup);
 
void StartStopVehicle(const Vehicle *v, bool texteffect);
 

	
 
Vehicle *CheckClickOnVehicle(const struct Viewport *vp, int x, int y);
src/window_gui.h
Show inline comments
 
@@ -11,7 +11,10 @@
 
#define WINDOW_GUI_H
 

	
 
#include <list>
 
#include <algorithm>
 
#include <functional>
 

	
 
#include "vehiclelist.h"
 
#include "vehicle_type.h"
 
#include "viewport_type.h"
 
#include "company_type.h"
 
@@ -681,12 +684,21 @@ public:
 

	
 
	/**
 
	 * The user clicked on a vehicle while HT_VEHICLE has been set.
 
	 * @param v clicked vehicle. It is guaranteed to be v->IsPrimaryVehicle() == true
 
	 * @return True if the click is handled, false if it is ignored.
 
	 * @param v clicked vehicle
 
	 * @return true if the click is handled, false if it is ignored
 
	 * @pre v->IsPrimaryVehicle() == true
 
	 */
 
	virtual bool OnVehicleSelect(const struct Vehicle *v) { return false; }
 

	
 
	/**
 
	 * The user clicked on a vehicle while HT_VEHICLE has been set.
 
	 * @param v clicked vehicle
 
	 * @return True if the click is handled, false if it is ignored
 
	 * @pre v->IsPrimaryVehicle() == true
 
	 */
 
	virtual bool OnVehicleSelect(VehicleList::const_iterator begin, VehicleList::const_iterator end) { return false; }
 

	
 
	/**
 
	 * The user cancelled a tile highlight mode that has been set.
 
	 */
 
	virtual void OnPlaceObjectAbort() {}
 
@@ -807,6 +819,19 @@ public:
 
};
 

	
 
/**
 
 * Generic helper function that checks if all elements of the range are equal with respect to the given predicate.
 
 * @param begin The start of the range.
 
 * @param end The end of the range.
 
 * @param pred The predicate to use.
 
 * @return True if all elements are equal, false otherwise.
 
 */
 
template <class It, class Pred>
 
inline bool AllEqual(It begin, It end, Pred pred)
 
{
 
	return std::adjacent_find(begin, end, std::not_fn(pred)) == end;
 
}
 

	
 
/**
 
 * Get the nested widget with number \a widnum from the nested widget tree.
 
 * @tparam NWID Type of the nested widget.
 
 * @param widnum Widget number of the widget to retrieve.
0 comments (0 inline, 0 general)