OpenTTD Source 20260218-master-g2123fca5ea
network_content_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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "../stdafx.h"
11#include "../strings_func.h"
12#include "../gfx_func.h"
13#include "../window_func.h"
14#include "../error.h"
15#include "../ai/ai.hpp"
16#include "../game/game.hpp"
17#include "../base_media_base.h"
19#include "../base_media_music.h"
21#include "../openttd.h"
22#include "../sortlist_type.h"
24#include "../querystring_gui.h"
26#include "../textfile_gui.h"
27#include "../fios.h"
28#include "network_content_gui.h"
29
30
31#include "table/strings.h"
32#include "../table/sprites.h"
33
34#include "../safeguards.h"
35
36
38static bool _accepted_external_search = false;
39
40
42struct ContentTextfileWindow : public TextfileWindow {
43 const ContentInfo *ci = nullptr;
44
45 ContentTextfileWindow(Window *parent, TextfileType file_type, const ContentInfo *ci) : TextfileWindow(parent, file_type), ci(ci)
46 {
47 this->ConstructWindow();
48
49 auto textfile = this->ci->GetTextfile(file_type);
50 this->LoadTextfile(textfile.value(), GetContentInfoSubDir(this->ci->type));
51 }
52
53 StringID GetTypeString() const
54 {
55 switch (this->ci->type) {
56 case CONTENT_TYPE_NEWGRF: return STR_CONTENT_TYPE_NEWGRF;
57 case CONTENT_TYPE_BASE_GRAPHICS: return STR_CONTENT_TYPE_BASE_GRAPHICS;
58 case CONTENT_TYPE_BASE_SOUNDS: return STR_CONTENT_TYPE_BASE_SOUNDS;
59 case CONTENT_TYPE_BASE_MUSIC: return STR_CONTENT_TYPE_BASE_MUSIC;
60 case CONTENT_TYPE_AI: return STR_CONTENT_TYPE_AI;
61 case CONTENT_TYPE_AI_LIBRARY: return STR_CONTENT_TYPE_AI_LIBRARY;
62 case CONTENT_TYPE_GAME: return STR_CONTENT_TYPE_GAME_SCRIPT;
63 case CONTENT_TYPE_GAME_LIBRARY: return STR_CONTENT_TYPE_GS_LIBRARY;
64 case CONTENT_TYPE_SCENARIO: return STR_CONTENT_TYPE_SCENARIO;
65 case CONTENT_TYPE_HEIGHTMAP: return STR_CONTENT_TYPE_HEIGHTMAP;
66 default: NOT_REACHED();
67 }
68 }
69
70 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
71 {
72 if (widget == WID_TF_CAPTION) {
73 return GetString(stringid, this->GetTypeString(), this->ci->name);
74 }
75
76 return this->Window::GetWidgetString(widget, stringid);
77 }
78};
79
80static void ShowContentTextfileWindow(Window *parent, TextfileType file_type, const ContentInfo *ci)
81{
82 parent->CloseChildWindowById(WC_TEXTFILE, file_type);
83 new ContentTextfileWindow(parent, file_type, ci);
84}
85
87static constexpr std::initializer_list<NWidgetPart> _nested_network_content_download_status_window_widgets = {
88 NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_CONTENT_DOWNLOAD_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
89 NWidget(WWT_PANEL, COLOUR_GREY),
91 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NCDS_PROGRESS_BAR), SetFill(1, 0),
92 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NCDS_PROGRESS_TEXT), SetFill(1, 0), SetMinimalSize(350, 0),
93 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCDS_CANCELOK), SetStringTip(STR_BUTTON_CANCEL), SetFill(1, 0),
96};
97
100 WDP_CENTER, {}, 0, 0,
104);
105
113
115{
116 _network_content_client.RemoveCallback(this);
117 this->Window::Close();
118}
119
120void BaseNetworkContentDownloadStatusWindow::UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize)
121{
122 switch (widget) {
124 auto max_value = GetParamMaxDigits(8);
125 size = GetStringBoundingBox(GetString(STR_CONTENT_DOWNLOAD_PROGRESS_SIZE, max_value, max_value, max_value));
126 /* We need some spacing for the 'border' */
127 size.height += WidgetDimensions::scaled.frametext.Horizontal();
128 size.width += WidgetDimensions::scaled.frametext.Vertical();
129 break;
130 }
131
133 size.height = GetCharacterHeight(FS_NORMAL) * 2 + WidgetDimensions::scaled.vsep_normal;
134 break;
135 }
136}
137
139{
140 switch (widget) {
142 /* Draw the % complete with a bar and a text */
145 DrawFrameRect(ir.WithWidth((uint64_t)ir.Width() * this->downloaded_bytes / this->total_bytes, _current_text_dir == TD_RTL), COLOUR_MAUVE, {});
146 DrawString(ir.left, ir.right, CentreBounds(ir.top, ir.bottom, GetCharacterHeight(FS_NORMAL)),
147 GetString(STR_CONTENT_DOWNLOAD_PROGRESS_SIZE, this->downloaded_bytes, this->total_bytes, this->downloaded_bytes * 100LL / this->total_bytes),
148 TC_FROMSTRING, SA_HOR_CENTER);
149 break;
150 }
151
153 if (this->downloaded_bytes == this->total_bytes) {
154 DrawStringMultiLine(r, STR_CONTENT_DOWNLOAD_COMPLETE, TC_FROMSTRING, SA_CENTER);
155 } else if (!this->name.empty()) {
157 GetString(STR_CONTENT_DOWNLOAD_FILE, this->name, this->downloaded_files, this->total_files),
158 TC_FROMSTRING, SA_CENTER);
159 } else {
160 DrawStringMultiLine(r, STR_CONTENT_DOWNLOAD_INITIALISE, TC_FROMSTRING, SA_CENTER);
161 }
162 break;
163 }
164}
165
167{
168 if (ci.id != this->cur_id) {
169 this->name = ci.filename;
170 this->cur_id = ci.id;
171 this->downloaded_files++;
172 }
173
174 /* A negative value means we are resetting; for example, when retrying or using a fallback. */
175 if (bytes < 0) {
176 this->downloaded_bytes = 0;
177 } else {
178 this->downloaded_bytes += bytes;
179 }
180
181 this->SetDirty();
182}
183
184
187private:
188 ContentTypes received_types{};
189
190public:
199
200 void Close([[maybe_unused]] int data = 0) override
201 {
202 TarScanner::Modes modes{};
203 for (auto ctype : this->received_types) {
204 switch (ctype) {
205 case CONTENT_TYPE_AI:
207 /* AI::Rescan calls the scanner. */
208 break;
211 /* Game::Rescan calls the scanner. */
212 break;
213
218 break;
219
221 /* ScanNewGRFFiles calls the scanner. */
222 break;
223
227 break;
228
229 default:
230 break;
231 }
232 }
233
234 TarScanner::DoScan(modes);
235
236 /* Tell all the backends about what we've downloaded */
237 for (auto ctype : this->received_types) {
238 switch (ctype) {
239 case CONTENT_TYPE_AI:
241 AI::Rescan();
242 break;
243
246 Game::Rescan();
247 break;
248
252 break;
253
257 break;
258
262 break;
263
266 break;
267
272 break;
273
274 default:
275 break;
276 }
277 }
278
279 /* Always invalidate the download window; tell it we are going to be gone */
281
283 }
284
285 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
286 {
287 if (widget == WID_NCDS_CANCELOK) {
288 if (this->downloaded_bytes != this->total_bytes) {
290 this->Close();
291 } else {
292 /* If downloading succeeded, close the online content window. This will close
293 * the current window as well. */
295 }
296 }
297 }
298
299 void OnDownloadProgress(const ContentInfo &ci, int bytes) override
300 {
302 this->received_types.Set(ci.type);
303
304 /* When downloading is finished change cancel in ok */
305 if (this->downloaded_bytes == this->total_bytes) {
306 this->GetWidget<NWidgetCore>(WID_NCDS_CANCELOK)->SetString(STR_BUTTON_OK);
307 }
308 }
309};
310
316
322
327
328 static const uint EDITBOX_MAX_SIZE = 50;
329
330 static Listing last_sorting;
332 static const std::initializer_list<GUIContentList::SortFunction * const> sorter_funcs;
333 static const std::initializer_list<GUIContentList::FilterFunction * const> filter_funcs;
335 bool auto_select = false;
339
340 const ContentInfo *selected = nullptr;
341 int list_pos = 0;
342 uint filesize_sum = 0;
343 Scrollbar *vscroll = nullptr;
344
346
349 {
350 std::string url;
351 url.reserve(1024);
352
353 url += "https://grfsearch.openttd.org/?";
354
355 if (this->auto_select) {
356 url += "do=searchgrfid&q=";
357
358 bool first = true;
359 for (const ContentInfo *ci : this->content) {
360 if (ci->state != ContentInfo::State::DoesNotExist) continue;
361
362 if (!first) url.push_back(',');
363 first = false;
364
365 format_append(url, "{:08X}:{}", ci->unique_id, FormatArrayAsHex(ci->md5sum));
366 }
367 } else {
368 url += "do=searchtext&q=";
369
370 /* Escape search term */
371 for (char search : this->filter_editbox.text.GetText()) {
372 /* Remove quotes */
373 if (search == '\'' || search == '"') continue;
374
375 /* Escape special chars, such as &%,= */
376 if (static_cast<unsigned char>(search) < 0x30) {
377 format_append(url, "%{:02X}", search);
378 } else {
379 url.push_back(search);
380 }
381 }
382 }
383
384 OpenBrowser(url);
385 }
386
392 static void ExternalSearchDisclaimerCallback(Window *w, bool accepted)
393 {
394 if (accepted) {
396 ((NetworkContentListWindow*)w)->OpenExternalSearch();
397 }
398 }
399
405 {
406 if (!this->content.NeedRebuild()) return;
407
408 /* Create temporary array of games to use for listing */
409 this->content.clear();
410
411 bool all_available = true;
412
413 for (const ContentInfo &ci : _network_content_client.Info()) {
414 if (ci.state == ContentInfo::State::DoesNotExist) all_available = false;
415 this->content.push_back(&ci);
416 }
417
418 this->SetWidgetDisabledState(WID_NCL_SEARCH_EXTERNAL, this->auto_select && all_available);
419
420 this->FilterContentList();
421 this->content.RebuildDone();
422 this->SortContentList();
423
424 this->vscroll->SetCount(this->content.size()); // Update the scrollbar
425 this->ScrollToSelected();
426 }
427
429 static bool NameSorter(const ContentInfo * const &a, const ContentInfo * const &b)
430 {
431 int r = StrNaturalCompare(a->name, b->name, true); // Sort by name (natural sorting).
432 if (r == 0) r = StrNaturalCompare(a->version, b->version, true);
433 return r < 0;
434 }
435
437 static bool TypeSorter(const ContentInfo * const &a, const ContentInfo * const &b)
438 {
439 int r = 0;
440 if (a->type != b->type) {
442 }
443 if (r == 0) return NameSorter(a, b);
444 return r < 0;
445 }
446
448 static bool StateSorter(const ContentInfo * const &a, const ContentInfo * const &b)
449 {
450 int r = to_underlying(a->state) - to_underlying(b->state);
451 if (r == 0) return TypeSorter(a, b);
452 return r < 0;
453 }
454
457 {
458 if (!this->content.Sort()) return;
459
460 int idx = find_index(this->content, this->selected);
461 if (idx >= 0) this->list_pos = idx;
462 }
463
465 static bool TagNameFilter(const ContentInfo * const *a, ContentListFilterData &filter)
466 {
467 if ((*a)->state == ContentInfo::State::Selected || (*a)->state == ContentInfo::State::Autoselected) return true;
468
469 filter.string_filter.ResetState();
470 for (auto &tag : (*a)->tags) filter.string_filter.AddLine(tag);
471
472 filter.string_filter.AddLine((*a)->name);
473 return filter.string_filter.GetState();
474 }
475
477 static bool TypeOrSelectedFilter(const ContentInfo * const *a, ContentListFilterData &filter)
478 {
479 if (filter.types.None()) return true;
480 if (filter.types.Test((*a)->type)) return true;
481 return ((*a)->state == ContentInfo::State::Selected || (*a)->state == ContentInfo::State::Autoselected);
482 }
483
486 {
487 /* Apply filters. */
488 bool changed = false;
489 if (!this->filter_data.string_filter.IsEmpty()) {
490 this->content.SetFilterType(CONTENT_FILTER_TEXT);
491 changed |= this->content.Filter(this->filter_data);
492 }
493 if (this->filter_data.types.Any()) {
495 changed |= this->content.Filter(this->filter_data);
496 }
497 if (!changed) return;
498
499 /* update list position */
500 int idx = find_index(this->content, this->selected);
501 if (idx >= 0) {
502 this->list_pos = idx;
503 return;
504 }
505
506 /* previously selected item not in list anymore */
507 this->selected = nullptr;
508 this->list_pos = 0;
509 }
510
516 {
517 Filtering old_params = this->content.GetFiltering();
518 bool new_state = !this->filter_data.string_filter.IsEmpty() || this->filter_data.types.Any();
519 if (new_state != old_params.state) {
520 this->content.SetFilterState(new_state);
521 }
522 return new_state != old_params.state;
523 }
524
527 {
528 if (this->selected == nullptr) return;
529
530 this->vscroll->ScrollTowards(this->list_pos);
531 }
532
534public:
544 NetworkContentListWindow(WindowDesc &desc, bool select_all, ContentTypes types) :
545 Window(desc),
546 auto_select(select_all),
548 {
549 this->CreateNestedTree();
550 this->vscroll = this->GetScrollbar(WID_NCL_SCROLLBAR);
552
553 this->GetWidget<NWidgetStacked>(WID_NCL_SEL_ALL_UPDATE)->SetDisplayedPlane(select_all);
554
555 this->querystrings[WID_NCL_FILTER] = &this->filter_editbox;
556 this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
558 this->SetWidgetDisabledState(WID_NCL_SEARCH_EXTERNAL, this->auto_select);
559 this->filter_data.types = types;
560
561 _network_content_client.AddCallback(this);
562 this->content.SetListing(this->last_sorting);
563 this->content.SetFiltering(this->last_filtering);
564 this->content.SetSortFuncs(this->sorter_funcs);
565 this->content.SetFilterFuncs(this->filter_funcs);
566 this->UpdateFilterState();
567 this->content.ForceRebuild();
568 this->FilterContentList();
569 this->SortContentList();
570 this->InvalidateData();
571 }
572
573 void Close([[maybe_unused]] int data = 0) override
574 {
575 _network_content_client.RemoveCallback(this);
576 this->Window::Close();
577 }
578
579 void OnInit() override
580 {
581 this->checkbox_size = maxdim(maxdim(GetScaledSpriteSize(SPR_BOX_EMPTY), GetScaledSpriteSize(SPR_BOX_CHECKED)), GetScaledSpriteSize(SPR_BLOT));
582 }
583
584 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
585 {
586 switch (widget) {
587 case WID_NCL_CHECKBOX:
588 size.width = std::max<uint>(this->checkbox_size.width, Window::SortButtonWidth()) + padding.width;
589 break;
590
591 case WID_NCL_TYPE: {
592 /* Width must be enough for header label and sort buttons.*/
593 size.width += Window::SortButtonWidth() * 2;
594 /* And also enough for the width of each type of content. */
595 Dimension d = size;
596 for (int i = CONTENT_TYPE_BEGIN; i < CONTENT_TYPE_END; i++) {
597 d = maxdim(d, GetStringBoundingBox(STR_CONTENT_TYPE_BASE_GRAPHICS + i - CONTENT_TYPE_BASE_GRAPHICS));
598 }
599 size.width = std::max(size.width, d.width + padding.width);
600 break;
601 }
602
603 case WID_NCL_NAME:
604 size.width += Window::SortButtonWidth() * 2;
605 break;
606
607 case WID_NCL_MATRIX:
608 fill.height = resize.height = std::max<uint>(std::max<uint>(this->checkbox_size.height, GetCharacterHeight(FS_NORMAL)), GetCharacterHeight(FS_SMALL)) + padding.height;
609 size.height = 10 * resize.height;
610 break;
611 }
612 }
613
614
615 void DrawWidget(const Rect &r, WidgetID widget) const override
616 {
617 switch (widget) {
618 case WID_NCL_DETAILS:
619 this->DrawDetails(r);
620 break;
621
622 case WID_NCL_MATRIX:
623 this->DrawMatrix(r);
624 break;
625 }
626 }
627
628 void OnPaint() override
629 {
630 const SortButtonState arrow = this->content.IsDescSortOrder() ? SBS_DOWN : SBS_UP;
631
632 if (this->content.NeedRebuild()) {
633 this->BuildContentList();
634 }
635
636 this->DrawWidgets();
637
638 switch (this->content.SortType()) {
642 }
643 }
644
649 void DrawMatrix(const Rect &r) const
650 {
651 bool rtl = _current_text_dir == TD_RTL;
652 const Rect checkbox = this->GetWidget<NWidgetBase>(WID_NCL_CHECKBOX)->GetCurrentRect();
653 const Rect name = this->GetWidget<NWidgetBase>(WID_NCL_NAME)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
654 const Rect type = this->GetWidget<NWidgetBase>(WID_NCL_TYPE)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
655
656 /* Fill the matrix with the information */
657 const uint step_height = this->GetWidget<NWidgetBase>(WID_NCL_MATRIX)->resize_y;
658 const int text_y_offset = WidgetDimensions::scaled.matrix.top + (step_height - WidgetDimensions::scaled.matrix.Vertical() - GetCharacterHeight(FS_NORMAL)) / 2;
659 const int version_y_offset = WidgetDimensions::scaled.matrix.top + (step_height - WidgetDimensions::scaled.matrix.Vertical() - GetCharacterHeight(FS_SMALL)) / 2;
660
661 Rect mr = r.WithHeight(step_height);
662 auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->content);
663 for (auto iter = first; iter != last; iter++) {
664 const ContentInfo *ci = *iter;
665
666 if (ci == this->selected) GfxFillRect(mr.Shrink(WidgetDimensions::scaled.bevel), PC_GREY);
667
668 SpriteID sprite;
669 SpriteID pal = PAL_NONE;
670 switch (ci->state) {
671 case ContentInfo::State::Unselected: sprite = SPR_BOX_EMPTY; break;
672 case ContentInfo::State::Selected: sprite = SPR_BOX_CHECKED; break;
673 case ContentInfo::State::Autoselected: sprite = SPR_BOX_CHECKED; break;
674 case ContentInfo::State::AlreadyHere: sprite = SPR_BLOT; pal = PALETTE_TO_GREEN; break;
675 case ContentInfo::State::DoesNotExist: sprite = SPR_BLOT; pal = PALETTE_TO_RED; break;
676 default: NOT_REACHED();
677 }
678 DrawSpriteIgnorePadding(sprite, pal, checkbox.WithY(mr), SA_CENTER);
679
680 StringID str = STR_CONTENT_TYPE_BASE_GRAPHICS + ci->type - CONTENT_TYPE_BASE_GRAPHICS;
681 DrawString(type.left, type.right, mr.top + text_y_offset, str, TC_BLACK, SA_HOR_CENTER);
682
683 int x = DrawString(name.left, name.right, mr.top + version_y_offset, ci->version, TC_BLACK, SA_RIGHT, false, FS_SMALL);
684 x += rtl ? WidgetDimensions::scaled.hsep_wide : -WidgetDimensions::scaled.hsep_wide;
685
686 DrawString(rtl ? x : name.left, rtl ? name.right : x, mr.top + text_y_offset, ci->name, TC_BLACK);
687 mr = mr.Translate(0, step_height);
688 }
689 }
690
695 void DrawDetails(const Rect &r) const
696 {
697 /* Height for the title banner */
698 int HEADER_HEIGHT = 3 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.frametext.Vertical();
699
700 Rect hr = r.WithHeight(HEADER_HEIGHT).Shrink(WidgetDimensions::scaled.frametext);
701 Rect tr = r.Shrink(WidgetDimensions::scaled.frametext);
702 tr.top += HEADER_HEIGHT;
703
704 /* Create the nice darker rectangle at the details top */
705 GfxFillRect(r.WithHeight(HEADER_HEIGHT).Shrink(WidgetDimensions::scaled.bevel.left, WidgetDimensions::scaled.bevel.top, WidgetDimensions::scaled.bevel.right, 0), GetColourGradient(COLOUR_LIGHT_BLUE, SHADE_NORMAL));
706 DrawString(hr.left, hr.right, hr.top, STR_CONTENT_DETAIL_TITLE, TC_FROMSTRING, SA_HOR_CENTER);
707
708 /* Draw the total download size */
709 DrawString(tr.left, tr.right, tr.bottom - GetCharacterHeight(FS_NORMAL) + 1, GetString(STR_CONTENT_TOTAL_DOWNLOAD_SIZE, this->filesize_sum));
710
711 if (this->selected == nullptr) return;
712
713 /* And fill the rest of the details when there's information to place there */
714 DrawStringMultiLine(hr.left, hr.right, hr.top + GetCharacterHeight(FS_NORMAL), hr.bottom, STR_CONTENT_DETAIL_SUBTITLE_UNSELECTED + to_underlying(this->selected->state), TC_FROMSTRING, SA_CENTER);
715
716 /* Also show the total download size, so keep some space from the bottom */
717 tr.bottom -= GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_wide;
718
719 if (this->selected->upgrade) {
720 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_UPDATE, STR_CONTENT_TYPE_BASE_GRAPHICS + this->selected->type - CONTENT_TYPE_BASE_GRAPHICS));
721 tr.top += WidgetDimensions::scaled.vsep_wide;
722 }
723
724 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_NAME, this->selected->name));
725
726 if (!this->selected->version.empty()) {
727 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_VERSION, this->selected->version));
728 }
729
730 if (!this->selected->description.empty()) {
731 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_DESCRIPTION, this->selected->description));
732 }
733
734 if (!this->selected->url.empty()) {
735 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_URL, this->selected->url));
736 }
737
738 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_TYPE, STR_CONTENT_TYPE_BASE_GRAPHICS + this->selected->type - CONTENT_TYPE_BASE_GRAPHICS));
739
740 tr.top += WidgetDimensions::scaled.vsep_wide;
741 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_FILESIZE, this->selected->filesize));
742
743 std::string_view list_separator = GetListSeparator();
744 if (!this->selected->dependencies.empty()) {
745 /* List dependencies */
746 std::string buf;
747 for (auto &cid : this->selected->dependencies) {
748 /* Try to find the dependency */
749 for (const ContentInfo &ci : _network_content_client.Info()) {
750 if (ci.id != cid) continue;
751
752 if (!buf.empty()) buf += list_separator;
753 buf += ci.name;
754 break;
755 }
756 }
757 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_DEPENDENCIES, std::move(buf)));
758 }
759
760 if (!this->selected->tags.empty()) {
761 /* List all tags */
762 std::string buf;
763 for (auto &tag : this->selected->tags) {
764 if (!buf.empty()) buf += list_separator;
765 buf += tag;
766 }
767 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_TAGS, std::move(buf)));
768 }
769
770 if (this->selected->IsSelected()) {
771 /* When selected show all manually selected content that depends on this */
773 _network_content_client.ReverseLookupTreeDependency(tree, this->selected);
774
775 std::string buf;
776 for (const ContentInfo *ci : tree) {
777 if (ci == this->selected || ci->state != ContentInfo::State::Selected) continue;
778
779 if (!buf.empty()) buf += list_separator;
780 buf += ci->name;
781 }
782 if (!buf.empty()) {
783 tr.top = DrawStringMultiLine(tr, GetString(STR_CONTENT_DETAIL_SELECTED_BECAUSE_OF, std::move(buf)));
784 }
785 }
786 }
787
788 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
789 {
790 if (widget >= WID_NCL_TEXTFILE && widget < WID_NCL_TEXTFILE + TFT_CONTENT_END) {
791 if (this->selected == nullptr || this->selected->state != ContentInfo::State::AlreadyHere) return;
792
793 ShowContentTextfileWindow(this, (TextfileType)(widget - WID_NCL_TEXTFILE), this->selected);
794 return;
795 }
796
797 switch (widget) {
798 case WID_NCL_MATRIX: {
799 auto it = this->vscroll->GetScrolledItemFromWidget(this->content, pt.y, this, WID_NCL_MATRIX);
800 if (it == this->content.end()) return; // click out of bounds
801
802 const NWidgetBase *checkbox = this->GetWidget<NWidgetBase>(WID_NCL_CHECKBOX);
803 if (click_count > 1 || IsInsideBS(pt.x, checkbox->pos_x, checkbox->current_x)) {
804 _network_content_client.ToggleSelectedState(**it);
805 this->content.ForceResort();
806 this->content.ForceRebuild();
807 } else {
808 this->selected = *it;
809 this->list_pos = it - this->content.begin();
810 }
811
812 if (this->filter_data.types.Any()) {
813 this->content.ForceRebuild();
814 }
815
816 this->InvalidateData();
817 break;
818 }
819
820 case WID_NCL_CHECKBOX:
821 case WID_NCL_TYPE:
822 case WID_NCL_NAME:
823 if (this->content.SortType() == widget - WID_NCL_CHECKBOX) {
824 this->content.ToggleSortOrder();
825 if (!this->content.empty()) this->list_pos = (int)this->content.size() - this->list_pos - 1;
826 } else {
827 this->content.SetSortType(widget - WID_NCL_CHECKBOX);
828 this->content.ForceResort();
829 this->SortContentList();
830 }
831 this->ScrollToSelected();
832 this->InvalidateData();
833 break;
834
836 _network_content_client.SelectAll();
837 this->InvalidateData();
838 break;
839
841 _network_content_client.SelectUpgrade();
842 this->InvalidateData();
843 break;
844
845 case WID_NCL_UNSELECT:
846 _network_content_client.UnselectAll();
847 this->InvalidateData();
848 break;
849
850 case WID_NCL_OPEN_URL:
851 if (this->selected != nullptr) {
852 OpenBrowser(this->selected->url);
853 }
854 break;
855
856 case WID_NCL_DOWNLOAD:
858 break;
859
862 this->OpenExternalSearch();
863 } else {
864 ShowQuery(
865 GetEncodedString(STR_CONTENT_SEARCH_EXTERNAL_DISCLAIMER_CAPTION),
866 GetEncodedString(STR_CONTENT_SEARCH_EXTERNAL_DISCLAIMER),
868 }
869 break;
870 }
871 }
872
873 EventState OnKeyPress([[maybe_unused]] char32_t key, uint16_t keycode) override
874 {
875 if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_NOT_HANDLED) {
876 switch (keycode) {
877 case WKC_SPACE:
878 case WKC_RETURN:
879 if (keycode == WKC_RETURN || !IsWidgetFocused(WID_NCL_FILTER)) {
880 if (this->selected != nullptr) {
881 _network_content_client.ToggleSelectedState(*this->selected);
882 this->content.ForceResort();
883 this->InvalidateData();
884 }
885 if (this->filter_data.types.Any()) {
886 this->content.ForceRebuild();
887 this->InvalidateData();
888 }
889 return ES_HANDLED;
890 }
891 /* space is pressed and filter is focused. */
892 [[fallthrough]];
893
894 default:
895 return ES_NOT_HANDLED;
896 }
897 }
898
899 if (this->content.empty()) {
900 if (this->UpdateFilterState()) {
901 this->content.ForceRebuild();
902 this->InvalidateData();
903 }
904 return ES_HANDLED;
905 }
906
907 this->selected = this->content[this->list_pos];
908
909 if (this->UpdateFilterState()) {
910 this->content.ForceRebuild();
911 } else {
912 /* Scroll to the new content if it is outside the current range. */
913 this->ScrollToSelected();
914 }
915
916 /* redraw window */
917 this->InvalidateData();
918 return ES_HANDLED;
919 }
920
921 void OnEditboxChanged(WidgetID wid) override
922 {
923 if (wid == WID_NCL_FILTER) {
924 this->filter_data.string_filter.SetFilterTerm(this->filter_editbox.text.GetText());
925 this->UpdateFilterState();
926 this->content.ForceRebuild();
927 this->InvalidateData();
928 }
929 }
930
931 void OnResize() override
932 {
933 this->vscroll->SetCapacityFromWidget(this, WID_NCL_MATRIX);
934 }
935
936 void OnReceiveContentInfo(const ContentInfo &rci) override
937 {
938 if (this->auto_select && !rci.IsSelected()) _network_content_client.ToggleSelectedState(rci);
939 this->content.ForceRebuild();
940 this->InvalidateData(0, false);
941 }
942
944 {
945 this->content.ForceResort();
946 this->InvalidateData();
947 }
948
949 void OnConnect(bool success) override
950 {
951 if (!success) {
952 ShowErrorMessage(GetEncodedString(STR_CONTENT_ERROR_COULD_NOT_CONNECT), {}, WL_ERROR);
953 this->Close();
954 return;
955 }
956
957 this->InvalidateData();
958 }
959
965 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
966 {
967 if (!gui_scope) return;
968 if (this->content.NeedRebuild()) this->BuildContentList();
969
970 /* To sum all the bytes we intend to download */
971 this->filesize_sum = 0;
972 bool show_select_all = false;
973 bool show_select_upgrade = false;
974 for (const ContentInfo *ci : this->content) {
975 switch (ci->state) {
978 this->filesize_sum += ci->filesize;
979 break;
980
982 show_select_all = true;
983 show_select_upgrade |= ci->upgrade;
984 break;
985
986 default:
987 break;
988 }
989 }
990
991 /* If data == 2 then the status window caused this OnInvalidate */
993 this->SetWidgetDisabledState(WID_NCL_UNSELECT, this->filesize_sum == 0);
994 this->SetWidgetDisabledState(WID_NCL_SELECT_ALL, !show_select_all);
995 this->SetWidgetDisabledState(WID_NCL_SELECT_UPDATE, !show_select_upgrade || !this->filter_data.string_filter.IsEmpty());
996 this->SetWidgetDisabledState(WID_NCL_OPEN_URL, this->selected == nullptr || this->selected->url.empty());
997 for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {
998 this->SetWidgetDisabledState(WID_NCL_TEXTFILE + tft, this->selected == nullptr || this->selected->state != ContentInfo::State::AlreadyHere || !this->selected->GetTextfile(tft).has_value());
999 }
1000 }
1001};
1002
1005
1006const std::initializer_list<NetworkContentListWindow::GUIContentList::SortFunction * const> NetworkContentListWindow::sorter_funcs = {
1007 &StateSorter,
1008 &TypeSorter,
1009 &NameSorter,
1010};
1011
1012const std::initializer_list<NetworkContentListWindow::GUIContentList::FilterFunction * const> NetworkContentListWindow::filter_funcs = {
1015};
1016
1018
1023{
1024 for (int i = CONTENT_TYPE_BEGIN; i < CONTENT_TYPE_END; i++) {
1025 NetworkContentListWindow::content_type_strs[i] = GetString(STR_CONTENT_TYPE_BASE_GRAPHICS + i - CONTENT_TYPE_BASE_GRAPHICS);
1026 }
1027}
1028
1030static constexpr std::initializer_list<NWidgetPart> _nested_network_content_list_widgets = {
1032 NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
1033 NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE), SetStringTip(STR_CONTENT_TITLE),
1034 NWidget(WWT_DEFSIZEBOX, COLOUR_LIGHT_BLUE),
1035 EndContainer(),
1036 NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NCL_BACKGROUND),
1038 /* Top */
1040 NWidget(WWT_TEXT, INVALID_COLOUR, WID_NCL_FILTER_CAPT), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_CONTENT_FILTER_TITLE), SetAlignment(SA_RIGHT | SA_VERT_CENTER),
1041 NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, WID_NCL_FILTER), SetFill(1, 0), SetResize(1, 0),
1042 SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
1043 EndContainer(),
1044 /* Lists and info. */
1046 /* Left side. */
1051 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_CHECKBOX), SetStringTip(STR_EMPTY),
1052 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_TYPE),
1053 SetStringTip(STR_CONTENT_TYPE_CAPTION, STR_CONTENT_TYPE_CAPTION_TOOLTIP),
1054 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_NAME), SetResize(1, 0), SetFill(1, 0),
1055 SetStringTip(STR_CONTENT_NAME_CAPTION, STR_CONTENT_NAME_CAPTION_TOOLTIP),
1056 EndContainer(),
1057 NWidget(WWT_MATRIX, COLOUR_LIGHT_BLUE, WID_NCL_MATRIX), SetResize(1, 1), SetFill(1, 1), SetScrollbar(WID_NCL_SCROLLBAR), SetMatrixDataTip(1, 0, STR_CONTENT_MATRIX_TOOLTIP),
1058 EndContainer(),
1059 NWidget(NWID_VSCROLLBAR, COLOUR_LIGHT_BLUE, WID_NCL_SCROLLBAR),
1060 EndContainer(),
1063 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_SELECT_UPDATE), SetResize(1, 0), SetFill(1, 0),
1064 SetStringTip(STR_CONTENT_SELECT_UPDATES_CAPTION, STR_CONTENT_SELECT_UPDATES_CAPTION_TOOLTIP),
1065 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_SELECT_ALL), SetResize(1, 0), SetFill(1, 0),
1066 SetStringTip(STR_CONTENT_SELECT_ALL_CAPTION, STR_CONTENT_SELECT_ALL_CAPTION_TOOLTIP),
1067 EndContainer(),
1068 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_UNSELECT), SetResize(1, 0), SetFill(1, 0),
1069 SetStringTip(STR_CONTENT_UNSELECT_ALL_CAPTION, STR_CONTENT_UNSELECT_ALL_CAPTION_TOOLTIP),
1070 EndContainer(),
1071 EndContainer(),
1072 /* Right side. */
1074 NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NCL_DETAILS), SetResize(1, 1), SetFill(1, 1),
1075 EndContainer(),
1078 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_OPEN_URL), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_CONTENT_OPEN_URL, STR_CONTENT_OPEN_URL_TOOLTIP),
1079 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_TEXTFILE + TFT_README), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_README, STR_TEXTFILE_VIEW_README_TOOLTIP),
1080 EndContainer(),
1082 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_TEXTFILE + TFT_CHANGELOG), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_TEXTFILE_VIEW_CHANGELOG_TOOLTIP),
1083 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_TEXTFILE + TFT_LICENSE), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_LICENCE, STR_TEXTFILE_VIEW_LICENCE_TOOLTIP),
1084 EndContainer(),
1085 EndContainer(),
1086 EndContainer(),
1087 EndContainer(),
1088 /* Bottom. */
1090 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_SEARCH_EXTERNAL), SetResize(1, 0), SetFill(1, 0),
1091 SetStringTip(STR_CONTENT_SEARCH_EXTERNAL, STR_CONTENT_SEARCH_EXTERNAL_TOOLTIP),
1092 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_DOWNLOAD), SetResize(1, 0), SetFill(1, 0),
1093 SetStringTip(STR_CONTENT_DOWNLOAD_CAPTION, STR_CONTENT_DOWNLOAD_CAPTION_TOOLTIP),
1094 EndContainer(),
1095 EndContainer(),
1096 /* Resize button. */
1098 NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
1099 NWidget(WWT_RESIZEBOX, COLOUR_LIGHT_BLUE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1100 EndContainer(),
1101 EndContainer(),
1102};
1103
1106 WDP_CENTER, "list_content", 630, 460,
1108 {},
1110);
1111
1120{
1121#if defined(WITH_ZLIB)
1122 ContentTypes types{};
1124 if (cv == nullptr) {
1125 assert(type1 != CONTENT_TYPE_END || type2 == CONTENT_TYPE_END);
1126 assert(type1 == CONTENT_TYPE_END || type1 != type2);
1127 _network_content_client.RequestContentList(type1);
1128 if (type2 != CONTENT_TYPE_END) _network_content_client.RequestContentList(type2);
1129
1130 if (type1 != CONTENT_TYPE_END) types.Set(type1);
1131 if (type2 != CONTENT_TYPE_END) types.Set(type2);
1132 } else {
1133 _network_content_client.RequestContentList(cv, true);
1134 }
1135
1137 new NetworkContentListWindow(_network_content_list_desc, cv != nullptr, types);
1138#else
1140 GetEncodedString(STR_CONTENT_NO_ZLIB),
1141 GetEncodedString(STR_CONTENT_NO_ZLIB_SUB),
1142 WL_ERROR);
1143#endif /* WITH_ZLIB */
1144}
Base functions for all AIs.
Generic functions for replacing base data (graphics, sounds).
Generic functions for replacing base graphics data.
Generic functions for replacing base music data.
Generic functions for replacing base sounds data.
static void Rescan()
Rescans all searchpaths for available AIs.
Definition ai_core.cpp:318
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr bool None() const
Test if none of the values are set.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
uint32_t cur_id
The current ID of the downloaded file.
BaseNetworkContentDownloadStatusWindow(WindowDesc &desc)
Create the window with the given description.
uint downloaded_bytes
Number of bytes downloaded.
uint total_files
Number of files to download.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnDownloadProgress(const ContentInfo &ci, int bytes) override
We have progress in the download of a file.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
std::string name
The current name of the downloaded file.
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 downloaded_files
Number of files downloaded.
uint total_bytes
Number of bytes to download.
List template of 'things' T to sort in a GUI.
bool Filter(FilterFunction *decide, F filter_data)
Filter the list.
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetFiltering(Filtering f)
Import filter conditions.
void SetListing(Listing l)
Import sort conditions.
void SetFilterState(bool state)
Enable or disable the filter.
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.
void SetFilterFuncs(std::span< FilterFunction *const > n_funcs)
Hand the filter function pointers to the GUIList.
Filtering GetFiltering() const
Export current filter conditions.
bool NeedRebuild() const
Check if a rebuild is needed.
void SetFilterType(uint8_t n_type)
Set the filtertype of the list.
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.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
void SetSortType(uint8_t n_type)
Set the sorttype of the list.
static void Rescan()
Rescans all searchpaths for available Game scripts.
Baseclass for nested widgets.
uint current_x
Current horizontal size (after resizing).
int pos_x
Horizontal position of top-left corner of the widget in the window.
Window that lists the content that's at the content server.
EventState OnKeyPress(char32_t key, uint16_t keycode) override
A key has been pressed.
static bool NameSorter(const ContentInfo *const &a, const ContentInfo *const &b)
Sort content by name.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void DrawDetails(const Rect &r) const
Helper function to draw the details part of this window.
void OpenExternalSearch()
Search external websites for content.
void DrawMatrix(const Rect &r) const
Draw/fill the matrix with the list of content to download.
void OnResize() override
Called after the window got resized.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
void OnReceiveContentInfo(const ContentInfo &rci) override
We received a content info.
static std::string content_type_strs[CONTENT_TYPE_END]
Cached strings for all content types.
GUIContentList content
List with content.
uint filesize_sum
The sum of all selected file sizes.
bool auto_select
Automatically select all content when the meta-data becomes available.
ContentListFilterData filter_data
Filter for content 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.
static Filtering last_filtering
The last filtering setting.
QueryString filter_editbox
Filter editbox;.
static const std::initializer_list< GUIContentList::SortFunction *const > sorter_funcs
Sorter functions.
static Listing last_sorting
The last sorting setting.
static bool TagNameFilter(const ContentInfo *const *a, ContentListFilterData &filter)
Filter content by tags/name.
int list_pos
Our position in the list.
static bool TypeOrSelectedFilter(const ContentInfo *const *a, ContentListFilterData &filter)
Filter content by type, but still show content selected for download.
const ContentInfo * selected
The selected content info.
void OnInit() override
Notification that the nested widget tree gets initialized.
static const std::initializer_list< GUIContentList::FilterFunction *const > filter_funcs
Filter functions.
static bool TypeSorter(const ContentInfo *const &a, const ContentInfo *const &b)
Sort content by type.
void SortContentList()
Sort the content list.
static void ExternalSearchDisclaimerCallback(Window *w, bool accepted)
Callback function for disclaimer about entering external websites.
bool UpdateFilterState()
Update filter state based on current window state.
void OnPaint() override
The window must be repainted.
NetworkContentListWindow(WindowDesc &desc, bool select_all, ContentTypes types)
Create the content list window.
GUIList< const ContentInfo *, std::nullptr_t, ContentListFilterData & > GUIContentList
List with content infos.
static const uint EDITBOX_MAX_SIZE
Maximum size of the editbox in characters.
void OnEditboxChanged(WidgetID wid) override
The text in an editbox has been edited.
void OnDownloadComplete(ContentID) override
We have finished downloading a file.
void BuildContentList()
(Re)build the network game list as its amount has changed because an item has been added or deleted f...
void OnConnect(bool success) override
Callback for when the connection has finished.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Scrollbar * vscroll
Cache of the vertical scrollbar.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
friend void BuildContentTypeStringList()
Build array of all strings corresponding to the content types.
void FilterContentList()
Filter the content list.
Dimension checkbox_size
Size of checkbox/"blot" sprite.
void ScrollToSelected()
Make sure that the currently selected content info is within the visible part of the matrix.
static bool StateSorter(const ContentInfo *const &a, const ContentInfo *const &b)
Sort content by state.
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:2500
EventState UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
Update the given list position as if it were on this scroll bar when the given keycode was pressed.
Definition widget.cpp:2447
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.
@ Scenario
Scan for scenarios and heightmaps.
Definition fileio_func.h:67
@ Baseset
Scan for base sets.
Definition fileio_func.h:64
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition fileio.cpp:378
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
int find_index(Container const &container, typename Container::const_reference item)
Helper function to get the index of an item Consider using std::set, std::unordered_set or std::flat_...
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Functions related to errors.
@ WL_ERROR
Errors (eg. saving/loading failed).
Definition error.h:26
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
void ScanScenarios()
Force a (re)scan of the scenarios.
Definition fios.cpp:691
Declarations for savegames operations.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
Base functions for all Games.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:900
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:669
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:788
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:116
Functions related to the gfx engine.
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:250
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:390
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:389
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:398
@ SA_VERT_CENTER
Vertically center the text.
Definition gfx_type.h:394
constexpr NWidgetPart SetMatrixDataTip(uint32_t cols, uint32_t rows, StringID tip={})
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetPIP(uint8_t pre, uint8_t inter, uint8_t post)
Widget part function for setting a pre/inter/post spaces.
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
constexpr NWidgetPart SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart SetResizeWidgetTypeTip(ResizeWidgetValues widget_type, StringID tip)
Widget part function for setting the resize widget type and tooltip.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart SetAlignment(StringAlignment align)
Widget part function for setting the alignment of text/images.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:967
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
void ShowQuery(EncodedString &&caption, EncodedString &&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...
@ WID_TF_CAPTION
The caption of the window.
Definition misc_widget.h:53
ClientNetworkContentSocketHandler _network_content_client
The client we use to connect to the server.
std::vector< std::unique_ptr< ContentInfo > > ContentVector
Vector with content info.
std::vector< const ContentInfo * > ConstContentVector
Vector with constant content info.
static bool _accepted_external_search
Whether the user accepted to enter external websites during this session.
static constexpr std::initializer_list< NWidgetPart > _nested_network_content_list_widgets
The widgets for the content list.
void ShowNetworkContentListWindow(ContentVector *cv, ContentType type1, ContentType type2)
Show the content list window with a given set of content.
static constexpr std::initializer_list< NWidgetPart > _nested_network_content_download_status_window_widgets
Nested widgets for the download window.
ContentListFilterCriteria
Filter criteria for NetworkContentListWindow.
@ CONTENT_FILTER_TYPE_OR_SELECTED
Filter by being of displayed type or selected for download.
@ CONTENT_FILTER_TEXT
Filter by query string.
static WindowDesc _network_content_download_status_window_desc(WDP_CENTER, {}, 0, 0, WC_NETWORK_STATUS_WINDOW, WC_NONE, WindowDefaultFlag::Modal, _nested_network_content_download_status_window_widgets)
Window description for the download window.
static WindowDesc _network_content_list_desc(WDP_CENTER, "list_content", 630, 460, WC_NETWORK_WINDOW, WC_NONE, {}, _nested_network_content_list_widgets)
Window description of the content list.
void BuildContentTypeStringList()
Build array of all strings corresponding to the content types.
User interface for downloading files.
@ WID_NCL_CHECKBOX
Button above checkboxes.
@ WID_NCL_NAME
'Name' button.
@ WID_NCL_TYPE
'Type' button.
@ WID_NCL_SEARCH_EXTERNAL
Search external sites for missing NewGRF.
@ WID_NCL_DETAILS
Panel with content details.
@ WID_NCL_SEL_ALL_UPDATE
NWID_SELECTION widget for select all/update buttons..
@ WID_NCL_SELECT_ALL
'Select all' button.
@ WID_NCL_FILTER
Filter editbox.
@ WID_NCL_FILTER_CAPT
Caption for the filter editbox.
@ WID_NCL_DOWNLOAD
'Download' button.
@ WID_NCL_SCROLLBAR
Scrollbar of matrix.
@ WID_NCL_SELECT_UPDATE
'Select updates' button.
@ WID_NCL_TEXTFILE
Open readme, changelog (+1) or license (+2) of a file in the content window.
@ WID_NCL_MATRIX
Panel with list of content.
@ WID_NCL_BACKGROUND
Resize button.
@ WID_NCL_UNSELECT
'Unselect all' button.
@ WID_NCL_OPEN_URL
'Open url' button.
@ WID_NCDS_CANCELOK
(Optional) Cancel/OK button.
@ WID_NCDS_PROGRESS_TEXT
Text explaining what is happening.
@ WID_NCDS_PROGRESS_BAR
Simple progress bar.
bool RequestNewGRFScan(NewGRFScanCallback *callback)
Request a new NewGRF scan.
Definition openttd.cpp:1325
Some generic types.
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
static constexpr PixelColour PC_GREY
Grey palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
Base types for having sorted lists in GUIs.
This file contains all sprite-related enums and defines.
Definition of base types and functions in a cross-platform compatible way.
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:77
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:427
Searching and filtering using a stringterm.
std::string_view GetListSeparator()
Get the list separator string for the current language.
Definition strings.cpp:299
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:218
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
Callbacks for notifying others about incoming data.
Container for all important information about a piece of content.
uint32_t unique_id
Unique ID; either GRF ID or shortname.
uint32_t filesize
Size of the file.
MD5Hash md5sum
The MD5 checksum.
std::string url
URL related to the content.
State state
Whether the content info is selected (for download).
std::string name
Name of the content.
std::string description
Description of the content.
std::optional< std::string > GetTextfile(TextfileType type) const
Search a textfile file next to this file in the content list.
std::string version
Version of the content.
ContentID id
Unique (server side) ID for the content.
std::string filename
Filename (for the .tar.gz; only valid on download).
bool IsSelected() const
Is the state either selected or autoselected?
ContentType type
Type of content.
std::vector< ContentID > dependencies
The dependencies (unique server side ids).
@ Unselected
The content has not been selected.
@ AlreadyHere
The content is already at the client side.
@ Selected
The content has been manually selected.
@ Autoselected
The content has been selected as dependency.
@ DoesNotExist
The content does not exist in the content system.
StringList tags
Tags associated with the content.
bool upgrade
This item is an upgrade.
Filter data for NetworkContentListWindow.
StringFilter string_filter
Text filter of content list.
ContentTypes types
Content types displayed.
Window for displaying the textfile of an item in the content list.
const ContentInfo * ci
View the textfile of this ContentInfo.
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
Dimensions (a width and height) of a rectangle in 2D.
Data structure describing what to show in the list (filter criteria).
bool state
Filter on/off.
Data structure describing how to show the list (what sort direction and criteria).
Window for showing the download status of content.
void OnDownloadProgress(const ContentInfo &ci, int bytes) override
We have progress in the download of a file.
ContentTypes received_types
Types we received so we can update their cache.
NetworkContentDownloadStatusWindow()
Create a new download window based on a list of content information with flags whether to download th...
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
Data stored about a string that can be modified in the GUI.
int cancel_button
Widget button of parent window to simulate when pressing CANCEL in OSK.
static const int ACTION_CLEAR
Clear editbox.
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.
Rect WithY(int new_top, int new_bottom) const
Create a new Rect, replacing the top and bottom coordiates.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
String filter and state.
bool IsEmpty() const
Check whether any filter words were entered.
void SetFilterTerm(std::string_view str)
Set the term to filter on.
void ResetState()
Reset the matching state to process a new item.
bool GetState() const
Get the matching state of the current item.
std::string_view GetText() const
Get the current text.
Definition textbuf.cpp:284
TextfileType file_type
Type of textfile to view.
virtual void LoadTextfile(const std::string &textfile, Subdirectory dir)
Loads the textfile text from file and setup lines.
High level window description.
Definition window_gui.h:168
Data structure for an opened window.
Definition window_gui.h:274
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1104
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:815
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1809
std::map< WidgetID, QueryString * > querystrings
QueryString associated to WWT_EDITBOX widgets.
Definition window_gui.h:321
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:766
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing).
Definition window.cpp:3247
Window * parent
Parent window.
Definition window_gui.h:329
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:505
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:798
ResizeInfo resize
Resize information.
Definition window_gui.h:315
bool IsWidgetFocused(WidgetID widget_index) const
Check if given widget is focused within this window.
Definition window_gui.h:421
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1799
void CloseChildWindowById(WindowClass wc, WindowNumber number) const
Close all children a window might have in a head-recursive manner.
Definition window.cpp:1091
bool SetFocusedWidget(WidgetID widget_index)
Set focus within this window to the given widget.
Definition window.cpp:486
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition window.cpp:1832
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:986
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1822
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:314
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
Subdirectory GetContentInfoSubDir(ContentType type)
Helper to get the subdirectory a ContentInfo is located in.
uint32_t ContentID
Unique identifier for the content.
ContentType
The values in the enum are important; they are used as database 'keys'.
@ CONTENT_TYPE_AI_LIBRARY
The content consists of an AI library.
@ CONTENT_TYPE_BASE_SOUNDS
The content consists of base sounds.
@ CONTENT_TYPE_GAME_LIBRARY
The content consists of a GS library.
@ CONTENT_TYPE_BASE_GRAPHICS
The content consists of base graphics.
@ CONTENT_TYPE_AI
The content consists of an AI.
@ CONTENT_TYPE_SCENARIO
The content consists of a scenario.
@ CONTENT_TYPE_NEWGRF
The content consists of a NewGRF.
@ CONTENT_TYPE_BEGIN
Helper to mark the begin of the types.
@ CONTENT_TYPE_BASE_MUSIC
The content consists of base music.
@ CONTENT_TYPE_GAME
The content consists of a game script.
@ CONTENT_TYPE_END
Helper to mark the end of the types.
@ CONTENT_TYPE_HEIGHTMAP
The content consists of a heightmap.
GUI functions related to textfiles.
TextfileType
Additional text files accompanying Tar archives.
@ TFT_LICENSE
Content license.
@ TFT_README
Content readme.
@ TFT_CHANGELOG
Content changelog.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:289
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:70
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:49
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:71
@ EqualSize
Containers should keep all their (resizing) children equally large.
@ RWV_HIDE_BEVEL
Bevel of resize box is hidden.
Definition widget_type.h:29
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1196
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3307
Window * BringWindowToFrontById(WindowClass cls, WindowNumber number)
Find a window and make it the relative top-window on the screen.
Definition window.cpp:1280
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1153
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3185
Window functions not directly related to making/drawing windows.
@ Modal
The window is a modal child of some other window, meaning the parent is 'inactive'.
Definition window_gui.h:154
@ BorderOnly
Draw border only, no background.
Definition window_gui.h:26
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed).
Definition window_gui.h:27
SortButtonState
State of a sort direction button.
Definition window_gui.h:217
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:219
@ SBS_UP
Sort descending.
Definition window_gui.h:220
@ WDP_CENTER
Center the window.
Definition window_gui.h:145
int WidgetID
Widget ID.
Definition window_type.h:20
@ WN_NETWORK_WINDOW_CONTENT_LIST
Network content list.
Definition window_type.h:41
@ WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD
Network content download status.
Definition window_type.h:45
@ WN_GAME_OPTIONS_GAME_OPTIONS
Game options.
Definition window_type.h:31
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:50
@ WC_GAME_OPTIONS
Game options window; Window numbers:
@ WC_NETWORK_WINDOW
Network window; Window numbers:
@ WC_TEXTFILE
textfile; Window numbers:
@ WC_SAVELOAD
Saveload window; Window numbers:
@ WC_NETWORK_STATUS_WINDOW
Network status window; Window numbers: