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(), ¶m);
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, ¶m);
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, ¶m)) {
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(¶m, 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, ¶m);
2513 i = cmafec_preset_get_index_of_parameter(const_cast <struct
2514 cm_parameter *const >(¶m));
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