58 static uint _max_news_items = 0; ///< size of news FIFO queue |
58 static uint _max_news_items = 0; ///< size of news FIFO queue |
59 static NewsID _current_news = INVALID_NEWS; ///< points to news item that should be shown next |
59 static NewsID _current_news = INVALID_NEWS; ///< points to news item that should be shown next |
60 static NewsID _oldest_news = 0; ///< points to first item in fifo queue |
60 static NewsID _oldest_news = 0; ///< points to first item in fifo queue |
61 static NewsID _latest_news = INVALID_NEWS; ///< points to last item in fifo queue |
61 static NewsID _latest_news = INVALID_NEWS; ///< points to last item in fifo queue |
62 |
62 |
63 struct news_d { |
|
64 uint16 chat_height; |
|
65 NewsItem *ni; |
|
66 }; |
|
67 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(news_d)); |
|
68 |
|
69 /** Forced news item. |
63 /** Forced news item. |
70 * Users can force an item by accessing the history or "last message". |
64 * Users can force an item by accessing the history or "last message". |
71 * If the message being shown was forced by the user, its index is stored in |
65 * If the message being shown was forced by the user, its index is stored in |
72 * _forced_news. Otherwise, \a _forced_news variable is INVALID_NEWS. */ |
66 * _forced_news. Otherwise, \a _forced_news variable is INVALID_NEWS. */ |
73 static NewsID _forced_news = INVALID_NEWS; |
67 static NewsID _forced_news = INVALID_NEWS; |
74 |
68 |
75 static uint _total_news = 0; ///< Number of news items in FIFO queue @see _news_items |
69 static uint _total_news = 0; ///< Number of news items in FIFO queue @see _news_items |
76 |
70 static void MoveToNextItem(); |
|
71 |
|
72 |
|
73 typedef void DrawNewsCallbackProc(struct Window *w, const NewsItem *ni); |
77 void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni); |
74 void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni); |
78 void DrawNewsBankrupcy(Window *w, const NewsItem *ni); |
75 void DrawNewsBankrupcy(Window *w, const NewsItem *ni); |
79 static void MoveToNextItem(); |
|
80 |
76 |
81 static DrawNewsCallbackProc * const _draw_news_callback[] = { |
77 static DrawNewsCallbackProc * const _draw_news_callback[] = { |
82 DrawNewsNewVehicleAvail, ///< DNC_VEHICLEAVAIL |
78 DrawNewsNewVehicleAvail, ///< DNC_VEHICLEAVAIL |
83 DrawNewsBankrupcy, ///< DNC_BANKRUPCY |
79 DrawNewsBankrupcy, ///< DNC_BANKRUPCY |
84 }; |
80 }; |
129 _latest_news = INVALID_NEWS; |
125 _latest_news = INVALID_NEWS; |
130 _forced_news = INVALID_NEWS; |
126 _forced_news = INVALID_NEWS; |
131 _total_news = 0; |
127 _total_news = 0; |
132 } |
128 } |
133 |
129 |
134 void DrawNewsBorder(const Window *w) |
130 struct NewsWindow : Window { |
135 { |
131 uint16 chat_height; |
136 int left = 0; |
132 NewsItem *ni; |
137 int right = w->width - 1; |
133 |
138 int top = 0; |
134 NewsWindow(const WindowDesc *desc, NewsItem *ni) : Window(desc), ni(ni) |
139 int bottom = w->height - 1; |
135 { |
140 |
136 const Window *w = FindWindowById(WC_SEND_NETWORK_MSG, 0); |
141 GfxFillRect(left, top, right, bottom, 0xF); |
137 this->chat_height = (w != NULL) ? w->height : 0; |
142 |
138 |
143 GfxFillRect(left, top, left, bottom, 0xD7); |
139 this->ni = &_news_items[_forced_news == INVALID_NEWS ? _current_news : _forced_news]; |
144 GfxFillRect(right, top, right, bottom, 0xD7); |
140 this->flags4 |= WF_DISABLE_VP_SCROLL; |
145 GfxFillRect(left, top, right, top, 0xD7); |
141 |
146 GfxFillRect(left, bottom, right, bottom, 0xD7); |
142 this->FindWindowPlacementAndResize(desc); |
147 |
143 } |
148 DrawString(left + 2, top + 1, STR_00C6, TC_FROMSTRING); |
144 |
149 } |
145 void DrawNewsBorder() |
150 |
146 { |
151 static void NewsWindowProc(Window *w, WindowEvent *e) |
147 int left = 0; |
152 { |
148 int right = this->width - 1; |
153 switch (e->event) { |
149 int top = 0; |
154 case WE_CREATE: { // If chatbar is open at creation time, we need to go above it |
150 int bottom = this->height - 1; |
155 const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0); |
151 |
156 WP(w, news_d).chat_height = (w1 != NULL) ? w1->height : 0; |
152 GfxFillRect(left, top, right, bottom, 0xF); |
157 break; |
153 |
|
154 GfxFillRect(left, top, left, bottom, 0xD7); |
|
155 GfxFillRect(right, top, right, bottom, 0xD7); |
|
156 GfxFillRect(left, top, right, top, 0xD7); |
|
157 GfxFillRect(left, bottom, right, bottom, 0xD7); |
|
158 |
|
159 DrawString(left + 2, top + 1, STR_00C6, TC_FROMSTRING); |
|
160 } |
|
161 |
|
162 virtual void OnPaint() |
|
163 { |
|
164 const NewsMode display_mode = _news_subtype_data[this->ni->subtype].display_mode; |
|
165 |
|
166 switch (display_mode) { |
|
167 case NM_NORMAL: |
|
168 case NM_THIN: { |
|
169 this->DrawNewsBorder(); |
|
170 |
|
171 DrawString(2, 1, STR_00C6, TC_FROMSTRING); |
|
172 |
|
173 SetDParam(0, this->ni->date); |
|
174 DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING); |
|
175 |
|
176 if (!(this->ni->flags & NF_VIEWPORT)) { |
|
177 CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); |
|
178 DrawStringMultiCenter(215, display_mode == NM_NORMAL ? 76 : 56, |
|
179 this->ni->string_id, this->width - 4); |
|
180 } else { |
|
181 /* Back up transparency options to draw news view */ |
|
182 TransparencyOptionBits to_backup = _transparency_opt; |
|
183 _transparency_opt = 0; |
|
184 DrawWindowViewport(this); |
|
185 _transparency_opt = to_backup; |
|
186 |
|
187 /* Shade the viewport into gray, or color*/ |
|
188 ViewPort *vp = this->viewport; |
|
189 GfxFillRect(vp->left - this->left, vp->top - this->top, |
|
190 vp->left - this->left + vp->width - 1, vp->top - this->top + vp->height - 1, |
|
191 (this->ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE) |
|
192 ); |
|
193 |
|
194 CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); |
|
195 DrawStringMultiCenter(this->width / 2, 20, this->ni->string_id, this->width - 4); |
|
196 } |
|
197 break; |
|
198 } |
|
199 |
|
200 case NM_CALLBACK: |
|
201 this->DrawNewsBorder(); |
|
202 _draw_news_callback[_news_subtype_data[this->ni->subtype].callback](this, ni); |
|
203 break; |
|
204 |
|
205 default: |
|
206 DrawWindowWidgets(this); |
|
207 if (!(this->ni->flags & NF_VIEWPORT)) { |
|
208 CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); |
|
209 DrawStringMultiCenter(140, 38, this->ni->string_id, 276); |
|
210 } else { |
|
211 DrawWindowViewport(this); |
|
212 CopyInDParam(0, this->ni->params, lengthof(this->ni->params)); |
|
213 DrawStringMultiCenter(this->width / 2, this->height - 16, this->ni->string_id, this->width - 4); |
|
214 } |
|
215 break; |
158 } |
216 } |
159 |
217 } |
160 case WE_PAINT: { |
218 |
161 const NewsItem *ni = WP(w, news_d).ni; |
219 virtual void OnClick(Point pt, int widget) |
162 const NewsMode display_mode = _news_subtype_data[ni->subtype].display_mode; |
220 { |
163 |
221 switch (widget) { |
164 switch (display_mode) { |
222 case 1: |
165 case NM_NORMAL: |
223 this->ni->duration = 0; |
166 case NM_THIN: { |
224 delete this; |
167 DrawNewsBorder(w); |
225 _forced_news = INVALID_NEWS; |
168 |
226 break; |
169 DrawString(2, 1, STR_00C6, TC_FROMSTRING); |
227 |
170 |
228 case 0: |
171 SetDParam(0, ni->date); |
229 if (this->ni->flags & NF_VEHICLE) { |
172 DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING); |
230 Vehicle *v = GetVehicle(this->ni->data_a); |
173 |
231 ScrollMainWindowTo(v->x_pos, v->y_pos); |
174 if (!(ni->flags & NF_VIEWPORT)) { |
232 } else if (this->ni->flags & NF_TILE) { |
175 CopyInDParam(0, ni->params, lengthof(ni->params)); |
233 if (_ctrl_pressed) { |
176 DrawStringMultiCenter(215, display_mode == NM_NORMAL ? 76 : 56, |
234 ShowExtraViewPortWindow(this->ni->data_a); |
177 ni->string_id, w->width - 4); |
235 if (this->ni->flags & NF_TILE2) { |
|
236 ShowExtraViewPortWindow(this->ni->data_b); |
|
237 } |
178 } else { |
238 } else { |
179 /* Back up transparency options to draw news view */ |
239 if (!ScrollMainWindowToTile(this->ni->data_a) && this->ni->flags & NF_TILE2) { |
180 TransparencyOptionBits to_backup = _transparency_opt; |
240 ScrollMainWindowToTile(this->ni->data_b); |
181 _transparency_opt = 0; |
|
182 DrawWindowViewport(w); |
|
183 _transparency_opt = to_backup; |
|
184 |
|
185 /* Shade the viewport into gray, or color*/ |
|
186 ViewPort *vp = w->viewport; |
|
187 GfxFillRect(vp->left - w->left, vp->top - w->top, |
|
188 vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1, |
|
189 (ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE) |
|
190 ); |
|
191 |
|
192 CopyInDParam(0, ni->params, lengthof(ni->params)); |
|
193 DrawStringMultiCenter(w->width / 2, 20, ni->string_id, w->width - 4); |
|
194 } |
|
195 break; |
|
196 } |
|
197 |
|
198 case NM_CALLBACK: |
|
199 _draw_news_callback[_news_subtype_data[ni->subtype].callback](w, ni); |
|
200 break; |
|
201 |
|
202 default: |
|
203 DrawWindowWidgets(w); |
|
204 if (!(ni->flags & NF_VIEWPORT)) { |
|
205 CopyInDParam(0, ni->params, lengthof(ni->params)); |
|
206 DrawStringMultiCenter(140, 38, ni->string_id, 276); |
|
207 } else { |
|
208 DrawWindowViewport(w); |
|
209 CopyInDParam(0, ni->params, lengthof(ni->params)); |
|
210 DrawStringMultiCenter(w->width / 2, w->height - 16, ni->string_id, w->width - 4); |
|
211 } |
|
212 break; |
|
213 } |
|
214 break; |
|
215 } |
|
216 |
|
217 case WE_CLICK: |
|
218 switch (e->we.click.widget) { |
|
219 case 1: { |
|
220 NewsItem *ni = WP(w, news_d).ni; |
|
221 delete w; |
|
222 ni->duration = 0; |
|
223 _forced_news = INVALID_NEWS; |
|
224 break; |
|
225 } |
|
226 |
|
227 case 0: { |
|
228 NewsItem *ni = WP(w, news_d).ni; |
|
229 if (ni->flags & NF_VEHICLE) { |
|
230 Vehicle *v = GetVehicle(ni->data_a); |
|
231 ScrollMainWindowTo(v->x_pos, v->y_pos); |
|
232 } else if (ni->flags & NF_TILE) { |
|
233 if (_ctrl_pressed) { |
|
234 ShowExtraViewPortWindow(ni->data_a); |
|
235 if (ni->flags & NF_TILE2) { |
|
236 ShowExtraViewPortWindow(ni->data_b); |
|
237 } |
|
238 } else { |
|
239 if (!ScrollMainWindowToTile(ni->data_a) && ni->flags & NF_TILE2) { |
|
240 ScrollMainWindowToTile(ni->data_b); |
|
241 } |
|
242 } |
241 } |
243 } |
242 } |
244 break; |
|
245 } |
243 } |
246 } |
244 break; |
247 break; |
|
248 |
|
249 case WE_KEYPRESS: |
|
250 if (e->we.keypress.keycode == WKC_SPACE) { |
|
251 /* Don't continue. */ |
|
252 e->we.keypress.cont = false; |
|
253 delete w; |
|
254 } |
|
255 break; |
|
256 |
|
257 case WE_INVALIDATE_DATA: // The chatbar has notified us that is was either created or closed |
|
258 WP(w, news_d).chat_height = e->we.invalidate.data; |
|
259 break; |
|
260 |
|
261 case WE_TICK: { // Scroll up newsmessages from the bottom in steps of 4 pixels |
|
262 int y = max(w->top - 4, _screen.height - w->height - 12 - WP(w, news_d).chat_height); |
|
263 if (y == w->top) return; |
|
264 |
|
265 if (w->viewport != NULL) { |
|
266 w->viewport->top += y - w->top; |
|
267 } |
|
268 |
|
269 int diff = Delta(w->top, y); |
|
270 w->top = y; |
|
271 |
|
272 SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height); |
|
273 break; |
|
274 } |
245 } |
275 } |
246 } |
276 } |
247 |
|
248 virtual bool OnKeyPress(uint16 key, uint16 keycode) |
|
249 { |
|
250 if (keycode == WKC_SPACE) { |
|
251 /* Don't continue. */ |
|
252 delete this; |
|
253 return false; |
|
254 } |
|
255 return true; |
|
256 } |
|
257 |
|
258 virtual void OnInvalidateData(int data) |
|
259 { |
|
260 /* The chatbar has notified us that is was either created or closed */ |
|
261 this->chat_height = data; |
|
262 } |
|
263 |
|
264 virtual void OnTick() |
|
265 { |
|
266 /* Scroll up newsmessages from the bottom in steps of 4 pixels */ |
|
267 int y = max(this->top - 4, _screen.height - this->height - 12 - this->chat_height); |
|
268 if (y == this->top) return; |
|
269 |
|
270 if (this->viewport != NULL) this->viewport->top += y - this->top; |
|
271 |
|
272 int diff = Delta(this->top, y); |
|
273 this->top = y; |
|
274 |
|
275 SetDirtyBlocks(this->left, this->top - diff, this->left + this->width, this->top + this->height); |
|
276 } |
|
277 }; |
277 |
278 |
278 /** |
279 /** |
279 * Return the correct index in the pseudo-fifo |
280 * Return the correct index in the pseudo-fifo |
280 * queue and deals with overflows when increasing the index |
281 * queue and deals with overflows when increasing the index |
281 */ |
282 */ |
448 Window *w; |
449 Window *w; |
449 switch (_news_subtype_data[ni->subtype].display_mode) { |
450 switch (_news_subtype_data[ni->subtype].display_mode) { |
450 case NM_NORMAL: |
451 case NM_NORMAL: |
451 case NM_CALLBACK: |
452 case NM_CALLBACK: |
452 _news_type13_desc.top = top; |
453 _news_type13_desc.top = top; |
453 w = new Window(&_news_type13_desc); |
454 w = new NewsWindow(&_news_type13_desc, ni); |
454 if (ni->flags & NF_VIEWPORT) { |
455 if (ni->flags & NF_VIEWPORT) { |
455 InitializeWindowViewport(w, 2, 58, 426, 110, |
456 InitializeWindowViewport(w, 2, 58, 426, 110, |
456 ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); |
457 ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); |
457 } |
458 } |
458 break; |
459 break; |
459 |
460 |
460 case NM_THIN: |
461 case NM_THIN: |
461 _news_type2_desc.top = top; |
462 _news_type2_desc.top = top; |
462 w = new Window(&_news_type2_desc); |
463 w = new NewsWindow(&_news_type2_desc, ni); |
463 if (ni->flags & NF_VIEWPORT) { |
464 if (ni->flags & NF_VIEWPORT) { |
464 InitializeWindowViewport(w, 2, 58, 426, 70, |
465 InitializeWindowViewport(w, 2, 58, 426, 70, |
465 ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); |
466 ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); |
466 } |
467 } |
467 break; |
468 break; |
468 |
469 |
469 default: |
470 default: |
470 _news_type0_desc.top = top; |
471 _news_type0_desc.top = top; |
471 w = new Window(&_news_type0_desc); |
472 w = new NewsWindow(&_news_type0_desc, ni); |
472 if (ni->flags & NF_VIEWPORT) { |
473 if (ni->flags & NF_VIEWPORT) { |
473 InitializeWindowViewport(w, 3, 17, 274, 47, |
474 InitializeWindowViewport(w, 3, 17, 274, 47, |
474 ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); |
475 ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS); |
475 } |
476 } |
476 break; |
477 break; |
477 } |
478 } |
478 |
479 |
479 /*DEBUG(misc, 0, " cur %3d, old %2d, lat %3d, for %3d, tot %2d", |
480 /*DEBUG(misc, 0, " cur %3d, old %2d, lat %3d, for %3d, tot %2d", |
480 _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/ |
481 _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/ |
481 |
|
482 WP(w, news_d).ni = &_news_items[_forced_news == INVALID_NEWS ? _current_news : _forced_news]; |
|
483 w->flags4 |= WF_DISABLE_VP_SCROLL; |
|
484 } |
482 } |
485 |
483 |
486 /** Show news item in the ticker */ |
484 /** Show news item in the ticker */ |
487 static void ShowTicker(const NewsItem *ni) |
485 static void ShowTicker(const NewsItem *ni) |
488 { |
486 { |