tron@2186: /* $Id$ */ tron@2186: rubidium@9111: /** @file news_gui.cpp GUI functions related to news messages. */ belugas@6279: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" peter1138@3155: #include "gui.h" rubidium@8107: #include "window_gui.h" rubidium@8224: #include "viewport_func.h" rubidium@8763: #include "news_func.h" rubidium@8270: #include "settings_type.h" belugas@7849: #include "transparency.h" rubidium@8114: #include "strings_func.h" rubidium@8131: #include "window_func.h" rubidium@8140: #include "date_func.h" rubidium@8144: #include "vehicle_base.h" rubidium@8157: #include "sound_func.h" rubidium@8214: #include "string_func.h" peter1138@8284: #include "widgets/dropdown_func.h" glx@9026: #include "map_func.h" rubidium@9248: #include "statusbar_gui.h" rubidium@9281: #include "player_face.h" truelight@0: rubidium@8264: #include "table/sprites.h" rubidium@8264: #include "table/strings.h" rubidium@8264: belugas@6279: #define NB_WIDG_PER_SETTING 4 tron@427: rubidium@8268: NewsItem _statusbar_news_item; rubidium@8270: bool _news_ticker_sound; rubidium@9406: rubidium@9406: static uint MIN_NEWS_AMOUNT = 30; ///< prefered minimum amount of news messages rubidium@9406: static uint _total_news = 0; ///< current number of news items rubidium@9406: static NewsItem *_oldest_news = NULL; ///< head of news items queue rubidium@9406: static NewsItem *_latest_news = NULL; ///< tail of news items queue rubidium@7598: rubidium@7598: /** Forced news item. rubidium@7598: * Users can force an item by accessing the history or "last message". rubidium@9406: * If the message being shown was forced by the user, a pointer is stored rubidium@9406: * in _forced_news. Otherwise, \a _forced_news variable is NULL. */ rubidium@9406: static NewsItem *_forced_news = NULL; ///< item the user has asked for dominik@79: rubidium@9406: /** Current news item (last item shown regularly). */ rubidium@9406: static NewsItem *_current_news = NULL; truelight@0: rubidium@9236: rubidium@9236: typedef void DrawNewsCallbackProc(struct Window *w, const NewsItem *ni); rubidium@8763: void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni); rubidium@9281: rubidium@9281: static void DrawNewsBankrupcy(Window *w, const NewsItem *ni) rubidium@9281: { rubidium@9658: const CompanyNewsInformation *cni = (const CompanyNewsInformation*)ni->free_data; rubidium@9658: rubidium@9658: DrawPlayerFace(cni->face, cni->colour, 2, 23); frosch@9607: GfxFillRect(3, 23, 3 + 91, 23 + 118, PALETTE_TO_STRUCT_GREY, FILLRECT_RECOLOR); rubidium@9281: rubidium@9658: SetDParamStr(0, cni->president_name); rubidium@9658: DrawStringMultiCenter(49, 148, STR_JUST_RAW_STRING, 94); rubidium@9281: rubidium@9281: switch (ni->subtype) { rubidium@9281: case NS_COMPANY_TROUBLE: rubidium@9281: DrawStringCentered(w->width >> 1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, TC_FROMSTRING); rubidium@9281: rubidium@9658: SetDParam(0, ni->params[2]); rubidium@9281: rubidium@9281: DrawStringMultiCenter( rubidium@9281: ((w->width - 101) >> 1) + 98, rubidium@9281: 90, rubidium@9281: STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED, rubidium@9281: w->width - 101); rubidium@9281: break; rubidium@9281: rubidium@9281: case NS_COMPANY_MERGER: rubidium@9281: DrawStringCentered(w->width >> 1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, TC_FROMSTRING); rubidium@9281: SetDParam(0, ni->params[2]); rubidium@9658: SetDParam(1, ni->params[3]); rubidium@9281: SetDParam(2, ni->params[4]); rubidium@9281: DrawStringMultiCenter( rubidium@9281: ((w->width - 101) >> 1) + 98, rubidium@9281: 90, rubidium@9281: ni->params[4] == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR, rubidium@9281: w->width - 101); rubidium@9281: break; rubidium@9281: rubidium@9281: case NS_COMPANY_BANKRUPT: rubidium@9281: DrawStringCentered(w->width >> 1, 1, STR_705C_BANKRUPT, TC_FROMSTRING); rubidium@9658: SetDParam(0, ni->params[2]); rubidium@9281: DrawStringMultiCenter( rubidium@9281: ((w->width - 101) >> 1) + 98, rubidium@9281: 90, rubidium@9281: STR_705D_HAS_BEEN_CLOSED_DOWN_BY, rubidium@9281: w->width - 101); rubidium@9281: break; rubidium@9281: rubidium@9281: case NS_COMPANY_NEW: rubidium@9281: DrawStringCentered(w->width >> 1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, TC_FROMSTRING); rubidium@9658: SetDParam(0, ni->params[2]); rubidium@9281: SetDParam(1, ni->params[3]); rubidium@9281: DrawStringMultiCenter( rubidium@9281: ((w->width - 101) >> 1) + 98, rubidium@9281: 90, rubidium@9281: STR_705F_STARTS_CONSTRUCTION_NEAR, rubidium@9281: w->width - 101); rubidium@9281: break; rubidium@9281: rubidium@9281: default: rubidium@9281: NOT_REACHED(); rubidium@9281: } rubidium@9281: } rubidium@9281: truelight@0: rubidium@9234: /** rubidium@9234: * Data common to all news items of a given subtype (structure) rubidium@9234: */ rubidium@9234: struct NewsSubtypeData { rubidium@9234: NewsType type; ///< News category @see NewsType rubidium@9234: NewsMode display_mode; ///< Display mode value @see NewsMode rubidium@9234: NewsFlag flags; ///< Initial NewsFlags bits @see NewsFlag rubidium@9313: DrawNewsCallbackProc *callback; ///< Call-back function rubidium@9234: }; rubidium@9234: rubidium@9234: /** rubidium@9234: * Data common to all news items of a given subtype (actual data) rubidium@9234: */ rubidium@9234: static const struct NewsSubtypeData _news_subtype_data[NS_END] = { rubidium@9234: /* type, display_mode, flags, callback */ rubidium@9313: { NT_ARRIVAL_PLAYER, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ARRIVAL_PLAYER rubidium@9313: { NT_ARRIVAL_OTHER, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ARRIVAL_OTHER rubidium@9313: { NT_ACCIDENT, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_ACCIDENT_TILE rubidium@9313: { NT_ACCIDENT, NM_THIN, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ACCIDENT_VEHICLE rubidium@9469: { NT_COMPANY_INFO, NM_NORMAL, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_TROUBLE rubidium@9469: { NT_COMPANY_INFO, NM_NORMAL, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_MERGER rubidium@9469: { NT_COMPANY_INFO, NM_NORMAL, NF_NONE, DrawNewsBankrupcy }, ///< NS_COMPANY_BANKRUPT rubidium@9469: { NT_COMPANY_INFO, NM_NORMAL, NF_TILE, DrawNewsBankrupcy }, ///< NS_COMPANY_NEW belugas@9739: { NT_INDUSTRY_OPEN, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_OPEN belugas@9739: { NT_INDUSTRY_CLOSE, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_CLOSE rubidium@9313: { NT_ECONOMY, NM_NORMAL, NF_NONE, NULL }, ///< NS_ECONOMY rubidium@9313: { NT_INDUSTRY_PLAYER, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_PLAYER rubidium@9313: { NT_INDUSTRY_OTHER, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_OTHER rubidium@9313: { NT_INDUSTRY_NOBODY, NM_THIN, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_INDUSTRY_NOBODY rubidium@9313: { NT_ADVICE, NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NULL }, ///< NS_ADVICE rubidium@9469: { NT_NEW_VEHICLES, NM_NORMAL, NF_NONE, DrawNewsNewVehicleAvail }, ///< NS_NEW_VEHICLES rubidium@9313: { NT_ACCEPTANCE, NM_SMALL, NF_VIEWPORT|NF_TILE, NULL }, ///< NS_ACCEPTANCE rubidium@9313: { NT_SUBSIDIES, NM_NORMAL, NF_TILE|NF_TILE2, NULL }, ///< NS_SUBSIDIES rubidium@9313: { NT_GENERAL, NM_NORMAL, NF_TILE, NULL }, ///< NS_GENERAL rubidium@9234: }; rubidium@9234: rubidium@9405: /** rubidium@9405: * Per-NewsType data rubidium@9405: */ rubidium@9405: NewsTypeData _news_type_data[NT_END] = { rubidium@9405: /* name, age, sound, display */ rubidium@9405: { "arrival_player", 60, SND_1D_APPLAUSE, ND_FULL }, ///< NT_ARRIVAL_PLAYER rubidium@9405: { "arrival_other", 60, SND_1D_APPLAUSE, ND_FULL }, ///< NT_ARRIVAL_OTHER rubidium@9405: { "accident", 90, SND_BEGIN, ND_FULL }, ///< NT_ACCIDENT rubidium@9405: { "company_info", 60, SND_BEGIN, ND_FULL }, ///< NT_COMPANY_INFO belugas@9739: { "open", 90, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_OPEN belugas@9739: { "close", 90, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_CLOSE rubidium@9405: { "economy", 30, SND_BEGIN, ND_FULL }, ///< NT_ECONOMY rubidium@9405: { "production_player", 30, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_PLAYER rubidium@9405: { "production_other", 30, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_OTHER rubidium@9405: { "production_nobody", 30, SND_BEGIN, ND_FULL }, ///< NT_INDUSTRY_NOBODY rubidium@9405: { "advice", 150, SND_BEGIN, ND_FULL }, ///< NT_ADVICE rubidium@9405: { "new_vehicles", 30, SND_1E_OOOOH, ND_FULL }, ///< NT_NEW_VEHICLES rubidium@9405: { "acceptance", 90, SND_BEGIN, ND_FULL }, ///< NT_ACCEPTANCE rubidium@9405: { "subsidies", 180, SND_BEGIN, ND_FULL }, ///< NT_SUBSIDIES rubidium@9405: { "general", 60, SND_BEGIN, ND_FULL }, ///< NT_GENERAL rubidium@9405: }; truelight@0: rubidium@9236: struct NewsWindow : Window { rubidium@9236: uint16 chat_height; rubidium@9236: NewsItem *ni; rubidium@9430: static uint duration; rubidium@8763: rubidium@9236: NewsWindow(const WindowDesc *desc, NewsItem *ni) : Window(desc), ni(ni) rubidium@9236: { rubidium@9430: NewsWindow::duration = 555; rubidium@9236: const Window *w = FindWindowById(WC_SEND_NETWORK_MSG, 0); rubidium@9236: this->chat_height = (w != NULL) ? w->height : 0; rubidium@8763: rubidium@9406: this->ni = _forced_news == NULL ? _current_news : _forced_news; rubidium@9236: this->flags4 |= WF_DISABLE_VP_SCROLL; truelight@0: rubidium@9236: this->FindWindowPlacementAndResize(desc); rubidium@9236: } rubidium@8763: rubidium@9236: void DrawNewsBorder() rubidium@9236: { rubidium@9236: int left = 0; rubidium@9236: int right = this->width - 1; rubidium@9236: int top = 0; rubidium@9236: int bottom = this->height - 1; rubidium@9236: belugas@9761: GfxFillRect(left, top, right, bottom, 0xF); rubidium@9236: belugas@9761: GfxFillRect(left, top, left, bottom, 0xD7); belugas@9761: GfxFillRect(right, top, right, bottom, 0xD7); belugas@9761: GfxFillRect(left, top, right, top, 0xD7); belugas@9761: GfxFillRect(left, bottom, right, bottom, 0xD7); rubidium@9236: rubidium@9236: DrawString(left + 2, top + 1, STR_00C6, TC_FROMSTRING); rubidium@9236: } rubidium@9236: rubidium@9236: virtual void OnPaint() rubidium@9236: { rubidium@9236: const NewsMode display_mode = _news_subtype_data[this->ni->subtype].display_mode; rubidium@9236: rubidium@9236: switch (display_mode) { rubidium@9236: case NM_NORMAL: rubidium@9236: case NM_THIN: { rubidium@9236: this->DrawNewsBorder(); rubidium@9236: rubidium@9469: if (_news_subtype_data[this->ni->subtype].callback != NULL) { rubidium@9469: (_news_subtype_data[this->ni->subtype].callback)(this, ni); rubidium@9469: break; rubidium@9469: } rubidium@9469: rubidium@9236: DrawString(2, 1, STR_00C6, TC_FROMSTRING); rubidium@9236: rubidium@9236: SetDParam(0, this->ni->date); rubidium@9236: DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING); rubidium@9236: rubidium@9236: if (!(this->ni->flags & NF_VIEWPORT)) { rubidium@9236: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); rubidium@9236: DrawStringMultiCenter(215, display_mode == NM_NORMAL ? 76 : 56, rubidium@9236: this->ni->string_id, this->width - 4); rubidium@9236: } else { rubidium@9236: /* Back up transparency options to draw news view */ rubidium@9236: TransparencyOptionBits to_backup = _transparency_opt; rubidium@9236: _transparency_opt = 0; rubidium@9273: this->DrawViewport(); rubidium@9236: _transparency_opt = to_backup; rubidium@9236: rubidium@9236: /* Shade the viewport into gray, or color*/ rubidium@9236: ViewPort *vp = this->viewport; rubidium@9236: GfxFillRect(vp->left - this->left, vp->top - this->top, rubidium@9236: vp->left - this->left + vp->width - 1, vp->top - this->top + vp->height - 1, frosch@9607: (this->ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY), FILLRECT_RECOLOR rubidium@9236: ); rubidium@9236: rubidium@9236: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); rubidium@9236: DrawStringMultiCenter(this->width / 2, 20, this->ni->string_id, this->width - 4); rubidium@9236: } rubidium@9236: break; rubidium@9236: } rubidium@9236: rubidium@9236: default: rubidium@9273: this->DrawWidgets(); rubidium@9236: if (!(this->ni->flags & NF_VIEWPORT)) { rubidium@9236: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); rubidium@9236: DrawStringMultiCenter(140, 38, this->ni->string_id, 276); rubidium@9236: } else { rubidium@9273: this->DrawViewport(); rubidium@9236: CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); rubidium@9236: DrawStringMultiCenter(this->width / 2, this->height - 16, this->ni->string_id, this->width - 4); rubidium@9236: } rubidium@9236: break; rubidium@9236: } rubidium@9236: } rubidium@9236: rubidium@9236: virtual void OnClick(Point pt, int widget) rubidium@9236: { rubidium@9236: switch (widget) { rubidium@9236: case 1: rubidium@9430: NewsWindow::duration = 0; rubidium@9236: delete this; rubidium@9406: _forced_news = NULL; rubidium@9236: break; rubidium@9236: rubidium@9236: case 0: rubidium@9236: if (this->ni->flags & NF_VEHICLE) { rubidium@9236: Vehicle *v = GetVehicle(this->ni->data_a); rubidium@9236: ScrollMainWindowTo(v->x_pos, v->y_pos); rubidium@9236: } else if (this->ni->flags & NF_TILE) { rubidium@9236: if (_ctrl_pressed) { rubidium@9236: ShowExtraViewPortWindow(this->ni->data_a); rubidium@9236: if (this->ni->flags & NF_TILE2) { rubidium@9236: ShowExtraViewPortWindow(this->ni->data_b); rubidium@9236: } rubidium@8763: } else { rubidium@9236: if (!ScrollMainWindowToTile(this->ni->data_a) && this->ni->flags & NF_TILE2) { rubidium@9236: ScrollMainWindowToTile(this->ni->data_b); peter1138@9093: } peter1138@9093: } peter1138@9093: } rubidium@9236: break; peter1138@9093: } truelight@0: } rubidium@9236: rubidium@9285: virtual EventState OnKeyPress(uint16 key, uint16 keycode) rubidium@9236: { rubidium@9236: if (keycode == WKC_SPACE) { rubidium@9236: /* Don't continue. */ rubidium@9236: delete this; rubidium@9285: return ES_HANDLED; rubidium@9236: } rubidium@9285: return ES_NOT_HANDLED; rubidium@9236: } rubidium@9236: rubidium@9236: virtual void OnInvalidateData(int data) rubidium@9236: { rubidium@9236: /* The chatbar has notified us that is was either created or closed */ rubidium@9236: this->chat_height = data; rubidium@9236: } rubidium@9236: rubidium@9236: virtual void OnTick() rubidium@9236: { rubidium@9236: /* Scroll up newsmessages from the bottom in steps of 4 pixels */ rubidium@9236: int y = max(this->top - 4, _screen.height - this->height - 12 - this->chat_height); rubidium@9236: if (y == this->top) return; rubidium@9236: rubidium@9236: if (this->viewport != NULL) this->viewport->top += y - this->top; rubidium@9236: rubidium@9236: int diff = Delta(this->top, y); rubidium@9236: this->top = y; rubidium@9236: rubidium@9236: SetDirtyBlocks(this->left, this->top - diff, this->left + this->width, this->top + this->height); rubidium@9236: } rubidium@9236: }; truelight@0: rubidium@9430: /* static */ uint NewsWindow::duration; ///< Remaining time for showing current news message rubidium@9430: truelight@0: truelight@0: static const Widget _news_type13_widgets[] = { belugas@9761: { WWT_PANEL, RESIZE_NONE, COLOUR_WHITE, 0, 429, 0, 169, 0x0, STR_NULL}, belugas@9761: { WWT_PANEL, RESIZE_NONE, COLOUR_WHITE, 0, 10, 0, 11, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static WindowDesc _news_type13_desc = { rubidium@7341: WDP_CENTER, 476, 430, 170, 430, 170, rubidium@5893: 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[] = { belugas@9761: { WWT_PANEL, RESIZE_NONE, COLOUR_WHITE, 0, 429, 0, 129, 0x0, STR_NULL}, belugas@9761: { WWT_PANEL, RESIZE_NONE, COLOUR_WHITE, 0, 10, 0, 11, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static WindowDesc _news_type2_desc = { rubidium@7341: WDP_CENTER, 476, 430, 130, 430, 130, rubidium@5893: 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[] = { belugas@9761: { WWT_PANEL, RESIZE_NONE, COLOUR_LIGHT_BLUE, 0, 279, 14, 86, 0x0, STR_NULL}, belugas@9761: { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_LIGHT_BLUE, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, belugas@9761: { WWT_CAPTION, RESIZE_NONE, COLOUR_LIGHT_BLUE, 11, 279, 0, 13, STR_012C_MESSAGE, STR_NULL}, belugas@9761: { WWT_INSET, RESIZE_NONE, COLOUR_LIGHT_BLUE, 2, 277, 16, 64, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static WindowDesc _news_type0_desc = { rubidium@7341: WDP_CENTER, 476, 280, 87, 280, 87, rubidium@5893: WC_NEWS_WINDOW, WC_NONE, truelight@0: WDF_DEF_WIDGET, truelight@0: _news_type0_widgets, truelight@0: }; truelight@0: truelight@0: rubidium@7598: /** Open up an own newspaper window for the news item */ dominik@79: static void ShowNewspaper(NewsItem *ni) truelight@0: { rubidium@9234: SoundFx sound = _news_type_data[_news_subtype_data[ni->subtype].type].sound; tron@2639: if (sound != 0) SndPlayFx(sound); dominik@79: rubidium@8776: int top = _screen.height; rubidium@8776: Window *w; rubidium@9234: switch (_news_subtype_data[ni->subtype].display_mode) { tron@427: case NM_NORMAL: tron@427: _news_type13_desc.top = top; rubidium@9236: w = new NewsWindow(&_news_type13_desc, ni); rubidium@8776: if (ni->flags & NF_VIEWPORT) { rubidium@8994: InitializeWindowViewport(w, 2, 58, 426, 110, truelight@6624: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); rubidium@8776: } tron@427: break; tron@427: rubidium@8776: case NM_THIN: tron@427: _news_type2_desc.top = top; rubidium@9236: w = new NewsWindow(&_news_type2_desc, ni); rubidium@8776: if (ni->flags & NF_VIEWPORT) { rubidium@8994: InitializeWindowViewport(w, 2, 58, 426, 70, truelight@6624: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); rubidium@8776: } tron@427: break; tron@427: rubidium@8776: default: tron@427: _news_type0_desc.top = top; rubidium@9236: w = new NewsWindow(&_news_type0_desc, ni); rubidium@8776: if (ni->flags & NF_VIEWPORT) { rubidium@8994: InitializeWindowViewport(w, 3, 17, 274, 47, truelight@6624: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); rubidium@8776: } tron@427: break; truelight@0: } dominik@79: } truelight@0: rubidium@7598: /** 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@9248: InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_TICKER); dominik@79: } dominik@79: rubidium@9405: /** Initialize the news-items data structures */ rubidium@9405: void InitNewsItemStructs() rubidium@9405: { rubidium@9406: for (NewsItem *ni = _oldest_news; ni != NULL; ) { rubidium@9406: NewsItem *next = ni->next; rubidium@9406: delete ni; rubidium@9406: ni = next; rubidium@9405: } rubidium@9405: rubidium@9406: _total_news = 0; rubidium@9406: _oldest_news = NULL; rubidium@9406: _latest_news = NULL; rubidium@9406: _forced_news = NULL; rubidium@9406: _current_news = NULL; rubidium@9405: } dominik@79: rubidium@7598: /** rubidium@7598: * Are we ready to show another news item? rubidium@7598: * Only if nothing is in the newsticker and no newspaper is displayed rubidium@7598: */ rubidium@6247: static bool ReadyForNextItem() dominik@79: { rubidium@9406: NewsItem *ni = _forced_news == NULL ? _current_news : _forced_news; rubidium@9406: if (ni == NULL) return true; dominik@79: belugas@6348: /* Ticker message belugas@6348: * Check if the status bar message is still being displayed? */ rubidium@9248: if (IsNewsTickerShown()) return false; truelight@0: belugas@6348: /* Newspaper message, decrement duration counter */ rubidium@9430: if (NewsWindow::duration != 0) NewsWindow::duration--; truelight@0: belugas@6348: /* neither newsticker nor newspaper are running */ rubidium@9430: return (NewsWindow::duration == 0 || FindWindowById(WC_NEWS_WINDOW, 0) == NULL); dominik@79: } dominik@79: rubidium@7598: /** Move to the next news item */ rubidium@6247: static void MoveToNextItem() dominik@79: { dominik@79: DeleteWindowById(WC_NEWS_WINDOW, 0); rubidium@9406: _forced_news = NULL; dominik@79: belugas@6348: /* if we're not at the last item, then move on */ tron@427: if (_current_news != _latest_news) { rubidium@9406: _current_news = (_current_news == NULL) ? _oldest_news : _current_news->next; rubidium@9406: NewsItem *ni = _current_news; rubidium@9234: const NewsType type = _news_subtype_data[ni->subtype].type; dominik@79: belugas@6348: /* check the date, don't show too old items */ rubidium@9234: if (_date - _news_type_data[type].age > ni->date) return; dominik@79: rubidium@9234: switch (_news_type_data[type].display) { rubidium@7598: default: NOT_REACHED(); rubidium@9248: case ND_OFF: // Off - show nothing only a small reminder in the status bar rubidium@9248: InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_REMINDER); tron@2639: break; tron@2639: rubidium@9434: case ND_SUMMARY: // Summary - show ticker rubidium@9434: ShowTicker(ni); rubidium@9434: break; rubidium@7598: rubidium@9010: case ND_FULL: // Full - show newspaper rubidium@7598: ShowNewspaper(ni); rubidium@7598: break; tron@2639: } truelight@0: } dominik@79: } truelight@0: rubidium@9406: /** rubidium@9406: * Add a new newsitem to be shown. rubidium@9406: * @param string String to display rubidium@9406: * @param subtype news category, any of the NewsSubtype enums (NS_) rubidium@9406: * @param data_a news-specific value based on news type rubidium@9406: * @param data_b news-specific value based on news type rubidium@9406: * rubidium@9406: * @see NewsSubype rubidium@9406: */ rubidium@9658: void AddNewsItem(StringID string, NewsSubtype subtype, uint data_a, uint data_b, void *free_data) rubidium@9406: { rubidium@9406: if (_game_mode == GM_MENU) return; rubidium@9406: rubidium@9406: /* Create new news item node */ rubidium@9406: NewsItem *ni = new NewsItem; rubidium@9406: rubidium@9406: ni->string_id = string; rubidium@9406: ni->subtype = subtype; rubidium@9406: ni->flags = _news_subtype_data[subtype].flags; rubidium@9406: rubidium@9406: /* show this news message in color? */ rubidium@9413: if (_cur_year >= _settings_client.gui.colored_news_year) ni->flags |= NF_INCOLOR; rubidium@9406: rubidium@9406: ni->data_a = data_a; rubidium@9406: ni->data_b = data_b; rubidium@9658: ni->free_data = free_data; rubidium@9406: ni->date = _date; rubidium@9406: CopyOutDParam(ni->params, 0, lengthof(ni->params)); rubidium@9406: rubidium@9406: if (_total_news++ == 0) { rubidium@9406: assert(_oldest_news == NULL); rubidium@9406: _oldest_news = ni; rubidium@9406: ni->prev = NULL; rubidium@9406: } else { rubidium@9406: assert(_latest_news->next == NULL); rubidium@9406: _latest_news->next = ni; rubidium@9406: ni->prev = _latest_news; rubidium@9406: } rubidium@9406: rubidium@9406: ni->next = NULL; rubidium@9406: _latest_news = ni; rubidium@9406: rubidium@9406: InvalidateWindow(WC_MESSAGE_HISTORY, 0); rubidium@9406: } rubidium@9406: rubidium@9406: /** Delete a news item from the queue */ rubidium@9406: static void DeleteNewsItem(NewsItem *ni) rubidium@9406: { rubidium@9406: if (_forced_news == ni) { rubidium@9406: /* about to remove the currently forced item; skip to next */ rubidium@9406: MoveToNextItem(); rubidium@9406: } rubidium@9406: smatz@9482: if ((_current_news == ni) && (FindWindowById(WC_NEWS_WINDOW, 0) != NULL)) { rubidium@9406: /* about to remove the currently displayed item; also skip */ rubidium@9406: MoveToNextItem(); rubidium@9406: } rubidium@9406: rubidium@9406: /* delete item */ rubidium@9406: rubidium@9406: if (ni->prev != NULL) { rubidium@9406: ni->prev->next = ni->next; rubidium@9406: } else { rubidium@9406: assert(_oldest_news == ni); rubidium@9406: _oldest_news = ni->next; rubidium@9406: } rubidium@9406: rubidium@9406: if (ni->next != NULL) { rubidium@9406: ni->next->prev = ni->prev; rubidium@9406: } else { rubidium@9406: assert(_latest_news == ni); rubidium@9406: _latest_news = ni->prev; rubidium@9406: } rubidium@9406: rubidium@9658: free(ni->free_data); rubidium@9658: rubidium@9406: if (_current_news == ni) _current_news = ni->prev; rubidium@9406: _total_news--; rubidium@9406: delete ni; rubidium@9406: rubidium@9406: InvalidateWindow(WC_MESSAGE_HISTORY, 0); rubidium@9406: } rubidium@9406: rubidium@9406: void DeleteVehicleNews(VehicleID vid, StringID news) rubidium@9406: { rubidium@9406: NewsItem *ni = _oldest_news; rubidium@9406: rubidium@9406: while (ni != NULL) { rubidium@9406: if (ni->flags & NF_VEHICLE && rubidium@9406: ni->data_a == vid && rubidium@9406: (news == INVALID_STRING_ID || ni->string_id == news)) { rubidium@9406: /* grab a pointer to the next item before ni is freed */ rubidium@9406: NewsItem *p = ni->next; rubidium@9406: DeleteNewsItem(ni); rubidium@9406: ni = p; rubidium@9406: } else { rubidium@9406: ni = ni->next; rubidium@9406: } rubidium@9406: } rubidium@9406: } rubidium@9406: rubidium@9406: void RemoveOldNewsItems() rubidium@9406: { rubidium@9406: NewsItem *next; rubidium@9406: for (NewsItem *cur = _oldest_news; _total_news > MIN_NEWS_AMOUNT && cur != NULL; cur = next) { rubidium@9406: next = cur->next; rubidium@9413: if (_date - _news_type_data[_news_subtype_data[cur->subtype].type].age * _settings_client.gui.news_message_timeout > cur->date) DeleteNewsItem(cur); rubidium@9406: } rubidium@9406: } rubidium@9406: rubidium@6247: void NewsLoop() truelight@0: { belugas@6348: /* no news item yet */ tron@427: if (_total_news == 0) return; dominik@79: rubidium@9406: static byte _last_clean_month = 0; rubidium@9406: rubidium@9406: if (_last_clean_month != _cur_month) { rubidium@9406: RemoveOldNewsItems(); rubidium@9406: _last_clean_month = _cur_month; rubidium@9406: } rubidium@9406: Darkvater@5253: if (ReadyForNextItem()) MoveToNextItem(); dominik@79: } dominik@79: rubidium@7598: /** Do a forced show of a specific message */ rubidium@9406: static void ShowNewsMessage(NewsItem *ni) dominik@79: { rubidium@9406: assert(_total_news != 0); dominik@89: belugas@6348: /* Delete the news window */ dominik@79: DeleteWindowById(WC_NEWS_WINDOW, 0); dominik@79: belugas@6348: /* setup forced news item */ rubidium@9406: _forced_news = ni; dominik@79: rubidium@9406: if (_forced_news != NULL) { dominik@79: DeleteWindowById(WC_NEWS_WINDOW, 0); dominik@79: ShowNewspaper(ni); dominik@79: } truelight@0: } truelight@0: rubidium@7598: /** Show previous news item */ rubidium@6247: void ShowLastNewsMessage() truelight@0: { rubidium@9406: if (_total_news == 0) { rubidium@9406: return; rubidium@9406: } else if (_forced_news == NULL) { Darkvater@5595: /* Not forced any news yet, show the current one, unless a news window is Darkvater@5595: * open (which can only be the current one), then show the previous item */ Darkvater@5595: const Window *w = FindWindowById(WC_NEWS_WINDOW, 0); rubidium@9406: ShowNewsMessage((w == NULL || (_current_news == _oldest_news)) ? _current_news : _current_news->prev); Darkvater@5595: } else if (_forced_news == _oldest_news) { Darkvater@5595: /* We have reached the oldest news, start anew with the latest */ Darkvater@5595: ShowNewsMessage(_latest_news); Darkvater@5595: } else { Darkvater@5595: /* 'Scrolling' through news history show each one in turn */ rubidium@9406: ShowNewsMessage(_forced_news->prev); dominik@83: } truelight@0: } truelight@0: dominik@79: rubidium@7598: /** rubidium@7598: * 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@9199: CopyInDParam(0, ni->params, lengthof(ni->params)); rubidium@9199: 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@8776: const char *ptr = buffer; rubidium@8776: char *dest = buffer2; truelight@6542: WChar c_last = '\0'; peter1138@5108: for (;;) { peter1138@5108: WChar c = Utf8Consume(&ptr); peter1138@5108: if (c == 0) break; truelight@6542: /* Make a space from a newline, but ignore multiple newlines */ truelight@6542: if (c == '\n' && c_last != '\n') { truelight@6542: dest[0] = ' '; truelight@6542: dest++; truelight@6542: } 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@6542: 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@9288: struct MessageHistoryWindow : Window { rubidium@9288: MessageHistoryWindow(const WindowDesc *desc) : Window(desc) rubidium@9288: { rubidium@9288: this->vscroll.cap = 10; rubidium@9288: this->vscroll.count = _total_news; rubidium@9288: this->resize.step_height = 12; rubidium@9288: this->resize.height = this->height - 12 * 6; // minimum of 4 items in the list, each item 12 high rubidium@9288: this->resize.step_width = 1; rubidium@9288: this->resize.width = 200; // can't make window any smaller than 200 pixel rubidium@8776: rubidium@9288: this->FindWindowPlacementAndResize(desc); rubidium@9288: } dominik@1097: rubidium@9288: virtual void OnPaint() rubidium@9288: { rubidium@9288: int y = 19; rubidium@8776: rubidium@9288: SetVScrollCount(this, _total_news); rubidium@9288: this->DrawWidgets(); rubidium@8776: rubidium@9288: if (_total_news == 0) return; rubidium@9288: rubidium@9406: NewsItem *ni = _latest_news; rubidium@9406: for (int n = this->vscroll.pos; n > 0; n--) { rubidium@9406: ni = ni->prev; rubidium@9406: if (ni == NULL) return; rubidium@9406: } rubidium@9288: rubidium@9406: for (int n = this->vscroll.cap; n > 0; n--) { rubidium@9288: SetDParam(0, ni->date); rubidium@9288: DrawString(4, y, STR_SHORT_DATE, TC_WHITE); rubidium@9288: rubidium@9288: DrawNewsString(82, y, TC_WHITE, ni, this->width - 95); rubidium@9288: y += 12; rubidium@9406: rubidium@9406: ni = ni->prev; rubidium@9406: if (ni == NULL) return; rubidium@9288: } dominik@89: } rubidium@9288: rubidium@9288: virtual void OnClick(Point pt, int widget) rubidium@9288: { rubidium@9288: if (widget == 3) { rubidium@9406: NewsItem *ni = _latest_news; rubidium@9406: if (ni == NULL) return; rubidium@9288: rubidium@9406: for (int n = (pt.y - 19) / 12 + this->vscroll.pos; n > 0; n--) { rubidium@9406: ni = ni->prev; rubidium@9406: if (ni == NULL) return; rubidium@9406: } rubidium@9406: rubidium@9406: ShowNewsMessage(ni); rubidium@9288: } rubidium@9288: } rubidium@9288: rubidium@9288: virtual void OnResize(Point new_size, Point delta) rubidium@9288: { rubidium@9288: this->vscroll.cap += delta.y / 12; rubidium@9288: } rubidium@9288: }; dominik@89: dominik@89: static const Widget _message_history_widgets[] = { belugas@9761: { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_BROWN, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, belugas@9761: { WWT_CAPTION, RESIZE_RIGHT, COLOUR_BROWN, 11, 387, 0, 13, STR_MESSAGE_HISTORY, STR_018C_WINDOW_TITLE_DRAG_THIS}, belugas@9761: { WWT_STICKYBOX, RESIZE_LR, COLOUR_BROWN, 388, 399, 0, 13, 0x0, STR_STICKY_BUTTON}, belugas@9761: { WWT_PANEL, RESIZE_RB, COLOUR_BROWN, 0, 387, 14, 139, 0x0, STR_MESSAGE_HISTORY_TIP}, belugas@9761: { WWT_SCROLLBAR, RESIZE_LRB, COLOUR_BROWN, 388, 399, 14, 127, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, belugas@9761: { WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_BROWN, 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@7341: 240, 22, 400, 140, 400, 140, rubidium@5893: 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@7598: /** Display window with news messages history */ rubidium@6247: void ShowMessageHistory() dominik@89: { dominik@89: DeleteWindowById(WC_MESSAGE_HISTORY, 0); rubidium@9288: new MessageHistoryWindow(&_message_history_desc); dominik@89: } dominik@89: belugas@6279: rubidium@7891: /** News settings window widget offset constants */ belugas@6279: enum { peter1138@8343: WIDGET_NEWSOPT_DROP_SUMMARY = 4, ///< Dropdown that adjusts at once the level for all settings peter1138@8343: WIDGET_NEWSOPT_SOUNDTICKER = 6, ///< Button activating sound on events peter1138@8343: WIDGET_NEWSOPT_START_OPTION = 8, ///< First widget that is part of a group [<] .. [.] belugas@6279: }; belugas@6279: rubidium@9249: static const StringID _message_opt[] = {STR_OFF, STR_SUMMARY, STR_FULL, INVALID_STRING_ID}; peter1138@8343: rubidium@9249: struct MessageOptionsWindow : Window { rubidium@9249: int state; belugas@6279: rubidium@9249: MessageOptionsWindow(const WindowDesc *desc) : Window(desc) rubidium@9249: { rubidium@9249: NewsDisplay all_val; belugas@6279: rubidium@9249: /* Set up the initial disabled buttons in the case of 'off' or 'full' */ rubidium@9249: all_val = _news_type_data[0].display; rubidium@9249: for (int i = 0; i < NT_END; i++) { rubidium@9249: this->SetMessageButtonStates(_news_type_data[i].display, i); rubidium@9249: /* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */ rubidium@9249: if (_news_type_data[i].display != all_val) all_val = ND_OFF; rubidium@9249: } rubidium@9249: /* If all values are the same value, the ALL-button will take over this value */ rubidium@9249: this->state = all_val; peter1138@9333: peter1138@9333: this->FindWindowPlacementAndResize(desc); rubidium@9249: } rubidium@9249: rubidium@9249: /** rubidium@9249: * Setup the disabled/enabled buttons in the message window rubidium@9249: * If the value is 'off' disable the [<] widget, and enable the [>] one rubidium@9249: * Same-wise for all the others. Starting value of 4 is the first widget rubidium@9249: * group. These are grouped as [<][>] .. [<][>], etc. rubidium@9249: * @param value to set in the widget rubidium@9249: * @param element index of the group of widget to set rubidium@9249: */ rubidium@9249: void SetMessageButtonStates(byte value, int element) rubidium@9249: { rubidium@9249: element *= NB_WIDG_PER_SETTING; rubidium@9249: rubidium@9249: this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION, value == 0); rubidium@9249: this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION + 2, value == 2); rubidium@9249: } rubidium@9249: rubidium@9249: virtual void OnPaint() rubidium@9249: { rubidium@9249: if (_news_ticker_sound) this->LowerWidget(WIDGET_NEWSOPT_SOUNDTICKER); rubidium@9249: rubidium@9249: this->widget[WIDGET_NEWSOPT_DROP_SUMMARY].data = _message_opt[this->state]; rubidium@9273: this->DrawWidgets(); rubidium@9249: rubidium@9249: /* Draw the string of each setting on each button. */ rubidium@9249: for (int i = 0, y = 26; i < NT_END; i++, y += 12) { rubidium@9249: /* 51 comes from 13 + 89 (left and right of the button)+1, shiefted by one as to get division, rubidium@9249: * which will give centered position */ rubidium@9249: DrawStringCentered(51, y + 1, _message_opt[_news_type_data[i].display], TC_BLACK); rubidium@9249: } rubidium@9249: } rubidium@9249: rubidium@9249: virtual void OnClick(Point pt, int widget) rubidium@9249: { rubidium@9249: switch (widget) { rubidium@9249: case WIDGET_NEWSOPT_DROP_SUMMARY: // Dropdown menu for all settings rubidium@9249: ShowDropDownMenu(this, _message_opt, this->state, WIDGET_NEWSOPT_DROP_SUMMARY, 0, 0); rubidium@9249: break; rubidium@9249: rubidium@9249: case WIDGET_NEWSOPT_SOUNDTICKER: // Change ticker sound on/off rubidium@9249: _news_ticker_sound ^= 1; rubidium@9249: this->ToggleWidgetLoweredState(widget); rubidium@9249: this->InvalidateWidget(widget); rubidium@9249: break; rubidium@9249: rubidium@9249: default: { // Clicked on the [<] .. [>] widgets rubidium@9249: int wid = widget - WIDGET_NEWSOPT_START_OPTION; rubidium@9249: if (wid >= 0 && wid < (NB_WIDG_PER_SETTING * NT_END)) { rubidium@9249: int element = wid / NB_WIDG_PER_SETTING; rubidium@9249: byte val = (_news_type_data[element].display + ((wid % NB_WIDG_PER_SETTING) ? 1 : -1)) % 3; rubidium@9249: rubidium@9249: this->SetMessageButtonStates(val, element); rubidium@9249: _news_type_data[element].display = (NewsDisplay)val; rubidium@9249: this->SetDirty(); peter1138@9093: } rubidium@9249: break; rubidium@8776: } rubidium@9249: } rubidium@9249: } belugas@6279: rubidium@9249: virtual void OnDropdownSelect(int widget, int index) rubidium@9249: { rubidium@9249: this->state = index; rubidium@9249: rubidium@9249: for (int i = 0; i < NT_END; i++) { rubidium@9249: this->SetMessageButtonStates(index, i); rubidium@9249: _news_type_data[i].display = (NewsDisplay)index; rubidium@9249: } rubidium@9249: this->SetDirty(); truelight@0: } rubidium@9249: }; truelight@0: rubidium@7891: rubidium@7891: /* rubidium@7891: * The news settings window widgets rubidium@7891: * rubidium@7891: * Main part of the window is a list of news-setting lines, one for each news category. rubidium@7891: * Each line is constructed by an expansion of the \c NEWS_SETTINGS_LINE macro rubidium@7891: */ rubidium@7891: rubidium@7891: /** rubidium@7891: * Macro to construct one news-setting line in the news-settings window. rubidium@7891: * One line consists of four widgets, namely rubidium@7891: * - A [<] button rubidium@7891: * - A [...] label rubidium@7891: * - A [>] button rubidium@7891: * - A text label describing the news category rubidium@7891: * Horizontal positions of the widgets are hard-coded, vertical start position is (\a basey + \a linenum * \c NEWS_SETTING_BASELINE_SKIP). rubidium@7891: * Height of one line is 12, with the text label shifted 1 pixel down. rubidium@7891: * rubidium@7891: * First line should be widget number WIDGET_NEWSOPT_START_OPTION rubidium@7891: * rubidium@7891: * @param basey: Base Y coordinate rubidium@7891: * @param linenum: Count, news-setting is the \a linenum-th line rubidium@7891: * @param text: StringID for the text label to display rubidium@7891: */ rubidium@7891: #define NEWS_SETTINGS_LINE(basey, linenum, text) \ rubidium@7891: { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@7891: 4, 12, basey + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@7891: SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, \ rubidium@7891: { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@7891: 13, 89, basey + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@7891: STR_EMPTY, STR_NULL}, \ rubidium@7891: { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@7891: 90, 98, basey + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@7891: SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, \ rubidium@7891: { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, \ rubidium@7891: 103, 409, basey + 1 + linenum * NEWS_SETTING_BASELINE_SKIP, basey + 13 + linenum * NEWS_SETTING_BASELINE_SKIP, \ rubidium@7891: text, STR_NULL} rubidium@7891: rubidium@7891: static const int NEWS_SETTING_BASELINE_SKIP = 12; ///< Distance between two news-setting lines, should be at least 12 rubidium@7891: rubidium@7891: truelight@0: static const Widget _message_options_widgets[] = { rubidium@7891: { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_BROWN, 0, 10, 0, 13, rubidium@7891: STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@7891: { WWT_CAPTION, RESIZE_NONE, COLOUR_BROWN, 11, 409, 0, 13, rubidium@7891: STR_0204_MESSAGE_OPTIONS, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@7891: { WWT_PANEL, RESIZE_NONE, COLOUR_BROWN, 0, 409, 14, 64 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@7891: 0x0, STR_NULL}, rubidium@4344: belugas@6279: /* Text at the top of the main panel, in black */ rubidium@7891: { WWT_LABEL, RESIZE_NONE, COLOUR_BROWN, rubidium@7891: 0, 409, 13, 26, rubidium@7891: STR_0205_MESSAGE_TYPES, STR_NULL}, belugas@6279: rubidium@7891: /* General drop down and sound button, widgets WIDGET_NEWSOPT_BTN_SUMMARY and WIDGET_NEWSOPT_DROP_SUMMARY */ peter1138@8343: { WWT_DROPDOWN, RESIZE_NONE, COLOUR_YELLOW, peter1138@8343: 4, 98, 34 + NT_END * NEWS_SETTING_BASELINE_SKIP, 45 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@7891: 0x0, STR_NULL}, belugas@6279: rubidium@7891: { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, rubidium@7891: 103, 409, 35 + NT_END * NEWS_SETTING_BASELINE_SKIP, 47 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@7891: STR_MESSAGES_ALL, STR_NULL}, belugas@6279: rubidium@7891: /* Below is widget WIDGET_NEWSOPT_SOUNDTICKER */ rubidium@7891: { WWT_TEXTBTN_2, RESIZE_NONE, COLOUR_YELLOW, rubidium@7891: 4, 98, 46 + NT_END * NEWS_SETTING_BASELINE_SKIP, 57 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@7891: STR_02DB_OFF, STR_NULL}, truelight@6283: rubidium@7891: { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, rubidium@7891: 103, 409, 47 + NT_END * NEWS_SETTING_BASELINE_SKIP, 59 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@7891: STR_MESSAGE_SOUND, STR_NULL}, rubidium@7891: rubidium@7891: /* List of news-setting lines (4 widgets for each line). rubidium@7891: * First widget must be number WIDGET_NEWSOPT_START_OPTION rubidium@7891: */ belugas@9735: NEWS_SETTINGS_LINE(26, NT_ARRIVAL_PLAYER, STR_0206_ARRIVAL_OF_FIRST_VEHICLE), belugas@9735: NEWS_SETTINGS_LINE(26, NT_ARRIVAL_OTHER, STR_0207_ARRIVAL_OF_FIRST_VEHICLE), belugas@9735: NEWS_SETTINGS_LINE(26, NT_ACCIDENT, STR_0208_ACCIDENTS_DISASTERS), belugas@9735: NEWS_SETTINGS_LINE(26, NT_COMPANY_INFO, STR_0209_COMPANY_INFORMATION), belugas@9739: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_OPEN, STR_NEWS_INDUSTRY_OPEN), belugas@9739: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_CLOSE, STR_NEWS_INDUSTRY_CLOSE), belugas@9735: NEWS_SETTINGS_LINE(26, NT_ECONOMY, STR_020A_ECONOMY_CHANGES), rubidium@7891: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_PLAYER, STR_INDUSTRY_CHANGES_SERVED_BY_PLAYER), belugas@9735: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_OTHER, STR_INDUSTRY_CHANGES_SERVED_BY_OTHER), rubidium@7891: NEWS_SETTINGS_LINE(26, NT_INDUSTRY_NOBODY, STR_OTHER_INDUSTRY_PRODUCTION_CHANGES), belugas@9735: NEWS_SETTINGS_LINE(26, NT_ADVICE, STR_020B_ADVICE_INFORMATION_ON_PLAYER), belugas@9735: NEWS_SETTINGS_LINE(26, NT_NEW_VEHICLES, STR_020C_NEW_VEHICLES), belugas@9735: NEWS_SETTINGS_LINE(26, NT_ACCEPTANCE, STR_020D_CHANGES_OF_CARGO_ACCEPTANCE), belugas@9735: NEWS_SETTINGS_LINE(26, NT_SUBSIDIES, STR_020E_SUBSIDIES), belugas@9735: NEWS_SETTINGS_LINE(26, NT_GENERAL, STR_020F_GENERAL_INFORMATION), belugas@6279: darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _message_options_desc = { rubidium@7891: 270, 22, 410, 65 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@7891: 410, 65 + NT_END * NEWS_SETTING_BASELINE_SKIP, rubidium@5893: 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@6247: void ShowMessageOptions() truelight@0: { truelight@0: DeleteWindowById(WC_GAME_OPTIONS, 0); rubidium@9249: new MessageOptionsWindow(&_message_options_desc); truelight@0: }