tron@2186: /* $Id$ */ tron@2186: rubidium@10455: /** @file news_gui.cpp GUI functions related to news messages. */ belugas@6605: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" peter1138@3155: #include "gui.h" rubidium@9723: #include "window_gui.h" rubidium@9723: #include "viewport_func.h" rubidium@9826: #include "news_func.h" rubidium@9724: #include "settings_type.h" rubidium@9722: #include "transparency.h" rubidium@9723: #include "strings_func.h" rubidium@9723: #include "window_func.h" rubidium@9723: #include "date_func.h" rubidium@9723: #include "vehicle_base.h" rubidium@9723: #include "sound_func.h" rubidium@9723: #include "string_func.h" rubidium@9724: #include "widgets/dropdown_func.h" rubidium@10355: #include "map_func.h" glx@10645: #include "statusbar_gui.h" glx@10645: #include "player_face.h" rubidium@9724: rubidium@9724: #include "table/sprites.h" rubidium@9724: #include "table/strings.h" truelight@0: rubidium@9703: /** @file news_gui.cpp rubidium@9703: * rubidium@4549: * News system is realized as a FIFO queue (in an array) rubidium@4549: * The positions in the queue can't be rearranged, we only access rubidium@4549: * the array elements through pointers to the elements. Once the rubidium@9703: * array is full, the oldest entry (\a _oldest_news) is being overwritten rubidium@9703: * by the newest (\a _latest_news). rubidium@4549: * rubidium@9703: * \verbatim rubidium@4549: * oldest current lastest rubidium@4549: * | | | rubidium@4549: * [O------------F-------------C---------L ] rubidium@4549: * | rubidium@4549: * forced rubidium@9703: * \endverbatim Darkvater@5254: * Darkvater@5254: * Of course by using an array we can have situations like Darkvater@5254: * rubidium@9703: * \verbatim Darkvater@5254: * [----L O-----F---------C-----------------] Darkvater@5254: * This is where we have wrapped around the array and have Darkvater@5254: * (MAX_NEWS - O) + L news items rubidium@9703: * \endverbatim rubidium@4549: */ dominik@79: belugas@6605: #define NB_WIDG_PER_SETTING 4 tron@427: Darkvater@5246: typedef byte NewsID; tron@427: #define INVALID_NEWS 255 dominik@79: rubidium@9724: NewsItem _statusbar_news_item; rubidium@9724: bool _news_ticker_sound; rubidium@10355: static NewsItem *_news_items = NULL; ///< The news FIFO queue rubidium@10355: static uint _max_news_items = 0; ///< size of news FIFO queue rubidium@9703: static NewsID _current_news = INVALID_NEWS; ///< points to news item that should be shown next rubidium@9703: static NewsID _oldest_news = 0; ///< points to first item in fifo queue rubidium@9703: static NewsID _latest_news = INVALID_NEWS; ///< points to last item in fifo queue rubidium@9703: rubidium@9703: /** Forced news item. rubidium@9703: * Users can force an item by accessing the history or "last message". rubidium@9703: * If the message being shown was forced by the user, its index is stored in rubidium@9703: * _forced_news. Otherwise, \a _forced_news variable is INVALID_NEWS. */ Darkvater@5246: static NewsID _forced_news = INVALID_NEWS; dominik@79: rubidium@10355: static uint _total_news = 0; ///< Number of news items in FIFO queue @see _news_items rubidium@6573: static void MoveToNextItem(); truelight@0: truelight@0: glx@10645: typedef void DrawNewsCallbackProc(struct Window *w, const NewsItem *ni); glx@10645: void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni); glx@10645: glx@10645: static void DrawNewsBankrupcy(Window *w, const NewsItem *ni) glx@10645: { glx@10645: Player *p = GetPlayer((PlayerID)(ni->data_b)); glx@10645: DrawPlayerFace(p->face, p->player_color, 2, 23); glx@10645: GfxFillRect(3, 23, 3 + 91, 23 + 118, PALETTE_TO_STRUCT_GREY | (1 << USE_COLORTABLE)); glx@10645: glx@10645: SetDParam(0, p->index); glx@10645: glx@10645: DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94); glx@10645: glx@10645: switch (ni->subtype) { glx@10645: case NS_COMPANY_TROUBLE: glx@10645: DrawStringCentered(w->width >> 1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, TC_FROMSTRING); glx@10645: glx@10645: SetDParam(0, p->index); glx@10645: glx@10645: DrawStringMultiCenter( glx@10645: ((w->width - 101) >> 1) + 98, glx@10645: 90, glx@10645: STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED, glx@10645: w->width - 101); glx@10645: break; glx@10645: glx@10645: case NS_COMPANY_MERGER: glx@10645: DrawStringCentered(w->width >> 1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, TC_FROMSTRING); glx@10645: SetDParam(0, ni->params[2]); glx@10645: SetDParam(1, p->index); glx@10645: SetDParam(2, ni->params[4]); glx@10645: DrawStringMultiCenter( glx@10645: ((w->width - 101) >> 1) + 98, glx@10645: 90, glx@10645: ni->params[4] == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR, glx@10645: w->width - 101); glx@10645: break; glx@10645: glx@10645: case NS_COMPANY_BANKRUPT: glx@10645: DrawStringCentered(w->width >> 1, 1, STR_705C_BANKRUPT, TC_FROMSTRING); glx@10645: SetDParam(0, p->index); glx@10645: DrawStringMultiCenter( glx@10645: ((w->width - 101) >> 1) + 98, glx@10645: 90, glx@10645: STR_705D_HAS_BEEN_CLOSED_DOWN_BY, glx@10645: w->width - 101); glx@10645: break; glx@10645: glx@10645: case NS_COMPANY_NEW: glx@10645: DrawStringCentered(w->width >> 1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, TC_FROMSTRING); glx@10645: SetDParam(0, p->index); glx@10645: SetDParam(1, ni->params[3]); glx@10645: DrawStringMultiCenter( glx@10645: ((w->width - 101) >> 1) + 98, glx@10645: 90, glx@10645: STR_705F_STARTS_CONSTRUCTION_NEAR, glx@10645: w->width - 101); glx@10645: break; glx@10645: glx@10645: default: glx@10645: NOT_REACHED(); glx@10645: } glx@10645: } glx@10645: glx@10645: glx@10645: /** glx@10645: * Data common to all news items of a given subtype (structure) glx@10645: */ glx@10645: struct NewsSubtypeData { glx@10645: NewsType type; ///< News category @see NewsType glx@10645: NewsMode display_mode; ///< Display mode value @see NewsMode glx@10645: NewsFlag flags; ///< Initial NewsFlags bits @see NewsFlag glx@10645: DrawNewsCallbackProc *callback; ///< Call-back function truelight@0: }; truelight@0: glx@10645: /** glx@10645: * Data common to all news items of a given subtype (actual data) glx@10645: */ glx@10645: static const struct NewsSubtypeData _news_subtype_data[NS_END] = { glx@10645: /* type, display_mode, flags, callback */ glx@10645: { NT_ARRIVAL_PLAYER, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ARRIVAL_PLAYER glx@10645: { NT_ARRIVAL_OTHER, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ARRIVAL_OTHER glx@10645: { NT_ACCIDENT, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_ACCIDENT_TILE glx@10645: { NT_ACCIDENT, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ACCIDENT_VEHICLE glx@10645: { NT_COMPANY_INFO, NM_CALLBACK, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_TROUBLE glx@10645: { NT_COMPANY_INFO, NM_CALLBACK, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_MERGER glx@10645: { NT_COMPANY_INFO, NM_CALLBACK, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_BANKRUPT glx@10645: { NT_COMPANY_INFO, NM_CALLBACK, NF_TILE, DrawNewsBankrupcy }, ///< NS_COMPANY_NEW glx@10645: { NT_OPENCLOSE, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_OPENCLOSE glx@10645: { NT_ECONOMY, NM_NORMAL, NF_NONE, NULL }, ///< NS_ECONOMY glx@10645: { NT_INDUSTRY_PLAYER, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_PLAYER glx@10645: { NT_INDUSTRY_OTHER, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_OTHER glx@10645: { NT_INDUSTRY_NOBODY, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_NOBODY glx@10645: { NT_ADVICE, NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ADVICE glx@10645: { NT_NEW_VEHICLES, NM_CALLBACK, NF_NONE, DrawNewsNewVehicleAvail }, ///< NS_NEW_VEHICLES glx@10645: { NT_ACCEPTANCE, NM_SMALL, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_ACCEPTANCE glx@10645: { NT_SUBSIDIES, NM_NORMAL, NF_TILE|NF_TILE2, NULL }, ///< NS_SUBSIDIES glx@10645: { NT_GENERAL, NM_NORMAL, NF_TILE, NULL }, ///< NS_GENERAL truelight@0: }; truelight@0: rubidium@9703: /** Initialize the news-items data structures */ rubidium@6573: void InitNewsItemStructs() dominik@79: { rubidium@10355: free(_news_items); rubidium@10355: _max_news_items = max(ScaleByMapSize(30), 30U); rubidium@10355: _news_items = CallocT(_max_news_items); tron@427: _current_news = INVALID_NEWS; dominik@89: _oldest_news = 0; tron@427: _latest_news = INVALID_NEWS; tron@427: _forced_news = INVALID_NEWS; dominik@89: _total_news = 0; dominik@79: } truelight@0: glx@10645: struct NewsWindow : Window { glx@10645: uint16 chat_height; glx@10645: NewsItem *ni; rubidium@9826: glx@10645: NewsWindow(const WindowDesc *desc, NewsItem *ni) : Window(desc), ni(ni) glx@10645: { glx@10645: const Window *w = FindWindowById(WC_SEND_NETWORK_MSG, 0); glx@10645: this->chat_height = (w != NULL) ? w->height : 0; rubidium@9826: glx@10645: this->ni = &_news_items[_forced_news == INVALID_NEWS ? _current_news : _forced_news]; glx@10645: this->flags4 |= WF_DISABLE_VP_SCROLL; truelight@0: glx@10645: this->FindWindowPlacementAndResize(desc); glx@10645: } rubidium@9826: glx@10645: void DrawNewsBorder() glx@10645: { glx@10645: int left = 0; glx@10645: int right = this->width - 1; glx@10645: int top = 0; glx@10645: int bottom = this->height - 1; glx@10645: glx@10645: GfxFillRect(left, top, right, bottom, 0xF); glx@10645: glx@10645: GfxFillRect(left, top, left, bottom, 0xD7); glx@10645: GfxFillRect(right, top, right, bottom, 0xD7); glx@10645: GfxFillRect(left, top, right, top, 0xD7); glx@10645: GfxFillRect(left, bottom, right, bottom, 0xD7); glx@10645: glx@10645: DrawString(left + 2, top + 1, STR_00C6, TC_FROMSTRING); glx@10645: } glx@10645: glx@10645: virtual void OnPaint() glx@10645: { glx@10645: const NewsMode display_mode = _news_subtype_data[this->ni->subtype].display_mode; glx@10645: glx@10645: switch (display_mode) { glx@10645: case NM_NORMAL: glx@10645: case NM_THIN: { glx@10645: this->DrawNewsBorder(); glx@10645: glx@10645: DrawString(2, 1, STR_00C6, TC_FROMSTRING); glx@10645: glx@10645: SetDParam(0, this->ni->date); glx@10645: DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING); glx@10645: glx@10645: if (!(this->ni->flags & NF_VIEWPORT)) { glx@10645: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); glx@10645: DrawStringMultiCenter(215, display_mode == NM_NORMAL ? 76 : 56, glx@10645: this->ni->string_id, this->width - 4); glx@10645: } else { glx@10645: /* Back up transparency options to draw news view */ glx@10645: TransparencyOptionBits to_backup = _transparency_opt; glx@10645: _transparency_opt = 0; glx@10645: this->DrawViewport(); glx@10645: _transparency_opt = to_backup; glx@10645: glx@10645: /* Shade the viewport into gray, or color*/ glx@10645: ViewPort *vp = this->viewport; glx@10645: GfxFillRect(vp->left - this->left, vp->top - this->top, glx@10645: vp->left - this->left + vp->width - 1, vp->top - this->top + vp->height - 1, glx@10645: (this->ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE) glx@10645: ); glx@10645: glx@10645: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); glx@10645: DrawStringMultiCenter(this->width / 2, 20, this->ni->string_id, this->width - 4); glx@10645: } glx@10645: break; glx@10645: } glx@10645: glx@10645: case NM_CALLBACK: glx@10645: this->DrawNewsBorder(); glx@10645: (_news_subtype_data[this->ni->subtype].callback)(this, ni); glx@10645: break; glx@10645: glx@10645: default: glx@10645: this->DrawWidgets(); glx@10645: if (!(this->ni->flags & NF_VIEWPORT)) { glx@10645: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); glx@10645: DrawStringMultiCenter(140, 38, this->ni->string_id, 276); glx@10645: } else { glx@10645: this->DrawViewport(); glx@10645: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); glx@10645: DrawStringMultiCenter(this->width / 2, this->height - 16, this->ni->string_id, this->width - 4); glx@10645: } glx@10645: break; glx@10645: } glx@10645: } glx@10645: glx@10645: virtual void OnClick(Point pt, int widget) glx@10645: { glx@10645: switch (widget) { glx@10645: case 1: glx@10645: this->ni->duration = 0; glx@10645: delete this; glx@10645: _forced_news = INVALID_NEWS; glx@10645: break; glx@10645: glx@10645: case 0: glx@10645: if (this->ni->flags & NF_VEHICLE) { glx@10645: Vehicle *v = GetVehicle(this->ni->data_a); glx@10645: ScrollMainWindowTo(v->x_pos, v->y_pos); glx@10645: } else if (this->ni->flags & NF_TILE) { glx@10645: if (_ctrl_pressed) { glx@10645: ShowExtraViewPortWindow(this->ni->data_a); glx@10645: if (this->ni->flags & NF_TILE2) { glx@10645: ShowExtraViewPortWindow(this->ni->data_b); glx@10645: } rubidium@9826: } else { glx@10645: if (!ScrollMainWindowToTile(this->ni->data_a) && this->ni->flags & NF_TILE2) { glx@10645: ScrollMainWindowToTile(this->ni->data_b); rubidium@10455: } rubidium@10455: } rubidium@10455: } glx@10645: break; rubidium@10455: } truelight@0: } glx@10645: glx@10645: virtual EventState OnKeyPress(uint16 key, uint16 keycode) glx@10645: { glx@10645: if (keycode == WKC_SPACE) { glx@10645: /* Don't continue. */ glx@10645: delete this; glx@10645: return ES_HANDLED; glx@10645: } glx@10645: return ES_NOT_HANDLED; glx@10645: } glx@10645: glx@10645: virtual void OnInvalidateData(int data) glx@10645: { glx@10645: /* The chatbar has notified us that is was either created or closed */ glx@10645: this->chat_height = data; glx@10645: } glx@10645: glx@10645: virtual void OnTick() glx@10645: { glx@10645: /* Scroll up newsmessages from the bottom in steps of 4 pixels */ glx@10645: int y = max(this->top - 4, _screen.height - this->height - 12 - this->chat_height); glx@10645: if (y == this->top) return; glx@10645: glx@10645: if (this->viewport != NULL) this->viewport->top += y - this->top; glx@10645: glx@10645: int diff = Delta(this->top, y); glx@10645: this->top = y; glx@10645: glx@10645: SetDirtyBlocks(this->left, this->top - diff, this->left + this->width, this->top + this->height); glx@10645: } glx@10645: }; truelight@0: rubidium@9703: /** rubidium@9703: * Return the correct index in the pseudo-fifo rubidium@9703: * queue and deals with overflows when increasing the index rubidium@9703: */ rubidium@9837: static inline NewsID IncreaseIndex(NewsID i) dominik@79: { Darkvater@5258: assert(i != INVALID_NEWS); rubidium@10355: return (i + 1) % _max_news_items; Darkvater@5245: } Darkvater@5245: rubidium@9703: /** rubidium@9703: * Return the correct index in the pseudo-fifo rubidium@9703: * queue and deals with overflows when decreasing the index rubidium@9703: */ rubidium@9837: static inline NewsID DecreaseIndex(NewsID i) Darkvater@5245: { Darkvater@5245: assert(i != INVALID_NEWS); rubidium@10355: return (i + _max_news_items - 1) % _max_news_items; dominik@79: } dominik@79: rubidium@9703: /** rubidium@9703: * Add a new newsitem to be shown. glx@10645: * @param string String to display glx@10645: * @param subtype news category, any of the NewsSubtype enums (NS_) Darkvater@4873: * @param data_a news-specific value based on news type Darkvater@4873: * @param data_b news-specific value based on news type rubidium@9703: * glx@10645: * @see NewsSubype rubidium@9703: */ glx@10645: void AddNewsItem(StringID string, NewsSubtype subtype, uint data_a, uint data_b) truelight@0: { tron@4000: if (_game_mode == GM_MENU) return; truelight@0: glx@9505: /* check the rare case that the oldest (to be overwritten) news item is open */ rubidium@10355: if (_total_news == _max_news_items && (_oldest_news == _current_news || _oldest_news == _forced_news)) { Darkvater@5253: MoveToNextItem(); rubidium@9837: } dominik@102: rubidium@10355: if (_total_news < _max_news_items) _total_news++; truelight@193: Darkvater@5258: /* Increase _latest_news. If we have no news yet, use _oldest news as an Darkvater@5258: * index. We cannot use 0 as _oldest_news can jump around due to Darkvater@5258: * DeleteVehicleNews */ rubidium@9837: NewsID l_news = _latest_news; rubidium@9837: _latest_news = (_latest_news == INVALID_NEWS) ? _oldest_news : IncreaseIndex(_latest_news); dominik@79: Darkvater@5244: /* If the fifo-buffer is full, overwrite the oldest entry */ Darkvater@5245: if (l_news != INVALID_NEWS && _latest_news == _oldest_news) { rubidium@10355: assert(_total_news == _max_news_items); rubidium@9837: _oldest_news = IncreaseIndex(_oldest_news); Darkvater@5245: } dominik@79: Darkvater@5846: /*DEBUG(misc, 0, "+cur %3d, old %2d, lat %3d, for %3d, tot %2d", Darkvater@5846: _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/ Darkvater@5771: glx@9505: /* Add news to _latest_news */ rubidium@9826: NewsItem *ni = &_news_items[_latest_news]; rubidium@9826: memset(ni, 0, sizeof(*ni)); Darkvater@5245: rubidium@9826: ni->string_id = string; glx@10645: ni->subtype = subtype; glx@10645: ni->flags = _news_subtype_data[subtype].flags; Darkvater@5245: rubidium@9826: /* show this news message in color? */ rubidium@10715: if (_cur_year >= _settings.gui.colored_news_year) ni->flags |= NF_INCOLOR; rubidium@9826: rubidium@9826: ni->data_a = data_a; rubidium@9826: ni->data_b = data_b; rubidium@9826: ni->date = _date; rubidium@9826: CopyOutDParam(ni->params, 0, lengthof(ni->params)); rubidium@9826: rubidium@9826: Window *w = FindWindowById(WC_MESSAGE_HISTORY, 0); rubidium@9826: if (w == NULL) return; rubidium@10455: w->SetDirty(); rubidium@9826: w->vscroll.count = _total_news; truelight@0: } truelight@0: dominik@715: rubidium@9703: /** glx@10294: * Per-NewsType data rubidium@9703: */ glx@10294: NewsTypeData _news_type_data[NT_END] = { glx@10294: /* name, age, sound, display */ glx@10294: { "arrival_player", 60, SND_1D_APPLAUSE, ND_FULL }, ///< NT_ARRIVAL_PLAYER glx@10294: { "arrival_other", 60, SND_1D_APPLAUSE, ND_FULL }, ///< NT_ARRIVAL_OTHER glx@10294: { "accident", 90, SND_BEGIN, ND_FULL }, ///< NT_ACCIDENT glx@10294: { "company_info", 60, SND_BEGIN, ND_FULL }, ///< NT_COMPANY_INFO glx@10294: { "openclose", 90, SND_BEGIN, ND_FULL }, ///< NT_OPENCLOSE glx@10294: { "economy", 30, SND_BEGIN, ND_FULL }, ///< NT_ECONOMY glx@10294: { "production_player", 30, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_PLAYER glx@10294: { "production_other", 30, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_OTHER glx@10294: { "production_nobody", 30, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_NOBODY glx@10294: { "advice", 150, SND_BEGIN, ND_FULL }, ///< NT_ADVICE glx@10294: { "new_vehicles", 30, SND_1E_OOOOH, ND_FULL }, ///< NT_NEW_VEHICLES glx@10294: { "acceptance", 90, SND_BEGIN, ND_FULL }, ///< NT_ACCEPTANCE glx@10294: { "subsidies", 180, SND_BEGIN, ND_FULL }, ///< NT_SUBSIDIES glx@10294: { "general", 60, SND_BEGIN, ND_FULL }, ///< NT_GENERAL rubidium@9703: }; rubidium@9703: truelight@0: truelight@0: static const Widget _news_type13_widgets[] = { truelight@867: { WWT_PANEL, RESIZE_NONE, 15, 0, 429, 0, 169, 0x0, STR_NULL}, truelight@867: { WWT_PANEL, RESIZE_NONE, 15, 0, 10, 0, 11, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static WindowDesc _news_type13_desc = { rubidium@9694: WDP_CENTER, 476, 430, 170, 430, 170, rubidium@6144: WC_NEWS_WINDOW, WC_NONE, truelight@0: WDF_DEF_WIDGET, truelight@0: _news_type13_widgets, truelight@0: }; truelight@0: truelight@0: static const Widget _news_type2_widgets[] = { truelight@867: { WWT_PANEL, RESIZE_NONE, 15, 0, 429, 0, 129, 0x0, STR_NULL}, truelight@867: { WWT_PANEL, RESIZE_NONE, 15, 0, 10, 0, 11, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static WindowDesc _news_type2_desc = { rubidium@9694: WDP_CENTER, 476, 430, 130, 430, 130, rubidium@6144: WC_NEWS_WINDOW, WC_NONE, truelight@0: WDF_DEF_WIDGET, truelight@0: _news_type2_widgets, truelight@0: }; truelight@0: truelight@0: static const Widget _news_type0_widgets[] = { rubidium@4344: { WWT_PANEL, RESIZE_NONE, 5, 0, 279, 14, 86, 0x0, STR_NULL}, rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 5, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 5, 11, 279, 0, 13, STR_012C_MESSAGE, STR_NULL}, Darkvater@4939: { WWT_INSET, RESIZE_NONE, 5, 2, 277, 16, 64, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static WindowDesc _news_type0_desc = { rubidium@9694: WDP_CENTER, 476, 280, 87, 280, 87, rubidium@6144: WC_NEWS_WINDOW, WC_NONE, truelight@0: WDF_DEF_WIDGET, truelight@0: _news_type0_widgets, truelight@0: }; truelight@0: Darkvater@1688: rubidium@9703: /** Open up an own newspaper window for the news item */ dominik@79: static void ShowNewspaper(NewsItem *ni) truelight@0: { Darkvater@4874: ni->flags &= ~NF_FORCE_BIG; dominik@79: ni->duration = 555; truelight@0: glx@10645: SoundFx sound = _news_type_data[_news_subtype_data[ni->subtype].type].sound; tron@2639: if (sound != 0) SndPlayFx(sound); dominik@79: rubidium@9837: int top = _screen.height; rubidium@9837: Window *w; glx@10645: switch (_news_subtype_data[ni->subtype].display_mode) { tron@427: case NM_NORMAL: rubidium@9837: case NM_CALLBACK: tron@427: _news_type13_desc.top = top; glx@10645: w = new NewsWindow(&_news_type13_desc, ni); rubidium@9837: if (ni->flags & NF_VIEWPORT) { glx@10294: InitializeWindowViewport(w, 2, 58, 426, 110, glx@9624: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); rubidium@9837: } tron@427: break; tron@427: rubidium@9837: case NM_THIN: tron@427: _news_type2_desc.top = top; glx@10645: w = new NewsWindow(&_news_type2_desc, ni); rubidium@9837: if (ni->flags & NF_VIEWPORT) { glx@10294: InitializeWindowViewport(w, 2, 58, 426, 70, glx@9624: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); rubidium@9837: } tron@427: break; tron@427: rubidium@9837: default: tron@427: _news_type0_desc.top = top; glx@10645: w = new NewsWindow(&_news_type0_desc, ni); rubidium@9837: if (ni->flags & NF_VIEWPORT) { glx@10294: InitializeWindowViewport(w, 3, 17, 274, 47, glx@9624: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); rubidium@9837: } tron@427: break; truelight@0: } Darkvater@5771: Darkvater@5846: /*DEBUG(misc, 0, " cur %3d, old %2d, lat %3d, for %3d, tot %2d", Darkvater@5846: _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/ dominik@79: } truelight@0: rubidium@9703: /** Show news item in the ticker */ tron@427: static void ShowTicker(const NewsItem *ni) dominik@79: { Darkvater@1688: if (_news_ticker_sound) SndPlayFx(SND_16_MORSE); Darkvater@1688: dominik@79: _statusbar_news_item = *ni; glx@10645: InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_TICKER); dominik@79: } dominik@79: dominik@79: rubidium@9703: /** rubidium@9703: * Are we ready to show another news item? rubidium@9703: * Only if nothing is in the newsticker and no newspaper is displayed rubidium@9703: */ rubidium@6573: static bool ReadyForNextItem() dominik@79: { Darkvater@5246: NewsID item = (_forced_news == INVALID_NEWS) ? _current_news : _forced_news; dominik@92: rubidium@10355: if (item >= _max_news_items) return true; rubidium@9837: NewsItem *ni = &_news_items[item]; dominik@79: glx@9505: /* Ticker message glx@9505: * Check if the status bar message is still being displayed? */ glx@10645: if (IsNewsTickerShown()) return false; truelight@0: glx@9505: /* Newspaper message, decrement duration counter */ Darkvater@5264: if (ni->duration != 0) ni->duration--; truelight@0: glx@9505: /* neither newsticker nor newspaper are running */ Darkvater@5264: return (ni->duration == 0 || FindWindowById(WC_NEWS_WINDOW, 0) == NULL); dominik@79: } dominik@79: rubidium@9703: /** Move to the next news item */ rubidium@6573: static void MoveToNextItem() dominik@79: { dominik@79: DeleteWindowById(WC_NEWS_WINDOW, 0); tron@427: _forced_news = INVALID_NEWS; dominik@79: glx@9505: /* if we're not at the last item, then move on */ tron@427: if (_current_news != _latest_news) { rubidium@9837: _current_news = (_current_news == INVALID_NEWS) ? _oldest_news : IncreaseIndex(_current_news); rubidium@9837: NewsItem *ni = &_news_items[_current_news]; glx@10645: const NewsType type = _news_subtype_data[ni->subtype].type; dominik@79: glx@9505: /* check the date, don't show too old items */ glx@10645: if (_date - _news_type_data[type].age > ni->date) return; dominik@79: glx@10645: switch (_news_type_data[type].display) { rubidium@9703: default: NOT_REACHED(); glx@10645: case ND_OFF: // Off - show nothing only a small reminder in the status bar glx@10645: InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_REMINDER); tron@2639: break; tron@2639: glx@10294: case ND_SUMMARY: // Summary - show ticker, but if forced big, cascade to full rubidium@9703: if (!(ni->flags & NF_FORCE_BIG)) { rubidium@9703: ShowTicker(ni); rubidium@9703: break; rubidium@9703: } rubidium@9703: /* Fallthrough */ rubidium@9703: glx@10294: case ND_FULL: // Full - show newspaper rubidium@9703: ShowNewspaper(ni); rubidium@9703: break; tron@2639: } truelight@0: } dominik@79: } truelight@0: rubidium@6573: void NewsLoop() truelight@0: { glx@9505: /* no news item yet */ tron@427: if (_total_news == 0) return; dominik@79: Darkvater@5253: if (ReadyForNextItem()) MoveToNextItem(); dominik@79: } dominik@79: rubidium@9703: /** Do a forced show of a specific message */ Darkvater@5246: static void ShowNewsMessage(NewsID i) dominik@79: { tron@427: if (_total_news == 0) return; dominik@89: glx@9505: /* Delete the news window */ dominik@79: DeleteWindowById(WC_NEWS_WINDOW, 0); dominik@79: glx@9505: /* setup forced news item */ dominik@79: _forced_news = i; dominik@79: tron@427: if (_forced_news != INVALID_NEWS) { dominik@79: NewsItem *ni = &_news_items[_forced_news]; dominik@79: ni->duration = 555; Darkvater@4874: ni->flags |= NF_FORCE_BIG; dominik@79: DeleteWindowById(WC_NEWS_WINDOW, 0); dominik@79: ShowNewspaper(ni); dominik@79: } truelight@0: } truelight@0: rubidium@9703: /** Show previous news item */ rubidium@6573: void ShowLastNewsMessage() truelight@0: { Darkvater@5846: if (_forced_news == INVALID_NEWS) { Darkvater@5846: /* Not forced any news yet, show the current one, unless a news window is Darkvater@5846: * open (which can only be the current one), then show the previous item */ Darkvater@5846: const Window *w = FindWindowById(WC_NEWS_WINDOW, 0); rubidium@9837: ShowNewsMessage((w == NULL || (_current_news == _oldest_news)) ? _current_news : DecreaseIndex(_current_news)); Darkvater@5846: } else if (_forced_news == _oldest_news) { Darkvater@5846: /* We have reached the oldest news, start anew with the latest */ Darkvater@5846: ShowNewsMessage(_latest_news); Darkvater@5846: } else { Darkvater@5846: /* 'Scrolling' through news history show each one in turn */ rubidium@9837: ShowNewsMessage(DecreaseIndex(_forced_news)); dominik@83: } truelight@0: } truelight@0: dominik@79: dominik@89: /* return news by number, with 0 being the most rubidium@4549: * recent news. Returns INVALID_NEWS if end of queue reached. */ Darkvater@5246: static NewsID getNews(NewsID i) dominik@89: { tron@2639: if (i >= _total_news) return INVALID_NEWS; dominik@89: tron@2639: if (_latest_news < i) { rubidium@10355: i = _latest_news + _max_news_items - i; tron@2639: } else { dominik@89: i = _latest_news - i; tron@2639: } dominik@89: rubidium@10355: i %= _max_news_items; dominik@89: return i; dominik@89: } dominik@89: rubidium@9703: /** rubidium@9703: * Draw an unformatted news message truncated to a maximum length. If Darkvater@2101: * length exceeds maximum length it will be postfixed by '...' Darkvater@2101: * @param x,y position of the string Darkvater@2101: * @param color the color the string will be shown in Darkvater@2101: * @param *ni NewsItem being printed Darkvater@2101: * @param maxw maximum width of string in pixels Darkvater@2101: */ Darkvater@2101: static void DrawNewsString(int x, int y, uint16 color, const NewsItem *ni, uint maxw) dominik@89: { Darkvater@2101: char buffer[512], buffer2[512]; dominik@89: StringID str; truelight@193: glx@10645: CopyInDParam(0, ni->params, lengthof(ni->params)); glx@10645: str = ni->string_id; dominik@89: Darkvater@4912: GetString(buffer, str, lastof(buffer)); Darkvater@2101: /* Copy the just gotten string to another buffer to remove any formatting Darkvater@2101: * from it such as big fonts, etc. */ rubidium@9837: const char *ptr = buffer; rubidium@9837: char *dest = buffer2; rubidium@9620: WChar c_last = '\0'; peter1138@5108: for (;;) { peter1138@5108: WChar c = Utf8Consume(&ptr); peter1138@5108: if (c == 0) break; rubidium@9620: /* Make a space from a newline, but ignore multiple newlines */ rubidium@9620: if (c == '\n' && c_last != '\n') { rubidium@9620: dest[0] = ' '; rubidium@9620: dest++; rubidium@9620: } else if (c == '\r') { Darkvater@2101: dest[0] = dest[1] = dest[2] = dest[3] = ' '; Darkvater@2101: dest += 4; peter1138@5108: } else if (IsPrintable(c)) { peter1138@5108: dest += Utf8Encode(dest, c); dominik@89: } rubidium@9620: c_last = c; dominik@89: } Darkvater@2101: Darkvater@2101: *dest = '\0'; Darkvater@2101: /* Truncate and show string; postfixed by '...' if neccessary */ Darkvater@2101: DoDrawStringTruncated(buffer2, x, y, color, maxw); dominik@89: } dominik@89: dominik@89: glx@10645: struct MessageHistoryWindow : Window { glx@10645: MessageHistoryWindow(const WindowDesc *desc) : Window(desc) glx@10645: { glx@10645: this->vscroll.cap = 10; glx@10645: this->vscroll.count = _total_news; glx@10645: this->resize.step_height = 12; glx@10645: this->resize.height = this->height - 12 * 6; // minimum of 4 items in the list, each item 12 high glx@10645: this->resize.step_width = 1; glx@10645: this->resize.width = 200; // can't make window any smaller than 200 pixel rubidium@9837: glx@10645: this->FindWindowPlacementAndResize(desc); glx@10645: } dominik@1097: glx@10645: virtual void OnPaint() glx@10645: { glx@10645: int y = 19; rubidium@9837: glx@10645: SetVScrollCount(this, _total_news); glx@10645: this->DrawWidgets(); rubidium@9837: glx@10645: if (_total_news == 0) return; glx@10645: NewsID show = min(_total_news, this->vscroll.cap); glx@10645: glx@10645: for (NewsID p = this->vscroll.pos; p < this->vscroll.pos + show; p++) { glx@10645: /* get news in correct order */ glx@10645: const NewsItem *ni = &_news_items[getNews(p)]; glx@10645: glx@10645: SetDParam(0, ni->date); glx@10645: DrawString(4, y, STR_SHORT_DATE, TC_WHITE); glx@10645: glx@10645: DrawNewsString(82, y, TC_WHITE, ni, this->width - 95); glx@10645: y += 12; glx@10645: } dominik@89: } glx@10645: glx@10645: virtual void OnClick(Point pt, int widget) glx@10645: { glx@10645: if (widget == 3) { glx@10645: int y = (pt.y - 19) / 12; glx@10645: NewsID p = getNews(y + this->vscroll.pos); glx@10645: glx@10645: if (p != INVALID_NEWS) ShowNewsMessage(p); glx@10645: } glx@10645: } glx@10645: glx@10645: virtual void OnResize(Point new_size, Point delta) glx@10645: { glx@10645: this->vscroll.cap += delta.y / 12; glx@10645: } glx@10645: }; dominik@89: dominik@89: static const Widget _message_history_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_RIGHT, 13, 11, 387, 0, 13, STR_MESSAGE_HISTORY, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@4344: { WWT_STICKYBOX, RESIZE_LR, 13, 388, 399, 0, 13, 0x0, STR_STICKY_BUTTON}, Darkvater@4938: { WWT_PANEL, RESIZE_RB, 13, 0, 387, 14, 139, 0x0, STR_MESSAGE_HISTORY_TIP}, rubidium@4344: { WWT_SCROLLBAR, RESIZE_LRB, 13, 388, 399, 14, 127, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_RESIZEBOX, RESIZE_LRTB, 13, 388, 399, 128, 139, 0x0, STR_RESIZE_BUTTON}, darkvater@176: { WIDGETS_END}, dominik@89: }; dominik@89: dominik@89: static const WindowDesc _message_history_desc = { rubidium@9694: 240, 22, 400, 140, 400, 140, rubidium@6144: WC_MESSAGE_HISTORY, WC_NONE, dominik@1097: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, dominik@89: _message_history_widgets, dominik@89: }; dominik@89: rubidium@9703: /** Display window with news messages history */ rubidium@6573: void ShowMessageHistory() dominik@89: { dominik@89: DeleteWindowById(WC_MESSAGE_HISTORY, 0); glx@10645: new MessageHistoryWindow(&_message_history_desc); dominik@89: } dominik@89: belugas@6605: rubidium@9722: /** News settings window widget offset constants */ belugas@6605: enum { rubidium@9724: WIDGET_NEWSOPT_DROP_SUMMARY = 4, ///< Dropdown that adjusts at once the level for all settings rubidium@9724: WIDGET_NEWSOPT_SOUNDTICKER = 6, ///< Button activating sound on events rubidium@9724: WIDGET_NEWSOPT_START_OPTION = 8, ///< First widget that is part of a group [<] .. [.] belugas@6605: }; belugas@6605: glx@10645: static const StringID _message_opt[] = {STR_OFF, STR_SUMMARY, STR_FULL, INVALID_STRING_ID}; rubidium@9724: glx@10645: struct MessageOptionsWindow : Window { glx@10645: int state; belugas@6605: glx@10645: MessageOptionsWindow(const WindowDesc *desc) : Window(desc) glx@10645: { glx@10645: NewsDisplay all_val; belugas@6605: glx@10645: /* Set up the initial disabled buttons in the case of 'off' or 'full' */ glx@10645: all_val = _news_type_data[0].display; glx@10645: for (int i = 0; i < NT_END; i++) { glx@10645: this->SetMessageButtonStates(_news_type_data[i].display, i); glx@10645: /* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */ glx@10645: if (_news_type_data[i].display != all_val) all_val = ND_OFF; glx@10645: } glx@10645: /* If all values are the same value, the ALL-button will take over this value */ glx@10645: this->state = all_val; rubidium@10715: rubidium@10715: this->FindWindowPlacementAndResize(desc); glx@10645: } glx@10645: glx@10645: /** glx@10645: * Setup the disabled/enabled buttons in the message window glx@10645: * If the value is 'off' disable the [<] widget, and enable the [>] one glx@10645: * Same-wise for all the others. Starting value of 4 is the first widget glx@10645: * group. These are grouped as [<][>] .. [<][>], etc. glx@10645: * @param value to set in the widget glx@10645: * @param element index of the group of widget to set glx@10645: */ glx@10645: void SetMessageButtonStates(byte value, int element) glx@10645: { glx@10645: element *= NB_WIDG_PER_SETTING; glx@10645: glx@10645: this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION, value == 0); glx@10645: this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION + 2, value == 2); glx@10645: } glx@10645: glx@10645: virtual void OnPaint() glx@10645: { glx@10645: if (_news_ticker_sound) this->LowerWidget(WIDGET_NEWSOPT_SOUNDTICKER); glx@10645: glx@10645: this->widget[WIDGET_NEWSOPT_DROP_SUMMARY].data = _message_opt[this->state]; glx@10645: this->DrawWidgets(); glx@10645: glx@10645: /* Draw the string of each setting on each button. */ glx@10645: for (int i = 0, y = 26; i < NT_END; i++, y += 12) { glx@10645: /* 51 comes from 13 + 89 (left and right of the button)+1, shiefted by one as to get division, glx@10645: * which will give centered position */ glx@10645: DrawStringCentered(51, y + 1, _message_opt[_news_type_data[i].display], TC_BLACK); glx@10645: } glx@10645: } glx@10645: glx@10645: virtual void OnClick(Point pt, int widget) glx@10645: { glx@10645: switch (widget) { glx@10645: case WIDGET_NEWSOPT_DROP_SUMMARY: // Dropdown menu for all settings glx@10645: ShowDropDownMenu(this, _message_opt, this->state, WIDGET_NEWSOPT_DROP_SUMMARY, 0, 0); glx@10645: break; glx@10645: glx@10645: case WIDGET_NEWSOPT_SOUNDTICKER: // Change ticker sound on/off glx@10645: _news_ticker_sound ^= 1; glx@10645: this->ToggleWidgetLoweredState(widget); glx@10645: this->InvalidateWidget(widget); glx@10645: break; glx@10645: glx@10645: default: { // Clicked on the [<] .. [>] widgets glx@10645: int wid = widget - WIDGET_NEWSOPT_START_OPTION; glx@10645: if (wid >= 0 && wid < (NB_WIDG_PER_SETTING * NT_END)) { glx@10645: int element = wid / NB_WIDG_PER_SETTING; glx@10645: byte val = (_news_type_data[element].display + ((wid % NB_WIDG_PER_SETTING) ? 1 : -1)) % 3; glx@10645: glx@10645: this->SetMessageButtonStates(val, element); glx@10645: _news_type_data[element].display = (NewsDisplay)val; glx@10645: this->SetDirty(); rubidium@10455: } glx@10645: break; rubidium@9837: } glx@10645: } glx@10645: } belugas@6605: glx@10645: virtual void OnDropdownSelect(int widget, int index) glx@10645: { glx@10645: this->state = index; glx@10645: glx@10645: for (int i = 0; i < NT_END; i++) { glx@10645: this->SetMessageButtonStates(index, i); glx@10645: _news_type_data[i].display = (NewsDisplay)index; glx@10645: } glx@10645: this->SetDirty(); truelight@0: } glx@10645: }; truelight@0: rubidium@9722: rubidium@9722: /* rubidium@9722: * The news settings window widgets rubidium@9722: * rubidium@9722: * Main part of the window is a list of news-setting lines, one for each news category. rubidium@9722: * Each line is constructed by an expansion of the \c NEWS_SETTINGS_LINE macro rubidium@9722: */ rubidium@9722: rubidium@9722: /** rubidium@9722: * Macro to construct one news-setting line in the news-settings window. rubidium@9722: * One line consists of four widgets, namely rubidium@9722: * - A [<] button rubidium@9722: * - A [...] label rubidium@9722: * - A [>] button rubidium@9722: * - A text label describing the news category rubidium@9722: * Horizontal positions of the widgets are hard-coded, vertical start position is (\a basey + \a linenum * \c NEWS_SETTING_BASELINE_SKIP). rubidium@9722: * Height of one line is 12, with the text label shifted 1 pixel down. rubidium@9722: * rubidium@9722: * First line should be widget number WIDGET_NEWSOPT_START_OPTION rubidium@9722: * rubidium@9722: * @param basey: Base Y coordinate rubidium@9722: * @param linenum: Count, news-setting is the \a linenum-th line rubidium@9722: * @param text: StringID for the text label to display rubidium@9722: */ rubidium@9722: #define NEWS_SETTINGS_LINE(basey, linenum, text) \ rubidium@9722: { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@9722: 4, 12, basey + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@9722: SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, \ rubidium@9722: { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@9722: 13, 89, basey + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@9722: STR_EMPTY, STR_NULL}, \ rubidium@9722: { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@9722: 90, 98, basey + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@9722: SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, \ rubidium@9722: { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@9722: 103, 409, basey + 1 + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 13 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@9722: text, STR_NULL} rubidium@9722: rubidium@9722: static const int NEWS_SETTING_BASELINE_SKIP = 12; ///< Distance between two news-setting lines, should be at least 12 rubidium@9722: rubidium@9722: truelight@0: static const Widget _message_options_widgets[] = { rubidium@9722: { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_BROWN, 0, 10, 0, 13, rubidium@9722: STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@9722: { WWT_CAPTION, RESIZE_NONE, COLOUR_BROWN, 11, 409, 0, 13, rubidium@9722: STR_0204_MESSAGE_OPTIONS, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@9722: { WWT_PANEL, RESIZE_NONE, COLOUR_BROWN, 0, 409, 14, 64 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@9722: 0x0, STR_NULL}, rubidium@4344: belugas@6605: /* Text at the top of the main panel, in black */ rubidium@9722: { WWT_LABEL, RESIZE_NONE, COLOUR_BROWN, rubidium@9722: 0, 409, 13, 26, rubidium@9722: STR_0205_MESSAGE_TYPES, STR_NULL}, belugas@6605: rubidium@9722: /* General drop down and sound button, widgets WIDGET_NEWSOPT_BTN_SUMMARY and WIDGET_NEWSOPT_DROP_SUMMARY */ rubidium@9724: { WWT_DROPDOWN, RESIZE_NONE, COLOUR_YELLOW, rubidium@9724: 4, 98, 34 + NT_END * NEWS_SETTING_BASELINE_SKIP, 45 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@9722: 0x0, STR_NULL}, belugas@6605: rubidium@9722: { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, rubidium@9722: 103, 409, 35 + NT_END * NEWS_SETTING_BASELINE_SKIP, 47 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@9722: STR_MESSAGES_ALL, STR_NULL}, belugas@6605: rubidium@9722: /* Below is widget WIDGET_NEWSOPT_SOUNDTICKER */ rubidium@9722: { WWT_TEXTBTN_2, RESIZE_NONE, COLOUR_YELLOW, rubidium@9722: 4, 98, 46 + NT_END * NEWS_SETTING_BASELINE_SKIP, 57 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@9722: STR_02DB_OFF, STR_NULL}, truelight@6609: rubidium@9722: { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, rubidium@9722: 103, 409, 47 + NT_END * NEWS_SETTING_BASELINE_SKIP, 59 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@9722: STR_MESSAGE_SOUND, STR_NULL}, rubidium@9722: rubidium@9722: /* List of news-setting lines (4 widgets for each line). rubidium@9722: * First widget must be number WIDGET_NEWSOPT_START_OPTION rubidium@9722: */ rubidium@9722: NEWS_SETTINGS_LINE(26, NT_ARRIVAL_PLAYER, STR_0206_ARRIVAL_OF_FIRST_VEHICLE), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_ARRIVAL_OTHER, STR_0207_ARRIVAL_OF_FIRST_VEHICLE), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_ACCIDENT, STR_0208_ACCIDENTS_DISASTERS), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_COMPANY_INFO, STR_0209_COMPANY_INFORMATION), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_OPENCLOSE, STR_NEWS_OPEN_CLOSE), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_ECONOMY, STR_020A_ECONOMY_CHANGES), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_PLAYER, STR_INDUSTRY_CHANGES_SERVED_BY_PLAYER), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_OTHER, STR_INDUSTRY_CHANGES_SERVED_BY_OTHER), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_NOBODY, STR_OTHER_INDUSTRY_PRODUCTION_CHANGES), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_ADVICE, STR_020B_ADVICE_INFORMATION_ON_PLAYER), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_NEW_VEHICLES, STR_020C_NEW_VEHICLES), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_ACCEPTANCE, STR_020D_CHANGES_OF_CARGO_ACCEPTANCE), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_SUBSIDIES, STR_020E_SUBSIDIES), rubidium@9722: NEWS_SETTINGS_LINE(26, NT_GENERAL, STR_020F_GENERAL_INFORMATION), belugas@6605: darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _message_options_desc = { rubidium@9722: 270, 22, 410, 65 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@9722: 410, 65 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@6144: WC_GAME_OPTIONS, WC_NONE, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, truelight@0: _message_options_widgets, truelight@0: }; truelight@0: rubidium@6573: void ShowMessageOptions() truelight@0: { truelight@0: DeleteWindowById(WC_GAME_OPTIONS, 0); glx@10645: new MessageOptionsWindow(&_message_options_desc); truelight@0: } tron@3139: tron@3139: tron@3139: void DeleteVehicleNews(VehicleID vid, StringID news) tron@3139: { rubidium@9837: for (NewsID n = _oldest_news; _latest_news != INVALID_NEWS; n = IncreaseIndex(n)) { Darkvater@5245: const NewsItem *ni = &_news_items[n]; tron@3139: tron@3139: if (ni->flags & NF_VEHICLE && tron@3139: ni->data_a == vid && tron@3139: (news == INVALID_STRING_ID || ni->string_id == news)) { Darkvater@5846: /* If we delete a forced news and it is just before the current news Darkvater@5846: * then we need to advance to the next news (if any) */ Darkvater@5846: if (_forced_news == n) MoveToNextItem(); Darkvater@5846: if (_forced_news == INVALID_NEWS && _current_news == n) MoveToNextItem(); Darkvater@5254: _total_news--; tron@3139: Darkvater@5771: /* If this is the last news item, invalidate _latest_news */ Darkvater@5771: if (_total_news == 0) { Darkvater@5771: assert(_latest_news == _oldest_news); Darkvater@5254: _latest_news = INVALID_NEWS; rubidium@10181: _current_news = INVALID_NEWS; Darkvater@5254: } tron@3139: Darkvater@5254: /* Since we only imitate a FIFO removing an arbitrary element does need Darkvater@5254: * some magic. Remove the item by shifting head towards the tail. eg Darkvater@5254: * oldest remove last Darkvater@5254: * | | | Darkvater@5254: * [------O--------n-----L--] Darkvater@5254: * will become (change dramatized to make clear) Darkvater@5254: * [---------O-----------L--] Darkvater@5771: * We also need an update of the current, forced and visible (open window) Darkvater@5771: * news's as this shifting could change the items they were pointing to */ Darkvater@5254: if (_total_news != 0) { glx@10645: NewsWindow *w = dynamic_cast(FindWindowById(WC_NEWS_WINDOW, 0)); glx@10645: NewsID visible_news = (w != NULL) ? (NewsID)(w->ni - _news_items) : INVALID_NEWS; Darkvater@5771: rubidium@9837: for (NewsID i = n;; i = DecreaseIndex(i)) { rubidium@9837: _news_items[i] = _news_items[DecreaseIndex(i)]; Darkvater@5254: Darkvater@6090: if (i != _latest_news) { rubidium@9837: if (i == _current_news) _current_news = IncreaseIndex(_current_news); rubidium@9837: if (i == _forced_news) _forced_news = IncreaseIndex(_forced_news); glx@10645: if (i == visible_news) w->ni = &_news_items[IncreaseIndex(visible_news)]; Darkvater@6090: } Darkvater@5771: Darkvater@5784: if (i == _oldest_news) break; Darkvater@5784: } rubidium@9837: _oldest_news = IncreaseIndex(_oldest_news); tron@3139: } tron@3139: Darkvater@5846: /*DEBUG(misc, 0, "-cur %3d, old %2d, lat %3d, for %3d, tot %2d", Darkvater@5846: _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/ Darkvater@5771: rubidium@9837: Window *w = FindWindowById(WC_MESSAGE_HISTORY, 0); tron@3143: if (w != NULL) { rubidium@10455: w->SetDirty(); tron@3143: w->vscroll.count = _total_news; tron@3143: } tron@3139: } Darkvater@5254: Darkvater@5254: if (n == _latest_news) break; tron@3139: } tron@3139: }