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 <QGroupBox>
21 #include <QMouseEvent>
22 #include <QPainter>
23 #include <QScreen>
24 #include <QVBoxLayout>
25 
26 // common
27 #include "multipliers.h"
28 
29 // gui-qt
30 #include "fc_client.h"
31 #include "dialogs.h"
32 #include "qtg_cxxside.h"
33 #include "sprite.h"
34 
35 #include "ratesdlg.h"
36 
37 static int scale_to_mult(const struct multiplier *pmul, int scale);
38 static int mult_to_scale(const struct multiplier *pmul, int val);
39 
40 /**************************************************************************
41   Dialog constructor for changing rates with sliders.
42   Automatic destructor will clean qobjects, so there is no one
43 **************************************************************************/
tax_rates_dialog(QWidget * parent)44 tax_rates_dialog::tax_rates_dialog(QWidget *parent)
45   : qfc_dialog(parent)
46 {
47   QHBoxLayout *some_layout;
48   QVBoxLayout *main_layout;
49   QPushButton *cancel_button;
50   QPushButton *ok_button;
51   QPushButton *apply_button;
52   QLabel *l1, *l2;
53   QString str;
54   int max;
55 
56   setWindowTitle(_("Tax rates"));
57   main_layout = new QVBoxLayout;
58 
59   if (client.conn.playing != nullptr) {
60     max = get_player_bonus(client.conn.playing, EFT_MAX_RATES);
61   } else {
62     max = 100;
63   }
64 
65   /* Trans: Government - max rate (of taxes) x% */
66   str = QString(_("%1 - max rate: %2%")).
67         arg(government_name_for_player(client.conn.playing),
68             QString::number(max));
69 
70   l2 = new QLabel(_("Select tax, luxury and science rates"));
71   l1 = new QLabel(str);
72   l1->setAlignment(Qt::AlignHCenter);
73   l2->setAlignment(Qt::AlignHCenter);
74   main_layout->addWidget(l2);
75   main_layout->addWidget(l1);
76   main_layout->addSpacing(20);
77 
78   cancel_button = new QPushButton(_("Cancel"));
79   ok_button = new QPushButton(_("Ok"));
80   apply_button = new QPushButton(_("Apply"));
81   some_layout = new QHBoxLayout;
82   connect(cancel_button, &QAbstractButton::pressed,
83           this, &tax_rates_dialog::slot_cancel_button_pressed);
84   connect(apply_button, &QAbstractButton::pressed,
85           this, &tax_rates_dialog::slot_apply_button_pressed);
86   connect(ok_button, &QAbstractButton::pressed,
87           this, &tax_rates_dialog::slot_ok_button_pressed);
88   some_layout->addWidget(cancel_button);
89   some_layout->addWidget(apply_button);
90   some_layout->addWidget(ok_button);
91   fcde = new fc_double_edge(this);
92   main_layout->addWidget(fcde);
93   main_layout->addSpacing(20);
94   main_layout->addLayout(some_layout);
95   setLayout(main_layout);
96 }
97 
98 /***************************************************************************
99   When cancel in qtpushbutton pressed selfdestruction :D.
100 ***************************************************************************/
slot_cancel_button_pressed()101 void tax_rates_dialog::slot_cancel_button_pressed()
102 {
103   delete this;
104 }
105 
106 /***************************************************************************
107   When ok in qpushbutton pressed send info to server and selfdestroy :D.
108 ***************************************************************************/
slot_ok_button_pressed()109 void tax_rates_dialog::slot_ok_button_pressed()
110 {
111   dsend_packet_player_rates(&client.conn, 10 * fcde->current_min,
112                             10 * (10 - fcde->current_max),
113                             10 * (fcde->current_max - fcde->current_min));
114   delete this;
115 }
116 
117 /***************************************************************************
118   Pressed "apply" in tax rates dialog.
119 ***************************************************************************/
slot_apply_button_pressed()120 void tax_rates_dialog::slot_apply_button_pressed()
121 {
122   dsend_packet_player_rates(&client.conn, 10 * fcde->current_min,
123                             10 * (10 - fcde->current_max),
124                             10 * (fcde->current_max - fcde->current_min));
125 }
126 
127 /**************************************************************************
128   Multipler rates dialog constructor
129   Inheriting from qfc_dialog will cause crash in Qt5.2
130 **************************************************************************/
multipler_rates_dialog(QWidget * parent)131 multipler_rates_dialog::multipler_rates_dialog(QWidget *parent)
132   : QDialog(parent)
133 {
134   QGroupBox *group_box;
135   QHBoxLayout *some_layout;
136   QLabel *label;
137   QSlider *slider;
138   QVBoxLayout *main_layout;
139   struct player *pplayer = client_player();
140 
141   cancel_button = new QPushButton;
142   ok_button = new QPushButton;
143   setWindowTitle(_("Change policies"));
144   main_layout = new QVBoxLayout;
145 
146   multipliers_iterate(pmul) {
147     QHBoxLayout *hb = new QHBoxLayout;
148     int val = player_multiplier_target_value(pplayer, pmul);
149 
150     group_box = new QGroupBox(multiplier_name_translation(pmul));
151     slider = new QSlider(Qt::Horizontal, this);
152     slider->setMinimum(mult_to_scale(pmul, pmul->start));
153     slider->setMaximum(mult_to_scale(pmul, pmul->stop));
154     slider->setValue(mult_to_scale(pmul, val));
155     connect(slider, &QAbstractSlider::valueChanged,
156             this, &multipler_rates_dialog::slot_set_value);
157     slider_list.append(slider);
158     label = new QLabel(QString::number(mult_to_scale(pmul, val)));
159     hb->addWidget(slider);
160     hb->addWidget(label);
161     group_box->setLayout(hb);
162     slider->setProperty("lab", QVariant::fromValue((void *) label));
163     main_layout->addWidget(group_box);
164 
165   } multipliers_iterate_end;
166   some_layout = new QHBoxLayout;
167   cancel_button->setText(_("Cancel"));
168   ok_button->setText(_("Ok"));
169   connect(cancel_button, &QAbstractButton::pressed,
170           this, &multipler_rates_dialog::slot_cancel_button_pressed);
171   connect(ok_button, &QAbstractButton::pressed,
172           this, &multipler_rates_dialog::slot_ok_button_pressed);
173   some_layout->addWidget(cancel_button);
174   some_layout->addWidget(ok_button);
175   main_layout->addSpacing(20);
176   main_layout->addLayout(some_layout);
177   setLayout(main_layout);
178 }
179 
180 /**************************************************************************
181   Slider value changed
182 **************************************************************************/
slot_set_value(int i)183 void multipler_rates_dialog::slot_set_value(int i)
184 {
185   QSlider *qo;
186   qo = (QSlider *) QObject::sender();
187   QVariant qvar;
188   QLabel *lab;
189 
190   qvar = qo->property("lab");
191   lab =  reinterpret_cast<QLabel *>(qvar.value<void *>());
192   lab->setText(QString::number(qo->value()));
193 }
194 
195 
196 /***************************************************************************
197   Cancel pressed
198 ***************************************************************************/
slot_cancel_button_pressed()199 void multipler_rates_dialog::slot_cancel_button_pressed()
200 {
201   close();
202   deleteLater();
203 }
204 
205 /***************************************************************************
206   Ok pressed - send mulipliers value.
207 ***************************************************************************/
slot_ok_button_pressed()208 void multipler_rates_dialog::slot_ok_button_pressed()
209 {
210   int j = 0;
211   int value;
212   struct packet_player_multiplier mul;
213 
214   multipliers_iterate(pmul) {
215     Multiplier_type_id i = multiplier_index(pmul);
216     value = slider_list.at(j)->value();
217     mul.multipliers[i] = scale_to_mult(pmul, value);
218     j++;
219   } multipliers_iterate_end;
220   mul.count = multiplier_count();
221   send_packet_player_multiplier(&client.conn, &mul);
222   close();
223   deleteLater();
224 }
225 
226 
227 /**************************************************************************
228   Convert real multiplier display value to scale value
229 **************************************************************************/
mult_to_scale(const struct multiplier * pmul,int val)230 int mult_to_scale(const struct multiplier *pmul, int val)
231 {
232   return (val - pmul->start) / pmul->step;
233 }
234 
235 /**************************************************************************
236   Convert scale units to real multiplier display value
237 **************************************************************************/
scale_to_mult(const struct multiplier * pmul,int scale)238 int scale_to_mult(const struct multiplier *pmul, int scale)
239 {
240   return scale * pmul->step + pmul->start;
241 }
242 
243 /**************************************************************************
244   Popup (or raise) the (tax/science/luxury) rates selection dialog.
245 **************************************************************************/
popup_rates_dialog(void)246 void popup_rates_dialog(void)
247 {
248   QPoint p;
249   QRect rect;
250 
251   p = QCursor::pos();
252   rect = QApplication::primaryScreen()->availableGeometry();
253   tax_rates_dialog *trd = new tax_rates_dialog(gui()->central_wdg);
254   p.setY(p.y() - trd->height() / 2);
255   if (p.y() < 50) {
256     p.setY(50);
257   }
258   if (p.y() + trd->height() > rect.bottom()) {
259     p.setY(rect.bottom() - trd->height());
260   }
261   if (p.x() + trd->width() > rect.right()) {
262     p.setX(rect.right() - trd->width());
263   }
264   trd->move(p);
265   trd->show();
266 }
267 
268 /**************************************************************************
269   Update multipliers (policies) dialog.
270 **************************************************************************/
real_multipliers_dialog_update(void * unused)271 void real_multipliers_dialog_update(void *unused)
272 {
273   /* PORTME */
274 }
275 
276 /**************************************************************************
277   Popups multiplier dialog
278 **************************************************************************/
popup_multiplier_dialog(void)279 void popup_multiplier_dialog(void)
280 {
281   multipler_rates_dialog *mrd;
282   if (!can_client_issue_orders()) {
283     return;
284   }
285   mrd = new multipler_rates_dialog(gui()->central_wdg);
286   mrd->show();
287 }
288 
289 /**************************************************************************
290   Double edged slider constructor
291 **************************************************************************/
fc_double_edge(QWidget * parent)292 fc_double_edge::fc_double_edge(QWidget *parent)
293   : QWidget(parent)
294 {
295 
296   current_min = client.conn.playing->economic.tax / 10;
297   current_max = 10 - (client.conn.playing->economic.luxury / 10);
298   mouse_x = 0.;
299   moved = 0;
300   on_min = false;
301   on_max = false;
302 
303   if (client.conn.playing !=  nullptr) {
304     max_rates = get_player_bonus(client.conn.playing, EFT_MAX_RATES) / 10;
305   } else {
306     max_rates = 10;
307   }
308   cursor_pix = *fc_icons::instance()->get_pixmap("control");
309   setMouseTracking(true);
310 }
311 
312 /**************************************************************************
313   Double edged slider destructor
314 **************************************************************************/
~fc_double_edge()315 fc_double_edge::~fc_double_edge()
316 {
317 }
318 
319 /**************************************************************************
320   Default size for double edge slider
321 **************************************************************************/
sizeHint() const322 QSize fc_double_edge::sizeHint() const
323 {
324   return QSize(30 * get_tax_sprite(tileset, O_LUXURY)->pm->width(),
325                3 * get_tax_sprite(tileset, O_LUXURY)->pm->height());
326 }
327 
328 /**************************************************************************
329   Double edge paint event
330 **************************************************************************/
paintEvent(QPaintEvent * event)331 void fc_double_edge::paintEvent(QPaintEvent *event)
332 {
333   QPainter p;
334   int i, j, pos;
335   QPixmap *pix;
336   QPixmap pix_scaled;
337   QSize s;
338   double x_min,  x_max;
339 
340   cursor_pix = cursor_pix.scaled(width() / 20,  height());
341   cursor_size = cursor_pix.width();
342   p.begin(this);
343   p.setRenderHint(QPainter::TextAntialiasing);
344   p.setBrush(Qt::SolidPattern);
345   p.setPen(Qt::SolidLine);
346 
347   x_min = static_cast<double>(current_min) / 10 *
348           ((width() - 1)  - 2 * cursor_size) + cursor_size;
349   x_max = static_cast<double>(current_max) / 10 *
350           ((width() - 1) - 2 * cursor_size) + cursor_size;
351 
352   pos = cursor_size;
353   pix = get_tax_sprite(tileset, O_GOLD)->pm;
354   s.setWidth((width() - 2 * cursor_size) / 10);
355   s.setHeight(height());
356   pix_scaled = pix->scaled(s, Qt::IgnoreAspectRatio,
357                            Qt::SmoothTransformation);
358   for (i = 0; i < current_min; i++) {
359     p.drawPixmap(pos, 0, pix_scaled);
360     pos = pos + pix_scaled.width();
361   }
362   j = i;
363   pix = get_tax_sprite(tileset, O_SCIENCE)->pm;
364   pix_scaled = pix->scaled(s, Qt::IgnoreAspectRatio,
365                            Qt::SmoothTransformation);
366   for (i = j; i < current_max; i++) {
367     p.drawPixmap(pos, 0, pix_scaled);
368     pos = pos + pix_scaled.width();
369   }
370   j = i;
371   pix = get_tax_sprite(tileset, O_LUXURY)->pm;
372   pix_scaled = pix->scaled(s, Qt::IgnoreAspectRatio,
373                            Qt::SmoothTransformation);
374   for (i = j; i < 10; i++) {
375     p.drawPixmap(pos, 0, pix_scaled);
376     pos = pos + pix_scaled.width();
377   }
378   p.drawPixmap(x_max - cursor_size / 2, 0,  cursor_pix);
379   p.drawPixmap(x_min - cursor_size / 2, 0,  cursor_pix);
380   p.end();
381 }
382 
383 /**************************************************************************
384   Double edged slider mouse press event
385 **************************************************************************/
mousePressEvent(QMouseEvent * event)386 void fc_double_edge::mousePressEvent(QMouseEvent *event)
387 {
388   if (event->buttons() & Qt::LeftButton) {
389     mouse_x = static_cast<double>(event->x());
390 
391     if (mouse_x <= current_max * width() / 10 - 2 * cursor_size) {
392       moved = 1;
393     } else {
394       moved = 2;
395     }
396   } else {
397     moved = 0;
398   }
399   mouseMoveEvent(event);
400   update();
401 }
402 
403 /**************************************************************************
404   Double edged slider mouse move event
405 **************************************************************************/
mouseMoveEvent(QMouseEvent * event)406 void fc_double_edge::mouseMoveEvent(QMouseEvent *event)
407 {
408   float x_min, x_max, x_mouse;
409   if (on_max || on_min) {
410     setCursor(Qt::SizeHorCursor);
411   } else {
412     setCursor(Qt::ArrowCursor);
413   }
414 
415   x_mouse = static_cast<float>(event->x());
416   x_min = static_cast<float>(current_min) / 10 *
417           ((width() - 1)  - 2 * cursor_size) + cursor_size;
418   x_max = static_cast<float>(current_max) / 10 *
419           ((width() - 1) - 2 * cursor_size) + cursor_size;
420 
421   on_min = (((x_mouse > (x_min - cursor_size * 1.1)) &&
422              (x_mouse < (x_min + cursor_size * 1.1)))
423             && (!on_max))
424            || (moved == 1);
425   on_max = (((x_mouse > (x_max - cursor_size * 1.1)) &&
426              (x_mouse < (x_max + cursor_size * 1.1)))
427             && !on_min)
428            || (moved == 2);
429   if (event->buttons() & Qt::LeftButton) {
430     if ((moved != 2) && on_min) {
431       x_min = x_mouse * width() /
432               ((width() - 1)  - 2 * cursor_size) - cursor_size;
433       if (x_min < 0) x_min = 0;
434       if (x_min > width()) x_min = width();
435       current_min = (x_min * 10 / (width() - 1));
436       if (current_min > max_rates) {
437         current_min = max_rates;
438       }
439       if (current_max < current_min) {
440         current_max = current_min;
441       }
442       if (current_max - current_min > max_rates) {
443         current_min = current_max - max_rates;
444       }
445       moved = 1;
446     } else if ((moved != 1) && on_max) {
447       x_max = x_mouse * width() /
448               ((width() - 1)  - 2 * cursor_size) - cursor_size;
449       if (x_max < 0) x_max = 0;
450       if (x_max > width()) x_max = width();
451       current_max = (x_max * 10 / (width() - 1));
452       if (current_max > max_rates + current_min) {
453         current_max = max_rates + current_min;
454       }
455       if (current_max < 10 - max_rates) {
456         current_max = 10 - max_rates;
457       }
458       if (current_min > current_max)
459         current_min = current_max;
460       moved = 2;
461     }
462     update();
463   } else {
464     moved = 0;
465   }
466 
467   mouse_x = x_mouse;
468 }
469 
470