Translations update
[openttd/fttd.git] / src / story_gui.cpp
bloba858c15728fd18324bb6795b64d516e95a27b7c8
1 /* $Id$ */
3 /*
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/>.
8 */
10 /** @file story_gui.cpp GUI for stories. */
12 #include "stdafx.h"
13 #include "window_gui.h"
14 #include "strings_func.h"
15 #include "date_func.h"
16 #include "gui.h"
17 #include "story_base.h"
18 #include "core/geometry_func.hpp"
19 #include "company_func.h"
20 #include "command_func.h"
21 #include "widgets/dropdown_type.h"
22 #include "widgets/dropdown_func.h"
23 #include "sortlist_type.h"
24 #include "goal_base.h"
25 #include "viewport_func.h"
26 #include "window_func.h"
27 #include "company_base.h"
29 #include "widgets/story_widget.h"
31 #include "table/strings.h"
32 #include "table/sprites.h"
34 typedef GUIList<const StoryPage*> GUIStoryPageList;
35 typedef GUIList<const StoryPageElement*> GUIStoryPageElementList;
37 struct StoryBookWindow : Window {
38 protected:
39 Scrollbar *vscroll; ///< Scrollbar of the page text.
41 GUIStoryPageList story_pages; ///< Sorted list of pages.
42 GUIStoryPageElementList story_page_elements; ///< Sorted list of page elements that belong to the current page.
43 StoryPageID selected_page_id; ///< Pool index of selected page.
44 char selected_generic_title[255]; ///< If the selected page doesn't have a custom title, this buffer is used to store a generic page title.
46 static GUIStoryPageList::SortFunction * const page_sorter_funcs[];
47 static GUIStoryPageElementList::SortFunction * const page_element_sorter_funcs[];
49 /** (Re)Build story page list. */
50 void BuildStoryPageList()
52 if (this->story_pages.NeedRebuild()) {
53 this->story_pages.Clear();
55 const StoryPage *p;
56 FOR_ALL_STORY_PAGES(p) {
57 if (this->IsPageAvailable(p)) {
58 *this->story_pages.Append() = p;
62 this->story_pages.Compact();
63 this->story_pages.RebuildDone();
66 this->story_pages.Sort();
69 /** Sort story pages by order value. */
70 static int CDECL PageOrderSorter(const StoryPage * const *a, const StoryPage * const *b)
72 return (*a)->sort_value - (*b)->sort_value;
75 /** (Re)Build story page element list. */
76 void BuildStoryPageElementList()
78 if (this->story_page_elements.NeedRebuild()) {
79 this->story_page_elements.Clear();
81 const StoryPage *p = GetSelPage();
82 if (p != NULL) {
83 const StoryPageElement *pe;
84 FOR_ALL_STORY_PAGE_ELEMENTS(pe) {
85 if (pe->page == p->index) {
86 *this->story_page_elements.Append() = pe;
91 this->story_page_elements.Compact();
92 this->story_page_elements.RebuildDone();
95 this->story_page_elements.Sort();
98 /** Sort story page elements by order value. */
99 static int CDECL PageElementOrderSorter(const StoryPageElement * const *a, const StoryPageElement * const *b)
101 return (*a)->sort_value - (*b)->sort_value;
105 * Checks if a given page should be visible in the story book.
106 * @param page The page to check.
107 * @return True if the page should be visible, otherwise false.
109 bool IsPageAvailable(const StoryPage *page) const
111 return page->company == INVALID_COMPANY || page->company == this->window_number;
115 * Get instance of selected page.
116 * @return Instance of selected page or NULL if no page is selected.
118 StoryPage *GetSelPage() const
120 if (!StoryPage::pool.IsValidID(selected_page_id)) return NULL;
121 return StoryPage::pool.Get(selected_page_id);
125 * Get the page number of selected page.
126 * @return Number of available pages before to the selected one, or -1 if no page is selected.
128 int GetSelPageNum() const
130 int page_number = 0;
131 for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) {
132 const StoryPage *p = *iter;
133 if (p->index == this->selected_page_id) {
134 return page_number;
136 page_number++;
138 return -1;
142 * Check if the selected page is also the first available page.
144 bool IsFirstPageSelected()
146 /* Verify that the selected page exist. */
147 if (!StoryPage::pool.IsValidID(this->selected_page_id)) return false;
149 return (*this->story_pages.Begin())->index == this->selected_page_id;
153 * Check if the selected page is also the last available page.
155 bool IsLastPageSelected()
157 /* Verify that the selected page exist. */
158 if (!StoryPage::pool.IsValidID(this->selected_page_id)) return false;
160 if (this->story_pages.Length() <= 1) return true;
161 const StoryPage *last = *(this->story_pages.End() - 1);
162 return last->index == this->selected_page_id;
166 * Updates the content of selected page.
168 void RefreshSelectedPage()
170 /* Generate generic title if selected page have no custom title. */
171 StoryPage *page = this->GetSelPage();
172 if (page != NULL && page->title == NULL) {
173 SetDParam(0, GetSelPageNum() + 1);
174 GetString (selected_generic_title, STR_STORY_BOOK_GENERIC_PAGE_ITEM);
177 this->story_page_elements.ForceRebuild();
178 this->BuildStoryPageElementList();
180 this->vscroll->SetCount(this->GetContentHeight());
181 this->SetWidgetDirty(WID_SB_SCROLLBAR);
182 this->SetWidgetDirty(WID_SB_SEL_PAGE);
183 this->SetWidgetDirty(WID_SB_PAGE_PANEL);
187 * Selects the previous available page before the currently selected page.
189 void SelectPrevPage()
191 if (!StoryPage::pool.IsValidID(this->selected_page_id)) return;
193 /* Find the last available page which is previous to the current selected page. */
194 const StoryPage *last_available;
195 last_available = NULL;
196 for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) {
197 const StoryPage *p = *iter;
198 if (p->index == this->selected_page_id) {
199 if (last_available == NULL) return; // No previous page available.
200 this->SetSelectedPage(last_available->index);
201 return;
203 last_available = p;
208 * Selects the next available page after the currently selected page.
210 void SelectNextPage()
212 if (!StoryPage::pool.IsValidID(this->selected_page_id)) return;
214 /* Find selected page. */
215 for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) {
216 const StoryPage *p = *iter;
217 if (p->index == this->selected_page_id) {
218 /* Select the page after selected page. */
219 iter++;
220 if (iter != this->story_pages.End()) {
221 this->SetSelectedPage((*iter)->index);
223 return;
229 * Builds the page selector drop down list.
231 DropDownList *BuildDropDownList() const
233 DropDownList *list = new DropDownList();
234 uint16 page_num = 1;
235 for (const StoryPage *const*iter = this->story_pages.Begin(); iter != this->story_pages.End(); iter++) {
236 const StoryPage *p = *iter;
237 bool current_page = p->index == this->selected_page_id;
238 DropDownListStringItem *item = NULL;
239 if (p->title != NULL) {
240 item = new DropDownListCharStringItem(p->title, p->index, current_page);
241 } else {
242 /* No custom title => use a generic page title with page number. */
243 DropDownListParamStringItem *str_item =
244 new DropDownListParamStringItem(STR_STORY_BOOK_GENERIC_PAGE_ITEM, p->index, current_page);
245 str_item->SetParam(0, page_num);
246 item = str_item;
249 *list->Append() = item;
250 page_num++;
253 /* Check if list is empty. */
254 if (list->Length() == 0) {
255 delete list;
256 list = NULL;
259 return list;
263 * Get the width available for displaying content on the page panel.
265 uint GetAvailablePageContentWidth()
267 return this->GetWidget<NWidgetCore>(WID_SB_PAGE_PANEL)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMERECT_RIGHT;
271 * Counts how many pixels of height that are used by Date and Title
272 * (excluding marginal after Title, as each body element has
273 * an empty row before the elment).
274 * @param max_width Available width to display content.
275 * @return the height in pixels.
277 uint GetHeadHeight(int max_width) const
279 StoryPage *page = this->GetSelPage();
280 if (page == NULL) return 0;
281 int height = 0;
283 /* Title lines */
284 height += FONT_HEIGHT_NORMAL; // Date always use exactly one line.
285 SetDParamStr(0, page->title != NULL ? page->title : this->selected_generic_title);
286 height += GetStringHeight(STR_STORY_BOOK_TITLE, max_width);
288 return height;
292 * Decides which sprite to display for a given page element.
293 * @param pe The page element.
294 * @return The SpriteID of the sprite to display.
295 * @pre pe.type must be SPET_GOAL or SPET_LOCATION.
297 SpriteID GetPageElementSprite(const StoryPageElement &pe) const
299 switch (pe.type) {
300 case SPET_GOAL: {
301 Goal *g = Goal::Get((GoalID) pe.referenced_id);
302 if (g == NULL) return SPR_IMG_GOAL_BROKEN_REF;
303 return g->completed ? SPR_IMG_GOAL_COMPLETED : SPR_IMG_GOAL;
305 case SPET_LOCATION:
306 return SPR_IMG_VIEW_LOCATION;
307 default:
308 NOT_REACHED();
313 * Get the height in pixels used by a page element.
314 * @param pe The story page element.
315 * @param max_width Available width to display content.
316 * @return the height in pixels.
318 uint GetPageElementHeight(const StoryPageElement &pe, int max_width)
320 switch (pe.type) {
321 default:
322 NOT_REACHED();
324 case SPET_TEXT:
325 SetDParamStr(0, pe.text);
326 return GetStringHeight(STR_BLACK_RAW_STRING, max_width);
328 case SPET_GOAL:
329 case SPET_LOCATION: {
330 Dimension sprite_dim = GetSpriteSize(GetPageElementSprite(pe));
331 return sprite_dim.height;
337 * Get the total height of the content displayed
338 * in this window.
339 * @return the height in pixels
341 uint GetContentHeight()
343 StoryPage *page = this->GetSelPage();
344 if (page == NULL) return 0;
345 int max_width = GetAvailablePageContentWidth();
346 uint element_vertical_dist = FONT_HEIGHT_NORMAL;
348 /* Head */
349 uint height = GetHeadHeight(max_width);
351 /* Body */
352 for (const StoryPageElement **iter = this->story_page_elements.Begin(); iter != this->story_page_elements.End(); iter++) {
353 const StoryPageElement *pe = *iter;
354 height += element_vertical_dist;
355 height += GetPageElementHeight(*pe, max_width);
358 return height;
362 * Internal event handler for when a page element is clicked.
363 * @param pe The clicked page element.
365 void OnPageElementClick(const StoryPageElement& pe)
367 switch (pe.type) {
368 case SPET_TEXT:
369 /* Do nothing. */
370 break;
372 case SPET_LOCATION:
373 if (_ctrl_pressed) {
374 ShowExtraViewPortWindow((TileIndex)pe.referenced_id);
375 } else {
376 ScrollMainWindowToTile((TileIndex)pe.referenced_id);
378 break;
380 case SPET_GOAL:
381 ShowGoalsList((CompanyID)this->window_number);
382 break;
384 default:
385 NOT_REACHED();
389 public:
390 StoryBookWindow (const WindowDesc *desc, WindowNumber window_number) :
391 Window (desc), vscroll (NULL),
392 story_pages(), story_page_elements(), selected_page_id (0)
394 this->CreateNestedTree();
395 this->vscroll = this->GetScrollbar(WID_SB_SCROLLBAR);
396 this->vscroll->SetStepSize(FONT_HEIGHT_NORMAL);
398 /* Initalize page sort. */
399 this->story_pages.SetSortFuncs(StoryBookWindow::page_sorter_funcs);
400 this->story_pages.ForceRebuild();
401 this->BuildStoryPageList();
402 this->story_page_elements.SetSortFuncs(StoryBookWindow::page_element_sorter_funcs);
403 /* story_page_elements will get built by SetSelectedPage */
405 this->InitNested(window_number);
406 this->owner = (Owner)this->window_number;
408 /* Initialize selected vars. */
409 this->selected_generic_title[0] = '\0';
410 this->selected_page_id = INVALID_STORY_PAGE;
412 this->OnInvalidateData(-1);
416 * Updates the disabled state of the prev/next buttons.
418 void UpdatePrevNextDisabledState()
420 this->SetWidgetDisabledState(WID_SB_PREV_PAGE, story_pages.Length() == 0 || this->IsFirstPageSelected());
421 this->SetWidgetDisabledState(WID_SB_NEXT_PAGE, story_pages.Length() == 0 || this->IsLastPageSelected());
422 this->SetWidgetDirty(WID_SB_PREV_PAGE);
423 this->SetWidgetDirty(WID_SB_NEXT_PAGE);
427 * Sets the selected page.
428 * @param page_index pool index of the page to select.
430 void SetSelectedPage(uint16 page_index)
432 if (this->selected_page_id != page_index) {
433 this->selected_page_id = page_index;
434 this->RefreshSelectedPage();
435 this->UpdatePrevNextDisabledState();
439 virtual void SetStringParameters(int widget) const
441 switch (widget) {
442 case WID_SB_SEL_PAGE: {
443 StoryPage *page = this->GetSelPage();
444 SetDParamStr(0, page != NULL && page->title != NULL ? page->title : this->selected_generic_title);
445 break;
447 case WID_SB_CAPTION:
448 if (this->window_number == INVALID_COMPANY) {
449 SetDParam(0, STR_STORY_BOOK_SPECTATOR_CAPTION);
450 } else {
451 SetDParam(0, STR_STORY_BOOK_CAPTION);
452 SetDParam(1, this->window_number);
454 break;
458 void OnPaint (BlitArea *dpi) OVERRIDE
460 /* Detect if content has changed height. This can happen if a
461 * multi-line text contains eg. {COMPANY} and that company is
462 * renamed.
464 if (this->vscroll->GetCount() != this->GetContentHeight()) {
465 this->vscroll->SetCount(this->GetContentHeight());
466 this->SetWidgetDirty(WID_SB_SCROLLBAR);
467 this->SetWidgetDirty(WID_SB_PAGE_PANEL);
470 this->DrawWidgets (dpi);
473 void DrawWidget (BlitArea *dpi, const Rect &r, int widget) const OVERRIDE
475 if (widget != WID_SB_PAGE_PANEL) return;
477 StoryPage *page = this->GetSelPage();
478 if (page == NULL) return;
480 const int x = r.left + WD_FRAMETEXT_LEFT;
481 const int y = r.top + WD_FRAMETEXT_TOP;
482 const int width = r.right - WD_FRAMETEXT_RIGHT - x;
483 const int height = r.bottom - WD_FRAMETEXT_BOTTOM - y;
485 /* Set up a clipping region for the panel. */
486 BlitArea tmp_dpi;
487 if (!InitBlitArea (dpi, &tmp_dpi, x, y, width + 1, height + 1)) return;
489 /* Draw content (now coordinates given to Draw** are local to the new clipping region). */
490 int line_height = FONT_HEIGHT_NORMAL;
491 int y_offset = - this->vscroll->GetPosition();
493 /* Date */
494 if (page->date != INVALID_DATE) {
495 SetDParam(0, page->date);
496 DrawString (&tmp_dpi, 0, width, y_offset, STR_JUST_DATE_LONG, TC_BLACK);
498 y_offset += line_height;
500 /* Title */
501 SetDParamStr(0, page->title != NULL ? page->title : this->selected_generic_title);
502 y_offset = DrawStringMultiLine (&tmp_dpi, 0, width, y_offset, height, STR_STORY_BOOK_TITLE, TC_BLACK, SA_TOP | SA_HOR_CENTER);
504 /* Page elements */
505 for (const StoryPageElement *const*iter = this->story_page_elements.Begin(); iter != this->story_page_elements.End(); iter++) {
506 const StoryPageElement *const pe = *iter;
507 y_offset += line_height; // margin to previous element
509 if (pe->type == SPET_TEXT) {
510 SetDParamStr (0, pe->text);
511 y_offset = DrawStringMultiLine (&tmp_dpi, 0, width, y_offset, height, STR_JUST_RAW_STRING, TC_BLACK, SA_TOP | SA_LEFT);
512 } else {
513 StringID string_id;
514 if (pe->type == SPET_GOAL) {
515 Goal *g = Goal::Get ((GoalID) pe->referenced_id);
516 if (g != NULL) SetDParamStr (0, g->text);
517 string_id = g == NULL ? STR_STORY_BOOK_INVALID_GOAL_REF : STR_JUST_RAW_STRING;
518 } else {
519 assert (pe->type == SPET_LOCATION);
520 SetDParamStr (0, pe->text);
521 string_id = STR_JUST_RAW_STRING;
524 SpriteID sprite = GetPageElementSprite (*pe);
526 Dimension sprite_dim = GetSpriteSize (sprite);
527 uint element_height = max (sprite_dim.height, (uint)line_height);
529 uint sprite_top = y_offset + (element_height - sprite_dim.height) / 2;
530 uint text_top = y_offset + (element_height - line_height) / 2;
532 DrawSprite (&tmp_dpi, sprite, PAL_NONE, 0, sprite_top);
533 DrawString (&tmp_dpi, sprite_dim.width + WD_FRAMETEXT_LEFT, width, text_top, string_id, TC_BLACK);
535 y_offset += element_height;
540 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
542 if (widget != WID_SB_SEL_PAGE && widget != WID_SB_PAGE_PANEL) return;
544 Dimension d;
545 d.height = FONT_HEIGHT_NORMAL;
546 d.width = 0;
548 switch (widget) {
549 case WID_SB_SEL_PAGE: {
551 /* Get max title width. */
552 for (uint16 i = 0; i < this->story_pages.Length(); i++) {
553 const StoryPage *s = this->story_pages[i];
555 if (s->title != NULL) {
556 SetDParamStr(0, s->title);
557 } else {
558 SetDParamStr(0, this->selected_generic_title);
560 Dimension title_d = GetStringBoundingBox(STR_BLACK_RAW_STRING);
562 if (title_d.width > d.width) {
563 d.width = title_d.width;
567 d.width += padding.width;
568 d.height += padding.height;
569 *size = maxdim(*size, d);
570 break;
573 case WID_SB_PAGE_PANEL: {
574 d.height *= 5;
575 d.height += padding.height + WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM;
576 *size = maxdim(*size, d);
577 break;
583 virtual void OnResize()
585 this->vscroll->SetCapacityFromWidget(this, WID_SB_PAGE_PANEL, WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM);
586 this->vscroll->SetCount(this->GetContentHeight());
589 virtual void OnClick(Point pt, int widget, int click_count)
591 switch (widget) {
592 case WID_SB_SEL_PAGE: {
593 DropDownList *list = this->BuildDropDownList();
594 if (list != NULL) {
595 /* Get the index of selected page. */
596 int selected = 0;
597 for (uint16 i = 0; i < this->story_pages.Length(); i++) {
598 const StoryPage *p = this->story_pages[i];
599 if (p->index == this->selected_page_id) break;
600 selected++;
603 ShowDropDownList(this, list, selected, widget);
605 break;
608 case WID_SB_PREV_PAGE:
609 this->SelectPrevPage();
610 break;
612 case WID_SB_NEXT_PAGE:
613 this->SelectNextPage();
614 break;
616 case WID_SB_PAGE_PANEL: {
617 uint clicked_y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SB_PAGE_PANEL, WD_FRAMETEXT_TOP);
618 uint max_width = GetAvailablePageContentWidth();
620 /* Skip head rows. */
621 uint head_height = this->GetHeadHeight(max_width);
622 if (clicked_y < head_height) return;
624 /* Detect if a page element was clicked. */
625 uint y = head_height;
626 uint element_vertical_dist = FONT_HEIGHT_NORMAL;
627 for (const StoryPageElement *const*iter = this->story_page_elements.Begin(); iter != this->story_page_elements.End(); iter++) {
628 const StoryPageElement *const pe = *iter;
630 y += element_vertical_dist; // margin row
632 uint content_height = GetPageElementHeight(*pe, max_width);
633 if (clicked_y >= y && clicked_y < y + content_height) {
634 this->OnPageElementClick(*pe);
635 return;
638 y += content_height;
644 virtual void OnDropdownSelect(int widget, int index)
646 if (widget != WID_SB_SEL_PAGE) return;
648 /* index (which is set in BuildDropDownList) is the page id. */
649 this->SetSelectedPage(index);
653 * Some data on this window has become invalid.
654 * @param data Information about the changed data.
655 * -1 Rebuild page list and refresh current page;
656 * >= 0 Id of the page that needs to be refreshed. If it is not the current page, nothing happens.
657 * @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.
659 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
661 if (!gui_scope) return;
663 /* If added/removed page, force rebuild. Sort order never change so just a
664 * re-sort is never needed.
666 if (data == -1) {
667 this->story_pages.ForceRebuild();
668 this->BuildStoryPageList();
670 /* Was the last page removed? */
671 if (this->story_pages.Length() == 0) {
672 this->selected_generic_title[0] = '\0';
675 /* Verify page selection. */
676 if (!StoryPage::pool.IsValidID(this->selected_page_id)) {
677 this->selected_page_id = INVALID_STORY_PAGE;
679 if (this->selected_page_id == INVALID_STORY_PAGE && this->story_pages.Length() > 0) {
680 /* No page is selected, but there exist at least one available.
681 * => Select first page.
683 this->SetSelectedPage(this->story_pages[0]->index);
686 this->SetWidgetDisabledState(WID_SB_SEL_PAGE, this->story_pages.Length() == 0);
687 this->SetWidgetDirty(WID_SB_SEL_PAGE);
688 this->UpdatePrevNextDisabledState();
689 } else if (data >= 0 && this->selected_page_id == data) {
690 this->RefreshSelectedPage();
695 GUIStoryPageList::SortFunction * const StoryBookWindow::page_sorter_funcs[] = {
696 &PageOrderSorter,
699 GUIStoryPageElementList::SortFunction * const StoryBookWindow::page_element_sorter_funcs[] = {
700 &PageElementOrderSorter,
703 static const NWidgetPart _nested_story_book_widgets[] = {
704 NWidget(NWID_HORIZONTAL),
705 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
706 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_SB_CAPTION), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
707 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
708 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
709 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
710 EndContainer(),
711 NWidget(NWID_HORIZONTAL), SetFill(1, 1),
712 NWidget(NWID_VERTICAL), SetFill(1, 1),
713 NWidget(WWT_PANEL, COLOUR_BROWN, WID_SB_PAGE_PANEL), SetResize(1, 1), SetScrollbar(WID_SB_SCROLLBAR), EndContainer(),
714 NWidget(NWID_HORIZONTAL),
715 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),
716 NWidget(NWID_BUTTON_DROPDOWN, COLOUR_BROWN, WID_SB_SEL_PAGE), SetMinimalSize(93, 12), SetFill(1, 0),
717 SetDataTip(STR_BLACK_RAW_STRING, STR_STORY_BOOK_SEL_PAGE_TOOLTIP), SetResize(1, 0),
718 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),
719 EndContainer(),
720 EndContainer(),
721 NWidget(NWID_VERTICAL), SetFill(0, 1),
722 NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_SB_SCROLLBAR),
723 NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
724 EndContainer(),
725 EndContainer(),
728 static WindowDesc::Prefs _story_book_prefs ("view_story");
730 static const WindowDesc _story_book_desc(
731 WDP_CENTER, 400, 300,
732 WC_STORY_BOOK, WC_NONE,
734 _nested_story_book_widgets, lengthof(_nested_story_book_widgets),
735 &_story_book_prefs
739 * Raise or create the story book window for \a company, at page \a page_id.
740 * @param company 'Owner' of the story book, may be #INVALID_COMPANY.
741 * @param page_id Page to open, may be #INVALID_STORY_PAGE.
743 void ShowStoryBook(CompanyID company, uint16 page_id)
745 if (!Company::IsValidID(company)) company = (CompanyID)INVALID_COMPANY;
747 StoryBookWindow *w = AllocateWindowDescFront<StoryBookWindow>(&_story_book_desc, company, true);
748 if (page_id != INVALID_STORY_PAGE) w->SetSelectedPage(page_id);