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)
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 ***********************************************************************/
15 #include <fc_config.h>
33 #include "specialist.h"
40 #include "citydlg_common.h" /* city_production_cost_str() */
43 #include "cityrepdata.h"
45 /************************************************************************
46 cr_entry = return an entry (one column for one city) for the city report
47 These return ptrs to filled in static strings.
48 Note the returned string may not be exactly the right length; that
50 *************************************************************************/
51 static const char *cr_entry_cityname(const struct city
*pcity
,
54 /* We used to truncate the name to 14 bytes. This should not be needed
55 * in any modern GUI library and may give an invalid string if a
56 * multibyte character is clipped. */
57 return city_name_get(pcity
);
60 /************************************************************************
61 Translated name of nation who owns this city.
62 *************************************************************************/
63 static const char *cr_entry_nation(const struct city
*pcity
,
66 return nation_adjective_for_player(city_owner(pcity
));
69 /************************************************************************
70 Returns city size written to string. Returned string is statically
71 allocated and its contents change when this function is called again.
72 *************************************************************************/
73 static const char *cr_entry_size(const struct city
*pcity
,
78 fc_snprintf(buf
, sizeof(buf
), "%2d", city_size_get(pcity
));
82 /************************************************************************
83 Returns concise city happiness state written to string.
84 Returned string is statically allocated and its contents change when
85 this function is called again.
86 *************************************************************************/
87 static const char *cr_entry_hstate_concise(const struct city
*pcity
,
91 fc_snprintf(buf
, sizeof(buf
), "%s",
92 (city_celebrating(pcity
) ? "*"
93 : (city_unhappy(pcity
) ? "X" : " ")));
97 /************************************************************************
98 Returns verbose city happiness state written to string.
99 Returned string is statically allocated and its contents change when
100 this function is called again.
101 *************************************************************************/
102 static const char *cr_entry_hstate_verbose(const struct city
*pcity
,
107 fc_snprintf(buf
, sizeof(buf
), "%s",
108 (city_celebrating(pcity
) ? Q_("?city_state:Celebrating")
109 : (city_unhappy(pcity
) ? Q_("?city_state:Disorder")
110 : Q_("?city_state:Peace"))));
114 /************************************************************************
115 Returns number of citizens of each happiness state written to string.
116 Returned string is statically allocated and its contents change when
117 this function is called again.
118 *************************************************************************/
119 static const char *cr_entry_workers(const struct city
*pcity
,
124 fc_snprintf(buf
, sizeof(buf
), "%d/%d/%d/%d",
125 pcity
->feel
[CITIZEN_HAPPY
][FEELING_FINAL
],
126 pcity
->feel
[CITIZEN_CONTENT
][FEELING_FINAL
],
127 pcity
->feel
[CITIZEN_UNHAPPY
][FEELING_FINAL
],
128 pcity
->feel
[CITIZEN_ANGRY
][FEELING_FINAL
]);
132 /************************************************************************
133 Returns number of happy citizens written to string.
134 Returned string is statically allocated and its contents change when
135 this function is called again.
136 *************************************************************************/
137 static const char *cr_entry_happy(const struct city
*pcity
,
141 fc_snprintf(buf
, sizeof(buf
), "%2d",
142 pcity
->feel
[CITIZEN_HAPPY
][FEELING_FINAL
]);
146 /************************************************************************
147 Returns number of content citizens written to string.
148 Returned string is statically allocated and its contents change when
149 this function is called again.
150 *************************************************************************/
151 static const char *cr_entry_content(const struct city
*pcity
,
155 fc_snprintf(buf
, sizeof(buf
), "%2d",
156 pcity
->feel
[CITIZEN_CONTENT
][FEELING_FINAL
]);
160 /************************************************************************
161 Returns number of unhappy citizens written to string.
162 Returned string is statically allocated and its contents change when
163 this function is called again.
164 *************************************************************************/
165 static const char *cr_entry_unhappy(const struct city
*pcity
,
169 fc_snprintf(buf
, sizeof(buf
), "%2d",
170 pcity
->feel
[CITIZEN_UNHAPPY
][FEELING_FINAL
]);
174 /************************************************************************
175 Returns number of angry citizens written to string.
176 Returned string is statically allocated and its contents change when
177 this function is called again.
178 *************************************************************************/
179 static const char *cr_entry_angry(const struct city
*pcity
,
183 fc_snprintf(buf
, sizeof(buf
), "%2d",
184 pcity
->feel
[CITIZEN_ANGRY
][FEELING_FINAL
]);
188 /************************************************************************
189 Returns list of specialists written to string.
190 Returned string is statically allocated and its contents change when
191 this function is called again.
192 *************************************************************************/
193 static const char *cr_entry_specialists(const struct city
*pcity
,
196 return specialists_string(pcity
->specialists
);
199 /************************************************************************
200 Returns number of specialists of type given as data written to string.
201 Returned string is statically allocated and its contents change when
202 this function is called again.
203 *************************************************************************/
204 static const char *cr_entry_specialist(const struct city
*pcity
,
208 const struct specialist
*sp
= data
;
210 fc_snprintf(buf
, sizeof(buf
), "%2d",
211 pcity
->specialists
[specialist_index(sp
)]);
215 /************************************************************************
216 Returns string with best attack values of units in city.
217 Returned string is statically allocated and its contents change when
218 this function is called again.
219 *************************************************************************/
220 static const char *cr_entry_attack(const struct city
*pcity
,
224 int attack_best
[4] = {-1, -1, -1, -1}, i
;
226 unit_list_iterate(pcity
->tile
->units
, punit
) {
227 /* What about allied units? Should we just count them? */
228 attack_best
[3] = unit_type_get(punit
)->attack_strength
;
230 /* Now that the element is appended to the end of the list, we simply
231 do an insertion sort. */
232 for (i
= 2; i
>= 0 && attack_best
[i
] < attack_best
[i
+ 1]; i
--) {
233 int tmp
= attack_best
[i
];
234 attack_best
[i
] = attack_best
[i
+ 1];
235 attack_best
[i
+ 1] = tmp
;
237 } unit_list_iterate_end
;
240 for (i
= 0; i
< 3; i
++) {
241 if (attack_best
[i
] >= 0) {
242 cat_snprintf(buf
, sizeof(buf
), "%s%d", (i
> 0) ? "/" : "",
245 cat_snprintf(buf
, sizeof(buf
), "%s-", (i
> 0) ? "/" : "");
252 /************************************************************************
253 Returns string with best defend values of units in city.
254 Returned string is statically allocated and its contents change when
255 this function is called again.
256 *************************************************************************/
257 static const char *cr_entry_defense(const struct city
*pcity
,
261 int defense_best
[4] = {-1, -1, -1, -1}, i
;
263 unit_list_iterate(pcity
->tile
->units
, punit
) {
264 /* What about allied units? Should we just count them? */
265 defense_best
[3] = unit_type_get(punit
)->defense_strength
;
267 /* Now that the element is appended to the end of the list, we simply
268 do an insertion sort. */
269 for (i
= 2; i
>= 0 && defense_best
[i
] < defense_best
[i
+ 1]; i
--) {
270 int tmp
= defense_best
[i
];
272 defense_best
[i
] = defense_best
[i
+ 1];
273 defense_best
[i
+ 1] = tmp
;
275 } unit_list_iterate_end
;
278 for (i
= 0; i
< 3; i
++) {
279 if (defense_best
[i
] >= 0) {
280 cat_snprintf(buf
, sizeof(buf
), "%s%d", (i
> 0) ? "/" : "",
283 cat_snprintf(buf
, sizeof(buf
), "%s-", (i
> 0) ? "/" : "");
290 /************************************************************************
291 Returns number of supported units written to string.
292 Returned string is statically allocated and its contents change when
293 this function is called again.
294 *************************************************************************/
295 static const char *cr_entry_supported(const struct city
*pcity
,
299 int num_supported
= unit_list_size(pcity
->units_supported
);
301 fc_snprintf(buf
, sizeof(buf
), "%2d", num_supported
);
306 /************************************************************************
307 Returns number of present units written to string.
308 Returned string is statically allocated and its contents change when
309 this function is called again.
310 *************************************************************************/
311 static const char *cr_entry_present(const struct city
*pcity
,
315 int num_present
= unit_list_size(pcity
->tile
->units
);
317 fc_snprintf(buf
, sizeof(buf
), "%2d", num_present
);
322 /************************************************************************
323 Returns string listing amounts of resources.
324 Returned string is statically allocated and its contents change when
325 this function is called again.
326 *************************************************************************/
327 static const char *cr_entry_resources(const struct city
*pcity
,
331 fc_snprintf(buf
, sizeof(buf
), "%d/%d/%d",
332 pcity
->surplus
[O_FOOD
],
333 pcity
->surplus
[O_SHIELD
],
334 pcity
->surplus
[O_TRADE
]);
338 /************************************************************************
339 Returns food surplus written to string.
340 Returned string is statically allocated and its contents change when
341 this function is called again.
342 *************************************************************************/
343 static const char *cr_entry_foodplus(const struct city
*pcity
,
347 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->surplus
[O_FOOD
]);
351 /************************************************************************
352 Returns production surplus written to string.
353 Returned string is statically allocated and its contents change when
354 this function is called again.
355 *************************************************************************/
356 static const char *cr_entry_prodplus(const struct city
*pcity
,
360 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->surplus
[O_SHIELD
]);
364 /************************************************************************
365 Returns trade surplus written to string.
366 Returned string is statically allocated and its contents change when
367 this function is called again.
368 *************************************************************************/
369 static const char *cr_entry_tradeplus(const struct city
*pcity
,
373 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->surplus
[O_TRADE
]);
377 /************************************************************************
378 Returns string describing resource output.
379 Returned string is statically allocated and its contents change when
380 this function is called again.
381 *************************************************************************/
382 static const char *cr_entry_output(const struct city
*pcity
,
386 int goldie
= pcity
->surplus
[O_GOLD
];
388 fc_snprintf(buf
, sizeof(buf
), "%3d/%d/%d",
389 goldie
, pcity
->prod
[O_LUXURY
], pcity
->prod
[O_SCIENCE
]);
393 /************************************************************************
394 Returns gold surplus written to string.
395 Returned string is statically allocated and its contents change when
396 this function is called again.
397 *************************************************************************/
398 static const char *cr_entry_gold(const struct city
*pcity
,
403 if (pcity
->surplus
[O_GOLD
] > 0) {
404 fc_snprintf(buf
, sizeof(buf
), "+%d", pcity
->surplus
[O_GOLD
]);
406 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->surplus
[O_GOLD
]);
411 /************************************************************************
412 Returns luxury output written to string.
413 Returned string is statically allocated and its contents change when
414 this function is called again.
415 *************************************************************************/
416 static const char *cr_entry_luxury(const struct city
*pcity
,
420 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->prod
[O_LUXURY
]);
424 /************************************************************************
425 Returns science output written to string.
426 Returned string is statically allocated and its contents change when
427 this function is called again.
428 *************************************************************************/
429 static const char *cr_entry_science(const struct city
*pcity
,
433 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->prod
[O_SCIENCE
]);
437 /************************************************************************
438 Returns number of turns before city grows written to string.
439 Returned string is statically allocated and its contents change when
440 this function is called again.
441 *************************************************************************/
442 static const char *cr_entry_growturns(const struct city
*pcity
,
445 int turns
= city_turns_to_grow(pcity
);
449 if (turns
== FC_INFINITY
) {
450 /* 'never' wouldn't be easily translatable here. */
451 fc_snprintf(buffer
, sizeof(buffer
), "---");
453 /* Shrinking cities get a negative value. */
454 fc_snprintf(buffer
, sizeof(buffer
), "%4d", turns
);
456 fc_snprintf(buf
, sizeof(buf
), "%s (%d/%d)",
457 buffer
, pcity
->food_stock
,
458 city_granary_size(city_size_get(pcity
)));
462 /************************************************************************
463 Returns pollution output written to string.
464 Returned string is statically allocated and its contents change when
465 this function is called again.
466 *************************************************************************/
467 static const char *cr_entry_pollution(const struct city
*pcity
,
471 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->pollution
);
475 /************************************************************************
476 Returns number and output of trade routes written to string.
477 Returned string is statically allocated and its contents change when
478 this function is called again.
479 *************************************************************************/
480 static const char *cr_entry_trade_routes(const struct city
*pcity
,
484 int num
= 0, value
= 0, i
;
486 for (i
= 0; i
< MAX_TRADE_ROUTES
; i
++) {
487 if (0 != pcity
->trade
[i
]) {
489 value
+= pcity
->trade_value
[i
];
494 sz_strlcpy(buf
, "0");
496 fc_snprintf(buf
, sizeof(buf
), "%d (+%d)", num
, value
);
501 /************************************************************************
502 Returns number of build slots written to string.
503 Returned string is statically allocated and its contents change when
504 this function is called again.
505 *************************************************************************/
506 static const char *cr_entry_build_slots(const struct city
*pcity
,
510 fc_snprintf(buf
, sizeof(buf
), "%3d", city_build_slots(pcity
));
514 /************************************************************************
515 Returns name of current production.
516 Returned string is statically allocated and its contents change when
517 this function is called again.
518 *************************************************************************/
519 static const char *cr_entry_building(const struct city
*pcity
,
522 static char buf
[192];
523 const char *from_worklist
=
524 worklist_is_empty(&pcity
->worklist
) ? "" :
525 gui_options
.concise_city_production
? "+" : _("(worklist)");
527 if (city_production_has_flag(pcity
, IF_GOLD
)) {
528 fc_snprintf(buf
, sizeof(buf
), "%s (%d)%s",
529 city_production_name_translation(pcity
),
530 MAX(0, pcity
->surplus
[O_SHIELD
]), from_worklist
);
532 fc_snprintf(buf
, sizeof(buf
), "%s (%d/%s)%s",
533 city_production_name_translation(pcity
),
535 city_production_cost_str(pcity
),
542 /************************************************************************
543 Returns cost of buying current production and turns to completion
544 written to string. Returned string is statically allocated and its
545 contents change when this function is called again.
546 *************************************************************************/
547 static const char *cr_entry_build_cost(const struct city
*pcity
,
556 if (city_production_has_flag(pcity
, IF_GOLD
)) {
557 fc_snprintf(buf
, sizeof(buf
), "*");
560 price
= city_production_buy_gold_cost(pcity
);
561 turns
= city_production_turns_to_build(pcity
, TRUE
);
564 fc_snprintf(bufone
, sizeof(bufone
), "---");
566 fc_snprintf(bufone
, sizeof(bufone
), "%d", price
);
569 fc_snprintf(buftwo
, sizeof(buftwo
), "--");
571 fc_snprintf(buftwo
, sizeof(buftwo
), "%3d", turns
);
573 fc_snprintf(buf
, sizeof(buf
), "%s/%s", buftwo
, bufone
);
577 /************************************************************************
578 Returns corruption amount written to string.
579 Returned string is statically allocated and its contents change when
580 this function is called again.
581 *************************************************************************/
582 static const char *cr_entry_corruption(const struct city
*pcity
,
586 fc_snprintf(buf
, sizeof(buf
), "%3d", -(pcity
->waste
[O_TRADE
]));
590 /************************************************************************
591 Returns waste amount written to string.
592 Returned string is statically allocated and its contents change when
593 this function is called again.
594 *************************************************************************/
595 static const char *cr_entry_waste(const struct city
*pcity
,
599 fc_snprintf(buf
, sizeof(buf
), "%3d", -(pcity
->waste
[O_SHIELD
]));
603 /************************************************************************
604 Returns risk percentage of plague written to string.
605 Returned string is statically allocated and its contents change when
606 this function is called again.
607 *************************************************************************/
608 static const char *cr_entry_plague_risk(const struct city
*pcity
,
612 if (!game
.info
.illness_on
) {
613 fc_snprintf(buf
, sizeof(buf
), " -.-");
615 fc_snprintf(buf
, sizeof(buf
), "%4.1f",
616 (float)city_illness_calc(pcity
, NULL
, NULL
, NULL
, NULL
)/10.0);
621 /************************************************************************
622 Returns number of continent
623 *************************************************************************/
624 static const char *cr_entry_continent(const struct city
*pcity
,
628 fc_snprintf(buf
, sizeof(buf
), "%3d", pcity
->tile
->continent
);
632 /************************************************************************
633 Returns city cma description.
634 Returned string is statically allocated and its contents change when
635 this function is called again.
636 *************************************************************************/
637 static const char *cr_entry_cma(const struct city
*pcity
,
640 return cmafec_get_short_descr_of_city(pcity
);
643 /* City report options (which columns get shown)
644 * To add a new entry, you should just have to:
645 * - add a function like those above
646 * - add an entry in the base_city_report_specs[] table
649 /* This generates the function name and the tagname: */
650 #define FUNC_TAG(var) cr_entry_##var, #var
652 static const struct city_report_spec base_city_report_specs
[] = {
653 { TRUE
, -15, 0, NULL
, N_("?city:Name"), N_("City Name"),
654 NULL
, FUNC_TAG(cityname
) },
655 { FALSE
, -15, 0, NULL
, N_("Nation"), N_("Nation"),
656 NULL
, FUNC_TAG(nation
) },
657 { TRUE
, 2, 1, NULL
, N_("?size [short]:Sz"), N_("Size"),
658 NULL
, FUNC_TAG(size
) },
659 { TRUE
, -8, 1, NULL
, N_("State"), N_("Celebrating/Peace/Disorder"),
660 NULL
, FUNC_TAG(hstate_verbose
) },
661 { FALSE
, 1, 1, NULL
, NULL
, N_("Concise *=Celebrating, X=Disorder"),
662 NULL
, FUNC_TAG(hstate_concise
) },
664 { FALSE
, 2, 1, NULL
, N_("?Happy workers:H"), N_("Workers: Happy"),
665 NULL
, FUNC_TAG(happy
) },
666 { FALSE
, 2, 1, NULL
, N_("?Content workers:C"), N_("Workers: Content"),
667 NULL
, FUNC_TAG(content
) },
668 { FALSE
, 2, 1, NULL
, N_("?Unhappy workers:U"), N_("Workers: Unhappy"),
669 NULL
, FUNC_TAG(unhappy
) },
670 { FALSE
, 2, 1, NULL
, N_("?Angry workers:A"), N_("Workers: Angry"),
671 NULL
, FUNC_TAG(angry
) },
672 { TRUE
, 10, 1, N_("?city:Workers"),
673 N_("?happy/content/unhappy/angry:H/C/U/A"),
674 N_("Workers: Happy, Content, Unhappy, Angry"),
675 NULL
, FUNC_TAG(workers
) },
677 { FALSE
, 8, 1, N_("Best"), N_("attack"),
678 N_("Best attacking units"), NULL
, FUNC_TAG(attack
)},
679 { FALSE
, 8, 1, N_("Best"), N_("defense"),
680 N_("Best defending units"), NULL
, FUNC_TAG(defense
)},
681 { FALSE
, 2, 1, N_("Units"),
682 /* TRANS: Header "Number of units inside city" */
683 N_("?Present (units):Here"),
684 N_("Number of units present"), NULL
, FUNC_TAG(present
) },
685 { FALSE
, 2, 1, N_("Units"),
686 /* TRANS: Header "Number of units supported by given city" */
687 N_("?Supported (units):Owned"),
688 N_("Number of units supported"), NULL
, FUNC_TAG(supported
) },
690 { /* TRANS: Header "It will take this many turns before city grows" */
691 TRUE
, 14, 1, N_("?food (population):Grow"),
692 N_("?Stock/Target:(Have/Need)"),
693 N_("Turns until growth/famine"),
694 NULL
, FUNC_TAG(growturns
) },
696 { TRUE
, 10, 1, N_("Surplus"), N_("?food/production/trade:F/P/T"),
697 N_("Surplus: Food, Production, Trade"),
698 NULL
, FUNC_TAG(resources
) },
699 { FALSE
, 3, 1, NULL
, N_("?Food surplus [short]:+F"), N_("Surplus: Food"),
700 NULL
, FUNC_TAG(foodplus
) },
701 { FALSE
, 3, 1, NULL
, N_("?Production surplus [short]:+P"),
702 N_("Surplus: Production"), NULL
, FUNC_TAG(prodplus
) },
703 { FALSE
, 3, 1, NULL
, N_("?Production loss (waste) [short]:-P"),
704 N_("Waste"), NULL
, FUNC_TAG(waste
) },
705 { FALSE
, 3, 1, NULL
, N_("?Trade surplus [short]:+T"), N_("Surplus: Trade"),
706 NULL
, FUNC_TAG(tradeplus
) },
707 { FALSE
, 3, 1, NULL
, N_("?Trade loss (corruption) [short]:-T"),
708 N_("Corruption"), NULL
, FUNC_TAG(corruption
) },
710 { TRUE
, 10, 1, N_("Economy"), N_("?gold/luxury/science:G/L/S"),
711 N_("Economy: Gold, Luxuries, Science"),
712 NULL
, FUNC_TAG(output
) },
713 { FALSE
, 3, 1, NULL
, N_("?Gold:G"), N_("Economy: Gold"),
714 NULL
, FUNC_TAG(gold
) },
715 { FALSE
, 3, 1, NULL
, N_("?Luxury:L"), N_("Economy: Luxury"),
716 NULL
, FUNC_TAG(luxury
) },
717 { FALSE
, 3, 1, NULL
, N_("?Science:S"), N_("Economy: Science"),
718 NULL
, FUNC_TAG(science
) },
719 { FALSE
, 3, 1, NULL
, N_("?Continent:C"), N_("Continent number"),
720 NULL
, FUNC_TAG(continent
) },
721 { FALSE
, 1, 1, N_("?number_trade_routes:n"), N_("?number_trade_routes:R"),
722 N_("Number (and total value) of trade routes"),
723 NULL
, FUNC_TAG(trade_routes
) },
724 { FALSE
, 3, 1, NULL
, N_("?pollution [short]:Pol"), N_("Pollution"),
725 NULL
, FUNC_TAG(pollution
) },
726 { FALSE
, 4, 1, N_("?plague risk [short]:Pla"), N_("(%)"), N_("Plague risk"),
727 NULL
, FUNC_TAG(plague_risk
) },
728 { FALSE
, 15, 1, NULL
, N_("?cma:Governor"), N_("Citizen Governor"),
729 NULL
, FUNC_TAG(cma
) },
731 /* TRANS: "BS" = "build slots" */
732 { FALSE
, 3, 1, NULL
, N_("BS"), N_("Maximum units buildable per turn"),
733 NULL
, FUNC_TAG(build_slots
) },
734 { TRUE
, 9, 1, N_("Production"), N_("Turns/Buy"),
735 /*N_("Turns or gold to complete production"), future menu needs translation */
737 NULL
, FUNC_TAG(build_cost
) },
738 { TRUE
, 0, 1, N_("Currently Building"),
739 N_("?Stock/Target:(Have/Need)"),
740 N_("Currently Building"),
741 NULL
, FUNC_TAG(building
) }
744 struct city_report_spec
*city_report_specs
;
745 static int num_creport_cols
;
747 /******************************************************************
748 Some simple wrappers:
749 ******************************************************************/
750 int num_city_report_spec(void)
752 return num_creport_cols
;
754 bool *city_report_spec_show_ptr(int i
)
756 return &(city_report_specs
[i
].show
);
758 const char *city_report_spec_tagname(int i
)
760 return city_report_specs
[i
].tagname
;
763 /******************************************************************
764 Initialize city report data. This deals with ruleset-depedent
765 columns and pre-translates the fields (to make things easier on
766 the GUI writers). Should be called before the GUI starts up.
767 ******************************************************************/
768 void init_city_report_game_data(void)
770 static char sp_explanation
[SP_MAX
][128];
771 static char sp_explanations
[SP_MAX
*128];
772 struct city_report_spec
*p
;
775 num_creport_cols
= ARRAY_SIZE(base_city_report_specs
)
776 + specialist_count() + 1;
778 = fc_realloc(city_report_specs
,
779 num_creport_cols
* sizeof(*city_report_specs
));
780 p
= &city_report_specs
[0];
782 fc_snprintf(sp_explanations
, sizeof(sp_explanations
),
783 "%s", _("Specialists: "));
784 specialist_type_iterate(sp
) {
785 struct specialist
*s
= specialist_by_number(sp
);
790 p
->title1
= Q_("?specialist:S");
791 p
->title2
= specialist_abbreviation_translation(s
);
792 fc_snprintf(sp_explanation
[sp
], sizeof(sp_explanation
[sp
]),
793 _("Specialists: %s"), specialist_plural_translation(s
));
794 cat_snprintf(sp_explanations
, sizeof(sp_explanations
),
795 "%s%s", (sp
== 0) ? "" : ", ",
796 specialist_plural_translation(s
));
797 p
->explanation
= sp_explanation
[sp
];
799 p
->func
= cr_entry_specialist
;
800 p
->tagname
= specialist_rule_name(s
);
802 } specialist_type_iterate_end
;
804 /* Summary column for all specialists. */
806 static char sp_summary
[128];
809 p
->width
= MAX(7, specialist_count()*2-1);
811 p
->title1
= _("Special");
812 fc_snprintf(sp_summary
, sizeof(sp_summary
),
813 "%s", specialists_abbreviation_string());
814 p
->title2
= sp_summary
;
815 p
->explanation
= sp_explanations
;
817 p
->func
= cr_entry_specialists
;
818 p
->tagname
= "specialists";
822 memcpy(p
, base_city_report_specs
,
823 sizeof(base_city_report_specs
));
825 for (i
= 0; i
< ARRAY_SIZE(base_city_report_specs
); i
++) {
827 p
->title1
= Q_(p
->title1
);
830 p
->title2
= Q_(p
->title2
);
832 p
->explanation
= _(p
->explanation
);
836 fc_assert(NUM_CREPORT_COLS
== ARRAY_SIZE(base_city_report_specs
)
837 + specialist_count() + 1);
840 /**********************************************************************
841 The following several functions allow intelligent sorting city report
842 fields by column. This doesn't necessarily do the right thing, but
843 it's better than sorting alphabetically.
845 The GUI gives us two values to compare (as strings). We try to split
846 them into an array of numeric and string fields, then we compare
847 lexicographically. Two numeric fields are compared in the obvious
848 way, two character fields are compared alphabetically. Arbitrarily, a
849 numeric field is sorted before a character field (for "justification"
850 note that numbers are before letters in the ASCII table).
851 **********************************************************************/
853 /* A datum is one short string, or one number.
854 A datum_vector represents a long string of alternating strings and
863 #define SPECVEC_TAG datum
866 /**********************************************************************
867 Init a datum from a substring.
868 **********************************************************************/
869 static void init_datum_string(struct datum
*dat
, const char *left
,
872 int len
= right
- left
;
874 dat
->is_numeric
= FALSE
;
875 dat
->val
.string_value
= fc_malloc(len
+ 1);
876 memcpy(dat
->val
.string_value
, left
, len
);
877 dat
->val
.string_value
[len
] = 0;
880 /**********************************************************************
881 Init a datum from a number (a float because we happen to use
883 **********************************************************************/
884 static void init_datum_number(struct datum
*dat
, float val
)
886 dat
->is_numeric
= TRUE
;
887 dat
->val
.numeric_value
= val
;
890 /**********************************************************************
891 Free the data associated with a datum -- that is, free the string if
893 **********************************************************************/
894 static void free_datum(struct datum
*dat
)
896 if(!dat
->is_numeric
) {
897 free(dat
->val
.string_value
);
901 /**********************************************************************
902 Compare two data items as described above:
903 - numbers in the obvious way
904 - strings alphabetically
905 - number < string for no good reason
906 **********************************************************************/
907 static int datum_compare(const struct datum
*a
, const struct datum
*b
)
909 if(a
->is_numeric
== b
->is_numeric
) {
911 if (a
->val
.numeric_value
== b
->val
.numeric_value
) {
913 } else if (a
->val
.numeric_value
< b
->val
.numeric_value
) {
915 } else if (a
->val
.numeric_value
> b
->val
.numeric_value
) {
918 return 0; /* shrug */
921 return strcmp(a
->val
.string_value
, b
->val
.string_value
);
932 /**********************************************************************
933 Compare two strings of data lexicographically.
934 **********************************************************************/
935 static int data_compare(const struct datum_vector
*a
,
936 const struct datum_vector
*b
)
940 n
= MIN(a
->size
, b
->size
);
942 for(i
= 0; i
< n
; i
++) {
943 int cmp
= datum_compare(&a
->p
[i
], &b
->p
[i
]);
950 /* The first n fields match; whoever has more fields goes last.
951 If they have equal numbers, the two really are equal. */
952 return a
->size
- b
->size
;
956 /**********************************************************************
957 Split a string into a vector of datum.
958 **********************************************************************/
959 static void split_string(struct datum_vector
*data
, const char *str
)
961 const char *string_start
;
963 datum_vector_init(data
);
970 value
= strtof(str
, &endptr
);
971 if (errno
!= 0 || endptr
== str
|| !isfinite(value
)) {
972 /* that wasn't a sensible number; go on */
975 /* that was a number, so stop the string we were parsing, add
976 it (unless it's empty), then add the number we just parsed */
979 if(str
!= string_start
) {
980 init_datum_string(&d
, string_start
, str
);
981 datum_vector_append(data
, d
);
984 init_datum_number(&d
, value
);
985 datum_vector_append(data
, d
);
987 /* finally, update the string position pointers */
988 string_start
= str
= endptr
;
992 /* if we have anything leftover then it's a string */
993 if(str
!= string_start
) {
996 init_datum_string(&d
, string_start
, str
);
997 datum_vector_append(data
, d
);
1001 /**********************************************************************
1002 Free every datum in the vector.
1003 **********************************************************************/
1004 static void free_data(struct datum_vector
*data
)
1008 for(i
= 0; i
< data
->size
; i
++) {
1009 free_datum(&data
->p
[i
]);
1011 datum_vector_free(data
);
1015 /**********************************************************************
1016 The real function: split the two strings, and compare them.
1017 **********************************************************************/
1018 int cityrepfield_compare(const char *str1
, const char *str2
)
1020 struct datum_vector data1
, data2
;
1025 } else if (NULL
== str1
) {
1027 } else if (NULL
== str2
) {
1031 split_string(&data1
, str1
);
1032 split_string(&data2
, str2
);
1034 retval
= data_compare(&data1
, &data2
);