mirror of https://github.com/OpenTTD/OpenTTD.git
Codechange: Use std::list for News Items. (#12338)
This commit is contained in:
parent
08cf106fc6
commit
f6a88e40a4
|
@ -56,11 +56,12 @@ static void SurveyRecentNews(nlohmann::json &json)
|
||||||
json = nlohmann::json::array();
|
json = nlohmann::json::array();
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) {
|
for (const auto &news : GetNews()) {
|
||||||
TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(news->date);
|
TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(news.date);
|
||||||
json.push_back(fmt::format("({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}",
|
json.push_back(fmt::format("({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}",
|
||||||
ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type,
|
ymd.year, ymd.month + 1, ymd.day, news.string_id, news.type,
|
||||||
news->reftype1, news->ref1, news->reftype2, news->ref2));
|
news.reftype1, news.ref1, news.reftype2, news.ref2));
|
||||||
|
if (++i > 32) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ inline void AddIndustryNewsItem(StringID string, NewsType type, IndustryID indus
|
||||||
void NewsLoop();
|
void NewsLoop();
|
||||||
void InitNewsItemStructs();
|
void InitNewsItemStructs();
|
||||||
|
|
||||||
extern const NewsItem *_statusbar_news_item;
|
const NewsItem *GetStatusbarNews();
|
||||||
|
|
||||||
void DeleteInvalidEngineNews();
|
void DeleteInvalidEngineNews();
|
||||||
void DeleteVehicleNews(VehicleID vid, StringID news);
|
void DeleteVehicleNews(VehicleID vid, StringID news);
|
||||||
|
|
329
src/news_gui.cpp
329
src/news_gui.cpp
|
@ -21,7 +21,6 @@
|
||||||
#include "town.h"
|
#include "town.h"
|
||||||
#include "sound_func.h"
|
#include "sound_func.h"
|
||||||
#include "string_func.h"
|
#include "string_func.h"
|
||||||
#include "dropdown_func.h"
|
|
||||||
#include "statusbar_gui.h"
|
#include "statusbar_gui.h"
|
||||||
#include "company_manager_face.h"
|
#include "company_manager_face.h"
|
||||||
#include "company_func.h"
|
#include "company_func.h"
|
||||||
|
@ -44,25 +43,42 @@
|
||||||
|
|
||||||
#include "safeguards.h"
|
#include "safeguards.h"
|
||||||
|
|
||||||
const NewsItem *_statusbar_news_item = nullptr;
|
static const uint MIN_NEWS_AMOUNT = 30; ///< preferred minimum amount of news messages.
|
||||||
|
static const uint MAX_NEWS_AMOUNT = 1U << 10; ///< Do not exceed this number of news messages.
|
||||||
|
|
||||||
static uint MIN_NEWS_AMOUNT = 30; ///< preferred minimum amount of news messages
|
static NewsContainer _news; ///< List of news, with newest items at the start.
|
||||||
static uint MAX_NEWS_AMOUNT = 1 << 10; ///< Do not exceed this number of news messages
|
|
||||||
static uint _total_news = 0; ///< current number of news items
|
|
||||||
static NewsItem *_oldest_news = nullptr; ///< head of news items queue
|
|
||||||
NewsItem *_latest_news = nullptr; ///< tail of news items queue
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forced news item.
|
* Forced news item.
|
||||||
* Users can force an item by accessing the history or "last message".
|
* Users can force an item by accessing the history or "last message".
|
||||||
* If the message being shown was forced by the user, a pointer is stored
|
* If the message being shown was forced by the user, an iterater is stored
|
||||||
* in _forced_news. Otherwise, \a _forced_news variable is nullptr.
|
* in _forced_news. Otherwise, \a _forced_news variable is the end of \a _news.
|
||||||
*/
|
*/
|
||||||
static const NewsItem *_forced_news = nullptr;
|
static NewsIterator _forced_news = std::end(_news);
|
||||||
|
|
||||||
/** Current news item (last item shown regularly). */
|
/** Current news item (last item shown regularly). */
|
||||||
static const NewsItem *_current_news = nullptr;
|
static NewsIterator _current_news = std::end(_news);
|
||||||
|
|
||||||
|
/** Current status bar news item. */
|
||||||
|
static NewsIterator _statusbar_news = std::end(_news);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get pointer to the current status bar news item.
|
||||||
|
* @return Pointer to the current status bar news item, or nullptr if there is none.
|
||||||
|
*/
|
||||||
|
const NewsItem *GetStatusbarNews()
|
||||||
|
{
|
||||||
|
return (_statusbar_news == std::end(_news)) ? nullptr : &*_statusbar_news;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get read-only reference to all news items.
|
||||||
|
* @return Read-only reference to all news items.
|
||||||
|
*/
|
||||||
|
const NewsContainer &GetNews()
|
||||||
|
{
|
||||||
|
return _news;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the position a news-reference is referencing.
|
* Get the position a news-reference is referencing.
|
||||||
|
@ -484,7 +500,7 @@ struct NewsWindow : Window {
|
||||||
case WID_N_CLOSEBOX:
|
case WID_N_CLOSEBOX:
|
||||||
NewsWindow::duration = 0;
|
NewsWindow::duration = 0;
|
||||||
this->Close();
|
this->Close();
|
||||||
_forced_news = nullptr;
|
_forced_news = std::end(_news);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WID_N_CAPTION:
|
case WID_N_CAPTION:
|
||||||
|
@ -622,29 +638,21 @@ static void ShowNewspaper(const NewsItem *ni)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Show news item in the ticker */
|
/** Show news item in the ticker */
|
||||||
static void ShowTicker(const NewsItem *ni)
|
static void ShowTicker(NewsIterator ni)
|
||||||
{
|
{
|
||||||
if (_settings_client.sound.news_ticker) SndPlayFx(SND_16_NEWS_TICKER);
|
if (_settings_client.sound.news_ticker) SndPlayFx(SND_16_NEWS_TICKER);
|
||||||
|
|
||||||
_statusbar_news_item = ni;
|
_statusbar_news = ni;
|
||||||
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_TICKER);
|
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_TICKER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initialize the news-items data structures */
|
/** Initialize the news-items data structures */
|
||||||
void InitNewsItemStructs()
|
void InitNewsItemStructs()
|
||||||
{
|
{
|
||||||
for (NewsItem *ni = _oldest_news; ni != nullptr; ) {
|
_news.clear();
|
||||||
NewsItem *next = ni->next;
|
_forced_news = std::end(_news);
|
||||||
delete ni;
|
_current_news = std::end(_news);
|
||||||
ni = next;
|
_statusbar_news = std::end(_news);
|
||||||
}
|
|
||||||
|
|
||||||
_total_news = 0;
|
|
||||||
_oldest_news = nullptr;
|
|
||||||
_latest_news = nullptr;
|
|
||||||
_forced_news = nullptr;
|
|
||||||
_current_news = nullptr;
|
|
||||||
_statusbar_news_item = nullptr;
|
|
||||||
NewsWindow::duration = 0;
|
NewsWindow::duration = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,7 +662,7 @@ void InitNewsItemStructs()
|
||||||
*/
|
*/
|
||||||
static bool ReadyForNextTickerItem()
|
static bool ReadyForNextTickerItem()
|
||||||
{
|
{
|
||||||
const NewsItem *ni = _statusbar_news_item;
|
const NewsItem *ni = GetStatusbarNews();
|
||||||
if (ni == nullptr) return true;
|
if (ni == nullptr) return true;
|
||||||
|
|
||||||
/* Ticker message
|
/* Ticker message
|
||||||
|
@ -668,8 +676,7 @@ static bool ReadyForNextTickerItem()
|
||||||
*/
|
*/
|
||||||
static bool ReadyForNextNewsItem()
|
static bool ReadyForNextNewsItem()
|
||||||
{
|
{
|
||||||
const NewsItem *ni = _forced_news == nullptr ? _current_news : _forced_news;
|
if (_forced_news == std::end(_news) && _current_news == std::end(_news)) return true;
|
||||||
if (ni == nullptr) return true;
|
|
||||||
|
|
||||||
/* neither newsticker nor newspaper are running */
|
/* neither newsticker nor newspaper are running */
|
||||||
return (NewsWindow::duration <= 0 || FindWindowById(WC_NEWS_WINDOW, 0) == nullptr);
|
return (NewsWindow::duration <= 0 || FindWindowById(WC_NEWS_WINDOW, 0) == nullptr);
|
||||||
|
@ -678,116 +685,98 @@ static bool ReadyForNextNewsItem()
|
||||||
/** Move to the next ticker item */
|
/** Move to the next ticker item */
|
||||||
static void MoveToNextTickerItem()
|
static void MoveToNextTickerItem()
|
||||||
{
|
{
|
||||||
|
assert(!std::empty(_news));
|
||||||
|
|
||||||
/* There is no status bar, so no reason to show news;
|
/* There is no status bar, so no reason to show news;
|
||||||
* especially important with the end game screen when
|
* especially important with the end game screen when
|
||||||
* there is no status bar but possible news. */
|
* there is no status bar but possible news. */
|
||||||
if (FindWindowById(WC_STATUS_BAR, 0) == nullptr) return;
|
if (FindWindowById(WC_STATUS_BAR, 0) == nullptr) return;
|
||||||
|
|
||||||
/* if we're not at the last item, then move on */
|
/* if we're not at the latest item, then move on */
|
||||||
while (_statusbar_news_item != _latest_news) {
|
while (_statusbar_news != std::begin(_news)) {
|
||||||
_statusbar_news_item = (_statusbar_news_item == nullptr) ? _oldest_news : _statusbar_news_item->next;
|
--_statusbar_news;
|
||||||
const NewsItem *ni = _statusbar_news_item;
|
const NewsType type = _statusbar_news->type;
|
||||||
const NewsType type = ni->type;
|
|
||||||
|
|
||||||
/* check the date, don't show too old items */
|
/* check the date, don't show too old items */
|
||||||
if (TimerGameEconomy::date - _news_type_data[type].age > ni->economy_date) continue;
|
if (TimerGameEconomy::date - _news_type_data[type].age > _statusbar_news->economy_date) continue;
|
||||||
|
|
||||||
switch (_news_type_data[type].GetDisplay()) {
|
switch (_news_type_data[type].GetDisplay()) {
|
||||||
default: NOT_REACHED();
|
default: NOT_REACHED();
|
||||||
case ND_OFF: // Off - show nothing only a small reminder in the status bar
|
case ND_OFF: // Off - show nothing only a small reminder in the status bar
|
||||||
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_REMINDER);
|
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_REMINDER);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
case ND_SUMMARY: // Summary - show ticker
|
case ND_SUMMARY: // Summary - show ticker
|
||||||
ShowTicker(ni);
|
ShowTicker(_statusbar_news);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
case ND_FULL: // Full - show newspaper, skipped here
|
case ND_FULL: // Full - show newspaper, skipped here
|
||||||
continue;
|
break;;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Move to the next news item */
|
/** Move to the next news item */
|
||||||
static void MoveToNextNewsItem()
|
static void MoveToNextNewsItem()
|
||||||
{
|
{
|
||||||
|
assert(!std::empty(_news));
|
||||||
|
|
||||||
/* There is no status bar, so no reason to show news;
|
/* There is no status bar, so no reason to show news;
|
||||||
* especially important with the end game screen when
|
* especially important with the end game screen when
|
||||||
* there is no status bar but possible news. */
|
* there is no status bar but possible news. */
|
||||||
if (FindWindowById(WC_STATUS_BAR, 0) == nullptr) return;
|
if (FindWindowById(WC_STATUS_BAR, 0) == nullptr) return;
|
||||||
|
|
||||||
CloseWindowById(WC_NEWS_WINDOW, 0); // close the newspapers window if shown
|
CloseWindowById(WC_NEWS_WINDOW, 0); // close the newspapers window if shown
|
||||||
_forced_news = nullptr;
|
_forced_news = std::end(_news);
|
||||||
|
|
||||||
/* if we're not at the last item, then move on */
|
/* if we're not at the latest item, then move on */
|
||||||
while (_current_news != _latest_news) {
|
while (_current_news != std::begin(_news)) {
|
||||||
_current_news = (_current_news == nullptr) ? _oldest_news : _current_news->next;
|
--_current_news;
|
||||||
const NewsItem *ni = _current_news;
|
const NewsType type = _current_news->type;
|
||||||
const NewsType type = ni->type;
|
|
||||||
|
|
||||||
/* check the date, don't show too old items */
|
/* check the date, don't show too old items */
|
||||||
if (TimerGameEconomy::date - _news_type_data[type].age > ni->economy_date) continue;
|
if (TimerGameEconomy::date - _news_type_data[type].age > _current_news->economy_date) continue;
|
||||||
|
|
||||||
switch (_news_type_data[type].GetDisplay()) {
|
switch (_news_type_data[type].GetDisplay()) {
|
||||||
default: NOT_REACHED();
|
default: NOT_REACHED();
|
||||||
case ND_OFF: // Off - show nothing only a small reminder in the status bar, skipped here
|
case ND_OFF: // Off - show nothing only a small reminder in the status bar, skipped here
|
||||||
continue;
|
break;
|
||||||
|
|
||||||
case ND_SUMMARY: // Summary - show ticker, skipped here
|
case ND_SUMMARY: // Summary - show ticker, skipped here
|
||||||
continue;
|
break;;
|
||||||
|
|
||||||
case ND_FULL: // Full - show newspaper
|
case ND_FULL: // Full - show newspaper
|
||||||
ShowNewspaper(ni);
|
ShowNewspaper(&*_current_news);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete a news item from the queue */
|
/** Delete a news item from the queue */
|
||||||
static void DeleteNewsItem(NewsItem *ni)
|
static std::list<NewsItem>::iterator DeleteNewsItem(std::list<NewsItem>::iterator ni)
|
||||||
{
|
{
|
||||||
/* Delete the news from the news queue. */
|
|
||||||
if (ni->prev != nullptr) {
|
|
||||||
ni->prev->next = ni->next;
|
|
||||||
} else {
|
|
||||||
assert(_oldest_news == ni);
|
|
||||||
_oldest_news = ni->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ni->next != nullptr) {
|
|
||||||
ni->next->prev = ni->prev;
|
|
||||||
} else {
|
|
||||||
assert(_latest_news == ni);
|
|
||||||
_latest_news = ni->prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
_total_news--;
|
|
||||||
|
|
||||||
if (_forced_news == ni || _current_news == ni) {
|
if (_forced_news == ni || _current_news == ni) {
|
||||||
/* When we're the current news, go to the previous item first;
|
/* When we're the current news, go to the previous item first;
|
||||||
* we just possibly made that the last news item. */
|
* we just possibly made that the last news item. */
|
||||||
if (_current_news == ni) _current_news = ni->prev;
|
if (_current_news == ni) _current_news = (_current_news == std::begin(_news)) ? std::end(_news) : std::prev(_current_news);
|
||||||
|
|
||||||
/* About to remove the currently forced item (shown as newspapers) ||
|
/* About to remove the currently forced item (shown as newspapers) ||
|
||||||
* about to remove the currently displayed item (newspapers) */
|
* about to remove the currently displayed item (newspapers) */
|
||||||
MoveToNextNewsItem();
|
MoveToNextNewsItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_statusbar_news_item == ni) {
|
if (_statusbar_news == ni) {
|
||||||
/* When we're the current news, go to the previous item first;
|
/* When we're the current news, go to the previous item first;
|
||||||
* we just possibly made that the last news item. */
|
* we just possibly made that the last news item. */
|
||||||
_statusbar_news_item = ni->prev;
|
if (_statusbar_news == ni) _statusbar_news = (_statusbar_news == std::begin(_news)) ? std::end(_news) : std::prev(_statusbar_news);
|
||||||
|
|
||||||
/* About to remove the currently displayed item (ticker, or just a reminder) */
|
/* About to remove the currently displayed item (ticker, or just a reminder) */
|
||||||
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
|
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
|
||||||
MoveToNextTickerItem();
|
MoveToNextTickerItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete ni;
|
/* Delete the news from the news queue. */
|
||||||
|
return _news.erase(ni);
|
||||||
SetWindowDirty(WC_MESSAGE_HISTORY, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -829,27 +818,14 @@ void AddNewsItem(StringID string, NewsType type, NewsFlag flags, NewsReferenceTy
|
||||||
if (_game_mode == GM_MENU) return;
|
if (_game_mode == GM_MENU) return;
|
||||||
|
|
||||||
/* Create new news item node */
|
/* Create new news item node */
|
||||||
NewsItem *ni = new NewsItem(string, type, flags, reftype1, ref1, reftype2, ref2, data);
|
_news.emplace_front(string, type, flags, reftype1, ref1, reftype2, ref2, data);
|
||||||
|
|
||||||
if (_total_news++ == 0) {
|
|
||||||
assert(_oldest_news == nullptr);
|
|
||||||
_oldest_news = ni;
|
|
||||||
ni->prev = nullptr;
|
|
||||||
} else {
|
|
||||||
assert(_latest_news->next == nullptr);
|
|
||||||
_latest_news->next = ni;
|
|
||||||
ni->prev = _latest_news;
|
|
||||||
}
|
|
||||||
|
|
||||||
ni->next = nullptr;
|
|
||||||
_latest_news = ni;
|
|
||||||
|
|
||||||
/* Keep the number of stored news items to a managable number */
|
/* Keep the number of stored news items to a managable number */
|
||||||
if (_total_news > MAX_NEWS_AMOUNT) {
|
if (std::size(_news) > MAX_NEWS_AMOUNT) {
|
||||||
DeleteNewsItem(_oldest_news);
|
DeleteNewsItem(std::prev(std::end(_news)));
|
||||||
}
|
}
|
||||||
|
|
||||||
SetWindowDirty(WC_MESSAGE_HISTORY, 0);
|
InvalidateWindowData(WC_MESSAGE_HISTORY, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -910,6 +886,29 @@ CommandCost CmdCustomNewsItem(DoCommandFlag flags, NewsType type, NewsReferenceT
|
||||||
return CommandCost();
|
return CommandCost();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete news items by predicate, and invalidate the message history if necessary.
|
||||||
|
* @tparam Tmin Stop if the number of news items remaining reaches \a min items.
|
||||||
|
* @tparam Tpredicate Condition for a news item to be deleted.
|
||||||
|
*/
|
||||||
|
template <size_t Tmin = 0, class Tpredicate>
|
||||||
|
void DeleteNews(Tpredicate predicate)
|
||||||
|
{
|
||||||
|
bool dirty = false;
|
||||||
|
for (auto it = std::rbegin(_news); it != std::rend(_news); /* nothing */) {
|
||||||
|
if constexpr (Tmin > 0) {
|
||||||
|
if (std::size(_news) <= Tmin) break;
|
||||||
|
}
|
||||||
|
if (predicate(*it)) {
|
||||||
|
it = std::make_reverse_iterator(DeleteNewsItem(std::prev(it.base())));
|
||||||
|
dirty = true;
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dirty) InvalidateWindowData(WC_MESSAGE_HISTORY, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a news item type about a vehicle.
|
* Delete a news item type about a vehicle.
|
||||||
* When the news item type is INVALID_STRING_ID all news about the vehicle gets deleted.
|
* When the news item type is INVALID_STRING_ID all news about the vehicle gets deleted.
|
||||||
|
@ -918,16 +917,9 @@ CommandCost CmdCustomNewsItem(DoCommandFlag flags, NewsType type, NewsReferenceT
|
||||||
*/
|
*/
|
||||||
void DeleteVehicleNews(VehicleID vid, StringID news)
|
void DeleteVehicleNews(VehicleID vid, StringID news)
|
||||||
{
|
{
|
||||||
NewsItem *ni = _oldest_news;
|
DeleteNews([&](const auto &ni) {
|
||||||
|
return ((ni.reftype1 == NR_VEHICLE && ni.ref1 == vid) || (ni.reftype2 == NR_VEHICLE && ni.ref2 == vid)) && (news == INVALID_STRING_ID || ni.string_id == news);
|
||||||
while (ni != nullptr) {
|
});
|
||||||
NewsItem *next = ni->next;
|
|
||||||
if (((ni->reftype1 == NR_VEHICLE && ni->ref1 == vid) || (ni->reftype2 == NR_VEHICLE && ni->ref2 == vid)) &&
|
|
||||||
(news == INVALID_STRING_ID || ni->string_id == news)) {
|
|
||||||
DeleteNewsItem(ni);
|
|
||||||
}
|
|
||||||
ni = next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -937,15 +929,9 @@ void DeleteVehicleNews(VehicleID vid, StringID news)
|
||||||
*/
|
*/
|
||||||
void DeleteStationNews(StationID sid)
|
void DeleteStationNews(StationID sid)
|
||||||
{
|
{
|
||||||
NewsItem *ni = _oldest_news;
|
DeleteNews([&](const auto &ni) {
|
||||||
|
return (ni.reftype1 == NR_STATION && ni.ref1 == sid) || (ni.reftype2 == NR_STATION && ni.ref2 == sid);
|
||||||
while (ni != nullptr) {
|
});
|
||||||
NewsItem *next = ni->next;
|
|
||||||
if ((ni->reftype1 == NR_STATION && ni->ref1 == sid) || (ni->reftype2 == NR_STATION && ni->ref2 == sid)) {
|
|
||||||
DeleteNewsItem(ni);
|
|
||||||
}
|
|
||||||
ni = next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -954,15 +940,9 @@ void DeleteStationNews(StationID sid)
|
||||||
*/
|
*/
|
||||||
void DeleteIndustryNews(IndustryID iid)
|
void DeleteIndustryNews(IndustryID iid)
|
||||||
{
|
{
|
||||||
NewsItem *ni = _oldest_news;
|
DeleteNews([&](const auto &ni) {
|
||||||
|
return (ni.reftype1 == NR_INDUSTRY && ni.ref1 == iid) || (ni.reftype2 == NR_INDUSTRY && ni.ref2 == iid);
|
||||||
while (ni != nullptr) {
|
});
|
||||||
NewsItem *next = ni->next;
|
|
||||||
if ((ni->reftype1 == NR_INDUSTRY && ni->ref1 == iid) || (ni->reftype2 == NR_INDUSTRY && ni->ref2 == iid)) {
|
|
||||||
DeleteNewsItem(ni);
|
|
||||||
}
|
|
||||||
ni = next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -970,25 +950,17 @@ void DeleteIndustryNews(IndustryID iid)
|
||||||
*/
|
*/
|
||||||
void DeleteInvalidEngineNews()
|
void DeleteInvalidEngineNews()
|
||||||
{
|
{
|
||||||
NewsItem *ni = _oldest_news;
|
DeleteNews([](const auto &ni) {
|
||||||
|
return (ni.reftype1 == NR_ENGINE && (!Engine::IsValidID(ni.ref1) || !Engine::Get(ni.ref1)->IsEnabled())) ||
|
||||||
while (ni != nullptr) {
|
(ni.reftype2 == NR_ENGINE && (!Engine::IsValidID(ni.ref2) || !Engine::Get(ni.ref2)->IsEnabled()));
|
||||||
NewsItem *next = ni->next;
|
});
|
||||||
if ((ni->reftype1 == NR_ENGINE && (!Engine::IsValidID(ni->ref1) || !Engine::Get(ni->ref1)->IsEnabled())) ||
|
|
||||||
(ni->reftype2 == NR_ENGINE && (!Engine::IsValidID(ni->ref2) || !Engine::Get(ni->ref2)->IsEnabled()))) {
|
|
||||||
DeleteNewsItem(ni);
|
|
||||||
}
|
|
||||||
ni = next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RemoveOldNewsItems()
|
static void RemoveOldNewsItems()
|
||||||
{
|
{
|
||||||
NewsItem *next;
|
DeleteNews<MIN_NEWS_AMOUNT>([](const auto &ni) {
|
||||||
for (NewsItem *cur = _oldest_news; _total_news > MIN_NEWS_AMOUNT && cur != nullptr; cur = next) {
|
return TimerGameEconomy::date - _news_type_data[ni.type].age * _settings_client.gui.news_message_timeout > ni.economy_date;
|
||||||
next = cur->next;
|
});
|
||||||
if (TimerGameEconomy::date - _news_type_data[cur->type].age * _settings_client.gui.news_message_timeout > cur->economy_date) DeleteNewsItem(cur);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -999,17 +971,17 @@ static void RemoveOldNewsItems()
|
||||||
*/
|
*/
|
||||||
void ChangeVehicleNews(VehicleID from_index, VehicleID to_index)
|
void ChangeVehicleNews(VehicleID from_index, VehicleID to_index)
|
||||||
{
|
{
|
||||||
for (NewsItem *ni = _oldest_news; ni != nullptr; ni = ni->next) {
|
for (auto &ni : _news) {
|
||||||
if (ni->reftype1 == NR_VEHICLE && ni->ref1 == from_index) ni->ref1 = to_index;
|
if (ni.reftype1 == NR_VEHICLE && ni.ref1 == from_index) ni.ref1 = to_index;
|
||||||
if (ni->reftype2 == NR_VEHICLE && ni->ref2 == from_index) ni->ref2 = to_index;
|
if (ni.reftype2 == NR_VEHICLE && ni.ref2 == from_index) ni.ref2 = to_index;
|
||||||
if (ni->flags & NF_VEHICLE_PARAM0 && ni->params[0].data == from_index) ni->params[0] = to_index;
|
if (ni.flags & NF_VEHICLE_PARAM0 && ni.params[0].data == from_index) ni.params[0] = to_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewsLoop()
|
void NewsLoop()
|
||||||
{
|
{
|
||||||
/* no news item yet */
|
/* no news item yet */
|
||||||
if (_total_news == 0) return;
|
if (std::empty(_news)) return;
|
||||||
|
|
||||||
static TimerGameEconomy::Month _last_clean_month = 0;
|
static TimerGameEconomy::Month _last_clean_month = 0;
|
||||||
|
|
||||||
|
@ -1023,9 +995,9 @@ void NewsLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Do a forced show of a specific message */
|
/** Do a forced show of a specific message */
|
||||||
static void ShowNewsMessage(const NewsItem *ni)
|
static void ShowNewsMessage(NewsIterator ni)
|
||||||
{
|
{
|
||||||
assert(_total_news != 0);
|
assert(!std::empty(_news));
|
||||||
|
|
||||||
/* Delete the news window */
|
/* Delete the news window */
|
||||||
CloseWindowById(WC_NEWS_WINDOW, 0);
|
CloseWindowById(WC_NEWS_WINDOW, 0);
|
||||||
|
@ -1033,9 +1005,9 @@ static void ShowNewsMessage(const NewsItem *ni)
|
||||||
/* setup forced news item */
|
/* setup forced news item */
|
||||||
_forced_news = ni;
|
_forced_news = ni;
|
||||||
|
|
||||||
if (_forced_news != nullptr) {
|
if (_forced_news != std::end(_news)) {
|
||||||
CloseWindowById(WC_NEWS_WINDOW, 0);
|
CloseWindowById(WC_NEWS_WINDOW, 0);
|
||||||
ShowNewspaper(ni);
|
ShowNewspaper(&*ni);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1054,26 +1026,26 @@ bool HideActiveNewsMessage()
|
||||||
/** Show previous news item */
|
/** Show previous news item */
|
||||||
void ShowLastNewsMessage()
|
void ShowLastNewsMessage()
|
||||||
{
|
{
|
||||||
const NewsItem *ni = nullptr;
|
if (std::empty(_news)) return;
|
||||||
if (_total_news == 0) {
|
|
||||||
return;
|
NewsIterator ni;
|
||||||
} else if (_forced_news == nullptr) {
|
if (_forced_news == std::end(_news)) {
|
||||||
/* Not forced any news yet, show the current one, unless a news window is
|
/* Not forced any news yet, show the current one, unless a news window is
|
||||||
* open (which can only be the current one), then show the previous item */
|
* open (which can only be the current one), then show the previous item */
|
||||||
if (_current_news == nullptr) {
|
if (_current_news == std::end(_news)) {
|
||||||
/* No news were shown yet resp. the last shown one was already deleted.
|
/* No news were shown yet resp. the last shown one was already deleted.
|
||||||
* Threat this as if _forced_news reached _oldest_news; so, wrap around and start anew with the latest. */
|
* Treat this as if _forced_news reached the oldest news; so, wrap around and start anew with the latest. */
|
||||||
ni = _latest_news;
|
ni = std::begin(_news);
|
||||||
} else {
|
} else {
|
||||||
const Window *w = FindWindowById(WC_NEWS_WINDOW, 0);
|
const Window *w = FindWindowById(WC_NEWS_WINDOW, 0);
|
||||||
ni = (w == nullptr || (_current_news == _oldest_news)) ? _current_news : _current_news->prev;
|
ni = (w == nullptr || (std::next(_current_news) == std::end(_news))) ? _current_news : std::next(_current_news);
|
||||||
}
|
}
|
||||||
} else if (_forced_news == _oldest_news) {
|
} else if (std::next(_forced_news) == std::end(_news)) {
|
||||||
/* We have reached the oldest news, start anew with the latest */
|
/* We have reached the oldest news, start anew with the latest */
|
||||||
ni = _latest_news;
|
ni = std::begin(_news);
|
||||||
} else {
|
} else {
|
||||||
/* 'Scrolling' through news history show each one in turn */
|
/* 'Scrolling' through news history show each one in turn */
|
||||||
ni = _forced_news->prev;
|
ni = std::next(_forced_news);
|
||||||
}
|
}
|
||||||
bool wrap = false;
|
bool wrap = false;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -1082,11 +1054,11 @@ void ShowLastNewsMessage()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ni = ni->prev;
|
++ni;
|
||||||
if (ni == nullptr) {
|
if (ni == std::end(_news)) {
|
||||||
if (wrap) break;
|
if (wrap) break;
|
||||||
/* We have reached the oldest news, start anew with the latest */
|
/* We have reached the oldest news, start anew with the latest */
|
||||||
ni = _latest_news;
|
ni = std::begin(_news);
|
||||||
wrap = true;
|
wrap = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1143,37 +1115,23 @@ struct MessageHistoryWindow : Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPaint() override
|
|
||||||
{
|
|
||||||
this->OnInvalidateData(0);
|
|
||||||
this->DrawWidgets();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawWidget(const Rect &r, WidgetID widget) const override
|
void DrawWidget(const Rect &r, WidgetID widget) const override
|
||||||
{
|
{
|
||||||
if (widget != WID_MH_BACKGROUND || _total_news == 0) return;
|
if (widget != WID_MH_BACKGROUND || std::empty(_news)) return;
|
||||||
|
|
||||||
/* Find the first news item to display. */
|
|
||||||
NewsItem *ni = _latest_news;
|
|
||||||
for (int n = this->vscroll->GetPosition(); n > 0; n--) {
|
|
||||||
ni = ni->prev;
|
|
||||||
if (ni == nullptr) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill the widget with news items. */
|
/* Fill the widget with news items. */
|
||||||
bool rtl = _current_text_dir == TD_RTL;
|
bool rtl = _current_text_dir == TD_RTL;
|
||||||
Rect news = r.Shrink(WidgetDimensions::scaled.framerect).Indent(this->date_width + WidgetDimensions::scaled.hsep_wide, rtl);
|
Rect news = r.Shrink(WidgetDimensions::scaled.framerect).Indent(this->date_width + WidgetDimensions::scaled.hsep_wide, rtl);
|
||||||
Rect date = r.Shrink(WidgetDimensions::scaled.framerect).WithWidth(this->date_width, rtl);
|
Rect date = r.Shrink(WidgetDimensions::scaled.framerect).WithWidth(this->date_width, rtl);
|
||||||
int y = news.top;
|
int y = news.top;
|
||||||
for (int n = this->vscroll->GetCapacity(); n > 0; n--) {
|
|
||||||
|
auto [first, last] = this->vscroll->GetVisibleRangeIterators(_news);
|
||||||
|
for (auto ni = first; ni != last; ++ni) {
|
||||||
SetDParam(0, ni->date);
|
SetDParam(0, ni->date);
|
||||||
DrawString(date.left, date.right, y, STR_JUST_DATE_TINY, TC_WHITE);
|
DrawString(date.left, date.right, y, STR_JUST_DATE_TINY, TC_WHITE);
|
||||||
|
|
||||||
DrawNewsString(news.left, news.right, y, TC_WHITE, ni);
|
DrawNewsString(news.left, news.right, y, TC_WHITE, &*ni);
|
||||||
y += this->line_height;
|
y += this->line_height;
|
||||||
|
|
||||||
ni = ni->prev;
|
|
||||||
if (ni == nullptr) return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1185,19 +1143,18 @@ struct MessageHistoryWindow : Window {
|
||||||
void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
|
void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
|
||||||
{
|
{
|
||||||
if (!gui_scope) return;
|
if (!gui_scope) return;
|
||||||
this->vscroll->SetCount(_total_news);
|
this->vscroll->SetCount(std::size(_news));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
|
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
|
||||||
{
|
{
|
||||||
if (widget == WID_MH_BACKGROUND) {
|
if (widget == WID_MH_BACKGROUND) {
|
||||||
NewsItem *ni = _latest_news;
|
/* Scheduled window invalidations currently occur after the input loop, which means the scrollbar count
|
||||||
if (ni == nullptr) return;
|
* could be invalid, so ensure it's correct now. Potentially this means that item clicked on might be
|
||||||
|
* different as well. */
|
||||||
for (int n = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_MH_BACKGROUND, WidgetDimensions::scaled.framerect.top); n > 0; n--) {
|
this->vscroll->SetCount(std::size(_news));
|
||||||
ni = ni->prev;
|
auto ni = this->vscroll->GetScrolledItemFromWidget(_news, pt.y, this, widget);
|
||||||
if (ni == nullptr) return;
|
if (ni == std::end(_news)) return;
|
||||||
}
|
|
||||||
|
|
||||||
ShowNewsMessage(ni);
|
ShowNewsMessage(ni);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,6 @@ void ShowLastNewsMessage();
|
||||||
void ShowMessageHistory();
|
void ShowMessageHistory();
|
||||||
bool HideActiveNewsMessage();
|
bool HideActiveNewsMessage();
|
||||||
|
|
||||||
extern NewsItem *_latest_news;
|
const NewsContainer &GetNews();
|
||||||
|
|
||||||
#endif /* NEWS_GUI_H */
|
#endif /* NEWS_GUI_H */
|
||||||
|
|
|
@ -126,8 +126,6 @@ struct NewsAllocatedData {
|
||||||
|
|
||||||
/** Information about a single item of news. */
|
/** Information about a single item of news. */
|
||||||
struct NewsItem {
|
struct NewsItem {
|
||||||
NewsItem *prev; ///< Previous news item
|
|
||||||
NewsItem *next; ///< Next news item
|
|
||||||
StringID string_id; ///< Message text
|
StringID string_id; ///< Message text
|
||||||
TimerGameCalendar::Date date; ///< Calendar date to show for the news
|
TimerGameCalendar::Date date; ///< Calendar date to show for the news
|
||||||
TimerGameEconomy::Date economy_date; ///< Economy date of the news item, never shown but used to calculate age
|
TimerGameEconomy::Date economy_date; ///< Economy date of the news item, never shown but used to calculate age
|
||||||
|
@ -169,4 +167,7 @@ struct CompanyNewsInformation : NewsAllocatedData {
|
||||||
CompanyNewsInformation(const struct Company *c, const struct Company *other = nullptr);
|
CompanyNewsInformation(const struct Company *c, const struct Company *other = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using NewsContainer = std::list<NewsItem>; ///< Container type for storing news items.
|
||||||
|
using NewsIterator = NewsContainer::const_iterator; ///< Iterator type for news items.
|
||||||
|
|
||||||
#endif /* NEWS_TYPE_H */
|
#endif /* NEWS_TYPE_H */
|
||||||
|
|
|
@ -145,9 +145,9 @@ struct StatusBarWindow : Window {
|
||||||
} else if (_pause_mode != PM_UNPAUSED) {
|
} else if (_pause_mode != PM_UNPAUSED) {
|
||||||
StringID msg = (_pause_mode & PM_PAUSED_LINK_GRAPH) ? STR_STATUSBAR_PAUSED_LINK_GRAPH : STR_STATUSBAR_PAUSED;
|
StringID msg = (_pause_mode & PM_PAUSED_LINK_GRAPH) ? STR_STATUSBAR_PAUSED_LINK_GRAPH : STR_STATUSBAR_PAUSED;
|
||||||
DrawString(tr, msg, TC_FROMSTRING, SA_HOR_CENTER);
|
DrawString(tr, msg, TC_FROMSTRING, SA_HOR_CENTER);
|
||||||
} else if (this->ticker_scroll < TICKER_STOP && _statusbar_news_item != nullptr && _statusbar_news_item->string_id != 0) {
|
} else if (this->ticker_scroll < TICKER_STOP && GetStatusbarNews() != nullptr && GetStatusbarNews()->string_id != 0) {
|
||||||
/* Draw the scrolling news text */
|
/* Draw the scrolling news text */
|
||||||
if (!DrawScrollingStatusText(_statusbar_news_item, ScaleGUITrad(this->ticker_scroll), tr.left, tr.right, tr.top, tr.bottom)) {
|
if (!DrawScrollingStatusText(GetStatusbarNews(), ScaleGUITrad(this->ticker_scroll), tr.left, tr.right, tr.top, tr.bottom)) {
|
||||||
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED);
|
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED);
|
||||||
if (Company::IsValidID(_local_company)) {
|
if (Company::IsValidID(_local_company)) {
|
||||||
/* This is the default text */
|
/* This is the default text */
|
||||||
|
|
Loading…
Reference in New Issue