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