2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
8 /** @file depot_gui.cpp The GUI for depots. */
16 #include "textbuf_gui.h"
17 #include "viewport_func.h"
18 #include "command_func.h"
19 #include "depot_base.h"
20 #include "spritecache.h"
21 #include "strings_func.h"
22 #include "vehicle_func.h"
23 #include "company_func.h"
24 #include "tilehighlight_func.h"
25 #include "window_gui.h"
26 #include "vehiclelist.h"
27 #include "vehicle_func.h"
28 #include "order_backup.h"
29 #include "zoom_func.h"
31 #include "depot_cmd.h"
32 #include "train_cmd.h"
33 #include "vehicle_cmd.h"
34 #include "core/geometry_func.hpp"
36 #include "widgets/depot_widget.h"
38 #include "table/strings.h"
40 #include "safeguards.h"
43 * Since all depot window sizes aren't the same, we need to modify sizes a little.
44 * It's done with the following arrays of widget indexes. Each of them tells if a widget side should be moved and in what direction.
45 * How long they should be moved and for what window types are controlled in ShowDepotWindow()
48 /** Nested widget definition for train depots. */
49 static constexpr NWidgetPart _nested_train_depot_widgets
[] = {
50 NWidget(NWID_HORIZONTAL
),
51 NWidget(WWT_CLOSEBOX
, COLOUR_GREY
),
52 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_D_SHOW_RENAME
), SetAspect(WidgetDimensions::ASPECT_RENAME
), // rename button
53 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_D_RENAME
), SetAspect(WidgetDimensions::ASPECT_RENAME
), SetDataTip(SPR_RENAME
, STR_DEPOT_RENAME_TOOLTIP
),
55 NWidget(WWT_CAPTION
, COLOUR_GREY
, WID_D_CAPTION
), SetDataTip(STR_DEPOT_CAPTION
, STR_NULL
),
56 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_D_LOCATION
), SetAspect(WidgetDimensions::ASPECT_LOCATION
), SetDataTip(SPR_GOTO_LOCATION
, STR_NULL
),
57 NWidget(WWT_SHADEBOX
, COLOUR_GREY
),
58 NWidget(WWT_DEFSIZEBOX
, COLOUR_GREY
),
59 NWidget(WWT_STICKYBOX
, COLOUR_GREY
),
61 NWidget(NWID_HORIZONTAL
),
62 NWidget(NWID_VERTICAL
),
63 NWidget(WWT_MATRIX
, COLOUR_GREY
, WID_D_MATRIX
), SetResize(1, 1), SetScrollbar(WID_D_V_SCROLL
),
64 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_D_SHOW_H_SCROLL
),
65 NWidget(NWID_HSCROLLBAR
, COLOUR_GREY
, WID_D_H_SCROLL
),
68 NWidget(NWID_VERTICAL
),
69 NWidget(WWT_IMGBTN
, COLOUR_GREY
, WID_D_SELL
), SetDataTip(0x0, STR_NULL
), SetResize(0, 1), SetFill(0, 1),
70 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_D_SHOW_SELL_CHAIN
),
71 NWidget(WWT_IMGBTN
, COLOUR_GREY
, WID_D_SELL_CHAIN
), SetDataTip(SPR_SELL_CHAIN_TRAIN
, STR_DEPOT_DRAG_WHOLE_TRAIN_TO_SELL_TOOLTIP
), SetResize(0, 1), SetFill(0, 1),
73 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_D_SELL_ALL
), SetDataTip(0x0, STR_NULL
),
74 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_D_AUTOREPLACE
), SetDataTip(0x0, STR_NULL
),
76 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_D_V_SCROLL
),
78 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
),
79 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_D_BUILD
), SetDataTip(0x0, STR_NULL
), SetFill(1, 1), SetResize(1, 0),
80 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_D_CLONE
), SetDataTip(0x0, STR_NULL
), SetFill(1, 1), SetResize(1, 0),
81 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_D_VEHICLE_LIST
), SetDataTip(0x0, STR_NULL
), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON
), SetFill(0, 1),
82 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_D_STOP_ALL
), SetDataTip(SPR_FLAG_VEH_STOPPED
, STR_NULL
), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG
), SetFill(0, 1),
83 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_D_START_ALL
), SetDataTip(SPR_FLAG_VEH_RUNNING
, STR_NULL
), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG
), SetFill(0, 1),
84 NWidget(WWT_RESIZEBOX
, COLOUR_GREY
),
88 static WindowDesc
_train_depot_desc(
89 WDP_AUTO
, "depot_train", 362, 123,
90 WC_VEHICLE_DEPOT
, WC_NONE
,
92 _nested_train_depot_widgets
95 static WindowDesc
_road_depot_desc(
96 WDP_AUTO
, "depot_roadveh", 316, 97,
97 WC_VEHICLE_DEPOT
, WC_NONE
,
99 _nested_train_depot_widgets
102 static WindowDesc
_ship_depot_desc(
103 WDP_AUTO
, "depot_ship", 306, 99,
104 WC_VEHICLE_DEPOT
, WC_NONE
,
106 _nested_train_depot_widgets
109 static WindowDesc
_aircraft_depot_desc(
110 WDP_AUTO
, "depot_aircraft", 332, 99,
111 WC_VEHICLE_DEPOT
, WC_NONE
,
113 _nested_train_depot_widgets
116 extern void DepotSortList(VehicleList
*list
);
119 * This is the Callback method after the cloning attempt of a vehicle
120 * @param result the result of the cloning command
121 * @param veh_id cloned vehicle ID
123 void CcCloneVehicle(Commands
, const CommandCost
&result
, VehicleID veh_id
)
125 if (result
.Failed()) return;
127 const Vehicle
*v
= Vehicle::Get(veh_id
);
129 ShowVehicleViewWindow(v
);
132 static void TrainDepotMoveVehicle(const Vehicle
*wagon
, VehicleID sel
, const Vehicle
*head
)
134 const Vehicle
*v
= Vehicle::Get(sel
);
136 if (v
== wagon
) return;
138 if (wagon
== nullptr) {
139 if (head
!= nullptr) wagon
= head
->Last();
141 wagon
= wagon
->Previous();
142 if (wagon
== nullptr) return;
145 if (wagon
== v
) return;
147 Command
<CMD_MOVE_RAIL_VEHICLE
>::Post(STR_ERROR_CAN_T_MOVE_VEHICLE
, v
->tile
, v
->index
, wagon
== nullptr ? INVALID_VEHICLE
: wagon
->index
, _ctrl_pressed
);
150 static VehicleCellSize _base_block_sizes_depot
[VEH_COMPANY_END
]; ///< Cell size for vehicle images in the depot view.
151 static VehicleCellSize _base_block_sizes_purchase
[VEH_COMPANY_END
]; ///< Cell size for vehicle images in the purchase list.
152 static uint _consistent_train_width
; ///< Whether trains of all lengths are consistently scaled. Either TRAININFO_DEFAULT_VEHICLE_WIDTH, VEHICLEINFO_FULL_VEHICLE_WIDTH, or 0.
155 * Get the GUI cell size for a vehicle image.
156 * @param type Vehicle type to get the size for.
157 * @param image_type Image type to get size for.
158 * @pre image_type == EIT_IN_DEPOT || image_type == EIT_PURCHASE
159 * @return Cell dimensions for the vehicle and image type.
161 VehicleCellSize
GetVehicleImageCellSize(VehicleType type
, EngineImageType image_type
)
163 switch (image_type
) {
164 case EIT_IN_DEPOT
: return _base_block_sizes_depot
[type
];
165 case EIT_PURCHASE
: return _base_block_sizes_purchase
[type
];
166 default: NOT_REACHED();
170 static void InitBlocksizeForVehicles(VehicleType type
, EngineImageType image_type
)
172 int max_extend_left
= 0;
173 int max_extend_right
= 0;
176 for (const Engine
*e
: Engine::IterateType(type
)) {
177 if (!e
->IsEnabled()) continue;
179 EngineID eid
= e
->index
;
184 default: NOT_REACHED();
185 case VEH_TRAIN
: GetTrainSpriteSize( eid
, x
, y
, x_offs
, y_offs
, image_type
); break;
186 case VEH_ROAD
: GetRoadVehSpriteSize( eid
, x
, y
, x_offs
, y_offs
, image_type
); break;
187 case VEH_SHIP
: GetShipSpriteSize( eid
, x
, y
, x_offs
, y_offs
, image_type
); break;
188 case VEH_AIRCRAFT
: GetAircraftSpriteSize(eid
, x
, y
, x_offs
, y_offs
, image_type
); break;
190 if (y
> max_height
) max_height
= y
;
191 if (-x_offs
> max_extend_left
) max_extend_left
= -x_offs
;
192 if ((int)x
+ x_offs
> max_extend_right
) max_extend_right
= x
+ x_offs
;
195 int min_extend
= ScaleSpriteTrad(16);
196 int max_extend
= ScaleSpriteTrad(98);
198 switch (image_type
) {
200 _base_block_sizes_depot
[type
].height
= std::max
<uint
>(ScaleSpriteTrad(GetVehicleHeight(type
)), max_height
);
201 _base_block_sizes_depot
[type
].extend_left
= Clamp(max_extend_left
, min_extend
, max_extend
);
202 _base_block_sizes_depot
[type
].extend_right
= Clamp(max_extend_right
, min_extend
, max_extend
);
205 _base_block_sizes_purchase
[type
].height
= std::max
<uint
>(ScaleSpriteTrad(GetVehicleHeight(type
)), max_height
);
206 _base_block_sizes_purchase
[type
].extend_left
= Clamp(max_extend_left
, min_extend
, max_extend
);
207 _base_block_sizes_purchase
[type
].extend_right
= Clamp(max_extend_right
, min_extend
, max_extend
);
210 default: NOT_REACHED();
215 * Set the size of the blocks in the window so we can be sure that they are big enough for the vehicle sprites in the current game.
216 * @note Calling this function once for each game is enough.
218 void InitDepotWindowBlockSizes()
220 for (VehicleType vt
= VEH_BEGIN
; vt
< VEH_COMPANY_END
; vt
++) {
221 InitBlocksizeForVehicles(vt
, EIT_IN_DEPOT
);
222 InitBlocksizeForVehicles(vt
, EIT_PURCHASE
);
225 _consistent_train_width
= TRAININFO_DEFAULT_VEHICLE_WIDTH
;
227 for (const Engine
*e
: Engine::IterateType(VEH_TRAIN
)) {
228 if (!e
->IsEnabled()) continue;
230 uint w
= TRAININFO_DEFAULT_VEHICLE_WIDTH
;
231 if (e
->GetGRF() != nullptr && is_custom_sprite(e
->u
.rail
.image_index
)) {
232 w
= e
->GetGRF()->traininfo_vehicle_width
;
233 if (w
!= VEHICLEINFO_FULL_VEHICLE_WIDTH
) {
235 * This is a NewGRF vehicle that uses TRAININFO_DEFAULT_VEHICLE_WIDTH.
236 * If the vehicles are shorter than 8/8 we have fractional lengths, which are not consistent after rounding.
238 _consistent_train_width
= 0;
244 _consistent_train_width
= w
;
246 } else if (w
!= _consistent_train_width
) {
247 _consistent_train_width
= 0;
253 static void DepotSellAllConfirmationCallback(Window
*w
, bool confirmed
);
254 const Sprite
*GetAircraftSprite(EngineID engine
);
256 struct DepotWindow
: Window
{
258 VehicleID vehicle_over
; ///< Rail vehicle over which another one is dragged, \c INVALID_VEHICLE if none.
261 bool check_unitnumber_digits
;
262 WidgetID hovered_widget
; ///< Index of the widget being hovered during drag/drop. -1 if no drag is in progress.
263 VehicleList vehicle_list
;
264 VehicleList wagon_list
;
265 uint unitnumber_digits
;
266 uint num_columns
; ///< Number of columns.
267 Scrollbar
*hscroll
; ///< Only for trains.
270 DepotWindow(WindowDesc
&desc
, TileIndex tile
, VehicleType type
) : Window(desc
)
272 assert(IsCompanyBuildableVehicleType(type
)); // ensure that we make the call with a valid type
274 this->sel
= INVALID_VEHICLE
;
275 this->vehicle_over
= INVALID_VEHICLE
;
276 this->generate_list
= true;
277 this->check_unitnumber_digits
= true;
278 this->hovered_widget
= -1;
280 this->num_columns
= 1; // for non-trains this gets set in FinishInitNested()
281 this->unitnumber_digits
= 2;
283 this->CreateNestedTree();
284 this->hscroll
= (this->type
== VEH_TRAIN
? this->GetScrollbar(WID_D_H_SCROLL
) : nullptr);
285 this->vscroll
= this->GetScrollbar(WID_D_V_SCROLL
);
286 /* Don't show 'rename button' of aircraft hangar */
287 this->GetWidget
<NWidgetStacked
>(WID_D_SHOW_RENAME
)->SetDisplayedPlane(type
== VEH_AIRCRAFT
? SZSP_NONE
: 0);
288 /* Only train depots have a horizontal scrollbar and a 'sell chain' button */
289 if (type
== VEH_TRAIN
) this->GetWidget
<NWidgetCore
>(WID_D_MATRIX
)->widget_data
= 1 << MAT_COL_START
;
290 this->GetWidget
<NWidgetStacked
>(WID_D_SHOW_H_SCROLL
)->SetDisplayedPlane(type
== VEH_TRAIN
? 0 : SZSP_HORIZONTAL
);
291 this->GetWidget
<NWidgetStacked
>(WID_D_SHOW_SELL_CHAIN
)->SetDisplayedPlane(type
== VEH_TRAIN
? 0 : SZSP_NONE
);
292 this->SetupWidgetData(type
);
293 this->FinishInitNested(tile
);
295 this->owner
= GetTileOwner(tile
);
296 OrderBackup::Reset();
299 void Close([[maybe_unused
]] int data
= 0) override
301 CloseWindowById(WC_BUILD_VEHICLE
, this->window_number
);
302 CloseWindowById(GetWindowClassForVehicleType(this->type
), VehicleListIdentifier(VL_DEPOT_LIST
, this->type
, this->owner
, this->GetDepotIndex()).Pack(), false);
303 OrderBackup::Reset(this->window_number
);
304 this->Window::Close();
308 * Draw a vehicle in the depot window in the box with the top left corner at x,y.
309 * @param v Vehicle to draw.
310 * @param r Rect to draw in.
312 void DrawVehicleInDepot(const Vehicle
*v
, const Rect
&r
) const
314 bool free_wagon
= false;
316 bool rtl
= _current_text_dir
== TD_RTL
;
317 Rect text
= r
.Shrink(RectPadding::zero
, WidgetDimensions::scaled
.matrix
); /* Ract for text elements, horizontal is already applied. */
318 Rect image
= r
.Indent(this->header_width
, rtl
).Indent(this->count_width
, !rtl
); /* Rect for vehicle images */
322 const Train
*u
= Train::From(v
);
323 free_wagon
= u
->IsFreeWagon();
325 uint x_space
= free_wagon
?
326 ScaleSpriteTrad(_consistent_train_width
!= 0 ? _consistent_train_width
: TRAININFO_DEFAULT_VEHICLE_WIDTH
) :
329 DrawTrainImage(u
, image
.Indent(x_space
, rtl
), this->sel
, EIT_IN_DEPOT
, free_wagon
? 0 : this->hscroll
->GetPosition(), this->vehicle_over
);
331 /* Length of consist in tiles with 1 fractional digit (rounded up) */
332 SetDParam(0, CeilDiv(u
->gcache
.cached_total_length
* 10, TILE_SIZE
));
334 Rect count
= text
.WithWidth(this->count_width
- WidgetDimensions::scaled
.hsep_normal
, !rtl
);
335 DrawString(count
.left
, count
.right
, count
.bottom
- GetCharacterHeight(FS_SMALL
) + 1, STR_JUST_DECIMAL
, TC_BLACK
, SA_RIGHT
, false, FS_SMALL
); // Draw the counter
339 case VEH_ROAD
: DrawRoadVehImage( v
, image
, this->sel
, EIT_IN_DEPOT
); break;
340 case VEH_SHIP
: DrawShipImage( v
, image
, this->sel
, EIT_IN_DEPOT
); break;
341 case VEH_AIRCRAFT
: DrawAircraftImage(v
, image
, this->sel
, EIT_IN_DEPOT
); break;
342 default: NOT_REACHED();
346 if (v
->IsGroundVehicle()) {
347 /* Arrange unitnumber and flag horizontally */
348 diff_x
= this->flag_size
.width
+ WidgetDimensions::scaled
.hsep_normal
;
349 diff_y
= WidgetDimensions::scaled
.matrix
.top
;
351 /* Arrange unitnumber and flag vertically */
353 diff_y
= WidgetDimensions::scaled
.matrix
.top
+ GetCharacterHeight(FS_NORMAL
) + WidgetDimensions::scaled
.vsep_normal
;
356 text
= text
.WithWidth(this->header_width
- WidgetDimensions::scaled
.hsep_normal
, rtl
).WithHeight(GetCharacterHeight(FS_NORMAL
)).Indent(diff_x
, rtl
);
358 DrawString(text
, STR_DEPOT_NO_ENGINE
);
360 Rect flag
= r
.WithWidth(this->flag_size
.width
, rtl
).WithHeight(this->flag_size
.height
).Translate(0, diff_y
);
361 DrawSpriteIgnorePadding((v
->vehstatus
& VS_STOPPED
) ? SPR_FLAG_VEH_STOPPED
: SPR_FLAG_VEH_RUNNING
, PAL_NONE
, flag
, SA_CENTER
);
363 SetDParam(0, v
->unitnumber
);
364 DrawString(text
, STR_JUST_COMMA
, (v
->max_age
- CalendarTime::DAYS_IN_LEAP_YEAR
) >= v
->age
? TC_BLACK
: TC_RED
);
368 void DrawWidget(const Rect
&r
, WidgetID widget
) const override
370 if (widget
!= WID_D_MATRIX
) return;
372 bool rtl
= _current_text_dir
== TD_RTL
;
374 /* Set the row and number of boxes in each row based on the number of boxes drawn in the matrix */
375 const NWidgetCore
*wid
= this->GetWidget
<NWidgetCore
>(WID_D_MATRIX
);
377 /* Set up rect for each cell */
378 Rect ir
= r
.WithHeight(this->resize
.step_height
);
379 if (this->num_columns
!= 1) ir
= ir
.WithWidth(this->resize
.step_width
, rtl
);
380 ir
= ir
.Shrink(WidgetDimensions::scaled
.framerect
, RectPadding::zero
);
382 /* Draw vertical separators at whole tiles.
383 * This only works in two cases:
384 * - All vehicles use VEHICLEINFO_FULL_VEHICLE_WIDTH as reference width.
385 * - All vehicles are 8/8. This cannot be checked for NewGRF, so instead we check for "all vehicles are original vehicles".
387 if (this->type
== VEH_TRAIN
&& _consistent_train_width
!= 0) {
388 int w
= ScaleSpriteTrad(2 * _consistent_train_width
);
389 int col
= GetColourGradient(wid
->colour
, SHADE_NORMAL
);
390 Rect image
= ir
.Indent(this->header_width
, rtl
).Indent(this->count_width
, !rtl
);
391 int first_line
= w
+ (-this->hscroll
->GetPosition()) % w
;
393 for (int x
= image
.right
- first_line
; x
>= image
.left
; x
-= w
) {
394 GfxDrawLine(x
, r
.top
, x
, r
.bottom
, col
, ScaleGUITrad(1), ScaleGUITrad(3));
397 for (int x
= image
.left
+ first_line
; x
<= image
.right
; x
+= w
) {
398 GfxDrawLine(x
, r
.top
, x
, r
.bottom
, col
, ScaleGUITrad(1), ScaleGUITrad(3));
403 uint16_t rows_in_display
= wid
->current_y
/ wid
->resize_y
;
405 uint num
= this->vscroll
->GetPosition() * this->num_columns
;
406 uint maxval
= static_cast<uint
>(std::min
<size_t>(this->vehicle_list
.size(), num
+ (rows_in_display
* this->num_columns
)));
407 for (; num
< maxval
; ir
= ir
.Translate(0, this->resize
.step_height
)) { // Draw the rows
408 Rect cell
= ir
; /* Keep track of horizontal cells */
409 for (uint i
= 0; i
< this->num_columns
&& num
< maxval
; i
++, num
++) {
410 /* Draw all vehicles in the current row */
411 const Vehicle
*v
= this->vehicle_list
[num
];
412 this->DrawVehicleInDepot(v
, cell
);
413 cell
= cell
.Translate(rtl
? -(int)this->resize
.step_width
: (int)this->resize
.step_width
, 0);
417 maxval
= static_cast<uint
>(std::min
<size_t>(this->vehicle_list
.size() + this->wagon_list
.size(), (this->vscroll
->GetPosition() * this->num_columns
) + (rows_in_display
* this->num_columns
)));
419 /* Draw the train wagons without an engine in front. */
420 for (; num
< maxval
; num
++, ir
= ir
.Translate(0, this->resize
.step_height
)) {
421 const Vehicle
*v
= this->wagon_list
[num
- this->vehicle_list
.size()];
422 this->DrawVehicleInDepot(v
, ir
);
426 void SetStringParameters(WidgetID widget
) const override
428 if (widget
!= WID_D_CAPTION
) return;
430 SetDParam(0, this->type
);
431 SetDParam(1, this->GetDepotIndex());
434 struct GetDepotVehiclePtData
{
436 const Vehicle
*wagon
;
439 enum DepotGUIAction
{
446 DepotGUIAction
GetVehicleFromDepotWndPt(int x
, int y
, const Vehicle
**veh
, GetDepotVehiclePtData
*d
)
448 this->RefreshVehicleList();
450 const NWidgetCore
*matrix_widget
= this->GetWidget
<NWidgetCore
>(WID_D_MATRIX
);
451 /* Make X relative to widget. Y is left alone for GetScrolledRowFromWidget(). */
452 x
-= matrix_widget
->pos_x
;
453 /* In case of RTL the widgets are swapped as a whole */
454 if (_current_text_dir
== TD_RTL
) x
= matrix_widget
->current_x
- x
;
456 uint xt
= 0, xm
= 0, ym
= 0;
457 if (this->type
== VEH_TRAIN
) {
460 xt
= x
/ this->resize
.step_width
;
461 xm
= x
% this->resize
.step_width
;
462 if (xt
>= this->num_columns
) return MODE_ERROR
;
464 ym
= (y
- matrix_widget
->pos_y
) % this->resize
.step_height
;
466 int32_t row
= this->vscroll
->GetScrolledRowFromWidget(y
, this, WID_D_MATRIX
);
467 uint pos
= (row
* this->num_columns
) + xt
;
469 if (row
== INT32_MAX
|| this->vehicle_list
.size() + this->wagon_list
.size() <= pos
) {
470 /* Clicking on 'line' / 'block' without a vehicle */
471 if (this->type
== VEH_TRAIN
) {
472 /* End the dragging */
475 return MODE_DRAG_VEHICLE
;
477 return MODE_ERROR
; // empty block, so no vehicle is selected
482 if (this->vehicle_list
.size() > pos
) {
483 *veh
= this->vehicle_list
[pos
];
484 /* Skip vehicles that are scrolled off the list */
485 if (this->type
== VEH_TRAIN
) x
+= this->hscroll
->GetPosition();
487 pos
-= (uint
)this->vehicle_list
.size();
488 *veh
= this->wagon_list
[pos
];
489 /* free wagons don't have an initial loco. */
490 x
-= ScaleSpriteTrad(VEHICLEINFO_FULL_VEHICLE_WIDTH
);
494 const Train
*v
= nullptr;
495 if (this->type
== VEH_TRAIN
) {
496 v
= Train::From(*veh
);
497 d
->head
= d
->wagon
= v
;
500 if (xm
<= this->header_width
) {
501 switch (this->type
) {
503 if (wagon
) return MODE_ERROR
;
507 if (xm
<= this->flag_size
.width
) return MODE_START_STOP
;
512 if (xm
<= this->flag_size
.width
&& ym
>= (uint
)(GetCharacterHeight(FS_NORMAL
) + WidgetDimensions::scaled
.vsep_normal
)) return MODE_START_STOP
;
515 default: NOT_REACHED();
517 return MODE_SHOW_VEHICLE
;
520 if (this->type
!= VEH_TRAIN
) return MODE_DRAG_VEHICLE
;
522 /* Clicking on the counter */
523 if (xm
>= matrix_widget
->current_x
- this->count_width
) return wagon
? MODE_ERROR
: MODE_SHOW_VEHICLE
;
525 /* Account for the header */
526 x
-= this->header_width
;
528 /* find the vehicle in this row that was clicked */
529 for (; v
!= nullptr; v
= v
->Next()) {
530 x
-= v
->GetDisplayImageWidth();
534 d
->wagon
= (v
!= nullptr ? v
->GetFirstEnginePart() : nullptr);
536 return MODE_DRAG_VEHICLE
;
540 * Handle click in the depot matrix.
541 * @param x Horizontal position in the matrix widget in pixels.
542 * @param y Vertical position in the matrix widget in pixels.
544 void DepotClick(int x
, int y
)
546 GetDepotVehiclePtData gdvp
= { nullptr, nullptr };
547 const Vehicle
*v
= nullptr;
548 DepotGUIAction mode
= this->GetVehicleFromDepotWndPt(x
, y
, &v
, &gdvp
);
550 if (this->type
== VEH_TRAIN
) v
= gdvp
.wagon
;
553 case MODE_ERROR
: // invalid
556 case MODE_DRAG_VEHICLE
: { // start dragging of vehicle
557 if (v
!= nullptr && VehicleClicked(v
)) return;
559 VehicleID sel
= this->sel
;
561 if (this->type
== VEH_TRAIN
&& sel
!= INVALID_VEHICLE
) {
562 this->sel
= INVALID_VEHICLE
;
563 TrainDepotMoveVehicle(v
, sel
, gdvp
.head
);
564 } else if (v
!= nullptr) {
565 SetObjectToPlaceWnd(SPR_CURSOR_MOUSE
, PAL_NONE
, HT_DRAG
, this);
566 SetMouseCursorVehicle(v
, EIT_IN_DEPOT
);
567 _cursor
.vehchain
= _ctrl_pressed
;
569 this->sel
= v
->index
;
575 case MODE_SHOW_VEHICLE
: // show info window
576 ShowVehicleViewWindow(v
);
579 case MODE_START_STOP
: // click start/stop flag
580 StartStopVehicle(v
, false);
583 default: NOT_REACHED();
588 * Function to set up vehicle specific widgets (mainly sprites and strings).
589 * Only use this function to if the widget is used for several vehicle types and each has
590 * different text/sprites. If the widget is only used for a single vehicle type, or the same
591 * text/sprites are used every time, use the nested widget array to initialize the widget.
593 void SetupWidgetData(VehicleType type
)
595 this->GetWidget
<NWidgetCore
>(WID_D_STOP_ALL
)->tool_tip
= STR_DEPOT_MASS_STOP_DEPOT_TRAIN_TOOLTIP
+ type
;
596 this->GetWidget
<NWidgetCore
>(WID_D_START_ALL
)->tool_tip
= STR_DEPOT_MASS_START_DEPOT_TRAIN_TOOLTIP
+ type
;
597 this->GetWidget
<NWidgetCore
>(WID_D_SELL
)->tool_tip
= STR_DEPOT_TRAIN_SELL_TOOLTIP
+ type
;
598 this->GetWidget
<NWidgetCore
>(WID_D_SELL_ALL
)->tool_tip
= STR_DEPOT_SELL_ALL_BUTTON_TRAIN_TOOLTIP
+ type
;
600 this->GetWidget
<NWidgetCore
>(WID_D_BUILD
)->SetDataTip(STR_DEPOT_TRAIN_NEW_VEHICLES_BUTTON
+ type
, STR_DEPOT_TRAIN_NEW_VEHICLES_TOOLTIP
+ type
);
601 this->GetWidget
<NWidgetCore
>(WID_D_CLONE
)->SetDataTip(STR_DEPOT_CLONE_TRAIN
+ type
, STR_DEPOT_CLONE_TRAIN_DEPOT_INFO
+ type
);
603 this->GetWidget
<NWidgetCore
>(WID_D_LOCATION
)->tool_tip
= STR_DEPOT_TRAIN_LOCATION_TOOLTIP
+ type
;
604 this->GetWidget
<NWidgetCore
>(WID_D_VEHICLE_LIST
)->tool_tip
= STR_DEPOT_VEHICLE_ORDER_LIST_TRAIN_TOOLTIP
+ type
;
605 this->GetWidget
<NWidgetCore
>(WID_D_AUTOREPLACE
)->tool_tip
= STR_DEPOT_AUTOREPLACE_TRAIN_TOOLTIP
+ type
;
606 this->GetWidget
<NWidgetCore
>(WID_D_MATRIX
)->tool_tip
= STR_DEPOT_TRAIN_LIST_TOOLTIP
+ this->type
;
609 default: NOT_REACHED();
612 this->GetWidget
<NWidgetCore
>(WID_D_VEHICLE_LIST
)->widget_data
= STR_TRAIN
;
615 this->GetWidget
<NWidgetCore
>(WID_D_SELL
)->widget_data
= SPR_SELL_TRAIN
;
616 this->GetWidget
<NWidgetCore
>(WID_D_SELL_ALL
)->widget_data
= SPR_SELL_ALL_TRAIN
;
617 this->GetWidget
<NWidgetCore
>(WID_D_AUTOREPLACE
)->widget_data
= SPR_REPLACE_TRAIN
;
621 this->GetWidget
<NWidgetCore
>(WID_D_VEHICLE_LIST
)->widget_data
= STR_LORRY
;
624 this->GetWidget
<NWidgetCore
>(WID_D_SELL
)->widget_data
= SPR_SELL_ROADVEH
;
625 this->GetWidget
<NWidgetCore
>(WID_D_SELL_ALL
)->widget_data
= SPR_SELL_ALL_ROADVEH
;
626 this->GetWidget
<NWidgetCore
>(WID_D_AUTOREPLACE
)->widget_data
= SPR_REPLACE_ROADVEH
;
630 this->GetWidget
<NWidgetCore
>(WID_D_VEHICLE_LIST
)->widget_data
= STR_SHIP
;
633 this->GetWidget
<NWidgetCore
>(WID_D_SELL
)->widget_data
= SPR_SELL_SHIP
;
634 this->GetWidget
<NWidgetCore
>(WID_D_SELL_ALL
)->widget_data
= SPR_SELL_ALL_SHIP
;
635 this->GetWidget
<NWidgetCore
>(WID_D_AUTOREPLACE
)->widget_data
= SPR_REPLACE_SHIP
;
639 this->GetWidget
<NWidgetCore
>(WID_D_VEHICLE_LIST
)->widget_data
= STR_PLANE
;
642 this->GetWidget
<NWidgetCore
>(WID_D_SELL
)->widget_data
= SPR_SELL_AIRCRAFT
;
643 this->GetWidget
<NWidgetCore
>(WID_D_SELL_ALL
)->widget_data
= SPR_SELL_ALL_AIRCRAFT
;
644 this->GetWidget
<NWidgetCore
>(WID_D_AUTOREPLACE
)->widget_data
= SPR_REPLACE_AIRCRAFT
;
649 uint count_width
; ///< Width of length count, including separator.
650 uint header_width
; ///< Width of unit number and flag, including separator.
651 Dimension flag_size
; ///< Size of start/stop flag.
652 VehicleCellSize cell_size
; ///< Vehicle sprite cell size.
654 void OnInit() override
656 this->cell_size
= GetVehicleImageCellSize(this->type
, EIT_IN_DEPOT
);
657 this->flag_size
= maxdim(GetScaledSpriteSize(SPR_FLAG_VEH_STOPPED
), GetScaledSpriteSize(SPR_FLAG_VEH_RUNNING
));
660 void UpdateWidgetSize(WidgetID widget
, Dimension
&size
, [[maybe_unused
]] const Dimension
&padding
, [[maybe_unused
]] Dimension
&fill
, [[maybe_unused
]] Dimension
&resize
) override
666 if (this->type
== VEH_TRAIN
) {
667 SetDParamMaxValue(0, 1000, 0, FS_SMALL
);
669 this->count_width
= GetStringBoundingBox(STR_JUST_DECIMAL
, FS_SMALL
).width
+ WidgetDimensions::scaled
.hsep_normal
;
671 this->count_width
= 0;
674 SetDParamMaxDigits(0, this->unitnumber_digits
);
675 Dimension unumber
= GetStringBoundingBox(STR_JUST_COMMA
);
677 if (this->type
== VEH_TRAIN
|| this->type
== VEH_ROAD
) {
678 min_height
= std::max
<uint
>(unumber
.height
, this->flag_size
.height
);
679 this->header_width
= unumber
.width
+ WidgetDimensions::scaled
.hsep_normal
+ this->flag_size
.width
+ WidgetDimensions::scaled
.hsep_normal
;
681 min_height
= unumber
.height
+ WidgetDimensions::scaled
.vsep_normal
+ this->flag_size
.height
;
682 this->header_width
= std::max
<uint
>(unumber
.width
, this->flag_size
.width
) + WidgetDimensions::scaled
.hsep_normal
;
684 int base_width
= this->count_width
+ this->header_width
+ padding
.width
;
686 resize
.height
= std::max
<uint
>(this->cell_size
.height
, min_height
+ padding
.height
);
687 if (this->type
== VEH_TRAIN
) {
689 size
.width
= base_width
+ 2 * ScaleSpriteTrad(29); // about 2 parts
690 size
.height
= resize
.height
* 6;
692 resize
.width
= base_width
+ this->cell_size
.extend_left
+ this->cell_size
.extend_right
;
693 size
.width
= resize
.width
* (this->type
== VEH_ROAD
? 5 : 3);
694 size
.height
= resize
.height
* (this->type
== VEH_ROAD
? 5 : 3);
696 fill
.width
= resize
.width
;
697 fill
.height
= resize
.height
;
704 * Some data on this window has become invalid.
705 * @param data Information about the changed data.
706 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
708 void OnInvalidateData([[maybe_unused
]] int data
= 0, [[maybe_unused
]] bool gui_scope
= true) override
710 this->generate_list
= true;
713 void RefreshVehicleList()
715 if (this->generate_list
) {
716 /* Generate the vehicle list
717 * It's ok to use the wagon pointers for non-trains as they will be ignored */
718 BuildDepotVehicleList(this->type
, this->window_number
, &this->vehicle_list
, &this->wagon_list
);
719 this->generate_list
= false;
720 DepotSortList(&this->vehicle_list
);
722 this->check_unitnumber_digits
= true;
726 void OnPaint() override
728 this->RefreshVehicleList();
730 if (this->check_unitnumber_digits
) {
731 this->check_unitnumber_digits
= false;
732 uint new_unitnumber_digits
= GetUnitNumberDigits(this->vehicle_list
);
733 /* Only increase the size; do not decrease to prevent constant changes */
734 if (this->unitnumber_digits
< new_unitnumber_digits
) {
735 this->unitnumber_digits
= new_unitnumber_digits
;
740 /* determine amount of items for scroller */
741 if (this->type
== VEH_TRAIN
) {
742 uint max_width
= ScaleSpriteTrad(VEHICLEINFO_FULL_VEHICLE_WIDTH
);
743 for (uint num
= 0; num
< this->vehicle_list
.size(); num
++) {
745 for (const Train
*v
= Train::From(this->vehicle_list
[num
]); v
!= nullptr; v
= v
->Next()) {
746 width
+= v
->GetDisplayImageWidth();
748 max_width
= std::max(max_width
, width
);
750 /* Always have 1 empty row, so people can change the setting of the train */
751 this->vscroll
->SetCount(this->vehicle_list
.size() + this->wagon_list
.size() + 1);
752 /* Always make it longer than the longest train, so you can attach vehicles at the end, and also see the next vertical tile separator line */
753 this->hscroll
->SetCount(max_width
+ ScaleSpriteTrad(2 * VEHICLEINFO_FULL_VEHICLE_WIDTH
+ 1));
755 this->vscroll
->SetCount(CeilDiv((uint
)this->vehicle_list
.size(), this->num_columns
));
758 /* Setup disabled buttons. */
759 TileIndex tile
= this->window_number
;
760 this->SetWidgetsDisabledState(!IsTileOwner(tile
, _local_company
),
774 void OnClick([[maybe_unused
]] Point pt
, WidgetID widget
, [[maybe_unused
]] int click_count
) override
777 case WID_D_MATRIX
: // List
778 this->DepotClick(pt
.x
, pt
.y
);
781 case WID_D_BUILD
: // Build vehicle
782 ResetObjectToPlace();
783 ShowBuildVehicleWindow(this->window_number
, this->type
);
786 case WID_D_CLONE
: // Clone button
787 this->SetWidgetDirty(WID_D_CLONE
);
788 this->ToggleWidgetLoweredState(WID_D_CLONE
);
790 if (this->IsWidgetLowered(WID_D_CLONE
)) {
791 static const CursorID clone_icons
[] = {
792 SPR_CURSOR_CLONE_TRAIN
, SPR_CURSOR_CLONE_ROADVEH
,
793 SPR_CURSOR_CLONE_SHIP
, SPR_CURSOR_CLONE_AIRPLANE
796 SetObjectToPlaceWnd(clone_icons
[this->type
], PAL_NONE
, HT_VEHICLE
, this);
798 ResetObjectToPlace();
804 ShowExtraViewportWindow(this->window_number
);
806 ScrollMainWindowToTile(this->window_number
);
810 case WID_D_RENAME
: // Rename button
811 SetDParam(0, this->type
);
812 SetDParam(1, Depot::GetByTile((TileIndex
)this->window_number
)->index
);
813 ShowQueryString(STR_DEPOT_NAME
, STR_DEPOT_RENAME_DEPOT_CAPTION
, MAX_LENGTH_DEPOT_NAME_CHARS
, this, CS_ALPHANUMERAL
, QSF_ENABLE_DEFAULT
| QSF_LEN_IN_CHARS
);
817 case WID_D_START_ALL
: {
818 VehicleListIdentifier
vli(VL_DEPOT_LIST
, this->type
, this->owner
);
819 Command
<CMD_MASS_START_STOP
>::Post(this->window_number
, widget
== WID_D_START_ALL
, false, vli
);
824 /* Only open the confirmation window if there are anything to sell */
825 if (!this->vehicle_list
.empty() || !this->wagon_list
.empty()) {
826 SetDParam(0, this->type
);
827 SetDParam(1, this->GetDepotIndex());
830 STR_DEPOT_SELL_CONFIRMATION_TEXT
,
832 DepotSellAllConfirmationCallback
837 case WID_D_VEHICLE_LIST
:
838 ShowVehicleListWindow(GetTileOwner(this->window_number
), this->type
, (TileIndex
)this->window_number
);
841 case WID_D_AUTOREPLACE
:
842 Command
<CMD_DEPOT_MASS_AUTOREPLACE
>::Post(this->window_number
, this->type
);
848 void OnQueryTextFinished(std::optional
<std::string
> str
) override
850 if (!str
.has_value()) return;
852 /* Do depot renaming */
853 Command
<CMD_RENAME_DEPOT
>::Post(STR_ERROR_CAN_T_RENAME_DEPOT
, this->GetDepotIndex(), *str
);
856 bool OnRightClick([[maybe_unused
]] Point pt
, WidgetID widget
) override
858 if (widget
!= WID_D_MATRIX
) return false;
860 GetDepotVehiclePtData gdvp
= { nullptr, nullptr };
861 const Vehicle
*v
= nullptr;
862 DepotGUIAction mode
= this->GetVehicleFromDepotWndPt(pt
.x
, pt
.y
, &v
, &gdvp
);
864 if (this->type
== VEH_TRAIN
) v
= gdvp
.wagon
;
866 if (v
== nullptr || mode
!= MODE_DRAG_VEHICLE
) return false;
868 CargoArray capacity
{}, loaded
{};
870 /* Display info for single (articulated) vehicle, or for whole chain starting with selected vehicle */
871 bool whole_chain
= (this->type
== VEH_TRAIN
&& _ctrl_pressed
);
873 /* loop through vehicle chain and collect cargoes */
875 for (const Vehicle
*w
= v
; w
!= nullptr; w
= w
->Next()) {
876 if (w
->cargo_cap
> 0 && w
->cargo_type
< NUM_CARGO
) {
877 capacity
[w
->cargo_type
] += w
->cargo_cap
;
878 loaded
[w
->cargo_type
] += w
->cargo
.StoredCount();
881 if (w
->type
== VEH_TRAIN
&& !w
->HasArticulatedPart()) {
883 if (!whole_chain
) break;
887 /* Build tooltipstring */
888 static std::string details
;
891 for (const CargoSpec
*cs
: _sorted_cargo_specs
) {
892 CargoID cargo_type
= cs
->Index();
893 if (capacity
[cargo_type
] == 0) continue;
895 SetDParam(0, cargo_type
); // {CARGO} #1
896 SetDParam(1, loaded
[cargo_type
]); // {CARGO} #2
897 SetDParam(2, cargo_type
); // {SHORTCARGO} #1
898 SetDParam(3, capacity
[cargo_type
]); // {SHORTCARGO} #2
899 AppendStringInPlace(details
, STR_DEPOT_VEHICLE_TOOLTIP_CARGO
);
902 /* Show tooltip window */
903 SetDParam(0, whole_chain
? num
: v
->engine_type
);
904 SetDParamStr(1, details
);
905 GuiShowTooltips(this, whole_chain
? STR_DEPOT_VEHICLE_TOOLTIP_CHAIN
: STR_DEPOT_VEHICLE_TOOLTIP
, TCC_RIGHT_CLICK
, 2);
912 * @param v the original vehicle to clone
913 * @return Always true.
915 bool OnVehicleSelect(const Vehicle
*v
) override
918 /* Share-clone, do not open new viewport, and keep tool active */
919 Command
<CMD_CLONE_VEHICLE
>::Post(STR_ERROR_CAN_T_BUY_TRAIN
+ v
->type
, this->window_number
, v
->index
, true);
921 /* Copy-clone, open viewport for new vehicle, and deselect the tool (assume player wants to change things on new vehicle) */
922 if (Command
<CMD_CLONE_VEHICLE
>::Post(STR_ERROR_CAN_T_BUY_TRAIN
+ v
->type
, CcCloneVehicle
, this->window_number
, v
->index
, false)) {
923 ResetObjectToPlace();
931 * 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.
932 * @return This always returns true, which indicates that the contextual action handled the mouse click.
933 * Note that it's correct behaviour to always handle the click even though an error is displayed,
934 * because users aren't going to expect the default action to be performed just because they overlooked that cloning doesn't make sense.
936 bool OnVehicleSelect(VehicleList::const_iterator begin
, VehicleList::const_iterator end
) override
938 if (!_ctrl_pressed
) {
939 /* If CTRL is not pressed: If all the vehicles in this list have the same orders, then copy orders */
940 if (AllEqual(begin
, end
, [](const Vehicle
*v1
, const Vehicle
*v2
) {
941 return VehiclesHaveSameEngineList(v1
, v2
);
943 if (AllEqual(begin
, end
, [](const Vehicle
*v1
, const Vehicle
*v2
) {
944 return VehiclesHaveSameOrderList(v1
, v2
);
946 OnVehicleSelect(*begin
);
948 ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN
+ (*begin
)->type
, STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST
, WL_INFO
);
951 ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN
+ (*begin
)->type
, STR_ERROR_CAN_T_CLONE_VEHICLE_LIST
, WL_INFO
);
954 /* If CTRL is pressed: If all the vehicles in this list share orders, then copy orders */
955 if (AllEqual(begin
, end
, [](const Vehicle
*v1
, const Vehicle
*v2
) {
956 return VehiclesHaveSameEngineList(v1
, v2
);
958 if (AllEqual(begin
, end
, [](const Vehicle
*v1
, const Vehicle
*v2
) {
959 return v1
->FirstShared() == v2
->FirstShared();
961 OnVehicleSelect(*begin
);
963 ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN
+ (*begin
)->type
, STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST
, WL_INFO
);
966 ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN
+ (*begin
)->type
, STR_ERROR_CAN_T_CLONE_VEHICLE_LIST
, WL_INFO
);
973 void OnPlaceObjectAbort() override
976 this->RaiseWidget(WID_D_CLONE
);
977 this->SetWidgetDirty(WID_D_CLONE
);
979 /* abort drag & drop */
980 this->sel
= INVALID_VEHICLE
;
981 this->vehicle_over
= INVALID_VEHICLE
;
982 this->SetWidgetDirty(WID_D_MATRIX
);
984 if (this->hovered_widget
!= -1) {
985 this->SetWidgetLoweredState(this->hovered_widget
, false);
986 this->SetWidgetDirty(this->hovered_widget
);
987 this->hovered_widget
= -1;
991 bool last_overlay_state
;
992 void OnMouseLoop() override
994 if (last_overlay_state
!= ShowCargoIconOverlay()) {
995 last_overlay_state
= ShowCargoIconOverlay();
1000 void OnMouseDrag(Point pt
, WidgetID widget
) override
1002 if (this->sel
== INVALID_VEHICLE
) return;
1003 if (widget
!= this->hovered_widget
) {
1004 if (this->hovered_widget
== WID_D_SELL
|| this->hovered_widget
== WID_D_SELL_CHAIN
) {
1005 this->SetWidgetLoweredState(this->hovered_widget
, false);
1006 this->SetWidgetDirty(this->hovered_widget
);
1008 this->hovered_widget
= widget
;
1009 if (this->hovered_widget
== WID_D_SELL
|| this->hovered_widget
== WID_D_SELL_CHAIN
) {
1010 this->SetWidgetLoweredState(this->hovered_widget
, true);
1011 this->SetWidgetDirty(this->hovered_widget
);
1014 if (this->type
!= VEH_TRAIN
) return;
1016 /* A rail vehicle is dragged.. */
1017 if (widget
!= WID_D_MATRIX
) { // ..outside of the depot matrix.
1018 if (this->vehicle_over
!= INVALID_VEHICLE
) {
1019 this->vehicle_over
= INVALID_VEHICLE
;
1020 this->SetWidgetDirty(WID_D_MATRIX
);
1025 const Vehicle
*v
= nullptr;
1026 GetDepotVehiclePtData gdvp
= {nullptr, nullptr};
1028 if (this->GetVehicleFromDepotWndPt(pt
.x
, pt
.y
, &v
, &gdvp
) != MODE_DRAG_VEHICLE
) return;
1030 VehicleID new_vehicle_over
= INVALID_VEHICLE
;
1031 if (gdvp
.head
!= nullptr) {
1032 if (gdvp
.wagon
== nullptr && gdvp
.head
->Last()->index
!= this->sel
) { // ..at the end of the train.
1033 /* NOTE: As a wagon can't be moved at the begin of a train, head index isn't used to mark a drag-and-drop
1034 * destination inside a train. This head index is then used to indicate that a wagon is inserted at
1035 * the end of the train.
1037 new_vehicle_over
= gdvp
.head
->index
;
1038 } else if (gdvp
.wagon
!= nullptr && gdvp
.head
!= gdvp
.wagon
&&
1039 gdvp
.wagon
->index
!= this->sel
&&
1040 gdvp
.wagon
->Previous()->index
!= this->sel
) { // ..over an existing wagon.
1041 new_vehicle_over
= gdvp
.wagon
->index
;
1045 if (this->vehicle_over
== new_vehicle_over
) return;
1047 this->vehicle_over
= new_vehicle_over
;
1048 this->SetWidgetDirty(widget
);
1051 void OnDragDrop(Point pt
, WidgetID widget
) override
1054 case WID_D_MATRIX
: {
1055 const Vehicle
*v
= nullptr;
1056 VehicleID sel
= this->sel
;
1058 this->sel
= INVALID_VEHICLE
;
1061 if (this->type
== VEH_TRAIN
) {
1062 GetDepotVehiclePtData gdvp
= { nullptr, nullptr };
1064 if (this->GetVehicleFromDepotWndPt(pt
.x
, pt
.y
, &v
, &gdvp
) == MODE_DRAG_VEHICLE
&& sel
!= INVALID_VEHICLE
) {
1065 if (gdvp
.wagon
!= nullptr && gdvp
.wagon
->index
== sel
&& _ctrl_pressed
) {
1066 Command
<CMD_REVERSE_TRAIN_DIRECTION
>::Post(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE
, Vehicle::Get(sel
)->tile
, Vehicle::Get(sel
)->index
, true);
1067 } else if (gdvp
.wagon
== nullptr || gdvp
.wagon
->index
!= sel
) {
1068 this->vehicle_over
= INVALID_VEHICLE
;
1069 TrainDepotMoveVehicle(gdvp
.wagon
, sel
, gdvp
.head
);
1070 } else if (gdvp
.head
!= nullptr && gdvp
.head
->IsFrontEngine()) {
1071 ShowVehicleViewWindow(gdvp
.head
);
1074 } else if (this->GetVehicleFromDepotWndPt(pt
.x
, pt
.y
, &v
, nullptr) == MODE_DRAG_VEHICLE
&& v
!= nullptr && sel
== v
->index
) {
1075 ShowVehicleViewWindow(v
);
1080 case WID_D_SELL
: case WID_D_SELL_CHAIN
: {
1081 if (this->IsWidgetDisabled(widget
)) return;
1082 if (this->sel
== INVALID_VEHICLE
) return;
1084 this->HandleButtonClick(widget
);
1086 const Vehicle
*v
= Vehicle::Get(this->sel
);
1087 this->sel
= INVALID_VEHICLE
;
1090 bool sell_cmd
= (v
->type
== VEH_TRAIN
&& (widget
== WID_D_SELL_CHAIN
|| _ctrl_pressed
));
1091 Command
<CMD_SELL_VEHICLE
>::Post(GetCmdSellVehMsg(v
->type
), v
->tile
, v
->index
, sell_cmd
, true, INVALID_CLIENT_ID
);
1096 this->sel
= INVALID_VEHICLE
;
1100 this->hovered_widget
= -1;
1101 _cursor
.vehchain
= false;
1104 void OnTimeout() override
1106 if (!this->IsWidgetDisabled(WID_D_SELL
)) {
1107 this->RaiseWidget(WID_D_SELL
);
1108 this->SetWidgetDirty(WID_D_SELL
);
1110 if (this->GetWidget
<NWidgetBase
>(WID_D_SELL
) != nullptr && !this->IsWidgetDisabled(WID_D_SELL_CHAIN
)) {
1111 this->RaiseWidget(WID_D_SELL_CHAIN
);
1112 this->SetWidgetDirty(WID_D_SELL_CHAIN
);
1116 void OnResize() override
1118 this->vscroll
->SetCapacityFromWidget(this, WID_D_MATRIX
);
1119 NWidgetCore
*nwi
= this->GetWidget
<NWidgetCore
>(WID_D_MATRIX
);
1120 if (this->type
== VEH_TRAIN
) {
1121 this->hscroll
->SetCapacity(nwi
->current_x
- this->header_width
- this->count_width
);
1123 this->num_columns
= nwi
->current_x
/ nwi
->resize_x
;
1127 EventState
OnCTRLStateChange() override
1129 if (this->sel
!= INVALID_VEHICLE
) {
1130 _cursor
.vehchain
= _ctrl_pressed
;
1131 this->SetWidgetDirty(WID_D_MATRIX
);
1135 return ES_NOT_HANDLED
;
1139 * Gets the DepotID of the current window.
1140 * In the case of airports, this is the station ID.
1141 * @return Depot or station ID of this window.
1143 inline uint16_t GetDepotIndex() const
1145 return (this->type
== VEH_AIRCRAFT
) ? ::GetStationIndex(this->window_number
) : ::GetDepotIndex(this->window_number
);
1149 static void DepotSellAllConfirmationCallback(Window
*win
, bool confirmed
)
1152 DepotWindow
*w
= (DepotWindow
*)win
;
1153 TileIndex tile
= w
->window_number
;
1154 VehicleType vehtype
= w
->type
;
1155 Command
<CMD_DEPOT_SELL_ALL_VEHICLES
>::Post(tile
, vehtype
);
1160 * Opens a depot window
1161 * @param tile The tile where the depot/hangar is located
1162 * @param type The type of vehicles in the depot
1164 void ShowDepotWindow(TileIndex tile
, VehicleType type
)
1166 if (BringWindowToFrontById(WC_VEHICLE_DEPOT
, tile
) != nullptr) return;
1169 default: NOT_REACHED();
1170 case VEH_TRAIN
: new DepotWindow(_train_depot_desc
, tile
, type
); break;
1171 case VEH_ROAD
: new DepotWindow(_road_depot_desc
, tile
, type
); break;
1172 case VEH_SHIP
: new DepotWindow(_ship_depot_desc
, tile
, type
); break;
1173 case VEH_AIRCRAFT
: new DepotWindow(_aircraft_depot_desc
, tile
, type
); break;
1178 * Removes the highlight of a vehicle in a depot window
1179 * @param *v Vehicle to remove all highlights from
1181 void DeleteDepotHighlightOfVehicle(const Vehicle
*v
)
1185 /* If we haven't got any vehicles on the mouse pointer, we haven't got any highlighted in any depots either
1186 * If that is the case, we can skip looping though the windows and save time
1188 if (_special_mouse_mode
!= WSM_DRAGDROP
) return;
1190 w
= dynamic_cast<DepotWindow
*>(FindWindowById(WC_VEHICLE_DEPOT
, v
->tile
));
1192 if (w
->sel
== v
->index
) ResetObjectToPlace();