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 (!_story_page_pool
.IsValidID(selected_page_id
)) return NULL
;
121 return _story_page_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 (!_story_page_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 (!_story_page_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
, lastof(selected_generic_title
));
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 (!_story_page_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 (!_story_page_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 * Draws a page element that is composed of a sprite to the left and a single line of
363 * text after that. These page elements are generally clickable and are thus called
365 * @param y_offset Current y_offset which will get updated when this method has completed its drawing.
366 * @param width Width of the region available for drawing.
367 * @param line_height Height of one line of text.
368 * @param action_sprite The sprite to draw.
369 * @param string_id The string id to draw.
370 * @return the number of lines.
372 void DrawActionElement(int &y_offset
, int width
, int line_height
, SpriteID action_sprite
, StringID string_id
= STR_JUST_RAW_STRING
) const
374 Dimension sprite_dim
= GetSpriteSize(action_sprite
);
375 uint element_height
= max(sprite_dim
.height
, (uint
)line_height
);
377 uint sprite_top
= y_offset
+ (element_height
- sprite_dim
.height
) / 2;
378 uint text_top
= y_offset
+ (element_height
- line_height
) / 2;
380 DrawSprite(action_sprite
, PAL_NONE
, 0, sprite_top
);
381 DrawString(sprite_dim
.width
+ WD_FRAMETEXT_LEFT
, width
, text_top
, string_id
, TC_BLACK
);
383 y_offset
+= element_height
;
387 * Internal event handler for when a page element is clicked.
388 * @param pe The clicked page element.
390 void OnPageElementClick(const StoryPageElement
& pe
)
399 ShowExtraViewPortWindow((TileIndex
)pe
.referenced_id
);
401 ScrollMainWindowToTile((TileIndex
)pe
.referenced_id
);
406 ShowGoalsList((CompanyID
)this->window_number
);
415 StoryBookWindow(WindowDesc
*desc
, WindowNumber window_number
) : Window(desc
)
417 this->CreateNestedTree();
418 this->vscroll
= this->GetScrollbar(WID_SB_SCROLLBAR
);
419 this->vscroll
->SetStepSize(FONT_HEIGHT_NORMAL
);
421 /* Initalize page sort. */
422 this->story_pages
.SetSortFuncs(StoryBookWindow::page_sorter_funcs
);
423 this->story_pages
.ForceRebuild();
424 this->BuildStoryPageList();
425 this->story_page_elements
.SetSortFuncs(StoryBookWindow::page_element_sorter_funcs
);
426 /* story_page_elements will get built by SetSelectedPage */
428 this->FinishInitNested(window_number
);
429 this->owner
= (Owner
)this->window_number
;
431 /* Initialize selected vars. */
432 this->selected_generic_title
[0] = '\0';
433 this->selected_page_id
= INVALID_STORY_PAGE
;
435 this->OnInvalidateData(-1);
439 * Updates the disabled state of the prev/next buttons.
441 void UpdatePrevNextDisabledState()
443 this->SetWidgetDisabledState(WID_SB_PREV_PAGE
, story_pages
.Length() == 0 || this->IsFirstPageSelected());
444 this->SetWidgetDisabledState(WID_SB_NEXT_PAGE
, story_pages
.Length() == 0 || this->IsLastPageSelected());
445 this->SetWidgetDirty(WID_SB_PREV_PAGE
);
446 this->SetWidgetDirty(WID_SB_NEXT_PAGE
);
450 * Sets the selected page.
451 * @param page_index pool index of the page to select.
453 void SetSelectedPage(uint16 page_index
)
455 if (this->selected_page_id
!= page_index
) {
456 this->selected_page_id
= page_index
;
457 this->RefreshSelectedPage();
458 this->UpdatePrevNextDisabledState();
462 virtual void SetStringParameters(int widget
) const
465 case WID_SB_SEL_PAGE
: {
466 StoryPage
*page
= this->GetSelPage();
467 SetDParamStr(0, page
!= NULL
&& page
->title
!= NULL
? page
->title
: this->selected_generic_title
);
471 if (this->window_number
== INVALID_COMPANY
) {
472 SetDParam(0, STR_STORY_BOOK_SPECTATOR_CAPTION
);
474 SetDParam(0, STR_STORY_BOOK_CAPTION
);
475 SetDParam(1, this->window_number
);
481 virtual void OnPaint()
483 /* Detect if content has changed height. This can happen if a
484 * multi-line text contains eg. {COMPANY} and that company is
487 if (this->vscroll
->GetCount() != this->GetContentHeight()) {
488 this->vscroll
->SetCount(this->GetContentHeight());
489 this->SetWidgetDirty(WID_SB_SCROLLBAR
);
490 this->SetWidgetDirty(WID_SB_PAGE_PANEL
);
496 virtual void DrawWidget(const Rect
&r
, int widget
) const
498 if (widget
!= WID_SB_PAGE_PANEL
) return;
500 StoryPage
*page
= this->GetSelPage();
501 if (page
== NULL
) return;
503 const int x
= r
.left
+ WD_FRAMETEXT_LEFT
;
504 const int y
= r
.top
+ WD_FRAMETEXT_TOP
;
505 const int right
= r
.right
- WD_FRAMETEXT_RIGHT
;
506 const int bottom
= r
.bottom
- WD_FRAMETEXT_BOTTOM
;
508 /* Set up a clipping region for the panel. */
509 DrawPixelInfo tmp_dpi
;
510 if (!FillDrawPixelInfo(&tmp_dpi
, x
, y
, right
- x
+ 1, bottom
- y
+ 1)) return;
512 DrawPixelInfo
*old_dpi
= _cur_dpi
;
515 /* Draw content (now coordinates given to Draw** are local to the new clipping region). */
516 int line_height
= FONT_HEIGHT_NORMAL
;
517 int y_offset
= - this->vscroll
->GetPosition();
520 SetDParam(0, page
->date
);
521 DrawString(0, right
- x
, y_offset
, STR_JUST_DATE_LONG
, TC_BLACK
);
522 y_offset
+= line_height
;
525 SetDParamStr(0, page
->title
!= NULL
? page
->title
: this->selected_generic_title
);
526 y_offset
= DrawStringMultiLine(0, right
- x
, y_offset
, bottom
- y
, STR_STORY_BOOK_TITLE
, TC_BLACK
, SA_TOP
| SA_HOR_CENTER
);
529 for (const StoryPageElement
*const*iter
= this->story_page_elements
.Begin(); iter
!= this->story_page_elements
.End(); iter
++) {
530 const StoryPageElement
*const pe
= *iter
;
531 y_offset
+= line_height
; // margin to previous element
535 SetDParamStr(0, pe
->text
);
536 y_offset
= DrawStringMultiLine(0, right
- x
, y_offset
, bottom
- y
, STR_JUST_RAW_STRING
, TC_BLACK
, SA_TOP
| SA_LEFT
);
540 Goal
*g
= Goal::Get((GoalID
) pe
->referenced_id
);
541 StringID string_id
= g
== NULL
? STR_STORY_BOOK_INVALID_GOAL_REF
: STR_JUST_RAW_STRING
;
542 if (g
!= NULL
) SetDParamStr(0, g
->text
);
543 DrawActionElement(y_offset
, right
- x
, line_height
, GetPageElementSprite(*pe
), string_id
);
548 SetDParamStr(0, pe
->text
);
549 DrawActionElement(y_offset
, right
- x
, line_height
, GetPageElementSprite(*pe
));
552 default: NOT_REACHED();
556 /* Restore clipping region. */
560 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
562 if (widget
!= WID_SB_SEL_PAGE
&& widget
!= WID_SB_PAGE_PANEL
) return;
565 d
.height
= FONT_HEIGHT_NORMAL
;
569 case WID_SB_SEL_PAGE
: {
571 /* Get max title width. */
572 for (uint16 i
= 0; i
< this->story_pages
.Length(); i
++) {
573 const StoryPage
*s
= this->story_pages
[i
];
575 if (s
->title
!= NULL
) {
576 SetDParamStr(0, s
->title
);
578 SetDParamStr(0, this->selected_generic_title
);
580 Dimension title_d
= GetStringBoundingBox(STR_BLACK_RAW_STRING
);
582 if (title_d
.width
> d
.width
) {
583 d
.width
= title_d
.width
;
587 d
.width
+= padding
.width
;
588 d
.height
+= padding
.height
;
589 *size
= maxdim(*size
, d
);
593 case WID_SB_PAGE_PANEL
: {
595 d
.height
+= padding
.height
+ WD_FRAMETEXT_TOP
+ WD_FRAMETEXT_BOTTOM
;
596 *size
= maxdim(*size
, d
);
603 virtual void OnResize()
605 this->vscroll
->SetCapacityFromWidget(this, WID_SB_PAGE_PANEL
, WD_FRAMETEXT_TOP
+ WD_FRAMETEXT_BOTTOM
);
606 this->vscroll
->SetCount(this->GetContentHeight());
609 virtual void OnClick(Point pt
, int widget
, int click_count
)
612 case WID_SB_SEL_PAGE
: {
613 DropDownList
*list
= this->BuildDropDownList();
615 /* Get the index of selected page. */
617 for (uint16 i
= 0; i
< this->story_pages
.Length(); i
++) {
618 const StoryPage
*p
= this->story_pages
[i
];
619 if (p
->index
== this->selected_page_id
) break;
623 ShowDropDownList(this, list
, selected
, widget
);
628 case WID_SB_PREV_PAGE
:
629 this->SelectPrevPage();
632 case WID_SB_NEXT_PAGE
:
633 this->SelectNextPage();
636 case WID_SB_PAGE_PANEL
: {
637 uint clicked_y
= this->vscroll
->GetScrolledRowFromWidget(pt
.y
, this, WID_SB_PAGE_PANEL
, WD_FRAMETEXT_TOP
);
638 uint max_width
= GetAvailablePageContentWidth();
640 /* Skip head rows. */
641 uint head_height
= this->GetHeadHeight(max_width
);
642 if (clicked_y
< head_height
) return;
644 /* Detect if a page element was clicked. */
645 uint y
= head_height
;
646 uint element_vertical_dist
= FONT_HEIGHT_NORMAL
;
647 for (const StoryPageElement
*const*iter
= this->story_page_elements
.Begin(); iter
!= this->story_page_elements
.End(); iter
++) {
648 const StoryPageElement
*const pe
= *iter
;
650 y
+= element_vertical_dist
; // margin row
652 uint content_height
= GetPageElementHeight(*pe
, max_width
);
653 if (clicked_y
>= y
&& clicked_y
< y
+ content_height
) {
654 this->OnPageElementClick(*pe
);
664 virtual void OnDropdownSelect(int widget
, int index
)
666 if (widget
!= WID_SB_SEL_PAGE
) return;
668 /* index (which is set in BuildDropDownList) is the page id. */
669 this->SetSelectedPage(index
);
673 * Some data on this window has become invalid.
674 * @param data Information about the changed data.
675 * -1 Rebuild page list and refresh current page;
676 * >= 0 Id of the page that needs to be refreshed. If it is not the current page, nothing happens.
677 * @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.
679 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
681 if (!gui_scope
) return;
683 /* If added/removed page, force rebuild. Sort order never change so just a
684 * re-sort is never needed.
687 this->story_pages
.ForceRebuild();
688 this->BuildStoryPageList();
690 /* Was the last page removed? */
691 if (this->story_pages
.Length() == 0) {
692 this->selected_generic_title
[0] = '\0';
695 /* Verify page selection. */
696 if (!_story_page_pool
.IsValidID(this->selected_page_id
)) {
697 this->selected_page_id
= INVALID_STORY_PAGE
;
699 if (this->selected_page_id
== INVALID_STORY_PAGE
&& this->story_pages
.Length() > 0) {
700 /* No page is selected, but there exist at least one available.
701 * => Select first page.
703 this->SetSelectedPage(this->story_pages
[0]->index
);
706 this->SetWidgetDisabledState(WID_SB_SEL_PAGE
, this->story_pages
.Length() == 0);
707 this->SetWidgetDirty(WID_SB_SEL_PAGE
);
708 this->UpdatePrevNextDisabledState();
709 } else if (data
>= 0 && this->selected_page_id
== data
) {
710 this->RefreshSelectedPage();
715 GUIStoryPageList::SortFunction
* const StoryBookWindow::page_sorter_funcs
[] = {
719 GUIStoryPageElementList::SortFunction
* const StoryBookWindow::page_element_sorter_funcs
[] = {
720 &PageElementOrderSorter
,
723 static const NWidgetPart _nested_story_book_widgets
[] = {
724 NWidget(NWID_HORIZONTAL
),
725 NWidget(WWT_CLOSEBOX
, COLOUR_BROWN
),
726 NWidget(WWT_CAPTION
, COLOUR_BROWN
, WID_SB_CAPTION
), SetDataTip(STR_JUST_STRING
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
727 NWidget(WWT_SHADEBOX
, COLOUR_BROWN
),
728 NWidget(WWT_DEFSIZEBOX
, COLOUR_BROWN
),
729 NWidget(WWT_STICKYBOX
, COLOUR_BROWN
),
731 NWidget(NWID_HORIZONTAL
), SetFill(1, 1),
732 NWidget(NWID_VERTICAL
), SetFill(1, 1),
733 NWidget(WWT_PANEL
, COLOUR_BROWN
, WID_SB_PAGE_PANEL
), SetResize(1, 1), SetScrollbar(WID_SB_SCROLLBAR
), EndContainer(),
734 NWidget(NWID_HORIZONTAL
),
735 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
),
736 NWidget(NWID_BUTTON_DROPDOWN
, COLOUR_BROWN
, WID_SB_SEL_PAGE
), SetMinimalSize(93, 12), SetFill(1, 0),
737 SetDataTip(STR_BLACK_RAW_STRING
, STR_STORY_BOOK_SEL_PAGE_TOOLTIP
), SetResize(1, 0),
738 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
),
741 NWidget(NWID_VERTICAL
), SetFill(0, 1),
742 NWidget(NWID_VSCROLLBAR
, COLOUR_BROWN
, WID_SB_SCROLLBAR
),
743 NWidget(WWT_RESIZEBOX
, COLOUR_BROWN
),
748 static WindowDesc
_story_book_desc(
749 WDP_CENTER
, "view_story", 400, 300,
750 WC_STORY_BOOK
, WC_NONE
,
752 _nested_story_book_widgets
, lengthof(_nested_story_book_widgets
)
755 void ShowStoryBook(CompanyID company
, uint16 page_id
)
757 if (!Company::IsValidID(company
)) company
= (CompanyID
)INVALID_COMPANY
;
759 StoryBookWindow
*w
= AllocateWindowDescFront
<StoryBookWindow
>(&_story_book_desc
, company
);
760 if (page_id
!= INVALID_STORY_PAGE
) {
761 if (w
== NULL
) w
= (StoryBookWindow
*)FindWindowById(WC_STORY_BOOK
, company
);
762 w
->SetSelectedPage(page_id
);