OpenTTD Source 20241222-master-gc72542431a
group_gui.cpp
Go to the documentation of this file.
1/*
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/>.
6 */
7
10#include "stdafx.h"
11#include "textbuf_gui.h"
12#include "command_func.h"
13#include "vehicle_gui.h"
14#include "vehicle_base.h"
15#include "string_func.h"
16#include "strings_func.h"
17#include "window_func.h"
18#include "vehicle_func.h"
19#include "autoreplace_gui.h"
20#include "company_func.h"
21#include "dropdown_func.h"
22#include "tilehighlight_func.h"
23#include "vehicle_gui_base.h"
26#include "company_base.h"
27#include "company_gui.h"
28#include "gui.h"
29#include "group_cmd.h"
30#include "group_gui.h"
31#include "vehicle_cmd.h"
32#include "gfx_func.h"
33
35
36#include "table/sprites.h"
37
38#include "safeguards.h"
39
40static constexpr NWidgetPart _nested_group_widgets[] = {
41 NWidget(NWID_HORIZONTAL), // Window header
42 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
43 NWidget(WWT_CAPTION, COLOUR_GREY, WID_GL_CAPTION),
44 NWidget(WWT_SHADEBOX, COLOUR_GREY),
45 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
46 NWidget(WWT_STICKYBOX, COLOUR_GREY),
49 /* left part */
54 NWidget(WWT_MATRIX, COLOUR_GREY, WID_GL_LIST_GROUP), SetMatrixDataTip(1, 0, STR_GROUPS_CLICK_ON_GROUP_FOR_TOOLTIP),
58 NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_INFO), SetFill(1, 1), SetMinimalTextLines(3, WidgetDimensions::unscaled.framerect.Vertical()), EndContainer(),
61 SetDataTip(SPR_GROUP_CREATE_TRAIN, STR_GROUP_CREATE_TOOLTIP),
63 SetDataTip(SPR_GROUP_DELETE_TRAIN, STR_GROUP_DELETE_TOOLTIP),
65 SetDataTip(SPR_GROUP_RENAME_TRAIN, STR_GROUP_RENAME_TOOLTIP),
67 SetDataTip(SPR_GROUP_LIVERY_TRAIN, STR_GROUP_LIVERY_TOOLTIP),
68 NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 0), EndContainer(),
70 SetDataTip(SPR_GROUP_REPLACE_OFF_TRAIN, STR_GROUP_REPLACE_PROTECTION_TOOLTIP),
73 /* right part */
77 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GL_GROUP_BY_ORDER), SetFill(1, 1), SetMinimalSize(0, 12), SetDataTip(STR_STATION_VIEW_GROUP, STR_TOOLTIP_GROUP_ORDER),
78 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GL_SORT_BY_ORDER), SetFill(1, 1), SetMinimalSize(0, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
81 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_GROUP_BY_DROPDOWN), SetFill(1, 1), SetMinimalSize(0, 12), SetDataTip(0x0, STR_TOOLTIP_GROUP_ORDER),
82 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_SORT_BY_DROPDOWN), SetFill(1, 1), SetMinimalSize(0, 12), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
85 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetFill(0, 1), SetResize(1, 0), EndContainer(),
87 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_FILTER_BY_CARGO), SetMinimalSize(0, 12), SetFill(0, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
88 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(0, 1), SetResize(1, 0), EndContainer(),
96 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(1, 0), SetFill(1, 1), SetResize(1, 0), EndContainer(),
99 SetDataTip(STR_JUST_STRING, STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP),
100 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetResize(1, 0), EndContainer(),
102 SetDataTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP),
103 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_STOP_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG),
104 SetDataTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP),
105 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_START_ALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG),
106 SetDataTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP),
107 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
108 EndContainer(),
109 EndContainer(),
110 EndContainer(),
111};
112
121static void GuiGroupListAddChildren(GUIGroupList &dst, const GUIGroupList &src, bool fold, GroupID parent = INVALID_GROUP, uint8_t indent = 0)
122{
123 for (const auto &item : src) {
124 if (item.group->parent != parent) continue;
125
126 dst.emplace_back(item.group, indent);
127
128 if (fold && item.group->folded) {
129 /* Test if this group has children at all. If not, the folded flag should be cleared to avoid lingering unfold buttons in the list. */
130 GroupID groupid = item.group->index;
131 bool has_children = std::any_of(src.begin(), src.end(), [groupid](const auto &child) { return child.group->parent == groupid; });
132 Group::Get(item.group->index)->folded = has_children;
133 } else {
134 GuiGroupListAddChildren(dst, src, fold, item.group->index, indent + 1);
135 }
136 }
137
138 if (indent > 0 || dst.empty()) return;
139
140 /* Hierarchy is complete, traverse in reverse to find where indentation levels continue. */
141 uint16_t level_mask = 0;
142 for (auto it = std::rbegin(dst); std::next(it) != std::rend(dst); ++it) {
143 auto next_it = std::next(it);
144 AssignBit(level_mask, it->indent, it->indent <= next_it->indent);
145 next_it->level_mask = level_mask;
146 }
147}
148
156void BuildGuiGroupList(GUIGroupList &dst, bool fold, Owner owner, VehicleType veh_type)
157{
158 GUIGroupList list;
159
160 for (const Group *g : Group::Iterate()) {
161 if (g->owner == owner && g->vehicle_type == veh_type) {
162 list.emplace_back(g, 0);
163 }
164 }
165
166 list.ForceResort();
167
168 /* Sort the groups by their name */
169 std::array<std::pair<const Group *, std::string>, 2> last_group{};
170
171 list.Sort([&last_group](const GUIGroupListItem &a, const GUIGroupListItem &b) -> bool {
172 if (a.group != last_group[0].first) {
173 SetDParam(0, a.group->index);
174 last_group[0] = {a.group, GetString(STR_GROUP_NAME)};
175 }
176
177 if (b.group != last_group[1].first) {
178 SetDParam(0, b.group->index);
179 last_group[1] = {b.group, GetString(STR_GROUP_NAME)};
180 }
181
182 int r = StrNaturalCompare(last_group[0].second, last_group[1].second); // Sort by name (natural sorting).
183 if (r == 0) return a.group->number < b.group->number;
184 return r < 0;
185 });
186
187 GuiGroupListAddChildren(dst, list, fold, INVALID_GROUP, 0);
188}
189
191private:
192 /* Columns in the group list */
203
210 Scrollbar *group_sb;
211
213
220 {
221 if (!this->groups.NeedRebuild()) return;
222
223 this->groups.clear();
224
225 BuildGuiGroupList(this->groups, true, owner, this->vli.vtype);
226
227 this->groups.RebuildDone();
228 }
229
235 {
236 this->column_size[VGC_FOLD] = maxdim(GetSpriteSize(SPR_CIRCLE_FOLDED), GetSpriteSize(SPR_CIRCLE_UNFOLDED));
237 this->tiny_step_height = this->column_size[VGC_FOLD].height;
238
240 this->column_size[VGC_NAME].width = std::max(170u, this->column_size[VGC_NAME].width) + WidgetDimensions::scaled.hsep_indent;
241 this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_NAME].height);
242
243 this->column_size[VGC_PROTECT] = GetSpriteSize(SPR_GROUP_REPLACE_PROTECT);
244 this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_PROTECT].height);
245
246 this->column_size[VGC_AUTOREPLACE] = GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE);
247 this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_AUTOREPLACE].height);
248
249 this->column_size[VGC_PROFIT].width = 0;
250 this->column_size[VGC_PROFIT].height = 0;
251 static const SpriteID profit_sprites[] = {SPR_PROFIT_NA, SPR_PROFIT_NEGATIVE, SPR_PROFIT_SOME, SPR_PROFIT_LOT};
252 for (const auto &profit_sprite : profit_sprites) {
254 this->column_size[VGC_PROFIT] = maxdim(this->column_size[VGC_PROFIT], d);
255 }
256 this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_PROFIT].height);
257
258 int num_vehicle = GetGroupNumVehicle(this->vli.company, ALL_GROUP, this->vli.vtype);
259 SetDParamMaxValue(0, num_vehicle, 3, FS_SMALL);
260 SetDParamMaxValue(1, num_vehicle, 3, FS_SMALL);
262 this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_NUMBER].height);
263
264 this->tiny_step_height += WidgetDimensions::scaled.framerect.Vertical();
265
267 this->column_size[VGC_FOLD].width + WidgetDimensions::scaled.hsep_normal +
268 this->column_size[VGC_NAME].width + WidgetDimensions::scaled.hsep_wide +
269 this->column_size[VGC_PROTECT].width + WidgetDimensions::scaled.hsep_normal +
270 this->column_size[VGC_AUTOREPLACE].width + WidgetDimensions::scaled.hsep_normal +
271 this->column_size[VGC_PROFIT].width + WidgetDimensions::scaled.hsep_normal +
272 this->column_size[VGC_NUMBER].width +
274 }
275
286 void DrawGroupInfo(int y, int left, int right, GroupID g_id, uint16_t level_mask = 0, uint8_t indent = 0, bool protection = false, bool has_children = false) const
287 {
288 /* Highlight the group if a vehicle is dragged over it */
289 if (g_id == this->group_over) {
290 GfxFillRect(left + WidgetDimensions::scaled.bevel.left, y + WidgetDimensions::scaled.framerect.top, right - WidgetDimensions::scaled.bevel.right, y + this->tiny_step_height - 1 - WidgetDimensions::scaled.framerect.bottom, GetColourGradient(COLOUR_GREY, SHADE_LIGHTEST));
291 }
292
293 if (g_id == NEW_GROUP) return;
294
295 /* draw the selected group in white, else we draw it in black */
296 TextColour colour = g_id == this->vli.index ? TC_WHITE : TC_BLACK;
297 const GroupStatistics &stats = GroupStatistics::Get(this->vli.company, g_id, this->vli.vtype);
298 bool rtl = _current_text_dir == TD_RTL;
299
300 const int offset = (rtl ? -(int)this->column_size[VGC_FOLD].width : (int)this->column_size[VGC_FOLD].width) / 2;
302 const int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
303
304 if (indent > 0) {
305 /* Draw tree continuation lines. */
306 int tx = (rtl ? right - WidgetDimensions::scaled.framerect.right : left + WidgetDimensions::scaled.framerect.left) + offset;
307 for (uint lvl = 1; lvl <= indent; ++lvl) {
308 if (HasBit(level_mask, lvl)) GfxDrawLine(tx, y, tx, y + this->tiny_step_height - 1, linecolour, WidgetDimensions::scaled.fullbevel.top);
309 if (lvl < indent) tx += level_width;
310 }
311 /* Draw our node in the tree. */
312 int ycentre = y + this->tiny_step_height / 2 - 1;
313 if (!HasBit(level_mask, indent)) GfxDrawLine(tx, y, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
314 GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
315 }
316
317 /* draw fold / unfold button */
318 int x = rtl ? right - WidgetDimensions::scaled.framerect.right - this->column_size[VGC_FOLD].width + 1 : left + WidgetDimensions::scaled.framerect.left;
319 if (has_children) {
320 DrawSprite(Group::Get(g_id)->folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, x + indent * level_width, y + (this->tiny_step_height - this->column_size[VGC_FOLD].height) / 2);
321 }
322
323 /* draw group name */
324 StringID str;
325 if (IsAllGroupID(g_id)) {
326 str = STR_GROUP_ALL_TRAINS + this->vli.vtype;
327 } else if (IsDefaultGroupID(g_id)) {
328 str = STR_GROUP_DEFAULT_TRAINS + this->vli.vtype;
329 } else {
330 SetDParam(0, g_id);
331 str = STR_GROUP_NAME;
332 }
333 x = rtl ? x - WidgetDimensions::scaled.hsep_normal - this->column_size[VGC_NAME].width : x + WidgetDimensions::scaled.hsep_normal + this->column_size[VGC_FOLD].width;
334 DrawString(x + (rtl ? 0 : indent * WidgetDimensions::scaled.hsep_indent), x + this->column_size[VGC_NAME].width - 1 - (rtl ? indent * WidgetDimensions::scaled.hsep_indent : 0), y + (this->tiny_step_height - this->column_size[VGC_NAME].height) / 2, str, colour);
335
336 /* draw autoreplace protection */
337 x = rtl ? x - WidgetDimensions::scaled.hsep_wide - this->column_size[VGC_PROTECT].width : x + WidgetDimensions::scaled.hsep_wide + this->column_size[VGC_NAME].width;
338 if (protection) DrawSprite(SPR_GROUP_REPLACE_PROTECT, PAL_NONE, x, y + (this->tiny_step_height - this->column_size[VGC_PROTECT].height) / 2);
339
340 /* draw autoreplace status */
341 x = rtl ? x - WidgetDimensions::scaled.hsep_normal - this->column_size[VGC_AUTOREPLACE].width : x + WidgetDimensions::scaled.hsep_normal + this->column_size[VGC_PROTECT].width;
342 if (stats.autoreplace_defined) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, stats.autoreplace_finished ? PALETTE_CRASH : PAL_NONE, x, y + (this->tiny_step_height - this->column_size[VGC_AUTOREPLACE].height) / 2);
343
344 /* draw the profit icon */
345 x = rtl ? x - WidgetDimensions::scaled.hsep_normal - this->column_size[VGC_PROFIT].width : x + WidgetDimensions::scaled.hsep_normal + this->column_size[VGC_AUTOREPLACE].width;
347 uint num_vehicle_min_age = GetGroupNumVehicleMinAge(this->vli.company, g_id, this->vli.vtype);
348 Money profit_last_year_min_age = GetGroupProfitLastYearMinAge(this->vli.company, g_id, this->vli.vtype);
349 if (num_vehicle_min_age == 0) {
350 spr = SPR_PROFIT_NA;
351 } else if (profit_last_year_min_age < 0) {
352 spr = SPR_PROFIT_NEGATIVE;
353 } else if (profit_last_year_min_age < VEHICLE_PROFIT_THRESHOLD * num_vehicle_min_age) {
354 spr = SPR_PROFIT_SOME;
355 } else {
356 spr = SPR_PROFIT_LOT;
357 }
358 DrawSprite(spr, PAL_NONE, x, y + (this->tiny_step_height - this->column_size[VGC_PROFIT].height) / 2);
359
360 /* draw the number of vehicles of the group */
361 x = rtl ? x - WidgetDimensions::scaled.hsep_normal - this->column_size[VGC_NUMBER].width : x + WidgetDimensions::scaled.hsep_normal + this->column_size[VGC_PROFIT].width;
362 int num_vehicle_with_subgroups = GetGroupNumVehicle(this->vli.company, g_id, this->vli.vtype);
363 int num_vehicle = GroupStatistics::Get(this->vli.company, g_id, this->vli.vtype).num_vehicle;
364 if (IsAllGroupID(g_id) || IsDefaultGroupID(g_id) || num_vehicle_with_subgroups == num_vehicle) {
365 SetDParam(0, num_vehicle);
366 DrawString(x, x + this->column_size[VGC_NUMBER].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NUMBER].height) / 2, STR_JUST_COMMA, colour, SA_RIGHT | SA_FORCE, false, FS_SMALL);
367 } else {
368 SetDParam(0, num_vehicle);
369 SetDParam(1, num_vehicle_with_subgroups - num_vehicle);
370 DrawString(x, x + this->column_size[VGC_NUMBER].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NUMBER].height) / 2, STR_GROUP_COUNT_WITH_SUBGROUP, colour, SA_RIGHT | SA_FORCE);
371 }
372 }
373
378 {
379 if (this->group_over == INVALID_GROUP) return;
380
381 if (IsAllGroupID(this->group_over)) {
383 } else if (IsDefaultGroupID(this->group_over)) {
385 } else {
387 }
388 }
389
390public:
392 {
393 this->CreateNestedTree();
394
395 this->vscroll = this->GetScrollbar(WID_GL_LIST_VEHICLE_SCROLLBAR);
396 this->group_sb = this->GetScrollbar(WID_GL_LIST_GROUP_SCROLLBAR);
397
398 this->vli.index = ALL_GROUP;
399 this->group_sel = INVALID_GROUP;
400 this->group_rename = INVALID_GROUP;
401 this->group_over = INVALID_GROUP;
402
403 this->groups.ForceRebuild();
404 this->groups.NeedResort();
406 this->group_sb->SetCount(this->groups.size());
407
410
411 this->GetWidget<NWidgetCore>(WID_GL_CREATE_GROUP)->widget_data += this->vli.vtype;
412 this->GetWidget<NWidgetCore>(WID_GL_RENAME_GROUP)->widget_data += this->vli.vtype;
413 this->GetWidget<NWidgetCore>(WID_GL_DELETE_GROUP)->widget_data += this->vli.vtype;
414 this->GetWidget<NWidgetCore>(WID_GL_LIVERY_GROUP)->widget_data += this->vli.vtype;
415 this->GetWidget<NWidgetCore>(WID_GL_REPLACE_PROTECTION)->widget_data += this->vli.vtype;
416
417 this->FinishInitNested(window_number);
418 this->owner = vli.company;
419
420 this->BuildVehicleList();
421 this->SortVehicleList();
422 }
423
425 {
426 *this->sorting = this->vehgroups.GetListing();
427 }
428
430 {
431 switch (widget) {
433 size.width = this->ComputeGroupInfoSize();
434 resize.height = this->tiny_step_height;
435 fill.height = this->tiny_step_height;
436 break;
437
440 size.width = this->ComputeGroupInfoSize();
441 size.height = this->tiny_step_height;
442 break;
443
445 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
446 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
447 d.height += padding.height;
448 size = maxdim(size, d);
449 break;
450 }
451
453 this->ComputeGroupInfoSize();
454 resize.height = GetVehicleListHeight(this->vli.vtype, this->tiny_step_height);
455 size.height = 4 * resize.height;
456 break;
457
459 size.width = GetStringListWidth(this->vehicle_group_by_names) + padding.width;
460 break;
461
463 size.width = GetStringListWidth(this->vehicle_group_none_sorter_names_calendar);
464 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_none_sorter_names_wallclock));
465 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_calendar));
466 size.width = std::max(size.width, GetStringListWidth(this->vehicle_group_shared_orders_sorter_names_wallclock));
467 size.width += padding.width;
468 break;
469
471 size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList(true)).width + padding.width);
472 break;
473
475 Dimension d = this->GetActionDropdownSize(true, true, true);
476 d.height += padding.height;
477 d.width += padding.width;
478 size = maxdim(size, d);
479 break;
480 }
481 }
482 }
483
489 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
490 {
491 if (data == 0) {
492 /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
493 this->vehgroups.ForceRebuild();
494 this->groups.ForceRebuild();
495 } else {
496 this->vehgroups.ForceResort();
497 this->groups.ForceResort();
498 }
499
500 /* Process ID-invalidation in command-scope as well */
501 if (this->group_rename != INVALID_GROUP && !Group::IsValidID(this->group_rename)) {
503 this->group_rename = INVALID_GROUP;
504 }
505
506 if (!(IsAllGroupID(this->vli.index) || IsDefaultGroupID(this->vli.index) || Group::IsValidID(this->vli.index))) {
507 this->vli.index = ALL_GROUP;
509 }
510 this->SetDirty();
511 }
512
513 void SetStringParameters(WidgetID widget) const override
514 {
515 switch (widget) {
517 SetDParam(0, this->GetCargoFilterLabel(this->cargo_filter_criteria));
518 break;
519
522 break;
523
524 case WID_GL_CAPTION:
525 /* If selected_group == DEFAULT_GROUP || ALL_GROUP, draw the standard caption
526 * We list all vehicles or ungrouped vehicles */
527 if (IsDefaultGroupID(this->vli.index) || IsAllGroupID(this->vli.index)) {
529 SetDParam(1, this->vli.company);
530 SetDParam(2, this->vehicles.size());
531 SetDParam(3, this->vehicles.size());
532 } else {
533 uint num_vehicle = GetGroupNumVehicle(this->vli.company, this->vli.index, this->vli.vtype);
534
536 SetDParam(1, this->vli.index);
537 SetDParam(2, num_vehicle);
538 SetDParam(3, num_vehicle);
539 }
540 break;
541 }
542 }
543
544 void OnPaint() override
545 {
546 /* If we select the all vehicles, this->list will contain all vehicles of the owner
547 * else this->list will contain all vehicles which belong to the selected group */
548 this->BuildVehicleList();
549 this->SortVehicleList();
550
551 this->BuildGroupList(this->owner);
552
553 this->group_sb->SetCount(this->groups.size());
554 this->vscroll->SetCount(this->vehgroups.size());
555
556 /* The drop down menu is out, *but* it may not be used, retract it. */
557 if (this->vehicles.empty() && this->IsWidgetLowered(WID_GL_MANAGE_VEHICLES_DROPDOWN)) {
560 }
561
562 /* Disable all lists management button when the list is empty */
563 this->SetWidgetsDisabledState(this->vehicles.empty() || _local_company != this->vli.company,
567
568 /* Disable the group specific function when we select the default group or all vehicles */
569 this->SetWidgetsDisabledState(IsDefaultGroupID(this->vli.index) || IsAllGroupID(this->vli.index) || _local_company != this->vli.company,
574
575 /* Disable remaining buttons for non-local companies
576 * Needed while changing _local_company, eg. by cheats
577 * All procedures (eg. move vehicle to another group)
578 * verify, whether you are the owner of the vehicle,
579 * so it doesn't have to be disabled
580 */
584
585 /* If not a default group and the group has replace protection, show an enabled replace sprite. */
586 uint16_t protect_sprite = SPR_GROUP_REPLACE_OFF_TRAIN;
587 if (!IsDefaultGroupID(this->vli.index) && !IsAllGroupID(this->vli.index) && HasBit(Group::Get(this->vli.index)->flags, GroupFlags::GF_REPLACE_PROTECTION)) protect_sprite = SPR_GROUP_REPLACE_ON_TRAIN;
588 this->GetWidget<NWidgetCore>(WID_GL_REPLACE_PROTECTION)->widget_data = protect_sprite + this->vli.vtype;
589
590 /* Set text of "group by" dropdown widget. */
591 this->GetWidget<NWidgetCore>(WID_GL_GROUP_BY_DROPDOWN)->widget_data = std::data(this->vehicle_group_by_names)[this->grouping];
592
593 /* Set text of "sort by" dropdown widget. */
594 this->GetWidget<NWidgetCore>(WID_GL_SORT_BY_DROPDOWN)->widget_data = this->GetVehicleSorterNames()[this->vehgroups.SortType()];
595
596 this->DrawWidgets();
597 }
598
599 void DrawWidget(const Rect &r, WidgetID widget) const override
600 {
601 switch (widget) {
603 DrawGroupInfo(r.top, r.left, r.right, ALL_GROUP);
604 break;
605
607 DrawGroupInfo(r.top, r.left, r.right, DEFAULT_GROUP);
608 break;
609
610 case WID_GL_INFO: {
611 Money this_year = 0;
612 Money last_year = 0;
614
615 for (const Vehicle * const v : this->vehicles) {
616 assert(v->owner == this->owner);
617
618 this_year += v->GetDisplayProfitThisYear();
619 last_year += v->GetDisplayProfitLastYear();
620 occupancy += v->trip_occupancy;
621 }
622
623 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
624
628
633
636 const size_t vehicle_count = this->vehicles.size();
637 if (vehicle_count > 0) {
640 }
641
642 break;
643 }
644
645 case WID_GL_LIST_GROUP: {
646 int y1 = r.top;
647 auto [first, last] = this->group_sb->GetVisibleRangeIterators(this->groups);
648 for (auto it = first; it != last; ++it) {
649 const Group *g = it->group;
650
651 assert(g->owner == this->owner);
652
653 DrawGroupInfo(y1, r.left, r.right, g->index, it->level_mask, it->indent, HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION), g->folded || (std::next(it) != std::end(this->groups) && std::next(it)->indent > it->indent));
654
655 y1 += this->tiny_step_height;
656 }
657 if ((uint)this->group_sb->GetPosition() + this->group_sb->GetCapacity() > this->groups.size()) {
658 DrawGroupInfo(y1, r.left, r.right, NEW_GROUP);
659 }
660 break;
661 }
662
665 break;
666
668 if (this->vli.index != ALL_GROUP && this->grouping == GB_NONE) {
669 /* Mark vehicles which are in sub-groups (only if we are not using shared order coalescing) */
671 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->vehgroups);
672 for (auto it = first; it != last; ++it) {
673 const Vehicle *v = it->GetSingleVehicle();
674 if (v->group_id != this->vli.index) {
676 }
677 mr = mr.Translate(0, this->resize.step_height);
678 }
679 }
680
681 this->DrawVehicleListItems(this->vehicle_sel, this->resize.step_height, r);
682 break;
683 }
684 }
685
686 static void DeleteGroupCallback(Window *win, bool confirmed)
687 {
688 if (confirmed) {
690 w->vli.index = ALL_GROUP;
692 }
693 }
694
695 bool last_overlay_state;
696 void OnMouseLoop() override
697 {
698 if (last_overlay_state != ShowCargoIconOverlay()) {
699 last_overlay_state = ShowCargoIconOverlay();
700 this->SetDirty();
701 }
702 }
703
704 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
705 {
706 switch (widget) {
707 case WID_GL_SORT_BY_ORDER: // Flip sorting method ascending/descending
709 this->SetDirty();
710 break;
711
712 case WID_GL_GROUP_BY_DROPDOWN: // Select grouping option dropdown menu
713 ShowDropDownMenu(this, this->vehicle_group_by_names, this->grouping, WID_GL_GROUP_BY_DROPDOWN, 0, 0);
714 return;
715
716 case WID_GL_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
717 ShowDropDownMenu(this, this->GetVehicleSorterNames(), this->vehgroups.SortType(), WID_GL_SORT_BY_DROPDOWN, 0, (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
718 return;
719
720 case WID_GL_FILTER_BY_CARGO: // Select filtering criteria dropdown menu
721 ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria, widget);
722 break;
723
724 case WID_GL_ALL_VEHICLES: // All vehicles button
725 if (!IsAllGroupID(this->vli.index)) {
726 this->vli.index = ALL_GROUP;
727 this->vehgroups.ForceRebuild();
728 this->SetDirty();
729 }
730 break;
731
732 case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles button
733 if (!IsDefaultGroupID(this->vli.index)) {
734 this->vli.index = DEFAULT_GROUP;
735 this->vehgroups.ForceRebuild();
736 this->SetDirty();
737 }
738 break;
739
740 case WID_GL_LIST_GROUP: { // Matrix Group
741 auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
742 if (it == this->groups.end()) return;
743
744 if (it->group->folded || (std::next(it) != std::end(this->groups) && std::next(it)->indent > it->indent)) {
745 /* The group has children, check if the user clicked the fold / unfold button. */
747 int x = _current_text_dir == TD_RTL ?
748 group_display->pos_x + group_display->current_x - WidgetDimensions::scaled.framerect.right - it->indent * WidgetDimensions::scaled.hsep_indent - this->column_size[VGC_FOLD].width :
750 if (click_count > 1 || (pt.x >= x && pt.x < (int)(x + this->column_size[VGC_FOLD].width))) {
751
752 GroupID g = this->vli.index;
753 if (!IsAllGroupID(g) && !IsDefaultGroupID(g)) {
754 do {
755 g = Group::Get(g)->parent;
756 if (g == it->group->index) {
757 this->vli.index = g;
758 break;
759 }
760 } while (g != INVALID_GROUP);
761 }
762
763 Group::Get(it->group->index)->folded = !it->group->folded;
764 this->groups.ForceRebuild();
765
766 this->SetDirty();
767 break;
768 }
769 }
770
771 this->group_sel = this->vli.index = it->group->index;
772
774
775 this->vehgroups.ForceRebuild();
776 this->SetDirty();
777 break;
778 }
779
780 case WID_GL_LIST_VEHICLE: { // Matrix Vehicle
781 auto it = this->vscroll->GetScrolledItemFromWidget(this->vehgroups, pt.y, this, WID_GL_LIST_VEHICLE);
782 if (it == this->vehgroups.end()) return; // click out of list bound
783
784 const GUIVehicleGroup &vehgroup = *it;
785
786 const Vehicle *v = nullptr;
787
788 switch (this->grouping) {
789 case GB_NONE: {
790 const Vehicle *v2 = vehgroup.GetSingleVehicle();
791 if (VehicleClicked(v2)) break;
792 v = v2;
793 break;
794 }
795
796 case GB_SHARED_ORDERS: {
797 assert(vehgroup.NumVehicles() > 0);
798 v = vehgroup.vehicles_begin[0];
799 /*
800 * No VehicleClicked(v) support for now, because don't want
801 * to enable any contextual actions except perhaps clicking/ctrl-clicking to clone orders.
802 */
803 break;
804 }
805
806 default:
807 NOT_REACHED();
808 }
809 if (v) {
810 if (_ctrl_pressed && this->grouping == GB_SHARED_ORDERS) {
811 ShowOrdersWindow(v);
812 } else {
813 this->vehicle_sel = v->index;
814
815 if (_ctrl_pressed && this->grouping == GB_NONE) {
816 /*
817 * It only makes sense to select a group if not using shared orders
818 * since two vehicles sharing orders can be from different groups.
819 */
820 this->SelectGroup(v->group_id);
821 }
822
825 _cursor.vehchain = true;
826
827 this->SetDirty();
828 }
829 }
830
831 break;
832 }
833
834 case WID_GL_CREATE_GROUP: { // Create a new group
836 break;
837 }
838
839 case WID_GL_DELETE_GROUP: { // Delete the selected group
840 this->group_confirm = this->vli.index;
842 break;
843 }
844
845 case WID_GL_RENAME_GROUP: // Rename the selected roup
846 this->ShowRenameGroupWindow(this->vli.index, false);
847 break;
848
849 case WID_GL_LIVERY_GROUP: // Set group livery
850 ShowCompanyLiveryWindow(this->owner, this->vli.index);
851 break;
852
854 ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
855 break;
856
858 ShowDropDownList(this, this->BuildActionDropdownList(true, Group::IsValidID(this->vli.index), IsDefaultGroupID(this->vli.index)), -1, WID_GL_MANAGE_VEHICLES_DROPDOWN);
859 break;
860 }
861
862 case WID_GL_START_ALL:
863 case WID_GL_STOP_ALL: { // Start/stop all vehicles of the list
865 break;
866 }
867
869 const Group *g = Group::GetIfValid(this->vli.index);
870 if (g != nullptr) {
872 }
873 break;
874 }
875 }
876 }
877
878 void OnDragDrop_Group(Point pt, WidgetID widget)
879 {
880 const Group *g = Group::Get(this->group_sel);
881
882 switch (widget) {
883 case WID_GL_ALL_VEHICLES: // All vehicles
884 case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles
885 if (g->parent != INVALID_GROUP) {
887 }
888
889 this->group_sel = INVALID_GROUP;
890 this->group_over = INVALID_GROUP;
891 this->SetDirty();
892 break;
893
894 case WID_GL_LIST_GROUP: { // Matrix group
895 auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
896 GroupID new_g = it == this->groups.end() ? INVALID_GROUP : it->group->index;
897
898 if (this->group_sel != new_g && g->parent != new_g) {
900 }
901
902 this->group_sel = INVALID_GROUP;
903 this->group_over = INVALID_GROUP;
904 this->SetDirty();
905 break;
906 }
907 }
908 }
909
910 void OnDragDrop_Vehicle(Point pt, WidgetID widget)
911 {
912 switch (widget) {
913 case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles
915
917 this->group_over = INVALID_GROUP;
918
919 this->SetDirty();
920 break;
921
922 case WID_GL_LIST_GROUP: { // Matrix group
923 const VehicleID vindex = this->vehicle_sel;
924 this->vehicle_sel = INVALID_VEHICLE;
925 this->group_over = INVALID_GROUP;
926 this->SetDirty();
927
928 auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
929 GroupID new_g = it == this->groups.end() ? NEW_GROUP : it->group->index;
930
932 break;
933 }
934
935 case WID_GL_LIST_VEHICLE: { // Matrix vehicle
936 const VehicleID vindex = this->vehicle_sel;
937 this->vehicle_sel = INVALID_VEHICLE;
938 this->group_over = INVALID_GROUP;
939 this->SetDirty();
940
941 auto it = this->vscroll->GetScrolledItemFromWidget(this->vehgroups, pt.y, this, WID_GL_LIST_VEHICLE);
942 if (it == this->vehgroups.end()) return; // click out of list bound
943
944 const GUIVehicleGroup &vehgroup = *it;
945 switch (this->grouping) {
946 case GB_NONE: {
947 const Vehicle *v = vehgroup.GetSingleVehicle();
948 if (!VehicleClicked(v) && vindex == v->index) {
950 }
951 break;
952 }
953
954 case GB_SHARED_ORDERS: {
955 if (!VehicleClicked(vehgroup)) {
956 const Vehicle *v = vehgroup.vehicles_begin[0];
957 if (vindex == v->index) {
958 if (vehgroup.NumVehicles() == 1) {
960 } else {
961 ShowVehicleListWindow(v);
962 }
963 }
964 }
965 break;
966 }
967
968 default:
969 NOT_REACHED();
970 }
971 break;
972 }
973 }
974 }
975
976 void OnDragDrop(Point pt, WidgetID widget) override
977 {
978 if (this->vehicle_sel != INVALID_VEHICLE) OnDragDrop_Vehicle(pt, widget);
979 if (this->group_sel != INVALID_GROUP) OnDragDrop_Group(pt, widget);
980
981 _cursor.vehchain = false;
982 }
983
984 void OnQueryTextFinished(std::optional<std::string> str) override
985 {
986 if (str.has_value()) Command<CMD_ALTER_GROUP>::Post(STR_ERROR_GROUP_CAN_T_RENAME, AlterGroupMode::Rename, this->group_rename, 0, *str);
987 this->group_rename = INVALID_GROUP;
988 }
989
990 void OnResize() override
991 {
992 this->group_sb->SetCapacityFromWidget(this, WID_GL_LIST_GROUP);
993 this->vscroll->SetCapacityFromWidget(this, WID_GL_LIST_VEHICLE);
994 }
995
996 void OnDropdownSelect(WidgetID widget, int index) override
997 {
998 switch (widget) {
1000 this->UpdateVehicleGroupBy(static_cast<GroupBy>(index));
1001 break;
1002
1004 this->vehgroups.SetSortType(index);
1005 break;
1006
1007 case WID_GL_FILTER_BY_CARGO: // Select a cargo filter criteria
1008 this->SetCargoFilter(index);
1009 break;
1010
1012 assert(!this->vehicles.empty());
1013
1014 switch (index) {
1015 case ADI_REPLACE: // Replace window
1016 ShowReplaceGroupVehicleWindow(this->vli.index, this->vli.vtype);
1017 break;
1018 case ADI_SERVICE: // Send for servicing
1019 case ADI_DEPOT: { // Send to Depots
1020 Command<CMD_SEND_VEHICLE_TO_DEPOT>::Post(GetCmdSendToDepotMsg(this->vli.vtype), 0, DepotCommand::MassSend | (index == ADI_SERVICE ? DepotCommand::Service : DepotCommand::None), this->vli);
1021 break;
1022 }
1023
1024 case ADI_CREATE_GROUP: // Create group
1026 break;
1027
1028 case ADI_ADD_SHARED: // Add shared Vehicles
1030
1032 break;
1033 case ADI_REMOVE_ALL: // Remove all Vehicles from the selected group
1035
1037 break;
1038 default: NOT_REACHED();
1039 }
1040 break;
1041
1042 default: NOT_REACHED();
1043 }
1044
1045 this->SetDirty();
1046 }
1047
1048 void OnGameTick() override
1049 {
1050 if (this->groups.NeedResort() || this->vehgroups.NeedResort()) {
1051 this->SetDirty();
1052 }
1053 }
1054
1055 void OnPlaceObjectAbort() override
1056 {
1057 /* abort drag & drop */
1058 this->vehicle_sel = INVALID_VEHICLE;
1060 this->group_sel = INVALID_GROUP;
1061 this->group_over = INVALID_GROUP;
1063 }
1064
1065 void OnMouseDrag(Point pt, WidgetID widget) override
1066 {
1067 if (this->vehicle_sel == INVALID_VEHICLE && this->group_sel == INVALID_GROUP) return;
1068
1069 /* A vehicle is dragged over... */
1071 switch (widget) {
1072 case WID_GL_DEFAULT_VEHICLES: // ... the 'default' group.
1074 break;
1075
1076 case WID_GL_LIST_GROUP: { // ... the list of custom groups.
1077 auto it = this->group_sb->GetScrolledItemFromWidget(this->groups, pt.y, this, WID_GL_LIST_GROUP);
1078 new_group_over = it == this->groups.end() ? NEW_GROUP : it->group->index;
1079 break;
1080 }
1081 }
1082
1083 /* Do not highlight when dragging over the current group */
1084 if (this->vehicle_sel != INVALID_VEHICLE) {
1086 } else if (this->group_sel != INVALID_GROUP) {
1087 if (this->group_sel == new_group_over || Group::Get(this->group_sel)->parent == new_group_over) new_group_over = INVALID_GROUP;
1088 }
1089
1090 /* Mark widgets as dirty if the group changed. */
1091 if (new_group_over != this->group_over) {
1093 this->group_over = new_group_over;
1095 }
1096 }
1097
1098 void ShowRenameGroupWindow(GroupID group, bool empty)
1099 {
1100 assert(Group::IsValidID(group));
1101 this->group_rename = group;
1102 /* Show empty query for new groups */
1103 StringID str = STR_EMPTY;
1104 if (!empty) {
1105 SetDParam(0, group);
1106 str = STR_GROUP_NAME;
1107 }
1109 }
1110
1117 {
1118 if (this->vehicle_sel == vehicle) ResetObjectToPlace();
1119 }
1120
1127 {
1128 if (g_id == INVALID_GROUP || g_id == this->vli.index) return;
1129
1130 this->vli.index = g_id;
1131 if (g_id != ALL_GROUP && g_id != DEFAULT_GROUP) {
1132 const Group *g = Group::Get(g_id);
1133
1134 auto found = std::ranges::find(this->groups, g, &GUIGroupListItem::group);
1135 if (found == std::end(this->groups)) {
1136 /* The group's branch is maybe collapsed, so try to expand it. */
1137 for (auto pg = Group::GetIfValid(g->parent); pg != nullptr; pg = Group::GetIfValid(pg->parent)) {
1138 pg->folded = false;
1139 }
1140 this->groups.ForceRebuild();
1141 this->BuildGroupList(this->owner);
1142 this->group_sb->SetCount(this->groups.size());
1143 found = std::ranges::find(this->groups, g, &GUIGroupListItem::group);
1144 }
1145 if (found != std::end(this->groups)) this->group_sb->ScrollTowards(std::distance(std::begin(this->groups), found));
1146 }
1147 this->vehgroups.ForceRebuild();
1148 this->SetDirty();
1149 }
1150
1151};
1152
1153
1154static WindowDesc _other_group_desc(
1155 WDP_AUTO, "list_groups", 460, 246,
1157 0,
1158 _nested_group_widgets
1159);
1160
1161static WindowDesc _train_group_desc(
1162 WDP_AUTO, "list_groups_train", 525, 246,
1164 0,
1165 _nested_group_widgets
1166);
1167
1175void ShowCompanyGroup(CompanyID company, VehicleType vehicle_type, GroupID group, bool need_existing_window)
1176{
1177 if (!Company::IsValidID(company)) return;
1178
1179 const WindowNumber num = VehicleListIdentifier(VL_GROUP_LIST, vehicle_type, company).Pack();
1181 if (vehicle_type == VEH_TRAIN) {
1182 w = AllocateWindowDescFront<VehicleGroupWindow>(_train_group_desc, num, need_existing_window);
1183 } else {
1184 _other_group_desc.cls = GetWindowClassForVehicleType(vehicle_type);
1185 w = AllocateWindowDescFront<VehicleGroupWindow>(_other_group_desc, num, need_existing_window);
1186 }
1187 if (w != nullptr) w->SelectGroup(group);
1188}
1189
1195{
1196 ShowCompanyGroup(v->owner, v->type, v->group_id, true);
1197}
1198
1206{
1207 return dynamic_cast<VehicleGroupWindow *>(FindWindowById(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, owner).Pack()));
1208}
1209
1214static void CcCreateGroup(GroupID gid, VehicleType veh_type)
1215{
1217 if (w != nullptr) w->ShowRenameGroupWindow(gid, true);
1218}
1219
1227void CcCreateGroup(Commands, const CommandCost &result, GroupID new_group, VehicleType vt, GroupID)
1228{
1229 if (result.Failed()) return;
1230
1231 assert(vt <= VEH_AIRCRAFT);
1232 CcCreateGroup(new_group, vt);
1233}
1234
1241{
1242 if (result.Failed()) return;
1243
1244 const Group *g = Group::Get(new_group);
1245 CcCreateGroup(new_group, g->vehicle_type);
1246}
1247
1253{
1254 /* If we haven't got any vehicles on the mouse pointer, we haven't got any highlighted in any group windows either
1255 * If that is the case, we can skip looping though the windows and save time
1256 */
1257 if (_special_mouse_mode != WSM_DRAGDROP) return;
1258
1260 if (w != nullptr) w->UnselectVehicle(v->index);
1261}
void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype)
Show the autoreplace configuration window for a particular group.
Functions related to the autoreplace GUIs.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T AssignBit(T &x, const uint8_t y, bool value)
Assigns a bit in a variable.
Common return value for all commands.
bool Failed() const
Did this command fail?
void RebuildDone()
Notify the sortlist that the rebuild is done.
bool IsDescSortOrder() const
Check if the sort order is descending.
void ToggleSortOrder()
Toggle the sort order Since that is the worst condition for the sort function reverse the list here.
bool NeedRebuild() const
Check if a rebuild is needed.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void ForceResort()
Force a resort next Sort call Reset the resort timer if used too.
uint8_t SortType() const
Get the sorttype of the list.
Listing GetListing() const
Export current sort conditions.
bool NeedResort()
Check if a resort is needed next loop If used the resort timer will decrease every call till 0.
void SetSortType(uint8_t n_type)
Set the sorttype of the list.
Base class for a 'real' widget.
Scrollbar data structure.
void SetCount(size_t num)
Sets the number of elements in the list.
auto GetScrolledItemFromWidget(Tcontainer &container, int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Return an iterator pointing to the element of a scrolled widget that a user clicked in.
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition widget.cpp:2451
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
size_type GetPosition() const
Gets the position of the first visible element in the list.
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
void SetStringParameters(WidgetID widget) const override
Initialize string parameters for a widget.
void UnselectVehicle(VehicleID vehicle)
Tests whether a given vehicle is selected in the window, and unselects it if necessary.
void DirtyHighlightedGroupWidget()
Mark the widget containing the currently highlighted group as dirty.
GroupID group_sel
Selected group (for drag/drop)
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void OnDragDrop(Point pt, WidgetID widget) override
A dragged 'object' has been released.
GroupID group_rename
Group being renamed, INVALID_GROUP if none.
void OnMouseDrag(Point pt, WidgetID widget) override
An 'object' is being dragged at the provided position, highlight the target if possible.
void BuildGroupList(Owner owner)
(Re)Build the group list.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
uint tiny_step_height
Step height for the group list.
void OnResize() override
Called after the window got resized.
void OnGameTick() override
Called once per (game) tick.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnPlaceObjectAbort() override
The user cancelled a tile highlight mode that has been set.
Dimension column_size[VGC_END]
Size of the columns in the group list.
GroupID group_confirm
Group awaiting delete confirmation.
GUIGroupList groups
List of groups.
GroupID group_over
Group over which a vehicle is dragged, INVALID_GROUP if none.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
uint ComputeGroupInfoSize()
Compute tiny_step_height and column_size.
void DrawGroupInfo(int y, int left, int right, GroupID g_id, uint16_t level_mask=0, uint8_t indent=0, bool protection=false, bool has_children=false) const
Draw a row in the group list.
@ VGC_PROFIT
Profit icon.
@ VGC_AUTOREPLACE
Autoreplace active icon.
@ VGC_FOLD
Fold / Unfold button.
@ VGC_NAME
Group name.
@ VGC_PROTECT
Autoreplace protect icon.
@ VGC_NUMBER
Number of vehicles in the group.
void OnQueryTextFinished(std::optional< std::string > str) override
The query window opened from this window has closed.
void OnMouseLoop() override
Called for every mouse loop run, which is at least once per (game) tick.
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
void SelectGroup(const GroupID g_id)
Selects the specified group in the list.
void OnPaint() override
The window must be repainted.
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:42
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:28
int hsep_wide
Wide horizontal spacing.
Definition window_gui.h:64
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:96
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:63
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:40
int hsep_indent
Width of identation for tree layouts.
Definition window_gui.h:65
Functions related to commands.
Commands
List of commands.
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
GUI Functions related to companies.
Owner
Enum for all companies/owners.
Some simple functions to help with accessing containers.
void ShowDropDownMenu(Window *w, std::span< const StringID > strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width)
Show a dropdown menu window near a widget of the parent window.
Definition dropdown.cpp:441
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:404
Dimension GetDropDownListDimension(const DropDownList &list)
Determine width and height required to fully display a DropDownList.
Definition dropdown.cpp:363
Functions related to the drop down widget.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:922
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:851
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:657
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
uint GetStringListWidth(std::span< const StringID > list, FontSize fontsize)
Get maximum width of a list of strings.
Definition gfx.cpp:874
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:114
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:988
Functions related to the gfx engine.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:18
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:345
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:355
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:210
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:209
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:260
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:299
Money GetGroupProfitLastYearMinAge(CompanyID company, GroupID id_g, VehicleType type)
Get last year's profit of vehicles above minimum age for the group with GroupID id_g and its sub-grou...
@ GF_REPLACE_PROTECTION
If set to true, the global autoreplace has no effect on the group.
Definition group.h:66
bool IsAllGroupID(GroupID id_g)
Checks if a GroupID stands for all vehicles of a company.
Definition group.h:100
uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type)
Get the number of vehicles in the group with GroupID id_g and its sub-groups.
uint GetGroupNumVehicleMinAge(CompanyID company, GroupID id_g, VehicleType type)
Get the number of vehicles above profit minimum age in the group with GroupID id_g and its sub-groups...
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetMatrixDataTip(uint8_t cols, uint8_t rows, StringID tip)
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FS_NORMAL)
Widget part function for setting the minimal text lines.
constexpr NWidgetPart SetAspect(float ratio, AspectFlags flags=AspectFlags::ResizeX)
Widget part function for setting the aspect ratio.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Command definitions related to engine groups.
@ Rename
Change group name.
@ SetParent
Change group parent.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:940
void ShowCompanyGroup(CompanyID company, VehicleType vehicle_type, GroupID group, bool need_existing_window)
Show the group window for the given company and vehicle type.
void DeleteGroupHighlightOfVehicle(const Vehicle *v)
Removes the highlight of a vehicle in a group window.
void CcAddVehicleNewGroup(Commands, const CommandCost &result, GroupID new_group, GroupID, VehicleID, bool, const VehicleListIdentifier &)
Open rename window after adding a vehicle to a new group via drag and drop.
static VehicleGroupWindow * FindVehicleGroupWindow(VehicleType vt, Owner owner)
Finds a group list window determined by vehicle type and owner.
static void GuiGroupListAddChildren(GUIGroupList &dst, const GUIGroupList &src, bool fold, GroupID parent=INVALID_GROUP, uint8_t indent=0)
Add children to GUI group list to build a hierarchical tree.
void ShowCompanyGroupForVehicle(const Vehicle *v)
Show the group window for the given vehicle.
static void CcCreateGroup(GroupID gid, VehicleType veh_type)
Opens a 'Rename group' window for newly created group.
void BuildGuiGroupList(GUIGroupList &dst, bool fold, Owner owner, VehicleType veh_type)
Build GUI group list, a sorted hierarchical list of groups for owner and vehicle type.
Functions/definitions that have something to do with groups.
uint16_t GroupID
Type for all group identifiers.
Definition group_type.h:13
static const GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition group_type.h:17
static const GroupID NEW_GROUP
Sentinel for a to-be-created group.
Definition group_type.h:15
static const uint MAX_LENGTH_GROUP_NAME_CHARS
The maximum length of a group name in characters including '\0'.
Definition group_type.h:20
static const GroupID INVALID_GROUP
Sentinel for invalid groups.
Definition group_type.h:18
static const GroupID ALL_GROUP
All vehicles are in this group.
Definition group_type.h:16
Types related to the group widgets.
@ WID_GL_LIST_GROUP
List of the groups.
@ WID_GL_STOP_ALL
Stop all button.
@ WID_GL_REPLACE_PROTECTION
Replace protection button.
@ WID_GL_MANAGE_VEHICLES_DROPDOWN
Manage vehicles dropdown list.
@ WID_GL_GROUP_BY_ORDER
Group order.
@ WID_GL_SORT_BY_ORDER
Sort order.
@ WID_GL_GROUP_BY_DROPDOWN
Group by dropdown list.
@ WID_GL_CAPTION
Caption of the window.
@ WID_GL_START_ALL
Start all button.
@ WID_GL_ALL_VEHICLES
All vehicles entry.
@ WID_GL_DELETE_GROUP
Delete group button.
@ WID_GL_AVAILABLE_VEHICLES
Available vehicles.
@ WID_GL_RENAME_GROUP
Rename group button.
@ WID_GL_LIST_VEHICLE_SCROLLBAR
Scrollbar for the list.
@ WID_GL_LIVERY_GROUP
Group livery button.
@ WID_GL_SORT_BY_DROPDOWN
Sort by dropdown list.
@ WID_GL_LIST_VEHICLE
List of the vehicles.
@ WID_GL_FILTER_BY_CARGO
Filter vehicles by cargo type.
@ WID_GL_CREATE_GROUP
Create group button.
@ WID_GL_LIST_GROUP_SCROLLBAR
Scrollbar for the list.
@ WID_GL_INFO
Group info.
@ WID_GL_DEFAULT_VEHICLES
Default vehicles entry.
GUI functions that shouldn't be here.
void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallbackProc *callback, bool focus)
Show a confirmation window with standard 'yes' and 'no' buttons The window is aligned to the centre o...
void ShowQueryString(StringID str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:314
A number of safeguards to prevent using unsafe methods.
This file contains all sprite-related enums and defines.
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1605
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1390
Definition of base types and functions in a cross-platform compatible way.
int StrNaturalCompare(std::string_view s1, std::string_view s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition string.cpp:589
Functions related to low-level strings.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition string_type.h:25
void SetDParamMaxValue(size_t n, uint64_t max_value, uint min_count, FontSize size)
Set DParam n to some number that is suitable for string size computations.
Definition strings.cpp:127
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition strings.cpp:333
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
Functions related to OTTD's strings.
@ TD_RTL
Text is written right-to-left by default.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
CargoID cargo_filter_criteria
Selected cargo filter index.
VehicleListIdentifier vli
Identifier of the vehicle list we want to currently show.
void DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const
Draw all the vehicle list items.
VehicleID vehicle_sel
Selected vehicle.
Listing * sorting
Pointer to the vehicle type related sorting.
void SetCargoFilter(uint8_t index)
Set cargo filter for the vehicle group list.
GroupBy grouping
How we want to group the list.
Dimension GetActionDropdownSize(bool show_autoreplace, bool show_group, bool show_create)
Compute the size for the Action dropdown.
DropDownList BuildActionDropdownList(bool show_autoreplace, bool show_group, bool show_create)
Display the Action dropdown window.
VehicleList vehicles
List of vehicles. This is the buffer for vehgroups to point into; if this is structurally modified,...
GUIVehicleGroupList vehgroups
List of (groups of) vehicles. This stores iterators of vehicles, and should be rebuilt if vehicles is...
DropDownList BuildCargoDropDownList(bool full) const
Build drop down list for cargo filter selection.
VehicleType type
Type of vehicle.
bool vehchain
vehicle chain is dragged
Definition gfx_type.h:150
Dimensions (a width and height) of a rectangle in 2D.
Statistics and caches on the vehicles in a group.
Definition group.h:24
uint16_t num_vehicle
Number of vehicles.
Definition group.h:28
static GroupStatistics & Get(CompanyID company, GroupID id_g, VehicleType type)
Returns the GroupStatistics for a specific group.
Definition group_cmd.cpp:63
Group data.
Definition group.h:72
GroupID parent
Parent group.
Definition group.h:83
VehicleType vehicle_type
Vehicle type of the group.
Definition group.h:75
bool folded
NOSAVE: Is this group folded in the group view?
Definition group.h:81
uint16_t number
Per-company group number.
Definition group.h:84
Owner owner
Group Owner.
Definition group.h:74
uint8_t flags
Group flags.
Definition group.h:77
Partial widget specification to allow NWidgets to be written nested.
Coordinates of a point in 2D.
Tindex index
Index of this pool item.
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
static Titem * Get(size_t index)
Returns Titem with given index.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
uint step_height
Step-size of height resize changes.
Definition window_gui.h:214
The information about a vehicle list.
Definition vehiclelist.h:28
CompanyID company
The company associated with this list.
Definition vehiclelist.h:31
uint32_t Pack() const
Pack a VehicleListIdentifier in a single uint32.
VehicleType vtype
The vehicle type associated with this list.
Definition vehiclelist.h:30
uint32_t index
A vehicle list type specific index.
Definition vehiclelist.h:32
Vehicle data structure.
GroupID group_id
Index of group Pool array.
Owner owner
Which company owns the vehicle?
High level window description.
Definition window_gui.h:159
WindowClass cls
Class of the window,.
Definition window_gui.h:170
Data structure for an opened window.
Definition window_gui.h:273
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:781
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1733
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:732
Window * parent
Parent window.
Definition window_gui.h:328
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:475
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:551
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:764
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1035
ResizeInfo resize
Resize information.
Definition window_gui.h:314
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:521
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1723
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:316
int left
x position of left edge of the window
Definition window_gui.h:309
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:977
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:314
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:302
Stuff related to the text buffer GUI.
@ QSF_ENABLE_DEFAULT
enable the 'Default' button ("\0" is returned)
Definition textbuf_gui.h:21
@ QSF_LEN_IN_CHARS
the length of the string is counted in characters
Definition textbuf_gui.h:22
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
void SetObjectToPlaceWnd(CursorID icon, PaletteID pal, HighLightStyle mode, Window *w)
Change the cursor and mouse click/drag handling to a mode for performing special operations like tile...
@ HT_DRAG
dragging items in the depot windows
Base class for all vehicles.
Command definitions for vehicles.
Functions related to vehicles.
static const Money VEHICLE_PROFIT_THRESHOLD
Threshold for a vehicle to be considered making good profit.
void ShowVehicleViewWindow(const Vehicle *v)
Shows the vehicle view window of the given vehicle.
bool VehicleClicked(const Vehicle *v)
Dispatch a "vehicle selected" event if any window waits for it.
uint GetVehicleListHeight(VehicleType type, uint divisor)
Get the height of a vehicle in the vehicle list GUIs.
void SetMouseCursorVehicle(const Vehicle *v, EngineImageType image_type)
Set the mouse cursor to look like a vehicle.
bool ShowCargoIconOverlay()
Test if cargo icon overlays should be drawn.
Functions related to the vehicle's GUIs.
WindowClass GetWindowClassForVehicleType(VehicleType vt)
Get WindowClass for vehicle list of given vehicle type.
Definition vehicle_gui.h:97
Functions/classes shared between the different vehicle list GUIs.
@ EIT_IN_LIST
Vehicle drawn in vehicle list, group list, ...
VehicleType
Available vehicle types.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_TRAIN
Train vehicle type.
uint32_t VehicleID
The type all our vehicle IDs have.
static const VehicleID INVALID_VEHICLE
Constant representing a non-existing vehicle.
@ None
No special flags.
@ MassSend
Tells that it's a mass send to depot command (type in VLW flag)
@ Service
The vehicle will leave the depot right after arrival (service only)
@ NC_EQUALSIZE
Value of the NCB_EQUALSIZE flag.
@ NC_BIGFIRST
Value of the NCB_BIGFIRST flag.
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:75
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:55
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:50
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:66
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:59
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:61
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:85
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:69
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:68
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:65
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:70
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1152
SpecialMouseMode _special_mouse_mode
Mode of the mouse.
Definition window.cpp:93
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1098
Window functions not directly related to making/drawing windows.
@ WSM_DRAGDROP
Drag&drop an object.
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:220
@ SBS_UP
Sort descending.
Definition window_gui.h:221
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:147
int WidgetID
Widget ID.
Definition window_type.h:18
int32_t WindowNumber
Number to differentiate different windows of the same class.
@ WC_INVALID
Invalid window.
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:45
@ WC_TRAINS_LIST
Trains list; Window numbers:
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
@ WC_QUERY_STRING
Query string window; Window numbers: