src/news_gui.cpp
branchNewGRF_ports
changeset 10184 fcf5fb2548eb
parent 6872 1c4a4a609f85
child 10210 a2131f7a315d
equal deleted inserted replaced
10179:eec5a7dcbf61 10184:fcf5fb2548eb
     4 #include "stdafx.h"
     4 #include "stdafx.h"
     5 #include "openttd.h"
     5 #include "openttd.h"
     6 #include "gui.h"
     6 #include "gui.h"
     7 #include "window_gui.h"
     7 #include "window_gui.h"
     8 #include "viewport_func.h"
     8 #include "viewport_func.h"
     9 #include "news.h"
     9 #include "news_func.h"
    10 #include "settings_type.h"
    10 #include "settings_type.h"
    11 #include "transparency.h"
    11 #include "transparency.h"
    12 #include "strings_func.h"
    12 #include "strings_func.h"
    13 #include "window_func.h"
    13 #include "window_func.h"
    14 #include "date_func.h"
    14 #include "date_func.h"
    58 static NewsItem _news_items[MAX_NEWS];      ///< The news FIFO queue
    58 static NewsItem _news_items[MAX_NEWS];      ///< The 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 follow_vehicle;
       
    65 	int32 scrollpos_x;
       
    66 	int32 scrollpos_y;
       
    67 	int32 dest_scrollpos_x;
       
    68 	int32 dest_scrollpos_y;
       
    69 	NewsItem *ni;
       
    70 };
       
    71 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(news_d));
       
    72 
    63 /** Forced news item.
    73 /** Forced news item.
    64  * Users can force an item by accessing the history or "last message".
    74  * Users can force an item by accessing the history or "last message".
    65  * If the message being shown was forced by the user, its index is stored in
    75  * If the message being shown was forced by the user, its index is stored in
    66  * _forced_news. Otherwise, \a _forced_news variable is INVALID_NEWS. */
    76  * _forced_news. Otherwise, \a _forced_news variable is INVALID_NEWS. */
    67 static NewsID _forced_news = INVALID_NEWS;
    77 static NewsID _forced_news = INVALID_NEWS;
    68 
    78 
    69 static byte _total_news = 0; ///< Number of news items in FIFO queue @see _news_items
    79 static byte _total_news = 0; ///< Number of news items in FIFO queue @see _news_items
    70 
    80 
    71 void DrawNewsNewVehicleAvail(Window *w);
    81 void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni);
    72 void DrawNewsBankrupcy(Window *w);
    82 void DrawNewsBankrupcy(Window *w, const NewsItem *ni);
    73 static void MoveToNextItem();
    83 static void MoveToNextItem();
    74 
    84 
    75 StringID GetNewsStringNewVehicleAvail(const NewsItem *ni);
    85 StringID GetNewsStringNewVehicleAvail(const NewsItem *ni);
    76 StringID GetNewsStringBankrupcy(const NewsItem *ni);
    86 StringID GetNewsStringBankrupcy(const NewsItem *ni);
    77 
    87 
   115 }
   125 }
   116 
   126 
   117 static void NewsWindowProc(Window *w, WindowEvent *e)
   127 static void NewsWindowProc(Window *w, WindowEvent *e)
   118 {
   128 {
   119 	switch (e->event) {
   129 	switch (e->event) {
   120 	case WE_CREATE: { // If chatbar is open at creation time, we need to go above it
   130 		case WE_CREATE: { // If chatbar is open at creation time, we need to go above it
   121 		const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0);
   131 			const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0);
   122 		w->message.msg = (w1 != NULL) ? w1->height : 0;
   132 			w->message.msg = (w1 != NULL) ? w1->height : 0;
   123 	} break;
   133 		} break;
   124 
   134 
   125 	case WE_PAINT: {
   135 		case WE_PAINT: {
   126 		const NewsItem *ni = WP(w, news_d).ni;
   136 			const NewsItem *ni = WP(w, news_d).ni;
   127 		ViewPort *vp;
   137 
   128 
   138 			switch (ni->display_mode) {
   129 		switch (ni->display_mode) {
   139 				case NM_NORMAL:
   130 			case NM_NORMAL:
   140 				case NM_THIN: {
   131 			case NM_THIN: {
   141 					DrawNewsBorder(w);
   132 				DrawNewsBorder(w);
   142 
   133 
   143 					DrawString(2, 1, STR_00C6, TC_FROMSTRING);
   134 				DrawString(2, 1, STR_00C6, TC_FROMSTRING);
   144 
   135 
   145 					SetDParam(0, ni->date);
   136 				SetDParam(0, ni->date);
   146 					DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING);
   137 				DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING);
   147 
   138 
   148 					if (!(ni->flags & NF_VIEWPORT)) {
   139 				if (!(ni->flags & NF_VIEWPORT)) {
   149 						CopyInDParam(0, ni->params, lengthof(ni->params));
   140 					CopyInDParam(0, ni->params, lengthof(ni->params));
   150 						DrawStringMultiCenter(215, ni->display_mode == NM_NORMAL ? 76 : 56,
   141 					DrawStringMultiCenter(215, ni->display_mode == NM_NORMAL ? 76 : 56,
   151 							ni->string_id, w->width - 4);
   142 						ni->string_id, w->width - 4);
   152 					} else {
   143 				} else {
   153 						/* Back up transparency options to draw news view */
   144 					/* Back up transparency options to draw news view */
   154 						TransparencyOptionBits to_backup = _transparency_opt;
   145 					TransparencyOptionBits to_backup = _transparency_opt;
   155 						_transparency_opt = 0;
   146 					_transparency_opt = 0;
   156 						DrawWindowViewport(w);
   147 					DrawWindowViewport(w);
   157 						_transparency_opt = to_backup;
   148 					_transparency_opt = to_backup;
   158 
   149 
   159 						/* Shade the viewport into gray, or color*/
   150 					/* Shade the viewport into gray, or color*/
   160 						ViewPort *vp = w->viewport;
   151 					vp = w->viewport;
   161 						GfxFillRect(vp->left - w->left, vp->top - w->top,
   152 					GfxFillRect(vp->left - w->left, vp->top - w->top,
   162 							vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1,
   153 						vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1,
   163 							(ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE)
   154 						(ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE)
   164 						);
   155 					);
   165 
   156 
   166 						CopyInDParam(0, ni->params, lengthof(ni->params));
   157 					CopyInDParam(0, ni->params, lengthof(ni->params));
   167 						DrawStringMultiCenter(w->width / 2, 20, ni->string_id, w->width - 4);
   158 					DrawStringMultiCenter(w->width / 2, 20, ni->string_id, w->width - 4);
   168 					}
       
   169 					break;
   159 				}
   170 				}
   160 				break;
   171 
   161 			}
   172 				case NM_CALLBACK:
   162 
   173 					_draw_news_callback[ni->callback](w, ni);
   163 			case NM_CALLBACK: {
   174 					break;
   164 				_draw_news_callback[ni->callback](w);
   175 
   165 				break;
   176 				default:
   166 			}
   177 					DrawWindowWidgets(w);
   167 
   178 					if (!(ni->flags & NF_VIEWPORT)) {
   168 			default: {
   179 						CopyInDParam(0, ni->params, lengthof(ni->params));
   169 				DrawWindowWidgets(w);
   180 						DrawStringMultiCenter(140, 38, ni->string_id, 276);
   170 				if (!(ni->flags & NF_VIEWPORT)) {
   181 					} else {
   171 					CopyInDParam(0, ni->params, lengthof(ni->params));
   182 						DrawWindowViewport(w);
   172 					DrawStringMultiCenter(140, 38, ni->string_id, 276);
   183 						CopyInDParam(0, ni->params, lengthof(ni->params));
   173 				} else {
   184 						DrawStringMultiCenter(w->width / 2, w->height - 16, ni->string_id, w->width - 4);
   174 					DrawWindowViewport(w);
   185 					}
   175 					CopyInDParam(0, ni->params, lengthof(ni->params));
   186 					break;
   176 					DrawStringMultiCenter(w->width / 2, w->height - 16, ni->string_id, w->width - 4);
   187 			}
       
   188 		} break;
       
   189 
       
   190 		case WE_CLICK: {
       
   191 			switch (e->we.click.widget) {
       
   192 			case 1: {
       
   193 				NewsItem *ni = WP(w, news_d).ni;
       
   194 				DeleteWindow(w);
       
   195 				ni->duration = 0;
       
   196 				_forced_news = INVALID_NEWS;
       
   197 			} break;
       
   198 			case 0: {
       
   199 				NewsItem *ni = WP(w, news_d).ni;
       
   200 				if (ni->flags & NF_VEHICLE) {
       
   201 					Vehicle *v = GetVehicle(ni->data_a);
       
   202 					ScrollMainWindowTo(v->x_pos, v->y_pos);
       
   203 				} else if (ni->flags & NF_TILE) {
       
   204 					if (!ScrollMainWindowToTile(ni->data_a) && ni->data_b != 0)
       
   205 						ScrollMainWindowToTile(ni->data_b);
   177 				}
   206 				}
   178 				break;
   207 			} break;
   179 			}
   208 			}
   180 		}
       
   181 	} break;
       
   182 
       
   183 	case WE_CLICK: {
       
   184 		switch (e->we.click.widget) {
       
   185 		case 1: {
       
   186 			NewsItem *ni = WP(w, news_d).ni;
       
   187 			DeleteWindow(w);
       
   188 			ni->duration = 0;
       
   189 			_forced_news = INVALID_NEWS;
       
   190 		} break;
   209 		} break;
   191 		case 0: {
   210 
   192 			NewsItem *ni = WP(w, news_d).ni;
   211 		case WE_KEYPRESS:
   193 			if (ni->flags & NF_VEHICLE) {
   212 			if (e->we.keypress.keycode == WKC_SPACE) {
   194 				Vehicle *v = GetVehicle(ni->data_a);
   213 				/* Don't continue. */
   195 				ScrollMainWindowTo(v->x_pos, v->y_pos);
   214 				e->we.keypress.cont = false;
   196 			} else if (ni->flags & NF_TILE) {
   215 				DeleteWindow(w);
   197 				if (!ScrollMainWindowToTile(ni->data_a) && ni->data_b != 0)
   216 			}
   198 					ScrollMainWindowToTile(ni->data_b);
   217 			break;
   199 			}
   218 
       
   219 		case WE_MESSAGE: // The chatbar has notified us that is was either created or closed
       
   220 			switch (e->we.message.msg) {
       
   221 				case WE_CREATE: w->message.msg = e->we.message.wparam; break;
       
   222 				case WE_DESTROY: w->message.msg = 0; break;
       
   223 			}
       
   224 			break;
       
   225 
       
   226 		case WE_TICK: { // Scroll up newsmessages from the bottom in steps of 4 pixels
       
   227 			int diff;
       
   228 			int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg);
       
   229 			if (y == w->top) return;
       
   230 
       
   231 			if (w->viewport != NULL)
       
   232 				w->viewport->top += y - w->top;
       
   233 
       
   234 			diff = Delta(w->top, y);
       
   235 			w->top = y;
       
   236 
       
   237 			SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height);
   200 		} break;
   238 		} break;
   201 		}
       
   202 	} break;
       
   203 
       
   204 	case WE_KEYPRESS:
       
   205 		if (e->we.keypress.keycode == WKC_SPACE) {
       
   206 			/* Don't continue. */
       
   207 			e->we.keypress.cont = false;
       
   208 			DeleteWindow(w);
       
   209 		}
       
   210 		break;
       
   211 
       
   212 	case WE_MESSAGE: // The chatbar has notified us that is was either created or closed
       
   213 		switch (e->we.message.msg) {
       
   214 			case WE_CREATE: w->message.msg = e->we.message.wparam; break;
       
   215 			case WE_DESTROY: w->message.msg = 0; break;
       
   216 		}
       
   217 		break;
       
   218 
       
   219 	case WE_TICK: { // Scroll up newsmessages from the bottom in steps of 4 pixels
       
   220 		int diff;
       
   221 		int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg);
       
   222 		if (y == w->top) return;
       
   223 
       
   224 		if (w->viewport != NULL)
       
   225 			w->viewport->top += y - w->top;
       
   226 
       
   227 		diff = Delta(w->top, y);
       
   228 		w->top = y;
       
   229 
       
   230 		SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height);
       
   231 	} break;
       
   232 	}
   239 	}
   233 }
   240 }
   234 
   241 
   235 /**
   242 /**
   236  * Return the correct index in the pseudo-fifo
   243  * Return the correct index in the pseudo-fifo
   237  * queue and deals with overflows when increasing the index
   244  * queue and deals with overflows when increasing the index
   238  */
   245  */
   239 static inline NewsID increaseIndex(NewsID i)
   246 static inline NewsID IncreaseIndex(NewsID i)
   240 {
   247 {
   241 	assert(i != INVALID_NEWS);
   248 	assert(i != INVALID_NEWS);
   242 	return (i + 1) % MAX_NEWS;
   249 	return (i + 1) % MAX_NEWS;
   243 }
   250 }
   244 
   251 
   245 /**
   252 /**
   246  * Return the correct index in the pseudo-fifo
   253  * Return the correct index in the pseudo-fifo
   247  * queue and deals with overflows when decreasing the index
   254  * queue and deals with overflows when decreasing the index
   248  */
   255  */
   249 static inline NewsID decreaseIndex(NewsID i)
   256 static inline NewsID DecreaseIndex(NewsID i)
   250 {
   257 {
   251 	assert(i != INVALID_NEWS);
   258 	assert(i != INVALID_NEWS);
   252 	return (i + MAX_NEWS - 1) % MAX_NEWS;
   259 	return (i + MAX_NEWS - 1) % MAX_NEWS;
   253 }
   260 }
   254 
   261 
   255 /**
   262 /**
   256  * Add a new newsitem to be shown.
   263  * Add a new newsitem to be shown.
   257  * @param string String to display, can have special values based on parameter \a flags
   264  * @param string String to display, can have special values based on parameter \a display_mode
   258  * @param flags various control bits that will show various news-types. See macro NEWS_FLAGS()
   265  * @param display_mode, any of the NewsMode enums (NM_)
       
   266  * @param flags any of the NewsFlag enums (NF_)
       
   267  * @param type news category, any of the NewsType enums (NT_)
       
   268  * @param callback news callback function, any of the NewsCallback enums (DNC_)
   259  * @param data_a news-specific value based on news type
   269  * @param data_a news-specific value based on news type
   260  * @param data_b news-specific value based on news type
   270  * @param data_b news-specific value based on news type
   261  * @note flags exists of 4 byte-sized extra parameters.
       
   262  *  -# Bits  0 -  7 display_mode, any of the NewsMode enums (NM_)
       
   263  *  -# Bits  8 - 15 news flags, any of the NewsFlags enums (NF_)
       
   264  *  -# Bits 16 - 23 news category, any of the NewsType enums (NT_)
       
   265  *  -# Bits 24 - 31 news callback function, any of the NewsCallback enums (DNC_)
       
   266  *
   271  *
   267  * If the display mode is NM_CALLBACK, special news is shown and parameter
   272  * @note If the display mode is NM_CALLBACK, special news is shown and parameter
   268  * \a string has a special meaning.
   273  * \a string has a special meaning.
   269  *  - For DNC_TRAINAVAIL, DNC_ROADAVAIL, DNC_SHIPAVAIL, DNC_AIRCRAFTAVAIL messages: StringID is
   274  *  - For DNC_TRAINAVAIL, DNC_ROADAVAIL, DNC_SHIPAVAIL, DNC_AIRCRAFTAVAIL messages: StringID is
   270  *    the index of the engine that is shown
   275  *    the index of the engine that is shown
   271  *
   276  *
   272  *  - For DNC_BANKRUPCY: bytes 0-3 of StringID contains the player that is in trouble,
   277  *  - For DNC_BANKRUPCY: bytes 0-3 of StringID contains the player that is in trouble,
   273  *    and 4-7 contains what kind of bankrupcy message is shown.
   278  *    and 4-7 contains what kind of bankrupcy message is shown.
   274  *    @see NewsBankrupcy
   279  *    @see NewsBankrupcy
   275  *
   280  *
   276  * @see NewsMode
   281  * @see NewsMode
   277  * @see NewsFlags
   282  * @see NewsFlag
   278  * @see NewsType
   283  * @see NewsType
   279  * @see NewsCallback
   284  * @see NewsCallback
   280  */
   285  */
   281 void AddNewsItem(StringID string, uint32 flags, uint data_a, uint data_b)
   286 void AddNewsItem(StringID string, NewsMode display_mode, NewsFlag flags, NewsType type, NewsCallback callback, uint data_a, uint data_b)
   282 {
   287 {
   283 	NewsID l_news;
       
   284 
       
   285 	if (_game_mode == GM_MENU) return;
   288 	if (_game_mode == GM_MENU) return;
   286 
   289 
   287 	/* check the rare case that the oldest (to be overwritten) news item is open */
   290 	/* check the rare case that the oldest (to be overwritten) news item is open */
   288 	if (_total_news == MAX_NEWS && (_oldest_news == _current_news || _oldest_news == _forced_news))
   291 	if (_total_news == MAX_NEWS && (_oldest_news == _current_news || _oldest_news == _forced_news)) {
   289 		MoveToNextItem();
   292 		MoveToNextItem();
       
   293 	}
   290 
   294 
   291 	if (_total_news < MAX_NEWS) _total_news++;
   295 	if (_total_news < MAX_NEWS) _total_news++;
   292 
   296 
   293 	/* Increase _latest_news. If we have no news yet, use _oldest news as an
   297 	/* Increase _latest_news. If we have no news yet, use _oldest news as an
   294 	 * index. We cannot use 0 as _oldest_news can jump around due to
   298 	 * index. We cannot use 0 as _oldest_news can jump around due to
   295 	 * DeleteVehicleNews */
   299 	 * DeleteVehicleNews */
   296 	l_news = _latest_news;
   300 	NewsID l_news = _latest_news;
   297 	_latest_news = (_latest_news == INVALID_NEWS) ? _oldest_news : increaseIndex(_latest_news);
   301 	_latest_news = (_latest_news == INVALID_NEWS) ? _oldest_news : IncreaseIndex(_latest_news);
   298 
   302 
   299 	/* If the fifo-buffer is full, overwrite the oldest entry */
   303 	/* If the fifo-buffer is full, overwrite the oldest entry */
   300 	if (l_news != INVALID_NEWS && _latest_news == _oldest_news) {
   304 	if (l_news != INVALID_NEWS && _latest_news == _oldest_news) {
   301 		assert(_total_news == MAX_NEWS);
   305 		assert(_total_news == MAX_NEWS);
   302 		_oldest_news = increaseIndex(_oldest_news);
   306 		_oldest_news = IncreaseIndex(_oldest_news);
   303 	}
   307 	}
   304 
   308 
   305 	/*DEBUG(misc, 0, "+cur %3d, old %2d, lat %3d, for %3d, tot %2d",
   309 	/*DEBUG(misc, 0, "+cur %3d, old %2d, lat %3d, for %3d, tot %2d",
   306 	  _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/
   310 	  _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/
   307 
   311 
   308 	/* Add news to _latest_news */
   312 	/* Add news to _latest_news */
   309 	{
   313 	NewsItem *ni = &_news_items[_latest_news];
   310 		Window *w;
   314 	memset(ni, 0, sizeof(*ni));
   311 		NewsItem *ni = &_news_items[_latest_news];
   315 
   312 		memset(ni, 0, sizeof(*ni));
   316 	ni->string_id = string;
   313 
   317 	ni->display_mode = display_mode;
   314 		ni->string_id = string;
   318 	ni->flags = flags;
   315 		ni->display_mode = (byte)flags;
   319 
   316 		ni->flags = (byte)(flags >> 8);
   320 	/* show this news message in color? */
   317 
   321 	if (_cur_year >= _patches.colored_news_year) ni->flags |= NF_INCOLOR;
   318 		/* show this news message in color? */
   322 
   319 		if (_cur_year >= _patches.colored_news_year) ni->flags |= NF_INCOLOR;
   323 	ni->type = type;
   320 
   324 	ni->callback = callback;
   321 		ni->type = (byte)(flags >> 16);
   325 	ni->data_a = data_a;
   322 		ni->callback = (byte)(flags >> 24);
   326 	ni->data_b = data_b;
   323 		ni->data_a = data_a;
   327 	ni->date = _date;
   324 		ni->data_b = data_b;
   328 	CopyOutDParam(ni->params, 0, lengthof(ni->params));
   325 		ni->date = _date;
   329 
   326 		CopyOutDParam(ni->params, 0, lengthof(ni->params));
   330 	Window *w = FindWindowById(WC_MESSAGE_HISTORY, 0);
   327 
   331 	if (w == NULL) return;
   328 		w = FindWindowById(WC_MESSAGE_HISTORY, 0);
   332 	SetWindowDirty(w);
   329 		if (w == NULL) return;
   333 	w->vscroll.count = _total_news;
   330 		SetWindowDirty(w);
       
   331 		w->vscroll.count = _total_news;
       
   332 	}
       
   333 }
   334 }
   334 
   335 
   335 
   336 
   336 /**
   337 /**
   337  * Maximum age of news items.
   338  * Maximum age of news items.
   338  * Don't show item if it's older than x days, corresponds with NewsType in news.h
   339  * Don't show item if it's older than x days, corresponds with NewsType in news_type.h
   339  * @see NewsType
   340  * @see NewsType
   340  */
   341  */
   341 static const byte _news_items_age[NT_END] = {
   342 static const byte _news_items_age[NT_END] = {
   342 	60,  ///< NT_ARRIVAL_PLAYER
   343 	60,  ///< NT_ARRIVAL_PLAYER
   343 	60,  ///< NT_ARRIVAL_OTHER
   344 	60,  ///< NT_ARRIVAL_OTHER
   459 }
   460 }
   460 
   461 
   461 /** Open up an own newspaper window for the news item */
   462 /** Open up an own newspaper window for the news item */
   462 static void ShowNewspaper(NewsItem *ni)
   463 static void ShowNewspaper(NewsItem *ni)
   463 {
   464 {
   464 	Window *w;
       
   465 	SoundFx sound;
       
   466 	int top;
       
   467 	ni->flags &= ~NF_FORCE_BIG;
   465 	ni->flags &= ~NF_FORCE_BIG;
   468 	ni->duration = 555;
   466 	ni->duration = 555;
   469 
   467 
   470 	sound = _news_sounds[ni->type];
   468 	SoundFx sound = _news_sounds[ni->type];
   471 	if (sound != 0) SndPlayFx(sound);
   469 	if (sound != 0) SndPlayFx(sound);
   472 
   470 
   473 	top = _screen.height;
   471 	int top = _screen.height;
       
   472 	Window *w;
   474 	switch (ni->display_mode) {
   473 	switch (ni->display_mode) {
   475 		case NM_NORMAL:
   474 		case NM_NORMAL:
   476 		case NM_CALLBACK: {
   475 		case NM_CALLBACK:
   477 			_news_type13_desc.top = top;
   476 			_news_type13_desc.top = top;
   478 			w = AllocateWindowDesc(&_news_type13_desc);
   477 			w = AllocateWindowDesc(&_news_type13_desc);
   479 			if (ni->flags & NF_VIEWPORT)
   478 			if (ni->flags & NF_VIEWPORT) {
   480 				AssignWindowViewport(w, 2, 58, 0x1AA, 0x6E,
   479 				AssignWindowViewport(w, 2, 58, 0x1AA, 0x6E,
   481 					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
   480 					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
       
   481 			}
   482 			break;
   482 			break;
   483 		}
   483 
   484 
   484 		case NM_THIN:
   485 		case NM_THIN: {
       
   486 			_news_type2_desc.top = top;
   485 			_news_type2_desc.top = top;
   487 			w = AllocateWindowDesc(&_news_type2_desc);
   486 			w = AllocateWindowDesc(&_news_type2_desc);
   488 			if (ni->flags & NF_VIEWPORT)
   487 			if (ni->flags & NF_VIEWPORT) {
   489 				AssignWindowViewport(w, 2, 58, 0x1AA, 0x46,
   488 				AssignWindowViewport(w, 2, 58, 0x1AA, 0x46,
   490 					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
   489 					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
       
   490 			}
   491 			break;
   491 			break;
   492 		}
   492 
   493 
   493 		default:
   494 		default: {
       
   495 			_news_type0_desc.top = top;
   494 			_news_type0_desc.top = top;
   496 			w = AllocateWindowDesc(&_news_type0_desc);
   495 			w = AllocateWindowDesc(&_news_type0_desc);
   497 			if (ni->flags & NF_VIEWPORT)
   496 			if (ni->flags & NF_VIEWPORT) {
   498 				AssignWindowViewport(w, 3, 17, 0x112, 0x2F,
   497 				AssignWindowViewport(w, 3, 17, 0x112, 0x2F,
   499 					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
   498 					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
       
   499 			}
   500 			break;
   500 			break;
   501 		}
       
   502 	}
   501 	}
   503 
   502 
   504 	/*DEBUG(misc, 0, " cur %3d, old %2d, lat %3d, for %3d, tot %2d",
   503 	/*DEBUG(misc, 0, " cur %3d, old %2d, lat %3d, for %3d, tot %2d",
   505 	  _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/
   504 	  _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/
   506 
   505 
   509 }
   508 }
   510 
   509 
   511 /** Show news item in the ticker */
   510 /** Show news item in the ticker */
   512 static void ShowTicker(const NewsItem *ni)
   511 static void ShowTicker(const NewsItem *ni)
   513 {
   512 {
   514 	Window *w;
       
   515 
       
   516 	if (_news_ticker_sound) SndPlayFx(SND_16_MORSE);
   513 	if (_news_ticker_sound) SndPlayFx(SND_16_MORSE);
   517 
   514 
   518 	_statusbar_news_item = *ni;
   515 	_statusbar_news_item = *ni;
   519 	w = FindWindowById(WC_STATUS_BAR, 0);
   516 	Window *w = FindWindowById(WC_STATUS_BAR, 0);
   520 	if (w != NULL) WP(w, def_d).data_1 = 360;
   517 	if (w != NULL) WP(w, def_d).data_1 = 360;
   521 }
   518 }
   522 
   519 
   523 
   520 
   524 /**
   521 /**
   525  * Are we ready to show another news item?
   522  * Are we ready to show another news item?
   526  * Only if nothing is in the newsticker and no newspaper is displayed
   523  * Only if nothing is in the newsticker and no newspaper is displayed
   527  */
   524  */
   528 static bool ReadyForNextItem()
   525 static bool ReadyForNextItem()
   529 {
   526 {
   530 	const Window *w;
       
   531 	NewsID item = (_forced_news == INVALID_NEWS) ? _current_news : _forced_news;
   527 	NewsID item = (_forced_news == INVALID_NEWS) ? _current_news : _forced_news;
   532 	NewsItem *ni;
       
   533 
   528 
   534 	if (item >= MAX_NEWS) return true;
   529 	if (item >= MAX_NEWS) return true;
   535 	ni = &_news_items[item];
   530 	NewsItem *ni = &_news_items[item];
   536 
   531 
   537 	/* Ticker message
   532 	/* Ticker message
   538 	 * Check if the status bar message is still being displayed? */
   533 	 * Check if the status bar message is still being displayed? */
   539 	w = FindWindowById(WC_STATUS_BAR, 0);
   534 	const Window *w = FindWindowById(WC_STATUS_BAR, 0);
   540 	if (w != NULL && WP(w, const def_d).data_1 > -1280) return false;
   535 	if (w != NULL && WP(w, const def_d).data_1 > -1280) return false;
   541 
   536 
   542 	/* Newspaper message, decrement duration counter */
   537 	/* Newspaper message, decrement duration counter */
   543 	if (ni->duration != 0) ni->duration--;
   538 	if (ni->duration != 0) ni->duration--;
   544 
   539 
   552 	DeleteWindowById(WC_NEWS_WINDOW, 0);
   547 	DeleteWindowById(WC_NEWS_WINDOW, 0);
   553 	_forced_news = INVALID_NEWS;
   548 	_forced_news = INVALID_NEWS;
   554 
   549 
   555 	/* if we're not at the last item, then move on */
   550 	/* if we're not at the last item, then move on */
   556 	if (_current_news != _latest_news) {
   551 	if (_current_news != _latest_news) {
   557 		NewsItem *ni;
   552 		_current_news = (_current_news == INVALID_NEWS) ? _oldest_news : IncreaseIndex(_current_news);
   558 
   553 		NewsItem *ni = &_news_items[_current_news];
   559 		_current_news = (_current_news == INVALID_NEWS) ? _oldest_news : increaseIndex(_current_news);
       
   560 		ni = &_news_items[_current_news];
       
   561 
   554 
   562 		/* check the date, don't show too old items */
   555 		/* check the date, don't show too old items */
   563 		if (_date - _news_items_age[ni->type] > ni->date) return;
   556 		if (_date - _news_items_age[ni->type] > ni->date) return;
   564 
   557 
   565 		switch (GetNewsDisplayValue(ni->type)) {
   558 		switch (GetNewsDisplayValue(ni->type)) {
   621 {
   614 {
   622 	if (_forced_news == INVALID_NEWS) {
   615 	if (_forced_news == INVALID_NEWS) {
   623 		/* Not forced any news yet, show the current one, unless a news window is
   616 		/* Not forced any news yet, show the current one, unless a news window is
   624 		 * open (which can only be the current one), then show the previous item */
   617 		 * open (which can only be the current one), then show the previous item */
   625 		const Window *w = FindWindowById(WC_NEWS_WINDOW, 0);
   618 		const Window *w = FindWindowById(WC_NEWS_WINDOW, 0);
   626 		ShowNewsMessage((w == NULL || (_current_news == _oldest_news)) ? _current_news : decreaseIndex(_current_news));
   619 		ShowNewsMessage((w == NULL || (_current_news == _oldest_news)) ? _current_news : DecreaseIndex(_current_news));
   627 	} else if (_forced_news == _oldest_news) {
   620 	} else if (_forced_news == _oldest_news) {
   628 		/* We have reached the oldest news, start anew with the latest */
   621 		/* We have reached the oldest news, start anew with the latest */
   629 		ShowNewsMessage(_latest_news);
   622 		ShowNewsMessage(_latest_news);
   630 	} else {
   623 	} else {
   631 		/* 'Scrolling' through news history show each one in turn */
   624 		/* 'Scrolling' through news history show each one in turn */
   632 		ShowNewsMessage(decreaseIndex(_forced_news));
   625 		ShowNewsMessage(DecreaseIndex(_forced_news));
   633 	}
   626 	}
   634 }
   627 }
   635 
   628 
   636 
   629 
   637 /* return news by number, with 0 being the most
   630 /* return news by number, with 0 being the most
   659  * @param maxw maximum width of string in pixels
   652  * @param maxw maximum width of string in pixels
   660  */
   653  */
   661 static void DrawNewsString(int x, int y, uint16 color, const NewsItem *ni, uint maxw)
   654 static void DrawNewsString(int x, int y, uint16 color, const NewsItem *ni, uint maxw)
   662 {
   655 {
   663 	char buffer[512], buffer2[512];
   656 	char buffer[512], buffer2[512];
   664 	const char *ptr;
       
   665 	char *dest;
       
   666 	StringID str;
   657 	StringID str;
   667 
   658 
   668 	if (ni->display_mode == NM_CALLBACK) {
   659 	if (ni->display_mode == NM_CALLBACK) {
   669 		str = _get_news_string_callback[ni->callback](ni);
   660 		str = _get_news_string_callback[ni->callback](ni);
   670 	} else {
   661 	} else {
   673 	}
   664 	}
   674 
   665 
   675 	GetString(buffer, str, lastof(buffer));
   666 	GetString(buffer, str, lastof(buffer));
   676 	/* Copy the just gotten string to another buffer to remove any formatting
   667 	/* Copy the just gotten string to another buffer to remove any formatting
   677 	 * from it such as big fonts, etc. */
   668 	 * from it such as big fonts, etc. */
   678 	ptr  = buffer;
   669 	const char *ptr = buffer;
   679 	dest = buffer2;
   670 	char *dest = buffer2;
   680 	WChar c_last = '\0';
   671 	WChar c_last = '\0';
   681 	for (;;) {
   672 	for (;;) {
   682 		WChar c = Utf8Consume(&ptr);
   673 		WChar c = Utf8Consume(&ptr);
   683 		if (c == 0) break;
   674 		if (c == 0) break;
   684 		/* Make a space from a newline, but ignore multiple newlines */
   675 		/* Make a space from a newline, but ignore multiple newlines */
   701 
   692 
   702 
   693 
   703 static void MessageHistoryWndProc(Window *w, WindowEvent *e)
   694 static void MessageHistoryWndProc(Window *w, WindowEvent *e)
   704 {
   695 {
   705 	switch (e->event) {
   696 	switch (e->event) {
   706 	case WE_PAINT: {
   697 		case WE_PAINT: {
   707 		int y = 19;
   698 			int y = 19;
   708 		NewsID p, show;
   699 
   709 
   700 			SetVScrollCount(w, _total_news);
   710 		SetVScrollCount(w, _total_news);
   701 			DrawWindowWidgets(w);
   711 		DrawWindowWidgets(w);
   702 
   712 
   703 			if (_total_news == 0) break;
   713 		if (_total_news == 0) break;
   704 			NewsID show = min(_total_news, w->vscroll.cap);
   714 		show = min(_total_news, w->vscroll.cap);
   705 
   715 
   706 			for (NewsID p = w->vscroll.pos; p < w->vscroll.pos + show; p++) {
   716 		for (p = w->vscroll.pos; p < w->vscroll.pos + show; p++) {
   707 				/* get news in correct order */
   717 			/* get news in correct order */
   708 				const NewsItem *ni = &_news_items[getNews(p)];
   718 			const NewsItem *ni = &_news_items[getNews(p)];
   709 
   719 
   710 				SetDParam(0, ni->date);
   720 			SetDParam(0, ni->date);
   711 				DrawString(4, y, STR_SHORT_DATE, TC_WHITE);
   721 			DrawString(4, y, STR_SHORT_DATE, TC_WHITE);
   712 
   722 
   713 				DrawNewsString(82, y, TC_WHITE, ni, w->width - 95);
   723 			DrawNewsString(82, y, TC_WHITE, ni, w->width - 95);
   714 				y += 12;
   724 			y += 12;
   715 			}
   725 		}
       
   726 		break;
       
   727 	}
       
   728 
       
   729 	case WE_CLICK:
       
   730 		switch (e->we.click.widget) {
       
   731 		case 3: {
       
   732 			int y = (e->we.click.pt.y - 19) / 12;
       
   733 			NewsID p = getNews(y + w->vscroll.pos);
       
   734 
       
   735 			if (p == INVALID_NEWS) break;
       
   736 
       
   737 			ShowNewsMessage(p);
       
   738 			break;
   716 			break;
   739 		}
   717 		}
   740 		}
   718 
   741 		break;
   719 		case WE_CLICK:
   742 
   720 			if (e->we.click.widget == 3) {
   743 	case WE_RESIZE:
   721 				int y = (e->we.click.pt.y - 19) / 12;
   744 		w->vscroll.cap += e->we.sizing.diff.y / 12;
   722 				NewsID p = getNews(y + w->vscroll.pos);
   745 		break;
   723 
       
   724 				if (p != INVALID_NEWS) ShowNewsMessage(p);
       
   725 			}
       
   726 			break;
       
   727 
       
   728 		case WE_RESIZE:
       
   729 			w->vscroll.cap += e->we.sizing.diff.y / 12;
       
   730 			break;
   746 	}
   731 	}
   747 }
   732 }
   748 
   733 
   749 static const Widget _message_history_widgets[] = {
   734 static const Widget _message_history_widgets[] = {
   750 {   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},
   735 {   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},
   765 };
   750 };
   766 
   751 
   767 /** Display window with news messages history */
   752 /** Display window with news messages history */
   768 void ShowMessageHistory()
   753 void ShowMessageHistory()
   769 {
   754 {
   770 	Window *w;
       
   771 
       
   772 	DeleteWindowById(WC_MESSAGE_HISTORY, 0);
   755 	DeleteWindowById(WC_MESSAGE_HISTORY, 0);
   773 	w = AllocateWindowDesc(&_message_history_desc);
   756 	Window *w = AllocateWindowDesc(&_message_history_desc);
   774 
   757 
   775 	if (w != NULL) {
   758 	if (w != NULL) {
   776 		w->vscroll.cap = 10;
   759 		w->vscroll.cap = 10;
   777 		w->vscroll.count = _total_news;
   760 		w->vscroll.count = _total_news;
   778 		w->resize.step_height = 12;
   761 		w->resize.step_height = 12;
   820 	/* WP(w, def_d).data_1 stores state of the ALL on/off/summary button */
   803 	/* WP(w, def_d).data_1 stores state of the ALL on/off/summary button */
   821 	switch (e->event) {
   804 	switch (e->event) {
   822 		case WE_CREATE: {
   805 		case WE_CREATE: {
   823 			uint32 val = _news_display_opt;
   806 			uint32 val = _news_display_opt;
   824 			uint32 all_val;
   807 			uint32 all_val;
   825 			int i;
       
   826 
   808 
   827 			/* Set up the initial disabled buttons in the case of 'off' or 'full' */
   809 			/* Set up the initial disabled buttons in the case of 'off' or 'full' */
   828 			all_val = val & 0x3;
   810 			all_val = val & 0x3;
   829 			for (i = 0; i < NT_END; i++, val >>= 2) {
   811 			for (int i = 0; i < NT_END; i++, val >>= 2) {
   830 				SetMessageButtonStates(w, val & 0x3, i);
   812 				SetMessageButtonStates(w, val & 0x3, i);
   831 				/* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */
   813 				/* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */
   832 				if ((val & 0x3) != all_val) all_val = 0;
   814 				if ((val & 0x3) != all_val) all_val = 0;
   833 			}
   815 			}
   834 			/* If all values are the same value, the ALL-button will take over this value */
   816 			/* If all values are the same value, the ALL-button will take over this value */
   835 			WP(w, def_d).data_1 = all_val;
   817 			WP(w, def_d).data_1 = all_val;
   836 		} break;
   818 		} break;
   837 
   819 
   838 		case WE_PAINT: {
   820 		case WE_PAINT: {
   839 			uint32 val = _news_display_opt;
   821 			uint32 val = _news_display_opt;
   840 			int i, y;
       
   841 
   822 
   842 			if (_news_ticker_sound) w->LowerWidget(WIDGET_NEWSOPT_SOUNDTICKER);
   823 			if (_news_ticker_sound) w->LowerWidget(WIDGET_NEWSOPT_SOUNDTICKER);
   843 
   824 
   844 			w->widget[WIDGET_NEWSOPT_DROP_SUMMARY].data = message_opt[WP(w, def_d).data_1];
   825 			w->widget[WIDGET_NEWSOPT_DROP_SUMMARY].data = message_opt[WP(w, def_d).data_1];
   845 			DrawWindowWidgets(w);
   826 			DrawWindowWidgets(w);
   846 
   827 
   847 			/* Draw the string of each setting on each button. */
   828 			/* Draw the string of each setting on each button. */
   848 			for (i = 0, y = 26; i < NT_END; i++, y += 12, val >>= 2) {
   829 			for (int i = 0, y = 26; i < NT_END; i++, y += 12, val >>= 2) {
   849 				/* 51 comes from 13 + 89 (left and right of the button)+1, shiefted by one as to get division,
   830 				/* 51 comes from 13 + 89 (left and right of the button)+1, shiefted by one as to get division,
   850 				 * which will give centered position */
   831 				 * which will give centered position */
   851 				DrawStringCentered(51, y + 1, message_opt[val & 0x3], TC_BLACK);
   832 				DrawStringCentered(51, y + 1, message_opt[val & 0x3], TC_BLACK);
   852 			}
   833 			}
   853 		} break;
   834 		} break;
   873 						SetMessageButtonStates(w, val, element);
   854 						SetMessageButtonStates(w, val, element);
   874 						SetNewsDisplayValue(element, val);
   855 						SetNewsDisplayValue(element, val);
   875 						SetWindowDirty(w);
   856 						SetWindowDirty(w);
   876 					}
   857 					}
   877 				} break;
   858 				} break;
   878 			} break;
   859 			}
   879 
   860 			break;
   880 		case WE_DROPDOWN_SELECT: { // Select all settings for newsmessages
   861 
   881 			int i;
   862 		case WE_DROPDOWN_SELECT: // Select all settings for newsmessages
   882 
       
   883 			WP(w, def_d).data_1 = e->we.dropdown.index;
   863 			WP(w, def_d).data_1 = e->we.dropdown.index;
   884 
   864 
   885 			for (i = 0; i < NT_END; i++) {
   865 			for (int i = 0; i < NT_END; i++) {
   886 				SetMessageButtonStates(w, e->we.dropdown.index, i);
   866 				SetMessageButtonStates(w, e->we.dropdown.index, i);
   887 				SetNewsDisplayValue(i, e->we.dropdown.index);
   867 				SetNewsDisplayValue(i, e->we.dropdown.index);
   888 			}
   868 			}
   889 			SetWindowDirty(w);
   869 			SetWindowDirty(w);
   890 		} break;
   870 			break;
   891 	}
   871 	}
   892 }
   872 }
   893 
   873 
   894 
   874 
   895 /*
   875 /*
  1000 }
   980 }
  1001 
   981 
  1002 
   982 
  1003 void DeleteVehicleNews(VehicleID vid, StringID news)
   983 void DeleteVehicleNews(VehicleID vid, StringID news)
  1004 {
   984 {
  1005 	NewsID n;
   985 	for (NewsID n = _oldest_news; _latest_news != INVALID_NEWS; n = IncreaseIndex(n)) {
  1006 
       
  1007 	for (n = _oldest_news; _latest_news != INVALID_NEWS; n = increaseIndex(n)) {
       
  1008 		const NewsItem *ni = &_news_items[n];
   986 		const NewsItem *ni = &_news_items[n];
  1009 
   987 
  1010 		if (ni->flags & NF_VEHICLE &&
   988 		if (ni->flags & NF_VEHICLE &&
  1011 				ni->data_a == vid &&
   989 				ni->data_a == vid &&
  1012 				(news == INVALID_STRING_ID || ni->string_id == news)) {
   990 				(news == INVALID_STRING_ID || ni->string_id == news)) {
  1013 			Window *w;
       
  1014 
       
  1015 			/* If we delete a forced news and it is just before the current news
   991 			/* If we delete a forced news and it is just before the current news
  1016 			 * then we need to advance to the next news (if any) */
   992 			 * then we need to advance to the next news (if any) */
  1017 			if (_forced_news == n) MoveToNextItem();
   993 			if (_forced_news == n) MoveToNextItem();
  1018 			if (_forced_news == INVALID_NEWS && _current_news == n) MoveToNextItem();
   994 			if (_forced_news == INVALID_NEWS && _current_news == n) MoveToNextItem();
  1019 			_total_news--;
   995 			_total_news--;
  1032 			 * will become (change dramatized to make clear)
  1008 			 * will become (change dramatized to make clear)
  1033 			 * [---------O-----------L--]
  1009 			 * [---------O-----------L--]
  1034 			 * We also need an update of the current, forced and visible (open window)
  1010 			 * We also need an update of the current, forced and visible (open window)
  1035 			 * news's as this shifting could change the items they were pointing to */
  1011 			 * news's as this shifting could change the items they were pointing to */
  1036 			if (_total_news != 0) {
  1012 			if (_total_news != 0) {
  1037 				w = FindWindowById(WC_NEWS_WINDOW, 0);
  1013 				Window *w = FindWindowById(WC_NEWS_WINDOW, 0);
  1038 				NewsID visible_news = (w != NULL) ? (NewsID)(WP(w, news_d).ni - _news_items) : INVALID_NEWS;
  1014 				NewsID visible_news = (w != NULL) ? (NewsID)(WP(w, news_d).ni - _news_items) : INVALID_NEWS;
  1039 
  1015 
  1040 				for (NewsID i = n;; i = decreaseIndex(i)) {
  1016 				for (NewsID i = n;; i = DecreaseIndex(i)) {
  1041 					_news_items[i] = _news_items[decreaseIndex(i)];
  1017 					_news_items[i] = _news_items[DecreaseIndex(i)];
  1042 
  1018 
  1043 					if (i != _latest_news) {
  1019 					if (i != _latest_news) {
  1044 						if (i == _current_news) _current_news = increaseIndex(_current_news);
  1020 						if (i == _current_news) _current_news = IncreaseIndex(_current_news);
  1045 						if (i == _forced_news) _forced_news = increaseIndex(_forced_news);
  1021 						if (i == _forced_news) _forced_news = IncreaseIndex(_forced_news);
  1046 						if (i == visible_news) WP(w, news_d).ni = &_news_items[increaseIndex(visible_news)];
  1022 						if (i == visible_news) WP(w, news_d).ni = &_news_items[IncreaseIndex(visible_news)];
  1047 					}
  1023 					}
  1048 
  1024 
  1049 					if (i == _oldest_news) break;
  1025 					if (i == _oldest_news) break;
  1050 				}
  1026 				}
  1051 				_oldest_news = increaseIndex(_oldest_news);
  1027 				_oldest_news = IncreaseIndex(_oldest_news);
  1052 			}
  1028 			}
  1053 
  1029 
  1054 			/*DEBUG(misc, 0, "-cur %3d, old %2d, lat %3d, for %3d, tot %2d",
  1030 			/*DEBUG(misc, 0, "-cur %3d, old %2d, lat %3d, for %3d, tot %2d",
  1055 			  _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/
  1031 			  _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/
  1056 
  1032 
  1057 			w = FindWindowById(WC_MESSAGE_HISTORY, 0);
  1033 			Window *w = FindWindowById(WC_MESSAGE_HISTORY, 0);
  1058 			if (w != NULL) {
  1034 			if (w != NULL) {
  1059 				SetWindowDirty(w);
  1035 				SetWindowDirty(w);
  1060 				w->vscroll.count = _total_news;
  1036 				w->vscroll.count = _total_news;
  1061 			}
  1037 			}
  1062 		}
  1038 		}