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 wldlg.c - description
15 -------------------
16 begin : Wed Sep 18 2002
17 copyright : (C) 2002 by Rafał Bursig
18 email : Rafał Bursig <bursig@poczta.fm>
19 ***********************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 #include <fc_config.h>
23 #endif
24
25 #include <stdlib.h>
26
27 /* SDL */
28 #include <SDL/SDL.h>
29
30 /* utility */
31 #include "fcintl.h"
32 #include "log.h"
33
34 /* common */
35 #include "game.h"
36 #include "movement.h"
37 #include "unit.h"
38
39 /* client */
40 #include "client_main.h"
41 #include "climisc.h"
42 #include "global_worklist.h"
43
44 /* gui-sdl */
45 #include "citydlg.h"
46 #include "colors.h"
47 #include "graphics.h"
48 #include "gui_iconv.h"
49 #include "gui_id.h"
50 #include "gui_main.h"
51 #include "gui_tilespec.h"
52 #include "helpdlg.h"
53 #include "mapview.h"
54 #include "sprite.h"
55 #include "themespec.h"
56 #include "widget.h"
57
58 #include "wldlg.h"
59
60 #define TARGETS_COL 4
61 #define TARGETS_ROW 4
62
63 struct EDITOR {
64 struct widget *pBeginWidgetList;
65 struct widget *pEndWidgetList;/* window */
66
67 struct ADVANCED_DLG *pTargets;
68 struct ADVANCED_DLG *pWork;
69 struct ADVANCED_DLG *pGlobal;
70
71 struct city *pCity;
72 int global_worklist_id;
73 struct worklist worklist_copy;
74 char worklist_name[MAX_LEN_NAME];
75
76 /* shortcuts */
77 struct widget *pDock;
78 struct widget *pWorkList_Counter;
79
80 struct widget *pProduction_Name;
81 struct widget *pProduction_Progres;
82
83 int stock;
84 struct universal currently_building;
85
86 } *pEditor = NULL;
87
88
89 static int worklist_editor_item_callback(struct widget *pWidget);
90 static SDL_Surface * get_progress_icon(int stock, int cost, int *progress);
91 static const char * get_production_name(struct city *pCity,
92 struct universal prod, int *cost);
93 static void refresh_worklist_count_label(void);
94 static void refresh_production_label(int stock);
95
96 /* =========================================================== */
97
98 /* Worklist Editor Window Callback */
window_worklist_editor_callback(struct widget * pWidget)99 static int window_worklist_editor_callback(struct widget *pWidget)
100 {
101 return -1;
102 }
103
104 /* Popdwon Worklist Editor */
popdown_worklist_editor_callback(struct widget * pWidget)105 static int popdown_worklist_editor_callback(struct widget *pWidget)
106 {
107 if (Main.event.button.button == SDL_BUTTON_LEFT) {
108 popdown_worklist_editor();
109 }
110 return -1;
111 }
112
113 /*
114 * Commit changes to city/global worklist
115 * In City Mode Remove Double entry of Imprv/Woder Targets from list.
116 */
ok_worklist_editor_callback(struct widget * pWidget)117 static int ok_worklist_editor_callback(struct widget *pWidget)
118 {
119 if (Main.event.button.button == SDL_BUTTON_LEFT) {
120 int i, j;
121 struct city *pCity = pEditor->pCity;
122
123 /* remove duplicate entry of impv./wonder target from worklist */
124 for (i = 0; i < worklist_length(&pEditor->worklist_copy); i++) {
125
126 if (VUT_IMPROVEMENT == pEditor->worklist_copy.entries[i].kind) {
127 for (j = i + 1; j < worklist_length(&pEditor->worklist_copy); j++) {
128 if (VUT_IMPROVEMENT == pEditor->worklist_copy.entries[j].kind
129 && pEditor->worklist_copy.entries[i].value.building ==
130 pEditor->worklist_copy.entries[j].value.building) {
131 worklist_remove(&pEditor->worklist_copy, j);
132 }
133 }
134 }
135 }
136
137 if (pCity) {
138 /* remove duplicate entry of currently building impv./wonder from worklist */
139 if (VUT_IMPROVEMENT == pEditor->currently_building.kind) {
140 for (i = 0; i < worklist_length(&pEditor->worklist_copy); i++) {
141 if (VUT_IMPROVEMENT == pEditor->worklist_copy.entries[i].kind
142 && pEditor->worklist_copy.entries[i].value.building ==
143 pEditor->currently_building.value.building) {
144 worklist_remove(&pEditor->worklist_copy, i);
145 }
146 }
147 }
148
149 /* change production */
150 if (!are_universals_equal(&pCity->production, &pEditor->currently_building)) {
151 city_change_production(pCity, &pEditor->currently_building);
152 }
153
154 /* commit new city worklist */
155 city_set_worklist(pCity, &pEditor->worklist_copy);
156 } else {
157 /* commit global worklist */
158 struct global_worklist *pGWL = global_worklist_by_id(pEditor->global_worklist_id);
159
160 if (pGWL) {
161 global_worklist_set(pGWL, &pEditor->worklist_copy);
162 global_worklist_set_name(pGWL, pEditor->worklist_name);
163 update_worklist_report_dialog();
164 }
165 }
166
167 /* popdown dialog */
168 popdown_worklist_editor();
169 }
170
171 return -1;
172 }
173
174 /*
175 * Rename Global Worklist
176 */
rename_worklist_editor_callback(struct widget * pWidget)177 static int rename_worklist_editor_callback(struct widget *pWidget)
178 {
179 if (Main.event.button.button == SDL_BUTTON_LEFT) {
180 if(pWidget->string16->text) {
181 char *pText = convert_to_chars(pWidget->string16->text);
182 fc_snprintf(pEditor->worklist_name, MAX_LEN_NAME, "%s", pText);
183 FC_FREE(pText);
184 } else {
185 /* empty input -> restore previous content */
186 copy_chars_to_string16(pWidget->string16, pEditor->worklist_name);
187 widget_redraw(pWidget);
188 widget_mark_dirty(pWidget);
189 flush_dirty();
190 }
191 }
192 return -1;
193 }
194
195 /* ====================================================================== */
196
197 /*
198 * Add target to worklist.
199 */
add_target_to_worklist(struct widget * pTarget)200 static void add_target_to_worklist(struct widget *pTarget)
201 {
202 struct widget *pBuf = NULL, *pDock = NULL;
203 SDL_String16 *pStr = NULL;
204 int i;
205 struct universal prod = cid_decode(MAX_ID - pTarget->ID);
206
207 set_wstate(pTarget, FC_WS_SELLECTED);
208 widget_redraw(pTarget);
209 widget_flush(pTarget);
210
211 /* Deny adding currently building Impr/Wonder Target */
212 if (pEditor->pCity
213 && VUT_IMPROVEMENT == prod.kind
214 && are_universals_equal(&prod, &pEditor->currently_building)) {
215 return;
216 }
217
218 if (worklist_length(&pEditor->worklist_copy) >= MAX_LEN_WORKLIST - 1) {
219 return;
220 }
221
222 for (i = 0; i < worklist_length(&pEditor->worklist_copy); i++) {
223 if (VUT_IMPROVEMENT == prod.kind
224 && are_universals_equal(&prod, &pEditor->worklist_copy.entries[i])) {
225 return;
226 }
227 }
228
229 worklist_append(&pEditor->worklist_copy, &prod);
230
231 /* create widget entry */
232 if (VUT_UTYPE == prod.kind) {
233 pStr = create_str16_from_char(utype_name_translation(prod.value.utype), adj_font(10));
234 } else {
235 pStr = create_str16_from_char(city_improvement_name_translation(pEditor->pCity, prod.value.building),
236 adj_font(10));
237 }
238
239 pStr->style |= SF_CENTER;
240 pBuf = create_iconlabel(NULL, pTarget->dst, pStr,
241 (WF_RESTORE_BACKGROUND|WF_FREE_DATA));
242
243 set_wstate(pBuf, FC_WS_NORMAL);
244 pBuf->action = worklist_editor_item_callback;
245
246 pBuf->data.ptr = fc_calloc(1, sizeof(int));
247 *((int *)pBuf->data.ptr) = worklist_length(&pEditor->worklist_copy) - 1;
248
249 pBuf->ID = MAX_ID - cid_encode(prod);
250
251 if(pEditor->pWork->pBeginActiveWidgetList) {
252 pDock = pEditor->pWork->pBeginActiveWidgetList;
253 } else {
254 pDock = pEditor->pDock;
255 }
256
257 /* FIXME */
258 #if 0
259 if (worklist_length(&pEditor->worklist_copy) > pEditor->pWork->pScroll->active + 1) {
260
261 setup_vertical_widgets_position(1,
262 pEditor->pEndWidgetList->area.x + adj_size(2),
263 get_widget_pointer_from_main_list(ID_WINDOW)->area.y + adj_size(152) +
264 pEditor->pWork->pScroll->pUp_Left_Button->size.h + 1,
265 adj_size(126), 0, pEditor->pWork->pBeginWidgetList,
266 pEditor->pWork->pEndWidgetList);
267
268 setup_vertical_scrollbar_area(pEditor->pWork->pScroll,
269 pEditor->pEndWidgetList->area.x + adj_size(2),
270 get_widget_pointer_from_main_list(ID_WINDOW)->area.y + adj_size(152),
271 adj_size(225), FALSE);
272
273 show_scrollbar(pEditor->pWork->pScroll);
274 }
275 #endif
276
277 add_widget_to_vertical_scroll_widget_list(pEditor->pWork, pBuf,
278 pDock, FALSE,
279 pEditor->pEndWidgetList->area.x + adj_size(2),
280 pEditor->pEndWidgetList->area.y + adj_size(152));
281
282 pBuf->size.w = adj_size(126);
283
284 refresh_worklist_count_label();
285 redraw_group(pEditor->pWork->pBeginWidgetList,
286 pEditor->pWork->pEndWidgetList, TRUE);
287 flush_dirty();
288 }
289
290 /**************************************************************************
291 Find if two targets are the same class (unit, imprv. , wonder).
292 This is needed by calculation of change production shields penalty.
293 [similar to are_universals_equal()]
294 **************************************************************************/
are_prods_same_class(const struct universal * one,const struct universal * two)295 static bool are_prods_same_class(const struct universal *one,
296 const struct universal *two)
297 {
298 if (one->kind != two->kind) {
299 return FALSE;
300 }
301
302 if (VUT_IMPROVEMENT == one->kind) {
303 if (is_wonder(one->value.building)) {
304 return is_wonder(two->value.building);
305 } else {
306 return !is_wonder(two->value.building);
307 }
308 }
309
310 return FALSE;
311 }
312
313 /**************************************************************************
314 Change production in editor shell, calculate production shields penalty and
315 refresh production progress label
316 **************************************************************************/
change_production(struct universal * prod)317 static void change_production(struct universal *prod)
318 {
319 if (!are_prods_same_class(&pEditor->currently_building, prod)) {
320 if (pEditor->stock != pEditor->pCity->shield_stock) {
321 if (are_prods_same_class(&pEditor->pCity->production, prod)) {
322 pEditor->stock = pEditor->pCity->shield_stock;
323 }
324 } else {
325 pEditor->stock =
326 city_change_production_penalty(pEditor->pCity, prod);
327 }
328 }
329
330 pEditor->currently_building = *prod;
331 refresh_production_label(pEditor->stock);
332
333 return;
334 }
335
336 /*
337 * Change production of city but only in Editor.
338 * You must commit this changes by exit editor via OK button (commit function).
339 *
340 * This function don't remove double imprv./wonder target entry from worklist
341 * and allow more entry of such target (In Production and worklist - this is
342 * fixed by commit function).
343 */
add_target_to_production(struct widget * pTarget)344 static void add_target_to_production(struct widget *pTarget)
345 {
346 int dummy;
347 struct universal prod;
348
349 fc_assert_ret(pTarget != NULL);
350
351 /* redraw Target Icon */
352 set_wstate(pTarget, FC_WS_SELLECTED);
353 widget_redraw(pTarget);
354 widget_flush(pTarget);
355
356 prod = cid_decode(MAX_ID - pTarget->ID);
357
358 /* check if we change to the same target */
359 if (are_universals_equal(&prod, &pEditor->currently_building)) {
360 /* comit changes and exit - double click detection */
361 ok_worklist_editor_callback(NULL);
362 return;
363 }
364
365 change_production(&prod);
366
367 /* change Production Text Label in Worklist Widget list */
368 copy_chars_to_string16(pEditor->pWork->pEndActiveWidgetList->string16,
369 get_production_name(pEditor->pCity, prod, &dummy));
370
371 pEditor->pWork->pEndActiveWidgetList->ID = MAX_ID - cid_encode(prod);
372
373 widget_redraw(pEditor->pWork->pEndActiveWidgetList);
374 widget_mark_dirty(pEditor->pWork->pEndActiveWidgetList);
375
376 flush_dirty();
377 }
378
379 /* Get Help Info about target */
get_target_help_data(struct widget * pTarget)380 static void get_target_help_data(struct widget *pTarget)
381 {
382 struct universal prod;
383
384 fc_assert_ret(pTarget != NULL);
385
386 /* redraw Target Icon */
387 set_wstate(pTarget, FC_WS_SELLECTED);
388 widget_redraw(pTarget);
389 /*widget_flush(pTarget);*/
390
391 prod = cid_decode(MAX_ID - pTarget->ID);
392
393 if (VUT_UTYPE == prod.kind)
394 {
395 popup_unit_info(utype_number(prod.value.utype));
396 } else {
397 popup_impr_info(improvement_number(prod.value.building));
398 }
399
400 }
401
402
403 /*
404 * Targets callback
405 * left mouse button -> In city mode change production to target.
406 In global mode add target to worklist.
407 * middle mouse button -> get target "help"
408 * right mouse button -> add target to worklist.
409 */
worklist_editor_targets_callback(struct widget * pWidget)410 static int worklist_editor_targets_callback(struct widget *pWidget)
411 {
412 if (Main.event.type == SDL_MOUSEBUTTONDOWN) {
413 switch (Main.event.button.button) {
414 case SDL_BUTTON_LEFT:
415 if(pEditor->pCity) {
416 add_target_to_production(pWidget);
417 } else {
418 add_target_to_worklist(pWidget);
419 }
420 break;
421 case SDL_BUTTON_MIDDLE:
422 get_target_help_data(pWidget);
423 break;
424 case SDL_BUTTON_RIGHT:
425 add_target_to_worklist(pWidget);
426 break;
427 default:
428 ;/* do nothing */
429 break;
430 }
431 }
432 return -1;
433 }
434
435 /* ====================================================================== */
436
437 /*
438 * Remove element from worklist or
439 * remove currently building imprv/unit and change production to first worklist
440 * element (or Capitalizations if worklist is empty)
441 */
remove_item_from_worklist(struct widget * pItem)442 static void remove_item_from_worklist(struct widget *pItem)
443 {
444 /* only one item (production) is left */
445 if (worklist_is_empty(&pEditor->worklist_copy))
446 return;
447
448 if(pItem->data.ptr) {
449 /* correct "data" widget fiels */
450 struct widget *pBuf = pItem;
451 if(pBuf != pEditor->pWork->pBeginActiveWidgetList) {
452 do{
453 pBuf = pBuf->prev;
454 *((int *)pBuf->data.ptr) = *((int *)pBuf->data.ptr) - 1;
455 } while(pBuf != pEditor->pWork->pBeginActiveWidgetList);
456 }
457
458 /* remove element from worklist */
459 worklist_remove(&pEditor->worklist_copy, *((int *)pItem->data.ptr));
460
461 /* remove widget from widget list */
462 del_widget_from_vertical_scroll_widget_list(pEditor->pWork, pItem);
463 } else {
464 /* change productions to first worklist element */
465 struct widget *pBuf = pItem->prev;
466
467 change_production(&pEditor->worklist_copy.entries[0]);
468 worklist_advance(&pEditor->worklist_copy);
469 del_widget_from_vertical_scroll_widget_list(pEditor->pWork, pItem);
470 FC_FREE(pBuf->data.ptr);
471 if (pBuf != pEditor->pWork->pBeginActiveWidgetList) {
472 do {
473 pBuf = pBuf->prev;
474 *((int *)pBuf->data.ptr) = *((int *)pBuf->data.ptr) - 1;
475 } while (pBuf != pEditor->pWork->pBeginActiveWidgetList);
476 }
477 }
478
479 /* FIXME: fix scrollbar code */
480 #if 0
481 /* worklist_length(&pEditor->worklist_copy): without production */
482 if (worklist_length(&pEditor->worklist_copy) <= pEditor->pWork->pScroll->active + 1) {
483
484 setup_vertical_widgets_position(1,
485 pEditor->pEndWidgetList->area.x + adj_size(2),
486 get_widget_pointer_from_main_list(ID_WINDOW)->area.y + adj_size(152),
487 adj_size(126), 0, pEditor->pWork->pBeginWidgetList,
488 pEditor->pWork->pEndWidgetList);
489 #if 0
490 setup_vertical_scrollbar_area(pEditor->pWork->pScroll,
491 pEditor->pEndWidgetList->area.x + adj_size(2),
492 get_widget_pointer_from_main_list(ID_WINDOW)->area.y + adj_size(152),
493 adj_size(225), FALSE);*/
494 #endif
495 hide_scrollbar(pEditor->pWork->pScroll);
496 }
497 #endif
498
499 refresh_worklist_count_label();
500 redraw_group(pEditor->pWork->pBeginWidgetList,
501 pEditor->pWork->pEndWidgetList, TRUE);
502 flush_dirty();
503 }
504
505 /*
506 * Swap worklist entries DOWN.
507 * Fuction swap current element with next element of worklist.
508 *
509 * If pItem is last widget or there is only one widget on widgets list
510 * fuction remove this widget from widget list and target from worklist
511 *
512 * In City mode, when pItem is first worklist element, function make
513 * change production (currently building is moved to first element of worklist
514 * and first element of worklist is build).
515 */
swap_item_down_from_worklist(struct widget * pItem)516 static void swap_item_down_from_worklist(struct widget *pItem)
517 {
518 Uint16 *pText, ID;
519 bool changed = FALSE;
520 struct universal tmp;
521
522 if(pItem == pEditor->pWork->pBeginActiveWidgetList) {
523 remove_item_from_worklist(pItem);
524 return;
525 }
526
527 pText = pItem->string16->text;
528 ID = pItem->ID;
529
530 /* second item or higher was clicked */
531 if(pItem->data.ptr) {
532 /* worklist operations -> swap down */
533 int row = *((int *)pItem->data.ptr);
534
535 tmp = pEditor->worklist_copy.entries[row];
536 pEditor->worklist_copy.entries[row] = pEditor->worklist_copy.entries[row + 1];
537 pEditor->worklist_copy.entries[row + 1] = tmp;
538
539 changed = TRUE;
540 } else {
541 /* first item was clicked -> change production */
542 change_production(&pEditor->worklist_copy.entries[0]);
543 pEditor->worklist_copy.entries[0] = cid_decode(MAX_ID - ID);
544 changed = TRUE;
545 }
546
547 if(changed) {
548 pItem->string16->text = pItem->prev->string16->text;
549 pItem->ID = pItem->prev->ID;
550
551 pItem->prev->string16->text = pText;
552 pItem->prev->ID = ID;
553
554 redraw_group(pEditor->pWork->pBeginWidgetList,
555 pEditor->pWork->pEndWidgetList, TRUE);
556 flush_dirty();
557 }
558
559 }
560
561 /*
562 * Swap worklist entries UP.
563 * Fuction swap current element with prev. element of worklist.
564 *
565 * If pItem is first widget on widgets list fuction remove this widget
566 * from widget list and target from worklist (global mode)
567 * or from production (city mode)
568 *
569 * In City mode, when pItem is first worklist element, function make
570 * change production (currently building is moved to first element of worklist
571 * and first element of worklist is build).
572 */
swap_item_up_from_worklist(struct widget * pItem)573 static void swap_item_up_from_worklist(struct widget *pItem)
574 {
575 Uint16 *pText = pItem->string16->text;
576 Uint16 ID = pItem->ID;
577 bool changed = FALSE;
578 struct universal tmp;
579
580 /* first item was clicked -> remove */
581 if(pItem == pEditor->pWork->pEndActiveWidgetList) {
582 remove_item_from_worklist(pItem);
583 return;
584 }
585
586 /* third item or higher was clicked */
587 if(pItem->data.ptr && *((int *)pItem->data.ptr) > 0) {
588 /* worklist operations -> swap up*/
589 int row = *((int *)pItem->data.ptr);
590
591 tmp = pEditor->worklist_copy.entries[row];
592 pEditor->worklist_copy.entries[row] = pEditor->worklist_copy.entries[row - 1];
593 pEditor->worklist_copy.entries[row - 1] = tmp;
594
595 changed = TRUE;
596 } else {
597 /* second item was clicked -> change production ... */
598 tmp = pEditor->currently_building;
599 change_production(&pEditor->worklist_copy.entries[0]);
600 pEditor->worklist_copy.entries[0] = tmp;
601
602 changed = TRUE;
603 }
604
605 if(changed) {
606 pItem->string16->text = pItem->next->string16->text;
607 pItem->ID = pItem->next->ID;
608
609 pItem->next->string16->text = pText;
610 pItem->next->ID = ID;
611
612 redraw_group(pEditor->pWork->pBeginWidgetList,
613 pEditor->pWork->pEndWidgetList, TRUE);
614 flush_dirty();
615 }
616 }
617
618 /*
619 * worklist callback
620 * left mouse button -> swap entries up.
621 * middle mouse button -> remove element from list
622 * right mouse button -> swap entries down.
623 */
worklist_editor_item_callback(struct widget * pWidget)624 static int worklist_editor_item_callback(struct widget *pWidget)
625 {
626 if (Main.event.type == SDL_MOUSEBUTTONDOWN) {
627 switch (Main.event.button.button) {
628 case SDL_BUTTON_LEFT:
629 swap_item_up_from_worklist(pWidget);
630 break;
631 case SDL_BUTTON_MIDDLE:
632 remove_item_from_worklist(pWidget);
633 break;
634 case SDL_BUTTON_RIGHT:
635 swap_item_down_from_worklist(pWidget);
636 break;
637 default:
638 ;/* do nothing */
639 break;
640 }
641 }
642 return -1;
643 }
644 /* ======================================================= */
645
646 /*
647 * Add global worklist to city worklist starting from last free entry.
648 * Add only avilable targets in current game state.
649 * If global worklist have more targets that city worklist have free
650 * entries then we adding only first part of global worklist.
651 */
add_global_worklist(struct widget * pWidget)652 static void add_global_worklist(struct widget *pWidget)
653 {
654 struct global_worklist *pGWL = global_worklist_by_id(MAX_ID - pWidget->ID);
655 struct widget *pBuf = pEditor->pWork->pEndActiveWidgetList;
656 const struct worklist *pWorkList;
657 int count, firstfree;
658
659 if (!pGWL
660 || !(pWorkList = global_worklist_get(pGWL))
661 || worklist_is_empty(pWorkList)) {
662 return;
663 }
664
665 if (worklist_length(&pEditor->worklist_copy) >= MAX_LEN_WORKLIST - 1) {
666 /* worklist is full */
667 return;
668 }
669
670 firstfree = worklist_length(&pEditor->worklist_copy);
671 /* copy global worklist to city worklist */
672 for (count = 0 ; count < worklist_length(pWorkList); count++) {
673
674 /* global worklist can have targets unavilable in current state of game
675 then we must remove those targets from new city worklist */
676 if (!can_city_build_later(pEditor->pCity, &pWorkList->entries[count])) {
677 continue;
678 }
679
680 worklist_append(&pEditor->worklist_copy, &pWorkList->entries[count]);
681
682 /* create widget */
683 if(VUT_UTYPE == pWorkList->entries[count].kind) {
684 pBuf = create_iconlabel(NULL, pWidget->dst,
685 create_str16_from_char(
686 utype_name_translation(pWorkList->entries[count].value.utype),
687 adj_font(10)),
688 (WF_RESTORE_BACKGROUND|WF_FREE_DATA));
689 pBuf->ID = MAX_ID - cid_encode_unit(pWorkList->entries[count].value.utype);
690 } else {
691 pBuf = create_iconlabel(NULL, pWidget->dst,
692 create_str16_from_char(
693 city_improvement_name_translation(pEditor->pCity,
694 pWorkList->entries[count].value.building),
695 adj_font(10)),
696 (WF_RESTORE_BACKGROUND|WF_FREE_DATA));
697 pBuf->ID = MAX_ID - cid_encode_building(pWorkList->entries[count].value.building);
698 }
699
700 pBuf->string16->style |= SF_CENTER;
701 set_wstate(pBuf, FC_WS_NORMAL);
702 pBuf->action = worklist_editor_item_callback;
703 pBuf->size.w = adj_size(126);
704 pBuf->data.ptr = fc_calloc(1, sizeof(int));
705 *((int *)pBuf->data.ptr) = firstfree;
706
707 add_widget_to_vertical_scroll_widget_list(pEditor->pWork,
708 pBuf, pEditor->pWork->pBeginActiveWidgetList, FALSE,
709 pEditor->pEndWidgetList->area.x + adj_size(2),
710 pEditor->pEndWidgetList->area.y + adj_size(152));
711
712 firstfree++;
713 if(firstfree == MAX_LEN_WORKLIST - 1) {
714 break;
715 }
716 }
717
718 refresh_worklist_count_label();
719 redraw_group(pEditor->pWork->pBeginWidgetList,
720 pEditor->pWork->pEndWidgetList, TRUE);
721
722 flush_dirty();
723 }
724
725 /*
726 * Clear city worklist and copy here global worklist.
727 * Copy only avilable targets in current game state.
728 * If all targets are unavilable then leave city worklist untouched.
729 */
set_global_worklist(struct widget * pWidget)730 static void set_global_worklist(struct widget *pWidget)
731 {
732 struct global_worklist *pGWL = global_worklist_by_id(MAX_ID - pWidget->ID);
733 struct widget *pBuf = pEditor->pWork->pEndActiveWidgetList;
734 const struct worklist *pWorkList;
735 struct worklist wl;
736 int count, wl_count;
737 struct universal target;
738
739 if (!pGWL
740 || !(pWorkList = global_worklist_get(pGWL))
741 || worklist_is_empty(pWorkList)) {
742 return;
743 }
744
745 /* clear tmp worklist */
746 worklist_init(&wl);
747
748 wl_count = 0;
749 /* copy global worklist to city worklist */
750 for (count = 0; count < worklist_length(pWorkList); count++) {
751
752 /* global worklist can have targets unavilable in current state of game
753 then we must remove those targets from new city worklist */
754 if (!can_city_build_later(pEditor->pCity, &pWorkList->entries[count])) {
755 continue;
756 }
757
758 wl.entries[wl_count] = pWorkList->entries[count];
759 wl_count++;
760 }
761 /* --------------------------------- */
762
763 if (!worklist_is_empty(&wl)) {
764 /* free old widget list */
765 if (pBuf != pEditor->pWork->pBeginActiveWidgetList) {
766 pBuf = pBuf->prev;
767 if (pBuf != pEditor->pWork->pBeginActiveWidgetList) {
768 do {
769 pBuf = pBuf->prev;
770 del_widget_from_vertical_scroll_widget_list(pEditor->pWork, pBuf->next);
771 } while (pBuf != pEditor->pWork->pBeginActiveWidgetList);
772 }
773 del_widget_from_vertical_scroll_widget_list(pEditor->pWork, pBuf);
774 }
775 /* --------------------------------- */
776
777 worklist_copy(&pEditor->worklist_copy, &wl);
778
779 /* --------------------------------- */
780 /* create new widget list */
781 for(count = 0; count < MAX_LEN_WORKLIST; count++) {
782 /* end of list */
783 if(!worklist_peek_ith(&pEditor->worklist_copy, &target, count)) {
784 break;
785 }
786
787 if(VUT_UTYPE == target.kind) {
788 pBuf = create_iconlabel(NULL, pWidget->dst,
789 create_str16_from_char(utype_name_translation(target.value.utype),
790 adj_font(10)),
791 (WF_RESTORE_BACKGROUND|WF_FREE_DATA));
792 pBuf->ID = MAX_ID - B_LAST - utype_number(target.value.utype);
793 } else {
794 pBuf = create_iconlabel(NULL, pWidget->dst,
795 create_str16_from_char(city_improvement_name_translation(pEditor->pCity, target.value.building),
796 adj_font(10)),
797 (WF_RESTORE_BACKGROUND|WF_FREE_DATA));
798 pBuf->ID = MAX_ID - improvement_number(target.value.building);
799 }
800 pBuf->string16->style |= SF_CENTER;
801 set_wstate(pBuf, FC_WS_NORMAL);
802 pBuf->action = worklist_editor_item_callback;
803 pBuf->size.w = adj_size(126);
804 pBuf->data.ptr = fc_calloc(1, sizeof(int));
805 *((int *)pBuf->data.ptr) = count;
806
807 add_widget_to_vertical_scroll_widget_list(pEditor->pWork,
808 pBuf, pEditor->pWork->pBeginActiveWidgetList, FALSE,
809 pEditor->pEndWidgetList->area.x + adj_size(2),
810 pEditor->pEndWidgetList->area.y + adj_size(152));
811 }
812
813 refresh_worklist_count_label();
814 redraw_group(pEditor->pWork->pBeginWidgetList,
815 pEditor->pWork->pEndWidgetList, TRUE);
816
817 flush_dirty();
818 }
819 }
820
821 /*
822 * global worklist callback
823 * left mouse button -> add global worklist to current city list
824 * right mouse button -> clear city worklist and copy here global worklist.
825 *
826 * There are problems with imprv./wonder targets because those can't be doubled
827 * on worklist and adding/setting can give you situation that global worklist
828 * have imprv./wonder entry that exist on city worklist or in building state.
829 * I don't make such check here and allow this "functionality" because doubled
830 * imprv./wonder entry are removed from city worklist during "commit" phase.
831 */
global_worklist_callback(struct widget * pWidget)832 static int global_worklist_callback(struct widget *pWidget)
833 {
834 if (Main.event.type == SDL_MOUSEBUTTONDOWN) {
835 switch (Main.event.button.button) {
836 case SDL_BUTTON_LEFT:
837 add_global_worklist(pWidget);
838 break;
839 case SDL_BUTTON_MIDDLE:
840 /* nothing */
841 break;
842 case SDL_BUTTON_RIGHT:
843 set_global_worklist(pWidget);
844 break;
845 default:
846 ;/* do nothing */
847 break;
848 }
849 }
850 return -1;
851 }
852
853 /* ======================================================= */
854
855 /* return full unit/imprv. name and build cost in "cost" pointer */
get_production_name(struct city * pCity,struct universal prod,int * cost)856 static const char * get_production_name(struct city *pCity,
857 struct universal prod, int *cost)
858 {
859 fc_assert_ret_val(cost != NULL, NULL);
860
861 *cost = universal_build_shield_cost(&prod);
862 if(VUT_UTYPE == prod.kind) {
863 return utype_name_translation(prod.value.utype);
864 } else {
865 return city_improvement_name_translation(pCity, prod.value.building);
866 }
867 }
868
869 /**************************************************************************
870 Return progress icon (bar) of current production state
871 stock - current shields stocks
872 cost - unit/imprv. cost
873 function return in "progress" pointer (0 - 100 %) progress in numbers
874 **************************************************************************/
get_progress_icon(int stock,int cost,int * progress)875 static SDL_Surface * get_progress_icon(int stock, int cost, int *progress)
876 {
877 SDL_Surface *pIcon = NULL;
878 int width;
879
880 fc_assert_ret_val(progress != NULL, NULL);
881
882 if (stock < cost) {
883 width = ((float)stock / cost) * adj_size(116.0);
884 *progress = ((float)stock / cost) * 100.0;
885 if(!width && stock) {
886 *progress = 1;
887 width = 1;
888 }
889 } else {
890 width = adj_size(116);
891 *progress = 100;
892 }
893
894 pIcon = create_bcgnd_surf(current_theme->Edit, FC_WS_NORMAL, adj_size(120),
895 adj_size(30));
896
897 if (width) {
898 SDL_Rect dst = {2,1,0,0};
899 SDL_Surface *pBuf = create_bcgnd_surf(current_theme->Button, FC_WS_DISABLED,
900 width, adj_size(28));
901
902 alphablit(pBuf, NULL, pIcon, &dst);
903 FREESURFACE(pBuf);
904 }
905
906 return pIcon;
907 }
908
909 /*
910 * update and redraw production name label in worklist editor
911 * stock - pCity->shields_stock or current stock after chnge production lost.
912 */
refresh_production_label(int stock)913 static void refresh_production_label(int stock)
914 {
915 int cost, turns;
916 char cBuf[64];
917 SDL_Rect area;
918 bool gold_prod = improvement_has_flag(pEditor->currently_building.value.building, IF_GOLD);
919 const char *name = get_production_name(pEditor->pCity,
920 pEditor->currently_building, &cost);
921
922 if (VUT_IMPROVEMENT == pEditor->currently_building.kind && gold_prod) {
923 int gold = MAX(0, pEditor->pCity->surplus[O_SHIELD]);
924
925 fc_snprintf(cBuf, sizeof(cBuf),
926 PL_("%s\n%d gold per turn",
927 "%s\n%d gold per turn", gold),
928 name, gold);
929 } else {
930 if (stock < cost) {
931 turns = city_turns_to_build(pEditor->pCity,
932 &pEditor->currently_building, TRUE);
933 if (turns == 999) {
934 fc_snprintf(cBuf, sizeof(cBuf), _("%s\nblocked!"), name);
935 } else {
936 fc_snprintf(cBuf, sizeof(cBuf), _("%s\n%d %s"),
937 name, turns, PL_("turn", "turns", turns));
938 }
939 } else {
940 fc_snprintf(cBuf, sizeof(cBuf), _("%s\nfinished!"), name);
941 }
942 }
943 copy_chars_to_string16(pEditor->pProduction_Name->string16, cBuf);
944
945 widget_undraw(pEditor->pProduction_Name);
946 remake_label_size(pEditor->pProduction_Name);
947
948 pEditor->pProduction_Name->size.x = pEditor->pEndWidgetList->area.x +
949 (adj_size(130) - pEditor->pProduction_Name->size.w) / 2;
950
951 /* Can't just widget_mark_dirty(), as it may have reduced in size */
952 area.x = pEditor->pEndWidgetList->area.x; /* left edge of client area */
953 area.y = pEditor->pProduction_Name->size.y;
954 area.w = adj_size(130);
955 area.h = pEditor->pProduction_Name->size.h;
956 layer_rect_to_screen_rect(pEditor->pEndWidgetList->dst, &area);
957
958 if (get_wflags(pEditor->pProduction_Name) & WF_RESTORE_BACKGROUND) {
959 refresh_widget_background(pEditor->pProduction_Name);
960 }
961
962 widget_redraw(pEditor->pProduction_Name);
963 sdl_dirty_rect(area);
964
965 FREESURFACE(pEditor->pProduction_Progres->theme);
966 pEditor->pProduction_Progres->theme =
967 get_progress_icon(stock, cost, &cost);
968
969 if (!gold_prod) {
970 fc_snprintf(cBuf, sizeof(cBuf), "%d%%" , cost);
971 } else {
972 fc_snprintf(cBuf, sizeof(cBuf), "-" );
973 }
974 copy_chars_to_string16(pEditor->pProduction_Progres->string16, cBuf);
975 widget_redraw(pEditor->pProduction_Progres);
976 widget_mark_dirty(pEditor->pProduction_Progres);
977 }
978
979 /**************************************************************************
980 Update and redraw worklist length counter in worklist editor
981 **************************************************************************/
refresh_worklist_count_label(void)982 static void refresh_worklist_count_label(void)
983 {
984 char cBuf[64];
985 SDL_Rect area;
986 int len = worklist_length(&pEditor->worklist_copy);
987
988 if (pEditor->pCity != NULL) {
989 len += 1; /* External entry from current production */
990 }
991
992 fc_snprintf(cBuf, sizeof(cBuf),
993 /* TRANS: length of worklist */
994 PL_("( %d entry )", "( %d entries )", len), len);
995 copy_chars_to_string16(pEditor->pWorkList_Counter->string16, cBuf);
996
997 widget_undraw(pEditor->pWorkList_Counter);
998 remake_label_size(pEditor->pWorkList_Counter);
999
1000 pEditor->pWorkList_Counter->size.x = pEditor->pEndWidgetList->area.x +
1001 (adj_size(130) - pEditor->pWorkList_Counter->size.w)/2;
1002
1003 if (get_wflags(pEditor->pWorkList_Counter) & WF_RESTORE_BACKGROUND) {
1004 refresh_widget_background(pEditor->pWorkList_Counter);
1005 }
1006
1007 widget_redraw(pEditor->pWorkList_Counter);
1008
1009 /* Can't just widget_mark_dirty(), as it may have reduced in size */
1010 area.x = pEditor->pEndWidgetList->area.x; /* left edge of client area */
1011 area.y = pEditor->pWorkList_Counter->size.y;
1012 area.w = adj_size(130);
1013 area.h = pEditor->pWorkList_Counter->size.h;
1014 layer_rect_to_screen_rect(pEditor->pEndWidgetList->dst, &area);
1015 sdl_dirty_rect(area);
1016 }
1017
1018
1019 /* ====================================================================== */
1020
1021
1022 /*
1023 * Global/City worklist editor.
1024 * if pCity == NULL then function takes pWorklist as global worklist.
1025 * pWorklist must be not NULL.
1026 */
popup_worklist_editor(struct city * pCity,struct global_worklist * gwl)1027 void popup_worklist_editor(struct city *pCity, struct global_worklist *gwl)
1028 {
1029 SDL_Color bg_color = {255,255,255,128};
1030 SDL_Color bg_color2 = {255,255,255,136};
1031
1032 int count = 0, turns;
1033 int widget_w = 0, widget_h = 0;
1034 SDL_String16 *pStr = NULL;
1035 struct widget *pBuf = NULL, *pWindow, *pLast;
1036 SDL_Surface *pText = NULL, *pText_Name = NULL, *pZoom = NULL;
1037 SDL_Surface *pMain;
1038 SDL_Surface *pIcon;
1039 SDL_Rect dst;
1040 char cBuf[128];
1041 struct unit_type *punittype = NULL;
1042 char *state = NULL;
1043 bool advanced_tech;
1044 bool can_build, can_eventually_build;
1045 SDL_Rect area;
1046 int len;
1047
1048 if (pEditor) {
1049 return;
1050 }
1051
1052 pEditor = fc_calloc(1, sizeof(struct EDITOR));
1053
1054 if (pCity) {
1055 pEditor->pCity = pCity;
1056 pEditor->global_worklist_id = -1;
1057 pEditor->currently_building = pCity->production;
1058 pEditor->stock = pCity->shield_stock;
1059 worklist_copy(&pEditor->worklist_copy, &pCity->worklist);
1060 fc_snprintf(pEditor->worklist_name, sizeof(pEditor->worklist_name),
1061 "%s worklist", city_name_get(pCity));
1062 } else if (gwl != NULL) {
1063 pEditor->pCity = NULL;
1064 pEditor->global_worklist_id = global_worklist_id(gwl);
1065 worklist_copy(&pEditor->worklist_copy, global_worklist_get(gwl));
1066 sz_strlcpy(pEditor->worklist_name, global_worklist_name(gwl));
1067 } else {
1068 /* Not valid variant! */
1069 return;
1070 }
1071
1072 len = worklist_length(&pEditor->worklist_copy);
1073 advanced_tech = (pCity == NULL);
1074
1075 /* --------------- */
1076 /* create Target Background Icon */
1077 pMain = create_surf_alpha(adj_size(116), adj_size(116), SDL_SWSURFACE);
1078 SDL_FillRect(pMain, NULL, map_rgba(pMain->format, bg_color));
1079 putframe(pMain,
1080 0, 0, pMain->w - 1, pMain->h - 1,
1081 get_theme_color(COLOR_THEME_WLDLG_FRAME));
1082
1083 /* ---------------- */
1084 /* Create Main Window */
1085 pWindow = create_window_skeleton(NULL, NULL, 0);
1086 pWindow->action = window_worklist_editor_callback;
1087 set_wstate(pWindow, FC_WS_NORMAL);
1088
1089 add_to_gui_list(ID_WINDOW, pWindow);
1090 pEditor->pEndWidgetList = pWindow;
1091
1092 area = pWindow->area;
1093
1094 /* ---------------- */
1095 if (pCity) {
1096 fc_snprintf(cBuf, sizeof(cBuf), _("Worklist of\n%s"), city_name_get(pCity));
1097 len += 1; /* External entry from current production */
1098 } else {
1099 fc_snprintf(cBuf, sizeof(cBuf), "%s", global_worklist_name(gwl));
1100 }
1101
1102 pStr = create_str16_from_char(cBuf, adj_font(12));
1103 pStr->style |= (TTF_STYLE_BOLD|SF_CENTER);
1104
1105 pBuf = create_iconlabel(NULL, pWindow->dst, pStr, WF_RESTORE_BACKGROUND);
1106
1107 add_to_gui_list(ID_LABEL, pBuf);
1108 /* --------------------------- */
1109
1110 fc_snprintf(cBuf, sizeof(cBuf),
1111 /* TRANS: length of worklist */
1112 PL_("( %d entry )", "( %d entries )", len), len);
1113 pStr = create_str16_from_char(cBuf, adj_font(10));
1114 pStr->bgcol = (SDL_Color) {0, 0, 0, 0};
1115 pBuf = create_iconlabel(NULL, pWindow->dst, pStr, WF_RESTORE_BACKGROUND);
1116 pEditor->pWorkList_Counter = pBuf;
1117 add_to_gui_list(ID_LABEL, pBuf);
1118 /* --------------------------- */
1119
1120 /* create production proggres label or rename worklist edit */
1121 if(pCity) {
1122 /* count == cost */
1123 /* turns == progress */
1124 const char *name = city_production_name_translation(pCity);
1125 bool gold_prod = city_production_has_flag(pCity, IF_GOLD);
1126
1127 count = city_production_build_shield_cost(pCity);
1128
1129 if (gold_prod) {
1130 int gold = MAX(0, pCity->surplus[O_SHIELD]);
1131
1132 fc_snprintf(cBuf, sizeof(cBuf),
1133 PL_("%s\n%d gold per turn",
1134 "%s\n%d gold per turn", gold),
1135 name, gold);
1136 } else {
1137 if (pCity->shield_stock < count) {
1138 turns = city_production_turns_to_build(pCity, TRUE);
1139 if (turns == 999) {
1140 fc_snprintf(cBuf, sizeof(cBuf), _("%s\nblocked!"), name);
1141 } else {
1142 fc_snprintf(cBuf, sizeof(cBuf), _("%s\n%d %s"),
1143 name, turns, PL_("turn", "turns", turns));
1144 }
1145 } else {
1146 fc_snprintf(cBuf, sizeof(cBuf), _("%s\nfinished!"), name);
1147 }
1148 }
1149 pStr = create_str16_from_char(cBuf, adj_font(10));
1150 pStr->style |= SF_CENTER;
1151 pBuf = create_iconlabel(NULL, pWindow->dst, pStr, WF_RESTORE_BACKGROUND);
1152
1153 pEditor->pProduction_Name = pBuf;
1154 add_to_gui_list(ID_LABEL, pBuf);
1155
1156 pIcon = get_progress_icon(pCity->shield_stock, count, &turns);
1157
1158 if (!gold_prod) {
1159 fc_snprintf(cBuf, sizeof(cBuf), "%d%%" , turns);
1160 } else {
1161 fc_snprintf(cBuf, sizeof(cBuf), "-");
1162 }
1163 pStr = create_str16_from_char(cBuf, adj_font(12));
1164 pStr->style |= (TTF_STYLE_BOLD|SF_CENTER);
1165
1166 pBuf = create_iconlabel(pIcon, pWindow->dst, pStr,
1167 (WF_RESTORE_BACKGROUND|WF_ICON_CENTER|WF_FREE_THEME));
1168
1169 pIcon = NULL;
1170 turns = 0;
1171 pEditor->pProduction_Progres = pBuf;
1172 add_to_gui_list(ID_LABEL, pBuf);
1173 } else {
1174 pBuf = create_edit_from_chars(NULL, pWindow->dst,
1175 global_worklist_name(gwl), adj_font(10),
1176 adj_size(120), WF_RESTORE_BACKGROUND);
1177 pBuf->action = rename_worklist_editor_callback;
1178 set_wstate(pBuf, FC_WS_NORMAL);
1179
1180 add_to_gui_list(ID_EDIT, pBuf);
1181 }
1182
1183 /* --------------------------- */
1184 /* Commit Widget */
1185 pBuf = create_themeicon(current_theme->OK_Icon, pWindow->dst, WF_RESTORE_BACKGROUND);
1186
1187 pBuf->action = ok_worklist_editor_callback;
1188 set_wstate(pBuf, FC_WS_NORMAL);
1189 pBuf->key = SDLK_RETURN;
1190
1191 add_to_gui_list(ID_BUTTON, pBuf);
1192 /* --------------------------- */
1193 /* Cancel Widget */
1194 pBuf = create_themeicon(current_theme->CANCEL_Icon, pWindow->dst,
1195 WF_RESTORE_BACKGROUND);
1196
1197 pBuf->action = popdown_worklist_editor_callback;
1198 set_wstate(pBuf, FC_WS_NORMAL);
1199 pBuf->key = SDLK_ESCAPE;
1200
1201 add_to_gui_list(ID_BUTTON, pBuf);
1202 /* --------------------------- */
1203 /* work list */
1204
1205 /*
1206 pWidget->data filed will contains position of target in worklist all
1207 action on worklist (swap/romove/add) must correct this fields
1208
1209 Production Widget Label in worklist Widget list
1210 will have this field NULL
1211 */
1212
1213 pEditor->pWork = fc_calloc(1, sizeof(struct ADVANCED_DLG));
1214
1215 pEditor->pWork->pScroll = fc_calloc(1, sizeof(struct ScrollBar));
1216 pEditor->pWork->pScroll->count = 0;
1217 pEditor->pWork->pScroll->active = MAX_LEN_WORKLIST;
1218 pEditor->pWork->pScroll->step = 1;
1219
1220 /* FIXME: this should replace the 4 lines above, but
1221 * pEditor->pWork->pEndWidgetList is not set yet */
1222 #if 0
1223 create_vertical_scrollbar(pEditor->pWork, 1, MAX_LEN_WORKLIST, TRUE, TRUE);
1224 #endif
1225
1226 if(pCity) {
1227 /* Production Widget Label */
1228 pStr = create_str16_from_char(city_production_name_translation(pCity), adj_font(10));
1229 turns = city_production_build_shield_cost(pCity);
1230 pStr->style |= SF_CENTER;
1231 pBuf = create_iconlabel(NULL, pWindow->dst, pStr, WF_RESTORE_BACKGROUND);
1232
1233 set_wstate(pBuf, FC_WS_NORMAL);
1234 pBuf->action = worklist_editor_item_callback;
1235
1236 add_to_gui_list(MAX_ID - cid_encode(pCity->production), pBuf);
1237
1238 pEditor->pWork->pEndWidgetList = pBuf;
1239 pEditor->pWork->pBeginWidgetList = pBuf;
1240 pEditor->pWork->pEndActiveWidgetList = pEditor->pWork->pEndWidgetList;
1241 pEditor->pWork->pBeginActiveWidgetList = pEditor->pWork->pBeginWidgetList;
1242 pEditor->pWork->pScroll->count++;
1243 }
1244
1245 pLast = pBuf;
1246 pEditor->pDock = pBuf;
1247
1248 /* create Widget Labels of worklist entries */
1249
1250 count = 0;
1251
1252 worklist_iterate(&pEditor->worklist_copy, prod) {
1253
1254 if(VUT_UTYPE == prod.kind) {
1255 pStr = create_str16_from_char(utype_name_translation(prod.value.utype),
1256 adj_font(10));
1257 } else {
1258 pStr = create_str16_from_char(city_improvement_name_translation(pCity,
1259 prod.value.building),
1260 adj_font(10));
1261 }
1262 pStr->style |= SF_CENTER;
1263 pBuf = create_iconlabel(NULL, pWindow->dst, pStr,
1264 (WF_RESTORE_BACKGROUND|WF_FREE_DATA));
1265
1266 set_wstate(pBuf, FC_WS_NORMAL);
1267 pBuf->action = worklist_editor_item_callback;
1268
1269 pBuf->data.ptr = fc_calloc(1, sizeof(int));
1270 *((int *)pBuf->data.ptr) = count;
1271
1272 add_to_gui_list(MAX_ID - cid_encode(prod), pBuf);
1273
1274 count++;
1275
1276 if(count > pEditor->pWork->pScroll->active - 1) {
1277 set_wflag(pBuf, WF_HIDDEN);
1278 }
1279
1280 } worklist_iterate_end;
1281
1282 if(count) {
1283 if(!pCity) {
1284 pEditor->pWork->pEndWidgetList = pLast->prev;
1285 pEditor->pWork->pEndActiveWidgetList = pEditor->pWork->pEndWidgetList;
1286 }
1287 pEditor->pWork->pBeginWidgetList = pBuf;
1288 pEditor->pWork->pBeginActiveWidgetList = pEditor->pWork->pBeginWidgetList;
1289 } else {
1290 if(!pCity) {
1291 pEditor->pWork->pEndWidgetList = pLast;
1292 }
1293 pEditor->pWork->pBeginWidgetList = pLast;
1294 }
1295
1296 /* FIXME */
1297 #if 0
1298 pEditor->pWork->pActiveWidgetList = pEditor->pWork->pEndActiveWidgetList;
1299 create_vertical_scrollbar(pEditor->pWork, 1,
1300 pEditor->pWork->pScroll->active, FALSE, TRUE);
1301 pEditor->pWork->pScroll->pUp_Left_Button->size.w = adj_size(122);
1302 pEditor->pWork->pScroll->pDown_Right_Button->size.w = adj_size(122);
1303
1304 /* count: without production */
1305 if(count <= pEditor->pWork->pScroll->active + 1) {
1306 if(count > 0) {
1307 struct widget *pTmp = pLast;
1308 do {
1309 pTmp = pTmp->prev;
1310 clear_wflag(pTmp, WF_HIDDEN);
1311 } while (pTmp != pBuf);
1312 }
1313 hide_scrollbar(pEditor->pWork->pScroll);
1314 }
1315 #endif
1316
1317 pEditor->pWork->pScroll->count += count;
1318 pLast = pEditor->pWork->pBeginWidgetList;
1319
1320 /* --------------------------- */
1321 /* global worklists */
1322 if (pCity) {
1323 count = 0;
1324
1325 global_worklists_iterate(iter_gwl) {
1326 pBuf = create_iconlabel_from_chars(NULL, pWindow->dst,
1327 global_worklist_name(iter_gwl),
1328 adj_font(10),
1329 WF_RESTORE_BACKGROUND);
1330 set_wstate(pBuf, FC_WS_NORMAL);
1331 add_to_gui_list(MAX_ID - global_worklist_id(iter_gwl), pBuf);
1332 pBuf->string16->style |= SF_CENTER;
1333 pBuf->action = global_worklist_callback;
1334 pBuf->string16->fgcol = bg_color;
1335
1336 count++;
1337
1338 if (count > 4) {
1339 set_wflag(pBuf, WF_HIDDEN);
1340 }
1341 } global_worklists_iterate_end;
1342
1343 if(count) {
1344 pEditor->pGlobal = fc_calloc(1, sizeof(struct ADVANCED_DLG));
1345 pEditor->pGlobal->pEndWidgetList = pLast->prev;
1346 pEditor->pGlobal->pEndActiveWidgetList = pEditor->pGlobal->pEndWidgetList;
1347 pEditor->pGlobal->pBeginWidgetList = pBuf;
1348 pEditor->pGlobal->pBeginActiveWidgetList = pEditor->pGlobal->pBeginWidgetList;
1349
1350 if(count > 6) {
1351 pEditor->pGlobal->pActiveWidgetList = pEditor->pGlobal->pEndActiveWidgetList;
1352
1353 create_vertical_scrollbar(pEditor->pGlobal, 1, 4, FALSE, TRUE);
1354 pEditor->pGlobal->pScroll->pUp_Left_Button->size.w = adj_size(122);
1355 pEditor->pGlobal->pScroll->pDown_Right_Button->size.w = adj_size(122);
1356 } else {
1357 struct widget *pTmp = pLast;
1358 do {
1359 pTmp = pTmp->prev;
1360 clear_wflag(pTmp, WF_HIDDEN);
1361 } while (pTmp != pBuf);
1362 }
1363
1364 pLast = pEditor->pGlobal->pBeginWidgetList;
1365 }
1366 }
1367 /* ----------------------------- */
1368 count = 0;
1369 /* Targets units and imprv. to build */
1370 pStr = create_string16(NULL, 0, adj_font(10));
1371 pStr->style |= (SF_CENTER|TTF_STYLE_BOLD);
1372 pStr->bgcol = (SDL_Color) {0, 0, 0, 0};
1373
1374 improvement_iterate(pImprove) {
1375 can_build = can_player_build_improvement_now(client.conn.playing, pImprove);
1376 can_eventually_build =
1377 can_player_build_improvement_later(client.conn.playing, pImprove);
1378
1379 /* If there's a city, can the city build the improvement? */
1380 if (pCity) {
1381 can_build = can_build && can_city_build_improvement_now(pCity, pImprove);
1382 can_eventually_build = can_eventually_build &&
1383 can_city_build_improvement_later(pCity, pImprove);
1384 }
1385
1386 if ((advanced_tech && can_eventually_build) ||
1387 (!advanced_tech && can_build)) {
1388
1389 pIcon = crop_rect_from_surface(pMain, NULL);
1390
1391 fc_snprintf(cBuf, sizeof(cBuf), "%s", improvement_name_translation(pImprove));
1392 copy_chars_to_string16(pStr, cBuf);
1393 pStr->style |= TTF_STYLE_BOLD;
1394
1395 pText_Name = create_text_surf_smaller_that_w(pStr, pIcon->w - 4);
1396
1397 if (is_wonder(pImprove)) {
1398 if (improvement_obsolete(client.conn.playing, pImprove, pCity)) {
1399 state = _("Obsolete");
1400 } else if (is_great_wonder(pImprove)) {
1401 if (great_wonder_is_built(pImprove)) {
1402 state = _("Built");
1403 } else if (great_wonder_is_destroyed(pImprove)) {
1404 state = _("Destroyed");
1405 } else {
1406 state = _("Great Wonder");
1407 }
1408 } else if (is_small_wonder(pImprove)) {
1409 if (small_wonder_is_built(client.conn.playing, pImprove)) {
1410 state = _("Built");
1411 } else {
1412 state = _("Small Wonder");
1413 }
1414 }
1415 } else {
1416 state = NULL;
1417 }
1418
1419 if (pCity) {
1420 if (!improvement_has_flag(pImprove, IF_GOLD)) {
1421 struct universal univ = cid_production(cid_encode_building(pImprove));
1422
1423 turns = city_turns_to_build(pCity, &univ, TRUE);
1424
1425 if (turns == FC_INFINITY) {
1426 if (state) {
1427 fc_snprintf(cBuf, sizeof(cBuf), _("(%s)\n%d/%d %s\n%s"),
1428 state, pCity->shield_stock,
1429 impr_build_shield_cost(pImprove),
1430 PL_("shield", "shields",
1431 impr_build_shield_cost(pImprove)),
1432 _("never"));
1433 } else {
1434 fc_snprintf(cBuf, sizeof(cBuf), _("%d/%d %s\n%s"),
1435 pCity->shield_stock, impr_build_shield_cost(pImprove),
1436 PL_("shield", "shields",
1437 impr_build_shield_cost(pImprove)), _("never"));
1438 }
1439 } else {
1440 if (state) {
1441 fc_snprintf(cBuf, sizeof(cBuf), _("(%s)\n%d/%d %s\n%d %s"),
1442 state, pCity->shield_stock,
1443 impr_build_shield_cost(pImprove),
1444 PL_("shield", "shields",
1445 impr_build_shield_cost(pImprove)),
1446 turns, PL_("turn", "turns", turns));
1447 } else {
1448 fc_snprintf(cBuf, sizeof(cBuf), _("%d/%d %s\n%d %s"),
1449 pCity->shield_stock, impr_build_shield_cost(pImprove),
1450 PL_("shield", "shields",
1451 impr_build_shield_cost(pImprove)),
1452 turns, PL_("turn", "turns", turns));
1453 }
1454 }
1455 } else {
1456 /* capitalization */
1457 int gold = MAX(0, pCity->surplus[O_SHIELD]);
1458
1459 fc_snprintf(cBuf, sizeof(cBuf), PL_("%d gold per turn",
1460 "%d gold per turn", gold),
1461 gold);
1462 }
1463 } else {
1464 /* non city mode */
1465 if (!improvement_has_flag(pImprove, IF_GOLD)) {
1466 if (state) {
1467 fc_snprintf(cBuf, sizeof(cBuf), _("(%s)\n%d %s"),
1468 state, impr_build_shield_cost(pImprove),
1469 PL_("shield", "shields",
1470 impr_build_shield_cost(pImprove)));
1471 } else {
1472 fc_snprintf(cBuf, sizeof(cBuf), _("%d %s"),
1473 impr_build_shield_cost(pImprove),
1474 PL_("shield", "shields",
1475 impr_build_shield_cost(pImprove)));
1476 }
1477 } else {
1478 fc_snprintf(cBuf, sizeof(cBuf), _("shields into gold"));
1479 }
1480 }
1481
1482 copy_chars_to_string16(pStr, cBuf);
1483 pStr->style &= ~TTF_STYLE_BOLD;
1484
1485 pText = create_text_surf_from_str16(pStr);
1486
1487 /*-----------------*/
1488
1489 pZoom = get_building_surface(pImprove);
1490 pZoom = zoomSurface(pZoom, DEFAULT_ZOOM * ((float)54 / pZoom->w), DEFAULT_ZOOM * ((float)54 / pZoom->w), 1);
1491 dst.x = (pIcon->w - pZoom->w)/2;
1492 dst.y = (pIcon->h/2 - pZoom->h)/2;
1493 alphablit(pZoom, NULL, pIcon, &dst);
1494 dst.y += pZoom->h;
1495 FREESURFACE(pZoom);
1496
1497 dst.x = (pIcon->w - pText_Name->w)/2;
1498 dst.y += ((pIcon->h - dst.y) - (pText_Name->h + pText->h))/2;
1499 alphablit(pText_Name, NULL, pIcon, &dst);
1500
1501 dst.x = (pIcon->w - pText->w)/2;
1502 dst.y += pText_Name->h;
1503 alphablit(pText, NULL, pIcon, &dst);
1504
1505 FREESURFACE(pText);
1506 FREESURFACE(pText_Name);
1507
1508 pBuf = create_icon2(pIcon, pWindow->dst,
1509 WF_RESTORE_BACKGROUND|WF_FREE_THEME);
1510 set_wstate(pBuf, FC_WS_NORMAL);
1511
1512 widget_w = MAX(widget_w, pBuf->size.w);
1513 widget_h = MAX(widget_h, pBuf->size.h);
1514
1515 pBuf->data.city = pCity;
1516 add_to_gui_list(MAX_ID - improvement_number(pImprove), pBuf);
1517 pBuf->action = worklist_editor_targets_callback;
1518
1519 if(count > (TARGETS_ROW * TARGETS_COL - 1)) {
1520 set_wflag(pBuf, WF_HIDDEN);
1521 }
1522 count++;
1523 }
1524 } improvement_iterate_end;
1525
1526 /* ------------------------------ */
1527
1528 unit_type_iterate(un) {
1529 can_build = can_player_build_unit_now(client.conn.playing, un);
1530 can_eventually_build =
1531 can_player_build_unit_later(client.conn.playing, un);
1532
1533 /* If there's a city, can the city build the unit? */
1534 if (pCity) {
1535 can_build = can_build && can_city_build_unit_now(pCity, un);
1536 can_eventually_build = can_eventually_build &&
1537 can_city_build_unit_later(pCity, un);
1538 }
1539
1540 if ((advanced_tech && can_eventually_build)
1541 || (!advanced_tech && can_build)) {
1542
1543 punittype = un;
1544
1545 pIcon = crop_rect_from_surface(pMain, NULL);
1546
1547 fc_snprintf(cBuf, sizeof(cBuf), "%s", utype_name_translation(un));
1548
1549 copy_chars_to_string16(pStr, cBuf);
1550 pStr->style |= TTF_STYLE_BOLD;
1551 pText_Name = create_text_surf_smaller_that_w(pStr, pIcon->w - 4);
1552
1553 if (pCity) {
1554 struct universal univ;
1555
1556 univ = cid_production(cid_encode_unit(un));
1557 turns = city_turns_to_build(pCity, &univ, TRUE);
1558 if (turns == FC_INFINITY) {
1559 fc_snprintf(cBuf, sizeof(cBuf),
1560 _("(%d/%d/%s)\n%d/%d %s\nnever"),
1561 punittype->attack_strength,
1562 punittype->defense_strength,
1563 move_points_text(punittype->move_rate, TRUE),
1564 pCity->shield_stock, utype_build_shield_cost(un),
1565 PL_("shield", "shields", utype_build_shield_cost(un)));
1566 } else {
1567 fc_snprintf(cBuf, sizeof(cBuf),
1568 _("(%d/%d/%s)\n%d/%d %s\n%d %s"),
1569 punittype->attack_strength,
1570 punittype->defense_strength,
1571 move_points_text(punittype->move_rate, TRUE),
1572 pCity->shield_stock, utype_build_shield_cost(un),
1573 PL_("shield", "shields", utype_build_shield_cost(un)),
1574 turns, PL_("turn", "turns", turns));
1575 }
1576 } else {
1577 fc_snprintf(cBuf, sizeof(cBuf),
1578 _("(%d/%d/%s)\n%d %s"),
1579 punittype->attack_strength,
1580 punittype->defense_strength,
1581 move_points_text(punittype->move_rate, TRUE),
1582 utype_build_shield_cost(un),
1583 PL_("shield", "shields", utype_build_shield_cost(un)));
1584 }
1585
1586 copy_chars_to_string16(pStr, cBuf);
1587 pStr->style &= ~TTF_STYLE_BOLD;
1588
1589 pText = create_text_surf_from_str16(pStr);
1590
1591 pZoom = adj_surf(get_unittype_surface(un, direction8_invalid()));
1592 dst.x = (pIcon->w - pZoom->w)/2;
1593 dst.y = (pIcon->h/2 - pZoom->h)/2;
1594 alphablit(pZoom, NULL, pIcon, &dst);
1595 FREESURFACE(pZoom);
1596
1597 dst.x = (pIcon->w - pText_Name->w)/2;
1598 dst.y = pIcon->h/2 + (pIcon->h/2 - (pText_Name->h + pText->h))/2;
1599 alphablit(pText_Name, NULL, pIcon, &dst);
1600
1601 dst.x = (pIcon->w - pText->w)/2;
1602 dst.y += pText_Name->h;
1603 alphablit(pText, NULL, pIcon, &dst);
1604
1605 FREESURFACE(pText);
1606 FREESURFACE(pText_Name);
1607
1608 pBuf = create_icon2(pIcon, pWindow->dst,
1609 WF_RESTORE_BACKGROUND|WF_FREE_THEME);
1610 set_wstate(pBuf, FC_WS_NORMAL);
1611
1612 widget_w = MAX(widget_w, pBuf->size.w);
1613 widget_h = MAX(widget_h, pBuf->size.h);
1614
1615 pBuf->data.city = pCity;
1616 add_to_gui_list(MAX_ID - cid_encode_unit(un), pBuf);
1617 pBuf->action = worklist_editor_targets_callback;
1618
1619 if(count > (TARGETS_ROW * TARGETS_COL - 1)) {
1620 set_wflag(pBuf, WF_HIDDEN);
1621 }
1622 count++;
1623
1624 }
1625 } unit_type_iterate_end;
1626
1627 pEditor->pTargets = fc_calloc(1, sizeof(struct ADVANCED_DLG));
1628
1629 pEditor->pTargets->pEndWidgetList = pLast->prev;
1630 pEditor->pTargets->pBeginWidgetList = pBuf;
1631 pEditor->pTargets->pEndActiveWidgetList = pEditor->pTargets->pEndWidgetList;
1632 pEditor->pTargets->pBeginActiveWidgetList = pEditor->pTargets->pBeginWidgetList;
1633 pEditor->pTargets->pActiveWidgetList = pEditor->pTargets->pEndActiveWidgetList;
1634
1635 /* --------------- */
1636 if(count > (TARGETS_ROW * TARGETS_COL - 1)) {
1637 count = create_vertical_scrollbar(pEditor->pTargets,
1638 TARGETS_COL, TARGETS_ROW, TRUE, TRUE);
1639 } else {
1640 count = 0;
1641 }
1642 /* --------------- */
1643
1644 pEditor->pBeginWidgetList = pEditor->pTargets->pBeginWidgetList;
1645
1646 /* Window */
1647 area.w = MAX(area.w, widget_w * TARGETS_COL + count + adj_size(130));
1648 area.h = MAX(area.h, widget_h * TARGETS_ROW);
1649
1650 pIcon = theme_get_background(theme, BACKGROUND_WLDLG);
1651 if(resize_window(pWindow, pIcon, NULL,
1652 (pWindow->size.w - pWindow->area.w) + area.w,
1653 (pWindow->size.h - pWindow->area.h) + area.h)) {
1654 FREESURFACE(pIcon);
1655 }
1656
1657 area = pWindow->area;
1658
1659 /* Backgrounds */
1660 dst.x = area.x;
1661 dst.y = area.y;
1662 dst.w = adj_size(130);
1663 dst.h = adj_size(145);
1664
1665 SDL_FillRect(pWindow->theme, &dst,
1666 map_rgba(pWindow->theme->format, *get_theme_color(COLOR_THEME_BACKGROUND)));
1667 putframe(pWindow->theme,
1668 dst.x, dst.y, dst.x + dst.w - 1, dst.y + dst.h - 1,
1669 get_theme_color(COLOR_THEME_WLDLG_FRAME));
1670 putframe(pWindow->theme,
1671 dst.x + 2, dst.y + 2, dst.x + dst.w - 3, dst.y + dst.h - 3,
1672 get_theme_color(COLOR_THEME_WLDLG_FRAME));
1673
1674 dst.x = area.x;
1675 dst.y += dst.h + adj_size(2);
1676 dst.w = adj_size(130);
1677 dst.h = adj_size(228);
1678 SDL_FillRectAlpha(pWindow->theme, &dst, &bg_color2);
1679 putframe(pWindow->theme,
1680 dst.x, dst.y, dst.x + dst.w - 1, dst.y + dst.h - 1,
1681 get_theme_color(COLOR_THEME_WLDLG_FRAME));
1682
1683 if(pEditor->pGlobal) {
1684 dst.x = area.x;
1685 dst.y += dst.h + adj_size(2);
1686 dst.w = adj_size(130);
1687 dst.h = pWindow->size.h - dst.y - adj_size(4);
1688
1689 SDL_FillRect(pWindow->theme, &dst,
1690 map_rgba(pWindow->theme->format, *get_theme_color(COLOR_THEME_BACKGROUND)));
1691 putframe(pWindow->theme,
1692 dst.x, dst.y, dst.x + dst.w - 1, dst.y + dst.h - 1,
1693 get_theme_color(COLOR_THEME_WLDLG_FRAME));
1694 putframe(pWindow->theme,
1695 dst.x + adj_size(2), dst.y + adj_size(2),
1696 dst.x + dst.w - adj_size(3), dst.y + dst.h - adj_size(3),
1697 get_theme_color(COLOR_THEME_WLDLG_FRAME));
1698 }
1699
1700 widget_set_position(pWindow,
1701 (Main.screen->w - pWindow->size.w) / 2,
1702 (Main.screen->h - pWindow->size.h) / 2);
1703
1704 /* name */
1705 pBuf = pWindow->prev;
1706 pBuf->size.x = area.x + (adj_size(130) - pBuf->size.w)/2;
1707 pBuf->size.y = area.y + adj_size(4);
1708
1709 /* size of worklist (without production) */
1710 pBuf = pBuf->prev;
1711 pBuf->size.x = area.x + (adj_size(130) - pBuf->size.w)/2;
1712 pBuf->size.y = pBuf->next->size.y + pBuf->next->size.h;
1713
1714 if(pCity) {
1715 /* current build and proggrse bar */
1716 pBuf = pBuf->prev;
1717 pBuf->size.x = area.x + (adj_size(130) - pBuf->size.w)/2;
1718 pBuf->size.y = pBuf->next->size.y + pBuf->next->size.h + adj_size(5);
1719
1720 pBuf = pBuf->prev;
1721 pBuf->size.x = area.x + (adj_size(130) - pBuf->size.w)/2;
1722 pBuf->size.y = pBuf->next->size.y + pBuf->next->size.h;
1723 } else {
1724 /* rename worklist */
1725 pBuf = pBuf->prev;
1726 pBuf->size.x = area.x + (adj_size(130) - pBuf->size.w)/2;
1727 pBuf->size.y = area.y + 1 + (adj_size(145) - pBuf->size.h)/2;
1728 }
1729
1730 /* ok button */
1731 pBuf = pBuf->prev;
1732 pBuf->size.x = area.x + (adj_size(65) - pBuf->size.w)/2;
1733 pBuf->size.y = area.y + adj_size(135) - pBuf->size.h;
1734
1735 /* exit button */
1736 pBuf = pBuf->prev;
1737 pBuf->size.x = area.x + adj_size(65) + (adj_size(65) - pBuf->size.w)/2;
1738 pBuf->size.y = area.y + adj_size(135) - pBuf->size.h;
1739
1740 /* worklist */
1741 /* pEditor->pWork->pScroll->count: including production */
1742 if (len > 0) {
1743 /* FIXME */
1744 setup_vertical_widgets_position(1,
1745 area.x + adj_size(2), area.y + adj_size(152)/* +
1746 ((pEditor->pWork->pScroll->count > pEditor->pWork->pScroll->active + 2) ?
1747 pEditor->pWork->pScroll->pUp_Left_Button->size.h + 1 : 0)*/,
1748 adj_size(126), 0, pEditor->pWork->pBeginWidgetList,
1749 pEditor->pWork->pEndWidgetList);
1750
1751 setup_vertical_scrollbar_area(pEditor->pWork->pScroll,
1752 area.x + adj_size(2),
1753 area.y + adj_size(152),
1754 adj_size(225), FALSE);
1755 }
1756
1757 /* global worklists */
1758 if(pEditor->pGlobal) {
1759 setup_vertical_widgets_position(1,
1760 area.x + adj_size(4),
1761 area.y + adj_size(384) +
1762 (pEditor->pGlobal->pScroll ?
1763 pEditor->pGlobal->pScroll->pUp_Left_Button->size.h + 1 : 0),
1764 adj_size(122), 0, pEditor->pGlobal->pBeginWidgetList,
1765 pEditor->pGlobal->pEndWidgetList);
1766
1767 if(pEditor->pGlobal->pScroll) {
1768 setup_vertical_scrollbar_area(pEditor->pGlobal->pScroll,
1769 area.x + adj_size(4),
1770 area.y + adj_size(384),
1771 adj_size(93), FALSE);
1772 }
1773
1774 }
1775
1776 /* Targets */
1777 setup_vertical_widgets_position(TARGETS_COL,
1778 area.x + adj_size(130), area.y,
1779 0, 0, pEditor->pTargets->pBeginWidgetList,
1780 pEditor->pTargets->pEndWidgetList);
1781
1782 if(pEditor->pTargets->pScroll) {
1783 setup_vertical_scrollbar_area(pEditor->pTargets->pScroll,
1784 area.x + area.w,
1785 area.y + 1,
1786 area.h - 1, TRUE);
1787
1788 }
1789
1790 /* ----------------------------------- */
1791 FREESTRING16(pStr);
1792 FREESURFACE(pMain);
1793
1794 redraw_group(pEditor->pBeginWidgetList, pWindow, 0);
1795 widget_flush(pWindow);
1796 }
1797
popdown_worklist_editor(void)1798 void popdown_worklist_editor(void)
1799 {
1800 if (pEditor) {
1801 popdown_window_group_dialog(pEditor->pBeginWidgetList,
1802 pEditor->pEndWidgetList);
1803 FC_FREE(pEditor->pTargets->pScroll);
1804 FC_FREE(pEditor->pTargets);
1805
1806 FC_FREE(pEditor->pWork->pScroll);
1807 FC_FREE(pEditor->pWork);
1808
1809 if (pEditor->pGlobal) {
1810 FC_FREE(pEditor->pGlobal->pScroll);
1811 FC_FREE(pEditor->pGlobal);
1812 }
1813
1814 if (city_dialog_is_open(pEditor->pCity)) {
1815 enable_city_dlg_widgets();
1816 }
1817
1818 FC_FREE(pEditor);
1819
1820 flush_dirty();
1821 }
1822 }
1823