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();
|
||||
|
||||
int i = 0;
|
||||
for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) {
|
||||
TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(news->date);
|
||||
for (const auto &news : GetNews()) {
|
||||
TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(news.date);
|
||||
json.push_back(fmt::format("({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}",
|
||||
ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type,
|
||||
news->reftype1, news->ref1, news->reftype2, news->ref2));
|
||||
ymd.year, ymd.month + 1, ymd.day, news.string_id, news.type,
|
||||
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 InitNewsItemStructs();
|
||||
|
||||
extern const NewsItem *_statusbar_news_item;
|
||||
const NewsItem *GetStatusbarNews();
|
||||
|
||||
void DeleteInvalidEngineNews();
|
||||
void DeleteVehicleNews(VehicleID vid, StringID news);
|
||||
|
|
329
src/news_gui.cpp
329
src/news_gui.cpp
|
@ -21,7 +21,6 @@
|
|||
#include "town.h"
|
||||
#include "sound_func.h"
|
||||
#include "string_func.h"
|
||||
#include "dropdown_func.h"
|
||||
#include "statusbar_gui.h"
|
||||
#include "company_manager_face.h"
|
||||
#include "company_func.h"
|
||||
|
@ -44,25 +43,42 @@
|
|||
|
||||
#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 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
|
||||
static NewsContainer _news; ///< List of news, with newest items at the start.
|
||||
|
||||
/**
|
||||
* Forced news item.
|
||||
* 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
|
||||
* in _forced_news. Otherwise, \a _forced_news variable is nullptr.
|
||||
* If the message being shown was forced by the user, an iterater is stored
|
||||
* 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). */
|
||||
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.
|
||||
|
@ -484,7 +500,7 @@ struct NewsWindow : Window {
|
|||
case WID_N_CLOSEBOX:
|
||||
NewsWindow::duration = 0;
|
||||
this->Close();
|
||||
_forced_news = nullptr;
|
||||
_forced_news = std::end(_news);
|
||||
break;
|
||||
|
||||
case WID_N_CAPTION:
|
||||
|
@ -622,29 +638,21 @@ static void ShowNewspaper(const NewsItem *ni)
|
|||
}
|
||||
|
||||
/** 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);
|
||||
|
||||
_statusbar_news_item = ni;
|
||||
_statusbar_news = ni;
|
||||
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_TICKER);
|
||||
}
|
||||
|
||||
/** Initialize the news-items data structures */
|
||||
void InitNewsItemStructs()
|
||||
{
|
||||
for (NewsItem *ni = _oldest_news; ni != nullptr; ) {
|
||||
NewsItem *next = ni->next;
|
||||
delete ni;
|
||||
ni = next;
|
||||
}
|
||||
|
||||
_total_news = 0;
|
||||
_oldest_news = nullptr;
|
||||
_latest_news = nullptr;
|
||||
_forced_news = nullptr;
|
||||
_current_news = nullptr;
|
||||
_statusbar_news_item = nullptr;
|
||||
_news.clear();
|
||||
_forced_news = std::end(_news);
|
||||
_current_news = std::end(_news);
|
||||
_statusbar_news = std::end(_news);
|
||||
NewsWindow::duration = 0;
|
||||
}
|
||||
|
||||
|
@ -654,7 +662,7 @@ void InitNewsItemStructs()
|
|||
*/
|
||||
static bool ReadyForNextTickerItem()
|
||||
{
|
||||
const NewsItem *ni = _statusbar_news_item;
|
||||
const NewsItem *ni = GetStatusbarNews();
|
||||
if (ni == nullptr) return true;
|
||||
|
||||
/* Ticker message
|
||||
|
@ -668,8 +676,7 @@ static bool ReadyForNextTickerItem()
|
|||
*/
|
||||
static bool ReadyForNextNewsItem()
|
||||
{
|
||||
const NewsItem *ni = _forced_news == nullptr ? _current_news : _forced_news;
|
||||
if (ni == nullptr) return true;
|
||||
if (_forced_news == std::end(_news) && _current_news == std::end(_news)) return true;
|
||||
|
||||
/* neither newsticker nor newspaper are running */
|
||||
return (NewsWindow::duration <= 0 || FindWindowById(WC_NEWS_WINDOW, 0) == nullptr);
|
||||
|
@ -678,116 +685,98 @@ static bool ReadyForNextNewsItem()
|
|||
/** Move to the next ticker item */
|
||||
static void MoveToNextTickerItem()
|
||||
{
|
||||
assert(!std::empty(_news));
|
||||
|
||||
/* There is no status bar, so no reason to show news;
|
||||
* especially important with the end game screen when
|
||||
* there is no status bar but possible news. */
|
||||
if (FindWindowById(WC_STATUS_BAR, 0) == nullptr) return;
|
||||
|
||||
/* if we're not at the last item, then move on */
|
||||
while (_statusbar_news_item != _latest_news) {
|
||||
_statusbar_news_item = (_statusbar_news_item == nullptr) ? _oldest_news : _statusbar_news_item->next;
|
||||
const NewsItem *ni = _statusbar_news_item;
|
||||
const NewsType type = ni->type;
|
||||
/* if we're not at the latest item, then move on */
|
||||
while (_statusbar_news != std::begin(_news)) {
|
||||
--_statusbar_news;
|
||||
const NewsType type = _statusbar_news->type;
|
||||
|
||||
/* 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()) {
|
||||
default: NOT_REACHED();
|
||||
case ND_OFF: // Off - show nothing only a small reminder in the status bar
|
||||
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SHOW_REMINDER);
|
||||
break;
|
||||
return;
|
||||
|
||||
case ND_SUMMARY: // Summary - show ticker
|
||||
ShowTicker(ni);
|
||||
break;
|
||||
ShowTicker(_statusbar_news);
|
||||
return;
|
||||
|
||||
case ND_FULL: // Full - show newspaper, skipped here
|
||||
continue;
|
||||
break;;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** Move to the next news item */
|
||||
static void MoveToNextNewsItem()
|
||||
{
|
||||
assert(!std::empty(_news));
|
||||
|
||||
/* There is no status bar, so no reason to show news;
|
||||
* especially important with the end game screen when
|
||||
* there is no status bar but possible news. */
|
||||
if (FindWindowById(WC_STATUS_BAR, 0) == nullptr) return;
|
||||
|
||||
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 */
|
||||
while (_current_news != _latest_news) {
|
||||
_current_news = (_current_news == nullptr) ? _oldest_news : _current_news->next;
|
||||
const NewsItem *ni = _current_news;
|
||||
const NewsType type = ni->type;
|
||||
/* if we're not at the latest item, then move on */
|
||||
while (_current_news != std::begin(_news)) {
|
||||
--_current_news;
|
||||
const NewsType type = _current_news->type;
|
||||
|
||||
/* 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()) {
|
||||
default: NOT_REACHED();
|
||||
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
|
||||
continue;
|
||||
break;;
|
||||
|
||||
case ND_FULL: // Full - show newspaper
|
||||
ShowNewspaper(ni);
|
||||
break;
|
||||
ShowNewspaper(&*_current_news);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
/* When we're the current news, go to the previous item first;
|
||||
* 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 displayed item (newspapers) */
|
||||
MoveToNextNewsItem();
|
||||
}
|
||||
|
||||
if (_statusbar_news_item == ni) {
|
||||
if (_statusbar_news == ni) {
|
||||
/* When we're the current news, go to the previous item first;
|
||||
* 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) */
|
||||
InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); // invalidate the statusbar
|
||||
MoveToNextTickerItem();
|
||||
}
|
||||
|
||||
delete ni;
|
||||
|
||||
SetWindowDirty(WC_MESSAGE_HISTORY, 0);
|
||||
/* Delete the news from the news queue. */
|
||||
return _news.erase(ni);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -829,27 +818,14 @@ void AddNewsItem(StringID string, NewsType type, NewsFlag flags, NewsReferenceTy
|
|||
if (_game_mode == GM_MENU) return;
|
||||
|
||||
/* Create new news item node */
|
||||
NewsItem *ni = new NewsItem(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;
|
||||
_news.emplace_front(string, type, flags, reftype1, ref1, reftype2, ref2, data);
|
||||
|
||||
/* Keep the number of stored news items to a managable number */
|
||||
if (_total_news > MAX_NEWS_AMOUNT) {
|
||||
DeleteNewsItem(_oldest_news);
|
||||
if (std::size(_news) > MAX_NEWS_AMOUNT) {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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)
|
||||
{
|
||||
NewsItem *ni = _oldest_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;
|
||||
}
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -937,15 +929,9 @@ void DeleteVehicleNews(VehicleID vid, StringID news)
|
|||
*/
|
||||
void DeleteStationNews(StationID sid)
|
||||
{
|
||||
NewsItem *ni = _oldest_news;
|
||||
|
||||
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;
|
||||
}
|
||||
DeleteNews([&](const auto &ni) {
|
||||
return (ni.reftype1 == NR_STATION && ni.ref1 == sid) || (ni.reftype2 == NR_STATION && ni.ref2 == sid);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -954,15 +940,9 @@ void DeleteStationNews(StationID sid)
|
|||
*/
|
||||
void DeleteIndustryNews(IndustryID iid)
|
||||
{
|
||||
NewsItem *ni = _oldest_news;
|
||||
|
||||
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;
|
||||
}
|
||||
DeleteNews([&](const auto &ni) {
|
||||
return (ni.reftype1 == NR_INDUSTRY && ni.ref1 == iid) || (ni.reftype2 == NR_INDUSTRY && ni.ref2 == iid);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -970,25 +950,17 @@ void DeleteIndustryNews(IndustryID iid)
|
|||
*/
|
||||
void DeleteInvalidEngineNews()
|
||||
{
|
||||
NewsItem *ni = _oldest_news;
|
||||
|
||||
while (ni != nullptr) {
|
||||
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;
|
||||
}
|
||||
DeleteNews([](const auto &ni) {
|
||||
return (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()));
|
||||
});
|
||||
}
|
||||
|
||||
static void RemoveOldNewsItems()
|
||||
{
|
||||
NewsItem *next;
|
||||
for (NewsItem *cur = _oldest_news; _total_news > MIN_NEWS_AMOUNT && cur != nullptr; cur = next) {
|
||||
next = cur->next;
|
||||
if (TimerGameEconomy::date - _news_type_data[cur->type].age * _settings_client.gui.news_message_timeout > cur->economy_date) DeleteNewsItem(cur);
|
||||
}
|
||||
DeleteNews<MIN_NEWS_AMOUNT>([](const auto &ni) {
|
||||
return TimerGameEconomy::date - _news_type_data[ni.type].age * _settings_client.gui.news_message_timeout > ni.economy_date;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -999,17 +971,17 @@ static void RemoveOldNewsItems()
|
|||
*/
|
||||
void ChangeVehicleNews(VehicleID from_index, VehicleID to_index)
|
||||
{
|
||||
for (NewsItem *ni = _oldest_news; ni != nullptr; ni = ni->next) {
|
||||
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->flags & NF_VEHICLE_PARAM0 && ni->params[0].data == from_index) ni->params[0] = to_index;
|
||||
for (auto &ni : _news) {
|
||||
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.flags & NF_VEHICLE_PARAM0 && ni.params[0].data == from_index) ni.params[0] = to_index;
|
||||
}
|
||||
}
|
||||
|
||||
void NewsLoop()
|
||||
{
|
||||
/* no news item yet */
|
||||
if (_total_news == 0) return;
|
||||
if (std::empty(_news)) return;
|
||||
|
||||
static TimerGameEconomy::Month _last_clean_month = 0;
|
||||
|
||||
|
@ -1023,9 +995,9 @@ void NewsLoop()
|
|||
}
|
||||
|
||||
/** 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 */
|
||||
CloseWindowById(WC_NEWS_WINDOW, 0);
|
||||
|
@ -1033,9 +1005,9 @@ static void ShowNewsMessage(const NewsItem *ni)
|
|||
/* setup forced news item */
|
||||
_forced_news = ni;
|
||||
|
||||
if (_forced_news != nullptr) {
|
||||
if (_forced_news != std::end(_news)) {
|
||||
CloseWindowById(WC_NEWS_WINDOW, 0);
|
||||
ShowNewspaper(ni);
|
||||
ShowNewspaper(&*ni);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1054,26 +1026,26 @@ bool HideActiveNewsMessage()
|
|||
/** Show previous news item */
|
||||
void ShowLastNewsMessage()
|
||||
{
|
||||
const NewsItem *ni = nullptr;
|
||||
if (_total_news == 0) {
|
||||
return;
|
||||
} else if (_forced_news == nullptr) {
|
||||
if (std::empty(_news)) return;
|
||||
|
||||
NewsIterator ni;
|
||||
if (_forced_news == std::end(_news)) {
|
||||
/* 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 */
|
||||
if (_current_news == nullptr) {
|
||||
if (_current_news == std::end(_news)) {
|
||||
/* 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. */
|
||||
ni = _latest_news;
|
||||
* Treat this as if _forced_news reached the oldest news; so, wrap around and start anew with the latest. */
|
||||
ni = std::begin(_news);
|
||||
} else {
|
||||
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 */
|
||||
ni = _latest_news;
|
||||
ni = std::begin(_news);
|
||||
} else {
|
||||
/* 'Scrolling' through news history show each one in turn */
|
||||
ni = _forced_news->prev;
|
||||
ni = std::next(_forced_news);
|
||||
}
|
||||
bool wrap = false;
|
||||
for (;;) {
|
||||
|
@ -1082,11 +1054,11 @@ void ShowLastNewsMessage()
|
|||
break;
|
||||
}
|
||||
|
||||
ni = ni->prev;
|
||||
if (ni == nullptr) {
|
||||
++ni;
|
||||
if (ni == std::end(_news)) {
|
||||
if (wrap) break;
|
||||
/* We have reached the oldest news, start anew with the latest */
|
||||
ni = _latest_news;
|
||||
ni = std::begin(_news);
|
||||
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
|
||||
{
|
||||
if (widget != WID_MH_BACKGROUND || _total_news == 0) 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;
|
||||
}
|
||||
if (widget != WID_MH_BACKGROUND || std::empty(_news)) return;
|
||||
|
||||
/* Fill the widget with news items. */
|
||||
bool rtl = _current_text_dir == TD_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);
|
||||
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);
|
||||
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;
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
if (widget == WID_MH_BACKGROUND) {
|
||||
NewsItem *ni = _latest_news;
|
||||
if (ni == nullptr) return;
|
||||
|
||||
for (int n = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_MH_BACKGROUND, WidgetDimensions::scaled.framerect.top); n > 0; n--) {
|
||||
ni = ni->prev;
|
||||
if (ni == nullptr) return;
|
||||
}
|
||||
/* Scheduled window invalidations currently occur after the input loop, which means the scrollbar count
|
||||
* could be invalid, so ensure it's correct now. Potentially this means that item clicked on might be
|
||||
* different as well. */
|
||||
this->vscroll->SetCount(std::size(_news));
|
||||
auto ni = this->vscroll->GetScrolledItemFromWidget(_news, pt.y, this, widget);
|
||||
if (ni == std::end(_news)) return;
|
||||
|
||||
ShowNewsMessage(ni);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,6 @@ void ShowLastNewsMessage();
|
|||
void ShowMessageHistory();
|
||||
bool HideActiveNewsMessage();
|
||||
|
||||
extern NewsItem *_latest_news;
|
||||
const NewsContainer &GetNews();
|
||||
|
||||
#endif /* NEWS_GUI_H */
|
||||
|
|
|
@ -126,8 +126,6 @@ struct NewsAllocatedData {
|
|||
|
||||
/** Information about a single item of news. */
|
||||
struct NewsItem {
|
||||
NewsItem *prev; ///< Previous news item
|
||||
NewsItem *next; ///< Next news item
|
||||
StringID string_id; ///< Message text
|
||||
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
|
||||
|
@ -169,4 +167,7 @@ struct CompanyNewsInformation : NewsAllocatedData {
|
|||
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 */
|
||||
|
|
|
@ -145,9 +145,9 @@ struct StatusBarWindow : Window {
|
|||
} else if (_pause_mode != PM_UNPAUSED) {
|
||||
StringID msg = (_pause_mode & PM_PAUSED_LINK_GRAPH) ? STR_STATUSBAR_PAUSED_LINK_GRAPH : STR_STATUSBAR_PAUSED;
|
||||
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 */
|
||||
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);
|
||||
if (Company::IsValidID(_local_company)) {
|
||||
/* This is the default text */
|
||||
|
|
Loading…
Reference in New Issue