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