1 /***********************************************************************
2  Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 // Qt
19 #include <QApplication>
20 #include <QCheckBox>
21 #include <QDesktopWidget>
22 #include <QGroupBox>
23 #include <QHeaderView>
24 #include <QImage>
25 #include <QMenu>
26 #include <QMessageBox>
27 #include <QPainter>
28 #include <QRadioButton>
29 #include <QRect>
30 #include <QScreen>
31 #include <QScrollArea>
32 #include <QScrollBar>
33 #include <QSplitter>
34 #include <QToolTip>
35 #include <QVBoxLayout>
36 #include <QWheelEvent>
37 #include <QWidgetAction>
38 
39 // utility
40 #include "support.h"
41 
42 // common
43 #include "citizens.h"
44 #include "city.h"
45 #include "game.h"
46 
47 //agents
48 #include "cma_core.h"
49 #include "cma_fec.h"
50 
51 // client
52 #include "citydlg_common.h"
53 #include "client_main.h"
54 #include "climisc.h"
55 #include "control.h"
56 #include "global_worklist.h"
57 #include "helpdata.h"
58 #include "mapview_common.h"
59 #include "movement.h"
60 #include "sprite.h"
61 #include "text.h"
62 #include "tilespec.h"
63 
64 //gui-qt
65 #include "citydlg.h"
66 #include "colors.h"
67 #include "fc_client.h"
68 #include "hudwidget.h"
69 
70 extern QApplication *qapp;
71 static bool city_dlg_created = false; /** defines if dialog for city has been
72                                        * already created. It's created only
73                                        * once per client
74                                        */
75 static city_dialog *city_dlg;
76 extern QString split_text(QString text, bool cut);
77 extern QString cut_helptext(QString text);
78 
79 /****************************************************************************
80   Custom progressbar constructor
81 ****************************************************************************/
progress_bar(QWidget * parent)82 progress_bar::progress_bar(QWidget *parent): QProgressBar(parent)
83 {
84   m_timer.start();
85   startTimer(50);
86   create_region();
87   sfont = new QFont;
88   m_animate_step = 0;
89   pix = nullptr;
90 }
91 
92 /****************************************************************************
93   Custom progressbar destructor
94 ****************************************************************************/
~progress_bar()95 progress_bar::~progress_bar()
96 {
97   if (pix != nullptr) {
98     delete pix;
99   }
100   delete sfont;
101 }
102 
103 /****************************************************************************
104   Custom progressbar resize event
105 ****************************************************************************/
resizeEvent(QResizeEvent * event)106 void progress_bar::resizeEvent(QResizeEvent *event)
107 {
108   create_region();
109 }
110 
111 /****************************************************************************
112   Sets pixmap from given universal for custom progressbar
113 ****************************************************************************/
set_pixmap(struct universal * target)114 void progress_bar::set_pixmap(struct universal *target)
115 {
116   struct sprite *sprite;
117   QImage cropped_img;
118   QImage img;
119   QPixmap tpix;
120   QRect crop;
121 
122   if (VUT_UTYPE == target->kind) {
123     sprite = get_unittype_sprite(get_tileset(), target->value.utype,
124                                  direction8_invalid(), true);
125   } else {
126     sprite = get_building_sprite(tileset, target->value.building);
127   }
128   if (pix != nullptr) {
129     delete pix;
130   }
131   if (sprite == nullptr) {
132     pix = nullptr;
133     return;
134   }
135   img = sprite->pm->toImage();
136   crop = zealous_crop_rect(img);
137   cropped_img = img.copy(crop);
138   tpix = QPixmap::fromImage(cropped_img);
139   pix = new QPixmap(tpix.width(), tpix.height());
140   pix->fill(Qt::transparent);
141   pixmap_copy(pix, &tpix, 0 , 0, 0, 0, tpix.width(), tpix.height());
142 }
143 
144 /****************************************************************************
145   Sets pixmap from given tech number for custom progressbar
146 ****************************************************************************/
set_pixmap(int n)147 void progress_bar::set_pixmap(int n)
148 {
149   struct sprite *sprite;
150 
151   if (valid_advance_by_number(n)) {
152     sprite = get_tech_sprite(tileset, n);
153   } else {
154     sprite = nullptr;
155   }
156   if (pix != nullptr) {
157     delete pix;
158   }
159   if (sprite == nullptr) {
160     pix = nullptr;
161     return;
162   }
163   pix = new QPixmap(sprite->pm->width(),
164                     sprite->pm->height());
165   pix->fill(Qt::transparent);
166   pixmap_copy(pix, sprite->pm, 0 , 0, 0, 0,
167               sprite->pm->width(), sprite->pm->height());
168   if (isVisible()) {
169     update();
170   }
171 }
172 
173 /****************************************************************************
174   Timer event used to animate progress
175 ****************************************************************************/
timerEvent(QTimerEvent * event)176 void progress_bar::timerEvent(QTimerEvent *event)
177 {
178   if ((value() != minimum() && value() < maximum())
179       || (0 == minimum() && 0 == maximum())) {
180     m_animate_step = m_timer.elapsed() / 50;
181     update();
182   }
183 }
184 
185 /****************************************************************************
186   Paint event for custom progress bar
187 ****************************************************************************/
paintEvent(QPaintEvent * event)188 void progress_bar::paintEvent(QPaintEvent *event)
189 {
190   QPainter p;
191   QLinearGradient g, gx;
192   QColor c;
193   QRect r, rx, r2;
194   int max;
195   int f_size;
196   int pix_width = 0;
197   int point_size = sfont->pointSize();
198   int pixel_size = sfont->pixelSize();
199 
200   if (pix != nullptr) {
201     pix_width = height() - 4;
202   }
203   if (point_size < 0) {
204     f_size = pixel_size;
205   } else {
206     f_size = point_size;
207   }
208 
209   rx.setX(0);
210   rx.setY(0);
211   rx.setWidth(width());
212   rx.setHeight(height());
213   p.begin(this);
214   p.drawLine(rx.topLeft(), rx.topRight());
215   p.drawLine(rx.bottomLeft(), rx.bottomRight());
216 
217   max = maximum();
218 
219   if (max == 0) {
220     max = 1;
221   }
222 
223   r = QRect(0, 0, width() * value() / max, height());
224 
225   gx = QLinearGradient(0 , 0, 0, height());
226   c = QColor(palette().color(QPalette::Highlight));
227   gx.setColorAt(0, c);
228   gx.setColorAt(0.5, QColor(40, 40, 40));
229   gx.setColorAt(1, c);
230   p.fillRect(r, QBrush(gx));
231   p.setClipRegion(reg.translated(m_animate_step % 32, 0));
232 
233   g = QLinearGradient(0 , 0, width(), height());
234   c.setAlphaF(0.1);
235   g.setColorAt(0, c);
236   c.setAlphaF(0.9);
237   g.setColorAt(1, c);
238   p.fillRect(r, QBrush(g));
239 
240   p.setClipping(false);
241   r2 = QRect(width() * value() / max, 0, width(), height());
242   c = palette().color(QPalette::Window);
243   p.fillRect(r2, c);
244 
245   /* draw icon */
246   if (pix != nullptr) {
247     p.setCompositionMode(QPainter::CompositionMode_SourceOver);
248     p.drawPixmap(2 , 2, pix_width
249     * static_cast<float>(pix->width()) / pix->height(),
250                  pix_width, *pix, 0, 0, pix->width(), pix->height());
251   }
252 
253   /* draw text */
254   c = palette().color(QPalette::Text);
255   p.setPen(c);
256   sfont->setCapitalization(QFont::AllUppercase);
257   sfont->setBold(true);
258   p.setFont(*sfont);
259 
260   if (text().contains('\n')) {
261     QString s1, s2;
262     int i, j;
263 
264     i = text().indexOf('\n');
265     s1 = text().left(i);
266     s2 = text().right(text().count() - i);
267 
268     if (2 * f_size >= 2 * height() / 3) {
269       if (point_size < 0) {
270         sfont->setPixelSize(height() / 4);
271       } else  {
272         sfont->setPointSize(height() / 4);
273       }
274     }
275 
276     j = height() - 2 * f_size;
277     p.setCompositionMode(QPainter::CompositionMode_ColorDodge);
278     QFontMetrics fm(*sfont);
279 
280     if (fm.horizontalAdvance(s1) > rx.width()) {
281       s1 = fm.elidedText(s1, Qt::ElideRight, rx.width());
282     }
283 
284     i = rx.width() - fm.horizontalAdvance(s1) + pix_width;
285     i = qMax(0, i);
286     p.drawText(i / 2, j / 3 + f_size, s1);
287 
288     if (fm.horizontalAdvance(s2) > rx.width()) {
289       s2 = fm.elidedText(s2, Qt::ElideRight, rx.width());
290     }
291 
292     i = rx.width() - fm.horizontalAdvance(s2) + pix_width;
293     i = qMax(0, i);
294 
295     p.drawText(i / 2, height() - j / 3, s2);
296   } else {
297     QString s;
298     int i, j;
299     s = text();
300     j = height() - f_size;
301     p.setCompositionMode(QPainter::CompositionMode_ColorDodge);
302     QFontMetrics fm(*sfont);
303 
304     if (fm.horizontalAdvance(s) > rx.width()) {
305       s = fm.elidedText(s, Qt::ElideRight, rx.width());
306     }
307 
308     i = rx.width() - fm.horizontalAdvance(s) + pix_width;
309     i = qMax(0, i);
310     p.drawText(i / 2, j / 2 + f_size, s);
311   }
312   p.end();
313 }
314 
315 /****************************************************************************
316   Creates region with diagonal lines
317 ****************************************************************************/
create_region()318 void progress_bar::create_region()
319 {
320   int offset;
321   QRect   r(-50, 0, width() + 50, height());
322   int chunk_width = 16;
323   int size = width()  + 50;
324   reg = QRegion();
325 
326   for (offset = 0; offset < (size * 2); offset += (chunk_width * 2)) {
327     QPolygon a;
328 
329     a.setPoints(4, r.x(), r.y() + offset,
330                 r.x() + r.width(), (r.y() + offset) - size,
331                 r.x() + r.width(),
332                 (r.y() + offset + chunk_width) - size,
333                 r.x(), r.y() + offset + chunk_width);
334     reg += QRegion(a);
335   }
336 
337 }
338 
339 /****************************************************************************
340   Draws X on pixmap pointing its useless
341 ****************************************************************************/
pixmap_put_x(QPixmap * pix)342 static void pixmap_put_x(QPixmap *pix)
343 {
344   QPen pen(QColor(0, 0, 0));
345   QPainter p;
346 
347   pen.setWidth(2);
348   p.begin(pix);
349   p.setRenderHint(QPainter::Antialiasing);
350   p.setPen(pen);
351   p.drawLine(0, 0, pix->width(), pix->height());
352   p.drawLine(pix->width(), 0, 0, pix->height());
353   p.end();
354 }
355 
356 /****************************************************************************
357   Improvement item constructor
358 ****************************************************************************/
impr_item(QWidget * parent,impr_type * building,struct city * city)359 impr_item::impr_item(QWidget *parent, impr_type *building,
360                      struct city *city): QLabel(parent)
361 {
362   setParent(parent);
363   pcity = city;
364   impr = building;
365   impr_pixmap = nullptr;
366   struct sprite *sprite;
367   sprite = get_building_sprite(tileset , building);
368 
369   if (sprite != nullptr) {
370     impr_pixmap = qtg_canvas_create(sprite->pm->width(),
371                                     sprite->pm->height());
372     impr_pixmap->map_pixmap.fill(Qt::transparent);
373     pixmap_copy(&impr_pixmap->map_pixmap, sprite->pm, 0 , 0, 0, 0,
374                 sprite->pm->width(), sprite->pm->height());
375   } else {
376     impr_pixmap = qtg_canvas_create(10, 10);
377     impr_pixmap->map_pixmap.fill(Qt::red);
378   }
379 
380   setFixedWidth(impr_pixmap->map_pixmap.width() + 4);
381   setFixedHeight(impr_pixmap->map_pixmap.height());
382   setToolTip(get_tooltip_improvement(building, city, true).trimmed());
383 }
384 
385 /****************************************************************************
386   Improvement item destructor
387 ****************************************************************************/
~impr_item()388 impr_item::~impr_item()
389 {
390   if (impr_pixmap) {
391     canvas_free(impr_pixmap);
392   }
393 }
394 
395 /****************************************************************************
396   Sets pixmap to improvemnt item
397 ****************************************************************************/
init_pix()398 void impr_item::init_pix()
399 {
400   setPixmap(impr_pixmap->map_pixmap);
401   update();
402 }
403 
404 /****************************************************************************
405   Mouse enters widget
406 ****************************************************************************/
enterEvent(QEvent * event)407 void impr_item::enterEvent(QEvent *event)
408 {
409   struct sprite *sprite;
410   QPainter p;
411 
412   if (impr_pixmap) {
413     canvas_free(impr_pixmap);
414   }
415 
416   sprite = get_building_sprite(tileset , impr);
417   if (impr && sprite) {
418     impr_pixmap = qtg_canvas_create(sprite->pm->width(),
419                                     sprite->pm->height());
420     impr_pixmap->map_pixmap.fill(QColor(palette().color(QPalette::Highlight)));
421     pixmap_copy(&impr_pixmap->map_pixmap, sprite->pm, 0 , 0, 0, 0,
422                 sprite->pm->width(), sprite->pm->height());
423   } else {
424     impr_pixmap = qtg_canvas_create(10, 10);
425     impr_pixmap->map_pixmap.fill(QColor(palette().color(QPalette::Highlight)));
426   }
427 
428   init_pix();
429 }
430 
431 /****************************************************************************
432   Mouse leaves widget
433 ****************************************************************************/
leaveEvent(QEvent * event)434 void impr_item::leaveEvent(QEvent *event)
435 {
436   struct sprite *sprite;
437 
438   if (impr_pixmap) {
439     canvas_free(impr_pixmap);
440   }
441 
442   sprite = get_building_sprite(tileset , impr);
443   if (impr && sprite) {
444     impr_pixmap = qtg_canvas_create(sprite->pm->width(),
445                                     sprite->pm->height());
446     impr_pixmap->map_pixmap.fill(Qt::transparent);
447     pixmap_copy(&impr_pixmap->map_pixmap, sprite->pm, 0 , 0, 0, 0,
448                 sprite->pm->width(), sprite->pm->height());
449   } else {
450     impr_pixmap = qtg_canvas_create(10, 10);
451     impr_pixmap->map_pixmap.fill(Qt::red);
452   }
453 
454   init_pix();
455 }
456 
457 /****************************************************************************
458   Improvement list constructor
459 ****************************************************************************/
impr_info(QWidget * parent)460 impr_info::impr_info(QWidget *parent): QFrame(parent)
461 {
462   setParent(parent);
463   layout = new QHBoxLayout(this);
464   init_layout();
465 }
466 
467 /****************************************************************************
468   Inits improvement list constructor
469 ****************************************************************************/
init_layout()470 void impr_info::init_layout()
471 {
472   QSizePolicy size_fixed_policy(QSizePolicy::Fixed,
473                                 QSizePolicy::MinimumExpanding,
474                                 QSizePolicy::Slider);
475 
476   setSizePolicy(size_fixed_policy);
477   setLayout(layout);
478 }
479 
480 /****************************************************************************
481   Improvement list destructor
482 ****************************************************************************/
~impr_info()483 impr_info::~impr_info()
484 {
485 
486 }
487 
488 /****************************************************************************
489   Adds improvement item to list
490 ****************************************************************************/
add_item(impr_item * item)491 void impr_info::add_item(impr_item *item)
492 {
493   impr_list.append(item);
494 }
495 
496 /****************************************************************************
497   Clears layout on improvement list
498 ****************************************************************************/
clear_layout()499 void impr_info::clear_layout()
500 {
501   int i = impr_list.count();
502   impr_item *ui;
503   int j;
504   setUpdatesEnabled(false);
505   setMouseTracking(false);
506 
507   for (j = 0; j < i; j++) {
508     ui = impr_list[j];
509     layout->removeWidget(ui);
510     delete ui;
511   }
512 
513   while (!impr_list.empty()) {
514     impr_list.removeFirst();
515   }
516 
517   setMouseTracking(true);
518   setUpdatesEnabled(true);
519 }
520 
521 /****************************************************************************
522   Mouse wheel event - send it to scrollbar
523 ****************************************************************************/
wheelEvent(QWheelEvent * event)524 void impr_info::wheelEvent(QWheelEvent *event)
525 {
526   QPoint p;
527 
528   p = parentWidget()->parentWidget()->pos();
529   p = mapToGlobal(p);
530   QWheelEvent new_event(QPoint(5, 5), p + QPoint(5,5), event->pixelDelta(),
531                         event->angleDelta(),
532                         event->angleDelta().y(),
533                         Qt::Horizontal,  event->buttons(),
534                         event->modifiers());
535   QApplication::sendEvent(parentWidget(), &new_event);
536 }
537 
538 /****************************************************************************
539   Updates list of improvements
540 ****************************************************************************/
update_buildings()541 void impr_info::update_buildings()
542 {
543   int i = impr_list.count();
544   int j;
545   int h = 0;
546   impr_item *ui;
547 
548   setUpdatesEnabled(false);
549   hide();
550 
551   for (j = 0; j < i; j++) {
552     ui = impr_list[j];
553     h = ui->height();
554     layout->addWidget(ui, 0, Qt::AlignVCenter);
555   }
556 
557   if (impr_list.count() > 0) {
558     parentWidget()->parentWidget()->setFixedHeight(city_dlg->scroll_height
559                                                    + h + 6);
560   } else {
561     parentWidget()->parentWidget()->setFixedHeight(0);
562   }
563 
564   show();
565   setUpdatesEnabled(true);
566   layout->update();
567   updateGeometry();
568 }
569 
570 /****************************************************************************
571   Mouse wheel event - send it to scrollbar
572 ****************************************************************************/
wheelEvent(QWheelEvent * event)573 void impr_item::wheelEvent(QWheelEvent *event)
574 {
575   QPoint p;
576 
577   p = parentWidget()->parentWidget()->pos();
578   p = mapToGlobal(p);
579   QWheelEvent new_event(QPoint(5, 5), p + QPoint(5,5), event->pixelDelta(),
580                         event->angleDelta(),
581                         event->angleDelta().y(),
582                         Qt::Horizontal,  event->buttons(),
583                         event->modifiers());
584   QApplication::sendEvent(parentWidget()->parentWidget(),
585                           &new_event);
586 }
587 
588 /****************************************************************************
589   Double click event on improvement item
590 ****************************************************************************/
mouseDoubleClickEvent(QMouseEvent * event)591 void impr_item::mouseDoubleClickEvent(QMouseEvent *event)
592 {
593   hud_message_box ask(city_dlg);
594   QString s;
595   char buf[256];
596   int price;
597   int ret;
598 
599   if (!can_client_issue_orders()) {
600     return;
601   }
602 
603   if (event->button() == Qt::LeftButton) {
604     if (test_player_sell_building_now(client.conn.playing, pcity,
605                                       impr) != TR_SUCCESS) {
606       return;
607     }
608 
609     price = impr_sell_gold(impr);
610     fc_snprintf(buf, ARRAY_SIZE(buf),
611                 PL_("Sell %s for %d gold?",
612                     "Sell %s for %d gold?", price),
613                 city_improvement_name_translation(pcity, impr), price);
614 
615     s = QString(buf);
616     ask.set_text_title(s, (_("Sell improvement?")));
617     ask.setStandardButtons(QMessageBox::Cancel | QMessageBox::Ok);
618     ret = ask.exec();
619 
620     switch (ret) {
621     case QMessageBox::Cancel:
622       return;
623 
624     case QMessageBox::Ok:
625       city_sell_improvement(pcity, improvement_number(impr));
626       break;
627     }
628   }
629 }
630 
631 
632 /****************************************************************************
633   Class representing one unit, allows context menu, holds pixmap for it
634 ****************************************************************************/
unit_item(QWidget * parent,struct unit * punit,bool supp,int hppy_cost)635 unit_item::unit_item(QWidget *parent, struct unit *punit,
636                      bool supp, int hppy_cost) : QLabel()
637 {
638   happy_cost = hppy_cost;
639   QImage cropped_img;
640   QImage img;
641   QRect crop;
642   qunit = punit;
643   struct canvas *unit_pixmap;
644   struct tileset *tmp;
645   float isosize;
646 
647   setParent(parent);
648   supported = supp;
649 
650   tmp = nullptr;
651   if (unscaled_tileset) {
652     tmp = tileset;
653     tileset = unscaled_tileset;
654   }
655   isosize = 0.6;
656   if (tileset_hex_height(tileset) > 0 || tileset_hex_width(tileset) > 0) {
657     isosize = 0.45;
658   }
659 
660   if (punit) {
661     if (supported) {
662       unit_pixmap = qtg_canvas_create(tileset_unit_width(get_tileset()),
663                              tileset_unit_with_upkeep_height(get_tileset()));
664     } else {
665       unit_pixmap = qtg_canvas_create(tileset_unit_width(get_tileset()),
666                                       tileset_unit_height(get_tileset()));
667     }
668 
669     unit_pixmap->map_pixmap.fill(Qt::transparent);
670     put_unit(punit, unit_pixmap, 1.0, 0, 0);
671 
672     if (supported) {
673       put_unit_city_overlays(punit, unit_pixmap, 0,
674                              tileset_unit_layout_offset_y(get_tileset()),
675                              punit->upkeep, happy_cost);
676     }
677   } else {
678     unit_pixmap = qtg_canvas_create(10, 10);
679     unit_pixmap->map_pixmap.fill(Qt::transparent);
680   }
681 
682   img = unit_pixmap->map_pixmap.toImage();
683   crop = zealous_crop_rect(img);
684   cropped_img = img.copy(crop);
685   if (tileset_is_isometric(tileset)) {
686     unit_img = cropped_img.scaledToHeight(tileset_unit_width(get_tileset())
687                                           * isosize, Qt::SmoothTransformation);
688   } else {
689     unit_img = cropped_img.scaledToHeight(tileset_unit_width(get_tileset()),
690                                           Qt::SmoothTransformation);
691   }
692   canvas_free(unit_pixmap);
693   if (tmp != nullptr) {
694     tileset = tmp;
695   }
696 
697   create_actions();
698   setFixedWidth(unit_img.width() + 4);
699   setFixedHeight(unit_img.height());
700   setToolTip(unit_description(qunit));
701 }
702 
703 /****************************************************************************
704   Sets pixmap for unit_item class
705 ****************************************************************************/
init_pix()706 void unit_item::init_pix()
707 {
708   setPixmap(QPixmap::fromImage(unit_img));
709   update();
710 }
711 
712 /****************************************************************************
713   Destructor for unit item
714 ****************************************************************************/
~unit_item()715 unit_item::~unit_item()
716 {
717 }
718 
719 /****************************************************************************
720   Context menu handler
721 ****************************************************************************/
contextMenuEvent(QContextMenuEvent * event)722 void unit_item::contextMenuEvent(QContextMenuEvent *event)
723 {
724   QMenu *menu;
725 
726   if (!can_client_issue_orders()) {
727     return;
728   }
729 
730   if (unit_owner(qunit) != client_player()) {
731     return;
732   }
733 
734   menu = new QMenu(gui()->central_wdg);
735   menu->addAction(activate);
736   menu->addAction(activate_and_close);
737 
738   if (sentry) {
739     menu->addAction(sentry);
740   }
741 
742   if (fortify) {
743     menu->addAction(fortify);
744   }
745 
746   if (change_home) {
747     menu->addAction(change_home);
748   }
749 
750   if (load) {
751     menu->addAction(load);
752   }
753 
754   if (unload) {
755     menu->addAction(unload);
756   }
757 
758   if (unload_trans) {
759     menu->addAction(unload_trans);
760   }
761 
762   if (disband_action) {
763     menu->addAction(disband_action);
764   }
765 
766   if (upgrade) {
767     menu->addAction(upgrade);
768   }
769 
770   menu->popup(event->globalPos());
771 }
772 
773 /****************************************************************************
774   Initializes context menu
775 ****************************************************************************/
create_actions()776 void unit_item::create_actions()
777 {
778   struct unit_list *qunits;
779 
780   if (unit_owner(qunit) != client_player() || !can_client_issue_orders()) {
781     return;
782   }
783 
784   qunits = unit_list_new();
785   unit_list_append(qunits, qunit);
786   activate = new QAction(_("Activate unit"), this);
787   connect(activate, &QAction::triggered, this, &unit_item::activate_unit);
788   activate_and_close = new QAction(_("Activate and close dialog"), this);
789   connect(activate_and_close, &QAction::triggered, this,
790           &unit_item::activate_and_close_dialog);
791 
792   if (can_unit_do_activity(qunit, ACTIVITY_SENTRY)) {
793     sentry = new QAction(_("Sentry unit"), this);
794     connect(sentry, &QAction::triggered, this, &unit_item::sentry_unit);
795   } else {
796     sentry = NULL;
797   }
798 
799   if (can_unit_do_activity(qunit, ACTIVITY_FORTIFYING)) {
800     fortify = new QAction(_("Fortify unit"), this);
801     connect(fortify, &QAction::triggered, this, &unit_item::fortify_unit);
802   } else {
803     fortify = NULL;
804   }
805 
806   if (!unit_has_type_flag(qunit, UTYF_UNDISBANDABLE)) {
807     disband_action = new QAction(_("Disband unit"), this);
808     connect(disband_action, &QAction::triggered, this, &unit_item::disband);
809   } else {
810     disband_action = NULL;
811   }
812 
813   if (can_unit_change_homecity(qunit)) {
814     change_home = new QAction(_("Change homecity"), this);
815     connect(change_home, &QAction::triggered, this, &unit_item::change_homecity);
816   } else {
817     change_home = NULL;
818   }
819 
820   if (units_can_load(qunits)) {
821     load = new QAction(_("Load"), this);
822     connect(load, &QAction::triggered, this, &unit_item::load_unit);
823   } else {
824     load = NULL;
825   }
826 
827   if (units_can_unload(qunits)) {
828     unload = new QAction(_("Unload"), this);
829     connect(unload, &QAction::triggered, this, &unit_item::unload_unit);
830   } else {
831     unload = NULL;
832   }
833 
834   if (units_are_occupied(qunits)) {
835     unload_trans = new QAction(_("Unload All From Transporter"), this);
836     connect(unload_trans, &QAction::triggered, this, &unit_item::unload_all);
837   } else {
838     unload_trans = NULL;
839   }
840 
841   if (units_can_upgrade(qunits)) {
842     upgrade = new QAction(_("Upgrade Unit"), this);
843     connect(upgrade, &QAction::triggered, this, &unit_item::upgrade_unit);
844   } else {
845     upgrade = NULL;
846   }
847 
848   unit_list_destroy(qunits);
849 }
850 
851 /****************************************************************************
852   Popups MessageBox  for disbanding unit and disbands it
853 ****************************************************************************/
disband()854 void unit_item::disband()
855 {
856   struct unit_list *punits;
857   struct unit *punit = player_unit_by_number(client_player(), qunit->id);
858 
859   if (punit == nullptr) {
860     return;
861   }
862 
863   punits = unit_list_new();
864   unit_list_append(punits, punit);
865   popup_disband_dialog(punits);
866   unit_list_destroy(punits);
867 }
868 
869 /****************************************************************************
870   Loads unit into some tranport
871 ****************************************************************************/
load_unit()872 void unit_item::load_unit()
873 {
874   qtg_request_transport(qunit, unit_tile(qunit));
875 }
876 
877 /****************************************************************************
878   Unloads unit
879 ****************************************************************************/
unload_unit()880 void unit_item::unload_unit()
881 {
882   request_unit_unload(qunit);
883 }
884 
885 /****************************************************************************
886   Unloads all units from transporter
887 ****************************************************************************/
unload_all()888 void unit_item::unload_all()
889 {
890   request_unit_unload_all(qunit);
891 }
892 
893 /****************************************************************************
894   Upgrades unit
895 ****************************************************************************/
upgrade_unit()896 void unit_item::upgrade_unit()
897 {
898   struct unit_list *qunits;
899   qunits = unit_list_new();
900   unit_list_append(qunits, qunit);
901   popup_upgrade_dialog(qunits);
902   unit_list_destroy(qunits);
903 }
904 
905 /****************************************************************************
906   Changes homecity for given unit
907 ****************************************************************************/
change_homecity()908 void unit_item::change_homecity()
909 {
910   if (qunit) {
911     request_unit_change_homecity(qunit);
912   }
913 }
914 
915 /****************************************************************************
916   Activates unit and closes city dialog
917 ****************************************************************************/
activate_and_close_dialog()918 void unit_item::activate_and_close_dialog()
919 {
920   if (qunit) {
921     unit_focus_set(qunit);
922     qtg_popdown_all_city_dialogs();
923   }
924 }
925 
926 /****************************************************************************
927   Activates unit in city dialog
928 ****************************************************************************/
activate_unit()929 void unit_item::activate_unit()
930 {
931   if (qunit) {
932     unit_focus_set(qunit);
933   }
934 }
935 
936 /****************************************************************************
937   Fortifies unit in city dialog
938 ****************************************************************************/
fortify_unit()939 void unit_item::fortify_unit()
940 {
941   if (qunit) {
942     request_unit_fortify(qunit);
943   }
944 }
945 
946 /****************************************************************************
947   Mouse entered widget
948 ****************************************************************************/
enterEvent(QEvent * event)949 void unit_item::enterEvent(QEvent *event)
950 {
951   QImage temp_img(unit_img.size(), QImage::Format_ARGB32_Premultiplied);
952   QPainter p;
953 
954   p.begin(&temp_img);
955   p.fillRect(0, 0, unit_img.width(), unit_img.height(),
956              QColor(palette().color(QPalette::Highlight)));
957   p.drawImage(0, 0, unit_img);
958   p.end();
959 
960   setPixmap(QPixmap::fromImage(temp_img));
961   update();
962 }
963 
964 /****************************************************************************
965   Mouse left widget
966 ****************************************************************************/
leaveEvent(QEvent * event)967 void unit_item::leaveEvent(QEvent *event)
968 {
969   init_pix();
970 }
971 
972 /****************************************************************************
973   Mouse wheel event - send it to scrollbar
974 ****************************************************************************/
wheelEvent(QWheelEvent * event)975 void unit_item::wheelEvent(QWheelEvent *event)
976 {
977   QPoint p;
978 
979   p = parentWidget()->parentWidget()->pos();
980   p = mapToGlobal(p);
981   QWheelEvent new_event(QPoint(5, 5), p + QPoint(5,5), event->pixelDelta(),
982                         event->angleDelta(),
983                         event->angleDelta().y(),
984                         Qt::Horizontal,  event->buttons(),
985                         event->modifiers());
986   QApplication::sendEvent(parentWidget()->parentWidget(),
987                           &new_event);
988 }
989 
990 
991 /****************************************************************************
992   Mouse press event -activates unit and closes dialog
993 ****************************************************************************/
mousePressEvent(QMouseEvent * event)994 void unit_item::mousePressEvent(QMouseEvent *event)
995 {
996   if (event->button() == Qt::LeftButton) {
997     if (qunit) {
998       unit_focus_set(qunit);
999       qtg_popdown_all_city_dialogs();
1000     }
1001   }
1002 }
1003 
1004 /****************************************************************************
1005   Sentries unit in city dialog
1006 ****************************************************************************/
sentry_unit()1007 void unit_item::sentry_unit()
1008 {
1009   if (qunit) {
1010     request_unit_sentry(qunit);
1011   }
1012 }
1013 
1014 /****************************************************************************
1015   Class representing list of units ( unit_item 's)
1016 ****************************************************************************/
unit_info(bool supp)1017 unit_info::unit_info(bool supp) : QFrame()
1018 {
1019   layout = new QHBoxLayout(this);
1020   init_layout();
1021   supports = supp;
1022 }
1023 
1024 /****************************************************************************
1025   Destructor for unit_info
1026 ****************************************************************************/
~unit_info()1027 unit_info::~unit_info()
1028 {
1029   qDeleteAll(unit_list);
1030   unit_list.clear();
1031 }
1032 
1033 /****************************************************************************
1034   Adds one unit to list
1035 ****************************************************************************/
add_item(unit_item * item)1036 void unit_info::add_item(unit_item *item)
1037 {
1038   unit_list.append(item);
1039 }
1040 
1041 /****************************************************************************
1042   Initiazlizes layout ( layout needs to be changed after adding units )
1043 ****************************************************************************/
init_layout()1044 void unit_info::init_layout()
1045 {
1046   QSizePolicy size_fixed_policy(QSizePolicy::Fixed,
1047                                 QSizePolicy::MinimumExpanding,
1048                                 QSizePolicy::Slider);
1049   setSizePolicy(size_fixed_policy);
1050   setLayout(layout);
1051 }
1052 
1053 /****************************************************************************
1054   Mouse wheel event - send it to scrollbar
1055 ****************************************************************************/
wheelEvent(QWheelEvent * event)1056 void unit_info::wheelEvent(QWheelEvent *event)
1057 {
1058   QPoint p;
1059 
1060   p = parentWidget()->parentWidget()->pos();
1061   p = mapToGlobal(p);
1062   QWheelEvent new_event(QPoint(5, 5), p + QPoint(5,5), event->pixelDelta(),
1063                         event->angleDelta(),
1064                         event->angleDelta().y(),
1065                         Qt::Horizontal,  event->buttons(),
1066                         event->modifiers());
1067   QApplication::sendEvent(parentWidget(), &new_event);
1068 }
1069 
1070 /****************************************************************************
1071   Updates units
1072 ****************************************************************************/
update_units()1073 void unit_info::update_units()
1074 {
1075   int i = unit_list.count();
1076   int j;
1077   int h;
1078   float hexfix;
1079   unit_item *ui;
1080 
1081   setUpdatesEnabled(false);
1082   hide();
1083 
1084   for (j = 0; j < i; j++) {
1085     ui = unit_list[j];
1086     layout->addWidget(ui, 0, Qt::AlignVCenter);
1087   }
1088 
1089   hexfix = 1.0;
1090   if (tileset_hex_height(tileset) > 0 || tileset_hex_width(tileset) > 0) {
1091     hexfix = 0.75;
1092   }
1093 
1094   if (tileset_is_isometric(tileset)) {
1095     h = tileset_unit_width(get_tileset()) * 0.7 * hexfix + 6;
1096   } else {
1097     h = tileset_unit_width(get_tileset()) + 6;
1098   }
1099   if (unit_list.count() > 0) {
1100     parentWidget()->parentWidget()->setFixedHeight(city_dlg->scroll_height
1101                                                    + h);
1102   } else {
1103     parentWidget()->parentWidget()->setFixedHeight(0);
1104   }
1105   show();
1106   setUpdatesEnabled(true);
1107   layout->update();
1108   updateGeometry();
1109 }
1110 
1111 /****************************************************************************
1112   Cleans layout - run it before layout initialization
1113 ****************************************************************************/
clear_layout()1114 void unit_info::clear_layout()
1115 {
1116   int i = unit_list.count();
1117   unit_item *ui;
1118   int j;
1119   setUpdatesEnabled(false);
1120   setMouseTracking(false);
1121 
1122   for (j = 0; j < i; j++) {
1123     ui = unit_list[j];
1124     layout->removeWidget(ui);
1125     delete ui;
1126   }
1127 
1128   while (!unit_list.empty()) {
1129     unit_list.removeFirst();
1130   }
1131 
1132   setMouseTracking(true);
1133   setUpdatesEnabled(true);
1134 }
1135 
1136 /****************************************************************************
1137   city_label is used only for showing citizens icons
1138   and was created only to catch mouse events
1139 ****************************************************************************/
city_label(int t,QWidget * parent)1140 city_label::city_label(int t, QWidget *parent) : QLabel(parent)
1141 {
1142   type = t;
1143 }
1144 
1145 /****************************************************************************
1146   Mouse handler for city_label
1147 ****************************************************************************/
mousePressEvent(QMouseEvent * event)1148 void city_label::mousePressEvent(QMouseEvent *event)
1149 {
1150   int citnum, i;
1151   int w = tileset_small_sprite_width(tileset) / gui()->map_scale;
1152   int num_citizens = pcity->size;
1153 
1154   if (cma_is_city_under_agent(pcity, NULL)) {
1155     return;
1156   }
1157 
1158   i = 1 + (num_citizens * 5 / 200);
1159   w = w / i;
1160   citnum = event->x() / w;
1161 
1162   if (!can_client_issue_orders()) {
1163     return;
1164   }
1165 
1166   city_rotate_specialist(pcity, citnum);
1167 }
1168 
1169 /****************************************************************************
1170   Just sets target city for city_label
1171 ****************************************************************************/
set_city(city * pciti)1172 void city_label::set_city(city *pciti)
1173 {
1174   pcity = pciti;
1175 }
1176 
1177 /****************************************************************************
1178   Used for showing tiles and workers view in city dialog
1179 ****************************************************************************/
city_map(QWidget * parent)1180 city_map::city_map(QWidget *parent): QWidget(parent)
1181 {
1182   setParent(parent);
1183   radius = 0;
1184   wdth = get_citydlg_canvas_width();
1185   hight = get_citydlg_canvas_height();
1186   cutted_width = wdth;
1187   cutted_height = hight;
1188   view = qtg_canvas_create(wdth, hight);
1189   view->map_pixmap.fill(Qt::black);
1190   miniview = qtg_canvas_create(0, 0);
1191   miniview->map_pixmap.fill(Qt::black);
1192   delta_x = 0;
1193   delta_y = 0;
1194   setContextMenuPolicy(Qt::CustomContextMenu);
1195   connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
1196           this, SLOT(context_menu(const QPoint &)));
1197 }
1198 
1199 /****************************************************************************
1200   Destructor for city map
1201 ****************************************************************************/
~city_map()1202 city_map::~city_map()
1203 {
1204   qtg_canvas_free(view);
1205   qtg_canvas_free(miniview);
1206 }
1207 
1208 /****************************************************************************
1209   Redraws whole view in city_map
1210 ****************************************************************************/
paintEvent(QPaintEvent * event)1211 void city_map::paintEvent(QPaintEvent *event)
1212 {
1213   QPainter painter;
1214   QString str;
1215 
1216   painter.begin(this);
1217   painter.drawPixmap(0, 0, zoomed_pixmap);
1218 
1219   if (cma_is_city_under_agent(mcity, NULL)) {
1220     painter.fillRect(0, 0, zoomed_pixmap.width(), zoomed_pixmap.height(),
1221                      QBrush(QColor(60, 60 , 60 , 110)));
1222     painter.setPen(QColor(255, 255, 255));
1223     /* TRANS: %1 is custom string choosen by player. */
1224     str = QString(_("Governor %1"))
1225           .arg(cmafec_get_short_descr_of_city(mcity));
1226     painter.drawText(5, zoomed_pixmap.height() - 10, str);
1227   }
1228 
1229   painter.end();
1230 }
1231 
1232 /****************************************************************************
1233   Calls function to put pixmap on view ( it doesn't draw on screen )
1234 ****************************************************************************/
set_pixmap(struct city * pcity,float z)1235 void city_map::set_pixmap(struct city *pcity, float z)
1236 {
1237   int r, max_r;
1238   QSize size;
1239 
1240   zoom = z;
1241   r = sqrt(city_map_radius_sq_get(pcity));
1242 
1243   if (radius != r) {
1244     max_r = sqrt(rs_max_city_radius_sq());
1245     radius = r;
1246     qtg_canvas_free(miniview);
1247     cutted_width = wdth * (r + 1) / max_r;
1248     cutted_height = hight * (r + 1) / max_r;
1249     cutted_width = qMin(cutted_width, wdth);
1250     cutted_height = qMin(cutted_height, hight);
1251     delta_x = (wdth - cutted_width) / 2;
1252     delta_y = (hight - cutted_height) / 2;
1253     miniview = qtg_canvas_create(cutted_width, cutted_height);
1254     miniview->map_pixmap.fill(Qt::black);
1255   }
1256 
1257   city_dialog_redraw_map(pcity, view);
1258   qtg_canvas_copy(miniview, view, delta_x, delta_y,
1259                   0, 0, cutted_width, cutted_height);
1260   size = miniview->map_pixmap.size();
1261   zoomed_pixmap = miniview->map_pixmap.scaled(size * zoom,
1262                                               Qt::KeepAspectRatio,
1263                                               Qt::SmoothTransformation);
1264   setFixedSize(zoomed_pixmap.size());
1265   mcity = pcity;
1266 }
1267 
1268 /****************************************************************************
1269   Size hint for city map
1270 ****************************************************************************/
sizeHint() const1271 QSize city_map::sizeHint() const
1272 {
1273   return zoomed_pixmap.size();
1274 }
1275 
1276 /****************************************************************************
1277   Minimum size hint for city map
1278 ****************************************************************************/
minimumSizeHint() const1279 QSize city_map::minimumSizeHint() const
1280 {
1281   return zoomed_pixmap.size();
1282 }
1283 
1284 /****************************************************************************
1285   Used to change workers on view
1286 ****************************************************************************/
mousePressEvent(QMouseEvent * event)1287 void city_map::mousePressEvent(QMouseEvent *event)
1288 {
1289   int canvas_x, canvas_y, city_x, city_y;
1290 
1291   if (!can_client_issue_orders() || event->button() != Qt::LeftButton) {
1292     return;
1293   }
1294 
1295   canvas_x = event->x() / zoom + delta_x;
1296   canvas_y = event->y() / zoom + delta_y;
1297 
1298   if (canvas_to_city_pos(&city_x, &city_y, city_map_radius_sq_get(mcity),
1299                          canvas_x, canvas_y)) {
1300     city_toggle_worker(mcity, city_x, city_y);
1301   }
1302 }
1303 
1304 /****************************************************************************
1305   Context menu for setting worker tasks
1306 ****************************************************************************/
context_menu(QPoint point)1307 void city_map::context_menu(QPoint point)
1308 {
1309   int canvas_x, canvas_y, city_x, city_y;
1310   QAction *act;
1311   QAction con_clear(_("Clear"), this);
1312   QAction con_irrig_tf(_("Irrigate"), this);
1313   QAction con_irrig(_("Irrigate"), this);
1314   QAction con_mine_tf(_("Plant"), this);
1315   QAction con_mine(_("Mine"), this);
1316   QAction con_road(_("Road"), this);
1317   QAction con_trfrm(_("Transform"), this);
1318   QAction con_pollution(_("Clean Pollution"), this);
1319   QAction con_fallout(_("Clean Fallout"), this);
1320   QMenu con_menu(this);
1321   QWidgetAction *wid_act;
1322   struct packet_worker_task task;
1323   struct terrain *pterr;
1324   struct tile *ptile;
1325   struct universal for_terr;
1326   struct worker_task *ptask;
1327 
1328   if (!can_client_issue_orders()) {
1329     return;
1330   }
1331 
1332   canvas_x = point.x() / zoom + delta_x;
1333   canvas_y = point.y() / zoom + delta_y;
1334 
1335   if (!canvas_to_city_pos(&city_x, &city_y, city_map_radius_sq_get(mcity),
1336                           canvas_x, canvas_y)) {
1337     return;
1338   }
1339 
1340   ptile = city_map_to_tile(mcity->tile, city_map_radius_sq_get(mcity),
1341                            city_x, city_y);
1342   task.city_id = mcity->id;
1343   pterr = tile_terrain(ptile);
1344   for_terr.kind = VUT_TERRAIN;
1345   for_terr.value.terrain = pterr;
1346   ptask = worker_task_list_get(mcity->task_reqs, 0);
1347 
1348   wid_act = new QWidgetAction(this);
1349   wid_act->setDefaultWidget(new QLabel(_("Autosettler activity:")));
1350   con_menu.addAction(wid_act);
1351 
1352   if (pterr->mining_result != pterr && pterr->mining_result != NULL
1353       && effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_terr) > 0) {
1354     con_menu.addAction(&con_mine_tf);
1355   } else if (pterr->mining_result == pterr
1356              && effect_cumulative_max(EFT_MINING_POSSIBLE, &for_terr) > 0) {
1357     con_menu.addAction(&con_mine);
1358   }
1359 
1360   if (pterr->irrigation_result != pterr && pterr->irrigation_result != NULL
1361       && effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_terr) > 0) {
1362     con_menu.addAction(&con_irrig_tf);
1363   } else if (pterr->irrigation_result == pterr
1364              && effect_cumulative_max(EFT_IRRIG_POSSIBLE, &for_terr) > 0) {
1365     con_menu.addAction(&con_irrig);
1366   }
1367 
1368   if (pterr->transform_result != pterr && pterr->transform_result != NULL
1369       && effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_terr) > 0) {
1370     con_menu.addAction(&con_trfrm);
1371   }
1372 
1373   if (next_extra_for_tile(ptile, EC_ROAD, city_owner(mcity), NULL) != NULL) {
1374     con_menu.addAction(&con_road);
1375   }
1376 
1377   if (prev_extra_in_tile(ptile, ERM_CLEANPOLLUTION,
1378                          city_owner(mcity), NULL) != NULL) {
1379     con_menu.addAction(&con_pollution);
1380   }
1381 
1382   if (prev_extra_in_tile(ptile, ERM_CLEANFALLOUT,
1383                          city_owner(mcity), NULL) != NULL) {
1384     con_menu.addAction(&con_fallout);
1385   }
1386 
1387   if (ptask != NULL) {
1388     con_menu.addAction(&con_clear);
1389   }
1390 
1391   act = con_menu.exec(mapToGlobal(point));
1392 
1393   if (act) {
1394     bool target = FALSE;
1395 
1396     if (act == &con_road) {
1397       task.activity = ACTIVITY_GEN_ROAD;
1398       target = TRUE;
1399     } else if (act == &con_mine) {
1400       task.activity = ACTIVITY_MINE;
1401       target = TRUE;
1402     } else if (act == &con_mine_tf) {
1403       task.activity = ACTIVITY_MINE;
1404     } else if (act == &con_irrig) {
1405       task.activity = ACTIVITY_IRRIGATE;
1406       target = TRUE;
1407     } else if (act == &con_irrig_tf) {
1408       task.activity = ACTIVITY_IRRIGATE;
1409     } else if (act == &con_trfrm) {
1410       task.activity = ACTIVITY_TRANSFORM;
1411     } else if (act == &con_pollution) {
1412       task.activity = ACTIVITY_POLLUTION;
1413       target = TRUE;
1414     } else if (act == &con_fallout) {
1415       task.activity = ACTIVITY_FALLOUT;
1416       target = TRUE;
1417     } else if (act == &con_clear) {
1418       task.activity = ACTIVITY_LAST;
1419     } else {
1420       /* Closed dialog without selecting any activity entry. */
1421       return;
1422     }
1423 
1424     task.want = 100;
1425 
1426     if (target) {
1427       enum extra_cause cause = activity_to_extra_cause(task.activity);
1428       enum extra_rmcause rmcause = activity_to_extra_rmcause(task.activity);
1429       struct extra_type *tgt;
1430 
1431       if (cause != EC_NONE) {
1432         tgt = next_extra_for_tile(ptile, cause, city_owner(mcity), NULL);
1433       } else if (rmcause != ERM_NONE) {
1434         tgt = prev_extra_in_tile(ptile, rmcause, city_owner(mcity), NULL);
1435       } else {
1436         tgt = NULL;
1437       }
1438 
1439       if (tgt != NULL) {
1440         task.tgt = extra_index(tgt);
1441       } else {
1442         task.tgt = -1;
1443       }
1444     } else {
1445       task.tgt = -1;
1446     }
1447 
1448     task.tile_id = ptile->index;
1449     send_packet_worker_task(&client.conn, &task);
1450   }
1451 }
1452 
1453 /****************************************************************************
1454   Constructor for city_dialog, sets layouts, policies ...
1455 ****************************************************************************/
city_dialog(QWidget * parent)1456 city_dialog::city_dialog(QWidget *parent): qfc_dialog(parent)
1457 {
1458   QFont f = QApplication::font();
1459   QFont *small_font;
1460   QFontMetrics fm(f);
1461   QGridLayout *gridl, *slider_grid;
1462   QGroupBox *group_box, *map_box, *prod_options,
1463             *qgbox, *qgbprod, *qsliderbox, *result_box;
1464   QHBoxLayout *hbox, *hbox_layout, *prod_option_layout,
1465               *work_but_layout;
1466   QHeaderView *header;
1467   QLabel *lab2, *label, *ql, *some_label;
1468   QPushButton *qpush2;
1469   QScrollArea *scroll, *scroll2, *scroll3, *scroll_info, *scroll_unit;
1470   QSizePolicy size_expanding_policy(QSizePolicy::Expanding,
1471                                     QSizePolicy::Expanding);
1472   QSlider *slider;
1473   QStringList info_list, str_list;
1474   QVBoxLayout *lefttop_layout, *units_layout, *worklist_layout,
1475               *right_layout, *vbox, *vbox_layout, *zoom_vbox, *v_layout;
1476   QWidget *split_widget1, *split_widget2, *info_wdg, *curr_unit_wdg,
1477           *supp_unit_wdg,  *curr_impr_wdg;;
1478 
1479   int h = 2 * fm.height() + 2;
1480   small_font = fc_font::instance()->get_font(fonts::city_label);
1481   zoom = 1.0;
1482 
1483   happines_shown = false;
1484   central_splitter = new QSplitter;
1485   central_splitter->setOpaqueResize(false);
1486   central_left_splitter = new QSplitter;
1487   central_left_splitter->setOpaqueResize(false);
1488   prod_unit_splitter = new QSplitter;
1489   prod_unit_splitter->setOpaqueResize(false);
1490 
1491   setMouseTracking(true);
1492   selected_row_p = -1;
1493   pcity = NULL;
1494   lcity_name = new QPushButton(this);
1495   lcity_name->setToolTip(_("Click to change city name"));
1496 
1497   single_page_layout = new QHBoxLayout();
1498   single_page_layout->setContentsMargins(0, 0 ,0 ,0);
1499   size_expanding_policy.setHorizontalStretch(0);
1500   size_expanding_policy.setVerticalStretch(0);
1501   current_building = 0;
1502 
1503   /* map view */
1504   map_box = new QGroupBox(this);
1505 
1506   /* City information widget texts about surpluses and so on */
1507   info_wdg = new QWidget(this);
1508 
1509   /* Fill info_wdg with labels */
1510   info_grid_layout = new QGridLayout(parent);
1511 
1512   info_wdg->setFont(*small_font);
1513   info_grid_layout->setSpacing(0);
1514   info_grid_layout->setMargin(0);
1515 
1516   for (enum city_info info_field = city_info_begin();
1517       info_field != city_info_end();
1518       info_field = city_info_next(info_field)) {
1519 
1520     ql = new QLabel(_(city_info_name(info_field)), info_wdg);
1521     ql->setFont(*small_font);
1522     ql->setProperty(fonts::city_label, "true");
1523     info_grid_layout->addWidget(ql, info_field, 0);
1524     qlt[info_field] = new QLabel(info_wdg);
1525     qlt[info_field]->setFont(*small_font);
1526     qlt[info_field]->setProperty(fonts::city_label, "true");
1527     info_grid_layout->addWidget(qlt[info_field], info_field, 1);
1528     info_grid_layout->setRowStretch(info_field, 0);
1529   }
1530 
1531   info_wdg->setLayout(info_grid_layout);
1532 
1533   /* Buy button */
1534   buy_button = new QPushButton();
1535   buy_button->setIcon(fc_icons::instance()->get_icon("help-donate"));
1536   connect(buy_button, &QAbstractButton::clicked, this, &city_dialog::buy);
1537 
1538   connect(lcity_name, &QAbstractButton::clicked, this, &city_dialog::city_rename);
1539   citizens_label = new city_label(FEELING_FINAL, this);
1540   citizen_pixmap = NULL;
1541   view = new city_map(this);
1542 
1543   zoom_vbox = new QVBoxLayout();
1544   zoom_in_button = new QPushButton();
1545   zoom_in_button->setIcon(fc_icons::instance()->get_icon("plus"));
1546   zoom_in_button->setIconSize(QSize(16, 16));
1547   zoom_in_button->setFixedSize(QSize(20, 20));
1548   zoom_in_button->setToolTip(_("Zoom in"));
1549   connect(zoom_in_button, &QAbstractButton::clicked, this, &city_dialog::zoom_in);
1550   zoom_out_button = new QPushButton();
1551   zoom_out_button->setIcon(fc_icons::instance()->get_icon("minus"));
1552   zoom_out_button->setIconSize(QSize(16, 16));
1553   zoom_out_button->setFixedSize(QSize(20, 20));
1554   zoom_out_button->setToolTip(_("Zoom out"));
1555   connect(zoom_out_button, &QAbstractButton::clicked, this, &city_dialog::zoom_out);
1556   zoom_vbox->addWidget(zoom_in_button);
1557   zoom_vbox->addWidget(zoom_out_button);
1558 
1559   /* City map group box */
1560   vbox_layout = new QVBoxLayout;
1561   hbox_layout = new QHBoxLayout;
1562   hbox_layout->addStretch(100);
1563   hbox_layout->addWidget(view);
1564   hbox_layout->addStretch(100);
1565   hbox_layout->addLayout(zoom_vbox);
1566   vbox_layout->addLayout(hbox_layout);
1567   vbox_layout->addWidget(lcity_name);
1568   map_box->setLayout(vbox_layout);
1569   map_box->setTitle(_("City map"));
1570 
1571   /* current/supported units/improvements widgets */
1572   supp_units = new QLabel();
1573   curr_units = new QLabel();
1574   curr_impr = new QLabel();
1575   curr_units->setAlignment(Qt::AlignLeft);
1576   curr_impr->setAlignment(Qt::AlignLeft);
1577   supp_units->setAlignment(Qt::AlignLeft);
1578   supported_units = new unit_info(true);
1579   scroll = new QScrollArea;
1580   scroll->setWidgetResizable(true);
1581   scroll->setMaximumHeight(tileset_unit_with_upkeep_height(get_tileset()) + 6
1582                            + scroll->horizontalScrollBar()->height());
1583   scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1584   scroll->setWidget(supported_units);
1585   current_units = new unit_info(false);
1586   scroll2 = new QScrollArea;
1587   scroll2->setWidgetResizable(true);
1588   scroll2->setMaximumHeight(tileset_unit_height(get_tileset()) + 6
1589                             + scroll2->horizontalScrollBar()->height());
1590   scroll2->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1591   scroll2->setWidget(current_units);
1592   scroll_height = scroll2->horizontalScrollBar()->height();
1593   city_buildings = new impr_info(this);
1594   scroll3 = new QScrollArea;
1595   scroll3->setWidgetResizable(true);
1596   scroll3->setMaximumHeight(tileset_unit_height(tileset) + 6
1597                             + scroll3->horizontalScrollBar()->height());
1598   scroll3->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1599   scroll3->setWidget(city_buildings);
1600   scroll->setProperty("city_scroll", true);
1601   scroll2->setProperty("city_scroll", true);
1602   scroll3->setProperty("city_scroll", true);
1603 
1604   lefttop_layout = new QVBoxLayout();
1605   worklist_layout = new QVBoxLayout();
1606   right_layout = new QVBoxLayout();
1607   leftbot_layout = new QHBoxLayout();
1608   units_layout = new QVBoxLayout();
1609   left_layout = new QVBoxLayout();
1610 
1611   /* Checkboxes to show units/wonders/imrovements
1612    * on production list */
1613   prod_option_layout = new QHBoxLayout;
1614   show_buildings = new QCheckBox;
1615   show_buildings->setToolTip(_("Show buildings"));
1616   show_buildings->setChecked(true);
1617   label = new QLabel();
1618   label->setPixmap(*fc_icons::instance()->get_pixmap("building"));
1619   label->setToolTip(_("Show buildings"));
1620   prod_option_layout->addWidget(show_buildings, Qt::AlignLeft);
1621   prod_option_layout->addWidget(label, Qt::AlignLeft);
1622   prod_option_layout->addStretch(100);
1623   label = new QLabel();
1624   label->setPixmap(*fc_icons::instance()->get_pixmap("cunits"));
1625   label->setToolTip(_("Show units"));
1626   show_units = new QCheckBox;
1627   show_units->setToolTip(_("Show units"));
1628   show_units->setChecked(true);
1629   prod_option_layout->addWidget(show_units, Qt::AlignHCenter);
1630   prod_option_layout->addWidget(label, Qt::AlignHCenter);
1631   prod_option_layout->addStretch(100);
1632   label = new QLabel();
1633   label->setPixmap(*fc_icons::instance()->get_pixmap("wonder"));
1634   label->setToolTip(_("Show wonders"));
1635   show_wonders = new QCheckBox;
1636   show_wonders->setToolTip(_("Show wonders"));
1637   show_wonders->setChecked(true);
1638   prod_option_layout->addWidget(show_wonders);
1639   prod_option_layout->addWidget(label);
1640   prod_option_layout->addStretch(100);
1641   label = new QLabel();
1642   label->setPixmap(*fc_icons::instance()->get_pixmap("future"));
1643   label->setToolTip(_("Show future targets"));
1644   future_targets = new QCheckBox;
1645   future_targets->setToolTip(_("Show future targets"));
1646   future_targets->setChecked(false);
1647   prod_option_layout->addWidget(future_targets);
1648   prod_option_layout->addWidget(label, Qt::AlignRight);
1649   prod_options = new QGroupBox(this);
1650   prod_options->setLayout(prod_option_layout);
1651   prod_options->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
1652 
1653   /* prev/next and close buttons */
1654   button = new QPushButton;
1655   button->setIcon(fc_icons::instance()->get_icon("city-close"));
1656   button->setIconSize(QSize(56, 56));
1657   button->setToolTip(_("Close city dialog"));
1658   connect(button, &QAbstractButton::clicked, this, &QWidget::hide);
1659 
1660   next_city_but = new QPushButton();
1661   next_city_but->setIcon(fc_icons::instance()->get_icon("city-right"));
1662   next_city_but->setIconSize(QSize(56, 56));
1663   next_city_but->setToolTip(_("Show next city"));
1664   connect(next_city_but, &QAbstractButton::clicked, this, &city_dialog::next_city);
1665 
1666   prev_city_but = new QPushButton();
1667   connect(prev_city_but, &QAbstractButton::clicked, this, &city_dialog::prev_city);
1668   prev_city_but->setIcon(fc_icons::instance()->get_icon("city-left"));
1669   prev_city_but->setIconSize(QSize(56, 56));
1670   prev_city_but->setToolTip(_("Show previous city"));
1671 
1672   happiness_button = new QPushButton();
1673   happiness_button->setIcon(fc_icons::instance()->get_icon("city-switch"));
1674   happiness_button->setIconSize(QSize(56, 28));
1675   connect(happiness_button, &QAbstractButton::clicked, this, &city_dialog::show_happiness);
1676   update_happiness_button();
1677 
1678   button->setFixedSize(64, 64);
1679   prev_city_but->setFixedSize(64, 64);
1680   next_city_but->setFixedSize(64, 64);
1681   happiness_button->setFixedSize(64, 32);
1682   vbox_layout = new QVBoxLayout;
1683   vbox_layout->addWidget(prev_city_but);
1684   vbox_layout->addWidget(next_city_but);
1685   vbox_layout->addWidget(button);
1686   vbox_layout->addWidget(happiness_button, Qt::AlignHCenter);
1687   hbox_layout = new QHBoxLayout;
1688 
1689   hbox_layout->addLayout(vbox_layout, Qt::AlignLeft);
1690   hbox_layout->addWidget(info_wdg, Qt::AlignLeft);
1691   hbox_layout->addWidget(map_box, Qt::AlignCenter);
1692 
1693   /* Layout with city view and buttons */
1694   lefttop_layout->addWidget(citizens_label, Qt::AlignHCenter);
1695   lefttop_layout->addStretch(0);
1696   lefttop_layout->addLayout(hbox_layout);
1697   lefttop_layout->addStretch(50);
1698 
1699   /* Layout for units/buildings */
1700   curr_unit_wdg = new QWidget();
1701   supp_unit_wdg = new QWidget();
1702   curr_impr_wdg = new QWidget();
1703   v_layout = new QVBoxLayout;
1704   v_layout->addWidget(curr_impr);
1705   v_layout->addWidget(scroll3);
1706   v_layout->setContentsMargins(0 , 0 , 0, 0);
1707   v_layout->setSpacing(0);
1708   curr_impr_wdg->setLayout(v_layout);
1709   v_layout = new QVBoxLayout;
1710   v_layout->addWidget(curr_units);
1711   v_layout->addWidget(scroll2);
1712   v_layout->setContentsMargins(0 , 0 , 0, 0);
1713   v_layout->setSpacing(0);
1714   curr_unit_wdg->setLayout(v_layout);
1715   v_layout = new QVBoxLayout;
1716   v_layout->addWidget(supp_units);
1717   v_layout->addWidget(scroll);
1718   v_layout->setContentsMargins(0 , 0 , 0, 0);
1719   v_layout->setSpacing(0);
1720   supp_unit_wdg->setLayout(v_layout);
1721 
1722   units_layout->addWidget(curr_unit_wdg);
1723   units_layout->addWidget(supp_unit_wdg);
1724   units_layout->addWidget(curr_impr_wdg);
1725   units_layout->setSpacing(0);
1726   units_layout->setContentsMargins(0 , 0 , 0, 0);
1727 
1728   vbox = new QVBoxLayout;
1729   vbox_layout = new QVBoxLayout;
1730   qgbprod = new QGroupBox;
1731   group_box = new QGroupBox(_("Worklist Option"));
1732   work_but_layout = new QHBoxLayout;
1733   work_next_but = new QPushButton(fc_icons::instance()->get_icon(
1734                                     "go-down"), "");
1735   work_prev_but = new QPushButton(fc_icons::instance()->get_icon(
1736                                     "go-up"), "");
1737   work_add_but = new QPushButton(fc_icons::instance()->get_icon(
1738                                    "list-add"), "");
1739   work_rem_but = new QPushButton(style()->standardIcon(
1740                                    QStyle::SP_DialogDiscardButton), "");
1741   work_but_layout->addWidget(work_add_but);
1742   work_but_layout->addWidget(work_next_but);
1743   work_but_layout->addWidget(work_prev_but);
1744   work_but_layout->addWidget(work_rem_but);
1745   but_menu_worklist = new QPushButton;
1746   production_combo_p = new progress_bar(parent);
1747   production_combo_p->setToolTip(_("Click to change current production"));
1748   p_table_p = new QTableWidget;
1749 
1750   r1 = new QRadioButton(_("Change"));
1751   r2 = new QRadioButton(_("Insert Before"));
1752   r3 = new QRadioButton(_("Insert After"));
1753   r4 = new QRadioButton(_("Add Last"));
1754   r4->setChecked(true);
1755   group_box->setLayout(vbox);
1756 
1757 
1758   p_table_p->setColumnCount(3);
1759   p_table_p->setProperty("showGrid", "false");
1760   p_table_p->setProperty("selectionBehavior", "SelectRows");
1761   p_table_p->setEditTriggers(QAbstractItemView::NoEditTriggers);
1762   p_table_p->verticalHeader()->setVisible(false);
1763   p_table_p->horizontalHeader()->setVisible(false);
1764   p_table_p->setSelectionMode(QAbstractItemView::SingleSelection);
1765   production_combo_p->setFixedHeight(h);
1766   p_table_p->setMinimumWidth(200);
1767   p_table_p->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
1768   p_table_p->setContextMenuPolicy(Qt::CustomContextMenu);
1769   p_table_p->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
1770   header = p_table_p->horizontalHeader();
1771   header->setStretchLastSection(true);
1772 
1773   qgbprod->setTitle(_("Worklist"));
1774   vbox_layout->setSpacing(0);
1775   vbox_layout->addWidget(prod_options);
1776   vbox_layout->addWidget(buy_button);
1777   vbox_layout->addWidget(production_combo_p);
1778   vbox_layout->addLayout(work_but_layout);
1779   vbox_layout->addWidget(p_table_p);
1780   qgbprod->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
1781   qgbprod->setLayout(vbox_layout);
1782 
1783   but_menu_worklist->setText(_("Worklist menu"));
1784   but_menu_worklist->setIcon(style()->standardIcon(
1785                                QStyle::SP_FileLinkIcon));
1786   worklist_layout->setSpacing(0);
1787   worklist_layout->addWidget(qgbprod);
1788   connect(p_table_p,
1789           &QWidget::customContextMenuRequested, this,
1790           &city_dialog::display_worklist_menu);
1791   connect(but_menu_worklist, &QAbstractButton::clicked, this, &city_dialog::delete_prod);
1792   connect(production_combo_p, &progress_bar::clicked, this, &city_dialog::show_targets);
1793   connect(work_add_but, &QAbstractButton::clicked, this, &city_dialog::show_targets_worklist);
1794   connect(work_prev_but, &QAbstractButton::clicked, this, &city_dialog::worklist_up);
1795   connect(work_next_but, &QAbstractButton::clicked, this, &city_dialog::worklist_down);
1796   connect(work_rem_but, &QAbstractButton::clicked, this, &city_dialog::worklist_del);
1797   connect(p_table_p,
1798           &QTableWidget::itemDoubleClicked,
1799           this, &city_dialog::dbl_click_p);
1800   connect(p_table_p->selectionModel(),
1801           SIGNAL(selectionChanged(const QItemSelection &,
1802                                   const QItemSelection &)),
1803           SLOT(item_selected(const QItemSelection &,
1804                              const QItemSelection &)));
1805   happiness_group = new QGroupBox(_("Happiness"));
1806   gridl = new QGridLayout;
1807 
1808   nationality_table = new QTableWidget;
1809   nationality_table->setColumnCount(3);
1810   nationality_table->setProperty("showGrid", "false");
1811   nationality_table->setProperty("selectionBehavior", "SelectRows");
1812   nationality_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
1813   nationality_table->verticalHeader()->setVisible(false);
1814   nationality_table->horizontalHeader()->setStretchLastSection(true);
1815 
1816   info_list.clear();
1817   info_list << _("Cities:") << _("Luxuries:") << _("Buildings:")
1818             << _("Nationality:") << _("Units:") <<  _("Wonders:");
1819 
1820   for (int i = 0; i < info_list.count(); i++) {
1821     lab_table[i] = new city_label(1 + i, this);
1822     gridl->addWidget(lab_table[i], i, 1, 1, 1);
1823     lab2 = new QLabel(this);
1824     lab2->setFont(*small_font);
1825     lab2->setProperty(fonts::city_label, "true");
1826     lab2->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
1827     lab2->setText(info_list.at(i));
1828     gridl->addWidget(lab2, i, 0, 1, 1);
1829   }
1830 
1831   gridl->setSpacing(0);
1832   happiness_group->setLayout(gridl);
1833 
1834 
1835   happiness_layout = new QHBoxLayout;
1836   happiness_layout->addWidget(happiness_group);
1837   happiness_layout->addWidget(nationality_table);
1838   happiness_layout->setStretch(0, 10);
1839   happiness_widget = new QWidget();
1840   happiness_widget->setLayout(happiness_layout);
1841   qgbox = new QGroupBox(_("Presets:"));
1842   qsliderbox = new QGroupBox(_("Governor settings"));
1843   result_box = new QGroupBox(_("Results:"));
1844   hbox = new QHBoxLayout;
1845   gridl = new QGridLayout;
1846   slider_grid = new QGridLayout;
1847 
1848   qpush2
1849     = new QPushButton(style()->standardIcon(QStyle::SP_DialogSaveButton),
1850                       _("Save"));
1851   connect(qpush2, &QAbstractButton::pressed, this, &city_dialog::save_cma);
1852 
1853   cma_info_text = new QLabel;
1854   cma_info_text->setFont(*small_font);
1855   cma_info_text->setAlignment(Qt::AlignCenter);
1856   cma_table = new QTableWidget;
1857   cma_table->setColumnCount(1);
1858   cma_table->setProperty("showGrid", "false");
1859   cma_table->setProperty("selectionBehavior", "SelectRows");
1860   cma_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
1861   cma_table->setSelectionMode(QAbstractItemView::SingleSelection);
1862   cma_table->setContextMenuPolicy(Qt::CustomContextMenu);
1863   cma_table->verticalHeader()->setVisible(false);
1864   cma_table->horizontalHeader()->setVisible(false);
1865   cma_table->horizontalHeader()->setSectionResizeMode(
1866     QHeaderView::Stretch);
1867 
1868   connect(cma_table->selectionModel(),
1869           SIGNAL(selectionChanged(const QItemSelection &,
1870                                   const QItemSelection &)),
1871           SLOT(cma_selected(const QItemSelection &,
1872                             const QItemSelection &)));
1873   connect(cma_table,
1874           &QWidget::customContextMenuRequested, this,
1875           &city_dialog::cma_context_menu);
1876   connect(cma_table, &QTableWidget::cellDoubleClicked, this,
1877           &city_dialog::cma_double_clicked);
1878   gridl->addWidget(cma_table, 0, 0, 1, 2);
1879   qgbox->setLayout(gridl);
1880   hbox->addWidget(cma_info_text);
1881   result_box->setLayout(hbox);
1882   str_list << _("Food") << _("Shield") << _("Trade") << _("Gold")
1883            << _("Luxury") << _("Science") << _("Celebrate");
1884   some_label = new QLabel(_("Minimal Surplus"));
1885   some_label->setFont(*fc_font::instance()->get_font(fonts::city_label));
1886   some_label->setAlignment(Qt::AlignRight);
1887   slider_grid->addWidget(some_label, 0, 0, 1, 3);
1888   some_label = new QLabel(_("Priority"));
1889   some_label->setFont(*fc_font::instance()->get_font(fonts::city_label));
1890   some_label->setAlignment(Qt::AlignCenter);
1891   slider_grid->addWidget(some_label, 0, 3, 1, 2);
1892 
1893   for (int i = 0; i < str_list.count(); i++) {
1894     some_label = new QLabel(str_list.at(i));
1895     slider_grid->addWidget(some_label, i + 1, 0, 1, 1);
1896     some_label = new QLabel("0");
1897     some_label->setMinimumWidth(25);
1898 
1899     if (i != str_list.count() - 1) {
1900       slider = new QSlider(Qt::Horizontal);
1901       slider->setPageStep(1);
1902       slider->setFocusPolicy(Qt::TabFocus);
1903       slider_tab[2 * i] = slider;
1904       slider->setRange(-20, 20);
1905       slider->setSingleStep(1);
1906       slider_grid->addWidget(some_label, i + 1, 1, 1, 1);
1907       slider_grid->addWidget(slider, i + 1, 2, 1, 1);
1908       slider->setProperty("FC", QVariant::fromValue((void *)some_label));
1909 
1910       connect(slider, &QAbstractSlider::valueChanged, this, &city_dialog::cma_slider);
1911     } else {
1912       cma_celeb_checkbox = new QCheckBox;
1913       slider_grid->addWidget(cma_celeb_checkbox, i + 1, 2 , 1 , 1);
1914       connect(cma_celeb_checkbox,
1915               &QCheckBox::stateChanged, this, &city_dialog::cma_celebrate_changed);
1916     }
1917 
1918     some_label = new QLabel("0");
1919     some_label->setMinimumWidth(25);
1920     slider = new QSlider(Qt::Horizontal);
1921     slider->setFocusPolicy(Qt::TabFocus);
1922     slider->setRange(0, 25);
1923     slider_tab[2 * i + 1] = slider;
1924     slider->setProperty("FC", QVariant::fromValue((void *)some_label));
1925     slider_grid->addWidget(some_label, i + 1, 3, 1, 1);
1926     slider_grid->addWidget(slider, i + 1, 4, 1, 1);
1927     connect(slider, &QAbstractSlider::valueChanged, this, &city_dialog::cma_slider);
1928   }
1929 
1930   cma_enable_but = new QPushButton();
1931   cma_enable_but->setFocusPolicy(Qt::TabFocus);
1932   connect(cma_enable_but, &QAbstractButton::pressed, this, &city_dialog::cma_enable);
1933   slider_grid->addWidget(cma_enable_but, O_LAST + 4, 0, 1, 3);
1934   slider_grid->addWidget(qpush2, O_LAST + 4, 3, 1, 2);
1935 
1936   qsliderbox->setLayout(slider_grid);
1937   cma_result = new QLabel;
1938   cma_result_pix = new QLabel;
1939 
1940   hbox = new QHBoxLayout;
1941   hbox->addWidget(cma_result_pix);
1942   hbox->addWidget(cma_result);
1943   hbox->addStretch(10);
1944   right_layout->addWidget(qgbox);
1945   right_layout->addLayout(hbox);
1946   right_layout->addWidget(qsliderbox);
1947 
1948   split_widget1 = new QWidget;
1949   split_widget1->setLayout(worklist_layout);
1950   split_widget2 = new QWidget;
1951   split_widget2->setLayout(units_layout);
1952   prod_unit_splitter->addWidget(split_widget1);
1953   prod_unit_splitter->addWidget(split_widget2);
1954   prod_unit_splitter->setStretchFactor(0, 3);
1955   prod_unit_splitter->setStretchFactor(1, 97);
1956   prod_unit_splitter->setOrientation(Qt::Horizontal);
1957   leftbot_layout->addWidget(prod_unit_splitter);
1958   top_widget = new QWidget;
1959   top_widget->setLayout(lefttop_layout);
1960   top_widget->setSizePolicy(QSizePolicy::Minimum,
1961                             QSizePolicy::Minimum);
1962   scroll_info = new QScrollArea();
1963   scroll_info->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
1964   scroll_unit = new QScrollArea();
1965   scroll_info->setWidget(top_widget);
1966   scroll_info->setWidgetResizable(true);
1967   prod_happ_widget = new QWidget;
1968   prod_happ_widget->setLayout(leftbot_layout);
1969   prod_happ_widget->setSizePolicy(QSizePolicy::MinimumExpanding,
1970                                   QSizePolicy::MinimumExpanding);
1971   scroll_unit->setWidget(prod_happ_widget);
1972   scroll_unit->setWidgetResizable(true);
1973   central_left_splitter->addWidget(scroll_info);
1974   central_left_splitter->addWidget(scroll_unit);
1975   central_left_splitter->setStretchFactor(0, 40);
1976   central_left_splitter->setStretchFactor(1, 60);
1977   central_left_splitter->setOrientation(Qt::Vertical);
1978   left_layout->addWidget(central_left_splitter);
1979 
1980   split_widget1 = new QWidget(this);
1981   split_widget2 = new QWidget(this);
1982   split_widget1->setLayout(left_layout);
1983   split_widget2->setLayout(right_layout);
1984   central_splitter->addWidget(split_widget1);
1985   central_splitter->addWidget(split_widget2);
1986   central_splitter->setStretchFactor(0, 99);
1987   central_splitter->setStretchFactor(1, 1);
1988   central_splitter->setOrientation(Qt::Horizontal);
1989   single_page_layout->addWidget(central_splitter);
1990   setSizeGripEnabled(true);
1991   setLayout(single_page_layout);
1992 
1993   installEventFilter(this);
1994 
1995   ::city_dlg_created = true;
1996 }
1997 
1998 /****************************************************************************
1999   Changes production to next one or previous
2000 ****************************************************************************/
change_production(bool next)2001 void city_dialog::change_production(bool next)
2002 {
2003 
2004   cid cprod;
2005   int i, pos;
2006   int item, targets_used;
2007   QList<cid> prod_list;
2008   QString str;
2009   struct item items[MAX_NUM_PRODUCTION_TARGETS];
2010   struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
2011   struct universal univ;
2012 
2013   pos = 0;
2014   cprod = cid_encode(pcity->production);
2015   targets_used = collect_eventually_buildable_targets(targets, pcity, false);
2016   name_and_sort_items(targets, targets_used, items, false, pcity);
2017 
2018   for (item = 0; item < targets_used; item++) {
2019     if (can_city_build_now(pcity, &items[item].item)) {
2020       prod_list << cid_encode(items[item].item);
2021     }
2022   }
2023 
2024   for (i = 0; i < prod_list.size(); i++) {
2025     if (prod_list.at(i) == cprod) {
2026       if (next) {
2027         pos = i + 1;
2028       } else {
2029         pos = i - 1;
2030       }
2031     }
2032   }
2033   if (pos == prod_list.size()) {
2034     pos = 0;
2035   }
2036   if (pos == - 1) {
2037     pos = prod_list.size() - 1;
2038   }
2039   univ = cid_decode(static_cast<cid>(prod_list.at(pos)));
2040   city_change_production(pcity, &univ);
2041 }
2042 
2043 /****************************************************************************
2044   Sets tooltip for happiness/pruction button switcher
2045 ****************************************************************************/
update_happiness_button()2046 void city_dialog::update_happiness_button()
2047 {
2048   if (happines_shown) {
2049     happiness_button->setToolTip(_("Show city production"));
2050   } else {
2051     happiness_button->setToolTip(_("Show happiness information"));
2052   }
2053 }
2054 
2055 /****************************************************************************
2056   Shows happiness tab
2057 ****************************************************************************/
show_happiness()2058 void city_dialog::show_happiness()
2059 {
2060   setUpdatesEnabled(false);
2061 
2062   if (!happines_shown) {
2063     leftbot_layout->replaceWidget(prod_unit_splitter,
2064                                   happiness_widget,
2065                                   Qt::FindDirectChildrenOnly);
2066     prod_unit_splitter->hide();
2067     happiness_widget->show();
2068     happiness_widget->updateGeometry();
2069   } else {
2070     leftbot_layout->replaceWidget(happiness_widget,
2071                                   prod_unit_splitter,
2072                                   Qt::FindDirectChildrenOnly);
2073     prod_unit_splitter->show();
2074     prod_unit_splitter->updateGeometry();
2075     happiness_widget->hide();
2076   }
2077 
2078   setUpdatesEnabled(true);
2079   update();
2080   happines_shown = !happines_shown;
2081   update_happiness_button();
2082 }
2083 
2084 
2085 /****************************************************************************
2086   Updates buttons/widgets which should be enabled/disabled
2087 ****************************************************************************/
update_disabled()2088 void city_dialog::update_disabled()
2089 {
2090   if (NULL == client.conn.playing
2091       || city_owner(pcity) != client.conn.playing) {
2092     prev_city_but->setDisabled(true);
2093     next_city_but->setDisabled(true);
2094     buy_button->setDisabled(true);
2095     cma_enable_but->setDisabled(true);
2096     production_combo_p->setDisabled(true);
2097     but_menu_worklist->setDisabled(true);
2098     current_units->setDisabled(true);
2099     supported_units->setDisabled(true);
2100     view->setDisabled(true);
2101 
2102     if (!client_is_observer()) {
2103     }
2104   } else {
2105     prev_city_but->setEnabled(true);
2106     next_city_but->setEnabled(true);
2107     buy_button->setEnabled(true);
2108     cma_enable_but->setEnabled(true);
2109     production_combo_p->setEnabled(true);
2110     but_menu_worklist->setEnabled(true);
2111     current_units->setEnabled(true);
2112     supported_units->setEnabled(true);
2113     view->setEnabled(true);
2114   }
2115 
2116   if (can_client_issue_orders()) {
2117     cma_enable_but->setEnabled(true);
2118   } else  {
2119     cma_enable_but->setDisabled(true);
2120   }
2121 
2122   update_prod_buttons();
2123 }
2124 
2125 /****************************************************************************
2126   Update sensitivity of buttons in production tab
2127 ****************************************************************************/
update_prod_buttons()2128 void city_dialog::update_prod_buttons()
2129 {
2130   work_next_but->setDisabled(true);
2131   work_prev_but->setDisabled(true);
2132   work_add_but->setDisabled(true);
2133   work_rem_but->setDisabled(true);
2134 
2135   if (client.conn.playing && city_owner(pcity) == client.conn.playing) {
2136     work_add_but->setEnabled(true);
2137 
2138     if (selected_row_p >= 0 && selected_row_p < p_table_p->rowCount()) {
2139       work_rem_but->setEnabled(true);
2140     }
2141 
2142     if (selected_row_p >= 0 && selected_row_p < p_table_p->rowCount() - 1) {
2143       work_next_but->setEnabled(true);
2144     }
2145 
2146     if (selected_row_p > 0 && selected_row_p < p_table_p->rowCount()) {
2147       work_prev_but->setEnabled(true);
2148     }
2149   }
2150 }
2151 
2152 /****************************************************************************
2153   City dialog destructor
2154 ****************************************************************************/
~city_dialog()2155 city_dialog::~city_dialog()
2156 {
2157   if (citizen_pixmap) {
2158     citizen_pixmap->detach();
2159     delete citizen_pixmap;
2160   }
2161 
2162   cma_table->clear();
2163   p_table_p->clear();
2164   nationality_table->clear();
2165   current_units->clear_layout();
2166   supported_units->clear_layout();
2167   removeEventFilter(this);
2168   ::city_dlg_created = false;
2169 }
2170 
2171 /****************************************************************************
2172   Hide event
2173 ****************************************************************************/
hideEvent(QHideEvent * event)2174 void city_dialog::hideEvent(QHideEvent *event)
2175 {
2176   gui()->qt_settings.city_geometry = saveGeometry();
2177   gui()->qt_settings.city_splitter1 = prod_unit_splitter->saveState();
2178   gui()->qt_settings.city_splitter2 = central_left_splitter->saveState();
2179   gui()->qt_settings.city_splitter3 = central_splitter->saveState();
2180 }
2181 
2182 /****************************************************************************
2183   Show event
2184 ****************************************************************************/
showEvent(QShowEvent * event)2185 void city_dialog::showEvent(QShowEvent *event)
2186 {
2187   if (!gui()->qt_settings.city_geometry.isNull()) {
2188     restoreGeometry(gui()->qt_settings.city_geometry);
2189     prod_unit_splitter->restoreState(gui()->qt_settings.city_splitter1);
2190     central_left_splitter->restoreState(gui()->qt_settings.city_splitter2);
2191     central_splitter->restoreState(gui()->qt_settings.city_splitter3);
2192   } else {
2193     QRect rect = QApplication::primaryScreen()->geometry();
2194     resize((rect.width() * 4) / 5, (rect.height() * 5) / 6);
2195   }
2196 }
2197 
2198 /****************************************************************************
2199   Show event
2200 ****************************************************************************/
closeEvent(QCloseEvent * event)2201 void city_dialog::closeEvent(QCloseEvent *event)
2202 {
2203   gui()->qt_settings.city_geometry = saveGeometry();
2204   gui()->qt_settings.city_splitter1 = prod_unit_splitter->saveState();
2205   gui()->qt_settings.city_splitter2 = central_left_splitter->saveState();
2206   gui()->qt_settings.city_splitter3 = central_splitter->saveState();
2207 }
2208 
2209 /****************************************************************************
2210   Event filter for catching keybaord events
2211 ****************************************************************************/
eventFilter(QObject * obj,QEvent * event)2212 bool city_dialog::eventFilter(QObject *obj, QEvent *event)
2213 {
2214 
2215   if (obj == this) {
2216     if (event->type() == QEvent::KeyPress) {
2217     }
2218 
2219     if (event->type() == QEvent::ShortcutOverride) {
2220       QKeyEvent *key_event = static_cast<QKeyEvent *>(event);
2221       if (key_event->key() == Qt::Key_Right) {
2222         next_city();
2223         event->setAccepted(true);
2224         return true;
2225       }
2226       if (key_event->key() == Qt::Key_Left) {
2227         prev_city();
2228         event->setAccepted(true);
2229         return true;
2230       }
2231       if (key_event->key() == Qt::Key_Up) {
2232         change_production(true);
2233         event->setAccepted(true);
2234         return true;
2235       }
2236       if (key_event->key() == Qt::Key_Down) {
2237         change_production(false);
2238         event->setAccepted(true);
2239         return true;
2240       }
2241     }
2242   }
2243   return QObject::eventFilter(obj, event);
2244 }
2245 
2246 
2247 /****************************************************************************
2248   City rename dialog input
2249 ****************************************************************************/
city_rename()2250 void city_dialog::city_rename()
2251 {
2252   hud_input_box ask(gui()->central_wdg);
2253 
2254   if (!can_client_issue_orders()) {
2255     return;
2256   }
2257 
2258   ask.set_text_title_definput(_("What should we rename the city to?"),
2259                               _("Rename City"), city_name_get(pcity));
2260   if (ask.exec() == QDialog::Accepted) {
2261     QByteArray ask_bytes;
2262 
2263     ask_bytes = ask.input_edit.text().toLocal8Bit();
2264     ::city_rename(pcity, ask_bytes.data());
2265   }
2266 }
2267 
2268 /****************************************************************************
2269   Zooms in tiles view
2270 ****************************************************************************/
zoom_in()2271 void city_dialog::zoom_in()
2272 {
2273   zoom = zoom * 1.2;
2274   if (pcity) {
2275     view->set_pixmap(pcity, zoom);
2276   }
2277   updateGeometry();
2278   left_layout->update();
2279 }
2280 
2281 /****************************************************************************
2282   Zooms out tiles view
2283 ****************************************************************************/
zoom_out()2284 void city_dialog::zoom_out()
2285 {
2286   zoom = zoom / 1.2;
2287   if (pcity) {
2288     view->set_pixmap(pcity, zoom);
2289   }
2290   updateGeometry();
2291   left_layout->update();
2292 }
2293 
2294 /****************************************************************************
2295   Save cma dialog input
2296 ****************************************************************************/
save_cma()2297 void city_dialog::save_cma()
2298 {
2299   struct cm_parameter param;
2300   QString text;
2301   hud_input_box ask(gui()->central_wdg);
2302 
2303   ask.set_text_title_definput(_("What should we name the preset?"),
2304                               _("Name new preset"),
2305                               _("new preset"));
2306   if (ask.exec() == QDialog::Accepted) {
2307     QByteArray ask_bytes;
2308 
2309     ask_bytes = ask.input_edit.text().toLocal8Bit();
2310     text = ask_bytes.data();
2311     if (!text.isEmpty()) {
2312       param.allow_disorder = false;
2313       param.allow_specialists = true;
2314       param.require_happy = cma_celeb_checkbox->isChecked();
2315       param.happy_factor = slider_tab[2 * O_LAST + 1]->value();
2316 
2317       for (int i = O_FOOD; i < O_LAST; i++) {
2318         param.minimal_surplus[i] = slider_tab[2 * i]->value();
2319         param.factor[i] = slider_tab[2 * i + 1]->value();
2320       }
2321 
2322       ask_bytes = text.toLocal8Bit();
2323       cmafec_preset_add(ask_bytes.data(), &param);
2324       update_cma_tab();
2325     }
2326   }
2327 }
2328 
2329 /****************************************************************************
2330   Enables cma slot, triggered by clicked button or changed cma
2331 ****************************************************************************/
cma_enable()2332 void city_dialog::cma_enable()
2333 {
2334   if (cma_is_city_under_agent(pcity, NULL)) {
2335     cma_release_city(pcity);
2336     return;
2337   }
2338 
2339   cma_changed();
2340   update_cma_tab();
2341 }
2342 
2343 /****************************************************************************
2344   Sliders moved and cma has been changed
2345 ****************************************************************************/
cma_changed()2346 void city_dialog::cma_changed()
2347 {
2348   struct cm_parameter param;
2349 
2350   param.allow_disorder = false;
2351   param.allow_specialists = true;
2352   param.require_happy = cma_celeb_checkbox->isChecked();
2353   param.happy_factor = slider_tab[2 * O_LAST + 1]->value();
2354 
2355   for (int i = O_FOOD; i < O_LAST; i++) {
2356     param.minimal_surplus[i] = slider_tab[2 * i]->value();
2357     param.factor[i] = slider_tab[2 * i + 1]->value();
2358   }
2359 
2360   cma_put_city_under_agent(pcity, &param);
2361 }
2362 
2363 /****************************************************************************
2364   Double click on some row ( column is unused )
2365 ****************************************************************************/
cma_double_clicked(int row,int column)2366 void city_dialog::cma_double_clicked(int row, int column)
2367 {
2368   const struct cm_parameter *param;
2369 
2370   if (!can_client_issue_orders()) {
2371     return;
2372   }
2373   param = cmafec_preset_get_parameter(row);
2374   if (cma_is_city_under_agent(pcity, NULL)) {
2375     cma_release_city(pcity);
2376   }
2377 
2378   cma_put_city_under_agent(pcity, param);
2379   update_cma_tab();
2380 }
2381 
2382 /****************************************************************************
2383   CMA has been selected from list
2384 ****************************************************************************/
cma_selected(const QItemSelection & sl,const QItemSelection & ds)2385 void city_dialog::cma_selected(const QItemSelection &sl,
2386                                const QItemSelection &ds)
2387 {
2388   const struct cm_parameter *param;
2389   QModelIndex index;
2390   QModelIndexList indexes = sl.indexes();
2391 
2392   if (indexes.isEmpty() || cma_table->signalsBlocked()) {
2393     return;
2394   }
2395 
2396   index = indexes.at(0);
2397   int ind = index.row();
2398 
2399   if (cma_table->currentRow() == -1 || cmafec_preset_num() == 0) {
2400     return;
2401   }
2402 
2403   param = cmafec_preset_get_parameter(ind);
2404   update_sliders();
2405 
2406   if (cma_is_city_under_agent(pcity, NULL)) {
2407     cma_release_city(pcity);
2408     cma_put_city_under_agent(pcity, param);
2409   }
2410 }
2411 
2412 /****************************************************************************
2413   Updates sliders ( cma params )
2414 ****************************************************************************/
update_sliders()2415 void city_dialog::update_sliders()
2416 {
2417   struct cm_parameter param;
2418   const struct cm_parameter *cparam;
2419   int output;
2420   QVariant qvar;
2421   QLabel *label;
2422 
2423   if (!cma_is_city_under_agent(pcity, &param)) {
2424     if (cma_table->currentRow() == -1 || cmafec_preset_num() == 0) {
2425       return;
2426     }
2427     cparam = cmafec_preset_get_parameter(cma_table->currentRow());
2428     cm_copy_parameter(&param, cparam);
2429   }
2430 
2431   for (output = O_FOOD; output < 2 * O_LAST; output++) {
2432     slider_tab[output]->blockSignals(true);
2433   }
2434 
2435   for (output = O_FOOD; output < O_LAST; output++) {
2436     qvar = slider_tab[2 * output + 1]->property("FC");
2437     label = reinterpret_cast<QLabel *>(qvar.value<void *>());
2438     label->setText(QString::number(param.factor[output]));
2439     slider_tab[2 * output + 1]->setValue(param.factor[output]);
2440     qvar = slider_tab[2 * output]->property("FC");
2441     label = reinterpret_cast<QLabel *>(qvar.value<void *>());
2442     label->setText(QString::number(param.minimal_surplus[output]));
2443     slider_tab[2 * output]->setValue(param.minimal_surplus[output]);
2444   }
2445 
2446   slider_tab[2 * O_LAST + 1]->blockSignals(true);
2447   qvar = slider_tab[2 * O_LAST + 1]->property("FC");
2448   label = reinterpret_cast<QLabel *>(qvar.value<void *>());
2449   label->setText(QString::number(param.happy_factor));
2450   slider_tab[2 * O_LAST + 1]->setValue(param.happy_factor);
2451   slider_tab[2 * O_LAST + 1]->blockSignals(false);
2452   cma_celeb_checkbox->blockSignals(true);
2453   cma_celeb_checkbox->setChecked(param.require_happy);
2454   cma_celeb_checkbox->blockSignals(false);
2455 
2456   for (output = O_FOOD; output < 2 * O_LAST; output++) {
2457     slider_tab[output]->blockSignals(false);
2458   }
2459 }
2460 
2461 /****************************************************************************
2462   Updates cma tab
2463 ****************************************************************************/
update_cma_tab()2464 void city_dialog::update_cma_tab()
2465 {
2466   QString s;
2467   QTableWidgetItem *item;
2468   struct cm_parameter param;
2469   QPixmap pix;
2470   int i;
2471 
2472   cma_table->clear();
2473   cma_table->setRowCount(0);
2474 
2475   for (i = 0; i < cmafec_preset_num(); i++) {
2476     item = new QTableWidgetItem;
2477     item->setText(cmafec_preset_get_descr(i));
2478     cma_table->insertRow(i);
2479     cma_table->setItem(i, 0, item);
2480   }
2481 
2482   if (cmafec_preset_num() == 0) {
2483     cma_table->insertRow(0);
2484     item = new QTableWidgetItem;
2485     item->setText(_("No governor defined"));
2486     cma_table->setItem(0, 0, item);
2487   }
2488 
2489   if (cma_is_city_under_agent(pcity, NULL)) {
2490     view->update();
2491     s = QString(cmafec_get_short_descr_of_city(pcity));
2492     pix = style()->standardPixmap(QStyle::SP_DialogApplyButton);
2493     pix = pix.scaled(2 * pix.width(), 2 * pix.height(),
2494                      Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2495     cma_result_pix->setPixmap(pix);
2496     cma_result_pix->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2497     /* TRANS: %1 is custom string chosen by player */
2498     cma_result->setText(QString(_("<h3>Governor Enabled<br>(%1)</h3>"))
2499                         .arg(s.toHtmlEscaped()));
2500     cma_result->setAlignment(Qt::AlignCenter);
2501   } else {
2502     pix = style()->standardPixmap(QStyle::SP_DialogCancelButton);
2503     pix = pix.scaled(1.6 * pix.width(), 1.6 * pix.height(),
2504                      Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2505     cma_result_pix->setPixmap(pix);
2506     cma_result_pix->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2507     cma_result->setText(QString(_("<h3>Governor Disabled</h3>")));
2508     cma_result->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
2509   }
2510 
2511   if (cma_is_city_under_agent(pcity, NULL)) {
2512     cmafec_get_fe_parameter(pcity, &param);
2513     i = cmafec_preset_get_index_of_parameter(const_cast <struct
2514                                              cm_parameter *const >(&param));
2515     if (i >= 0 && i < cma_table->rowCount()) {
2516       cma_table->blockSignals(true);
2517       cma_table->setCurrentCell(i, 0);
2518       cma_table->blockSignals(false);
2519     }
2520 
2521     cma_enable_but->setText(_("Disable"));
2522   } else {
2523     cma_enable_but->setText(_("Enable"));
2524   }
2525   update_sliders();
2526 }
2527 
2528 /****************************************************************************
2529   Removes selected CMA
2530 ****************************************************************************/
cma_remove()2531 void city_dialog::cma_remove()
2532 {
2533   int i;
2534   hud_message_box ask(city_dlg);
2535   int ret;
2536 
2537   i = cma_table->currentRow();
2538 
2539   if (i == -1 || cmafec_preset_num() == 0) {
2540     return;
2541   }
2542 
2543   ask.set_text_title(_("Remove this preset?"), cmafec_preset_get_descr(i));
2544   ask.setStandardButtons(QMessageBox::Cancel | QMessageBox::Ok);
2545   ask.setDefaultButton(QMessageBox::Cancel);
2546   ret = ask.exec();
2547 
2548   switch (ret) {
2549   case QMessageBox::Cancel:
2550     return;
2551 
2552   case QMessageBox::Ok:
2553     cmafec_preset_remove(i);
2554     update_cma_tab();
2555     break;
2556   }
2557 }
2558 
2559 /****************************************************************************
2560   CMA option 'celebrate' qcheckbox state has been changed
2561 ****************************************************************************/
cma_celebrate_changed(int val)2562 void city_dialog::cma_celebrate_changed(int val)
2563 {
2564   if (cma_is_city_under_agent(pcity, NULL)) {
2565     cma_changed();
2566     update_cma_tab();
2567   }
2568 }
2569 
2570 /****************************************************************************
2571   CMA options on slider has been changed
2572 ****************************************************************************/
cma_slider(int value)2573 void city_dialog::cma_slider(int value)
2574 {
2575   QVariant qvar;
2576   QSlider *slider;
2577   QLabel *label;
2578 
2579   slider = qobject_cast<QSlider *>(sender());
2580   qvar = slider->property("FC");
2581 
2582   if (qvar.isNull() || !qvar.isValid()) {
2583     return;
2584   }
2585 
2586   label = reinterpret_cast<QLabel *>(qvar.value<void *>());
2587   label->setText(QString::number(value));
2588 
2589   if (cma_is_city_under_agent(pcity, NULL)) {
2590     cma_changed();
2591     update_cma_tab();
2592   }
2593 }
2594 
2595 
2596 /****************************************************************************
2597   Received signal about changed qcheckbox - allow disbanding city
2598 ****************************************************************************/
disband_state_changed(int state)2599 void city_dialog::disband_state_changed(int state)
2600 {
2601   bv_city_options new_options;
2602 
2603   BV_CLR_ALL(new_options);
2604 
2605   if (state == Qt::Checked) {
2606     BV_SET(new_options, CITYO_DISBAND);
2607   } else if (state == Qt::Unchecked) {
2608     BV_CLR(new_options, CITYO_DISBAND);
2609   }
2610 
2611   if (!client_is_observer()) {
2612     dsend_packet_city_options_req(&client.conn, pcity->id, new_options);
2613   }
2614 }
2615 
2616 /****************************************************************************
2617   Context menu on governor tab in city worklist
2618 ****************************************************************************/
cma_context_menu(const QPoint & p)2619 void city_dialog::cma_context_menu(const QPoint &p)
2620 {
2621   QMenu cma_menu(this);
2622   QAction *cma_del_item;
2623   QAction *act;
2624 
2625   cma_del_item = cma_menu.addAction(_("Remove Governor"));
2626   act = cma_menu.exec(QCursor::pos());
2627 
2628   if (act == cma_del_item) {
2629     cma_remove();
2630   }
2631 
2632 }
2633 
2634 /****************************************************************************
2635   Context menu on production tab in city worklist
2636 ****************************************************************************/
display_worklist_menu(const QPoint & p)2637 void city_dialog::display_worklist_menu(const QPoint &p)
2638 {
2639   bool worklist_defined = true;
2640   cid c_id;
2641   int which_menu;
2642   QAction *act;
2643   QAction *action;
2644   QAction *disband;
2645   QAction *wl_save;
2646   QAction wl_clear(_("Clear"), 0);
2647   QAction wl_empty(_("(no worklists defined)"), 0);
2648   QMap<QString, cid> list;
2649   QMap<QString, cid>::const_iterator map_iter;
2650   QMenu *add_menu;
2651   QMenu *insert_menu;
2652   QMenu list_menu(this);
2653   QMenu *options_menu;
2654   QVariant qvar;
2655 
2656   if (!can_client_issue_orders()) {
2657     return;
2658   }
2659 
2660   add_menu = list_menu.addMenu(_("Change worklist"));
2661   insert_menu = list_menu.addMenu(_("Insert worklist"));
2662   connect(&wl_clear, &QAction::triggered, this, &city_dialog::clear_worklist);
2663   list_menu.addAction(&wl_clear);
2664   list.clear();
2665 
2666   global_worklists_iterate(pgwl) {
2667     list.insert(global_worklist_name(pgwl), global_worklist_id(pgwl));
2668   } global_worklists_iterate_end;
2669 
2670   if (list.count() == 0) {
2671     add_menu->addAction(&wl_empty);
2672     insert_menu->addAction(&wl_empty);
2673     worklist_defined = false;
2674   }
2675 
2676   map_iter = list.constBegin();
2677 
2678   while (map_iter != list.constEnd()) {
2679     action = add_menu->addAction(map_iter.key());
2680     action->setData(map_iter.value());
2681     action->setProperty("FC", 1);
2682     action = insert_menu->addAction(map_iter.key());
2683     action->setData(map_iter.value());
2684     action->setProperty("FC", 2);
2685     map_iter++;
2686   }
2687 
2688   wl_save = list_menu.addAction(_("Save worklist"));
2689   options_menu = list_menu.addMenu(_("Options"));
2690   disband = options_menu->addAction(_("Allow disbanding city"));
2691   disband->setCheckable(true);
2692   disband->setChecked(is_city_option_set(pcity, CITYO_DISBAND));
2693 
2694   act = 0;
2695   act = list_menu.exec(QCursor::pos());
2696 
2697   if (act) {
2698     if (act == disband) {
2699       int state;
2700 
2701       if (disband->isChecked()) {
2702         state = Qt::Checked;
2703       } else {
2704         state = Qt::Unchecked;
2705       }
2706 
2707       disband_state_changed(state);
2708     }
2709     if (act == wl_save) {
2710       save_worklist();
2711       return;
2712     }
2713     qvar = act->property("FC");
2714 
2715     if (!qvar.isValid() || qvar.isNull() || !worklist_defined) {
2716       return;
2717     }
2718 
2719     which_menu = qvar.toInt();
2720     qvar = act->data();
2721     c_id = qvar.toInt();
2722 
2723     if (which_menu == 1) { /* Change Worklist */
2724       city_set_queue(pcity,
2725                      global_worklist_get(global_worklist_by_id(c_id)));
2726     } else if (which_menu == 2) { /* Insert Worklist */
2727       if (worklist_defined) {
2728         city_queue_insert_worklist(pcity, selected_row_p + 1,
2729                                    global_worklist_get(global_worklist_by_id(
2730                                          c_id)));
2731       }
2732     }
2733   }
2734 }
2735 
2736 
2737 /****************************************************************************
2738   Enables/disables buy buttons depending on gold
2739 ****************************************************************************/
update_buy_button()2740 void city_dialog::update_buy_button()
2741 {
2742   QString str;
2743   int value;
2744 
2745   buy_button->setDisabled(true);
2746 
2747   if (!client_is_observer() && client.conn.playing != NULL) {
2748     value = city_production_buy_gold_cost(pcity);
2749     str = QString(PL_("Buy (%1 gold)", "Buy (%1 gold)",
2750                       value)).arg(QString::number(value));
2751 
2752     if (client.conn.playing->economic.gold >= value && value != 0) {
2753       buy_button->setEnabled(true);
2754     }
2755   } else {
2756     str = QString(_("Buy"));
2757   }
2758 
2759   buy_button->setText(str);
2760 }
2761 
2762 /****************************************************************************
2763   Redraws citizens for city_label (citizens_label)
2764 ****************************************************************************/
update_citizens()2765 void city_dialog::update_citizens()
2766 {
2767   enum citizen_category categories[MAX_CITY_SIZE];
2768   int i, j, width, height;
2769   QPainter p;
2770   QPixmap *pix;
2771   int num_citizens = get_city_citizen_types(pcity, FEELING_FINAL, categories);
2772   int w = tileset_small_sprite_width(tileset) / gui()->map_scale;
2773   int h = tileset_small_sprite_height(tileset) / gui()->map_scale;
2774 
2775   i = 1 + (num_citizens * 5 / 200);
2776   w = w  / i;
2777   QRect source_rect(0, 0, w, h);
2778   QRect dest_rect(0, 0, w, h);
2779   width = w * num_citizens;
2780   height = h;
2781 
2782   if (citizen_pixmap) {
2783     citizen_pixmap->detach();
2784     delete citizen_pixmap;
2785   }
2786 
2787   citizen_pixmap = new QPixmap(width, height);
2788 
2789   for (j = 0, i = 0; i < num_citizens; i++, j++) {
2790     dest_rect.moveTo(i * w, 0);
2791     pix = get_citizen_sprite(tileset, categories[j], j, pcity)->pm;
2792     p.begin(citizen_pixmap);
2793     p.drawPixmap(dest_rect, *pix, source_rect);
2794     p.end();
2795   }
2796 
2797   citizens_label->set_city(pcity);
2798   citizens_label->setPixmap(*citizen_pixmap);
2799 
2800   lab_table[FEELING_FINAL]->setPixmap(*citizen_pixmap);
2801   lab_table[FEELING_FINAL]->setToolTip(text_happiness_wonders(pcity));
2802 
2803   for (int k = 0; k < FEELING_LAST - 1; k++) {
2804     lab_table[k]->set_city(pcity);
2805     num_citizens = get_city_citizen_types(pcity,
2806                                           static_cast<citizen_feeling>(k),
2807                                           categories);
2808 
2809     for (j = 0, i = 0; i < num_citizens; i++, j++) {
2810       dest_rect.moveTo(i * w, 0);
2811       pix = get_citizen_sprite(tileset, categories[j], j, pcity)->pm;
2812       p.begin(citizen_pixmap);
2813       p.drawPixmap(dest_rect, *pix, source_rect);
2814       p.end();
2815     }
2816 
2817     lab_table[k]->setPixmap(*citizen_pixmap);
2818 
2819     switch (k) {
2820     case FEELING_BASE:
2821       lab_table[k]->setToolTip(text_happiness_cities(pcity));
2822       break;
2823 
2824     case FEELING_LUXURY:
2825       lab_table[k]->setToolTip(text_happiness_luxuries(pcity));
2826       break;
2827 
2828     case FEELING_EFFECT :
2829       lab_table[k]->setToolTip(text_happiness_buildings(pcity));
2830       break;
2831 
2832     case FEELING_NATIONALITY:
2833       lab_table[k]->setToolTip(text_happiness_nationality(pcity));
2834       break;
2835 
2836     case FEELING_MARTIAL:
2837       lab_table[k]->setToolTip(text_happiness_units(pcity));
2838       break;
2839 
2840     default:
2841       break;
2842     }
2843   }
2844 }
2845 
2846 /****************************************************************************
2847   Various refresh after getting new info/reply from server
2848 ****************************************************************************/
refresh()2849 void city_dialog::refresh()
2850 {
2851   setUpdatesEnabled(false);
2852   production_combo_p->blockSignals(true);
2853 
2854   if (pcity) {
2855     view->set_pixmap(pcity, zoom);
2856     view->update();
2857     update_title();
2858     update_info_label();
2859     update_buy_button();
2860     update_citizens();
2861     update_building();
2862     update_improvements();
2863     update_units();
2864     update_nation_table();
2865     update_cma_tab();
2866     update_disabled();
2867   } else {
2868     destroy_city_dialog();
2869   }
2870 
2871   production_combo_p->blockSignals(false);
2872   setUpdatesEnabled(true);
2873   updateGeometry();
2874   update();
2875 }
2876 
2877 
2878 /****************************************************************************
2879   Updates nationality table in happiness tab
2880 ****************************************************************************/
update_nation_table()2881 void city_dialog::update_nation_table()
2882 {
2883   QFont f = QApplication::font();
2884   QFontMetrics fm(f);
2885   QPixmap *pix = NULL;
2886   QPixmap pix_scaled;
2887   QString str;
2888   QStringList info_list;
2889   QTableWidgetItem *item;
2890   char buf[8];
2891   citizens nationality_i;
2892   int h;
2893   int i = 0;
2894   struct sprite *sprite;
2895 
2896   h = fm.height() + 6;
2897   nationality_table->clear();
2898   nationality_table->setRowCount(0);
2899   info_list.clear();
2900   info_list << _("#") << _("Flag") << _("Nation");
2901   nationality_table->setHorizontalHeaderLabels(info_list);
2902 
2903   citizens_iterate(pcity, pslot, nationality) {
2904     nationality_table->insertRow(i);
2905 
2906     for (int j = 0; j < nationality_table->columnCount(); j++) {
2907       item = new QTableWidgetItem;
2908 
2909       switch (j) {
2910       case 0:
2911         nationality_i = citizens_nation_get(pcity, pslot);
2912 
2913         if (nationality_i == 0) {
2914           str = "-";
2915         } else {
2916           fc_snprintf(buf, sizeof(buf), "%d", nationality_i);
2917           str = QString(buf);
2918         }
2919 
2920         item->setText(str);
2921         break;
2922 
2923       case 1:
2924         sprite = get_nation_flag_sprite(tileset,
2925                                         nation_of_player
2926                                         (player_slot_get_player(pslot)));
2927 
2928         if (sprite != NULL) {
2929           pix = sprite->pm;
2930           pix_scaled = pix->scaledToHeight(h);
2931           item->setData(Qt::DecorationRole, pix_scaled);
2932         } else {
2933           item->setText("FLAG MISSING");
2934         }
2935         break;
2936 
2937       case 2:
2938         item->setText(nation_adjective_for_player
2939                       (player_slot_get_player(pslot)));
2940         break;
2941 
2942       default:
2943         break;
2944       }
2945       nationality_table->setItem(i, j, item);
2946     }
2947     i++;
2948   } citizens_iterate_end;
2949   nationality_table->horizontalHeader()->setStretchLastSection(false);
2950   nationality_table->resizeColumnsToContents();
2951   nationality_table->resizeRowsToContents();
2952   nationality_table->horizontalHeader()->setStretchLastSection(true);
2953 }
2954 
2955 /****************************************************************************
2956   Updates information label ( food, prod ... surpluses ...)
2957 ****************************************************************************/
update_info_label()2958 void city_dialog::update_info_label()
2959 {
2960   int illness = 0;
2961   char buffer[512];
2962   char buf_info[NUM_INFO_FIELDS][512];
2963   char buf_tooltip[NUM_INFO_FIELDS][512];
2964   int granaryturns;
2965 
2966   for (int i = 0; i < NUM_INFO_FIELDS; i++) {
2967     buf_info[i][0] = '\0';
2968     buf_tooltip[i][0] = '\0';
2969   }
2970 
2971   /* fill the buffers with the necessary info */
2972   fc_snprintf(buf_info[INFO_CITIZEN], sizeof(buf_info[INFO_CITIZEN]),
2973               "%3d (%4d)", pcity->size, city_specialists(pcity));
2974   fc_snprintf(buf_info[INFO_FOOD], sizeof(buf_info[INFO_FOOD]), "%3d (%+4d)",
2975               pcity->prod[O_FOOD], pcity->surplus[O_FOOD]);
2976   fc_snprintf(buf_info[INFO_SHIELD], sizeof(buf_info[INFO_SHIELD]),
2977               "%3d (%+4d)", pcity->prod[O_SHIELD] + pcity->waste[O_SHIELD],
2978               pcity->surplus[O_SHIELD]);
2979   fc_snprintf(buf_info[INFO_TRADE], sizeof(buf_info[INFO_TRADE]), "%3d (%+4d)",
2980               pcity->surplus[O_TRADE] + pcity->waste[O_TRADE],
2981               pcity->surplus[O_TRADE]);
2982   fc_snprintf(buf_info[INFO_GOLD], sizeof(buf_info[INFO_GOLD]), "%3d (%+4d)",
2983               pcity->prod[O_GOLD], pcity->surplus[O_GOLD]);
2984   fc_snprintf(buf_info[INFO_LUXURY], sizeof(buf_info[INFO_LUXURY]), "%3d",
2985               pcity->prod[O_LUXURY]);
2986   fc_snprintf(buf_info[INFO_SCIENCE], sizeof(buf_info[INFO_SCIENCE]), "%3d",
2987               pcity->prod[O_SCIENCE]);
2988   fc_snprintf(buf_info[INFO_GRANARY], sizeof(buf_info[INFO_GRANARY]),
2989               "%4d/%-4d", pcity->food_stock,
2990               city_granary_size(city_size_get(pcity)));
2991 
2992   get_city_dialog_output_text(pcity, O_FOOD, buf_tooltip[INFO_FOOD],
2993                               sizeof(buf_tooltip[INFO_FOOD]));
2994   get_city_dialog_output_text(pcity, O_SHIELD, buf_tooltip[INFO_SHIELD],
2995                               sizeof(buf_tooltip[INFO_SHIELD]));
2996   get_city_dialog_output_text(pcity, O_TRADE, buf_tooltip[INFO_TRADE],
2997                               sizeof(buf_tooltip[INFO_TRADE]));
2998   get_city_dialog_output_text(pcity, O_GOLD, buf_tooltip[INFO_GOLD],
2999                               sizeof(buf_tooltip[INFO_GOLD]));
3000   get_city_dialog_output_text(pcity, O_SCIENCE, buf_tooltip[INFO_SCIENCE],
3001                               sizeof(buf_tooltip[INFO_SCIENCE]));
3002   get_city_dialog_output_text(pcity, O_LUXURY, buf_tooltip[INFO_LUXURY],
3003                               sizeof(buf_tooltip[INFO_LUXURY]));
3004   get_city_dialog_culture_text(pcity, buf_tooltip[INFO_CULTURE],
3005                                sizeof(buf_tooltip[INFO_CULTURE]));
3006   get_city_dialog_pollution_text(pcity, buf_tooltip[INFO_POLLUTION],
3007                                  sizeof(buf_tooltip[INFO_POLLUTION]));
3008   get_city_dialog_illness_text(pcity, buf_tooltip[INFO_ILLNESS],
3009                                sizeof(buf_tooltip[INFO_ILLNESS]));
3010 
3011   granaryturns = city_turns_to_grow(pcity);
3012 
3013   if (granaryturns == 0) {
3014     /* TRANS: city growth is blocked.  Keep short. */
3015     fc_snprintf(buf_info[INFO_GROWTH], sizeof(buf_info[INFO_GROWTH]), _("blocked"));
3016   } else if (granaryturns == FC_INFINITY) {
3017     /* TRANS: city is not growing.  Keep short. */
3018     fc_snprintf(buf_info[INFO_GROWTH], sizeof(buf_info[INFO_GROWTH]), _("never"));
3019   } else {
3020     /* A negative value means we'll have famine in that many turns.
3021        But that's handled down below. */
3022     /* TRANS: city growth turns.  Keep short. */
3023     fc_snprintf(buf_info[INFO_GROWTH], sizeof(buf_info[INFO_GROWTH]),
3024                 PL_("%d turn", "%d turns", abs(granaryturns)),
3025                 abs(granaryturns));
3026   }
3027 
3028   fc_snprintf(buf_info[INFO_CORRUPTION], sizeof(buf_info[INFO_CORRUPTION]),
3029               "%4d", pcity->waste[O_TRADE]);
3030   fc_snprintf(buf_info[INFO_WASTE], sizeof(buf_info[INFO_WASTE]), "%4d",
3031               pcity->waste[O_SHIELD]);
3032   fc_snprintf(buf_info[INFO_CULTURE], sizeof(buf_info[INFO_CULTURE]), "%4d",
3033               pcity->client.culture);
3034   fc_snprintf(buf_info[INFO_POLLUTION], sizeof(buf_info[INFO_POLLUTION]), "%4d",
3035               pcity->pollution);
3036 
3037   if (!game.info.illness_on) {
3038     fc_snprintf(buf_info[INFO_ILLNESS], sizeof(buf_info[INFO_ILLNESS]), "  -.-");
3039   } else {
3040     illness = city_illness_calc(pcity, NULL, NULL, NULL, NULL);
3041     /* illness is in tenth of percent */
3042     fc_snprintf(buf_info[INFO_ILLNESS], sizeof(buf_info[INFO_ILLNESS]),
3043                 "%5.1f%%", (float) illness / 10.0);
3044   }
3045 
3046   get_city_dialog_airlift_value(pcity, buf_info[INFO_AIRLIFT],
3047                                 sizeof(buf_info[INFO_AIRLIFT]));
3048   get_city_dialog_airlift_text(pcity, buf_tooltip[INFO_AIRLIFT],
3049                                sizeof(buf_tooltip[INFO_AIRLIFT]));
3050 
3051   get_city_dialog_output_text(pcity, O_FOOD, buffer, sizeof(buffer));
3052 
3053   for (int i = 0; i < NUM_INFO_FIELDS; i++) {
3054     qlt[i]->setText(QString(buf_info[i]));
3055 
3056     if (buf_tooltip[i][0]) {
3057       qlt[i]->setToolTip("<pre>" + QString(buf_tooltip[i]).toHtmlEscaped()
3058                          + "</pre>");
3059     }
3060   }
3061 }
3062 
3063 /****************************************************************************
3064   Setups whole city dialog, public function
3065 ****************************************************************************/
setup_ui(struct city * qcity)3066 void city_dialog::setup_ui(struct city *qcity)
3067 {
3068   QPixmap q_pix = *get_icon_sprite(tileset, ICON_CITYDLG)->pm;
3069   QIcon q_icon =::QIcon(q_pix);
3070 
3071   setContentsMargins(0, 0 ,0 ,0);
3072   setWindowIcon(q_icon);
3073   pcity = qcity;
3074   production_combo_p->blockSignals(true);
3075   refresh();
3076   production_combo_p->blockSignals(false);
3077 
3078 }
3079 
3080 
3081 /****************************************************************************
3082   Removes selected item from city worklist
3083 ****************************************************************************/
delete_prod()3084 void city_dialog::delete_prod()
3085 {
3086   display_worklist_menu(QCursor::pos());
3087 }
3088 
3089 /****************************************************************************
3090   Double clicked item in worklist table in production tab
3091 ****************************************************************************/
dbl_click_p(QTableWidgetItem * item)3092 void city_dialog::dbl_click_p(QTableWidgetItem *item)
3093 {
3094   struct worklist queue;
3095   city_get_queue(pcity, &queue);
3096 
3097   if (selected_row_p < 0 || selected_row_p > worklist_length(&queue)) {
3098     return;
3099   }
3100 
3101   worklist_remove(&queue, selected_row_p);
3102   city_set_queue(pcity, &queue);
3103 }
3104 
3105 /****************************************************************************
3106   Updates layouts for supported and present units in city
3107 ****************************************************************************/
update_units()3108 void city_dialog::update_units()
3109 {
3110   unit_item *ui;
3111   struct unit_list *units;
3112   char buf[256];
3113   int n;
3114   int happy_cost;
3115   int free_unhappy = get_city_bonus(pcity, EFT_MAKE_CONTENT_MIL);
3116   supported_units->setUpdatesEnabled(false);
3117   supported_units->clear_layout();
3118 
3119   if (NULL != client.conn.playing
3120       && city_owner(pcity) != client.conn.playing) {
3121     units = pcity->client.info_units_supported;
3122   } else {
3123     units = pcity->units_supported;
3124   }
3125 
3126   unit_list_iterate(units, punit) {
3127     happy_cost = city_unit_unhappiness(punit, &free_unhappy);
3128     ui = new unit_item(this, punit, true, happy_cost);
3129     ui->init_pix();
3130     supported_units->add_item(ui);
3131   } unit_list_iterate_end;
3132   n = unit_list_size(units);
3133   fc_snprintf(buf, sizeof(buf), _("Supported units %d"), n);
3134   supp_units->setText(QString(buf));
3135   supported_units->update_units();
3136   supported_units->setUpdatesEnabled(true);
3137   current_units->setUpdatesEnabled(true);
3138   current_units->clear_layout();
3139 
3140   if (NULL != client.conn.playing
3141       && city_owner(pcity) != client.conn.playing) {
3142     units = pcity->client.info_units_present;
3143   } else {
3144     units = pcity->tile->units;
3145   }
3146 
3147   unit_list_iterate(units, punit) {
3148     ui = new unit_item(this , punit, false);
3149     ui->init_pix();
3150     current_units->add_item(ui);
3151   } unit_list_iterate_end;
3152 
3153   n = unit_list_size(units);
3154   fc_snprintf(buf, sizeof(buf), _("Present units %d"), n);
3155   curr_units->setText(QString(buf));
3156 
3157   current_units->update_units();
3158   current_units->setUpdatesEnabled(true);
3159 }
3160 
3161 /****************************************************************************
3162   Selection changed in production tab, in worklist tab
3163 ****************************************************************************/
item_selected(const QItemSelection & sl,const QItemSelection & ds)3164 void city_dialog::item_selected(const QItemSelection &sl,
3165                                 const QItemSelection &ds)
3166 {
3167   QModelIndex index;
3168   QModelIndexList indexes = sl.indexes();
3169 
3170   if (indexes.isEmpty()) {
3171     return;
3172   }
3173 
3174   index = indexes.at(0);
3175   selected_row_p = index.row();
3176   update_prod_buttons();
3177 }
3178 
3179 /****************************************************************************
3180   Changes city_dialog to next city after pushing next city button
3181 ****************************************************************************/
next_city()3182 void city_dialog::next_city()
3183 {
3184   int size, i, j;
3185   struct city *other_pcity = NULL;
3186 
3187   if (NULL == client.conn.playing) {
3188     return;
3189   }
3190 
3191   size = city_list_size(client.conn.playing->cities);
3192 
3193   if (size == 1) {
3194     return;
3195   }
3196 
3197   for (i = 0; i < size; i++) {
3198     if (pcity == city_list_get(client.conn.playing->cities, i)) {
3199       break;
3200     }
3201   }
3202 
3203   for (j = 1; j < size; j++) {
3204     other_pcity = city_list_get(client.conn.playing->cities,
3205                                 (i + j + size) % size);
3206   }
3207   center_tile_mapcanvas(other_pcity->tile);
3208   qtg_real_city_dialog_popup(other_pcity);
3209 }
3210 
3211 /****************************************************************************
3212   Changes city_dialog to previous city after pushing prev city button
3213 ****************************************************************************/
prev_city()3214 void city_dialog::prev_city()
3215 {
3216   int size, i, j;
3217   struct city *other_pcity = NULL;
3218 
3219   if (NULL == client.conn.playing) {
3220     return;
3221   }
3222 
3223   size = city_list_size(client.conn.playing->cities);
3224 
3225   if (size == 1) {
3226     return;
3227   }
3228 
3229   for (i = 0; i < size; i++) {
3230     if (pcity == city_list_get(client.conn.playing->cities, i)) {
3231       break;
3232     }
3233   }
3234 
3235   for (j = 1; j < size; j++) {
3236     other_pcity = city_list_get(client.conn.playing->cities,
3237                                 (i - j + size) % size);
3238   }
3239 
3240   center_tile_mapcanvas(other_pcity->tile);
3241   qtg_real_city_dialog_popup(other_pcity);
3242 }
3243 
3244 /****************************************************************************
3245   Updates building improvement/unit
3246 ****************************************************************************/
update_building()3247 void city_dialog::update_building()
3248 {
3249   char buf[32];
3250   QString str;
3251   int cost = city_production_build_shield_cost(pcity);
3252 
3253   get_city_dialog_production(pcity, buf, sizeof(buf));
3254   production_combo_p->setRange(0, cost);
3255   production_combo_p->set_pixmap(&pcity->production);
3256 
3257   if (pcity->shield_stock >= cost) {
3258     production_combo_p->setValue(cost);
3259   } else {
3260     production_combo_p->setValue(pcity->shield_stock);
3261   }
3262 
3263   production_combo_p->setAlignment(Qt::AlignCenter);
3264   str = QString(buf);
3265   str = str.simplified();
3266 
3267   production_combo_p->setFormat(QString("(%p%) %2\n%1")
3268                                 .arg(city_production_name_translation(pcity),
3269                                      str));
3270 
3271   production_combo_p->updateGeometry();
3272 
3273 }
3274 
3275 /****************************************************************************
3276   Buy button. Shows message box asking for confirmation
3277 ****************************************************************************/
buy()3278 void city_dialog::buy()
3279 {
3280   char buf[1024], buf2[1024];
3281   int ret;
3282   const char *name = city_production_name_translation(pcity);
3283   int value = city_production_buy_gold_cost(pcity);
3284   hud_message_box ask(city_dlg);
3285 
3286   if (!can_client_issue_orders()) {
3287     return;
3288   }
3289 
3290   fc_snprintf(buf2, ARRAY_SIZE(buf2), PL_("Treasury contains %d gold.",
3291                                         "Treasury contains %d gold.",
3292                                         client_player()->economic.gold),
3293               client_player()->economic.gold);
3294   fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Buy %s for %d gold?",
3295                                         "Buy %s for %d gold?", value),
3296               name, value);
3297   ask.set_text_title(QString(buf), QString(buf2));
3298   ask.setStandardButtons(QMessageBox::Cancel | QMessageBox::Ok);
3299   ask.setDefaultButton(QMessageBox::Cancel);
3300   ret = ask.exec();
3301 
3302   switch (ret) {
3303   case QMessageBox::Cancel:
3304     return;
3305     break;
3306 
3307   case QMessageBox::Ok:
3308     city_buy_production(pcity);
3309     break;
3310   }
3311 }
3312 
3313 /****************************************************************************
3314   Updates list of improvements
3315 ****************************************************************************/
update_improvements()3316 void city_dialog::update_improvements()
3317 {
3318   QFont f = QApplication::font();
3319   QFontMetrics fm(f);
3320   QPixmap *pix = NULL;
3321   QPixmap pix_scaled;
3322   QString str, tooltip;
3323   QTableWidgetItem *qitem;
3324   struct sprite *sprite;
3325   int h, cost, item, targets_used, col, upkeep;
3326   struct item items[MAX_NUM_PRODUCTION_TARGETS];
3327   struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
3328   struct worklist queue;
3329   impr_item *ui;
3330 
3331   upkeep = 0;
3332   city_buildings->setUpdatesEnabled(false);
3333   city_buildings->clear_layout();
3334 
3335   h = fm.height() + 6;
3336   targets_used = collect_already_built_targets(targets, pcity);
3337   name_and_sort_items(targets, targets_used, items, false, pcity);
3338 
3339   for (item = 0; item < targets_used; item++) {
3340     struct universal target = items[item].item;
3341 
3342     ui = new impr_item(this, target.value.building, pcity);
3343     ui->init_pix();
3344     city_buildings->add_item(ui);
3345 
3346     fc_assert_action(VUT_IMPROVEMENT == target.kind, continue);
3347     sprite = get_building_sprite(tileset, target.value.building);
3348     upkeep = upkeep + city_improvement_upkeep(pcity, target.value.building);
3349     if (sprite != nullptr) {
3350       pix = sprite->pm;
3351       pix_scaled = pix->scaledToHeight(h);
3352     }
3353   }
3354 
3355   city_get_queue(pcity, &queue);
3356   p_table_p->setRowCount(worklist_length(&queue));
3357 
3358   for (int i = 0; i < worklist_length(&queue); i++) {
3359     struct universal target = queue.entries[i];
3360     tooltip = "";
3361 
3362     if (VUT_UTYPE == target.kind) {
3363       str = utype_values_translation(target.value.utype);
3364       cost = utype_build_shield_cost(target.value.utype);
3365       tooltip = get_tooltip_unit(target.value.utype, true).trimmed();
3366       sprite = get_unittype_sprite(get_tileset(), target.value.utype,
3367                                    direction8_invalid(), true);
3368     } else {
3369       str = city_improvement_name_translation(pcity, target.value.building);
3370       sprite = get_building_sprite(tileset, target.value.building);
3371       tooltip = get_tooltip_improvement(target.value.building,
3372                                         pcity, true).trimmed();
3373 
3374       if (improvement_has_flag(target.value.building, IF_GOLD)) {
3375         cost = -1;
3376       } else {
3377         cost = impr_build_shield_cost(target.value.building);
3378       }
3379     }
3380 
3381     for (col = 0; col < 3; col++) {
3382       qitem = new QTableWidgetItem();
3383       qitem->setToolTip(tooltip);
3384 
3385       switch (col) {
3386       case 0:
3387         if (sprite) {
3388           pix = sprite->pm;
3389           pix_scaled = pix->scaledToHeight(h);
3390           qitem->setData(Qt::DecorationRole, pix_scaled);
3391         }
3392         break;
3393 
3394       case 1:
3395         if (str.contains('[') && str.contains(']')) {
3396           int ii, ij;
3397 
3398           ii = str.lastIndexOf('[');
3399           ij = str.lastIndexOf(']');
3400           if (ij > ii) {
3401             str = str.remove(ii, ij - ii + 1);
3402           }
3403         }
3404         qitem->setText(str);
3405         break;
3406 
3407       case 2:
3408         qitem->setTextAlignment(Qt::AlignRight);
3409         qitem->setText(QString::number(cost));
3410         break;
3411       }
3412       p_table_p->setItem(i, col, qitem);
3413     }
3414   }
3415 
3416   p_table_p->horizontalHeader()->setStretchLastSection(false);
3417   p_table_p->resizeColumnsToContents();
3418   p_table_p->resizeRowsToContents();
3419   p_table_p->horizontalHeader()->setStretchLastSection(true);
3420 
3421   city_buildings->update_buildings();
3422   city_buildings->setUpdatesEnabled(true);
3423   city_buildings->setUpdatesEnabled(true);
3424 
3425   curr_impr->setText(QString(_("Improvements - upkeep %1")).arg(upkeep));
3426 }
3427 
3428 /****************************************************************************
3429   Slot executed when user changed production in customized table widget
3430 ****************************************************************************/
production_changed(int index)3431 void city_dialog::production_changed(int index)
3432 {
3433   cid id;
3434   QVariant qvar;
3435 
3436   if (can_client_issue_orders()) {
3437     struct universal univ;
3438 
3439     id = qvar.toInt();
3440     univ = cid_production(id);
3441     city_change_production(pcity, &univ);
3442   }
3443 }
3444 
3445 /****************************************************************************
3446   Shows customized table widget with available items to produce
3447   Shows default targets in overview city page
3448 ****************************************************************************/
show_targets()3449 void city_dialog::show_targets()
3450 {
3451   production_widget *pw;
3452   int when = 1;
3453   pw = new production_widget(this, pcity, future_targets->isChecked(),
3454                              when, selected_row_p, show_units->isChecked(),
3455                              false, show_wonders->isChecked(),
3456                              show_buildings->isChecked());
3457   pw->show();
3458 }
3459 
3460 /****************************************************************************
3461   Shows customized table widget with available items to produce
3462   Shows customized targets in city production page
3463 ****************************************************************************/
show_targets_worklist()3464 void city_dialog::show_targets_worklist()
3465 {
3466   production_widget *pw;
3467   int when = 4;
3468   pw = new production_widget(this, pcity, future_targets->isChecked(),
3469                              when, selected_row_p, show_units->isChecked(),
3470                              false, show_wonders->isChecked(),
3471                              show_buildings->isChecked());
3472   pw->show();
3473 }
3474 
3475 /****************************************************************************
3476   Clears worklist in production page
3477 ****************************************************************************/
clear_worklist()3478 void city_dialog::clear_worklist()
3479 {
3480   struct worklist empty;
3481 
3482   if (!can_client_issue_orders()) {
3483     return;
3484   }
3485 
3486   worklist_init(&empty);
3487   city_set_worklist(pcity, &empty);
3488 }
3489 
3490 /****************************************************************************
3491   Move current item on worklist up
3492 ****************************************************************************/
worklist_up()3493 void city_dialog::worklist_up()
3494 {
3495   QModelIndex index;
3496   struct worklist queue;
3497   struct universal *target;
3498 
3499   if (selected_row_p < 1 || selected_row_p >= p_table_p->rowCount()) {
3500     return;
3501   }
3502 
3503   target = new universal;
3504   city_get_queue(pcity, &queue);
3505   worklist_peek_ith(&queue, target, selected_row_p);
3506   worklist_remove(&queue, selected_row_p);
3507   worklist_insert(&queue, target, selected_row_p - 1);
3508   city_set_queue(pcity, &queue);
3509   index = p_table_p->model()->index(selected_row_p - 1, 0);
3510   p_table_p->setCurrentIndex(index);
3511   delete target;
3512 
3513 }
3514 
3515 /****************************************************************************
3516   Remove current item on worklist
3517 ****************************************************************************/
worklist_del()3518 void city_dialog::worklist_del()
3519 {
3520   QTableWidgetItem *item;
3521 
3522   if (selected_row_p < 0
3523       || selected_row_p >= p_table_p->rowCount()) {
3524     return;
3525   }
3526 
3527   item = p_table_p->item(selected_row_p, 0);
3528   dbl_click_p(item);
3529   update_prod_buttons();
3530 }
3531 
3532 /****************************************************************************
3533   Move current item on worklist down
3534 ****************************************************************************/
worklist_down()3535 void city_dialog::worklist_down()
3536 {
3537   QModelIndex index;
3538   struct worklist queue;
3539   struct universal *target;
3540 
3541   if (selected_row_p < 0 || selected_row_p >= p_table_p->rowCount() - 1) {
3542     return;
3543   }
3544 
3545   target = new universal;
3546   city_get_queue(pcity, &queue);
3547   worklist_peek_ith(&queue, target, selected_row_p);
3548   worklist_remove(&queue, selected_row_p);
3549   worklist_insert(&queue, target, selected_row_p + 1);
3550   city_set_queue(pcity, &queue);
3551   index = p_table_p->model()->index(selected_row_p + 1, 0);
3552   p_table_p->setCurrentIndex(index);
3553   delete target;
3554 }
3555 
3556 /****************************************************************************
3557   Save worklist
3558 ****************************************************************************/
save_worklist()3559 void city_dialog::save_worklist()
3560 {
3561   struct worklist queue;
3562   struct global_worklist *gw;
3563   QString text;
3564   hud_input_box ask(gui()->central_wdg);
3565 
3566   ask.set_text_title_definput(_("What should we name new worklist?"),
3567                               _("Save current worklist"),
3568                               _("New worklist"));
3569   if (ask.exec() == QDialog::Accepted) {
3570     QByteArray ask_bytes;
3571 
3572     ask_bytes = ask.input_edit.text().toLocal8Bit();
3573     text = ask_bytes.data();
3574     if (!text.isEmpty()) {
3575       ask_bytes = text.toLocal8Bit();
3576       gw = global_worklist_new(ask_bytes.data());
3577       city_get_queue(pcity, &queue);
3578       global_worklist_set(gw, &queue);
3579     }
3580   }
3581 }
3582 
3583 /****************************************************************************
3584   Puts city name and people count on title
3585 ****************************************************************************/
update_title()3586 void city_dialog::update_title()
3587 {
3588   QString buf;
3589 
3590   // Defeat keyboard shortcut mnemonics
3591   lcity_name->setText(QString(city_name_get(pcity))
3592                       .replace("&", "&&"));
3593 
3594   if (city_unhappy(pcity)) {
3595     /* TRANS: city dialog title */
3596     buf = QString(_("%1 - %2 citizens - DISORDER")).arg(city_name_get(pcity),
3597           population_to_text(city_population(pcity)));
3598   } else if (city_celebrating(pcity)) {
3599     /* TRANS: city dialog title */
3600     buf = QString(_("%1 - %2 citizens - celebrating")).arg(city_name_get(pcity),
3601           population_to_text(city_population(pcity)));
3602   } else if (city_happy(pcity)) {
3603     /* TRANS: city dialog title */
3604     buf = QString(_("%1 - %2 citizens - happy")).arg(city_name_get(pcity),
3605           population_to_text(city_population(pcity)));
3606   } else {
3607     /* TRANS: city dialog title */
3608     buf = QString(_("%1 - %2 citizens")).arg(city_name_get(pcity),
3609           population_to_text(city_population(pcity)));
3610   }
3611 
3612   setWindowTitle(buf);
3613 }
3614 
3615 /**************************************************************************
3616   Pop up (or bring to the front) a dialog for the given city.  It may or
3617   may not be modal.
3618 **************************************************************************/
qtg_real_city_dialog_popup(struct city * pcity)3619 void qtg_real_city_dialog_popup(struct city *pcity)
3620 {
3621   if (!::city_dlg_created) {
3622     ::city_dlg = new city_dialog(gui()->mapview_wdg);
3623   }
3624 
3625   city_dlg->setup_ui(pcity);
3626   city_dlg->show();
3627   city_dlg->activateWindow();
3628   city_dlg->raise();
3629 }
3630 
3631 /****************************************************************************
3632   Closes city dialog
3633 ****************************************************************************/
destroy_city_dialog()3634 void destroy_city_dialog()
3635 {
3636   if (!::city_dlg_created) {
3637     return;
3638   }
3639 
3640   city_dlg->close();
3641   ::city_dlg_created = false;
3642 }
3643 
3644 
3645 /**************************************************************************
3646   Close the dialog for the given city.
3647 **************************************************************************/
qtg_popdown_city_dialog(struct city * pcity)3648 void qtg_popdown_city_dialog(struct city *pcity)
3649 {
3650   if (!::city_dlg_created) {
3651     return;
3652   }
3653 
3654   destroy_city_dialog();
3655 }
3656 
3657 /**************************************************************************
3658   Close the dialogs for all cities.
3659 **************************************************************************/
qtg_popdown_all_city_dialogs()3660 void qtg_popdown_all_city_dialogs()
3661 {
3662   destroy_city_dialog();
3663 }
3664 
3665 /**************************************************************************
3666   City is about to disappear from client
3667 **************************************************************************/
qtg_city_to_disappear(struct city * pcity)3668 void qtg_city_to_disappear(struct city *pcity)
3669 {
3670   gui()->rallies.clear(pcity);
3671 }
3672 
3673 /**************************************************************************
3674   Refresh (update) all data for the given city's dialog.
3675 **************************************************************************/
qtg_real_city_dialog_refresh(struct city * pcity)3676 void qtg_real_city_dialog_refresh(struct city *pcity)
3677 {
3678   if (!::city_dlg_created) {
3679     return;
3680   }
3681 
3682   if (qtg_city_dialog_is_open(pcity)) {
3683     city_dlg->refresh();
3684   }
3685 }
3686 
3687 /**************************************************************************
3688   Updates city font
3689 **************************************************************************/
city_font_update()3690 void city_font_update()
3691 {
3692   QList<QLabel *> l;
3693   QFont *f;
3694 
3695   if (!::city_dlg_created) {
3696     return;
3697   }
3698 
3699   l = city_dlg->findChildren<QLabel *>();
3700 
3701   f = fc_font::instance()->get_font(fonts::city_label);
3702 
3703   for (int i = 0; i < l.size(); ++i) {
3704     if (l.at(i)->property(fonts::city_label).isValid()) {
3705       l.at(i)->setFont(*f);
3706     }
3707   }
3708 }
3709 /**************************************************************************
3710   Update city dialogs when the given unit's status changes.  This
3711   typically means updating both the unit's home city (if any) and the
3712   city in which it is present (if any).
3713 **************************************************************************/
qtg_refresh_unit_city_dialogs(struct unit * punit)3714 void qtg_refresh_unit_city_dialogs(struct unit *punit)
3715 {
3716 
3717   struct city *pcity_sup, *pcity_pre;
3718 
3719   pcity_sup = game_city_by_number(punit->homecity);
3720   pcity_pre = tile_city(punit->tile);
3721 
3722   qtg_real_city_dialog_refresh(pcity_sup);
3723   qtg_real_city_dialog_refresh(pcity_pre);
3724 
3725 }
3726 
3727 /**************************************************************************
3728   Return whether the dialog for the given city is open.
3729 **************************************************************************/
qtg_city_dialog_is_open(struct city * pcity)3730 bool qtg_city_dialog_is_open(struct city *pcity)
3731 {
3732   if (!::city_dlg_created) {
3733     return false;
3734   }
3735 
3736   if (city_dlg->pcity == pcity && city_dlg->isVisible()) {
3737     return true;
3738   }
3739 
3740   return false;
3741 }
3742 
3743 /**************************************************************************
3744   Event filter for catching tooltip events
3745 **************************************************************************/
eventFilter(QObject * obj,QEvent * ev)3746 bool fc_tooltip::eventFilter(QObject *obj, QEvent *ev)
3747 {
3748   QHelpEvent *help_event;
3749   QString item_tooltip;
3750   QRect rect;
3751 
3752   if (ev->type() == QEvent::ToolTip) {
3753     QAbstractItemView *view = qobject_cast<QAbstractItemView *>(obj->parent());
3754 
3755     if (!view) {
3756       return false;
3757     }
3758 
3759     help_event = static_cast<QHelpEvent *>(ev);
3760     QPoint pos = help_event->pos();
3761     QModelIndex index = view->indexAt(pos);
3762 
3763     if (!index.isValid()) {
3764       return false;
3765     }
3766 
3767     item_tooltip = view->model()->data(index, Qt::ToolTipRole).toString();
3768     rect = view->visualRect(index);
3769     rect.setX(rect.x() + help_event->globalPos().x());
3770     rect.setY(rect.y() + help_event->globalPos().y());
3771 
3772     if (!item_tooltip.isEmpty()) {
3773       QToolTip::showText(help_event->globalPos(), item_tooltip, view, rect);
3774     } else {
3775       QToolTip::hideText();
3776     }
3777 
3778     return true;
3779   }
3780 
3781   return false;
3782 }
3783 
3784 /**************************************************************************
3785   'text' is assumed to have already been HTML-escaped if necessary
3786 **************************************************************************/
bold(QString text)3787 QString bold(QString text)
3788 {
3789   return QString("<b>" + text + "</b>");
3790 }
3791 
3792 
3793 /***************************************************************************
3794   Returns improvement properties to append in tooltip
3795   ext is used to get extra info from help
3796 ***************************************************************************/
get_tooltip_improvement(impr_type * building,struct city * pcity,bool ext)3797 QString get_tooltip_improvement(impr_type *building, struct city *pcity,
3798                                 bool ext)
3799 {
3800   QString def_str;
3801   QString upkeep;
3802   QString s1, s2, str;
3803   const char *req = skip_intl_qualifier_prefix(_("?tech:None"));
3804 
3805   if (pcity !=  nullptr) {
3806     upkeep = QString::number(city_improvement_upkeep(pcity, building));
3807   } else {
3808     upkeep = QString::number(building->upkeep);
3809   }
3810   requirement_vector_iterate(&building->obsolete_by, pobs) {
3811     if (pobs->source.kind == VUT_ADVANCE) {
3812       req = advance_name_translation(pobs->source.value.advance);
3813       break;
3814     }
3815   } requirement_vector_iterate_end;
3816   s2 = QString(req);
3817   str = _("Obsolete by:");
3818   str = str + " " + s2;
3819   def_str = "<p style='white-space:pre'><b>"
3820             + QString(improvement_name_translation(building)).toHtmlEscaped()
3821             + "</b>\n";
3822   def_str += QString(_("Cost: %1, Upkeep: %2\n"))
3823              .arg(impr_build_shield_cost(building))
3824              .arg(upkeep).toHtmlEscaped();
3825   if (s1.compare(s2) != 0) {
3826     def_str = def_str + str.toHtmlEscaped() + "\n";
3827   }
3828   def_str = def_str + "\n";
3829   if (ext) {
3830     char buffer[8192];
3831 
3832     str = helptext_building(buffer, sizeof(buffer), client.conn.playing,
3833                             NULL, building);
3834     str = cut_helptext(str);
3835     str = split_text(str, true);
3836     str = str.trimmed();
3837     def_str = def_str + str.toHtmlEscaped();
3838   }
3839   return def_str;
3840 }
3841 
3842 /***************************************************************************
3843   Returns unit properties to append in tooltip
3844   ext is used to get extra info from help
3845 ***************************************************************************/
get_tooltip_unit(struct unit_type * unit,bool ext)3846 QString get_tooltip_unit(struct unit_type *unit, bool ext)
3847 {
3848   QString def_str;
3849   QString str;
3850   QString obsolete_str;
3851   struct unit_type *obsolete;
3852   struct advance *tech;
3853 
3854   def_str = "<b>"
3855     + QString(utype_name_translation(unit)).toHtmlEscaped()
3856     + "</b>\n";
3857   obsolete = unit->obsoleted_by;
3858   if (obsolete) {
3859     tech = obsolete->require_advance;
3860     obsolete_str = QString("</td></tr><tr><td colspan=\"3\">");
3861     if (tech && tech != advance_by_number(0)) {
3862       /* TRANS: this and nearby literal strings are interpreted
3863        * as (Qt) HTML */
3864       obsolete_str = obsolete_str + QString(_("Obsoleted by %1 (%2)."))
3865           .arg(utype_name_translation(obsolete))
3866           .arg(advance_name_translation(tech)).toHtmlEscaped();
3867     } else {
3868       obsolete_str = obsolete_str + QString(_("Obsoleted by %1."))
3869           .arg(utype_name_translation(obsolete)).toHtmlEscaped();
3870     }
3871   }
3872   def_str += "<table width=\"100\%\"><tr><td>"
3873              + bold(QString(_("Attack:"))) + " "
3874              + QString::number(unit->attack_strength).toHtmlEscaped()
3875              + QString("</td><td>") + bold(QString(_("Defense:"))) + " "
3876              + QString::number(unit->defense_strength).toHtmlEscaped()
3877              + QString("</td><td>") + bold(QString(_("Move:"))) + " "
3878              + QString(move_points_text(unit->move_rate, TRUE)).toHtmlEscaped()
3879              + QString("</td></tr><tr><td>")
3880              + bold(QString(_("Cost:"))) + " "
3881              + QString::number(utype_build_shield_cost(unit)).toHtmlEscaped()
3882              + QString("</td><td colspan=\"2\">")
3883              + bold(QString(_("Basic Upkeep:")))
3884              + " " + QString(helptext_unit_upkeep_str(unit)).toHtmlEscaped()
3885              + QString("</td></tr><tr><td>")
3886              + bold(QString(_("Hitpoints:"))) + " "
3887              + QString::number(unit->hp).toHtmlEscaped()
3888              + QString("</td><td>") + bold(QString(_("FirePower:"))) + " "
3889              + QString::number(unit->firepower).toHtmlEscaped()
3890              + QString("</td><td>") + bold(QString(_("Vision:"))) + " "
3891              + QString::number((int) sqrt((double) unit->vision_radius_sq))
3892                .toHtmlEscaped()
3893              + obsolete_str
3894              + QString("</td></tr></table><p style='white-space:pre'>");
3895   if (ext) {
3896     char buffer[8192];
3897     char buf2[1];
3898 
3899     buf2[0] = '\0';
3900     str = helptext_unit(buffer, sizeof(buffer), client.conn.playing,
3901                         buf2, unit);
3902     str = cut_helptext(str);
3903     str = split_text(str, true);
3904     str = str.trimmed().toHtmlEscaped();
3905     def_str = def_str + str;
3906   }
3907   return def_str;
3908 };
3909 
3910 /**************************************************************************
3911   Returns shortened help for given universal ( stored in qvar )
3912 **************************************************************************/
get_tooltip(QVariant qvar)3913 QString get_tooltip(QVariant qvar)
3914 {
3915   QString str, def_str, ret_str;
3916   QStringList sl;
3917   char buffer[8192];
3918   char buf2[1];
3919 
3920   buf2[0] = '\0';
3921   struct universal *target;
3922   target = reinterpret_cast<universal *>(qvar.value<void *>());
3923 
3924   if (target == NULL) {
3925   } else if (VUT_UTYPE == target->kind) {
3926     def_str = get_tooltip_unit(target->value.utype);
3927     str = helptext_unit(buffer, sizeof(buffer), client.conn.playing,
3928                         buf2, target->value.utype);
3929   } else {
3930     if (!improvement_has_flag(target->value.building, IF_GOLD)) {
3931       def_str = get_tooltip_improvement(target->value.building);
3932     }
3933 
3934     str = helptext_building(buffer, sizeof(buffer), client.conn.playing,
3935                             NULL, target->value.building);
3936   }
3937 
3938   /* Remove all lines from help which has '*' in first 3 chars */
3939   ret_str = cut_helptext(str);
3940   ret_str = split_text(ret_str, true);
3941   ret_str = ret_str.trimmed();
3942   ret_str = def_str + ret_str.toHtmlEscaped();
3943 
3944   return ret_str;
3945 }
3946 
3947 /***************************************************************************
3948   City item delegate constructor
3949 ***************************************************************************/
city_production_delegate(QPoint sh,QObject * parent,struct city * city)3950 city_production_delegate::city_production_delegate(QPoint sh,
3951     QObject *parent,
3952     struct city *city)
3953   : QItemDelegate(parent)
3954 {
3955   pd = sh;
3956   item_height = sh.y();
3957   pcity = city;
3958 }
3959 
3960 /***************************************************************************
3961   City item delgate paint event
3962 ***************************************************************************/
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const3963 void city_production_delegate::paint(QPainter *painter,
3964                                      const QStyleOptionViewItem &option,
3965                                      const QModelIndex &index) const
3966 {
3967   struct universal *target;
3968   QString name;
3969   QVariant qvar;
3970   QPixmap *pix;
3971   QPixmap pix_scaled;
3972   QRect rect1;
3973   QRect rect2;
3974   struct sprite *sprite;
3975   bool useless = false;
3976   bool is_coinage = false;
3977   bool is_neutral = false;
3978   bool is_sea = false;
3979   bool is_flying = false;
3980   bool is_unit = true;
3981   QPixmap pix_dec(option.rect.width(), option.rect.height());
3982   QStyleOptionViewItem opt;
3983   color col;
3984   QIcon icon = qapp->style()->standardIcon(QStyle::SP_DialogCancelButton);
3985   bool free_sprite = false;
3986   struct unit_class *pclass;
3987 
3988   if (!option.rect.isValid()) {
3989     return;
3990   }
3991 
3992   qvar = index.data();
3993 
3994   if (!qvar.isValid()) {
3995     return;
3996   }
3997 
3998   target = reinterpret_cast<universal *>(qvar.value<void *>());
3999 
4000   if (target == NULL) {
4001     col.qcolor = Qt::white;
4002     sprite = qtg_create_sprite(100, 100, &col);
4003     free_sprite = true;
4004     *sprite->pm = icon.pixmap(100, 100);
4005     name = _("Cancel");
4006     is_unit = false;
4007   } else if (VUT_UTYPE == target->kind) {
4008     name = utype_name_translation(target->value.utype);
4009     is_neutral = utype_has_flag(target->value.utype, UTYF_CIVILIAN);
4010     pclass = utype_class(target->value.utype);
4011     if (!uclass_has_flag(pclass, UCF_TERRAIN_DEFENSE)
4012         && !uclass_has_flag(pclass, UCF_CAN_FORTIFY)
4013         && !uclass_has_flag(pclass, UCF_ZOC)) {
4014       is_sea = true;
4015     }
4016 
4017     if ((utype_fuel(target->value.utype)
4018          && !uclass_has_flag(pclass, UCF_TERRAIN_DEFENSE)
4019         && !uclass_has_flag(pclass, UCF_CAN_PILLAGE)
4020         && !uclass_has_flag(pclass, UCF_CAN_FORTIFY)
4021         && !uclass_has_flag(pclass, UCF_ZOC))
4022         || uclass_has_flag(pclass, UCF_MISSILE)) {
4023       if (is_sea) {
4024         is_sea = false;
4025       }
4026       is_flying = true;
4027     }
4028 
4029     sprite = get_unittype_sprite(get_tileset(), target->value.utype,
4030                                  direction8_invalid(), true);
4031   } else {
4032     is_unit = false;
4033     name = improvement_name_translation(target->value.building);
4034     sprite = get_building_sprite(tileset, target->value.building);
4035     useless = is_improvement_redundant(pcity, target->value.building);
4036     is_coinage = improvement_has_flag(target->value.building, IF_GOLD);
4037   }
4038 
4039   if (sprite != NULL) {
4040     pix = sprite->pm;
4041     pix_scaled = pix->scaledToHeight(item_height - 2, Qt::SmoothTransformation);
4042 
4043     if (useless) {
4044       pixmap_put_x(&pix_scaled);
4045     }
4046   }
4047 
4048   opt = QItemDelegate::setOptions(index, option);
4049   painter->save();
4050   opt.displayAlignment = Qt::AlignLeft;
4051   opt.textElideMode = Qt::ElideMiddle;
4052   QItemDelegate::drawBackground(painter, opt, index);
4053   rect1 = option.rect;
4054   rect1.setWidth(pix_scaled.width() + 4);
4055   rect2 = option.rect;
4056   rect2.setLeft(option.rect.left() + rect1.width());
4057   rect2.setTop(rect2.top() + (rect2.height()
4058                               - painter->fontMetrics().height()) / 2);
4059   QItemDelegate::drawDisplay(painter, opt, rect2, name);
4060 
4061   if (is_unit) {
4062     if (is_sea) {
4063       pix_dec.fill(QColor(0, 0, 255, 80));
4064     } else if (is_flying) {
4065       pix_dec.fill(QColor(220, 0, 0, 80));
4066     } else if (is_neutral) {
4067       pix_dec.fill(QColor(0, 120, 0, 40));
4068     } else {
4069       pix_dec.fill(QColor(0, 0, 150, 40));
4070     }
4071 
4072     QItemDelegate::drawDecoration(painter, option, option.rect, pix_dec);
4073   }
4074 
4075   if (is_coinage) {
4076     pix_dec.fill(QColor(255, 255, 0, 70));
4077     QItemDelegate::drawDecoration(painter, option, option.rect, pix_dec);
4078   }
4079 
4080   if (!pix_scaled.isNull()) {
4081     QItemDelegate::drawDecoration(painter, opt, rect1, pix_scaled);
4082   }
4083 
4084   drawFocus(painter, opt, option.rect);
4085 
4086   painter->restore();
4087 
4088   if (free_sprite) {
4089     qtg_free_sprite(sprite);
4090   }
4091 }
4092 
4093 /****************************************************************************
4094   Draws focus for given item
4095 ****************************************************************************/
drawFocus(QPainter * painter,const QStyleOptionViewItem & option,const QRect & rect) const4096 void city_production_delegate::drawFocus(QPainter *painter,
4097     const QStyleOptionViewItem &option,
4098     const QRect &rect) const
4099 {
4100   QPixmap pix(option.rect.width(), option.rect.height());
4101 
4102   if ((option.state & QStyle::State_MouseOver) == 0 || !rect.isValid()) {
4103     return;
4104   }
4105 
4106   pix.fill(QColor(50, 50, 50, 50));
4107   QItemDelegate::drawDecoration(painter, option, option.rect, pix);
4108 }
4109 
4110 
4111 /***************************************************************************
4112   Size hint for city item delegate
4113 ***************************************************************************/
sizeHint(const QStyleOptionViewItem & option,const QModelIndex & index) const4114 QSize city_production_delegate::sizeHint(const QStyleOptionViewItem &option,
4115     const QModelIndex &index) const
4116 {
4117   QSize s;
4118 
4119   s.setWidth(pd.x());
4120   s.setHeight(pd.y());
4121   return s;
4122 }
4123 
4124 /****************************************************************************
4125   Production item constructor
4126 ****************************************************************************/
production_item(struct universal * ptarget,QObject * parent)4127 production_item::production_item(struct universal *ptarget,
4128                                  QObject *parent): QObject()
4129 {
4130   setParent(parent);
4131   target = ptarget;
4132 }
4133 
4134 /****************************************************************************
4135   Production item destructor
4136 ****************************************************************************/
~production_item()4137 production_item::~production_item()
4138 {
4139   /* allocated as renegade in model */
4140   if (target != NULL) {
4141     delete target;
4142   }
4143 }
4144 
4145 /****************************************************************************
4146   Returns stored data
4147 ****************************************************************************/
data() const4148 QVariant production_item::data() const
4149 {
4150   return QVariant::fromValue((void *)target);
4151 }
4152 
4153 /****************************************************************************
4154   Sets data for item, must be declared.
4155 ****************************************************************************/
setData()4156 bool production_item::setData()
4157 {
4158   return false;
4159 }
4160 
4161 /****************************************************************************
4162   Constructor for city production model
4163 ****************************************************************************/
city_production_model(struct city * pcity,bool f,bool su,bool sw,bool sb,QObject * parent)4164 city_production_model::city_production_model(struct city *pcity, bool f,
4165     bool su, bool sw, bool sb,
4166     QObject *parent)
4167   : QAbstractListModel(parent)
4168 {
4169   show_units = su;
4170   show_wonders = sw;
4171   show_buildings = sb;
4172   mcity = pcity;
4173   future_t = f;
4174   populate();
4175 }
4176 
4177 /****************************************************************************
4178   Destructor for city production model
4179 ****************************************************************************/
~city_production_model()4180 city_production_model::~city_production_model()
4181 {
4182   qDeleteAll(city_target_list);
4183   city_target_list.clear();
4184 }
4185 
4186 /****************************************************************************
4187   Returns data from model
4188 ****************************************************************************/
data(const QModelIndex & index,int role) const4189 QVariant city_production_model::data(const QModelIndex &index, int role) const
4190 {
4191   if (!index.isValid()) return QVariant();
4192 
4193   if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0
4194       && index.column() < columnCount()
4195       && (index.column() + index.row() * 3 < city_target_list.count())) {
4196     int r, c, t ,new_index;
4197     r = index.row();
4198     c = index.column();
4199     t = r * 3 + c;
4200     new_index = t / 3 + rowCount() * c;
4201     /* Exception, shift whole column */
4202     if ((c == 2) && city_target_list.count() % 3 == 1) {
4203       new_index = t / 3 + rowCount() * c - 1;
4204     }
4205     if (role == Qt::ToolTipRole) {
4206       return get_tooltip(city_target_list[new_index]->data());
4207     }
4208 
4209     return city_target_list[new_index]->data();
4210   }
4211 
4212   return QVariant();
4213 }
4214 
4215 /****************************************************************************
4216   Fills model with data
4217 ****************************************************************************/
populate()4218 void city_production_model::populate()
4219 {
4220   production_item *pi;
4221   struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
4222   struct item items[MAX_NUM_PRODUCTION_TARGETS];
4223   struct universal *renegade;
4224   int item, targets_used;
4225   QString str;
4226   QFont f = *fc_font::instance()->get_font(fonts::default_font);
4227   QFontMetrics fm(f);
4228 
4229   sh.setY(fm.height() * 2);
4230   sh.setX(0);
4231 
4232   qDeleteAll(city_target_list);
4233   city_target_list.clear();
4234 
4235   targets_used = collect_eventually_buildable_targets(targets, mcity,
4236                  future_t);
4237   name_and_sort_items(targets, targets_used, items, false, mcity);
4238 
4239   for (item = 0; item < targets_used; item++) {
4240     if (future_t || can_city_build_now(mcity, &items[item].item)) {
4241       renegade = new universal(items[item].item);
4242 
4243       /* renagade deleted in production_item destructor */
4244       if (VUT_UTYPE == renegade->kind) {
4245         str = utype_name_translation(renegade->value.utype);
4246         sh.setX(qMax(sh.x(), fm.horizontalAdvance(str)));
4247 
4248         if (show_units) {
4249           pi = new production_item(renegade, this);
4250           city_target_list << pi;
4251         }
4252       } else {
4253         str = improvement_name_translation(renegade->value.building);
4254         sh.setX(qMax(sh.x(), fm.horizontalAdvance(str)));
4255 
4256         if ((is_wonder(renegade->value.building) && show_wonders)
4257             || (is_improvement(renegade->value.building) && show_buildings)
4258             || (improvement_has_flag(renegade->value.building, IF_GOLD))
4259             || (is_special_improvement(renegade->value.building)
4260             && show_buildings)) {
4261           pi = new production_item(renegade, this);
4262           city_target_list << pi;
4263         }
4264       }
4265     }
4266   }
4267 
4268   renegade = NULL;
4269   pi = new production_item(renegade, this);
4270   city_target_list << pi;
4271   sh.setX(2 * sh.y() + sh.x());
4272   sh.setX(qMin(sh.x(), 250));
4273 }
4274 
4275 /****************************************************************************
4276   Sets data in model
4277 ****************************************************************************/
setData(const QModelIndex & index,const QVariant & value,int role)4278 bool city_production_model::setData(const QModelIndex &index,
4279                                     const QVariant &value, int role)
4280 {
4281   if (!index.isValid() || role != Qt::DisplayRole || role != Qt::ToolTipRole)
4282     return false;
4283 
4284   if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0
4285       && index.column() < columnCount()) {
4286     bool change = city_target_list[index.row()]->setData();
4287     return change;
4288   }
4289 
4290   return false;
4291 }
4292 
4293 /****************************************************************************
4294   Constructor for production widget
4295   future - show future targets
4296   show_units - if to show units
4297   when - where to insert
4298   curr - current index to insert
4299   buy - buy if possible
4300 ****************************************************************************/
production_widget(QWidget * parent,struct city * pcity,bool future,int when,int curr,bool show_units,bool buy,bool show_wonders,bool show_buildings)4301 production_widget::production_widget(QWidget *parent, struct city *pcity,
4302                                      bool future, int when, int curr,
4303                                      bool show_units, bool buy,
4304                                      bool show_wonders,
4305                                      bool show_buildings): QTableView()
4306 {
4307   QPoint pos, sh;
4308   int desk_width = QApplication::desktop()->width();
4309   int desk_height = QApplication::desktop()->height();
4310   fc_tt = new fc_tooltip(this);
4311   setAttribute(Qt::WA_DeleteOnClose);
4312   setWindowFlags(Qt::Popup);
4313   verticalHeader()->setVisible(false);
4314   horizontalHeader()->setVisible(false);
4315   setProperty("showGrid", false);
4316   curr_selection = curr;
4317   sh_units = show_units;
4318   pw_city = pcity;
4319   buy_it = buy;
4320   when_change = when;
4321   list_model = new city_production_model(pw_city, future, show_units,
4322                                          show_wonders, show_buildings, this);
4323   sh = list_model->sh;
4324   c_p_d = new city_production_delegate(sh, this, pw_city);
4325   setItemDelegate(c_p_d);
4326   setModel(list_model);
4327   viewport()->installEventFilter(fc_tt);
4328   installEventFilter(this);
4329   connect(selectionModel(), SIGNAL(selectionChanged(const QItemSelection &,
4330                                    const QItemSelection &)),
4331           SLOT(prod_selected(const QItemSelection &,
4332                              const QItemSelection &)));
4333   resizeRowsToContents();
4334   resizeColumnsToContents();
4335   setFixedWidth(3 * sh.x() + 6);
4336   setFixedHeight(list_model->rowCount()*sh.y() + 6);
4337 
4338   if (width() > desk_width) {
4339     setFixedWidth(desk_width);
4340     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
4341   } else {
4342     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
4343   }
4344 
4345   if (height() > desk_height) {
4346     setFixedHeight(desk_height);
4347     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
4348   } else {
4349     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
4350   }
4351 
4352   pos = QCursor::pos();
4353 
4354   if (pos.x() + width() > desk_width) {
4355     pos.setX(desk_width - width());
4356   } else if (pos.x() - width() < 0) {
4357     pos.setX(0);
4358   }
4359 
4360   if (pos.y() + height() > desk_height) {
4361     pos.setY(desk_height - height());
4362   } else if (pos.y() - height() < 0) {
4363     pos.setY(0);
4364   }
4365 
4366   move(pos);
4367   setMouseTracking(true);
4368   setFocus();
4369 }
4370 
4371 /****************************************************************************
4372   Mouse press event for production widget
4373 ****************************************************************************/
mousePressEvent(QMouseEvent * event)4374 void production_widget::mousePressEvent(QMouseEvent *event)
4375 {
4376   if (event->button() == Qt::RightButton) {
4377     close();
4378     return;
4379   }
4380 
4381   QAbstractItemView::mousePressEvent(event);
4382 }
4383 
4384 /****************************************************************************
4385   Event filter for production widget
4386 ****************************************************************************/
eventFilter(QObject * obj,QEvent * ev)4387 bool production_widget::eventFilter(QObject *obj, QEvent *ev)
4388 {
4389   QRect pw_rect;
4390   QPoint br;
4391 
4392   if (obj != this)
4393     return false;
4394 
4395   if (ev->type() == QEvent::MouseButtonPress) {
4396     pw_rect.setTopLeft(pos());
4397     br.setX(pos().x() + width());
4398     br.setY(pos().y() + height());
4399     pw_rect.setBottomRight(br);
4400 
4401     if (!pw_rect.contains(QCursor::pos())) {
4402       close();
4403     }
4404   }
4405 
4406   return false;
4407 }
4408 
4409 /****************************************************************************
4410   Changed selection in production widget
4411 ****************************************************************************/
prod_selected(const QItemSelection & sl,const QItemSelection & ds)4412 void production_widget::prod_selected(const QItemSelection &sl,
4413                                       const QItemSelection &ds)
4414 {
4415   QModelIndexList indexes = selectionModel()->selectedIndexes();
4416   QModelIndex index;
4417   QVariant qvar;
4418   struct worklist queue;
4419   struct universal *target;
4420 
4421   if (indexes.isEmpty() || client_is_observer()) {
4422     return;
4423   }
4424 
4425   index = indexes.at(0);
4426   qvar = index.data(Qt::UserRole);
4427 
4428   if (!qvar.isValid()) {
4429     return;
4430   }
4431 
4432   target = reinterpret_cast<universal *>(qvar.value<void *>());
4433 
4434   if (target != NULL) {
4435     city_get_queue(pw_city, &queue);
4436 
4437     switch (when_change) {
4438     case 0: /* Change current production */
4439       city_change_production(pw_city, target);
4440 
4441       if (city_can_buy(pw_city) && buy_it) {
4442         city_buy_production(pw_city);
4443       }
4444 
4445       break;
4446 
4447     case 1:                 /* Change current (selected on list)*/
4448       if (curr_selection < 0 || curr_selection > worklist_length(&queue)) {
4449         city_change_production(pw_city, target);
4450       } else {
4451         worklist_remove(&queue, curr_selection);
4452         worklist_insert(&queue, target, curr_selection);
4453         city_set_queue(pw_city, &queue);
4454       }
4455       break;
4456 
4457     case 2:                 /* Insert before */
4458       if (curr_selection < 0 || curr_selection > worklist_length(&queue)) {
4459         curr_selection = 0;
4460       }
4461 
4462       curr_selection--;
4463       curr_selection = qMax(0, curr_selection);
4464       worklist_insert(&queue, target, curr_selection);
4465       city_set_queue(pw_city, &queue);
4466       break;
4467 
4468     case 3:                 /* Insert after */
4469       if (curr_selection < 0 || curr_selection > worklist_length(&queue)) {
4470         city_queue_insert(pw_city, -1, target);
4471         break;
4472       }
4473 
4474       curr_selection++;
4475       worklist_insert(&queue, target, curr_selection);
4476       city_set_queue(pw_city, &queue);
4477       break;
4478 
4479     case 4:                 /* Add last */
4480       city_queue_insert(pw_city, -1, target);
4481       break;
4482 
4483     default:
4484       break;
4485     }
4486   }
4487 
4488   close();
4489   destroy();
4490 }
4491 
4492 /****************************************************************************
4493   Destructor for production widget
4494 ****************************************************************************/
~production_widget()4495 production_widget::~production_widget()
4496 {
4497   delete c_p_d;
4498   delete list_model;
4499   viewport()->removeEventFilter(fc_tt);
4500   removeEventFilter(this);
4501 }
4502 
4503