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 <QGraphicsDropShadowEffect>
21 #include <QGroupBox>
22 #include <QProgressBar>
23 #include <QPushButton>
24 #include <QScreen>
25 #include <QScrollArea>
26 #include <QSplitter>
27 #include <QStack>
28 #include <QStringList>
29 #include <QTextBrowser>
30 #include <QTreeWidget>
31 #include <QVBoxLayout>
32 
33 // utility
34 #include "fcintl.h"
35 
36 // common
37 #include "movement.h"
38 #include "nation.h"
39 #include "specialist.h"
40 #include "terrain.h"
41 #include "unit.h"
42 
43 // client
44 #include "helpdata.h"
45 
46 // gui-qt
47 #include "fc_client.h"
48 #include "fonts.h"
49 #include "helpdlg.h"
50 #include "sprite.h"
51 
52 #define MAX_HELP_TEXT_SIZE 8192
53 #define REQ_LABEL_NEVER _("(Never)")
54 #define REQ_LABEL_NONE _("?tech:None")
55 static help_dialog *help_dlg = NULL;
56 canvas *terrain_canvas(struct terrain *terrain,
57                        const struct resource *resource = NULL,
58                        enum extra_cause cause = EC_COUNT);
59 /**************************************************************************
60   Popup the help dialog to get help on the given string topic.  Note
61   that the topic may appear in multiple sections of the help (it may
62   be both an improvement and a unit, for example).
63 
64   The given string should be untranslated.
65 **************************************************************************/
popup_help_dialog_string(const char * item)66 void popup_help_dialog_string(const char *item)
67 {
68   popup_help_dialog_typed(Q_(item), HELP_ANY);
69 }
70 
71 /**************************************************************************
72   Popup the help dialog to display help on the given string topic from
73   the given section.
74 
75   The string will be translated.
76 **************************************************************************/
popup_help_dialog_typed(const char * item,enum help_page_type htype)77 void popup_help_dialog_typed(const char *item, enum help_page_type htype)
78 {
79   int pos;
80   const help_item *topic;
81 
82   if (!help_dlg) {
83     help_dlg = new help_dialog();
84   }
85   topic = get_help_item_spec(item, htype, &pos);
86   if (pos >= 0) {
87     help_dlg->set_topic(topic);
88   }
89   help_dlg->setVisible(true);
90   help_dlg->activateWindow();
91 }
92 
93 /**************************************************************************
94   Close the help dialog.
95 **************************************************************************/
popdown_help_dialog(void)96 void popdown_help_dialog(void)
97 {
98   if (help_dlg) {
99     help_dlg->setVisible(false);
100     help_dlg->deleteLater();
101     help_dlg = NULL;
102   }
103 }
104 
105 /**************************************************************************
106   Updates fonts
107 **************************************************************************/
update_help_fonts()108 void update_help_fonts()
109 {
110   if (help_dlg) {
111     help_dlg->update_fonts();
112   }
113 }
114 
115 /**************************************************************************
116   Constructor for help dialog
117 **************************************************************************/
help_dialog(QWidget * parent)118 help_dialog::help_dialog(QWidget *parent) : qfc_dialog(parent)
119 {
120   QHBoxLayout *hbox;
121   QPushButton *but;
122   QTreeWidgetItem *first;
123   QVBoxLayout *layout;
124   QWidget *buttons;
125 
126   setWindowTitle(_("Freeciv Help Browser"));
127   history_pos = -1;
128   update_history = true;
129   layout = new QVBoxLayout(this);
130 
131   splitter = new QSplitter(this);
132   layout->addWidget(splitter, 10);
133 
134   tree_wdg = new QTreeWidget();
135   tree_wdg->setHeaderHidden(true);
136   make_tree();
137   splitter->addWidget(tree_wdg);
138 
139   help_wdg = new help_widget(splitter);
140   connect(
141     tree_wdg,
142     &QTreeWidget::currentItemChanged,
143     this, &help_dialog::item_changed
144   );
145   help_wdg->layout()->setContentsMargins(0, 0, 0, 0);
146   splitter->addWidget(help_wdg);
147 
148   buttons = new QWidget;
149   hbox = new QHBoxLayout;
150   prev_butt = new QPushButton(style()->standardIcon(
151                               QStyle::QStyle::SP_ArrowLeft), (""));
152   connect(prev_butt, &QAbstractButton::clicked, this,
153           &help_dialog::history_back);
154   hbox->addWidget(prev_butt);
155   next_butt = new QPushButton(style()->standardIcon(
156                               QStyle::QStyle::SP_ArrowRight), (""));
157   connect(next_butt, &QAbstractButton::clicked, this,
158           &help_dialog::history_forward);
159   hbox->addWidget(next_butt);
160   hbox->addStretch(20);
161   but = new QPushButton(style()->standardIcon(
162                         QStyle::SP_DialogHelpButton), _("About Qt"));
163   connect(but, &QPushButton::pressed, &QApplication::aboutQt);
164   hbox->addWidget(but, Qt::AlignRight);
165   but = new QPushButton(style()->standardIcon(
166                         QStyle::SP_DialogDiscardButton), _("Close"));
167   connect(but, &QPushButton::pressed, this, &QWidget::close);
168   hbox->addWidget(but, Qt::AlignRight);
169   buttons->setLayout(hbox);
170   layout->addWidget(buttons, 0, Qt::AlignBottom);
171 
172 
173   first = tree_wdg->topLevelItem(0);
174   if (first) {
175     tree_wdg->setCurrentItem(first);
176   }
177 }
178 
179 /**************************************************************************
180   Update fonts for help_wdg
181 **************************************************************************/
update_fonts()182 void help_dialog::update_fonts()
183 {
184   help_wdg->update_fonts();
185 }
186 
187 /****************************************************************************
188   Hide event
189 ****************************************************************************/
hideEvent(QHideEvent * event)190 void help_dialog::hideEvent(QHideEvent *event)
191 {
192   gui()->qt_settings.help_geometry = saveGeometry();
193   gui()->qt_settings.help_splitter1 = splitter->saveState();
194 }
195 
196 /****************************************************************************
197   Show event
198 ****************************************************************************/
showEvent(QShowEvent * event)199 void help_dialog::showEvent(QShowEvent *event)
200 {
201   QList<int> sizes;
202 
203   if (!gui()->qt_settings.help_geometry.isNull()) {
204     restoreGeometry(gui()->qt_settings.help_geometry);
205     splitter->restoreState(gui()->qt_settings.help_splitter1);
206   } else {
207     QRect rect = QApplication::primaryScreen()->geometry();
208 
209     resize((rect.width() * 3) / 5, (rect.height() * 3) / 6);
210     sizes << rect.width() / 10 << rect.width() / 3;
211     splitter->setSizes(sizes);
212   }
213 }
214 
215 /****************************************************************************
216   Close event
217 ****************************************************************************/
closeEvent(QCloseEvent * event)218 void help_dialog::closeEvent(QCloseEvent *event)
219 {
220   gui()->qt_settings.help_geometry = saveGeometry();
221   gui()->qt_settings.help_splitter1 = splitter->saveState();
222 }
223 
224 
225 /**************************************************************************
226   Create the help tree.
227 **************************************************************************/
make_tree()228 void help_dialog::make_tree()
229 {
230   char *title;
231   int dep;
232   int i;
233   QHash<int, QTreeWidgetItem *> hash;
234   QIcon icon;
235   QTreeWidgetItem *item;
236   sprite *spite;
237   struct advance *padvance;
238   struct canvas *pcan;
239   struct extra_type *pextra;
240   struct government *gov;
241   struct impr_type *imp;
242   struct nation_type *nation;
243   struct terrain *pterrain;
244   struct unit_type *f_type;
245   struct drawn_sprite sprs[80];
246 
247   help_items_iterate(pitem) {
248     const char *s;
249     int last;
250     title = pitem->topic;
251 
252     for (s = pitem->topic; *s == ' '; s++) {
253       /* nothing */
254     }
255 
256     item = new QTreeWidgetItem(QStringList(title));
257     topics_map[item] = pitem;
258     dep = s - pitem->topic;
259     hash.insert(dep, item);
260 
261     if (dep == 0) {
262       tree_wdg->addTopLevelItem(item);
263     } else {
264       last = dep - 1;
265       spite = nullptr;
266 
267       switch (pitem->type) {
268       case HELP_EXTRA:
269         pextra = extra_type_by_translated_name(s);
270         fill_basic_extra_sprite_array(tileset, sprs, pextra);
271         icon = QIcon(*sprs->sprite->pm);
272         break;
273 
274       case HELP_GOVERNMENT:
275         gov = government_by_translated_name(s);
276         spite = get_government_sprite(tileset, gov);
277         if (spite) {
278           icon = QIcon(*spite->pm);
279         }
280         break;
281 
282       case HELP_IMPROVEMENT:
283       case HELP_WONDER:
284         imp = improvement_by_translated_name(s);
285         spite = get_building_sprite(tileset, imp);
286         if (spite) {
287           icon = QIcon(*spite->pm);
288         }
289         break;
290       case HELP_NATIONS:
291         nation = nation_by_translated_plural(s);
292         spite = get_nation_flag_sprite(tileset, nation);
293         if (spite) {
294             icon = QIcon(*spite->pm);
295         }
296         break;
297       case HELP_TECH:
298         padvance  = advance_by_translated_name(s);
299         if (padvance && !is_future_tech(i = advance_number(padvance))) {
300           spite = get_tech_sprite(tileset, i);
301           if (spite) {
302             icon = QIcon(*spite->pm);
303           }
304         }
305         break;
306 
307       case HELP_TERRAIN:
308         pterrain = terrain_by_translated_name(s);
309         pcan = terrain_canvas(pterrain);
310         if (pcan) {
311           icon = QIcon(pcan->map_pixmap);
312           delete pcan;
313         }
314         break;
315 
316       case HELP_UNIT:
317         f_type = unit_type_by_translated_name(s);
318         if (f_type) {
319           spite = get_unittype_sprite(tileset, f_type, direction8_invalid(),
320                                       true);
321         }
322         if (spite) {
323           icon = QIcon(*spite->pm);
324         }
325         break;
326 
327       default:
328         break;
329       }
330 
331       if (!icon.isNull()) {
332         item->setIcon(0, icon);
333       }
334 
335       hash.value(last)->addChild(item);
336     }
337   } help_items_iterate_end;
338 }
339 
340 /**************************************************************************
341   Changes the displayed topic.
342 **************************************************************************/
set_topic(const help_item * topic)343 void help_dialog::set_topic(const help_item *topic)
344 {
345   help_wdg->set_topic(topic);
346   // Reverse search of the item to select.
347   QHash<QTreeWidgetItem *, const help_item *>::const_iterator
348       i = topics_map.cbegin();
349   for ( ; i != topics_map.cend(); ++i) {
350     if (i.value() == topic) {
351       tree_wdg->setCurrentItem(i.key());
352       break;
353     }
354   }
355 }
356 
357 /**************************************************************************
358   Goes to next topic in history
359 **************************************************************************/
history_forward()360 void help_dialog::history_forward()
361 {
362   QTreeWidgetItem *i;
363 
364   update_history = false;
365   if (history_pos < item_history.count()) {
366     history_pos++;
367   }
368   i = item_history.value(history_pos);
369   if (i != nullptr) {
370     tree_wdg->setCurrentItem(i);
371   }
372 }
373 
374 /**************************************************************************
375   Backs in history to previous topic
376 **************************************************************************/
history_back()377 void help_dialog::history_back()
378 {
379   QTreeWidgetItem *i;
380 
381   update_history = false;
382   if (history_pos > 0) {
383     history_pos--;
384   }
385   i = item_history.value(history_pos);
386   if (i != nullptr) {
387     tree_wdg->setCurrentItem(i);
388   }
389 }
390 
391 /**************************************************************************
392   Update buttons (back and next)
393 **************************************************************************/
update_buttons()394 void help_dialog::update_buttons()
395 {
396   if (history_pos == 0) {
397     prev_butt->setEnabled(false);
398   } else {
399     prev_butt->setEnabled(true);
400   }
401   if (history_pos >= item_history.size() - 1) {
402     next_butt->setEnabled(false);
403   } else {
404     next_butt->setEnabled(true);
405   }
406 }
407 
408 /**************************************************************************
409   Called when a tree item is activated.
410 **************************************************************************/
item_changed(QTreeWidgetItem * item,QTreeWidgetItem * prev)411 void help_dialog::item_changed(QTreeWidgetItem *item, QTreeWidgetItem *prev)
412 {
413 
414   if (prev == item) {
415     return;
416   }
417 
418   help_wdg->set_topic(topics_map[item]);
419 
420   if (update_history) {
421     history_pos++;
422     item_history.append(item);
423   } else {
424     update_history = true;
425   }
426   update_buttons();
427 }
428 
429 /**************************************************************************
430   Creates a new, empty help widget.
431 **************************************************************************/
help_widget(QWidget * parent)432 help_widget::help_widget(QWidget *parent) :
433   QWidget(parent),
434   main_widget(NULL), text_browser(NULL), bottom_panel(NULL),
435   info_panel(NULL), splitter(NULL), info_layout(NULL)
436 {
437   setup_ui();
438 }
439 
440 /**************************************************************************
441   Creates a new help widget displaying the specified topic.
442 **************************************************************************/
help_widget(const help_item * topic,QWidget * parent)443 help_widget::help_widget(const help_item *topic, QWidget *parent) :
444   QWidget(parent),
445   main_widget(NULL), text_browser(NULL), bottom_panel(NULL),
446   info_panel(NULL), splitter(NULL), info_layout(NULL)
447 {
448   setup_ui();
449   set_topic(topic);
450 }
451 
452 /**************************************************************************
453   Destructor.
454 **************************************************************************/
~help_widget()455 help_widget::~help_widget() {
456   // Nothing to do here
457 }
458 
459 /****************************************************************************
460   Creates the UI.
461 ****************************************************************************/
setup_ui()462 void help_widget::setup_ui()
463 {
464   QVBoxLayout *layout;
465   QHBoxLayout *group_layout;
466 
467   layout = new QVBoxLayout();
468   setLayout(layout);
469 
470   box_wdg = new QFrame(this);
471   layout->addWidget(box_wdg);
472   group_layout = new QHBoxLayout(box_wdg);
473   box_wdg->setLayout(group_layout);
474   box_wdg->setFrameShape(QFrame::StyledPanel);
475   box_wdg->setFrameShadow(QFrame::Raised);
476 
477   title_label = new QLabel(box_wdg);
478   title_label->setProperty(fonts::help_title, "true");
479   group_layout->addWidget(title_label);
480 
481   text_browser = new QTextBrowser(this);
482   text_browser->setProperty(fonts::help_text, "true");
483   layout->addWidget(text_browser);
484   main_widget = text_browser;
485 
486   update_fonts();
487   splitter_sizes << 200 << 400;
488 }
489 
490 /****************************************************************************
491   Lays things out. The widget is organized as follows, with the additional
492   complexity that info_ and/or bottom_panel may be absent.
493 
494     +---------------------------------+
495     | title_label                     |
496     +---------------------------------+
497     |+-main_widget-------------------+|
498     ||+------------+ +--------------+||
499     |||            | |              |||
500     ||| info_panel | | text_browser |||
501     |||            | |              |||
502     |||            |.+--------------+||
503     |||            |.|              |||
504     |||            |.| bottom_panel |||
505     |||            | |              |||
506     |||            | +--------------+||
507     |||            | |   (spacer)   |||
508     ||+------------+ +--------------+||
509     |+-------------------------------+|
510     +---------------------------------+
511 ****************************************************************************/
do_layout()512 void help_widget::do_layout()
513 {
514   QWidget *right;
515   QVBoxLayout *vbox;
516 
517   layout()->removeWidget(main_widget);
518   main_widget->setParent(NULL);
519 
520   if (bottom_panel) {
521     right = new QWidget();
522     vbox = new QVBoxLayout(right);
523     vbox->setMargin(0);
524     vbox->addWidget(text_browser, 100);
525     vbox->addWidget(bottom_panel);
526 //     vbox->addStretch(100);
527   } else {
528     right = text_browser;
529   }
530 
531   if (info_panel) {
532     splitter = new QSplitter();
533     splitter->addWidget(info_panel);
534     splitter->setStretchFactor(0, 25);
535     splitter->addWidget(right);
536     splitter->setStretchFactor(1, 75);
537     splitter->setSizes(splitter_sizes);
538     main_widget = splitter;
539     info_panel->setLayout(info_layout);
540   } else {
541     main_widget = right;
542   }
543 
544   layout()->addWidget(main_widget);
545   qobject_cast<QVBoxLayout *>(layout())->setStretchFactor(main_widget, 100);
546 }
547 
548 /**************************************************************************
549   Updates fonts for manual
550 **************************************************************************/
update_fonts()551 void help_widget::update_fonts()
552 {
553   QList<QWidget *> l;
554   QFont *f;
555 
556   l = findChildren<QWidget *>();
557 
558   f = fc_font::instance()->get_font(fonts::help_label);
559   for (int i = 0; i < l.size(); ++i) {
560     if (l.at(i)->property(fonts::help_label).isValid()) {
561       l.at(i)->setFont(*f);
562     }
563   }
564   f = fc_font::instance()->get_font(fonts::help_text);
565   for (int i = 0; i < l.size(); ++i) {
566     if (l.at(i)->property(fonts::help_text).isValid()) {
567       l.at(i)->setFont(*f);
568     }
569   }
570   f = fc_font::instance()->get_font(fonts::help_title);
571   for (int i = 0; i < l.size(); ++i) {
572     if (l.at(i)->property(fonts::help_title).isValid()) {
573       l.at(i)->setFont(*f);
574     }
575   }
576 }
577 
578 /****************************************************************************
579   Deletes the widgets created by do_complex_layout().
580 ****************************************************************************/
undo_layout()581 void help_widget::undo_layout()
582 {
583   // Save the splitter sizes to avoid jumps
584   if (info_panel) {
585     splitter_sizes = splitter->sizes();
586   }
587   // Unparent the widget we want to keep
588   text_browser->setParent(NULL);
589   // Delete everything else
590   if (text_browser != main_widget) {
591     main_widget->deleteLater();
592   }
593   // Reset pointers to defaults
594   main_widget = text_browser;
595   bottom_panel = NULL;
596   info_panel = NULL;
597   splitter = NULL;
598   info_layout = NULL;
599 }
600 
601 /****************************************************************************
602   Creates the information panel. It will be shown by do_complex_layout().
603 ****************************************************************************/
show_info_panel()604 void help_widget::show_info_panel()
605 {
606   info_panel = new QWidget();
607   info_layout = new QVBoxLayout();
608 }
609 
610 /****************************************************************************
611   Adds a pixmap to the information panel.
612 ****************************************************************************/
add_info_pixmap(QPixmap * pm,bool shadow)613 void help_widget::add_info_pixmap(QPixmap *pm, bool shadow)
614 {
615   QLabel *label = new QLabel();
616   QGraphicsDropShadowEffect *effect;
617 
618   label->setAlignment(Qt::AlignHCenter);
619   label->setPixmap(*pm);
620 
621   if (shadow) {
622     effect = new QGraphicsDropShadowEffect(label);
623     effect->setBlurRadius(3);
624     effect->setOffset(0, 2);
625     label->setGraphicsEffect(effect);
626   }
627 
628   info_layout->addWidget(label);
629 }
630 
631 /****************************************************************************
632   Adds a text label to the information panel.
633 ****************************************************************************/
add_info_label(const QString & text)634 void help_widget::add_info_label(const QString &text)
635 {
636   QLabel *label = new QLabel(text);
637   label->setWordWrap(true);
638   label->setTextFormat(Qt::RichText);
639   label->setProperty(fonts::help_label, "true");
640   info_layout->addWidget(label);
641 }
642 
643 /**************************************************************************
644   Adds a widget indicating a progress to the information panel.
645   Arguments:
646     text: A descriptive text
647     progress: The progress to display
648     [min,max]: The interval progress is in
649     value: Use this to display a non-numeral value
650 **************************************************************************/
add_info_progress(const QString & text,int progress,int min,int max,const QString & value)651 void help_widget::add_info_progress(const QString &text, int progress,
652                                     int min, int max, const QString &value)
653 {
654   QGridLayout *layout;
655   QLabel *label;
656   QProgressBar *bar;
657   QWidget *wdg;
658 
659   wdg = new QWidget();
660   layout = new QGridLayout(wdg);
661   layout->setMargin(0);
662   layout->setVerticalSpacing(0);
663 
664   label = new QLabel(text, wdg);
665   layout->addWidget(label, 0, 0);
666   label->setProperty(fonts::help_label, "true");
667   label = new QLabel(wdg);
668   if (value.isEmpty()) {
669     label->setNum(progress);
670   } else {
671     label->setText(value);
672   }
673   label->setProperty(fonts::help_label, "true");
674   layout->addWidget(label, 0, 1, Qt::AlignRight);
675 
676   bar = new QProgressBar(wdg);
677   bar->setMaximumHeight(4);
678   bar->setRange(min, max != min ? max : min + 1);
679   bar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
680   bar->setTextVisible(false);
681   bar->setValue(progress);
682   layout->addWidget(bar, 1, 0, 1, 2);
683 
684   info_layout->addWidget(wdg);
685 }
686 
687 /**************************************************************************
688   Create labels about all extras of one cause buildable to the terrain.
689 **************************************************************************/
add_extras_of_act_for_terrain(struct terrain * pterr,enum unit_activity act,const char * label)690 void help_widget::add_extras_of_act_for_terrain(struct terrain *pterr,
691                                                 enum unit_activity act,
692                                                 const char *label)
693 {
694   struct universal for_terr;
695   enum extra_cause cause = activity_to_extra_cause(act);
696 
697   for_terr.kind = VUT_TERRAIN;
698   for_terr.value.terrain = pterr;
699 
700   extra_type_by_cause_iterate(cause, pextra) {
701     if (pextra->buildable
702         && universal_fulfills_requirements(FALSE, &(pextra->reqs),
703                                            &for_terr)) {
704       QLabel *tb;
705       QString str;
706 
707       tb = new QLabel(this);
708       tb->setProperty(fonts::help_label, "true");
709       tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
710       tb->setTextFormat(Qt::RichText);
711 
712       str = str + QString(label)
713             + link_me(extra_name_translation(pextra), HELP_EXTRA)
714             + QString(helptext_extra_for_terrain_str(pextra, pterr, act))
715               .toHtmlEscaped()
716             + "\n";
717       tb->setText(str.trimmed());
718       connect(tb, &QLabel::linkActivated,
719               this, &help_widget::anchor_clicked);
720       info_layout->addWidget(tb);
721     }
722   } extra_type_by_cause_iterate_end;
723 }
724 
725 /****************************************************************************
726   Creates link to given help page
727 ****************************************************************************/
link_me(const char * str,help_page_type hpt)728 QString help_widget::link_me(const char *str, help_page_type hpt)
729 {
730   QString s;
731   s = QString(str).toHtmlEscaped().replace(" ", "&nbsp;");
732   return " <a href=" + QString::number(hpt)
733             + "," + s + ">" + s + "</a> ";
734 }
735 
736 /****************************************************************************
737   Adds a separator to the information panel.
738 ****************************************************************************/
add_info_separator()739 void help_widget::add_info_separator()
740 {
741   info_layout->addSpacing(2 * info_layout->spacing());
742 }
743 
744 /****************************************************************************
745   Called when everything needed has been added to the information panel.
746 ****************************************************************************/
info_panel_done()747 void help_widget::info_panel_done()
748 {
749   info_layout->addStretch();
750 }
751 
752 /****************************************************************************
753   Hyperlink clicked, link has 2 variables, string(name of given help)
754   and int(help_page_type)
755 ****************************************************************************/
anchor_clicked(const QString & link)756 void help_widget::anchor_clicked(const QString &link)
757 {
758   QStringList sl;
759   int n;
760   QString st;
761   enum help_page_type type;
762 
763   sl = link.split(",");
764   n = sl.at(0).toInt();
765   type = static_cast<help_page_type>(n);
766   st = sl.at(1);
767   st = st.replace("\u00A0", " ");
768 
769   if (strcmp(qPrintable(st), REQ_LABEL_NEVER) != 0
770       && strcmp(qPrintable(st),
771                 skip_intl_qualifier_prefix(REQ_LABEL_NONE)) != 0
772       && strcmp(qPrintable(st),
773                 advance_name_translation(advance_by_number(A_NONE))) != 0) {
774     popup_help_dialog_typed(qPrintable(st), type);
775   }
776 }
777 
778 
779 /**************************************************************************
780   Shows the given help page.
781 **************************************************************************/
set_topic(const help_item * topic)782 void help_widget::set_topic(const help_item *topic)
783 {
784   char *title = topic->topic;
785   for ( ; *title == ' '; ++title) {
786     // Do nothing
787   }
788   title_label->setTextFormat(Qt::PlainText);
789   title_label->setText(title);
790 
791   undo_layout();
792 
793   switch (topic->type) {
794     case HELP_ANY:
795     case HELP_MULTIPLIER:
796     case HELP_RULESET:
797     case HELP_TILESET:
798     case HELP_TEXT:
799       set_topic_other(topic, title);
800       break;
801     case HELP_EXTRA:
802       set_topic_extra(topic, title);
803       break;
804     case HELP_GOVERNMENT:
805       set_topic_government(topic, title);
806       break;
807     case HELP_IMPROVEMENT:
808     case HELP_WONDER:
809       set_topic_building(topic, title);
810       break;
811     case HELP_NATIONS:
812       set_topic_nation(topic, title);
813       break;
814     case HELP_SPECIALIST:
815       set_topic_specialist(topic, title);
816       break;
817     case HELP_TECH:
818       set_topic_tech(topic, title);
819       break;
820     case HELP_TERRAIN:
821       set_topic_terrain(topic, title);
822       break;
823     case HELP_UNIT:
824       set_topic_unit(topic, title);
825       break;
826     case HELP_LAST: // Just to avoid warning
827       break;
828   }
829 
830   do_layout();
831 }
832 
833 /****************************************************************************
834   Sets the bottom panel.
835 ****************************************************************************/
set_bottom_panel(QWidget * widget)836 void help_widget::set_bottom_panel(QWidget *widget) {
837   bottom_panel = widget;
838 }
839 
840 /**************************************************************************
841   Creates help pages with no special widgets.
842 **************************************************************************/
set_topic_other(const help_item * topic,const char * title)843 void help_widget::set_topic_other(const help_item *topic,
844                                     const char *title)
845 {
846   if (topic->text) {
847     text_browser->setPlainText(topic->text);
848   } else {
849     text_browser->setPlainText(""); // Something better to do ?
850   }
851 }
852 
853 /**************************************************************************
854   Creates unit help pages.
855 **************************************************************************/
set_topic_unit(const help_item * topic,const char * title)856 void help_widget::set_topic_unit(const help_item *topic,
857                                    const char *title)
858 {
859   char buffer[MAX_HELP_TEXT_SIZE];
860   int upkeep, max_upkeep;
861   struct advance *tech;
862   struct canvas *canvas;
863   struct unit_type *obsolete, *utype, *max_utype;
864   QList<int> list;
865   QString str;
866 
867   utype = unit_type_by_translated_name(title);
868   if (utype) {
869     helptext_unit(buffer, sizeof(buffer), client.conn.playing,
870                   topic->text, utype);
871     text_browser->setPlainText(buffer);
872 
873     // Create information panel
874     show_info_panel();
875     max_utype = uclass_max_values(utype->uclass);
876 
877     // Unit icon
878     canvas = qtg_canvas_create(
879                tileset_full_tile_width(tileset),
880                tileset_full_tile_height(tileset)
881              );
882     canvas->map_pixmap.fill(Qt::transparent);
883     put_unittype(utype, canvas, 1.0f, 0, 0);
884     add_info_pixmap(&canvas->map_pixmap);
885     qtg_canvas_free(canvas);
886 
887     add_info_progress(_("Attack:"), utype->attack_strength, 0,
888                       max_utype->attack_strength);
889     add_info_progress(_("Defense:"), utype->defense_strength,
890                       0, max_utype->defense_strength);
891     add_info_progress(_("Moves:"), utype->move_rate, 0, max_utype->move_rate,
892                       move_points_text(utype->move_rate, true));
893 
894     add_info_separator();
895 
896     add_info_progress(_("Hitpoints:"), utype->hp, 0, max_utype->hp);
897     add_info_progress(_("Cost:"), utype_build_shield_cost(utype),
898                       0, utype_build_shield_cost(max_utype));
899     add_info_progress(_("Firepower:"), utype->firepower, 0,
900                       max_utype->firepower);
901 
902     // Upkeep
903     upkeep = utype->upkeep[O_FOOD] + utype->upkeep[O_GOLD]
904              + utype->upkeep[O_LUXURY] + utype->upkeep[O_SCIENCE]
905              + utype->upkeep[O_SHIELD] + utype->upkeep[O_TRADE]
906              + utype->happy_cost;
907     max_upkeep = max_utype->upkeep[O_FOOD] + max_utype->upkeep[O_GOLD]
908                  + max_utype->upkeep[O_LUXURY] + max_utype->upkeep[O_SCIENCE]
909                  + max_utype->upkeep[O_SHIELD] + max_utype->upkeep[O_TRADE]
910                  + max_utype->happy_cost;
911     add_info_progress(_("Basic upkeep:"), upkeep, 0, max_upkeep,
912                       helptext_unit_upkeep_str(utype));
913 
914     add_info_separator();
915 
916     // Tech requirement
917     tech = utype->require_advance;
918     if (tech && tech != advance_by_number(0)) {
919       QLabel *tb;
920 
921       tb = new QLabel(this);
922       /* TRANS: this and similar literal strings interpreted as (Qt) HTML */
923       str = _("Requires");
924       str = "<b>" + str + "</b> "
925             + link_me(advance_name_translation(tech), HELP_TECH);
926 
927       tb->setProperty(fonts::help_label, "true");
928       tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
929       tb->setTextFormat(Qt::RichText);
930       tb->setText(str.trimmed());
931       connect(tb, &QLabel::linkActivated,
932               this, &help_widget::anchor_clicked);
933       info_layout->addWidget(tb);
934     } else {
935       add_info_label(_("No technology required."));
936     }
937 
938     // Obsolescence
939     obsolete = utype->obsoleted_by;
940     if (obsolete) {
941       tech = obsolete->require_advance;
942       if (tech && tech != advance_by_number(0)) {
943         QLabel *tb;
944 
945         tb = new QLabel(this);
946         str = _("Obsoleted by");
947         str = "<b>" + str + "</b> "
948               + link_me(utype_name_translation(obsolete), HELP_UNIT)
949               + "("  + link_me(advance_name_translation(tech), HELP_TECH)
950               + ")";
951         tb->setProperty(fonts::help_label, "true");
952         tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
953         tb->setTextFormat(Qt::RichText);
954         tb->setText(str.trimmed());
955         connect(tb, &QLabel::linkActivated,
956                 this, &help_widget::anchor_clicked);
957         info_layout->addWidget(tb);
958       } else {
959         add_info_label(
960           // TRANS: Current unit obsoleted by other unit
961           QString(_("Obsoleted by %1."))
962           .arg(utype_name_translation(obsolete))
963           .toHtmlEscaped());
964       }
965     } else {
966       add_info_label(_("Never obsolete."));
967     }
968 
969     info_panel_done();
970 
971     delete max_utype;
972   } else {
973     set_topic_other(topic, title);
974   }
975 }
976 
977 /**************************************************************************
978   Creates improvement help pages.
979 **************************************************************************/
set_topic_building(const help_item * topic,const char * title)980 void help_widget::set_topic_building(const help_item *topic,
981                                      const char *title)
982 {
983   char buffer[MAX_HELP_TEXT_SIZE];
984   int type, value;
985   struct sprite *spr;
986   struct impr_type *itype = improvement_by_translated_name(title);
987   char req_buf[512];
988   QString str, s1, s2;
989   QLabel *tb;
990 
991   if (itype) {
992     helptext_building(buffer, sizeof(buffer), client.conn.playing,
993                       topic->text, itype);
994     text_browser->setPlainText(buffer);
995     show_info_panel();
996     spr = get_building_sprite(tileset, itype);
997     if (spr) {
998       add_info_pixmap(spr->pm);
999     }
1000     str = _("Cost:");
1001     str = "<b>" + str + "</b>" + " "
1002           + QString::number(impr_build_shield_cost(itype)).toHtmlEscaped();
1003     add_info_label(str);
1004     if (!is_great_wonder(itype)) {
1005       str = _("Upkeep:");
1006       str = "<b>" + str + "</b>" + " "
1007             + QString::number(itype->upkeep).toHtmlEscaped();
1008       add_info_label(str);
1009     }
1010 
1011     requirement_vector_iterate(&itype->reqs, preq) {
1012       if (!preq->present) {
1013         continue;
1014       }
1015       universal_extraction(&preq->source, &type, &value);
1016       if (type == VUT_ADVANCE) {
1017         s1 = link_me(universal_name_translation(&preq->source, req_buf,
1018                                        sizeof(req_buf)), HELP_TECH);
1019       } else if (type == VUT_GOVERNMENT) {
1020         s1 = link_me(universal_name_translation(&preq->source, req_buf,
1021                                        sizeof(req_buf)), HELP_GOVERNMENT);
1022       } else if (type == VUT_TERRAIN) {
1023         s1 = link_me(universal_name_translation(&preq->source, req_buf,
1024                                        sizeof(req_buf)), HELP_TERRAIN);
1025       }
1026       break;
1027     } requirement_vector_iterate_end;
1028 
1029     if (!s1.isEmpty()) {
1030       tb = new QLabel(this);
1031       str = _("Requirement:");
1032       str = "<b>" + str + "</b> " + s1;
1033       tb->setProperty(fonts::help_label, "true");
1034       tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
1035       tb->setTextFormat(Qt::RichText);
1036       tb->setText(str.trimmed());
1037       connect(tb, &QLabel::linkActivated,
1038               this, &help_widget::anchor_clicked);
1039       info_layout->addWidget(tb);
1040     }
1041 
1042     requirement_vector_iterate(&itype->obsolete_by, pobs) {
1043       if (pobs->source.kind == VUT_ADVANCE) {
1044         s2 = link_me(advance_name_translation(pobs->source.value.advance),
1045                      HELP_TECH);
1046         break;
1047       }
1048     } requirement_vector_iterate_end;
1049 
1050     str = _("Obsolete by:");
1051     str = "<b>" + str + "</b> " + s2;
1052     if (!s2.isEmpty()) {
1053       tb = new QLabel(this);
1054       tb->setProperty(fonts::help_label, "true");
1055       tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
1056       tb->setTextFormat(Qt::RichText);
1057       tb->setText(str.trimmed());
1058       connect(tb, &QLabel::linkActivated,
1059               this, &help_widget::anchor_clicked);
1060       info_layout->addWidget(tb);
1061     }
1062     info_panel_done();
1063   } else {
1064     set_topic_other(topic, title);
1065   }
1066 }
1067 
1068 /**************************************************************************
1069   Creates technology help pages.
1070 **************************************************************************/
set_topic_tech(const help_item * topic,const char * title)1071 void help_widget::set_topic_tech(const help_item *topic,
1072                                  const char *title)
1073 {
1074   char buffer[MAX_HELP_TEXT_SIZE];
1075   struct sprite *spr;
1076   QLabel *tb;
1077   struct advance *padvance = advance_by_translated_name(title);
1078   QString str;
1079 
1080   if (padvance) {
1081     int n = advance_number(padvance);
1082     if (!is_future_tech(n)) {
1083 
1084       show_info_panel();
1085       spr = get_tech_sprite(tileset, n);
1086       if (spr) {
1087         add_info_pixmap(spr->pm);
1088       }
1089 
1090       governments_iterate(pgov) {
1091         requirement_vector_iterate(&pgov->reqs, preq) {
1092           if (VUT_ADVANCE == preq->source.kind
1093               && preq->source.value.advance == padvance) {
1094             tb = new QLabel(this);
1095             tb->setProperty(fonts::help_label, "true");
1096             tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
1097             tb->setTextFormat(Qt::RichText);
1098             str = _("Allows");
1099             str = "<b>" + str + "</b> "
1100                + link_me(government_name_translation(pgov), HELP_GOVERNMENT);
1101             tb->setText(str.trimmed());
1102             connect(tb, &QLabel::linkActivated,
1103                     this, &help_widget::anchor_clicked);
1104             info_layout->addWidget(tb);
1105           }
1106         } requirement_vector_iterate_end;
1107       } governments_iterate_end;
1108 
1109       improvement_iterate(pimprove) {
1110         if (valid_improvement(pimprove)) {
1111           requirement_vector_iterate(&pimprove->reqs, preq) {
1112             if (VUT_ADVANCE == preq->source.kind
1113                 && preq->source.value.advance == padvance) {
1114               str = _("Allows");
1115               str = "<b>" + str + "</b> "
1116                 + link_me(improvement_name_translation(pimprove),
1117                           is_great_wonder(pimprove) ? HELP_WONDER
1118                           : HELP_IMPROVEMENT);
1119               tb = new QLabel(this);
1120               tb->setProperty(fonts::help_label, "true");
1121               tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
1122               tb->setTextFormat(Qt::RichText);
1123               tb->setText(str.trimmed());
1124               connect(tb, &QLabel::linkActivated,
1125                       this, &help_widget::anchor_clicked);
1126               info_layout->addWidget(tb);
1127             }
1128           } requirement_vector_iterate_end;
1129 
1130           requirement_vector_iterate(&pimprove->obsolete_by, pobs) {
1131             if (pobs->source.kind == VUT_ADVANCE
1132                 && pobs->source.value.advance == padvance) {
1133               str = _("Obsoletes");
1134               str = "<b>" + str + "</b> "
1135                 + link_me(improvement_name_translation(pimprove),
1136                           is_great_wonder(pimprove) ? HELP_WONDER
1137                           : HELP_IMPROVEMENT);
1138               tb = new QLabel(this);
1139               tb->setProperty(fonts::help_label, "true");
1140               tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
1141               tb->setTextFormat(Qt::RichText);
1142               tb->setText(str.trimmed());
1143               connect(tb, &QLabel::linkActivated,
1144                       this, &help_widget::anchor_clicked);
1145               info_layout->addWidget(tb);
1146             }
1147           } requirement_vector_iterate_end;
1148         }
1149       } improvement_iterate_end;
1150 
1151       unit_type_iterate(punittype) {
1152         if (padvance != punittype->require_advance) {
1153           continue;
1154         }
1155         str = _("Allows");
1156         str = "<b>" + str + "</b> "
1157               + link_me(utype_name_translation(punittype), HELP_UNIT);
1158         tb = new QLabel(this);
1159         tb->setProperty(fonts::help_label, "true");
1160         tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
1161         tb->setTextFormat(Qt::RichText);
1162         tb->setText(str.trimmed());
1163         connect(tb, &QLabel::linkActivated,
1164                 this, &help_widget::anchor_clicked);
1165         info_layout->addWidget(tb);
1166       } unit_type_iterate_end;
1167 
1168       info_panel_done();
1169       helptext_advance(buffer, sizeof(buffer), client.conn.playing,
1170                        topic->text, n);
1171       text_browser->setPlainText(buffer);
1172 
1173     }
1174   } else {
1175     set_topic_other(topic, title);
1176   }
1177 }
1178 
1179 /****************************************************************************
1180   Creates a terrain image on the given canvas.
1181 ****************************************************************************/
terrain_canvas(struct terrain * terrain,const struct resource * resource,enum extra_cause cause)1182 canvas *terrain_canvas(struct terrain *terrain,
1183                        const struct resource *resource,
1184                        enum extra_cause cause)
1185 {
1186   struct canvas *canvas;
1187   struct drawn_sprite sprs[80];
1188   int canvas_y, count, i, width, height;
1189   struct extra_type *pextra;
1190   struct sprite *sprite;
1191 
1192   width = tileset_full_tile_width(tileset);
1193   height = tileset_full_tile_height(tileset);
1194   canvas_y = height - tileset_tile_height(tileset);
1195 
1196   canvas = qtg_canvas_create(width, height);
1197   canvas->map_pixmap.fill(Qt::transparent);
1198   for (i = 0; i < 3; ++i) {
1199     count = fill_basic_terrain_layer_sprite_array(tileset, sprs,
1200                                                   i, terrain);
1201     put_drawn_sprites(canvas, 1.0f, 0, canvas_y, count, sprs, false);
1202   }
1203 
1204   pextra = NULL;
1205   if (cause != EC_COUNT) {
1206     extra_type_by_cause_iterate(cause, e) {
1207       pextra = e;
1208       break;
1209     } extra_type_by_cause_iterate_end;
1210 
1211     count = fill_basic_extra_sprite_array(tileset, sprs, pextra);
1212     put_drawn_sprites(canvas, 1.0f, 0, canvas_y, count, sprs, false);
1213   }
1214 
1215   if (resource != NULL) {
1216     sprite = get_resource_sprite(tileset, resource);
1217     qtg_canvas_put_sprite(canvas, 0, canvas_y, sprite, 0, 0, width, height);
1218   }
1219 
1220   return canvas;
1221 }
1222 
1223 /****************************************************************************
1224   Creates a terrain widget with title, terrain image, legend. An optional
1225   tooltip can be given to explain the legend.
1226 ****************************************************************************/
create_terrain_widget(const QString & title,const struct canvas * image,const QString & legend,const QString & tooltip)1227 QLayout *help_widget::create_terrain_widget(const QString &title,
1228                                             const struct canvas *image,
1229                                             const QString &legend,
1230                                             const QString &tooltip)
1231 {
1232   QGraphicsDropShadowEffect *effect;
1233   QLabel *label;
1234   QGridLayout *layout = new QGridLayout();
1235 
1236   label = new QLabel();
1237   effect = new QGraphicsDropShadowEffect(label);
1238   effect->setBlurRadius(3);
1239   effect->setOffset(0, 2);
1240   label->setGraphicsEffect(effect);
1241   label->setPixmap(image->map_pixmap);
1242   layout->addWidget(label, 0, 0, 2, 1);
1243 
1244   label = new QLabel(title);
1245   label->setTextFormat(Qt::PlainText);
1246   layout->addWidget(label, 0, 1, Qt::AlignBottom);
1247   label->setProperty(fonts::help_title, "true");
1248 
1249   label = new QLabel(legend);
1250   label->setTextFormat(Qt::PlainText);
1251   layout->addWidget(label, 1, 1, Qt::AlignTop);
1252   label->setProperty(fonts::help_label, "true");
1253 
1254   if (!tooltip.isEmpty()) {
1255     label->setToolTip(tooltip);
1256     label->setCursor(Qt::WhatsThisCursor);
1257   }
1258 
1259   layout->setColumnStretch(0, 0);
1260   layout->setColumnStretch(1, 100);
1261 
1262   return layout;
1263 }
1264 
1265 /****************************************************************************
1266   Creates terrain help pages.
1267 ****************************************************************************/
set_topic_terrain(const help_item * topic,const char * title)1268 void help_widget::set_topic_terrain(const help_item *topic,
1269                                       const char *title)
1270 {
1271   char buffer[MAX_HELP_TEXT_SIZE];
1272   struct terrain *pterrain, *max;
1273   canvas *canvas;
1274   QVBoxLayout *vbox;
1275   bool show_panel = false;
1276   QScrollArea *area;
1277   QWidget *panel;
1278   QString str;
1279 
1280   pterrain = terrain_by_translated_name(title);
1281   if (pterrain) {
1282     struct universal for_terr;
1283 
1284     for_terr.kind = VUT_TERRAIN;
1285     for_terr.value.terrain = pterrain;
1286 
1287     helptext_terrain(buffer, sizeof(buffer), client.conn.playing,
1288                      topic->text, pterrain);
1289     text_browser->setPlainText(buffer);
1290 
1291     // Create information panel
1292     show_info_panel();
1293     max = terrain_max_values();
1294 
1295     // Create terrain icon. Use shadow to help distinguish terrain.
1296     canvas = terrain_canvas(pterrain);
1297     add_info_pixmap(&canvas->map_pixmap, true);
1298     qtg_canvas_free(canvas);
1299 
1300     add_info_progress(_("Food:"), pterrain->output[O_FOOD],
1301                       0, max->output[O_FOOD]);
1302     add_info_progress(_("Production:"), pterrain->output[O_SHIELD],
1303                       0, max->output[O_SHIELD]);
1304     add_info_progress(_("Trade:"), pterrain->output[O_TRADE],
1305                       0, max->output[O_TRADE]);
1306 
1307     add_info_separator();
1308 
1309     add_info_progress(_("Move cost:"), pterrain->movement_cost,
1310                       0, max->movement_cost);
1311     add_info_progress(_("Defense bonus:"), MIN(100, pterrain->defense_bonus),
1312                       0, 100,
1313                       // TRANS: Display a percentage, eg "50%".
1314                       QString(_("%1%")).arg(pterrain->defense_bonus));
1315 
1316     add_info_separator();
1317 
1318     if (pterrain->irrigation_result != pterrain
1319         && pterrain->irrigation_result != T_NONE
1320         && pterrain->irrigation_time != 0
1321         && effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_terr) > 0) {
1322       QLabel *tb;
1323       char irrig_buffer[1024];
1324 
1325       tb = new QLabel(this);
1326       fc_snprintf(irrig_buffer, sizeof(irrig_buffer), PL_("%d turn", "%d turns",
1327                                                           pterrain->irrigation_time),
1328                   pterrain->irrigation_time);
1329       str = N_("Irrig. Rslt/Time:");;
1330       str = str + link_me(terrain_name_translation(pterrain->irrigation_result),
1331                           HELP_TERRAIN)
1332             + QString(irrig_buffer).toHtmlEscaped();
1333       tb = new QLabel(this);
1334       tb->setProperty(fonts::help_label, "true");
1335       tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
1336       tb->setTextFormat(Qt::RichText);
1337       tb->setText(str.trimmed());
1338       connect(tb, &QLabel::linkActivated,
1339               this, &help_widget::anchor_clicked);
1340       info_layout->addWidget(tb);
1341     }
1342 
1343     if (pterrain->mining_result != pterrain
1344         && pterrain->mining_result != T_NONE
1345         && pterrain->mining_time != 0
1346         && effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_terr) > 0) {
1347       QLabel *tb;
1348       char mine_buffer[1024];
1349 
1350       tb = new QLabel(this);
1351       fc_snprintf(mine_buffer, sizeof(mine_buffer), PL_("%d turn", "%d turns",
1352                                                         pterrain->mining_time),
1353                   pterrain->mining_time);
1354       str = N_("Mine Rslt/Time:");;
1355       str = str + link_me(terrain_name_translation(pterrain->mining_result),
1356                           HELP_TERRAIN)
1357             + QString(mine_buffer).toHtmlEscaped();
1358       tb = new QLabel(this);
1359       tb->setProperty(fonts::help_label, "true");
1360       tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
1361       tb->setTextFormat(Qt::RichText);
1362       tb->setText(str.trimmed());
1363       connect(tb, &QLabel::linkActivated,
1364               this, &help_widget::anchor_clicked);
1365       info_layout->addWidget(tb);
1366     }
1367 
1368     if (pterrain->transform_result != T_NONE
1369         && pterrain->transform_time != 0
1370         && effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_terr) > 0) {
1371       QLabel *tb;
1372       char tf_buffer[1024];
1373 
1374       tb = new QLabel(this);
1375       fc_snprintf(tf_buffer, sizeof(tf_buffer), PL_("%d turn", "%d turns",
1376                                                     pterrain->transform_time),
1377                   pterrain->transform_time);
1378       str = N_("Trans. Rslt/Time:");
1379       str = str + link_me(terrain_name_translation(pterrain->transform_result),
1380                           HELP_TERRAIN)
1381             + QString(tf_buffer).toHtmlEscaped();
1382       tb = new QLabel(this);
1383       tb->setProperty(fonts::help_label, "true");
1384       tb->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
1385       tb->setTextFormat(Qt::RichText);
1386       tb->setText(str.trimmed());
1387       connect(tb, &QLabel::linkActivated,
1388               this, &help_widget::anchor_clicked);
1389       info_layout->addWidget(tb);
1390     }
1391 
1392     if (pterrain->irrigation_result == pterrain
1393         && pterrain->irrigation_time != 0
1394         && effect_cumulative_max(EFT_IRRIG_POSSIBLE, &for_terr) > 0) {
1395       /* TRANS: this and similar literal strings interpreted as (Qt) HTML */
1396       add_extras_of_act_for_terrain(pterrain, ACTIVITY_IRRIGATE, _("Build as irrigation"));
1397     }
1398     if (pterrain->mining_result == pterrain
1399         && pterrain->mining_time != 0
1400         && effect_cumulative_max(EFT_MINING_POSSIBLE, &for_terr) > 0) {
1401       add_extras_of_act_for_terrain(pterrain, ACTIVITY_MINE, _("Build as mine"));
1402     }
1403     if (pterrain->road_time != 0) {
1404       add_extras_of_act_for_terrain(pterrain, ACTIVITY_GEN_ROAD, _("Build as road"));
1405     }
1406     if (pterrain->base_time != 0) {
1407       add_extras_of_act_for_terrain(pterrain, ACTIVITY_BASE, _("Build as base"));
1408     }
1409 
1410     info_panel_done();
1411 
1412     // Create bottom widget
1413     panel = new QWidget();
1414     vbox = new QVBoxLayout(panel);
1415 
1416     if (*(pterrain->resources)) {
1417       struct resource **r;
1418       for (r = pterrain->resources; *r; r++) {
1419         canvas = terrain_canvas(pterrain, *r);
1420         vbox->addLayout(create_terrain_widget(
1421           resource_name_translation(*r),
1422           canvas,
1423           // TRANS: %1 food, %2 shields, %3 trade
1424           QString(_("Tile output becomes %1, %2, %3."))
1425             .arg(pterrain->output[O_FOOD]   + (*r)->output[O_FOOD])
1426             .arg(pterrain->output[O_SHIELD] + (*r)->output[O_SHIELD])
1427             .arg(pterrain->output[O_TRADE]  + (*r)->output[O_TRADE]),
1428           // TRANS: Tooltip decorating strings like "1, 2, 3".
1429           _("Output (Food, Shields, Trade) of a tile where the resource is "
1430             "present.")));
1431         qtg_canvas_free(canvas);
1432         show_panel = true;
1433       }
1434     }
1435 
1436     vbox->addStretch(100);
1437     vbox->setSizeConstraint(QLayout::SetMinimumSize);
1438     if (show_panel) {
1439       area = new QScrollArea();
1440       area->setWidget(panel);
1441       set_bottom_panel(area);
1442     } else {
1443       panel->deleteLater();
1444     }
1445 
1446     delete max;
1447   } else {
1448     set_topic_other(topic, title);
1449   }
1450 }
1451 
1452 /**************************************************************************
1453   Creates extra help pages.
1454 **************************************************************************/
set_topic_extra(const help_item * topic,const char * title)1455 void help_widget::set_topic_extra(const help_item *topic,
1456                                     const char *title)
1457 {
1458   char buffer[MAX_HELP_TEXT_SIZE];
1459   struct extra_type *pextra = extra_type_by_translated_name(title);
1460 
1461   if (pextra) {
1462     helptext_extra(buffer, sizeof(buffer), client.conn.playing,
1463                   topic->text, pextra);
1464     text_browser->setPlainText(buffer);
1465   } else {
1466     set_topic_other(topic, title);
1467   }
1468 }
1469 
1470 /**************************************************************************
1471   Creates specialist help pages.
1472 **************************************************************************/
set_topic_specialist(const help_item * topic,const char * title)1473 void help_widget::set_topic_specialist(const help_item *topic,
1474                                          const char *title)
1475 {
1476   char buffer[MAX_HELP_TEXT_SIZE];
1477   struct specialist *pspec = specialist_by_translated_name(title);
1478   if (pspec) {
1479     helptext_specialist(buffer, sizeof(buffer), client.conn.playing,
1480                         topic->text, pspec);
1481     text_browser->setPlainText(buffer);
1482   } else {
1483     set_topic_other(topic, title);
1484   }
1485 }
1486 
1487 /**************************************************************************
1488   Creates government help pages.
1489 **************************************************************************/
set_topic_government(const help_item * topic,const char * title)1490 void help_widget::set_topic_government(const help_item *topic,
1491                                          const char *title)
1492 {
1493   char buffer[MAX_HELP_TEXT_SIZE];
1494   struct government *pgov = government_by_translated_name(title);
1495   if (pgov) {
1496     helptext_government(buffer, sizeof(buffer), client.conn.playing,
1497                         topic->text, pgov);
1498     text_browser->setPlainText(buffer);
1499   } else {
1500     set_topic_other(topic, title);
1501   }
1502 }
1503 
1504 /**************************************************************************
1505   Creates nation help pages.
1506 **************************************************************************/
set_topic_nation(const help_item * topic,const char * title)1507 void help_widget::set_topic_nation(const help_item *topic,
1508                                      const char *title)
1509 {
1510   char buffer[MAX_HELP_TEXT_SIZE];
1511   struct nation_type *pnation = nation_by_translated_plural(title);
1512   if (pnation) {
1513     helptext_nation(buffer, sizeof(buffer), pnation, topic->text);
1514     text_browser->setPlainText(buffer);
1515   } else {
1516     set_topic_other(topic, title);
1517   }
1518 }
1519 
1520 /****************************************************************************
1521   Retrieves the maximum values any terrain will ever have.
1522   Supported fields:
1523     base_time, clean_fallout_time, clean_pollution_time, defense_bonus,
1524     irrigation_food_incr, irrigation_time, mining_shield_incr, mining_time,
1525     movement_cost, output, pillage_time, road_output_incr_pct, road_time,
1526     transform_time
1527   Other fields in returned value are undefined. Especially, all pointers are
1528   invalid.
1529 ****************************************************************************/
terrain_max_values()1530 struct terrain *help_widget::terrain_max_values()
1531 {
1532   Terrain_type_id i, count;
1533   struct terrain *terrain;
1534   struct terrain *max = new struct terrain;
1535   max->base_time = 0;
1536   max->clean_fallout_time = 0;
1537   max->clean_pollution_time = 0;
1538   max->defense_bonus = 0;
1539   max->irrigation_food_incr = 0;
1540   max->irrigation_time = 0;
1541   max->mining_shield_incr = 0;
1542   max->mining_time = 0;
1543   max->movement_cost = 0;
1544   max->output[O_FOOD] = 0;
1545   max->output[O_GOLD] = 0;
1546   max->output[O_LUXURY] = 0;
1547   max->output[O_SCIENCE] = 0;
1548   max->output[O_SHIELD] = 0;
1549   max->output[O_TRADE] = 0;
1550   max->pillage_time = 0;
1551   max->road_output_incr_pct[O_FOOD] = 0;
1552   max->road_output_incr_pct[O_GOLD] = 0;
1553   max->road_output_incr_pct[O_LUXURY] = 0;
1554   max->road_output_incr_pct[O_SCIENCE] = 0;
1555   max->road_output_incr_pct[O_SHIELD] = 0;
1556   max->road_output_incr_pct[O_TRADE] = 0;
1557   max->road_time = 0;
1558   max->transform_time = 0;
1559   count = terrain_count();
1560   for (i = 0; i < count; ++i) {
1561     terrain = terrain_by_number(i);
1562 #define SET_MAX(v) \
1563     max->v = max->v > terrain->v ? max->v : terrain->v
1564     SET_MAX(base_time);
1565     SET_MAX(clean_fallout_time);
1566     SET_MAX(clean_pollution_time);
1567     SET_MAX(defense_bonus);
1568     SET_MAX(irrigation_food_incr);
1569     SET_MAX(irrigation_time);
1570     SET_MAX(mining_shield_incr);
1571     SET_MAX(mining_time);
1572     SET_MAX(movement_cost);
1573     SET_MAX(output[O_FOOD]);
1574     SET_MAX(output[O_GOLD]);
1575     SET_MAX(output[O_LUXURY]);
1576     SET_MAX(output[O_SCIENCE]);
1577     SET_MAX(output[O_SHIELD]);
1578     SET_MAX(output[O_TRADE]);
1579     SET_MAX(pillage_time);
1580     SET_MAX(road_output_incr_pct[O_FOOD]);
1581     SET_MAX(road_output_incr_pct[O_GOLD]);
1582     SET_MAX(road_output_incr_pct[O_LUXURY]);
1583     SET_MAX(road_output_incr_pct[O_SCIENCE]);
1584     SET_MAX(road_output_incr_pct[O_SHIELD]);
1585     SET_MAX(road_output_incr_pct[O_TRADE]);
1586     SET_MAX(road_time);
1587     SET_MAX(transform_time);
1588 #undef SET_MAX
1589   }
1590   return max;
1591 }
1592 
1593 /**************************************************************************
1594   Retrieves the maximum values any unit of uclass will ever have.
1595   Supported fields:
1596     attack_strength, bombard_rate, build_cost, city_size, convert_time,
1597     defense_strength, firepower, fuel, happy_cost, hp, move_rate, pop_cost,
1598     upkeep, vision_radius_sq
1599   Other fiels in returned value are undefined. Especially, all pointers are
1600   invalid except uclass.
1601 **************************************************************************/
uclass_max_values(struct unit_class * uclass)1602 struct unit_type *help_widget::uclass_max_values(struct unit_class *uclass)
1603 {
1604   struct unit_type *max = new struct unit_type;
1605   max->uclass = uclass;
1606   max->attack_strength = 0;
1607   max->bombard_rate = 0;
1608   max->build_cost = 0;
1609   max->city_size = 0;
1610   max->defense_strength = 0;
1611   max->firepower = 0;
1612   max->fuel = 0;
1613   max->happy_cost = 0;
1614   max->hp = 0;
1615   max->move_rate = 0;
1616   max->pop_cost = 0;
1617   max->upkeep[O_FOOD] = 0;
1618   max->upkeep[O_GOLD] = 0;
1619   max->upkeep[O_LUXURY] = 0;
1620   max->upkeep[O_SCIENCE] = 0;
1621   max->upkeep[O_SHIELD] = 0;
1622   max->upkeep[O_TRADE] = 0;
1623   max->vision_radius_sq = 0;
1624   unit_type_iterate(utype) {
1625     if (utype->uclass == uclass) {
1626 #define SET_MAX(v) \
1627       max->v = max->v > utype->v ? max->v : utype->v
1628       SET_MAX(attack_strength);
1629       SET_MAX(bombard_rate);
1630       SET_MAX(build_cost);
1631       SET_MAX(city_size);
1632       SET_MAX(convert_time);
1633       SET_MAX(defense_strength);
1634       SET_MAX(firepower);
1635       SET_MAX(fuel);
1636       SET_MAX(happy_cost);
1637       SET_MAX(hp);
1638       SET_MAX(move_rate);
1639       SET_MAX(pop_cost);
1640       SET_MAX(upkeep[O_FOOD]);
1641       SET_MAX(upkeep[O_GOLD]);
1642       SET_MAX(upkeep[O_LUXURY]);
1643       SET_MAX(upkeep[O_SCIENCE]);
1644       SET_MAX(upkeep[O_SHIELD]);
1645       SET_MAX(upkeep[O_TRADE]);
1646       SET_MAX(vision_radius_sq);
1647 #undef SET_MAX
1648     }
1649   } unit_type_iterate_end
1650   return max;
1651 }
1652