news_gui.cpp

Go to the documentation of this file.
00001 /* $Id: news_gui.cpp 12868 2008-04-24 12:32:37Z rubidium $ */
00002 
00003 
00004 #include "stdafx.h"
00005 #include "openttd.h"
00006 #include "gui.h"
00007 #include "window_gui.h"
00008 #include "viewport_func.h"
00009 #include "news.h"
00010 #include "settings_type.h"
00011 #include "transparency.h"
00012 #include "strings_func.h"
00013 #include "window_func.h"
00014 #include "date_func.h"
00015 #include "vehicle_base.h"
00016 #include "sound_func.h"
00017 #include "string_func.h"
00018 #include "widgets/dropdown_func.h"
00019 
00020 #include "table/sprites.h"
00021 #include "table/strings.h"
00022 
00049 #define MAX_NEWS 30
00050 #define NB_WIDG_PER_SETTING 4
00051 
00052 typedef byte NewsID;
00053 #define INVALID_NEWS 255
00054 
00055 NewsItem _statusbar_news_item;
00056 uint32 _news_display_opt;
00057 bool _news_ticker_sound;
00058 static NewsItem _news_items[MAX_NEWS];      
00059 static NewsID _current_news = INVALID_NEWS; 
00060 static NewsID _oldest_news = 0;             
00061 static NewsID _latest_news = INVALID_NEWS;  
00062 
00067 static NewsID _forced_news = INVALID_NEWS;
00068 
00069 static byte _total_news = 0; 
00070 
00071 void DrawNewsNewVehicleAvail(Window *w);
00072 void DrawNewsBankrupcy(Window *w);
00073 static void MoveToNextItem();
00074 
00075 StringID GetNewsStringNewVehicleAvail(const NewsItem *ni);
00076 StringID GetNewsStringBankrupcy(const NewsItem *ni);
00077 
00078 static DrawNewsCallbackProc * const _draw_news_callback[] = {
00079   DrawNewsNewVehicleAvail,  
00080   DrawNewsBankrupcy,        
00081 };
00082 
00083 extern GetNewsStringCallbackProc * const _get_news_string_callback[];
00084 GetNewsStringCallbackProc * const _get_news_string_callback[] = {
00085   GetNewsStringNewVehicleAvail,  
00086   GetNewsStringBankrupcy,        
00087 };
00088 
00090 void InitNewsItemStructs()
00091 {
00092   memset(_news_items, 0, sizeof(_news_items));
00093   _current_news = INVALID_NEWS;
00094   _oldest_news = 0;
00095   _latest_news = INVALID_NEWS;
00096   _forced_news = INVALID_NEWS;
00097   _total_news = 0;
00098 }
00099 
00100 void DrawNewsBorder(const Window *w)
00101 {
00102   int left = 0;
00103   int right = w->width - 1;
00104   int top = 0;
00105   int bottom = w->height - 1;
00106 
00107   GfxFillRect(left, top, right, bottom, 0xF);
00108 
00109   GfxFillRect(left, top, left, bottom, 0xD7);
00110   GfxFillRect(right, top, right, bottom, 0xD7);
00111   GfxFillRect(left, top, right, top, 0xD7);
00112   GfxFillRect(left, bottom, right, bottom, 0xD7);
00113 
00114   DrawString(left + 2, top + 1, STR_00C6, TC_FROMSTRING);
00115 }
00116 
00117 static void NewsWindowProc(Window *w, WindowEvent *e)
00118 {
00119   switch (e->event) {
00120   case WE_CREATE: { // If chatbar is open at creation time, we need to go above it
00121     const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0);
00122     w->message.msg = (w1 != NULL) ? w1->height : 0;
00123   } break;
00124 
00125   case WE_PAINT: {
00126     const NewsItem *ni = WP(w, news_d).ni;
00127     ViewPort *vp;
00128 
00129     switch (ni->display_mode) {
00130       case NM_NORMAL:
00131       case NM_THIN: {
00132         DrawNewsBorder(w);
00133 
00134         DrawString(2, 1, STR_00C6, TC_FROMSTRING);
00135 
00136         SetDParam(0, ni->date);
00137         DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING);
00138 
00139         if (!(ni->flags & NF_VIEWPORT)) {
00140           CopyInDParam(0, ni->params, lengthof(ni->params));
00141           DrawStringMultiCenter(215, ni->display_mode == NM_NORMAL ? 76 : 56,
00142             ni->string_id, w->width - 4);
00143         } else {
00144           /* Back up transparency options to draw news view */
00145           TransparencyOptionBits to_backup = _transparency_opt;
00146           _transparency_opt = 0;
00147           DrawWindowViewport(w);
00148           _transparency_opt = to_backup;
00149 
00150           /* Shade the viewport into gray, or color*/
00151           vp = w->viewport;
00152           GfxFillRect(vp->left - w->left, vp->top - w->top,
00153             vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1,
00154             (ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE)
00155           );
00156 
00157           CopyInDParam(0, ni->params, lengthof(ni->params));
00158           DrawStringMultiCenter(w->width / 2, 20, ni->string_id, w->width - 4);
00159         }
00160         break;
00161       }
00162 
00163       case NM_CALLBACK: {
00164         _draw_news_callback[ni->callback](w);
00165         break;
00166       }
00167 
00168       default: {
00169         DrawWindowWidgets(w);
00170         if (!(ni->flags & NF_VIEWPORT)) {
00171           CopyInDParam(0, ni->params, lengthof(ni->params));
00172           DrawStringMultiCenter(140, 38, ni->string_id, 276);
00173         } else {
00174           DrawWindowViewport(w);
00175           CopyInDParam(0, ni->params, lengthof(ni->params));
00176           DrawStringMultiCenter(w->width / 2, w->height - 16, ni->string_id, w->width - 4);
00177         }
00178         break;
00179       }
00180     }
00181   } break;
00182 
00183   case WE_CLICK: {
00184     switch (e->we.click.widget) {
00185     case 1: {
00186       NewsItem *ni = WP(w, news_d).ni;
00187       DeleteWindow(w);
00188       ni->duration = 0;
00189       _forced_news = INVALID_NEWS;
00190     } break;
00191     case 0: {
00192       NewsItem *ni = WP(w, news_d).ni;
00193       if (ni->flags & NF_VEHICLE) {
00194         Vehicle *v = GetVehicle(ni->data_a);
00195         ScrollMainWindowTo(v->x_pos, v->y_pos);
00196       } else if (ni->flags & NF_TILE) {
00197         if (!ScrollMainWindowToTile(ni->data_a) && ni->data_b != 0)
00198           ScrollMainWindowToTile(ni->data_b);
00199       }
00200     } break;
00201     }
00202   } break;
00203 
00204   case WE_KEYPRESS:
00205     if (e->we.keypress.keycode == WKC_SPACE) {
00206       /* Don't continue. */
00207       e->we.keypress.cont = false;
00208       DeleteWindow(w);
00209     }
00210     break;
00211 
00212   case WE_MESSAGE: // The chatbar has notified us that is was either created or closed
00213     switch (e->we.message.msg) {
00214       case WE_CREATE: w->message.msg = e->we.message.wparam; break;
00215       case WE_DESTROY: w->message.msg = 0; break;
00216     }
00217     break;
00218 
00219   case WE_TICK: { // Scroll up newsmessages from the bottom in steps of 4 pixels
00220     int diff;
00221     int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg);
00222     if (y == w->top) return;
00223 
00224     if (w->viewport != NULL)
00225       w->viewport->top += y - w->top;
00226 
00227     diff = Delta(w->top, y);
00228     w->top = y;
00229 
00230     SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height);
00231   } break;
00232   }
00233 }
00234 
00239 static inline NewsID increaseIndex(NewsID i)
00240 {
00241   assert(i != INVALID_NEWS);
00242   return (i + 1) % MAX_NEWS;
00243 }
00244 
00249 static inline NewsID decreaseIndex(NewsID i)
00250 {
00251   assert(i != INVALID_NEWS);
00252   return (i + MAX_NEWS - 1) % MAX_NEWS;
00253 }
00254 
00281 void AddNewsItem(StringID string, uint32 flags, uint data_a, uint data_b)
00282 {
00283   NewsID l_news;
00284 
00285   if (_game_mode == GM_MENU) return;
00286 
00287   /* check the rare case that the oldest (to be overwritten) news item is open */
00288   if (_total_news == MAX_NEWS && (_oldest_news == _current_news || _oldest_news == _forced_news))
00289     MoveToNextItem();
00290 
00291   if (_total_news < MAX_NEWS) _total_news++;
00292 
00293   /* Increase _latest_news. If we have no news yet, use _oldest news as an
00294    * index. We cannot use 0 as _oldest_news can jump around due to
00295    * DeleteVehicleNews */
00296   l_news = _latest_news;
00297   _latest_news = (_latest_news == INVALID_NEWS) ? _oldest_news : increaseIndex(_latest_news);
00298 
00299   /* If the fifo-buffer is full, overwrite the oldest entry */
00300   if (l_news != INVALID_NEWS && _latest_news == _oldest_news) {
00301     assert(_total_news == MAX_NEWS);
00302     _oldest_news = increaseIndex(_oldest_news);
00303   }
00304 
00305   /*DEBUG(misc, 0, "+cur %3d, old %2d, lat %3d, for %3d, tot %2d",
00306     _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/
00307 
00308   /* Add news to _latest_news */
00309   {
00310     Window *w;
00311     NewsItem *ni = &_news_items[_latest_news];
00312     memset(ni, 0, sizeof(*ni));
00313 
00314     ni->string_id = string;
00315     ni->display_mode = (byte)flags;
00316     ni->flags = (byte)(flags >> 8);
00317 
00318     /* show this news message in color? */
00319     if (_cur_year >= _patches.colored_news_year) ni->flags |= NF_INCOLOR;
00320 
00321     ni->type = (byte)(flags >> 16);
00322     ni->callback = (byte)(flags >> 24);
00323     ni->data_a = data_a;
00324     ni->data_b = data_b;
00325     ni->date = _date;
00326     CopyOutDParam(ni->params, 0, lengthof(ni->params));
00327 
00328     w = FindWindowById(WC_MESSAGE_HISTORY, 0);
00329     if (w == NULL) return;
00330     SetWindowDirty(w);
00331     w->vscroll.count = _total_news;
00332   }
00333 }
00334 
00335 
00341 static const byte _news_items_age[NT_END] = {
00342   60,  
00343   60,  
00344   90,  
00345   60,  
00346   90,  
00347   30,  
00348   30,  
00349   30,  
00350   30,  
00351   150, 
00352   30,  
00353   90,  
00354   180, 
00355   60   
00356 };
00357 
00358 
00359 static const Widget _news_type13_widgets[] = {
00360 {      WWT_PANEL,   RESIZE_NONE,    15,     0,   429,     0,   169, 0x0, STR_NULL},
00361 {      WWT_PANEL,   RESIZE_NONE,    15,     0,    10,     0,    11, 0x0, STR_NULL},
00362 {   WIDGETS_END},
00363 };
00364 
00365 static WindowDesc _news_type13_desc = {
00366   WDP_CENTER, 476, 430, 170, 430, 170,
00367   WC_NEWS_WINDOW, WC_NONE,
00368   WDF_DEF_WIDGET,
00369   _news_type13_widgets,
00370   NewsWindowProc
00371 };
00372 
00373 static const Widget _news_type2_widgets[] = {
00374 {      WWT_PANEL,   RESIZE_NONE,    15,     0,   429,     0,   129, 0x0, STR_NULL},
00375 {      WWT_PANEL,   RESIZE_NONE,    15,     0,    10,     0,    11, 0x0, STR_NULL},
00376 {   WIDGETS_END},
00377 };
00378 
00379 static WindowDesc _news_type2_desc = {
00380   WDP_CENTER, 476, 430, 130, 430, 130,
00381   WC_NEWS_WINDOW, WC_NONE,
00382   WDF_DEF_WIDGET,
00383   _news_type2_widgets,
00384   NewsWindowProc
00385 };
00386 
00387 static const Widget _news_type0_widgets[] = {
00388 {      WWT_PANEL,   RESIZE_NONE,     5,     0,   279,    14,    86, 0x0,              STR_NULL},
00389 {   WWT_CLOSEBOX,   RESIZE_NONE,     5,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
00390 {    WWT_CAPTION,   RESIZE_NONE,     5,    11,   279,     0,    13, STR_012C_MESSAGE, STR_NULL},
00391 {      WWT_INSET,   RESIZE_NONE,     5,     2,   277,    16,    64, 0x0,              STR_NULL},
00392 {   WIDGETS_END},
00393 };
00394 
00395 static WindowDesc _news_type0_desc = {
00396   WDP_CENTER, 476, 280, 87, 280, 87,
00397   WC_NEWS_WINDOW, WC_NONE,
00398   WDF_DEF_WIDGET,
00399   _news_type0_widgets,
00400   NewsWindowProc
00401 };
00402 
00403 static const SoundFx _news_sounds[NT_END] = {
00404   SND_1D_APPLAUSE,  
00405   SND_1D_APPLAUSE,  
00406   SND_BEGIN,    
00407   SND_BEGIN,    
00408   SND_BEGIN,    
00409   SND_BEGIN,    
00410   SND_BEGIN,    
00411   SND_BEGIN,    
00412   SND_BEGIN,    
00413   SND_BEGIN,    
00414   SND_1E_OOOOH,   
00415   SND_BEGIN,    
00416   SND_BEGIN,    
00417   SND_BEGIN,    
00418 };
00419 
00420 const char *_news_display_name[NT_END] = {
00421   "arrival_player",
00422   "arrival_other",
00423   "accident",
00424   "company_info",
00425   "openclose",
00426   "economy",
00427   "production_player",
00428   "production_other",
00429   "production_nobody",
00430   "advice",
00431   "new_vehicles",
00432   "acceptance",
00433   "subsidies",
00434   "general",
00435 };
00436 
00443 static inline byte GetNewsDisplayValue(byte item)
00444 {
00445   assert(item < NT_END && GB(_news_display_opt, item * 2, 2) <= 2);
00446   return GB(_news_display_opt, item * 2, 2);
00447 }
00448 
00455 static inline void SetNewsDisplayValue(byte item, byte val)
00456 {
00457   assert(item < NT_END && val <= 2);
00458   SB(_news_display_opt, item * 2, 2, val);
00459 }
00460 
00462 static void ShowNewspaper(NewsItem *ni)
00463 {
00464   Window *w;
00465   SoundFx sound;
00466   int top;
00467   ni->flags &= ~NF_FORCE_BIG;
00468   ni->duration = 555;
00469 
00470   sound = _news_sounds[ni->type];
00471   if (sound != 0) SndPlayFx(sound);
00472 
00473   top = _screen.height;
00474   switch (ni->display_mode) {
00475     case NM_NORMAL:
00476     case NM_CALLBACK: {
00477       _news_type13_desc.top = top;
00478       w = AllocateWindowDesc(&_news_type13_desc);
00479       if (ni->flags & NF_VIEWPORT)
00480         AssignWindowViewport(w, 2, 58, 0x1AA, 0x6E,
00481           ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
00482       break;
00483     }
00484 
00485     case NM_THIN: {
00486       _news_type2_desc.top = top;
00487       w = AllocateWindowDesc(&_news_type2_desc);
00488       if (ni->flags & NF_VIEWPORT)
00489         AssignWindowViewport(w, 2, 58, 0x1AA, 0x46,
00490           ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
00491       break;
00492     }
00493 
00494     default: {
00495       _news_type0_desc.top = top;
00496       w = AllocateWindowDesc(&_news_type0_desc);
00497       if (ni->flags & NF_VIEWPORT)
00498         AssignWindowViewport(w, 3, 17, 0x112, 0x2F,
00499           ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
00500       break;
00501     }
00502   }
00503 
00504   /*DEBUG(misc, 0, " cur %3d, old %2d, lat %3d, for %3d, tot %2d",
00505     _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/
00506 
00507   WP(w, news_d).ni = &_news_items[_forced_news == INVALID_NEWS ? _current_news : _forced_news];
00508   w->flags4 |= WF_DISABLE_VP_SCROLL;
00509 }
00510 
00512 static void ShowTicker(const NewsItem *ni)
00513 {
00514   Window *w;
00515 
00516   if (_news_ticker_sound) SndPlayFx(SND_16_MORSE);
00517 
00518   _statusbar_news_item = *ni;
00519   w = FindWindowById(WC_STATUS_BAR, 0);
00520   if (w != NULL) WP(w, def_d).data_1 = 360;
00521 }
00522 
00523 
00528 static bool ReadyForNextItem()
00529 {
00530   const Window *w;
00531   NewsID item = (_forced_news == INVALID_NEWS) ? _current_news : _forced_news;
00532   NewsItem *ni;
00533 
00534   if (item >= MAX_NEWS) return true;
00535   ni = &_news_items[item];
00536 
00537   /* Ticker message
00538    * Check if the status bar message is still being displayed? */
00539   w = FindWindowById(WC_STATUS_BAR, 0);
00540   if (w != NULL && WP(w, const def_d).data_1 > -1280) return false;
00541 
00542   /* Newspaper message, decrement duration counter */
00543   if (ni->duration != 0) ni->duration--;
00544 
00545   /* neither newsticker nor newspaper are running */
00546   return (ni->duration == 0 || FindWindowById(WC_NEWS_WINDOW, 0) == NULL);
00547 }
00548 
00550 static void MoveToNextItem()
00551 {
00552   DeleteWindowById(WC_NEWS_WINDOW, 0);
00553   _forced_news = INVALID_NEWS;
00554 
00555   /* if we're not at the last item, then move on */
00556   if (_current_news != _latest_news) {
00557     NewsItem *ni;
00558 
00559     _current_news = (_current_news == INVALID_NEWS) ? _oldest_news : increaseIndex(_current_news);
00560     ni = &_news_items[_current_news];
00561 
00562     /* check the date, don't show too old items */
00563     if (_date - _news_items_age[ni->type] > ni->date) return;
00564 
00565     switch (GetNewsDisplayValue(ni->type)) {
00566       default: NOT_REACHED();
00567       case 0: { // Off - show nothing only a small reminder in the status bar
00568         Window *w = FindWindowById(WC_STATUS_BAR, 0);
00569 
00570         if (w != NULL) {
00571           WP(w, def_d).data_2 = 91;
00572           SetWindowDirty(w);
00573         }
00574         break;
00575       }
00576 
00577       case 1: // Summary - show ticker, but if forced big, cascade to full
00578         if (!(ni->flags & NF_FORCE_BIG)) {
00579           ShowTicker(ni);
00580           break;
00581         }
00582         /* Fallthrough */
00583 
00584       case 2: // Full - show newspaper
00585         ShowNewspaper(ni);
00586         break;
00587     }
00588   }
00589 }
00590 
00591 void NewsLoop()
00592 {
00593   /* no news item yet */
00594   if (_total_news == 0) return;
00595 
00596   if (ReadyForNextItem()) MoveToNextItem();
00597 }
00598 
00600 static void ShowNewsMessage(NewsID i)
00601 {
00602   if (_total_news == 0) return;
00603 
00604   /* Delete the news window */
00605   DeleteWindowById(WC_NEWS_WINDOW, 0);
00606 
00607   /* setup forced news item */
00608   _forced_news = i;
00609 
00610   if (_forced_news != INVALID_NEWS) {
00611     NewsItem *ni = &_news_items[_forced_news];
00612     ni->duration = 555;
00613     ni->flags |= NF_FORCE_BIG;
00614     DeleteWindowById(WC_NEWS_WINDOW, 0);
00615     ShowNewspaper(ni);
00616   }
00617 }
00618 
00620 void ShowLastNewsMessage()
00621 {
00622   if (_forced_news == INVALID_NEWS) {
00623     /* Not forced any news yet, show the current one, unless a news window is
00624      * open (which can only be the current one), then show the previous item */
00625     const Window *w = FindWindowById(WC_NEWS_WINDOW, 0);
00626     ShowNewsMessage((w == NULL || (_current_news == _oldest_news)) ? _current_news : decreaseIndex(_current_news));
00627   } else if (_forced_news == _oldest_news) {
00628     /* We have reached the oldest news, start anew with the latest */
00629     ShowNewsMessage(_latest_news);
00630   } else {
00631     /* 'Scrolling' through news history show each one in turn */
00632     ShowNewsMessage(decreaseIndex(_forced_news));
00633   }
00634 }
00635 
00636 
00637 /* return news by number, with 0 being the most
00638  * recent news. Returns INVALID_NEWS if end of queue reached. */
00639 static NewsID getNews(NewsID i)
00640 {
00641   if (i >= _total_news) return INVALID_NEWS;
00642 
00643   if (_latest_news < i) {
00644     i = _latest_news + MAX_NEWS - i;
00645   } else {
00646     i = _latest_news - i;
00647   }
00648 
00649   i %= MAX_NEWS;
00650   return i;
00651 }
00652 
00661 static void DrawNewsString(int x, int y, uint16 color, const NewsItem *ni, uint maxw)
00662 {
00663   char buffer[512], buffer2[512];
00664   const char *ptr;
00665   char *dest;
00666   StringID str;
00667 
00668   if (ni->display_mode == NM_CALLBACK) {
00669     str = _get_news_string_callback[ni->callback](ni);
00670   } else {
00671     CopyInDParam(0, ni->params, lengthof(ni->params));
00672     str = ni->string_id;
00673   }
00674 
00675   GetString(buffer, str, lastof(buffer));
00676   /* Copy the just gotten string to another buffer to remove any formatting
00677    * from it such as big fonts, etc. */
00678   ptr  = buffer;
00679   dest = buffer2;
00680   WChar c_last = '\0';
00681   for (;;) {
00682     WChar c = Utf8Consume(&ptr);
00683     if (c == 0) break;
00684     /* Make a space from a newline, but ignore multiple newlines */
00685     if (c == '\n' && c_last != '\n') {
00686       dest[0] = ' ';
00687       dest++;
00688     } else if (c == '\r') {
00689       dest[0] = dest[1] = dest[2] = dest[3] = ' ';
00690       dest += 4;
00691     } else if (IsPrintable(c)) {
00692       dest += Utf8Encode(dest, c);
00693     }
00694     c_last = c;
00695   }
00696 
00697   *dest = '\0';
00698   /* Truncate and show string; postfixed by '...' if neccessary */
00699   DoDrawStringTruncated(buffer2, x, y, color, maxw);
00700 }
00701 
00702 
00703 static void MessageHistoryWndProc(Window *w, WindowEvent *e)
00704 {
00705   switch (e->event) {
00706   case WE_PAINT: {
00707     int y = 19;
00708     NewsID p, show;
00709 
00710     SetVScrollCount(w, _total_news);
00711     DrawWindowWidgets(w);
00712 
00713     if (_total_news == 0) break;
00714     show = min(_total_news, w->vscroll.cap);
00715 
00716     for (p = w->vscroll.pos; p < w->vscroll.pos + show; p++) {
00717       /* get news in correct order */
00718       const NewsItem *ni = &_news_items[getNews(p)];
00719 
00720       SetDParam(0, ni->date);
00721       DrawString(4, y, STR_SHORT_DATE, TC_WHITE);
00722 
00723       DrawNewsString(82, y, TC_WHITE, ni, w->width - 95);
00724       y += 12;
00725     }
00726     break;
00727   }
00728 
00729   case WE_CLICK:
00730     switch (e->we.click.widget) {
00731     case 3: {
00732       int y = (e->we.click.pt.y - 19) / 12;
00733       NewsID p = getNews(y + w->vscroll.pos);
00734 
00735       if (p == INVALID_NEWS) break;
00736 
00737       ShowNewsMessage(p);
00738       break;
00739     }
00740     }
00741     break;
00742 
00743   case WE_RESIZE:
00744     w->vscroll.cap += e->we.sizing.diff.y / 12;
00745     break;
00746   }
00747 }
00748 
00749 static const Widget _message_history_widgets[] = {
00750 {   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},
00751 {    WWT_CAPTION,  RESIZE_RIGHT,    13,    11,   387,     0,    13, STR_MESSAGE_HISTORY, STR_018C_WINDOW_TITLE_DRAG_THIS},
00752 {  WWT_STICKYBOX,     RESIZE_LR,    13,   388,   399,     0,    13, 0x0,                 STR_STICKY_BUTTON},
00753 {      WWT_PANEL,     RESIZE_RB,    13,     0,   387,    14,   139, 0x0,                 STR_MESSAGE_HISTORY_TIP},
00754 {  WWT_SCROLLBAR,    RESIZE_LRB,    13,   388,   399,    14,   127, 0x0,                 STR_0190_SCROLL_BAR_SCROLLS_LIST},
00755 {  WWT_RESIZEBOX,   RESIZE_LRTB,    13,   388,   399,   128,   139, 0x0,                 STR_RESIZE_BUTTON},
00756 {   WIDGETS_END},
00757 };
00758 
00759 static const WindowDesc _message_history_desc = {
00760   240, 22, 400, 140, 400, 140,
00761   WC_MESSAGE_HISTORY, WC_NONE,
00762   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00763   _message_history_widgets,
00764   MessageHistoryWndProc
00765 };
00766 
00768 void ShowMessageHistory()
00769 {
00770   Window *w;
00771 
00772   DeleteWindowById(WC_MESSAGE_HISTORY, 0);
00773   w = AllocateWindowDesc(&_message_history_desc);
00774 
00775   if (w != NULL) {
00776     w->vscroll.cap = 10;
00777     w->vscroll.count = _total_news;
00778     w->resize.step_height = 12;
00779     w->resize.height = w->height - 12 * 6; // minimum of 4 items in the list, each item 12 high
00780     w->resize.step_width = 1;
00781     w->resize.width = 200; // can't make window any smaller than 200 pixel
00782     SetWindowDirty(w);
00783   }
00784 }
00785 
00786 
00788 enum {
00789   WIDGET_NEWSOPT_DROP_SUMMARY = 4,  
00790   WIDGET_NEWSOPT_SOUNDTICKER  = 6,  
00791   WIDGET_NEWSOPT_START_OPTION = 8,  
00792 };
00793 
00803 static void SetMessageButtonStates(Window *w, byte value, int element)
00804 {
00805   element *= NB_WIDG_PER_SETTING;
00806 
00807   w->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION, value == 0);
00808   w->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION + 2, value == 2);
00809 }
00810 
00816 static void MessageOptionsWndProc(Window *w, WindowEvent *e)
00817 {
00818   static const StringID message_opt[] = {STR_OFF, STR_SUMMARY, STR_FULL, INVALID_STRING_ID};
00819 
00820   /* WP(w, def_d).data_1 stores state of the ALL on/off/summary button */
00821   switch (e->event) {
00822     case WE_CREATE: {
00823       uint32 val = _news_display_opt;
00824       uint32 all_val;
00825       int i;
00826 
00827       /* Set up the initial disabled buttons in the case of 'off' or 'full' */
00828       all_val = val & 0x3;
00829       for (i = 0; i < NT_END; i++, val >>= 2) {
00830         SetMessageButtonStates(w, val & 0x3, i);
00831         /* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */
00832         if ((val & 0x3) != all_val) all_val = 0;
00833       }
00834       /* If all values are the same value, the ALL-button will take over this value */
00835       WP(w, def_d).data_1 = all_val;
00836     } break;
00837 
00838     case WE_PAINT: {
00839       uint32 val = _news_display_opt;
00840       int i, y;
00841 
00842       if (_news_ticker_sound) w->LowerWidget(WIDGET_NEWSOPT_SOUNDTICKER);
00843 
00844       w->widget[WIDGET_NEWSOPT_DROP_SUMMARY].data = message_opt[WP(w, def_d).data_1];
00845       DrawWindowWidgets(w);
00846 
00847       /* Draw the string of each setting on each button. */
00848       for (i = 0, y = 26; i < NT_END; i++, y += 12, val >>= 2) {
00849         /* 51 comes from 13 + 89 (left and right of the button)+1, shiefted by one as to get division,
00850          * which will give centered position */
00851         DrawStringCentered(51, y + 1, message_opt[val & 0x3], TC_BLACK);
00852       }
00853     } break;
00854 
00855     case WE_CLICK:
00856       switch (e->we.click.widget) {
00857         case WIDGET_NEWSOPT_DROP_SUMMARY: // Dropdown menu for all settings
00858           ShowDropDownMenu(w, message_opt, WP(w, def_d).data_1, WIDGET_NEWSOPT_DROP_SUMMARY, 0, 0);
00859           break;
00860 
00861         case WIDGET_NEWSOPT_SOUNDTICKER: // Change ticker sound on/off
00862           _news_ticker_sound ^= 1;
00863           w->ToggleWidgetLoweredState(e->we.click.widget);
00864           w->InvalidateWidget(e->we.click.widget);
00865           break;
00866 
00867         default: { // Clicked on the [<] .. [>] widgets
00868           int wid = e->we.click.widget - WIDGET_NEWSOPT_START_OPTION;
00869           if (wid >= 0 && wid < (NB_WIDG_PER_SETTING * NT_END)) {
00870             int element = wid / NB_WIDG_PER_SETTING;
00871             byte val = (GetNewsDisplayValue(element) + ((wid % NB_WIDG_PER_SETTING) ? 1 : -1)) % 3;
00872 
00873             SetMessageButtonStates(w, val, element);
00874             SetNewsDisplayValue(element, val);
00875             SetWindowDirty(w);
00876           }
00877         } break;
00878       } break;
00879 
00880     case WE_DROPDOWN_SELECT: { // Select all settings for newsmessages
00881       int i;
00882 
00883       WP(w, def_d).data_1 = e->we.dropdown.index;
00884 
00885       for (i = 0; i < NT_END; i++) {
00886         SetMessageButtonStates(w, e->we.dropdown.index, i);
00887         SetNewsDisplayValue(i, e->we.dropdown.index);
00888       }
00889       SetWindowDirty(w);
00890     } break;
00891   }
00892 }
00893 
00894 
00895 /*
00896 * The news settings window widgets
00897 *
00898 * Main part of the window is a list of news-setting lines, one for each news category.
00899 * Each line is constructed by an expansion of the \c NEWS_SETTINGS_LINE macro
00900 */
00901 
00918 #define NEWS_SETTINGS_LINE(basey, linenum, text) \
00919   { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \
00920       4,  12,  basey     + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \
00921     SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, \
00922   { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_YELLOW, \
00923      13,  89,  basey     + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \
00924     STR_EMPTY, STR_NULL}, \
00925   { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \
00926      90,  98,  basey     + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \
00927     SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, \
00928         { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, \
00929     103, 409,  basey + 1 + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 13 + linenum * NEWS_SETTING_BASELINE_SKIP, \
00930     text, STR_NULL}
00931 
00932 static const int NEWS_SETTING_BASELINE_SKIP = 12; 
00933 
00934 
00935 static const Widget _message_options_widgets[] = {
00936 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_BROWN,   0,  10,  0, 13,
00937   STR_00C5,                 STR_018B_CLOSE_WINDOW},
00938 {  WWT_CAPTION, RESIZE_NONE, COLOUR_BROWN,  11, 409,  0, 13,
00939   STR_0204_MESSAGE_OPTIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
00940 {    WWT_PANEL, RESIZE_NONE, COLOUR_BROWN,   0, 409, 14, 64 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00941   0x0,                      STR_NULL},
00942 
00943 /* Text at the top of the main panel, in black */
00944 {    WWT_LABEL, RESIZE_NONE, COLOUR_BROWN,
00945     0, 409, 13, 26,
00946   STR_0205_MESSAGE_TYPES,   STR_NULL},
00947 
00948 /* General drop down and sound button, widgets WIDGET_NEWSOPT_BTN_SUMMARY and WIDGET_NEWSOPT_DROP_SUMMARY */
00949 {  WWT_DROPDOWN, RESIZE_NONE, COLOUR_YELLOW,
00950     4,  98,  34 + NT_END * NEWS_SETTING_BASELINE_SKIP,  45 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00951   0x0, STR_NULL},
00952 
00953 {      WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW,
00954   103, 409,  35 + NT_END * NEWS_SETTING_BASELINE_SKIP,  47 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00955   STR_MESSAGES_ALL, STR_NULL},
00956 
00957 /* Below is widget WIDGET_NEWSOPT_SOUNDTICKER */
00958 { WWT_TEXTBTN_2, RESIZE_NONE, COLOUR_YELLOW,
00959     4,  98,  46 + NT_END * NEWS_SETTING_BASELINE_SKIP,  57 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00960   STR_02DB_OFF,  STR_NULL},
00961 
00962 {      WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW,
00963   103, 409,  47 + NT_END * NEWS_SETTING_BASELINE_SKIP,  59 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00964   STR_MESSAGE_SOUND, STR_NULL},
00965 
00966 /* List of news-setting lines (4 widgets for each line).
00967  * First widget must be number WIDGET_NEWSOPT_START_OPTION
00968  */
00969 NEWS_SETTINGS_LINE(26, NT_ARRIVAL_PLAYER, STR_0206_ARRIVAL_OF_FIRST_VEHICLE),
00970 NEWS_SETTINGS_LINE(26, NT_ARRIVAL_OTHER,  STR_0207_ARRIVAL_OF_FIRST_VEHICLE),
00971 NEWS_SETTINGS_LINE(26, NT_ACCIDENT, STR_0208_ACCIDENTS_DISASTERS),
00972 NEWS_SETTINGS_LINE(26, NT_COMPANY_INFO, STR_0209_COMPANY_INFORMATION),
00973 NEWS_SETTINGS_LINE(26, NT_OPENCLOSE, STR_NEWS_OPEN_CLOSE),
00974 NEWS_SETTINGS_LINE(26, NT_ECONOMY, STR_020A_ECONOMY_CHANGES),
00975 NEWS_SETTINGS_LINE(26, NT_INDUSTRY_PLAYER, STR_INDUSTRY_CHANGES_SERVED_BY_PLAYER),
00976 NEWS_SETTINGS_LINE(26, NT_INDUSTRY_OTHER, STR_INDUSTRY_CHANGES_SERVED_BY_OTHER),
00977 NEWS_SETTINGS_LINE(26, NT_INDUSTRY_NOBODY, STR_OTHER_INDUSTRY_PRODUCTION_CHANGES),
00978 NEWS_SETTINGS_LINE(26, NT_ADVICE, STR_020B_ADVICE_INFORMATION_ON_PLAYER),
00979 NEWS_SETTINGS_LINE(26, NT_NEW_VEHICLES, STR_020C_NEW_VEHICLES),
00980 NEWS_SETTINGS_LINE(26, NT_ACCEPTANCE, STR_020D_CHANGES_OF_CARGO_ACCEPTANCE),
00981 NEWS_SETTINGS_LINE(26, NT_SUBSIDIES, STR_020E_SUBSIDIES),
00982 NEWS_SETTINGS_LINE(26, NT_GENERAL, STR_020F_GENERAL_INFORMATION),
00983 
00984 {   WIDGETS_END},
00985 };
00986 
00987 static const WindowDesc _message_options_desc = {
00988   270,  22,  410,  65 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00989              410,  65 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00990   WC_GAME_OPTIONS, WC_NONE,
00991   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
00992   _message_options_widgets,
00993   MessageOptionsWndProc
00994 };
00995 
00996 void ShowMessageOptions()
00997 {
00998   DeleteWindowById(WC_GAME_OPTIONS, 0);
00999   AllocateWindowDesc(&_message_options_desc);
01000 }
01001 
01002 
01003 void DeleteVehicleNews(VehicleID vid, StringID news)
01004 {
01005   NewsID n;
01006 
01007   for (n = _oldest_news; _latest_news != INVALID_NEWS; n = increaseIndex(n)) {
01008     const NewsItem *ni = &_news_items[n];
01009 
01010     if (ni->flags & NF_VEHICLE &&
01011         ni->data_a == vid &&
01012         (news == INVALID_STRING_ID || ni->string_id == news)) {
01013       Window *w;
01014 
01015       /* If we delete a forced news and it is just before the current news
01016        * then we need to advance to the next news (if any) */
01017       if (_forced_news == n) MoveToNextItem();
01018       if (_forced_news == INVALID_NEWS && _current_news == n) MoveToNextItem();
01019       _total_news--;
01020 
01021       /* If this is the last news item, invalidate _latest_news */
01022       if (_total_news == 0) {
01023         assert(_latest_news == _oldest_news);
01024         _latest_news = INVALID_NEWS;
01025         _current_news = INVALID_NEWS;
01026       }
01027 
01028       /* Since we only imitate a FIFO removing an arbitrary element does need
01029        * some magic. Remove the item by shifting head towards the tail. eg
01030        *    oldest    remove  last
01031        *        |        |     |
01032        * [------O--------n-----L--]
01033        * will become (change dramatized to make clear)
01034        * [---------O-----------L--]
01035        * We also need an update of the current, forced and visible (open window)
01036        * news's as this shifting could change the items they were pointing to */
01037       if (_total_news != 0) {
01038         w = FindWindowById(WC_NEWS_WINDOW, 0);
01039         NewsID visible_news = (w != NULL) ? (NewsID)(WP(w, news_d).ni - _news_items) : INVALID_NEWS;
01040 
01041         for (NewsID i = n;; i = decreaseIndex(i)) {
01042           _news_items[i] = _news_items[decreaseIndex(i)];
01043 
01044           if (i != _latest_news) {
01045             if (i == _current_news) _current_news = increaseIndex(_current_news);
01046             if (i == _forced_news) _forced_news = increaseIndex(_forced_news);
01047             if (i == visible_news) WP(w, news_d).ni = &_news_items[increaseIndex(visible_news)];
01048           }
01049 
01050           if (i == _oldest_news) break;
01051         }
01052         _oldest_news = increaseIndex(_oldest_news);
01053       }
01054 
01055       /*DEBUG(misc, 0, "-cur %3d, old %2d, lat %3d, for %3d, tot %2d",
01056         _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/
01057 
01058       w = FindWindowById(WC_MESSAGE_HISTORY, 0);
01059       if (w != NULL) {
01060         SetWindowDirty(w);
01061         w->vscroll.count = _total_news;
01062       }
01063     }
01064 
01065     if (n == _latest_news) break;
01066   }
01067 }

Generated on Mon Sep 22 20:34:17 2008 for openttd by  doxygen 1.5.6