Freeciv-3.1
Loading...
Searching...
No Matches
mapctrl.c
Go to the documentation of this file.
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 <gtk/gtk.h>
19
20/* utility */
21#include "fcintl.h"
22#include "support.h"
23
24/* common */
25#include "combat.h"
26#include "game.h"
27#include "map.h"
28#include "player.h"
29#include "unit.h"
30
31#include "overview_common.h"
32
33/* client */
34#include "client_main.h"
35#include "climap.h"
36#include "climisc.h"
37#include "control.h"
38#include "editor.h"
39#include "tilespec.h"
40#include "text.h"
41#include "zoom.h"
42
43/* client/agents */
44#include "cma_core.h"
45
46/* client/gui-gtk-3.0 */
47#include "chatline.h"
48#include "citydlg.h"
49#include "colors.h"
50#include "dialogs.h"
51#include "editgui.h"
52#include "graphics.h"
53#include "gui_main.h"
54#include "inputdlg.h"
55#include "mapview.h"
56#include "menu.h"
57
58#include "mapctrl.h"
59
60struct tmousepos { int x, y; };
61extern gint cur_x, cur_y;
62
63/**********************************************************************/
66static gboolean popit_button_release(GtkWidget *w, GdkEventButton *event)
67{
68 gtk_grab_remove(w);
69 gdk_device_ungrab(event->device, event->time);
70 gtk_widget_destroy(w);
71 return FALSE;
72}
73
74/**********************************************************************/
80static void popupinfo_positioning_callback(GtkWidget *w, GtkAllocation *alloc,
81 gpointer data)
82{
83 struct tmousepos *mousepos = data;
84 float x, y;
85 struct tile *ptile;
86
87 ptile = canvas_pos_to_tile(mousepos->x, mousepos->y, map_zoom);
88 if (tile_to_canvas_pos(&x, &y, map_zoom, ptile)) {
89 gint minx, miny, maxy;
90
91 gdk_window_get_origin(gtk_widget_get_window(map_canvas), &minx, &miny);
92 maxy = miny + gtk_widget_get_allocated_height(map_canvas);
93
94 if (x > mapview.width/2) {
95 /* right part of the map */
96 x += minx;
97 y += miny + (tileset_tile_height(tileset) - alloc->height)/2;
98
99 y = CLIP(miny, y, maxy - alloc->height);
100
101 gtk_window_move(GTK_WINDOW(w), x - alloc->width, y);
102 } else {
103 /* left part of the map */
104 x += minx + tileset_tile_width(tileset);
105 y += miny + (tileset_tile_height(tileset) - alloc->height)/2;
106
107 y = CLIP(miny, y, maxy - alloc->height);
108
109 gtk_window_move(GTK_WINDOW(w), x, y);
110 }
111 }
112}
113
114/**********************************************************************/
118static void popit(GdkEventButton *event, struct tile *ptile)
119{
120 GtkWidget *p;
121 static struct tmousepos mousepos;
122 struct unit *punit;
123
124 if (TILE_UNKNOWN != client_tile_get_known(ptile)) {
125 p = gtk_window_new(GTK_WINDOW_POPUP);
126 gtk_container_set_border_width(GTK_CONTAINER(p), 4);
127 gtk_window_set_transient_for(GTK_WINDOW(p), GTK_WINDOW(toplevel));
128 gtk_container_add(GTK_CONTAINER(p), gtk_label_new(popup_info_text(ptile)));
129
130 punit = find_visible_unit(ptile);
131
132 if (punit) {
134 if (punit->goto_tile) {
136 }
137 }
139
140 g_signal_connect(p, "destroy",
141 G_CALLBACK(popupinfo_popdown_callback), NULL);
142
143 mousepos.x = event->x;
144 mousepos.y = event->y;
145
146 g_signal_connect(p, "size-allocate",
148 &mousepos);
149
150 gtk_widget_show_all(p);
151 gdk_device_grab(event->device, gtk_widget_get_window(p),
152 GDK_OWNERSHIP_NONE, TRUE, GDK_BUTTON_RELEASE_MASK, NULL,
153 event->time);
154 gtk_grab_add(p);
155
156 g_signal_connect_after(p, "button_release_event",
157 G_CALLBACK(popit_button_release), NULL);
158 }
159}
160
161/**********************************************************************/
164void popupinfo_popdown_callback(GtkWidget *w, gpointer data)
165{
168}
169
170/**********************************************************************/
173static void name_new_city_popup_callback(gpointer data, gint response,
174 const char *input)
175{
176 int idx = GPOINTER_TO_INT(data);
177
178 switch (response) {
179 case GTK_RESPONSE_OK:
180 finish_city(index_to_tile(&(wld.map), idx), input);
181 break;
182 case GTK_RESPONSE_CANCEL:
183 case GTK_RESPONSE_DELETE_EVENT:
185 break;
186 }
187}
188
189/**********************************************************************/
194void popup_newcity_dialog(struct unit *punit, const char *suggestname)
195{
196 input_dialog_create(GTK_WINDOW(toplevel), /*"shellnewcityname" */
197 _("Build New City"),
198 _("What should we call our new city?"), suggestname,
200 GINT_TO_POINTER(tile_index(unit_tile(punit))));
201}
202
203/**********************************************************************/
208{
209 gtk_widget_set_sensitive(turn_done_button, state);
210}
211
212/**********************************************************************/
216gboolean butt_release_mapcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data)
217{
218 if (editor_is_active()) {
220 }
221
222 if (ev->button == 1 || ev->button == 3) {
223 release_goto_button(ev->x, ev->y);
224 }
225 if (ev->button == 3 && (rbutton_down || hover_state != HOVER_NONE)) {
226 release_right_button(ev->x, ev->y,
227 (ev->state & GDK_SHIFT_MASK) != 0);
228 }
229
230 return TRUE;
231}
232
233/**********************************************************************/
237gboolean butt_down_mapcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data)
238{
239 struct city *pcity = NULL;
240 struct tile *ptile = NULL;
241
242 if (editor_is_active()) {
244 }
245
246 if (!can_client_change_view()) {
247 return TRUE;
248 }
249
250 gtk_widget_grab_focus(map_canvas);
251 ptile = canvas_pos_to_tile(ev->x, ev->y, mouse_zoom);
252 pcity = ptile ? tile_city(ptile) : NULL;
253
254 switch (ev->button) {
255
256 case 1: /* LEFT mouse button */
257
258 /* <SHIFT> + <CONTROL> + LMB : Adjust workers. */
259 if ((ev->state & GDK_SHIFT_MASK) && (ev->state & GDK_CONTROL_MASK)) {
260 adjust_workers_button_pressed(ev->x, ev->y);
261 } else if (ev->state & GDK_CONTROL_MASK) {
262 /* <CONTROL> + LMB : Quickselect a sea unit. */
263 action_button_pressed(ev->x, ev->y, SELECT_SEA);
264 } else if (ptile && (ev->state & GDK_SHIFT_MASK)) {
265 /* <SHIFT> + LMB: Append focus unit. */
267 } else if (ptile && (ev->state & GDK_MOD1_MASK)) {
268 /* <ALT> + LMB: popit (same as middle-click) */
269 /* FIXME: we need a general mechanism for letting freeciv work with
270 * 1- or 2-button mice. */
271 popit(ev, ptile);
272 } else if (tiles_hilited_cities) {
273 /* LMB in Area Selection mode. */
274 if (ptile) {
275 toggle_tile_hilite(ptile);
276 }
277 } else {
278 /* Plain LMB click. */
279 action_button_pressed(ev->x, ev->y, SELECT_POPUP);
280 }
281 break;
282
283 case 2: /* MIDDLE mouse button */
284
285 /* <CONTROL> + MMB: Wake up sentries. */
286 if (ev->state & GDK_CONTROL_MASK) {
287 wakeup_button_pressed(ev->x, ev->y);
288 } else if (ptile) {
289 /* Plain Middle click. */
290 popit(ev, ptile);
291 }
292 break;
293
294 case 3: /* RIGHT mouse button */
295
296 /* <CONTROL> + <ALT> + RMB : insert city or tile chat link. */
297 /* <CONTROL> + <ALT> + <SHIFT> + RMB : insert unit chat link. */
298 if (ptile && (ev->state & GDK_MOD1_MASK)
299 && (ev->state & GDK_CONTROL_MASK)) {
300 inputline_make_chat_link(ptile, (ev->state & GDK_SHIFT_MASK) != 0);
301 } else if ((ev->state & GDK_SHIFT_MASK) && (ev->state & GDK_MOD1_MASK)) {
302 /* <SHIFT> + <ALT> + RMB : Show/hide workers. */
303 key_city_overlay(ev->x, ev->y);
304 } else if ((ev->state & GDK_SHIFT_MASK) && (ev->state & GDK_CONTROL_MASK)
305 && pcity != NULL) {
306 /* <SHIFT + CONTROL> + RMB: Paste Production. */
309 } else if (ev->state & GDK_SHIFT_MASK
310 && clipboard_copy_production(ptile)) {
311 /* <SHIFT> + RMB on city/unit: Copy Production. */
312 /* If nothing to copy, fall through to rectangle selection. */
313
314 /* Already done the copy */
315 } else if (ev->state & GDK_CONTROL_MASK) {
316 /* <CONTROL> + RMB : Quickselect a land unit. */
317 action_button_pressed(ev->x, ev->y, SELECT_LAND);
318 } else {
319 /* Plain RMB click. Area selection. */
320
321 /* A foolproof user will depress button on canvas,
322 * release it on another widget, and return to canvas
323 * to find rectangle still active.
324 */
325 if (rectangle_active) {
326 release_right_button(ev->x, ev->y,
327 (ev->state & GDK_SHIFT_MASK) != 0);
328 return TRUE;
329 }
330 if (hover_state == HOVER_NONE) {
331 anchor_selection_rectangle(ev->x, ev->y);
332 rbutton_down = TRUE; /* causes rectangle updates */
333 }
334 }
335 break;
336
337 default:
338 break;
339 }
340
341 return TRUE;
342}
343
344/**********************************************************************/
348{
349 int x, y;
350 GdkWindow *window;
351 GdkDeviceManager *manager =
352 gdk_display_get_device_manager(gtk_widget_get_display(toplevel));
353 GdkDevice *pointer = gdk_device_manager_get_client_pointer(manager);
354
355 if (!pointer) {
356 return;
357 }
358
359 window = gdk_device_get_window_at_position(pointer, &x, &y);
360 if (window) {
361 if (window == gtk_widget_get_window(map_canvas)) {
362 update_line(x, y);
363 } else if (window == gtk_widget_get_window(overview_canvas)) {
365 }
366 }
367}
368
369/**********************************************************************/
374{
375 int x, y;
376 GdkWindow *window;
377 GdkDevice *pointer;
378 GdkModifierType mask;
379 GdkDeviceManager *manager =
380 gdk_display_get_device_manager(gtk_widget_get_display(toplevel));
381
382 pointer = gdk_device_manager_get_client_pointer(manager);
383 if (!pointer) {
384 return;
385 }
386
387 window = gdk_device_get_window_at_position(pointer, &x, &y);
388 if (window && window == gtk_widget_get_window(map_canvas)) {
389 gdk_device_get_state(pointer, window, NULL, &mask);
390 if (mask & GDK_BUTTON3_MASK) {
392 }
393 }
394}
395
396/**********************************************************************/
400gboolean move_mapcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data)
401{
402 if (GUI_GTK_OPTION(mouse_over_map_focus)
403 && !gtk_widget_has_focus(map_canvas)) {
404 gtk_widget_grab_focus(map_canvas);
405 }
406
407 if (editor_is_active()) {
408 return handle_edit_mouse_move(ev);
409 }
410
411 cur_x = ev->x;
412 cur_y = ev->y;
413 update_line(ev->x, ev->y);
414 if (rbutton_down && (ev->state & GDK_BUTTON3_MASK)) {
415 update_selection_rectangle(ev->x, ev->y);
416 }
417
420 }
422
423 return TRUE;
424}
425
426/**********************************************************************/
429gboolean leave_mapcanvas(GtkWidget *widget, GdkEventCrossing *event)
430{
431 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(top_notebook))
432 != gtk_notebook_page_num(GTK_NOTEBOOK(top_notebook), map_widget)) {
433 /* Map is not currently topmost tab. Do not use tile specific cursors. */
435 return TRUE;
436 }
437
438 /* Bizarrely, this function can be called even when we don't "leave"
439 * the map canvas, for instance, it gets called any time the mouse is
440 * clicked. */
441 if (!map_is_empty()
442 && event->x >= 0 && event->y >= 0
443 && event->x < mapview.width && event->y < mapview.height) {
445 mouse_zoom));
446 } else {
448 }
449
451
452 return TRUE;
453}
454
455/**********************************************************************/
458gboolean move_overviewcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data)
459{
460 overview_update_line(ev->x, ev->y);
461 return TRUE;
462}
463
464/**********************************************************************/
467gboolean butt_down_overviewcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data)
468{
469 int xtile, ytile;
470
471 if (ev->type != GDK_BUTTON_PRESS) {
472 return TRUE; /* Double-clicks? Triple-clicks? No thanks! */
473 }
474
475 overview_to_map_pos(&xtile, &ytile, ev->x, ev->y);
476
477 if (can_client_change_view() && (ev->button == 3)) {
478 center_tile_mapcanvas(map_pos_to_tile(&(wld.map), xtile, ytile));
479 } else if (can_client_issue_orders() && (ev->button == 1)) {
480 do_map_click(map_pos_to_tile(&(wld.map), xtile, ytile),
481 (ev->state & GDK_SHIFT_MASK) ? SELECT_APPEND : SELECT_POPUP);
482 }
483
484 return TRUE;
485}
486
487/**********************************************************************/
491{
493}
bool can_client_issue_orders(void)
bool can_client_change_view(void)
enum known_type client_tile_get_known(const struct tile *ptile)
Definition climap.c:36
void do_map_click(struct tile *ptile, enum quickselect_type qtype)
Definition control.c:2795
struct unit_list * get_units_in_focus(void)
Definition control.c:177
void request_center_focus_unit(void)
Definition control.c:2637
enum cursor_hover_state hover_state
Definition control.c:89
void finish_city(struct tile *ptile, const char *name)
Definition control.c:3969
struct unit * find_visible_unit(struct tile *ptile)
Definition control.c:823
void control_mouse_cursor(struct tile *ptile)
Definition control.c:1214
void cancel_city(struct tile *ptile)
Definition control.c:3988
@ SELECT_LAND
Definition control.h:37
@ SELECT_APPEND
Definition control.h:37
@ SELECT_SEA
Definition control.h:37
@ SELECT_POPUP
Definition control.h:37
@ HOVER_NONE
Definition control.h:26
struct unit struct city struct unit struct tile struct extra_type const struct act_prob *act_probs int actor_unit_id struct unit struct unit * punit
Definition dialogs_g.h:73
bool editor_is_active(void)
Definition editor.c:346
enum event_type event
Definition events.c:81
#define _(String)
Definition fcintl.h:67
struct world wld
Definition game.c:58
void inputline_make_chat_link(struct tile *ptile, bool unit)
Definition chatline.c:474
gboolean handle_edit_mouse_button_release(GdkEventButton *ev)
Definition editgui.c:955
gboolean handle_edit_mouse_button_press(GdkEventButton *ev)
Definition editgui.c:939
gboolean handle_edit_mouse_move(GdkEventMotion *ev)
Definition editgui.c:970
GtkWidget * top_notebook
Definition gui_main.c:128
GtkWidget * turn_done_button
Definition gui_main.c:148
GtkWidget * overview_canvas
Definition gui_main.c:110
GtkWidget * toplevel
Definition gui_main.c:124
GtkWidget * map_canvas
Definition gui_main.c:106
GtkWidget * map_widget
Definition gui_main.c:129
#define GUI_GTK_OPTION(optname)
Definition gui_main.h:25
GtkWidget * input_dialog_create(GtkWindow *parent, const char *dialogname, const char *text, const char *postinputtest, input_dialog_callback_t response_callback, gpointer response_cli_data)
Definition inputdlg.c:66
gboolean butt_release_mapcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data)
Definition mapctrl.c:216
gboolean move_overviewcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data)
Definition mapctrl.c:458
gboolean butt_down_overviewcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data)
Definition mapctrl.c:467
gint cur_y
Definition mapctrl.c:61
void popup_newcity_dialog(struct unit *punit, const char *suggestname)
Definition mapctrl.c:194
void center_on_unit(void)
Definition mapctrl.c:490
void create_line_at_mouse_pos(void)
Definition mapctrl.c:347
static gboolean popit_button_release(GtkWidget *w, GdkEventButton *event)
Definition mapctrl.c:66
gboolean butt_down_mapcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data)
Definition mapctrl.c:237
gboolean move_mapcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data)
Definition mapctrl.c:400
gint cur_x
Definition gui_main.c:183
void set_turn_done_button_state(bool state)
Definition mapctrl.c:207
gboolean leave_mapcanvas(GtkWidget *widget, GdkEventCrossing *event)
Definition mapctrl.c:429
static void popupinfo_positioning_callback(GtkWidget *w, GtkAllocation *alloc, gpointer data)
Definition mapctrl.c:80
static void popit(GdkEventButton *event, struct tile *ptile)
Definition mapctrl.c:118
void popupinfo_popdown_callback(GtkWidget *w, gpointer data)
Definition mapctrl.c:164
static void name_new_city_popup_callback(gpointer data, gint response, const char *input)
Definition mapctrl.c:173
void update_rect_at_mouse_pos(void)
Definition mapctrl.c:373
void update_unit_info_label(struct unit_list *punits)
Definition mapview.c:258
void update_mouse_cursor(enum cursor_type new_cursor_type)
Definition mapview.c:240
struct tile * index_to_tile(const struct civ_map *imap, int mindex)
Definition map.c:454
struct tile * map_pos_to_tile(const struct civ_map *nmap, int map_x, int map_y)
Definition map.c:417
bool map_is_empty(void)
Definition map.c:149
void update_line(int canvas_x, int canvas_y)
void maybe_activate_keyboardless_goto(int canvas_x, int canvas_y)
void adjust_workers_button_pressed(int canvas_x, int canvas_y)
bool rbutton_down
bool rectangle_active
void release_goto_button(int canvas_x, int canvas_y)
bool tiles_hilited_cities
void action_button_pressed(int canvas_x, int canvas_y, enum quickselect_type qtype)
void wakeup_button_pressed(int canvas_x, int canvas_y)
void release_right_button(int canvas_x, int canvas_y, bool shift)
bool keyboardless_goto_button_down
void key_city_overlay(int canvas_x, int canvas_y)
void update_selection_rectangle(float canvas_x, float canvas_y)
void overview_update_line(int overview_x, int overview_y)
void anchor_selection_rectangle(int canvas_x, int canvas_y)
void cancel_tile_hiliting(void)
void clipboard_paste_production(struct city *pcity)
bool clipboard_copy_production(struct tile *ptile)
void toggle_tile_hilite(struct tile *ptile)
void mapdeco_set_crosshair(const struct tile *ptile, bool crosshair)
struct view mapview
struct tile * canvas_pos_to_tile(float canvas_x, float canvas_y, float zoom)
void mapdeco_set_gotoroute(const struct unit *punit)
void mapdeco_clear_gotoroutes(void)
void center_tile_mapcanvas(const struct tile *ptile)
void mapdeco_clear_crosshairs(void)
bool tile_to_canvas_pos(float *canvas_x, float *canvas_y, float zoom, const struct tile *ptile)
void overview_to_map_pos(int *map_x, int *map_y, int overview_x, int overview_y)
#define CLIP(lower, current, upper)
Definition shared.h:57
Definition city.h:309
Definition tile.h:49
int x
Definition mapctrl.c:60
int y
Definition mapctrl.c:60
Definition unit.h:138
struct tile * goto_tile
Definition unit.h:155
int height
int width
struct civ_map map
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
const char * popup_info_text(struct tile *ptile)
Definition text.c:146
struct city * tile_city(const struct tile *ptile)
Definition tile.c:83
#define tile_index(_pt_)
Definition tile.h:87
@ TILE_UNKNOWN
Definition tile.h:35
int tileset_tile_height(const struct tileset *t)
Definition tilespec.c:728
int tileset_tile_width(const struct tileset *t)
Definition tilespec.c:716
@ CURSOR_DEFAULT
Definition tilespec.h:297
#define unit_tile(_pu)
Definition unit.h:395
float mouse_zoom
Definition zoom.c:28
float map_zoom
Definition zoom.c:25