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(" ", " ");
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