4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file story_gui.cpp GUI for stories. */
13 #include "window_gui.h"
14 #include "strings_func.h"
15 #include "date_func.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
{
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();
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();
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
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
) {
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
);
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. */
220 if (iter
!= this->story_pages
.End()) {
221 this->SetSelectedPage((*iter
)->index
);
229 * Builds the page selector drop down list.
231 DropDownList
*BuildDropDownList() const
233 DropDownList
*list
= new DropDownList();
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
);
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
);
249 *list
->Append() = item
;
253 /* Check if list is empty. */
254 if (list
->Length() == 0) {
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;
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
);
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
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
;
306 return SPR_IMG_VIEW_LOCATION
;
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
)
325 SetDParamStr(0, pe
.text
);
326 return GetStringHeight(STR_BLACK_RAW_STRING
, max_width
);
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
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
;
349 uint height
= GetHeadHeight(max_width
);
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
);
362 * Internal event handler for when a page element is clicked.
363 * @param pe The clicked page element.
365 void OnPageElementClick(const StoryPageElement
& pe
)
374 ShowExtraViewPortWindow((TileIndex
)pe
.referenced_id
);
376 ScrollMainWindowToTile((TileIndex
)pe
.referenced_id
);
381 ShowGoalsList((CompanyID
)this->window_number
);
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
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
);
448 if (this->window_number
== INVALID_COMPANY
) {
449 SetDParam(0, STR_STORY_BOOK_SPECTATOR_CAPTION
);
451 SetDParam(0, STR_STORY_BOOK_CAPTION
);
452 SetDParam(1, this->window_number
);
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
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. */
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();
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
;
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
);
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
);
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
;
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;
545 d
.height
= FONT_HEIGHT_NORMAL
;
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
);
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
);
573 case WID_SB_PAGE_PANEL
: {
575 d
.height
+= padding
.height
+ WD_FRAMETEXT_TOP
+ WD_FRAMETEXT_BOTTOM
;
576 *size
= maxdim(*size
, d
);
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
)
592 case WID_SB_SEL_PAGE
: {
593 DropDownList
*list
= this->BuildDropDownList();
595 /* Get the index of selected page. */
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;
603 ShowDropDownList(this, list
, selected
, widget
);
608 case WID_SB_PREV_PAGE
:
609 this->SelectPrevPage();
612 case WID_SB_NEXT_PAGE
:
613 this->SelectNextPage();
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
);
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.
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
[] = {
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
),
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
),
721 NWidget(NWID_VERTICAL
), SetFill(0, 1),
722 NWidget(NWID_VSCROLLBAR
, COLOUR_BROWN
, WID_SB_SCROLLBAR
),
723 NWidget(WWT_RESIZEBOX
, COLOUR_BROWN
),
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
),
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
);