# story_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: story_gui.cpp 26307 2014-02-06 19:50:34Z zuu $ */
    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 #include "stdafx.h"
    00013 #include "window_gui.h"
    00014 #include "strings_func.h"
    00015 #include "date_func.h"
    00016 #include "gui.h"
    00017 #include "story_base.h"
    00018 #include "core/geometry_func.hpp"
    00019 #include "company_func.h"
    00020 #include "command_func.h"
    00021 #include "widgets/dropdown_type.h"
    00022 #include "widgets/dropdown_func.h"
    00023 #include "sortlist_type.h"
    00024 #include "goal_base.h"
    00025 #include "viewport_func.h"
    00026 #include "window_func.h"
    00027 #include "company_base.h"
    00028
    00029 #include "widgets/story_widget.h"
    00030
    00031 #include "table/strings.h"
    00032 #include "table/sprites.h"
    00033
    00034 typedef GUIList<const StoryPage*> GUIStoryPageList;
    00035 typedef GUIList<const StoryPageElement*> GUIStoryPageElementList;
    00036
    00037 struct StoryBookWindow : Window {
    00038 protected:
    00039   Scrollbar *vscroll;
    00040
    00041   GUIStoryPageList story_pages;
    00042   GUIStoryPageElementList story_page_elements;
    00043   StoryPageID selected_page_id;
    00044   char selected_generic_title[255];
    00045
    00046   static GUIStoryPageList::SortFunction * const page_sorter_funcs[];
    00047   static GUIStoryPageElementList::SortFunction * const page_element_sorter_funcs[];
    00048
    00050   void BuildStoryPageList()
    00051   {
    00052     if (this->story_pages.NeedRebuild()) {
    00053       this->story_pages.Clear();
    00054
    00055       const StoryPage *p;
    00056       FOR_ALL_STORY_PAGES(p) {
    00057         if (this->IsPageAvailable(p)) {
    00058           *this->story_pages.Append() = p;
    00059         }
    00060       }
    00061
    00062       this->story_pages.Compact();
    00063       this->story_pages.RebuildDone();
    00064     }
    00065
    00066     this->story_pages.Sort();
    00067   }
    00068
    00070   static int CDECL PageOrderSorter(const StoryPage * const *a, const StoryPage * const *b)
    00071   {
    00072     return (*a)->sort_value - (*b)->sort_value;
    00073   }
    00074
    00076   void BuildStoryPageElementList()
    00077   {
    00078     if (this->story_page_elements.NeedRebuild()) {
    00079       this->story_page_elements.Clear();
    00080
    00081       const StoryPage *p = GetSelPage();
    00082       if (p != NULL) {
    00083         const StoryPageElement *pe;
    00084         FOR_ALL_STORY_PAGE_ELEMENTS(pe) {
    00085           if (pe->page == p->index) {
    00086             *this->story_page_elements.Append() = pe;
    00087           }
    00088         }
    00089       }
    00090
    00091       this->story_page_elements.Compact();
    00092       this->story_page_elements.RebuildDone();
    00093     }
    00094
    00095     this->story_page_elements.Sort();
    00096   }
    00097
    00099   static int CDECL PageElementOrderSorter(const StoryPageElement * const *a, const StoryPageElement * const *b)
    00100   {
    00101     return (*a)->sort_value - (*b)->sort_value;
    00102   }
    00103
    00104   /*
    00105    * Checks if a given page should be visible in the story book.
    00106    * @param page The page to check.
    00107    * @return True if the page should be visible, otherwise false.
    00108    */
    00109   bool IsPageAvailable(const StoryPage *page) const
    00110   {
    00111     return page->company == INVALID_COMPANY || page->company == this->window_number;
    00112   }
    00113
    00118   StoryPage *GetSelPage() const
    00119   {
    00120     if (!_story_page_pool.IsValidID(selected_page_id)) return NULL;
    00121     return _story_page_pool.Get(selected_page_id);
    00122   }
    00123
    00128   int GetSelPageNum() const
    00129   {
    00130     int page_number = 0;
    00131     for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) {
    00132       const StoryPage *p = *iter;
    00133       if (p->index == this->selected_page_id) {
    00134         return page_number;
    00135       }
    00136       page_number++;
    00137     }
    00138     return -1;
    00139   }
    00140
    00144   bool IsFirstPageSelected()
    00145   {
    00146     /* Verify that the selected page exist. */
    00147     if (!_story_page_pool.IsValidID(this->selected_page_id)) return false;
    00148
    00149     return (*this->story_pages.Begin())->index == this->selected_page_id;
    00150   }
    00151
    00155   bool IsLastPageSelected()
    00156   {
    00157     /* Verify that the selected page exist. */
    00158     if (!_story_page_pool.IsValidID(this->selected_page_id)) return false;
    00159
    00160     if (this->story_pages.Length() <= 1) return true;
    00161     const StoryPage *last = *(this->story_pages.End() - 1);
    00162     return last->index == this->selected_page_id;
    00163   }
    00164
    00168   void RefreshSelectedPage()
    00169   {
    00170     /* Generate generic title if selected page have no custom title. */
    00171     StoryPage *page = this->GetSelPage();
    00172     if (page != NULL && page->title == NULL) {
    00173       SetDParam(0, GetSelPageNum() + 1);
    00174       GetString(selected_generic_title, STR_STORY_BOOK_GENERIC_PAGE_ITEM, lastof(selected_generic_title));
    00175     }
    00176
    00177     this->story_page_elements.ForceRebuild();
    00178     this->BuildStoryPageElementList();
    00179
    00180     this->vscroll->SetCount(this->GetContentHeight());
    00181     this->SetWidgetDirty(WID_SB_SCROLLBAR);
    00182     this->SetWidgetDirty(WID_SB_SEL_PAGE);
    00183     this->SetWidgetDirty(WID_SB_PAGE_PANEL);
    00184   }
    00185
    00189   void SelectPrevPage()
    00190   {
    00191     if (!_story_page_pool.IsValidID(this->selected_page_id)) return;
    00192
    00193     /* Find the last available page which is previous to the current selected page. */
    00194     const StoryPage *last_available;
    00195     last_available = NULL;
    00196     for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) {
    00197       const StoryPage *p = *iter;
    00198       if (p->index == this->selected_page_id) {
    00199         if (last_available == NULL) return; // No previous page available.
    00200         this->SetSelectedPage(last_available->index);
    00201         return;
    00202       }
    00203       last_available = p;
    00204     }
    00205   }
    00206
    00210   void SelectNextPage()
    00211   {
    00212     if (!_story_page_pool.IsValidID(this->selected_page_id)) return;
    00213
    00214     /* Find selected page. */
    00215     for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) {
    00216       const StoryPage *p = *iter;
    00217       if (p->index == this->selected_page_id) {
    00218         /* Select the page after selected page. */
    00219         iter++;
    00220         if (iter != this->story_pages.End()) {
    00221           this->SetSelectedPage((*iter)->index);
    00222         }
    00223         return;
    00224       }
    00225     }
    00226   }
    00227
    00231   DropDownList *BuildDropDownList() const
    00232   {
    00233     DropDownList *list = new DropDownList();
    00234     uint16 page_num = 1;
    00235     for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) {
    00236       const StoryPage *p = *iter;
    00237       bool current_page = p->index == this->selected_page_id;
    00238       DropDownListStringItem *item = NULL;
    00239       if (p->title != NULL) {
    00240         item = new DropDownListCharStringItem(p->title, p->index, current_page);
    00241       } else {
    00242         /* No custom title => use a generic page title with page number. */
    00243         DropDownListParamStringItem *str_item =
    00244             new DropDownListParamStringItem(STR_STORY_BOOK_GENERIC_PAGE_ITEM, p->index, current_page);
    00245         str_item->SetParam(0, page_num);
    00246         item = str_item;
    00247       }
    00248
    00249       *list->Append() = item;
    00250       page_num++;
    00251     }
    00252
    00253     /* Check if list is empty. */
    00254     if (list->Length() == 0) {
    00255       delete list;
    00256       list = NULL;
    00257     }
    00258
    00259     return list;
    00260   }
    00261
    00265   uint GetAvailablePageContentWidth()
    00266   {
    00267     return this->GetWidget<NWidgetCore>(WID_SB_PAGE_PANEL)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMERECT_RIGHT;
    00268   }
    00269
    00277   uint GetHeadHeight(int max_width) const
    00278   {
    00279     StoryPage *page = this->GetSelPage();
    00280     if (page == NULL) return 0;
    00281     int height = 0;
    00282
    00283     /* Title lines */
    00284     height += FONT_HEIGHT_NORMAL; // Date always use exactly one line.
    00285     SetDParamStr(0, page->title != NULL ? page->title : this->selected_generic_title);
    00286     height += GetStringHeight(STR_STORY_BOOK_TITLE, max_width);
    00287
    00288     return height;
    00289   }
    00290
    00297   SpriteID GetPageElementSprite(const StoryPageElement &pe) const
    00298   {
    00299     switch (pe.type) {
    00300       case SPET_GOAL: {
    00301         Goal *g = Goal::Get((GoalID) pe.referenced_id);
    00302         if (g == NULL) return SPR_IMG_GOAL_BROKEN_REF;
    00303         return g->completed ? SPR_IMG_GOAL_COMPLETED : SPR_IMG_GOAL;
    00304       }
    00305       case SPET_LOCATION:
    00306         return SPR_IMG_VIEW_LOCATION;
    00307       default:
    00308         NOT_REACHED();
    00309     }
    00310   }
    00311
    00318   uint GetPageElementHeight(const StoryPageElement &pe, int max_width)
    00319   {
    00320     switch (pe.type) {
    00321       case SPET_TEXT:
    00322         SetDParamStr(0, pe.text);
    00323         return GetStringHeight(STR_BLACK_RAW_STRING, max_width);
    00324         break;
    00325
    00326       case SPET_GOAL:
    00327       case SPET_LOCATION: {
    00328         Dimension sprite_dim = GetSpriteSize(GetPageElementSprite(pe));
    00329         return sprite_dim.height;
    00330         break;
    00331       }
    00332       default:
    00333         NOT_REACHED();
    00334     }
    00335     return 0;
    00336   }
    00337
    00343   uint GetContentHeight()
    00344   {
    00345     StoryPage *page = this->GetSelPage();
    00346     if (page == NULL) return 0;
    00347     int max_width = GetAvailablePageContentWidth();
    00348     uint element_vertical_dist = FONT_HEIGHT_NORMAL;
    00349
    00350     /* Head */
    00351     uint height = GetHeadHeight(max_width);
    00352
    00353     /* Body */
    00354     for (const StoryPageElement **iter = this->story_page_elements.Begin(); iter != this->story_page_elements.End(); iter++) {
    00355       const StoryPageElement *pe = *iter;
    00356       height += element_vertical_dist;
    00357       height += GetPageElementHeight(*pe, max_width);
    00358     }
    00359
    00360     return height;
    00361   }
    00362
    00374   void DrawActionElement(int &y_offset, int width, int line_height, SpriteID action_sprite, StringID string_id = STR_JUST_RAW_STRING) const
    00375   {
    00376     Dimension sprite_dim = GetSpriteSize(action_sprite);
    00377     uint element_height = max(sprite_dim.height, (uint)line_height);
    00378
    00379     uint sprite_top = y_offset + (element_height - sprite_dim.height) / 2;
    00380     uint text_top = y_offset + (element_height - line_height) / 2;
    00381
    00382     DrawSprite(action_sprite, PAL_NONE, 0, sprite_top);
    00383     DrawString(sprite_dim.width + WD_FRAMETEXT_LEFT, width, text_top, string_id, TC_BLACK);
    00384
    00385     y_offset += element_height;
    00386   }
    00387
    00392   void OnPageElementClick(const StoryPageElement& pe)
    00393   {
    00394     switch (pe.type) {
    00395       case SPET_TEXT:
    00396         /* Do nothing. */
    00397         break;
    00398
    00399       case SPET_LOCATION:
    00400         if (_ctrl_pressed) {
    00401           ShowExtraViewPortWindow((TileIndex)pe.referenced_id);
    00402         } else {
    00403           ScrollMainWindowToTile((TileIndex)pe.referenced_id);
    00404         }
    00405         break;
    00406
    00407       case SPET_GOAL:
    00408         ShowGoalsList((CompanyID)this->window_number);
    00409         break;
    00410
    00411       default:
    00412         NOT_REACHED();
    00413     }
    00414   }
    00415
    00416 public:
    00417   StoryBookWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
    00418   {
    00419     this->CreateNestedTree();
    00420     this->vscroll = this->GetScrollbar(WID_SB_SCROLLBAR);
    00421     this->vscroll->SetStepSize(FONT_HEIGHT_NORMAL);
    00422
    00423     /* Initalize page sort. */
    00424     this->story_pages.SetSortFuncs(StoryBookWindow::page_sorter_funcs);
    00425     this->story_pages.ForceRebuild();
    00426     this->BuildStoryPageList();
    00427     this->story_page_elements.SetSortFuncs(StoryBookWindow::page_element_sorter_funcs);
    00428     /* story_page_elements will get built by SetSelectedPage */
    00429
    00430     this->FinishInitNested(window_number);
    00431     this->owner = (Owner)this->window_number;
    00432
    00433     /* Initialize selected vars. */
    00434     this->selected_generic_title[0] = '\0';
    00435     this->selected_page_id = INVALID_STORY_PAGE;
    00436
    00437     this->OnInvalidateData(-1);
    00438   }
    00439
    00443   void UpdatePrevNextDisabledState()
    00444   {
    00445     this->SetWidgetDisabledState(WID_SB_PREV_PAGE, story_pages.Length() == 0 || this->IsFirstPageSelected());
    00446     this->SetWidgetDisabledState(WID_SB_NEXT_PAGE, story_pages.Length() == 0 || this->IsLastPageSelected());
    00447     this->SetWidgetDirty(WID_SB_PREV_PAGE);
    00448     this->SetWidgetDirty(WID_SB_NEXT_PAGE);
    00449   }
    00450
    00455   void SetSelectedPage(uint16 page_index)
    00456   {
    00457     if (this->selected_page_id != page_index) {
    00458       this->selected_page_id = page_index;
    00459       this->RefreshSelectedPage();
    00460       this->UpdatePrevNextDisabledState();
    00461     }
    00462   }
    00463
    00464   virtual void SetStringParameters(int widget) const
    00465   {
    00466     switch (widget) {
    00467       case WID_SB_SEL_PAGE: {
    00468         StoryPage *page = this->GetSelPage();
    00469         SetDParamStr(0, page != NULL && page->title != NULL ? page->title : this->selected_generic_title);
    00470         break;
    00471       }
    00472       case WID_SB_CAPTION:
    00473         if (this->window_number == INVALID_COMPANY) {
    00474           SetDParam(0, STR_STORY_BOOK_SPECTATOR_CAPTION);
    00475         } else {
    00476           SetDParam(0, STR_STORY_BOOK_CAPTION);
    00477           SetDParam(1, this->window_number);
    00478         }
    00479         break;
    00480     }
    00481   }
    00482
    00483   virtual void OnPaint()
    00484   {
    00485     /* Detect if content has changed height. This can happen if a
    00486      * multi-line text contains eg. {COMPANY} and that company is
    00487      * renamed.
    00488      */
    00489     if (this->vscroll->GetCount() != this->GetContentHeight()) {
    00490       this->vscroll->SetCount(this->GetContentHeight());
    00491       this->SetWidgetDirty(WID_SB_SCROLLBAR);
    00492       this->SetWidgetDirty(WID_SB_PAGE_PANEL);
    00493     }
    00494
    00495     this->DrawWidgets();
    00496   }
    00497
    00498   virtual void DrawWidget(const Rect &r, int widget) const
    00499   {
    00500     if (widget != WID_SB_PAGE_PANEL) return;
    00501
    00502     StoryPage *page = this->GetSelPage();
    00503     if (page == NULL) return;
    00504
    00505     const int x = r.left + WD_FRAMETEXT_LEFT;
    00506     const int y = r.top + WD_FRAMETEXT_TOP;
    00507     const int right = r.right - WD_FRAMETEXT_RIGHT;
    00508     const int bottom = r.bottom - WD_FRAMETEXT_BOTTOM;
    00509
    00510     /* Set up a clipping region for the panel. */
    00511     DrawPixelInfo tmp_dpi;
    00512     if (!FillDrawPixelInfo(&tmp_dpi, x, y, right - x + 1, bottom - y + 1)) return;
    00513
    00514     DrawPixelInfo *old_dpi = _cur_dpi;
    00515     _cur_dpi = &tmp_dpi;
    00516
    00517     /* Draw content (now coordinates given to Draw** are local to the new clipping region). */
    00518     int line_height = FONT_HEIGHT_NORMAL;
    00519     int y_offset = - this->vscroll->GetPosition();
    00520
    00521     /* Date */
    00522     if (page->date != INVALID_DATE) {
    00523       SetDParam(0, page->date);
    00524       DrawString(0, right - x, y_offset, STR_JUST_DATE_LONG, TC_BLACK);
    00525     }
    00526     y_offset += line_height;
    00527
    00528     /* Title */
    00529     SetDParamStr(0, page->title != NULL ? page->title : this->selected_generic_title);
    00530     y_offset = DrawStringMultiLine(0, right - x, y_offset, bottom - y, STR_STORY_BOOK_TITLE, TC_BLACK, SA_TOP | SA_HOR_CENTER);
    00531
    00532     /* Page elements */
    00533     for (const StoryPageElement *const*iter = this->story_page_elements.Begin(); iter != this->story_page_elements.End(); iter++) {
    00534       const StoryPageElement *const pe = *iter;
    00535       y_offset += line_height; // margin to previous element
    00536
    00537       switch (pe->type) {
    00538         case SPET_TEXT:
    00539           SetDParamStr(0, pe->text);
    00540           y_offset = DrawStringMultiLine(0, right - x, y_offset, bottom - y, STR_JUST_RAW_STRING, TC_BLACK, SA_TOP | SA_LEFT);
    00541           break;
    00542
    00543         case SPET_GOAL: {
    00544           Goal *g = Goal::Get((GoalID) pe->referenced_id);
    00545           StringID string_id = g == NULL ? STR_STORY_BOOK_INVALID_GOAL_REF : STR_JUST_RAW_STRING;
    00546           if (g != NULL) SetDParamStr(0, g->text);
    00547           DrawActionElement(y_offset, right - x, line_height, GetPageElementSprite(*pe), string_id);
    00548           break;
    00549         }
    00550
    00551         case SPET_LOCATION:
    00552           SetDParamStr(0, pe->text);
    00553           DrawActionElement(y_offset, right - x, line_height, GetPageElementSprite(*pe));
    00554           break;
    00555
    00556         default: NOT_REACHED();
    00557       }
    00558     }
    00559
    00560     /* Restore clipping region. */
    00561     _cur_dpi = old_dpi;
    00562   }
    00563
    00564   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00565   {
    00566     if (widget != WID_SB_SEL_PAGE && widget != WID_SB_PAGE_PANEL) return;
    00567
    00568     Dimension d;
    00569     d.height= FONT_HEIGHT_NORMAL;
    00570     d.width = 0;
    00571
    00572     switch(widget) {
    00573       case WID_SB_SEL_PAGE: {
    00574
    00575         /* Get max title width. */
    00576         for (uint16 i = 0; i < this->story_pages.Length(); i++) {
    00577           const StoryPage *s = this->story_pages[i];
    00578
    00579           if (s->title != NULL) {
    00580             SetDParamStr(0, s->title);
    00581           } else {
    00582             SetDParamStr(0, this->selected_generic_title);
    00583           }
    00584           Dimension title_d = GetStringBoundingBox(STR_BLACK_RAW_STRING);
    00585
    00586           if (title_d.width > d.width) {
    00587             d.width = title_d.width;
    00588           }
    00589         }
    00590
    00591         d.width += padding.width;
    00592         d.height += padding.height;
    00593         *size = maxdim(*size, d);
    00594         break;
    00595       }
    00596
    00597       case WID_SB_PAGE_PANEL: {
    00598         d.height *= 5;
    00599         d.height += padding.height + WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
    00600         *size = maxdim(*size, d);
    00601         break;
    00602       }
    00603     }
    00604
    00605   }
    00606
    00607   virtual void OnResize()
    00608   {
    00609     this->vscroll->SetCapacityFromWidget(this, WID_SB_PAGE_PANEL, WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM);
    00610     this->vscroll->SetCount(this->GetContentHeight());
    00611   }
    00612
    00613   virtual void OnClick(Point pt, int widget, int click_count)
    00614   {
    00615     switch (widget) {
    00616       case WID_SB_SEL_PAGE: {
    00617         DropDownList *list = this->BuildDropDownList();
    00618         if (list != NULL) {
    00619           /* Get the index of selected page. */
    00620           int selected = 0;
    00621           for (uint16 i = 0; i < this->story_pages.Length(); i++) {
    00622             const StoryPage *p = this->story_pages[i];
    00623             if (p->index == this->selected_page_id) break;
    00624             selected++;
    00625           }
    00626
    00627           ShowDropDownList(this, list, selected, widget);
    00628         }
    00629         break;
    00630       }
    00631
    00632       case WID_SB_PREV_PAGE:
    00633         this->SelectPrevPage();
    00634         break;
    00635
    00636       case WID_SB_NEXT_PAGE:
    00637         this->SelectNextPage();
    00638         break;
    00639
    00640       case WID_SB_PAGE_PANEL: {
    00641         uint clicked_y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SB_PAGE_PANEL, WD_FRAMETEXT_TOP);
    00642         uint max_width = GetAvailablePageContentWidth();
    00643
    00644         /* Skip head rows. */
    00645         uint head_height = this->GetHeadHeight(max_width);
    00646         if (clicked_y < head_height) return;
    00647
    00648         /* Detect if a page element was clicked. */
    00649         uint y = head_height;
    00650         uint element_vertical_dist = FONT_HEIGHT_NORMAL;
    00651         for (const StoryPageElement *const*iter = this->story_page_elements.Begin(); iter != this->story_page_elements.End(); iter++) {
    00652           const StoryPageElement *const pe = *iter;
    00653
    00654           y += element_vertical_dist; // margin row
    00655
    00656           uint content_height = GetPageElementHeight(*pe, max_width);
    00657           if (clicked_y >= y && clicked_y < y + content_height) {
    00658             this->OnPageElementClick(*pe);
    00659             return;
    00660           }
    00661
    00662           y += content_height;
    00663         }
    00664       }
    00665     }
    00666   }
    00667
    00668   virtual void OnDropdownSelect(int widget, int index)
    00669   {
    00670     if (widget != WID_SB_SEL_PAGE) return;
    00671
    00672     /* index (which is set in BuildDropDownList) is the page id. */
    00673     this->SetSelectedPage(index);
    00674   }
    00675
    00683   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00684   {
    00685     if (!gui_scope) return;
    00686
    00687     /* If added/removed page, force rebuild. Sort order never change so just a
    00688      * re-sort is never needed.
    00689      */
    00690     if (data == -1) {
    00691       this->story_pages.ForceRebuild();
    00692       this->BuildStoryPageList();
    00693
    00694       /* Was the last page removed? */
    00695       if (this->story_pages.Length() == 0) {
    00696         this->selected_generic_title[0] = '\0';
    00697       }
    00698
    00699       /* Verify page selection. */
    00700       if (!_story_page_pool.IsValidID(this->selected_page_id)) {
    00701         this->selected_page_id = INVALID_STORY_PAGE;
    00702       }
    00703       if (this->selected_page_id == INVALID_STORY_PAGE && this->story_pages.Length() > 0) {
    00704         /* No page is selected, but there exist at least one available.
    00705          * => Select first page.
    00706          */
    00707         this->SetSelectedPage(this->story_pages[0]->index);
    00708       }
    00709
    00710       this->SetWidgetDisabledState(WID_SB_SEL_PAGE, this->story_pages.Length() == 0);
    00711       this->SetWidgetDirty(WID_SB_SEL_PAGE);
    00712       this->UpdatePrevNextDisabledState();
    00713     } else if (data >= 0 && this->selected_page_id == data) {
    00714       this->RefreshSelectedPage();
    00715     }
    00716   }
    00717 };
    00718
    00719 GUIStoryPageList::SortFunction * const StoryBookWindow::page_sorter_funcs[] = {
    00720   &PageOrderSorter,
    00721 };
    00722
    00723 GUIStoryPageElementList::SortFunction * const StoryBookWindow::page_element_sorter_funcs[] = {
    00724   &PageElementOrderSorter,
    00725 };
    00726
    00727 static const NWidgetPart _nested_story_book_widgets[] = {
    00728   NWidget(NWID_HORIZONTAL),
    00729     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    00730     NWidget(WWT_CAPTION, COLOUR_BROWN, WID_SB_CAPTION), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00731     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    00732     NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
    00733     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    00734   EndContainer(),
    00735   NWidget(NWID_HORIZONTAL), SetFill(1, 1),
    00736     NWidget(NWID_VERTICAL), SetFill(1, 1),
    00737       NWidget(WWT_PANEL, COLOUR_BROWN, WID_SB_PAGE_PANEL), SetResize(1, 1), SetScrollbar(WID_SB_SCROLLBAR), EndContainer(),
    00738       NWidget(NWID_HORIZONTAL),
    00739         NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_SB_PREV_PAGE), SetMinimalSize(100, 0), SetFill(0, 0), SetDataTip(STR_STORY_BOOK_PREV_PAGE, STR_STORY_BOOK_PREV_PAGE_TOOLTIP),
    00740         NWidget(NWID_BUTTON_DROPDOWN, COLOUR_BROWN, WID_SB_SEL_PAGE), SetMinimalSize(93, 12), SetFill(1, 0),
    00741                             SetDataTip(STR_BLACK_RAW_STRING, STR_STORY_BOOK_SEL_PAGE_TOOLTIP), SetResize(1, 0),
    00742         NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_SB_NEXT_PAGE), SetMinimalSize(100, 0), SetFill(0, 0), SetDataTip(STR_STORY_BOOK_NEXT_PAGE, STR_STORY_BOOK_NEXT_PAGE_TOOLTIP),
    00743       EndContainer(),
    00744     EndContainer(),
    00745     NWidget(NWID_VERTICAL), SetFill(0, 1),
    00746       NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_SB_SCROLLBAR),
    00747       NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    00748     EndContainer(),
    00749   EndContainer(),
    00750 };
    00751
    00752 static WindowDesc _story_book_desc(
    00753   WDP_CENTER, "view_story", 400, 300,
    00754   WC_STORY_BOOK, WC_NONE,
    00755   0,
    00756   _nested_story_book_widgets, lengthof(_nested_story_book_widgets)
    00757 );
    00758
    00759 void ShowStoryBook(CompanyID company, uint16 page_id)
    00760 {
    00761   if (!Company::IsValidID(company)) company = (CompanyID)INVALID_COMPANY;
    00762
    00763   StoryBookWindow *w = AllocateWindowDescFront<StoryBookWindow>(&_story_book_desc, company);
    00764   if (page_id != INVALID_STORY_PAGE) {
    00765     if (w == NULL) w = (StoryBookWindow *)FindWindowById(WC_STORY_BOOK, company);
    00766     w->SetSelectedPage(page_id);
    00767   }
    00768 }