Freeciv21
Develop your civilization from humble roots to a global empire
upkeep_widget.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2023 Louis Moureaux <m_louis30@yahoo.com>
3  *
4  * SPDX-License-Identifier: GPLv3-or-later
5  */
6 
7 #include "upkeep_widget.h"
8 
9 // common
10 #include "control.h"
11 #include "fc_types.h"
12 #include "game.h"
13 
14 // client
15 #include "citydlg.h"
16 #include "client_main.h"
17 #include "climisc.h"
18 #include "helpdata.h"
19 #include "helpdlg.h"
20 #include "page_game.h"
21 #include "qtg_cxxside.h"
22 #include "text.h"
23 #include "tileset/tilespec.h"
24 #include "tooltips.h"
26 #include "utils/unit_quick_menu.h"
27 #include "views/view_map_common.h"
28 
29 #include <QContextMenuEvent>
30 #include <QMenu>
31 #include <QPainter>
32 
33 namespace freeciv {
34 
41 namespace {
42 constexpr auto BuildingRole =
43  Qt::UserRole + 1;
44 constexpr auto UnitRole = Qt::UserRole + 2;
45 } // namespace
46 
51  : QListView(parent), m_model(new QStandardItemModel(this))
52 {
53  setModel(m_model);
54 
55  setEditTriggers(NoEditTriggers);
56  setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
57  setSelectionMode(NoSelection);
58  setSizeAdjustPolicy(AdjustToContents);
59  setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
60  setVerticalScrollMode(ScrollPerPixel);
61 
62  connect(this, &QAbstractItemView::doubleClicked, this,
64 }
65 
70 {
71  m_model->clear();
72  const auto city = game_city_by_number(m_city);
73  if (!city) {
74  return;
75  }
76 
77  // We assume that improvement icons are at most as wide as the city sprite
78  // Otherwise they will be cropped.
79  const auto icon_width = tileset_unit_width(get_tileset());
80 
81  // Improvements and wonders
82  {
84  const auto targets_used = collect_already_built_targets(targets, city);
85 
87  name_and_sort_items(targets, targets_used, items, false, city);
88 
89  for (int i = 0; i < targets_used; i++) {
90  const auto *building = items[i].item.value.building;
91  const auto &sprite = *get_building_sprite(tileset, building);
92 
93  // Center the sprite
94  auto rect = QRect(QPoint(), sprite.size());
95  rect.moveCenter(QPoint(icon_width / 2, sprite.height() / 2));
96 
97  QPixmap pixmap(icon_width, sprite.height());
98  pixmap.fill(Qt::transparent);
99  QPainter p(&pixmap);
100  p.drawPixmap(rect, sprite);
101  p.end();
102 
103  auto item = new QStandardItem;
104  item->setData(pixmap, Qt::DecorationRole);
105  item->setData(improvement_number(building), BuildingRole);
106  item->setEditable(false);
107  item->setToolTip(
108  get_tooltip_improvement(building, city, true).trimmed());
109  m_model->appendRow(item);
110  }
111  }
112 
113  // Units
114  {
115  const bool playing = (client.conn.playing == nullptr
117  const auto units =
118  playing ? city->units_supported : city->client.info_units_supported;
119 
120  unit_list_iterate(units, unit)
121  {
122  auto pixmap = QPixmap(icon_width,
124  pixmap.fill(Qt::transparent);
125  put_unit(unit, &pixmap, QPoint());
126 
127  auto free_unhappy = get_city_bonus(city, EFT_MAKE_CONTENT_MIL);
128  const auto happy_cost = city_unit_unhappiness(unit, &free_unhappy);
129  put_unit_city_overlays(unit, &pixmap, 0,
131  unit->upkeep, happy_cost);
132 
133  auto *item = new QStandardItem();
134  item->setData(pixmap, Qt::DecorationRole);
135  item->setData(unit->id, Qt::UserRole + 2);
136  item->setEditable(false);
137  item->setToolTip(unit_description(unit));
138  m_model->appendRow(item);
139  }
141  }
142 }
143 
147 void upkeep_widget::set_city(int city_id)
148 {
149  if (city_id != m_city) {
150  m_city = city_id;
151  refresh();
152  }
153 }
154 
159 {
160  int height = 0;
161  for (int i = 0; i < m_model->rowCount(); ++i) {
162  height += sizeHintForRow(i);
163  }
164  // The 6 extra pixels seem to come from the style
165  return QSize(6 + sizeHintForColumn(0), height);
166 }
167 
171 QSize upkeep_widget::minimumSizeHint() const { return QSize(0, 0); }
172 
176 void upkeep_widget::contextMenuEvent(QContextMenuEvent *event)
177 {
178  const auto index = indexAt(event->pos());
179  const auto item = m_model->itemFromIndex(index);
180  if (!item) {
181  return;
182  }
183 
185  if (!city) {
186  return;
187  }
188 
189  if (const auto data = item->data(BuildingRole); data.isValid()) {
190  const auto building = improvement_by_number(data.toInt());
191  fc_assert_ret(building);
192 
193  auto menu = new QMenu;
194  menu->setAttribute(Qt::WA_DeleteOnClose);
195  improvement_seller::add_to_menu(window(), menu, city, data.toInt());
196  menu->addSeparator();
197  menu->addAction(_("Show in Help"), [=] {
199  is_great_wonder(building) ? HELP_WONDER
200  : HELP_IMPROVEMENT);
201  });
202  menu->popup(event->globalPos());
203  } else if (const auto data = item->data(UnitRole); data.isValid()) {
204  const auto unit = game_unit_by_number(data.toInt());
205  if (!unit) {
206  return;
207  }
208 
209  auto menu = new QMenu;
210  menu->setAttribute(Qt::WA_DeleteOnClose);
211  add_quick_unit_actions(menu, {unit});
212  menu->addSeparator();
213  menu->addAction(_("Show in Help"), [=] {
215  });
216  menu->popup(event->globalPos());
217  }
218 }
219 
224 {
225  if (event->type() == TilesetChanged) {
226  refresh();
227  return true;
228  }
229  return QListView::event(event);
230 }
231 
236 void upkeep_widget::item_double_clicked(const QModelIndex &index)
237 {
238  const auto item = m_model->itemFromIndex(index);
239  if (!item) {
240  return;
241  }
242 
243  if (const auto data = item->data(BuildingRole); data.isValid()) {
244  auto seller = improvement_seller(window(), m_city, data.toInt());
245  seller();
246  } else if (const auto data = item->data(UnitRole); data.isValid()) {
247  auto unit = game_unit_by_number(data.toInt());
248  if (!unit || unit->owner != client.conn.playing
249  || !can_client_issue_orders()) {
250  return;
251  }
252  unit_focus_set(nullptr);
254  queen()->city_overlay->dont_focus = true;
256  }
257 }
258 
259 } // namespace freeciv
struct player * city_owner(const struct city *pcity)
Return the owner of the city.
Definition: city.cpp:1083
int city_unit_unhappiness(const unit *punit, int *free_unhappy)
Query unhappiness caused by a given unit.
Definition: city.cpp:2921
void popdown_city_dialog()
Closes the city overlay.
Definition: citydlg.cpp:2264
bool dont_focus
Definition: citydlg.h:300
Helper class to safely sell a city improvement.
static QAction * add_to_menu(QWidget *parent, QMenu *menu, const city *city, int improvement_id)
Adds a menu item to sell an improvement in a city.
QSize minimumSizeHint() const override
Reimplemented to allow for tiny tilesets.
QStandardItemModel * m_model
Definition: upkeep_widget.h:35
void contextMenuEvent(QContextMenuEvent *event) override
Reimplemented to provide the improvement and unit actions.
bool event(QEvent *event) override
Reimplemented to handle tileset changes.
void refresh()
Updates the widget from the city.
void set_city(int city_id)
Changes the city displayed by this widget.
void item_double_clicked(const QModelIndex &index)
Called when an item is double clicked.
upkeep_widget(QWidget *parent=nullptr)
Constructor.
QSize viewportSizeHint() const override
Reimplemented to provide a meaningful size hint.
city_dialog * city_overlay
Definition: page_game.h:83
struct civclient client
bool can_client_issue_orders()
Returns TRUE iff the client can issue orders (such as giving unit commands).
void name_and_sort_items(struct universal *targets, int num_targets, struct item *items, bool show_cost, struct city *pcity)
Takes an array of compound ids (cids).
Definition: climisc.cpp:596
int collect_already_built_targets(struct universal *targets, struct city *pcity)
Collect the cids of all improvements which are built in the given city.
Definition: climisc.cpp:806
#define MAX_NUM_PRODUCTION_TARGETS
Definition: climisc.h:73
void unit_focus_add(struct unit *punit)
Adds this unit to the list of units in focus.
Definition: control.cpp:534
void unit_focus_set(struct unit *punit)
Sets the focus unit directly.
Definition: control.cpp:479
int get_city_bonus(const struct city *pcity, enum effect_type effect_type, enum vision_layer vlayer)
Returns the effect bonus at a city.
Definition: effects.cpp:688
enum event_type event
Definition: events.cpp:68
#define _(String)
Definition: fcintl.h:50
struct unit * game_unit_by_number(int id)
Find unit out of all units in game: now uses fast idex method, instead of looking through all units o...
Definition: game.cpp:112
struct city * game_city_by_number(int id)
Often used function to get a city pointer from a city ID.
Definition: game.cpp:103
@ HELP_IMPROVEMENT
Definition: helpdata.h:24
@ HELP_UNIT
Definition: helpdata.h:23
@ HELP_WONDER
Definition: helpdata.h:25
void popup_help_dialog_typed(const char *item, enum help_page_type htype)
Popup the help dialog to display help on the given string topic from the given section.
Definition: helpdlg.cpp:56
struct impr_type * improvement_by_number(const Impr_type_id id)
Returns the improvement type for the given index/ID.
Impr_type_id improvement_number(const struct impr_type *pimprove)
Return the improvement index.
bool is_great_wonder(const struct impr_type *pimprove)
Is this building a great wonder?
const char * improvement_name_translation(const struct impr_type *pimprove)
Return the (translated) name of the given improvement.
#define fc_assert_ret(condition)
Definition: log.h:112
Definition: path.cpp:10
void add_quick_unit_actions(QMenu *menu, const std::vector< unit * > &units)
Adds a small set of common unit actions to a menu.
pageGame * queen()
Return game instandce.
Definition: page_game.cpp:557
Definition: city.h:291
struct city::@15::@18 client
struct unit_list * units_supported
Definition: city.h:377
struct connection conn
Definition: client_main.h:89
struct player * playing
Definition: connection.h:142
Definition: climisc.h:66
struct universal item
Definition: climisc.h:67
Definition: unit.h:134
int upkeep[O_LAST]
Definition: unit.h:145
int id
Definition: unit.h:141
struct player * owner
Definition: unit.h:139
universals_u value
Definition: fc_types.h:739
const QString unit_description(const unit *punit)
Returns the unit description.
Definition: text.cpp:551
int tileset_unit_width(const struct tileset *t)
Return the unit tile width of the current tileset.
Definition: tilespec.cpp:426
int tileset_unit_layout_offset_y(const struct tileset *t)
Offset to layout extra unit sprites, such as upkeep.
Definition: tilespec.cpp:485
QEvent::Type TilesetChanged
An event type sent to all widgets when the current tileset changes.
Definition: tilespec.cpp:719
struct tileset * get_tileset()
Returns the tileset.
Definition: tilespec.cpp:326
int tileset_unit_with_upkeep_height(const struct tileset *t)
Suitable canvas height for a unit icon that includes upkeep sprites.
Definition: tilespec.cpp:473
const QPixmap * get_building_sprite(const struct tileset *t, const struct impr_type *pimprove)
Return the sprite for the building/improvement.
Definition: tilespec.cpp:3394
QString get_tooltip_improvement(const impr_type *building, struct city *pcity, bool ext)
Returns improvement properties to append in tooltip ext is used to get extra info from help.
Definition: tooltips.cpp:82
const struct impr_type * building
Definition: fc_types.h:579
#define unit_list_iterate(unitlist, punit)
Definition: unitlist.h:25
#define unit_list_iterate_end
Definition: unitlist.h:27
const char * unit_name_translation(const struct unit *punit)
Return the (translated) name of the unit.
Definition: unittype.cpp:1265
void put_unit_city_overlays(const unit *punit, QPixmap *pcanvas, int canvas_x, int canvas_y, const int *upkeep_cost, int happy_cost)
Draw food, gold, and shield upkeep values on the unit.
void put_unit(const struct unit *punit, QPixmap *pcanvas, const QPoint &canvas_loc)
Draw the given unit onto the canvas store at the given location.