1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17
18 #include <stdio.h>
19
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23
24 #include <gtk/gtk.h>
25
26 /* utility */
27 #include "fcintl.h"
28 #include "log.h"
29 #include "mem.h"
30 #include "rand.h"
31 #include "support.h"
32 #include "timing.h"
33
34 /* common */
35 #include "game.h"
36 #include "government.h" /* government_graphic() */
37 #include "map.h"
38 #include "player.h"
39
40 /* client */
41 #include "client_main.h"
42 #include "climap.h"
43 #include "climisc.h"
44 #include "colors.h"
45 #include "control.h" /* get_unit_in_focus() */
46 #include "editor.h"
47 #include "options.h"
48 #include "overview_common.h"
49 #include "tilespec.h"
50 #include "text.h"
51 #include "zoom.h"
52
53 /* client/gui-gtk-3.22 */
54 #include "citydlg.h" /* For reset_city_dialogs() */
55 #include "editgui.h"
56 #include "graphics.h"
57 #include "gui_main.h"
58 #include "gui_stuff.h"
59 #include "mapctrl.h"
60 #include "repodlgs.h"
61 #include "wldlg.h"
62
63 #include "mapview.h"
64
65 static GtkAdjustment *map_hadj, *map_vadj;
66 static int cursor_timer_id = 0, cursor_type = -1, cursor_frame = 0;
67 static int mapview_frozen_level = 0;
68
69 /**************************************************************************
70 If do_restore is FALSE it will invert the turn done button style. If
71 called regularly from a timer this will give a blinking turn done
72 button. If do_restore is TRUE this will reset the turn done button
73 to the default style.
74 **************************************************************************/
update_turn_done_button(bool do_restore)75 void update_turn_done_button(bool do_restore)
76 {
77 static bool flip = FALSE;
78
79 if (!get_turn_done_button_state()) {
80 return;
81 }
82
83 if ((do_restore && flip) || !do_restore) {
84 static GtkCssProvider *tdb_provider = NULL;
85 GtkStyleContext *scontext = gtk_widget_get_style_context(turn_done_button);
86
87 if (tdb_provider == NULL) {
88 tdb_provider = gtk_css_provider_new();
89
90 gtk_css_provider_load_from_data(tdb_provider,
91 ".lighted {\n"
92 "color: rgba(235, 127, 235, 255);\n"
93 "background-color: rgba(127, 127, 127, 255);\n"
94 "}\n",
95 -1, NULL);
96
97 /* Turn Done button is persistent, so we only need to do this
98 * once too. */
99 gtk_style_context_add_provider(scontext,
100 GTK_STYLE_PROVIDER(tdb_provider),
101 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
102 }
103
104 if (flip) {
105 gtk_style_context_add_class(scontext, "lighted");
106 } else {
107 gtk_style_context_remove_class(scontext, "lighted");
108 }
109
110 flip = !flip;
111 }
112 }
113
114 /**************************************************************************
115 Timeout label requires refreshing
116 **************************************************************************/
update_timeout_label(void)117 void update_timeout_label(void)
118 {
119 gtk_label_set_text(GTK_LABEL(timeout_label), get_timeout_label_text());
120
121 if (current_turn_timeout() > 0) {
122 gtk_widget_set_tooltip_text(timeout_label,
123 _("Time to forced turn change,\n"
124 "or estimated time to finish turn change "
125 "processing."));
126 } else {
127 gtk_widget_set_tooltip_text(timeout_label,
128 _("Turn timeout disabled.\n"
129 "Between turns this shows estimated time "
130 "to finish turn change processing."));
131 }
132 }
133
134 /**************************************************************************
135 Refresh info label
136 **************************************************************************/
update_info_label(void)137 void update_info_label(void)
138 {
139 GtkWidget *label;
140 const struct player *pplayer = client.conn.playing;
141
142 label = gtk_frame_get_label_widget(GTK_FRAME(main_frame_civ_name));
143 if (pplayer != NULL) {
144 const gchar *name;
145 gunichar c;
146
147 /* Capitalize the first character of the translated nation
148 * plural name so that the frame label looks good. */
149 name = nation_plural_for_player(pplayer);
150 c = g_utf8_get_char_validated(name, -1);
151 if ((gunichar) -1 != c && (gunichar) -2 != c) {
152 gchar nation[MAX_LEN_NAME];
153 gchar *next;
154 gint len;
155
156 len = g_unichar_to_utf8(g_unichar_toupper(c), nation);
157 nation[len] = '\0';
158 next = g_utf8_find_next_char(name, NULL);
159 if (NULL != next) {
160 sz_strlcat(nation, next);
161 }
162 gtk_label_set_text(GTK_LABEL(label), nation);
163 } else {
164 gtk_label_set_text(GTK_LABEL(label), name);
165 }
166 } else {
167 gtk_label_set_text(GTK_LABEL(label), "-");
168 }
169
170 gtk_label_set_text(GTK_LABEL(main_label_info),
171 get_info_label_text(!GUI_GTK_OPTION(small_display_layout)));
172
173 set_indicator_icons(client_research_sprite(),
174 client_warming_sprite(),
175 client_cooling_sprite(),
176 client_government_sprite());
177
178 if (NULL != client.conn.playing) {
179 int d = 0;
180
181 for (; d < client.conn.playing->economic.luxury /10; d++) {
182 struct sprite *spr = get_tax_sprite(tileset, O_LUXURY);
183 GdkPixbuf *pb;
184
185 pb = sprite_get_pixbuf(spr);
186 gtk_image_set_from_pixbuf(GTK_IMAGE(econ_label[d]), pb);
187 g_object_unref(pb);
188 }
189
190 for (; d < (client.conn.playing->economic.science
191 + client.conn.playing->economic.luxury) / 10; d++) {
192 struct sprite *spr = get_tax_sprite(tileset, O_SCIENCE);
193 GdkPixbuf *pb;
194
195 pb = sprite_get_pixbuf(spr);
196 gtk_image_set_from_pixbuf(GTK_IMAGE(econ_label[d]), pb);
197 g_object_unref(pb);
198 }
199
200 for (; d < 10; d++) {
201 struct sprite *spr = get_tax_sprite(tileset, O_GOLD);
202 GdkPixbuf *pb;
203
204 pb = sprite_get_pixbuf(spr);
205 gtk_image_set_from_pixbuf(GTK_IMAGE(econ_label[d]), pb);
206 g_object_unref(pb);
207 }
208 }
209
210 update_timeout_label();
211
212 /* update tooltips. */
213 gtk_widget_set_tooltip_text(econ_ebox,
214 _("Shows your current luxury/science/tax rates; "
215 "click to toggle them."));
216
217 gtk_widget_set_tooltip_text(bulb_ebox, get_bulb_tooltip());
218 gtk_widget_set_tooltip_text(sun_ebox, get_global_warming_tooltip());
219 gtk_widget_set_tooltip_text(flake_ebox, get_nuclear_winter_tooltip());
220 gtk_widget_set_tooltip_text(government_ebox, get_government_tooltip());
221 }
222
223 /**************************************************************************
224 This function is used to animate the mouse cursor.
225 **************************************************************************/
anim_cursor_cb(gpointer data)226 static gboolean anim_cursor_cb(gpointer data)
227 {
228 if (!cursor_timer_id) {
229 return FALSE;
230 }
231
232 cursor_frame++;
233 if (cursor_frame == NUM_CURSOR_FRAMES) {
234 cursor_frame = 0;
235 }
236
237 if (cursor_type == CURSOR_DEFAULT) {
238 gdk_window_set_cursor(root_window, NULL);
239 cursor_timer_id = 0;
240 return FALSE;
241 }
242
243 gdk_window_set_cursor(root_window,
244 fc_cursors[cursor_type][cursor_frame]);
245 control_mouse_cursor(NULL);
246 return TRUE;
247 }
248
249 /**************************************************************************
250 This function will change the current mouse cursor.
251 **************************************************************************/
update_mouse_cursor(enum cursor_type new_cursor_type)252 void update_mouse_cursor(enum cursor_type new_cursor_type)
253 {
254 cursor_type = new_cursor_type;
255 if (!cursor_timer_id) {
256 cursor_timer_id = g_timeout_add(CURSOR_INTERVAL, anim_cursor_cb, NULL);
257 }
258 }
259
260 /**************************************************************************
261 Update the information label which gives info on the current unit and the
262 square under the current unit, for specified unit. Note that in practice
263 punit is always the focus unit.
264 Clears label if punit is NULL.
265 Also updates the cursor for the map_canvas (this is related because the
266 info label includes a "select destination" prompt etc).
267 Also calls update_unit_pix_label() to update the icons for units on this
268 square.
269 **************************************************************************/
update_unit_info_label(struct unit_list * punits)270 void update_unit_info_label(struct unit_list *punits)
271 {
272 GtkWidget *label;
273
274 label = gtk_frame_get_label_widget(GTK_FRAME(unit_info_frame));
275 gtk_label_set_text(GTK_LABEL(label),
276 get_unit_info_label_text1(punits));
277
278 gtk_label_set_text(GTK_LABEL(unit_info_label),
279 get_unit_info_label_text2(punits, 0));
280
281 update_unit_pix_label(punits);
282 }
283
284 /**************************************************************************
285 Get sprite for treaty acceptance or rejection.
286 **************************************************************************/
get_thumb_pixbuf(int onoff)287 GdkPixbuf *get_thumb_pixbuf(int onoff)
288 {
289 return sprite_get_pixbuf(get_treaty_thumb_sprite(tileset, BOOL_VAL(onoff)));
290 }
291
292 /****************************************************************************
293 Set information for the indicator icons typically shown in the main
294 client window. The parameters tell which sprite to use for the
295 indicator.
296 ****************************************************************************/
set_indicator_icons(struct sprite * bulb,struct sprite * sol,struct sprite * flake,struct sprite * gov)297 void set_indicator_icons(struct sprite *bulb, struct sprite *sol,
298 struct sprite *flake, struct sprite *gov)
299 {
300 GdkPixbuf *pb;
301
302 pb = sprite_get_pixbuf(bulb);
303 gtk_image_set_from_pixbuf(GTK_IMAGE(bulb_label), pb);
304 g_object_unref(pb);
305 pb = sprite_get_pixbuf(sol);
306 gtk_image_set_from_pixbuf(GTK_IMAGE(sun_label), pb);
307 g_object_unref(pb);
308 pb = sprite_get_pixbuf(flake);
309 gtk_image_set_from_pixbuf(GTK_IMAGE(flake_label), pb);
310 g_object_unref(pb);
311 pb = sprite_get_pixbuf(gov);
312 gtk_image_set_from_pixbuf(GTK_IMAGE(government_label), pb);
313 g_object_unref(pb);
314 }
315
316 /****************************************************************************
317 Return the maximum dimensions of the area (container widget) for the
318 overview. Due to the fact that the scaling factor is at least 1, the real
319 size could be larger. The calculation in calculate_overview_dimensions()
320 limit it to the smallest possible size.
321 ****************************************************************************/
get_overview_area_dimensions(int * width,int * height)322 void get_overview_area_dimensions(int *width, int *height)
323 {
324 *width = GUI_GTK_OVERVIEW_MIN_XSIZE;
325 *height = GUI_GTK_OVERVIEW_MIN_YSIZE;
326 }
327
328 /**************************************************************************
329 Size of overview changed
330 **************************************************************************/
overview_size_changed(void)331 void overview_size_changed(void)
332 {
333 gtk_widget_set_size_request(overview_canvas,
334 gui_options.overview.width,
335 gui_options.overview.height);
336 update_map_canvas_scrollbars_size();
337 }
338
339 /****************************************************************************
340 Return a canvas that is the overview window.
341 ****************************************************************************/
get_overview_window(void)342 struct canvas *get_overview_window(void)
343 {
344 #if 0
345 static struct canvas store;
346
347 store.surface = NULL;
348 store.drawable = gdk_cairo_create(gtk_widget_get_window(overview_canvas));
349
350 return &store;
351 #endif /* 0 */
352 if (can_client_change_view()) {
353 gtk_widget_queue_draw(overview_canvas);
354 }
355 return NULL;
356 }
357
358 /**************************************************************************
359 Redraw overview canvas
360 **************************************************************************/
overview_canvas_draw(GtkWidget * w,cairo_t * cr,gpointer data)361 gboolean overview_canvas_draw(GtkWidget *w, cairo_t *cr, gpointer data)
362 {
363 gpointer source = (can_client_change_view()) ?
364 (gpointer)gui_options.overview.window : (gpointer)radar_gfx_sprite;
365
366 if (source) {
367 cairo_surface_t *surface = (can_client_change_view()) ?
368 gui_options.overview.window->surface :
369 radar_gfx_sprite->surface;
370
371 cairo_set_source_surface(cr, surface, 0, 0);
372 cairo_paint(cr);
373 }
374 return TRUE;
375 }
376
377 /****************************************************************************
378 Freeze the drawing of the map.
379 ****************************************************************************/
mapview_freeze(void)380 void mapview_freeze(void)
381 {
382 mapview_frozen_level++;
383 }
384
385 /****************************************************************************
386 Thaw the drawing of the map.
387 ****************************************************************************/
mapview_thaw(void)388 void mapview_thaw(void)
389 {
390 if (1 < mapview_frozen_level) {
391 mapview_frozen_level--;
392 } else {
393 fc_assert(0 < mapview_frozen_level);
394 mapview_frozen_level = 0;
395 dirty_all();
396 }
397 }
398
399 /****************************************************************************
400 Return whether the map should be drawn or not.
401 ****************************************************************************/
mapview_is_frozen(void)402 bool mapview_is_frozen(void)
403 {
404 return (0 < mapview_frozen_level);
405 }
406
407 /**************************************************************************
408 Update on canvas widget size change
409 **************************************************************************/
map_canvas_configure(GtkWidget * w,GdkEventConfigure * ev,gpointer data)410 gboolean map_canvas_configure(GtkWidget *w, GdkEventConfigure *ev,
411 gpointer data)
412 {
413 map_canvas_resized(ev->width, ev->height);
414
415 return TRUE;
416 }
417
418 /**************************************************************************
419 Redraw map canvas.
420 **************************************************************************/
map_canvas_draw(GtkWidget * w,cairo_t * cr,gpointer data)421 gboolean map_canvas_draw(GtkWidget *w, cairo_t *cr, gpointer data)
422 {
423 if (can_client_change_view() && map_exists() && !mapview_is_frozen()) {
424 /* First we mark the area to be updated as dirty. Then we unqueue
425 * any pending updates, to make sure only the most up-to-date data
426 * is written (otherwise drawing bugs happen when old data is copied
427 * to screen). Then we draw all changed areas to the screen. */
428 update_animation();
429 unqueue_mapview_updates(FALSE);
430 cairo_set_source_surface(cr, mapview.store->surface, 0, 0);
431 cairo_paint(cr);
432 }
433 return TRUE;
434 }
435
436 /**************************************************************************
437 Flush the given part of the canvas buffer (if there is one) to the
438 screen.
439 **************************************************************************/
flush_mapcanvas(int canvas_x,int canvas_y,int pixel_width,int pixel_height)440 void flush_mapcanvas(int canvas_x, int canvas_y,
441 int pixel_width, int pixel_height)
442 {
443 GdkRectangle rectangle = {canvas_x, canvas_y, pixel_width, pixel_height};
444 if (gtk_widget_get_realized(map_canvas) && !mapview_is_frozen()) {
445 gdk_window_invalidate_rect(gtk_widget_get_window(map_canvas), &rectangle, FALSE);
446 }
447 }
448
449 /**************************************************************************
450 Mark the rectangular region as "dirty" so that we know to flush it
451 later.
452 **************************************************************************/
dirty_rect(int canvas_x,int canvas_y,int pixel_width,int pixel_height)453 void dirty_rect(int canvas_x, int canvas_y,
454 int pixel_width, int pixel_height)
455 {
456 GdkRectangle rectangle = {canvas_x, canvas_y, pixel_width, pixel_height};
457 if (gtk_widget_get_realized(map_canvas)) {
458 gdk_window_invalidate_rect(gtk_widget_get_window(map_canvas), &rectangle, FALSE);
459 }
460 }
461
462 /**************************************************************************
463 Mark the entire screen area as "dirty" so that we can flush it later.
464 **************************************************************************/
dirty_all(void)465 void dirty_all(void)
466 {
467 if (gtk_widget_get_realized(map_canvas)) {
468 gdk_window_invalidate_rect(gtk_widget_get_window(map_canvas), NULL, FALSE);
469 }
470 }
471
472 /**************************************************************************
473 Flush all regions that have been previously marked as dirty. See
474 dirty_rect and dirty_all. This function is generally called after we've
475 processed a batch of drawing operations.
476 **************************************************************************/
flush_dirty(void)477 void flush_dirty(void)
478 {
479 if (map_canvas != NULL && gtk_widget_get_realized(map_canvas)) {
480 gdk_window_process_updates(gtk_widget_get_window(map_canvas), FALSE);
481 }
482 }
483
484 /****************************************************************************
485 Do any necessary synchronization to make sure the screen is up-to-date.
486 The canvas should have already been flushed to screen via flush_dirty -
487 all this function does is make sure the hardware has caught up.
488 ****************************************************************************/
gui_flush(void)489 void gui_flush(void)
490 {
491 cairo_surface_flush(mapview.store->surface);
492 }
493
494 /**************************************************************************
495 Update display of descriptions associated with cities on the main map.
496 **************************************************************************/
update_city_descriptions(void)497 void update_city_descriptions(void)
498 {
499 update_map_canvas_visible();
500 }
501
502 /**************************************************************************
503 Fill image with unit gfx
504 **************************************************************************/
put_unit_image(struct unit * punit,GtkImage * p,int height)505 void put_unit_image(struct unit *punit, GtkImage *p, int height)
506 {
507 struct canvas store = FC_STATIC_CANVAS_INIT;
508 int width;
509
510 if (height <= 0) {
511 height = tileset_full_tile_height(tileset);
512 }
513 width = tileset_full_tile_width(tileset);
514
515 store.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
516 width, height);
517
518 put_unit(punit, &store, 1.0, 0, 0);
519
520 gtk_image_set_from_surface(p, store.surface);
521 cairo_surface_destroy(store.surface);
522 }
523
524 /**************************************************************************
525 FIXME:
526 For now only two food, two gold one shield and two masks can be drawn per
527 unit, the proper way to do this is probably something like what Civ II does.
528 (One food/shield/mask drawn N times, possibly one top of itself. -- SKi
529 **************************************************************************/
put_unit_image_city_overlays(struct unit * punit,GtkImage * p,int height,int * upkeep_cost,int happy_cost)530 void put_unit_image_city_overlays(struct unit *punit, GtkImage *p,
531 int height,
532 int *upkeep_cost, int happy_cost)
533 {
534 struct canvas store = FC_STATIC_CANVAS_INIT;
535 GdkPixbuf *pb;
536 int width = tileset_full_tile_width(tileset);
537
538 store.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
539 width, height);
540
541 put_unit(punit, &store, 1.0, 0, 0);
542
543 put_unit_city_overlays(punit, &store, 0, tileset_unit_layout_offset_y(tileset),
544 upkeep_cost, happy_cost);
545
546 pb = surface_get_pixbuf(store.surface, width, height);
547 gtk_image_set_from_pixbuf(p, pb);
548 g_object_unref(pb);
549 cairo_surface_destroy(store.surface);
550 }
551
552 /**************************************************************************
553 Put overlay tile to pixmap
554 **************************************************************************/
pixmap_put_overlay_tile(GdkWindow * pixmap,float zoom,int canvas_x,int canvas_y,struct sprite * ssprite)555 void pixmap_put_overlay_tile(GdkWindow *pixmap, float zoom,
556 int canvas_x, int canvas_y,
557 struct sprite *ssprite)
558 {
559 cairo_t *cr;
560 GdkDrawingContext *ctx;
561
562 if (!ssprite) {
563 return;
564 }
565
566 ctx = gdk_window_begin_draw_frame(pixmap,
567 gdk_window_get_clip_region(pixmap));
568 cr = gdk_drawing_context_get_cairo_context(ctx);
569 cairo_scale(cr, zoom, zoom);
570 cairo_set_source_surface(cr, ssprite->surface, canvas_x, canvas_y);
571 cairo_paint(cr);
572 gdk_window_end_draw_frame(pixmap, ctx);
573 }
574
575 /**************************************************************************
576 Only used for isometric view.
577 **************************************************************************/
pixmap_put_overlay_tile_draw(struct canvas * pcanvas,int canvas_x,int canvas_y,struct sprite * ssprite,bool fog)578 void pixmap_put_overlay_tile_draw(struct canvas *pcanvas,
579 int canvas_x, int canvas_y,
580 struct sprite *ssprite,
581 bool fog)
582 {
583 cairo_t *cr;
584 int sswidth, ssheight;
585 const double bright = 0.65; /* Fogged brightness compared to unfogged */
586
587 if (!ssprite) {
588 return;
589 }
590
591 get_sprite_dimensions(ssprite, &sswidth, &ssheight);
592
593 if (fog) {
594 struct color *fogcol = color_alloc(0.0, 0.0, 0.0); /* black */
595 cairo_surface_t *fog_surface;
596 struct sprite *fogged;
597 unsigned char *mask_in;
598 unsigned char *mask_out;
599 int i, j;
600
601 /* Create sprites initially fully transparent */
602 fogcol->color.alpha = 0.0;
603 fogged = create_sprite(sswidth, ssheight, fogcol);
604 fog_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sswidth, ssheight);
605
606 /* Calculate black fog mask from the original sprite's alpha channel;
607 * we don't want to blacken transparent parts of the sprite. */
608 mask_in = cairo_image_surface_get_data(ssprite->surface);
609 mask_out = cairo_image_surface_get_data(fog_surface);
610
611 for (i = 0; i < sswidth; i++) {
612 for (j = 0; j < ssheight; j++) {
613 /* In order to darken pixels of ssprite to 'bright' fraction of
614 * their original value, we need to overlay blackness of
615 * (1-bright) transparency. */
616 #ifndef WORDS_BIGENDIAN
617 mask_out[(j * sswidth + i) * 4 + 3]
618 = (1-bright) * mask_in[(j * sswidth + i) * 4 + 3];
619 #else /* WORDS_BIGENDIAN */
620 mask_out[(j * sswidth + i) * 4 + 0]
621 = (1-bright) * mask_in[(j * sswidth + i) * 4 + 0];
622 #endif /* WORDS_BIGENDIAN */
623 }
624 }
625
626 cairo_surface_mark_dirty(fog_surface);
627
628 /* First copy original sprite canvas to intermediate sprite canvas */
629 cr = cairo_create(fogged->surface);
630 cairo_set_source_surface(cr, ssprite->surface, 0, 0);
631 cairo_paint(cr);
632
633 /* Then apply created fog to the intermediate sprite to darken it */
634 cairo_set_source_surface(cr, fog_surface, 0, 0);
635 cairo_paint(cr);
636 cairo_destroy(cr);
637
638 /* Put intermediate sprite to the target canvas */
639 canvas_put_sprite(pcanvas, canvas_x, canvas_y,
640 fogged, 0, 0, sswidth, ssheight);
641
642 /* Free intermediate stuff */
643 cairo_surface_destroy(fog_surface);
644 free_sprite(fogged);
645 color_free(fogcol);
646 } else {
647 canvas_put_sprite(pcanvas, canvas_x, canvas_y,
648 ssprite, 0, 0, sswidth, ssheight);
649 }
650 }
651
652 /**************************************************************************
653 Draws a cross-hair overlay on a tile
654 **************************************************************************/
put_cross_overlay_tile(struct tile * ptile)655 void put_cross_overlay_tile(struct tile *ptile)
656 {
657 float canvas_x, canvas_y;
658
659 if (tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) {
660 pixmap_put_overlay_tile(gtk_widget_get_window(map_canvas), map_zoom,
661 canvas_x / map_zoom, canvas_y / map_zoom,
662 get_attention_crosshair_sprite(tileset));
663 }
664 }
665
666 /*****************************************************************************
667 Sets the position of the overview scroll window based on mapview position.
668 *****************************************************************************/
update_overview_scroll_window_pos(int x,int y)669 void update_overview_scroll_window_pos(int x, int y)
670 {
671 gdouble ov_scroll_x, ov_scroll_y;
672 GtkAdjustment *ov_hadj, *ov_vadj;
673
674 ov_hadj = gtk_scrolled_window_get_hadjustment(
675 GTK_SCROLLED_WINDOW(overview_scrolled_window));
676 ov_vadj = gtk_scrolled_window_get_vadjustment(
677 GTK_SCROLLED_WINDOW(overview_scrolled_window));
678
679 ov_scroll_x = MIN(x - (overview_canvas_store_width / 2),
680 gtk_adjustment_get_upper(ov_hadj)
681 - gtk_adjustment_get_page_size(ov_hadj));
682 ov_scroll_y = MIN(y - (overview_canvas_store_height / 2),
683 gtk_adjustment_get_upper(ov_vadj)
684 - gtk_adjustment_get_page_size(ov_vadj));
685
686 gtk_adjustment_set_value(ov_hadj, ov_scroll_x);
687 gtk_adjustment_set_value(ov_vadj, ov_scroll_y);
688 }
689
690 /**************************************************************************
691 Refresh map canvas scrollbars
692 **************************************************************************/
update_map_canvas_scrollbars(void)693 void update_map_canvas_scrollbars(void)
694 {
695 int scroll_x, scroll_y;
696
697 get_mapview_scroll_pos(&scroll_x, &scroll_y);
698 gtk_adjustment_set_value(map_hadj, scroll_x);
699 gtk_adjustment_set_value(map_vadj, scroll_y);
700 if (can_client_change_view()) {
701 gtk_widget_queue_draw(overview_canvas);
702 }
703 }
704
705 /**************************************************************************
706 Refresh map canvas scrollbar as canvas size changes
707 **************************************************************************/
update_map_canvas_scrollbars_size(void)708 void update_map_canvas_scrollbars_size(void)
709 {
710 float xmin, ymin, xmax, ymax;
711 int xsize, ysize, xstep, ystep;
712
713 get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize);
714 get_mapview_scroll_step(&xstep, &ystep);
715
716 map_hadj = gtk_adjustment_new(-1, xmin, xmax, xstep, xsize, xsize);
717 map_vadj = gtk_adjustment_new(-1, ymin, ymax, ystep, ysize, ysize);
718
719 gtk_range_set_adjustment(GTK_RANGE(map_horizontal_scrollbar), map_hadj);
720 gtk_range_set_adjustment(GTK_RANGE(map_vertical_scrollbar), map_vadj);
721
722 g_signal_connect(map_hadj, "value_changed",
723 G_CALLBACK(scrollbar_jump_callback),
724 GINT_TO_POINTER(TRUE));
725 g_signal_connect(map_vadj, "value_changed",
726 G_CALLBACK(scrollbar_jump_callback),
727 GINT_TO_POINTER(FALSE));
728 }
729
730 /**************************************************************************
731 Scrollbar has moved
732 **************************************************************************/
scrollbar_jump_callback(GtkAdjustment * adj,gpointer hscrollbar)733 void scrollbar_jump_callback(GtkAdjustment *adj, gpointer hscrollbar)
734 {
735 int scroll_x, scroll_y;
736
737 if (!can_client_change_view()) {
738 return;
739 }
740
741 get_mapview_scroll_pos(&scroll_x, &scroll_y);
742
743 if (hscrollbar) {
744 scroll_x = gtk_adjustment_get_value(adj);
745 } else {
746 scroll_y = gtk_adjustment_get_value(adj);
747 }
748
749 set_mapview_scroll_pos(scroll_x, scroll_y);
750 }
751
752 /**************************************************************************
753 Draws a rectangle with top left corner at (canvas_x, canvas_y), and
754 width 'w' and height 'h'. It is drawn using the 'selection_gc' context,
755 so the pixel combining function is XOR. This means that drawing twice
756 in the same place will restore the image to its original state.
757
758 NB: A side effect of this function is to set the 'selection_gc' color
759 to COLOR_MAPVIEW_SELECTION.
760 **************************************************************************/
draw_selection_rectangle(int canvas_x,int canvas_y,int w,int h)761 void draw_selection_rectangle(int canvas_x, int canvas_y, int w, int h)
762 {
763 double dashes[2] = {4.0, 4.0};
764 struct color *pcolor;
765 cairo_t *cr;
766 GdkDrawingContext *ctx;
767 GdkWindow *wndw;
768
769 if (w == 0 || h == 0) {
770 return;
771 }
772
773 pcolor = get_color(tileset, COLOR_MAPVIEW_SELECTION);
774 if (!pcolor) {
775 return;
776 }
777
778 wndw = gtk_widget_get_window(map_canvas);
779 ctx = gdk_window_begin_draw_frame(wndw,
780 gdk_window_get_clip_region(wndw));
781 cr = gdk_drawing_context_get_cairo_context(ctx);
782 gdk_cairo_set_source_rgba(cr, &pcolor->color);
783 cairo_set_line_width(cr, 2.0);
784 cairo_set_dash(cr, dashes, 2, 0);
785 cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE);
786 cairo_rectangle(cr, canvas_x, canvas_y, w, h);
787 cairo_stroke(cr);
788 gdk_window_end_draw_frame(wndw, ctx);
789 }
790
791 /**************************************************************************
792 This function is called when the tileset is changed.
793 **************************************************************************/
tileset_changed(void)794 void tileset_changed(void)
795 {
796 science_report_dialog_redraw();
797 reset_city_dialogs();
798 reset_unit_table();
799 blank_max_unit_size();
800 editgui_tileset_changed();
801
802 /* keep the icon of the executable on Windows (see PR#36491) */
803 #ifndef WIN32_NATIVE
804 {
805 GdkPixbuf *pixbuf = sprite_get_pixbuf(get_icon_sprite(tileset, ICON_FREECIV));
806
807 /* Only call this after tileset_load_tiles is called. */
808 gtk_window_set_icon(GTK_WINDOW(toplevel), pixbuf);
809 g_object_unref(pixbuf);
810 }
811 #endif /* WIN32_NATIVE */
812 }
813
814 /**************************************************************************
815 New turn callback
816 **************************************************************************/
start_turn(void)817 void start_turn(void)
818 {}
819