tron@2186: /* $Id$ */ tron@2186: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@2163: #include "functions.h" tron@1309: #include "strings.h" Darkvater@1688: #include "table/sprites.h" tron@507: #include "table/strings.h" truelight@0: #include "window.h" peter1138@3155: #include "gui.h" truelight@0: #include "viewport.h" truelight@0: #include "gfx.h" truelight@0: #include "news.h" truelight@0: #include "vehicle.h" tron@337: #include "sound.h" tron@2159: #include "variables.h" rubidium@4261: #include "date.h" peter1138@5108: #include "string.h" truelight@0: dominik@79: /* News system 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@4549: * array is full, the oldest entry (_oldest_news) is being overwritten rubidium@4549: * by the newest (_latest news). rubidium@4549: * rubidium@4549: * oldest current lastest rubidium@4549: * | | | rubidium@4549: * [O------------F-------------C---------L ] rubidium@4549: * | rubidium@4549: * forced Darkvater@5254: * Darkvater@5254: * Of course by using an array we can have situations like Darkvater@5254: * 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@4549: */ dominik@79: tron@427: #define MAX_NEWS 30 tron@427: Darkvater@5246: typedef byte NewsID; tron@427: #define INVALID_NEWS 255 dominik@79: dominik@79: static NewsItem _news_items[MAX_NEWS]; Darkvater@5246: static NewsID _current_news = INVALID_NEWS; // points to news item that should be shown next Darkvater@5246: static NewsID _oldest_news = 0; // points to first item in fifo queue Darkvater@5246: static NewsID _latest_news = INVALID_NEWS; // points to last item in fifo queue tron@427: /* if the message being shown was forced by the user, its index is stored in tron@427: * _forced_news. forced_news is INVALID_NEWS otherwise. tron@427: * (Users can force messages through history or "last message") */ Darkvater@5246: static NewsID _forced_news = INVALID_NEWS; dominik@79: dominik@79: static byte _total_news = 0; // total news count truelight@0: rubidium@5461: void DrawNewsNewVehicleAvail(Window *w); truelight@0: void DrawNewsBankrupcy(Window *w); Darkvater@5253: static void MoveToNextItem(void); truelight@0: rubidium@5461: StringID GetNewsStringNewVehicleAvail(const NewsItem *ni); Darkvater@2101: StringID GetNewsStringBankrupcy(const NewsItem *ni); truelight@0: truelight@0: static DrawNewsCallbackProc * const _draw_news_callback[] = { rubidium@5461: DrawNewsNewVehicleAvail, /* DNC_VEHICLEAVAIL */ tron@427: DrawNewsBankrupcy, /* DNC_BANKRUPCY */ truelight@0: }; truelight@0: rubidium@5461: extern GetNewsStringCallbackProc * const _get_news_string_callback[]; truelight@0: GetNewsStringCallbackProc * const _get_news_string_callback[] = { rubidium@5461: GetNewsStringNewVehicleAvail, /* DNC_VEHICLEAVAIL */ tron@427: GetNewsStringBankrupcy, /* DNC_BANKRUPCY */ truelight@0: }; truelight@0: tron@427: void InitNewsItemStructs(void) dominik@79: { tron@427: memset(_news_items, 0, sizeof(_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: tron@427: void DrawNewsBorder(const Window *w) truelight@0: { truelight@0: int left = 0; truelight@0: int right = w->width - 1; truelight@0: int top = 0; truelight@0: int bottom = w->height - 1; truelight@0: truelight@0: GfxFillRect(left, top, right, bottom, 0xF); truelight@193: truelight@0: GfxFillRect(left, top, left, bottom, 0xD7); truelight@0: GfxFillRect(right, top, right, bottom, 0xD7); truelight@0: GfxFillRect(left, top, right, top, 0xD7); truelight@0: GfxFillRect(left, bottom, right, bottom, 0xD7); truelight@193: truelight@193: DrawString(left + 2, top + 1, STR_00C6, 0); truelight@0: } truelight@0: truelight@0: static void NewsWindowProc(Window *w, WindowEvent *e) truelight@0: { tron@427: switch (e->event) { darkvater@1648: case WE_CREATE: { /* If chatbar is open at creation time, we need to go above it */ darkvater@1648: const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0); darkvater@1648: w->message.msg = (w1 != NULL) ? w1->height : 0; darkvater@1648: } break; darkvater@1648: truelight@0: case WE_PAINT: { tron@427: const NewsItem *ni = WP(w, news_d).ni; truelight@0: ViewPort *vp; truelight@0: tron@427: switch (ni->display_mode) { tron@427: case NM_NORMAL: tron@427: case NM_THIN: { tron@427: DrawNewsBorder(w); truelight@0: tron@427: DrawString(2, 1, STR_00C6, 0); dominik@79: tron@534: SetDParam(0, ni->date); tron@427: DrawStringRightAligned(428, 1, STR_01FF, 0); tron@427: tron@427: if (!(ni->flags & NF_VIEWPORT)) { tron@427: COPY_IN_DPARAM(0, ni->params, lengthof(ni->params)); tron@427: DrawStringMultiCenter(215, ni->display_mode == NM_NORMAL ? 76 : 56, tron@427: ni->string_id, 426); tron@427: } else { tron@427: byte bk = _display_opt; tron@497: _display_opt &= ~DO_TRANS_BUILDINGS; tron@427: DrawWindowViewport(w); tron@427: _display_opt = bk; tron@427: tron@427: /* Shade the viewport into gray, or color*/ tron@427: vp = w->viewport; tron@427: GfxFillRect(vp->left - w->left, vp->top - w->top, tron@427: vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1, celestar@2227: (ni->flags & NF_INCOLOR ? 0x322 : 0x323) | USE_COLORTABLE tron@427: ); tron@427: tron@427: COPY_IN_DPARAM(0, ni->params, lengthof(ni->params)); tron@427: DrawStringMultiCenter(w->width / 2, 20, ni->string_id, 428); tron@427: } tron@427: break; truelight@0: } tron@427: tron@427: case NM_CALLBACK: { tron@427: _draw_news_callback[ni->callback](w); tron@427: break; tron@427: } tron@427: tron@427: default: { tron@427: DrawWindowWidgets(w); tron@427: if (!(ni->flags & NF_VIEWPORT)) { tron@427: COPY_IN_DPARAM(0, ni->params, lengthof(ni->params)); tron@427: DrawStringMultiCenter(140, 38, ni->string_id, 276); tron@427: } else { tron@427: DrawWindowViewport(w); tron@427: COPY_IN_DPARAM(0, ni->params, lengthof(ni->params)); tron@427: DrawStringMultiCenter(w->width / 2, w->height - 16, ni->string_id, 276); tron@427: } tron@427: break; truelight@0: } truelight@0: } truelight@0: } break; truelight@0: truelight@0: case WE_CLICK: { belugas@4634: switch (e->we.click.widget) { dominik@79: case 1: { tron@427: NewsItem *ni = WP(w, news_d).ni; truelight@80: DeleteWindow(w); dominik@79: ni->duration = 0; tron@427: _forced_news = INVALID_NEWS; dominik@79: } break; truelight@0: case 0: { tron@427: NewsItem *ni = WP(w, news_d).ni; truelight@0: if (ni->flags & NF_VEHICLE) { truelight@919: Vehicle *v = GetVehicle(ni->data_a); truelight@0: ScrollMainWindowTo(v->x_pos, v->y_pos); truelight@0: } else if (ni->flags & NF_TILE) { truelight@0: if (!ScrollMainWindowToTile(ni->data_a) && ni->data_b != 0) truelight@0: ScrollMainWindowToTile(ni->data_b); truelight@0: } truelight@0: } break; truelight@0: } truelight@0: } break; truelight@0: truelight@0: case WE_KEYPRESS: belugas@4634: if (e->we.keypress.keycode == WKC_SPACE) { truelight@0: // Don't continue. belugas@4634: e->we.keypress.cont = false; truelight@0: DeleteWindow(w); truelight@0: } truelight@0: break; truelight@0: darkvater@1648: case WE_MESSAGE: /* The chatbar has notified us that is was either created or closed */ belugas@4634: switch (e->we.message.msg) { belugas@4634: case WE_CREATE: w->message.msg = e->we.message.wparam; break; darkvater@1648: case WE_DESTROY: w->message.msg = 0; break; darkvater@1648: } darkvater@1648: break; darkvater@1648: darkvater@1648: case WE_TICK: { /* Scroll up newsmessages from the bottom in steps of 4 pixels */ darkvater@1648: int diff; darkvater@1648: int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg); darkvater@1648: if (y == w->top) return; truelight@0: truelight@0: if (w->viewport != NULL) truelight@0: w->viewport->top += y - w->top; truelight@0: darkvater@1648: diff = abs(w->top - y); truelight@0: w->top = y; truelight@0: darkvater@1648: SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height); truelight@0: } break; truelight@0: } truelight@0: } truelight@0: Darkvater@5245: /** Return the correct index in the pseudo-fifo Darkvater@5245: * queue and deals with overflows when increasing the index */ Darkvater@5246: static inline NewsID increaseIndex(NewsID i) dominik@79: { Darkvater@5258: assert(i != INVALID_NEWS); Darkvater@5245: return (i + 1) % MAX_NEWS; Darkvater@5245: } Darkvater@5245: Darkvater@5245: /** Return the correct index in the pseudo-fifo Darkvater@5245: * queue and deals with overflows when decreasing the index */ Darkvater@5246: static inline NewsID decreaseIndex(NewsID i) Darkvater@5245: { Darkvater@5245: assert(i != INVALID_NEWS); Darkvater@5245: return (i + MAX_NEWS - 1) % MAX_NEWS; dominik@79: } dominik@79: Darkvater@4873: /** Add a new newsitem to be shown. Darkvater@4873: * @param string String to display, can have special values based on parameter 'flags' Darkvater@4873: * @param flags various control bits that will show various news-types. See macro NEWS_FLAGS() Darkvater@4873: * @param data_a news-specific value based on news type Darkvater@4873: * @param data_b news-specific value based on news type Darkvater@4873: * @note flags exists of 4 byte-sized extra parameters.
Darkvater@4873: * 1. 0 - 7 display_mode, any of the NewsMode enums (NM_)
Darkvater@4874: * 2. 8 - 15 news flags, any of the NewsFlags enums (NF_) NF_INCOLOR are set automatically if needed
Darkvater@4873: * 3. 16 - 23 news category, any of the NewsType enums (NT_)
Darkvater@4873: * 4. 24 - 31 news callback function, any of the NewsCallback enums (DNC_)
Darkvater@4873: * If the display mode is NM_CALLBACK special news is shown and parameter Darkvater@4873: * stringid has a special meaning.
Darkvater@4873: * DNC_TRAINAVAIL, DNC_ROADAVAIL, DNC_SHIPAVAIL, DNC_AIRCRAFTAVAIL: StringID is Darkvater@4873: * the index of the engine that is shown
Darkvater@4873: * DNC_BANKRUPCY: bytes 0-3 of StringID contains the player that is in trouble, Darkvater@4873: * and 4-7 contains what kind of bankrupcy message is shown, NewsBankrupcy enum (NB_)
Darkvater@4873: * @see NewsMode Darkvater@4873: * @see NewsFlags Darkvater@4873: * @see NewsType Darkvater@4873: * @see NewsCallback */ truelight@0: void AddNewsItem(StringID string, uint32 flags, uint data_a, uint data_b) truelight@0: { Darkvater@5246: NewsID l_news; truelight@0: tron@4000: if (_game_mode == GM_MENU) return; truelight@0: dominik@102: // check the rare case that the oldest (to be overwritten) news item is open Darkvater@5245: if (_total_news == MAX_NEWS && (_oldest_news == _current_news || _oldest_news == _forced_news)) Darkvater@5253: MoveToNextItem(); dominik@102: tron@427: if (_total_news < MAX_NEWS) _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 */ Darkvater@5244: l_news = _latest_news; Darkvater@5258: _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) { Darkvater@5245: assert(_total_news == MAX_NEWS); Darkvater@5245: _oldest_news = increaseIndex(_oldest_news); Darkvater@5245: } dominik@79: Darkvater@5395: /*DEBUG(misc, 0) ("+cur %3d, old %2d, lat %3d, for %3d, tot %2d", Darkvater@5395: _current_news, _oldest_news, _latest_news, _forced_news, _total_news); */ Darkvater@5395: Darkvater@5245: { /* Add news to _latest_news */ Darkvater@5245: Window *w; Darkvater@5245: NewsItem *ni = &_news_items[_latest_news]; Darkvater@5245: memset(ni, 0, sizeof(*ni)); dominik@79: Darkvater@5245: ni->string_id = string; Darkvater@5245: ni->display_mode = (byte)flags; Darkvater@5245: ni->flags = (byte)(flags >> 8); dominik@79: Darkvater@5245: // show this news message in color? Darkvater@5245: if (_cur_year >= _patches.colored_news_year) ni->flags |= NF_INCOLOR; Darkvater@5245: Darkvater@5245: ni->type = (byte)(flags >> 16); Darkvater@5245: ni->callback = (byte)(flags >> 24); Darkvater@5245: ni->data_a = data_a; Darkvater@5245: ni->data_b = data_b; Darkvater@5245: ni->date = _date; Darkvater@5245: COPY_OUT_DPARAM(ni->params, 0, lengthof(ni->params)); Darkvater@5245: Darkvater@5245: w = FindWindowById(WC_MESSAGE_HISTORY, 0); Darkvater@5245: if (w == NULL) return; Darkvater@5245: SetWindowDirty(w); Darkvater@5245: w->vscroll.count = _total_news; Darkvater@5245: } truelight@0: } truelight@0: dominik@715: Darkvater@5395: /* Don't show item if it's older than x days, corresponds with NewsType in news.h */ truelight@0: static const byte _news_items_age[] = {60, 60, 90, 60, 90, 30, 150, 30, 90, 180}; 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 = { truelight@0: WDP_CENTER, 476, 430, 170, tron@427: WC_NEWS_WINDOW, 0, truelight@0: WDF_DEF_WIDGET, truelight@0: _news_type13_widgets, truelight@0: NewsWindowProc 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 = { truelight@0: WDP_CENTER, 476, 430, 130, tron@427: WC_NEWS_WINDOW, 0, truelight@0: WDF_DEF_WIDGET, truelight@0: _news_type2_widgets, truelight@0: NewsWindowProc 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 = { truelight@0: WDP_CENTER, 476, 280, 87, tron@427: WC_NEWS_WINDOW, 0, truelight@0: WDF_DEF_WIDGET, truelight@0: _news_type0_widgets, truelight@0: NewsWindowProc truelight@0: }; truelight@0: rubidium@5461: static const SoundFx _news_sounds[NT_END] = { tron@427: SND_1D_APPLAUSE, tron@427: SND_1D_APPLAUSE, tron@427: 0, tron@427: 0, tron@427: 0, tron@427: 0, rubidium@5461: 0, tron@427: SND_1E_OOOOH, tron@427: 0, tron@427: 0, tron@427: 0 tron@427: }; truelight@0: rubidium@5461: const char *_news_display_name[NT_END] = { rubidium@5461: "arrival_player", rubidium@5461: "arrival_other", rubidium@5461: "accident", rubidium@5461: "company_info", rubidium@5461: "openclose", rubidium@5461: "economy", rubidium@5461: "advice", rubidium@5461: "new_vehicles", rubidium@5461: "acceptance", rubidium@5461: "subsidies", rubidium@5461: "general", rubidium@5461: }; rubidium@5461: Darkvater@1688: /** Get the value of an item of the news-display settings. This is Darkvater@1688: * a little tricky since on/off/summary must use 2 bits to store the value Darkvater@1688: * @param item the item whose value is requested Darkvater@1688: * @return return the found value which is between 0-2 Darkvater@1688: */ Darkvater@1688: static inline byte GetNewsDisplayValue(byte item) Darkvater@1688: { rubidium@5461: assert(item < NT_END && GB(_news_display_opt, item * 2, 2) <= 2); tron@2549: return GB(_news_display_opt, item * 2, 2); Darkvater@1688: } Darkvater@1688: Darkvater@1688: /** Set the value of an item in the news-display settings. This is Darkvater@1688: * a little tricky since on/off/summary must use 2 bits to store the value Darkvater@1688: * @param item the item whose value is being set Darkvater@1688: * @param val new value Darkvater@1688: */ Darkvater@1688: static inline void SetNewsDisplayValue(byte item, byte val) Darkvater@1688: { rubidium@5461: assert(item < NT_END && val <= 2); tron@2549: SB(_news_display_opt, item * 2, 2, val); Darkvater@1688: } Darkvater@1688: dominik@79: // open up an own newspaper window for the news item dominik@79: static void ShowNewspaper(NewsItem *ni) truelight@0: { truelight@0: Window *w; tron@2498: SoundFx sound; dominik@79: int top; Darkvater@4874: ni->flags &= ~NF_FORCE_BIG; dominik@79: ni->duration = 555; truelight@0: dominik@79: sound = _news_sounds[ni->type]; tron@2639: if (sound != 0) SndPlayFx(sound); dominik@79: darkvater@1648: top = _screen.height; tron@427: switch (ni->display_mode) { tron@427: case NM_NORMAL: tron@427: case NM_CALLBACK: { tron@427: _news_type13_desc.top = top; tron@427: w = AllocateWindowDesc(&_news_type13_desc); tron@427: if (ni->flags & NF_VIEWPORT) tron@427: AssignWindowViewport(w, 2, 58, 0x1AA, 0x6E, tron@427: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), 0); tron@427: break; dominik@79: } tron@427: tron@427: case NM_THIN: { tron@427: _news_type2_desc.top = top; tron@427: w = AllocateWindowDesc(&_news_type2_desc); tron@427: if (ni->flags & NF_VIEWPORT) tron@427: AssignWindowViewport(w, 2, 58, 0x1AA, 0x46, tron@427: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), 0); tron@427: break; dominik@79: } tron@427: tron@427: default: { tron@427: _news_type0_desc.top = top; tron@427: w = AllocateWindowDesc(&_news_type0_desc); tron@427: if (ni->flags & NF_VIEWPORT) tron@427: AssignWindowViewport(w, 3, 17, 0x112, 0x2F, tron@427: ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), 0); tron@427: break; dominik@79: } truelight@0: } Darkvater@5395: Darkvater@5395: /*DEBUG(misc, 0) (" cur %3d, old %2d, lat %3d, for %3d, tot %2d", Darkvater@5395: _current_news, _oldest_news, _latest_news, _forced_news, _total_news); */ Darkvater@5395: tron@427: WP(w, news_d).ni = &_news_items[_forced_news == INVALID_NEWS ? _current_news : _forced_news]; dominik@79: w->flags4 |= WF_DISABLE_VP_SCROLL; dominik@79: } truelight@0: dominik@79: // show news item in the ticker tron@427: static void ShowTicker(const NewsItem *ni) dominik@79: { dominik@79: Window *w; dominik@79: Darkvater@1688: if (_news_ticker_sound) SndPlayFx(SND_16_MORSE); Darkvater@1688: dominik@79: _statusbar_news_item = *ni; dominik@79: w = FindWindowById(WC_STATUS_BAR, 0); tron@2639: if (w != NULL) WP(w, def_d).data_1 = 360; dominik@79: } dominik@79: dominik@79: dominik@79: // Are we ready to show another news item? dominik@79: // Only if nothing is in the newsticker and no newspaper is displayed tron@427: static bool ReadyForNextItem(void) dominik@79: { tron@427: const Window *w; Darkvater@5246: NewsID item = (_forced_news == INVALID_NEWS) ? _current_news : _forced_news; dominik@92: NewsItem *ni; dominik@92: tron@427: if (item >= MAX_NEWS) return true; dominik@92: ni = &_news_items[item]; dominik@79: dominik@79: // Ticker message dominik@79: // Check if the status bar message is still being displayed? dominik@79: w = FindWindowById(WC_STATUS_BAR, 0); tron@2639: if (w != NULL && WP(w, const def_d).data_1 > -1280) return false; truelight@0: Darkvater@5264: // Newspaper message, decrement duration counter Darkvater@5264: if (ni->duration != 0) ni->duration--; truelight@0: dominik@79: // neither newsticker nor newspaper are running Darkvater@5264: return (ni->duration == 0 || FindWindowById(WC_NEWS_WINDOW, 0) == NULL); dominik@79: } dominik@79: Darkvater@5253: static void MoveToNextItem(void) dominik@79: { dominik@79: DeleteWindowById(WC_NEWS_WINDOW, 0); tron@427: _forced_news = INVALID_NEWS; dominik@79: Darkvater@5245: // if we're not at the last item, then move on tron@427: if (_current_news != _latest_news) { dominik@79: NewsItem *ni; dominik@79: Darkvater@5258: _current_news = (_current_news == INVALID_NEWS) ? _oldest_news : increaseIndex(_current_news); dominik@79: ni = &_news_items[_current_news]; dominik@79: dominik@79: // check the date, don't show too old items tron@2639: if (_date - _news_items_age[ni->type] > ni->date) return; dominik@79: tron@2639: switch (GetNewsDisplayValue(ni->type)) { tron@2639: case 0: { /* Off - show nothing only a small reminder in the status bar */ belugas@4171: Window *w = FindWindowById(WC_STATUS_BAR, 0); tron@2639: Darkvater@1688: if (w != NULL) { Darkvater@1688: WP(w, def_d).data_2 = 91; Darkvater@1688: SetWindowDirty(w); Darkvater@1688: } tron@2639: break; tron@2639: } tron@2639: tron@2639: case 1: /* Summary - show ticker, but if forced big, cascade to full */ tron@2639: if (!(ni->flags & NF_FORCE_BIG)) { tron@2639: ShowTicker(ni); tron@2639: break; tron@2639: } tron@2639: /* Fallthrough */ tron@2639: tron@2639: case 2: /* Full - show newspaper*/ tron@2639: ShowNewspaper(ni); tron@2639: break; tron@2639: } truelight@0: } dominik@79: } truelight@0: tron@427: void NewsLoop(void) truelight@0: { dominik@79: // no news item yet tron@427: if (_total_news == 0) return; dominik@79: Darkvater@5253: if (ReadyForNextItem()) MoveToNextItem(); dominik@79: } dominik@79: dominik@79: /* 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: dominik@79: // Delete the news window dominik@79: DeleteWindowById(WC_NEWS_WINDOW, 0); dominik@79: dominik@79: // 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: tron@427: void ShowLastNewsMessage(void) truelight@0: { Darkvater@5414: if (_forced_news == INVALID_NEWS) { Darkvater@5414: /* Not forced any news yet, show the current one, unless a news window is Darkvater@5414: * open (which can only be the current one), then show the previous item */ Darkvater@5414: const Window *w = FindWindowById(WC_NEWS_WINDOW, 0); rubidium@5431: ShowNewsMessage((w == NULL || (_current_news == _oldest_news)) ? _current_news : decreaseIndex(_current_news)); Darkvater@5414: } else if (_forced_news == _oldest_news) { Darkvater@5414: /* We have reached the oldest news, start anew with the latest */ Darkvater@5414: ShowNewsMessage(_latest_news); Darkvater@5414: } else { Darkvater@5414: /* 'Scrolling' through news history show each one in turn */ Darkvater@5414: 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) { dominik@89: i = _latest_news + MAX_NEWS - i; tron@2639: } else { dominik@89: i = _latest_news - i; tron@2639: } dominik@89: tron@427: i %= MAX_NEWS; dominik@89: return i; dominik@89: } dominik@89: Darkvater@2101: /** 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]; peter1138@5108: const char *ptr; peter1138@5108: char *dest; dominik@89: StringID str; rubidium@5495: WChar c_last; truelight@193: dominik@89: if (ni->display_mode == 3) { dominik@89: str = _get_news_string_callback[ni->callback](ni); dominik@89: } else { dominik@89: COPY_IN_DPARAM(0, ni->params, lengthof(ni->params)); truelight@193: str = ni->string_id; dominik@89: } 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. */ peter1138@5108: ptr = buffer; peter1138@5108: dest = buffer2; rubidium@5495: c_last = '\0'; peter1138@5108: for (;;) { peter1138@5108: WChar c = Utf8Consume(&ptr); peter1138@5108: if (c == 0) break; rubidium@5495: /* Make a space from a newline, but ignore multiple newlines */ rubidium@5495: if (c == '\n' && c_last != '\n') { rubidium@5495: dest[0] = ' '; rubidium@5495: dest++; rubidium@5495: } 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@5495: 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: dominik@89: static void MessageHistoryWndProc(Window *w, WindowEvent *e) dominik@89: { tron@427: switch (e->event) { dominik@89: case WE_PAINT: { tron@427: int y = 19; Darkvater@5246: NewsID p, show; dominik@89: peter1138@3155: SetVScrollCount(w, _total_news); dominik@89: DrawWindowWidgets(w); dominik@89: tron@427: if (_total_news == 0) break; dominik@1097: show = min(_total_news, w->vscroll.cap); dominik@89: tron@427: for (p = w->vscroll.pos; p < w->vscroll.pos + show; p++) { dominik@89: // get news in correct order Darkvater@2101: const NewsItem *ni = &_news_items[getNews(p)]; dominik@89: tron@534: SetDParam(0, ni->date); Darkvater@2101: DrawString(4, y, STR_SHORT_DATE, 12); dominik@89: Darkvater@2101: DrawNewsString(82, y, 12, ni, w->width - 95); dominik@89: y += 12; dominik@89: } dominik@89: break; dominik@89: } dominik@89: dominik@89: case WE_CLICK: belugas@4634: switch (e->we.click.widget) { dominik@1097: case 3: { belugas@4634: int y = (e->we.click.pt.y - 19) / 12; Darkvater@5246: NewsID p = getNews(y + w->vscroll.pos); dominik@89: Darkvater@5246: if (p == INVALID_NEWS) break; dominik@89: Darkvater@5246: ShowNewsMessage(p); dominik@89: break; dominik@89: } dominik@89: } dominik@89: break; dominik@1097: dominik@1097: case WE_RESIZE: belugas@4634: w->vscroll.cap += e->we.sizing.diff.y / 12; dominik@1097: break; dominik@89: } dominik@89: } 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 = { dominik@89: 240, 22, 400, 140, tron@427: WC_MESSAGE_HISTORY, 0, 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: MessageHistoryWndProc dominik@89: }; dominik@89: tron@427: void ShowMessageHistory(void) dominik@89: { dominik@89: Window *w; dominik@89: dominik@89: DeleteWindowById(WC_MESSAGE_HISTORY, 0); dominik@89: w = AllocateWindowDesc(&_message_history_desc); dominik@89: tron@427: if (w != NULL) { dominik@89: w->vscroll.cap = 10; dominik@89: w->vscroll.count = _total_news; dominik@1097: w->resize.step_height = 12; dominik@1097: w->resize.height = w->height - 12 * 6; // minimum of 4 items in the list, each item 12 high dominik@1097: w->resize.step_width = 1; dominik@1097: w->resize.width = 200; // can't make window any smaller than 200 pixel dominik@89: SetWindowDirty(w); dominik@89: } dominik@89: } dominik@89: Darkvater@1688: /** Setup the disabled/enabled buttons in the message window Darkvater@1688: * If the value is 'off' disable the [<] widget, and enable the [>] one Darkvater@1688: * Same-wise for all the others. Starting value of 3 is the first widget Darkvater@1688: * group. These are grouped as [<][>] .. [<][>], etc. Darkvater@1688: */ Darkvater@1688: static void SetMessageButtonStates(Window *w, byte value, int element) Darkvater@1688: { Darkvater@1688: element *= 2; belugas@4709: belugas@4709: SetWindowWidgetDisabledState(w, element + 3, value == 0); belugas@4709: SetWindowWidgetDisabledState(w, element + 3 + 1, value == 2); Darkvater@1688: } truelight@0: truelight@0: static void MessageOptionsWndProc(Window *w, WindowEvent *e) truelight@0: { Darkvater@1688: static const StringID message_opt[] = {STR_OFF, STR_SUMMARY, STR_FULL, INVALID_STRING_ID}; Darkvater@1688: Darkvater@1688: /* WP(w, def_d).data_1 are stores the clicked state of the fake widgets Darkvater@1688: * WP(w, def_d).data_2 stores state of the ALL on/off/summary button */ tron@427: switch (e->event) { Darkvater@1688: case WE_CREATE: { Darkvater@1688: uint32 val = _news_display_opt; rubidium@5461: uint32 all_val; Darkvater@1688: int i; rubidium@5461: WP(w, def_d).data_1 = 0; Darkvater@1688: rubidium@5461: /* Set up the initial disabled buttons in the case of 'off' or 'full' */ rubidium@5461: all_val = val & 0x3; rubidium@5461: for (i = 0; i != 10; i++, val >>= 2) { rubidium@5461: SetMessageButtonStates(w, val & 0x3, i); rubidium@5461: /* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */ rubidium@5461: if ((val & 0x3) != all_val) all_val = 0; rubidium@5461: } rubidium@5461: /* If all values are the same value, the ALL-button will take over this value */ rubidium@5461: WP(w, def_d).data_2 = all_val; Darkvater@1688: } break; Darkvater@1688: truelight@0: case WE_PAINT: { Darkvater@1688: uint32 val = _news_display_opt; Darkvater@1688: int click_state = WP(w, def_d).data_1; truelight@0: int i, y; truelight@0: rubidium@5461: if (_news_ticker_sound) LowerWindowWidget(w, 27); truelight@0: DrawWindowWidgets(w); dominik@79: Darkvater@1688: /* XXX - Draw the fake widgets-buttons. Can't add these to the widget-desc since Darkvater@1688: * openttd currently can only handle 32 widgets. So hack it *g* */ rubidium@5461: for (i = 0, y = 26; i < NT_END; i++, y += 12, click_state >>= 1, val >>= 2) { Darkvater@1688: bool clicked = !!(click_state & 1); Darkvater@1688: hackykid@1938: DrawFrameRect(13, y, 89, 11 + y, 3, (clicked) ? FR_LOWERED : 0); Darkvater@1688: DrawStringCentered(((13 + 89 + 1) >> 1) + clicked, ((y + 11 + y + 1) >> 1) - 5 + clicked, message_opt[val & 0x3], 0x10); Darkvater@1688: DrawString(103, y + 1, i + STR_0206_ARRIVAL_OF_FIRST_VEHICLE, 0); truelight@193: } truelight@0: rubidium@5461: DrawStringCentered(((13 + 89 + 1) >> 1), y + 9, message_opt[WP(w, def_d).data_2], 0x10); Darkvater@1688: DrawString(103, y + 9, STR_MESSAGES_ALL, 0); Darkvater@1688: DrawString(103, y + 9 + 12, STR_MESSAGE_SOUND, 0); truelight@0: truelight@0: } break; Darkvater@1688: Darkvater@1688: case WE_CLICK: belugas@4634: switch (e->we.click.widget) { Darkvater@1688: case 2: /* Clicked on any of the fake widgets */ rubidium@5461: if (e->we.click.pt.x > 13 && e->we.click.pt.x < 89 && e->we.click.pt.y > 26 && e->we.click.pt.y < NT_END * 12 + 26) { belugas@4634: int element = (e->we.click.pt.y - 26) / 12; Darkvater@1688: byte val = (GetNewsDisplayValue(element) + 1) % 3; Darkvater@1688: Darkvater@1688: SetMessageButtonStates(w, val, element); Darkvater@1688: SetNewsDisplayValue(element, val); Darkvater@1688: Darkvater@1688: WP(w, def_d).data_1 |= (1 << element); Darkvater@1688: w->flags4 |= 5 << WF_TIMEOUT_SHL; // XXX - setup unclick (fake widget) Darkvater@1688: SetWindowDirty(w); Darkvater@1688: } Darkvater@1688: break; rubidium@5461: case 25: case 26: /* Dropdown menu for all settings */ rubidium@5461: ShowDropDownMenu(w, message_opt, WP(w, def_d).data_2, 26, 0, 0); Darkvater@1688: break; rubidium@5461: case 27: /* Change ticker sound on/off */ Darkvater@1688: _news_ticker_sound ^= 1; belugas@4719: ToggleWidgetLoweredState(w, e->we.click.widget); belugas@4634: InvalidateWidget(w, e->we.click.widget); Darkvater@1688: break; Darkvater@1688: default: { /* Clicked on the [<] .. [>] widgets */ belugas@4634: int wid = e->we.click.widget; rubidium@5461: if (wid > 2 && wid <= NT_END * 2 + 2) { Darkvater@1688: int element = (wid - 3) / 2; Darkvater@1688: byte val = (GetNewsDisplayValue(element) + ((wid & 1) ? -1 : 1)) % 3; Darkvater@1688: Darkvater@1688: SetMessageButtonStates(w, val, element); Darkvater@1688: SetNewsDisplayValue(element, val); Darkvater@1688: SetWindowDirty(w); Darkvater@1688: } Darkvater@1688: } break; Darkvater@1688: } break; Darkvater@1688: belugas@4709: case WE_DROPDOWN_SELECT: {/* Select all settings for newsmessages */ belugas@4709: int i; belugas@4709: belugas@4634: WP(w, def_d).data_2 = e->we.dropdown.index; belugas@4709: rubidium@5461: for (i = 0; i < NT_END; i++) { belugas@4709: SetMessageButtonStates(w, e->we.dropdown.index, i); rubidium@5461: SetNewsDisplayValue(i, e->we.dropdown.index); belugas@4709: } Darkvater@1688: SetWindowDirty(w); Darkvater@1688: break; belugas@4709: } Darkvater@1688: Darkvater@1688: case WE_TIMEOUT: /* XXX - Hack to animate 'fake' buttons */ Darkvater@1688: WP(w, def_d).data_1 = 0; Darkvater@1688: SetWindowDirty(w); Darkvater@1688: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static const Widget _message_options_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, Darkvater@1688: { WWT_CAPTION, RESIZE_NONE, 13, 11, 409, 0, 13, STR_0204_MESSAGE_OPTIONS, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@5461: { WWT_PANEL, RESIZE_NONE, 13, 0, 409, 14, 196, 0x0, STR_NULL}, Darkvater@1688: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 26, 37, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 26, 37, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, Darkvater@1688: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 38, 49, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 38, 49, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, Darkvater@1688: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 50, 61, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 50, 61, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, Darkvater@1688: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 62, 73, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 62, 73, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 74, 85, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 74, 85, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 86, 97, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 86, 97, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 98, 109, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 98, 109, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 110, 121, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 110, 121, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 122, 133, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 122, 133, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 134, 145, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 134, 145, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@4344: rubidium@5461: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 4, 12, 146, 157, SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@5461: { WWT_PUSHIMGBTN, RESIZE_NONE, 3, 90, 98, 146, 157, SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, rubidium@5461: rubidium@5461: { WWT_PANEL, RESIZE_NONE, 3, 4, 86, 166, 177, 0x0, STR_NULL}, rubidium@5461: { WWT_TEXTBTN, RESIZE_NONE, 3, 87, 98, 166, 177, STR_0225, STR_NULL}, rubidium@5461: { WWT_TEXTBTN_2, RESIZE_NONE, 3, 4, 98, 178, 189, STR_02DB_OFF, STR_NULL}, truelight@0: belugas@4345: { WWT_LABEL, RESIZE_NONE, 13, 0, 409, 13, 26, STR_0205_MESSAGE_TYPES, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _message_options_desc = { rubidium@5461: 270, 22, 410, 197, tron@427: WC_GAME_OPTIONS, 0, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, truelight@0: _message_options_widgets, truelight@0: MessageOptionsWndProc truelight@0: }; truelight@0: tron@427: void ShowMessageOptions(void) truelight@0: { truelight@0: DeleteWindowById(WC_GAME_OPTIONS, 0); truelight@0: AllocateWindowDesc(&_message_options_desc); truelight@0: } tron@3139: tron@3139: tron@3139: void DeleteVehicleNews(VehicleID vid, StringID news) tron@3139: { Darkvater@5246: NewsID n; tron@3139: Darkvater@5254: for (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)) { belugas@4171: Window *w; tron@3139: Darkvater@5414: /* If we delete a forced news and it is just before the current news Darkvater@5414: * then we need to advance to the next news (if any) */ Darkvater@5414: if (_forced_news == n) MoveToNextItem(); Darkvater@5414: if (_forced_news == INVALID_NEWS && _current_news == n) MoveToNextItem(); Darkvater@5254: _total_news--; tron@3139: Darkvater@5395: /* If this is the last news item, invalidate _latest_news */ Darkvater@5395: if (_total_news == 0) { Darkvater@5395: assert(_latest_news == _oldest_news); Darkvater@5254: _latest_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@5395: * We also need an update of the current, forced and visible (open window) Darkvater@5395: * news's as this shifting could change the items they were pointing to */ Darkvater@5254: if (_total_news != 0) { Darkvater@5414: NewsID visible_news, i; Darkvater@5395: w = FindWindowById(WC_NEWS_WINDOW, 0); Darkvater@5395: visible_news = (w != NULL) ? (NewsID)(WP(w, news_d).ni - _news_items) : INVALID_NEWS; Darkvater@5395: Darkvater@5395: for (i = n;; i = decreaseIndex(i)) { Darkvater@5246: _news_items[i] = _news_items[decreaseIndex(i)]; Darkvater@5395: rubidium@5431: if (i != _latest_news) { rubidium@5431: if (i == _current_news) _current_news = increaseIndex(_current_news); rubidium@5431: if (i == _forced_news) _forced_news = increaseIndex(_forced_news); rubidium@5431: if (i == visible_news) WP(w, news_d).ni = &_news_items[increaseIndex(visible_news)]; rubidium@5431: } Darkvater@5395: Darkvater@5395: if (i == _oldest_news) break; Darkvater@5244: } Darkvater@5244: _oldest_news = increaseIndex(_oldest_news); tron@3139: } tron@3139: Darkvater@5395: /*DEBUG(misc, 0) ("-cur %3d, old %2d, lat %3d, for %3d, tot %2d", Darkvater@5395: _current_news, _oldest_news, _latest_news, _forced_news, _total_news); */ Darkvater@5395: tron@3139: w = FindWindowById(WC_MESSAGE_HISTORY, 0); tron@3143: if (w != NULL) { tron@3143: SetWindowDirty(w); tron@3143: w->vscroll.count = _total_news; tron@3143: } tron@3139: } Darkvater@5254: Darkvater@5254: if (n == _latest_news) break; tron@3139: } tron@3139: }