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