# network_content_gui.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #if defined(ENABLE_NETWORK)
    00013 #include "../stdafx.h"
    00014 #include "../strings_func.h"
    00015 #include "../gfx_func.h"
    00016 #include "../window_func.h"
    00017 #include "../error.h"
    00018 #include "../ai/ai.hpp"
    00019 #include "../game/game.hpp"
    00020 #include "../base_media_base.h"
    00021 #include "../sortlist_type.h"
    00022 #include "../stringfilter_type.h"
    00023 #include "../querystring_gui.h"
    00024 #include "../core/geometry_func.hpp"
    00025 #include "../textfile_gui.h"
    00026 #include "network_content_gui.h"
    00027
    00028
    00029 #include "table/strings.h"
    00030 #include "../table/sprites.h"
    00031
    00033 struct ContentTextfileWindow : public TextfileWindow {
    00034   const ContentInfo *ci;
    00035
    00036   ContentTextfileWindow(TextfileType file_type, const ContentInfo *ci) : TextfileWindow(file_type), ci(ci)
    00037   {
    00038     const char *textfile = this->ci->GetTextfile(file_type);
    00039     this->LoadTextfile(textfile, GetContentInfoSubDir(this->ci->type));
    00040   }
    00041
    00042   StringID GetTypeString() const
    00043   {
    00044     switch (this->ci->type) {
    00045       case CONTENT_TYPE_NEWGRF:        return STR_CONTENT_TYPE_NEWGRF;
    00046       case CONTENT_TYPE_BASE_GRAPHICS: return STR_CONTENT_TYPE_BASE_GRAPHICS;
    00047       case CONTENT_TYPE_BASE_SOUNDS:   return STR_CONTENT_TYPE_BASE_SOUNDS;
    00048       case CONTENT_TYPE_BASE_MUSIC:    return STR_CONTENT_TYPE_BASE_MUSIC;
    00049       case CONTENT_TYPE_AI:            return STR_CONTENT_TYPE_AI;
    00050       case CONTENT_TYPE_AI_LIBRARY:    return STR_CONTENT_TYPE_AI_LIBRARY;
    00051       case CONTENT_TYPE_GAME:          return STR_CONTENT_TYPE_GAME_SCRIPT;
    00052       case CONTENT_TYPE_GAME_LIBRARY:  return STR_CONTENT_TYPE_GS_LIBRARY;
    00053       case CONTENT_TYPE_SCENARIO:      return STR_CONTENT_TYPE_SCENARIO;
    00054       case CONTENT_TYPE_HEIGHTMAP:     return STR_CONTENT_TYPE_HEIGHTMAP;
    00055       default: NOT_REACHED();
    00056     }
    00057   }
    00058
    00059   /* virtual */ void SetStringParameters(int widget) const
    00060   {
    00061     if (widget == WID_TF_CAPTION) {
    00062       SetDParam(0, this->GetTypeString());
    00063       SetDParamStr(1, this->ci->name);
    00064     }
    00065   }
    00066 };
    00067
    00068 void ShowContentTextfileWindow(TextfileType file_type, const ContentInfo *ci)
    00069 {
    00070   DeleteWindowByClass(WC_TEXTFILE);
    00071   new ContentTextfileWindow(file_type, ci);
    00072 }
    00073
    00075 static const NWidgetPart _nested_network_content_download_status_window_widgets[] = {
    00076   NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_CONTENT_DOWNLOAD_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00077   NWidget(WWT_PANEL, COLOUR_GREY, WID_NCDS_BACKGROUND),
    00078     NWidget(NWID_SPACER), SetMinimalSize(350, 0), SetMinimalTextLines(3, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 30),
    00079     NWidget(NWID_HORIZONTAL),
    00080       NWidget(NWID_SPACER), SetMinimalSize(125, 0),
    00081       NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCDS_CANCELOK), SetMinimalSize(101, 12), SetDataTip(STR_BUTTON_CANCEL, STR_NULL),
    00082       NWidget(NWID_SPACER), SetFill(1, 0),
    00083     EndContainer(),
    00084     NWidget(NWID_SPACER), SetMinimalSize(0, 4),
    00085   EndContainer(),
    00086 };
    00087
    00089 static const WindowDesc _network_content_download_status_window_desc(
    00090   WDP_CENTER, 0, 0,
    00091   WC_NETWORK_STATUS_WINDOW, WC_NONE,
    00092   WDF_MODAL,
    00093   _nested_network_content_download_status_window_widgets, lengthof(_nested_network_content_download_status_window_widgets)
    00094 );
    00095
    00096 BaseNetworkContentDownloadStatusWindow::BaseNetworkContentDownloadStatusWindow(const WindowDesc *desc) :
    00097     cur_id(UINT32_MAX)
    00098 {
    00099   _network_content_client.AddCallback(this);
    00100   _network_content_client.DownloadSelectedContent(this->total_files, this->total_bytes);
    00101
    00102   this->InitNested(desc, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD);
    00103 }
    00104
    00105 BaseNetworkContentDownloadStatusWindow::~BaseNetworkContentDownloadStatusWindow()
    00106 {
    00107   _network_content_client.RemoveCallback(this);
    00108 }
    00109
    00110 /* virtual */ void BaseNetworkContentDownloadStatusWindow::DrawWidget(const Rect &r, int widget) const
    00111 {
    00112   if (widget != WID_NCDS_BACKGROUND) return;
    00113
    00114   /* Draw nice progress bar :) */
    00115   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);
    00116
    00117   int y = r.top + 20;
    00118   SetDParam(0, this->downloaded_bytes);
    00119   SetDParam(1, this->total_bytes);
    00120   SetDParam(2, this->downloaded_bytes * 100LL / this->total_bytes);
    00121   DrawString(r.left + 2, r.right - 2, y, STR_CONTENT_DOWNLOAD_PROGRESS_SIZE, TC_FROMSTRING, SA_HOR_CENTER);
    00122
    00123   StringID str;
    00124   if (this->downloaded_bytes == this->total_bytes) {
    00125     str = STR_CONTENT_DOWNLOAD_COMPLETE;
    00126   } else if (!StrEmpty(this->name)) {
    00127     SetDParamStr(0, this->name);
    00128     SetDParam(1, this->downloaded_files);
    00129     SetDParam(2, this->total_files);
    00130     str = STR_CONTENT_DOWNLOAD_FILE;
    00131   } else {
    00132     str = STR_CONTENT_DOWNLOAD_INITIALISE;
    00133   }
    00134
    00135   y += FONT_HEIGHT_NORMAL + 5;
    00136   DrawStringMultiLine(r.left + 2, r.right - 2, y, y + FONT_HEIGHT_NORMAL * 2, str, TC_FROMSTRING, SA_CENTER);
    00137 }
    00138
    00139 /* virtual */ void BaseNetworkContentDownloadStatusWindow::OnDownloadProgress(const ContentInfo *ci, int bytes)
    00140 {
    00141   if (ci->id != this->cur_id) {
    00142     strecpy(this->name, ci->filename, lastof(this->name));
    00143     this->cur_id = ci->id;
    00144     this->downloaded_files++;
    00145   }
    00146
    00147   this->downloaded_bytes += bytes;
    00148   this->SetDirty();
    00149 }
    00150
    00151
    00153 struct NetworkContentDownloadStatusWindow : public BaseNetworkContentDownloadStatusWindow {
    00154 private:
    00155   SmallVector<ContentType, 4> receivedTypes;
    00156
    00157 public:
    00162   NetworkContentDownloadStatusWindow() : BaseNetworkContentDownloadStatusWindow(&_network_content_download_status_window_desc)
    00163   {
    00164     this->parent = FindWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_CONTENT_LIST);
    00165   }
    00166
    00168   ~NetworkContentDownloadStatusWindow()
    00169   {
    00170     TarScanner::Mode mode = TarScanner::NONE;
    00171     for (ContentType *iter = this->receivedTypes.Begin(); iter != this->receivedTypes.End(); iter++) {
    00172       switch (*iter) {
    00173         case CONTENT_TYPE_AI:
    00174         case CONTENT_TYPE_AI_LIBRARY:
    00175           /* AI::Rescan calls the scanner. */
    00176           break;
    00177         case CONTENT_TYPE_GAME:
    00178         case CONTENT_TYPE_GAME_LIBRARY:
    00179           /* Game::Rescan calls the scanner. */
    00180           break;
    00181
    00182         case CONTENT_TYPE_BASE_GRAPHICS:
    00183         case CONTENT_TYPE_BASE_SOUNDS:
    00184         case CONTENT_TYPE_BASE_MUSIC:
    00185           mode |= TarScanner::BASESET;
    00186           break;
    00187
    00188         case CONTENT_TYPE_NEWGRF:
    00189           /* ScanNewGRFFiles calls the scanner. */
    00190           break;
    00191
    00192         case CONTENT_TYPE_SCENARIO:
    00193         case CONTENT_TYPE_HEIGHTMAP:
    00194           mode |= TarScanner::SCENARIO;
    00195           break;
    00196
    00197         default:
    00198           break;
    00199       }
    00200     }
    00201
    00202     TarScanner::DoScan(mode);
    00203
    00204     /* Tell all the backends about what we've downloaded */
    00205     for (ContentType *iter = this->receivedTypes.Begin(); iter != this->receivedTypes.End(); iter++) {
    00206       switch (*iter) {
    00207         case CONTENT_TYPE_AI:
    00208         case CONTENT_TYPE_AI_LIBRARY:
    00209           AI::Rescan();
    00210           break;
    00211
    00212         case CONTENT_TYPE_GAME:
    00213         case CONTENT_TYPE_GAME_LIBRARY:
    00214           Game::Rescan();
    00215           break;
    00216
    00217         case CONTENT_TYPE_BASE_GRAPHICS:
    00218           BaseGraphics::FindSets();
    00219           SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS);
    00220           break;
    00221
    00222         case CONTENT_TYPE_BASE_SOUNDS:
    00223           BaseSounds::FindSets();
    00224           SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS);
    00225           break;
    00226
    00227         case CONTENT_TYPE_BASE_MUSIC:
    00228           BaseMusic::FindSets();
    00229           SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS);
    00230           break;
    00231
    00232         case CONTENT_TYPE_NEWGRF:
    00233           ScanNewGRFFiles(NULL);
    00234           break;
    00235
    00236         case CONTENT_TYPE_SCENARIO:
    00237         case CONTENT_TYPE_HEIGHTMAP:
    00238           extern void ScanScenarios();
    00239           ScanScenarios();
    00240           InvalidateWindowData(WC_SAVELOAD, 0, 0);
    00241           break;
    00242
    00243         default:
    00244           break;
    00245       }
    00246     }
    00247
    00248     /* Always invalidate the download window; tell it we are going to be gone */
    00249     InvalidateWindowData(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_CONTENT_LIST, 2);
    00250   }
    00251
    00252   virtual void OnClick(Point pt, int widget, int click_count)
    00253   {
    00254     if (widget == WID_NCDS_CANCELOK) {
    00255       if (this->downloaded_bytes != this->total_bytes) {
    00256         _network_content_client.Close();
    00257         delete this;
    00258       } else {
    00259         /* If downloading succeeded, close the online content window. This will close
    00260          * the current window as well. */
    00261         DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_CONTENT_LIST);
    00262       }
    00263     }
    00264   }
    00265
    00266   virtual void OnDownloadProgress(const ContentInfo *ci, int bytes)
    00267   {
    00268     BaseNetworkContentDownloadStatusWindow::OnDownloadProgress(ci, bytes);
    00269     this->receivedTypes.Include(ci->type);
    00270
    00271     /* When downloading is finished change cancel in ok */
    00272     if (this->downloaded_bytes == this->total_bytes) {
    00273       this->GetWidget<NWidgetCore>(WID_NCDS_CANCELOK)->widget_data = STR_BUTTON_OK;
    00274     }
    00275   }
    00276 };
    00277
    00279 class NetworkContentListWindow : public QueryStringBaseWindow, ContentCallback {
    00281   typedef GUIList<const ContentInfo *, StringFilter &> GUIContentList;
    00282
    00283   static const uint EDITBOX_MAX_SIZE   =  50;
    00284   static const uint EDITBOX_MAX_LENGTH = 300;
    00285
    00286   static Listing last_sorting;
    00287   static Filtering last_filtering;
    00288   static GUIContentList::SortFunction * const sorter_funcs[];
    00289   static GUIContentList::FilterFunction * const filter_funcs[];
    00290   GUIContentList content;
    00291   bool auto_select;
    00292   StringFilter string_filter;
    00293
    00294   const ContentInfo *selected;
    00295   int list_pos;
    00296   uint filesize_sum;
    00297   Scrollbar *vscroll;
    00298
    00303   void BuildContentList()
    00304   {
    00305     if (!this->content.NeedRebuild()) return;
    00306
    00307     /* Create temporary array of games to use for listing */
    00308     this->content.Clear();
    00309
    00310     for (ConstContentIterator iter = _network_content_client.Begin(); iter != _network_content_client.End(); iter++) {
    00311       *this->content.Append() = *iter;
    00312     }
    00313
    00314     this->FilterContentList();
    00315     this->content.Compact();
    00316     this->content.RebuildDone();
    00317     this->SortContentList();
    00318
    00319     this->vscroll->SetCount(this->content.Length()); // Update the scrollbar
    00320     this->ScrollToSelected();
    00321   }
    00322
    00324   static int CDECL NameSorter(const ContentInfo * const *a, const ContentInfo * const *b)
    00325   {
    00326     return strnatcmp((*a)->name, (*b)->name); // Sort by name (natural sorting).
    00327   }
    00328
    00330   static int CDECL TypeSorter(const ContentInfo * const *a, const ContentInfo * const *b)
    00331   {
    00332     int r = 0;
    00333     if ((*a)->type != (*b)->type) {
    00334       char a_str[64];
    00335       char b_str[64];
    00336       GetString(a_str, STR_CONTENT_TYPE_BASE_GRAPHICS + (*a)->type - CONTENT_TYPE_BASE_GRAPHICS, lastof(a_str));
    00337       GetString(b_str, STR_CONTENT_TYPE_BASE_GRAPHICS + (*b)->type - CONTENT_TYPE_BASE_GRAPHICS, lastof(b_str));
    00338       r = strnatcmp(a_str, b_str);
    00339     }
    00340     if (r == 0) r = NameSorter(a, b);
    00341     return r;
    00342   }
    00343
    00345   static int CDECL StateSorter(const ContentInfo * const *a, const ContentInfo * const *b)
    00346   {
    00347     int r = (*a)->state - (*b)->state;
    00348     if (r == 0) r = TypeSorter(a, b);
    00349     return r;
    00350   }
    00351
    00353   void SortContentList()
    00354   {
    00355     if (!this->content.Sort()) return;
    00356
    00357     for (ConstContentIterator iter = this->content.Begin(); iter != this->content.End(); iter++) {
    00358       if (*iter == this->selected) {
    00359         this->list_pos = iter - this->content.Begin();
    00360         break;
    00361       }
    00362     }
    00363   }
    00364
    00366   static bool CDECL TagNameFilter(const ContentInfo * const *a, StringFilter &filter)
    00367   {
    00368     filter.ResetState();
    00369     for (int i = 0; i < (*a)->tag_count; i++) {
    00370       filter.AddLine((*a)->tags[i]);
    00371     }
    00372     filter.AddLine((*a)->name);
    00373     return filter.GetState();
    00374   }
    00375
    00377   void FilterContentList()
    00378   {
    00379     if (!this->content.Filter(this->string_filter)) return;
    00380
    00381     /* update list position */
    00382     for (ConstContentIterator iter = this->content.Begin(); iter != this->content.End(); iter++) {
    00383       if (*iter == this->selected) {
    00384         this->list_pos = iter - this->content.Begin();
    00385         return;
    00386       }
    00387     }
    00388
    00389     /* previously selected item not in list anymore */
    00390     this->selected = NULL;
    00391     this->list_pos = 0;
    00392   }
    00393
    00395   void ScrollToSelected()
    00396   {
    00397     if (this->selected == NULL) return;
    00398
    00399     this->vscroll->ScrollTowards(this->list_pos);
    00400   }
    00401
    00402 public:
    00408   NetworkContentListWindow(const WindowDesc *desc, bool select_all) :
    00409       QueryStringBaseWindow(EDITBOX_MAX_SIZE),
    00410       auto_select(select_all),
    00411       selected(NULL),
    00412       list_pos(0)
    00413   {
    00414     this->CreateNestedTree(desc);
    00415     this->vscroll = this->GetScrollbar(WID_NCL_SCROLLBAR);
    00416     this->FinishInitNested(desc, WN_NETWORK_WINDOW_CONTENT_LIST);
    00417
    00418     this->GetWidget<NWidgetStacked>(WID_NCL_SEL_ALL_UPDATE)->SetDisplayedPlane(select_all);
    00419
    00420     this->afilter = CS_ALPHANUMERAL;
    00421     this->text.Initialize(this->edit_str_buf, this->edit_str_size, EDITBOX_MAX_LENGTH);
    00422     this->SetFocusedWidget(WID_NCL_FILTER);
    00423
    00424     _network_content_client.AddCallback(this);
    00425     this->content.SetListing(this->last_sorting);
    00426     this->content.SetFiltering(this->last_filtering);
    00427     this->content.SetSortFuncs(this->sorter_funcs);
    00428     this->content.SetFilterFuncs(this->filter_funcs);
    00429     this->content.ForceRebuild();
    00430     this->FilterContentList();
    00431     this->SortContentList();
    00432     this->InvalidateData();
    00433   }
    00434
    00436   ~NetworkContentListWindow()
    00437   {
    00438     _network_content_client.RemoveCallback(this);
    00439   }
    00440
    00441   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00442   {
    00443     switch (widget) {
    00444       case WID_NCL_FILTER_CAPT:
    00445         *size = maxdim(*size, GetStringBoundingBox(STR_CONTENT_FILTER_TITLE));
    00446         break;
    00447
    00448       case WID_NCL_TYPE: {
    00449         Dimension d = *size;
    00450         for (int i = CONTENT_TYPE_BEGIN; i < CONTENT_TYPE_END; i++) {
    00451           d = maxdim(d, GetStringBoundingBox(STR_CONTENT_TYPE_BASE_GRAPHICS + i - CONTENT_TYPE_BASE_GRAPHICS));
    00452         }
    00453         size->width = d.width + WD_MATRIX_RIGHT + WD_MATRIX_LEFT;
    00454         break;
    00455       }
    00456
    00457       case WID_NCL_MATRIX:
    00458         resize->height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
    00459         size->height = 10 * resize->height;
    00460         break;
    00461     }
    00462   }
    00463
    00464
    00465   virtual void DrawWidget(const Rect &r, int widget) const
    00466   {
    00467     switch (widget) {
    00468       case WID_NCL_FILTER_CAPT:
    00469         DrawString(r.left, r.right, r.top, STR_CONTENT_FILTER_TITLE, TC_FROMSTRING, SA_RIGHT);
    00470         break;
    00471
    00472       case WID_NCL_DETAILS:
    00473         this->DrawDetails(r);
    00474         break;
    00475
    00476       case WID_NCL_MATRIX:
    00477         this->DrawMatrix(r);
    00478         break;
    00479     }
    00480   }
    00481
    00482   virtual void OnPaint()
    00483   {
    00484     const SortButtonState arrow = this->content.IsDescSortOrder() ? SBS_DOWN : SBS_UP;
    00485
    00486     if (this->content.NeedRebuild()) {
    00487       this->BuildContentList();
    00488     }
    00489
    00490     this->DrawWidgets();
    00491
    00492     /* Edit box to filter for keywords */
    00493     this->DrawEditBox(WID_NCL_FILTER);
    00494
    00495     switch (this->content.SortType()) {
    00496       case WID_NCL_CHECKBOX - WID_NCL_CHECKBOX: this->DrawSortButtonState(WID_NCL_CHECKBOX, arrow); break;
    00497       case WID_NCL_TYPE     - WID_NCL_CHECKBOX: this->DrawSortButtonState(WID_NCL_TYPE,     arrow); break;
    00498       case WID_NCL_NAME     - WID_NCL_CHECKBOX: this->DrawSortButtonState(WID_NCL_NAME,     arrow); break;
    00499     }
    00500   }
    00501
    00506   void DrawMatrix(const Rect &r) const
    00507   {
    00508     const NWidgetBase *nwi_checkbox = this->GetWidget<NWidgetBase>(WID_NCL_CHECKBOX);
    00509     const NWidgetBase *nwi_name = this->GetWidget<NWidgetBase>(WID_NCL_NAME);
    00510     const NWidgetBase *nwi_type = this->GetWidget<NWidgetBase>(WID_NCL_TYPE);
    00511
    00512
    00513     /* Fill the matrix with the information */
    00514     int sprite_y_offset = WD_MATRIX_TOP + (FONT_HEIGHT_NORMAL - 10) / 2;
    00515     uint y = r.top;
    00516     int cnt = 0;
    00517     for (ConstContentIterator iter = this->content.Get(this->vscroll->GetPosition()); iter != this->content.End() && cnt < this->vscroll->GetCapacity(); iter++, cnt++) {
    00518       const ContentInfo *ci = *iter;
    00519
    00520       if (ci == this->selected) GfxFillRect(r.left + 1, y + 1, r.right - 1, y + this->resize.step_height - 1, PC_GREY);
    00521
    00522       SpriteID sprite;
    00523       SpriteID pal = PAL_NONE;
    00524       switch (ci->state) {
    00525         case ContentInfo::UNSELECTED:     sprite = SPR_BOX_EMPTY;   break;
    00526         case ContentInfo::SELECTED:       sprite = SPR_BOX_CHECKED; break;
    00527         case ContentInfo::AUTOSELECTED:   sprite = SPR_BOX_CHECKED; break;
    00528         case ContentInfo::ALREADY_HERE:   sprite = SPR_BLOT; pal = PALETTE_TO_GREEN; break;
    00529         case ContentInfo::DOES_NOT_EXIST: sprite = SPR_BLOT; pal = PALETTE_TO_RED;   break;
    00530         default: NOT_REACHED();
    00531       }
    00532       DrawSprite(sprite, pal, nwi_checkbox->pos_x + (pal == PAL_NONE ? 2 : 3), y + sprite_y_offset + (pal == PAL_NONE ? 1 : 0));
    00533
    00534       StringID str = STR_CONTENT_TYPE_BASE_GRAPHICS + ci->type - CONTENT_TYPE_BASE_GRAPHICS;
    00535       DrawString(nwi_type->pos_x, nwi_type->pos_x + nwi_type->current_x - 1, y + WD_MATRIX_TOP, str, TC_BLACK, SA_HOR_CENTER);
    00536
    00537       DrawString(nwi_name->pos_x + WD_FRAMERECT_LEFT, nwi_name->pos_x + nwi_name->current_x - WD_FRAMERECT_RIGHT, y + WD_MATRIX_TOP, ci->name, TC_BLACK);
    00538       y += this->resize.step_height;
    00539     }
    00540   }
    00541
    00546   void DrawDetails(const Rect &r) const
    00547   {
    00548     static const int DETAIL_LEFT         =  5;
    00549     static const int DETAIL_RIGHT        =  5;
    00550     static const int DETAIL_TOP          =  5;
    00551
    00552     /* Height for the title banner */
    00553     int DETAIL_TITLE_HEIGHT = 5 * FONT_HEIGHT_NORMAL;
    00554
    00555     /* Create the nice grayish rectangle at the details top */
    00556     GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.top + DETAIL_TITLE_HEIGHT, PC_DARK_BLUE);
    00557     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);
    00558
    00559     /* Draw the total download size */
    00560     SetDParam(0, this->filesize_sum);
    00561     DrawString(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, r.bottom - FONT_HEIGHT_NORMAL - WD_PAR_VSEP_NORMAL, STR_CONTENT_TOTAL_DOWNLOAD_SIZE);
    00562
    00563     if (this->selected == NULL) return;
    00564
    00565     /* And fill the rest of the details when there's information to place there */
    00566     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);
    00567
    00568     /* Also show the total download size, so keep some space from the bottom */
    00569     const uint max_y = r.bottom - FONT_HEIGHT_NORMAL - WD_PAR_VSEP_WIDE;
    00570     int y = r.top + DETAIL_TITLE_HEIGHT + DETAIL_TOP;
    00571
    00572     if (this->selected->upgrade) {
    00573       SetDParam(0, STR_CONTENT_TYPE_BASE_GRAPHICS + this->selected->type - CONTENT_TYPE_BASE_GRAPHICS);
    00574       y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_UPDATE);
    00575       y += WD_PAR_VSEP_WIDE;
    00576     }
    00577
    00578     SetDParamStr(0, this->selected->name);
    00579     y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_NAME);
    00580
    00581     if (!StrEmpty(this->selected->version)) {
    00582       SetDParamStr(0, this->selected->version);
    00583       y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_VERSION);
    00584     }
    00585
    00586     if (!StrEmpty(this->selected->description)) {
    00587       SetDParamStr(0, this->selected->description);
    00588       y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_DESCRIPTION);
    00589     }
    00590
    00591     if (!StrEmpty(this->selected->url)) {
    00592       SetDParamStr(0, this->selected->url);
    00593       y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_URL);
    00594     }
    00595
    00596     SetDParam(0, STR_CONTENT_TYPE_BASE_GRAPHICS + this->selected->type - CONTENT_TYPE_BASE_GRAPHICS);
    00597     y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_TYPE);
    00598
    00599     y += WD_PAR_VSEP_WIDE;
    00600     SetDParam(0, this->selected->filesize);
    00601     y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_FILESIZE);
    00602
    00603     if (this->selected->dependency_count != 0) {
    00604       /* List dependencies */
    00605       char buf[DRAW_STRING_BUFFER] = "";
    00606       char *p = buf;
    00607       for (uint i = 0; i < this->selected->dependency_count; i++) {
    00608         ContentID cid = this->selected->dependencies[i];
    00609
    00610         /* Try to find the dependency */
    00611         ConstContentIterator iter = _network_content_client.Begin();
    00612         for (; iter != _network_content_client.End(); iter++) {
    00613           const ContentInfo *ci = *iter;
    00614           if (ci->id != cid) continue;
    00615
    00616           p += seprintf(p, lastof(buf), p == buf ? "%s" : ", %s", (*iter)->name);
    00617           break;
    00618         }
    00619       }
    00620       SetDParamStr(0, buf);
    00621       y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_DEPENDENCIES);
    00622     }
    00623
    00624     if (this->selected->tag_count != 0) {
    00625       /* List all tags */
    00626       char buf[DRAW_STRING_BUFFER] = "";
    00627       char *p = buf;
    00628       for (uint i = 0; i < this->selected->tag_count; i++) {
    00629         p += seprintf(p, lastof(buf), i == 0 ? "%s" : ", %s", this->selected->tags[i]);
    00630       }
    00631       SetDParamStr(0, buf);
    00632       y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_TAGS);
    00633     }
    00634
    00635     if (this->selected->IsSelected()) {
    00636       /* When selected show all manually selected content that depends on this */
    00637       ConstContentVector tree;
    00638       _network_content_client.ReverseLookupTreeDependency(tree, this->selected);
    00639
    00640       char buf[DRAW_STRING_BUFFER] = "";
    00641       char *p = buf;
    00642       for (ConstContentIterator iter = tree.Begin(); iter != tree.End(); iter++) {
    00643         const ContentInfo *ci = *iter;
    00644         if (ci == this->selected || ci->state != ContentInfo::SELECTED) continue;
    00645
    00646         p += seprintf(p, lastof(buf), buf == p ? "%s" : ", %s", ci->name);
    00647       }
    00648       if (p != buf) {
    00649         SetDParamStr(0, buf);
    00650         y = DrawStringMultiLine(r.left + DETAIL_LEFT, r.right - DETAIL_RIGHT, y, max_y, STR_CONTENT_DETAIL_SELECTED_BECAUSE_OF);
    00651       }
    00652     }
    00653   }
    00654
    00655   virtual void OnClick(Point pt, int widget, int click_count)
    00656   {
    00657     if (widget >= WID_NCL_TEXTFILE && widget < WID_NCL_TEXTFILE + TFT_END) {
    00658       if (this->selected == NULL || this->selected->state != ContentInfo::ALREADY_HERE) return;
    00659
    00660       ShowContentTextfileWindow((TextfileType)(widget - WID_NCL_TEXTFILE), this->selected);
    00661       return;
    00662     }
    00663
    00664     switch (widget) {
    00665       case WID_NCL_MATRIX: {
    00666         uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NCL_MATRIX);
    00667         if (id_v >= this->content.Length()) return; // click out of bounds
    00668
    00669         this->selected = *this->content.Get(id_v);
    00670         this->list_pos = id_v;
    00671
    00672         const NWidgetBase *checkbox = this->GetWidget<NWidgetBase>(WID_NCL_CHECKBOX);
    00673         if (click_count > 1 || IsInsideBS(pt.x, checkbox->pos_x, checkbox->current_x)) {
    00674           _network_content_client.ToggleSelectedState(this->selected);
    00675           this->content.ForceResort();
    00676         }
    00677
    00678         this->InvalidateData();
    00679         break;
    00680       }
    00681
    00682       case WID_NCL_CHECKBOX:
    00683       case WID_NCL_TYPE:
    00684       case WID_NCL_NAME:
    00685         if (this->content.SortType() == widget - WID_NCL_CHECKBOX) {
    00686           this->content.ToggleSortOrder();
    00687           this->list_pos = this->content.Length() - this->list_pos - 1;
    00688         } else {
    00689           this->content.SetSortType(widget - WID_NCL_CHECKBOX);
    00690           this->content.ForceResort();
    00691           this->SortContentList();
    00692         }
    00693         this->ScrollToSelected();
    00694         this->InvalidateData();
    00695         break;
    00696
    00697       case WID_NCL_SELECT_ALL:
    00698         _network_content_client.SelectAll();
    00699         this->InvalidateData();
    00700         break;
    00701
    00702       case WID_NCL_SELECT_UPDATE:
    00703         _network_content_client.SelectUpgrade();
    00704         this->InvalidateData();
    00705         break;
    00706
    00707       case WID_NCL_UNSELECT:
    00708         _network_content_client.UnselectAll();
    00709         this->InvalidateData();
    00710         break;
    00711
    00712       case WID_NCL_CANCEL:
    00713         delete this;
    00714         break;
    00715
    00716       case WID_NCL_OPEN_URL:
    00717         if (this->selected != NULL) {
    00718           extern void OpenBrowser(const char *url);
    00719           OpenBrowser(this->selected->url);
    00720         }
    00721         break;
    00722
    00723       case WID_NCL_DOWNLOAD:
    00724         if (BringWindowToFrontById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD) == NULL) new NetworkContentDownloadStatusWindow();
    00725         break;
    00726     }
    00727   }
    00728
    00729   virtual void OnMouseLoop()
    00730   {
    00731     this->HandleEditBox(WID_NCL_FILTER);
    00732   }
    00733
    00734   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    00735   {
    00736     switch (keycode) {
    00737       case WKC_UP:
    00738         /* scroll up by one */
    00739         if (this->list_pos > 0) this->list_pos--;
    00740         break;
    00741       case WKC_DOWN:
    00742         /* scroll down by one */
    00743         if (this->list_pos < (int)this->content.Length() - 1) this->list_pos++;
    00744         break;
    00745       case WKC_PAGEUP:
    00746         /* scroll up a page */
    00747         this->list_pos = (this->list_pos < this->vscroll->GetCapacity()) ? 0 : this->list_pos - this->vscroll->GetCapacity();
    00748         break;
    00749       case WKC_PAGEDOWN:
    00750         /* scroll down a page */
    00751         this->list_pos = min(this->list_pos + this->vscroll->GetCapacity(), (int)this->content.Length() - 1);
    00752         break;
    00753       case WKC_HOME:
    00754         /* jump to beginning */
    00755         this->list_pos = 0;
    00756         break;
    00757       case WKC_END:
    00758         /* jump to end */
    00759         this->list_pos = this->content.Length() - 1;
    00760         break;
    00761
    00762       case WKC_SPACE:
    00763       case WKC_RETURN:
    00764         if (keycode == WKC_RETURN || !IsWidgetFocused(WID_NCL_FILTER)) {
    00765           if (this->selected != NULL) {
    00766             _network_content_client.ToggleSelectedState(this->selected);
    00767             this->content.ForceResort();
    00768             this->InvalidateData();
    00769           }
    00770           return ES_HANDLED;
    00771         }
    00772         /* FALL THROUGH, space is pressed and filter isn't focused. */
    00773
    00774       default: {
    00775         /* Handle editbox input */
    00776         EventState state = ES_NOT_HANDLED;
    00777         if (this->HandleEditBoxKey(WID_NCL_FILTER, key, keycode, state) == HEBR_EDITING) {
    00778           this->OnOSKInput(WID_NCL_FILTER);
    00779         }
    00780
    00781         return state;
    00782       }
    00783     }
    00784
    00785     if (_network_content_client.Length() == 0) return ES_HANDLED;
    00786
    00787     this->selected = *this->content.Get(this->list_pos);
    00788
    00789     /* scroll to the new server if it is outside the current range */
    00790     this->ScrollToSelected();
    00791
    00792     /* redraw window */
    00793     this->InvalidateData();
    00794     return ES_HANDLED;
    00795   }
    00796
    00797   virtual void OnOSKInput(int wid)
    00798   {
    00799     this->string_filter.SetFilterTerm(this->edit_str_buf);
    00800     this->content.SetFilterState(!this->string_filter.IsEmpty());
    00801     this->content.ForceRebuild();
    00802     this->InvalidateData();
    00803   }
    00804
    00805   virtual void OnResize()
    00806   {
    00807     this->vscroll->SetCapacityFromWidget(this, WID_NCL_MATRIX);
    00808     this->GetWidget<NWidgetCore>(WID_NCL_MATRIX)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
    00809   }
    00810
    00811   virtual void OnReceiveContentInfo(const ContentInfo *rci)
    00812   {
    00813     if (this->auto_select && !rci->IsSelected()) _network_content_client.ToggleSelectedState(rci);
    00814     this->content.ForceRebuild();
    00815     this->InvalidateData();
    00816   }
    00817
    00818   virtual void OnDownloadComplete(ContentID cid)
    00819   {
    00820     this->content.ForceResort();
    00821     this->InvalidateData();
    00822   }
    00823
    00824   virtual void OnConnect(bool success)
    00825   {
    00826     if (!success) {
    00827       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_CONNECT, INVALID_STRING_ID, WL_ERROR);
    00828       delete this;
    00829       return;
    00830     }
    00831
    00832     this->InvalidateData();
    00833   }
    00834
    00840   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00841   {
    00842     if (!gui_scope) return;
    00843     if (this->content.NeedRebuild()) this->BuildContentList();
    00844
    00845     /* To sum all the bytes we intend to download */
    00846     this->filesize_sum = 0;
    00847     bool show_select_all = false;
    00848     bool show_select_upgrade = false;
    00849     for (ConstContentIterator iter = this->content.Begin(); iter != this->content.End(); iter++) {
    00850       const ContentInfo *ci = *iter;
    00851       switch (ci->state) {
    00852         case ContentInfo::SELECTED:
    00853         case ContentInfo::AUTOSELECTED:
    00854           this->filesize_sum += ci->filesize;
    00855           break;
    00856
    00857         case ContentInfo::UNSELECTED:
    00858           show_select_all = true;
    00859           show_select_upgrade |= ci->upgrade;
    00860           break;
    00861
    00862         default:
    00863           break;
    00864       }
    00865     }
    00866
    00867     /* If data == 2 then the status window caused this OnInvalidate */
    00868     this->SetWidgetDisabledState(WID_NCL_DOWNLOAD, this->filesize_sum == 0 || (FindWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD) != NULL && data != 2));
    00869     this->SetWidgetDisabledState(WID_NCL_UNSELECT, this->filesize_sum == 0);
    00870     this->SetWidgetDisabledState(WID_NCL_SELECT_ALL, !show_select_all);
    00871     this->SetWidgetDisabledState(WID_NCL_SELECT_UPDATE, !show_select_upgrade);
    00872     this->SetWidgetDisabledState(WID_NCL_OPEN_URL, this->selected == NULL || StrEmpty(this->selected->url));
    00873     for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
    00874       this->SetWidgetDisabledState(WID_NCL_TEXTFILE + tft, this->selected == NULL || this->selected->state != ContentInfo::ALREADY_HERE || this->selected->GetTextfile(tft) == NULL);
    00875     }
    00876
    00877     this->GetWidget<NWidgetCore>(WID_NCL_CANCEL)->widget_data = this->filesize_sum == 0 ? STR_AI_SETTINGS_CLOSE : STR_AI_LIST_CANCEL;
    00878   }
    00879 };
    00880
    00881 Listing NetworkContentListWindow::last_sorting = {false, 1};
    00882 Filtering NetworkContentListWindow::last_filtering = {false, 0};
    00883
    00884 NetworkContentListWindow::GUIContentList::SortFunction * const NetworkContentListWindow::sorter_funcs[] = {
    00885   &StateSorter,
    00886   &TypeSorter,
    00887   &NameSorter,
    00888 };
    00889
    00890 NetworkContentListWindow::GUIContentList::FilterFunction * const NetworkContentListWindow::filter_funcs[] = {
    00891   &TagNameFilter,
    00892 };
    00893
    00895 static const NWidgetPart _nested_network_content_list_widgets[] = {
    00896   NWidget(NWID_HORIZONTAL),
    00897     NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
    00898     NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE), SetDataTip(STR_CONTENT_TITLE, STR_NULL),
    00899   EndContainer(),
    00900   NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NCL_BACKGROUND),
    00901     NWidget(NWID_SPACER), SetMinimalSize(0, 7), SetResize(1, 0),
    00902     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(8, 8, 8),
    00903       /* Top */
    00904       NWidget(WWT_EMPTY, COLOUR_LIGHT_BLUE, WID_NCL_FILTER_CAPT), SetFill(1, 0), SetResize(1, 0),
    00905       NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, WID_NCL_FILTER), SetFill(1, 0), SetResize(1, 0),
    00906             SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
    00907     EndContainer(),
    00908     NWidget(NWID_SPACER), SetMinimalSize(0, 7), SetResize(1, 0),
    00909     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(8, 8, 8),
    00910       /* Left side. */
    00911       NWidget(NWID_VERTICAL),
    00912         NWidget(NWID_HORIZONTAL),
    00913           NWidget(NWID_VERTICAL),
    00914             NWidget(NWID_HORIZONTAL),
    00915               NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_CHECKBOX), SetMinimalSize(13, 1), SetDataTip(STR_EMPTY, STR_NULL),
    00916               NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_TYPE),
    00917                       SetDataTip(STR_CONTENT_TYPE_CAPTION, STR_CONTENT_TYPE_CAPTION_TOOLTIP),
    00918               NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_NAME), SetResize(1, 0), SetFill(1, 0),
    00919                       SetDataTip(STR_CONTENT_NAME_CAPTION, STR_CONTENT_NAME_CAPTION_TOOLTIP),
    00920             EndContainer(),
    00921             NWidget(WWT_MATRIX, COLOUR_LIGHT_BLUE, WID_NCL_MATRIX), SetResize(1, 14), SetFill(1, 1), SetScrollbar(WID_NCL_SCROLLBAR), SetDataTip(STR_NULL, STR_CONTENT_MATRIX_TOOLTIP),
    00922           EndContainer(),
    00923           NWidget(NWID_VSCROLLBAR, COLOUR_LIGHT_BLUE, WID_NCL_SCROLLBAR),
    00924         EndContainer(),
    00925       EndContainer(),
    00926       /* Right side. */
    00927       NWidget(NWID_VERTICAL),
    00928         NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NCL_DETAILS), SetResize(1, 1), SetFill(1, 1), EndContainer(),
    00929         NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7),
    00930           NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_TEXTFILE + TFT_README), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_README, STR_NULL),
    00931           NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_TEXTFILE + TFT_CHANGELOG), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_NULL),
    00932           NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_TEXTFILE + TFT_LICENSE), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_LICENCE, STR_NULL),
    00933         EndContainer(),
    00934       EndContainer(),
    00935     EndContainer(),
    00936     NWidget(NWID_SPACER), SetMinimalSize(0, 7), SetResize(1, 0),
    00937     /* Bottom. */
    00938     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(8, 8, 8),
    00939       NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(8, 8, 8),
    00940         NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NCL_SEL_ALL_UPDATE), SetResize(1, 0), SetFill(1, 0),
    00941           NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_SELECT_UPDATE), SetResize(1, 0), SetFill(1, 0),
    00942                       SetDataTip(STR_CONTENT_SELECT_UPDATES_CAPTION, STR_CONTENT_SELECT_UPDATES_CAPTION_TOOLTIP),
    00943           NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_SELECT_ALL), SetResize(1, 0), SetFill(1, 0),
    00944                       SetDataTip(STR_CONTENT_SELECT_ALL_CAPTION, STR_CONTENT_SELECT_ALL_CAPTION_TOOLTIP),
    00945         EndContainer(),
    00946         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_UNSELECT), SetResize(1, 0), SetFill(1, 0),
    00947                       SetDataTip(STR_CONTENT_UNSELECT_ALL_CAPTION, STR_CONTENT_UNSELECT_ALL_CAPTION_TOOLTIP),
    00948       EndContainer(),
    00949       NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(8, 8, 8),
    00950         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_OPEN_URL), SetResize(1, 0), SetFill(1, 0),
    00951                       SetDataTip(STR_CONTENT_OPEN_URL, STR_CONTENT_OPEN_URL_TOOLTIP),
    00952         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_CANCEL), SetResize(1, 0), SetFill(1, 0),
    00953                       SetDataTip(STR_BUTTON_CANCEL, STR_NULL),
    00954         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NCL_DOWNLOAD), SetResize(1, 0), SetFill(1, 0),
    00955                       SetDataTip(STR_CONTENT_DOWNLOAD_CAPTION, STR_CONTENT_DOWNLOAD_CAPTION_TOOLTIP),
    00956       EndContainer(),
    00957     EndContainer(),
    00958     NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetResize(1, 0),
    00959     /* Resize button. */
    00960     NWidget(NWID_HORIZONTAL),
    00961       NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
    00962       NWidget(WWT_RESIZEBOX, COLOUR_LIGHT_BLUE),
    00963     EndContainer(),
    00964   EndContainer(),
    00965 };
    00966
    00968 static const WindowDesc _network_content_list_desc(
    00969   WDP_CENTER, 630, 460,
    00970   WC_NETWORK_WINDOW, WC_NONE,
    00971   WDF_UNCLICK_BUTTONS,
    00972   _nested_network_content_list_widgets, lengthof(_nested_network_content_list_widgets)
    00973 );
    00974
    00980 void ShowNetworkContentListWindow(ContentVector *cv, ContentType type)
    00981 {
    00982 #if defined(WITH_ZLIB)
    00983   _network_content_client.Clear();
    00984   if (cv == NULL) {
    00985     _network_content_client.RequestContentList(type);
    00986   } else {
    00987     _network_content_client.RequestContentList(cv, true);
    00988   }
    00989
    00990   DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_CONTENT_LIST);
    00991   new NetworkContentListWindow(&_network_content_list_desc, cv != NULL);
    00992 #else
    00993   ShowErrorMessage(STR_CONTENT_NO_ZLIB, STR_CONTENT_NO_ZLIB_SUB, WL_ERROR);
    00994   /* Connection failed... clean up the mess */
    00995   if (cv != NULL) {
    00996     for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) delete *iter;
    00997   }
    00998 #endif /* WITH_ZLIB */
    00999 }
    01000
    01001 #endif /* ENABLE_NETWORK */