tron@2186: /* $Id$ */ tron@2186: rubidium@10429: /** @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@8603: #include "window_gui.h" rubidium@8720: #include "viewport_func.h" rubidium@9259: #include "news_func.h" rubidium@8766: #include "settings_type.h" belugas@8345: #include "transparency.h" rubidium@8610: #include "strings_func.h" rubidium@8627: #include "window_func.h" rubidium@8636: #include "date_func.h" rubidium@8640: #include "vehicle_base.h" rubidium@8653: #include "sound_func.h" rubidium@8710: #include "string_func.h" peter1138@8780: #include "widgets/dropdown_func.h" glx@10301: #include "map_func.h" rubidium@10570: #include "statusbar_gui.h" rubidium@10603: #include "player_face.h" truelight@0: rubidium@8760: #include "table/sprites.h" rubidium@8760: #include "table/strings.h" rubidium@8760: rubidium@8094: /** @file news_gui.cpp rubidium@8094: * 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@8094: * array is full, the oldest entry (\a _oldest_news) is being overwritten rubidium@8094: * by the newest (\a _latest_news). rubidium@4549: * rubidium@8094: * \verbatim rubidium@4549: * oldest current lastest rubidium@4549: * | | | rubidium@4549: * [O------------F-------------C---------L ] rubidium@4549: * | rubidium@4549: * forced rubidium@8094: * \endverbatim Darkvater@5254: * Darkvater@5254: * Of course by using an array we can have situations like Darkvater@5254: * rubidium@8094: * \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@8094: * \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@8764: NewsItem _statusbar_news_item; rubidium@8766: bool _news_ticker_sound; glx@10301: static NewsItem *_news_items = NULL; ///< The news FIFO queue glx@10301: static uint _max_news_items = 0; ///< size of news FIFO queue rubidium@8094: static NewsID _current_news = INVALID_NEWS; ///< points to news item that should be shown next rubidium@8094: static NewsID _oldest_news = 0; ///< points to first item in fifo queue rubidium@8094: static NewsID _latest_news = INVALID_NEWS; ///< points to last item in fifo queue rubidium@8094: rubidium@8094: /** Forced news item. rubidium@8094: * Users can force an item by accessing the history or "last message". rubidium@8094: * If the message being shown was forced by the user, its index is stored in rubidium@8094: * _forced_news. Otherwise, \a _forced_news variable is INVALID_NEWS. */ Darkvater@5246: static NewsID _forced_news = INVALID_NEWS; dominik@79: glx@10301: static uint _total_news = 0; ///< Number of news items in FIFO queue @see _news_items rubidium@10558: static void MoveToNextItem(); truelight@0: rubidium@10558: rubidium@10558: typedef void DrawNewsCallbackProc(struct Window *w, const NewsItem *ni); rubidium@9259: void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni); rubidium@10603: rubidium@10603: static void DrawNewsBankrupcy(Window *w, const NewsItem *ni) rubidium@10603: { rubidium@10603: Player *p = GetPlayer((PlayerID)(ni->data_b)); rubidium@10603: DrawPlayerFace(p->face, p->player_color, 2, 23); rubidium@10603: GfxFillRect(3, 23, 3 + 91, 23 + 118, PALETTE_TO_STRUCT_GREY | (1 << USE_COLORTABLE)); rubidium@10603: rubidium@10603: SetDParam(0, p->index); rubidium@10603: rubidium@10603: DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94); rubidium@10603: rubidium@10603: switch (ni->subtype) { rubidium@10603: case NS_COMPANY_TROUBLE: rubidium@10603: DrawStringCentered(w->width >> 1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, TC_FROMSTRING); rubidium@10603: rubidium@10603: SetDParam(0, p->index); rubidium@10603: rubidium@10603: DrawStringMultiCenter( rubidium@10603: ((w->width - 101) >> 1) + 98, rubidium@10603: 90, rubidium@10603: STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED, rubidium@10603: w->width - 101); rubidium@10603: break; rubidium@10603: rubidium@10603: case NS_COMPANY_MERGER: rubidium@10603: DrawStringCentered(w->width >> 1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, TC_FROMSTRING); rubidium@10603: SetDParam(0, ni->params[2]); rubidium@10603: SetDParam(1, p->index); rubidium@10603: SetDParam(2, ni->params[4]); rubidium@10603: DrawStringMultiCenter( rubidium@10603: ((w->width - 101) >> 1) + 98, rubidium@10603: 90, rubidium@10603: ni->params[4] == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR, rubidium@10603: w->width - 101); rubidium@10603: break; rubidium@10603: rubidium@10603: case NS_COMPANY_BANKRUPT: rubidium@10603: DrawStringCentered(w->width >> 1, 1, STR_705C_BANKRUPT, TC_FROMSTRING); rubidium@10603: SetDParam(0, p->index); rubidium@10603: DrawStringMultiCenter( rubidium@10603: ((w->width - 101) >> 1) + 98, rubidium@10603: 90, rubidium@10603: STR_705D_HAS_BEEN_CLOSED_DOWN_BY, rubidium@10603: w->width - 101); rubidium@10603: break; rubidium@10603: rubidium@10603: case NS_COMPANY_NEW: rubidium@10603: DrawStringCentered(w->width >> 1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, TC_FROMSTRING); rubidium@10603: SetDParam(0, p->index); rubidium@10603: SetDParam(1, ni->params[3]); rubidium@10603: DrawStringMultiCenter( rubidium@10603: ((w->width - 101) >> 1) + 98, rubidium@10603: 90, rubidium@10603: STR_705F_STARTS_CONSTRUCTION_NEAR, rubidium@10603: w->width - 101); rubidium@10603: break; rubidium@10603: rubidium@10603: default: rubidium@10603: NOT_REACHED(); rubidium@10603: } rubidium@10603: } rubidium@10603: truelight@0: rubidium@10556: /** rubidium@10556: * Data common to all news items of a given subtype (structure) rubidium@10556: */ rubidium@10556: struct NewsSubtypeData { rubidium@10556: NewsType type; ///< News category @see NewsType rubidium@10556: NewsMode display_mode; ///< Display mode value @see NewsMode rubidium@10556: NewsFlag flags; ///< Initial NewsFlags bits @see NewsFlag rubidium@10637: DrawNewsCallbackProc *callback; ///< Call-back function rubidium@10556: }; rubidium@10556: rubidium@10556: /** rubidium@10556: * Data common to all news items of a given subtype (actual data) rubidium@10556: */ rubidium@10556: static const struct NewsSubtypeData _news_subtype_data[NS_END] = { rubidium@10556: /* type, display_mode, flags, callback */ rubidium@10637: { NT_ARRIVAL_PLAYER, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ARRIVAL_PLAYER rubidium@10637: { NT_ARRIVAL_OTHER, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ARRIVAL_OTHER rubidium@10637: { NT_ACCIDENT, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_ACCIDENT_TILE rubidium@10637: { NT_ACCIDENT, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ACCIDENT_VEHICLE rubidium@10637: { NT_COMPANY_INFO, NM_CALLBACK, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_TROUBLE rubidium@10637: { NT_COMPANY_INFO, NM_CALLBACK, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_MERGER rubidium@10637: { NT_COMPANY_INFO, NM_CALLBACK, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_BANKRUPT rubidium@10637: { NT_COMPANY_INFO, NM_CALLBACK, NF_TILE, DrawNewsBankrupcy }, ///< NS_COMPANY_NEW rubidium@10637: { NT_OPENCLOSE, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_OPENCLOSE rubidium@10637: { NT_ECONOMY, NM_NORMAL, NF_NONE, NULL }, ///< NS_ECONOMY rubidium@10637: { NT_INDUSTRY_PLAYER, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_PLAYER rubidium@10637: { NT_INDUSTRY_OTHER, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_OTHER rubidium@10637: { NT_INDUSTRY_NOBODY, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_NOBODY rubidium@10637: { NT_ADVICE, NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ADVICE rubidium@10637: { NT_NEW_VEHICLES, NM_CALLBACK, NF_NONE, DrawNewsNewVehicleAvail }, ///< NS_NEW_VEHICLES rubidium@10637: { NT_ACCEPTANCE, NM_SMALL, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_ACCEPTANCE rubidium@10637: { NT_SUBSIDIES, NM_NORMAL, NF_TILE|NF_TILE2, NULL }, ///< NS_SUBSIDIES rubidium@10637: { NT_GENERAL, NM_NORMAL, NF_TILE, NULL }, ///< NS_GENERAL rubidium@10556: }; rubidium@10556: rubidium@8094: /** Initialize the news-items data structures */ rubidium@6573: void InitNewsItemStructs() dominik@79: { glx@10301: free(_news_items); glx@10301: _max_news_items = max(ScaleByMapSize(30), 30U); glx@10301: _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: rubidium@10558: struct NewsWindow : Window { rubidium@10558: uint16 chat_height; rubidium@10558: NewsItem *ni; rubidium@9259: rubidium@10558: NewsWindow(const WindowDesc *desc, NewsItem *ni) : Window(desc), ni(ni) rubidium@10558: { rubidium@10558: const Window *w = FindWindowById(WC_SEND_NETWORK_MSG, 0); rubidium@10558: this->chat_height = (w != NULL) ? w->height : 0; rubidium@9259: rubidium@10558: this->ni = &_news_items[_forced_news == INVALID_NEWS ? _current_news : _forced_news]; rubidium@10558: this->flags4 |= WF_DISABLE_VP_SCROLL; truelight@0: rubidium@10558: this->FindWindowPlacementAndResize(desc); rubidium@10558: } rubidium@9259: rubidium@10558: void DrawNewsBorder() rubidium@10558: { rubidium@10558: int left = 0; rubidium@10558: int right = this->width - 1; rubidium@10558: int top = 0; rubidium@10558: int bottom = this->height - 1; rubidium@10558: rubidium@10558: GfxFillRect(left, top, right, bottom, 0xF); rubidium@10558: rubidium@10558: GfxFillRect(left, top, left, bottom, 0xD7); rubidium@10558: GfxFillRect(right, top, right, bottom, 0xD7); rubidium@10558: GfxFillRect(left, top, right, top, 0xD7); rubidium@10558: GfxFillRect(left, bottom, right, bottom, 0xD7); rubidium@10558: rubidium@10558: DrawString(left + 2, top + 1, STR_00C6, TC_FROMSTRING); rubidium@10558: } rubidium@10558: rubidium@10558: virtual void OnPaint() rubidium@10558: { rubidium@10558: const NewsMode display_mode = _news_subtype_data[this->ni->subtype].display_mode; rubidium@10558: rubidium@10558: switch (display_mode) { rubidium@10558: case NM_NORMAL: rubidium@10558: case NM_THIN: { rubidium@10558: this->DrawNewsBorder(); rubidium@10558: rubidium@10558: DrawString(2, 1, STR_00C6, TC_FROMSTRING); rubidium@10558: rubidium@10558: SetDParam(0, this->ni->date); rubidium@10558: DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING); rubidium@10558: rubidium@10558: if (!(this->ni->flags & NF_VIEWPORT)) { rubidium@10558: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); rubidium@10558: DrawStringMultiCenter(215, display_mode == NM_NORMAL ? 76 : 56, rubidium@10558: this->ni->string_id, this->width - 4); rubidium@10558: } else { rubidium@10558: /* Back up transparency options to draw news view */ rubidium@10558: TransparencyOptionBits to_backup = _transparency_opt; rubidium@10558: _transparency_opt = 0; rubidium@10595: this->DrawViewport(); rubidium@10558: _transparency_opt = to_backup; rubidium@10558: rubidium@10558: /* Shade the viewport into gray, or color*/ rubidium@10558: ViewPort *vp = this->viewport; rubidium@10558: GfxFillRect(vp->left - this->left, vp->top - this->top, rubidium@10558: vp->left - this->left + vp->width - 1, vp->top - this->top + vp->height - 1, rubidium@10558: (this->ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE) rubidium@10558: ); rubidium@10558: rubidium@10558: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); rubidium@10558: DrawStringMultiCenter(this->width / 2, 20, this->ni->string_id, this->width - 4); rubidium@10558: } rubidium@10558: break; rubidium@10558: } rubidium@10558: rubidium@10558: case NM_CALLBACK: rubidium@10558: this->DrawNewsBorder(); rubidium@10637: (_news_subtype_data[this->ni->subtype].callback)(this, ni); rubidium@10558: break; rubidium@10558: rubidium@10558: default: rubidium@10595: this->DrawWidgets(); rubidium@10558: if (!(this->ni->flags & NF_VIEWPORT)) { rubidium@10558: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); rubidium@10558: DrawStringMultiCenter(140, 38, this->ni->string_id, 276); rubidium@10558: } else { rubidium@10595: this->DrawViewport(); rubidium@10558: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); rubidium@10558: DrawStringMultiCenter(this->width / 2, this->height - 16, this->ni->string_id, this->width - 4); rubidium@10558: } rubidium@10558: break; rubidium@10558: } rubidium@10558: } rubidium@10558: rubidium@10558: virtual void OnClick(Point pt, int widget) rubidium@10558: { rubidium@10558: switch (widget) { rubidium@10558: case 1: rubidium@10558: this->ni->duration = 0; rubidium@10558: delete this; rubidium@10558: _forced_news = INVALID_NEWS; rubidium@10558: break; rubidium@10558: rubidium@10558: case 0: rubidium@10558: if (this->ni->flags & NF_VEHICLE) { rubidium@10558: Vehicle *v = GetVehicle(this->ni->data_a); rubidium@10558: ScrollMainWindowTo(v->x_pos, v->y_pos); rubidium@10558: } else if (this->ni->flags & NF_TILE) { rubidium@10558: if (_ctrl_pressed) { rubidium@10558: ShowExtraViewPortWindow(this->ni->data_a); rubidium@10558: if (this->ni->flags & NF_TILE2) { rubidium@10558: ShowExtraViewPortWindow(this->ni->data_b); rubidium@10558: } rubidium@9259: } else { rubidium@10558: if (!ScrollMainWindowToTile(this->ni->data_a) && this->ni->flags & NF_TILE2) { rubidium@10558: ScrollMainWindowToTile(this->ni->data_b); peter1138@10410: } peter1138@10410: } peter1138@10410: } rubidium@10558: break; peter1138@10410: } truelight@0: } rubidium@10558: rubidium@10607: virtual EventState OnKeyPress(uint16 key, uint16 keycode) rubidium@10558: { rubidium@10558: if (keycode == WKC_SPACE) { rubidium@10558: /* Don't continue. */ rubidium@10558: delete this; rubidium@10607: return ES_HANDLED; rubidium@10558: } rubidium@10607: return ES_NOT_HANDLED; rubidium@10558: } rubidium@10558: rubidium@10558: virtual void OnInvalidateData(int data) rubidium@10558: { rubidium@10558: /* The chatbar has notified us that is was either created or closed */ rubidium@10558: this->chat_height = data; rubidium@10558: } rubidium@10558: rubidium@10558: virtual void OnTick() rubidium@10558: { rubidium@10558: /* Scroll up newsmessages from the bottom in steps of 4 pixels */ rubidium@10558: int y = max(this->top - 4, _screen.height - this->height - 12 - this->chat_height); rubidium@10558: if (y == this->top) return; rubidium@10558: rubidium@10558: if (this->viewport != NULL) this->viewport->top += y - this->top; rubidium@10558: rubidium@10558: int diff = Delta(this->top, y); rubidium@10558: this->top = y; rubidium@10558: rubidium@10558: SetDirtyBlocks(this->left, this->top - diff, this->left + this->width, this->top + this->height); rubidium@10558: } rubidium@10558: }; truelight@0: rubidium@8094: /** rubidium@8094: * Return the correct index in the pseudo-fifo rubidium@8094: * queue and deals with overflows when increasing the index rubidium@8094: */ rubidium@9272: static inline NewsID IncreaseIndex(NewsID i) dominik@79: { Darkvater@5258: assert(i != INVALID_NEWS); glx@10301: return (i + 1) % _max_news_items; Darkvater@5245: } Darkvater@5245: rubidium@8094: /** rubidium@8094: * Return the correct index in the pseudo-fifo rubidium@8094: * queue and deals with overflows when decreasing the index rubidium@8094: */ rubidium@9272: static inline NewsID DecreaseIndex(NewsID i) Darkvater@5245: { Darkvater@5245: assert(i != INVALID_NEWS); glx@10301: return (i + _max_news_items - 1) % _max_news_items; dominik@79: } dominik@79: rubidium@8094: /** rubidium@8094: * Add a new newsitem to be shown. rubidium@10567: * @param string String to display rubidium@10567: * @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@8094: * rubidium@10567: * @see NewsSubype rubidium@8094: */ rubidium@10556: void AddNewsItem(StringID string, NewsSubtype subtype, uint data_a, uint data_b) truelight@0: { tron@4000: if (_game_mode == GM_MENU) return; truelight@0: belugas@6674: /* check the rare case that the oldest (to be overwritten) news item is open */ glx@10301: if (_total_news == _max_news_items && (_oldest_news == _current_news || _oldest_news == _forced_news)) { Darkvater@5253: MoveToNextItem(); rubidium@9272: } dominik@102: glx@10301: 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@9272: NewsID l_news = _latest_news; rubidium@9272: _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) { glx@10301: assert(_total_news == _max_news_items); rubidium@9272: _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: belugas@6674: /* Add news to _latest_news */ rubidium@9259: NewsItem *ni = &_news_items[_latest_news]; rubidium@9259: memset(ni, 0, sizeof(*ni)); Darkvater@5245: rubidium@9259: ni->string_id = string; rubidium@10556: ni->subtype = subtype; rubidium@10556: ni->flags = _news_subtype_data[subtype].flags; Darkvater@5245: rubidium@9259: /* show this news message in color? */ rubidium@10703: if (_cur_year >= _settings.gui.colored_news_year) ni->flags |= NF_INCOLOR; rubidium@9259: rubidium@9259: ni->data_a = data_a; rubidium@9259: ni->data_b = data_b; rubidium@9259: ni->date = _date; rubidium@9259: CopyOutDParam(ni->params, 0, lengthof(ni->params)); rubidium@9259: rubidium@9259: Window *w = FindWindowById(WC_MESSAGE_HISTORY, 0); rubidium@9259: if (w == NULL) return; rubidium@10434: w->SetDirty(); rubidium@9259: w->vscroll.count = _total_news; truelight@0: } truelight@0: dominik@715: rubidium@8094: /** rubidium@10253: * Per-NewsType data rubidium@8094: */ rubidium@10273: NewsTypeData _news_type_data[NT_END] = { rubidium@10273: /* name, age, sound, display */ rubidium@10273: { "arrival_player", 60, SND_1D_APPLAUSE, ND_FULL }, ///< NT_ARRIVAL_PLAYER rubidium@10273: { "arrival_other", 60, SND_1D_APPLAUSE, ND_FULL }, ///< NT_ARRIVAL_OTHER rubidium@10273: { "accident", 90, SND_BEGIN, ND_FULL }, ///< NT_ACCIDENT rubidium@10273: { "company_info", 60, SND_BEGIN, ND_FULL }, ///< NT_COMPANY_INFO rubidium@10273: { "openclose", 90, SND_BEGIN, ND_FULL }, ///< NT_OPENCLOSE rubidium@10273: { "economy", 30, SND_BEGIN, ND_FULL }, ///< NT_ECONOMY rubidium@10273: { "production_player", 30, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_PLAYER rubidium@10273: { "production_other", 30, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_OTHER rubidium@10273: { "production_nobody", 30, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_NOBODY rubidium@10273: { "advice", 150, SND_BEGIN, ND_FULL }, ///< NT_ADVICE rubidium@10273: { "new_vehicles", 30, SND_1E_OOOOH, ND_FULL }, ///< NT_NEW_VEHICLES rubidium@10273: { "acceptance", 90, SND_BEGIN, ND_FULL }, ///< NT_ACCEPTANCE rubidium@10273: { "subsidies", 180, SND_BEGIN, ND_FULL }, ///< NT_SUBSIDIES rubidium@10273: { "general", 60, SND_BEGIN, ND_FULL }, ///< NT_GENERAL rubidium@8094: }; rubidium@8094: 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@7837: 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@7837: 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@7837: 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: truelight@0: rubidium@8094: /** 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: rubidium@10556: SoundFx sound = _news_type_data[_news_subtype_data[ni->subtype].type].sound; tron@2639: if (sound != 0) SndPlayFx(sound); dominik@79: rubidium@9272: int top = _screen.height; rubidium@9272: Window *w; rubidium@10556: switch (_news_subtype_data[ni->subtype].display_mode) { tron@427: case NM_NORMAL: rubidium@9272: case NM_CALLBACK: tron@427: _news_type13_desc.top = top; rubidium@10558: w = new NewsWindow(&_news_type13_desc, ni); rubidium@9272: if (ni->flags & NF_VIEWPORT) { rubidium@10257: InitializeWindowViewport(w, 2, 58, 426, 110, truelight@7120: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); rubidium@9272: } tron@427: break; tron@427: rubidium@9272: case NM_THIN: tron@427: _news_type2_desc.top = top; rubidium@10558: w = new NewsWindow(&_news_type2_desc, ni); rubidium@9272: if (ni->flags & NF_VIEWPORT) { rubidium@10257: InitializeWindowViewport(w, 2, 58, 426, 70, truelight@7120: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); rubidium@9272: } tron@427: break; tron@427: rubidium@9272: default: tron@427: _news_type0_desc.top = top; rubidium@10558: w = new NewsWindow(&_news_type0_desc, ni); rubidium@9272: if (ni->flags & NF_VIEWPORT) { rubidium@10257: InitializeWindowViewport(w, 3, 17, 274, 47, truelight@7120: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); rubidium@9272: } 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@8094: /** 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; rubidium@10570: InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_TICKER); dominik@79: } dominik@79: dominik@79: rubidium@8094: /** rubidium@8094: * Are we ready to show another news item? rubidium@8094: * Only if nothing is in the newsticker and no newspaper is displayed rubidium@8094: */ rubidium@6573: static bool ReadyForNextItem() dominik@79: { Darkvater@5246: NewsID item = (_forced_news == INVALID_NEWS) ? _current_news : _forced_news; dominik@92: glx@10301: if (item >= _max_news_items) return true; rubidium@9272: NewsItem *ni = &_news_items[item]; dominik@79: belugas@6674: /* Ticker message belugas@6674: * Check if the status bar message is still being displayed? */ rubidium@10570: if (IsNewsTickerShown()) return false; truelight@0: belugas@6674: /* Newspaper message, decrement duration counter */ Darkvater@5264: if (ni->duration != 0) ni->duration--; truelight@0: belugas@6674: /* neither newsticker nor newspaper are running */ Darkvater@5264: return (ni->duration == 0 || FindWindowById(WC_NEWS_WINDOW, 0) == NULL); dominik@79: } dominik@79: rubidium@8094: /** 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: belugas@6674: /* if we're not at the last item, then move on */ tron@427: if (_current_news != _latest_news) { rubidium@9272: _current_news = (_current_news == INVALID_NEWS) ? _oldest_news : IncreaseIndex(_current_news); rubidium@9272: NewsItem *ni = &_news_items[_current_news]; rubidium@10556: const NewsType type = _news_subtype_data[ni->subtype].type; dominik@79: belugas@6674: /* check the date, don't show too old items */ rubidium@10556: if (_date - _news_type_data[type].age > ni->date) return; dominik@79: rubidium@10556: switch (_news_type_data[type].display) { rubidium@8094: default: NOT_REACHED(); rubidium@10570: case ND_OFF: // Off - show nothing only a small reminder in the status bar rubidium@10570: InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_REMINDER); tron@2639: break; tron@2639: rubidium@10273: case ND_SUMMARY: // Summary - show ticker, but if forced big, cascade to full rubidium@8094: if (!(ni->flags & NF_FORCE_BIG)) { rubidium@8094: ShowTicker(ni); rubidium@8094: break; rubidium@8094: } rubidium@8094: /* Fallthrough */ rubidium@8094: rubidium@10273: case ND_FULL: // Full - show newspaper rubidium@8094: ShowNewspaper(ni); rubidium@8094: break; tron@2639: } truelight@0: } dominik@79: } truelight@0: rubidium@6573: void NewsLoop() truelight@0: { belugas@6674: /* no news item yet */ tron@427: if (_total_news == 0) return; dominik@79: Darkvater@5253: if (ReadyForNextItem()) MoveToNextItem(); dominik@79: } dominik@79: rubidium@8094: /** 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: belugas@6674: /* Delete the news window */ dominik@79: DeleteWindowById(WC_NEWS_WINDOW, 0); dominik@79: belugas@6674: /* 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@8094: /** 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@9272: 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@9272: 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) { glx@10301: i = _latest_news + _max_news_items - i; tron@2639: } else { dominik@89: i = _latest_news - i; tron@2639: } dominik@89: glx@10301: i %= _max_news_items; dominik@89: return i; dominik@89: } dominik@89: rubidium@8094: /** rubidium@8094: * 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: rubidium@10521: CopyInDParam(0, ni->params, lengthof(ni->params)); rubidium@10521: 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@9272: const char *ptr = buffer; rubidium@9272: char *dest = buffer2; truelight@7038: WChar c_last = '\0'; peter1138@5108: for (;;) { peter1138@5108: WChar c = Utf8Consume(&ptr); peter1138@5108: if (c == 0) break; truelight@7038: /* Make a space from a newline, but ignore multiple newlines */ truelight@7038: if (c == '\n' && c_last != '\n') { truelight@7038: dest[0] = ' '; truelight@7038: dest++; truelight@7038: } 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: } truelight@7038: 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: rubidium@10610: struct MessageHistoryWindow : Window { rubidium@10610: MessageHistoryWindow(const WindowDesc *desc) : Window(desc) rubidium@10610: { rubidium@10610: this->vscroll.cap = 10; rubidium@10610: this->vscroll.count = _total_news; rubidium@10610: this->resize.step_height = 12; rubidium@10610: this->resize.height = this->height - 12 * 6; // minimum of 4 items in the list, each item 12 high rubidium@10610: this->resize.step_width = 1; rubidium@10610: this->resize.width = 200; // can't make window any smaller than 200 pixel rubidium@9272: rubidium@10610: this->FindWindowPlacementAndResize(desc); rubidium@10610: } dominik@1097: rubidium@10610: virtual void OnPaint() rubidium@10610: { rubidium@10610: int y = 19; rubidium@9272: rubidium@10610: SetVScrollCount(this, _total_news); rubidium@10610: this->DrawWidgets(); rubidium@9272: rubidium@10610: if (_total_news == 0) return; rubidium@10610: NewsID show = min(_total_news, this->vscroll.cap); rubidium@10610: rubidium@10610: for (NewsID p = this->vscroll.pos; p < this->vscroll.pos + show; p++) { rubidium@10610: /* get news in correct order */ rubidium@10610: const NewsItem *ni = &_news_items[getNews(p)]; rubidium@10610: rubidium@10610: SetDParam(0, ni->date); rubidium@10610: DrawString(4, y, STR_SHORT_DATE, TC_WHITE); rubidium@10610: rubidium@10610: DrawNewsString(82, y, TC_WHITE, ni, this->width - 95); rubidium@10610: y += 12; rubidium@10610: } dominik@89: } rubidium@10610: rubidium@10610: virtual void OnClick(Point pt, int widget) rubidium@10610: { rubidium@10610: if (widget == 3) { rubidium@10610: int y = (pt.y - 19) / 12; rubidium@10610: NewsID p = getNews(y + this->vscroll.pos); rubidium@10610: rubidium@10610: if (p != INVALID_NEWS) ShowNewsMessage(p); rubidium@10610: } rubidium@10610: } rubidium@10610: rubidium@10610: virtual void OnResize(Point new_size, Point delta) rubidium@10610: { rubidium@10610: this->vscroll.cap += delta.y / 12; rubidium@10610: } rubidium@10610: }; 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@7837: 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@8094: /** Display window with news messages history */ rubidium@6573: void ShowMessageHistory() dominik@89: { dominik@89: DeleteWindowById(WC_MESSAGE_HISTORY, 0); rubidium@10610: new MessageHistoryWindow(&_message_history_desc); dominik@89: } dominik@89: belugas@6605: rubidium@8387: /** News settings window widget offset constants */ belugas@6605: enum { peter1138@8839: WIDGET_NEWSOPT_DROP_SUMMARY = 4, ///< Dropdown that adjusts at once the level for all settings peter1138@8839: WIDGET_NEWSOPT_SOUNDTICKER = 6, ///< Button activating sound on events peter1138@8839: WIDGET_NEWSOPT_START_OPTION = 8, ///< First widget that is part of a group [<] .. [.] belugas@6605: }; belugas@6605: rubidium@10571: static const StringID _message_opt[] = {STR_OFF, STR_SUMMARY, STR_FULL, INVALID_STRING_ID}; peter1138@8839: rubidium@10571: struct MessageOptionsWindow : Window { rubidium@10571: int state; belugas@6605: rubidium@10571: MessageOptionsWindow(const WindowDesc *desc) : Window(desc) rubidium@10571: { rubidium@10571: NewsDisplay all_val; belugas@6605: rubidium@10571: /* Set up the initial disabled buttons in the case of 'off' or 'full' */ rubidium@10571: all_val = _news_type_data[0].display; rubidium@10571: for (int i = 0; i < NT_END; i++) { rubidium@10571: this->SetMessageButtonStates(_news_type_data[i].display, i); rubidium@10571: /* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */ rubidium@10571: if (_news_type_data[i].display != all_val) all_val = ND_OFF; rubidium@10571: } rubidium@10571: /* If all values are the same value, the ALL-button will take over this value */ rubidium@10571: this->state = all_val; peter1138@10681: peter1138@10681: this->FindWindowPlacementAndResize(desc); rubidium@10571: } rubidium@10571: rubidium@10571: /** rubidium@10571: * Setup the disabled/enabled buttons in the message window rubidium@10571: * If the value is 'off' disable the [<] widget, and enable the [>] one rubidium@10571: * Same-wise for all the others. Starting value of 4 is the first widget rubidium@10571: * group. These are grouped as [<][>] .. [<][>], etc. rubidium@10571: * @param value to set in the widget rubidium@10571: * @param element index of the group of widget to set rubidium@10571: */ rubidium@10571: void SetMessageButtonStates(byte value, int element) rubidium@10571: { rubidium@10571: element *= NB_WIDG_PER_SETTING; rubidium@10571: rubidium@10571: this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION, value == 0); rubidium@10571: this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION + 2, value == 2); rubidium@10571: } rubidium@10571: rubidium@10571: virtual void OnPaint() rubidium@10571: { rubidium@10571: if (_news_ticker_sound) this->LowerWidget(WIDGET_NEWSOPT_SOUNDTICKER); rubidium@10571: rubidium@10571: this->widget[WIDGET_NEWSOPT_DROP_SUMMARY].data = _message_opt[this->state]; rubidium@10595: this->DrawWidgets(); rubidium@10571: rubidium@10571: /* Draw the string of each setting on each button. */ rubidium@10571: for (int i = 0, y = 26; i < NT_END; i++, y += 12) { rubidium@10571: /* 51 comes from 13 + 89 (left and right of the button)+1, shiefted by one as to get division, rubidium@10571: * which will give centered position */ rubidium@10571: DrawStringCentered(51, y + 1, _message_opt[_news_type_data[i].display], TC_BLACK); rubidium@10571: } rubidium@10571: } rubidium@10571: rubidium@10571: virtual void OnClick(Point pt, int widget) rubidium@10571: { rubidium@10571: switch (widget) { rubidium@10571: case WIDGET_NEWSOPT_DROP_SUMMARY: // Dropdown menu for all settings rubidium@10571: ShowDropDownMenu(this, _message_opt, this->state, WIDGET_NEWSOPT_DROP_SUMMARY, 0, 0); rubidium@10571: break; rubidium@10571: rubidium@10571: case WIDGET_NEWSOPT_SOUNDTICKER: // Change ticker sound on/off rubidium@10571: _news_ticker_sound ^= 1; rubidium@10571: this->ToggleWidgetLoweredState(widget); rubidium@10571: this->InvalidateWidget(widget); rubidium@10571: break; rubidium@10571: rubidium@10571: default: { // Clicked on the [<] .. [>] widgets rubidium@10571: int wid = widget - WIDGET_NEWSOPT_START_OPTION; rubidium@10571: if (wid >= 0 && wid < (NB_WIDG_PER_SETTING * NT_END)) { rubidium@10571: int element = wid / NB_WIDG_PER_SETTING; rubidium@10571: byte val = (_news_type_data[element].display + ((wid % NB_WIDG_PER_SETTING) ? 1 : -1)) % 3; rubidium@10571: rubidium@10571: this->SetMessageButtonStates(val, element); rubidium@10571: _news_type_data[element].display = (NewsDisplay)val; rubidium@10571: this->SetDirty(); peter1138@10410: } rubidium@10571: break; rubidium@9272: } rubidium@10571: } rubidium@10571: } belugas@6605: rubidium@10571: virtual void OnDropdownSelect(int widget, int index) rubidium@10571: { rubidium@10571: this->state = index; rubidium@10571: rubidium@10571: for (int i = 0; i < NT_END; i++) { rubidium@10571: this->SetMessageButtonStates(index, i); rubidium@10571: _news_type_data[i].display = (NewsDisplay)index; rubidium@10571: } rubidium@10571: this->SetDirty(); truelight@0: } rubidium@10571: }; truelight@0: rubidium@8387: rubidium@8387: /* rubidium@8387: * The news settings window widgets rubidium@8387: * rubidium@8387: * Main part of the window is a list of news-setting lines, one for each news category. rubidium@8387: * Each line is constructed by an expansion of the \c NEWS_SETTINGS_LINE macro rubidium@8387: */ rubidium@8387: rubidium@8387: /** rubidium@8387: * Macro to construct one news-setting line in the news-settings window. rubidium@8387: * One line consists of four widgets, namely rubidium@8387: * - A [<] button rubidium@8387: * - A [...] label rubidium@8387: * - A [>] button rubidium@8387: * - A text label describing the news category rubidium@8387: * Horizontal positions of the widgets are hard-coded, vertical start position is (\a basey + \a linenum * \c NEWS_SETTING_BASELINE_SKIP). rubidium@8387: * Height of one line is 12, with the text label shifted 1 pixel down. rubidium@8387: * rubidium@8387: * First line should be widget number WIDGET_NEWSOPT_START_OPTION rubidium@8387: * rubidium@8387: * @param basey: Base Y coordinate rubidium@8387: * @param linenum: Count, news-setting is the \a linenum-th line rubidium@8387: * @param text: StringID for the text label to display rubidium@8387: */ rubidium@8387: #define NEWS_SETTINGS_LINE(basey, linenum, text) \ rubidium@8387: { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@8387: 4, 12, basey + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@8387: SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, \ rubidium@8387: { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@8387: 13, 89, basey + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@8387: STR_EMPTY, STR_NULL}, \ rubidium@8387: { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@8387: 90, 98, basey + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@8387: SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, \ rubidium@8387: { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@8387: 103, 409, basey + 1 + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 13 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@8387: text, STR_NULL} rubidium@8387: rubidium@8387: static const int NEWS_SETTING_BASELINE_SKIP = 12; ///< Distance between two news-setting lines, should be at least 12 rubidium@8387: rubidium@8387: truelight@0: static const Widget _message_options_widgets[] = { rubidium@8387: { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_BROWN, 0, 10, 0, 13, rubidium@8387: STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@8387: { WWT_CAPTION, RESIZE_NONE, COLOUR_BROWN, 11, 409, 0, 13, rubidium@8387: STR_0204_MESSAGE_OPTIONS, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@8387: { WWT_PANEL, RESIZE_NONE, COLOUR_BROWN, 0, 409, 14, 64 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@8387: 0x0, STR_NULL}, rubidium@4344: belugas@6605: /* Text at the top of the main panel, in black */ rubidium@8387: { WWT_LABEL, RESIZE_NONE, COLOUR_BROWN, rubidium@8387: 0, 409, 13, 26, rubidium@8387: STR_0205_MESSAGE_TYPES, STR_NULL}, belugas@6605: rubidium@8387: /* General drop down and sound button, widgets WIDGET_NEWSOPT_BTN_SUMMARY and WIDGET_NEWSOPT_DROP_SUMMARY */ peter1138@8839: { WWT_DROPDOWN, RESIZE_NONE, COLOUR_YELLOW, peter1138@8839: 4, 98, 34 + NT_END * NEWS_SETTING_BASELINE_SKIP, 45 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@8387: 0x0, STR_NULL}, belugas@6605: rubidium@8387: { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, rubidium@8387: 103, 409, 35 + NT_END * NEWS_SETTING_BASELINE_SKIP, 47 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@8387: STR_MESSAGES_ALL, STR_NULL}, belugas@6605: rubidium@8387: /* Below is widget WIDGET_NEWSOPT_SOUNDTICKER */ rubidium@8387: { WWT_TEXTBTN_2, RESIZE_NONE, COLOUR_YELLOW, rubidium@8387: 4, 98, 46 + NT_END * NEWS_SETTING_BASELINE_SKIP, 57 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@8387: STR_02DB_OFF, STR_NULL}, truelight@6609: rubidium@8387: { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, rubidium@8387: 103, 409, 47 + NT_END * NEWS_SETTING_BASELINE_SKIP, 59 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@8387: STR_MESSAGE_SOUND, STR_NULL}, rubidium@8387: rubidium@8387: /* List of news-setting lines (4 widgets for each line). rubidium@8387: * First widget must be number WIDGET_NEWSOPT_START_OPTION rubidium@8387: */ rubidium@8387: NEWS_SETTINGS_LINE(26, NT_ARRIVAL_PLAYER, STR_0206_ARRIVAL_OF_FIRST_VEHICLE), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_ARRIVAL_OTHER, STR_0207_ARRIVAL_OF_FIRST_VEHICLE), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_ACCIDENT, STR_0208_ACCIDENTS_DISASTERS), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_COMPANY_INFO, STR_0209_COMPANY_INFORMATION), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_OPENCLOSE, STR_NEWS_OPEN_CLOSE), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_ECONOMY, STR_020A_ECONOMY_CHANGES), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_PLAYER, STR_INDUSTRY_CHANGES_SERVED_BY_PLAYER), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_OTHER, STR_INDUSTRY_CHANGES_SERVED_BY_OTHER), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_NOBODY, STR_OTHER_INDUSTRY_PRODUCTION_CHANGES), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_ADVICE, STR_020B_ADVICE_INFORMATION_ON_PLAYER), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_NEW_VEHICLES, STR_020C_NEW_VEHICLES), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_ACCEPTANCE, STR_020D_CHANGES_OF_CARGO_ACCEPTANCE), rubidium@8387: NEWS_SETTINGS_LINE(26, NT_SUBSIDIES, STR_020E_SUBSIDIES), rubidium@8387: 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@8387: 270, 22, 410, 65 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@8387: 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); rubidium@10571: new MessageOptionsWindow(&_message_options_desc); truelight@0: } tron@3139: tron@3139: tron@3139: void DeleteVehicleNews(VehicleID vid, StringID news) tron@3139: { rubidium@9272: 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@10152: _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) { rubidium@10558: NewsWindow *w = dynamic_cast(FindWindowById(WC_NEWS_WINDOW, 0)); rubidium@10558: NewsID visible_news = (w != NULL) ? (NewsID)(w->ni - _news_items) : INVALID_NEWS; Darkvater@5771: rubidium@9272: for (NewsID i = n;; i = DecreaseIndex(i)) { rubidium@9272: _news_items[i] = _news_items[DecreaseIndex(i)]; Darkvater@5254: Darkvater@6090: if (i != _latest_news) { rubidium@9272: if (i == _current_news) _current_news = IncreaseIndex(_current_news); rubidium@9272: if (i == _forced_news) _forced_news = IncreaseIndex(_forced_news); rubidium@10558: 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@9272: _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@9272: Window *w = FindWindowById(WC_MESSAGE_HISTORY, 0); tron@3143: if (w != NULL) { rubidium@10434: 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: }