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)
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 ***********************************************************************/
15 #include <fc_config.h>
36 #include "government.h" /* government_graphic() */
41 #include "client_main.h"
45 #include "control.h" /* get_unit_in_focus() */
48 #include "overview_common.h"
53 /* client/gui-gtk-3.0 */
54 #include "citydlg.h" /* For reset_city_dialogs() */
58 #include "gui_stuff.h"
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;
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
74 **************************************************************************/
75 void update_turn_done_button(bool do_restore
)
77 static bool flip
= FALSE
;
79 if (!get_turn_done_button_state()) {
83 if ((do_restore
&& flip
) || !do_restore
) {
86 GtkStyleContext
*context
= gtk_widget_get_style_context(turn_done_button
);
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
);
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
);
98 /**************************************************************************
99 Timeout label requires refreshing
100 **************************************************************************/
101 void update_timeout_label(void)
103 gtk_label_set_text(GTK_LABEL(timeout_label
), get_timeout_label_text());
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 "
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."));
118 /**************************************************************************
120 **************************************************************************/
121 void update_info_label(void)
124 const struct player
*pplayer
= client
.conn
.playing
;
126 label
= gtk_frame_get_label_widget(GTK_FRAME(main_frame_civ_name
));
127 if (pplayer
!= NULL
) {
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
];
140 len
= g_unichar_to_utf8(g_unichar_toupper(c
), nation
);
142 next
= g_utf8_find_next_char(name
, NULL
);
144 sz_strlcat(nation
, next
);
146 gtk_label_set_text(GTK_LABEL(label
), nation
);
148 gtk_label_set_text(GTK_LABEL(label
), name
);
151 gtk_label_set_text(GTK_LABEL(label
), "-");
154 gtk_label_set_text(GTK_LABEL(main_label_info
),
155 get_info_label_text(!GUI_GTK_OPTION(small_display_layout
)));
157 set_indicator_icons(client_research_sprite(),
158 client_warming_sprite(),
159 client_cooling_sprite(),
160 client_government_sprite());
162 if (NULL
!= client
.conn
.playing
) {
165 for (; d
< client
.conn
.playing
->economic
.luxury
/10; d
++) {
166 struct sprite
*spr
= get_tax_sprite(tileset
, O_LUXURY
);
168 gtk_image_set_from_surface(GTK_IMAGE(econ_label
[d
]), spr
->surface
);
171 for (; d
< (client
.conn
.playing
->economic
.science
172 + client
.conn
.playing
->economic
.luxury
) / 10; d
++) {
173 struct sprite
*spr
= get_tax_sprite(tileset
, O_SCIENCE
);
175 gtk_image_set_from_surface(GTK_IMAGE(econ_label
[d
]), spr
->surface
);
178 for (; d
< 10; d
++) {
179 struct sprite
*spr
= get_tax_sprite(tileset
, O_GOLD
);
181 gtk_image_set_from_surface(GTK_IMAGE(econ_label
[d
]), spr
->surface
);
185 update_timeout_label();
187 /* update tooltips. */
188 gtk_widget_set_tooltip_text(econ_ebox
,
189 _("Shows your current luxury/science/tax rates; "
190 "click to toggle them."));
192 gtk_widget_set_tooltip_text(bulb_ebox
, get_bulb_tooltip());
193 gtk_widget_set_tooltip_text(sun_ebox
, get_global_warming_tooltip());
194 gtk_widget_set_tooltip_text(flake_ebox
, get_nuclear_winter_tooltip());
195 gtk_widget_set_tooltip_text(government_ebox
, get_government_tooltip());
198 /**************************************************************************
199 This function is used to animate the mouse cursor.
200 **************************************************************************/
201 static gboolean
anim_cursor_cb(gpointer data
)
203 if (!cursor_timer_id
) {
208 if (cursor_frame
== NUM_CURSOR_FRAMES
) {
212 if (cursor_type
== CURSOR_DEFAULT
) {
213 gdk_window_set_cursor(root_window
, NULL
);
218 gdk_window_set_cursor(root_window
,
219 fc_cursors
[cursor_type
][cursor_frame
]);
220 control_mouse_cursor(NULL
);
224 /**************************************************************************
225 This function will change the current mouse cursor.
226 **************************************************************************/
227 void update_mouse_cursor(enum cursor_type new_cursor_type
)
229 cursor_type
= new_cursor_type
;
230 if (!cursor_timer_id
) {
231 cursor_timer_id
= g_timeout_add(CURSOR_INTERVAL
, anim_cursor_cb
, NULL
);
235 /**************************************************************************
236 Update the information label which gives info on the current unit and the
237 square under the current unit, for specified unit. Note that in practice
238 punit is always the focus unit.
239 Clears label if punit is NULL.
240 Also updates the cursor for the map_canvas (this is related because the
241 info label includes a "select destination" prompt etc).
242 Also calls update_unit_pix_label() to update the icons for units on this
244 **************************************************************************/
245 void update_unit_info_label(struct unit_list
*punits
)
249 label
= gtk_frame_get_label_widget(GTK_FRAME(unit_info_frame
));
250 gtk_label_set_text(GTK_LABEL(label
),
251 get_unit_info_label_text1(punits
));
253 gtk_label_set_text(GTK_LABEL(unit_info_label
),
254 get_unit_info_label_text2(punits
, 0));
256 update_unit_pix_label(punits
);
259 /**************************************************************************
260 Get sprite for treaty acceptance or rejection.
261 **************************************************************************/
262 GdkPixbuf
*get_thumb_pixbuf(int onoff
)
264 return sprite_get_pixbuf(get_treaty_thumb_sprite(tileset
, BOOL_VAL(onoff
)));
267 /****************************************************************************
268 Set information for the indicator icons typically shown in the main
269 client window. The parameters tell which sprite to use for the
271 ****************************************************************************/
272 void set_indicator_icons(struct sprite
*bulb
, struct sprite
*sol
,
273 struct sprite
*flake
, struct sprite
*gov
)
275 gtk_image_set_from_surface(GTK_IMAGE(bulb_label
), bulb
->surface
);
276 gtk_image_set_from_surface(GTK_IMAGE(sun_label
), sol
->surface
);
277 gtk_image_set_from_surface(GTK_IMAGE(flake_label
), flake
->surface
);
278 gtk_image_set_from_surface(GTK_IMAGE(government_label
), gov
->surface
);
281 /****************************************************************************
282 Return the maximum dimensions of the area (container widget) for the
283 overview. Due to the fact that the scaling factor is at least 1, the real
284 size could be larger. The calculation in calculate_overview_dimensions()
285 limit it to the smallest possible size.
286 ****************************************************************************/
287 void get_overview_area_dimensions(int *width
, int *height
)
289 *width
= GUI_GTK_OVERVIEW_MIN_XSIZE
;
290 *height
= GUI_GTK_OVERVIEW_MIN_YSIZE
;
293 /**************************************************************************
294 Size of overview changed
295 **************************************************************************/
296 void overview_size_changed(void)
298 gtk_widget_set_size_request(overview_canvas
,
299 gui_options
.overview
.width
,
300 gui_options
.overview
.height
);
301 update_map_canvas_scrollbars_size();
304 /****************************************************************************
305 Return a canvas that is the overview window.
306 ****************************************************************************/
307 struct canvas
*get_overview_window(void)
310 static struct canvas store
;
312 store
.surface
= NULL
;
313 store
.drawable
= gdk_cairo_create(gtk_widget_get_window(overview_canvas
));
321 /**************************************************************************
322 Redraw overview canvas
323 **************************************************************************/
324 gboolean
overview_canvas_draw(GtkWidget
*w
, cairo_t
*cr
, gpointer data
)
326 gpointer source
= (can_client_change_view()) ?
327 (gpointer
)gui_options
.overview
.window
: NULL
;
330 cairo_surface_t
*surface
= gui_options
.overview
.window
->surface
;
332 cairo_set_source_surface(cr
, surface
, 0, 0);
338 /****************************************************************************
339 Freeze the drawing of the map.
340 ****************************************************************************/
341 void mapview_freeze(void)
343 mapview_frozen_level
++;
346 /****************************************************************************
347 Thaw the drawing of the map.
348 ****************************************************************************/
349 void mapview_thaw(void)
351 if (1 < mapview_frozen_level
) {
352 mapview_frozen_level
--;
354 fc_assert(0 < mapview_frozen_level
);
355 mapview_frozen_level
= 0;
360 /****************************************************************************
361 Return whether the map should be drawn or not.
362 ****************************************************************************/
363 bool mapview_is_frozen(void)
365 return (0 < mapview_frozen_level
);
368 /**************************************************************************
369 Update on canvas widget size change
370 **************************************************************************/
371 gboolean
map_canvas_configure(GtkWidget
*w
, GdkEventConfigure
*ev
,
374 map_canvas_resized(ev
->width
, ev
->height
);
379 /**************************************************************************
381 **************************************************************************/
382 gboolean
map_canvas_draw(GtkWidget
*w
, cairo_t
*cr
, gpointer data
)
384 if (can_client_change_view() && !map_is_empty() && !mapview_is_frozen()) {
385 /* First we mark the area to be updated as dirty. Then we unqueue
386 * any pending updates, to make sure only the most up-to-date data
387 * is written (otherwise drawing bugs happen when old data is copied
388 * to screen). Then we draw all changed areas to the screen. */
389 unqueue_mapview_updates(FALSE
);
390 cairo_set_source_surface(cr
, mapview
.store
->surface
, 0, 0);
396 /**************************************************************************
397 Flush the given part of the canvas buffer (if there is one) to the
399 **************************************************************************/
400 void flush_mapcanvas(int canvas_x
, int canvas_y
,
401 int pixel_width
, int pixel_height
)
403 GdkRectangle rectangle
= {canvas_x
, canvas_y
, pixel_width
, pixel_height
};
404 if (gtk_widget_get_realized(map_canvas
) && !mapview_is_frozen()) {
405 gdk_window_invalidate_rect(gtk_widget_get_window(map_canvas
), &rectangle
, FALSE
);
409 /**************************************************************************
410 Mark the rectangular region as "dirty" so that we know to flush it
412 **************************************************************************/
413 void dirty_rect(int canvas_x
, int canvas_y
,
414 int pixel_width
, int pixel_height
)
416 GdkRectangle rectangle
= {canvas_x
, canvas_y
, pixel_width
, pixel_height
};
417 if (gtk_widget_get_realized(map_canvas
)) {
418 gdk_window_invalidate_rect(gtk_widget_get_window(map_canvas
), &rectangle
, FALSE
);
422 /**************************************************************************
423 Mark the entire screen area as "dirty" so that we can flush it later.
424 **************************************************************************/
427 if (gtk_widget_get_realized(map_canvas
)) {
428 gdk_window_invalidate_rect(gtk_widget_get_window(map_canvas
), NULL
, FALSE
);
432 /**************************************************************************
433 Flush all regions that have been previously marked as dirty. See
434 dirty_rect and dirty_all. This function is generally called after we've
435 processed a batch of drawing operations.
436 **************************************************************************/
437 void flush_dirty(void)
439 if (map_canvas
!= NULL
&& gtk_widget_get_realized(map_canvas
)) {
440 gdk_window_process_updates(gtk_widget_get_window(map_canvas
), FALSE
);
444 /****************************************************************************
445 Do any necessary synchronization to make sure the screen is up-to-date.
446 The canvas should have already been flushed to screen via flush_dirty -
447 all this function does is make sure the hardware has caught up.
448 ****************************************************************************/
451 cairo_surface_flush(mapview
.store
->surface
);
454 /**************************************************************************
455 Update display of descriptions associated with cities on the main map.
456 **************************************************************************/
457 void update_city_descriptions(void)
459 update_map_canvas_visible();
462 /**************************************************************************
463 Fill image with unit gfx
464 **************************************************************************/
465 void put_unit_image(struct unit
*punit
, GtkImage
*p
, int height
)
467 struct canvas store
= FC_STATIC_CANVAS_INIT
;
471 height
= tileset_full_tile_height(tileset
);
473 width
= tileset_full_tile_width(tileset
);
475 store
.surface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
,
478 put_unit(punit
, &store
, 1.0, 0, 0);
480 gtk_image_set_from_surface(p
, store
.surface
);
481 cairo_surface_destroy(store
.surface
);
484 /**************************************************************************
486 For now only two food, two gold one shield and two masks can be drawn per
487 unit, the proper way to do this is probably something like what Civ II does.
488 (One food/shield/mask drawn N times, possibly one top of itself. -- SKi
489 **************************************************************************/
490 void put_unit_image_city_overlays(struct unit
*punit
, GtkImage
*p
,
492 int *upkeep_cost
, int happy_cost
)
494 struct canvas store
= FC_STATIC_CANVAS_INIT
;
495 int width
= tileset_full_tile_width(tileset
);
497 store
.surface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
,
500 put_unit(punit
, &store
, 1.0, 0, 0);
502 put_unit_city_overlays(punit
, &store
, 0, tileset_unit_layout_offset_y(tileset
),
503 upkeep_cost
, happy_cost
);
505 gtk_image_set_from_surface(p
, store
.surface
);
506 cairo_surface_destroy(store
.surface
);
509 /**************************************************************************
510 Put overlay tile to pixmap
511 **************************************************************************/
512 void pixmap_put_overlay_tile(GdkWindow
*pixmap
, float zoom
,
513 int canvas_x
, int canvas_y
,
514 struct sprite
*ssprite
)
522 cr
= gdk_cairo_create(pixmap
);
523 cairo_scale(cr
, zoom
, zoom
);
524 cairo_set_source_surface(cr
, ssprite
->surface
, canvas_x
, canvas_y
);
529 /**************************************************************************
530 Only used for isometric view.
531 **************************************************************************/
532 void pixmap_put_overlay_tile_draw(struct canvas
*pcanvas
,
533 int canvas_x
, int canvas_y
,
534 struct sprite
*ssprite
,
538 int sswidth
, ssheight
;
544 get_sprite_dimensions(ssprite
, &sswidth
, &ssheight
);
547 struct color
*fogcol
= color_alloc(0.0, 0.0, 0.0);
548 cairo_surface_t
*fog_surface
;
549 struct sprite
*fogged
;
550 unsigned char *mask_in
;
551 unsigned char *mask_out
;
554 /* Create sprites fully transparent */
555 fogcol
->color
.alpha
= 0.0;
556 fogged
= create_sprite(sswidth
, ssheight
, fogcol
);
557 fog_surface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, sswidth
, ssheight
);
559 /* Calculate black fog mask from the original sprite,
560 * we don't want to blacken transparent parts of the sprite */
561 mask_in
= cairo_image_surface_get_data(ssprite
->surface
);
562 mask_out
= cairo_image_surface_get_data(fog_surface
);
564 for (i
= 0; i
< sswidth
; i
++) {
565 for (j
= 0; j
< ssheight
; j
++) {
566 if (!is_bigendian()) {
567 mask_out
[(j
* sswidth
+ i
) * 4 + 3]
568 = 0.65 * mask_in
[(j
* sswidth
+ i
) * 4 + 3];
570 mask_out
[(j
* sswidth
+ i
) * 4 + 0]
571 = 0.65 * mask_in
[(j
* sswidth
+ i
) * 4 + 0];
576 cairo_surface_mark_dirty(fog_surface
);
578 /* First copy original sprite canvas to intermediate sprite canvas */
579 cr
= cairo_create(fogged
->surface
);
580 cairo_set_source_surface(cr
, ssprite
->surface
, 0, 0);
583 /* Then apply created fog to the intermediate sprite */
584 cairo_set_source_surface(cr
, fog_surface
, 0, 0);
588 /* Put intermediate sprite to the target canvas */
589 canvas_put_sprite(pcanvas
, canvas_x
, canvas_y
,
590 fogged
, 0, 0, sswidth
, ssheight
);
592 /* Free intermediate stuff */
593 cairo_surface_destroy(fog_surface
);
597 canvas_put_sprite(pcanvas
, canvas_x
, canvas_y
,
598 ssprite
, 0, 0, sswidth
, ssheight
);
602 /**************************************************************************
603 Draws a cross-hair overlay on a tile
604 **************************************************************************/
605 void put_cross_overlay_tile(struct tile
*ptile
)
607 float canvas_x
, canvas_y
;
609 if (tile_to_canvas_pos(&canvas_x
, &canvas_y
, ptile
)) {
610 pixmap_put_overlay_tile(gtk_widget_get_window(map_canvas
), map_zoom
,
611 canvas_x
/ map_zoom
, canvas_y
/ map_zoom
,
612 get_attention_crosshair_sprite(tileset
));
616 /*****************************************************************************
617 Sets the position of the overview scroll window based on mapview position.
618 *****************************************************************************/
619 void update_overview_scroll_window_pos(int x
, int y
)
621 gdouble ov_scroll_x
, ov_scroll_y
;
622 GtkAdjustment
*ov_hadj
, *ov_vadj
;
624 ov_hadj
= gtk_scrolled_window_get_hadjustment(
625 GTK_SCROLLED_WINDOW(overview_scrolled_window
));
626 ov_vadj
= gtk_scrolled_window_get_vadjustment(
627 GTK_SCROLLED_WINDOW(overview_scrolled_window
));
629 ov_scroll_x
= MIN(x
- (overview_canvas_store_width
/ 2),
630 gtk_adjustment_get_upper(ov_hadj
)
631 - gtk_adjustment_get_page_size(ov_hadj
));
632 ov_scroll_y
= MIN(y
- (overview_canvas_store_height
/ 2),
633 gtk_adjustment_get_upper(ov_vadj
)
634 - gtk_adjustment_get_page_size(ov_vadj
));
636 gtk_adjustment_set_value(ov_hadj
, ov_scroll_x
);
637 gtk_adjustment_set_value(ov_vadj
, ov_scroll_y
);
640 /**************************************************************************
641 Refresh map canvas scrollbars
642 **************************************************************************/
643 void update_map_canvas_scrollbars(void)
645 int scroll_x
, scroll_y
;
647 get_mapview_scroll_pos(&scroll_x
, &scroll_y
);
648 gtk_adjustment_set_value(map_hadj
, scroll_x
);
649 gtk_adjustment_set_value(map_vadj
, scroll_y
);
650 if (can_client_change_view()) {
651 gtk_widget_queue_draw(overview_canvas
);
655 /**************************************************************************
656 Refresh map canvas scrollbar as canvas size changes
657 **************************************************************************/
658 void update_map_canvas_scrollbars_size(void)
660 float xmin
, ymin
, xmax
, ymax
;
661 int xsize
, ysize
, xstep
, ystep
;
663 get_mapview_scroll_window(&xmin
, &ymin
, &xmax
, &ymax
, &xsize
, &ysize
);
664 get_mapview_scroll_step(&xstep
, &ystep
);
666 map_hadj
= gtk_adjustment_new(-1, xmin
, xmax
, xstep
, xsize
, xsize
);
667 map_vadj
= gtk_adjustment_new(-1, ymin
, ymax
, ystep
, ysize
, ysize
);
669 gtk_range_set_adjustment(GTK_RANGE(map_horizontal_scrollbar
), map_hadj
);
670 gtk_range_set_adjustment(GTK_RANGE(map_vertical_scrollbar
), map_vadj
);
672 g_signal_connect(map_hadj
, "value_changed",
673 G_CALLBACK(scrollbar_jump_callback
),
674 GINT_TO_POINTER(TRUE
));
675 g_signal_connect(map_vadj
, "value_changed",
676 G_CALLBACK(scrollbar_jump_callback
),
677 GINT_TO_POINTER(FALSE
));
680 /**************************************************************************
682 **************************************************************************/
683 void scrollbar_jump_callback(GtkAdjustment
*adj
, gpointer hscrollbar
)
685 int scroll_x
, scroll_y
;
687 if (!can_client_change_view()) {
691 get_mapview_scroll_pos(&scroll_x
, &scroll_y
);
694 scroll_x
= gtk_adjustment_get_value(adj
);
696 scroll_y
= gtk_adjustment_get_value(adj
);
699 set_mapview_scroll_pos(scroll_x
, scroll_y
);
702 /**************************************************************************
703 Draws a rectangle with top left corner at (canvas_x, canvas_y), and
704 width 'w' and height 'h'. It is drawn using the 'selection_gc' context,
705 so the pixel combining function is XOR. This means that drawing twice
706 in the same place will restore the image to its original state.
708 NB: A side effect of this function is to set the 'selection_gc' color
709 to COLOR_MAPVIEW_SELECTION.
710 **************************************************************************/
711 void draw_selection_rectangle(int canvas_x
, int canvas_y
, int w
, int h
)
713 double dashes
[2] = {4.0, 4.0};
714 struct color
*pcolor
;
717 if (w
== 0 || h
== 0) {
721 pcolor
= get_color(tileset
, COLOR_MAPVIEW_SELECTION
);
726 cr
= gdk_cairo_create(gtk_widget_get_window(map_canvas
));
727 gdk_cairo_set_source_rgba(cr
, &pcolor
->color
);
728 cairo_set_line_width(cr
, 2.0);
729 cairo_set_dash(cr
, dashes
, 2, 0);
730 cairo_set_operator(cr
, CAIRO_OPERATOR_DIFFERENCE
);
731 cairo_rectangle(cr
, canvas_x
, canvas_y
, w
, h
);
736 /**************************************************************************
737 This function is called when the tileset is changed.
738 **************************************************************************/
739 void tileset_changed(void)
741 science_report_dialog_redraw();
742 reset_city_dialogs();
744 blank_max_unit_size();
745 editgui_tileset_changed();
747 /* keep the icon of the executable on Windows (see PR#36491) */
748 #ifndef FREECIV_MSWINDOWS
750 GdkPixbuf
*pixbuf
= sprite_get_pixbuf(get_icon_sprite(tileset
, ICON_FREECIV
));
752 /* Only call this after tileset_load_tiles is called. */
753 gtk_window_set_icon(GTK_WINDOW(toplevel
), pixbuf
);
754 g_object_unref(pixbuf
);
756 #endif /* FREECIV_MSWINDOWS */