news_gui.cpp

Go to the documentation of this file.
00001 /* $Id: news_gui.cpp 15725 2009-03-15 15:25:18Z smatz $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "gui.h"
00008 #include "window_gui.h"
00009 #include "viewport_func.h"
00010 #include "news_type.h"
00011 #include "settings_type.h"
00012 #include "transparency.h"
00013 #include "strings_func.h"
00014 #include "window_func.h"
00015 #include "date_func.h"
00016 #include "vehicle_base.h"
00017 #include "sound_func.h"
00018 #include "string_func.h"
00019 #include "widgets/dropdown_func.h"
00020 #include "statusbar_gui.h"
00021 #include "company_manager_face.h"
00022 
00023 #include "table/strings.h"
00024 
00025 #define NB_WIDG_PER_SETTING 4
00026 
00027 NewsItem _statusbar_news_item;
00028 bool _news_ticker_sound;
00029 
00030 static uint MIN_NEWS_AMOUNT = 30;           
00031 static uint _total_news = 0;                
00032 static NewsItem *_oldest_news = NULL;       
00033 static NewsItem *_latest_news = NULL;       
00034 
00039 static NewsItem *_forced_news = NULL;       
00040 
00042 static NewsItem *_current_news = NULL;
00043 
00044 
00045 typedef void DrawNewsCallbackProc(struct Window *w, const NewsItem *ni);
00046 void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni);
00047 
00048 static void DrawNewsBankrupcy(Window *w, const NewsItem *ni)
00049 {
00050   const CompanyNewsInformation *cni = (const CompanyNewsInformation*)ni->free_data;
00051 
00052   DrawCompanyManagerFace(cni->face, cni->colour, 2, 23);
00053   GfxFillRect(3, 23, 3 + 91, 23 + 118, PALETTE_TO_STRUCT_GREY, FILLRECT_RECOLOUR);
00054 
00055   SetDParamStr(0, cni->president_name);
00056   DrawStringMultiCenter(49, 148, STR_JUST_RAW_STRING, 94);
00057 
00058   switch (ni->subtype) {
00059     case NS_COMPANY_TROUBLE:
00060       DrawStringCentered(w->width >> 1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, TC_FROMSTRING);
00061 
00062       SetDParam(0, ni->params[2]);
00063 
00064       DrawStringMultiCenter(
00065         ((w->width - 101) >> 1) + 98,
00066         90,
00067         STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED,
00068         w->width - 101);
00069       break;
00070 
00071     case NS_COMPANY_MERGER:
00072       DrawStringCentered(w->width >> 1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, TC_FROMSTRING);
00073       SetDParam(0, ni->params[2]);
00074       SetDParam(1, ni->params[3]);
00075       SetDParam(2, ni->params[4]);
00076       DrawStringMultiCenter(
00077         ((w->width - 101) >> 1) + 98,
00078         90,
00079         ni->params[4] == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR,
00080         w->width - 101);
00081       break;
00082 
00083     case NS_COMPANY_BANKRUPT:
00084       DrawStringCentered(w->width >> 1, 1, STR_705C_BANKRUPT, TC_FROMSTRING);
00085       SetDParam(0, ni->params[2]);
00086       DrawStringMultiCenter(
00087         ((w->width - 101) >> 1) + 98,
00088         90,
00089         STR_705D_HAS_BEEN_CLOSED_DOWN_BY,
00090         w->width - 101);
00091       break;
00092 
00093     case NS_COMPANY_NEW:
00094       DrawStringCentered(w->width >> 1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, TC_FROMSTRING);
00095       SetDParam(0, ni->params[2]);
00096       SetDParam(1, ni->params[3]);
00097       DrawStringMultiCenter(
00098         ((w->width - 101) >> 1) + 98,
00099         90,
00100         STR_705F_STARTS_CONSTRUCTION_NEAR,
00101         w->width - 101);
00102       break;
00103 
00104     default:
00105       NOT_REACHED();
00106   }
00107 }
00108 
00109 
00113 struct NewsSubtypeData {
00114   NewsType type;         
00115   NewsMode display_mode; 
00116   NewsFlag flags;        
00117   DrawNewsCallbackProc *callback; 
00118 };
00119 
00123 static const struct NewsSubtypeData _news_subtype_data[NS_END] = {
00124   /* type,               display_mode, flags,                  callback */
00125   { NT_ARRIVAL_COMPANY,  NM_THIN,     NF_VIEWPORT|NF_VEHICLE, NULL                    }, 
00126   { NT_ARRIVAL_OTHER,    NM_THIN,     NF_VIEWPORT|NF_VEHICLE, NULL                    }, 
00127   { NT_ACCIDENT,         NM_THIN,     NF_VIEWPORT|NF_TILE,    NULL                    }, 
00128   { NT_ACCIDENT,         NM_THIN,     NF_VIEWPORT|NF_VEHICLE, NULL                    }, 
00129   { NT_COMPANY_INFO,     NM_NORMAL,   NF_NONE,                DrawNewsBankrupcy       }, 
00130   { NT_COMPANY_INFO,     NM_NORMAL,   NF_NONE,                DrawNewsBankrupcy       }, 
00131   { NT_COMPANY_INFO,     NM_NORMAL,   NF_NONE,                DrawNewsBankrupcy       }, 
00132   { NT_COMPANY_INFO,     NM_NORMAL,   NF_TILE,                DrawNewsBankrupcy       }, 
00133   { NT_INDUSTRY_OPEN,    NM_THIN,     NF_VIEWPORT|NF_TILE,    NULL                    }, 
00134   { NT_INDUSTRY_CLOSE,   NM_THIN,     NF_VIEWPORT|NF_TILE,    NULL                    }, 
00135   { NT_ECONOMY,          NM_NORMAL,   NF_NONE,                NULL                    }, 
00136   { NT_INDUSTRY_COMPANY, NM_THIN,     NF_VIEWPORT|NF_TILE,    NULL                    }, 
00137   { NT_INDUSTRY_OTHER,   NM_THIN,     NF_VIEWPORT|NF_TILE,    NULL                    }, 
00138   { NT_INDUSTRY_NOBODY,  NM_THIN,     NF_VIEWPORT|NF_TILE,    NULL                    }, 
00139   { NT_ADVICE,           NM_SMALL,    NF_VIEWPORT|NF_VEHICLE, NULL                    }, 
00140   { NT_NEW_VEHICLES,     NM_NORMAL,   NF_NONE,                DrawNewsNewVehicleAvail }, 
00141   { NT_ACCEPTANCE,       NM_SMALL,    NF_VIEWPORT|NF_TILE,    NULL                    }, 
00142   { NT_SUBSIDIES,        NM_NORMAL,   NF_TILE|NF_TILE2,       NULL                    }, 
00143   { NT_GENERAL,          NM_NORMAL,   NF_TILE,                NULL                    }, 
00144 };
00145 
00149 NewsTypeData _news_type_data[NT_END] = {
00150   /* name,              age, sound,           display */
00151   { "arrival_player",    60, SND_1D_APPLAUSE, ND_FULL },  
00152   { "arrival_other",     60, SND_1D_APPLAUSE, ND_FULL },  
00153   { "accident",          90, SND_BEGIN,       ND_FULL },  
00154   { "company_info",      60, SND_BEGIN,       ND_FULL },  
00155   { "open",              90, SND_BEGIN,       ND_FULL },  
00156   { "close",             90, SND_BEGIN,       ND_FULL },  
00157   { "economy",           30, SND_BEGIN,       ND_FULL },  
00158   { "production_player", 30, SND_BEGIN,       ND_FULL },  
00159   { "production_other",  30, SND_BEGIN,       ND_FULL },  
00160   { "production_nobody", 30, SND_BEGIN,       ND_FULL },  
00161   { "advice",           150, SND_BEGIN,       ND_FULL },  
00162   { "new_vehicles",      30, SND_1E_OOOOH,    ND_FULL },  
00163   { "acceptance",        90, SND_BEGIN,       ND_FULL },  
00164   { "subsidies",        180, SND_BEGIN,       ND_FULL },  
00165   { "general",           60, SND_BEGIN,       ND_FULL },  
00166 };
00167 
00168 struct NewsWindow : Window {
00169   uint16 chat_height;
00170   NewsItem *ni;
00171   static uint duration;
00172 
00173   NewsWindow(const WindowDesc *desc, NewsItem *ni) : Window(desc), ni(ni)
00174   {
00175     NewsWindow::duration = 555;
00176     const Window *w = FindWindowById(WC_SEND_NETWORK_MSG, 0);
00177     this->chat_height = (w != NULL) ? w->height : 0;
00178 
00179     this->ni = _forced_news == NULL ? _current_news : _forced_news;
00180     this->flags4 |= WF_DISABLE_VP_SCROLL;
00181 
00182     this->FindWindowPlacementAndResize(desc);
00183   }
00184 
00185   void DrawNewsBorder()
00186   {
00187     int left = 0;
00188     int right = this->width - 1;
00189     int top = 0;
00190     int bottom = this->height - 1;
00191 
00192     GfxFillRect(left,  top,    right, bottom, 0xF);
00193 
00194     GfxFillRect(left,  top,    left,  bottom, 0xD7);
00195     GfxFillRect(right, top,    right, bottom, 0xD7);
00196     GfxFillRect(left,  top,    right, top,    0xD7);
00197     GfxFillRect(left,  bottom, right, bottom, 0xD7);
00198 
00199     DrawString(left + 2, top + 1, STR_00C6, TC_FROMSTRING);
00200   }
00201 
00202   virtual void OnPaint()
00203   {
00204     const NewsMode display_mode = _news_subtype_data[this->ni->subtype].display_mode;
00205 
00206     switch (display_mode) {
00207       case NM_NORMAL:
00208       case NM_THIN: {
00209         this->DrawNewsBorder();
00210 
00211         if (_news_subtype_data[this->ni->subtype].callback != NULL) {
00212           (_news_subtype_data[this->ni->subtype].callback)(this, ni);
00213           break;
00214         }
00215 
00216         DrawString(2, 1, STR_00C6, TC_FROMSTRING);
00217 
00218         SetDParam(0, this->ni->date);
00219         DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING);
00220 
00221         if (!(this->ni->flags & NF_VIEWPORT)) {
00222           CopyInDParam(0, this->ni->params, lengthof(this->ni->params));
00223           DrawStringMultiCenter(215, display_mode == NM_NORMAL ? 76 : 56,
00224             this->ni->string_id, this->width - 4);
00225         } else {
00226           /* Back up transparency options to draw news view */
00227           TransparencyOptionBits to_backup = _transparency_opt;
00228           _transparency_opt = 0;
00229           this->DrawViewport();
00230           _transparency_opt = to_backup;
00231 
00232           /* Shade the viewport into gray, or colour*/
00233           ViewPort *vp = this->viewport;
00234           GfxFillRect(vp->left - this->left, vp->top - this->top,
00235             vp->left - this->left + vp->width - 1, vp->top - this->top + vp->height - 1,
00236             (this->ni->flags & NF_INCOLOUR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY), FILLRECT_RECOLOUR
00237           );
00238 
00239           CopyInDParam(0, this->ni->params, lengthof(this->ni->params));
00240           DrawStringMultiCenter(this->width / 2, 20, this->ni->string_id, this->width - 4);
00241         }
00242         break;
00243       }
00244 
00245       default:
00246         this->DrawWidgets();
00247         if (!(this->ni->flags & NF_VIEWPORT)) {
00248           CopyInDParam(0, this->ni->params, lengthof(this->ni->params));
00249           DrawStringMultiCenter(140, 38, this->ni->string_id, 276);
00250         } else {
00251           this->DrawViewport();
00252           CopyInDParam(0, this->ni->params, lengthof(this->ni->params));
00253           DrawStringMultiCenter(this->width / 2, this->height - 16, this->ni->string_id, this->width - 4);
00254         }
00255         break;
00256     }
00257   }
00258 
00259   virtual void OnClick(Point pt, int widget)
00260   {
00261     switch (widget) {
00262       case 1:
00263         NewsWindow::duration = 0;
00264         delete this;
00265         _forced_news = NULL;
00266         break;
00267 
00268       case 0:
00269         if (this->ni->flags & NF_VEHICLE) {
00270           const Vehicle *v = GetVehicle(this->ni->data_a);
00271           ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos);
00272         } else if (this->ni->flags & NF_TILE) {
00273           if (_ctrl_pressed) {
00274             ShowExtraViewPortWindow(this->ni->data_a);
00275             if (this->ni->flags & NF_TILE2) {
00276               ShowExtraViewPortWindow(this->ni->data_b);
00277             }
00278           } else {
00279             if (!ScrollMainWindowToTile(this->ni->data_a) && this->ni->flags & NF_TILE2) {
00280               ScrollMainWindowToTile(this->ni->data_b);
00281             }
00282           }
00283         }
00284         break;
00285     }
00286   }
00287 
00288   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
00289   {
00290     if (keycode == WKC_SPACE) {
00291       /* Don't continue. */
00292       delete this;
00293       return ES_HANDLED;
00294     }
00295     return ES_NOT_HANDLED;
00296   }
00297 
00298   virtual void OnInvalidateData(int data)
00299   {
00300     /* The chatbar has notified us that is was either created or closed */
00301     this->chat_height = data;
00302   }
00303 
00304   virtual void OnTick()
00305   {
00306     /* Scroll up newsmessages from the bottom in steps of 4 pixels */
00307     int y = max(this->top - 4, _screen.height - this->height - 12 - this->chat_height);
00308     if (y == this->top) return;
00309 
00310     if (this->viewport != NULL) this->viewport->top += y - this->top;
00311 
00312     int diff = Delta(this->top, y);
00313     this->top = y;
00314 
00315     SetDirtyBlocks(this->left, this->top - diff, this->left + this->width, this->top + this->height);
00316   }
00317 };
00318 
00319 /* static */ uint NewsWindow::duration; 
00320 
00321 
00322 static const Widget _news_type13_widgets[] = {
00323 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_WHITE,     0,   429,     0,   169, 0x0, STR_NULL},
00324 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_WHITE,     0,    10,     0,    11, 0x0, STR_NULL},
00325 {   WIDGETS_END},
00326 };
00327 
00328 static WindowDesc _news_type13_desc(
00329   WDP_CENTER, 476, 430, 170, 430, 170,
00330   WC_NEWS_WINDOW, WC_NONE,
00331   WDF_DEF_WIDGET,
00332   _news_type13_widgets
00333 );
00334 
00335 static const Widget _news_type2_widgets[] = {
00336 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_WHITE,     0,   429,     0,   129, 0x0, STR_NULL},
00337 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_WHITE,     0,    10,     0,    11, 0x0, STR_NULL},
00338 {   WIDGETS_END},
00339 };
00340 
00341 static WindowDesc _news_type2_desc(
00342   WDP_CENTER, 476, 430, 130, 430, 130,
00343   WC_NEWS_WINDOW, WC_NONE,
00344   WDF_DEF_WIDGET,
00345   _news_type2_widgets
00346 );
00347 
00348 static const Widget _news_type0_widgets[] = {
00349 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_LIGHT_BLUE,     0,   279,    14,    86, 0x0,              STR_NULL},
00350 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_LIGHT_BLUE,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
00351 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_LIGHT_BLUE,    11,   279,     0,    13, STR_012C_MESSAGE, STR_NULL},
00352 {      WWT_INSET,   RESIZE_NONE,  COLOUR_LIGHT_BLUE,     2,   277,    16,    64, 0x0,              STR_NULL},
00353 {   WIDGETS_END},
00354 };
00355 
00356 static WindowDesc _news_type0_desc(
00357   WDP_CENTER, 476, 280, 87, 280, 87,
00358   WC_NEWS_WINDOW, WC_NONE,
00359   WDF_DEF_WIDGET,
00360   _news_type0_widgets
00361 );
00362 
00363 
00365 static void ShowNewspaper(NewsItem *ni)
00366 {
00367   SoundFx sound = _news_type_data[_news_subtype_data[ni->subtype].type].sound;
00368   if (sound != 0) SndPlayFx(sound);
00369 
00370   int top = _screen.height;
00371   Window *w;
00372   switch (_news_subtype_data[ni->subtype].display_mode) {
00373     case NM_NORMAL:
00374       _news_type13_desc.top = top;
00375       w = new NewsWindow(&_news_type13_desc, ni);
00376       if (ni->flags & NF_VIEWPORT) {
00377         InitializeWindowViewport(w, 2, 58, 426, 110,
00378           ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
00379       }
00380       break;
00381 
00382     case NM_THIN:
00383       _news_type2_desc.top = top;
00384       w = new NewsWindow(&_news_type2_desc, ni);
00385       if (ni->flags & NF_VIEWPORT) {
00386         InitializeWindowViewport(w, 2, 58, 426, 70,
00387           ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
00388       }
00389       break;
00390 
00391     default:
00392       _news_type0_desc.top = top;
00393       w = new NewsWindow(&_news_type0_desc, ni);
00394       if (ni->flags & NF_VIEWPORT) {
00395         InitializeWindowViewport(w, 3, 17, 274, 47,
00396           ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
00397       }
00398       break;
00399   }
00400 }
00401 
00403 static void ShowTicker(const NewsItem *ni)
00404 {
00405   if (_news_ticker_sound) SndPlayFx(SND_16_MORSE);
00406 
00407   _statusbar_news_item = *ni;
00408   InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_TICKER);
00409 }
00410 
00412 void InitNewsItemStructs()
00413 {
00414   for (NewsItem *ni = _oldest_news; ni != NULL; ) {
00415     NewsItem *next = ni->next;
00416     delete ni;
00417     ni = next;
00418   }
00419 
00420   _total_news = 0;
00421   _oldest_news = NULL;
00422   _latest_news = NULL;
00423   _forced_news = NULL;
00424   _current_news = NULL;
00425 }
00426 
00431 static bool ReadyForNextItem()
00432 {
00433   NewsItem *ni = _forced_news == NULL ? _current_news : _forced_news;
00434   if (ni == NULL) return true;
00435 
00436   /* Ticker message
00437    * Check if the status bar message is still being displayed? */
00438   if (IsNewsTickerShown()) return false;
00439 
00440   /* Newspaper message, decrement duration counter */
00441   if (NewsWindow::duration != 0) NewsWindow::duration--;
00442 
00443   /* neither newsticker nor newspaper are running */
00444   return (NewsWindow::duration == 0 || FindWindowById(WC_NEWS_WINDOW, 0) == NULL);
00445 }
00446 
00448 static void MoveToNextItem()
00449 {
00450   InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
00451   DeleteWindowById(WC_NEWS_WINDOW, 0); // close the newspapers window if shown
00452   _forced_news = NULL;
00453 
00454   /* if we're not at the last item, then move on */
00455   if (_current_news != _latest_news) {
00456     _current_news = (_current_news == NULL) ? _oldest_news : _current_news->next;
00457     NewsItem *ni = _current_news;
00458     const NewsType type = _news_subtype_data[ni->subtype].type;
00459 
00460     /* check the date, don't show too old items */
00461     if (_date - _news_type_data[type].age > ni->date) return;
00462 
00463     switch (_news_type_data[type].display) {
00464       default: NOT_REACHED();
00465       case ND_OFF: // Off - show nothing only a small reminder in the status bar
00466         InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_REMINDER);
00467         break;
00468 
00469       case ND_SUMMARY: // Summary - show ticker
00470         ShowTicker(ni);
00471         break;
00472 
00473       case ND_FULL: // Full - show newspaper
00474         ShowNewspaper(ni);
00475         break;
00476     }
00477   }
00478 }
00479 
00489 void AddNewsItem(StringID string, NewsSubtype subtype, uint data_a, uint data_b, void *free_data)
00490 {
00491   if (_game_mode == GM_MENU) return;
00492 
00493   /* Create new news item node */
00494   NewsItem *ni = new NewsItem;
00495 
00496   ni->string_id = string;
00497   ni->subtype = subtype;
00498   ni->flags = _news_subtype_data[subtype].flags;
00499 
00500   /* show this news message in colour? */
00501   if (_cur_year >= _settings_client.gui.coloured_news_year) ni->flags |= NF_INCOLOUR;
00502 
00503   ni->data_a = data_a;
00504   ni->data_b = data_b;
00505   ni->free_data = free_data;
00506   ni->date = _date;
00507   CopyOutDParam(ni->params, 0, lengthof(ni->params));
00508 
00509   if (_total_news++ == 0) {
00510     assert(_oldest_news == NULL);
00511     _oldest_news = ni;
00512     ni->prev = NULL;
00513   } else {
00514     assert(_latest_news->next == NULL);
00515     _latest_news->next = ni;
00516     ni->prev = _latest_news;
00517   }
00518 
00519   ni->next = NULL;
00520   _latest_news = ni;
00521 
00522   InvalidateWindow(WC_MESSAGE_HISTORY, 0);
00523 }
00524 
00526 static void DeleteNewsItem(NewsItem *ni)
00527 {
00528   if (_forced_news == ni || _current_news == ni) {
00529     /* about to remove the currently forced item (shown as newspapers) ||
00530      * about to remove the currently displayed item (newspapers, ticker, or just a reminder) */
00531     MoveToNextItem();
00532   }
00533 
00534   /* delete item */
00535 
00536   if (ni->prev != NULL) {
00537     ni->prev->next = ni->next;
00538   } else {
00539     assert(_oldest_news == ni);
00540     _oldest_news = ni->next;
00541   }
00542 
00543   if (ni->next != NULL) {
00544     ni->next->prev = ni->prev;
00545   } else {
00546     assert(_latest_news == ni);
00547     _latest_news = ni->prev;
00548   }
00549 
00550   free(ni->free_data);
00551 
00552   if (_current_news == ni) _current_news = ni->prev;
00553   _total_news--;
00554   delete ni;
00555 
00556   InvalidateWindow(WC_MESSAGE_HISTORY, 0);
00557 }
00558 
00559 void DeleteVehicleNews(VehicleID vid, StringID news)
00560 {
00561   NewsItem *ni = _oldest_news;
00562 
00563   while (ni != NULL) {
00564     if (ni->flags & NF_VEHICLE &&
00565         ni->data_a == vid &&
00566         (news == INVALID_STRING_ID || ni->string_id == news)) {
00567       /* grab a pointer to the next item before ni is freed */
00568       NewsItem *p = ni->next;
00569       DeleteNewsItem(ni);
00570       ni = p;
00571     } else {
00572       ni = ni->next;
00573     }
00574   }
00575 }
00576 
00581 void DeleteStationNews(StationID sid)
00582 {
00583   NewsItem *ni = _oldest_news;
00584 
00585   while (ni != NULL) {
00586     NewsItem *next = ni->next;
00587     switch (ni->subtype) {
00588       case NS_ARRIVAL_COMPANY:
00589       case NS_ARRIVAL_OTHER:
00590       case NS_ACCEPTANCE:
00591         if (ni->data_b == sid) DeleteNewsItem(ni);
00592         break;
00593       default:
00594         break;
00595     }
00596     ni = next;
00597   }
00598 }
00599 
00600 void RemoveOldNewsItems()
00601 {
00602   NewsItem *next;
00603   for (NewsItem *cur = _oldest_news; _total_news > MIN_NEWS_AMOUNT && cur != NULL; cur = next) {
00604     next = cur->next;
00605     if (_date - _news_type_data[_news_subtype_data[cur->subtype].type].age * _settings_client.gui.news_message_timeout > cur->date) DeleteNewsItem(cur);
00606   }
00607 }
00608 
00609 void NewsLoop()
00610 {
00611   /* no news item yet */
00612   if (_total_news == 0) return;
00613 
00614   static byte _last_clean_month = 0;
00615 
00616   if (_last_clean_month != _cur_month) {
00617     RemoveOldNewsItems();
00618     _last_clean_month = _cur_month;
00619   }
00620 
00621   if (ReadyForNextItem()) MoveToNextItem();
00622 }
00623 
00625 static void ShowNewsMessage(NewsItem *ni)
00626 {
00627   assert(_total_news != 0);
00628 
00629   /* Delete the news window */
00630   DeleteWindowById(WC_NEWS_WINDOW, 0);
00631 
00632   /* setup forced news item */
00633   _forced_news = ni;
00634 
00635   if (_forced_news != NULL) {
00636     DeleteWindowById(WC_NEWS_WINDOW, 0);
00637     ShowNewspaper(ni);
00638   }
00639 }
00640 
00642 void ShowLastNewsMessage()
00643 {
00644   if (_total_news == 0) {
00645     return;
00646   } else if (_forced_news == NULL) {
00647     /* Not forced any news yet, show the current one, unless a news window is
00648      * open (which can only be the current one), then show the previous item */
00649     const Window *w = FindWindowById(WC_NEWS_WINDOW, 0);
00650     ShowNewsMessage((w == NULL || (_current_news == _oldest_news)) ? _current_news : _current_news->prev);
00651   } else if (_forced_news == _oldest_news) {
00652     /* We have reached the oldest news, start anew with the latest */
00653     ShowNewsMessage(_latest_news);
00654   } else {
00655     /* 'Scrolling' through news history show each one in turn */
00656     ShowNewsMessage(_forced_news->prev);
00657   }
00658 }
00659 
00660 
00669 static void DrawNewsString(int x, int y, TextColour colour, const NewsItem *ni, uint maxw)
00670 {
00671   char buffer[512], buffer2[512];
00672   StringID str;
00673 
00674   CopyInDParam(0, ni->params, lengthof(ni->params));
00675   str = ni->string_id;
00676 
00677   GetString(buffer, str, lastof(buffer));
00678   /* Copy the just gotten string to another buffer to remove any formatting
00679    * from it such as big fonts, etc. */
00680   const char *ptr = buffer;
00681   char *dest = buffer2;
00682   WChar c_last = '\0';
00683   for (;;) {
00684     WChar c = Utf8Consume(&ptr);
00685     if (c == 0) break;
00686     /* Make a space from a newline, but ignore multiple newlines */
00687     if (c == '\n' && c_last != '\n') {
00688       dest[0] = ' ';
00689       dest++;
00690     } else if (c == '\r') {
00691       dest[0] = dest[1] = dest[2] = dest[3] = ' ';
00692       dest += 4;
00693     } else if (IsPrintable(c)) {
00694       dest += Utf8Encode(dest, c);
00695     }
00696     c_last = c;
00697   }
00698 
00699   *dest = '\0';
00700   /* Truncate and show string; postfixed by '...' if neccessary */
00701   DoDrawStringTruncated(buffer2, x, y, colour, maxw);
00702 }
00703 
00704 
00705 struct MessageHistoryWindow : Window {
00706   MessageHistoryWindow(const WindowDesc *desc) : Window(desc)
00707   {
00708     this->vscroll.cap = 10;
00709     this->vscroll.count = _total_news;
00710     this->resize.step_height = 12;
00711     this->resize.height = this->height - 12 * 6; // minimum of 4 items in the list, each item 12 high
00712     this->resize.step_width = 1;
00713     this->resize.width = 200; // can't make window any smaller than 200 pixel
00714 
00715     this->FindWindowPlacementAndResize(desc);
00716   }
00717 
00718   virtual void OnPaint()
00719   {
00720     int y = 19;
00721 
00722     SetVScrollCount(this, _total_news);
00723     this->DrawWidgets();
00724 
00725     if (_total_news == 0) return;
00726 
00727     NewsItem *ni = _latest_news;
00728     for (int n = this->vscroll.pos; n > 0; n--) {
00729       ni = ni->prev;
00730       if (ni == NULL) return;
00731     }
00732 
00733     for (int n = this->vscroll.cap; n > 0; n--) {
00734       SetDParam(0, ni->date);
00735       DrawString(4, y, STR_SHORT_DATE, TC_WHITE);
00736 
00737       DrawNewsString(82, y, TC_WHITE, ni, this->width - 95);
00738       y += 12;
00739 
00740       ni = ni->prev;
00741       if (ni == NULL) return;
00742     }
00743   }
00744 
00745   virtual void OnClick(Point pt, int widget)
00746   {
00747     if (widget == 3) {
00748       NewsItem *ni = _latest_news;
00749       if (ni == NULL) return;
00750 
00751       for (int n = (pt.y - 19) / 12 + this->vscroll.pos; n > 0; n--) {
00752         ni = ni->prev;
00753         if (ni == NULL) return;
00754       }
00755 
00756       ShowNewsMessage(ni);
00757     }
00758   }
00759 
00760   virtual void OnResize(Point new_size, Point delta)
00761   {
00762     this->vscroll.cap += delta.y / 12;
00763   }
00764 };
00765 
00766 static const Widget _message_history_widgets[] = {
00767 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_BROWN,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},
00768 {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_BROWN,    11,   387,     0,    13, STR_MESSAGE_HISTORY, STR_018C_WINDOW_TITLE_DRAG_THIS},
00769 {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_BROWN,   388,   399,     0,    13, 0x0,                 STR_STICKY_BUTTON},
00770 {      WWT_PANEL,     RESIZE_RB,  COLOUR_BROWN,     0,   387,    14,   139, 0x0,                 STR_MESSAGE_HISTORY_TIP},
00771 {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_BROWN,   388,   399,    14,   127, 0x0,                 STR_0190_SCROLL_BAR_SCROLLS_LIST},
00772 {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_BROWN,   388,   399,   128,   139, 0x0,                 STR_RESIZE_BUTTON},
00773 {   WIDGETS_END},
00774 };
00775 
00776 static const WindowDesc _message_history_desc(
00777   240, 22, 400, 140, 400, 140,
00778   WC_MESSAGE_HISTORY, WC_NONE,
00779   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00780   _message_history_widgets
00781 );
00782 
00784 void ShowMessageHistory()
00785 {
00786   DeleteWindowById(WC_MESSAGE_HISTORY, 0);
00787   new MessageHistoryWindow(&_message_history_desc);
00788 }
00789 
00790 
00792 enum {
00793   WIDGET_NEWSOPT_DROP_SUMMARY = 4,  
00794   WIDGET_NEWSOPT_SOUNDTICKER  = 6,  
00795   WIDGET_NEWSOPT_START_OPTION = 8,  
00796 };
00797 
00798 static const StringID _message_opt[] = {STR_OFF, STR_SUMMARY, STR_FULL, INVALID_STRING_ID};
00799 
00800 struct MessageOptionsWindow : Window {
00801   int state;
00802 
00803   MessageOptionsWindow(const WindowDesc *desc) : Window(desc)
00804   {
00805     NewsDisplay all_val;
00806 
00807     /* Set up the initial disabled buttons in the case of 'off' or 'full' */
00808     all_val = _news_type_data[0].display;
00809     for (int i = 0; i < NT_END; i++) {
00810       this->SetMessageButtonStates(_news_type_data[i].display, i);
00811       /* If the value doesn't match the ALL-button value, set the ALL-button value to 'off' */
00812       if (_news_type_data[i].display != all_val) all_val = ND_OFF;
00813     }
00814     /* If all values are the same value, the ALL-button will take over this value */
00815     this->state = all_val;
00816 
00817     this->FindWindowPlacementAndResize(desc);
00818   }
00819 
00828   void SetMessageButtonStates(byte value, int element)
00829   {
00830     element *= NB_WIDG_PER_SETTING;
00831 
00832     this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION, value == 0);
00833     this->SetWidgetDisabledState(element + WIDGET_NEWSOPT_START_OPTION + 2, value == 2);
00834   }
00835 
00836   virtual void OnPaint()
00837   {
00838     if (_news_ticker_sound) this->LowerWidget(WIDGET_NEWSOPT_SOUNDTICKER);
00839 
00840     this->widget[WIDGET_NEWSOPT_DROP_SUMMARY].data = _message_opt[this->state];
00841     this->DrawWidgets();
00842 
00843     /* Draw the string of each setting on each button. */
00844     for (int i = 0, y = 26; i < NT_END; i++, y += 12) {
00845       /* 51 comes from 13 + 89 (left and right of the button)+1, shiefted by one as to get division,
00846        * which will give centered position */
00847       DrawStringCentered(51, y + 1, _message_opt[_news_type_data[i].display], TC_BLACK);
00848     }
00849   }
00850 
00851   virtual void OnClick(Point pt, int widget)
00852   {
00853     switch (widget) {
00854       case WIDGET_NEWSOPT_DROP_SUMMARY: // Dropdown menu for all settings
00855         ShowDropDownMenu(this, _message_opt, this->state, WIDGET_NEWSOPT_DROP_SUMMARY, 0, 0);
00856         break;
00857 
00858       case WIDGET_NEWSOPT_SOUNDTICKER: // Change ticker sound on/off
00859         _news_ticker_sound ^= 1;
00860         this->ToggleWidgetLoweredState(widget);
00861         this->InvalidateWidget(widget);
00862         break;
00863 
00864       default: { // Clicked on the [<] .. [>] widgets
00865         int wid = widget - WIDGET_NEWSOPT_START_OPTION;
00866         if (wid >= 0 && wid < (NB_WIDG_PER_SETTING * NT_END)) {
00867           int element = wid / NB_WIDG_PER_SETTING;
00868           byte val = (_news_type_data[element].display + ((wid % NB_WIDG_PER_SETTING) ? 1 : -1)) % 3;
00869 
00870           this->SetMessageButtonStates(val, element);
00871           _news_type_data[element].display = (NewsDisplay)val;
00872           this->SetDirty();
00873         }
00874         break;
00875       }
00876     }
00877   }
00878 
00879   virtual void OnDropdownSelect(int widget, int index)
00880   {
00881     this->state = index;
00882 
00883     for (int i = 0; i < NT_END; i++) {
00884       this->SetMessageButtonStates(index, i);
00885       _news_type_data[i].display = (NewsDisplay)index;
00886     }
00887     this->SetDirty();
00888   }
00889 };
00890 
00891 
00892 /*
00893  * The news settings window widgets
00894  *
00895  * Main part of the window is a list of news setting lines, one for each news category.
00896  * Each line is constructed by an expansion of the \c NEWS_SETTINGS_LINE macro
00897  */
00898 
00915 #define NEWS_SETTINGS_LINE(basey, linenum, text) \
00916   { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \
00917       4,  12,  basey     + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \
00918     SPR_ARROW_LEFT, STR_HSCROLL_BAR_SCROLLS_LIST}, \
00919   { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_YELLOW, \
00920      13,  89,  basey     + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \
00921     STR_EMPTY, STR_NULL}, \
00922   { WWT_PUSHIMGBTN, RESIZE_NONE, COLOUR_YELLOW, \
00923      90,  98,  basey     + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 11 + linenum * NEWS_SETTING_BASELINE_SKIP, \
00924     SPR_ARROW_RIGHT, STR_HSCROLL_BAR_SCROLLS_LIST}, \
00925         { WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW, \
00926     103, 409,  basey + 1 + linenum * NEWS_SETTING_BASELINE_SKIP,  basey + 13 + linenum * NEWS_SETTING_BASELINE_SKIP, \
00927     text, STR_NULL}
00928 
00929 static const int NEWS_SETTING_BASELINE_SKIP = 12; 
00930 
00931 
00932 static const Widget _message_options_widgets[] = {
00933 { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_BROWN,   0,  10,  0, 13,
00934   STR_00C5,                 STR_018B_CLOSE_WINDOW},
00935 {  WWT_CAPTION, RESIZE_NONE, COLOUR_BROWN,  11, 409,  0, 13,
00936   STR_0204_MESSAGE_OPTIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
00937 {    WWT_PANEL, RESIZE_NONE, COLOUR_BROWN,   0, 409, 14, 64 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00938   0x0,                      STR_NULL},
00939 
00940 /* Text at the top of the main panel, in black */
00941 {    WWT_LABEL, RESIZE_NONE, COLOUR_BROWN,
00942     0, 409, 13, 26,
00943   STR_0205_MESSAGE_TYPES,   STR_NULL},
00944 
00945 /* General drop down and sound button, widgets WIDGET_NEWSOPT_BTN_SUMMARY and WIDGET_NEWSOPT_DROP_SUMMARY */
00946 {  WWT_DROPDOWN, RESIZE_NONE, COLOUR_YELLOW,
00947     4,  98,  34 + NT_END * NEWS_SETTING_BASELINE_SKIP,  45 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00948   0x0, STR_NULL},
00949 
00950 {      WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW,
00951   103, 409,  35 + NT_END * NEWS_SETTING_BASELINE_SKIP,  47 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00952   STR_MESSAGES_ALL, STR_NULL},
00953 
00954 /* Below is widget WIDGET_NEWSOPT_SOUNDTICKER */
00955 { WWT_TEXTBTN_2, RESIZE_NONE, COLOUR_YELLOW,
00956     4,  98,  46 + NT_END * NEWS_SETTING_BASELINE_SKIP,  57 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00957   STR_02DB_OFF,  STR_NULL},
00958 
00959 {      WWT_TEXT, RESIZE_NONE, COLOUR_YELLOW,
00960   103, 409,  47 + NT_END * NEWS_SETTING_BASELINE_SKIP,  59 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00961   STR_MESSAGE_SOUND, STR_NULL},
00962 
00963 /* List of news-setting lines (4 widgets for each line).
00964  * First widget must be number WIDGET_NEWSOPT_START_OPTION
00965  */
00966 NEWS_SETTINGS_LINE(26, NT_ARRIVAL_COMPANY,  STR_0206_ARRIVAL_OF_FIRST_VEHICLE),
00967 NEWS_SETTINGS_LINE(26, NT_ARRIVAL_OTHER,    STR_0207_ARRIVAL_OF_FIRST_VEHICLE),
00968 NEWS_SETTINGS_LINE(26, NT_ACCIDENT,         STR_0208_ACCIDENTS_DISASTERS),
00969 NEWS_SETTINGS_LINE(26, NT_COMPANY_INFO,     STR_0209_COMPANY_INFORMATION),
00970 NEWS_SETTINGS_LINE(26, NT_INDUSTRY_OPEN,    STR_NEWS_INDUSTRY_OPEN),
00971 NEWS_SETTINGS_LINE(26, NT_INDUSTRY_CLOSE,   STR_NEWS_INDUSTRY_CLOSE),
00972 NEWS_SETTINGS_LINE(26, NT_ECONOMY,          STR_020A_ECONOMY_CHANGES),
00973 NEWS_SETTINGS_LINE(26, NT_INDUSTRY_COMPANY, STR_INDUSTRY_CHANGES_SERVED_BY_COMPANY),
00974 NEWS_SETTINGS_LINE(26, NT_INDUSTRY_OTHER,   STR_INDUSTRY_CHANGES_SERVED_BY_OTHER),
00975 NEWS_SETTINGS_LINE(26, NT_INDUSTRY_NOBODY,  STR_OTHER_INDUSTRY_PRODUCTION_CHANGES),
00976 NEWS_SETTINGS_LINE(26, NT_ADVICE,           STR_020B_ADVICE_INFORMATION_ON_COMPANY),
00977 NEWS_SETTINGS_LINE(26, NT_NEW_VEHICLES,     STR_020C_NEW_VEHICLES),
00978 NEWS_SETTINGS_LINE(26, NT_ACCEPTANCE,       STR_020D_CHANGES_OF_CARGO_ACCEPTANCE),
00979 NEWS_SETTINGS_LINE(26, NT_SUBSIDIES,        STR_020E_SUBSIDIES),
00980 NEWS_SETTINGS_LINE(26, NT_GENERAL,          STR_020F_GENERAL_INFORMATION),
00981 
00982 {   WIDGETS_END},
00983 };
00984 
00985 static const WindowDesc _message_options_desc(
00986   270,  22,  410,  65 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00987              410,  65 + NT_END * NEWS_SETTING_BASELINE_SKIP,
00988   WC_GAME_OPTIONS, WC_NONE,
00989   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
00990   _message_options_widgets
00991 );
00992 
00993 void ShowMessageOptions()
00994 {
00995   DeleteWindowById(WC_GAME_OPTIONS, 0);
00996   new MessageOptionsWindow(&_message_options_desc);
00997 }

Generated on Mon May 11 15:48:05 2009 for OpenTTD by  doxygen 1.5.6