Freeciv21
Develop your civilization from humble roots to a global empire
view_cities.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 1996-2023 Freeciv21 and Freeciv contributors. This file is
3  part of Freeciv21. Freeciv21 is free software: you can redistribute it
4  and/or modify it under the terms of the GNU General Public License as
5  published by the Free Software Foundation, either version 3 of the
6  License, or (at your option) any later version. You should have received
7  a copy of the GNU General Public License along with Freeciv21. If not,
8  see https://www.gnu.org/licenses/.
9  */
10 
11 /*
12  * This file contains functions to generate the table based GUI for
13  * the city view (formally known as the city report).
14  */
15 
16 // Qt
17 #include <QApplication>
18 #include <QHeaderView>
19 #include <QVBoxLayout>
20 // utility
21 #include "fcintl.h"
22 // common
23 #include "citydlg_common.h"
24 #include "game.h"
25 #include "global_worklist.h"
26 // client
27 #include "cityrep_g.h"
28 #include "client_main.h"
29 #include "fc_client.h"
30 #include "governor.h"
31 #include "hudwidget.h"
32 #include "icons.h"
33 #include "page_game.h"
34 #include "qtg_cxxside.h"
35 #include "top_bar.h"
36 #include "views/view_cities.h"
37 #include "views/view_map.h"
38 
42 bool city_sort_model::lessThan(const QModelIndex &left,
43  const QModelIndex &right) const
44 {
45  QVariant qleft;
46  QVariant qright;
47  int i;
48  QByteArray l_bytes;
49  QByteArray r_bytes;
50 
51  qleft = sourceModel()->data(left);
52  qright = sourceModel()->data(right);
53  l_bytes = qleft.toString().toLocal8Bit();
54  r_bytes = qright.toString().toLocal8Bit();
55  i = cityrepfield_compare(l_bytes.data(), r_bytes.data());
56 
57  return i >= 0;
58 }
59 
64  : QItemDelegate(parent)
65 {
66  QFont f = QApplication::font();
67  QFontMetrics fm(f);
68 
69  item_height = fm.height() + 4;
70 }
71 
75 void city_item_delegate::paint(QPainter *painter,
76  const QStyleOptionViewItem &option,
77  const QModelIndex &index) const
78 {
79  QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option);
80  QString txt;
81  QFont font;
82  QPalette palette;
83  struct city_report_spec spec;
84  spec = city_report_specs[index.column()];
85  txt = spec.tagname;
86  if (txt == QLatin1String("cityname")) {
87  font.setCapitalization(QFont::SmallCaps);
88  font.setBold(true);
89  opt.font = font;
90  }
91  if (txt == QLatin1String("hstate_verbose")) {
92  font.setItalic(true);
93  opt.font = font;
94  }
95  if (txt == QLatin1String("prodplus")) {
96  txt = index.data().toString();
97  if (txt.toInt() < 0) {
98  font.setBold(true);
99  palette.setColor(QPalette::Text, QColor(255, 0, 0));
100  opt.font = font;
101  opt.palette = palette;
102  }
103  }
104 
105  QItemDelegate::paint(painter, opt, index);
106 }
107 
111 QSize city_item_delegate::sizeHint(const QStyleOptionViewItem &option,
112  const QModelIndex &index) const
113 {
114  QSize s = QItemDelegate::sizeHint(option, index);
115 
116  s.setHeight(item_height + 4);
117  return s;
118 }
119 
123 city_item::city_item(city *pcity) : QObject() { i_city = pcity; }
124 
129 
133 bool city_item::setData(int column, const QVariant &value, int role)
134 {
135  Q_UNUSED(role)
136  Q_UNUSED(column)
137  Q_UNUSED(value)
138  return false;
139 }
140 
144 QVariant city_item::data(int column, int role) const
145 {
146  if (role == Qt::UserRole && column == 0) {
147  return QVariant::fromValue((void *) i_city);
148  }
149  if (role != Qt::DisplayRole) {
150  return QVariant();
151  }
152  const auto spec = city_report_specs[column];
153  return spec.func(i_city, spec.data).trimmed();
154 }
155 
159 city_model::city_model(QObject *parent) : QAbstractListModel(parent)
160 {
161  populate();
162 }
163 
168 {
169  qDeleteAll(city_list);
170  city_list.clear();
171 }
172 
177 {
178  emit dataChanged(index(row, 0), index(row, columnCount() - 1));
179 }
180 
184 QVariant city_model::data(const QModelIndex &index, int role) const
185 {
186  if (!index.isValid()) {
187  return QVariant();
188  }
189  if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0
190  && index.column() < columnCount()) {
191  return city_list[index.row()]->data(index.column(), role);
192  }
193  return QVariant();
194 }
195 
199 bool city_model::setData(const QModelIndex &index, const QVariant &value,
200  int role)
201 {
202  if (!index.isValid() || role != Qt::DisplayRole) {
203  return false;
204  }
205  if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0
206  && index.column() < columnCount()) {
207  bool change =
208  city_list[index.row()]->setData(index.column(), value, role);
209 
210  if (change) {
211  notify_city_changed(index.row());
212  }
213  return change;
214  }
215  return false;
216 }
217 
221 QVariant city_model::headerData(int section, Qt::Orientation orientation,
222  int role) const
223 {
224  const auto &spec = city_report_specs[section];
225 
226  if (orientation == Qt::Horizontal && section < NUM_CREPORT_COLS) {
227  if (role == Qt::DisplayRole) {
228  QString buf = QStringLiteral("%1\n%2").arg(
229  spec.title1 ? spec.title1 : "", spec.title2 ? spec.title2 : "");
230  QIcon i = hIcon::i()->get(spec.tagname);
231  if (!i.isNull()) { // icon exists for that header
232  return QString();
233  }
234  return buf.trimmed();
235  }
236  if (role == Qt::ToolTipRole) {
237  return QString(spec.explanation);
238  }
239  if (role == Qt::DecorationRole) {
240  QIcon i = hIcon::i()->get(spec.tagname);
241  if (!i.isNull()) {
242  return i;
243  }
244  }
245  }
246  return QVariant();
247 }
248 
252 QVariant city_model::menu_data(int section) const
253 {
254  struct city_report_spec spec;
255 
256  if (section < NUM_CREPORT_COLS) {
257  spec = city_report_specs[section];
258  return QString(spec.explanation);
259  }
260  return QVariant();
261 }
262 
266 QVariant city_model::hide_data(int section) const
267 {
268  struct city_report_spec spec;
269 
270  if (section < NUM_CREPORT_COLS) {
271  spec = city_report_specs[section];
272  return spec.show;
273  }
274  return QVariant();
275 }
276 
281 {
282  city_item *ci;
283 
284  if (client_has_player()) {
286  {
287  ci = new city_item(pcity);
288  city_list << ci;
289  }
291  } else {
292  cities_iterate(pcity)
293  {
294  ci = new city_item(pcity);
295  city_list << ci;
296  }
298  }
299 }
300 
304 void city_model::city_changed(struct city *pcity)
305 {
306  city_item *item;
307 
308  beginResetModel();
309  endResetModel();
310  for (int i = 0; i < city_list.count(); i++) {
311  item = city_list.at(i);
312  if (pcity == item->get_city()) {
314  }
315  }
316 }
317 
322 {
323  city_list.clear();
324  beginResetModel();
325  populate();
326  endResetModel();
327 }
328 
333 {
334  QItemSelection selection;
335  QModelIndex i;
336  struct city *pcity;
337  QVariant qvar;
338 
339  if (selected_cities.isEmpty()) {
340  return;
341  }
342  for (int j = 0; j < filter_model->rowCount(); j++) {
343  i = filter_model->index(j, 0);
344  qvar = i.data(Qt::UserRole);
345  if (qvar.isNull()) {
346  continue;
347  }
348  pcity = reinterpret_cast<city *>(qvar.value<void *>());
349  if (selected_cities.contains(pcity)) {
350  selection.append(QItemSelectionRange(i));
351  }
352  }
353  selectionModel()->select(selection,
354  QItemSelectionModel::Rows
355  | QItemSelectionModel::SelectCurrent);
356 }
357 
362 {
363  cr = ctr;
364  c_i_d = new city_item_delegate(this);
365  setItemDelegate(c_i_d);
366  list_model = new city_model(this);
368  filter_model->setDynamicSortFilter(true);
369  filter_model->setSourceModel(list_model);
370  filter_model->setFilterRole(Qt::DisplayRole);
371  setModel(filter_model);
372  setRootIsDecorated(false);
373  setAllColumnsShowFocus(true);
374  setSortingEnabled(true);
375  setSelectionMode(QAbstractItemView::ExtendedSelection);
376  setSelectionBehavior(QAbstractItemView::SelectRows);
377  setItemsExpandable(false);
378  setAutoScroll(true);
379  setProperty("uniformRowHeights", "true");
380  setAlternatingRowColors(true);
381  header()->setContextMenuPolicy(Qt::CustomContextMenu);
382  header()->setMinimumSectionSize(10);
383  setContextMenuPolicy(Qt::CustomContextMenu);
384  hide_columns();
385  connect(header(), &QWidget::customContextMenuRequested, this,
387  connect(selectionModel(), &QItemSelectionModel::selectionChanged, this,
389  connect(this, &QAbstractItemView::doubleClicked, this,
391  connect(this, &QWidget::customContextMenuRequested, this,
393 }
394 
398 void city_widget::city_doubleclick(const QModelIndex &index)
399 {
400  Q_UNUSED(index);
401  city_view();
402 }
403 
408 {
409  struct city *pcity;
410 
411  if (selected_cities.isEmpty()) {
412  return;
413  }
414  pcity = selected_cities[0];
415 
416  Q_ASSERT(pcity != nullptr);
418  queen()->mapview_wdg->center_on_tile(pcity->tile);
419  }
420  real_city_dialog_popup(pcity);
421 }
422 
427 {
428  struct worklist empty;
429  worklist_init(&empty);
430 
431  const auto saved_selection =
432  selected_cities; // Copy to avoid invalidations
433  for (auto *pcity : saved_selection) {
434  Q_ASSERT(pcity != nullptr);
435  city_set_worklist(pcity, &empty);
436  }
437 }
438 
443 {
444  auto saved_selection = selected_cities; // Copy to avoid invalidations
445 
446  // Sort cheapest first
447  std::sort(saved_selection.begin(), saved_selection.end(),
448  [](const city *lhs, const city *rhs) {
449  return lhs->client.buy_cost < rhs->client.buy_cost;
450  });
451 
452  // Buy
453  for (auto *pcity : saved_selection) {
454  cityrep_buy(pcity);
455  }
456 }
457 
462 {
463  struct city *pcity;
464 
465  if (selected_cities.isEmpty()) {
466  return;
467  }
468  pcity = selected_cities[0];
469  Q_ASSERT(pcity != nullptr);
470  queen()->mapview_wdg->center_on_tile(pcity->tile);
472 }
473 
478 {
479  QMap<QString, cid> custom_labels;
480  QMap<QString, int> cma_labels;
481  QMenu *some_menu;
482  QMenu *tmp2_menu;
483  QMenu *tmp_menu;
484  bool select_only = false;
485  int sell_gold;
486  QMenu *list_menu;
487  QAction cty_view(style()->standardIcon(QStyle::SP_CommandLink),
488  Q_("?verb:View"), 0);
489  sell_gold = 0;
490  if (selected_cities.isEmpty()) {
491  select_only = true;
492  }
493  for (auto *pcity : qAsConst(selected_cities)) {
494  sell_gold = sell_gold + pcity->client.buy_cost;
495  }
496  if (!can_client_issue_orders()) {
497  return;
498  }
499  list_menu = new QMenu(this);
500  QString buf =
501  QString(_("Buy ( Cost: %1 )")).arg(QString::number(sell_gold));
502 
503  QAction *cty_buy = new QAction(QString(buf), list_menu);
504  QAction *cty_center = new QAction(
505  style()->standardIcon(QStyle::SP_ArrowRight), _("Center"), list_menu);
506  QAction *wl_clear = new QAction(_("Clear"), list_menu);
507  QAction *wl_empty = new QAction(_("(no worklists defined)"), list_menu);
508  bool worklist_defined = true;
509 
510  if (!select_only) {
511  some_menu = list_menu->addMenu(_("Production"));
512  tmp_menu = some_menu->addMenu(_("Change"));
514  tmp_menu);
515  tmp_menu = some_menu->addMenu(_("Add next"));
517  can_city_build_now, tmp_menu);
518  tmp_menu = some_menu->addMenu(_("Add before last"));
520  can_city_build_now, tmp_menu);
521  tmp_menu = some_menu->addMenu(_("Add last"));
523  can_city_build_now, tmp_menu);
524 
525  tmp_menu = some_menu->addMenu(_("Worklist"));
526  tmp_menu->addAction(wl_clear);
527  connect(wl_clear, &QAction::triggered, this,
529  tmp2_menu = tmp_menu->addMenu(_("Add"));
530  gen_worklist_labels(cma_labels);
531  if (cma_labels.count() == 0) {
532  tmp2_menu->addAction(wl_empty);
533  worklist_defined = false;
534  }
535  fill_data(WORKLIST_ADD, cma_labels, tmp2_menu);
536  tmp2_menu = tmp_menu->addMenu(_("Change"));
537  if (cma_labels.count() == 0) {
538  tmp2_menu->addAction(wl_empty);
539  worklist_defined = false;
540  }
541  fill_data(WORKLIST_CHANGE, cma_labels, tmp2_menu);
542  some_menu = list_menu->addMenu(_("Governor"));
543  gen_cma_labels(cma_labels);
544  fill_data(CMA, cma_labels, some_menu);
545  some_menu = list_menu->addMenu(_("Sell"));
546  gen_production_labels(SELL, custom_labels, false, false,
548  fill_data(SELL, custom_labels, some_menu);
549  }
550  some_menu = list_menu->addMenu(_("Select"));
551  gen_select_labels(some_menu);
552  if (!select_only) {
553  list_menu->addAction(&cty_view);
554  connect(&cty_view, &QAction::triggered, this, &city_widget::city_view);
555  list_menu->addAction(cty_buy);
556  connect(cty_buy, &QAction::triggered, this, &city_widget::buy);
557  list_menu->addAction(cty_center);
558  connect(cty_center, &QAction::triggered, this, &city_widget::center);
559  }
560  sell_gold = 0;
561 
562  list_menu->setAttribute(Qt::WA_DeleteOnClose);
563  connect(list_menu, &QMenu::triggered, this, [=](QAction *act) {
564  QVariant qvar, qvar2;
565  enum menu_labels m_state;
566  cid id;
567  struct universal target;
568  QString imprname;
569  const struct impr_type *building;
570  Impr_type_id impr_id;
571  int city_id;
572  bool need_clear = true;
573  bool sell_ask = true;
574 
575  if (!act) {
576  return;
577  }
578 
579  qvar2 = act->property("FC");
580  m_state = static_cast<menu_labels>(qvar2.toInt());
581  qvar = act->data();
582  id = qvar.toInt();
583  target = cid_decode(id);
584 
585  city_list_iterate(client_player()->cities, iter_city)
586  {
587  if (nullptr != iter_city) {
588  switch (m_state) {
589  case SELECT_IMPR:
590  if (need_clear) {
591  clearSelection();
592  }
593  need_clear = false;
594  if (city_building_present(iter_city, &target)) {
595  select_city(iter_city);
596  }
597  break;
598  case SELECT_WONDERS:
599  if (need_clear) {
600  clearSelection();
601  }
602  need_clear = false;
603  if (city_building_present(iter_city, &target)) {
604  select_city(iter_city);
605  }
606  break;
607  case SELECT_SUPP_UNITS:
608  if (need_clear) {
609  clearSelection();
610  }
611  need_clear = false;
612  if (city_unit_supported(iter_city, &target)) {
613  select_city(iter_city);
614  }
615  break;
616  case SELECT_PRES_UNITS:
617  if (need_clear) {
618  clearSelection();
619  }
620  need_clear = false;
621  if (city_unit_present(iter_city, &target)) {
622  select_city(iter_city);
623  }
624  break;
625  case SELECT_AVAIL_UNITS:
626  if (need_clear) {
627  clearSelection();
628  }
629  need_clear = false;
630  if (can_city_build_now(iter_city, &target)) {
631  select_city(iter_city);
632  }
633  break;
634  case SELECT_AVAIL_IMPR:
635  if (need_clear) {
636  clearSelection();
637  }
638  need_clear = false;
639  if (can_city_build_now(iter_city, &target)) {
640  select_city(iter_city);
641  }
642  break;
644  if (need_clear) {
645  clearSelection();
646  }
647  need_clear = false;
648  if (can_city_build_now(iter_city, &target)) {
649  select_city(iter_city);
650  }
651  break;
652  default:
653  break;
654  }
655  }
656  }
658 
659  const auto saved_selection =
660  selected_cities; // Copy to avoid invalidations
661  for (auto *pcity : saved_selection) {
662  if (nullptr != pcity) {
663  switch (m_state) {
664  case CHANGE_PROD_NOW:
665  city_change_production(pcity, &target);
666  break;
667  case CHANGE_PROD_NEXT:
668  city_queue_insert(pcity, 1, &target);
669  break;
671  city_queue_insert(pcity, worklist_length(&pcity->worklist),
672  &target);
673  break;
674  case CHANGE_PROD_LAST:
675  city_queue_insert(pcity, -1, &target);
676  break;
677  case SELL:
678  building = target.value.building;
679  if (sell_ask) {
680  hud_message_box *ask = new hud_message_box(king()->central_wdg);
681  imprname = improvement_name_translation(building);
682  QString buf = QString(_("Are you sure you want to sell the %1?"))
683  .arg(imprname);
684  sell_ask = false;
685  ask->setStandardButtons(QMessageBox::Cancel | QMessageBox::Yes);
686  ask->setDefaultButton(QMessageBox::No);
687  ask->button(QMessageBox::Yes)->setText(_("Yes Sell"));
688  ask->set_text_title(buf, _("Sell?"));
689  ask->setAttribute(Qt::WA_DeleteOnClose);
690  city_id = pcity->id;
691  impr_id = improvement_number(building);
692  connect(ask, &hud_message_box::accepted, this, [=]() {
693  struct city *pcity = game_city_by_number(city_id);
694  struct impr_type *building = improvement_by_number(impr_id);
695  if (!pcity || !building) {
696  return;
697  }
698  if (!pcity->did_sell && city_has_building(pcity, building)) {
699  city_sell_improvement(pcity, impr_id);
700  }
701  });
702  ask->show();
703  }
704  break;
705  case CMA:
706  if (CMA_NONE == id) {
707  cma_release_city(pcity);
708  } else {
710  }
711 
712  break;
713  case WORKLIST_ADD:
714  if (worklist_defined) {
717  }
718  break;
719 
720  case WORKLIST_CHANGE:
721  if (worklist_defined) {
722  city_set_queue(pcity,
724  }
725  break;
726  default:
727  break;
728  }
729  }
730  }
731  });
732  list_menu->popup(QCursor::pos());
733 }
734 
739  QMap<QString, cid> &custom_labels,
740  TestCityFunc test_func, QMenu *menu)
741 {
742  QMenu *m1, *m2, *m3;
743 
744  m1 = menu->addMenu(_("Buildings"));
745  m2 = menu->addMenu(_("Units"));
746  m3 = menu->addMenu(_("Wonders"));
747  gen_production_labels(what, custom_labels, false, false, test_func);
748  fill_data(what, custom_labels, m1);
749  gen_production_labels(what, custom_labels, true, false, test_func);
750  fill_data(what, custom_labels, m2);
751  gen_production_labels(what, custom_labels, false, true, test_func);
752  fill_data(what, custom_labels, m3);
753 }
754 
759  QMap<QString, cid> &custom_labels, QMenu *menu)
760 {
761  QAction *action;
763 
764  map_iter = custom_labels.constBegin();
765  while (map_iter != custom_labels.constEnd()) {
766  action = menu->addAction(map_iter.key());
767  action->setData(map_iter.value());
768  action->setProperty("FC", which);
769  ++map_iter;
770  }
771  if (custom_labels.isEmpty()) {
772  menu->setDisabled(true);
773  }
774 }
775 
779 void city_widget::select_all() { selectAll(); }
780 
784 void city_widget::select_none() { clearSelection(); }
785 
790 {
791  QItemSelection selection;
792  QModelIndex i;
793  struct city *pcity;
794  QVariant qvar;
795 
796  for (int j = 0; j < filter_model->rowCount(); j++) {
797  i = filter_model->index(j, 0);
798  qvar = i.data(Qt::UserRole);
799  if (qvar.isNull()) {
800  continue;
801  }
802  pcity = reinterpret_cast<city *>(qvar.value<void *>());
803  if (!selected_cities.contains(pcity)) {
804  selection.append(QItemSelectionRange(i));
805  }
806  }
807  clearSelection();
808  selectionModel()->select(selection,
809  QItemSelectionModel::Rows
810  | QItemSelectionModel::SelectCurrent);
811 }
812 
817 {
818  QItemSelection selection;
819  QModelIndex i;
820  struct city *pcity;
821  QVariant qvar;
822 
823  for (int j = 0; j < filter_model->rowCount(); j++) {
824  i = filter_model->index(j, 0);
825  qvar = i.data(Qt::UserRole);
826  if (qvar.isNull()) {
827  continue;
828  }
829  pcity = reinterpret_cast<city *>(qvar.value<void *>());
830  if (pcity == spcity) {
831  selection.append(QItemSelectionRange(i));
832  }
833  }
834  selectionModel()->select(selection, QItemSelectionModel::Rows
835  | QItemSelectionModel::Select);
836 }
837 
842 {
843  QItemSelection selection;
844  QModelIndex i;
845  struct city *pcity;
846  QVariant qvar;
847 
848  clearSelection();
849  for (int j = 0; j < filter_model->rowCount(); j++) {
850  i = filter_model->index(j, 0);
851  qvar = i.data(Qt::UserRole);
852  if (qvar.isNull()) {
853  continue;
854  }
855  pcity = reinterpret_cast<city *>(qvar.value<void *>());
856  if (nullptr != pcity
857  && is_terrain_class_near_tile(pcity->tile, TC_OCEAN)) {
858  selection.append(QItemSelectionRange(i));
859  }
860  }
861  selectionModel()->select(selection,
862  QItemSelectionModel::Rows
863  | QItemSelectionModel::SelectCurrent);
864 }
865 
870 {
871  QItemSelection selection;
872  QModelIndex i;
873  struct city *pcity;
874  QVariant qvar;
875 
876  for (int j = 0; j < filter_model->rowCount(); j++) {
877  i = filter_model->index(j, 0);
878  qvar = i.data(Qt::UserRole);
879  if (qvar.isNull()) {
880  continue;
881  }
882  pcity = reinterpret_cast<city *>(qvar.value<void *>());
883  for (auto *pscity : qAsConst(selected_cities)) {
884  if (nullptr != pcity
885  && (tile_continent(pcity->tile) == tile_continent(pscity->tile))) {
886  selection.append(QItemSelectionRange(i));
887  }
888  }
889  }
890  selectionModel()->select(selection,
891  QItemSelectionModel::Rows
892  | QItemSelectionModel::SelectCurrent);
893 }
894 
900 {
901  QItemSelection selection;
902  QModelIndex i;
903  struct city *pcity;
904  QVariant qvar;
905  QAction *act;
906  QString str;
907 
908  clearSelection();
909  for (int j = 0; j < filter_model->rowCount(); j++) {
910  i = filter_model->index(j, 0);
911  qvar = i.data(Qt::UserRole);
912  if (qvar.isNull()) {
913  continue;
914  }
915  pcity = reinterpret_cast<city *>(qvar.value<void *>());
916  act = qobject_cast<QAction *>(sender());
917  qvar = act->data();
918  str = qvar.toString();
919  if (nullptr != pcity) {
920  if (str == QLatin1String("impr")
921  && VUT_IMPROVEMENT == pcity->production.kind
922  && !is_wonder(pcity->production.value.building)
924  IF_GOLD)) {
925  selection.append(QItemSelectionRange(i));
926  } else if (str == QLatin1String("unit")
927  && VUT_UTYPE == pcity->production.kind) {
928  selection.append(QItemSelectionRange(i));
929  } else if (str == QLatin1String("wonder")
930  && VUT_IMPROVEMENT == pcity->production.kind
931  && is_wonder(pcity->production.value.building)) {
932  selection.append(QItemSelectionRange(i));
933  }
934  }
935  }
936  selectionModel()->select(selection,
937  QItemSelectionModel::Rows
938  | QItemSelectionModel::SelectCurrent);
939 }
940 
945 {
946  list.clear();
947  for (int i = 0; i < cmafec_preset_num(); i++) {
948  list.insert(cmafec_preset_get_descr(i), i);
949  }
950 }
951 
956 {
957  QAction *act;
958  QMenu *tmp_menu;
959  QMap<QString, cid> custom_labels;
960 
961  act = menu->addAction(_("All Cities"));
962  connect(act, &QAction::triggered, this, &city_widget::select_all);
963  act = menu->addAction(_("No Cities"));
964  connect(act, &QAction::triggered, this, &city_widget::select_none);
965  act = menu->addAction(_("Invert Selection"));
966  connect(act, &QAction::triggered, this, &city_widget::invert_selection);
967  menu->addSeparator();
968  act = menu->addAction(_("Coastal Cities"));
969  connect(act, &QAction::triggered, this, &city_widget::select_coastal);
970  act = menu->addAction(_("Same Island"));
971  connect(act, &QAction::triggered, this, &city_widget::select_same_island);
972  if (selected_cities.isEmpty()) {
973  act->setDisabled(true);
974  }
975  menu->addSeparator();
976  act = menu->addAction(_("Building Units"));
977  act->setData("unit");
978  connect(act, &QAction::triggered, this,
980  act = menu->addAction(_("Building Improvements"));
981  act->setData("impr");
982  connect(act, &QAction::triggered, this,
984  act = menu->addAction(_("Building Wonders"));
985  act->setData("wonder");
986  connect(act, &QAction::triggered, this,
988  menu->addSeparator();
989  tmp_menu = menu->addMenu(_("Improvements in City"));
990  gen_production_labels(SELECT_IMPR, custom_labels, false, false,
991  city_building_present, true);
992  fill_data(SELECT_IMPR, custom_labels, tmp_menu);
993  tmp_menu = menu->addMenu(_("Wonders in City"));
994  gen_production_labels(SELECT_WONDERS, custom_labels, false, true,
995  city_building_present, true);
996  fill_data(SELECT_WONDERS, custom_labels, tmp_menu);
997  menu->addSeparator();
998  tmp_menu = menu->addMenu(_("Supported Units"));
999  gen_production_labels(SELECT_SUPP_UNITS, custom_labels, true, false,
1000  city_unit_supported, true);
1001  fill_data(SELECT_SUPP_UNITS, custom_labels, tmp_menu);
1002  tmp_menu = menu->addMenu(_("Units Present"));
1003  gen_production_labels(SELECT_PRES_UNITS, custom_labels, true, false,
1004  city_unit_present, true);
1005  fill_data(SELECT_PRES_UNITS, custom_labels, tmp_menu);
1006  menu->addSeparator();
1007  tmp_menu = menu->addMenu(_("Available Units"));
1008  gen_production_labels(SELECT_AVAIL_UNITS, custom_labels, true, false,
1009  can_city_build_now, true);
1010  fill_data(SELECT_AVAIL_UNITS, custom_labels, tmp_menu);
1011  tmp_menu = menu->addMenu(_("Available Improvements"));
1012  gen_production_labels(SELECT_AVAIL_IMPR, custom_labels, false, false,
1013  can_city_build_now, true);
1014  fill_data(SELECT_AVAIL_IMPR, custom_labels, tmp_menu);
1015  tmp_menu = menu->addMenu(_("Available Wonders"));
1016  gen_production_labels(SELECT_AVAIL_WONDERS, custom_labels, false, true,
1017  can_city_build_now, true);
1018  fill_data(SELECT_AVAIL_WONDERS, custom_labels, tmp_menu);
1019 }
1020 
1025 {
1026  list.clear();
1028  {
1029  list.insert(global_worklist_name(pgwl), global_worklist_id(pgwl));
1030  }
1032 }
1033 
1038  QMap<QString, cid> &list,
1039  bool append_units,
1040  bool append_wonders,
1041  TestCityFunc test_func, bool global)
1042 {
1043  Q_UNUSED(what)
1044  struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
1045  struct item items[MAX_NUM_PRODUCTION_TARGETS];
1046  int i, item, targets_used;
1047  QString str;
1048  char buf[64];
1049  struct city **city_data;
1050  int num_sel = 0;
1051 
1052  if (global) {
1053  num_sel = list_model->rowCount();
1054  } else {
1055  num_sel = selected_cities.count();
1056  }
1057  std::vector<struct city *> array;
1058  array.resize(num_sel);
1059 
1060  if (global) {
1061  i = 0;
1063  {
1064  array[i] = pcity;
1065  i++;
1066  }
1068  } else {
1069  for (i = 0; i < num_sel; i++) {
1070  array[i] = selected_cities.at(i);
1071  }
1072  }
1073  city_data = &array[0];
1074  targets_used =
1075  collect_production_targets(targets, city_data, num_sel, append_units,
1076  append_wonders, true, test_func);
1077  name_and_sort_items(targets, targets_used, items, true, nullptr);
1078  list.clear();
1079  for (item = 0; item < targets_used; item++) {
1080  struct universal target = items[item].item;
1081 
1082  str.clear();
1083  universal_name_translation(&target, buf, sizeof(buf));
1084  QString txt = QStringLiteral("%1 ").arg(buf);
1085  str = str + txt;
1086  list.insert(str, cid_encode(target));
1087  }
1088 }
1089 
1094 {
1095  list_model->city_changed(pcity);
1097 }
1098 
1103 {
1104  setUpdatesEnabled(false);
1107  header()->resizeSections(QHeaderView::ResizeToContents);
1108  setUpdatesEnabled(true);
1109 }
1110 
1115 {
1116  QMenu *hideshow_column = new QMenu(this);
1117  QList<QAction *> actions;
1118 
1119  hideshow_column->setTitle(_("Column visibility"));
1120  for (int i = 0; i < list_model->columnCount(); i++) {
1121  QAction *myAct =
1122  hideshow_column->addAction(list_model->menu_data(i).toString());
1123  myAct->setCheckable(true);
1124  myAct->setChecked(!isColumnHidden(i));
1125  actions.append(myAct);
1126  }
1127  hideshow_column->setAttribute(Qt::WA_DeleteOnClose);
1128  connect(hideshow_column, &QMenu::triggered, this, [=](QAction *act) {
1129  int col;
1130  if (!act) {
1131  return;
1132  }
1133 
1134  col = actions.indexOf(act);
1135  fc_assert_ret(col >= 0);
1136  setColumnHidden(col, !isColumnHidden(col));
1137  city_report_specs[col].show = act->isChecked();
1138  if (!isColumnHidden(col) && columnWidth(col) <= 5) {
1139  setColumnWidth(col, 100);
1140  }
1141  });
1142  hideshow_column->popup(QCursor::pos());
1143 }
1144 
1149 {
1150  int col;
1151 
1152  for (col = 0; col < list_model->columnCount(); col++) {
1153  if (!list_model->hide_data(col).toBool()) {
1154  setColumnHidden(col, !isColumnHidden(col));
1155  }
1156  }
1157 }
1158 
1163 void city_widget::cities_selected(const QItemSelection &sl,
1164  const QItemSelection &ds)
1165 {
1166  Q_UNUSED(sl)
1167  Q_UNUSED(ds)
1168  QModelIndexList indexes = selectionModel()->selectedIndexes();
1169  QVariant qvar;
1170  struct city *pcity;
1171 
1172  selected_cities.clear();
1173 
1174  if (indexes.isEmpty()) {
1175  return;
1176  }
1177  for (auto i : qAsConst(indexes)) {
1178  qvar = i.data(Qt::UserRole);
1179  if (qvar.isNull()) {
1180  continue;
1181  }
1182  pcity = reinterpret_cast<city *>(qvar.value<void *>());
1183  selected_cities << pcity;
1184  }
1185 }
1186 
1191 {
1192  delete c_i_d;
1193  delete list_model;
1194  delete filter_model;
1195  king()->qt_settings.city_repo_sort_col = header()->sortIndicatorSection();
1196  king()->qt_settings.city_report_sort = header()->sortIndicatorOrder();
1197 }
1198 
1203 {
1204  layout = new QVBoxLayout;
1205  city_wdg = new city_widget(this);
1206  if (king()->qt_settings.city_repo_sort_col != -1) {
1207  city_wdg->sortByColumn(king()->qt_settings.city_repo_sort_col,
1208  king()->qt_settings.city_report_sort);
1209  }
1210  layout->addWidget(city_wdg);
1211  setLayout(layout);
1212  index = 0;
1213 }
1214 
1219 {
1220  queen()->removeRepoDlg(QStringLiteral("CTS"));
1221 }
1222 
1227 {
1228  queen()->gimmePlace(this, QStringLiteral("CTS"));
1229  index = queen()->addGameTab(this);
1230  queen()->game_tab_widget->setCurrentIndex(index);
1231 }
1232 
1237 
1241 void city_report::update_city(struct city *pcity)
1242 {
1243  city_wdg->update_city(pcity);
1244 }
1245 
1250 {
1251  int i;
1252  city_report *cr;
1253  QWidget *w;
1254 
1255  if (!queen()->isRepoDlgOpen(QStringLiteral("CTS"))) {
1256  cr = new city_report;
1257  cr->init();
1258  cr->update_report();
1259  } else {
1260  i = queen()->gimmeIndexOf(QStringLiteral("CTS"));
1261  fc_assert(i != -1);
1262  w = queen()->game_tab_widget->widget(i);
1263  if (w->isVisible()) {
1264  top_bar_show_map();
1265  return;
1266  }
1267  cr = reinterpret_cast<city_report *>(w);
1268  queen()->game_tab_widget->setCurrentWidget(cr);
1269  cr->update_report();
1270  }
1271 }
1272 
1273 static void update_city_report(struct city *pcity)
1274 {
1275  int i;
1276  city_report *cr;
1277  QWidget *w;
1278 
1279  if (queen()->isRepoDlgOpen(QStringLiteral("CTS"))) {
1280  i = queen()->gimmeIndexOf(QStringLiteral("CTS"));
1281  if (queen()->game_tab_widget->currentIndex() == i) {
1282  w = queen()->game_tab_widget->widget(i);
1283  cr = reinterpret_cast<city_report *>(w);
1284  if (pcity) {
1285  cr->update_city(pcity);
1286  } else {
1287  cr->update_report();
1288  }
1289  }
1290  }
1291 }
1292 
1297 {
1298  update_city_report(nullptr);
1299 }
1300 
1305 {
1306  update_city_report(pcity);
1307 }
1308 
1313 {
1314  int i;
1315  city_report *cr;
1316  QWidget *w;
1317 
1318  if (queen()->isRepoDlgOpen(QStringLiteral("CTS"))) {
1319  i = queen()->gimmeIndexOf(QStringLiteral("CTS"));
1320  fc_assert(i != -1);
1321  w = queen()->game_tab_widget->widget(i);
1322  cr = reinterpret_cast<city_report *>(w);
1323  cr->deleteLater();
1324  }
1325  hIcon::drop();
1326 }
static struct action * actions[MAX_NUM_ACTIONS]
Definition: actions.cpp:91
bool can_city_build_now(const struct city *pcity, const struct universal *target)
Returns whether city can immediately build given target, unit or improvement.
Definition: city.cpp:953
bool city_has_building(const struct city *pcity, const struct impr_type *pimprove)
Return TRUE iff the city has this building in it.
Definition: city.cpp:1189
#define cities_iterate_end
Definition: city.h:493
#define city_list_iterate(citylist, pcity)
Definition: city.h:482
#define cities_iterate(pcity)
Definition: city.h:486
#define city_list_iterate_end
Definition: city.h:484
void real_city_dialog_popup(struct city *pcity)
Pop up (or bring to the front) a dialog for the given city.
Definition: citydlg.cpp:2247
int city_set_worklist(struct city *pcity, const struct worklist *pworklist)
Set the worklist for a given city.
bool city_queue_insert(struct city *pcity, int position, struct universal *item)
Insert an item into the city's queue.
bool city_queue_insert_worklist(struct city *pcity, int position, const struct worklist *worklist)
Insert the worklist into the city's queue at the given position.
bool city_set_queue(struct city *pcity, const struct worklist *pqueue)
Set the city current production and the worklist, like it should be.
int city_change_production(struct city *pcity, struct universal *target)
Change the production of a given city.
int city_sell_improvement(struct city *pcity, Impr_type_id sell_id)
Change the production of a given city.
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
City item delgate paint event.
Definition: view_cities.cpp:75
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
Size hint for city item delegate.
city_item_delegate(QObject *parent)
City item delegate constructor.
Definition: view_cities.cpp:63
struct city * get_city()
Returns used city pointer for city item creation.
QVariant data(int column, int role=Qt::DisplayRole) const
Returns data from city item (or city pointer from Qt::UserRole)
struct city * i_city
Definition: view_cities.h:80
city_item(struct city *pcity)
Constructor for city item.
bool setData(int column, const QVariant &value, int role=Qt::DisplayRole)
Sets nothing, but must be declared.
void city_changed(struct city *pcity)
Notifies about changed item.
QVariant hide_data(int section) const
Hides given column if show is false.
int rowCount(const QModelIndex &index=QModelIndex()) const override
Definition: view_cities.h:93
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
Returns header data for given section(column)
void populate()
Creates city model.
QVariant menu_data(int section) const
Returns header information about section.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Definition: view_cities.h:98
void all_changed()
Notifies about whole model changed.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::DisplayRole) override
Sets data in model under index.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Returns stored data in index.
QList< city_item * > city_list
Definition: view_cities.h:118
~city_model() override
Destructor for city model.
city_model(QObject *parent=0)
Constructor for city model.
void notify_city_changed(int row)
Notifies about changed row.
~city_report() override
Destructor for city report.
city_report()
Constructor for city report.
void init()
Inits place in game tab widget.
QVBoxLayout * layout
Definition: view_cities.h:193
void update_report()
Updates whole report.
city_widget * city_wdg
Definition: view_cities.h:192
void update_city(struct city *pcity)
Updates single city.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
Overriden compare for sorting items.
Definition: view_cities.cpp:42
void city_view()
Shows first selected city.
void buy()
Buys current item in city.
void select_city(struct city *pcity)
Marks given city selected.
city_widget(city_report *ctr)
Constructor for city widget.
void fill_data(menu_labels which, QMap< QString, cid > &custom_labels, QMenu *menu)
Fills menu actions.
void city_doubleclick(const QModelIndex &index)
Slot for double clicking row.
void center()
Centers map on city.
void update_model()
Updates whole model.
city_item_delegate * c_i_d
Definition: view_cities.h:128
~city_widget() override
Destructor for city widget.
void display_list_menu(const QPoint)
Displays right click menu on city row.
QSortFilterProxyModel * filter_model
Definition: view_cities.h:127
void fill_production_menus(city_widget::menu_labels what, QMap< QString, cid > &custom_labels, TestCityFunc test_func, QMenu *menu)
Fills menu items that can be produced or sold.
city_report * cr
Definition: view_cities.h:129
void select_coastal()
Selects coastal cities on report.
void gen_production_labels(menu_labels which, QMap< QString, cid > &list, bool append_units, bool append_wonders, TestCityFunc test_func, bool global=false)
Creates menu labels and id about available production targets.
void select_building_something()
Selects cities building units or buildings or wonders depending on data stored in QAction.
void hide_columns()
Hides columns for city widget, depending on stored data (bool spec->show)
void clear_worlist()
Clears worklist for selected cities.
void update_city(struct city *pcity)
Updates single city.
city_model * list_model
Definition: view_cities.h:126
void select_all()
Selects all cities on report.
void select_none()
Selects no cities on report.
void gen_worklist_labels(QMap< QString, int > &list)
Creates menu labels and info of available worklists, stored in list.
void cities_selected(const QItemSelection &sl, const QItemSelection &ds)
Slot for selecting items in city widget, they are stored in selected_cities until deselected.
void gen_cma_labels(QMap< QString, int > &list)
Creates menu labels and id of available cma, stored in list.
QList< city * > selected_cities
Definition: view_cities.h:151
void restore_selection()
Restores last selection.
void invert_selection()
Inverts selection on report.
void gen_select_labels(QMenu *menu)
Creates menu labels for selecting cities.
void display_header_menu(const QPoint)
Context menu for header.
void select_same_island()
Selects same cities on the same island.
@ CHANGE_PROD_BEF_LAST
Definition: view_cities.h:134
@ SELECT_AVAIL_UNITS
Definition: view_cities.h:143
@ SELECT_AVAIL_WONDERS
Definition: view_cities.h:145
fc_settings qt_settings
Definition: fc_client.h:126
static void drop()
Definition: icons.cpp:214
static hIcon * i()
Definition: icons.cpp:205
QIcon get(const QString &id)
Definition: icons.cpp:236
void set_text_title(const QString &s1, const QString &s2)
Sets text and title and shows message box.
Definition: hudwidget.cpp:117
void center_on_tile(tile *tile, bool animate=true)
Centers the view on a tile.
Definition: view_map.cpp:197
int gimmeIndexOf(const QString &str)
Returns index on game tab page of given report dialog.
Definition: page_game.cpp:706
int addGameTab(QWidget *widget)
Inserts tab widget to game view page.
Definition: page_game.cpp:670
map_view * mapview_wdg
Definition: page_game.h:81
void removeRepoDlg(const QString &str)
Removes report dialog string from the list marking it as closed.
Definition: page_game.cpp:723
void gimmePlace(QWidget *widget, const QString &str)
Finds not used index on game_view_tab and returns it.
Definition: page_game.cpp:690
fc_game_tab_widget * game_tab_widget
Definition: page_game.h:72
bool client_has_player()
Either controlling or observing.
struct player * client_player()
Either controlling or observing.
struct civclient client
bool can_client_issue_orders()
Returns TRUE iff the client can issue orders (such as giving unit commands).
bool city_unit_supported(const struct city *pcity, const struct universal *target)
Return TRUE if the city supports at least one unit of the given production type (returns FALSE if the...
Definition: climisc.cpp:497
void cityrep_buy(struct city *pcity)
Called when the "Buy" button is pressed in the city report for every selected city.
Definition: climisc.cpp:1019
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
cid cid_encode(struct universal target)
Encode a CID for the target production.
Definition: climisc.cpp:446
bool city_building_present(const struct city *pcity, const struct universal *target)
A TestCityFunc to tell whether the item is a building and is present.
Definition: climisc.cpp:538
struct universal cid_decode(cid id)
Decode the CID into a city_production structure.
Definition: climisc.cpp:478
bool city_unit_present(const struct city *pcity, const struct universal *target)
Return TRUE if the city has present at least one unit of the given production type (returns FALSE if ...
Definition: climisc.cpp:518
int collect_production_targets(struct universal *targets, struct city **selected_cities, int num_selected_cities, bool append_units, bool append_wonders, bool change_prod, TestCityFunc test_func)
Return possible production targets for the current player's cities.
Definition: climisc.cpp:648
#define MAX_NUM_PRODUCTION_TARGETS
Definition: climisc.h:73
int cid
Definition: climisc.h:21
bool(* TestCityFunc)(const struct city *, const struct universal *)
Definition: climisc.h:71
class fc_client * king()
Return fc_client instance.
Definition: gui_main.cpp:58
int Impr_type_id
Definition: fc_types.h:293
#define Q_(String)
Definition: fcintl.h:53
#define _(String)
Definition: fcintl.h:50
struct city * game_city_by_number(int id)
Often used function to get a city pointer from a city ID.
Definition: game.cpp:103
struct global_worklist * global_worklist_by_id(int id)
Returns the global worklist corresponding to this id.
int global_worklist_id(const struct global_worklist *pgwl)
Returns the id of the global worklist.
const char * global_worklist_name(const struct global_worklist *pgwl)
Return the name of the global worklist.
const struct worklist * global_worklist_get(const struct global_worklist *pgwl)
Returns the worklist of this global worklist or nullptr if it's not valid.
#define global_worklists_iterate(pgwl)
#define global_worklists_iterate_end
int cmafec_preset_num()
Returns the total number of presets.
Definition: governor.cpp:769
const struct cm_parameter * cmafec_preset_get_parameter(int idx)
Returns the indexed preset's parameter.
Definition: governor.cpp:738
char * cmafec_preset_get_descr(int idx)
Returns the indexed preset's description.
Definition: governor.cpp:725
void cma_put_city_under_agent(struct city *pcity, const struct cm_parameter *const parameter)
Put city under governor control.
Definition: governor.cpp:618
void cma_release_city(struct city *pcity)
Release city from governor control.
Definition: governor.cpp:627
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_wonder(const struct impr_type *pimprove)
Returns whether improvement is some kind of wonder.
bool improvement_has_flag(const struct impr_type *pimprove, enum impr_flag_id flag)
Return TRUE if the impr has this flag otherwise FALSE.
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
#define fc_assert(condition)
Definition: log.h:89
client_options * gui_options
Definition: options.cpp:74
struct city_list * cities
Definition: packhand.cpp:122
pageGame * queen()
Return game instandce.
Definition: page_game.cpp:557
const char * universal_name_translation(const struct universal *psource, char *buf, size_t bufsz)
Make user-friendly text for the source.
const char * tagname
const char * explanation
Definition: city.h:291
bool did_sell
Definition: city.h:352
int id
Definition: city.h:296
struct universal production
Definition: city.h:368
int style
Definition: city.h:297
struct tile * tile
Definition: city.h:293
struct connection conn
Definition: client_main.h:89
bool center_when_popup_city
Definition: options.h:91
struct player * playing
Definition: connection.h:142
Qt::SortOrder city_report_sort
Definition: fc_client.h:64
int city_repo_sort_col
Definition: fc_client.h:63
Definition: climisc.h:66
struct universal item
Definition: climisc.h:67
The base class for options.
Definition: options.cpp:209
struct city_list * cities
Definition: player.h:263
enum universals_n kind
Definition: fc_types.h:740
universals_u value
Definition: fc_types.h:739
bool is_terrain_class_near_tile(const struct tile *ptile, enum terrain_class tclass)
Is there terrain of the given class near tile? (Does not check ptile itself.)
Definition: terrain.cpp:495
#define tile_continent(_tile)
Definition: tile.h:74
void top_bar_show_map()
Callback to show map.
Definition: top_bar.cpp:448
const struct impr_type * building
Definition: fc_types.h:579
void city_report_dialog_popup()
Display the city report dialog.
void real_city_report_dialog_update(void *unused)
Update (refresh) the entire city report dialog.
void real_city_report_update_city(struct city *pcity)
Update the information for a single city in the city report.
static void update_city_report(struct city *pcity)
void popdown_city_report()
Closes city report.
#define CMA_NONE
Definition: view_cities.h:23
bool can_city_sell_universal(const struct city *pcity, const struct universal *target)
Same as can_city_sell_building(), but with universal argument.
int cityrepfield_compare(const char *str1, const char *str2)
The real function: split the two strings, and compare them.
std::vector< city_report_spec > city_report_specs
#define NUM_CREPORT_COLS
void worklist_init(struct worklist *pwl)
Initialize a worklist to be empty.
Definition: worklist.cpp:32
int worklist_length(const struct worklist *pwl)
Returns the number of entries in the worklist.
Definition: worklist.cpp:51