1 /* $Id: network_content_gui.cpp 26167 2013-12-20 18:29:53Z frosch $ */
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file network_content_gui.cpp Implementation of the Network Content related GUIs. */
12 #if defined(ENABLE_NETWORK)
13 #include "../stdafx.h"
14 #include "../strings_func.h"
15 #include "../gfx_func.h"
16 #include "../window_func.h"
18 #include "../ai/ai.hpp"
19 #include "../game/game.hpp"
20 #include "../base_media_base.h"
21 #include "../sortlist_type.h"
22 #include "../stringfilter_type.h"
23 #include "../querystring_gui.h"
24 #include "../core/geometry_func.hpp"
25 #include "../textfile_gui.h"
26 #include "network_content_gui.h"
29 #include "table/strings.h"
30 #include "../table/sprites.h"
34 #include "../safeguards.h"
37 /** Whether the user accepted to enter external websites during this session. */
38 static bool _accepted_external_search
= false;
41 /** Window for displaying the textfile of an item in the content list. */
42 struct ContentTextfileWindow
: public TextfileWindow
{
43 const ContentInfo
*ci
; ///< View the textfile of this ContentInfo.
45 ContentTextfileWindow(TextfileType file_type
, const ContentInfo
*ci
) : TextfileWindow(file_type
), ci(ci
)
47 const char *textfile
= this->ci
->GetTextfile(file_type
);
48 this->LoadTextfile(textfile
, GetContentInfoSubDir(this->ci
->type
));
51 StringID
GetTypeString() const
53 switch (this->ci
->type
) {
54 case CONTENT_TYPE_NEWGRF
: return STR_CONTENT_TYPE_NEWGRF
;
55 case CONTENT_TYPE_BASE_GRAPHICS
: return STR_CONTENT_TYPE_BASE_GRAPHICS
;
56 case CONTENT_TYPE_BASE_SOUNDS
: return STR_CONTENT_TYPE_BASE_SOUNDS
;
57 case CONTENT_TYPE_BASE_MUSIC
: return STR_CONTENT_TYPE_BASE_MUSIC
;
58 case CONTENT_TYPE_AI
: return STR_CONTENT_TYPE_AI
;
59 case CONTENT_TYPE_AI_LIBRARY
: return STR_CONTENT_TYPE_AI_LIBRARY
;
60 case CONTENT_TYPE_GAME
: return STR_CONTENT_TYPE_GAME_SCRIPT
;
61 case CONTENT_TYPE_GAME_LIBRARY
: return STR_CONTENT_TYPE_GS_LIBRARY
;
62 case CONTENT_TYPE_SCENARIO
: return STR_CONTENT_TYPE_SCENARIO
;
63 case CONTENT_TYPE_HEIGHTMAP
: return STR_CONTENT_TYPE_HEIGHTMAP
;
64 default: NOT_REACHED();
68 /* virtual */ void SetStringParameters(int widget
) const
70 if (widget
== WID_TF_CAPTION
) {
71 SetDParam(0, this->GetTypeString());
72 SetDParamStr(1, this->ci
->name
);
77 void ShowContentTextfileWindow(TextfileType file_type
, const ContentInfo
*ci
)
79 DeleteWindowByClass(WC_TEXTFILE
);
80 new ContentTextfileWindow(file_type
, ci
);
83 /** Nested widgets for the download window. */
84 static const NWidgetPart _nested_network_content_download_status_window_widgets
[] = {
85 NWidget(WWT_CAPTION
, COLOUR_GREY
), SetDataTip(STR_CONTENT_DOWNLOAD_TITLE
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
86 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_NCDS_BACKGROUND
),
87 NWidget(NWID_SPACER
), SetMinimalSize(350, 0), SetMinimalTextLines(3, WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
+ 30),
88 NWidget(NWID_HORIZONTAL
),
89 NWidget(NWID_SPACER
), SetMinimalSize(125, 0),
90 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCDS_CANCELOK
), SetMinimalSize(101, 12), SetDataTip(STR_BUTTON_CANCEL
, STR_NULL
),
91 NWidget(NWID_SPACER
), SetFill(1, 0),
93 NWidget(NWID_SPACER
), SetMinimalSize(0, 4),
97 /** Window description for the download window */
98 static WindowDesc
_network_content_download_status_window_desc(
99 WDP_CENTER
, nullptr, 0, 0,
100 WC_NETWORK_STATUS_WINDOW
, WC_NONE
,
102 _nested_network_content_download_status_window_widgets
, lengthof(_nested_network_content_download_status_window_widgets
)
105 BaseNetworkContentDownloadStatusWindow::BaseNetworkContentDownloadStatusWindow(WindowDesc
*desc
) :
106 Window(desc
), cur_id(UINT32_MAX
)
108 _network_content_client
.AddCallback(this);
109 _network_content_client
.DownloadSelectedContent(this->total_files
, this->total_bytes
);
111 this->InitNested(WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD
);
114 BaseNetworkContentDownloadStatusWindow::~BaseNetworkContentDownloadStatusWindow()
116 _network_content_client
.RemoveCallback(this);
119 /* virtual */ void BaseNetworkContentDownloadStatusWindow::DrawWidget(const Rect
&r
, int widget
) const
121 if (widget
!= WID_NCDS_BACKGROUND
) return;
123 /* Draw nice progress bar :) */
124 DrawFrameRect(r
.left
+ 20, r
.top
+ 4, r
.left
+ 20 + (int)((this->width
- 40LL) * this->downloaded_bytes
/ this->total_bytes
), r
.top
+ 14, COLOUR_MAUVE
, FR_NONE
);
127 SetDParam(0, this->downloaded_bytes
);
128 SetDParam(1, this->total_bytes
);
129 SetDParam(2, this->downloaded_bytes
* 100LL / this->total_bytes
);
130 DrawString(r
.left
+ 2, r
.right
- 2, y
, STR_CONTENT_DOWNLOAD_PROGRESS_SIZE
, TC_FROMSTRING
, SA_HOR_CENTER
);
133 if (this->downloaded_bytes
== this->total_bytes
) {
134 str
= STR_CONTENT_DOWNLOAD_COMPLETE
;
135 } else if (!StrEmpty(this->name
)) {
136 SetDParamStr(0, this->name
);
137 SetDParam(1, this->downloaded_files
);
138 SetDParam(2, this->total_files
);
139 str
= STR_CONTENT_DOWNLOAD_FILE
;
141 str
= STR_CONTENT_DOWNLOAD_INITIALISE
;
144 y
+= FONT_HEIGHT_NORMAL
+ 5;
145 DrawStringMultiLine(r
.left
+ 2, r
.right
- 2, y
, y
+ FONT_HEIGHT_NORMAL
* 2, str
, TC_FROMSTRING
, SA_CENTER
);
148 /* virtual */ void BaseNetworkContentDownloadStatusWindow::OnDownloadProgress(const ContentInfo
*ci
, int bytes
)
150 if (ci
->id
!= this->cur_id
) {
151 strecpy(this->name
, ci
->filename
, lastof(this->name
));
152 this->cur_id
= ci
->id
;
153 this->downloaded_files
++;
156 this->downloaded_bytes
+= bytes
;
161 /** Window for showing the download status of content */
162 struct NetworkContentDownloadStatusWindow
: public BaseNetworkContentDownloadStatusWindow
{
164 SmallVector
<ContentType
, 4> receivedTypes
; ///< Types we received so we can update their cache
168 * Create a new download window based on a list of content information
169 * with flags whether to download them or not.
171 NetworkContentDownloadStatusWindow() : BaseNetworkContentDownloadStatusWindow(&_network_content_download_status_window_desc
)
173 this->parent
= FindWindowById(WC_NETWORK_WINDOW
, WN_NETWORK_WINDOW_CONTENT_LIST
);
176 /** Free whatever we've allocated */
177 ~NetworkContentDownloadStatusWindow()
179 TarScanner::Mode mode
= TarScanner::NONE
;
180 for (ContentType
*iter
= this->receivedTypes
.Begin(); iter
!= this->receivedTypes
.End(); iter
++) {
182 case CONTENT_TYPE_AI
:
183 case CONTENT_TYPE_AI_LIBRARY
:
184 /* AI::Rescan calls the scanner. */
186 case CONTENT_TYPE_GAME
:
187 case CONTENT_TYPE_GAME_LIBRARY
:
188 /* Game::Rescan calls the scanner. */
191 case CONTENT_TYPE_BASE_GRAPHICS
:
192 case CONTENT_TYPE_BASE_SOUNDS
:
193 case CONTENT_TYPE_BASE_MUSIC
:
194 mode
|= TarScanner::BASESET
;
197 case CONTENT_TYPE_NEWGRF
:
198 /* ScanNewGRFFiles calls the scanner. */
201 case CONTENT_TYPE_SCENARIO
:
202 case CONTENT_TYPE_HEIGHTMAP
:
203 mode
|= TarScanner::SCENARIO
;
211 TarScanner::DoScan(mode
);
213 /* Tell all the backends about what we've downloaded */
214 for (ContentType
*iter
= this->receivedTypes
.Begin(); iter
!= this->receivedTypes
.End(); iter
++) {
216 case CONTENT_TYPE_AI
:
217 case CONTENT_TYPE_AI_LIBRARY
:
221 case CONTENT_TYPE_GAME
:
222 case CONTENT_TYPE_GAME_LIBRARY
:
226 case CONTENT_TYPE_BASE_GRAPHICS
:
227 BaseGraphics::FindSets();
228 SetWindowDirty(WC_GAME_OPTIONS
, WN_GAME_OPTIONS_GAME_OPTIONS
);
231 case CONTENT_TYPE_BASE_SOUNDS
:
232 BaseSounds::FindSets();
233 SetWindowDirty(WC_GAME_OPTIONS
, WN_GAME_OPTIONS_GAME_OPTIONS
);
236 case CONTENT_TYPE_BASE_MUSIC
:
237 BaseMusic::FindSets();
238 SetWindowDirty(WC_GAME_OPTIONS
, WN_GAME_OPTIONS_GAME_OPTIONS
);
241 case CONTENT_TYPE_NEWGRF
:
242 ScanNewGRFFiles(nullptr);
245 case CONTENT_TYPE_SCENARIO
:
246 case CONTENT_TYPE_HEIGHTMAP
:
247 extern void ScanScenarios();
249 InvalidateWindowData(WC_SAVELOAD
, 0, 0);
257 /* Always invalidate the download window; tell it we are going to be gone */
258 InvalidateWindowData(WC_NETWORK_WINDOW
, WN_NETWORK_WINDOW_CONTENT_LIST
, 2);
261 virtual void OnClick(Point pt
, int widget
, int click_count
)
263 if (widget
== WID_NCDS_CANCELOK
) {
264 if (this->downloaded_bytes
!= this->total_bytes
) {
265 _network_content_client
.Close();
268 /* If downloading succeeded, close the online content window. This will close
269 * the current window as well. */
270 DeleteWindowById(WC_NETWORK_WINDOW
, WN_NETWORK_WINDOW_CONTENT_LIST
);
275 virtual void OnDownloadProgress(const ContentInfo
*ci
, int bytes
)
277 BaseNetworkContentDownloadStatusWindow::OnDownloadProgress(ci
, bytes
);
278 this->receivedTypes
.Include(ci
->type
);
280 /* When downloading is finished change cancel in ok */
281 if (this->downloaded_bytes
== this->total_bytes
) {
282 this->GetWidget
<NWidgetCore
>(WID_NCDS_CANCELOK
)->widget_data
= STR_BUTTON_OK
;
287 /** Filter data for NetworkContentListWindow. */
288 struct ContentListFilterData
{
289 StringFilter string_filter
; ///< Text filter of content list
290 std::bitset
<CONTENT_TYPE_END
> types
; ///< Content types displayed
293 /** Filter criterias for NetworkContentListWindow. */
294 enum ContentListFilterCriteria
{
295 CONTENT_FILTER_TEXT
= 0, ///< Filter by query sting
296 CONTENT_FILTER_TYPE_OR_SELECTED
,///< Filter by being of displayed type or selected for download
299 /** Window that lists the content that's at the content server */
300 class NetworkContentListWindow
: public Window
, ContentCallback
{
301 /** List with content infos. */
302 typedef GUIList
<const ContentInfo
*, ContentListFilterData
&> GUIContentList
;
304 static const uint EDITBOX_MAX_SIZE
= 50; ///< Maximum size of the editbox in characters.
306 static Listing last_sorting
; ///< The last sorting setting.
307 static Filtering last_filtering
; ///< The last filtering setting.
308 static GUIContentList::SortFunction
* const sorter_funcs
[]; ///< Sorter functions
309 static GUIContentList::FilterFunction
* const filter_funcs
[]; ///< Filter functions.
310 GUIContentList content
; ///< List with content
311 bool auto_select
; ///< Automatically select all content when the meta-data becomes available
312 ContentListFilterData filter_data
; ///< Filter for content list
313 QueryString filter_editbox
; ///< Filter editbox;
314 Dimension checkbox_size
; ///< Size of checkbox/"blot" sprite
316 const ContentInfo
*selected
; ///< The selected content info
317 int list_pos
; ///< Our position in the list
318 uint filesize_sum
; ///< The sum of all selected file sizes
319 Scrollbar
*vscroll
; ///< Cache of the vertical scrollbar
321 static char content_type_strs
[CONTENT_TYPE_END
][64]; ///< Cached strings for all content types.
323 /** Search external websites for content */
324 void OpenExternalSearch()
326 extern void OpenBrowser(const char *url
);
329 const char *last
= lastof(url
);
331 char *pos
= strecpy(url
, "http://grfsearch.openttd.org/?", last
);
333 if (this->auto_select
) {
334 pos
= strecpy(pos
, "do=searchgrfid&q=", last
);
337 for (ConstContentIterator iter
= this->content
.Begin(); iter
!= this->content
.End(); iter
++) {
338 const ContentInfo
*ci
= *iter
;
339 if (ci
->state
!= ContentInfo::DOES_NOT_EXIST
) continue;
341 if (!first
) pos
= strecpy(pos
, ",", last
);
344 pos
+= seprintf(pos
, last
, "%08X", ci
->unique_id
);
345 pos
= strecpy(pos
, ":", last
);
346 pos
= md5sumToString(pos
, last
, ci
->md5sum
);
349 pos
= strecpy(pos
, "do=searchtext&q=", last
);
351 /* Escape search term */
352 for (const char *search
= this->filter_editbox
.text
.buf
; *search
!= '\0'; search
++) {
354 if (*search
== '\'' || *search
== '"') continue;
356 /* Escape special chars, such as &%,= */
357 if (*search
< 0x30) {
358 pos
+= seprintf(pos
, last
, "%%%02X", *search
);
359 } else if (pos
< last
) {
370 * Callback function for disclaimer about entering external websites.
372 static void ExternalSearchDisclaimerCallback(Window
*w
, bool accepted
)
375 _accepted_external_search
= true;
376 ((NetworkContentListWindow
*)w
)->OpenExternalSearch();
381 * (Re)build the network game list as its amount has changed because
382 * an item has been added or deleted for example
384 void BuildContentList()
386 if (!this->content
.NeedRebuild()) return;
388 /* Create temporary array of games to use for listing */
389 this->content
.Clear();
391 bool all_available
= true;
393 for (ConstContentIterator iter
= _network_content_client
.Begin(); iter
!= _network_content_client
.End(); iter
++) {
394 if ((*iter
)->state
== ContentInfo::DOES_NOT_EXIST
) all_available
= false;
395 *this->content
.Append() = *iter
;
398 this->SetWidgetDisabledState(WID_NCL_SEARCH_EXTERNAL
, this->auto_select
&& all_available
);
400 this->FilterContentList();
401 this->content
.Compact();
402 this->content
.RebuildDone();
403 this->SortContentList();
405 this->vscroll
->SetCount(this->content
.Length()); // Update the scrollbar
406 this->ScrollToSelected();
409 /** Sort content by name. */
410 static int CDECL
NameSorter(const ContentInfo
* const *a
, const ContentInfo
* const *b
)
412 return strnatcmp((*a
)->name
, (*b
)->name
, true); // Sort by name (natural sorting).
415 /** Sort content by type. */
416 static int CDECL
TypeSorter(const ContentInfo
* const *a
, const ContentInfo
* const *b
)
419 if ((*a
)->type
!= (*b
)->type
) {
420 r
= strnatcmp(content_type_strs
[(*a
)->type
], content_type_strs
[(*b
)->type
]);
422 if (r
== 0) r
= NameSorter(a
, b
);
426 /** Sort content by state. */
427 static int CDECL
StateSorter(const ContentInfo
* const *a
, const ContentInfo
* const *b
)
429 int r
= (*a
)->state
- (*b
)->state
;
430 if (r
== 0) r
= TypeSorter(a
, b
);
434 /** Sort the content list */
435 void SortContentList()
437 if (!this->content
.Sort()) return;
439 for (ConstContentIterator iter
= this->content
.Begin(); iter
!= this->content
.End(); iter
++) {
440 if (*iter
== this->selected
) {
441 this->list_pos
= iter
- this->content
.Begin();
447 /** Filter content by tags/name */
448 static bool CDECL
TagNameFilter(const ContentInfo
* const *a
, ContentListFilterData
&filter
)
450 filter
.string_filter
.ResetState();
451 for (int i
= 0; i
< (*a
)->tag_count
; i
++) {
452 filter
.string_filter
.AddLine((*a
)->tags
[i
]);
454 filter
.string_filter
.AddLine((*a
)->name
);
455 return filter
.string_filter
.GetState();
458 /** Filter content by type, but still show content selected for download. */
459 static bool CDECL
TypeOrSelectedFilter(const ContentInfo
* const *a
, ContentListFilterData
&filter
)
461 if (filter
.types
.none()) return true;
462 if (filter
.types
[(*a
)->type
]) return true;
463 return ((*a
)->state
== ContentInfo::SELECTED
|| (*a
)->state
== ContentInfo::AUTOSELECTED
);
466 /** Filter the content list */
467 void FilterContentList()
470 bool changed
= false;
471 if (!this->filter_data
.string_filter
.IsEmpty()) {
472 this->content
.SetFilterType(CONTENT_FILTER_TEXT
);
473 changed
|= this->content
.Filter(this->filter_data
);
475 if (this->filter_data
.types
.any()) {
476 this->content
.SetFilterType(CONTENT_FILTER_TYPE_OR_SELECTED
);
477 changed
|= this->content
.Filter(this->filter_data
);
479 if (!changed
) return;
481 /* update list position */
482 for (ConstContentIterator iter
= this->content
.Begin(); iter
!= this->content
.End(); iter
++) {
483 if (*iter
== this->selected
) {
484 this->list_pos
= iter
- this->content
.Begin();
489 /* previously selected item not in list anymore */
490 this->selected
= nullptr;
495 * Update filter state based on current window state.
496 * @return true if filter state was changed, otherwise false.
498 bool UpdateFilterState()
500 Filtering old_params
= this->content
.GetFiltering();
501 bool new_state
= !this->filter_data
.string_filter
.IsEmpty() || this->filter_data
.types
.any();
502 if (new_state
!= old_params
.state
) {
503 this->content
.SetFilterState(new_state
);
505 return new_state
!= old_params
.state
;
508 /** Make sure that the currently selected content info is within the visible part of the matrix */
509 void ScrollToSelected()
511 if (this->selected
== nullptr) return;
513 this->vscroll
->ScrollTowards(this->list_pos
);
516 friend void BuildContentTypeStringList();
519 * Create the content list window.
520 * @param desc the window description to pass to Window's constructor.
521 * @param select_all Whether the select all button is allowed or not.
522 * @param type the main type of content to display or #CONTENT_TYPE_END.
523 * When a type other than #CONTENT_TYPE_END is given, dependencies of
524 * other types are only shown when content that depend on them are
527 NetworkContentListWindow(WindowDesc
*desc
, bool select_all
, const std::bitset
<CONTENT_TYPE_END
> &types
) :
529 auto_select(select_all
),
530 filter_editbox(EDITBOX_MAX_SIZE
),
534 this->checkbox_size
= maxdim(maxdim(GetSpriteSize(SPR_BOX_EMPTY
), GetSpriteSize(SPR_BOX_CHECKED
)), GetSpriteSize(SPR_BLOT
));
536 this->CreateNestedTree();
537 this->vscroll
= this->GetScrollbar(WID_NCL_SCROLLBAR
);
538 this->FinishInitNested(WN_NETWORK_WINDOW_CONTENT_LIST
);
540 this->GetWidget
<NWidgetStacked
>(WID_NCL_SEL_ALL_UPDATE
)->SetDisplayedPlane(select_all
);
542 this->querystrings
[WID_NCL_FILTER
] = &this->filter_editbox
;
543 this->filter_editbox
.cancel_button
= QueryString::ACTION_CLEAR
;
544 this->SetFocusedWidget(WID_NCL_FILTER
);
545 this->SetWidgetDisabledState(WID_NCL_SEARCH_EXTERNAL
, this->auto_select
);
546 this->filter_data
.types
= types
;
548 _network_content_client
.AddCallback(this);
549 this->content
.SetListing(this->last_sorting
);
550 this->content
.SetFiltering(this->last_filtering
);
551 this->content
.SetSortFuncs(this->sorter_funcs
);
552 this->content
.SetFilterFuncs(this->filter_funcs
);
553 this->UpdateFilterState();
554 this->content
.ForceRebuild();
555 this->FilterContentList();
556 this->SortContentList();
557 this->InvalidateData();
560 /** Free everything we allocated */
561 ~NetworkContentListWindow()
563 _network_content_client
.RemoveCallback(this);
566 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
569 case WID_NCL_FILTER_CAPT
:
570 *size
= maxdim(*size
, GetStringBoundingBox(STR_CONTENT_FILTER_TITLE
));
573 case WID_NCL_CHECKBOX
:
574 size
->width
= this->checkbox_size
.width
+ WD_MATRIX_RIGHT
+ WD_MATRIX_LEFT
;
579 for (int i
= CONTENT_TYPE_BEGIN
; i
< CONTENT_TYPE_END
; i
++) {
580 d
= maxdim(d
, GetStringBoundingBox(STR_CONTENT_TYPE_BASE_GRAPHICS
+ i
- CONTENT_TYPE_BASE_GRAPHICS
));
582 size
->width
= d
.width
+ WD_MATRIX_RIGHT
+ WD_MATRIX_LEFT
;
587 resize
->height
= max(this->checkbox_size
.height
, (uint
)FONT_HEIGHT_NORMAL
) + WD_MATRIX_TOP
+ WD_MATRIX_BOTTOM
;
588 size
->height
= 10 * resize
->height
;
594 virtual void DrawWidget(const Rect
&r
, int widget
) const
597 case WID_NCL_FILTER_CAPT
:
598 DrawString(r
.left
, r
.right
, r
.top
, STR_CONTENT_FILTER_TITLE
, TC_FROMSTRING
, SA_RIGHT
);
601 case WID_NCL_DETAILS
:
602 this->DrawDetails(r
);
611 virtual void OnPaint()
613 const SortButtonState arrow
= this->content
.IsDescSortOrder() ? SBS_DOWN
: SBS_UP
;
615 if (this->content
.NeedRebuild()) {
616 this->BuildContentList();
621 switch (this->content
.SortType()) {
622 case WID_NCL_CHECKBOX
- WID_NCL_CHECKBOX
: this->DrawSortButtonState(WID_NCL_CHECKBOX
, arrow
); break;
623 case WID_NCL_TYPE
- WID_NCL_CHECKBOX
: this->DrawSortButtonState(WID_NCL_TYPE
, arrow
); break;
624 case WID_NCL_NAME
- WID_NCL_CHECKBOX
: this->DrawSortButtonState(WID_NCL_NAME
, arrow
); break;
629 * Draw/fill the matrix with the list of content to download.
630 * @param r The boundaries of the matrix.
632 void DrawMatrix(const Rect
&r
) const
634 const NWidgetBase
*nwi_checkbox
= this->GetWidget
<NWidgetBase
>(WID_NCL_CHECKBOX
);
635 const NWidgetBase
*nwi_name
= this->GetWidget
<NWidgetBase
>(WID_NCL_NAME
);
636 const NWidgetBase
*nwi_type
= this->GetWidget
<NWidgetBase
>(WID_NCL_TYPE
);
638 int line_height
= max(this->checkbox_size
.height
, (uint
)FONT_HEIGHT_NORMAL
);
640 /* Fill the matrix with the information */
641 int sprite_y_offset
= WD_MATRIX_TOP
+ (line_height
- this->checkbox_size
.height
) / 2 - 1;
642 int text_y_offset
= WD_MATRIX_TOP
+ (line_height
- FONT_HEIGHT_NORMAL
) / 2;
645 for (ConstContentIterator iter
= this->content
.Get(this->vscroll
->GetPosition()); iter
!= this->content
.End() && cnt
< this->vscroll
->GetCapacity(); iter
++, cnt
++) {
646 const ContentInfo
*ci
= *iter
;
648 if (ci
== this->selected
) GfxFillRect(r
.left
+ 1, y
+ 1, r
.right
- 1, y
+ this->resize
.step_height
- 1, PC_GREY
);
651 SpriteID pal
= PAL_NONE
;
653 case ContentInfo::UNSELECTED
: sprite
= SPR_BOX_EMPTY
; break;
654 case ContentInfo::SELECTED
: sprite
= SPR_BOX_CHECKED
; break;
655 case ContentInfo::AUTOSELECTED
: sprite
= SPR_BOX_CHECKED
; break;
656 case ContentInfo::ALREADY_HERE
: sprite
= SPR_BLOT
; pal
= PALETTE_TO_GREEN
; break;
657 case ContentInfo::DOES_NOT_EXIST
: sprite
= SPR_BLOT
; pal
= PALETTE_TO_RED
; break;
658 default: NOT_REACHED();
660 DrawSprite(sprite
, pal
, nwi_checkbox
->pos_x
+ (pal
== PAL_NONE
? 2 : 3), y
+ sprite_y_offset
+ (pal
== PAL_NONE
? 1 : 0));
662 StringID str
= STR_CONTENT_TYPE_BASE_GRAPHICS
+ ci
->type
- CONTENT_TYPE_BASE_GRAPHICS
;
663 DrawString(nwi_type
->pos_x
, nwi_type
->pos_x
+ nwi_type
->current_x
- 1, y
+ text_y_offset
, str
, TC_BLACK
, SA_HOR_CENTER
);
665 DrawString(nwi_name
->pos_x
+ WD_FRAMERECT_LEFT
, nwi_name
->pos_x
+ nwi_name
->current_x
- WD_FRAMERECT_RIGHT
, y
+ text_y_offset
, ci
->name
, TC_BLACK
);
666 y
+= this->resize
.step_height
;
671 * Helper function to draw the details part of this window.
672 * @param r the rectangle to stay within while drawing
674 void DrawDetails(const Rect
&r
) const
676 static const int DETAIL_LEFT
= 5; ///< Number of pixels at the left
677 static const int DETAIL_RIGHT
= 5; ///< Number of pixels at the right
678 static const int DETAIL_TOP
= 5; ///< Number of pixels at the top
680 /* Height for the title banner */
681 int DETAIL_TITLE_HEIGHT
= 5 * FONT_HEIGHT_NORMAL
;
683 /* Create the nice grayish rectangle at the details top */
684 GfxFillRect(r
.left
+ 1, r
.top
+ 1, r
.right
- 1, r
.top
+ DETAIL_TITLE_HEIGHT
, PC_DARK_BLUE
);
685 DrawString(r
.left
+ WD_INSET_LEFT
, r
.right
- WD_INSET_RIGHT
, r
.top
+ FONT_HEIGHT_NORMAL
+ WD_INSET_TOP
, STR_CONTENT_DETAIL_TITLE
, TC_FROMSTRING
, SA_HOR_CENTER
);
687 /* Draw the total download size */
688 SetDParam(0, this->filesize_sum
);
689 DrawString(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, r
.bottom
- FONT_HEIGHT_NORMAL
- WD_PAR_VSEP_NORMAL
, STR_CONTENT_TOTAL_DOWNLOAD_SIZE
);
691 if (this->selected
== nullptr) return;
693 /* And fill the rest of the details when there's information to place there */
694 DrawStringMultiLine(r
.left
+ WD_INSET_LEFT
, r
.right
- WD_INSET_RIGHT
, r
.top
+ DETAIL_TITLE_HEIGHT
/ 2, r
.top
+ DETAIL_TITLE_HEIGHT
, STR_CONTENT_DETAIL_SUBTITLE_UNSELECTED
+ this->selected
->state
, TC_FROMSTRING
, SA_CENTER
);
696 /* Also show the total download size, so keep some space from the bottom */
697 const uint max_y
= r
.bottom
- FONT_HEIGHT_NORMAL
- WD_PAR_VSEP_WIDE
;
698 int y
= r
.top
+ DETAIL_TITLE_HEIGHT
+ DETAIL_TOP
;
700 if (this->selected
->upgrade
) {
701 SetDParam(0, STR_CONTENT_TYPE_BASE_GRAPHICS
+ this->selected
->type
- CONTENT_TYPE_BASE_GRAPHICS
);
702 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_UPDATE
);
703 y
+= WD_PAR_VSEP_WIDE
;
706 SetDParamStr(0, this->selected
->name
);
707 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_NAME
);
709 if (!StrEmpty(this->selected
->version
)) {
710 SetDParamStr(0, this->selected
->version
);
711 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_VERSION
);
714 if (!StrEmpty(this->selected
->description
)) {
715 SetDParamStr(0, this->selected
->description
);
716 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_DESCRIPTION
);
719 if (!StrEmpty(this->selected
->url
)) {
720 SetDParamStr(0, this->selected
->url
);
721 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_URL
);
724 SetDParam(0, STR_CONTENT_TYPE_BASE_GRAPHICS
+ this->selected
->type
- CONTENT_TYPE_BASE_GRAPHICS
);
725 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_TYPE
);
727 y
+= WD_PAR_VSEP_WIDE
;
728 SetDParam(0, this->selected
->filesize
);
729 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_FILESIZE
);
731 if (this->selected
->dependency_count
!= 0) {
732 /* List dependencies */
733 char buf
[DRAW_STRING_BUFFER
] = "";
735 for (uint i
= 0; i
< this->selected
->dependency_count
; i
++) {
736 ContentID cid
= this->selected
->dependencies
[i
];
738 /* Try to find the dependency */
739 ConstContentIterator iter
= _network_content_client
.Begin();
740 for (; iter
!= _network_content_client
.End(); iter
++) {
741 const ContentInfo
*ci
= *iter
;
742 if (ci
->id
!= cid
) continue;
744 p
+= seprintf(p
, lastof(buf
), p
== buf
? "%s" : ", %s", (*iter
)->name
);
748 SetDParamStr(0, buf
);
749 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_DEPENDENCIES
);
752 if (this->selected
->tag_count
!= 0) {
754 char buf
[DRAW_STRING_BUFFER
] = "";
756 for (uint i
= 0; i
< this->selected
->tag_count
; i
++) {
757 p
+= seprintf(p
, lastof(buf
), i
== 0 ? "%s" : ", %s", this->selected
->tags
[i
]);
759 SetDParamStr(0, buf
);
760 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_TAGS
);
763 if (this->selected
->IsSelected()) {
764 /* When selected show all manually selected content that depends on this */
765 ConstContentVector tree
;
766 _network_content_client
.ReverseLookupTreeDependency(tree
, this->selected
);
768 char buf
[DRAW_STRING_BUFFER
] = "";
770 for (ConstContentIterator iter
= tree
.Begin(); iter
!= tree
.End(); iter
++) {
771 const ContentInfo
*ci
= *iter
;
772 if (ci
== this->selected
|| ci
->state
!= ContentInfo::SELECTED
) continue;
774 p
+= seprintf(p
, lastof(buf
), buf
== p
? "%s" : ", %s", ci
->name
);
777 SetDParamStr(0, buf
);
778 y
= DrawStringMultiLine(r
.left
+ DETAIL_LEFT
, r
.right
- DETAIL_RIGHT
, y
, max_y
, STR_CONTENT_DETAIL_SELECTED_BECAUSE_OF
);
783 virtual void OnClick(Point pt
, int widget
, int click_count
)
785 if (widget
>= WID_NCL_TEXTFILE
&& widget
< WID_NCL_TEXTFILE
+ TFT_END
) {
786 if (this->selected
== nullptr || this->selected
->state
!= ContentInfo::ALREADY_HERE
) return;
788 ShowContentTextfileWindow((TextfileType
)(widget
- WID_NCL_TEXTFILE
), this->selected
);
793 case WID_NCL_MATRIX
: {
794 uint id_v
= this->vscroll
->GetScrolledRowFromWidget(pt
.y
, this, WID_NCL_MATRIX
);
795 if (id_v
>= this->content
.Length()) return; // click out of bounds
797 this->selected
= *this->content
.Get(id_v
);
798 this->list_pos
= id_v
;
800 const NWidgetBase
*checkbox
= this->GetWidget
<NWidgetBase
>(WID_NCL_CHECKBOX
);
801 if (click_count
> 1 || IsInsideBS(pt
.x
, checkbox
->pos_x
, checkbox
->current_x
)) {
802 _network_content_client
.ToggleSelectedState(this->selected
);
803 this->content
.ForceResort();
806 if (this->filter_data
.types
.any()) {
807 this->content
.ForceRebuild();
810 this->InvalidateData();
814 case WID_NCL_CHECKBOX
:
817 if (this->content
.SortType() == widget
- WID_NCL_CHECKBOX
) {
818 this->content
.ToggleSortOrder();
819 if (this->content
.Length() > 0) this->list_pos
= this->content
.Length() - this->list_pos
- 1;
821 this->content
.SetSortType(widget
- WID_NCL_CHECKBOX
);
822 this->content
.ForceResort();
823 this->SortContentList();
825 this->ScrollToSelected();
826 this->InvalidateData();
829 case WID_NCL_SELECT_ALL
:
830 _network_content_client
.SelectAll();
831 this->InvalidateData();
834 case WID_NCL_SELECT_UPDATE
:
835 _network_content_client
.SelectUpgrade();
836 this->InvalidateData();
839 case WID_NCL_UNSELECT
:
840 _network_content_client
.UnselectAll();
841 this->InvalidateData();
848 case WID_NCL_OPEN_URL
:
849 if (this->selected
!= nullptr) {
850 extern void OpenBrowser(const char *url
);
851 OpenBrowser(this->selected
->url
);
855 case WID_NCL_DOWNLOAD
:
856 if (BringWindowToFrontById(WC_NETWORK_STATUS_WINDOW
, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD
) == nullptr) new NetworkContentDownloadStatusWindow();
859 case WID_NCL_SEARCH_EXTERNAL
:
860 if (_accepted_external_search
) {
861 this->OpenExternalSearch();
863 ShowQuery(STR_CONTENT_SEARCH_EXTERNAL_DISCLAIMER_CAPTION
, STR_CONTENT_SEARCH_EXTERNAL_DISCLAIMER
, this, ExternalSearchDisclaimerCallback
);
869 virtual EventState
OnKeyPress(WChar key
, uint16 keycode
)
873 /* scroll up by one */
874 if (this->list_pos
> 0) this->list_pos
--;
877 /* scroll down by one */
878 if (this->list_pos
< (int)this->content
.Length() - 1) this->list_pos
++;
881 /* scroll up a page */
882 this->list_pos
= (this->list_pos
< this->vscroll
->GetCapacity()) ? 0 : this->list_pos
- this->vscroll
->GetCapacity();
885 /* scroll down a page */
886 this->list_pos
= min(this->list_pos
+ this->vscroll
->GetCapacity(), (int)this->content
.Length() - 1);
889 /* jump to beginning */
894 this->list_pos
= this->content
.Length() - 1;
899 if (keycode
== WKC_RETURN
|| !IsWidgetFocused(WID_NCL_FILTER
)) {
900 if (this->selected
!= nullptr) {
901 _network_content_client
.ToggleSelectedState(this->selected
);
902 this->content
.ForceResort();
903 this->InvalidateData();
905 if (this->filter_data
.types
.any()) {
906 this->content
.ForceRebuild();
907 this->InvalidateData();
911 /* space is pressed and filter is focused. */
915 return ES_NOT_HANDLED
;
918 if (this->content
.Length() == 0) {
919 this->list_pos
= 0; // above stuff may result in "-1".
920 if (this->UpdateFilterState()) {
921 this->content
.ForceRebuild();
922 this->InvalidateData();
927 this->selected
= *this->content
.Get(this->list_pos
);
929 if (this->UpdateFilterState()) {
930 this->content
.ForceRebuild();
932 /* Scroll to the new content if it is outside the current range. */
933 this->ScrollToSelected();
937 this->InvalidateData();
941 virtual void OnEditboxChanged(int wid
)
943 if (wid
== WID_NCL_FILTER
) {
944 this->filter_data
.string_filter
.SetFilterTerm(this->filter_editbox
.text
.buf
);
945 this->UpdateFilterState();
946 this->content
.ForceRebuild();
947 this->InvalidateData();
951 virtual void OnResize()
953 this->vscroll
->SetCapacityFromWidget(this, WID_NCL_MATRIX
);
956 virtual void OnReceiveContentInfo(const ContentInfo
*rci
)
958 if (this->auto_select
&& !rci
->IsSelected()) _network_content_client
.ToggleSelectedState(rci
);
959 this->content
.ForceRebuild();
960 this->InvalidateData();
963 virtual void OnDownloadComplete(ContentID cid
)
965 this->content
.ForceResort();
966 this->InvalidateData();
969 virtual void OnConnect(bool success
)
972 ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_CONNECT
, INVALID_STRING_ID
, WL_ERROR
);
977 this->InvalidateData();
981 * Some data on this window has become invalid.
982 * @param data Information about the changed data.
983 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
985 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
987 if (!gui_scope
) return;
988 if (this->content
.NeedRebuild()) this->BuildContentList();
990 /* To sum all the bytes we intend to download */
991 this->filesize_sum
= 0;
992 bool show_select_all
= false;
993 bool show_select_upgrade
= false;
994 for (ConstContentIterator iter
= this->content
.Begin(); iter
!= this->content
.End(); iter
++) {
995 const ContentInfo
*ci
= *iter
;
997 case ContentInfo::SELECTED
:
998 case ContentInfo::AUTOSELECTED
:
999 this->filesize_sum
+= ci
->filesize
;
1002 case ContentInfo::UNSELECTED
:
1003 show_select_all
= true;
1004 show_select_upgrade
|= ci
->upgrade
;
1012 /* If data == 2 then the status window caused this OnInvalidate */
1013 this->SetWidgetDisabledState(WID_NCL_DOWNLOAD
, this->filesize_sum
== 0 || (FindWindowById(WC_NETWORK_STATUS_WINDOW
, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD
) != nullptr && data
!= 2));
1014 this->SetWidgetDisabledState(WID_NCL_UNSELECT
, this->filesize_sum
== 0);
1015 this->SetWidgetDisabledState(WID_NCL_SELECT_ALL
, !show_select_all
);
1016 this->SetWidgetDisabledState(WID_NCL_SELECT_UPDATE
, !show_select_upgrade
);
1017 this->SetWidgetDisabledState(WID_NCL_OPEN_URL
, this->selected
== nullptr || StrEmpty(this->selected
->url
));
1018 for (TextfileType tft
= TFT_BEGIN
; tft
< TFT_END
; tft
++) {
1019 this->SetWidgetDisabledState(WID_NCL_TEXTFILE
+ tft
, this->selected
== nullptr || this->selected
->state
!= ContentInfo::ALREADY_HERE
|| this->selected
->GetTextfile(tft
) == nullptr);
1022 this->GetWidget
<NWidgetCore
>(WID_NCL_CANCEL
)->widget_data
= this->filesize_sum
== 0 ? STR_AI_SETTINGS_CLOSE
: STR_AI_LIST_CANCEL
;
1026 Listing
NetworkContentListWindow::last_sorting
= {false, 1};
1027 Filtering
NetworkContentListWindow::last_filtering
= {false, 0};
1029 NetworkContentListWindow::GUIContentList::SortFunction
* const NetworkContentListWindow::sorter_funcs
[] = {
1035 NetworkContentListWindow::GUIContentList::FilterFunction
* const NetworkContentListWindow::filter_funcs
[] = {
1037 &TypeOrSelectedFilter
,
1040 char NetworkContentListWindow::content_type_strs
[CONTENT_TYPE_END
][64];
1043 * Build array of all strings corresponding to the content types.
1045 void BuildContentTypeStringList()
1047 for (int i
= CONTENT_TYPE_BEGIN
; i
< CONTENT_TYPE_END
; i
++) {
1048 GetString(NetworkContentListWindow::content_type_strs
[i
], STR_CONTENT_TYPE_BASE_GRAPHICS
+ i
- CONTENT_TYPE_BASE_GRAPHICS
, lastof(NetworkContentListWindow::content_type_strs
[i
]));
1052 /** The widgets for the content list. */
1053 static const NWidgetPart _nested_network_content_list_widgets
[] = {
1054 NWidget(NWID_HORIZONTAL
),
1055 NWidget(WWT_CLOSEBOX
, COLOUR_LIGHT_BLUE
),
1056 NWidget(WWT_CAPTION
, COLOUR_LIGHT_BLUE
), SetDataTip(STR_CONTENT_TITLE
, STR_NULL
),
1057 NWidget(WWT_DEFSIZEBOX
, COLOUR_LIGHT_BLUE
),
1059 NWidget(WWT_PANEL
, COLOUR_LIGHT_BLUE
, WID_NCL_BACKGROUND
),
1060 NWidget(NWID_SPACER
), SetMinimalSize(0, 7), SetResize(1, 0),
1061 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(8, 8, 8),
1063 NWidget(WWT_EMPTY
, COLOUR_LIGHT_BLUE
, WID_NCL_FILTER_CAPT
), SetFill(1, 0), SetResize(1, 0),
1064 NWidget(WWT_EDITBOX
, COLOUR_LIGHT_BLUE
, WID_NCL_FILTER
), SetFill(1, 0), SetResize(1, 0),
1065 SetDataTip(STR_LIST_FILTER_OSKTITLE
, STR_LIST_FILTER_TOOLTIP
),
1067 NWidget(NWID_SPACER
), SetMinimalSize(0, 7), SetResize(1, 0),
1068 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(8, 8, 8),
1070 NWidget(NWID_VERTICAL
), SetPIP(0, 4, 0),
1071 NWidget(NWID_HORIZONTAL
),
1072 NWidget(NWID_VERTICAL
),
1073 NWidget(NWID_HORIZONTAL
),
1074 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_CHECKBOX
), SetMinimalSize(13, 1), SetDataTip(STR_EMPTY
, STR_NULL
),
1075 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_TYPE
),
1076 SetDataTip(STR_CONTENT_TYPE_CAPTION
, STR_CONTENT_TYPE_CAPTION_TOOLTIP
),
1077 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_NAME
), SetResize(1, 0), SetFill(1, 0),
1078 SetDataTip(STR_CONTENT_NAME_CAPTION
, STR_CONTENT_NAME_CAPTION_TOOLTIP
),
1080 NWidget(WWT_MATRIX
, COLOUR_LIGHT_BLUE
, WID_NCL_MATRIX
), SetResize(1, 14), SetFill(1, 1), SetScrollbar(WID_NCL_SCROLLBAR
), SetMatrixDataTip(1, 0, STR_CONTENT_MATRIX_TOOLTIP
),
1082 NWidget(NWID_VSCROLLBAR
, COLOUR_LIGHT_BLUE
, WID_NCL_SCROLLBAR
),
1084 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(0, 8, 0),
1085 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_NCL_SEL_ALL_UPDATE
), SetResize(1, 0), SetFill(1, 0),
1086 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_SELECT_UPDATE
), SetResize(1, 0), SetFill(1, 0),
1087 SetDataTip(STR_CONTENT_SELECT_UPDATES_CAPTION
, STR_CONTENT_SELECT_UPDATES_CAPTION_TOOLTIP
),
1088 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_SELECT_ALL
), SetResize(1, 0), SetFill(1, 0),
1089 SetDataTip(STR_CONTENT_SELECT_ALL_CAPTION
, STR_CONTENT_SELECT_ALL_CAPTION_TOOLTIP
),
1091 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_UNSELECT
), SetResize(1, 0), SetFill(1, 0),
1092 SetDataTip(STR_CONTENT_UNSELECT_ALL_CAPTION
, STR_CONTENT_UNSELECT_ALL_CAPTION_TOOLTIP
),
1096 NWidget(NWID_VERTICAL
), SetPIP(0, 4, 0),
1097 NWidget(WWT_PANEL
, COLOUR_LIGHT_BLUE
, WID_NCL_DETAILS
), SetResize(1, 1), SetFill(1, 1), EndContainer(),
1098 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(0, 8, 0),
1099 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_TEXTFILE
+ TFT_README
), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_README
, STR_NULL
),
1100 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_TEXTFILE
+ TFT_CHANGELOG
), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_CHANGELOG
, STR_NULL
),
1102 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(0, 8, 0),
1103 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_OPEN_URL
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_CONTENT_OPEN_URL
, STR_CONTENT_OPEN_URL_TOOLTIP
),
1104 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_TEXTFILE
+ TFT_LICENSE
), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_LICENCE
, STR_NULL
),
1108 NWidget(NWID_SPACER
), SetMinimalSize(0, 7), SetResize(1, 0),
1110 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(8, 8, 8),
1111 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_SEARCH_EXTERNAL
), SetResize(1, 0), SetFill(1, 0),
1112 SetDataTip(STR_CONTENT_SEARCH_EXTERNAL
, STR_CONTENT_SEARCH_EXTERNAL_TOOLTIP
),
1113 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(0, 8, 0),
1114 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_CANCEL
), SetResize(1, 0), SetFill(1, 0),
1115 SetDataTip(STR_BUTTON_CANCEL
, STR_NULL
),
1116 NWidget(WWT_PUSHTXTBTN
, COLOUR_WHITE
, WID_NCL_DOWNLOAD
), SetResize(1, 0), SetFill(1, 0),
1117 SetDataTip(STR_CONTENT_DOWNLOAD_CAPTION
, STR_CONTENT_DOWNLOAD_CAPTION_TOOLTIP
),
1120 NWidget(NWID_SPACER
), SetMinimalSize(0, 2), SetResize(1, 0),
1121 /* Resize button. */
1122 NWidget(NWID_HORIZONTAL
),
1123 NWidget(NWID_SPACER
), SetFill(1, 0), SetResize(1, 0),
1124 NWidget(WWT_RESIZEBOX
, COLOUR_LIGHT_BLUE
),
1129 /** Window description of the content list */
1130 static WindowDesc
_network_content_list_desc(
1131 WDP_CENTER
, "list_content", 630, 460,
1132 WC_NETWORK_WINDOW
, WC_NONE
,
1134 _nested_network_content_list_widgets
, lengthof(_nested_network_content_list_widgets
)
1138 * Show the content list window with a given set of content
1139 * @param cv the content to show, or nullptr when it has to search for itself
1140 * @param type1 the first type to (only) show or #CONTENT_TYPE_END to show all.
1141 * @param type2 the second type to (only) show in addition to type1. If type2 is != #CONTENT_TYPE_END, then also type1 should be != #CONTENT_TYPE_END.
1142 * If type2 != #CONTENT_TYPE_END, then type1 != type2 must be true.
1144 void ShowNetworkContentListWindow(ContentVector
*cv
, ContentType type1
, ContentType type2
)
1146 #if defined(WITH_ZLIB)
1147 std::bitset
<CONTENT_TYPE_END
> types
;
1148 _network_content_client
.Clear();
1149 if (cv
== nullptr) {
1150 assert(type1
!= CONTENT_TYPE_END
|| type2
== CONTENT_TYPE_END
);
1151 assert(type1
== CONTENT_TYPE_END
|| type1
!= type2
);
1152 _network_content_client
.RequestContentList(type1
);
1153 if (type2
!= CONTENT_TYPE_END
) _network_content_client
.RequestContentList(type2
);
1155 if (type1
!= CONTENT_TYPE_END
) types
[type1
] = true;
1156 if (type2
!= CONTENT_TYPE_END
) types
[type2
] = true;
1158 _network_content_client
.RequestContentList(cv
, true);
1161 DeleteWindowById(WC_NETWORK_WINDOW
, WN_NETWORK_WINDOW_CONTENT_LIST
);
1162 new NetworkContentListWindow(&_network_content_list_desc
, cv
!= nullptr, types
);
1164 ShowErrorMessage(STR_CONTENT_NO_ZLIB
, STR_CONTENT_NO_ZLIB_SUB
, WL_ERROR
);
1165 /* Connection failed... clean up the mess */
1166 if (cv
!= nullptr) {
1167 for (ContentIterator iter
= cv
->Begin(); iter
!= cv
->End(); iter
++) delete *iter
;
1169 #endif /* WITH_ZLIB */
1172 #endif /* ENABLE_NETWORK */