From 5b617acb773926a12324a78ac0579911b4ef7b3f Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Tue, 9 Feb 2021 23:01:51 +0200 Subject: [PATCH 05/13] Drop gtk3-client See osdn #41531 Signed-off-by: Marko Lindqvist --- INSTALL | 83 +- Makefile.am | 1 - bootstrap/Makefile.am | 2 - bootstrap/freeciv-gtk3.appdata.xml.in | 24 - bootstrap/org.freeciv.gtk3.desktop.in | 24 - client/Makefile.am | 22 - client/gui-gtk-3.0/.gitignore | 5 - client/gui-gtk-3.0/Makefile.am | 108 - client/gui-gtk-3.0/action_dialog.c | 1776 ----- client/gui-gtk-3.0/canvas.c | 402 -- client/gui-gtk-3.0/canvas.h | 29 - client/gui-gtk-3.0/chatline.c | 1477 ---- client/gui-gtk-3.0/chatline.h | 42 - client/gui-gtk-3.0/choice_dialog.c | 241 - client/gui-gtk-3.0/choice_dialog.h | 38 - client/gui-gtk-3.0/citizensinfo.c | 393 -- client/gui-gtk-3.0/citizensinfo.h | 27 - client/gui-gtk-3.0/citydlg.c | 3490 ---------- client/gui-gtk-3.0/citydlg.h | 20 - client/gui-gtk-3.0/cityrep.c | 2106 ------ client/gui-gtk-3.0/cityrep.h | 20 - client/gui-gtk-3.0/cma_fe.c | 772 --- client/gui-gtk-3.0/cma_fe.h | 55 - client/gui-gtk-3.0/colors.c | 99 - client/gui-gtk-3.0/colors.h | 30 - client/gui-gtk-3.0/connectdlg.c | 60 - client/gui-gtk-3.0/connectdlg.h | 18 - client/gui-gtk-3.0/dialogs.c | 1583 ----- client/gui-gtk-3.0/dialogs.h | 26 - client/gui-gtk-3.0/diplodlg.c | 1186 ---- client/gui-gtk-3.0/diplodlg.h | 23 - client/gui-gtk-3.0/editgui.c | 1922 ------ client/gui-gtk-3.0/editgui.h | 69 - client/gui-gtk-3.0/editprop.c | 6532 ------------------ client/gui-gtk-3.0/editprop.h | 42 - client/gui-gtk-3.0/finddlg.c | 214 - client/gui-gtk-3.0/finddlg.h | 20 - client/gui-gtk-3.0/gamedlgs.c | 561 -- client/gui-gtk-3.0/gamedlgs.h | 18 - client/gui-gtk-3.0/gotodlg.c | 549 -- client/gui-gtk-3.0/gotodlg.h | 20 - client/gui-gtk-3.0/graphics.c | 112 - client/gui-gtk-3.0/graphics.h | 32 - client/gui-gtk-3.0/gui_main.c | 2424 ------- client/gui-gtk-3.0/gui_main.h | 88 - client/gui-gtk-3.0/gui_stuff.c | 1141 --- client/gui-gtk-3.0/gui_stuff.h | 133 - client/gui-gtk-3.0/happiness.c | 360 - client/gui-gtk-3.0/happiness.h | 29 - client/gui-gtk-3.0/helpdlg.c | 1674 ----- client/gui-gtk-3.0/helpdlg.h | 20 - client/gui-gtk-3.0/infradlg.c | 26 - client/gui-gtk-3.0/inputdlg.c | 104 - client/gui-gtk-3.0/inputdlg.h | 26 - client/gui-gtk-3.0/inteldlg.c | 470 -- client/gui-gtk-3.0/inteldlg.h | 21 - client/gui-gtk-3.0/luaconsole.c | 495 -- client/gui-gtk-3.0/luaconsole.h | 23 - client/gui-gtk-3.0/mapctrl.c | 490 -- client/gui-gtk-3.0/mapctrl.h | 33 - client/gui-gtk-3.0/mapview.c | 765 -- client/gui-gtk-3.0/mapview.h | 60 - client/gui-gtk-3.0/menu.c | 2928 -------- client/gui-gtk-3.0/menu.h | 24 - client/gui-gtk-3.0/messagedlg.c | 224 - client/gui-gtk-3.0/messagedlg.h | 20 - client/gui-gtk-3.0/messagewin.c | 455 -- client/gui-gtk-3.0/messagewin.h | 20 - client/gui-gtk-3.0/optiondlg.c | 1055 --- client/gui-gtk-3.0/optiondlg.h | 18 - client/gui-gtk-3.0/pages.c | 3570 ---------- client/gui-gtk-3.0/pages.h | 43 - client/gui-gtk-3.0/plrdlg.c | 943 --- client/gui-gtk-3.0/plrdlg.h | 26 - client/gui-gtk-3.0/ratesdlg.h | 20 - client/gui-gtk-3.0/repodlgs.c | 1926 ------ client/gui-gtk-3.0/repodlgs.h | 22 - client/gui-gtk-3.0/soundset_dlg.c | 145 - client/gui-gtk-3.0/spaceshipdlg.c | 294 - client/gui-gtk-3.0/spaceshipdlg.h | 23 - client/gui-gtk-3.0/sprite.c | 512 -- client/gui-gtk-3.0/sprite.h | 39 - client/gui-gtk-3.0/theme_dlg.c | 88 - client/gui-gtk-3.0/themes.c | 203 - client/gui-gtk-3.0/tileset_dlg.c | 94 - client/gui-gtk-3.0/transportdlg.c | 118 - client/gui-gtk-3.0/transportdlg.h | 18 - client/gui-gtk-3.0/unitselect.c | 1294 ---- client/gui-gtk-3.0/unitselect.h | 27 - client/gui-gtk-3.0/unitselextradlg.c | 236 - client/gui-gtk-3.0/unitselextradlg.h | 25 - client/gui-gtk-3.0/unitselunitdlg.c | 196 - client/gui-gtk-3.0/unitselunitdlg.h | 25 - client/gui-gtk-3.0/voteinfo_bar.c | 322 - client/gui-gtk-3.0/voteinfo_bar.h | 26 - client/gui-gtk-3.0/wldlg.c | 1511 ---- client/gui-gtk-3.0/wldlg.h | 40 - configure.ac | 26 +- data/Makefile.am | 4 - data/gtk3_menus.xml | 577 -- data/themes/Makefile.am | 3 - data/themes/gtk3/.gitignore | 3 - data/themes/gtk3/Freeciv/.gitignore | 3 - data/themes/gtk3/Freeciv/Makefile.am | 3 - data/themes/gtk3/Freeciv/gtk-3.0/.gitignore | 3 - data/themes/gtk3/Freeciv/gtk-3.0/Makefile.am | 11 - data/themes/gtk3/Freeciv/gtk-3.0/bg.png | Bin 657164 -> 0 bytes data/themes/gtk3/Freeciv/gtk-3.0/gtk.css | 249 - data/themes/gtk3/Freeciv/gtk-3.0/menubar.css | 35 - data/themes/gtk3/Makefile.am | 3 - doc/BUGS | 3 +- doc/README | 5 +- doc/README.msys2 | 4 +- doc/README.packaging | 1 + doc/man/Makefile.am | 1 - doc/man/freeciv-client.6.in | 21 +- doc/man/freeciv-gtk3.6 | 12 - m4/gtk3-client.m4 | 30 - translations/core/POTFILES.in | 39 - windows/installer_legacy/Makefile | 12 +- 120 files changed, 23 insertions(+), 50210 deletions(-) delete mode 100644 bootstrap/freeciv-gtk3.appdata.xml.in delete mode 100644 bootstrap/org.freeciv.gtk3.desktop.in delete mode 100644 client/gui-gtk-3.0/.gitignore delete mode 100644 client/gui-gtk-3.0/Makefile.am delete mode 100644 client/gui-gtk-3.0/action_dialog.c delete mode 100644 client/gui-gtk-3.0/canvas.c delete mode 100644 client/gui-gtk-3.0/canvas.h delete mode 100644 client/gui-gtk-3.0/chatline.c delete mode 100644 client/gui-gtk-3.0/chatline.h delete mode 100644 client/gui-gtk-3.0/choice_dialog.c delete mode 100644 client/gui-gtk-3.0/choice_dialog.h delete mode 100644 client/gui-gtk-3.0/citizensinfo.c delete mode 100644 client/gui-gtk-3.0/citizensinfo.h delete mode 100644 client/gui-gtk-3.0/citydlg.c delete mode 100644 client/gui-gtk-3.0/citydlg.h delete mode 100644 client/gui-gtk-3.0/cityrep.c delete mode 100644 client/gui-gtk-3.0/cityrep.h delete mode 100644 client/gui-gtk-3.0/cma_fe.c delete mode 100644 client/gui-gtk-3.0/cma_fe.h delete mode 100644 client/gui-gtk-3.0/colors.c delete mode 100644 client/gui-gtk-3.0/colors.h delete mode 100644 client/gui-gtk-3.0/connectdlg.c delete mode 100644 client/gui-gtk-3.0/connectdlg.h delete mode 100644 client/gui-gtk-3.0/dialogs.c delete mode 100644 client/gui-gtk-3.0/dialogs.h delete mode 100644 client/gui-gtk-3.0/diplodlg.c delete mode 100644 client/gui-gtk-3.0/diplodlg.h delete mode 100644 client/gui-gtk-3.0/editgui.c delete mode 100644 client/gui-gtk-3.0/editgui.h delete mode 100644 client/gui-gtk-3.0/editprop.c delete mode 100644 client/gui-gtk-3.0/editprop.h delete mode 100644 client/gui-gtk-3.0/finddlg.c delete mode 100644 client/gui-gtk-3.0/finddlg.h delete mode 100644 client/gui-gtk-3.0/gamedlgs.c delete mode 100644 client/gui-gtk-3.0/gamedlgs.h delete mode 100644 client/gui-gtk-3.0/gotodlg.c delete mode 100644 client/gui-gtk-3.0/gotodlg.h delete mode 100644 client/gui-gtk-3.0/graphics.c delete mode 100644 client/gui-gtk-3.0/graphics.h delete mode 100644 client/gui-gtk-3.0/gui_main.c delete mode 100644 client/gui-gtk-3.0/gui_main.h delete mode 100644 client/gui-gtk-3.0/gui_stuff.c delete mode 100644 client/gui-gtk-3.0/gui_stuff.h delete mode 100644 client/gui-gtk-3.0/happiness.c delete mode 100644 client/gui-gtk-3.0/happiness.h delete mode 100644 client/gui-gtk-3.0/helpdlg.c delete mode 100644 client/gui-gtk-3.0/helpdlg.h delete mode 100644 client/gui-gtk-3.0/infradlg.c delete mode 100644 client/gui-gtk-3.0/inputdlg.c delete mode 100644 client/gui-gtk-3.0/inputdlg.h delete mode 100644 client/gui-gtk-3.0/inteldlg.c delete mode 100644 client/gui-gtk-3.0/inteldlg.h delete mode 100644 client/gui-gtk-3.0/luaconsole.c delete mode 100644 client/gui-gtk-3.0/luaconsole.h delete mode 100644 client/gui-gtk-3.0/mapctrl.c delete mode 100644 client/gui-gtk-3.0/mapctrl.h delete mode 100644 client/gui-gtk-3.0/mapview.c delete mode 100644 client/gui-gtk-3.0/mapview.h delete mode 100644 client/gui-gtk-3.0/menu.c delete mode 100644 client/gui-gtk-3.0/menu.h delete mode 100644 client/gui-gtk-3.0/messagedlg.c delete mode 100644 client/gui-gtk-3.0/messagedlg.h delete mode 100644 client/gui-gtk-3.0/messagewin.c delete mode 100644 client/gui-gtk-3.0/messagewin.h delete mode 100644 client/gui-gtk-3.0/optiondlg.c delete mode 100644 client/gui-gtk-3.0/optiondlg.h delete mode 100644 client/gui-gtk-3.0/pages.c delete mode 100644 client/gui-gtk-3.0/pages.h delete mode 100644 client/gui-gtk-3.0/plrdlg.c delete mode 100644 client/gui-gtk-3.0/plrdlg.h delete mode 100644 client/gui-gtk-3.0/ratesdlg.h delete mode 100644 client/gui-gtk-3.0/repodlgs.c delete mode 100644 client/gui-gtk-3.0/repodlgs.h delete mode 100644 client/gui-gtk-3.0/soundset_dlg.c delete mode 100644 client/gui-gtk-3.0/spaceshipdlg.c delete mode 100644 client/gui-gtk-3.0/spaceshipdlg.h delete mode 100644 client/gui-gtk-3.0/sprite.c delete mode 100644 client/gui-gtk-3.0/sprite.h delete mode 100644 client/gui-gtk-3.0/theme_dlg.c delete mode 100644 client/gui-gtk-3.0/themes.c delete mode 100644 client/gui-gtk-3.0/tileset_dlg.c delete mode 100644 client/gui-gtk-3.0/transportdlg.c delete mode 100644 client/gui-gtk-3.0/transportdlg.h delete mode 100644 client/gui-gtk-3.0/unitselect.c delete mode 100644 client/gui-gtk-3.0/unitselect.h delete mode 100644 client/gui-gtk-3.0/unitselextradlg.c delete mode 100644 client/gui-gtk-3.0/unitselextradlg.h delete mode 100644 client/gui-gtk-3.0/unitselunitdlg.c delete mode 100644 client/gui-gtk-3.0/unitselunitdlg.h delete mode 100644 client/gui-gtk-3.0/voteinfo_bar.c delete mode 100644 client/gui-gtk-3.0/voteinfo_bar.h delete mode 100644 client/gui-gtk-3.0/wldlg.c delete mode 100644 client/gui-gtk-3.0/wldlg.h delete mode 100644 data/gtk3_menus.xml delete mode 100644 data/themes/gtk3/.gitignore delete mode 100644 data/themes/gtk3/Freeciv/.gitignore delete mode 100644 data/themes/gtk3/Freeciv/Makefile.am delete mode 100644 data/themes/gtk3/Freeciv/gtk-3.0/.gitignore delete mode 100644 data/themes/gtk3/Freeciv/gtk-3.0/Makefile.am delete mode 100644 data/themes/gtk3/Freeciv/gtk-3.0/bg.png delete mode 100644 data/themes/gtk3/Freeciv/gtk-3.0/gtk.css delete mode 100644 data/themes/gtk3/Freeciv/gtk-3.0/menubar.css delete mode 100644 data/themes/gtk3/Makefile.am delete mode 100644 doc/man/freeciv-gtk3.6 delete mode 100644 m4/gtk3-client.m4 diff --git a/INSTALL b/INSTALL index 836608e9b6..405cb498b0 100644 --- a/INSTALL +++ b/INSTALL @@ -4,7 +4,7 @@ Installing Freeciv: This file describes how to compile and install Freeciv. Last time we made sure this file is up to date was 16-Jul-06. -Last minor update was 23-Nov-20. +Last minor update was 09-Feb-21. There may be a localized version of this file in the ./doc directory, named INSTALL. (e.g., INSTALL.de). @@ -12,10 +12,9 @@ named INSTALL. (e.g., INSTALL.de). This document contains sections and subsections as follows: 0. Prerequisites: 1. Prerequisites for the clients: - 1a. Prerequisites for the Gtk3 client: - 1b. Prerequisites for the Gtk3.22 client: - 1c. Prerequisites for the SDL2 client: - 1d. Prerequisites for the Qt client: + 1a. Prerequisites for the Gtk3.22 client: + 1b. Prerequisites for the SDL2 client: + 1c. Prerequisites for the Qt client: 2. Generating Makefiles 2a. Generating the Makefile for git versions: 2b. Generating the Makefile for release versions: @@ -108,7 +107,6 @@ contains the generated files. ================================= The Freeciv client comes in the following forms: -* Gtk+ 3.0 widget library ("Gtk+ 3.0"). * Gtk+ 3.0 widget library, version 3.22 ("Gtk+ 3.22") * SDL2 * Qt @@ -128,74 +126,7 @@ files are found from the system. https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-2.0.4.tar.gz -1a. Prerequisites for the Gtk+ 3.0 client: -========================================== - - - "pkg-config". - - "pkg-config" is a system for managing library compile/link flags that - works with automake and autoconf. You may obtain it at: - - http://pkgconfig.freedesktop.org/releases/pkg-config-0.28.tar.gz - - - The "Glib" utility library. - - The "Glib" utility library provides non-graphical functions used by the - "Gdk" and "Gtk+" libraries, like hash tables, single linked lists, etc. - - Freeciv requires a version of "Glib" greater or equal to 2.36 - - If the Freeciv configure process tells you that you don't have the - Glib library installed, then it may be obtained from here: - - http://ftp.gnome.org/pub/gnome/sources/glib/2.36/glib-2.36.1.tar.xz - - - The "Atk" accessibility library. - - The "Atk" library provides a set of interfaces for accessibility. - It allows people with disabilities to utilize all the functionality - provided by Gtk+ apps. You may obtain it at: - - http://ftp.gnome.org/pub/gnome/sources/atk/2.8/atk-2.8.0.tar.xz - - - The "Pango" text layout and rendering library. - - "Pango" is library for layout and rendering of text, with an emphasis - on internationalization. You may obtain it at: - - http://ftp.gnome.org/pub/gnome/sources/pango/1.34/pango-1.34.0.tar.xz - - - The "Gdk-Pixbuf" image loading/saving library - - "Gdk-Pixbuf" used to be part of "Gtk+" itself, but is now separate - project. It may be obtained from: - - http://ftp.gnome.org/pub/gnome/sources/gdk-pixbuf/2.28/gdk-pixbuf-2.28.1.tar.xz - - - The "Gtk+" widget library. - - The "Gtk+" widget library was designed for the GIMP graphics program. - Since then it has gained popularity as an easy to program, free toolkit. - - The "Gtk+" library comes with one companion libraries: - - "Gdk": - Provides an abstraction layer over X-Windows/LinuxFB/Win32 to implement - basic drawing functions, windows, clipping, etc. - - Freeciv requires a version of "Gtk+" greater or equal to 3.10.0. - - If the Freeciv configure process tells you that you don't have the - Gtk+ library installed, then it may be obtained from here: - - http://ftp.gnome.org/pub/gnome/sources/gtk+/3.12/gtk+-3.12.2.tar.xz - - "Gtk+" depends on the "Glib", "Atk", "Gdk-Pixbuf", and "Pango" libraries. - -If you are going to make these yourself, build and install them in the -following order: pkg-config, Glib, Atk, Pango, Gdk-Pixbuf, Gtk+. - -1b. Prerequisites for the Gtk+ 3.22 client: +1a. Prerequisites for the Gtk+ 3.22 client: ========================================== - "pkg-config". @@ -265,7 +196,7 @@ following order: pkg-config, Glib, Atk, Pango, Gdk-Pixbuf, Gtk+. If you are going to make these yourself, build and install them in the following order: pkg-config, Glib, Atk, Pango, Gdk-Pixbuf, Gtk+. -1c. Prerequisites for the SDL2 client: +1b. Prerequisites for the SDL2 client: ===================================== - The "SDL2" library. @@ -306,7 +237,7 @@ following order: pkg-config, Glib, Atk, Pango, Gdk-Pixbuf, Gtk+. http://download.savannah.gnu.org/releases/freetype/freetype-2.7.1.tar.bz2 -1d. Prerequisites for the Qt client: +1c. Prerequisites for the Qt client: ==================================== - C++ compiler. diff --git a/Makefile.am b/Makefile.am index c2764d140f..366cc9211b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -50,7 +50,6 @@ EXTRA_DIST = autogen.sh \ m4/features.m4 \ m4/gettext.m4 \ m4/gettimeofday.m4 \ - m4/gtk3-client.m4 \ m4/gtk3.22-client.m4 \ m4/gtk3x-client.m4 \ m4/host-cpu-c-abi.m4 \ diff --git a/bootstrap/Makefile.am b/bootstrap/Makefile.am index 12aaa7a2dc..db01dd2b97 100644 --- a/bootstrap/Makefile.am +++ b/bootstrap/Makefile.am @@ -16,7 +16,6 @@ EXTRA_DIST = freeciv.project \ langstat_core.txt \ langstat_nations.txt \ langstat_ruledit.txt \ - org.freeciv.gtk3.desktop.in \ org.freeciv.gtk322.desktop.in \ org.freeciv.sdl2.desktop.in \ org.freeciv.qt.desktop.in \ @@ -25,7 +24,6 @@ EXTRA_DIST = freeciv.project \ org.freeciv.mp.gtk4.desktop.in \ org.freeciv.mp.qt.desktop.in \ org.freeciv.ruledit.desktop.in \ - freeciv-gtk3.appdata.xml.in \ freeciv-sdl2.appdata.xml.in \ freeciv-qt.appdata.xml.in \ freeciv-server.appdata.xml.in \ diff --git a/bootstrap/freeciv-gtk3.appdata.xml.in b/bootstrap/freeciv-gtk3.appdata.xml.in deleted file mode 100644 index 7973b54058..0000000000 --- a/bootstrap/freeciv-gtk3.appdata.xml.in +++ /dev/null @@ -1,24 +0,0 @@ - - - Freeciv gtk+-3 client - org.freeciv.gtk3 - CC0 - Gtk+-3 based client for the Freeciv game - -

- Freeciv is a Free and Open Source empire-building strategy game inspired by the history of human civilization. The game commences in prehistory and your - mission is to lead your tribe from the Stone Age to the Space Age... -

-

- This client for connecting to network games, or to launch local single-player games, is based on gtk3 widget set. -

-
- org.freeciv.gtk3.desktop - http://www.freeciv.org/ - - - http://images1.wikia.nocookie.net/__cb20120308234702/freeciv/images/thumb/4/43/Hex2.3.png/1000px-Hex2.3.png - - - freeciv-dev@freelists.org -
diff --git a/bootstrap/org.freeciv.gtk3.desktop.in b/bootstrap/org.freeciv.gtk3.desktop.in deleted file mode 100644 index 104a7e4faf..0000000000 --- a/bootstrap/org.freeciv.gtk3.desktop.in +++ /dev/null @@ -1,24 +0,0 @@ -[Desktop Entry] -Name=Freeciv -Name[ca]=Freeciv -Name[es]=Freeciv -Name[fr]=Freeciv -Name[nb]=Freeciv -Name[pt]=Freeciv -Name[ru]=Freeciv -Comment=Turn-based strategy game inspired by the history of human civilization -Comment[ca]=Joc d'estratègia inspirat en la història de la civilització humana -Comment[da]=Strategispil inspireret af den menneskelige civilisations historie -Comment[de]=Rundenbasiertes Strategiespiel, inspiriert durch die Geschichte der menschlichen Zivilisation -Comment[fi]=Ihmiskunnan historian inspiroima vuoropohjainen strategiapeli -Comment[nb]=Strategispill inspirert av historien til menneskelig sivilisasjon -Comment[pt]=Jogo de estratégia por turnos inspirado na História da civilização humana -Comment[ru]=Пошаговая стратегическая игра, вдохновлённая историей человеческой цивилизации -Comment[sv]=Turordningsbaserat strategispel inspirerat av den mänskliga historien -Exec=freeciv-gtk3 -Icon=freeciv-client -StartupNotify=true -Terminal=false -Type=Application -Categories=GTK;Game;StrategyGame; -Keywords=strategy;simulation;civilization;tiles;history;mankind;multiplayer; diff --git a/client/Makefile.am b/client/Makefile.am index adfd9aba95..180350b15e 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -16,13 +16,6 @@ desktopfile_DATA += \ appdatafile_DATA += \ freeciv-sdl2.appdata.xml endif -if CLIENT_GUI_GTK_3_0 -GUI_SUBDIRS += gui-gtk-3.0 -desktopfile_DATA += \ - org.freeciv.gtk3.desktop -appdatafile_DATA += \ - freeciv-gtk3.appdata.xml -endif if CLIENT_GUI_GTK_3_22 GUI_SUBDIRS += gui-gtk-3.22 desktopfile_DATA += \ @@ -186,21 +179,6 @@ clientlibs = \ $(top_builddir)/client/luascript/libscripting_client.la \ $(top_builddir)/dependencies/cvercmp/libcvercmp.la -if CLIENT_GUI_GTK_3_0 -bin_PROGRAMS += freeciv-gtk3 -noinst_LTLIBRARIES += libfcgui-gtk3.la -libfcgui_gtk3_la_SOURCES = $(freeciv_client_src) -libfcgui_gtk3_la_LIBADD = gui-gtk-3.0/libgui-gtk3.la $(clientlibs) -freeciv_gtk3_SOURCES = dummy.c -freeciv_gtk3_LDFLAGS = $(gui_gtk3_ldflags) -freeciv_gtk3_LDADD = \ - libfcgui-gtk3.la $(SOUND_LIBS) gui-gtk-3.0/gui_main.lo \ - $(top_builddir)/common/libfreeciv.la \ - $(INTLLIBS) $(CLIENT_LIBS) $(CLIENTICON) \ - $(TINYCTHR_LIBS) $(MAPIMG_WAND_LIBS) \ - $(gui_gtk3_libs) -endif - if CLIENT_GUI_GTK_3_22 bin_PROGRAMS += freeciv-gtk3.22 noinst_LTLIBRARIES += libfcgui-gtk3_22.la diff --git a/client/gui-gtk-3.0/.gitignore b/client/gui-gtk-3.0/.gitignore deleted file mode 100644 index a27cc1080a..0000000000 --- a/client/gui-gtk-3.0/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/Makefile -/Makefile.in -/.deps -/Freeciv.h -/libguiclient.a diff --git a/client/gui-gtk-3.0/Makefile.am b/client/gui-gtk-3.0/Makefile.am deleted file mode 100644 index f5bf666adf..0000000000 --- a/client/gui-gtk-3.0/Makefile.am +++ /dev/null @@ -1,108 +0,0 @@ -## Process this file with automake to produce Makefile.in - -noinst_LTLIBRARIES = libgui-gtk3.la -AM_CPPFLAGS = \ - -I$(srcdir)/.. \ - -I$(srcdir)/../include \ - -I$(top_srcdir)/utility \ - -I$(top_srcdir)/common \ - -I$(top_srcdir)/common/aicore \ - -I$(top_srcdir)/common/networking \ - -I$(top_srcdir)/common/scriptcore \ - -I$(srcdir)/../agents \ - -I$(srcdir)/../luascript \ - -I$(top_srcdir)/dependencies/tinycthread \ - $(gui_gtk3_cflags) $(SOUND_CFLAGS) - -libgui_gtk3_la_SOURCES = \ - action_dialog.c \ - canvas.c \ - canvas.h \ - chatline.h \ - chatline.c \ - choice_dialog.c \ - choice_dialog.h \ - citizensinfo.c \ - citizensinfo.h \ - citydlg.c \ - citydlg.h \ - cityrep.c \ - cityrep.h \ - cma_fe.c \ - cma_fe.h \ - colors.c \ - colors.h \ - connectdlg.c \ - connectdlg.h \ - dialogs.c \ - dialogs.h \ - diplodlg.c \ - diplodlg.h \ - editgui.c \ - editgui.h \ - editprop.c \ - editprop.h \ - finddlg.c \ - finddlg.h \ - gamedlgs.c \ - gamedlgs.h \ - gotodlg.c \ - gotodlg.h \ - graphics.c \ - graphics.h \ - gui_main.c \ - gui_main.h \ - gui_stuff.c \ - gui_stuff.h \ - happiness.c \ - happiness.h \ - helpdlg.c \ - helpdlg.h \ - infradlg.c \ - inputdlg.c \ - inputdlg.h \ - inteldlg.c \ - inteldlg.h \ - luaconsole.c \ - luaconsole.h \ - mapctrl.c \ - mapctrl.h \ - mapview.c \ - mapview.h \ - menu.c \ - menu.h \ - messagedlg.c \ - messagedlg.h \ - messagewin.c \ - messagewin.h \ - optiondlg.c \ - optiondlg.h \ - pages.c \ - pages.h \ - plrdlg.c \ - plrdlg.h \ - ratesdlg.h \ - repodlgs.c \ - repodlgs.h \ - soundset_dlg.c \ - spaceshipdlg.c \ - spaceshipdlg.h \ - sprite.c \ - sprite.h \ - theme_dlg.c \ - themes.c \ - tileset_dlg.c \ - transportdlg.c \ - transportdlg.h \ - unitselextradlg.c \ - unitselextradlg.h \ - unitselunitdlg.c \ - unitselunitdlg.h \ - unitselect.h \ - unitselect.c \ - voteinfo_bar.c \ - voteinfo_bar.h \ - wldlg.c \ - wldlg.h - -libgui_gtk3_la_LIBADD = -lm diff --git a/client/gui-gtk-3.0/action_dialog.c b/client/gui-gtk-3.0/action_dialog.c deleted file mode 100644 index 27e877f412..0000000000 --- a/client/gui-gtk-3.0/action_dialog.c +++ /dev/null @@ -1,1776 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "astring.h" -#include "support.h" - -/* common */ -#include "actions.h" -#include "game.h" -#include "traderoutes.h" -#include "movement.h" -#include "research.h" -#include "unit.h" -#include "unitlist.h" - -/* client */ -#include "dialogs_g.h" -#include "chatline.h" -#include "choice_dialog.h" -#include "client_main.h" -#include "climisc.h" -#include "connectdlg_common.h" -#include "control.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapview.h" -#include "packhand.h" -#include "text.h" - -/* client/gui-gtk-3.0 */ -#include "citydlg.h" -#include "dialogs.h" -#include "unitselextradlg.h" -#include "unitselunitdlg.h" -#include "wldlg.h" - -/* Locations for non action enabler controlled buttons. */ -#define BUTTON_MOVE ACTION_COUNT -#define BUTTON_NEW_UNIT_TGT BUTTON_MOVE + 1 -#define BUTTON_NEW_EXTRA_TGT BUTTON_MOVE + 2 -#define BUTTON_LOCATION BUTTON_MOVE + 3 -#define BUTTON_WAIT BUTTON_MOVE + 4 -#define BUTTON_CANCEL BUTTON_MOVE + 5 -#define BUTTON_COUNT BUTTON_MOVE + 6 - -#define BUTTON_NOT_THERE -1 - - -static GtkWidget *act_sel_dialog; -static int action_button_map[BUTTON_COUNT]; - -static int actor_unit_id; -static int target_ids[ATK_COUNT]; -static int target_extra_id; -static bool is_more_user_input_needed = FALSE; -static bool did_not_decide = FALSE; -static bool action_selection_restart = FALSE; - -static GtkWidget *spy_tech_shell; - -static GtkWidget *spy_sabotage_shell; - -/* A structure to hold parameters for actions inside the GUI in stead of - * storing the needed data in a global variable. */ -struct action_data { - action_id act_id; - int actor_unit_id; - int target_city_id; - int target_unit_id; - int target_tile_id; - int target_building_id; - int target_tech_id; - int target_extra_id; -}; - -/* TODO: maybe this should be in the dialog itself? */ -static struct action_data *act_sel_dialog_data; - -/**********************************************************************//** - Create a new action data structure that can be stored in the - dialogs. -**************************************************************************/ -static struct action_data *act_data(action_id act_id, - int actor_id, - int target_city_id, - int target_unit_id, - int target_tile_id, - int target_building_id, - int target_tech_id, - int tgt_extra_id) -{ - struct action_data *data = fc_malloc(sizeof(*data)); - - data->act_id = act_id; - data->actor_unit_id = actor_id; - data->target_city_id = target_city_id; - data->target_unit_id = target_unit_id; - data->target_tile_id = target_tile_id; - data->target_building_id = target_building_id; - data->target_tech_id = target_tech_id; - data->target_extra_id = tgt_extra_id; - - return data; -} - -/**********************************************************************//** - Move the queue of units that need user input forward unless the current - unit are going to need more input. -**************************************************************************/ -static void diplomat_queue_handle_primary(void) -{ - if (!is_more_user_input_needed) { - /* The client isn't waiting for information for any unanswered follow - * up questions. */ - - struct unit *actor_unit; - - if ((actor_unit = game_unit_by_number(actor_unit_id))) { - /* The action selection dialog wasn't closed because the actor unit - * was lost. */ - - /* The probabilities didn't just disappear, right? */ - fc_assert_action(actor_unit->client.act_prob_cache, - client_unit_init_act_prob_cache(actor_unit)); - - FC_FREE(actor_unit->client.act_prob_cache); - } - - if (action_selection_restart) { - /* The action selection dialog was closed but only so it can be - * redrawn with fresh data. */ - - action_selection_restart = FALSE; - } else { - /* The action selection process is over, at least for now. */ - action_selection_no_longer_in_progress(actor_unit_id); - } - - if (did_not_decide) { - /* The action selection dialog was closed but the player didn't - * decide what the unit should do. */ - - /* Reset so the next action selection dialog does the right thing. */ - did_not_decide = FALSE; - } else { - /* An action, or no action at all, was selected. */ - action_decision_clear_want(actor_unit_id); - action_selection_next_in_focus(actor_unit_id); - } - } -} - -/**********************************************************************//** - Move the queue of diplomats that need user input forward since the - current diplomat got the extra input that was required. -**************************************************************************/ -static void diplomat_queue_handle_secondary(void) -{ - /* Stop waiting. Move on to the next queued diplomat. */ - is_more_user_input_needed = FALSE; - diplomat_queue_handle_primary(); -} - -/**********************************************************************//** - Let the non shared client code know that the action selection process - no longer is in progress for the specified unit. - - This allows the client to clean up any client specific assumptions. -**************************************************************************/ -void action_selection_no_longer_in_progress_gui_specific(int actor_id) -{ - /* Stop assuming the answer to a follow up question will arrive. */ - is_more_user_input_needed = FALSE; -} - -/**********************************************************************//** - Get the non targeted version of an action so it, if enabled, can appear - in the target selection dialog. -**************************************************************************/ -static action_id get_non_targeted_action_id(action_id tgt_action_id) -{ - /* Don't add an action mapping here unless the non targeted version is - * selectable in the targeted version's target selection dialog. */ - switch ((enum gen_action)tgt_action_id) { - case ACTION_SPY_TARGETED_SABOTAGE_CITY: - return ACTION_SPY_SABOTAGE_CITY; - case ACTION_SPY_TARGETED_SABOTAGE_CITY_ESC: - return ACTION_SPY_SABOTAGE_CITY_ESC; - case ACTION_SPY_TARGETED_STEAL_TECH: - return ACTION_SPY_STEAL_TECH; - case ACTION_SPY_TARGETED_STEAL_TECH_ESC: - return ACTION_SPY_STEAL_TECH_ESC; - default: - /* No non targeted version found. */ - return ACTION_NONE; - } -} - -/**********************************************************************//** - Get the production targeted version of an action so it, if enabled, can - appear in the target selection dialog. -**************************************************************************/ -static action_id get_production_targeted_action_id(action_id tgt_action_id) -{ - /* Don't add an action mapping here unless the non targeted version is - * selectable in the targeted version's target selection dialog. */ - switch ((enum gen_action)tgt_action_id) { - case ACTION_SPY_TARGETED_SABOTAGE_CITY: - return ACTION_SPY_SABOTAGE_CITY_PRODUCTION; - case ACTION_SPY_TARGETED_SABOTAGE_CITY_ESC: - return ACTION_SPY_SABOTAGE_CITY_PRODUCTION_ESC; - case ACTION_STRIKE_BUILDING: - return ACTION_STRIKE_PRODUCTION; - default: - /* No non targeted version found. */ - return ACTION_NONE; - } -} - -/**********************************************************************//** - User selected an action from the choice dialog and the action has no - special needs. -**************************************************************************/ -static void simple_action_callback(GtkWidget *w, gpointer data) -{ - int actor_id, target_id, sub_target; - struct action *paction; - - struct action_data *args = act_sel_dialog_data; - - bool failed = FALSE; - - /* Data */ - args->act_id = GPOINTER_TO_INT(data); - paction = action_by_number(args->act_id); - - /* Actor */ - fc_assert(action_get_actor_kind(paction) == AAK_UNIT); - actor_id = args->actor_unit_id; - if (NULL == game_unit_by_number(actor_id)) { - /* Probably dead. */ - failed = TRUE; - } - - /* Target */ - target_id = IDENTITY_NUMBER_ZERO; - switch (action_get_target_kind(paction)) { - case ATK_CITY: - target_id = args->target_city_id; - if (NULL == game_city_by_number(target_id)) { - /* Probably destroyed. */ - failed = TRUE; - } - break; - case ATK_UNIT: - target_id = args->target_unit_id; - if (NULL == game_unit_by_number(target_id)) { - /* Probably dead. */ - failed = TRUE; - } - break; - case ATK_UNITS: - case ATK_TILE: - case ATK_EXTRAS: - target_id = args->target_tile_id; - if (NULL == index_to_tile(&(wld.map), target_id)) { - /* TODO: Should this be possible at all? If not: add assertion. */ - failed = TRUE; - } - break; - case ATK_SELF: - target_id = IDENTITY_NUMBER_ZERO; - break; - case ATK_COUNT: - fc_assert(action_get_target_kind(paction) != ATK_COUNT); - failed = TRUE; - } - - /* Sub target. */ - sub_target = NO_TARGET; - if (paction->target_complexity != ACT_TGT_COMPL_SIMPLE) { - switch (action_get_sub_target_kind(paction)) { - case ASTK_BUILDING: - sub_target = args->target_building_id; - if (NULL == improvement_by_number(sub_target)) { - /* Did the ruleset change? */ - failed = TRUE; - } - break; - case ASTK_TECH: - sub_target = args->target_tech_id; - if (NULL == valid_advance_by_number(sub_target)) { - /* Did the ruleset change? */ - failed = TRUE; - } - break; - case ASTK_EXTRA: - case ASTK_EXTRA_NOT_THERE: - /* TODO: validate if the extra is there? */ - sub_target = args->target_extra_id; - if (NULL == extra_by_number(sub_target)) { - /* Did the ruleset change? */ - failed = TRUE; - } - break; - case ASTK_NONE: - case ASTK_COUNT: - /* Shouldn't happen. */ - fc_assert(action_get_sub_target_kind(paction) != ASTK_NONE); - failed = TRUE; - break; - } - } - - /* Send request. */ - if (!failed) { - request_do_action(paction->id, actor_id, target_id, sub_target, ""); - } - - /* Clean up. */ - gtk_widget_destroy(act_sel_dialog); - /* No follow up questions. */ - act_sel_dialog_data = NULL; - FC_FREE(args); -} - -/**********************************************************************//** - User selected an action from the choice dialog that needs details from - the server. -**************************************************************************/ -static void request_action_details_callback(GtkWidget *w, gpointer data) -{ - int actor_id, target_id; - struct action *paction; - - struct action_data *args = act_sel_dialog_data; - - bool failed = FALSE; - - /* Data */ - args->act_id = GPOINTER_TO_INT(data); - paction = action_by_number(args->act_id); - - /* Actor */ - fc_assert(action_get_actor_kind(paction) == AAK_UNIT); - actor_id = args->actor_unit_id; - if (NULL == game_unit_by_number(actor_id)) { - /* Probably dead. */ - failed = TRUE; - } - - /* Target */ - target_id = IDENTITY_NUMBER_ZERO; - switch (action_get_target_kind(paction)) { - case ATK_CITY: - target_id = args->target_city_id; - if (NULL == game_city_by_number(target_id)) { - /* Probably destroyed. */ - failed = TRUE; - } - break; - case ATK_UNIT: - target_id = args->target_unit_id; - if (NULL == game_unit_by_number(target_id)) { - /* Probably dead. */ - failed = TRUE; - } - break; - case ATK_UNITS: - case ATK_TILE: - case ATK_EXTRAS: - target_id = args->target_tile_id; - if (NULL == index_to_tile(&(wld.map), target_id)) { - /* TODO: Should this be possible at all? If not: add assertion. */ - failed = TRUE; - } - break; - case ATK_SELF: - target_id = IDENTITY_NUMBER_ZERO; - break; - case ATK_COUNT: - fc_assert(action_get_target_kind(paction) != ATK_COUNT); - failed = TRUE; - } - - /* Send request. */ - if (!failed) { - request_action_details(paction->id, actor_id, target_id); - } - - /* Wait for the server's reply before moving on to the next unit that - * needs to know what action to take. */ - is_more_user_input_needed = TRUE; - - /* Clean up. */ - gtk_widget_destroy(act_sel_dialog); - /* No client side follow up questions. */ - act_sel_dialog_data = NULL; - FC_FREE(args); -} - -/**********************************************************************//** - User selected build city from the choice dialog -**************************************************************************/ -static void found_city_callback(GtkWidget *w, gpointer data) -{ - struct action_data *args = act_sel_dialog_data; - - dsend_packet_city_name_suggestion_req(&client.conn, - args->actor_unit_id); - - gtk_widget_destroy(act_sel_dialog); - free(args); -} - -/**********************************************************************//** - User selected "Upgrade Unit" from choice dialog. -**************************************************************************/ -static void upgrade_callback(GtkWidget *w, gpointer data) -{ - struct unit *punit; - - struct action_data *args = act_sel_dialog_data; - - if ((punit = game_unit_by_number(args->actor_unit_id)) - && NULL != game_city_by_number(args->target_city_id)) { - struct unit_list *as_list; - - as_list = unit_list_new(); - unit_list_append(as_list, punit); - popup_upgrade_dialog(as_list); - unit_list_destroy(as_list); - } - - gtk_widget_destroy(act_sel_dialog); - free(args); -} - -/**********************************************************************//** - User responded to bribe dialog -**************************************************************************/ -static void bribe_response(GtkWidget *w, gint response, gpointer data) -{ - struct action_data *args = (struct action_data *)data; - - if (response == GTK_RESPONSE_YES) { - request_do_action(args->act_id, args->actor_unit_id, - args->target_unit_id, 0, ""); - } - - gtk_widget_destroy(w); - free(args); - - /* The user have answered the follow up question. Move on. */ - diplomat_queue_handle_secondary(); -} - -/**********************************************************************//** - Popup unit bribe dialog -**************************************************************************/ -void popup_bribe_dialog(struct unit *actor, struct unit *punit, int cost, - const struct action *paction) -{ - GtkWidget *shell; - char buf[1024]; - - fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.", - "Treasury contains %d gold.", - client_player()->economic.gold), - client_player()->economic.gold); - - if (cost <= client_player()->economic.gold) { - shell = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */ - PL_("Bribe unit for %d gold?\n%s", - "Bribe unit for %d gold?\n%s", cost), cost, buf); - gtk_window_set_title(GTK_WINDOW(shell), _("Bribe Enemy Unit")); - setup_dialog(shell, toplevel); - } else { - shell = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, - /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */ - PL_("Bribing the unit costs %d gold.\n%s", - "Bribing the unit costs %d gold.\n%s", cost), cost, buf); - gtk_window_set_title(GTK_WINDOW(shell), _("Traitors Demand Too Much!")); - setup_dialog(shell, toplevel); - } - gtk_window_present(GTK_WINDOW(shell)); - - g_signal_connect(shell, "response", G_CALLBACK(bribe_response), - act_data(paction->id, actor->id, - 0, punit->id, 0, - 0, 0, 0)); -} - -/**********************************************************************//** - User responded to steal advances dialog -**************************************************************************/ -static void spy_advances_response(GtkWidget *w, gint response, - gpointer data) -{ - struct action_data *args = (struct action_data *)data; - - if (response == GTK_RESPONSE_ACCEPT && args->target_tech_id > 0) { - if (NULL != game_unit_by_number(args->actor_unit_id) - && NULL != game_city_by_number(args->target_city_id)) { - if (args->target_tech_id == A_UNSET) { - /* This is the untargeted version. */ - request_do_action(get_non_targeted_action_id(args->act_id), - args->actor_unit_id, args->target_city_id, - args->target_tech_id, ""); - } else { - /* This is the targeted version. */ - request_do_action(args->act_id, - args->actor_unit_id, args->target_city_id, - args->target_tech_id, ""); - } - } - } - - gtk_widget_destroy(spy_tech_shell); - spy_tech_shell = NULL; - free(data); - - /* The user have answered the follow up question. Move on. */ - diplomat_queue_handle_secondary(); -} - -/**********************************************************************//** - User selected entry in steal advances dialog -**************************************************************************/ -static void spy_advances_callback(GtkTreeSelection *select, - gpointer data) -{ - struct action_data *args = (struct action_data *)data; - - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(select, &model, &it)) { - gtk_tree_model_get(model, &it, 1, &(args->target_tech_id), -1); - - gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell), - GTK_RESPONSE_ACCEPT, TRUE); - } else { - args->target_tech_id = 0; - - gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell), - GTK_RESPONSE_ACCEPT, FALSE); - } -} - -/**********************************************************************//** - Create spy's tech stealing dialog -**************************************************************************/ -static void create_advances_list(struct player *pplayer, - struct player *pvictim, - struct action_data *args) -{ - GtkWidget *sw, *label, *vbox, *view; - GtkListStore *store; - GtkCellRenderer *rend; - GtkTreeViewColumn *col; - - struct unit *actor_unit = game_unit_by_number(args->actor_unit_id); - - spy_tech_shell = gtk_dialog_new_with_buttons(_("Steal Technology"), - NULL, - 0, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - _("_Steal"), - GTK_RESPONSE_ACCEPT, - NULL); - setup_dialog(spy_tech_shell, toplevel); - gtk_window_set_position(GTK_WINDOW(spy_tech_shell), GTK_WIN_POS_MOUSE); - - gtk_dialog_set_default_response(GTK_DIALOG(spy_tech_shell), - GTK_RESPONSE_ACCEPT); - - label = gtk_frame_new(_("Select Advance to Steal")); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(spy_tech_shell))), label); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 6); - gtk_container_add(GTK_CONTAINER(label), vbox); - - store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(store); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, rend, - "text", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", view, - "label", _("_Advances:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_container_add(GTK_CONTAINER(vbox), label); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_container_add(GTK_CONTAINER(sw), view); - - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); - gtk_widget_set_size_request(sw, -1, 200); - - gtk_container_add(GTK_CONTAINER(vbox), sw); - - /* Now populate the list */ - if (pvictim) { /* you don't want to know what lag can do -- Syela */ - const struct research *presearch = research_get(pplayer); - const struct research *vresearch = research_get(pvictim); - GtkTreeIter it; - GValue value = { 0, }; - - advance_index_iterate(A_FIRST, i) { - if (research_invention_gettable(presearch, i, - game.info.tech_steal_allow_holes) - && research_invention_state(vresearch, i) == TECH_KNOWN - && research_invention_state(presearch, i) != TECH_KNOWN) { - gtk_list_store_append(store, &it); - - g_value_init(&value, G_TYPE_STRING); - g_value_set_static_string(&value, research_advance_name_translation - (presearch, i)); - gtk_list_store_set_value(store, &it, 0, &value); - g_value_unset(&value); - gtk_list_store_set(store, &it, 1, i, -1); - } - } advance_index_iterate_end; - - if (action_prob_possible(actor_unit->client.act_prob_cache[ - get_non_targeted_action_id(args->act_id)])) { - gtk_list_store_append(store, &it); - - g_value_init(&value, G_TYPE_STRING); - { - struct astring str = ASTRING_INIT; - /* TRANS: %s is a unit name, e.g., Spy */ - astr_set(&str, _("At %s's Discretion"), - unit_name_translation(actor_unit)); - g_value_set_string(&value, astr_str(&str)); - astr_free(&str); - } - gtk_list_store_set_value(store, &it, 0, &value); - g_value_unset(&value); - gtk_list_store_set(store, &it, 1, A_UNSET, -1); - } - } - - gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell), - GTK_RESPONSE_ACCEPT, FALSE); - - gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(spy_tech_shell))); - - g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), "changed", - G_CALLBACK(spy_advances_callback), args); - g_signal_connect(spy_tech_shell, "response", - G_CALLBACK(spy_advances_response), args); - - args->target_tech_id = 0; - - gtk_tree_view_focus(GTK_TREE_VIEW(view)); -} - -/**********************************************************************//** - User has responded to spy's sabotage building dialog -**************************************************************************/ -static void spy_improvements_response(GtkWidget *w, gint response, gpointer data) -{ - struct action_data *args = (struct action_data *)data; - - if (response == GTK_RESPONSE_ACCEPT && args->target_building_id > -2) { - if (NULL != game_unit_by_number(args->actor_unit_id) - && NULL != game_city_by_number(args->target_city_id)) { - if (args->target_building_id == B_LAST) { - /* This is the untargeted version. */ - request_do_action(get_non_targeted_action_id(args->act_id), - args->actor_unit_id, - args->target_city_id, - args->target_building_id, ""); - } else if (args->target_building_id == -1) { - /* This is the city production version. */ - request_do_action(get_production_targeted_action_id(args->act_id), - args->actor_unit_id, - args->target_city_id, - args->target_building_id, ""); - } else { - /* This is the targeted version. */ - request_do_action(args->act_id, - args->actor_unit_id, - args->target_city_id, - args->target_building_id, ""); - } - } - } - - gtk_widget_destroy(spy_sabotage_shell); - spy_sabotage_shell = NULL; - free(args); - - /* The user have answered the follow up question. Move on. */ - diplomat_queue_handle_secondary(); -} - -/**********************************************************************//** - User has selected new building from spy's sabotage dialog -**************************************************************************/ -static void spy_improvements_callback(GtkTreeSelection *select, gpointer data) -{ - struct action_data *args = (struct action_data *)data; - - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(select, &model, &it)) { - gtk_tree_model_get(model, &it, 1, &(args->target_building_id), -1); - - gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell), - GTK_RESPONSE_ACCEPT, TRUE); - } else { - args->target_building_id = -2; - - gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell), - GTK_RESPONSE_ACCEPT, FALSE); - } -} - -/**********************************************************************//** - Creates spy's building sabotaging dialog -**************************************************************************/ -static void create_improvements_list(struct player *pplayer, - struct city *pcity, - struct action_data *args) -{ - GtkWidget *sw, *label, *vbox, *view; - GtkListStore *store; - GtkCellRenderer *rend; - GtkTreeViewColumn *col; - GtkTreeIter it; - - struct unit *actor_unit = game_unit_by_number(args->actor_unit_id); - - spy_sabotage_shell = gtk_dialog_new_with_buttons(_("Sabotage Improvements"), - NULL, - 0, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - _("_Sabotage"), - GTK_RESPONSE_ACCEPT, - NULL); - setup_dialog(spy_sabotage_shell, toplevel); - gtk_window_set_position(GTK_WINDOW(spy_sabotage_shell), GTK_WIN_POS_MOUSE); - - gtk_dialog_set_default_response(GTK_DIALOG(spy_sabotage_shell), - GTK_RESPONSE_ACCEPT); - - label = gtk_frame_new(_("Select Improvement to Sabotage")); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(spy_sabotage_shell))), label); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 6); - gtk_container_add(GTK_CONTAINER(label), vbox); - - store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(store); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, rend, - "text", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", view, - "label", _("_Improvements:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_container_add(GTK_CONTAINER(vbox), label); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_container_add(GTK_CONTAINER(sw), view); - - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); - gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 200); - - gtk_container_add(GTK_CONTAINER(vbox), sw); - - /* Now populate the list */ - if (action_prob_possible(actor_unit->client.act_prob_cache[ - get_production_targeted_action_id( - args->act_id)])) { - gtk_list_store_append(store, &it); - gtk_list_store_set(store, &it, 0, _("City Production"), 1, -1, -1); - } - - city_built_iterate(pcity, pimprove) { - if (pimprove->sabotage > 0) { - gtk_list_store_append(store, &it); - gtk_list_store_set(store, &it, - 0, city_improvement_name_translation(pcity, pimprove), - 1, improvement_number(pimprove), - -1); - } - } city_built_iterate_end; - - if (action_prob_possible(actor_unit->client.act_prob_cache[ - get_non_targeted_action_id(args->act_id)])) { - struct astring str = ASTRING_INIT; - - gtk_list_store_append(store, &it); - - /* TRANS: %s is a unit name, e.g., Spy */ - astr_set(&str, _("At %s's Discretion"), - unit_name_translation(actor_unit)); - gtk_list_store_set(store, &it, 0, astr_str(&str), 1, B_LAST, -1); - - astr_free(&str); - } - - gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell), - GTK_RESPONSE_ACCEPT, FALSE); - - gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(spy_sabotage_shell))); - - g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), "changed", - G_CALLBACK(spy_improvements_callback), args); - g_signal_connect(spy_sabotage_shell, "response", - G_CALLBACK(spy_improvements_response), args); - - args->target_building_id = -2; - - gtk_tree_view_focus(GTK_TREE_VIEW(view)); -} - -/**********************************************************************//** - Popup tech stealing dialog with list of possible techs -**************************************************************************/ -static void spy_steal_popup_shared(GtkWidget *w, gpointer data) -{ - struct action_data *args = (struct action_data *)data; - - args->act_id = args->act_id; - - struct city *pvcity = game_city_by_number(args->target_city_id); - struct player *pvictim = NULL; - - if (pvcity) { - pvictim = city_owner(pvcity); - } - -/* it is concievable that pvcity will not be found, because something -has happened to the city during latency. Therefore we must initialize -pvictim to NULL and account for !pvictim in create_advances_list. -- Syela */ - - /* FIXME: Don't discard the second tech choice dialog. */ - if (!spy_tech_shell) { - create_advances_list(client.conn.playing, pvictim, args); - gtk_window_present(GTK_WINDOW(spy_tech_shell)); - } else { - free(args); - } - - /* Wait for the player's reply before moving on to the next unit that - * needs to know what action to take. */ - is_more_user_input_needed = TRUE; - - gtk_widget_destroy(act_sel_dialog); -} - -/**********************************************************************//** - Popup tech stealing dialog with list of possible techs for - "Targeted Steal Tech" -**************************************************************************/ -static void spy_steal_popup(GtkWidget *w, gpointer data) -{ - act_sel_dialog_data->act_id = ACTION_SPY_TARGETED_STEAL_TECH; - spy_steal_popup_shared(w, act_sel_dialog_data); -} - -/**********************************************************************//** - Popup tech stealing dialog with list of possible techs for - "Targeted Steal Tech Escape Expected" -**************************************************************************/ -static void spy_steal_esc_popup(GtkWidget *w, gpointer data) -{ - act_sel_dialog_data->act_id = ACTION_SPY_TARGETED_STEAL_TECH_ESC; - spy_steal_popup_shared(w, act_sel_dialog_data); -} - -/**********************************************************************//** - Pops-up the Spy sabotage dialog, upon return of list of - available improvements requested by the above function. -**************************************************************************/ -void popup_sabotage_dialog(struct unit *actor, struct city *pcity, - const struct action *paction) -{ - /* FIXME: Don't discard the second target choice dialog. */ - if (!spy_sabotage_shell) { - create_improvements_list(client.conn.playing, pcity, - act_data(paction->id, - actor->id, pcity->id, 0, 0, - 0, 0, 0)); - gtk_window_present(GTK_WINDOW(spy_sabotage_shell)); - } -} - -/**********************************************************************//** - User has responded to incite dialog -**************************************************************************/ -static void incite_response(GtkWidget *w, gint response, gpointer data) -{ - struct action_data *args = (struct action_data *)data; - - if (response == GTK_RESPONSE_YES) { - request_do_action(args->act_id, args->actor_unit_id, - args->target_city_id, 0, ""); - } - - gtk_widget_destroy(w); - free(args); - - /* The user have answered the follow up question. Move on. */ - diplomat_queue_handle_secondary(); -} - -/**********************************************************************//** - Popup the yes/no dialog for inciting, since we know the cost now -**************************************************************************/ -void popup_incite_dialog(struct unit *actor, struct city *pcity, int cost, - const struct action *paction) -{ - GtkWidget *shell; - char buf[1024]; - - fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.", - "Treasury contains %d gold.", - client_player()->economic.gold), - client_player()->economic.gold); - - if (INCITE_IMPOSSIBLE_COST == cost) { - shell = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, - _("You can't incite a revolt in %s."), - city_name_get(pcity)); - gtk_window_set_title(GTK_WINDOW(shell), _("City can't be incited!")); - setup_dialog(shell, toplevel); - } else if (cost <= client_player()->economic.gold) { - shell = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */ - PL_("Incite a revolt for %d gold?\n%s", - "Incite a revolt for %d gold?\n%s", cost), cost, buf); - gtk_window_set_title(GTK_WINDOW(shell), _("Incite a Revolt!")); - setup_dialog(shell, toplevel); - } else { - shell = gtk_message_dialog_new(NULL, - 0, - GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, - /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */ - PL_("Inciting a revolt costs %d gold.\n%s", - "Inciting a revolt costs %d gold.\n%s", cost), cost, buf); - gtk_window_set_title(GTK_WINDOW(shell), _("Traitors Demand Too Much!")); - setup_dialog(shell, toplevel); - } - gtk_window_present(GTK_WINDOW(shell)); - - g_signal_connect(shell, "response", G_CALLBACK(incite_response), - act_data(paction->id, actor->id, - pcity->id, 0, 0, - 0, 0, 0)); -} - -/**********************************************************************//** - Callback from the unit target selection dialog. -**************************************************************************/ -static void tgt_unit_change_callback(GtkWidget *dlg, gint arg) -{ - int au_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg), "actor")); - - if (arg == GTK_RESPONSE_YES) { - struct unit *actor = game_unit_by_number(au_id); - - if (actor != NULL) { - int tgt_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg), - "target")); - struct unit *tgt_unit = game_unit_by_number(tgt_id); - struct tile *tgt_tile = g_object_get_data(G_OBJECT(dlg), "tile"); - - if (tgt_unit == NULL) { - /* Make the action dialog pop up again. */ - dsend_packet_unit_get_actions(&client.conn, - actor->id, - /* Let the server choose the target - * unit. */ - IDENTITY_NUMBER_ZERO, - tgt_tile->index, - action_selection_target_extra(), - TRUE); - } else { - dsend_packet_unit_get_actions(&client.conn, - actor->id, - tgt_id, - tgt_tile->index, - action_selection_target_extra(), - TRUE); - } - } - } else { - /* Dialog canceled. This ends the action selection process. */ - action_selection_no_longer_in_progress(au_id); - } - - gtk_widget_destroy(dlg); -} - -/**********************************************************************//** - Callback from action selection dialog for "Change unit target". -**************************************************************************/ -static void act_sel_new_unit_tgt_callback(GtkWidget *w, gpointer data) -{ - struct action_data *args = act_sel_dialog_data; - - struct unit *punit; - struct unit *tunit; - struct tile *ptile; - - if ((punit = game_unit_by_number(args->actor_unit_id)) - && (ptile = index_to_tile(&(wld.map), args->target_tile_id)) - && (tunit = game_unit_by_number(args->target_unit_id))) { - select_tgt_unit(punit, ptile, ptile->units, tunit, - _("Target unit selection"), - _("Looking for target unit:"), - _("Units at tile:"), - _("Select"), - G_CALLBACK(tgt_unit_change_callback)); - } - - did_not_decide = TRUE; - action_selection_restart = TRUE; - gtk_widget_destroy(act_sel_dialog); - free(args); -} - -/**********************************************************************//** - Callback from the extra target selection dialog. -**************************************************************************/ -static void tgt_extra_change_callback(GtkWidget *dlg, gint arg) -{ - int au_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg), "actor")); - - if (arg == GTK_RESPONSE_YES) { - struct unit *actor = game_unit_by_number(au_id); - - if (actor != NULL) { - int tgt_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg), - "target")); - struct extra_type *tgt_extra = extra_by_number(tgt_id); - struct tile *tgt_tile = g_object_get_data(G_OBJECT(dlg), "tile"); - - if (tgt_extra == NULL) { - /* Make the action dialog pop up again. */ - dsend_packet_unit_get_actions(&client.conn, - actor->id, - action_selection_target_unit(), - tgt_tile->index, - /* Let the server choose the target - * extra. */ - action_selection_target_extra(), - TRUE); - } else { - dsend_packet_unit_get_actions(&client.conn, - actor->id, - action_selection_target_unit(), - tgt_tile->index, - tgt_id, - TRUE); - } - } - } else { - /* Dialog canceled. This ends the action selection process. */ - action_selection_no_longer_in_progress(au_id); - } - - gtk_widget_destroy(dlg); -} - -/**********************************************************************//** - Callback from action selection dialog for "Change extra target". -**************************************************************************/ -static void act_sel_new_extra_tgt_callback(GtkWidget *w, gpointer data) -{ - struct action_data *args = act_sel_dialog_data; - - struct unit *act_unit; - struct extra_type *tgt_extra; - struct tile *tgt_tile; - - if ((act_unit = game_unit_by_number(args->actor_unit_id)) - && (tgt_tile = index_to_tile(&(wld.map), args->target_tile_id)) - && (tgt_extra = extra_by_number(args->target_extra_id))) { - bv_extras potential_targets; - - /* Start with the extras at the tile */ - potential_targets = *tile_extras(tgt_tile); - - extra_type_re_active_iterate(pextra) { - if (BV_ISSET(potential_targets, extra_number(pextra))) { - /* This extra is at the tile. Can anything be done to it? */ - if (!utype_can_remove_extra(unit_type_get(act_unit), - pextra)) { - BV_CLR(potential_targets, extra_number(pextra)); - } - } else { - /* This extra isn't at the tile yet. Can it be created? */ - if (utype_can_create_extra(unit_type_get(act_unit), - pextra)) { - BV_SET(potential_targets, extra_number(pextra)); - } - } - } extra_type_re_active_iterate_end; - - select_tgt_extra(act_unit, tgt_tile, potential_targets, tgt_extra, - /* TRANS: GTK action selection dialog extra target - * selection dialog title. */ - _("Target extra selection"), - /* TRANS: GTK action selection dialog extra target - * selection dialog actor unit label. */ - _("Looking for target extra:"), - /* TRANS: GTK action selection dialog extra target - * selection dialog extra list label. */ - _("Extra targets:"), - _("Select"), - G_CALLBACK(tgt_extra_change_callback)); - } - - did_not_decide = TRUE; - action_selection_restart = TRUE; - gtk_widget_destroy(act_sel_dialog); - free(args); -} - -/**********************************************************************//** - Callback from action selection dialog for "Show Location". -**************************************************************************/ -static void act_sel_location_callback(GtkWidget *w, gpointer data) -{ - struct action_data *args = act_sel_dialog_data; - - struct unit *punit; - - if ((punit = game_unit_by_number(args->actor_unit_id))) { - center_tile_mapcanvas(unit_tile(punit)); - } -} - -/**********************************************************************//** - Callback from action selection dialog for "keep moving". - (This should only occur when entering a tile that has an allied city or - an allied unit.) -**************************************************************************/ -static void act_sel_keep_moving_callback(GtkWidget *w, gpointer data) -{ - struct action_data *args = act_sel_dialog_data; - - struct unit *punit; - struct tile *ptile; - - if ((punit = game_unit_by_number(args->actor_unit_id)) - && (ptile = index_to_tile(&(wld.map), args->target_tile_id)) - && !same_pos(unit_tile(punit), ptile)) { - request_unit_non_action_move(punit, ptile); - } - - gtk_widget_destroy(act_sel_dialog); - free(args); -} - -/**********************************************************************//** - Delay selection of what action to take. -**************************************************************************/ -static void act_sel_wait_callback(GtkWidget *w, gpointer data) -{ - struct action_data *args = act_sel_dialog_data; - - key_unit_wait(); - - /* the dialog was destroyed when key_unit_wait() resulted in - * action_selection_close() being called. */ - - free(args); -} - -/**********************************************************************//** - Action selection dialog has been destroyed -**************************************************************************/ -static void act_sel_destroy_callback(GtkWidget *w, gpointer data) -{ - act_sel_dialog = NULL; - diplomat_queue_handle_primary(); -} - -/**********************************************************************//** - Action selection dialog has been canceled -**************************************************************************/ -static void act_sel_cancel_callback(GtkWidget *w, gpointer data) -{ - gtk_widget_destroy(act_sel_dialog); - free(act_sel_dialog_data); -} - -/**********************************************************************//** - Action selection dialog has been closed -**************************************************************************/ -static void act_sel_close_callback(GtkWidget *w, - gint response_id, - gpointer data) -{ - gtk_widget_destroy(act_sel_dialog); - free(act_sel_dialog_data); -} - -/* Mapping from an action to the function to call when its button is - * pushed. */ -static const GCallback af_map[ACTION_COUNT] = { - /* Unit acting against a city target. */ - [ACTION_SPY_TARGETED_SABOTAGE_CITY] = - (GCallback)request_action_details_callback, - [ACTION_SPY_TARGETED_SABOTAGE_CITY_ESC] = - (GCallback)request_action_details_callback, - [ACTION_SPY_TARGETED_STEAL_TECH] = (GCallback)spy_steal_popup, - [ACTION_SPY_TARGETED_STEAL_TECH_ESC] = (GCallback)spy_steal_esc_popup, - [ACTION_SPY_INCITE_CITY] = (GCallback)request_action_details_callback, - [ACTION_SPY_INCITE_CITY_ESC] = (GCallback)request_action_details_callback, - [ACTION_UPGRADE_UNIT] = (GCallback)upgrade_callback, - [ACTION_STRIKE_BUILDING] = (GCallback)request_action_details_callback, - - /* Unit acting against a unit target. */ - [ACTION_SPY_BRIBE_UNIT] = (GCallback)request_action_details_callback, - - /* Unit acting against all units at a tile. */ - /* No special callback functions needed for any unit stack targeted - * actions. */ - - /* Unit acting against a tile. */ - [ACTION_FOUND_CITY] = (GCallback)found_city_callback, - - /* Unit acting with no target except itself. */ - /* No special callback functions needed for any self targeted actions. */ -}; - -/**********************************************************************//** - Show the user the action if it is enabled. -**************************************************************************/ -static void action_entry(GtkWidget *shl, - action_id act_id, - const struct act_prob *act_probs, - const char *custom, - action_id act_num) -{ - const gchar *label; - const gchar *tooltip; - GCallback cb; - - if (af_map[act_id] == NULL) { - /* No special call back function needed for this action. */ - cb = (GCallback)simple_action_callback; - } else { - /* Special action specific callback function specified. */ - cb = af_map[act_id]; - } - - /* Don't show disabled actions. */ - if (!action_prob_possible(act_probs[act_id])) { - return; - } - - label = action_prepare_ui_name(act_id, "_", - act_probs[act_id], - custom); - - tooltip = act_sel_action_tool_tip(action_by_number(act_id), - act_probs[act_id]); - - action_button_map[act_id] = choice_dialog_get_number_of_buttons(shl); - choice_dialog_add(shl, label, cb, GINT_TO_POINTER(act_num), - FALSE, tooltip); -} - -/**********************************************************************//** - Update an existing button. -**************************************************************************/ -static void action_entry_update(GtkWidget *shl, - action_id act_id, - const struct act_prob *act_probs, - const char *custom, - action_id act_num) -{ - const gchar *label; - const gchar *tooltip; - - /* An action that just became impossible has its button disabled. - * An action that became possible again must be reenabled. */ - choice_dialog_button_set_sensitive(act_sel_dialog, - action_button_map[act_id], - action_prob_possible(act_probs[act_id])); - - /* The probability may have changed. */ - label = action_prepare_ui_name(act_id, "_", - act_probs[act_id], custom); - - tooltip = act_sel_action_tool_tip(action_by_number(act_id), - act_probs[act_id]); - - choice_dialog_button_set_label(act_sel_dialog, - action_button_map[act_id], - label); - choice_dialog_button_set_tooltip(act_sel_dialog, - action_button_map[act_id], - tooltip); -} - -/**********************************************************************//** - Popup a dialog that allows the player to select what action a unit - should take. -**************************************************************************/ -void popup_action_selection(struct unit *actor_unit, - struct city *target_city, - struct unit *target_unit, - struct tile *target_tile, - struct extra_type *target_extra, - const struct act_prob *act_probs) -{ - GtkWidget *shl; - struct astring title = ASTRING_INIT, text = ASTRING_INIT; - struct city *actor_homecity; - - int button_id; - - act_sel_dialog_data = - act_data(ACTION_ANY, /* Not decided yet */ - actor_unit->id, - (target_city) ? target_city->id : IDENTITY_NUMBER_ZERO, - (target_unit) ? target_unit->id : IDENTITY_NUMBER_ZERO, - (target_tile) ? target_tile->index : TILE_INDEX_NONE, - /* No target_building or target_tech supplied. (Dec 2019) */ - B_LAST, A_UNSET, - target_extra ? target_extra->id : EXTRA_NONE); - - /* Could be caused by the server failing to reply to a request for more - * information or a bug in the client code. */ - fc_assert_msg(!is_more_user_input_needed, - "Diplomat queue problem. Is another diplomat window open?"); - - /* No extra input is required as no action has been chosen yet. */ - is_more_user_input_needed = FALSE; - - /* No buttons are added yet. */ - for (button_id = 0; button_id < BUTTON_COUNT; button_id++) { - action_button_map[button_id] = BUTTON_NOT_THERE; - } - - actor_homecity = game_city_by_number(actor_unit->homecity); - - actor_unit_id = actor_unit->id; - target_ids[ATK_SELF] = actor_unit_id; - target_ids[ATK_CITY] = target_city ? - target_city->id : - IDENTITY_NUMBER_ZERO; - target_ids[ATK_UNIT] = target_unit ? - target_unit->id : - IDENTITY_NUMBER_ZERO; - target_ids[ATK_UNITS] = target_tile ? - tile_index(target_tile) : - TILE_INDEX_NONE; - target_ids[ATK_TILE] = target_tile ? - tile_index(target_tile) : - TILE_INDEX_NONE; - target_ids[ATK_EXTRAS] = target_tile ? - tile_index(target_tile) : - TILE_INDEX_NONE; - target_extra_id = target_extra ? - extra_number(target_extra) : - EXTRA_NONE; - - astr_set(&title, - /* TRANS: %s is a unit name, e.g., Spy */ - _("Choose Your %s's Strategy"), - unit_name_translation(actor_unit)); - - if (target_city && actor_homecity) { - astr_set(&text, - _("Your %s from %s reaches the city of %s.\nWhat now?"), - unit_name_translation(actor_unit), - city_name_get(actor_homecity), - city_name_get(target_city)); - } else if (target_city) { - astr_set(&text, - _("Your %s has arrived at %s.\nWhat is your command?"), - unit_name_translation(actor_unit), - city_name_get(target_city)); - } else if (target_unit) { - astr_set(&text, - /* TRANS: Your Spy is ready to act against Roman Freight. */ - _("Your %s is ready to act against %s %s."), - unit_name_translation(actor_unit), - nation_adjective_for_player(unit_owner(target_unit)), - unit_name_translation(target_unit)); - } else { - fc_assert_msg(target_unit || target_city || target_tile, - "No target specified."); - astr_set(&text, - /* TRANS: %s is a unit name, e.g., Diplomat, Spy */ - _("Your %s is waiting for your command."), - unit_name_translation(actor_unit)); - } - - shl = choice_dialog_start(GTK_WINDOW(toplevel), astr_str(&title), - astr_str(&text)); - - /* Unit acting against a city */ - - action_iterate(act) { - if (action_id_get_actor_kind(act) == AAK_UNIT - && action_id_get_target_kind(act) == ATK_CITY) { - action_entry(shl, act, act_probs, - get_act_sel_action_custom_text(action_by_number(act), - act_probs[act], - actor_unit, - target_city), - act); - } - } action_iterate_end; - - /* Unit acting against another unit */ - - action_iterate(act) { - if (action_id_get_actor_kind(act) == AAK_UNIT - && action_id_get_target_kind(act) == ATK_UNIT) { - action_entry(shl, act, act_probs, - get_act_sel_action_custom_text(action_by_number(act), - act_probs[act], - actor_unit, - target_city), - act); - } - } action_iterate_end; - - /* Unit acting against all units at a tile */ - - action_iterate(act) { - if (action_id_get_actor_kind(act) == AAK_UNIT - && action_id_get_target_kind(act) == ATK_UNITS) { - action_entry(shl, act, act_probs, - get_act_sel_action_custom_text(action_by_number(act), - act_probs[act], - actor_unit, - target_city), - act); - } - } action_iterate_end; - - /* Unit acting against a tile */ - - action_iterate(act) { - if (action_id_get_actor_kind(act) == AAK_UNIT - && action_id_get_target_kind(act) == ATK_TILE) { - action_entry(shl, act, act_probs, - get_act_sel_action_custom_text(action_by_number(act), - act_probs[act], - actor_unit, - target_city), - act); - } - } action_iterate_end; - - /* Unit acting against a tile's extras */ - - action_iterate(act) { - if (action_id_get_actor_kind(act) == AAK_UNIT - && action_id_get_target_kind(act) == ATK_EXTRAS) { - action_entry(shl, act, act_probs, - get_act_sel_action_custom_text(action_by_number(act), - act_probs[act], - actor_unit, - target_city), - act); - } - } action_iterate_end; - - /* Unit acting against itself. */ - - action_iterate(act) { - if (action_id_get_actor_kind(act) == AAK_UNIT - && action_id_get_target_kind(act) == ATK_SELF) { - action_entry(shl, act, act_probs, - get_act_sel_action_custom_text(action_by_number(act), - act_probs[act], - actor_unit, - target_city), - act); - } - } action_iterate_end; - - if (unit_can_move_to_tile(&(wld.map), actor_unit, target_tile, - FALSE, FALSE)) { - action_button_map[BUTTON_MOVE] = - choice_dialog_get_number_of_buttons(shl); - choice_dialog_add(shl, _("_Keep moving"), - (GCallback)act_sel_keep_moving_callback, - GINT_TO_POINTER(ACTION_NONE), FALSE, NULL); - } - - if (target_unit != NULL - && unit_list_size(target_tile->units) > 1) { - action_button_map[BUTTON_NEW_UNIT_TGT] = - choice_dialog_get_number_of_buttons(shl); - choice_dialog_add(shl, _("Change unit target"), - (GCallback)act_sel_new_unit_tgt_callback, - GINT_TO_POINTER(ACTION_NONE), TRUE, NULL); - } - - if (target_extra != NULL) { - action_button_map[BUTTON_NEW_EXTRA_TGT] = - choice_dialog_get_number_of_buttons(shl); - choice_dialog_add(shl, _("Change extra target"), - (GCallback)act_sel_new_extra_tgt_callback, - GINT_TO_POINTER(ACTION_NONE), TRUE, NULL); - } - - action_button_map[BUTTON_LOCATION] = - choice_dialog_get_number_of_buttons(shl); - choice_dialog_add(shl, _("Show Location"), - (GCallback)act_sel_location_callback, - GINT_TO_POINTER(ACTION_NONE), - TRUE, NULL); - - action_button_map[BUTTON_WAIT] = - choice_dialog_get_number_of_buttons(shl); - choice_dialog_add(shl, _("_Wait"), - (GCallback)act_sel_wait_callback, - GINT_TO_POINTER(ACTION_NONE), - TRUE, NULL); - - action_button_map[BUTTON_CANCEL] = - choice_dialog_get_number_of_buttons(shl); - choice_dialog_add(shl, GTK_STOCK_CANCEL, - (GCallback)act_sel_cancel_callback, - GINT_TO_POINTER(ACTION_NONE), - FALSE, NULL); - - choice_dialog_end(shl); - - act_sel_dialog = shl; - - choice_dialog_set_hide(shl, TRUE); - g_signal_connect(shl, "destroy", - G_CALLBACK(act_sel_destroy_callback), NULL); - g_signal_connect(shl, "delete_event", - G_CALLBACK(act_sel_close_callback), - GINT_TO_POINTER(ACTION_NONE)); - - /* Give follow up questions access to action probabilities. */ - client_unit_init_act_prob_cache(actor_unit); - action_iterate(act) { - actor_unit->client.act_prob_cache[act] = act_probs[act]; - } action_iterate_end; - - astr_free(&title); - astr_free(&text); -} - -/**********************************************************************//** - Returns the id of the actor unit currently handled in action selection - dialog when the action selection dialog is open. - Returns IDENTITY_NUMBER_ZERO if no action selection dialog is open. -**************************************************************************/ -int action_selection_actor_unit(void) -{ - if (act_sel_dialog == NULL) { - return IDENTITY_NUMBER_ZERO; - } - return actor_unit_id; -} - -/**********************************************************************//** - Returns id of the target city of the actions currently handled in action - selection dialog when the action selection dialog is open and it has a - city target. Returns IDENTITY_NUMBER_ZERO if no action selection dialog - is open or no city target is present in the action selection dialog. -**************************************************************************/ -int action_selection_target_city(void) -{ - if (act_sel_dialog == NULL) { - return IDENTITY_NUMBER_ZERO; - } - return target_ids[ATK_CITY]; -} - -/**********************************************************************//** - Returns id of the target unit of the actions currently handled in action - selection dialog when the action selection dialog is open and it has a - unit target. Returns IDENTITY_NUMBER_ZERO if no action selection dialog - is open or no unit target is present in the action selection dialog. -**************************************************************************/ -int action_selection_target_unit(void) -{ - if (act_sel_dialog == NULL) { - return IDENTITY_NUMBER_ZERO; - } - - return target_ids[ATK_UNIT]; -} - -/**********************************************************************//** - Returns id of the target tile of the actions currently handled in action - selection dialog when the action selection dialog is open and it has a - tile target. Returns TILE_INDEX_NONE if no action selection dialog is - open. -**************************************************************************/ -int action_selection_target_tile(void) -{ - if (act_sel_dialog == NULL) { - return TILE_INDEX_NONE; - } - - return target_ids[ATK_TILE]; -} - -/**********************************************************************//** - Returns id of the target extra of the actions currently handled in action - selection dialog when the action selection dialog is open and it has an - extra target. Returns EXTRA_NONE if no action selection dialog is open - or no extra target is present in the action selection dialog. -**************************************************************************/ -int action_selection_target_extra(void) -{ - if (act_sel_dialog == NULL) { - return EXTRA_NONE; - } - - return target_extra_id; -} - -/**********************************************************************//** - Updates the action selection dialog with new information. -**************************************************************************/ -void action_selection_refresh(struct unit *actor_unit, - struct city *target_city, - struct unit *target_unit, - struct tile *target_tile, - struct extra_type *target_extra, - const struct act_prob *act_probs) -{ - if (act_sel_dialog == NULL) { - fc_assert_msg(act_sel_dialog != NULL, - "The action selection dialog should have been open"); - return; - } - - if (actor_unit->id != action_selection_actor_unit()) { - fc_assert_msg(actor_unit->id == action_selection_actor_unit(), - "The action selection dialog is for another actor unit."); - return; - } - - /* A new target may have appeared. */ - if (target_city) { - act_sel_dialog_data->target_city_id = target_city->id; - } - if (target_unit) { - act_sel_dialog_data->target_unit_id = target_unit->id; - } - if (target_tile) { - act_sel_dialog_data->target_tile_id = target_tile->index; - } - /* No target_building or target_tech supplied. (Dec 2019) */ - if (target_extra) { - act_sel_dialog_data->target_extra_id = target_extra->id; - } - - action_iterate(act) { - const char *custom; - - if (action_id_get_actor_kind(act) != AAK_UNIT) { - /* Not relevant. */ - continue; - } - - custom = get_act_sel_action_custom_text(action_by_number(act), - act_probs[act], - actor_unit, - target_city); - - if (BUTTON_NOT_THERE == action_button_map[act]) { - /* Add the button (unless its probability is 0). */ - action_entry(act_sel_dialog, act, act_probs, custom, act); - } else { - /* Update the existing button. */ - action_entry_update(act_sel_dialog, act, act_probs, custom, act); - } - } action_iterate_end; - - /* DO NOT change the action_button_map[] for any button to reflect its - * new position. A button keeps its choice dialog internal name when its - * position changes. A button's id number is therefore based on when - * it was added, not on its current position. */ - - if (BUTTON_NOT_THERE != action_button_map[BUTTON_WAIT]) { - /* Move the wait button below the recently added button. */ - choice_dialog_button_move_to_the_end(act_sel_dialog, - action_button_map[BUTTON_WAIT]); - } - - if (BUTTON_NOT_THERE != action_button_map[BUTTON_CANCEL]) { - /* Move the cancel button below the recently added button. */ - choice_dialog_button_move_to_the_end(act_sel_dialog, - action_button_map[BUTTON_CANCEL]); - } - - choice_dialog_end(act_sel_dialog); -} - -/**********************************************************************//** - Closes the action selection dialog -**************************************************************************/ -void action_selection_close(void) -{ - if (act_sel_dialog != NULL) { - did_not_decide = TRUE; - gtk_widget_destroy(act_sel_dialog); - } -} diff --git a/client/gui-gtk-3.0/canvas.c b/client/gui-gtk-3.0/canvas.c deleted file mode 100644 index 164482aa5d..0000000000 --- a/client/gui-gtk-3.0/canvas.c +++ /dev/null @@ -1,402 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -/* gui-gtk-3.0 */ -#include "colors.h" -#include "gui_main.h" -#include "mapview.h" - -#include "canvas.h" - -/************************************************************************//** - Create a canvas of the given size. -****************************************************************************/ -struct canvas *canvas_create(int width, int height) -{ - struct canvas *result = fc_malloc(sizeof(*result)); - - result->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - width, height); - result->drawable = NULL; - result->zoom = 1.0; - - return result; -} - -/************************************************************************//** - Free any resources associated with this canvas and the canvas struct - itself. -****************************************************************************/ -void canvas_free(struct canvas *store) -{ - cairo_surface_destroy(store->surface); - free(store); -} - -/************************************************************************//** - Set canvas zoom for future drawing operations. -****************************************************************************/ -void canvas_set_zoom(struct canvas *store, float zoom) -{ - store->zoom = zoom; -} - -/************************************************************************//** - This gui has zoom support. -****************************************************************************/ -bool has_zoom_support(void) -{ - return TRUE; -} - -/************************************************************************//** - Copies an area from the source canvas to the destination canvas. -****************************************************************************/ -void canvas_copy(struct canvas *dest, struct canvas *src, - int src_x, int src_y, int dest_x, int dest_y, - int width, int height) -{ - cairo_t *cr; - - if (!dest->drawable) { - cr = cairo_create(dest->surface); - } else { - cr = dest->drawable; - } - - if (dest->drawable) { - cairo_save(cr); - } - - cairo_scale(cr, dest->zoom / src->zoom, dest->zoom / src->zoom); - cairo_set_source_surface(cr, src->surface, dest_x - src_x, dest_y - src_y); - cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); - cairo_rectangle(cr, dest_x, dest_y, width, height); - cairo_fill(cr); - - if (!dest->drawable) { - cairo_destroy(cr); - } else { - cairo_restore(cr); - } -} - -/************************************************************************//** - Draw some or all of a sprite onto the mapview or citydialog canvas. - Supplied coordinates are prior to any canvas zoom. -****************************************************************************/ -void canvas_put_sprite(struct canvas *pcanvas, - int canvas_x, int canvas_y, - struct sprite *sprite, - int offset_x, int offset_y, int width, int height) -{ - int sswidth, ssheight; - cairo_t *cr; - - get_sprite_dimensions(sprite, &sswidth, &ssheight); - - if (!pcanvas->drawable) { - cr = cairo_create(pcanvas->surface); - } else { - cr = pcanvas->drawable; - } - - if (pcanvas->drawable) { - cairo_save(cr); - } - - cairo_scale(cr, pcanvas->zoom, pcanvas->zoom); - cairo_set_source_surface(cr, sprite->surface, canvas_x - offset_x, canvas_y - offset_y); - cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); - cairo_rectangle(cr, canvas_x - offset_x, canvas_y - offset_y, - MIN(width, MAX(0, sswidth - offset_x)), - MIN(height, MAX(0, ssheight - offset_y))); - cairo_fill(cr); - - if (!pcanvas->drawable) { - cairo_destroy(cr); - } else { - cairo_restore(cr); - } -} - -/************************************************************************//** - Draw a full sprite onto the mapview or citydialog canvas. - Supplied canvas_x/y are prior to any canvas zoom. -****************************************************************************/ -void canvas_put_sprite_full(struct canvas *pcanvas, - int canvas_x, int canvas_y, - struct sprite *sprite) -{ - int width, height; - - get_sprite_dimensions(sprite, &width, &height); - canvas_put_sprite(pcanvas, canvas_x, canvas_y, sprite, - 0, 0, width, height); -} - -/************************************************************************//** - Draw a full sprite onto the canvas. If "fog" is specified draw it with - fog. -****************************************************************************/ -void canvas_put_sprite_fogged(struct canvas *pcanvas, - int canvas_x, int canvas_y, - struct sprite *psprite, - bool fog, int fog_x, int fog_y) -{ - pixmap_put_overlay_tile_draw(pcanvas, canvas_x, canvas_y, - psprite, fog); -} - -/************************************************************************//** - Draw a filled-in colored rectangle onto the mapview or citydialog canvas. - Supplied coordinates are prior to any canvas zoom. -****************************************************************************/ -void canvas_put_rectangle(struct canvas *pcanvas, - struct color *pcolor, - int canvas_x, int canvas_y, int width, int height) -{ - cairo_t *cr; - - if (!pcanvas->drawable) { - cr = cairo_create(pcanvas->surface); - } else { - cr = pcanvas->drawable; - } - - if (pcanvas->drawable) { - cairo_save(cr); - } - - cairo_scale(cr, pcanvas->zoom, pcanvas->zoom); - gdk_cairo_set_source_rgba(cr, &pcolor->color); - cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); - cairo_rectangle(cr, canvas_x, canvas_y, width, height); - cairo_fill(cr); - - if (!pcanvas->drawable) { - cairo_destroy(cr); - } else { - cairo_restore(cr); - } -} - -/************************************************************************//** - Fill the area covered by the sprite with the given color. -****************************************************************************/ -void canvas_fill_sprite_area(struct canvas *pcanvas, - struct sprite *psprite, - struct color *pcolor, - int canvas_x, int canvas_y) -{ - int width, height; - get_sprite_dimensions(psprite, &width, &height); - canvas_put_rectangle(pcanvas, pcolor, canvas_x, canvas_y, width, height); -} - -/************************************************************************//** - Draw a colored line onto the mapview or citydialog canvas. - XXX: unlike other canvas_put functions, supplied x/y are *not* prior to - any canvas zoom. -****************************************************************************/ -void canvas_put_line(struct canvas *pcanvas, - struct color *pcolor, - enum line_type ltype, int start_x, int start_y, - int dx, int dy) -{ - cairo_t *cr; - double dashes[2] = {4.0, 4.0}; - - if (!pcanvas->drawable) { - cr = cairo_create(pcanvas->surface); - } else { - cr = pcanvas->drawable; - } - - if (pcanvas->drawable) { - cairo_save(cr); - } - - switch (ltype) { - case LINE_NORMAL: - cairo_set_line_width(cr, 1.); - break; - case LINE_BORDER: - cairo_set_line_width(cr, (double)BORDER_WIDTH); - cairo_set_dash(cr, dashes, 2, 0); - break; - case LINE_TILE_FRAME: - cairo_set_line_width(cr, 2.); - break; - case LINE_GOTO: - cairo_set_line_width(cr, 2.); - break; - } - - gdk_cairo_set_source_rgba(cr, &pcolor->color); - cairo_move_to(cr, start_x, start_y); - cairo_line_to(cr, start_x + dx, start_y + dy); - cairo_stroke(cr); - - if (!pcanvas->drawable) { - cairo_destroy(cr); - } else { - cairo_restore(cr); - } -} - -/************************************************************************//** - Draw a colored curved line for the Technology Tree connectors - A curved line is: 1 horizontal line, 2 arcs, 1 horizontal line -****************************************************************************/ -void canvas_put_curved_line(struct canvas *pcanvas, - struct color *pcolor, - enum line_type ltype, int start_x, int start_y, - int dx, int dy) -{ - int end_x = start_x + dx; - int end_y = start_y + dy; - cairo_t *cr; - double dashes[2] = {4.0, 4.0}; - - if (!pcanvas->drawable) { - cr = cairo_create(pcanvas->surface); - } else { - cr = pcanvas->drawable; - } - - if (pcanvas->drawable) { - cairo_save(cr); - } - - switch (ltype) { - case LINE_NORMAL: - cairo_set_line_width(cr, 1.); - break; - case LINE_BORDER: - cairo_set_dash(cr, dashes, 2, 0); - cairo_set_line_width(cr, (double)BORDER_WIDTH); - break; - case LINE_TILE_FRAME: - cairo_set_line_width(cr, 2.); - break; - case LINE_GOTO: - cairo_set_line_width(cr, 2.); - break; - } - - gdk_cairo_set_source_rgba(cr, &pcolor->color); - cairo_move_to(cr, start_x, start_y); - cairo_curve_to(cr, end_x, start_y, start_x, end_y, end_x, end_y); - cairo_stroke(cr); - - if (!pcanvas->drawable) { - cairo_destroy(cr); - } else { - cairo_restore(cr); - } -} - -static PangoLayout *layout; -static struct { - PangoFontDescription **styles; - bool shadowed; -} fonts[FONT_COUNT] = { - {&city_names_style, TRUE}, - {&city_productions_style, TRUE}, - {&reqtree_text_style, FALSE} -}; -#define FONT(font) (*fonts[font].styles) - -/************************************************************************//** - Return the size of the given text in the given font. This size should - include the ascent and descent of the text. Either of width or height - may be NULL in which case those values simply shouldn't be filled out. -****************************************************************************/ -void get_text_size(int *width, int *height, - enum client_font font, const char *text) -{ - PangoRectangle rect; - - if (!layout) { - layout = pango_layout_new(gdk_pango_context_get_for_screen(gdk_screen_get_default())); - } - - pango_layout_set_font_description(layout, FONT(font)); - pango_layout_set_text(layout, text, -1); - - pango_layout_get_pixel_extents(layout, NULL, &rect); - if (width) { - *width = rect.width; - } - if (height) { - *height = rect.height; - } -} - -/************************************************************************//** - Draw the text onto the canvas in the given color and font. The canvas - position does not account for the ascent of the text; this function must - take care of this manually. The text will not be NULL but may be empty. - Supplied canvas_x/y are prior to any cavas zoom. -****************************************************************************/ -void canvas_put_text(struct canvas *pcanvas, int canvas_x, int canvas_y, - enum client_font font, - struct color *pcolor, - const char *text) -{ - cairo_t *cr; - - if (!layout) { - layout = pango_layout_new(gdk_pango_context_get_for_screen(gdk_screen_get_default())); - } - - if (!pcanvas->drawable) { - cr = cairo_create(pcanvas->surface); - } else { - cr = pcanvas->drawable; - } - - if (pcanvas->drawable) { - cairo_save(cr); - } - - pango_layout_set_font_description(layout, FONT(font)); - pango_layout_set_text(layout, text, -1); - - if (fonts[font].shadowed) { - /* Suppress drop shadow for black text */ - const GdkRGBA black = { 0.0, 0.0, 0.0, 1.0 }; - - if (!gdk_rgba_equal(&pcolor->color, &black)) { - gdk_cairo_set_source_rgba(cr, &black); - cairo_move_to(cr, canvas_x * pcanvas->zoom + 1, - canvas_y * pcanvas->zoom + 1); - pango_cairo_show_layout (cr, layout); - } - } - - cairo_move_to(cr, canvas_x * pcanvas->zoom, canvas_y * pcanvas->zoom); - gdk_cairo_set_source_rgba(cr, &pcolor->color); - pango_cairo_show_layout(cr, layout); - - if (!pcanvas->drawable) { - cairo_destroy(cr); - } else { - cairo_restore(cr); - } -} diff --git a/client/gui-gtk-3.0/canvas.h b/client/gui-gtk-3.0/canvas.h deleted file mode 100644 index d10fa883bc..0000000000 --- a/client/gui-gtk-3.0/canvas.h +++ /dev/null @@ -1,29 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__CANVAS_H -#define FC__CANVAS_H - -#include - -#include "canvas_g.h" - -struct canvas -{ - cairo_surface_t *surface; - cairo_t *drawable; - float zoom; -}; - -#define FC_STATIC_CANVAS_INIT { NULL, NULL, 1.0 } - -#endif /* FC__CANVAS_H */ diff --git a/client/gui-gtk-3.0/chatline.c b/client/gui-gtk-3.0/chatline.c deleted file mode 100644 index 4886b3a4ea..0000000000 --- a/client/gui-gtk-3.0/chatline.c +++ /dev/null @@ -1,1477 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include - -/* utility */ -#include "fcintl.h" -#include "genlist.h" -#include "log.h" -#include "mem.h" -#include "support.h" - -/* common */ -#include "chat.h" -#include "featured_text.h" -#include "game.h" -#include "packets.h" - -/* client */ -#include "client_main.h" -#include "climap.h" -#include "control.h" -#include "mapview_common.h" - -/* client/gui-gtk-3.0 */ -#include "colors.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "pages.h" - -#include "chatline.h" - -#define MAX_CHATLINE_HISTORY 20 - -static struct genlist *history_list = NULL; -static int history_pos = -1; - -static struct inputline_toolkit { - GtkWidget *main_widget; - GtkWidget *entry; - GtkWidget *button_box; - GtkWidget *toolbar; - GtkWidget *toggle_button; - bool toolbar_displayed; -} toolkit; /* Singleton. */ - -static void inputline_make_tag(GtkEntry *entry, enum text_tag_type type); - -/**********************************************************************//** - Returns TRUE iff the input line has focus. -**************************************************************************/ -bool inputline_has_focus(void) -{ - return gtk_widget_has_focus(toolkit.entry); -} - -/**********************************************************************//** - Gives the focus to the intput line. -**************************************************************************/ -void inputline_grab_focus(void) -{ - gtk_widget_grab_focus(toolkit.entry); -} - -/**********************************************************************//** - Returns TRUE iff the input line is currently visible. -**************************************************************************/ -bool inputline_is_visible(void) -{ - return gtk_widget_get_mapped(toolkit.entry); -} - -/**********************************************************************//** - Helper function to determine if a given client input line is intended as - a "plain" public message. Note that messages prefixed with : are a - special case (explicit public messages), and will return FALSE. -**************************************************************************/ -static bool is_plain_public_message(const char *s) -{ - const char *p; - - /* If it is a server command or an explicit ally - * message, then it is not a public message. */ - if (s[0] == SERVER_COMMAND_PREFIX || s[0] == CHAT_ALLIES_PREFIX) { - return FALSE; - } - - /* It might be a private message of the form - * 'player name with spaces':the message - * or with ". So skip past the player name part. */ - if (s[0] == '\'' || s[0] == '"') { - p = strchr(s + 1, s[0]); - } else { - p = s; - } - - /* Now we just need to check that it is not a private - * message. If we encounter a space then the preceeding - * text could not have been a user/player name (the - * quote check above eliminated names with spaces) so - * it must be a public message. Otherwise if we encounter - * the message prefix : then the text parsed up until now - * was a player/user name and the line is intended as - * a private message (or explicit public message if the - * first character is :). */ - while (p != NULL && *p != '\0') { - if (fc_isspace(*p)) { - return TRUE; - } else if (*p == CHAT_DIRECT_PREFIX) { - return FALSE; - } - p++; - } - return TRUE; -} - -/**********************************************************************//** - Called when the return key is pressed. -**************************************************************************/ -static void inputline_return(GtkEntry *w, gpointer data) -{ - const char *theinput; - - theinput = gtk_entry_get_text(w); - - if (*theinput) { - if (client_state() == C_S_RUNNING - && GUI_GTK_OPTION(allied_chat_only) - && is_plain_public_message(theinput)) { - char buf[MAX_LEN_MSG]; - - fc_snprintf(buf, sizeof(buf), ". %s", theinput); - send_chat(buf); - } else { - send_chat(theinput); - } - - if (genlist_size(history_list) >= MAX_CHATLINE_HISTORY) { - void *history_data; - - history_data = genlist_get(history_list, -1); - genlist_remove(history_list, history_data); - free(history_data); - } - - genlist_prepend(history_list, fc_strdup(theinput)); - history_pos=-1; - } - - gtk_entry_set_text(w, ""); -} - -/**********************************************************************//** - Returns the name of player or user, set in the same list. -**************************************************************************/ -static const char *get_player_or_user_name(int id) -{ - size_t size = conn_list_size(game.all_connections); - - if (id < size) { - return conn_list_get(game.all_connections, id)->username; - } else { - struct player *pplayer = player_by_number(id - size); - if (pplayer) { - return pplayer->name; - } else { - /* Empty slot. Relies on being used with comparison function - * which can cope with NULL. */ - return NULL; - } - } -} - -/**********************************************************************//** - Find a player or a user by prefix. - - prefix - The prefix. - matches - A string array to set the matches result. - max_matches - The maximum of matches. - match_len - The length of the string used to returns matches. - - Returns the number of the matches names. -**************************************************************************/ -static int check_player_or_user_name(const char *prefix, - const char **matches, - const int max_matches) -{ - int matches_id[max_matches * 2], ind, num; - - switch (match_prefix_full(get_player_or_user_name, - player_slot_count() - + conn_list_size(game.all_connections), - MAX_LEN_NAME, fc_strncasecmp, strlen, - prefix, &ind, matches_id, - max_matches * 2, &num)) { - case M_PRE_EXACT: - case M_PRE_ONLY: - matches[0] = get_player_or_user_name(ind); - return 1; - case M_PRE_AMBIGUOUS: - { - /* Remove duplications playername/username. */ - const char *name; - int i, j, c = 0; - - for (i = 0; i < num && c < max_matches; i++) { - name = get_player_or_user_name(matches_id[i]); - for (j = 0; j < c; j++) { - if (0 == fc_strncasecmp(name, matches[j], MAX_LEN_NAME)) { - break; - } - } - if (j >= c) { - matches[c++] = name; - } - } - return c; - } - case M_PRE_EMPTY: - case M_PRE_LONG: - case M_PRE_FAIL: - case M_PRE_LAST: - break; - } - - return 0; -} - -/**********************************************************************//** - Find the larger common prefix. - - prefixes - A list of prefixes. - num_prefixes - The number of prefixes. - buf - The buffer to set. - buf_len - The maximal size of the buffer. - - Returns the length of the common prefix (in characters). -**************************************************************************/ -static size_t get_common_prefix(const char *const *prefixes, - size_t num_prefixes, - char *buf, size_t buf_len) -{ - const char *p; - char *q; - size_t i; - - fc_strlcpy(buf, prefixes[0], buf_len); - for (i = 1; i < num_prefixes; i++) { - for (p = prefixes[i], q = buf; *p != '\0' && *q != '\0'; - p = g_utf8_next_char(p), q = g_utf8_next_char(q)) { - if (g_unichar_toupper(g_utf8_get_char(p)) - != g_unichar_toupper(g_utf8_get_char(q))) { - *q = '\0'; - break; - } - } - } - - return g_utf8_strlen(buf, -1); -} - -/**********************************************************************//** - Autocompletes the input line with a player or user name. - Returns FALSE if there is no string to complete. -**************************************************************************/ -static bool chatline_autocomplete(GtkEditable *editable) -{ -#define MAX_MATCHES 10 - const char *name[MAX_MATCHES]; - char buf[MAX_LEN_NAME * MAX_MATCHES]; - gint pos; - gchar *chars, *p, *prev; - int num, i; - size_t prefix_len; - - /* Part 1: get the string to complete. */ - pos = gtk_editable_get_position(editable); - chars = gtk_editable_get_chars(editable, 0, pos); - - p = chars + strlen(chars); - while ((prev = g_utf8_find_prev_char(chars, p))) { - if (!g_unichar_isalnum(g_utf8_get_char(prev))) { - break; - } - p = prev; - } - /* p points to the start of the last word, or the start of the string. */ - - prefix_len = g_utf8_strlen(p, -1); - if (0 == prefix_len) { - /* Empty: nothing to complete, propagate the event. */ - g_free(chars); - return FALSE; - } - - /* Part 2: compare with player and user names. */ - num = check_player_or_user_name(p, name, MAX_MATCHES); - if (1 == num) { - gtk_editable_delete_text(editable, pos - prefix_len, pos); - pos -= prefix_len; - gtk_editable_insert_text(editable, name[0], strlen(name[0]), &pos); - gtk_editable_set_position(editable, pos); - g_free(chars); - return TRUE; - } else if (num > 1) { - if (get_common_prefix(name, num, buf, sizeof(buf)) > prefix_len) { - gtk_editable_delete_text(editable, pos - prefix_len, pos); - pos -= prefix_len; - gtk_editable_insert_text(editable, buf, strlen(buf), &pos); - gtk_editable_set_position(editable, pos); - } - sz_strlcpy(buf, name[0]); - for (i = 1; i < num; i++) { - cat_snprintf(buf, sizeof(buf), ", %s", name[i]); - } - /* TRANS: comma-separated list of player/user names for completion */ - output_window_printf(ftc_client, _("Suggestions: %s."), buf); - } - - g_free(chars); - return TRUE; -} - -/**********************************************************************//** - Called when a key is pressed. -**************************************************************************/ -static gboolean inputline_handler(GtkWidget *w, GdkEventKey *ev) -{ - if ((ev->state & GDK_CONTROL_MASK)) { - /* Chatline featured text support. */ - switch (ev->keyval) { - case GDK_KEY_b: - inputline_make_tag(GTK_ENTRY(w), TTT_BOLD); - return TRUE; - - case GDK_KEY_c: - inputline_make_tag(GTK_ENTRY(w), TTT_COLOR); - return TRUE; - - case GDK_KEY_i: - inputline_make_tag(GTK_ENTRY(w), TTT_ITALIC); - return TRUE; - - case GDK_KEY_s: - inputline_make_tag(GTK_ENTRY(w), TTT_STRIKE); - return TRUE; - - case GDK_KEY_u: - inputline_make_tag(GTK_ENTRY(w), TTT_UNDERLINE); - return TRUE; - - default: - break; - } - - } else { - /* Chatline history controls. */ - switch (ev->keyval) { - case GDK_KEY_Up: - if (history_pos < genlist_size(history_list) - 1) { - gtk_entry_set_text(GTK_ENTRY(w), - genlist_get(history_list, ++history_pos)); - gtk_editable_set_position(GTK_EDITABLE(w), -1); - } - return TRUE; - - case GDK_KEY_Down: - if (history_pos >= 0) { - history_pos--; - } - - if (history_pos >= 0) { - gtk_entry_set_text(GTK_ENTRY(w), - genlist_get(history_list, history_pos)); - } else { - gtk_entry_set_text(GTK_ENTRY(w), ""); - } - gtk_editable_set_position(GTK_EDITABLE(w), -1); - return TRUE; - - case GDK_KEY_Tab: - if (GUI_GTK_OPTION(chatline_autocompletion)) { - return chatline_autocomplete(GTK_EDITABLE(w)); - } - - default: - break; - } - } - - return FALSE; -} - -/**********************************************************************//** - Make a text tag for the selected text. -**************************************************************************/ -void inputline_make_tag(GtkEntry *entry, enum text_tag_type type) -{ - char buf[MAX_LEN_MSG]; - GtkEditable *editable = GTK_EDITABLE(entry); - gint start_pos, end_pos; - gchar *selection; - gchar *fg_color_text = NULL, *bg_color_text = NULL; - - if (!gtk_editable_get_selection_bounds(editable, &start_pos, &end_pos)) { - /* Let's say the selection starts and ends at the current position. */ - start_pos = end_pos = gtk_editable_get_position(editable); - } - - selection = gtk_editable_get_chars(editable, start_pos, end_pos); - - if (type == TTT_COLOR) { - /* Get the color arguments. */ - GdkRGBA *fg_color = g_object_get_data(G_OBJECT(entry), "fg_color"); - GdkRGBA *bg_color = g_object_get_data(G_OBJECT(entry), "bg_color"); - - if (!fg_color && !bg_color) { - goto CLEAN_UP; - } - - if (fg_color) { - fg_color_text = gdk_rgba_to_string(fg_color); - } - if (bg_color) { - bg_color_text = gdk_rgba_to_string(bg_color); - } - - if (0 == featured_text_apply_tag(selection, buf, sizeof(buf), - TTT_COLOR, 0, FT_OFFSET_UNSET, - ft_color_construct(fg_color_text, - bg_color_text))) { - goto CLEAN_UP; - } - } else if (0 == featured_text_apply_tag(selection, buf, sizeof(buf), - type, 0, FT_OFFSET_UNSET)) { - goto CLEAN_UP; - } - - /* Replace the selection. */ - gtk_editable_delete_text(editable, start_pos, end_pos); - end_pos = start_pos; - gtk_editable_insert_text(editable, buf, -1, &end_pos); - gtk_editable_select_region(editable, start_pos, end_pos); - -CLEAN_UP: - g_free(selection); - g_free(fg_color_text); - g_free(bg_color_text); -} - -/**********************************************************************//** - Make a chat link at the current position or make the current selection - clickable. -**************************************************************************/ -void inputline_make_chat_link(struct tile *ptile, bool unit) -{ - char buf[MAX_LEN_MSG]; - GtkWidget *entry = toolkit.entry; - GtkEditable *editable = GTK_EDITABLE(entry); - gint start_pos, end_pos; - gchar *chars; - struct unit *punit; - - /* Get the target. */ - if (unit) { - punit = find_visible_unit(ptile); - if (!punit) { - output_window_append(ftc_client, _("No visible unit on this tile.")); - return; - } - } else { - punit = NULL; - } - - if (gtk_editable_get_selection_bounds(editable, &start_pos, &end_pos)) { - /* There is a selection, make it clickable. */ - gpointer target; - enum text_link_type type; - - chars = gtk_editable_get_chars(editable, start_pos, end_pos); - if (punit) { - type = TLT_UNIT; - target = punit; - } else if (tile_city(ptile)) { - type = TLT_CITY; - target = tile_city(ptile); - } else { - type = TLT_TILE; - target = ptile; - } - - if (0 != featured_text_apply_tag(chars, buf, sizeof(buf), TTT_LINK, - 0, FT_OFFSET_UNSET, type, target)) { - /* Replace the selection. */ - gtk_editable_delete_text(editable, start_pos, end_pos); - end_pos = start_pos; - gtk_editable_insert_text(editable, buf, -1, &end_pos); - gtk_widget_grab_focus(entry); - gtk_editable_select_region(editable, start_pos, end_pos); - } - } else { - /* Just insert the link at the current position. */ - start_pos = gtk_editable_get_position(editable); - end_pos = start_pos; - chars = gtk_editable_get_chars(editable, MAX(start_pos - 1, 0), - start_pos + 1); - if (punit) { - sz_strlcpy(buf, unit_link(punit)); - } else if (tile_city(ptile)) { - sz_strlcpy(buf, city_link(tile_city(ptile))); - } else { - sz_strlcpy(buf, tile_link(ptile)); - } - - if (start_pos > 0 && strlen(chars) > 0 && chars[0] != ' ') { - /* Maybe insert an extra space. */ - gtk_editable_insert_text(editable, " ", 1, &end_pos); - } - gtk_editable_insert_text(editable, buf, -1, &end_pos); - if (chars[start_pos > 0 ? 1 : 0] != '\0' - && chars[start_pos > 0 ? 1 : 0] != ' ') { - /* Maybe insert an extra space. */ - gtk_editable_insert_text(editable, " ", 1, &end_pos); - } - gtk_widget_grab_focus(entry); - gtk_editable_set_position(editable, end_pos); - } - - g_free(chars); -} - -/**********************************************************************//** - Scroll a textview so that the given mark is visible, but only if the - scroll window containing the textview is very close to the bottom. The - text mark 'scroll_target' should probably be the first character of the - last line in the text buffer. -**************************************************************************/ -void scroll_if_necessary(GtkTextView *textview, GtkTextMark *scroll_target) -{ - GtkWidget *sw; - GtkAdjustment *vadj; - gdouble val, max, upper, page_size; - - fc_assert_ret(textview != NULL); - fc_assert_ret(scroll_target != NULL); - - sw = gtk_widget_get_parent(GTK_WIDGET(textview)); - fc_assert_ret(sw != NULL); - fc_assert_ret(GTK_IS_SCROLLED_WINDOW(sw)); - - vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)); - val = gtk_adjustment_get_value(vadj); - g_object_get(G_OBJECT(vadj), "upper", &upper, - "page-size", &page_size, NULL); - max = upper - page_size; - if (max - val < 10.0) { - gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(textview), scroll_target, - 0.0, TRUE, 1.0, 0.0); - } -} - -/**********************************************************************//** - Click a link. -**************************************************************************/ -static gboolean event_after(GtkWidget *text_view, GdkEventButton *event) -{ - GtkTextIter start, end, iter; - GtkTextBuffer *buffer; - GSList *tags, *tagp; - gint x, y; - struct tile *ptile = NULL; - - if (event->type != GDK_BUTTON_RELEASE || event->button != 1) { - return FALSE; - } - - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)); - - /* We shouldn't follow a link if the user has selected something. */ - gtk_text_buffer_get_selection_bounds(buffer, &start, &end); - if (gtk_text_iter_get_offset(&start) != gtk_text_iter_get_offset(&end)) { - return FALSE; - } - - gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW (text_view), - GTK_TEXT_WINDOW_WIDGET, - event->x, event->y, &x, &y); - - gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(text_view), &iter, x, y); - - if ((tags = gtk_text_iter_get_tags(&iter))) { - for (tagp = tags; tagp; tagp = tagp->next) { - GtkTextTag *tag = tagp->data; - enum text_link_type type = - GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "type")); - - if (type != 0) { - /* This is a link. */ - int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "id")); - ptile = NULL; - - /* Real type is type - 1. - * See comment in apply_text_tag() for g_object_set_data(). */ - type--; - - switch (type) { - case TLT_CITY: - { - struct city *pcity = game_city_by_number(id); - - if (pcity) { - ptile = client_city_tile(pcity); - } else { - output_window_append(ftc_client, _("This city isn't known!")); - } - } - break; - case TLT_TILE: - ptile = index_to_tile(&(wld.map), id); - - if (!ptile) { - output_window_append(ftc_client, - _("This tile doesn't exist in this game!")); - } - break; - case TLT_UNIT: - { - struct unit *punit = game_unit_by_number(id); - - if (punit) { - ptile = unit_tile(punit); - } else { - output_window_append(ftc_client, _("This unit isn't known!")); - } - } - break; - } - - if (ptile) { - center_tile_mapcanvas(ptile); - link_mark_restore(type, id); - gtk_widget_grab_focus(GTK_WIDGET(map_canvas)); - } - } - } - g_slist_free(tags); - } - - return FALSE; -} - -/**********************************************************************//** - Set the "hand" cursor when moving over a link. -**************************************************************************/ -static void set_cursor_if_appropriate(GtkTextView *text_view, gint x, gint y) -{ - static gboolean hovering_over_link = FALSE; - static GdkCursor *hand_cursor = NULL; - static GdkCursor *regular_cursor = NULL; - GSList *tags, *tagp; - GtkTextIter iter; - gboolean hovering = FALSE; - - /* Initialize the cursors. */ - if (!hand_cursor) { - hand_cursor = gdk_cursor_new_for_display( - gtk_widget_get_display(GTK_WIDGET(text_view)), GDK_HAND2); - } - if (!regular_cursor) { - regular_cursor = gdk_cursor_new_for_display( - gtk_widget_get_display(GTK_WIDGET(text_view)), GDK_XTERM); - } - - gtk_text_view_get_iter_at_location(text_view, &iter, x, y); - - tags = gtk_text_iter_get_tags(&iter); - for (tagp = tags; tagp; tagp = tagp->next) { - enum text_link_type type = - GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tagp->data), "type")); - - if (type != 0) { - hovering = TRUE; - break; - } - } - - if (hovering != hovering_over_link) { - hovering_over_link = hovering; - - if (hovering_over_link) { - gdk_window_set_cursor(gtk_text_view_get_window(text_view, - GTK_TEXT_WINDOW_TEXT), - hand_cursor); - } else { - gdk_window_set_cursor(gtk_text_view_get_window(text_view, - GTK_TEXT_WINDOW_TEXT), - regular_cursor); - } - } - - if (tags) { - g_slist_free(tags); - } -} - -/**********************************************************************//** - Maybe are the mouse is moving over a link. -**************************************************************************/ -static gboolean motion_notify_event(GtkWidget *text_view, - GdkEventMotion *event) -{ - gint x, y; - - gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text_view), - GTK_TEXT_WINDOW_WIDGET, - event->x, event->y, &x, &y); - set_cursor_if_appropriate(GTK_TEXT_VIEW(text_view), x, y); - - return FALSE; -} - -/**********************************************************************//** - Set the appropriate callbacks for the message buffer. -**************************************************************************/ -void set_message_buffer_view_link_handlers(GtkWidget *view) -{ - g_signal_connect(view, "event-after", - G_CALLBACK(event_after), NULL); - g_signal_connect(view, "motion-notify-event", - G_CALLBACK(motion_notify_event), NULL); -} - -/**********************************************************************//** - Convert a struct text_tag to a GtkTextTag. -**************************************************************************/ -void apply_text_tag(const struct text_tag *ptag, GtkTextBuffer *buf, - ft_offset_t text_start_offset, const char *text) -{ - static bool initalized = FALSE; - GtkTextIter start, stop; - - if (!initalized) { - gtk_text_buffer_create_tag(buf, "bold", - "weight", PANGO_WEIGHT_BOLD, NULL); - gtk_text_buffer_create_tag(buf, "italic", - "style", PANGO_STYLE_ITALIC, NULL); - gtk_text_buffer_create_tag(buf, "strike", - "strikethrough", TRUE, NULL); - gtk_text_buffer_create_tag(buf, "underline", - "underline", PANGO_UNDERLINE_SINGLE, NULL); - initalized = TRUE; - } - - /* Get the position. */ - /* - * N.B.: text_tag_*_offset() value is in bytes, so we need to convert it - * to utf8 character offset. - */ - gtk_text_buffer_get_iter_at_offset(buf, &start, text_start_offset - + g_utf8_pointer_to_offset(text, - text + text_tag_start_offset(ptag))); - if (text_tag_stop_offset(ptag) == FT_OFFSET_UNSET) { - gtk_text_buffer_get_end_iter(buf, &stop); - } else { - gtk_text_buffer_get_iter_at_offset(buf, &stop, text_start_offset - + g_utf8_pointer_to_offset(text, - text + text_tag_stop_offset(ptag))); - } - - switch (text_tag_type(ptag)) { - case TTT_BOLD: - gtk_text_buffer_apply_tag_by_name(buf, "bold", &start, &stop); - break; - case TTT_ITALIC: - gtk_text_buffer_apply_tag_by_name(buf, "italic", &start, &stop); - break; - case TTT_STRIKE: - gtk_text_buffer_apply_tag_by_name(buf, "strike", &start, &stop); - break; - case TTT_UNDERLINE: - gtk_text_buffer_apply_tag_by_name(buf, "underline", &start, &stop); - break; - case TTT_COLOR: - { - /* We have to make a new tag every time. */ - GtkTextTag *tag = NULL; - const char *foreground = text_tag_color_foreground(ptag); - const char *background = text_tag_color_background(ptag); - - if (foreground && foreground[0]) { - if (background && background[0]) { - tag = gtk_text_buffer_create_tag(buf, NULL, - "foreground", foreground, - "background", background, - NULL); - } else { - tag = gtk_text_buffer_create_tag(buf, NULL, - "foreground", foreground, - NULL); - } - } else if (background && background[0]) { - tag = gtk_text_buffer_create_tag(buf, NULL, - "background", background, - NULL); - } - - if (!tag) { - break; /* No color. */ - } - gtk_text_buffer_apply_tag(buf, tag, &start, &stop); - } - break; - case TTT_LINK: - { - struct color *pcolor = NULL; - GtkTextTag *tag; - - switch (text_tag_link_type(ptag)) { - case TLT_CITY: - pcolor = get_color(tileset, COLOR_MAPVIEW_CITY_LINK); - break; - case TLT_TILE: - pcolor = get_color(tileset, COLOR_MAPVIEW_TILE_LINK); - break; - case TLT_UNIT: - pcolor = get_color(tileset, COLOR_MAPVIEW_UNIT_LINK); - break; - } - - if (!pcolor) { - break; /* Not a valid link type case. */ - } - - tag = gtk_text_buffer_create_tag(buf, NULL, - "foreground-rgba", &pcolor->color, - "underline", PANGO_UNDERLINE_SINGLE, - NULL); - - /* Type 0 is reserved for non-link tags. So, add 1 to the - * type value. */ - g_object_set_data(G_OBJECT(tag), "type", - GINT_TO_POINTER(text_tag_link_type(ptag) + 1)); - g_object_set_data(G_OBJECT(tag), "id", - GINT_TO_POINTER(text_tag_link_id(ptag))); - gtk_text_buffer_apply_tag(buf, tag, &start, &stop); - break; - } - } -} - -/**********************************************************************//** - Appends the string to the chat output window. The string should be - inserted on its own line, although it will have no newline. -**************************************************************************/ -void real_output_window_append(const char *astring, - const struct text_tag_list *tags, - int conn_id) -{ - GtkTextBuffer *buf; - GtkTextIter iter; - GtkTextMark *mark; - ft_offset_t text_start_offset; - - buf = message_buffer; - - if (buf == NULL) { - log_error("Output when no message buffer: %s", astring); - - return; - } - - gtk_text_buffer_get_end_iter(buf, &iter); - gtk_text_buffer_insert(buf, &iter, "\n", -1); - mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE); - - if (GUI_GTK_OPTION(show_chat_message_time)) { - char timebuf[64]; - time_t now; - struct tm *now_tm; - - now = time(NULL); - now_tm = localtime(&now); - strftime(timebuf, sizeof(timebuf), "[%H:%M:%S] ", now_tm); - gtk_text_buffer_insert(buf, &iter, timebuf, -1); - } - - text_start_offset = gtk_text_iter_get_offset(&iter); - gtk_text_buffer_insert(buf, &iter, astring, -1); - text_tag_list_iterate(tags, ptag) { - apply_text_tag(ptag, buf, text_start_offset, astring); - } text_tag_list_iterate_end; - - if (main_message_area) { - scroll_if_necessary(GTK_TEXT_VIEW(main_message_area), mark); - } - if (start_message_area) { - scroll_if_necessary(GTK_TEXT_VIEW(start_message_area), mark); - } - gtk_text_buffer_delete_mark(buf, mark); - - append_network_statusbar(astring, FALSE); -} - -/**********************************************************************//** - I have no idea what module this belongs in -- Syela - I've decided to put output_window routines in chatline.c, because - the are somewhat related and output_window_* is already here. --dwp -**************************************************************************/ -void log_output_window(void) -{ - GtkTextIter start, end; - gchar *txt; - - gtk_text_buffer_get_bounds(message_buffer, &start, &end); - txt = gtk_text_buffer_get_text(message_buffer, &start, &end, TRUE); - - write_chatline_content(txt); - g_free(txt); -} - -/**********************************************************************//** - Clear output window. This does *not* destroy it, or free its resources -**************************************************************************/ -void clear_output_window(void) -{ - set_output_window_text(_("Cleared output window.")); -} - -/**********************************************************************//** - Set given text to output window -**************************************************************************/ -void set_output_window_text(const char *text) -{ - gtk_text_buffer_set_text(message_buffer, text, -1); -} - -/**********************************************************************//** - Returns whether the chatline is scrolled to the bottom. -**************************************************************************/ -bool chatline_is_scrolled_to_bottom(void) -{ - GtkWidget *sw, *w; - GtkAdjustment *vadj; - gdouble val, max, upper, page_size; - - if (get_client_page() == PAGE_GAME) { - w = GTK_WIDGET(main_message_area); - } else { - w = GTK_WIDGET(start_message_area); - } - - if (w == NULL) { - return TRUE; - } - - sw = gtk_widget_get_parent(w); - vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)); - val = gtk_adjustment_get_value(vadj); - g_object_get(G_OBJECT(vadj), "upper", &upper, - "page-size", &page_size, NULL); - max = upper - page_size; - - /* Approximation. */ - return max - val < 0.00000001; -} - -/**********************************************************************//** - Scrolls the pregame and in-game chat windows all the way to the bottom. - - Why do we do it in such a convuluted fasion rather than calling - chatline_scroll_to_bottom directly from toplevel_configure? - Because the widget is not at its final size yet when the configure - event occurs. -**************************************************************************/ -static gboolean chatline_scroll_callback(gpointer data) -{ - chatline_scroll_to_bottom(FALSE); /* Not delayed this time! */ - - *((guint *) data) = 0; - return FALSE; /* Remove this idle function. */ -} - -/**********************************************************************//** - Scrolls the pregame and in-game chat windows all the way to the bottom. - If delayed is TRUE, it will be done in a idle_callback. -**************************************************************************/ -void chatline_scroll_to_bottom(bool delayed) -{ - static guint callback_id = 0; - - if (delayed) { - if (callback_id == 0) { - callback_id = g_idle_add(chatline_scroll_callback, &callback_id); - } - } else if (message_buffer) { - GtkTextIter end; - - gtk_text_buffer_get_end_iter(message_buffer, &end); - - if (main_message_area) { - gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(main_message_area), - &end, 0.0, TRUE, 1.0, 0.0); - } - if (start_message_area) { - gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(start_message_area), - &end, 0.0, TRUE, 1.0, 0.0); - } - } -} - -/**********************************************************************//** - Tool button clicked. -**************************************************************************/ -static void make_tag_callback(GtkToolButton *button, gpointer data) -{ - inputline_make_tag(GTK_ENTRY(data), - GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), - "text_tag_type"))); -} - -/**********************************************************************//** - Set the color for an object. Update the button if not NULL. -**************************************************************************/ -static void color_set(GObject *object, const gchar *color_target, - GdkRGBA *color, GtkToolButton *button) -{ - GdkRGBA *current_color = g_object_get_data(object, color_target); - - if (NULL == color) { - /* Clears the current color. */ - if (NULL != current_color) { - gdk_rgba_free(current_color); - g_object_set_data(object, color_target, NULL); - if (NULL != button) { - gtk_tool_button_set_icon_widget(button, NULL); - } - } - } else { - /* Apply the new color. */ - if (NULL != current_color) { - /* We already have a GdkRGBA pointer. */ - *current_color = *color; - } else { - /* We need to make a GdkRGBA pointer. */ - current_color = gdk_rgba_copy(color); - g_object_set_data(object, color_target, current_color); - } - - if (NULL != button) { - /* Update the button. */ - GdkPixbuf *pixbuf; - GtkWidget *image; - - { - cairo_surface_t *surface = cairo_image_surface_create( - CAIRO_FORMAT_RGB24, 16, 16); - cairo_t *cr = cairo_create(surface); - gdk_cairo_set_source_rgba(cr, current_color); - cairo_paint(cr); - cairo_destroy(cr); - pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, 16, 16); - cairo_surface_destroy(surface); - } - image = gtk_image_new_from_pixbuf(pixbuf); - gtk_tool_button_set_icon_widget(button, image); - gtk_widget_show(image); - g_object_unref(G_OBJECT(pixbuf)); - } - } -} - -/**********************************************************************//** - Color selection dialog response. -**************************************************************************/ -static void color_selected(GtkDialog *dialog, gint res, gpointer data) -{ - GtkToolButton *button = - GTK_TOOL_BUTTON(g_object_get_data(G_OBJECT(dialog), "button")); - const gchar *color_target = - g_object_get_data(G_OBJECT(button), "color_target"); - - if (res == GTK_RESPONSE_REJECT) { - /* Clears the current color. */ - color_set(G_OBJECT(data), color_target, NULL, button); - } else if (res == GTK_RESPONSE_OK) { - /* Apply the new color. */ - GtkColorChooser *chooser = - GTK_COLOR_CHOOSER(g_object_get_data(G_OBJECT(dialog), "chooser")); - GdkRGBA new_color; - - gtk_color_chooser_get_rgba(chooser, &new_color); - color_set(G_OBJECT(data), color_target, &new_color, button); - } - - gtk_widget_destroy(GTK_WIDGET(dialog)); -} - -/**********************************************************************//** - Color selection tool button clicked. -**************************************************************************/ -static void select_color_callback(GtkToolButton *button, gpointer data) -{ - GtkWidget *dialog, *chooser; - /* "fg_color" or "bg_color". */ - const gchar *color_target = g_object_get_data(G_OBJECT(button), - "color_target"); - GdkRGBA *current_color = g_object_get_data(G_OBJECT(data), color_target); - - /* TRANS: "text" or "background". */ - gchar *buf = g_strdup_printf(_("Select the %s color"), - (const char *) g_object_get_data(G_OBJECT(button), - "color_info")); - dialog = gtk_dialog_new_with_buttons(buf, NULL, GTK_DIALOG_MODAL, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_CLEAR, GTK_RESPONSE_REJECT, - GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); - setup_dialog(dialog, toplevel); - g_object_set_data(G_OBJECT(dialog), "button", button); - g_signal_connect(dialog, "response", G_CALLBACK(color_selected), data); - - chooser = gtk_color_chooser_widget_new(); - gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), - chooser, FALSE, FALSE, 0); - g_object_set_data(G_OBJECT(dialog), "chooser", chooser); - if (current_color) { - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(chooser), current_color); - } - - gtk_widget_show_all(dialog); - g_free(buf); -} - -/**********************************************************************//** - Moves the tool kit to the toolkit view. -**************************************************************************/ -static gboolean move_toolkit(GtkWidget *toolkit_view, - gpointer data) -{ - struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data; - GtkWidget *parent = gtk_widget_get_parent(ptoolkit->main_widget); - GtkWidget *button_box = GTK_WIDGET(g_object_get_data(G_OBJECT(toolkit_view), - "button_box")); - GList *list, *iter; - - if (parent) { - if (parent == toolkit_view) { - return FALSE; /* Already owned. */ - } - - /* N.B.: We need to hide/show the toolbar to reset the sensitivity - * of the tool buttons. */ - if (ptoolkit->toolbar_displayed) { - gtk_widget_hide(ptoolkit->toolbar); - } - gtk_widget_reparent(ptoolkit->main_widget, toolkit_view); - if (ptoolkit->toolbar_displayed) { - gtk_widget_show(ptoolkit->toolbar); - } - - if (!gtk_widget_get_parent(button_box)) { - /* Attach to the toolkit button_box. */ - gtk_container_add(GTK_CONTAINER(ptoolkit->button_box), button_box); - } - gtk_widget_show_all(ptoolkit->main_widget); - if (!ptoolkit->toolbar_displayed) { - gtk_widget_hide(ptoolkit->toolbar); - } - - /* Hide all other buttons boxes. */ - list = gtk_container_get_children(GTK_CONTAINER(ptoolkit->button_box)); - for (iter = list; iter != NULL; iter = g_list_next(iter)) { - GtkWidget *widget = GTK_WIDGET(iter->data); - - if (widget != button_box) { - gtk_widget_hide(widget); - } - } - g_list_free(list); - - } else { - /* First time attached to a parent. */ - gtk_container_add(GTK_CONTAINER(toolkit_view), ptoolkit->main_widget); - gtk_container_add(GTK_CONTAINER(ptoolkit->button_box), button_box); - gtk_widget_show_all(ptoolkit->main_widget); - } - - return FALSE; -} - -/**********************************************************************//** - Show/Hide the toolbar. -**************************************************************************/ -static gboolean set_toolbar_visibility(GtkWidget *w, - gpointer data) -{ - struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data; - GtkToggleButton *button = GTK_TOGGLE_BUTTON(toolkit.toggle_button); - - if (ptoolkit->toolbar_displayed) { - if (!gtk_toggle_button_get_active(button)) { - /* button_toggled() will be called and the toolbar shown. */ - gtk_toggle_button_set_active(button, TRUE); - } else { - /* Unsure the widget is visible. */ - gtk_widget_show(ptoolkit->toolbar); - } - } else { - if (gtk_toggle_button_get_active(button)) { - /* button_toggled() will be called and the toolbar hiden. */ - gtk_toggle_button_set_active(button, FALSE); - } else { - /* Unsure the widget is visible. */ - gtk_widget_hide(ptoolkit->toolbar); - } - } - - return FALSE; -} - -/**********************************************************************//** - Show/Hide the toolbar. -**************************************************************************/ -static void button_toggled(GtkToggleButton *button, gpointer data) -{ - struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data; - - if (gtk_toggle_button_get_active(button)) { - gtk_widget_show(ptoolkit->toolbar); - ptoolkit->toolbar_displayed = TRUE; - if (chatline_is_scrolled_to_bottom()) { - /* Make sure to be still at the end. */ - chatline_scroll_to_bottom(TRUE); - } - } else { - gtk_widget_hide(ptoolkit->toolbar); - ptoolkit->toolbar_displayed = FALSE; - } -} - -/**********************************************************************//** - Returns a new inputline toolkit view widget that can contain the - inputline. - - This widget has the following datas: - "button_box": pointer to the GtkHBox where to append buttons. -**************************************************************************/ -GtkWidget *inputline_toolkit_view_new(void) -{ - GtkWidget *toolkit_view, *bbox; - - /* Main widget. */ - toolkit_view = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(toolkit_view), - GTK_ORIENTATION_VERTICAL); - g_signal_connect_after(toolkit_view, "map", - G_CALLBACK(move_toolkit), &toolkit); - - /* Button box. */ - bbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(bbox), 12); - g_object_set_data(G_OBJECT(toolkit_view), "button_box", bbox); - - return toolkit_view; -} - -/**********************************************************************//** - Appends a button to the inputline toolkit view widget. -**************************************************************************/ -void inputline_toolkit_view_append_button(GtkWidget *toolkit_view, - GtkWidget *button) -{ - gtk_container_add(GTK_CONTAINER(g_object_get_data(G_OBJECT(toolkit_view), - "button_box")), button); -} - -/**********************************************************************//** - Initializes the chatline stuff. -**************************************************************************/ -void chatline_init(void) -{ - GtkWidget *vbox, *toolbar, *hbox, *button, *entry, *bbox; - GtkToolItem *item; - GdkRGBA color; - - /* Chatline history. */ - if (!history_list) { - history_list = genlist_new(); - history_pos = -1; - } - - /* Inputline toolkit. */ - memset(&toolkit, 0, sizeof(toolkit)); - - vbox = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 2); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - toolkit.main_widget = vbox; - g_signal_connect_after(vbox, "map", - G_CALLBACK(set_toolbar_visibility), &toolkit); - - entry = gtk_entry_new(); - g_object_set(entry, "margin", 2, NULL); - gtk_widget_set_hexpand(entry, TRUE); - toolkit.entry = entry; - - /* First line: toolbar */ - toolbar = gtk_toolbar_new(); - gtk_container_add(GTK_CONTAINER(vbox), toolbar); - gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_MENU); - gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), FALSE); - gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ); - gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), - GTK_ORIENTATION_HORIZONTAL); - toolkit.toolbar = toolbar; - - /* Bold button. */ - item = gtk_tool_button_new_from_stock(GTK_STOCK_BOLD); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); - g_object_set_data(G_OBJECT(item), "text_tag_type", - GINT_TO_POINTER(TTT_BOLD)); - g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry); - gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Bold (Ctrl-B)")); - - /* Italic button. */ - item = gtk_tool_button_new_from_stock(GTK_STOCK_ITALIC); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); - g_object_set_data(G_OBJECT(item), "text_tag_type", - GINT_TO_POINTER(TTT_ITALIC)); - g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry); - gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Italic (Ctrl-I)")); - - /* Strike button. */ - item = gtk_tool_button_new_from_stock(GTK_STOCK_STRIKETHROUGH); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); - g_object_set_data(G_OBJECT(item), "text_tag_type", - GINT_TO_POINTER(TTT_STRIKE)); - g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry); - gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Strikethrough (Ctrl-S)")); - - /* Underline button. */ - item = gtk_tool_button_new_from_stock(GTK_STOCK_UNDERLINE); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); - g_object_set_data(G_OBJECT(item), "text_tag_type", - GINT_TO_POINTER(TTT_UNDERLINE)); - g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry); - gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Underline (Ctrl-U)")); - - /* Color button. */ - item = gtk_tool_button_new_from_stock(GTK_STOCK_SELECT_COLOR); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); - g_object_set_data(G_OBJECT(item), "text_tag_type", - GINT_TO_POINTER(TTT_COLOR)); - g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry); - gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Color (Ctrl-C)")); - - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), - gtk_separator_tool_item_new(), -1); - - /* Foreground selector. */ - item = gtk_tool_button_new(NULL, ""); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); - g_object_set_data(G_OBJECT(item), "color_target", fc_strdup("fg_color")); - g_object_set_data(G_OBJECT(item), "color_info", - fc_strdup(_("foreground"))); - g_signal_connect(item, "clicked", - G_CALLBACK(select_color_callback), entry); - gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Select the text color")); - if (gdk_rgba_parse(&color, "#000000")) { - /* Set default foreground color. */ - color_set(G_OBJECT(entry), "fg_color", &color, GTK_TOOL_BUTTON(item)); - } else { - log_error("Failed to set the default foreground color."); - } - - /* Background selector. */ - item = gtk_tool_button_new(NULL, ""); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); - g_object_set_data(G_OBJECT(item), "color_target", fc_strdup("bg_color")); - g_object_set_data(G_OBJECT(item), "color_info", - fc_strdup(_("background"))); - g_signal_connect(item, "clicked", - G_CALLBACK(select_color_callback), entry); - gtk_widget_set_tooltip_text(GTK_WIDGET(item), - _("Select the background color")); - if (gdk_rgba_parse(&color, "#ffffff")) { - /* Set default background color. */ - color_set(G_OBJECT(entry), "bg_color", &color, GTK_TOOL_BUTTON(item)); - } else { - log_error("Failed to set the default background color."); - } - - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), - gtk_separator_tool_item_new(), -1); - - /* Return button. */ - item = gtk_tool_button_new_from_stock(GTK_STOCK_OK); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1); - g_signal_connect_swapped(item, "clicked", - G_CALLBACK(inputline_return), entry); - gtk_widget_set_tooltip_text(GTK_WIDGET(item), - /* TRANS: "Return" means the return key. */ - _("Send the chat (Return)")); - - /* Second line */ - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - /* Toggle button. */ - button = gtk_toggle_button_new(); - g_object_set(button, "margin", 2, NULL); - gtk_container_add(GTK_CONTAINER(hbox), button); - gtk_button_set_image(GTK_BUTTON(button), - gtk_image_new_from_stock(GTK_STOCK_EDIT, - GTK_ICON_SIZE_MENU)); - g_signal_connect(button, "toggled", G_CALLBACK(button_toggled), &toolkit); - gtk_widget_set_tooltip_text(GTK_WIDGET(button), _("Chat tools")); - toolkit.toggle_button = button; - - /* Entry. */ - gtk_container_add(GTK_CONTAINER(hbox), entry); - g_signal_connect(entry, "activate", G_CALLBACK(inputline_return), NULL); - g_signal_connect(entry, "key_press_event", - G_CALLBACK(inputline_handler), NULL); - - /* Button box. */ - bbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(hbox), bbox); - toolkit.button_box = bbox; -} - -/**********************************************************************//** - Main thread side callback to print version message -**************************************************************************/ -static gboolean version_message_main_thread(gpointer user_data) -{ - char *vertext = (char *)user_data; - - output_window_append(ftc_client, vertext); - - FC_FREE(vertext); - - return G_SOURCE_REMOVE; -} - -/**********************************************************************//** - Got version message from metaserver thread. -**************************************************************************/ -void version_message(const char *vertext) -{ - int len = strlen(vertext) + 1; - char *persistent = fc_malloc(len); - - strncpy(persistent, vertext, len); - - gdk_threads_add_idle(version_message_main_thread, persistent); -} diff --git a/client/gui-gtk-3.0/chatline.h b/client/gui-gtk-3.0/chatline.h deleted file mode 100644 index 4d43a634dd..0000000000 --- a/client/gui-gtk-3.0/chatline.h +++ /dev/null @@ -1,42 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__CHATLINE_H -#define FC__CHATLINE_H - -#include - -/* include */ -#include "chatline_g.h" - -void chatline_init(void); - -void inputline_make_chat_link(struct tile *ptile, bool unit); -bool inputline_has_focus(void); -void inputline_grab_focus(void); -bool inputline_is_visible(void); - -void set_output_window_text(const char *text); -bool chatline_is_scrolled_to_bottom(void); -void chatline_scroll_to_bottom(bool delayed); - -void set_message_buffer_view_link_handlers(GtkWidget *view); - -GtkWidget *inputline_toolkit_view_new(void); -void inputline_toolkit_view_append_button(GtkWidget *toolkit_view, - GtkWidget *button); - -void apply_text_tag(const struct text_tag *ptag, GtkTextBuffer *buf, - ft_offset_t text_start_offset, const char *text); -void scroll_if_necessary(GtkTextView *textview, GtkTextMark *scroll_target); - -#endif /* FC__CHATLINE_H */ diff --git a/client/gui-gtk-3.0/choice_dialog.c b/client/gui-gtk-3.0/choice_dialog.c deleted file mode 100644 index 672f635717..0000000000 --- a/client/gui-gtk-3.0/choice_dialog.c +++ /dev/null @@ -1,241 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -/* utility */ -#include "support.h" - -/* gui-gtk-3.0 */ -#include "gui_main.h" -#include "gui_stuff.h" - -#include "choice_dialog.h" - -/**************************************************************** - Choice dialog: A dialog with a label and a list of buttons - placed vertically -****************************************************************/ - -/*******************************************************************//** - Get the number of buttons in the choice dialog. -***********************************************************************/ -int choice_dialog_get_number_of_buttons(GtkWidget *cd) -{ - return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cd), "nbuttons")); -} - -/*******************************************************************//** - Get nth button widget from dialog -***********************************************************************/ -static GtkWidget* choice_dialog_get_nth_button(GtkWidget *cd, - int button) -{ - char button_name[512]; - GtkWidget *b; - - fc_snprintf(button_name, sizeof(button_name), "button%d", button); - - b = g_object_get_data(G_OBJECT(cd), button_name); - - return b; -} - -/*******************************************************************//** - Set sensitivity state of choice dialog button. -***********************************************************************/ -void choice_dialog_button_set_sensitive(GtkWidget *cd, int button, - gboolean state) -{ - gtk_widget_set_sensitive(choice_dialog_get_nth_button(cd, button), state); -} - -/*******************************************************************//** - Set label for choice dialog button. -***********************************************************************/ -void choice_dialog_button_set_label(GtkWidget *cd, int number, - const char *label) -{ - GtkWidget* button = choice_dialog_get_nth_button(cd, number); - gtk_button_set_label(GTK_BUTTON(button), label); -} - -/*******************************************************************//** - Set tool tip for choice dialog button. -***********************************************************************/ -void choice_dialog_button_set_tooltip(GtkWidget *cd, int number, - const char *tool_tip) -{ - GtkWidget* button = choice_dialog_get_nth_button(cd, number); - gtk_widget_set_tooltip_text(button, tool_tip); -} - -/*******************************************************************//** - Move the specified button to the end. -***********************************************************************/ -void choice_dialog_button_move_to_the_end(GtkWidget *cd, - const int number) -{ - GtkWidget *button = choice_dialog_get_nth_button(cd, number); - GtkWidget *bbox = g_object_get_data(G_OBJECT(cd), "bbox"); - - gtk_box_reorder_child(GTK_BOX(bbox), button, -1); -} - -/*******************************************************************//** - Create choice dialog -***********************************************************************/ -GtkWidget *choice_dialog_start(GtkWindow *parent, const gchar *name, - const gchar *text) -{ - GtkWidget *dshell, *dlabel, *vbox, *bbox; - - dshell = gtk_window_new(GTK_WINDOW_TOPLEVEL); - setup_dialog(dshell, toplevel); - gtk_window_set_position (GTK_WINDOW(dshell), GTK_WIN_POS_MOUSE); - - gtk_window_set_title(GTK_WINDOW(dshell), name); - - gtk_window_set_transient_for(GTK_WINDOW(dshell), parent); - gtk_window_set_destroy_with_parent(GTK_WINDOW(dshell), TRUE); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 5); - gtk_container_add(GTK_CONTAINER(dshell),vbox); - - gtk_container_set_border_width(GTK_CONTAINER(vbox), 5); - - dlabel = gtk_label_new(text); - gtk_container_add(GTK_CONTAINER(vbox), dlabel); - - bbox = gtk_button_box_new(GTK_ORIENTATION_VERTICAL); - gtk_box_set_spacing(GTK_BOX(bbox), 2); - gtk_container_add(GTK_CONTAINER(vbox), bbox); - - g_object_set_data(G_OBJECT(dshell), "bbox", bbox); - g_object_set_data(G_OBJECT(dshell), "nbuttons", GINT_TO_POINTER(0)); - g_object_set_data(G_OBJECT(dshell), "hide", GINT_TO_POINTER(FALSE)); - - gtk_widget_show(vbox); - gtk_widget_show(dlabel); - - return dshell; -} - -/*******************************************************************//** - Choice dialog has been clicked and primary handling has - taken place already. -***********************************************************************/ -static void choice_dialog_clicked(GtkWidget *w, gpointer data) -{ - if (g_object_get_data(G_OBJECT(data), "hide")) { - gtk_widget_hide(GTK_WIDGET(data)); - } else { - gtk_widget_destroy(GTK_WIDGET(data)); - } -} - -/*******************************************************************//** - Add button to choice dialog. -***********************************************************************/ -void choice_dialog_add(GtkWidget *dshell, const gchar *label, - GCallback handler, gpointer data, - bool meta, const gchar *tool_tip) -{ - GtkWidget *button, *bbox; - char name[512]; - int nbuttons; - - bbox = g_object_get_data(G_OBJECT(dshell), "bbox"); - nbuttons = choice_dialog_get_number_of_buttons(dshell); - g_object_set_data(G_OBJECT(dshell), "nbuttons", GINT_TO_POINTER(nbuttons+1)); - - fc_snprintf(name, sizeof(name), "button%d", nbuttons); - - button = gtk_button_new_from_stock(label); - gtk_container_add(GTK_CONTAINER(bbox), button); - g_object_set_data(G_OBJECT(dshell), name, button); - - if (handler) { - g_signal_connect(button, "clicked", handler, data); - } - - if (!meta) { - /* This button makes the choice. */ - g_signal_connect_after(button, "clicked", - G_CALLBACK(choice_dialog_clicked), dshell); - } - - if (tool_tip != NULL) { - gtk_widget_set_tooltip_text(button, tool_tip); - } -} - -/*******************************************************************//** - Choice dialog construction ready -***********************************************************************/ -void choice_dialog_end(GtkWidget *dshell) -{ - GtkWidget *bbox; - - bbox = g_object_get_data(G_OBJECT(dshell), "bbox"); - - gtk_widget_show_all(bbox); - gtk_widget_show(dshell); -} - -/*******************************************************************//** - Set hide property of choice dialog -***********************************************************************/ -void choice_dialog_set_hide(GtkWidget *dshell, gboolean setting) -{ - g_object_set_data(G_OBJECT(dshell), "hide", GINT_TO_POINTER(setting)); -} - -/*******************************************************************//** - Open new choice dialog. -***********************************************************************/ -GtkWidget *popup_choice_dialog(GtkWindow *parent, const gchar *dialogname, - const gchar *text, ...) -{ - GtkWidget *dshell; - va_list args; - gchar *name; - - dshell = choice_dialog_start(parent, dialogname, text); - - va_start(args, text); - - while ((name = va_arg(args, gchar *))) { - GCallback handler; - gpointer data; - - handler = va_arg(args, GCallback); - data = va_arg(args, gpointer); - - choice_dialog_add(dshell, name, handler, data, FALSE, NULL); - } - - va_end(args); - - choice_dialog_end(dshell); - - return dshell; -} diff --git a/client/gui-gtk-3.0/choice_dialog.h b/client/gui-gtk-3.0/choice_dialog.h deleted file mode 100644 index 0bb4db4801..0000000000 --- a/client/gui-gtk-3.0/choice_dialog.h +++ /dev/null @@ -1,38 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__CHOICE_DIALOG_H -#define FC__CHOICE_DIALOG_H - -#include - -GtkWidget *popup_choice_dialog(GtkWindow *parent, const gchar *dialogname, - const gchar *text, ...); - -void choice_dialog_set_hide(GtkWidget *dshell, gboolean setting); - -GtkWidget *choice_dialog_start(GtkWindow *parent, const gchar *name, - const gchar *text); -void choice_dialog_add(GtkWidget *dshell, const gchar *label, - GCallback handler, gpointer data, - bool meta, const gchar *tool_tip); -void choice_dialog_end(GtkWidget *dshell); -int choice_dialog_get_number_of_buttons(GtkWidget *cd); -void choice_dialog_button_set_sensitive(GtkWidget *shl, int button, - gboolean state); -void choice_dialog_button_set_label(GtkWidget *cd, int button, - const char *label); -void choice_dialog_button_set_tooltip(GtkWidget *cd, int number, - const char *tool_tip); -void choice_dialog_button_move_to_the_end(GtkWidget *cd, - const int number); -#endif /* FC__CHOICE_DIALOG_H */ diff --git a/client/gui-gtk-3.0/citizensinfo.c b/client/gui-gtk-3.0/citizensinfo.c deleted file mode 100644 index ea2e885e2e..0000000000 --- a/client/gui-gtk-3.0/citizensinfo.c +++ /dev/null @@ -1,393 +0,0 @@ -/***************************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -*****************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - - -#include -#include -#include - -#include -#include - -/* common */ -#include "citizens.h" -#include "city.h" -#include "player.h" - -/* client/gui-gtk-3.0 */ -#include "gui_stuff.h" -#include "plrdlg.h" - -#include "citizensinfo.h" - - -static struct citizens_dialog *citizens_dialog_get(const struct city *pcity); -static struct citizens_dialog - *citizens_dialog_create(const struct city *pcity); -static GtkTreeStore *citizens_dialog_store_new(void); -static int citizens_dialog_default_sort_column(void); -static void citizens_dialog_row(GtkTreeStore *store, GtkTreeIter *it, - const struct city *pcity, - const struct player_slot *pslot); - -static const char *col_nation(const struct city *pcity, - const struct player_slot *pslot); -static const char *col_citizens(const struct city *pcity, - const struct player_slot *pslot); - -/***************************************************************************** - The layout of the citizens display. -*****************************************************************************/ -static struct citizens_column { - bool show; - enum player_dlg_column_type type; - const char *title; - const char *(*func)(const struct city *, const struct player_slot *); - /* if type = COL_*TEXT */ - const char *tagname; /* for save_options */ -} citizens_cols[] = { - {TRUE, COL_RIGHT_TEXT, N_("#"), col_citizens, "citizens"}, - {TRUE, COL_FLAG, N_("Flag"), NULL, "flag"}, - {TRUE, COL_TEXT, N_("Nation"), col_nation, "nation"} -}; -static const int num_citizens_cols = ARRAY_SIZE(citizens_cols); -#define CITIZENS_DLG_COL_STYLE (0 + num_citizens_cols) -#define CITIZENS_DLG_COL_WEIGHT (1 + num_citizens_cols) -#define CITIZENS_DLG_COL_ID (2 + num_citizens_cols) - -/***************************************************************************** - The citizens dialog. -*****************************************************************************/ -struct citizens_dialog { - const struct city *pcity; - GtkWidget *shell; - GtkTreeStore *store; - GtkWidget *list; - GtkTreeModel *sort; -}; - -#define SPECLIST_TAG dialog -#define SPECLIST_TYPE struct citizens_dialog -#include "speclist.h" - -#define dialog_list_iterate(dialoglist, pdialog) \ - TYPED_LIST_ITERATE(struct citizens_dialog, dialoglist, pdialog) -#define dialog_list_iterate_end LIST_ITERATE_END - -static struct dialog_list *dialog_list; - -/*************************************************************************//** - The name of the player's nation for the plrdlg. -*****************************************************************************/ -static const char *col_nation(const struct city *pcity, - const struct player_slot *pslot) -{ - return nation_adjective_for_player(player_slot_get_player(pslot)); -} - -/*************************************************************************//** - The number of citizens for the player in the city. -*****************************************************************************/ -static const char *col_citizens(const struct city *pcity, - const struct player_slot *pslot) -{ - citizens nationality = citizens_nation_get(pcity, pslot); - - if (nationality == 0) { - return "-"; - } else { - static char buf[8]; - - fc_snprintf(buf, sizeof(buf), "%d", nationality); - - return buf; - } -} - -/*************************************************************************//** - Create a citizens dialog store. - - FIXME: copy of players_dialog_store_new(); -*****************************************************************************/ -static GtkTreeStore *citizens_dialog_store_new(void) -{ - GtkTreeStore *store; - GType model_types[num_citizens_cols + 3]; - int i; - - for (i = 0; i < num_citizens_cols; i++) { - switch (citizens_cols[i].type) { - case COL_FLAG: - model_types[i] = GDK_TYPE_PIXBUF; - break; - case COL_COLOR: - model_types[i] = GDK_TYPE_PIXBUF; - break; - case COL_BOOLEAN: - model_types[i] = G_TYPE_BOOLEAN; - break; - case COL_TEXT: - case COL_RIGHT_TEXT: - model_types[i] = G_TYPE_STRING; - break; - } - } - /* special (invisible rows) - Text style, weight and player id */ - model_types[i++] = G_TYPE_INT; /* CITIZENS_DLG_COL_STYLE. */ - model_types[i++] = G_TYPE_INT; /* CITIZENS_DLG_COL_WEIGHT. */ - model_types[i++] = G_TYPE_INT; /* CITIZENS_DLG_COL_ID. */ - - store = gtk_tree_store_newv(i, model_types); - - return store; -} - -/*************************************************************************//** - Returns column to sort by by default -*****************************************************************************/ -static int citizens_dialog_default_sort_column(void) { - return 0; -} - -/*************************************************************************//** - Create citizens dialog -*****************************************************************************/ -static struct citizens_dialog - *citizens_dialog_create(const struct city *pcity) -{ - GtkWidget *frame, *sw; - struct citizens_dialog *pdialog = fc_malloc(sizeof(struct citizens_dialog)); - int i; - - pdialog->pcity = pcity; - pdialog->store = citizens_dialog_store_new(); - pdialog->sort - = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(pdialog->store)); - g_object_unref(pdialog->store); - - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(pdialog->sort), - citizens_dialog_default_sort_column(), - GTK_SORT_DESCENDING); - - pdialog->list - = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pdialog->sort)); - gtk_widget_set_halign(pdialog->list, GTK_ALIGN_CENTER); - g_object_unref(pdialog->sort); - - for (i = 0; i < num_citizens_cols; i++) { - struct citizens_column *pcol; - GtkCellRenderer *renderer; - GtkTreeViewColumn *col; - - pcol = &citizens_cols[i]; - col = NULL; - - switch (pcol->type) { - case COL_FLAG: - renderer = gtk_cell_renderer_pixbuf_new(); - col = gtk_tree_view_column_new_with_attributes(_(pcol->title), renderer, - "pixbuf", i, NULL); - break; - case COL_TEXT: - renderer = gtk_cell_renderer_text_new(); - g_object_set(renderer, "style-set", TRUE, "weight-set", TRUE, NULL); - - col = gtk_tree_view_column_new_with_attributes(_(pcol->title), renderer, - "text", i, - "style", CITIZENS_DLG_COL_STYLE, - "weight", CITIZENS_DLG_COL_WEIGHT, - NULL); - gtk_tree_view_column_set_sort_column_id(col, i); - break; - case COL_RIGHT_TEXT: - renderer = gtk_cell_renderer_text_new(); - g_object_set(renderer, "style-set", TRUE, "weight-set", TRUE, NULL); - - col = gtk_tree_view_column_new_with_attributes(_(pcol->title), renderer, - "text", i, - "style", CITIZENS_DLG_COL_STYLE, - "weight", CITIZENS_DLG_COL_WEIGHT, - NULL); - gtk_tree_view_column_set_sort_column_id(col, i); - g_object_set(renderer, "xalign", 1.0, NULL); - gtk_tree_view_column_set_alignment(col, 1.0); - break; - case COL_COLOR: - case COL_BOOLEAN: - /* These are not used. */ - fc_assert(pcol->type != COL_COLOR && pcol->type != COL_BOOLEAN); - continue; - } - - if (col) { - gtk_tree_view_append_column(GTK_TREE_VIEW(pdialog->list), col); - } - } - - gtk_widget_set_hexpand(GTK_WIDGET(pdialog->list), TRUE); - gtk_widget_set_vexpand(GTK_WIDGET(pdialog->list), TRUE); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_NONE); - gtk_container_add(GTK_CONTAINER(sw), pdialog->list); - - frame = gtk_frame_new(_("Citizens")); - gtk_container_add(GTK_CONTAINER(frame), sw); - - pdialog->shell = frame; - - dialog_list_prepend(dialog_list, pdialog); - - citizens_dialog_refresh(pcity); - - return pdialog; -} - -/*************************************************************************//** - Initialize citizens dialog -*****************************************************************************/ -void citizens_dialog_init(void) -{ - dialog_list = dialog_list_new(); -} - -/*************************************************************************//** - Free resources allocated for citizens dialog -*****************************************************************************/ -void citizens_dialog_done(void) -{ - dialog_list_destroy(dialog_list); -} - -/*************************************************************************//** - Get citizen dialog of the given city -*****************************************************************************/ -static struct citizens_dialog *citizens_dialog_get(const struct city *pcity) -{ - dialog_list_iterate(dialog_list, pdialog) { - if (pdialog->pcity == pcity) { - return pdialog; - } - } dialog_list_iterate_end; - - return NULL; -} - -/*************************************************************************//** - Refresh citizen dialog of the given city -*****************************************************************************/ -void citizens_dialog_refresh(const struct city *pcity) -{ - struct citizens_dialog *pdialog = citizens_dialog_get(pcity); - - if (pdialog == NULL) { - return; - } - - gtk_tree_store_clear(pdialog->store); - citizens_iterate(pcity, pslot, nationality) { - GtkTreeIter iter; - - gtk_tree_store_append(pdialog->store, &iter, NULL); - citizens_dialog_row(pdialog->store, &iter, pcity, pslot); - } citizens_iterate_end; -} - -/*************************************************************************//** - Fills the citizens list with the data for 'pslot' at the row given by 'it'. -*****************************************************************************/ -static void citizens_dialog_row(GtkTreeStore *store, GtkTreeIter *it, - const struct city *pcity, - const struct player_slot *pslot) -{ - GdkPixbuf *pixbuf; - int style = PANGO_STYLE_NORMAL, weight = PANGO_WEIGHT_NORMAL; - int k; - - for (k = 0; k < num_citizens_cols; k++) { - struct citizens_column *pcol = &citizens_cols[k]; - switch (pcol->type) { - case COL_TEXT: - case COL_RIGHT_TEXT: - gtk_tree_store_set(store, it, k, pcol->func(pcity, pslot), -1); - break; - case COL_FLAG: - pixbuf = get_flag(nation_of_player(player_slot_get_player(pslot))); - if (pixbuf != NULL) { - gtk_tree_store_set(store, it, k, pixbuf, -1); - g_object_unref(pixbuf); - } - break; - case COL_COLOR: - case COL_BOOLEAN: - /* These are not used. */ - fc_assert(pcol->type != COL_COLOR && pcol->type != COL_BOOLEAN); - continue; - break; - } - } - - if (city_owner(pcity)->slot == pslot) { - weight = PANGO_WEIGHT_BOLD; - style = PANGO_STYLE_NORMAL; - } - - gtk_tree_store_set(store, it, - CITIZENS_DLG_COL_STYLE, style, - CITIZENS_DLG_COL_WEIGHT, weight, - CITIZENS_DLG_COL_ID, player_slot_index(pslot), - -1); -} - -/*************************************************************************//** - Close citizens dialog of one city -*****************************************************************************/ -void citizens_dialog_close(const struct city *pcity) -{ - struct citizens_dialog *pdialog = citizens_dialog_get(pcity); - if (pdialog == NULL) { - return; - } - - gtk_widget_hide(pdialog->shell); - - dialog_list_remove(dialog_list, pdialog); - - gtk_widget_destroy(pdialog->shell); - free(pdialog); -} - -/*************************************************************************//** - Make citizen dialog of the city visible. -*****************************************************************************/ -GtkWidget *citizens_dialog_display(const struct city *pcity) -{ - struct citizens_dialog *pdialog = citizens_dialog_get(pcity); - - if (!pdialog) { - pdialog = citizens_dialog_create(pcity); - } - - gtk_widget_show_all(pdialog->shell); - citizens_dialog_refresh(pcity); - - return pdialog->shell; -} diff --git a/client/gui-gtk-3.0/citizensinfo.h b/client/gui-gtk-3.0/citizensinfo.h deleted file mode 100644 index 7e5eb1eccb..0000000000 --- a/client/gui-gtk-3.0/citizensinfo.h +++ /dev/null @@ -1,27 +0,0 @@ -/***************************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -*****************************************************************************/ -#ifndef FC__CITIZENSINFO_H -#define FC__CITIZENSINFO_H - -#include - -struct city; - -void citizens_dialog_init(void); -void citizens_dialog_done(void); - -GtkWidget *citizens_dialog_display(const struct city *pcity); -void citizens_dialog_refresh(const struct city *pcity); -void citizens_dialog_close(const struct city *pcity); - -#endif /* FC__CITIZENSINFO_H */ diff --git a/client/gui-gtk-3.0/citydlg.c b/client/gui-gtk-3.0/citydlg.c deleted file mode 100644 index a0b24db349..0000000000 --- a/client/gui-gtk-3.0/citydlg.c +++ /dev/null @@ -1,3490 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include - -/* utility */ -#include "bitvector.h" -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "city.h" -#include "game.h" -#include "map.h" -#include "movement.h" -#include "packets.h" -#include "player.h" -#include "unitlist.h" - -/* client */ -#include "chatline_common.h" -#include "client_main.h" -#include "colors.h" -#include "control.h" -#include "climap.h" -#include "options.h" -#include "text.h" -#include "tilespec.h" - -/* client/agents */ -#include "cma_fec.h" - -/* client/gui-gtk-3.0 */ -#include "choice_dialog.h" -#include "citizensinfo.h" -#include "cityrep.h" -#include "cma_fe.h" -#include "dialogs.h" -#include "graphics.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "happiness.h" -#include "helpdlg.h" -#include "inputdlg.h" -#include "mapview.h" -#include "repodlgs.h" -#include "wldlg.h" - -#include "citydlg.h" - -#define CITYMAP_WIDTH MIN(512, canvas_width) -#define CITYMAP_HEIGHT (CITYMAP_WIDTH * canvas_height / canvas_width) -#define CITYMAP_SCALE ((double)CITYMAP_WIDTH / (double)canvas_width) - -#define TINYSCREEN_MAX_HEIGHT (500 - 1) - -/* Only CDLGR_UNITS button currently uses these, others have - * direct callback. */ -enum citydlg_response { CDLGR_UNITS, CDLGR_PREV, CDLGR_NEXT }; - -struct city_dialog; - -/* get 'struct dialog_list' and related function */ -#define SPECLIST_TAG dialog -#define SPECLIST_TYPE struct city_dialog -#include "speclist.h" - -#define dialog_list_iterate(dialoglist, pdialog) \ - TYPED_LIST_ITERATE(struct city_dialog, dialoglist, pdialog) -#define dialog_list_iterate_end LIST_ITERATE_END - -struct unit_node { - GtkWidget *cmd; - GtkWidget *pix; - int height; -}; - -/* get 'struct unit_node' and related function */ -#define SPECVEC_TAG unit_node -#define SPECVEC_TYPE struct unit_node -#include "specvec.h" - -#define unit_node_vector_iterate(list, elt) \ - TYPED_VECTOR_ITERATE(struct unit_node, list, elt) -#define unit_node_vector_iterate_end VECTOR_ITERATE_END - -#define NUM_CITIZENS_SHOWN 30 - -enum { OVERVIEW_PAGE, WORKLIST_PAGE, HAPPINESS_PAGE, CMA_PAGE, - SETTINGS_PAGE, STICKY_PAGE, - NUM_PAGES /* the number of pages in city dialog notebook - * must match enries in misc_whichtab_label[] */ -}; - -enum { - INFO_SIZE, INFO_FOOD, INFO_SHIELD, INFO_TRADE, INFO_GOLD, - INFO_LUXURY, INFO_SCIENCE, INFO_GRANARY, INFO_GROWTH, - INFO_CORRUPTION, INFO_WASTE, INFO_CULTURE, INFO_POLLUTION, - INFO_ILLNESS, INFO_STEAL, INFO_AIRLIFT, - NUM_INFO_FIELDS /* number of fields in city_info - * must match entries in output_label[] */ -}; - -/* minimal size for the city map scrolling windows*/ -#define CITY_MAP_MIN_SIZE_X 200 -#define CITY_MAP_MIN_SIZE_Y 150 - -struct city_map_canvas { - GtkWidget *sw; - GtkWidget *ebox; - GtkWidget *darea; -}; - -struct city_dialog { - struct city *pcity; - - GtkWidget *shell; - GtkWidget *name_label; - cairo_surface_t *map_canvas_store_unscaled; - GtkWidget *notebook; - - GtkWidget *popup_menu; - GtkWidget *citizen_images; - cairo_surface_t *citizen_surface; - - struct { - struct city_map_canvas map_canvas; - - GtkWidget *production_bar; - GtkWidget *production_combo; - GtkWidget *buy_command; - GtkWidget *improvement_list; - - GtkWidget *supported_units_frame; - GtkWidget *supported_unit_table; - - GtkWidget *present_units_frame; - GtkWidget *present_unit_table; - - struct unit_node_vector supported_units; - struct unit_node_vector present_units; - - GtkWidget *info_ebox[NUM_INFO_FIELDS]; - GtkWidget *info_label[NUM_INFO_FIELDS]; - - GtkListStore* change_production_store; - } overview; - - struct { - GtkWidget *production_label; - GtkWidget *production_bar; - GtkWidget *buy_command; - GtkWidget *worklist; - } production; - - struct { - struct city_map_canvas map_canvas; - - GtkWidget *widget; - GtkWidget *info_ebox[NUM_INFO_FIELDS]; - GtkWidget *info_label[NUM_INFO_FIELDS]; - GtkWidget *citizens; - } happiness; - - struct cma_dialog *cma_editor; - - struct { - GtkWidget *rename_command; - GtkWidget *new_citizens_radio[3]; - GtkWidget *disband_on_settler; - GtkWidget *whichtab_radio[NUM_PAGES]; - short block_signal; - } misc; - - GtkWidget *buy_shell, *sell_shell; - GtkTreeSelection *change_selection; - GtkWidget *rename_shell, *rename_input; - - GtkWidget *show_units_command; - GtkWidget *prev_command, *next_command; - - Impr_type_id sell_id; - - int cwidth; -}; - -static struct dialog_list *dialog_list; -static bool city_dialogs_have_been_initialised = FALSE; -static int canvas_width, canvas_height; -static int new_dialog_def_page = OVERVIEW_PAGE; -static int last_page = OVERVIEW_PAGE; - -static bool is_showing_workertask_dialog = FALSE; - -static struct -{ - struct city *owner; - struct tile *loc; -} workertask_req; - -static bool low_citydlg; - -/****************************************/ - -static void initialize_city_dialogs(void); -static void city_dialog_map_create(struct city_dialog *pdialog, - struct city_map_canvas *cmap_canvas); -static void city_dialog_map_recenter(GtkWidget *map_canvas_sw); - -static struct city_dialog *get_city_dialog(struct city *pcity); -static gboolean keyboard_handler(GtkWidget * widget, GdkEventKey * event, - struct city_dialog *pdialog); - -static GtkWidget *create_city_info_table(struct city_dialog *pdialog, - GtkWidget **info_ebox, - GtkWidget **info_label); -static void create_and_append_overview_page(struct city_dialog *pdialog); -static void create_and_append_map_page(struct city_dialog *pdialog); -static void create_and_append_buildings_page(struct city_dialog *pdialog); -static void create_and_append_worklist_page(struct city_dialog *pdialog); -static void create_and_append_happiness_page(struct city_dialog *pdialog); -static void create_and_append_cma_page(struct city_dialog *pdialog); -static void create_and_append_settings_page(struct city_dialog *pdialog); - -static struct city_dialog *create_city_dialog(struct city *pcity); - -static void city_dialog_update_title(struct city_dialog *pdialog); -static void city_dialog_update_citizens(struct city_dialog *pdialog); -static void city_dialog_update_information(GtkWidget **info_ebox, - GtkWidget **info_label, - struct city_dialog *pdialog); -static void city_dialog_update_map(struct city_dialog *pdialog); -static void city_dialog_update_building(struct city_dialog *pdialog); -static void city_dialog_update_improvement_list(struct city_dialog - *pdialog); -static void city_dialog_update_supported_units(struct city_dialog - *pdialog); -static void city_dialog_update_present_units(struct city_dialog *pdialog); -static void city_dialog_update_prev_next(void); - -static void show_units_response(void *data); - -static gboolean supported_unit_callback(GtkWidget * w, GdkEventButton * ev, - gpointer data); -static gboolean present_unit_callback(GtkWidget * w, GdkEventButton * ev, - gpointer data); -static gboolean supported_unit_middle_callback(GtkWidget * w, - GdkEventButton * ev, - gpointer data); -static gboolean present_unit_middle_callback(GtkWidget * w, - GdkEventButton * ev, - gpointer data); - -static void unit_center_callback(GtkWidget * w, gpointer data); -static void unit_activate_callback(GtkWidget * w, gpointer data); -static void supported_unit_activate_close_callback(GtkWidget * w, - gpointer data); -static void present_unit_activate_close_callback(GtkWidget * w, - gpointer data); -static void unit_load_callback(GtkWidget * w, gpointer data); -static void unit_unload_callback(GtkWidget * w, gpointer data); -static void unit_sentry_callback(GtkWidget * w, gpointer data); -static void unit_fortify_callback(GtkWidget * w, gpointer data); -static void unit_disband_callback(GtkWidget * w, gpointer data); -static void unit_homecity_callback(GtkWidget * w, gpointer data); -static void unit_upgrade_callback(GtkWidget * w, gpointer data); - -static gboolean citizens_callback(GtkWidget * w, GdkEventButton * ev, - gpointer data); -static gboolean button_down_citymap(GtkWidget * w, GdkEventButton * ev, - gpointer data); -static void draw_map_canvas(struct city_dialog *pdialog); - -static void buy_callback(GtkWidget * w, gpointer data); -static void change_production_callback(GtkComboBox *combo, - struct city_dialog *pdialog); - -static void sell_callback(struct impr_type *pimprove, gpointer data); -static void sell_callback_response(GtkWidget *w, gint response, gpointer data); - -static void impr_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data); - -static void rename_callback(GtkWidget * w, gpointer data); -static void rename_popup_callback(gpointer data, gint response, - const char *input); -static void set_cityopt_values(struct city_dialog *pdialog); -static void cityopt_callback(GtkWidget * w, gpointer data); -static void misc_whichtab_callback(GtkWidget * w, gpointer data); - -static void city_destroy_callback(GtkWidget *w, gpointer data); -static void close_city_dialog(struct city_dialog *pdialog); -static void citydlg_response_callback(GtkDialog *dlg, gint response, - void *data); -static void close_callback(GtkWidget *w, gpointer data); -static void switch_city_callback(GtkWidget *w, gpointer data); - -/**********************************************************************//** - Called to set the dimensions of the city dialog, both on - startup and if the tileset is changed. -**************************************************************************/ -static void init_citydlg_dimensions(void) -{ - canvas_width = get_citydlg_canvas_width(); - canvas_height = get_citydlg_canvas_height(); -} - -/**********************************************************************//** - Initialize stuff needed for city dialogs -**************************************************************************/ -static void initialize_city_dialogs(void) -{ - int height; - - fc_assert_ret(!city_dialogs_have_been_initialised); - - dialog_list = dialog_list_new(); - init_citydlg_dimensions(); - height = screen_height(); - - /* Use default layout when height cannot be determined - * (when height == 0) */ - if (height > 0 && height <= TINYSCREEN_MAX_HEIGHT) { - low_citydlg = TRUE; - } else { - low_citydlg = FALSE; - } - - city_dialogs_have_been_initialised = TRUE; -} - -/**********************************************************************//** - Called when the tileset changes. -**************************************************************************/ -void reset_city_dialogs(void) -{ - if (!city_dialogs_have_been_initialised) { - return; - } - - init_citydlg_dimensions(); - - dialog_list_iterate(dialog_list, pdialog) { - /* There's no reasonable way to resize a GtkImage, so we don't try. - Instead we just redraw the overview within the existing area. The - player has to close and reopen the dialog to fix this. */ - city_dialog_update_map(pdialog); - } dialog_list_iterate_end; - - popdown_all_city_dialogs(); -} - -/**********************************************************************//** - Return city dialog of the given city, or NULL is it doesn't - already exist -**************************************************************************/ -static struct city_dialog *get_city_dialog(struct city *pcity) -{ - if (!city_dialogs_have_been_initialised) { - initialize_city_dialogs(); - } - - dialog_list_iterate(dialog_list, pdialog) { - if (pdialog->pcity == pcity) - return pdialog; - } - dialog_list_iterate_end; - return NULL; -} - -/**********************************************************************//** - Redraw map canvas on expose. -**************************************************************************/ -static gboolean canvas_exposed_cb(GtkWidget *w, cairo_t *cr, - gpointer data) -{ - struct city_dialog *pdialog = data; - - cairo_scale(cr, CITYMAP_SCALE, CITYMAP_SCALE); - cairo_set_source_surface(cr, pdialog->map_canvas_store_unscaled, 0, 0); - if (!gtk_widget_get_sensitive(pdialog->overview.map_canvas.ebox)) { - cairo_paint_with_alpha(cr, 0.5); - } else { - cairo_paint(cr); - } - - return TRUE; -} - -/**********************************************************************//** - Create a city map widget; used in the overview and in the happiness page. -**************************************************************************/ -static void city_dialog_map_create(struct city_dialog *pdialog, - struct city_map_canvas *cmap_canvas) -{ - GtkWidget *sw, *ebox, *darea; - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw), - CITYMAP_WIDTH); - gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), - CITYMAP_HEIGHT); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_NONE); - - ebox = gtk_event_box_new(); - gtk_widget_set_halign(ebox, GTK_ALIGN_CENTER); - gtk_widget_set_valign(ebox, GTK_ALIGN_CENTER); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); - gtk_container_add(GTK_CONTAINER(sw), ebox); - - darea = gtk_drawing_area_new(); - gtk_widget_add_events(darea, GDK_BUTTON_PRESS_MASK); - gtk_widget_set_size_request(darea, CITYMAP_WIDTH, CITYMAP_HEIGHT); - g_signal_connect(ebox, "button-press-event", - G_CALLBACK(button_down_citymap), pdialog); - g_signal_connect(darea, "draw", - G_CALLBACK(canvas_exposed_cb), pdialog); - gtk_container_add(GTK_CONTAINER(ebox), darea); - - /* save all widgets for the city map */ - cmap_canvas->sw = sw; - cmap_canvas->ebox = ebox; - cmap_canvas->darea = darea; -} - -/**********************************************************************//** - Center city dialog map. -**************************************************************************/ -static void city_dialog_map_recenter(GtkWidget *map_canvas_sw) -{ - GtkAdjustment *adjust = NULL; - gdouble value; - - fc_assert_ret(map_canvas_sw != NULL); - - adjust = gtk_scrolled_window_get_hadjustment( - GTK_SCROLLED_WINDOW(map_canvas_sw)); - value = (gtk_adjustment_get_lower(adjust) - + gtk_adjustment_get_upper(adjust) - - gtk_adjustment_get_page_size(adjust)) / 2; - gtk_adjustment_set_value(adjust, value); - gtk_adjustment_value_changed(adjust); - - adjust = gtk_scrolled_window_get_vadjustment( - GTK_SCROLLED_WINDOW(map_canvas_sw)); - value = (gtk_adjustment_get_lower(adjust) - + gtk_adjustment_get_upper(adjust) - - gtk_adjustment_get_page_size(adjust)) / 2; - gtk_adjustment_set_value(adjust, value); - gtk_adjustment_value_changed(adjust); -} - -/**********************************************************************//** - Refresh city dialog of the given city -**************************************************************************/ -void real_city_dialog_refresh(struct city *pcity) -{ - struct city_dialog *pdialog = get_city_dialog(pcity); - - log_debug("CITYMAP_WIDTH: %d", CITYMAP_WIDTH); - log_debug("CITYMAP_HEIGHT: %d", CITYMAP_HEIGHT); - log_debug("CITYMAP_SCALE: %.3f", CITYMAP_SCALE); - - if (city_owner(pcity) == client.conn.playing) { - city_report_dialog_update_city(pcity); - economy_report_dialog_update(); - } - - if (!pdialog) - return; - - city_dialog_update_title(pdialog); - city_dialog_update_citizens(pdialog); - city_dialog_update_information(pdialog->overview.info_ebox, - pdialog->overview.info_label, pdialog); - city_dialog_update_map(pdialog); - city_dialog_update_building(pdialog); - city_dialog_update_improvement_list(pdialog); - city_dialog_update_supported_units(pdialog); - city_dialog_update_present_units(pdialog); - - if (!client_has_player() || city_owner(pcity) == client_player()) { - bool have_present_units = (unit_list_size(pcity->tile->units) > 0); - - refresh_worklist(pdialog->production.worklist); - - if (!low_citydlg) { - city_dialog_update_information(pdialog->happiness.info_ebox, - pdialog->happiness.info_label, pdialog); - } - refresh_happiness_dialog(pdialog->pcity); - if (game.info.citizen_nationality) { - citizens_dialog_refresh(pdialog->pcity); - } - - if (!client_is_observer()) { - refresh_cma_dialog(pdialog->pcity, REFRESH_ALL); - } - - gtk_widget_set_sensitive(pdialog->show_units_command, - can_client_issue_orders() - && have_present_units); - } else { - /* Set the buttons we do not want live while a Diplomat investigates */ - gtk_widget_set_sensitive(pdialog->show_units_command, FALSE); - } -} - -/**********************************************************************//** - Refresh city dialogs of unit's homecity and city where unit - currently is. -**************************************************************************/ -void refresh_unit_city_dialogs(struct unit *punit) -{ - struct city *pcity_sup, *pcity_pre; - struct city_dialog *pdialog; - - pcity_sup = game_city_by_number(punit->homecity); - pcity_pre = tile_city(unit_tile(punit)); - - if (pcity_sup && (pdialog = get_city_dialog(pcity_sup))) { - city_dialog_update_supported_units(pdialog); - } - - if (pcity_pre && (pdialog = get_city_dialog(pcity_pre))) { - city_dialog_update_present_units(pdialog); - } -} - -/**********************************************************************//** - Popup the dialog 10% inside the main-window -**************************************************************************/ -void real_city_dialog_popup(struct city *pcity) -{ - struct city_dialog *pdialog; - - if (!(pdialog = get_city_dialog(pcity))) { - pdialog = create_city_dialog(pcity); - } - - gtk_window_present(GTK_WINDOW(pdialog->shell)); - - /* center the city map(s); this must be *after* the city dialog was drawn - * else the size information is missing! */ - city_dialog_map_recenter(pdialog->overview.map_canvas.sw); - if (pdialog->happiness.map_canvas.sw) { - city_dialog_map_recenter(pdialog->happiness.map_canvas.sw); - } -} - -/**********************************************************************//** - Return whether city dialog for given city is open -**************************************************************************/ -bool city_dialog_is_open(struct city *pcity) -{ - return get_city_dialog(pcity) != NULL; -} - -/**********************************************************************//** - Popdown the dialog -**************************************************************************/ -void popdown_city_dialog(struct city *pcity) -{ - struct city_dialog *pdialog = get_city_dialog(pcity); - - if (pdialog) { - close_city_dialog(pdialog); - } -} - -/**********************************************************************//** - Popdown all dialogs -**************************************************************************/ -void popdown_all_city_dialogs(void) -{ - if (!city_dialogs_have_been_initialised) { - return; - } - - while (dialog_list_size(dialog_list)) { - close_city_dialog(dialog_list_get(dialog_list, 0)); - } - dialog_list_destroy(dialog_list); - - city_dialogs_have_been_initialised = FALSE; -} - -/**********************************************************************//** - Keyboard handler for city dialog -**************************************************************************/ -static gboolean keyboard_handler(GtkWidget * widget, GdkEventKey * event, - struct city_dialog *pdialog) -{ - if (event->state & GDK_CONTROL_MASK) { - switch (event->keyval) { - case GDK_KEY_Left: - gtk_notebook_prev_page(GTK_NOTEBOOK(pdialog->notebook)); - return TRUE; - - case GDK_KEY_Right: - gtk_notebook_next_page(GTK_NOTEBOOK(pdialog->notebook)); - return TRUE; - - default: - break; - } - } - - return FALSE; -} - -/**********************************************************************//** - Destroy info popup dialog when button released -**************************************************************************/ -static gboolean show_info_button_release(GtkWidget *w, GdkEventButton *ev, - gpointer data) -{ - gtk_grab_remove(w); - gdk_device_ungrab(ev->device, ev->time); - gtk_widget_destroy(w); - return FALSE; -} - - -/**********************************************************************//** - Popup info dialog -**************************************************************************/ -static gboolean show_info_popup(GtkWidget *w, GdkEventButton *ev, - gpointer data) -{ - struct city_dialog *pdialog = g_object_get_data(G_OBJECT(w), "pdialog"); - - if (ev->button == 1) { - GtkWidget *p, *label, *frame; - char buf[1024]; - - switch (GPOINTER_TO_UINT(data)) { - case INFO_FOOD: - get_city_dialog_output_text(pdialog->pcity, O_FOOD, buf, sizeof(buf)); - break; - case INFO_SHIELD: - get_city_dialog_output_text(pdialog->pcity, O_SHIELD, - buf, sizeof(buf)); - break; - case INFO_TRADE: - get_city_dialog_output_text(pdialog->pcity, O_TRADE, buf, sizeof(buf)); - break; - case INFO_GOLD: - get_city_dialog_output_text(pdialog->pcity, O_GOLD, buf, sizeof(buf)); - break; - case INFO_SCIENCE: - get_city_dialog_output_text(pdialog->pcity, O_SCIENCE, - buf, sizeof(buf)); - break; - case INFO_LUXURY: - get_city_dialog_output_text(pdialog->pcity, O_LUXURY, - buf, sizeof(buf)); - break; - case INFO_CULTURE: - get_city_dialog_culture_text(pdialog->pcity, buf, sizeof(buf)); - break; - case INFO_POLLUTION: - get_city_dialog_pollution_text(pdialog->pcity, buf, sizeof(buf)); - break; - case INFO_ILLNESS: - get_city_dialog_illness_text(pdialog->pcity, buf, sizeof(buf)); - break; - case INFO_AIRLIFT: - get_city_dialog_airlift_text(pdialog->pcity, buf, sizeof(buf)); - break; - default: - return TRUE; - } - - p = gtk_window_new(GTK_WINDOW_POPUP); - gtk_widget_set_name(p, "Freeciv"); - gtk_container_set_border_width(GTK_CONTAINER(p), 2); - gtk_window_set_transient_for(GTK_WINDOW(p), GTK_WINDOW(pdialog->shell)); - gtk_window_set_position(GTK_WINDOW(p), GTK_WIN_POS_MOUSE); - - frame = gtk_frame_new(NULL); - gtk_container_add(GTK_CONTAINER(p), frame); - - label = gtk_label_new(buf); - gtk_widget_set_name(label, "city_label"); - gtk_widget_set_margin_left(label, 4); - gtk_widget_set_margin_right(label, 4); - gtk_widget_set_margin_top(label, 4); - gtk_widget_set_margin_bottom(label, 4); - gtk_container_add(GTK_CONTAINER(frame), label); - gtk_widget_show_all(p); - - gdk_device_grab(ev->device, gtk_widget_get_window(p), - GDK_OWNERSHIP_NONE, TRUE, GDK_BUTTON_RELEASE_MASK, NULL, - ev->time); - gtk_grab_add(p); - - g_signal_connect_after(p, "button_release_event", - G_CALLBACK(show_info_button_release), NULL); - } - return TRUE; -} - -/**********************************************************************//** - Used once in the overview page and once in the happiness page - **info_label points to the info_label in the respective struct -**************************************************************************/ -static GtkWidget *create_city_info_table(struct city_dialog *pdialog, - GtkWidget **info_ebox, - GtkWidget **info_label) -{ - int i; - GtkWidget *table, *label, *ebox; - - static const char *output_label[NUM_INFO_FIELDS] = { - N_("Size:"), - N_("Food:"), - N_("Prod:"), - N_("Trade:"), - N_("Gold:"), - N_("Luxury:"), - N_("Science:"), - N_("Granary:"), - N_("Change in:"), - N_("Corruption:"), - N_("Waste:"), - N_("Culture:"), - N_("Pollution:"), - N_("Plague risk:"), - N_("Tech Stolen:"), - N_("Airlift:"), - }; - static bool output_label_done; - - table = gtk_grid_new(); - g_object_set(table, "margin", 4, NULL); - - intl_slist(ARRAY_SIZE(output_label), output_label, &output_label_done); - - for (i = 0; i < NUM_INFO_FIELDS; i++) { - label = gtk_label_new(output_label[i]); - switch (i) { - case INFO_SIZE: - case INFO_TRADE: - case INFO_SCIENCE: - case INFO_GROWTH: - gtk_widget_set_margin_bottom(label, 5); - break; - - case INFO_FOOD: - case INFO_GOLD: - case INFO_GRANARY: - case INFO_CORRUPTION: - gtk_widget_set_margin_top(label, 5); - break; - default: - break; - } - gtk_widget_set_margin_right(label, 5); - gtk_widget_set_name(label, "city_label"); /* for font style? */ - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_grid_attach(GTK_GRID(table), label, 0, i, 1, 1); - - ebox = gtk_event_box_new(); - switch (i) { - case INFO_TRADE: - case INFO_SCIENCE: - case INFO_GROWTH: - gtk_widget_set_margin_bottom(ebox, 5); - break; - - case INFO_GOLD: - case INFO_GRANARY: - case INFO_CORRUPTION: - gtk_widget_set_margin_top(ebox, 5); - break; - default: - break; - } - gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); - g_object_set_data(G_OBJECT(ebox), "pdialog", pdialog); - g_signal_connect(ebox, "button_press_event", - G_CALLBACK(show_info_popup), GUINT_TO_POINTER(i)); - info_ebox[i] = ebox; - - label = gtk_label_new(""); - info_label[i] = label; - gtk_widget_set_name(label, "city_label"); /* ditto */ - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - - gtk_container_add(GTK_CONTAINER(ebox), label); - - gtk_grid_attach(GTK_GRID(table), ebox, 1, i, 1, 1); - } - - gtk_widget_show_all(table); - - return table; -} - -/**********************************************************************//** - Create main citydlg map -**************************************************************************/ -static void create_citydlg_main_map(struct city_dialog *pdialog, - GtkWidget *container) -{ - GtkWidget *frame; - - frame = gtk_frame_new(_("City map")); - gtk_widget_set_size_request(frame, CITY_MAP_MIN_SIZE_X, - CITY_MAP_MIN_SIZE_Y); - gtk_container_add(GTK_CONTAINER(container), frame); - - city_dialog_map_create(pdialog, &pdialog->overview.map_canvas); - gtk_container_add(GTK_CONTAINER(frame), pdialog->overview.map_canvas.sw); -} - -/**********************************************************************//** - Create improvements list -**************************************************************************/ -static GtkWidget *create_citydlg_improvement_list(struct city_dialog *pdialog, - GtkWidget *vbox) -{ - GtkWidget *view; - GtkListStore *store; - GtkCellRenderer *rend; - - /* improvements */ - /* gtk list store columns: 0 - sell value, 1 - sprite, - 2 - description, 3 - upkeep, 4 - is redundant, 5 - tooltip */ - store = gtk_list_store_new(6, G_TYPE_POINTER, GDK_TYPE_PIXBUF, - G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN, - G_TYPE_STRING); - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(store); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - gtk_widget_set_name(view, "small_font"); - pdialog->overview.improvement_list = view; - - rend = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL, - rend, "pixbuf", 1, NULL); - rend = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL, - rend, "text", 2, - "strikethrough", 4, NULL); - rend = gtk_cell_renderer_text_new(); - g_object_set(rend, "xalign", 1.0, NULL); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL, - rend, "text", 3, - "strikethrough", 4, NULL); - - gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(view), 5); - - g_signal_connect(view, "row_activated", G_CALLBACK(impr_callback), - pdialog); - - return view; -} - -/**********************************************************************//** - **** Overview page **** - +- GtkWidget *page ------------------------------------------+ - | +- GtkWidget *middle -----------+------------------------+ | - | | City map | Production | | - | +-------------------------------+------------------------+ | - +------------------------------------------------------------+ - | +- GtkWidget *bottom -------+----------------------------+ | - | | Info | +- GtkWidget *right -----+ | | - | | | | supported units | | | - | | | +------------------------+ | | - | | | | present units | | | - | | | +------------------------+ | | - | +---------------------------+----------------------------+ | - +------------------------------------------------------------+ -**************************************************************************/ -static void create_and_append_overview_page(struct city_dialog *pdialog) -{ - GtkWidget *page, *bottom; - GtkWidget *hbox, *right, *vbox, *frame, *table; - GtkWidget *label, *sw, *view, *bar, *production_combo; - GtkCellRenderer *rend; - GtkListStore *production_store; - /* TRANS: Overview tab in city dialog */ - const char *tab_title = _("_Overview"); - int unit_height = tileset_unit_with_upkeep_height(tileset); - - /* main page */ - page = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(page), - GTK_ORIENTATION_VERTICAL); - gtk_container_set_border_width(GTK_CONTAINER(page), 8); - label = gtk_label_new_with_mnemonic(tab_title); - gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label); - - if (!low_citydlg) { - GtkWidget *middle; - - /* middle: city map, improvements */ - middle = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(middle), 6); - gtk_container_add(GTK_CONTAINER(page), middle); - - /* city map */ - create_citydlg_main_map(pdialog, middle); - - /* improvements */ - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(middle), vbox); - - view = create_citydlg_improvement_list(pdialog, middle); - - label = g_object_new(GTK_TYPE_LABEL, "label", _("Production:"), - "xalign", 0.0, "yalign", 0.5, NULL); - gtk_container_add(GTK_CONTAINER(vbox), label); - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 10); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - production_store = gtk_list_store_new(4, GDK_TYPE_PIXBUF, G_TYPE_STRING, - G_TYPE_INT, G_TYPE_BOOLEAN); - pdialog->overview.change_production_store = production_store; - - production_combo = - gtk_combo_box_new_with_model(GTK_TREE_MODEL(production_store)); - gtk_widget_set_hexpand(production_combo, TRUE); - pdialog->overview.production_combo = production_combo; - gtk_container_add(GTK_CONTAINER(hbox), production_combo); - g_object_unref(production_store); - g_signal_connect(production_combo, "changed", - G_CALLBACK(change_production_callback), pdialog); - - gtk_cell_layout_clear(GTK_CELL_LAYOUT(production_combo)); - rend = gtk_cell_renderer_pixbuf_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo), rend, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo), - rend, "pixbuf", 0, NULL); - g_object_set(rend, "xalign", 0.0, NULL); - - rend = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo), rend, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo), - rend, "text", 1, "strikethrough", 3, NULL); - - bar = gtk_progress_bar_new(); - gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(bar), TRUE); - pdialog->overview.production_bar = bar; - gtk_container_add(GTK_CONTAINER(production_combo), bar); - gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(production_combo), 3); - - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar), _("%d/%d %d turns")); - - pdialog->overview.buy_command = gtk_stockbutton_new(GTK_STOCK_EXECUTE, - _("_Buy")); - gtk_container_add(GTK_CONTAINER(hbox), pdialog->overview.buy_command); - g_signal_connect(pdialog->overview.buy_command, "clicked", - G_CALLBACK(buy_callback), pdialog); - - label = g_object_new(GTK_TYPE_LABEL, "use-underline", TRUE, - "mnemonic-widget", view, - "label", _("I_mprovements:"), - "xalign", 0.0, "yalign", 0.5, NULL); - gtk_container_add(GTK_CONTAINER(vbox), label); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(vbox), sw); - - gtk_container_add(GTK_CONTAINER(sw), view); - } else { - pdialog->overview.buy_command = NULL; - pdialog->overview.production_bar = NULL; - pdialog->overview.production_combo = NULL; - pdialog->overview.change_production_store = NULL; - } - - /* bottom: info, units */ - bottom = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(bottom), 6); - gtk_container_add(GTK_CONTAINER(page), bottom); - - /* info */ - frame = gtk_frame_new(_("Info")); - gtk_container_add(GTK_CONTAINER(bottom), frame); - - table = create_city_info_table(pdialog, - pdialog->overview.info_ebox, - pdialog->overview.info_label); - gtk_widget_set_halign(table, GTK_ALIGN_CENTER); - gtk_widget_set_valign(table, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(frame), table); - - /* right: present and supported units (overview page) */ - right = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(right), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(bottom), right); - - pdialog->overview.supported_units_frame = gtk_frame_new(""); - gtk_container_add(GTK_CONTAINER(right), - pdialog->overview.supported_units_frame); - pdialog->overview.present_units_frame = gtk_frame_new(""); - gtk_container_add(GTK_CONTAINER(right), - pdialog->overview.present_units_frame); - - /* supported units */ - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_widget_set_hexpand(sw, TRUE); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_NONE); - gtk_container_add(GTK_CONTAINER(pdialog->overview.supported_units_frame), - sw); - - - table = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(table), 2); - gtk_widget_set_size_request(table, -1, unit_height); - gtk_container_add(GTK_CONTAINER(sw), table); - - gtk_container_set_focus_hadjustment(GTK_CONTAINER(table), - gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw))); - gtk_container_set_focus_vadjustment(GTK_CONTAINER(table), - gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw))); - - pdialog->overview.supported_unit_table = table; - unit_node_vector_init(&pdialog->overview.supported_units); - - /* present units */ - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_widget_set_hexpand(sw, TRUE); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_NONE); - gtk_container_add(GTK_CONTAINER(pdialog->overview.present_units_frame), sw); - - table = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(table), 2); - gtk_widget_set_size_request(table, -1, unit_height); - gtk_container_add(GTK_CONTAINER(sw), table); - - gtk_container_set_focus_hadjustment(GTK_CONTAINER(table), - gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw))); - gtk_container_set_focus_vadjustment(GTK_CONTAINER(table), - gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw))); - - pdialog->overview.present_unit_table = table; - unit_node_vector_init(&pdialog->overview.present_units); - - /* show page */ - gtk_widget_show_all(page); -} - -/**********************************************************************//** - Create map page for small screens -**************************************************************************/ -static void create_and_append_map_page(struct city_dialog *pdialog) -{ - if (low_citydlg) { - GtkWidget *page; - GtkWidget *label; - const char *tab_title = _("Citymap"); - - page = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(page), - GTK_ORIENTATION_VERTICAL); - gtk_container_set_border_width(GTK_CONTAINER(page), 8); - label = gtk_label_new_with_mnemonic(tab_title); - gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label); - - create_citydlg_main_map(pdialog, page); - - gtk_widget_show_all(page); - } -} - -/**********************************************************************//** - Something dragged to worklist dialog. -**************************************************************************/ -static void target_drag_data_received(GtkWidget *w, - GdkDragContext *context, - gint x, gint y, - GtkSelectionData *data, - guint info, guint time, - gpointer user_data) -{ - struct city_dialog *pdialog = (struct city_dialog *) user_data; - GtkTreeModel *model; - GtkTreePath *path; - - if (NULL != client.conn.playing - && city_owner(pdialog->pcity) != client.conn.playing) { - gtk_drag_finish(context, FALSE, FALSE, time); - } - - if (gtk_tree_get_row_drag_data(data, &model, &path)) { - GtkTreeIter it; - - if (gtk_tree_model_get_iter(model, &it, path)) { - cid id; - struct universal univ; - - gtk_tree_model_get(model, &it, 0, &id, -1); - univ = cid_production(id); - city_change_production(pdialog->pcity, &univ); - gtk_drag_finish(context, TRUE, FALSE, time); - } - gtk_tree_path_free(path); - } - - gtk_drag_finish(context, FALSE, FALSE, time); -} - -/**********************************************************************//** - Create production page header - what tab this actually is, - depends on screen size and layout. -**************************************************************************/ -static void create_production_header(struct city_dialog *pdialog, GtkContainer *contain) -{ - GtkWidget *hbox, *bar; - - hbox = gtk_grid_new(); - g_object_set(hbox, "margin", 2, NULL); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 10); - gtk_container_add(contain, hbox); - - /* The label is set in city_dialog_update_building() */ - bar = gtk_progress_bar_new(); - gtk_widget_set_hexpand(bar, TRUE); - gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(bar), TRUE); - pdialog->production.production_bar = bar; - gtk_container_add(GTK_CONTAINER(hbox), bar); - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar), _("%d/%d %d turns")); - - add_worklist_dnd_target(bar); - - g_signal_connect(bar, "drag_data_received", - G_CALLBACK(target_drag_data_received), pdialog); - - pdialog->production.buy_command = gtk_stockbutton_new(GTK_STOCK_EXECUTE, - _("_Buy")); - gtk_container_add(GTK_CONTAINER(hbox), pdialog->production.buy_command); - - g_signal_connect(pdialog->production.buy_command, "clicked", - G_CALLBACK(buy_callback), pdialog); -} - -/**********************************************************************//** - Create buildings list page for small screens -**************************************************************************/ -static void create_and_append_buildings_page(struct city_dialog *pdialog) -{ - if (low_citydlg) { - GtkWidget *page; - GtkWidget *label; - GtkWidget *vbox; - GtkWidget *view; - const char *tab_title = _("Buildings"); - - page = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(page), - GTK_ORIENTATION_VERTICAL); - gtk_container_set_border_width(GTK_CONTAINER(page), 8); - label = gtk_label_new_with_mnemonic(tab_title); - - create_production_header(pdialog, GTK_CONTAINER(page)); - gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(page), vbox); - - view = create_citydlg_improvement_list(pdialog, vbox); - - gtk_container_add(GTK_CONTAINER(vbox), view); - - gtk_widget_show_all(page); - } -} - -/**********************************************************************//** - **** Production Page **** -**************************************************************************/ -static void create_and_append_worklist_page(struct city_dialog *pdialog) -{ - const char *tab_title = _("P_roduction"); - GtkWidget *label = gtk_label_new_with_mnemonic(tab_title); - GtkWidget *page, *editor; - - page = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(page), - GTK_ORIENTATION_VERTICAL); - gtk_container_set_border_width(GTK_CONTAINER(page), 8); - gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label); - - /* stuff that's being currently built */ - if (!low_citydlg) { - label = g_object_new(GTK_TYPE_LABEL, - "label", _("Production:"), - "xalign", 0.0, "yalign", 0.5, NULL); - pdialog->production.production_label = label; - gtk_container_add(GTK_CONTAINER(page), label); - - create_production_header(pdialog, GTK_CONTAINER(page)); - } else { - pdialog->production.production_label = NULL; - } - - editor = create_worklist(); - g_object_set(editor, "margin", 6, NULL); - reset_city_worklist(editor, pdialog->pcity); - gtk_container_add(GTK_CONTAINER(page), editor); - pdialog->production.worklist = editor; - - gtk_widget_show_all(page); -} - -/**********************************************************************//** - **** Happiness Page **** - +- GtkWidget *page ----------+-------------------------------------------+ - | +- GtkWidget *left ------+ | +- GtkWidget *right --------------------+ | - | | Info | | | City map | | - | +- GtkWidget *citizens --+ | +- GtkWidget pdialog->happiness.widget -+ | - | | Citizens data | | | Happiness | | - | +------------------------+ | +---------------------------------------+ | - +----------------------------+-------------------------------------------+ -**************************************************************************/ -static void create_and_append_happiness_page(struct city_dialog *pdialog) -{ - GtkWidget *page, *label, *table, *right, *left, *frame; - const char *tab_title = _("Happ_iness"); - - /* main page */ - page = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(page), 6); - gtk_container_set_border_width(GTK_CONTAINER(page), 8); - label = gtk_label_new_with_mnemonic(tab_title); - gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label); - - /* left: info, citizens */ - left = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(left), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(page), left); - - if (!low_citydlg) { - /* upper left: info */ - frame = gtk_frame_new(_("Info")); - gtk_container_add(GTK_CONTAINER(left), frame); - - table = create_city_info_table(pdialog, - pdialog->happiness.info_ebox, - pdialog->happiness.info_label); - gtk_widget_set_halign(table, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(frame), table); - } - - /* lower left: citizens */ - if (game.info.citizen_nationality) { - pdialog->happiness.citizens = gtk_grid_new(); - gtk_orientable_set_orientation( - GTK_ORIENTABLE(pdialog->happiness.citizens), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(left), pdialog->happiness.citizens); - gtk_container_add(GTK_CONTAINER(pdialog->happiness.citizens), - citizens_dialog_display(pdialog->pcity)); - } - - /* right: city map, happiness */ - right = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(right), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(page), right); - - if (!low_citydlg) { - /* upper right: city map */ - frame = gtk_frame_new(_("City map")); - gtk_widget_set_size_request(frame, CITY_MAP_MIN_SIZE_X, - CITY_MAP_MIN_SIZE_Y); - gtk_container_add(GTK_CONTAINER(right), frame); - - city_dialog_map_create(pdialog, &pdialog->happiness.map_canvas); - gtk_container_add(GTK_CONTAINER(frame), pdialog->happiness.map_canvas.sw); - } - - /* lower right: happiness */ - pdialog->happiness.widget = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(pdialog->happiness.widget), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(right), pdialog->happiness.widget); - gtk_container_add(GTK_CONTAINER(pdialog->happiness.widget), - get_top_happiness_display(pdialog->pcity, low_citydlg, pdialog->shell)); - - /* show page */ - gtk_widget_show_all(page); -} - -/**********************************************************************//** - **** Citizen Management Agent (CMA) Page **** -**************************************************************************/ -static void create_and_append_cma_page(struct city_dialog *pdialog) -{ - GtkWidget *page, *label; - const char *tab_title = _("_Governor"); - - page = gtk_grid_new(); - - label = gtk_label_new_with_mnemonic(tab_title); - - gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label); - - pdialog->cma_editor = create_cma_dialog(pdialog->pcity, low_citydlg); - gtk_container_add(GTK_CONTAINER(page), pdialog->cma_editor->shell); - - gtk_widget_show(page); -} - -/**********************************************************************//** - **** Misc. Settings Page **** -**************************************************************************/ -static void create_and_append_settings_page(struct city_dialog *pdialog) -{ - int i; - GtkWidget *vbox2, *page, *frame, *label, *button; - GtkSizeGroup *size; - GSList *group; - const char *tab_title = _("_Settings"); - - static const char *new_citizens_output_label[] = { - N_("Luxury"), - N_("Science"), - N_("Gold") - }; - - static const char *disband_label - = N_("Allow unit production to disband city"); - - static const char *misc_whichtab_label[NUM_PAGES] = { - N_("Overview page"), - N_("Production page"), - N_("Happiness page"), - N_("Governor page"), - N_("This Settings page"), - N_("Last active page") - }; - - static bool new_citizens_label_done; - static bool misc_whichtab_label_done; - - /* initialize signal_blocker */ - pdialog->misc.block_signal = 0; - - - page = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(page), 18); - gtk_container_set_border_width(GTK_CONTAINER(page), 8); - - size = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); - - label = gtk_label_new_with_mnemonic(tab_title); - - gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label); - - /* new_citizens radio */ - frame = gtk_frame_new(_("New citizens produce")); - gtk_grid_attach(GTK_GRID(page), frame, 0, 0, 1, 1); - gtk_size_group_add_widget(size, frame); - - vbox2 = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(frame), vbox2); - - intl_slist(ARRAY_SIZE(new_citizens_output_label), new_citizens_output_label, - &new_citizens_label_done); - - group = NULL; - for (i = 0; i < ARRAY_SIZE(new_citizens_output_label); i++) { - button = gtk_radio_button_new_with_mnemonic(group, new_citizens_output_label[i]); - pdialog->misc.new_citizens_radio[i] = button; - gtk_container_add(GTK_CONTAINER(vbox2), button); - g_signal_connect(button, "toggled", - G_CALLBACK(cityopt_callback), pdialog); - group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button)); - } - - /* next is the next-time-open radio group in the right column */ - frame = gtk_frame_new(_("Next time open")); - gtk_grid_attach(GTK_GRID(page), frame, 1, 0, 1, 1); - gtk_size_group_add_widget(size, frame); - - vbox2 = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(frame), vbox2); - - intl_slist(ARRAY_SIZE(misc_whichtab_label), misc_whichtab_label, - &misc_whichtab_label_done); - - group = NULL; - for (i = 0; i < ARRAY_SIZE(misc_whichtab_label); i++) { - button = gtk_radio_button_new_with_mnemonic(group, misc_whichtab_label[i]); - pdialog->misc.whichtab_radio[i] = button; - gtk_container_add(GTK_CONTAINER(vbox2), button); - g_signal_connect(button, "toggled", - G_CALLBACK(misc_whichtab_callback), GINT_TO_POINTER(i)); - group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button)); - } - - /* now we go back and fill the hbox rename */ - frame = gtk_frame_new(_("City")); - gtk_widget_set_margin_top(frame, 12); - gtk_widget_set_margin_bottom(frame, 12); - gtk_grid_attach(GTK_GRID(page), frame, 0, 1, 1, 1); - - vbox2 = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(frame), vbox2); - - button = gtk_button_new_with_mnemonic(_("R_ename...")); - pdialog->misc.rename_command = button; - gtk_container_add(GTK_CONTAINER(vbox2), button); - g_signal_connect(button, "clicked", - G_CALLBACK(rename_callback), pdialog); - - gtk_widget_set_sensitive(button, can_client_issue_orders()); - - /* the disband-city-on-unit-production button */ - button = gtk_check_button_new_with_mnemonic(_(disband_label)); - pdialog->misc.disband_on_settler = button; - gtk_container_add(GTK_CONTAINER(vbox2), button); - g_signal_connect(button, "toggled", - G_CALLBACK(cityopt_callback), pdialog); - - /* we choose which page to popup by default */ - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON - (pdialog-> - misc.whichtab_radio[new_dialog_def_page]), - TRUE); - - set_cityopt_values(pdialog); - - gtk_widget_show_all(page); - - if (new_dialog_def_page == (NUM_PAGES - 1)) { - gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook), - last_page); - } else { - gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook), - new_dialog_def_page); - } -} - - - -/**********************************************************************//** - **** Main City Dialog **** - +----------------------------+-------------------------------+ - | GtkWidget *top: Citizens | city name | - +----------------------------+-------------------------------+ - | | - +------------------------------------------------------------+ -**************************************************************************/ -static struct city_dialog *create_city_dialog(struct city *pcity) -{ - struct city_dialog *pdialog; - GtkWidget *close_command; - GtkWidget *vbox, *hbox, *cbox, *ebox; - int citizen_bar_width; - int citizen_bar_height; - - if (!city_dialogs_have_been_initialised) { - initialize_city_dialogs(); - } - - pdialog = fc_malloc(sizeof(struct city_dialog)); - pdialog->pcity = pcity; - pdialog->buy_shell = NULL; - pdialog->sell_shell = NULL; - pdialog->rename_shell = NULL; - pdialog->happiness.map_canvas.sw = NULL; /* make sure NULL if spy */ - pdialog->happiness.map_canvas.ebox = NULL; /* ditto */ - pdialog->happiness.map_canvas.darea = NULL; /* ditto */ - pdialog->happiness.citizens = NULL; /* ditto */ - pdialog->cma_editor = NULL; - pdialog->map_canvas_store_unscaled - = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - canvas_width, canvas_height); - - pdialog->shell = gtk_dialog_new(); - gtk_window_set_title(GTK_WINDOW(pdialog->shell), city_name_get(pcity)); - setup_dialog(pdialog->shell, toplevel); - gtk_window_set_role(GTK_WINDOW(pdialog->shell), "city"); - - g_signal_connect(pdialog->shell, "destroy", - G_CALLBACK(city_destroy_callback), pdialog); - gtk_window_set_position(GTK_WINDOW(pdialog->shell), GTK_WIN_POS_MOUSE); - gtk_widget_set_name(pdialog->shell, "Freeciv"); - - gtk_widget_realize(pdialog->shell); - - /* keep the icon of the executable on Windows (see PR#36491) */ -#ifndef FREECIV_MSWINDOWS - { - GdkPixbuf *pixbuf = sprite_get_pixbuf(get_icon_sprite(tileset, ICON_CITYDLG)); - - /* Only call this after tileset_load_tiles is called. */ - gtk_window_set_icon(GTK_WINDOW(pdialog->shell), pixbuf); - g_object_unref(pixbuf); - } -#endif /* FREECIV_MSWINDOWS */ - - /* Restore size of the city dialog. */ - gtk_window_set_default_size(GTK_WINDOW(pdialog->shell), - GUI_GTK_OPTION(citydlg_xsize), - GUI_GTK_OPTION(citydlg_ysize)); - - pdialog->popup_menu = gtk_menu_new(); - - vbox = gtk_dialog_get_content_area(GTK_DIALOG(pdialog->shell)); - hbox = gtk_grid_new(); - gtk_grid_set_column_homogeneous(GTK_GRID(hbox), TRUE); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - /**** Citizens bar here ****/ - cbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(hbox), cbox); - - ebox = gtk_event_box_new(); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); - gtk_container_add(GTK_CONTAINER(cbox), ebox); - - citizen_bar_width = tileset_small_sprite_width(tileset) * NUM_CITIZENS_SHOWN; - citizen_bar_height = tileset_small_sprite_height(tileset); - - pdialog->citizen_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - citizen_bar_width, citizen_bar_height); - pdialog->citizen_images = gtk_image_new_from_surface(pdialog->citizen_surface); - - gtk_widget_add_events(pdialog->citizen_images, GDK_BUTTON_PRESS_MASK); - gtk_widget_set_margin_left(pdialog->citizen_images, 2); - gtk_widget_set_margin_right(pdialog->citizen_images, 2); - gtk_widget_set_margin_top(pdialog->citizen_images, 2); - gtk_widget_set_margin_bottom(pdialog->citizen_images, 2); - gtk_widget_set_halign(pdialog->citizen_images, GTK_ALIGN_START); - gtk_widget_set_valign(pdialog->citizen_images, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(ebox), pdialog->citizen_images); - g_signal_connect(G_OBJECT(ebox), "button-press-event", - G_CALLBACK(citizens_callback), pdialog); - - /**** City name label here ****/ - pdialog->name_label = gtk_label_new(NULL); - gtk_widget_set_hexpand(pdialog->name_label, TRUE); - gtk_widget_set_halign(pdialog->name_label, GTK_ALIGN_START); - gtk_widget_set_valign(pdialog->name_label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), pdialog->name_label); - - /**** -Start of Notebook- ****/ - - pdialog->notebook = gtk_notebook_new(); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(pdialog->notebook), - GTK_POS_BOTTOM); - gtk_container_add(GTK_CONTAINER(vbox), pdialog->notebook); - - create_and_append_overview_page(pdialog); - create_and_append_map_page(pdialog); - create_and_append_buildings_page(pdialog); - create_and_append_worklist_page(pdialog); - - /* only create these tabs if not a spy */ - if (!client_has_player() || city_owner(pcity) == client_player()) { - create_and_append_happiness_page(pdialog); - } - - if (city_owner(pcity) == client_player() - && !client_is_observer()) { - create_and_append_cma_page(pdialog); - create_and_append_settings_page(pdialog); - } else { - gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook), - OVERVIEW_PAGE); - } - - /**** End of Notebook ****/ - - /* bottom buttons */ - - pdialog->show_units_command = - gtk_dialog_add_button(GTK_DIALOG(pdialog->shell), _("_List present units..."), CDLGR_UNITS); - - g_signal_connect(GTK_DIALOG(pdialog->shell), "response", - G_CALLBACK(citydlg_response_callback), pdialog); - - pdialog->prev_command = gtk_stockbutton_new(GTK_STOCK_GO_BACK, - _("_Prev city")); - gtk_dialog_add_action_widget(GTK_DIALOG(pdialog->shell), - pdialog->prev_command, 1); - - pdialog->next_command = gtk_stockbutton_new(GTK_STOCK_GO_FORWARD, - _("_Next city")); - gtk_dialog_add_action_widget(GTK_DIALOG(pdialog->shell), - pdialog->next_command, 2); - - if (city_owner(pcity) != client.conn.playing) { - gtk_widget_set_sensitive(pdialog->prev_command, FALSE); - gtk_widget_set_sensitive(pdialog->next_command, FALSE); - } - - close_command = gtk_dialog_add_button(GTK_DIALOG(pdialog->shell), - GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - - gtk_dialog_set_default_response(GTK_DIALOG(pdialog->shell), - GTK_RESPONSE_CLOSE); - - g_signal_connect(close_command, "clicked", - G_CALLBACK(close_callback), pdialog); - - g_signal_connect(pdialog->prev_command, "clicked", - G_CALLBACK(switch_city_callback), pdialog); - - g_signal_connect(pdialog->next_command, "clicked", - G_CALLBACK(switch_city_callback), pdialog); - - /* some other things we gotta do */ - - g_signal_connect(pdialog->shell, "key_press_event", - G_CALLBACK(keyboard_handler), pdialog); - - dialog_list_prepend(dialog_list, pdialog); - - real_city_dialog_refresh(pdialog->pcity); - - /* need to do this every time a new dialog is opened. */ - city_dialog_update_prev_next(); - - gtk_widget_show_all(pdialog->shell); - - gtk_window_set_focus(GTK_WINDOW(pdialog->shell), close_command); - - return pdialog; -} - -/**************** Functions to update parts of the dialog ****************/ -/**********************************************************************//** - Update title of city dialog. -**************************************************************************/ -static void city_dialog_update_title(struct city_dialog *pdialog) -{ - gchar *buf; - const gchar *now; - - if (city_unhappy(pdialog->pcity)) { - /* TRANS: city dialog title */ - buf = g_strdup_printf(_("%s - %s citizens - DISORDER"), - city_name_get(pdialog->pcity), - population_to_text(city_population(pdialog->pcity))); - } else if (city_celebrating(pdialog->pcity)) { - /* TRANS: city dialog title */ - buf = g_strdup_printf(_("%s - %s citizens - celebrating"), - city_name_get(pdialog->pcity), - population_to_text(city_population(pdialog->pcity))); - } else if (city_happy(pdialog->pcity)) { - /* TRANS: city dialog title */ - buf = g_strdup_printf(_("%s - %s citizens - happy"), - city_name_get(pdialog->pcity), - population_to_text(city_population(pdialog->pcity))); - } else { - /* TRANS: city dialog title */ - buf = g_strdup_printf(_("%s - %s citizens"), - city_name_get(pdialog->pcity), - population_to_text(city_population(pdialog->pcity))); - } - - now = gtk_label_get_text(GTK_LABEL(pdialog->name_label)); - if (strcmp(now, buf) != 0) { - gtk_window_set_title(GTK_WINDOW(pdialog->shell), city_name_get(pdialog->pcity)); - gtk_label_set_markup(GTK_LABEL(pdialog->name_label), buf); - } - - g_free(buf); -} - -/**********************************************************************//** - Update citizens in city dialog -**************************************************************************/ -static void city_dialog_update_citizens(struct city_dialog *pdialog) -{ - enum citizen_category categories[MAX_CITY_SIZE]; - int i, width; - int citizen_bar_height; - struct city *pcity = pdialog->pcity; - int num_citizens = get_city_citizen_types(pcity, FEELING_FINAL, categories); - cairo_t *cr; - - /* If there is not enough space we stack the icons. We draw from left to */ - /* right. width is how far we go to the right for each drawn pixmap. The */ - /* last icon is always drawn in full, and so has reserved */ - /* tileset_small_sprite_width(tileset) pixels. */ - - if (num_citizens > 1) { - width = MIN(tileset_small_sprite_width(tileset), - ((NUM_CITIZENS_SHOWN - 1) * tileset_small_sprite_width(tileset)) / - (num_citizens - 1)); - } else { - width = tileset_small_sprite_width(tileset); - } - pdialog->cwidth = width; - - /* overview page */ - citizen_bar_height = tileset_small_sprite_height(tileset); - - cr = cairo_create(pdialog->citizen_surface); - - for (i = 0; i < num_citizens; i++) { - cairo_set_source_surface(cr, - get_citizen_sprite(tileset, categories[i], i, pcity)->surface, - i * width, 0); - cairo_rectangle(cr, i * width, 0, width, citizen_bar_height); - cairo_fill(cr); - } - cairo_rectangle(cr, i * width, 0, width * (NUM_CITIZENS_SHOWN - i), citizen_bar_height); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_fill(cr); - - cairo_destroy(cr); - - gtk_widget_queue_draw(pdialog->citizen_images); -} - -/**********************************************************************//** - Update textual info fields in city dialog -**************************************************************************/ -static void city_dialog_update_information(GtkWidget **info_ebox, - GtkWidget **info_label, - struct city_dialog *pdialog) -{ - int i, illness = 0; - char buf[NUM_INFO_FIELDS][512]; - struct city *pcity = pdialog->pcity; - int granaryturns; - int non_workers = city_specialists(pcity); - GdkRGBA red = {1.0, 0, 0, 1.0}; - GdkRGBA *color; - - - /* fill the buffers with the necessary info */ - if (non_workers) { - fc_snprintf(buf[INFO_SIZE], sizeof(buf[INFO_SIZE]), "%3d (%3d)", - pcity->size, non_workers); - } else { - fc_snprintf(buf[INFO_SIZE], sizeof(buf[INFO_SIZE]), "%3d", pcity->size); - } - fc_snprintf(buf[INFO_FOOD], sizeof(buf[INFO_FOOD]), "%3d (%+4d)", - pcity->prod[O_FOOD], pcity->surplus[O_FOOD]); - fc_snprintf(buf[INFO_SHIELD], sizeof(buf[INFO_SHIELD]), "%3d (%+4d)", - pcity->prod[O_SHIELD] + pcity->waste[O_SHIELD], - pcity->surplus[O_SHIELD]); - fc_snprintf(buf[INFO_TRADE], sizeof(buf[INFO_TRADE]), "%3d (%+4d)", - pcity->surplus[O_TRADE] + pcity->waste[O_TRADE], - pcity->surplus[O_TRADE]); - fc_snprintf(buf[INFO_GOLD], sizeof(buf[INFO_GOLD]), "%3d (%+4d)", - pcity->prod[O_GOLD], pcity->surplus[O_GOLD]); - fc_snprintf(buf[INFO_LUXURY], sizeof(buf[INFO_LUXURY]), "%3d", - pcity->prod[O_LUXURY]); - fc_snprintf(buf[INFO_SCIENCE], sizeof(buf[INFO_SCIENCE]), "%3d", - pcity->prod[O_SCIENCE]); - fc_snprintf(buf[INFO_GRANARY], sizeof(buf[INFO_GRANARY]), "%4d/%-4d", - pcity->food_stock, city_granary_size(city_size_get(pcity))); - - granaryturns = city_turns_to_grow(pcity); - if (granaryturns == 0) { - /* TRANS: city growth is blocked. Keep short. */ - fc_snprintf(buf[INFO_GROWTH], sizeof(buf[INFO_GROWTH]), _("blocked")); - } else if (granaryturns == FC_INFINITY) { - /* TRANS: city is not growing. Keep short. */ - fc_snprintf(buf[INFO_GROWTH], sizeof(buf[INFO_GROWTH]), _("never")); - } else { - /* A negative value means we'll have famine in that many turns. - But that's handled down below. */ - /* TRANS: city growth turns. Keep short. */ - fc_snprintf(buf[INFO_GROWTH], sizeof(buf[INFO_GROWTH]), - PL_("%d turn", "%d turns", abs(granaryturns)), - abs(granaryturns)); - } - fc_snprintf(buf[INFO_CORRUPTION], sizeof(buf[INFO_CORRUPTION]), "%4d", - pcity->waste[O_TRADE]); - fc_snprintf(buf[INFO_WASTE], sizeof(buf[INFO_WASTE]), "%4d", - pcity->waste[O_SHIELD]); - fc_snprintf(buf[INFO_CULTURE], sizeof(buf[INFO_CULTURE]), "%4d", - pcity->client.culture); - fc_snprintf(buf[INFO_POLLUTION], sizeof(buf[INFO_POLLUTION]), "%4d", - pcity->pollution); - if (!game.info.illness_on) { - fc_snprintf(buf[INFO_ILLNESS], sizeof(buf[INFO_ILLNESS]), " -.-"); - } else { - illness = city_illness_calc(pcity, NULL, NULL, NULL, NULL); - /* illness is in tenth of percent */ - fc_snprintf(buf[INFO_ILLNESS], sizeof(buf[INFO_ILLNESS]), "%4.1f%%", - (float)illness / 10.0); - } - if (pcity->steal) { - fc_snprintf(buf[INFO_STEAL], sizeof(buf[INFO_STEAL]), _("%d times"), pcity->steal); - } else { - fc_snprintf(buf[INFO_STEAL], sizeof(buf[INFO_STEAL]), _("Not stolen")); - } - - get_city_dialog_airlift_value(pcity, buf[INFO_AIRLIFT], sizeof(buf[INFO_AIRLIFT])); - - /* stick 'em in the labels */ - for (i = 0; i < NUM_INFO_FIELDS; i++) { - gtk_label_set_text(GTK_LABEL(info_label[i]), buf[i]); - } - - /* - * Special style stuff for granary, growth, pollution, and plague below. - * For starvation, the "4" below is arbitrary. 3 turns should be enough - * of a warning. - */ - color = (granaryturns > -4 && granaryturns < 0) ? &red : NULL; - gtk_widget_override_color(info_label[INFO_GRANARY], GTK_STATE_FLAG_NORMAL, color); - - color = (granaryturns == 0 || pcity->surplus[O_FOOD] < 0) ? &red : NULL; - gtk_widget_override_color(info_label[INFO_GROWTH], GTK_STATE_FLAG_NORMAL, color); - - /* someone could add the color &orange for better granularity here */ - - color = (pcity->pollution >= 10) ? &red : NULL; - gtk_widget_override_color(info_label[INFO_POLLUTION], GTK_STATE_FLAG_NORMAL, color); - - /* illness is in tenth of percent, i.e 100 == 10.0% */ - color = (illness >= 100) ? &red : NULL; - gtk_widget_override_color(info_label[INFO_ILLNESS], GTK_STATE_FLAG_NORMAL, color); -} - -/**********************************************************************//** - Update map display of city dialog -**************************************************************************/ -static void city_dialog_update_map(struct city_dialog *pdialog) -{ - struct canvas store = FC_STATIC_CANVAS_INIT; - - store.surface = pdialog->map_canvas_store_unscaled; - - /* The drawing is done in three steps. - * 1. First we render to a pixmap with the appropriate canvas size. - * 2. Then the pixmap is rendered into a pixbuf of equal size. - * 3. Finally this pixbuf is composited and scaled onto the GtkImage's - * target pixbuf. - */ - - city_dialog_redraw_map(pdialog->pcity, &store); - - /* draw to real window */ - draw_map_canvas(pdialog); - - if (cma_is_city_under_agent(pdialog->pcity, NULL)) { - gtk_widget_set_sensitive(pdialog->overview.map_canvas.ebox, FALSE); - if (pdialog->happiness.map_canvas.ebox) { - gtk_widget_set_sensitive(pdialog->happiness.map_canvas.ebox, FALSE); - } - } else { - gtk_widget_set_sensitive(pdialog->overview.map_canvas.ebox, TRUE); - if (pdialog->happiness.map_canvas.ebox) { - gtk_widget_set_sensitive(pdialog->happiness.map_canvas.ebox, TRUE); - } - } -} - -/**********************************************************************//** - Update what city is building and buy cost in city dialog -**************************************************************************/ -static void city_dialog_update_building(struct city_dialog *pdialog) -{ - char buf[32], buf2[200]; - gdouble pct; - - GtkListStore* store; - GtkTreeIter iter; - struct universal targets[MAX_NUM_PRODUCTION_TARGETS]; - struct item items[MAX_NUM_PRODUCTION_TARGETS]; - int targets_used, item; - struct city *pcity = pdialog->pcity; - gboolean sensitive = city_can_buy(pcity); - const char *descr = city_production_name_translation(pcity); - int cost = city_production_build_shield_cost(pcity); - - if (pdialog->overview.buy_command != NULL) { - gtk_widget_set_sensitive(pdialog->overview.buy_command, sensitive); - } - if (pdialog->production.buy_command != NULL) { - gtk_widget_set_sensitive(pdialog->production.buy_command, sensitive); - } - - /* Make sure build slots info is up to date */ - if (pdialog->production.production_label != NULL) { - int build_slots = city_build_slots(pcity); - - /* Only display extra info if more than one slot is available */ - if (build_slots > 1) { - fc_snprintf(buf2, sizeof(buf2), - /* TRANS: never actually used with built_slots <= 1 */ - PL_("Production (up to %d unit per turn):", - "Production (up to %d units per turn):", build_slots), - build_slots); - gtk_label_set_text( - GTK_LABEL(pdialog->production.production_label), buf2); - } else { - gtk_label_set_text( - GTK_LABEL(pdialog->production.production_label), _("Production:")); - } - } - - /* Update what the city is working on */ - get_city_dialog_production(pcity, buf, sizeof(buf)); - - if (cost > 0) { - pct = (gdouble) pcity->shield_stock / (gdouble) cost; - pct = CLAMP(pct, 0.0, 1.0); - } else { - pct = 1.0; - } - - if (pdialog->overview.production_bar != NULL) { - fc_snprintf(buf2, sizeof(buf2), "%s%s\n%s", descr, - worklist_is_empty(&pcity->worklist) ? "" : " (+)", buf); - gtk_progress_bar_set_text( - GTK_PROGRESS_BAR(pdialog->overview.production_bar), buf2); - gtk_progress_bar_set_fraction( - GTK_PROGRESS_BAR(pdialog->overview.production_bar), pct); - } - - if (pdialog->production.production_bar != NULL) { - fc_snprintf(buf2, sizeof(buf2), "%s%s: %s", descr, - worklist_is_empty(&pcity->worklist) ? "" : " (+)", buf); - gtk_progress_bar_set_text( - GTK_PROGRESS_BAR(pdialog->production.production_bar), buf2); - gtk_progress_bar_set_fraction( - GTK_PROGRESS_BAR(pdialog->production.production_bar), pct); - } - - if (pdialog->overview.production_combo != NULL) { - gtk_combo_box_set_active(GTK_COMBO_BOX(pdialog->overview.production_combo), - -1); - } - - store = pdialog->overview.change_production_store; - if (store != NULL) { - gtk_list_store_clear(pdialog->overview.change_production_store); - - targets_used - = collect_eventually_buildable_targets(targets, pdialog->pcity, FALSE); - name_and_sort_items(targets, targets_used, items, FALSE, pcity); - - for (item = 0; item < targets_used; item++) { - if (can_city_build_now(pcity, &items[item].item)) { - const char *name; - struct sprite* sprite; - GdkPixbuf *pix; - struct universal target = items[item].item; - bool useless; - - if (VUT_UTYPE == target.kind) { - name = utype_name_translation(target.value.utype); - sprite = get_unittype_sprite(tileset, target.value.utype, - direction8_invalid()); - useless = FALSE; - } else { - name = improvement_name_translation(target.value.building); - sprite = get_building_sprite(tileset, target.value.building); - useless = is_improvement_redundant(pcity, target.value.building); - } - pix = sprite_get_pixbuf(sprite); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, pix, - 1, name, 3, useless, - 2, (gint)cid_encode(items[item].item), -1); - g_object_unref(G_OBJECT(pix)); - } - } - } - - /* work around GTK+ refresh bug. */ - if (pdialog->overview.production_bar != NULL) { - gtk_widget_queue_resize(pdialog->overview.production_bar); - } - if (pdialog->production.production_bar != NULL) { - gtk_widget_queue_resize(pdialog->production.production_bar); - } -} - -/**********************************************************************//** - Update list of improvements in city dialog -**************************************************************************/ -static void city_dialog_update_improvement_list(struct city_dialog *pdialog) -{ - int total, item, targets_used; - struct universal targets[MAX_NUM_PRODUCTION_TARGETS]; - struct item items[MAX_NUM_PRODUCTION_TARGETS]; - GtkTreeModel *model; - GtkListStore *store; - - const char *tooltip_sellable = _("Press ENTER or double-click to " - "sell an improvement."); - const char *tooltip_great_wonder = _("Great Wonder - cannot be sold."); - const char *tooltip_small_wonder = _("Small Wonder - cannot be sold."); - - model = - gtk_tree_view_get_model(GTK_TREE_VIEW(pdialog->overview.improvement_list)); - store = GTK_LIST_STORE(model); - - targets_used = collect_already_built_targets(targets, pdialog->pcity); - name_and_sort_items(targets, targets_used, items, FALSE, pdialog->pcity); - - gtk_list_store_clear(store); - - total = 0; - for (item = 0; item < targets_used; item++) { - GdkPixbuf *pix; - GtkTreeIter it; - int upkeep; - struct sprite *sprite; - struct universal target = items[item].item; - - fc_assert_action(VUT_IMPROVEMENT == target.kind, continue); - /* This takes effects (like Adam Smith's) into account. */ - upkeep = city_improvement_upkeep(pdialog->pcity, target.value.building); - sprite = get_building_sprite(tileset, target.value.building); - - pix = sprite_get_pixbuf(sprite); - gtk_list_store_append(store, &it); - gtk_list_store_set(store, &it, - 0, target.value.building, - 1, pix, - 2, items[item].descr, - 3, upkeep, - 4, - is_improvement_redundant(pdialog->pcity, - target.value.building), - 5, - is_great_wonder(target.value.building) ? - tooltip_great_wonder : - (is_small_wonder(target.value.building) ? - tooltip_small_wonder : tooltip_sellable), - -1); - g_object_unref(G_OBJECT(pix)); - - total += upkeep; - } -} - -/**********************************************************************//** - Update list of supported units in city dialog -**************************************************************************/ -static void city_dialog_update_supported_units(struct city_dialog *pdialog) -{ - struct unit_list *units; - struct unit_node_vector *nodes; - int n, m, i; - gchar *buf; - int free_unhappy = get_city_bonus(pdialog->pcity, EFT_MAKE_CONTENT_MIL); - - if (NULL != client.conn.playing - && city_owner(pdialog->pcity) != client.conn.playing) { - units = pdialog->pcity->client.info_units_supported; - } else { - units = pdialog->pcity->units_supported; - } - - nodes = &pdialog->overview.supported_units; - - n = unit_list_size(units); - m = unit_node_vector_size(nodes); - - if (m > n) { - i = 0; - unit_node_vector_iterate(nodes, elt) { - if (i++ >= n) { - gtk_widget_destroy(elt->cmd); - } - } unit_node_vector_iterate_end; - - unit_node_vector_reserve(nodes, n); - } else { - for (i = m; i < n; i++) { - GtkWidget *cmd, *pix; - struct unit_node node; - - cmd = gtk_button_new(); - node.cmd = cmd; - - gtk_button_set_relief(GTK_BUTTON(cmd), GTK_RELIEF_NONE); - gtk_widget_add_events(cmd, - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); - - pix = gtk_image_new(); - node.pix = pix; - node.height = tileset_unit_with_upkeep_height(tileset); - - gtk_container_add(GTK_CONTAINER(cmd), pix); - - gtk_grid_attach(GTK_GRID(pdialog->overview.supported_unit_table), - cmd, i, 0, 1, 1); - unit_node_vector_append(nodes, node); - } - } - - i = 0; - unit_list_iterate(units, punit) { - struct unit_node *pnode; - int happy_cost = city_unit_unhappiness(punit, &free_unhappy); - - pnode = unit_node_vector_get(nodes, i); - if (pnode) { - GtkWidget *cmd, *pix; - - cmd = pnode->cmd; - pix = pnode->pix; - - put_unit_image_city_overlays(punit, GTK_IMAGE(pix), pnode->height, - punit->upkeep, happy_cost); - - g_signal_handlers_disconnect_matched(cmd, - G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, supported_unit_callback, NULL); - - g_signal_handlers_disconnect_matched(cmd, - G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, supported_unit_middle_callback, NULL); - - gtk_widget_set_tooltip_text(cmd, unit_description(punit)); - - g_signal_connect(cmd, "button_press_event", - G_CALLBACK(supported_unit_callback), - GINT_TO_POINTER(punit->id)); - - g_signal_connect(cmd, "button_release_event", - G_CALLBACK(supported_unit_middle_callback), - GINT_TO_POINTER(punit->id)); - - if (city_owner(pdialog->pcity) != client.conn.playing) { - gtk_widget_set_sensitive(cmd, FALSE); - } else { - gtk_widget_set_sensitive(cmd, TRUE); - } - - gtk_widget_show(pix); - gtk_widget_show(cmd); - } - i++; - } unit_list_iterate_end; - - buf = g_strdup_printf(_("Supported units %d"), n); - gtk_frame_set_label(GTK_FRAME(pdialog->overview.supported_units_frame), buf); - g_free(buf); -} - -/**********************************************************************//** - Update list of present units in city dialog -**************************************************************************/ -static void city_dialog_update_present_units(struct city_dialog *pdialog) -{ - struct unit_list *units; - struct unit_node_vector *nodes; - int n, m, i; - gchar *buf; - - if (NULL != client.conn.playing - && city_owner(pdialog->pcity) != client.conn.playing) { - units = pdialog->pcity->client.info_units_present; - } else { - units = pdialog->pcity->tile->units; - } - - nodes = &pdialog->overview.present_units; - - n = unit_list_size(units); - m = unit_node_vector_size(nodes); - - if (m > n) { - i = 0; - unit_node_vector_iterate(nodes, elt) { - if (i++ >= n) { - gtk_widget_destroy(elt->cmd); - } - } unit_node_vector_iterate_end; - - unit_node_vector_reserve(nodes, n); - } else { - for (i = m; i < n; i++) { - GtkWidget *cmd, *pix; - struct unit_node node; - - cmd = gtk_button_new(); - node.cmd = cmd; - - gtk_button_set_relief(GTK_BUTTON(cmd), GTK_RELIEF_NONE); - gtk_widget_add_events(cmd, - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); - - pix = gtk_image_new(); - node.pix = pix; - node.height = tileset_full_tile_height(tileset); - - gtk_container_add(GTK_CONTAINER(cmd), pix); - - gtk_grid_attach(GTK_GRID(pdialog->overview.present_unit_table), - cmd, i, 0, 1, 1); - unit_node_vector_append(nodes, node); - } - } - - i = 0; - unit_list_iterate(units, punit) { - struct unit_node *pnode; - - pnode = unit_node_vector_get(nodes, i); - if (pnode) { - GtkWidget *cmd, *pix; - - cmd = pnode->cmd; - pix = pnode->pix; - - put_unit_image(punit, GTK_IMAGE(pix), pnode->height); - - g_signal_handlers_disconnect_matched(cmd, - G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, present_unit_callback, NULL); - - g_signal_handlers_disconnect_matched(cmd, - G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, present_unit_middle_callback, NULL); - - gtk_widget_set_tooltip_text(cmd, unit_description(punit)); - - g_signal_connect(cmd, "button_press_event", - G_CALLBACK(present_unit_callback), - GINT_TO_POINTER(punit->id)); - - g_signal_connect(cmd, "button_release_event", - G_CALLBACK(present_unit_middle_callback), - GINT_TO_POINTER(punit->id)); - - if (city_owner(pdialog->pcity) != client.conn.playing) { - gtk_widget_set_sensitive(cmd, FALSE); - } else { - gtk_widget_set_sensitive(cmd, TRUE); - } - - gtk_widget_show(pix); - gtk_widget_show(cmd); - } - i++; - } unit_list_iterate_end; - - buf = g_strdup_printf(_("Present units %d"), n); - gtk_frame_set_label(GTK_FRAME(pdialog->overview.present_units_frame), buf); - g_free(buf); -} - -/**********************************************************************//** - Updates the sensitivity of the prev and next buttons. - this does not need pdialog as a parameter, since it iterates - over all the open dialogs. - note: we still need the sensitivity code in create_city_dialog() - for the spied dialogs. -**************************************************************************/ -static void city_dialog_update_prev_next(void) -{ - int count = 0; - int city_number; - - if (client_is_global_observer()) { - return; /* Keep them insensitive as initially set */ - } - - city_number = city_list_size(client.conn.playing->cities); - - /* the first time, we see if all the city dialogs are open */ - dialog_list_iterate(dialog_list, pdialog) { - if (city_owner(pdialog->pcity) == client.conn.playing) { - count++; - } - } dialog_list_iterate_end; - - if (count == city_number) { /* all are open, shouldn't prev/next */ - dialog_list_iterate(dialog_list, pdialog) { - gtk_widget_set_sensitive(pdialog->prev_command, FALSE); - gtk_widget_set_sensitive(pdialog->next_command, FALSE); - } dialog_list_iterate_end; - } else { - dialog_list_iterate(dialog_list, pdialog) { - if (city_owner(pdialog->pcity) == client.conn.playing) { - gtk_widget_set_sensitive(pdialog->prev_command, TRUE); - gtk_widget_set_sensitive(pdialog->next_command, TRUE); - } - } dialog_list_iterate_end; - } -} - -/**********************************************************************//** - User clicked button from action area. -**************************************************************************/ -static void citydlg_response_callback(GtkDialog *dlg, gint response, - void *data) -{ - switch (response) { - case CDLGR_UNITS: - show_units_response(data); - break; - } -} - -/**********************************************************************//** - User has clicked show units -**************************************************************************/ -static void show_units_response(void *data) -{ - struct city_dialog *pdialog = (struct city_dialog *) data; - struct tile *ptile = pdialog->pcity->tile; - - if (unit_list_size(ptile->units)) { - unit_select_dialog_popup(ptile); - } -} - -/**********************************************************************//** - Set city menu position -**************************************************************************/ -static void city_menu_position(GtkMenu *menu, gint *x, gint *y, - gboolean *push_in, gpointer data) -{ - GtkWidget *widget; - GtkAllocation allocation; - gint xpos; - gint ypos; - - fc_assert_ret(GTK_IS_BUTTON(data)); - - widget = GTK_WIDGET(data); - - gtk_widget_get_allocation(widget, &allocation); - - gdk_window_get_origin(gtk_widget_get_window(widget), &xpos, &ypos); - - xpos += allocation.x + allocation.width/2; - ypos += allocation.y + allocation.height/2; - - *x = xpos; - *y = ypos; - *push_in = TRUE; -} - -/**********************************************************************//** - Destroy widget -callback -**************************************************************************/ -static void destroy_func(GtkWidget *w, gpointer data) -{ - gtk_widget_destroy(w); -} - -/**********************************************************************//** - Pop-up menu to change attributes of supported units -**************************************************************************/ -static gboolean supported_unit_callback(GtkWidget *w, GdkEventButton *ev, - gpointer data) -{ - GtkWidget *menu, *item; - struct city_dialog *pdialog; - struct city *pcity; - struct unit *punit = - player_unit_by_number(client_player(), (size_t) data); - - if (NULL != punit - && NULL != (pcity = game_city_by_number(punit->homecity)) - && NULL != (pdialog = get_city_dialog(pcity))) { - - if (ev->type != GDK_BUTTON_PRESS || ev->button == 2 || ev->button == 3 - || !can_client_issue_orders()) { - return FALSE; - } - - menu = pdialog->popup_menu; - - gtk_menu_popdown(GTK_MENU(menu)); - gtk_container_foreach(GTK_CONTAINER(menu), destroy_func, NULL); - - item = gtk_menu_item_new_with_mnemonic(_("Cen_ter")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_center_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - item = gtk_menu_item_new_with_mnemonic(_("_Activate unit")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_activate_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - item = gtk_menu_item_new_with_mnemonic(_("Activate unit, _close dialog")); - g_signal_connect(item, "activate", - G_CALLBACK(supported_unit_activate_close_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - item = gtk_menu_item_new_with_mnemonic(_("_Disband unit")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_disband_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - if (!unit_can_do_action(punit, ACTION_DISBAND_UNIT)) { - gtk_widget_set_sensitive(item, FALSE); - } - - gtk_widget_show_all(menu); - - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, - city_menu_position, w, ev->button, ev->time); - - - } - return TRUE; -} - -/**********************************************************************//** - Pop-up menu to change attributes of units, ex. change homecity. -**************************************************************************/ -static gboolean present_unit_callback(GtkWidget *w, GdkEventButton *ev, - gpointer data) -{ - GtkWidget *menu, *item; - struct city_dialog *pdialog; - struct city *pcity; - struct unit *punit = - player_unit_by_number(client_player(), (size_t) data); - - if (NULL != punit - && NULL != (pcity = tile_city(unit_tile(punit))) - && NULL != (pdialog = get_city_dialog(pcity))) { - - if (ev->type != GDK_BUTTON_PRESS || ev->button == 2 || ev->button == 3 - || !can_client_issue_orders()) { - return FALSE; - } - - menu = pdialog->popup_menu; - - gtk_menu_popdown(GTK_MENU(menu)); - gtk_container_foreach(GTK_CONTAINER(menu), destroy_func, NULL); - - item = gtk_menu_item_new_with_mnemonic(_("_Activate unit")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_activate_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - item = gtk_menu_item_new_with_mnemonic(_("Activate unit, _close dialog")); - g_signal_connect(item, "activate", - G_CALLBACK(present_unit_activate_close_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - item = gtk_menu_item_new_with_mnemonic(_("_Load unit")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_load_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - if (!unit_can_load(punit)) { - gtk_widget_set_sensitive(item, FALSE); - } - - item = gtk_menu_item_new_with_mnemonic(_("_Unload unit")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_unload_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - if (!can_unit_unload(punit, unit_transport_get(punit)) - || !can_unit_exist_at_tile(&(wld.map), punit, unit_tile(punit))) { - gtk_widget_set_sensitive(item, FALSE); - } - - item = gtk_menu_item_new_with_mnemonic(_("_Sentry unit")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_sentry_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - if (punit->activity == ACTIVITY_SENTRY - || !can_unit_do_activity(punit, ACTIVITY_SENTRY)) { - gtk_widget_set_sensitive(item, FALSE); - } - - item = gtk_menu_item_new_with_mnemonic(_("_Fortify unit")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_fortify_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - if (punit->activity == ACTIVITY_FORTIFYING - || !can_unit_do_activity(punit, ACTIVITY_FORTIFYING)) { - gtk_widget_set_sensitive(item, FALSE); - } - - item = gtk_menu_item_new_with_mnemonic(_("_Disband unit")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_disband_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - if (!unit_can_do_action(punit, ACTION_DISBAND_UNIT)) { - gtk_widget_set_sensitive(item, FALSE); - } - - item = gtk_menu_item_new_with_mnemonic( - action_id_name_translation(ACTION_HOME_CITY)); - g_signal_connect(item, "activate", - G_CALLBACK(unit_homecity_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_set_sensitive(item, can_unit_change_homecity_to(punit, pcity)); - - item = gtk_menu_item_new_with_mnemonic(_("U_pgrade unit")); - g_signal_connect(item, "activate", - G_CALLBACK(unit_upgrade_callback), - GINT_TO_POINTER(punit->id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - if (!can_client_issue_orders() - || NULL == can_upgrade_unittype(client.conn.playing, - unit_type_get(punit))) { - gtk_widget_set_sensitive(item, FALSE); - } - - gtk_widget_show_all(menu); - - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, - city_menu_position, w, ev->button, ev->time); - } - return TRUE; -} - -/**********************************************************************//** - If user middle-clicked on a unit, activate it and close dialog -**************************************************************************/ -static gboolean present_unit_middle_callback(GtkWidget *w, - GdkEventButton *ev, - gpointer data) -{ - struct city_dialog *pdialog; - struct city *pcity; - struct unit *punit = - player_unit_by_number(client_player(), (size_t) data); - - if (NULL != punit - && NULL != (pcity = tile_city(unit_tile(punit))) - && NULL != (pdialog = get_city_dialog(pcity)) - && can_client_issue_orders()) { - - if (ev->button == 3) { - unit_focus_set(punit); - } else if (ev->button == 2) { - unit_focus_set(punit); - close_city_dialog(pdialog); - } - } - - return TRUE; -} - -/**********************************************************************//** - If user middle-clicked on a unit, activate it and close dialog -**************************************************************************/ -static gboolean supported_unit_middle_callback(GtkWidget *w, - GdkEventButton *ev, - gpointer data) -{ - struct city_dialog *pdialog; - struct city *pcity; - struct unit *punit = - player_unit_by_number(client_player(), (size_t) data); - - if (NULL != punit - && NULL != (pcity = game_city_by_number(punit->homecity)) - && NULL != (pdialog = get_city_dialog(pcity)) - && can_client_issue_orders()) { - - if (ev->button == 3) { - unit_focus_set(punit); - } else if (ev->button == 2) { - unit_focus_set(punit); - close_city_dialog(pdialog); - } - } - - return TRUE; -} - -/**********************************************************************//** - User has requested centering to unit -**************************************************************************/ -static void unit_center_callback(GtkWidget *w, gpointer data) -{ - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL != punit) { - center_tile_mapcanvas(unit_tile(punit)); - } -} - -/**********************************************************************//** - User has requested unit activation -**************************************************************************/ -static void unit_activate_callback(GtkWidget *w, gpointer data) -{ - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL != punit) { - unit_focus_set(punit); - } -} - -/**********************************************************************//** - User has requested some supported unit to be activated and - city dialog to be closed -**************************************************************************/ -static void supported_unit_activate_close_callback(GtkWidget *w, - gpointer data) -{ - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL != punit) { - struct city *pcity = - player_city_by_number(client_player(), punit->homecity); - - unit_focus_set(punit); - if (NULL != pcity) { - struct city_dialog *pdialog = get_city_dialog(pcity); - - if (NULL != pdialog) { - close_city_dialog(pdialog); - } - } - } -} - -/**********************************************************************//** - User has requested some present unit to be activated and - city dialog to be closed -**************************************************************************/ -static void present_unit_activate_close_callback(GtkWidget *w, - gpointer data) -{ - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL != punit) { - struct city *pcity = tile_city(unit_tile(punit)); - - unit_focus_set(punit); - if (NULL != pcity) { - struct city_dialog *pdialog = get_city_dialog(pcity); - - if (NULL != pdialog) { - close_city_dialog(pdialog); - } - } - } -} - -/**********************************************************************//** - User has requested unit to be loaded to transport -**************************************************************************/ -static void unit_load_callback(GtkWidget *w, gpointer data) -{ - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL != punit) { - request_transport(punit, unit_tile(punit)); - } -} - -/**********************************************************************//** - User has requested unit to be unloaded from transport -**************************************************************************/ -static void unit_unload_callback(GtkWidget *w, gpointer data) -{ - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL != punit) { - request_unit_unload(punit); - } -} - -/**********************************************************************//** - User has requested unit to be sentried -**************************************************************************/ -static void unit_sentry_callback(GtkWidget *w, gpointer data) -{ - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL != punit) { - request_unit_sentry(punit); - } -} - -/**********************************************************************//** - User has requested unit to be fortified -**************************************************************************/ -static void unit_fortify_callback(GtkWidget *w, gpointer data) -{ - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL != punit) { - request_unit_fortify(punit); - } -} - -/**********************************************************************//** - User has requested unit to be disbanded -**************************************************************************/ -static void unit_disband_callback(GtkWidget *w, gpointer data) -{ - struct unit_list *punits; - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL == punit) { - return; - } - - punits = unit_list_new(); - unit_list_append(punits, punit); - popup_disband_dialog(punits); - unit_list_destroy(punits); -} - -/**********************************************************************//** - User has requested unit to change homecity to city where it - currently is -**************************************************************************/ -static void unit_homecity_callback(GtkWidget *w, gpointer data) -{ - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL != punit) { - request_unit_change_homecity(punit); - } -} - -/**********************************************************************//** - User has requested unit to be upgraded -**************************************************************************/ -static void unit_upgrade_callback(GtkWidget *w, gpointer data) -{ - struct unit_list *punits; - struct unit *punit = - player_unit_by_number(client_player(), (size_t)data); - - if (NULL == punit) { - return; - } - - punits = unit_list_new(); - unit_list_append(punits, punit); - popup_upgrade_dialog(punits); - unit_list_destroy(punits); -} - -/******** Callbacks for citizen bar, map funcs that are not update *******/ -/**********************************************************************//** - Somebody clicked our list of citizens. If they clicked a specialist - then change the type of him, else do nothing. -**************************************************************************/ -static gboolean citizens_callback(GtkWidget *w, GdkEventButton *ev, - gpointer data) -{ - struct city_dialog *pdialog = data; - struct city *pcity = pdialog->pcity; - int citnum, tlen, len; - - if (!can_client_issue_orders()) { - return FALSE; - } - - tlen = tileset_small_sprite_width(tileset); - len = (city_size_get(pcity) - 1) * pdialog->cwidth + tlen; - if (ev->x > len) { - /* no citizen that far to the right */ - return FALSE; - } - citnum = MIN(city_size_get(pcity) - 1, ev->x / pdialog->cwidth); - - city_rotate_specialist(pcity, citnum); - - return TRUE; -} - -/**********************************************************************//** - Set requested workertask -**************************************************************************/ -static void set_city_workertask(GtkWidget *w, gpointer data) -{ - enum unit_activity act = (enum unit_activity)GPOINTER_TO_INT(data); - struct city *pcity = workertask_req.owner; - struct tile *ptile = workertask_req.loc; - struct packet_worker_task task; - - task.city_id = pcity->id; - - if (act == ACTIVITY_LAST) { - task.tgt = -1; - task.want = 0; - } else { - enum extra_cause cause = activity_to_extra_cause(act); - enum extra_rmcause rmcause = activity_to_extra_rmcause(act); - struct extra_type *tgt; - - if (cause != EC_NONE) { - tgt = next_extra_for_tile(ptile, cause, city_owner(pcity), NULL); - } else if (rmcause != ERM_NONE) { - tgt = prev_extra_in_tile(ptile, rmcause, city_owner(pcity), NULL); - } else { - tgt = NULL; - } - - if (tgt == NULL) { - struct terrain *pterr = tile_terrain(ptile); - - if ((act != ACTIVITY_TRANSFORM - || pterr->transform_result == NULL || pterr->transform_result == pterr) - && (act != ACTIVITY_CULTIVATE || pterr->cultivate_result == NULL) - && (act != ACTIVITY_PLANT || pterr->plant_result == NULL)) { - /* No extra to order */ - output_window_append(ftc_client, _("There's no suitable extra to order.")); - - return; - } - - task.tgt = -1; - } else { - task.tgt = extra_index(tgt); - } - - task.want = 100; - } - - task.tile_id = ptile->index; - task.activity = act; - - send_packet_worker_task(&client.conn, &task); -} - -/**********************************************************************//** - Destroy workertask dlg -**************************************************************************/ -static void workertask_dlg_destroy(GtkWidget *w, gpointer data) -{ - is_showing_workertask_dialog = FALSE; -} - -/**********************************************************************//** - Open dialog for setting worker task -**************************************************************************/ -static void popup_workertask_dlg(struct city *pcity, struct tile *ptile) -{ - if (!is_showing_workertask_dialog) { - GtkWidget *shl; - struct terrain *pterr = tile_terrain(ptile); - struct universal for_terr = { .kind = VUT_TERRAIN, - .value = { .terrain = pterr }}; - struct worker_task *ptask; - - is_showing_workertask_dialog = TRUE; - workertask_req.owner = pcity; - workertask_req.loc = ptile; - - shl = choice_dialog_start(GTK_WINDOW(toplevel), - _("What Action to Request"), - _("Select autosettler activity:")); - - ptask = worker_task_list_get(pcity->task_reqs, 0); - if (ptask != NULL) { - choice_dialog_add(shl, _("Clear request"), - G_CALLBACK(set_city_workertask), - GINT_TO_POINTER(ACTIVITY_LAST), FALSE, NULL); - } - - if (pterr->mining_time != 0 - && action_id_univs_not_blocking(ACTION_MINE, NULL, &for_terr)) { - choice_dialog_add(shl, _("Mine"), - G_CALLBACK(set_city_workertask), - GINT_TO_POINTER(ACTIVITY_MINE), FALSE, NULL); - } - if (pterr->plant_result != NULL - && action_id_univs_not_blocking(ACTION_PLANT, - NULL, &for_terr)) { - choice_dialog_add(shl, _("Plant"), - G_CALLBACK(set_city_workertask), - GINT_TO_POINTER(ACTIVITY_PLANT), FALSE, NULL); - } - if (pterr->irrigation_time != 0 - && action_id_univs_not_blocking(ACTION_IRRIGATE, NULL, &for_terr)) { - choice_dialog_add(shl, _("Irrigate"), - G_CALLBACK(set_city_workertask), - GINT_TO_POINTER(ACTIVITY_IRRIGATE), FALSE, NULL); - } - if (pterr->cultivate_result != NULL - && action_id_univs_not_blocking(ACTION_CULTIVATE, - NULL, &for_terr)) { - choice_dialog_add(shl, _("Cultivate"), - G_CALLBACK(set_city_workertask), - GINT_TO_POINTER(ACTIVITY_CULTIVATE), FALSE, NULL); - } - if (next_extra_for_tile(ptile, EC_ROAD, city_owner(pcity), NULL) != NULL) { - choice_dialog_add(shl, _("Road"), - G_CALLBACK(set_city_workertask), - GINT_TO_POINTER(ACTIVITY_GEN_ROAD), FALSE, NULL); - } - if (pterr->transform_result != pterr && pterr->transform_result != NULL - && action_id_univs_not_blocking(ACTION_TRANSFORM_TERRAIN, - NULL, &for_terr)) { - choice_dialog_add(shl, _("Transform"), - G_CALLBACK(set_city_workertask), - GINT_TO_POINTER(ACTIVITY_TRANSFORM), FALSE, NULL); - } - if (prev_extra_in_tile(ptile, ERM_CLEANPOLLUTION, - city_owner(pcity), NULL) != NULL) { - choice_dialog_add(shl, _("Clean Pollution"), - G_CALLBACK(set_city_workertask), - GINT_TO_POINTER(ACTIVITY_POLLUTION), FALSE, NULL); - } - if (prev_extra_in_tile(ptile, ERM_CLEANFALLOUT, - city_owner(pcity), NULL) != NULL) { - choice_dialog_add(shl, _("Clean Fallout"), - G_CALLBACK(set_city_workertask), - GINT_TO_POINTER(ACTIVITY_FALLOUT), FALSE, NULL); - } - - choice_dialog_add(shl, GTK_STOCK_CANCEL, 0, 0, FALSE, NULL); - choice_dialog_end(shl); - - g_signal_connect(shl, "destroy", G_CALLBACK(workertask_dlg_destroy), - NULL); - } -} - -/**********************************************************************//** - User has pressed button on citymap -**************************************************************************/ -static gboolean button_down_citymap(GtkWidget *w, GdkEventButton *ev, - gpointer data) -{ - struct city_dialog *pdialog = data; - int canvas_x, canvas_y, city_x, city_y; - - if (!can_client_issue_orders()) { - return FALSE; - } - - canvas_x = ev->x * (double)canvas_width / (double)CITYMAP_WIDTH; - canvas_y = ev->y * (double)canvas_height / (double)CITYMAP_HEIGHT; - - if (canvas_to_city_pos(&city_x, &city_y, - city_map_radius_sq_get(pdialog->pcity), - canvas_x, canvas_y)) { - if (ev->button == 1) { - city_toggle_worker(pdialog->pcity, city_x, city_y); - } else if (ev->button == 3) { - struct city *pcity = pdialog->pcity; - - popup_workertask_dlg(pdialog->pcity, - city_map_to_tile(pcity->tile, city_map_radius_sq_get(pcity), - city_x, city_y)); - } - } - - return TRUE; -} - -/**********************************************************************//** - Set map canvas to be drawn -**************************************************************************/ -static void draw_map_canvas(struct city_dialog *pdialog) -{ - gtk_widget_queue_draw(pdialog->overview.map_canvas.darea); - if (pdialog->happiness.map_canvas.darea) { /* in case of spy */ - gtk_widget_queue_draw(pdialog->happiness.map_canvas.darea); - } -} - -/************** Callbacks for Buy, Change, Sell, Worklist ****************/ -/**********************************************************************//** - User has answered buy cost dialog -**************************************************************************/ -static void buy_callback_response(GtkWidget *w, gint response, gpointer data) -{ - struct city_dialog *pdialog = data; - - if (response == GTK_RESPONSE_YES) { - city_buy_production(pdialog->pcity); - } - gtk_widget_destroy(w); -} - -/**********************************************************************//** - User has clicked buy-button -**************************************************************************/ -static void buy_callback(GtkWidget *w, gpointer data) -{ - GtkWidget *shell; - struct city_dialog *pdialog = data; - const char *name = city_production_name_translation(pdialog->pcity); - int value = pdialog->pcity->client.buy_cost; - char buf[1024]; - - if (!can_client_issue_orders()) { - return; - } - - fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.", - "Treasury contains %d gold.", - client_player()->economic.gold), - client_player()->economic.gold); - - if (value <= client_player()->economic.gold) { - shell = gtk_message_dialog_new(NULL, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */ - PL_("Buy %s for %d gold?\n%s", - "Buy %s for %d gold?\n%s", value), - name, value, buf); - setup_dialog(shell, pdialog->shell); - gtk_window_set_title(GTK_WINDOW(shell), _("Buy It!")); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_NO); - g_signal_connect(shell, "response", G_CALLBACK(buy_callback_response), - pdialog); - gtk_window_present(GTK_WINDOW(shell)); - } else { - shell = gtk_message_dialog_new(NULL, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, - /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */ - PL_("%s costs %d gold.\n%s", - "%s costs %d gold.\n%s", value), - name, value, buf); - setup_dialog(shell, pdialog->shell); - gtk_window_set_title(GTK_WINDOW(shell), _("Buy It!")); - g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy), - NULL); - gtk_window_present(GTK_WINDOW(shell)); - } -} - -/**********************************************************************//** - Callback for the dropdown production menu. -**************************************************************************/ -static void change_production_callback(GtkComboBox *combo, - struct city_dialog *pdialog) -{ - GtkTreeIter iter; - - if (can_client_issue_orders() - && gtk_combo_box_get_active_iter(combo, &iter)) { - cid id; - struct universal univ; - - gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, 2, &id, -1); - univ = cid_production(id); - city_change_production(pdialog->pcity, &univ); - } -} - -/**********************************************************************//** - User has clicked sell-button -**************************************************************************/ -static void sell_callback(struct impr_type *pimprove, gpointer data) -{ - GtkWidget *shl; - struct city_dialog *pdialog = (struct city_dialog *) data; - pdialog->sell_id = improvement_number(pimprove); - int price; - - if (!can_client_issue_orders()) { - return; - } - - if (test_player_sell_building_now(client.conn.playing, pdialog->pcity, - pimprove) != TR_SUCCESS) { - return; - } - - price = impr_sell_gold(pimprove); - shl = gtk_message_dialog_new(NULL, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, - PL_("Sell %s for %d gold?", - "Sell %s for %d gold?", price), - city_improvement_name_translation(pdialog->pcity, pimprove), price); - setup_dialog(shl, pdialog->shell); - pdialog->sell_shell = shl; - - gtk_window_set_title(GTK_WINDOW(shl), _("Sell It!")); - gtk_window_set_position(GTK_WINDOW(shl), GTK_WIN_POS_CENTER_ON_PARENT); - - g_signal_connect(shl, "response", - G_CALLBACK(sell_callback_response), pdialog); - - gtk_window_present(GTK_WINDOW(shl)); -} - -/**********************************************************************//** - User has responded to sell price dialog -**************************************************************************/ -static void sell_callback_response(GtkWidget *w, gint response, gpointer data) -{ - struct city_dialog *pdialog = data; - - if (response == GTK_RESPONSE_YES) { - city_sell_improvement(pdialog->pcity, pdialog->sell_id); - } - gtk_widget_destroy(w); - - pdialog->sell_shell = NULL; -} - -/**********************************************************************//** - This is here because it's closely related to the sell stuff -**************************************************************************/ -static void impr_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - GdkWindow *win; - GdkDeviceManager *manager; - GdkModifierType mask; - struct impr_type *pimprove; - - model = gtk_tree_view_get_model(view); - - if (!gtk_tree_model_get_iter(model, &it, path)) { - return; - } - - gtk_tree_model_get(model, &it, 0, &pimprove, -1); - - win = gdk_get_default_root_window(); - manager = gdk_display_get_device_manager(gdk_window_get_display(win)); - - gdk_window_get_device_position(win, - gdk_device_manager_get_client_pointer(manager), - NULL, NULL, &mask); - - if (!(mask & GDK_CONTROL_MASK)) { - sell_callback(pimprove, data); - } else { - if (is_great_wonder(pimprove)) { - popup_help_dialog_typed(improvement_name_translation(pimprove), HELP_WONDER); - } else { - popup_help_dialog_typed(improvement_name_translation(pimprove), HELP_IMPROVEMENT); - } - } -} - -/************ Callbacks for stuff on the Misc. Settings page *************/ -/**********************************************************************//** - Called when Rename button pressed -**************************************************************************/ -static void rename_callback(GtkWidget *w, gpointer data) -{ - struct city_dialog *pdialog; - - pdialog = (struct city_dialog *) data; - - pdialog->rename_shell = input_dialog_create(GTK_WINDOW(pdialog->shell), - /* "shellrenamecity" */ - _("Rename City"), - _("What should we rename the city to?"), - city_name_get(pdialog->pcity), - rename_popup_callback, pdialog); -} - -/**********************************************************************//** - Called when user has finished with "Rename City" popup -**************************************************************************/ -static void rename_popup_callback(gpointer data, gint response, - const char *input) -{ - struct city_dialog *pdialog = data; - - if (pdialog) { - if (response == GTK_RESPONSE_OK) { - city_rename(pdialog->pcity, input); - } /* else CANCEL or DELETE_EVENT */ - - pdialog->rename_shell = NULL; - } -} - -/**********************************************************************//** - Sets which page will be set on reopen of dialog -**************************************************************************/ -static void misc_whichtab_callback(GtkWidget *w, gpointer data) -{ - new_dialog_def_page = GPOINTER_TO_INT(data); -} - -/**********************************************************************//** - City options callbacks -**************************************************************************/ -static void cityopt_callback(GtkWidget *w, gpointer data) -{ - struct city_dialog *pdialog = (struct city_dialog *) data; - - if (!can_client_issue_orders()) { - return; - } - - if (!pdialog->misc.block_signal) { - struct city *pcity = pdialog->pcity; - bv_city_options new_options; - - fc_assert(CITYO_LAST == 3); - - BV_CLR_ALL(new_options); - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.disband_on_settler))) { - BV_SET(new_options, CITYO_DISBAND); - } - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.new_citizens_radio[1]))) { - BV_SET(new_options, CITYO_SCIENCE_SPECIALISTS); - } - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.new_citizens_radio[2]))) { - BV_SET(new_options, CITYO_GOLD_SPECIALISTS); - } - - dsend_packet_city_options_req(&client.conn, pcity->id,new_options); - } -} - -/**********************************************************************//** - Refresh the city options (auto_[land, air, sea, helicopter] and - disband-is-size-1) in the misc page. -**************************************************************************/ -static void set_cityopt_values(struct city_dialog *pdialog) -{ - struct city *pcity = pdialog->pcity; - - pdialog->misc.block_signal = 1; - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdialog->misc.disband_on_settler), - is_city_option_set(pcity, CITYO_DISBAND)); - - if (is_city_option_set(pcity, CITYO_SCIENCE_SPECIALISTS)) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON - (pdialog->misc.new_citizens_radio[1]), TRUE); - } else if (is_city_option_set(pcity, CITYO_GOLD_SPECIALISTS)) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON - (pdialog->misc.new_citizens_radio[2]), TRUE); - } else { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON - (pdialog->misc.new_citizens_radio[0]), TRUE); - } - pdialog->misc.block_signal = 0; -} - -/******************** Callbacks for: Close, Prev, Next. ******************/ -/**********************************************************************//** - User has clicked rename city-button -**************************************************************************/ -static void close_callback(GtkWidget *w, gpointer data) -{ - close_city_dialog((struct city_dialog *) data); -} - -/**********************************************************************//** - User has closed rename city dialog -**************************************************************************/ -static void city_destroy_callback(GtkWidget *w, gpointer data) -{ - struct city_dialog *pdialog; - - pdialog = (struct city_dialog *) data; - - gtk_widget_hide(pdialog->shell); - - if (game.info.citizen_nationality) { - citizens_dialog_close(pdialog->pcity); - } - close_happiness_dialog(pdialog->pcity); - close_cma_dialog(pdialog->pcity); - - /* Save size of the city dialog. */ - GUI_GTK_OPTION(citydlg_xsize) - = CLIP(GUI_GTK3_CITYDLG_MIN_XSIZE, - gtk_widget_get_allocated_width(pdialog->shell), - GUI_GTK3_CITYDLG_MAX_XSIZE); - GUI_GTK_OPTION(citydlg_ysize) - = CLIP(GUI_GTK3_CITYDLG_MIN_XSIZE, - gtk_widget_get_allocated_height(pdialog->shell), - GUI_GTK3_CITYDLG_MAX_XSIZE); - - last_page - = gtk_notebook_get_current_page(GTK_NOTEBOOK(pdialog->notebook)); - - if (pdialog->popup_menu) { - gtk_widget_destroy(pdialog->popup_menu); - } - - dialog_list_remove(dialog_list, pdialog); - - unit_node_vector_free(&pdialog->overview.supported_units); - unit_node_vector_free(&pdialog->overview.present_units); - - if (pdialog->buy_shell) { - gtk_widget_destroy(pdialog->buy_shell); - } - if (pdialog->sell_shell) { - gtk_widget_destroy(pdialog->sell_shell); - } - if (pdialog->rename_shell) { - gtk_widget_destroy(pdialog->rename_shell); - } - - cairo_surface_destroy(pdialog->map_canvas_store_unscaled); - cairo_surface_destroy(pdialog->citizen_surface); - - free(pdialog); - - /* need to do this every time a new dialog is closed. */ - city_dialog_update_prev_next(); -} - -/**********************************************************************//** - Close city dialog -**************************************************************************/ -static void close_city_dialog(struct city_dialog *pdialog) -{ - gtk_widget_destroy(pdialog->shell); -} - -/**********************************************************************//** - Callback for the prev/next buttons. Switches to the previous/next - city. -**************************************************************************/ -static void switch_city_callback(GtkWidget *w, gpointer data) -{ - struct city_dialog *pdialog = (struct city_dialog *) data; - int i, j, dir, size; - struct city *new_pcity = NULL; - - if (client_is_global_observer()) { - return; - } - - size = city_list_size(client.conn.playing->cities); - - fc_assert_ret(city_dialogs_have_been_initialised); - fc_assert_ret(size >= 1); - fc_assert_ret(city_owner(pdialog->pcity) == client.conn.playing); - - if (size == 1) { - return; - } - - /* dir = 1 will advance to the city, dir = -1 will get previous */ - if (w == pdialog->next_command) { - dir = 1; - } else if (w == pdialog->prev_command) { - dir = -1; - } else { - /* Always fails. */ - fc_assert_ret(w == pdialog->next_command - || w == pdialog->prev_command); - dir = 1; - } - - for (i = 0; i < size; i++) { - if (pdialog->pcity == city_list_get(client.conn.playing->cities, i)) { - break; - } - } - - fc_assert_ret(i < size); - - for (j = 1; j < size; j++) { - struct city *other_pcity = city_list_get(client.conn.playing->cities, - (i + dir * j + size) % size); - struct city_dialog *other_pdialog = get_city_dialog(other_pcity); - - fc_assert_ret(other_pdialog != pdialog); - if (!other_pdialog) { - new_pcity = other_pcity; - break; - } - } - - if (!new_pcity) { - /* Every other city has an open city dialog. */ - return; - } - - /* cleanup happiness dialog */ - if (game.info.citizen_nationality) { - citizens_dialog_close(pdialog->pcity); - } - close_happiness_dialog(pdialog->pcity); - - pdialog->pcity = new_pcity; - - /* reinitialize happiness, and cma dialogs */ - if (game.info.citizen_nationality) { - gtk_container_add(GTK_CONTAINER(pdialog->happiness.citizens), - citizens_dialog_display(pdialog->pcity)); - } - gtk_container_add(GTK_CONTAINER(pdialog->happiness.widget), - get_top_happiness_display(pdialog->pcity, low_citydlg, pdialog->shell)); - if (!client_is_observer()) { - fc_assert(pdialog->cma_editor != NULL); - pdialog->cma_editor->pcity = new_pcity; - } - - reset_city_worklist(pdialog->production.worklist, pdialog->pcity); - - can_slide = FALSE; - center_tile_mapcanvas(pdialog->pcity->tile); - can_slide = TRUE; - if (!client_is_observer()) { - set_cityopt_values(pdialog); /* need not be in real_city_dialog_refresh */ - } - - real_city_dialog_refresh(pdialog->pcity); - - /* recenter the city map(s) */ - city_dialog_map_recenter(pdialog->overview.map_canvas.sw); - if (pdialog->happiness.map_canvas.sw) { - city_dialog_map_recenter(pdialog->happiness.map_canvas.sw); - } -} diff --git a/client/gui-gtk-3.0/citydlg.h b/client/gui-gtk-3.0/citydlg.h deleted file mode 100644 index 0170360bfd..0000000000 --- a/client/gui-gtk-3.0/citydlg.h +++ /dev/null @@ -1,20 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__CITYDLG_H -#define FC__CITYDLG_H - -#include "citydlg_g.h" - -void reset_city_dialogs(void); - -#endif /* FC__CITYDLG_H */ diff --git a/client/gui-gtk-3.0/cityrep.c b/client/gui-gtk-3.0/cityrep.c deleted file mode 100644 index 7560e93f84..0000000000 --- a/client/gui-gtk-3.0/cityrep.c +++ /dev/null @@ -1,2106 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "city.h" -#include "game.h" -#include "packets.h" -#include "unit.h" - -/* client/agents */ -#include "cma_fec.h" - -/* client */ -#include "citydlg_common.h" -#include "cityrepdata.h" -#include "client_main.h" -#include "climisc.h" -#include "global_worklist.h" -#include "mapview_common.h" -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "citydlg.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapview.h" -#include "mapctrl.h" /* is_city_hilited() */ -#include "optiondlg.h" -#include "repodlgs.h" - -#include "cityrep.h" - -#define NEG_VAL(x) ((x)<0 ? (x) : (-x)) - -/* Some versions of gcc have problems with negative values here (PR#39722). */ -#define CMA_NONE (10000) -#define CMA_CUSTOM (10001) - -struct sell_data { - int count; /* Number of cities. */ - int gold; /* Amount of gold. */ - const struct impr_type *target; /* The target for selling. */ -}; - -enum city_operation_type { - CO_CHANGE, CO_LAST, CO_NEXT, CO_FIRST, CO_NEXT_TO_LAST, CO_SELL, CO_NONE -}; - -/******************************************************************/ -static void create_city_report_dialog(bool make_modal); - -static void city_activated_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data); - -static void city_command_callback(struct gui_dialog *dlg, int response, - gpointer data); - -static void city_selection_changed_callback(GtkTreeSelection *selection); -static void city_clear_worklist_callback(GtkMenuItem *item, gpointer data); -static void update_total_buy_cost(void); - -static void create_select_menu(GtkWidget *item); -static void create_change_menu(GtkWidget *item); -static void create_last_menu(GtkWidget *item); -static void create_first_menu(GtkWidget *item); -static void create_next_menu(GtkWidget *item); -static void create_next_to_last_menu(GtkWidget *item); -static void create_sell_menu(GtkWidget *item); - -static struct gui_dialog *city_dialog_shell = NULL; - -enum { - CITY_CENTER = 1, CITY_POPUP, CITY_BUY -}; - -static GtkWidget *city_view; -static GtkTreeSelection *city_selection; -static GtkListStore *city_model; -#define CRD_COL_CITY_ID (0 + NUM_CREPORT_COLS) - -static void popup_select_menu(GtkMenuShell *menu, gpointer data); -static void popup_change_menu(GtkMenuShell *menu, gpointer data); -static void popup_last_menu(GtkMenuShell *menu, gpointer data); -static void popup_first_menu(GtkMenuShell *menu, gpointer data); -static void popup_next_menu(GtkMenuShell *menu, gpointer data); -static void popup_next_to_last_menu(GtkMenuShell *menu, gpointer data); - -static void recreate_sell_menu(void); - -static GtkWidget *city_center_command; -static GtkWidget *city_popup_command; -static GtkWidget *city_buy_command; -static GtkWidget *city_production_command; -static GtkWidget *city_governor_command; -static GtkWidget *city_sell_command; -static GtkWidget *city_total_buy_cost_label; - -static GtkWidget *change_improvements_item; -static GtkWidget *change_units_item; -static GtkWidget *change_wonders_item; - -static GtkWidget *last_improvements_item; -static GtkWidget *last_units_item; -static GtkWidget *last_wonders_item; - -static GtkWidget *first_improvements_item; -static GtkWidget *first_units_item; -static GtkWidget *first_wonders_item; - -static GtkWidget *next_improvements_item; -static GtkWidget *next_units_item; -static GtkWidget *next_wonders_item; - -static GtkWidget *next_to_last_improvements_item; -static GtkWidget *next_to_last_units_item; -static GtkWidget *next_to_last_wonders_item; - -static GtkWidget *select_island_item; - -static GtkWidget *select_bunit_item; -static GtkWidget *select_bimprovement_item; -static GtkWidget *select_bwonder_item; - -static GtkWidget *select_supported_item; -static GtkWidget *select_present_item; -static GtkWidget *select_built_improvements_item; -static GtkWidget *select_built_wonders_item; - -static GtkWidget *select_improvements_item; -static GtkWidget *select_units_item; -static GtkWidget *select_wonders_item; -static GtkWidget *select_cma_item; - -static int city_dialog_shell_is_modal; - -bool select_menu_cached; - -/************************************************************************//** - Return text line for the column headers for the city report -****************************************************************************/ -static void get_city_table_header(char **text, int n) -{ - struct city_report_spec *spec; - int i; - - for (i = 0, spec = city_report_specs; i < NUM_CREPORT_COLS; i++, spec++) { - fc_snprintf(text[i], n, "%*s\n%*s", - NEG_VAL(spec->width), spec->title1 ? spec->title1 : "", - NEG_VAL(spec->width), spec->title2 ? spec->title2 : ""); - } -} - -/**************************************************************************** - CITY REPORT DIALOG -****************************************************************************/ - -/************************************************************************//** - Returns a new tree model for the city report. -****************************************************************************/ -static GtkListStore *city_report_dialog_store_new(void) -{ - GType model_types[NUM_CREPORT_COLS + 1]; - gint i; - - /* City report data. */ - for (i = 0; i < NUM_CREPORT_COLS; i++) { - model_types[i] = G_TYPE_STRING; - } - - /* Specific gtk client data. */ - model_types[i++] = G_TYPE_INT; /* CRD_COL_CITY_ID */ - - return gtk_list_store_newv(i, model_types); -} - -/************************************************************************//** - Set the values of the iterator. -****************************************************************************/ -static void city_model_set(GtkListStore *store, GtkTreeIter *iter, - struct city *pcity) -{ - struct city_report_spec *spec; - char buf[64]; - gint i; - - for (i = 0; i < NUM_CREPORT_COLS; i++) { - spec = city_report_specs + i; - fc_snprintf(buf, sizeof(buf), "%*s", NEG_VAL(spec->width), - spec->func(pcity, spec->data)); - gtk_list_store_set(store, iter, i, buf, -1); - } - gtk_list_store_set(store, iter, CRD_COL_CITY_ID, pcity->id, -1); -} - -/************************************************************************//** - Set the values of the iterator. -****************************************************************************/ -static struct city *city_model_get(GtkTreeModel *model, GtkTreeIter *iter) -{ - struct city *pcity; - int id; - - gtk_tree_model_get(model, iter, CRD_COL_CITY_ID, &id, -1); - pcity = game_city_by_number(id); - return ((NULL != pcity - && client_has_player() - && city_owner(pcity) != client_player()) - ? NULL : pcity); -} - -/************************************************************************//** - Return TRUE if 'iter' has been set to the city row. -****************************************************************************/ -static gboolean city_model_find(GtkTreeModel *model, GtkTreeIter *iter, - const struct city *pcity) -{ - const int searched = pcity->id; - int id; - - if (gtk_tree_model_get_iter_first(model, iter)) { - do { - gtk_tree_model_get(model, iter, CRD_COL_CITY_ID, &id, -1); - if (searched == id) { - return TRUE; - } - } while (gtk_tree_model_iter_next(model, iter)); - } - return FALSE; -} - -/************************************************************************//** - Fill the model with the current configuration. -****************************************************************************/ -static void city_model_fill(GtkListStore *store, - GtkTreeSelection *selection, GHashTable *select) -{ - GtkTreeIter iter; - - if (client_has_player()) { - city_list_iterate(client_player()->cities, pcity) { - gtk_list_store_append(store, &iter); - city_model_set(store, &iter, pcity); - if (NULL != select - && g_hash_table_remove(select, GINT_TO_POINTER(pcity->id))) { - gtk_tree_selection_select_iter(selection, &iter); - } - } city_list_iterate_end; - } else { - /* Global observer case. */ - cities_iterate(pcity) { - gtk_list_store_append(store, &iter); - city_model_set(store, &iter, pcity); - if (NULL != select - && g_hash_table_remove(select, GINT_TO_POINTER(pcity->id))) { - gtk_tree_selection_select_iter(selection, &iter); - } - } cities_iterate_end; - } -} - -/************************************************************************//** - Popup the city report dialog, and optionally raise it. -****************************************************************************/ -void city_report_dialog_popup(bool raise) -{ - if (!city_dialog_shell) { - city_dialog_shell_is_modal = FALSE; - - create_city_report_dialog(FALSE); - - select_menu_cached = FALSE; - } - - gui_dialog_present(city_dialog_shell); - hilite_cities_from_canvas(); - if (raise) { - gui_dialog_raise(city_dialog_shell); - } -} - -/************************************************************************//** - Closes the city report dialog. -****************************************************************************/ -void city_report_dialog_popdown(void) -{ - if (city_dialog_shell) { - gui_dialog_destroy(city_dialog_shell); - } -} - -/************************************************************************//** - Make submenu listing possible build targets -****************************************************************************/ -static void append_impr_or_unit_to_menu_item(GtkMenuItem *parent_item, - bool append_units, - bool append_wonders, - enum city_operation_type - city_operation, - TestCityFunc test_func, - GCallback callback, - int size) -{ - GtkWidget *menu; - struct universal targets[MAX_NUM_PRODUCTION_TARGETS]; - struct item items[MAX_NUM_PRODUCTION_TARGETS]; - int i, item, targets_used; - char *row[4]; - char buf[4][64]; - - GtkSizeGroup *group[3]; - const char *markup[3] = { - "weight=\"bold\"", - "", - "" - }; - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(parent_item, menu); - - if (city_operation != CO_NONE) { - GPtrArray *selected; - ITree it; - int num_selected = 0; - GtkTreeModel *model = GTK_TREE_MODEL(city_model); - struct city **data; - - selected = g_ptr_array_sized_new(size); - - for (itree_begin(model, &it); !itree_end(&it); itree_next(&it)) { - struct city *pcity; - - if (!itree_is_selected(city_selection, &it) - || !(pcity = city_model_get(model, TREE_ITER_PTR(it)))) { - continue; - } - - g_ptr_array_add(selected, pcity); - num_selected++; - } - - data = (struct city **)g_ptr_array_free(selected, FALSE); - targets_used - = collect_production_targets(targets, data, num_selected, append_units, - append_wonders, TRUE, test_func); - g_free(data); - } else { - targets_used = collect_production_targets(targets, NULL, 0, append_units, - append_wonders, FALSE, - test_func); - } - - name_and_sort_items(targets, targets_used, items, - city_operation != CO_NONE, NULL); - - for (i = 0; i < 4; i++) { - row[i] = buf[i]; - } - - g_object_set_data(G_OBJECT(menu), "freeciv_test_func", test_func); - g_object_set_data(G_OBJECT(menu), "freeciv_city_operation", - GINT_TO_POINTER(city_operation)); - - for (i = 0; i < 3; i++) { - group[i] = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); - } - - for (item = 0; item < targets_used; item++) { - struct universal target = items[item].item; - GtkWidget *menu_item, *hbox, *label; - char txt[256]; - - get_city_dialog_production_row(row, sizeof(buf[0]), &target, NULL); - - menu_item = gtk_menu_item_new(); - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 18); - gtk_container_add(GTK_CONTAINER(menu_item), hbox); - - for (i = 0; i < 3; i++) { - if (row[i][0] == '\0') { - continue; - } - - if (city_operation == CO_SELL && i != 0) { - continue; - } - - fc_snprintf(txt, ARRAY_SIZE(txt), "%s", - markup[i], row[i]); - - label = gtk_label_new(NULL); - gtk_label_set_markup(GTK_LABEL(label), txt); - - switch (i) { - case 0: - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - break; - case 2: - gtk_widget_set_halign(label, GTK_ALIGN_END); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - break; - default: - break; - } - - gtk_container_add(GTK_CONTAINER(hbox), label); - gtk_size_group_add_widget(group[i], label); - } - - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - g_signal_connect(menu_item, "activate", callback, - GINT_TO_POINTER(cid_encode(target))); - } - - for (i = 0; i < 3; i++) { - g_object_unref(group[i]); - } - - gtk_widget_show_all(menu); - - gtk_widget_set_sensitive(GTK_WIDGET(parent_item), (targets_used > 0)); -} - -/************************************************************************//** - Change the production of one single selected city. -****************************************************************************/ -static void impr_or_unit_iterate(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - struct universal target = cid_decode(GPOINTER_TO_INT(data)); - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity) { - city_change_production(pcity, &target); - } -} - -/************************************************************************//** - Called by select_impr_or_unit_callback for each city that is selected in - the city list dialog to have a object appended to the worklist. Sends a - packet adding the item to the end of the worklist. -****************************************************************************/ -static void worklist_last_impr_or_unit_iterate(GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - struct universal target = cid_decode(GPOINTER_TO_INT(data)); - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity) { - (void) city_queue_insert(pcity, -1, &target); - } - /* perhaps should warn the user if not successful? */ -} - -/************************************************************************//** - Called by select_impr_or_unit_callback for each city that is selected in - the city list dialog to have a object inserted first to the worklist. - Sends a packet adding the current production to the first place after the - current production of the worklist. Then changes the production to the - requested item. -****************************************************************************/ -static void worklist_first_impr_or_unit_iterate(GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - struct universal target = cid_decode(GPOINTER_TO_INT(data)); - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity) { - (void) city_queue_insert(pcity, 0, &target); - } - /* perhaps should warn the user if not successful? */ -} - -/************************************************************************//** - Called by select_impr_or_unit_callback for each city that is selected in - the city list dialog to have a object added next to the worklist. Sends a - packet adding the item to the first place after the current production of - the worklist. -****************************************************************************/ -static void worklist_next_impr_or_unit_iterate(GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - struct universal target = cid_decode(GPOINTER_TO_INT(data)); - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity) { - (void) city_queue_insert(pcity, 1, &target); - } - /* perhaps should warn the user if not successful? */ -} - -/************************************************************************//** - Called by select_impr_or_unit_callback for each city that is selected in - the city list dialog to have an object added before the last position in - the worklist. -****************************************************************************/ -static void worklist_next_to_last_impr_or_unit_iterate(GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - struct universal target = cid_decode(GPOINTER_TO_INT(data)); - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity) { - city_queue_insert(pcity, worklist_length(&pcity->worklist), &target); - } -} - -/************************************************************************//** - Iterate the cities going to sell. -****************************************************************************/ -static void sell_impr_iterate(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - struct sell_data *sd = (struct sell_data *) data; - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity - && !pcity->did_sell - && city_has_building(pcity, sd->target)) { - sd->count++; - sd->gold += impr_sell_gold(sd->target); - city_sell_improvement(pcity, improvement_number(sd->target)); - } -} - -/************************************************************************//** - Some build target, either improvement or unit, has been selected from - some menu. -****************************************************************************/ -static void select_impr_or_unit_callback(GtkWidget *wdg, gpointer data) -{ - struct universal target = cid_decode(GPOINTER_TO_INT(data)); - GObject *parent = G_OBJECT(gtk_widget_get_parent(wdg)); - TestCityFunc test_func = g_object_get_data(parent, "freeciv_test_func"); - enum city_operation_type city_operation = - GPOINTER_TO_INT(g_object_get_data(parent, "freeciv_city_operation")); - - /* if this is not a city operation: */ - if (city_operation == CO_NONE) { - GtkTreeModel *model = GTK_TREE_MODEL(city_model); - ITree it; - - gtk_tree_selection_unselect_all(city_selection); - for (itree_begin(model, &it); !itree_end(&it); itree_next(&it)) { - struct city *pcity = city_model_get(model, TREE_ITER_PTR(it)); - - if (NULL != pcity && test_func(pcity, &target)) { - itree_select(city_selection, &it); - } - } - } else { - GtkTreeSelectionForeachFunc foreach_func; - - connection_do_buffer(&client.conn); - switch (city_operation) { - case CO_LAST: - gtk_tree_selection_selected_foreach(city_selection, - worklist_last_impr_or_unit_iterate, - GINT_TO_POINTER(cid_encode(target))); - break; - case CO_CHANGE: - gtk_tree_selection_selected_foreach(city_selection, - impr_or_unit_iterate, - GINT_TO_POINTER(cid_encode(target))); - break; - case CO_FIRST: - gtk_tree_selection_selected_foreach(city_selection, - worklist_first_impr_or_unit_iterate, - GINT_TO_POINTER(cid_encode(target))); - break; - case CO_NEXT: - gtk_tree_selection_selected_foreach(city_selection, - worklist_next_impr_or_unit_iterate, - GINT_TO_POINTER(cid_encode(target))); - break; - case CO_NEXT_TO_LAST: - foreach_func = worklist_next_to_last_impr_or_unit_iterate; - gtk_tree_selection_selected_foreach(city_selection, foreach_func, - GINT_TO_POINTER(cid_encode(target))); - break; - case CO_SELL: - fc_assert_action(target.kind == VUT_IMPROVEMENT, break); - { - const struct impr_type *building = target.value.building; - struct sell_data sd = { 0, 0, building }; - GtkWidget *w; - gint res; - gchar *buf; - const char *imprname = improvement_name_translation(building); - - /* Ask confirmation */ - buf = g_strdup_printf(_("Are you sure you want to sell those %s?"), imprname); - w = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, "%s", buf); - g_free(buf); - res = gtk_dialog_run(GTK_DIALOG(w)); /* Synchron. */ - gtk_widget_destroy(w); - if (res == GTK_RESPONSE_NO) { - break; - } - - gtk_tree_selection_selected_foreach(city_selection, - sell_impr_iterate, &sd); - if (sd.count > 0) { - /* FIXME: plurality of sd.count is ignored! */ - /* TRANS: "Sold 3 Harbor for 90 gold." (Pluralisation is in gold -- - * second %d -- not in buildings.) */ - w = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, - GTK_MESSAGE_INFO, GTK_BUTTONS_OK, - PL_("Sold %d %s for %d gold.", - "Sold %d %s for %d gold.", - sd.gold), - sd.count, imprname, sd.gold); - } else { - w = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, - GTK_MESSAGE_INFO, GTK_BUTTONS_OK, - _("No %s could be sold."), - imprname); - } - - g_signal_connect(w, "response", - G_CALLBACK(gtk_widget_destroy), NULL); - gtk_window_present(GTK_WINDOW(w)); /* Asynchron. */ - } - break; - case CO_NONE: - break; - } - connection_do_unbuffer(&client.conn); - } -} - -/************************************************************************//** - CMA callback. -****************************************************************************/ -static void cma_iterate(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - struct city *pcity = city_model_get(model, iter); - int idx = GPOINTER_TO_INT(data); - - if (NULL != pcity) { - if (CMA_NONE == idx) { - cma_release_city(pcity); - } else { - cma_put_city_under_agent(pcity, cmafec_preset_get_parameter(idx)); - } - refresh_city_dialog(pcity); - } -} - -/************************************************************************//** - Called when one clicks on an CMA item to make a selection or to - change a selection's preset. -****************************************************************************/ -static void select_cma_callback(GtkWidget *w, gpointer data) -{ - int idx = GPOINTER_TO_INT(data); - GObject *parent = G_OBJECT(gtk_widget_get_parent(w)); - bool change_cma = - GPOINTER_TO_INT(g_object_get_data(parent, "freeciv_change_cma")); - struct cm_parameter parameter; - - /* If this is not the change button but the select cities button. */ - if (!change_cma) { - ITree it; - GtkTreeModel *model = GTK_TREE_MODEL(city_model); - - gtk_tree_selection_unselect_all(city_selection); - for (itree_begin(model, &it); !itree_end(&it); itree_next(&it)) { - struct city *pcity = city_model_get(model, TREE_ITER_PTR(it)); - int controlled; - bool select; - - if (NULL == pcity) { - continue; - } - controlled = cma_is_city_under_agent(pcity, ¶meter); - select = FALSE; - - if (idx == CMA_NONE) { - /* CMA_NONE selects not-controlled, all others require controlled */ - if (!controlled) { - select = TRUE; - } - } else if (controlled) { - if (idx == CMA_CUSTOM) { - if (cmafec_preset_get_index_of_parameter(¶meter) == -1) { - select = TRUE; - } - } else if (cm_are_parameter_equal(¶meter, - cmafec_preset_get_parameter(idx))) { - select = TRUE; - } - } - - if (select) { - itree_select(city_selection, &it); - } - } - } else { - gtk_tree_selection_selected_foreach(city_selection, - cma_iterate, GINT_TO_POINTER(idx)); - } -} - -/************************************************************************//** - Create the cma entries in the change menu and the select menu. The - indices CMA_NONE and CMA_CUSTOM are special. - CMA_NONE signifies a preset of "none" and CMA_CUSTOM a - "custom" preset. -****************************************************************************/ -static void append_cma_to_menu_item(GtkMenuItem *parent_item, bool change_cma) -{ - GtkWidget *menu; - int i; - struct cm_parameter parameter; - GtkWidget *w; - - w = gtk_menu_item_get_submenu(parent_item); - if (w != NULL && gtk_widget_get_visible(w)) { - return; - } - - if (!can_client_issue_orders()) { - gtk_menu_item_set_submenu(parent_item, NULL); - return; - } - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(parent_item, menu); - - if (change_cma) { - w = gtk_menu_item_new_with_label(_("none")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), w); - g_signal_connect(w, "activate", G_CALLBACK(select_cma_callback), - GINT_TO_POINTER(CMA_NONE)); - fc_assert(GPOINTER_TO_INT(GINT_TO_POINTER(CMA_NONE)) == CMA_NONE); - - for (i = 0; i < cmafec_preset_num(); i++) { - w = gtk_menu_item_new_with_label(cmafec_preset_get_descr(i)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), w); - g_signal_connect(w, "activate", G_CALLBACK(select_cma_callback), - GINT_TO_POINTER(i)); - fc_assert(GPOINTER_TO_INT(GINT_TO_POINTER(i)) == i); - } - } else { - /* search for a "none" */ - int found; - - found = 0; - city_list_iterate(client.conn.playing->cities, pcity) { - if (!cma_is_city_under_agent(pcity, NULL)) { - found = 1; - break; - } - } city_list_iterate_end; - - if (found) { - w = gtk_menu_item_new_with_label(_("none")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), w); - g_signal_connect(w, "activate", G_CALLBACK(select_cma_callback), - GINT_TO_POINTER(CMA_NONE)); - } - - /* - * Search for a city that's under custom (not preset) agent. Might - * take a lonnggg time. - */ - found = 0; - city_list_iterate(client.conn.playing->cities, pcity) { - if (cma_is_city_under_agent(pcity, ¶meter) - && cmafec_preset_get_index_of_parameter(¶meter) == -1) { - found = 1; - break; - } - } city_list_iterate_end; - - if (found) { - /* we found city that's under agent but not a preset */ - w = gtk_menu_item_new_with_label(_("custom")); - - gtk_menu_shell_append(GTK_MENU_SHELL(menu), w); - g_signal_connect(w, "activate", - G_CALLBACK(select_cma_callback), - GINT_TO_POINTER(CMA_CUSTOM)); - } - - /* only fill in presets that are being used. */ - for (i = 0; i < cmafec_preset_num(); i++) { - found = 0; - city_list_iterate(client.conn.playing->cities, pcity) { - if (cma_is_city_under_agent(pcity, ¶meter) - && cm_are_parameter_equal(¶meter, - cmafec_preset_get_parameter(i))) { - found = 1; - break; - } - } city_list_iterate_end; - if (found) { - w = gtk_menu_item_new_with_label(cmafec_preset_get_descr(i)); - - gtk_menu_shell_append(GTK_MENU_SHELL(menu), w); - g_signal_connect(w, "activate", - G_CALLBACK(select_cma_callback), GINT_TO_POINTER(i)); - } - } - } - - g_object_set_data(G_OBJECT(menu), "freeciv_change_cma", - GINT_TO_POINTER(change_cma)); - gtk_widget_show_all(menu); -} - -/************************************************************************//** - Helper function to append a worklist to the current work list of one city - in the city report. This function is called over all selected rows in the - list view. -****************************************************************************/ -static void append_worklist_foreach(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - const struct worklist *pwl = data; - struct city *pcity = city_model_get(model, iter); - - fc_assert_ret(pwl != NULL); - - if (NULL != pcity) { - city_queue_insert_worklist(pcity, -1, pwl); - } -} - -/************************************************************************//** - Menu item callback to append the global worklist associated with this - item to the worklists of all selected cities. The worklist pointer is - passed in 'data'. -****************************************************************************/ -static void append_worklist_callback(GtkMenuItem *menuitem, gpointer data) -{ - struct global_worklist *pgwl = - global_worklist_by_id(GPOINTER_TO_INT(data)); - - fc_assert_ret(city_selection != NULL); - - if (!pgwl) { - /* Maybe removed by an other way, not an error. */ - return; - } - - gtk_tree_selection_selected_foreach(city_selection, - append_worklist_foreach, - (gpointer) global_worklist_get(pgwl)); -} - -/************************************************************************//** - Helper function to set a worklist for one city in the city report. This - function is called over all selected rows in the list view. -****************************************************************************/ -static void set_worklist_foreach(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - const struct worklist *pwl = data; - struct city *pcity = city_model_get(model, iter); - - fc_assert_ret(pwl != NULL); - - if (NULL != pcity) { - city_set_queue(pcity, pwl); - } -} - -/************************************************************************//** - Menu item callback to set a city's worklist to the global worklist - associated with this menu item. The worklist pointer is passed in 'data'. -****************************************************************************/ -static void set_worklist_callback(GtkMenuItem *menuitem, gpointer data) -{ - struct global_worklist *pgwl = - global_worklist_by_id(GPOINTER_TO_INT(data)); - - fc_assert_ret(city_selection != NULL); - gtk_tree_selection_selected_foreach(city_selection, set_worklist_foreach, - (gpointer) global_worklist_get(pgwl)); - - if (!pgwl) { - /* Maybe removed by an other way, not an error. */ - return; - } - - gtk_tree_selection_selected_foreach(city_selection, - set_worklist_foreach, - (gpointer) global_worklist_get(pgwl)); -} - -/************************************************************************//** - Empty and refill the submenu of the menu item passed as 'data'. The menu - will be filled with menu items corresponding to the global worklists. -****************************************************************************/ -static void production_menu_shown(GtkWidget *widget, gpointer data) -{ - GtkWidget *menu, *item; - GtkMenuItem *parent_item; - GCallback callback; - int count = 0; - - parent_item = data; - fc_assert_ret(parent_item != NULL); - fc_assert_ret(GTK_IS_MENU_ITEM(parent_item)); - - callback = g_object_get_data(G_OBJECT(parent_item), "item_callback"); - fc_assert_ret(callback != NULL); - - menu = gtk_menu_item_get_submenu(parent_item); - if (menu != NULL && gtk_widget_get_visible(menu)) { - gtk_menu_shell_deactivate(GTK_MENU_SHELL(menu)); - } - - if (menu == NULL) { - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(parent_item, menu); - } - - if (!can_client_issue_orders()) { - return; - } - - gtk_container_forall(GTK_CONTAINER(menu), - (GtkCallback) gtk_widget_destroy, NULL); - - global_worklists_iterate(pgwl) { - item = gtk_menu_item_new_with_label(global_worklist_name(pgwl)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", callback, - GINT_TO_POINTER(global_worklist_id(pgwl))); - count++; - } global_worklists_iterate_end; - - if (count == 0) { - item = gtk_menu_item_new_with_label(_("(no worklists defined)")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - } - - gtk_widget_show_all(menu); -} - -/************************************************************************//** - Update city report views -****************************************************************************/ -static void city_report_update_views(void) -{ - struct city_report_spec *spec; - GtkTreeView *view; - GtkTreeViewColumn *col; - GList *columns, *p; - - view = GTK_TREE_VIEW(city_view); - fc_assert_ret(view != NULL); - - columns = gtk_tree_view_get_columns(view); - - for (p = columns; p != NULL; p = p->next) { - col = p->data; - spec = g_object_get_data(G_OBJECT(col), "city_report_spec"); - gtk_tree_view_column_set_visible(col, spec->show); - } - - g_list_free(columns); -} - -/************************************************************************//** - User has toggled some column viewing option -****************************************************************************/ -static void toggle_view(GtkCheckMenuItem *item, gpointer data) -{ - struct city_report_spec *spec = data; - - spec->show ^= 1; - city_report_update_views(); -} - -/************************************************************************//** - Create view menu for city report menubar. -****************************************************************************/ -static void update_view_menu(GtkWidget *show_item) -{ - GtkWidget *menu, *item; - struct city_report_spec *spec; - int i; - - menu = gtk_menu_new(); - for (i = 0, spec = city_report_specs + i; i < NUM_CREPORT_COLS; i++, spec++) { - item = gtk_check_menu_item_new_with_label(spec->explanation); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), spec->show); - g_signal_connect(item, "toggled", G_CALLBACK(toggle_view), (gpointer)spec); - } - gtk_menu_item_set_submenu(GTK_MENU_ITEM(show_item), menu); -} - -/************************************************************************//** - Create menubar for city report -****************************************************************************/ -static GtkWidget *create_city_report_menubar(void) -{ - GtkWidget *vbox, *sep, *menubar, *menu, *item; - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); - gtk_container_add(GTK_CONTAINER(vbox), sep); - - menubar = gtk_aux_menu_bar_new(); - gtk_container_add(GTK_CONTAINER(vbox), menubar); - - item = gtk_menu_item_new_with_mnemonic(_("_Production")); - city_production_command = item; - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - - item = gtk_menu_item_new_with_mnemonic(_("Chan_ge")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - create_change_menu(item); - - item = gtk_menu_item_new_with_mnemonic(_("Add _First")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - create_first_menu(item); - - item = gtk_menu_item_new_with_mnemonic(_("Add _Next")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - create_next_menu(item); - - item = gtk_menu_item_new_with_mnemonic(_("Add _2nd Last")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - create_next_to_last_menu(item); - - item = gtk_menu_item_new_with_mnemonic(_("Add _Last")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - create_last_menu(item); - - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - item = gtk_menu_item_new_with_label(_("Set Worklist")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data(G_OBJECT(item), "item_callback", - set_worklist_callback); - g_signal_connect(menu, "show", G_CALLBACK(production_menu_shown), item); - - item = gtk_menu_item_new_with_label(_("Append Worklist")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data(G_OBJECT(item), "item_callback", - append_worklist_callback); - g_signal_connect(menu, "show", G_CALLBACK(production_menu_shown), item); - - item = gtk_menu_item_new_with_mnemonic(_("Clear _Worklist")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(city_clear_worklist_callback), NULL); - - item = gtk_menu_item_new_with_mnemonic(_("Gover_nor")); - city_governor_command = item; - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - append_cma_to_menu_item(GTK_MENU_ITEM(item), TRUE); - - item = gtk_menu_item_new_with_mnemonic(_("S_ell")); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - city_sell_command = item; - create_sell_menu(item); - - item = gtk_menu_item_new_with_mnemonic(_("_Select")); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - create_select_menu(item); - - item = gtk_menu_item_new_with_mnemonic(_("_Display")); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - update_view_menu(item); - return vbox; -} - -/************************************************************************//** - Sort callback. -****************************************************************************/ -static gint cityrep_sort_func(GtkTreeModel *model, GtkTreeIter *a, - GtkTreeIter *b, gpointer data) -{ - gint col = GPOINTER_TO_INT(data); - const gchar *str1, *str2; - - gtk_tree_model_get(model, a, col, &str1, -1); - gtk_tree_model_get(model, b, col, &str2, -1); - - return cityrepfield_compare(str1, str2); -} - -/************************************************************************//** - Create city report dialog. -****************************************************************************/ -static void create_city_report_dialog(bool make_modal) -{ - static char **titles; - static char (*buf)[128]; - struct city_report_spec *spec; - GtkWidget *w, *sw, *menubar; - int i; - - gui_dialog_new(&city_dialog_shell, GTK_NOTEBOOK(top_notebook), NULL, TRUE); - gui_dialog_set_title(city_dialog_shell, _("Cities")); - - gui_dialog_set_default_size(city_dialog_shell, -1, 420); - - gui_dialog_response_set_callback(city_dialog_shell, - city_command_callback); - - /* menubar */ - menubar = create_city_report_menubar(); - gui_dialog_add_widget(city_dialog_shell, menubar); - - /* buttons */ - city_total_buy_cost_label = gtk_label_new(NULL); - gtk_widget_set_hexpand(city_total_buy_cost_label, TRUE); - gtk_label_set_ellipsize(GTK_LABEL(city_total_buy_cost_label), - PANGO_ELLIPSIZE_START); - gtk_container_add(GTK_CONTAINER(city_dialog_shell->action_area), - city_total_buy_cost_label); - - w = gui_dialog_add_stockbutton(city_dialog_shell, GTK_STOCK_EXECUTE, - _("_Buy"), CITY_BUY); - city_buy_command = w; - - w = gui_dialog_add_stockbutton(city_dialog_shell, GTK_STOCK_ZOOM_IN, - _("_Inspect"), CITY_POPUP); - city_popup_command = w; - - w = gui_dialog_add_stockbutton(city_dialog_shell, GTK_STOCK_ZOOM_FIT, - _("Cen_ter"), CITY_CENTER); - city_center_command = w; - - gui_dialog_set_default_response(city_dialog_shell, - GTK_RESPONSE_CLOSE); - - /* tree view */ - buf = fc_realloc(buf, NUM_CREPORT_COLS * sizeof(buf[0])); - titles = fc_realloc(titles, NUM_CREPORT_COLS * sizeof(titles[0])); - for (i = 0; i < NUM_CREPORT_COLS; i++) { - titles[i] = buf[i]; - } - get_city_table_header(titles, sizeof(buf[0])); - - city_model = city_report_dialog_store_new(); - - city_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(city_model)); - gtk_widget_set_hexpand(city_view, TRUE); - gtk_widget_set_vexpand(city_view, TRUE); - g_object_unref(city_model); - gtk_widget_set_name(city_view, "small_font"); - g_signal_connect(city_view, "row_activated", - G_CALLBACK(city_activated_callback), NULL); - city_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(city_view)); - gtk_tree_selection_set_mode(city_selection, GTK_SELECTION_MULTIPLE); - g_signal_connect(city_selection, "changed", - G_CALLBACK(city_selection_changed_callback), NULL); - - for (i = 0, spec = city_report_specs; i < NUM_CREPORT_COLS; i++, spec++) { - GtkWidget *header; - GtkCellRenderer *renderer; - GtkTreeViewColumn *col; - - renderer = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, renderer, - "text", i, NULL); - header = gtk_label_new(titles[i]); - gtk_widget_set_tooltip_text(header, spec->explanation); - gtk_widget_show(header); - gtk_tree_view_column_set_widget(col, header); - gtk_tree_view_column_set_visible(col, spec->show); - gtk_tree_view_column_set_sort_column_id(col, i); - gtk_tree_view_column_set_reorderable(col, TRUE); - g_object_set_data(G_OBJECT(col), "city_report_spec", spec); - gtk_tree_view_append_column(GTK_TREE_VIEW(city_view), col); - gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(city_model), i, - cityrep_sort_func, GINT_TO_POINTER(i), - NULL); - } - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(sw), city_view); - - gtk_container_add(GTK_CONTAINER(city_dialog_shell->vbox), sw); - - city_model_fill(city_model, NULL, NULL); - gui_dialog_show_all(city_dialog_shell); - - city_selection_changed_callback(city_selection); -} - -/************************************************************************//** - User has chosen to select all cities -****************************************************************************/ -static void city_select_all_callback(GtkMenuItem *item, gpointer data) -{ - gtk_tree_selection_select_all(city_selection); -} - -/************************************************************************//** - User has chosen to unselect all cities -****************************************************************************/ -static void city_unselect_all_callback(GtkMenuItem *item, gpointer data) -{ - gtk_tree_selection_unselect_all(city_selection); -} - -/************************************************************************//** - User has chosen to invert selection -****************************************************************************/ -static void city_invert_selection_callback(GtkMenuItem *item, gpointer data) -{ - ITree it; - GtkTreeModel *model = GTK_TREE_MODEL(city_model); - - for (itree_begin(model, &it); !itree_end(&it); itree_next(&it)) { - if (itree_is_selected(city_selection, &it)) { - itree_unselect(city_selection, &it); - } else { - itree_select(city_selection, &it); - } - } -} - -/************************************************************************//** - User has chosen to select coastal cities -****************************************************************************/ -static void city_select_coastal_callback(GtkMenuItem *item, gpointer data) -{ - ITree it; - GtkTreeModel *model = GTK_TREE_MODEL(city_model); - - gtk_tree_selection_unselect_all(city_selection); - - for (itree_begin(model, &it); !itree_end(&it); itree_next(&it)) { - struct city *pcity = city_model_get(model, TREE_ITER_PTR(it)); - - if (NULL != pcity && is_terrain_class_near_tile(pcity->tile, TC_OCEAN)) { - itree_select(city_selection, &it); - } - } -} - -/************************************************************************//** - Select all cities on the same continent. -****************************************************************************/ -static void same_island_iterate(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - struct city *selected_pcity = city_model_get(model, iter); - ITree it; - - if (NULL == selected_pcity) { - return; - } - - for (itree_begin(model, &it); !itree_end(&it); itree_next(&it)) { - struct city *pcity = city_model_get(model, TREE_ITER_PTR(it)); - - if (NULL != pcity - && (tile_continent(pcity->tile) - == tile_continent(selected_pcity->tile))) { - itree_select(city_selection, &it); - } - } -} - -/************************************************************************//** - User has chosen to select all cities on same island -****************************************************************************/ -static void city_select_same_island_callback(GtkMenuItem *item, gpointer data) -{ - gtk_tree_selection_selected_foreach(city_selection,same_island_iterate,NULL); -} - -/************************************************************************//** - User has chosen to select cities with certain target in production -****************************************************************************/ -static void city_select_building_callback(GtkMenuItem *item, gpointer data) -{ - enum production_class_type which = GPOINTER_TO_INT(data); - ITree it; - GtkTreeModel *model = GTK_TREE_MODEL(city_model); - - gtk_tree_selection_unselect_all(city_selection); - - for (itree_begin(model, &it); !itree_end(&it); itree_next(&it)) { - struct city *pcity = city_model_get(model, TREE_ITER_PTR(it)); - - if (NULL != pcity - && ((which == PCT_UNIT && VUT_UTYPE == pcity->production.kind) - || (which == PCT_NORMAL_IMPROVEMENT - && VUT_IMPROVEMENT == pcity->production.kind - && !is_wonder(pcity->production.value.building)) - || (which == PCT_WONDER - && VUT_IMPROVEMENT == pcity->production.kind - && is_wonder(pcity->production.value.building)))) { - itree_select(city_selection, &it); - } - } -} - -/************************************************************************//** - Buy the production in one single city. -****************************************************************************/ -static void buy_iterate(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity) { - cityrep_buy(pcity); - } -} - -/************************************************************************//** - Center to one single city. -****************************************************************************/ -static void center_iterate(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity) { - center_tile_mapcanvas(pcity->tile); - } -} - -/************************************************************************//** - Popup the dialog of a single city. -****************************************************************************/ -static void popup_iterate(GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gpointer data) -{ - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity) { - if (gui_options.center_when_popup_city) { - center_tile_mapcanvas(pcity->tile); - } - popup_city_dialog(pcity); - } -} - -/************************************************************************//** - gui_dialog response callback. -****************************************************************************/ -static void city_command_callback(struct gui_dialog *dlg, int response, - gpointer data) -{ - switch (response) { - case CITY_CENTER: - if (1 == gtk_tree_selection_count_selected_rows(city_selection)) { - /* Center to city doesn't make sense if many city are selected. */ - gtk_tree_selection_selected_foreach(city_selection, center_iterate, - NULL); - } - break; - case CITY_POPUP: - gtk_tree_selection_selected_foreach(city_selection, popup_iterate, NULL); - break; - case CITY_BUY: - gtk_tree_selection_selected_foreach(city_selection, buy_iterate, NULL); - break; - default: - gui_dialog_destroy(dlg); - break; - } -} - -/************************************************************************//** - User has selected city row from city report. -****************************************************************************/ -static void city_activated_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter iter; - GdkWindow *win; - GdkDeviceManager *manager; - GdkModifierType mask; - - model = gtk_tree_view_get_model(view); - - if (!gtk_tree_model_get_iter(model, &iter, path)) { - return; - } - - win = gdk_get_default_root_window(); - manager = gdk_display_get_device_manager(gdk_window_get_display(win)); - - gdk_window_get_device_position(win, - gdk_device_manager_get_client_pointer(manager), - NULL, NULL, &mask); - - if (!(mask & GDK_CONTROL_MASK)) { - popup_iterate(model, path, &iter, NULL); - } else { - center_iterate(model, path, &iter, NULL); - } -} - -/************************************************************************//** - Update the city report dialog -****************************************************************************/ -void real_city_report_dialog_update(void *unused) -{ - GHashTable *selected; - ITree iter; - gint city_id; - - if (NULL == city_dialog_shell) { - return; - } - - /* Save the selection. */ - selected = g_hash_table_new(NULL, NULL); - for (itree_begin(GTK_TREE_MODEL(city_model), &iter); - !itree_end(&iter); itree_next(&iter)) { - if (itree_is_selected(city_selection, &iter)) { - itree_get(&iter, CRD_COL_CITY_ID, &city_id, -1); - g_hash_table_insert(selected, GINT_TO_POINTER(city_id), NULL); - } - } - - /* Update and restore the selection. */ - gtk_list_store_clear(city_model); - city_model_fill(city_model, city_selection, selected); - g_hash_table_destroy(selected); - - if (gtk_widget_get_sensitive(city_governor_command)) { - append_cma_to_menu_item(GTK_MENU_ITEM(city_governor_command), TRUE); - } - - select_menu_cached = FALSE; -} - -/************************************************************************//** - Update the text for a single city in the city report -****************************************************************************/ -void real_city_report_update_city(struct city *pcity) -{ - GtkTreeIter iter; - - if (NULL == city_dialog_shell) { - return; - } - - if (!city_model_find(GTK_TREE_MODEL(city_model), &iter, pcity)) { - gtk_list_store_prepend(city_model, &iter); - } - city_model_set(city_model, &iter, pcity); - - update_total_buy_cost(); -} - -/************************************************************************//** - Create submenu for changing production target -****************************************************************************/ -static void create_change_menu(GtkWidget *item) -{ - GtkWidget *menu; - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - g_signal_connect(menu, "show", G_CALLBACK(popup_change_menu), NULL); - - change_units_item = gtk_menu_item_new_with_label(_("Units")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), change_units_item); - change_improvements_item = gtk_menu_item_new_with_label(_("Improvements")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), change_improvements_item); - change_wonders_item = gtk_menu_item_new_with_label(_("Wonders")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), change_wonders_item); -} - -/************************************************************************//** - Creates the last menu. -****************************************************************************/ -static void create_last_menu(GtkWidget *item) -{ - GtkWidget *menu; - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - g_signal_connect(menu, "show", G_CALLBACK(popup_last_menu), NULL); - - last_units_item = gtk_menu_item_new_with_label(_("Units")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), last_units_item); - last_improvements_item = gtk_menu_item_new_with_label(_("Improvements")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), last_improvements_item); - last_wonders_item = gtk_menu_item_new_with_label(_("Wonders")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), last_wonders_item); -} - -/************************************************************************//** - Creates the first menu. -****************************************************************************/ -static void create_first_menu(GtkWidget *item) -{ - GtkWidget *menu; - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - g_signal_connect(menu, "show", G_CALLBACK(popup_first_menu), NULL); - - first_units_item = gtk_menu_item_new_with_label(_("Units")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), first_units_item); - first_improvements_item = gtk_menu_item_new_with_label(_("Improvements")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), first_improvements_item); - first_wonders_item = gtk_menu_item_new_with_label(_("Wonders")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), first_wonders_item); -} - -/************************************************************************//** - Creates the next menu. -****************************************************************************/ -static void create_next_menu(GtkWidget *item) -{ - GtkWidget *menu; - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - g_signal_connect(menu, "show", G_CALLBACK(popup_next_menu), NULL); - - next_units_item = gtk_menu_item_new_with_label(_("Units")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), next_units_item); - next_improvements_item = gtk_menu_item_new_with_label(_("Improvements")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), next_improvements_item); - next_wonders_item = gtk_menu_item_new_with_label(_("Wonders")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), next_wonders_item); -} - -/************************************************************************//** - Append the "next to last" submenu to the given menu item. -****************************************************************************/ -static void create_next_to_last_menu(GtkWidget *parent_item) -{ - GtkWidget *menu, *item; - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), menu); - g_signal_connect(menu, "show", - G_CALLBACK(popup_next_to_last_menu), NULL); - - item = gtk_menu_item_new_with_label(_("Units")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - next_to_last_units_item = item; - - item = gtk_menu_item_new_with_label(_("Improvements")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - next_to_last_improvements_item = item; - - item = gtk_menu_item_new_with_label(_("Wonders")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - next_to_last_wonders_item = item; -} - -/************************************************************************//** - Create the sell menu (empty). -****************************************************************************/ -static void create_sell_menu(GtkWidget *item) -{ - GtkWidget *menu; - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); -} - -/************************************************************************//** - Pops up menu where user can select build target. -****************************************************************************/ -static void popup_change_menu(GtkMenuShell *menu, gpointer data) -{ - int n; - - n = gtk_tree_selection_count_selected_rows(city_selection); - - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(change_improvements_item), - FALSE, FALSE, CO_CHANGE, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(change_units_item), - TRUE, FALSE, CO_CHANGE, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(change_wonders_item), - FALSE, TRUE, CO_CHANGE, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); -} - -/************************************************************************//** - Pops up the last menu. -****************************************************************************/ -static void popup_last_menu(GtkMenuShell *menu, gpointer data) -{ - int n; - - n = gtk_tree_selection_count_selected_rows(city_selection); - - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(last_improvements_item), - FALSE, FALSE, CO_LAST, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(last_units_item), - TRUE, FALSE, CO_LAST, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(last_wonders_item), - FALSE, TRUE, CO_LAST, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); -} - -/************************************************************************//** - Pops up the first menu. -****************************************************************************/ -static void popup_first_menu(GtkMenuShell *menu, gpointer data) -{ - int n; - - n = gtk_tree_selection_count_selected_rows(city_selection); - - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(first_improvements_item), - FALSE, FALSE, CO_FIRST, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(first_units_item), - TRUE, FALSE, CO_FIRST, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(first_wonders_item), - FALSE, TRUE, CO_FIRST, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); -} - -/************************************************************************//** - Pops up the next menu. -****************************************************************************/ -static void popup_next_menu(GtkMenuShell *menu, gpointer data) -{ - int n; - - n = gtk_tree_selection_count_selected_rows(city_selection); - - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(next_improvements_item), - FALSE, FALSE, CO_NEXT, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(next_units_item), - TRUE, FALSE, CO_NEXT, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(next_wonders_item), - FALSE, TRUE, CO_NEXT, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), n); -} - -/************************************************************************//** - Re-create the submenus in the next-to-last production change menu. -****************************************************************************/ -static void popup_next_to_last_menu(GtkMenuShell *menu, gpointer data) -{ - GtkWidget *item; - GCallback callback; - int n; - - fc_assert_ret(city_selection != NULL); - - n = gtk_tree_selection_count_selected_rows(city_selection); - callback = G_CALLBACK(select_impr_or_unit_callback); - - item = next_to_last_improvements_item; - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(item), - FALSE, FALSE, CO_NEXT_TO_LAST, - can_city_build_now, - callback, n); - item = next_to_last_units_item; - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(item), - TRUE, FALSE, CO_NEXT_TO_LAST, - can_city_build_now, - callback, n); - item = next_to_last_wonders_item; - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(item), - FALSE, TRUE, CO_NEXT_TO_LAST, - can_city_build_now, - callback, n); -} - -/************************************************************************//** - Update the sell menu. -****************************************************************************/ -static void recreate_sell_menu(void) -{ - int n; - GList *children; - GtkWidget *menu; - - n = gtk_tree_selection_count_selected_rows(city_selection); - menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(city_sell_command)); - gtk_menu_popdown(GTK_MENU(menu)); - - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(city_sell_command), - FALSE, FALSE, CO_SELL, - can_city_sell_universal, - G_CALLBACK(select_impr_or_unit_callback), - n); - - menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(city_sell_command)); - children = gtk_container_get_children(GTK_CONTAINER(menu)); - - n = g_list_length(children); - gtk_widget_set_sensitive(city_sell_command, n > 0); - g_list_free(children); -} - -/************************************************************************//** - Creates select menu -****************************************************************************/ -static void create_select_menu(GtkWidget *item) -{ - GtkWidget *menu; - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - g_signal_connect(menu, "show", G_CALLBACK(popup_select_menu), NULL); - - item = gtk_menu_item_new_with_label(_("All Cities")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(city_select_all_callback), NULL); - - item = gtk_menu_item_new_with_label(_("No Cities")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(city_unselect_all_callback), NULL); - - item = gtk_menu_item_new_with_label(_("Invert Selection")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(city_invert_selection_callback), NULL); - - - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - - item = gtk_menu_item_new_with_label(_("Building Units")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(city_select_building_callback), - GINT_TO_POINTER(PCT_UNIT)); - - item = gtk_menu_item_new_with_label( _("Building Improvements")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(city_select_building_callback), - GINT_TO_POINTER(PCT_NORMAL_IMPROVEMENT)); - - item = gtk_menu_item_new_with_label(_("Building Wonders")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(city_select_building_callback), - GINT_TO_POINTER(PCT_WONDER)); - - - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - - select_bunit_item = - gtk_menu_item_new_with_label(_("Building Unit")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_bunit_item); - - select_bimprovement_item = - gtk_menu_item_new_with_label( _("Building Improvement")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_bimprovement_item); - - select_bwonder_item = - gtk_menu_item_new_with_label(_("Building Wonder")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_bwonder_item); - - - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - - item = gtk_menu_item_new_with_label(_("Coastal Cities")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(city_select_coastal_callback), NULL); - - select_island_item = gtk_menu_item_new_with_label(_("Same Island")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_island_item); - g_signal_connect(select_island_item, "activate", - G_CALLBACK(city_select_same_island_callback), NULL); - - - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - - select_supported_item = gtk_menu_item_new_with_label(_("Supported Units")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_supported_item); - - select_present_item = gtk_menu_item_new_with_label(_("Units Present")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_present_item); - - select_built_improvements_item = - gtk_menu_item_new_with_label(_("Improvements in City")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_built_improvements_item); - - select_built_wonders_item = - gtk_menu_item_new_with_label(_("Wonders in City")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_built_wonders_item); - - - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - - select_units_item = - gtk_menu_item_new_with_label(_("Available Units")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_units_item); - select_improvements_item = - gtk_menu_item_new_with_label(_("Available Improvements")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_improvements_item); - select_wonders_item = - gtk_menu_item_new_with_label(_("Available Wonders")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_wonders_item); - select_cma_item = - gtk_menu_item_new_with_label(_("Citizen Governor")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), select_cma_item); -} - -/************************************************************************//** - Returns whether city is building given target -****************************************************************************/ -static bool city_building_impr_or_unit(const struct city *pcity, - const struct universal *target) -{ - return are_universals_equal(&pcity->production, target); -} - -/************************************************************************//** - Popup select menu -****************************************************************************/ -static void popup_select_menu(GtkMenuShell *menu, gpointer data) -{ - int n; - - if (select_menu_cached) - return; - - n = gtk_tree_selection_count_selected_rows(city_selection); - gtk_widget_set_sensitive(select_island_item, (n > 0)); - - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_bunit_item), - TRUE, FALSE, CO_NONE, - city_building_impr_or_unit, - G_CALLBACK(select_impr_or_unit_callback), -1); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_bimprovement_item), - FALSE, FALSE, CO_NONE, - city_building_impr_or_unit, - G_CALLBACK(select_impr_or_unit_callback), -1); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_bwonder_item), - FALSE, TRUE, CO_NONE, - city_building_impr_or_unit, - G_CALLBACK(select_impr_or_unit_callback), -1); - - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_supported_item), - TRUE, FALSE, CO_NONE, - city_unit_supported, - G_CALLBACK(select_impr_or_unit_callback), -1); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_present_item), - TRUE, FALSE, CO_NONE, - city_unit_present, - G_CALLBACK(select_impr_or_unit_callback), -1); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_built_improvements_item), - FALSE, FALSE, CO_NONE, - city_building_present, - G_CALLBACK(select_impr_or_unit_callback), -1); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_built_wonders_item), - FALSE, TRUE, CO_NONE, - city_building_present, - G_CALLBACK(select_impr_or_unit_callback), -1); - - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_improvements_item), - FALSE, FALSE, CO_NONE, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), -1); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_units_item), - TRUE, FALSE, CO_NONE, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), -1); - append_impr_or_unit_to_menu_item(GTK_MENU_ITEM(select_wonders_item), - FALSE, TRUE, CO_NONE, - can_city_build_now, - G_CALLBACK(select_impr_or_unit_callback), -1); - append_cma_to_menu_item(GTK_MENU_ITEM(select_cma_item), FALSE); - - select_menu_cached = TRUE; -} - -/************************************************************************//** - Update the value displayed by the "total buy cost" label in the city - report, or make it blank if nothing can be bought. -****************************************************************************/ -static void update_total_buy_cost(void) -{ - GtkWidget *label, *view; - GList *rows, *p; - GtkTreeModel *model; - GtkTreeSelection *sel; - GtkTreePath *path; - GtkTreeIter iter; - struct city *pcity; - int total = 0; - - view = city_view; - label = city_total_buy_cost_label; - - if (!view || !label) { - return; - } - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - rows = gtk_tree_selection_get_selected_rows(sel, &model); - - for (p = rows; p != NULL; p = p->next) { - path = p->data; - if (gtk_tree_model_get_iter(model, &iter, path)) { - if ((pcity = city_model_get(model, &iter))) { - total += pcity->client.buy_cost; - } - } - gtk_tree_path_free(path); - } - g_list_free(rows); - - if (total > 0) { - gchar *buf = g_strdup_printf(_("Total Buy Cost: %d"), total); - - gtk_label_set_text(GTK_LABEL(label), buf); - g_free(buf); - } else { - gtk_label_set_text(GTK_LABEL(label), NULL); - } -} - -/************************************************************************//** - Update city report button sensitivity and total buy cost label when the - user makes a change in the selection of cities. -****************************************************************************/ -static void city_selection_changed_callback(GtkTreeSelection *selection) -{ - int n; - bool obs_may, plr_may; - - n = gtk_tree_selection_count_selected_rows(selection); - obs_may = n > 0; - plr_may = obs_may && can_client_issue_orders(); - - gtk_widget_set_sensitive(city_production_command, plr_may); - gtk_widget_set_sensitive(city_governor_command, plr_may); - gtk_widget_set_sensitive(city_center_command, obs_may); - gtk_widget_set_sensitive(city_popup_command, obs_may); - gtk_widget_set_sensitive(city_buy_command, plr_may); - if (plr_may) { - recreate_sell_menu(); - } else { - gtk_widget_set_sensitive(city_sell_command, FALSE); - } - - update_total_buy_cost(); -} - -/************************************************************************//** - Clear the worklist in one selected city in the city report. -****************************************************************************/ -static void clear_worklist_foreach_func(GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - struct city *pcity = city_model_get(model, iter); - - if (NULL != pcity) { - struct worklist empty; - - worklist_init(&empty); - city_set_worklist(pcity, &empty); - } -} - -/************************************************************************//** - Called when the "clear worklist" menu item is activated. -****************************************************************************/ -static void city_clear_worklist_callback(GtkMenuItem *item, gpointer data) -{ - struct connection *pconn = &client.conn; - - fc_assert_ret(city_selection != NULL); - - connection_do_buffer(pconn); - gtk_tree_selection_selected_foreach(city_selection, - clear_worklist_foreach_func, NULL); - connection_do_unbuffer(pconn); -} - -/************************************************************************//** - After a selection rectangle is defined, make the cities that - are hilited on the canvas exclusively hilited in the - City List window. -****************************************************************************/ -void hilite_cities_from_canvas(void) -{ - ITree it; - GtkTreeModel *model; - - if (!city_dialog_shell) { - return; - } - - model = GTK_TREE_MODEL(city_model); - - gtk_tree_selection_unselect_all(city_selection); - - for (itree_begin(model, &it); !itree_end(&it); itree_next(&it)) { - struct city *pcity = city_model_get(model, TREE_ITER_PTR(it)); - - if (NULL != pcity && is_city_hilited(pcity)) { - itree_select(city_selection, &it); - } - } -} - -/************************************************************************//** - Toggle a city's hilited status. -****************************************************************************/ -void toggle_city_hilite(struct city *pcity, bool on_off) -{ - GtkTreeIter iter; - - if (NULL == city_dialog_shell) { - return; - } - - if (city_model_find(GTK_TREE_MODEL(city_model), &iter, pcity)) { - if (on_off) { - gtk_tree_selection_select_iter(city_selection, &iter); - } else { - gtk_tree_selection_unselect_iter(city_selection, &iter); - } - } -} diff --git a/client/gui-gtk-3.0/cityrep.h b/client/gui-gtk-3.0/cityrep.h deleted file mode 100644 index 397f6fac8d..0000000000 --- a/client/gui-gtk-3.0/cityrep.h +++ /dev/null @@ -1,20 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__CITYREP_H -#define FC__CITYREP_H - -#include "cityrep_g.h" - -void city_report_dialog_popdown(void); - -#endif /* FC__CITYREP_H */ diff --git a/client/gui-gtk-3.0/cma_fe.c b/client/gui-gtk-3.0/cma_fe.c deleted file mode 100644 index 80abd864fd..0000000000 --- a/client/gui-gtk-3.0/cma_fe.c +++ /dev/null @@ -1,772 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 2001 - R. Falke, M. Kaufman - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "support.h" - -/* common */ -#include "events.h" -#include "game.h" - -/* client */ -#include "chatline_g.h" -#include "citydlg_g.h" -#include "client_main.h" -#include "cma_fec.h" -#include "messagewin_g.h" -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "cityrep.h" -#include "dialogs.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "helpdlg.h" -#include "inputdlg.h" - -#include "cma_fe.h" - -#define BUFFER_SIZE 64 - -#define SPECLIST_TAG dialog -#define SPECLIST_TYPE struct cma_dialog -#include "speclist.h" - -#define dialog_list_iterate(dialoglist, pdialog) \ - TYPED_LIST_ITERATE(struct cma_dialog, dialoglist, pdialog) -#define dialog_list_iterate_end LIST_ITERATE_END - -static struct dialog_list *dialog_list; - -static int allow_refreshes = 1; - -static struct cma_dialog *get_cma_dialog(struct city *pcity); - -static void update_cma_preset_list(struct cma_dialog *pdialog); - -static gboolean cma_preset_key_pressed_callback(GtkWidget *w, GdkEventKey *ev, - gpointer data); -static void cma_del_preset_callback(GtkWidget *w, gpointer data); -static void cma_preset_remove(struct cma_dialog *pdialog, int preset_index); -static void cma_preset_remove_response(GtkWidget *w, gint response, - gpointer data); - -static void cma_add_preset_callback(GtkWidget *w, gpointer data); -static void cma_preset_add_popup_callback(gpointer data, gint response, - const char *input); - -static void cma_active_callback(GtkWidget *w, gpointer data); -static void cma_activate_preset_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data); - -static void hscale_changed(GtkWidget *get, gpointer data); -static void set_hscales(const struct cm_parameter *const parameter, - struct cma_dialog *pdialog); - -/**********************************************************************//** - Initialize cma front end system -**************************************************************************/ -void cma_fe_init(void) -{ - dialog_list = dialog_list_new(); -} - -/**********************************************************************//** - Free resources allocated for cma front end system -**************************************************************************/ -void cma_fe_done(void) -{ - dialog_list_destroy(dialog_list); -} - -/**********************************************************************//** - only called when the city dialog is closed. -**************************************************************************/ -void close_cma_dialog(struct city *pcity) -{ - struct cma_dialog *pdialog = get_cma_dialog(pcity); - - if (pdialog == NULL) { - /* A city which is being investigated doesn't contain cma dialog */ - return; - } - gtk_widget_destroy(pdialog->shell); -} - -/**********************************************************************//** - Destroy cma dialog -**************************************************************************/ -static void cma_dialog_destroy_callback(GtkWidget *w, gpointer data) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - - dialog_list_remove(dialog_list, pdialog); - free(pdialog); -} - -/**********************************************************************//** - Return the cma_dialog for a given city. -**************************************************************************/ -struct cma_dialog *get_cma_dialog(struct city *pcity) -{ - dialog_list_iterate(dialog_list, pdialog) { - if (pdialog->pcity == pcity) { - return pdialog; - } - } dialog_list_iterate_end; - - return NULL; -} - -/**********************************************************************//** - User has pressed button in cma dialog -**************************************************************************/ -static gboolean button_press_callback(GtkTreeView *view, GdkEventButton *ev, - gpointer data) -{ - GtkTreePath *path; - GtkTreeViewColumn *column; - - if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), - ev->x, ev->y, &path, &column, NULL, NULL)) { - if (ev->type == GDK_BUTTON_PRESS) { - cma_activate_preset_callback(view, path, column, data); - } else if (ev->type == GDK_2BUTTON_PRESS) { - struct cma_dialog *pdialog = (struct cma_dialog *) data; - struct cm_parameter param; - - cmafec_get_fe_parameter(pdialog->pcity, ¶m); - cma_put_city_under_agent(pdialog->pcity, ¶m); - refresh_city_dialog(pdialog->pcity); - } - } - gtk_tree_path_free(path); - - return FALSE; -} - -/**********************************************************************//** - User has requested help -**************************************************************************/ -static void help_callback(GtkWidget *w, gpointer data) -{ - popup_help_dialog_string(HELP_CMA_ITEM); -} - -/**********************************************************************//** - Cell data function for cma dialog -**************************************************************************/ -static void cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *cell, - GtkTreeModel *model, GtkTreeIter *it, gpointer data) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - gchararray s1; - const char *s2; - int i1, i2; - struct cm_parameter param; - GtkTreePath *path; - - gtk_tree_model_get(model, it, 0, &s1, -1); - if (s1 == NULL) { - return; - } - path = gtk_tree_model_get_path(model, it); - i1 = gtk_tree_path_get_indices(path)[0]; - gtk_tree_path_free(path); - - cmafec_get_fe_parameter(pdialog->pcity, ¶m); - s2 = cmafec_get_short_descr(¶m); - i2 = cmafec_preset_get_index_of_parameter(¶m); - - if (!strcmp(s1, s2) && i1 == i2) { - g_object_set(G_OBJECT(cell), "style", PANGO_STYLE_ITALIC, - "weight", PANGO_WEIGHT_BOLD, NULL); - } else { - g_object_set(G_OBJECT(cell), "style", PANGO_STYLE_NORMAL, - "weight", PANGO_WEIGHT_NORMAL, NULL); - } - - g_free(s1); -} - -/**********************************************************************//** - Instantiates a new struct for each city_dialog window that is open. -**************************************************************************/ -struct cma_dialog *create_cma_dialog(struct city *pcity, bool tiny) -{ - struct cma_dialog *pdialog; - struct cm_parameter param; - GtkWidget *frame, *page, *hbox, *label, *table; - GtkWidget *vbox, *sw, *hscale, *button, *image; - GtkListStore *store; - GtkCellRenderer *rend; - GtkWidget *view; - GtkTreeViewColumn *column; - gint layout_width; - - cmafec_get_fe_parameter(pcity, ¶m); - pdialog = fc_malloc(sizeof(struct cma_dialog)); - pdialog->pcity = pcity; - pdialog->shell = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(pdialog->shell), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(pdialog->shell), 8); - gtk_container_set_border_width(GTK_CONTAINER(pdialog->shell), 8); - g_signal_connect(pdialog->shell, "destroy", - G_CALLBACK(cma_dialog_destroy_callback), pdialog); - - page = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(page), 12); - gtk_container_add(GTK_CONTAINER(pdialog->shell), page); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(pdialog->shell), 2); - gtk_container_add(GTK_CONTAINER(page), vbox); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - - store = gtk_list_store_new(1, G_TYPE_STRING); - pdialog->store = store; - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(store); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - pdialog->preset_list = view; - pdialog->selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - - g_signal_connect(pdialog->preset_list, "button_press_event", - G_CALLBACK(button_press_callback), pdialog); - - gtk_widget_set_tooltip_text(view, - _("For information on\n" - "the citizen governor and governor presets,\n" - "including sample presets,\n" - "see README.governor.")); - - rend = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(NULL, rend, - "text", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); - gtk_tree_view_column_set_cell_data_func(column, rend, cell_data_func, - pdialog, NULL); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", view, - "label", _("Prese_ts:"), - "xalign", 0.0, "yalign", 0.5, NULL); - gtk_container_add(GTK_CONTAINER(vbox), label); - - gtk_container_add(GTK_CONTAINER(sw), view); - gtk_container_add(GTK_CONTAINER(vbox), sw); - - g_signal_connect(view, "row_activated", - G_CALLBACK(cma_activate_preset_callback), pdialog); - g_signal_connect(view, "key-press-event", - G_CALLBACK(cma_preset_key_pressed_callback), pdialog); - - hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); - gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_EDGE); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - button = gtk_button_new_with_mnemonic(_("Ne_w")); - image = gtk_image_new_from_stock(GTK_STOCK_NEW, GTK_ICON_SIZE_BUTTON); - gtk_button_set_image(GTK_BUTTON(button), image); - gtk_container_add(GTK_CONTAINER(hbox), button); - g_signal_connect(button, "clicked", - G_CALLBACK(cma_add_preset_callback), pdialog); - pdialog->add_preset_command = button; - - pdialog->del_preset_command = gtk_button_new_from_stock(GTK_STOCK_DELETE); - gtk_container_add(GTK_CONTAINER(hbox), pdialog->del_preset_command); - g_signal_connect(pdialog->del_preset_command, "clicked", - G_CALLBACK(cma_del_preset_callback), pdialog); - - /* the right-hand side */ - - vbox = gtk_grid_new(); - g_object_set(vbox, "margin", 2, NULL); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(page), vbox); - - /* Result */ - if (!tiny) { - frame = gtk_frame_new(_("Results")); - gtk_widget_set_vexpand(frame, TRUE); - gtk_widget_set_valign(frame, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(vbox), frame); - - pdialog->result_label = - gtk_label_new("food\n prod\n trade\n\n people\n grow\n prod\n name"); - gtk_widget_set_name(pdialog->result_label, "city_label"); - gtk_container_add(GTK_CONTAINER(frame), pdialog->result_label); - gtk_label_set_justify(GTK_LABEL(pdialog->result_label), GTK_JUSTIFY_LEFT); - } else { - pdialog->result_label = NULL; - } - - /* Minimal Surplus and Factor */ - - table = gtk_grid_new(); - g_object_set(table, "margin", 2, NULL); - gtk_container_add(GTK_CONTAINER(vbox), table); - - label = gtk_label_new(_("Minimal Surplus")); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_grid_attach(GTK_GRID(table), label, 1, 0, 1, 1); - label = gtk_label_new(_("Factor")); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_grid_attach(GTK_GRID(table), label, 2, 0, 1, 1); - - output_type_iterate(i) { - label = gtk_label_new(get_output_name(i)); - gtk_grid_attach(GTK_GRID(table), label, 0, i + 1, 1, 1); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - - pdialog->minimal_surplus[i] = hscale = - gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, NULL); - gtk_range_set_range(GTK_RANGE(hscale), - GUI_GTK_OPTION(governor_range_min), - GUI_GTK_OPTION(governor_range_max)); - gtk_range_set_increments(GTK_RANGE(hscale), 1, 1); - pango_layout_get_pixel_size(gtk_scale_get_layout(GTK_SCALE(hscale)), - &layout_width, NULL); - gtk_widget_set_size_request(hscale, layout_width + 51 * 2, -1); - - gtk_grid_attach(GTK_GRID(table), hscale, 1, i + 1, 1, 1); - gtk_scale_set_digits(GTK_SCALE(hscale), 0); - gtk_scale_set_value_pos(GTK_SCALE(hscale), GTK_POS_LEFT); - - g_signal_connect(pdialog->minimal_surplus[i], - "value-changed", - G_CALLBACK(hscale_changed), pdialog); - - pdialog->factor[i] = hscale = - gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, NULL); - gtk_range_set_range(GTK_RANGE(hscale), 0, 25); - gtk_range_set_increments(GTK_RANGE(hscale), 1, 1); - pango_layout_get_pixel_size(gtk_scale_get_layout(GTK_SCALE(hscale)), - &layout_width, NULL); - gtk_widget_set_size_request(hscale, layout_width + 26 * 2, -1); - - gtk_grid_attach(GTK_GRID(table), hscale, 2, i + 1, 1, 1); - gtk_scale_set_digits(GTK_SCALE(hscale), 0); - gtk_scale_set_value_pos(GTK_SCALE(hscale), GTK_POS_LEFT); - - g_signal_connect(pdialog->factor[i], "value-changed", - G_CALLBACK(hscale_changed), pdialog); - } output_type_iterate_end; - - /* Happy Surplus and Factor */ - - label = gtk_label_new(_("Celebrate")); - gtk_grid_attach(GTK_GRID(table), label, 0, O_LAST + 1, 1, 1); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - - pdialog->happy_button = gtk_check_button_new(); - gtk_widget_set_halign(pdialog->happy_button, GTK_ALIGN_END); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdialog->happy_button), - FALSE); - gtk_grid_attach(GTK_GRID(table), pdialog->happy_button, 1, O_LAST + 1, 1, 1); - - g_signal_connect(pdialog->happy_button, "toggled", - G_CALLBACK(hscale_changed), pdialog); - - pdialog->factor[O_LAST] = hscale = - gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, NULL); - gtk_range_set_range(GTK_RANGE(hscale), 0, 50); - gtk_range_set_increments(GTK_RANGE(hscale), 1, 1); - pango_layout_get_pixel_size(gtk_scale_get_layout(GTK_SCALE(hscale)), - &layout_width, NULL); - gtk_widget_set_size_request(hscale, layout_width + 51 * 2, -1); - - gtk_grid_attach(GTK_GRID(table), hscale, 2, O_LAST + 1, 1, 1); - gtk_scale_set_digits(GTK_SCALE(hscale), 0); - gtk_scale_set_value_pos(GTK_SCALE(hscale), GTK_POS_LEFT); - - g_signal_connect(pdialog->factor[O_LAST], - "value-changed", - G_CALLBACK(hscale_changed), pdialog); - - /* buttons */ - - hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); - gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_EDGE); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - button = gtk_button_new_from_stock(GTK_STOCK_HELP); - g_signal_connect(button, "clicked", - G_CALLBACK(help_callback), NULL); - gtk_container_add(GTK_CONTAINER(hbox), button); - gtk_button_box_set_child_non_homogeneous(GTK_BUTTON_BOX(hbox), - button, TRUE); - - pdialog->active_command = gtk_toggle_button_new(); - gtk_button_set_use_underline(GTK_BUTTON(pdialog->active_command), TRUE); - gtk_button_set_image_position(GTK_BUTTON(pdialog->active_command), - GTK_POS_TOP); - gtk_widget_set_name(pdialog->active_command, "comment_label"); - gtk_container_add(GTK_CONTAINER(hbox), pdialog->active_command); - - gtk_widget_show_all(pdialog->shell); - - dialog_list_prepend(dialog_list, pdialog); - - update_cma_preset_list(pdialog); - - gtk_tree_view_focus(GTK_TREE_VIEW(view)); - - /* refresh is done in refresh_city_dialog */ - - return pdialog; -} - -/**********************************************************************//** - Refreshes the cma dialog -**************************************************************************/ -void refresh_cma_dialog(struct city *pcity, enum cma_refresh refresh) -{ - struct cm_result *result = cm_result_new(pcity); - struct cm_parameter param; - struct cma_dialog *pdialog = get_cma_dialog(pcity); - int controlled = cma_is_city_under_agent(pcity, NULL); - - cmafec_get_fe_parameter(pcity, ¶m); - - if (pdialog->result_label != NULL) { - /* fill in result label */ - cm_result_from_main_map(result, pcity); - gtk_label_set_text(GTK_LABEL(pdialog->result_label), - cmafec_get_result_descr(pcity, result, ¶m)); - } - - /* if called from a hscale, we _don't_ want to do this */ - if (refresh != DONT_REFRESH_HSCALES) { - set_hscales(¶m, pdialog); - } - - gtk_widget_queue_draw(pdialog->preset_list); - - gtk_widget_set_sensitive(pdialog->active_command, can_client_issue_orders()); - - g_signal_handlers_disconnect_matched(pdialog->active_command, - G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, cma_active_callback, NULL); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdialog->active_command), - controlled); - g_signal_connect(pdialog->active_command, "clicked", - G_CALLBACK(cma_active_callback), pdialog); - - if (controlled) { - GtkWidget *image = gtk_image_new_from_stock( - GTK_STOCK_YES, GTK_ICON_SIZE_DND); - - gtk_button_set_image(GTK_BUTTON(pdialog->active_command), image); - gtk_button_set_label(GTK_BUTTON(pdialog->active_command), - _("Governor Enabl_ed")); - } else { - GtkWidget *image = gtk_image_new_from_stock( - GTK_STOCK_NO, GTK_ICON_SIZE_DND); - - gtk_button_set_image(GTK_BUTTON(pdialog->active_command), image); - gtk_button_set_label(GTK_BUTTON(pdialog->active_command), - _("Governor Disabl_ed")); - } - gtk_button_set_always_show_image(GTK_BUTTON(pdialog->active_command), TRUE); - - if (pdialog->result_label != NULL) { - gtk_widget_set_sensitive(pdialog->result_label, controlled); - } - - cm_result_destroy(result); -} - -/**********************************************************************//** - Fills in the preset list -**************************************************************************/ -static void update_cma_preset_list(struct cma_dialog *pdialog) -{ - char buf[BUFFER_SIZE]; - GtkTreeIter it; - int i; - - /* Fill preset list */ - gtk_list_store_clear(pdialog->store); - - /* Append the presets */ - if (cmafec_preset_num()) { - for (i = 0; i < cmafec_preset_num(); i++) { - fc_strlcpy(buf, cmafec_preset_get_descr(i), sizeof(buf)); - gtk_list_store_append(pdialog->store, &it); - gtk_list_store_set(pdialog->store, &it, 0, buf, -1); - } - } -} - -/**********************************************************************//** - Callback for selecting a preset from the preset view -**************************************************************************/ -static void cma_activate_preset_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - int preset_index; - const struct cm_parameter *pparam; - - preset_index = gtk_tree_path_get_indices(path) [0]; - - pparam = cmafec_preset_get_parameter(preset_index); - - /* save the change */ - cmafec_set_fe_parameter(pdialog->pcity, pparam); - - if (cma_is_city_under_agent(pdialog->pcity, NULL)) { - cma_release_city(pdialog->pcity); - cma_put_city_under_agent(pdialog->pcity, pparam); - } - refresh_city_dialog(pdialog->pcity); -} - -/**********************************************************************//** - Pops up a dialog to allow to name your new preset -**************************************************************************/ -static void cma_add_preset_callback(GtkWidget *w, gpointer data) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - const char *default_name; - GtkWidget *parent = gtk_widget_get_toplevel(pdialog->shell); - int index; - - if ((index = gtk_tree_selection_get_row(pdialog->selection)) != -1) { - default_name = cmafec_preset_get_descr(index); - } else { - default_name = _("new preset"); - } - - pdialog->name_shell = input_dialog_create(GTK_WINDOW(parent), - _("Name new preset"), - _("What should we name the preset?"), - default_name, - cma_preset_add_popup_callback, pdialog); -} - -/**********************************************************************//** - Callback for the add_preset popup -**************************************************************************/ -static void cma_preset_add_popup_callback(gpointer data, gint response, - const char *input) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - - if (pdialog) { - if (response == GTK_RESPONSE_OK) { - struct cm_parameter param; - - cmafec_get_fe_parameter(pdialog->pcity, ¶m); - cmafec_preset_add(input, ¶m); - update_cma_preset_list(pdialog); - refresh_cma_dialog(pdialog->pcity, DONT_REFRESH_HSCALES); - /* if this or other cities have this set as "custom" */ - city_report_dialog_update(); - } /* else CANCEL or DELETE_EVENT */ - - pdialog->name_shell = NULL; - } -} - -/**********************************************************************//** - Key pressed in preset list -**************************************************************************/ -static gboolean cma_preset_key_pressed_callback(GtkWidget *w, GdkEventKey *ev, - gpointer data) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - int index; - - if ((index = gtk_tree_selection_get_row(pdialog->selection)) == -1) { - return FALSE; - } - - if (ev->type == GDK_KEY_PRESS) { - switch (ev->keyval) { - case GDK_KEY_Delete: - cma_preset_remove(pdialog, index); - break; - case GDK_KEY_Insert: - cma_add_preset_callback(NULL, pdialog); - break; - default: - return FALSE; - } - return TRUE; - } - return FALSE; -} - -/**********************************************************************//** - Callback for del_preset -**************************************************************************/ -static void cma_del_preset_callback(GtkWidget *w, gpointer data) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - int index; - - if ((index = gtk_tree_selection_get_row(pdialog->selection)) == -1) { - return; - } - - cma_preset_remove(pdialog, index); -} - -/**********************************************************************//** - Pops up a dialog to remove a preset -**************************************************************************/ -static void cma_preset_remove(struct cma_dialog *pdialog, int preset_index) -{ - GtkWidget *parent = gtk_widget_get_toplevel(pdialog->shell), *shl; - - pdialog->id = preset_index; - shl = gtk_message_dialog_new(NULL, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, - _("Remove this preset?")); - setup_dialog(shl, parent); - pdialog->preset_remove_shell = shl; - - gtk_window_set_title(GTK_WINDOW(shl), cmafec_preset_get_descr(preset_index)); - gtk_window_set_position(GTK_WINDOW(shl), GTK_WIN_POS_CENTER_ON_PARENT); - - g_signal_connect(shl, "response", - G_CALLBACK(cma_preset_remove_response), pdialog); - - gtk_window_present(GTK_WINDOW(shl)); -} - -/**********************************************************************//** - Callback for the remove_preset popup -**************************************************************************/ -static void cma_preset_remove_response(GtkWidget *w, gint response, - gpointer data) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - - if (response == GTK_RESPONSE_YES) { - cmafec_preset_remove(pdialog->id); - pdialog->id = -1; - update_cma_preset_list(pdialog); - refresh_cma_dialog(pdialog->pcity, DONT_REFRESH_HSCALES); - /* if this or other cities have this set, reset to "custom" */ - city_report_dialog_update(); - } - gtk_widget_destroy(w); - - pdialog->preset_remove_shell = NULL; -} - -/**********************************************************************//** - Activates/deactivates agent control. -**************************************************************************/ -static void cma_active_callback(GtkWidget *w, gpointer data) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - - if (cma_is_city_under_agent(pdialog->pcity, NULL)) { - cma_release_city(pdialog->pcity); - } else { - struct cm_parameter param; - - cmafec_get_fe_parameter(pdialog->pcity, ¶m); - cma_put_city_under_agent(pdialog->pcity, ¶m); - } - refresh_city_dialog(pdialog->pcity); -} - -/**********************************************************************//** - Called to adjust the sliders when a preset is selected - notice that we don't want to call update_result here. -**************************************************************************/ -static void set_hscales(const struct cm_parameter *const parameter, - struct cma_dialog *pdialog) -{ - allow_refreshes = 0; - output_type_iterate(i) { - gtk_range_set_value(GTK_RANGE(pdialog->minimal_surplus[i]), - parameter->minimal_surplus[i]); - gtk_range_set_value(GTK_RANGE(pdialog->factor[i]), parameter->factor[i]); - } output_type_iterate_end; - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdialog->happy_button), - parameter->require_happy); - gtk_range_set_value(GTK_RANGE(pdialog->factor[O_LAST]), - parameter->happy_factor); - allow_refreshes = 1; -} - -/**********************************************************************//** - Callback if we moved the sliders. -**************************************************************************/ -static void hscale_changed(GtkWidget *get, gpointer data) -{ - struct cma_dialog *pdialog = (struct cma_dialog *) data; - struct cm_parameter param; - - if (!allow_refreshes) { - return; - } - - cmafec_get_fe_parameter(pdialog->pcity, ¶m); - output_type_iterate(i) { - param.minimal_surplus[i] = - (int) (gtk_range_get_value(GTK_RANGE(pdialog->minimal_surplus[i]))); - param.factor[i] = - (int) (gtk_range_get_value(GTK_RANGE(pdialog->factor[i]))); - } output_type_iterate_end; - param.require_happy = - (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->happy_button)) ? 1 : 0); - param.happy_factor = - (int) (gtk_range_get_value(GTK_RANGE(pdialog->factor[O_LAST]))); - - /* save the change */ - cmafec_set_fe_parameter(pdialog->pcity, ¶m); - - /* refreshes the cma */ - if (cma_is_city_under_agent(pdialog->pcity, NULL)) { - cma_release_city(pdialog->pcity); - cma_put_city_under_agent(pdialog->pcity, ¶m); - refresh_city_dialog(pdialog->pcity); - } else { - refresh_cma_dialog(pdialog->pcity, DONT_REFRESH_HSCALES); - } -} diff --git a/client/gui-gtk-3.0/cma_fe.h b/client/gui-gtk-3.0/cma_fe.h deleted file mode 100644 index 44f0ecd461..0000000000 --- a/client/gui-gtk-3.0/cma_fe.h +++ /dev/null @@ -1,55 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 2001 - R. Falke - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifndef FC__GTK_CMA_H -#define FC__GTK_CMA_H - -#include - -/* common */ -#include "fc_types.h" - -/* client/agents */ -#include "cma_core.h" - -enum cma_refresh { - REFRESH_ALL, - DONT_REFRESH_SELECT, - DONT_REFRESH_HSCALES -}; - -struct cma_dialog { - struct city *pcity; - GtkWidget *shell; - GtkWidget *name_shell; - GtkWidget *preset_remove_shell; - GtkWidget *preset_list; - GtkWidget *result_label; - GtkWidget *add_preset_command; - GtkWidget *del_preset_command; - GtkWidget *active_command; - GtkWidget *minimal_surplus[O_LAST]; - GtkWidget *happy_button; - GtkWidget *factor[O_LAST + 1]; - GtkTreeSelection *selection; - GtkListStore *store; - int id; /* needed to pass a preset_index */ -}; - -void cma_fe_init(void); -void cma_fe_done(void); -struct cma_dialog *create_cma_dialog(struct city *pcity, bool tiny); -void close_cma_dialog(struct city *pcity); -void refresh_cma_dialog(struct city *pcity, enum cma_refresh refresh); - -#endif /* FC__GTK_CMA_H */ diff --git a/client/gui-gtk-3.0/colors.c b/client/gui-gtk-3.0/colors.c deleted file mode 100644 index f0eee5f8b2..0000000000 --- a/client/gui-gtk-3.0/colors.c +++ /dev/null @@ -1,99 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -/* utility */ -#include "log.h" -#include "mem.h" - -/* common */ -#include "rgbcolor.h" - -/* client/gui-gtk-3.0 */ -#include "gui_main.h" - -#include "colors.h" - -/************************************************************************//** - Get display color type of default visual -****************************************************************************/ -enum Display_color_type get_visual(void) -{ - GdkVisual *visual; - GdkVisualType type; - - visual = gdk_screen_get_system_visual(gdk_screen_get_default()); - type = gdk_visual_get_visual_type(visual); - - if (type == GDK_VISUAL_STATIC_GRAY) { - /* StaticGray, use black and white */ - log_verbose("found B/W display."); - return BW_DISPLAY; - } - - if (type < GDK_VISUAL_STATIC_COLOR) { - /* No color visual available at default depth */ - log_verbose("found grayscale(?) display."); - return GRAYSCALE_DISPLAY; - } - - log_verbose("color system booted ok."); - - return COLOR_DISPLAY; -} - -/************************************************************************//** - Allocate a color (well, sort of) - and return a pointer to it. -****************************************************************************/ -struct color *color_alloc(int r, int g, int b) -{ - struct color *color = fc_malloc(sizeof(*color)); - - color->color.red = (double)r/255; - color->color.green = (double)g/255; - color->color.blue = (double)b/255; - color->color.alpha = 1.0; - - return color; -} - -/************************************************************************//** - Free a previously allocated color. See color_alloc. -****************************************************************************/ -void color_free(struct color *color) -{ - free(color); -} - -/************************************************************************//** - Return a number indicating the perceptual brightness of this color - relative to others (larger is brighter). -****************************************************************************/ -int color_brightness_score(struct color *pcolor) -{ - struct rgbcolor *prgb = rgbcolor_new(pcolor->color.red * 255, - pcolor->color.green * 255, - pcolor->color.blue * 255); - int score = rgbcolor_brightness_score(prgb); - - rgbcolor_destroy(prgb); - return score; -} diff --git a/client/gui-gtk-3.0/colors.h b/client/gui-gtk-3.0/colors.h deleted file mode 100644 index 6e239e058d..0000000000 --- a/client/gui-gtk-3.0/colors.h +++ /dev/null @@ -1,30 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__COLORS_H -#define FC__COLORS_H - -#include - -#include "colors_g.h" - -struct color { - GdkRGBA color; -}; - -enum Display_color_type { - BW_DISPLAY, GRAYSCALE_DISPLAY, COLOR_DISPLAY -}; - -enum Display_color_type get_visual(void); - -#endif /* FC__COLORS_H */ diff --git a/client/gui-gtk-3.0/connectdlg.c b/client/gui-gtk-3.0/connectdlg.c deleted file mode 100644 index 2ade39450e..0000000000 --- a/client/gui-gtk-3.0/connectdlg.c +++ /dev/null @@ -1,60 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "packets.h" -#include "support.h" -#include "version.h" - -/* client */ -#include "client_main.h" -#include "chatline.h" -#include "colors.h" -#include "connectdlg_common.h" -#include "dialogs.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "options.h" -#include "packhand.h" -#include "tilespec.h" - -#include "connectdlg.h" - - -/**********************************************************************//** - Close and destroy the dialog. -**************************************************************************/ -void close_connection_dialog(void) -{ -} - -/**********************************************************************//** - gtk client does nothing here. This gets called when one is rejected - from game. -**************************************************************************/ -void server_connect(void) -{ -} diff --git a/client/gui-gtk-3.0/connectdlg.h b/client/gui-gtk-3.0/connectdlg.h deleted file mode 100644 index 9009f74381..0000000000 --- a/client/gui-gtk-3.0/connectdlg.h +++ /dev/null @@ -1,18 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__CONNECTDLG_H -#define FC__CONNECTDLG_H - -#include "connectdlg_g.h" - -#endif /* FC__CONNECTDLG_H */ diff --git a/client/gui-gtk-3.0/dialogs.c b/client/gui-gtk-3.0/dialogs.c deleted file mode 100644 index ab1eaf4300..0000000000 --- a/client/gui-gtk-3.0/dialogs.c +++ /dev/null @@ -1,1583 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include -#include - -/* utility */ -#include "astring.h" -#include "bitvector.h" -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "rand.h" -#include "support.h" - -/* common */ -#include "game.h" -#include "government.h" -#include "map.h" -#include "packets.h" -#include "player.h" - -/* client */ -#include "client_main.h" -#include "climisc.h" -#include "connectdlg_common.h" -#include "control.h" -#include "helpdata.h" /* for helptext_nation() */ -#include "goto.h" -#include "options.h" -#include "packhand.h" -#include "text.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "choice_dialog.h" -#include "citydlg.h" -#include "editprop.h" -#include "graphics.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapview.h" -#include "plrdlg.h" -#include "wldlg.h" -#include "unitselect.h" -#include "unitselextradlg.h" - -#include "dialogs.h" - -/******************************************************************/ -static GtkWidget *races_shell; -static GtkWidget *nationsets_chooser; -struct player *races_player; -/* One entry per nation group, plus one at the end for 'all nations' */ -static GtkWidget *races_nation_list[MAX_NUM_NATION_GROUPS + 1]; -static GtkWidget *races_notebook; -static GtkWidget *races_properties; -static GtkWidget *races_leader; -static GtkWidget *races_sex[2]; -static GtkWidget *races_style_list; -static GtkTextBuffer *races_text; - -static void create_races_dialog(struct player *pplayer); -static void races_response(GtkWidget *w, gint response, gpointer data); -static void races_nation_callback(GtkTreeSelection *select, gpointer data); -static void races_leader_callback(void); -static void races_sex_callback(GtkWidget *w, gpointer data); -static void races_style_callback(GtkTreeSelection *select, gpointer data); -static gboolean races_selection_func(GtkTreeSelection *select, - GtkTreeModel *model, GtkTreePath *path, - gboolean selected, gpointer data); - -static int selected_nation; -static int selected_sex; -static int selected_style; - -static int is_showing_pillage_dialog = FALSE; - -/**********************************************************************//** - Popup a generic dialog to display some generic information. -**************************************************************************/ -void popup_notify_dialog(const char *caption, const char *headline, - const char *lines) -{ - static struct gui_dialog *shell; - GtkWidget *vbox, *label, *headline_label, *sw; - - gui_dialog_new(&shell, GTK_NOTEBOOK(bottom_notebook), NULL, TRUE); - gui_dialog_set_title(shell, caption); - - gui_dialog_add_button(shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - gui_dialog_set_default_response(shell, GTK_RESPONSE_CLOSE); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 2); - gtk_container_add(GTK_CONTAINER(shell->vbox), vbox); - - headline_label = gtk_label_new(headline); - gtk_container_add(GTK_CONTAINER(vbox), headline_label); - gtk_widget_set_name(headline_label, "notify_label"); - - gtk_label_set_justify(GTK_LABEL(headline_label), GTK_JUSTIFY_LEFT); - gtk_widget_set_halign(headline_label, GTK_ALIGN_START); - gtk_widget_set_valign(headline_label, GTK_ALIGN_START); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - label = gtk_label_new(lines); - gtk_widget_set_hexpand(label, TRUE); - gtk_widget_set_vexpand(label, TRUE); - gtk_container_add(GTK_CONTAINER(sw), label); - - gtk_widget_set_name(label, "notify_label"); - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_START); - - gtk_container_add(GTK_CONTAINER(vbox), sw); - - gui_dialog_show_all(shell); - - gui_dialog_set_default_size(shell, -1, 265); - gui_dialog_present(shell); - - shell = NULL; -} - -/**********************************************************************//** - User has responded to notify dialog with possibility to - center (goto) on event location. -**************************************************************************/ -static void notify_goto_response(GtkWidget *w, gint response) -{ - struct city *pcity = NULL; - struct tile *ptile = g_object_get_data(G_OBJECT(w), "tile"); - - switch (response) { - case 1: - center_tile_mapcanvas(ptile); - break; - case 2: - pcity = tile_city(ptile); - - if (gui_options.center_when_popup_city) { - center_tile_mapcanvas(ptile); - } - - if (pcity) { - popup_city_dialog(pcity); - } - break; - } - gtk_widget_destroy(w); -} - -/**********************************************************************//** - User clicked close for connect message dialog -**************************************************************************/ -static void notify_connect_msg_response(GtkWidget *w, gint response) -{ - gtk_widget_destroy(w); -} - -/**********************************************************************//** - Popup a dialog to display information about an event that has a - specific location. The user should be given the option to goto that - location. -**************************************************************************/ -void popup_notify_goto_dialog(const char *headline, const char *lines, - const struct text_tag_list *tags, - struct tile *ptile) -{ - GtkWidget *shell, *label, *goto_command, *popcity_command; - - shell = gtk_dialog_new(); - gtk_window_set_title(GTK_WINDOW(shell), headline); - setup_dialog(shell, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CLOSE); - gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT); - - label = gtk_label_new(lines); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))), label); - gtk_widget_show(label); - - goto_command = gtk_stockbutton_new(GTK_STOCK_JUMP_TO, - _("Goto _Location")); - gtk_dialog_add_action_widget(GTK_DIALOG(shell), goto_command, 1); - gtk_widget_show(goto_command); - - popcity_command = gtk_stockbutton_new(GTK_STOCK_ZOOM_IN, - _("I_nspect City")); - gtk_dialog_add_action_widget(GTK_DIALOG(shell), popcity_command, 2); - gtk_widget_show(popcity_command); - - gtk_dialog_add_button(GTK_DIALOG(shell), GTK_STOCK_CLOSE, - GTK_RESPONSE_CLOSE); - - if (!ptile) { - gtk_widget_set_sensitive(goto_command, FALSE); - gtk_widget_set_sensitive(popcity_command, FALSE); - } else { - struct city *pcity; - - pcity = tile_city(ptile); - gtk_widget_set_sensitive(popcity_command, - (NULL != pcity && city_owner(pcity) == client.conn.playing)); - } - - g_object_set_data(G_OBJECT(shell), "tile", ptile); - - g_signal_connect(shell, "response", G_CALLBACK(notify_goto_response), NULL); - gtk_widget_show(shell); -} - -/**********************************************************************//** - Popup a dialog to display connection message from server. -**************************************************************************/ -void popup_connect_msg(const char *headline, const char *message) -{ - GtkWidget *shell, *label; - - shell = gtk_dialog_new(); - gtk_window_set_title(GTK_WINDOW(shell), headline); - setup_dialog(shell, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CLOSE); - gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT); - - label = gtk_label_new(message); - gtk_label_set_selectable(GTK_LABEL(label), 1); - - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))), label); - gtk_widget_show(label); - - gtk_dialog_add_button(GTK_DIALOG(shell), GTK_STOCK_CLOSE, - GTK_RESPONSE_CLOSE); - - g_signal_connect(shell, "response", G_CALLBACK(notify_connect_msg_response), - NULL); - gtk_widget_show(shell); -} - -/**********************************************************************//** - User has responded to revolution dialog -**************************************************************************/ -static void revolution_response(GtkWidget *w, gint response, gpointer data) -{ - struct government *government = data; - - if (response == GTK_RESPONSE_YES) { - if (!government) { - start_revolution(); - } else { - set_government_choice(government); - } - } - if (w) { - gtk_widget_destroy(w); - } -} - -/**********************************************************************//** - Popup revolution dialog for user -**************************************************************************/ -void popup_revolution_dialog(struct government *government) -{ - static GtkWidget *shell = NULL; - - if (0 > client.conn.playing->revolution_finishes) { - if (!shell) { - shell = gtk_message_dialog_new(NULL, - 0, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_YES_NO, - _("You say you wanna revolution?")); - gtk_window_set_title(GTK_WINDOW(shell), _("Revolution!")); - setup_dialog(shell, toplevel); - - g_signal_connect(shell, "destroy", - G_CALLBACK(gtk_widget_destroyed), &shell); - } - g_signal_connect(shell, "response", - G_CALLBACK(revolution_response), government); - - gtk_window_present(GTK_WINDOW(shell)); - } else { - revolution_response(shell, GTK_RESPONSE_YES, government); - } -} - -/**********************************************************************//** - Callback for pillage dialog. -**************************************************************************/ -static void pillage_callback(GtkWidget *dlg, gint arg) -{ - is_showing_pillage_dialog = FALSE; - - if (arg == GTK_RESPONSE_YES) { - int au_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg), - "actor")); - struct unit *actor = game_unit_by_number(au_id); - - int tgt_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg), - "target")); - struct extra_type *tgt_extra = extra_by_number(tgt_id); - - if (actor && tgt_extra) { - request_new_unit_activity_targeted(actor, ACTIVITY_PILLAGE, - tgt_extra); - } - } - - gtk_widget_destroy(dlg); -} - -/**********************************************************************//** - Opens pillage dialog listing possible pillage targets. -**************************************************************************/ -void popup_pillage_dialog(struct unit *punit, bv_extras extras) -{ - if (!is_showing_pillage_dialog) { - /* Possibly legal target extras. */ - bv_extras alternative; - /* Selected by default. */ - struct extra_type *preferred_tgt; - /* Current target to check. */ - struct extra_type *tgt; - - is_showing_pillage_dialog = TRUE; - - BV_CLR_ALL(alternative); - preferred_tgt = get_preferred_pillage(extras); - - while ((tgt = get_preferred_pillage(extras))) { - int what; - - what = extra_index(tgt); - BV_CLR(extras, what); - BV_SET(alternative, what); - } - - select_tgt_extra(punit, unit_tile(punit), alternative, preferred_tgt, - /* TRANS: Pillage dialog title. */ - _("What To Pillage"), - /* TRANS: Pillage dialog actor text. */ - _("Looking for target extra:"), - /* TRANS: Pillage dialog target text. */ - _("Select what to pillage:"), - /* TRANS: Pillage dialog do button text. */ - _("Pillage"), G_CALLBACK(pillage_callback)); - } -} - -/**********************************************************************//** - Popup unit selection dialog. It is a wrapper for the main function; see - unitselect.c:unit_select_dialog_popup_main(). -**************************************************************************/ -void unit_select_dialog_popup(struct tile *ptile) -{ - unit_select_dialog_popup_main(ptile, TRUE); -} - -/**********************************************************************//** - Update unit selection dialog. It is a wrapper for the main function; see - unitselect.c:unit_select_dialog_popup_main(). -**************************************************************************/ -void unit_select_dialog_update_real(void *unused) -{ - unit_select_dialog_popup_main(NULL, FALSE); -} - -/************************************************************************** - NATION SELECTION DIALOG -**************************************************************************/ -/**********************************************************************//** - Return the GtkTreePath for a given nation on the specified list, or NULL - if it's not there at all. - Caller must free with gtk_tree_path_free(). -**************************************************************************/ -static GtkTreePath *path_to_nation_on_list(Nation_type_id nation, - GtkTreeView *list) -{ - if (nation == -1 || list == NULL) { - return NULL; - } else { - GtkTreeModel *model = gtk_tree_view_get_model(list); - GtkTreeIter iter; - GtkTreePath *path = NULL; - gtk_tree_model_get_iter_first(model, &iter); - do { - int nation_of_row; - gtk_tree_model_get(model, &iter, 0, &nation_of_row, -1); - if (nation == nation_of_row) { - path = gtk_tree_model_get_path(model, &iter); - break; - } - } while (gtk_tree_model_iter_next(model, &iter)); - return path; - } -} - -/**********************************************************************//** - Make sure the given nation is selected in the list on a given groups - notebook tab, if it's present on that tab. - Intended for synchronising the tabs to the current selection, so does not - disturb the controls on the right-hand side. -**************************************************************************/ -static void select_nation_on_tab(GtkWidget *tab_list, int nation) -{ - /* tab_list is a GtkTreeView (not its enclosing GtkScrolledWindow). */ - GtkTreeView *list = GTK_TREE_VIEW(tab_list); - GtkTreeSelection *select = gtk_tree_view_get_selection(GTK_TREE_VIEW(list)); - GtkTreePath *path = path_to_nation_on_list(nation, list); - - /* Suppress normal effects of selection change to avoid loops. */ - g_signal_handlers_block_by_func(select, races_nation_callback, NULL); - if (path) { - /* Found nation on this list. */ - /* Avoid disturbing tabs that already have the correct selection. */ - if (!gtk_tree_selection_path_is_selected(select, path)) { - /* Set cursor -- this will cause the nation to be selected */ - gtk_tree_view_set_cursor(list, path, NULL, FALSE); - /* Make sure selected nation is visible in list */ - gtk_tree_view_scroll_to_cell(list, path, NULL, FALSE, 0, 0); - } - } else { - /* Either no nation was selected, or the nation is not mentioned in - * this tab. Either way we want to end up with no selection. */ - GtkTreePath *cursorpath; - - /* If there is no cursor, Gtk tends to focus and select the first row - * at the first opportunity, disturbing any existing state. We want to - * allow the no-rows-selected state, so detect this case and defuse - * it by setting a cursor. */ - gtk_tree_view_get_cursor(list, &cursorpath, NULL); - /* Set the cursor in the case above, or if there was a previous - * selection */ - if (!cursorpath || gtk_tree_selection_get_selected(select, NULL, NULL)) { - cursorpath = gtk_tree_path_new_first(); - gtk_tree_view_set_cursor(list, cursorpath, NULL, FALSE); - } - gtk_tree_selection_unselect_all(select); - gtk_tree_path_free(cursorpath); - } - gtk_tree_path_free(path); - /* Re-enable selection change side-effects */ - g_signal_handlers_unblock_by_func(select, races_nation_callback, NULL); -} - -/**********************************************************************//** - Select the given nation in the nation lists in the left-hand-side notebook. -**************************************************************************/ -static void sync_tabs_to_nation(int nation) -{ - /* Ensure that all tabs are in sync with the new selection */ - int i; - - for (i = 0; i <= nation_group_count(); i++) { - if (races_nation_list[i]) { - select_nation_on_tab(races_nation_list[i], nation); - } - } -} - -/**********************************************************************//** - Populates leader list. - If no nation selected, blanks it. -**************************************************************************/ -static void populate_leader_list(void) -{ - int i; - GtkListStore *model = - GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(races_leader))); - - i = 0; - gtk_list_store_clear(model); - if (selected_nation >= 0) { - nation_leader_list_iterate(nation_leaders(nation_by_number - (selected_nation)), pleader) { - const char *leader_name = nation_leader_name(pleader); - GtkTreeIter iter; /* unused */ - - gtk_list_store_insert_with_values(model, &iter, i, 0, leader_name, -1); - i++; - } nation_leader_list_iterate_end; - } -} - -/**********************************************************************//** - Update dialog state by selecting a nation and choosing values for its - parameters, and update the right-hand side of the dialog accordingly. - If 'leadername' is NULL, pick a random leader name and sex from the - nation's list (ignoring the 'is_male' parameter). -**************************************************************************/ -static void select_nation(int nation, - const char *leadername, bool is_male, - int style_id) -{ - selected_nation = nation; - - /* Refresh the available leaders. */ - populate_leader_list(); - - if (selected_nation != -1) { - - /* Select leader name and sex. */ - if (leadername) { - gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))), - leadername); - /* Assume is_male is valid too. */ - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex[is_male]), - TRUE); - } else { - int idx = fc_rand(nation_leader_list_size( - nation_leaders(nation_by_number(selected_nation)))); - - gtk_combo_box_set_active(GTK_COMBO_BOX(races_leader), idx); - /* This also updates the leader sex, eventually. */ - } - - /* Select the appropriate city style entry. */ - { - int i; - int j = 0; - GtkTreePath *path; - - styles_iterate(pstyle) { - i = basic_city_style_for_style(pstyle); - - if (i >= 0 && i < style_id) { - j++; - } else { - break; - } - } styles_iterate_end; - - path = gtk_tree_path_new(); - gtk_tree_path_append_index(path, j); - gtk_tree_view_set_cursor(GTK_TREE_VIEW(races_style_list), path, - NULL, FALSE); - gtk_tree_path_free(path); - } - - /* Update nation description. */ - { - char buf[4096]; - - helptext_nation(buf, sizeof(buf), - nation_by_number(selected_nation), NULL); - gtk_text_buffer_set_text(races_text, buf, -1); - } - - gtk_widget_set_sensitive(races_properties, TRUE); - /* Once we've made a nation selection, allow user to ok */ - gtk_dialog_set_response_sensitive(GTK_DIALOG(races_shell), - GTK_RESPONSE_ACCEPT, TRUE); - } else { - /* No nation selected. Blank properties and make controls insensitive. */ - /* Leader name */ - gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))), - ""); - /* Leader sex (*shrug*) */ - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex[0]), TRUE); - /* City style */ - { - GtkTreeSelection* select - = gtk_tree_view_get_selection(GTK_TREE_VIEW(races_style_list)); - - gtk_tree_selection_unselect_all(select); - } - /* Nation description */ - gtk_text_buffer_set_text(races_text, "", 0); - - gtk_widget_set_sensitive(races_properties, FALSE); - /* Don't allow OK without a selection - * (user can still do "Random Nation") */ - gtk_dialog_set_response_sensitive(GTK_DIALOG(races_shell), - GTK_RESPONSE_ACCEPT, FALSE); - } - - /* Update notebook to reflect the current selection */ - sync_tabs_to_nation(selected_nation); -} - -/**********************************************************************//** - Creates a list of currently-pickable nations in the given group - Inserts appropriate gtk_tree_view into races_nation_list[index] (or NULL if - the group has no nations) - If group == NULL, create a list of all nations -**************************************************************************/ -static GtkWidget* create_list_of_nations_in_group(struct nation_group* group, - int index) -{ - GtkWidget *sw = NULL; - GtkListStore *store = NULL; - GtkWidget *list = NULL; - - /* Populate nation list store. */ - nations_iterate(pnation) { - bool used; - GdkPixbuf *img; - GtkTreeIter it; - GValue value = { 0, }; - - if (!is_nation_playable(pnation) || !is_nation_pickable(pnation)) { - continue; - } - - if (NULL != group && !nation_is_in_group(pnation, group)) { - continue; - } - - /* Only create tab on demand -- we don't want it if there aren't any - * currently pickable nations in this group. */ - if (sw == NULL) { - GtkTreeSelection *select; - GtkCellRenderer *render; - GtkTreeViewColumn *column; - - store = gtk_list_store_new(4, G_TYPE_INT, G_TYPE_BOOLEAN, - GDK_TYPE_PIXBUF, G_TYPE_STRING); - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), - 3, GTK_SORT_ASCENDING); - - list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(list, TRUE); - gtk_widget_set_vexpand(list, TRUE); - gtk_tree_view_set_search_column(GTK_TREE_VIEW(list), 3); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE); - g_object_unref(store); - - select = gtk_tree_view_get_selection(GTK_TREE_VIEW(list)); - g_signal_connect(select, "changed", G_CALLBACK(races_nation_callback), - NULL); - gtk_tree_selection_set_select_function(select, races_selection_func, - NULL, NULL); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(sw), list); - - render = gtk_cell_renderer_pixbuf_new(); - column = gtk_tree_view_column_new_with_attributes("Flag", render, - "pixbuf", 2, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(list), column); - render = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes("Nation", render, - "text", 3, "strikethrough", 1, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(list), column); - } - - gtk_list_store_append(store, &it); - - used = (pnation->player != NULL && pnation->player != races_player); - gtk_list_store_set(store, &it, 0, nation_number(pnation), 1, used, -1); - img = get_flag(pnation); - if (img != NULL) { - gtk_list_store_set(store, &it, 2, img, -1); - g_object_unref(img); - } - - g_value_init(&value, G_TYPE_STRING); - g_value_set_static_string(&value, nation_adjective_translation(pnation)); - gtk_list_store_set_value(store, &it, 3, &value); - g_value_unset(&value); - } nations_iterate_end; - - races_nation_list[index] = list; - return sw; -} - -/**********************************************************************//** - Creates lists of nations for left side of nation selection dialog -**************************************************************************/ -static void create_nation_selection_lists(void) -{ - GtkWidget *nation_list; - GtkWidget *group_name_label; - int i; - - for (i = 0; i < nation_group_count(); i++) { - struct nation_group* group = (nation_group_by_number(i)); - - if (is_nation_group_hidden(group)) { - races_nation_list[i] = NULL; - continue; - } - nation_list = create_list_of_nations_in_group(group, i); - if (nation_list) { - group_name_label = gtk_label_new(nation_group_name_translation(group)); - gtk_notebook_append_page(GTK_NOTEBOOK(races_notebook), nation_list, - group_name_label); - } - } - - nation_list = create_list_of_nations_in_group(NULL, nation_group_count()); - /* Even this list can be empty if there are no pickable nations (due to - * a combination of start position and nationset restrictions). */ - if (nation_list) { - group_name_label = gtk_label_new(_("All")); - gtk_notebook_append_page(GTK_NOTEBOOK(races_notebook), nation_list, - group_name_label); - } -} - -/**********************************************************************//** - The server has changed the set of selectable nations. - Update any current nations dialog accordingly. -**************************************************************************/ -void races_update_pickable(bool nationset_change) -{ - int tab, groupidx; - - if (!races_shell) { - return; - } - - /* Save selected tab */ - tab = gtk_notebook_get_current_page(GTK_NOTEBOOK(races_notebook)); - if (tab != -1) { - int i = 0; - - groupidx = 0; - /* Turn tab index into a nation group index (they're not always equal, - * as some groups may not currently have tabs). */ - do { - while (groupidx <= nation_group_count() - && races_nation_list[groupidx] == NULL) { - groupidx++; - } - fc_assert_action(groupidx <= nation_group_count(), break); - /* Nation group 'groupidx' is what's displayed on the i'th tab */ - if (i == tab) { - break; - } - i++; - groupidx++; - } while (TRUE); - } else { - /* No tabs currently */ - groupidx = -1; - } - - /* selected_nation already contains currently selected nation; however, - * it may no longer be a valid choice */ - if (selected_nation != -1 - && !is_nation_pickable(nation_by_number(selected_nation))) { - select_nation(-1, NULL, FALSE, 0); - } - - /* Delete all list stores, treeviews, tabs */ - while (gtk_notebook_get_n_pages(GTK_NOTEBOOK(races_notebook)) > 0) { - gtk_notebook_remove_page(GTK_NOTEBOOK(races_notebook), -1); - } - - /* (Re)create all of them */ - create_nation_selection_lists(); - - /* Can't set current tab before child widget is visible */ - gtk_widget_show_all(GTK_WIDGET(races_notebook)); - - /* Restore selected tab */ - if (groupidx != -1 && races_nation_list[groupidx] != NULL) { - int i; - - tab = 0; - for (i = 0; i < groupidx; i++) { - if (races_nation_list[i] != NULL) { - tab++; - } - } - gtk_notebook_set_current_page(GTK_NOTEBOOK(races_notebook), tab); - } - - /* Restore selected nation */ - sync_tabs_to_nation(selected_nation); -} - -/**********************************************************************//** - Sync nationset control with the current state of the server. -**************************************************************************/ -void nationset_sync_to_server(const char *nationset) -{ - if (nationsets_chooser) { - struct nation_set *set = nation_set_by_setting_value(nationset); - - gtk_combo_box_set_active(GTK_COMBO_BOX(nationsets_chooser), - nation_set_index(set)); - } -} - -/**********************************************************************//** - Called when the nationset control's value has changed. -**************************************************************************/ -static void nationset_callback(GtkComboBox *b, gpointer data) -{ - GtkTreeIter iter; - if (gtk_combo_box_get_active_iter(b, &iter)) { - struct option *poption = optset_option_by_name(server_optset, "nationset"); - gchar *rule_name; - - gtk_tree_model_get(gtk_combo_box_get_model(b), &iter, - 0, &rule_name, -1); - /* Suppress propagation of an option value equivalent to the current - * server state, after canonicalisation, to avoid loops from - * nationset_sync_to_server(). - * (HACK: relies on local Gtk "changed" signal getting here before - * server response.) */ - if (nation_set_by_setting_value(option_str_get(poption)) - != nation_set_by_rule_name(rule_name)) { - option_str_set(poption, rule_name); - } - FC_FREE(rule_name); - } -} - -/**********************************************************************//** - Create nations dialog -**************************************************************************/ -static void create_races_dialog(struct player *pplayer) -{ - GtkWidget *shell; - GtkWidget *cmd; - GtkWidget *hbox, *table; - GtkWidget *frame, *label, *combo; - GtkWidget *text; - GtkWidget *notebook; - GtkWidget *sw; - GtkWidget *list; - GtkListStore *store; - GtkCellRenderer *render; - GtkTreeViewColumn *column; - int i; - char *title; - - /* Init. */ - selected_nation = -1; - - if (C_S_RUNNING == client_state()) { - title = _("Edit Nation"); - } else if (NULL != pplayer && pplayer == client.conn.playing) { - title = _("What Nation Will You Be?"); - } else { - title = _("Pick Nation"); - } - - shell = gtk_dialog_new_with_buttons(title, - NULL, - 0, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - _("_Random Nation"), - GTK_RESPONSE_NO, /* arbitrary */ - GTK_STOCK_OK, - GTK_RESPONSE_ACCEPT, - NULL); - races_shell = shell; - races_player = pplayer; - setup_dialog(shell, toplevel); - - gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT); - gtk_window_set_default_size(GTK_WINDOW(shell), -1, 590); - - frame = gtk_frame_new(_("Select a nation")); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))), frame); - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 18); - gtk_container_set_border_width(GTK_CONTAINER(hbox), 3); - gtk_container_add(GTK_CONTAINER(frame), hbox); - - /* Left side: nation list */ - { - GtkWidget* nation_selection_list = gtk_grid_new(); - - nationsets_chooser = NULL; - - gtk_grid_set_row_spacing(GTK_GRID(nation_selection_list), 2); - - /* Nationset selector dropdown */ - /* Only present this if there is more than one choice. - * (If ruleset is changed, possibly changing the number of available sets - * and invalidating this decision, then dialog will be popped down.) */ - if (nation_set_count() > 1) { - GtkListStore *sets_model = gtk_list_store_new(4, G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING); - GtkCellRenderer *renderer; - - nation_sets_iterate(pset) { - /* Index in list store must match nation_set_index(). */ - gchar *escaped; - struct astring s = ASTRING_INIT; - int num_nations = 0; - nations_iterate(pnation) { - if (is_nation_playable(pnation) && nation_is_in_set(pnation, pset)) { - num_nations++; - } - } nations_iterate_end; - escaped = g_markup_escape_text(nation_set_name_translation(pset), -1); - /* TRANS: nation set name followed by number of playable nations; - * and are Pango markup and should be left alone */ - astr_set(&s, PL_("%s (%d nation)", - "%s (%d nations)", num_nations), - escaped, num_nations); - g_free(escaped); - if (strlen(nation_set_description(pset)) > 0) { - /* While in principle it would be better to get Gtk to wrap the - * drop-down (e.g. via "wrap-width" property), there's no way - * to specify the indentation we want. So we do it ourselves. */ - char *desc = fc_strdup(_(nation_set_description(pset))); - char *p = desc; - - fc_break_lines(desc, 70); - astr_add(&s, "\n"); - while (*p) { - int len = strcspn(p, "\n"); - - if (p[len] == '\n') { - len++; - } - escaped = g_markup_escape_text(p, len); - astr_add(&s, "\t%s", escaped); - g_free(escaped); - p += len; - } - FC_FREE(desc); - } - gtk_list_store_insert_with_values(sets_model, NULL, -1, - 0, nation_set_rule_name(pset), - 1, astr_str(&s), - 2, nation_set_name_translation(pset), - -1); - astr_free(&s); - } nation_sets_iterate_end; - - /* We want a combo box where the button displays just the set name, - * but the dropdown displays the expanded description. */ - nationsets_chooser - = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(sets_model)); - g_object_unref(G_OBJECT(sets_model)); - { - /* Do our best to turn the text-entry widget into something more - * like a cell-view: disable editing, and focusing (which removes - * the caret). */ - GtkWidget *entry = gtk_bin_get_child(GTK_BIN(nationsets_chooser)); - - gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE); - gtk_widget_set_can_focus(entry, FALSE); - } - /* The entry displays the set name. */ - gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(nationsets_chooser), - 2); - /* The dropdown displays the marked-up description. */ - renderer = gtk_cell_renderer_text_new(); - gtk_cell_layout_clear(GTK_CELL_LAYOUT(nationsets_chooser)); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(nationsets_chooser), - renderer, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(nationsets_chooser), - renderer, "markup", 1, NULL); - g_signal_connect(nationsets_chooser, "destroy", - G_CALLBACK(gtk_widget_destroyed), &nationsets_chooser); - g_signal_connect(nationsets_chooser, "changed", - G_CALLBACK(nationset_callback), NULL); - { - /* Populate initially from client's view of server setting */ - struct option *poption = optset_option_by_name(server_optset, - "nationset"); - - if (poption) { - nationset_sync_to_server(option_str_get(poption)); - } - } - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "label", _("_Nation Set:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), nationsets_chooser); - - gtk_widget_set_hexpand(nationsets_chooser, TRUE); - gtk_grid_attach(GTK_GRID(nation_selection_list), label, - 0, 0, 1, 1); - gtk_grid_attach(GTK_GRID(nation_selection_list), nationsets_chooser, - 1, 0, 1, 1); - } - - races_notebook = gtk_notebook_new(); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(races_notebook), GTK_POS_LEFT); - gtk_grid_attach(GTK_GRID(nation_selection_list), races_notebook, - 0, 2, 2, 1); - - /* Suppress notebook tabs if there will be only one ("All") */ - { - bool show_groups = FALSE; - - nation_groups_iterate(pgroup) { - if (!is_nation_group_hidden(pgroup)) { - show_groups = TRUE; - break; - } - } nation_groups_iterate_end; - if (!show_groups) { - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(races_notebook), FALSE); - } else { - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "label", _("Nation _Groups:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_label_set_mnemonic_widget(GTK_LABEL(label), races_notebook); - gtk_grid_attach(GTK_GRID(nation_selection_list), label, - 0, 1, 2, 1); - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(races_notebook), TRUE); - } - } - - /* Populate treeview */ - create_nation_selection_lists(); - - gtk_container_add(GTK_CONTAINER(hbox), nation_selection_list); - } - - /* Right side. */ - notebook = gtk_notebook_new(); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM); - gtk_container_add(GTK_CONTAINER(hbox), notebook); - - /* Properties pane. */ - label = gtk_label_new_with_mnemonic(_("_Properties")); - - races_properties = table = gtk_grid_new(); - g_signal_connect(table, "destroy", - G_CALLBACK(gtk_widget_destroyed), &races_properties); - g_object_set(table, "margin", 6, NULL); - gtk_grid_set_row_spacing(GTK_GRID(table), 2); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table, label); - - /* Leader. */ - { - GtkListStore *model = gtk_list_store_new(1, G_TYPE_STRING); - - combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model)); - gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0); - g_object_unref(G_OBJECT(model)); - } - races_leader = combo; - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", GTK_COMBO_BOX(combo), - "label", _("_Leader:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_widget_set_margin_bottom(label, 6); - gtk_widget_set_margin_right(label, 12); - gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 2); - gtk_grid_attach(GTK_GRID(table), combo, 1, 0, 2, 1); - - cmd = gtk_radio_button_new_with_mnemonic(NULL, _("_Female")); - gtk_widget_set_margin_bottom(cmd, 6); - races_sex[0] = cmd; - gtk_grid_attach(GTK_GRID(table), cmd, 1, 1, 1, 1); - - cmd = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(cmd), - _("_Male")); - gtk_widget_set_margin_bottom(cmd, 6); - races_sex[1] = cmd; - gtk_grid_attach(GTK_GRID(table), cmd, 2, 1, 1, 1); - - /* City style. */ - store = gtk_list_store_new(3, G_TYPE_INT, - GDK_TYPE_PIXBUF, G_TYPE_STRING); - - list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(list, TRUE); - gtk_widget_set_vexpand(list, TRUE); - races_style_list = list; - g_object_unref(store); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE); - g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), "changed", - G_CALLBACK(races_style_callback), NULL); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_widget_set_margin_top(sw, 6); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), list); - gtk_grid_attach(GTK_GRID(table), sw, 1, 2, 2, 2); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", list, - "label", _("City _Styles:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_widget_set_margin_top(label, 6); - gtk_widget_set_margin_right(label, 12); - gtk_grid_attach(GTK_GRID(table), label, 0, 2, 1, 1); - - render = gtk_cell_renderer_pixbuf_new(); - column = gtk_tree_view_column_new_with_attributes(NULL, render, - "pixbuf", 1, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(list), column); - render = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(NULL, render, - "text", 2, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(list), column); - - /* Populate style store. */ - styles_iterate(pstyle) { - GdkPixbuf *img; - struct sprite *s; - GtkTreeIter it; - - i = basic_city_style_for_style(pstyle); - - if (i >= 0) { - gtk_list_store_append(store, &it); - - s = crop_blankspace(get_sample_city_sprite(tileset, i)); - img = sprite_get_pixbuf(s); - free_sprite(s); - gtk_list_store_set(store, &it, 0, i, 1, img, 2, - city_style_name_translation(i), -1); - g_object_unref(img); - } - } styles_iterate_end; - - /* Legend pane. */ - label = gtk_label_new_with_mnemonic(_("_Description")); - - text = gtk_text_view_new(); - g_object_set(text, "margin", 6, NULL); - gtk_widget_set_hexpand(text, TRUE); - gtk_widget_set_vexpand(text, TRUE); - races_text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); - gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); - gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6); - gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6); - - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), text, label); - - /* Signals. */ - g_signal_connect(shell, "destroy", - G_CALLBACK(gtk_widget_destroyed), &races_shell); - g_signal_connect(shell, "response", - G_CALLBACK(races_response), NULL); - - g_signal_connect(GTK_COMBO_BOX(races_leader), "changed", - G_CALLBACK(races_leader_callback), NULL); - - g_signal_connect(races_sex[0], "toggled", - G_CALLBACK(races_sex_callback), GINT_TO_POINTER(0)); - g_signal_connect(races_sex[1], "toggled", - G_CALLBACK(races_sex_callback), GINT_TO_POINTER(1)); - - /* Finish up. */ - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CANCEL); - - /* You can't assign NO_NATION during a running game. */ - if (C_S_RUNNING == client_state()) { - gtk_dialog_set_response_sensitive(GTK_DIALOG(shell), GTK_RESPONSE_NO, - FALSE); - } - - gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(shell))); - - /* Select player's current nation in UI, if any */ - if (races_player->nation) { - select_nation(nation_number(races_player->nation), - player_name(races_player), - races_player->is_male, - style_number(races_player->style)); - /* Make sure selected nation is visible - * (last page, "All", will certainly contain it) */ - fc_assert(gtk_notebook_get_n_pages(GTK_NOTEBOOK(races_notebook)) > 0); - gtk_notebook_set_current_page(GTK_NOTEBOOK(races_notebook), -1); - } else { - select_nation(-1, NULL, FALSE, 0); - } -} - -/**********************************************************************//** - Popup the dialog 10% inside the main-window -**************************************************************************/ -void popup_races_dialog(struct player *pplayer) -{ - if (!pplayer) { - return; - } - - if (!races_shell) { - create_races_dialog(pplayer); - gtk_window_present(GTK_WINDOW(races_shell)); - } -} - -/**********************************************************************//** - Close nations dialog -**************************************************************************/ -void popdown_races_dialog(void) -{ - if (races_shell) { - gtk_widget_destroy(races_shell); - } - - /* We're probably starting a new game, maybe with a new ruleset. - So we warn the worklist dialog. */ - blank_max_unit_size(); -} - -/**********************************************************************//** - Update which nations are allowed to be selected (due to e.g. another - player choosing a nation). -**************************************************************************/ -void races_toggles_set_sensitive(void) -{ - int i; - - if (!races_shell) { - return; - } - - for (i = 0; i <= nation_group_count(); i++) { - if (races_nation_list[i]) { - GtkTreeView *list = GTK_TREE_VIEW(races_nation_list[i]); - GtkTreeModel *model = gtk_tree_view_get_model(list); - GtkTreeSelection* select = gtk_tree_view_get_selection(list); - GtkTreeIter it; - gboolean chosen; - - /* Update 'chosen' column in model */ - if (gtk_tree_model_get_iter_first(model, &it)) { - do { - int nation_no; - struct nation_type *nation; - - gtk_tree_model_get(model, &it, 0, &nation_no, -1); - nation = nation_by_number(nation_no); - - chosen = !is_nation_pickable(nation) - || (nation->player && nation->player != races_player); - - gtk_list_store_set(GTK_LIST_STORE(model), &it, 1, chosen, -1); - - } while (gtk_tree_model_iter_next(model, &it)); - } - - /* If our selection is now invalid, deselect it */ - if (gtk_tree_selection_get_selected(select, &model, &it)) { - gtk_tree_model_get(model, &it, 1, &chosen, -1); - - if (chosen) { - gtk_tree_selection_unselect_all(select); - } - } - } - } -} - -/**********************************************************************//** - Called whenever a user selects a nation in nation list -**************************************************************************/ -static void races_nation_callback(GtkTreeSelection *select, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(select, &model, &it)) { - gboolean chosen; - int newnation; - - gtk_tree_model_get(model, &it, 0, &newnation, 1, &chosen, -1); - - /* Only allow nations not chosen by another player */ - if (!chosen) { - if (newnation != selected_nation) { - /* Choose a random leader */ - select_nation(newnation, NULL, FALSE, - style_number(style_of_nation(nation_by_number(newnation)))); - } - return; - } - } - - /* Fall-through if no valid nation selected */ - select_nation(-1, NULL, FALSE, 0); -} - -/**********************************************************************//** - Leader name has been chosen -**************************************************************************/ -static void races_leader_callback(void) -{ - const struct nation_leader *pleader; - const gchar *name; - - name = - gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader)))); - - if (selected_nation != -1 - &&(pleader = nation_leader_by_name(nation_by_number(selected_nation), - name))) { - selected_sex = nation_leader_is_male(pleader); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex[selected_sex]), - TRUE); - } -} - -/**********************************************************************//** - Leader sex has been chosen -**************************************************************************/ -static void races_sex_callback(GtkWidget *w, gpointer data) -{ - selected_sex = GPOINTER_TO_INT(data); -} - -/**********************************************************************//** - Determines which nations can be selected in the UI -**************************************************************************/ -static gboolean races_selection_func(GtkTreeSelection *select, - GtkTreeModel *model, GtkTreePath *path, - gboolean selected, gpointer data) -{ - GtkTreeIter it; - gboolean chosen; - - gtk_tree_model_get_iter(model, &it, path); - gtk_tree_model_get(model, &it, 1, &chosen, -1); - return (!chosen || selected); -} - -/**********************************************************************//** - City style has been chosen -**************************************************************************/ -static void races_style_callback(GtkTreeSelection *select, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(select, &model, &it)) { - gtk_tree_model_get(model, &it, 0, &selected_style, -1); - } else { - selected_style = -1; - } -} - -/**********************************************************************//** - User has selected some of the responses for whole nations dialog -**************************************************************************/ -static void races_response(GtkWidget *w, gint response, gpointer data) -{ - if (response == GTK_RESPONSE_ACCEPT) { - const char *s; - - /* This shouldn't be possible but... */ - if (selected_nation == -1) { - return; - } - - if (selected_sex == -1) { - output_window_append(ftc_client, _("You must select your sex.")); - return; - } - - if (selected_style == -1) { - output_window_append(ftc_client, _("You must select your style.")); - return; - } - - s = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader)))); - - /* Perform a minimum of sanity test on the name. */ - /* This could call is_allowed_player_name if it were available. */ - if (strlen(s) == 0) { - output_window_append(ftc_client, _("You must type a legal name.")); - return; - } - - dsend_packet_nation_select_req(&client.conn, - player_number(races_player), selected_nation, - selected_sex, s, - selected_style); - } else if (response == GTK_RESPONSE_NO) { - dsend_packet_nation_select_req(&client.conn, - player_number(races_player), - -1, FALSE, "", 0); - } - - popdown_races_dialog(); -} - - -/**********************************************************************//** - Adjust tax rates from main window -**************************************************************************/ -gboolean taxrates_callback(GtkWidget *w, GdkEventButton *ev, gpointer data) -{ - common_taxrates_callback((size_t) data); - return TRUE; -} - -/**********************************************************************//** - Pops up a dialog to confirm upgrading of the unit. -**************************************************************************/ -void popup_upgrade_dialog(struct unit_list *punits) -{ - GtkWidget *shell; - char buf[512]; - - if (!punits || unit_list_size(punits) == 0) { - return; - } - - if (!get_units_upgrade_info(buf, sizeof(buf), punits)) { - shell = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, - "%s", buf); - gtk_window_set_title(GTK_WINDOW(shell), _("Upgrade Unit!")); - setup_dialog(shell, toplevel); - g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy), - NULL); - gtk_window_present(GTK_WINDOW(shell)); - } else { - shell = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - "%s", buf); - gtk_window_set_title(GTK_WINDOW(shell), _("Upgrade Obsolete Units")); - setup_dialog(shell, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_YES); - - if (gtk_dialog_run(GTK_DIALOG(shell)) == GTK_RESPONSE_YES) { - unit_list_iterate(punits, punit) { - request_unit_upgrade(punit); - } unit_list_iterate_end; - } - gtk_widget_destroy(shell); - } -} - -/**********************************************************************//** - Pops up a dialog to confirm disband of the unit(s). -**************************************************************************/ -void popup_disband_dialog(struct unit_list *punits) -{ - GtkWidget *shell; - char buf[512]; - - if (!punits || unit_list_size(punits) == 0) { - return; - } - - if (!get_units_disband_info(buf, sizeof(buf), punits)) { - shell = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, - "%s", buf); - gtk_window_set_title(GTK_WINDOW(shell), _("Disband Units")); - setup_dialog(shell, toplevel); - g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy), - NULL); - gtk_window_present(GTK_WINDOW(shell)); - } else { - shell = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - "%s", buf); - gtk_window_set_title(GTK_WINDOW(shell), _("Disband Units")); - setup_dialog(shell, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_YES); - - if (gtk_dialog_run(GTK_DIALOG(shell)) == GTK_RESPONSE_YES) { - unit_list_iterate(punits, punit) { - if (unit_can_do_action(punit, ACTION_DISBAND_UNIT)) { - request_unit_disband(punit); - } - } unit_list_iterate_end; - } - gtk_widget_destroy(shell); - } -} - -/**********************************************************************//** - This function is called when the client disconnects or the game is - over. It should close all dialog windows for that game. -**************************************************************************/ -void popdown_all_game_dialogs(void) -{ - gui_dialog_destroy_all(); - property_editor_popdown(editprop_get_property_editor()); - unit_select_dialog_popdown(); -} - -/**********************************************************************//** - Player has gained a new tech. -**************************************************************************/ -void show_tech_gained_dialog(Tech_type_id tech) -{ - const struct advance *padvance = valid_advance_by_number(tech); - - if (NULL != padvance - && (GUI_GTK_OPTION(popup_tech_help) == GUI_POPUP_TECH_HELP_ENABLED - || (GUI_GTK_OPTION(popup_tech_help) == GUI_POPUP_TECH_HELP_RULESET - && game.control.popup_tech_help))) { - popup_help_dialog_typed(advance_name_translation(padvance), HELP_TECH); - } -} - -/**********************************************************************//** - Show tileset error dialog. It's blocking as client will - shutdown as soon as this function returns. -**************************************************************************/ -void show_tileset_error(const char *msg) -{ - if (is_gui_up()) { - GtkWidget *dialog; - - dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - _("Tileset problem, it's probably incompatible with the ruleset:\n%s"), - msg); - setup_dialog(dialog, toplevel); - - gtk_dialog_run(GTK_DIALOG(dialog)); - - gtk_widget_destroy(dialog); - } -} - -/**********************************************************************//** - Give a warning when user is about to edit scenario with manually - set properties. -**************************************************************************/ -bool handmade_scenario_warning(void) -{ - /* Just tell the client common code to handle this. */ - return FALSE; -} - -/**********************************************************************//** - Popup detailed information about battle or save information for - some kind of statistics -**************************************************************************/ -void popup_combat_info(int attacker_unit_id, int defender_unit_id, - int attacker_hp, int defender_hp, - bool make_att_veteran, bool make_def_veteran) -{ -} diff --git a/client/gui-gtk-3.0/dialogs.h b/client/gui-gtk-3.0/dialogs.h deleted file mode 100644 index c6c4e74f4c..0000000000 --- a/client/gui-gtk-3.0/dialogs.h +++ /dev/null @@ -1,26 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__DIALOGS_H -#define FC__DIALOGS_H - -#include - -#include "dialogs_g.h" - -void popup_revolution_dialog(struct government *government); -void message_dialog_button_set_sensitive(GtkWidget *shl, int button, - gboolean state); -gboolean taxrates_callback(GtkWidget *w, GdkEventButton *ev, gpointer data); -void nationset_sync_to_server(const char *nationset); - -#endif /* FC__DIALOGS_H */ diff --git a/client/gui-gtk-3.0/diplodlg.c b/client/gui-gtk-3.0/diplodlg.c deleted file mode 100644 index 32075ce140..0000000000 --- a/client/gui-gtk-3.0/diplodlg.c +++ /dev/null @@ -1,1186 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include - -/* utility */ -#include "log.h" -#include "mem.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "diptreaty.h" -#include "fcintl.h" -#include "game.h" -#include "government.h" -#include "map.h" -#include "packets.h" -#include "player.h" -#include "research.h" - -/* client */ -#include "chatline.h" -#include "client_main.h" -#include "climisc.h" -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "diplodlg.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapview.h" -#include "plrdlg.h" - -#define MAX_NUM_CLAUSES 64 - -struct Diplomacy_dialog { - struct Treaty treaty; - struct gui_dialog* dialog; - - GtkWidget *menu0; - GtkWidget *menu1; - - GtkWidget *image0; - GtkWidget *image1; - - GtkListStore *store; -}; - -struct Diplomacy_notebook { - struct gui_dialog* dialog; - GtkWidget *notebook; -}; - -#define SPECLIST_TAG dialog -#define SPECLIST_TYPE struct Diplomacy_dialog -#include "speclist.h" - -#define dialog_list_iterate(dialoglist, pdialog) \ - TYPED_LIST_ITERATE(struct Diplomacy_dialog, dialoglist, pdialog) -#define dialog_list_iterate_end LIST_ITERATE_END - -static struct dialog_list *dialog_list; -static struct Diplomacy_notebook *dipl_main; - -static struct Diplomacy_dialog *create_diplomacy_dialog(struct player *plr0, - struct player *plr1); - -static struct Diplomacy_dialog *find_diplomacy_dialog(int other_player_id); -static void popup_diplomacy_dialog(int other_player_id, int initiated_from); -static void diplomacy_dialog_map_callback(GtkWidget *w, gpointer data); -static void diplomacy_dialog_seamap_callback(GtkWidget *w, gpointer data); -static void diplomacy_dialog_tech_callback(GtkWidget *w, gpointer data); -static void diplomacy_dialog_city_callback(GtkWidget *w, gpointer data); -static void diplomacy_dialog_ceasefire_callback(GtkWidget *w, gpointer data); -static void diplomacy_dialog_peace_callback(GtkWidget *w, gpointer data); -static void diplomacy_dialog_alliance_callback(GtkWidget *w, gpointer data); -static void diplomacy_dialog_vision_callback(GtkWidget *w, gpointer data); -static void diplomacy_dialog_embassy_callback(GtkWidget *w, gpointer data); -static void close_diplomacy_dialog(struct Diplomacy_dialog *pdialog); -static void update_diplomacy_dialog(struct Diplomacy_dialog *pdialog); -static void diplo_dialog_returnkey(GtkWidget *w, gpointer data); - -static struct Diplomacy_notebook *diplomacy_main_create(void); -static void diplomacy_main_destroy(void); -static void diplomacy_main_response(struct gui_dialog *dlg, int response, - gpointer data); - -#define RESPONSE_CANCEL_MEETING 100 -#define RESPONSE_CANCEL_MEETING_ALL 101 - -/************************************************************************//** - Server tells us that either party has accepted treaty -****************************************************************************/ -void handle_diplomacy_accept_treaty(int counterpart, bool I_accepted, - bool other_accepted) -{ - struct Diplomacy_dialog *pdialog = find_diplomacy_dialog(counterpart); - - if (!pdialog) { - return; - } - - pdialog->treaty.accept0 = I_accepted; - pdialog->treaty.accept1 = other_accepted; - - update_diplomacy_dialog(pdialog); - gui_dialog_alert(pdialog->dialog); -} - -/************************************************************************//** - Someone is initiating meeting with us. -****************************************************************************/ -void handle_diplomacy_init_meeting(int counterpart, int initiated_from) -{ - popup_diplomacy_dialog(counterpart, initiated_from); -} - -/************************************************************************//** - Meeting has been cancelled. -****************************************************************************/ -void handle_diplomacy_cancel_meeting(int counterpart, int initiated_from) -{ - struct Diplomacy_dialog *pdialog = find_diplomacy_dialog(counterpart); - - if (!pdialog) { - return; - } - - close_diplomacy_dialog(pdialog); -} - -/************************************************************************//** - Added clause to the meeting -****************************************************************************/ -void handle_diplomacy_create_clause(int counterpart, int giver, - enum clause_type type, int value) -{ - struct Diplomacy_dialog *pdialog = find_diplomacy_dialog(counterpart); - - if (!pdialog) { - return; - } - - add_clause(&pdialog->treaty, player_by_number(giver), type, value); - update_diplomacy_dialog(pdialog); - gui_dialog_alert(pdialog->dialog); -} - -/************************************************************************//** - Removed clause from meeting. -****************************************************************************/ -void handle_diplomacy_remove_clause(int counterpart, int giver, - enum clause_type type, int value) -{ - struct Diplomacy_dialog *pdialog = find_diplomacy_dialog(counterpart); - - if (!pdialog) { - return; - } - - remove_clause(&pdialog->treaty, player_by_number(giver), type, value); - update_diplomacy_dialog(pdialog); - gui_dialog_alert(pdialog->dialog); -} - -/************************************************************************//** - Popup the dialog 10% inside the main-window -****************************************************************************/ -static void popup_diplomacy_dialog(int other_player_id, int initiated_from) -{ - struct Diplomacy_dialog *pdialog = find_diplomacy_dialog(other_player_id); - - if (!can_client_issue_orders()) { - return; - } - - if (!is_human(client.conn.playing)) { - return; /* Don't show if we are not human controlled. */ - } - - if (!pdialog) { - pdialog = create_diplomacy_dialog(client.conn.playing, - player_by_number(other_player_id)); - } - - gui_dialog_present(pdialog->dialog); - /* We initated the meeting - Make the tab active */ - if (player_by_number(initiated_from) == client.conn.playing) { - /* we have to raise the diplomacy meeting tab as well as the selected - * meeting. */ - fc_assert_ret(dipl_main != NULL); - gui_dialog_raise(dipl_main->dialog); - gui_dialog_raise(pdialog->dialog); - - if (players_dialog_shell != NULL) { - gui_dialog_set_return_dialog(pdialog->dialog, players_dialog_shell); - } - } -} - -/************************************************************************//** - Utility for g_list_sort(). See below. -****************************************************************************/ -static gint sort_advance_names(gconstpointer a, gconstpointer b) -{ - const struct advance *padvance1 = (const struct advance *) a; - const struct advance *padvance2 = (const struct advance *) b; - - return fc_strcoll(advance_name_translation(padvance1), - advance_name_translation(padvance2)); -} - -/************************************************************************//** - Popup menu about adding clauses -****************************************************************************/ -static void popup_add_menu(GtkMenuShell *parent, gpointer data) -{ - struct Diplomacy_dialog *pdialog; - struct player *pgiver, *pother; - GtkWidget *item, *menu; - - - /* init. */ - gtk_container_foreach(GTK_CONTAINER(parent), - (GtkCallback) gtk_widget_destroy, NULL); - - pdialog = (struct Diplomacy_dialog *) data; - pgiver = (struct player *) g_object_get_data(G_OBJECT(parent), "plr"); - pother = (pgiver == pdialog->treaty.plr0 - ? pdialog->treaty.plr1 : pdialog->treaty.plr0); - - - /* Maps. */ - menu = gtk_menu_new(); - item = gtk_menu_item_new_with_mnemonic(_("World-map")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu),item); - g_object_set_data(G_OBJECT(item), "plr", pgiver); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_map_callback), pdialog); - - item = gtk_menu_item_new_with_mnemonic(_("Sea-map")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data(G_OBJECT(item), "plr", pgiver); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_seamap_callback), pdialog); - - item = gtk_menu_item_new_with_mnemonic(_("_Maps")); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - gtk_menu_shell_append(GTK_MENU_SHELL(parent), item); - gtk_widget_show_all(item); - - /* Trading: advances */ - if (game.info.trading_tech) { - const struct research *gresearch = research_get(pgiver); - const struct research *oresearch = research_get(pother); - GtkWidget *advance_item; - GList *sorting_list = NULL; - - advance_item = gtk_menu_item_new_with_mnemonic(_("_Advances")); - gtk_menu_shell_append(GTK_MENU_SHELL(parent), advance_item); - - advance_iterate(A_FIRST, padvance) { - Tech_type_id i = advance_number(padvance); - - if (research_invention_state(gresearch, i) == TECH_KNOWN - && research_invention_gettable(oresearch, i, - game.info.tech_trade_allow_holes) - && (research_invention_state(oresearch, i) == TECH_UNKNOWN - || research_invention_state(oresearch, i) - == TECH_PREREQS_KNOWN)) { - sorting_list = g_list_prepend(sorting_list, padvance); - } - } advance_iterate_end; - - if (NULL == sorting_list) { - /* No advance. */ - gtk_widget_set_sensitive(advance_item, FALSE); - } else { - GList *list_item; - const struct advance *padvance; - - sorting_list = g_list_sort(sorting_list, sort_advance_names); - menu = gtk_menu_new(); - - /* TRANS: All technologies menu item in the diplomatic dialog. */ - item = gtk_menu_item_new_with_label(_("All advances")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data(G_OBJECT(item), "player_from", - GINT_TO_POINTER(player_number(pgiver))); - g_object_set_data(G_OBJECT(item), "player_to", - GINT_TO_POINTER(player_number(pother))); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_tech_callback), - GINT_TO_POINTER(A_LAST)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), - gtk_separator_menu_item_new()); - - for (list_item = sorting_list; NULL != list_item; - list_item = g_list_next(list_item)) { - padvance = (const struct advance *) list_item->data; - item = - gtk_menu_item_new_with_label(advance_name_translation(padvance)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data(G_OBJECT(item), "player_from", - GINT_TO_POINTER(player_number(pgiver))); - g_object_set_data(G_OBJECT(item), "player_to", - GINT_TO_POINTER(player_number(pother))); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_tech_callback), - GINT_TO_POINTER(advance_number(padvance))); - } - - gtk_menu_item_set_submenu(GTK_MENU_ITEM(advance_item), menu); - g_list_free(sorting_list); - } - - gtk_widget_show_all(advance_item); - } - - - /* Trading: cities. */ - - /**************************************************************** - Creates a sorted list of plr0's cities, excluding the capital and - any cities not visible to plr1. This means that you can only trade - cities visible to requesting player. - - - Kris Bubendorfer - *****************************************************************/ - if (game.info.trading_city) { - int i = 0, j = 0, n = city_list_size(pgiver->cities); - struct city **city_list_ptrs; - - if (n > 0) { - city_list_ptrs = fc_malloc(sizeof(struct city *) * n); - } else { - city_list_ptrs = NULL; - } - - city_list_iterate(pgiver->cities, pcity) { - if (!is_capital(pcity)) { - city_list_ptrs[i] = pcity; - i++; - } - } city_list_iterate_end; - - qsort(city_list_ptrs, i, sizeof(struct city *), city_name_compare); - - menu = gtk_menu_new(); - - for (j = 0; j < i; j++) { - item = gtk_menu_item_new_with_label(city_name_get(city_list_ptrs[j])); - - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_city_callback), - GINT_TO_POINTER((player_number(pgiver) << 24) | - (player_number(pother) << 16) | - city_list_ptrs[j]->id)); - } - free(city_list_ptrs); - - item = gtk_menu_item_new_with_mnemonic(_("_Cities")); - gtk_widget_set_sensitive(item, (i > 0)); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - gtk_menu_shell_append(GTK_MENU_SHELL(parent), item); - gtk_widget_show_all(item); - } - - - /* Give shared vision. */ - item = gtk_menu_item_new_with_mnemonic(_("_Give shared vision")); - g_object_set_data(G_OBJECT(item), "plr", pgiver); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_vision_callback), pdialog); - - if (gives_shared_vision(pgiver, pother)) { - gtk_widget_set_sensitive(item, FALSE); - } - gtk_menu_shell_append(GTK_MENU_SHELL(parent), item); - gtk_widget_show(item); - - - /* Give embassy. */ - item = gtk_menu_item_new_with_mnemonic(_("Give _embassy")); - g_object_set_data(G_OBJECT(item), "plr", pgiver); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_embassy_callback), pdialog); - - /* Don't take in account the embassy effects. */ - if (player_has_real_embassy(pother, pgiver)) { - gtk_widget_set_sensitive(item, FALSE); - } - gtk_menu_shell_append(GTK_MENU_SHELL(parent), item); - gtk_widget_show(item); - - - /* Pacts. */ - if (pgiver == pdialog->treaty.plr0) { - enum diplstate_type ds; - - ds = player_diplstate_get(pgiver, pother)->type; - - menu = gtk_menu_new(); - item = gtk_menu_item_new_with_mnemonic(Q_("?diplomatic_state:Cease-fire")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu),item); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_ceasefire_callback), pdialog); - gtk_widget_set_sensitive(item, ds != DS_CEASEFIRE && ds != DS_TEAM); - - item = gtk_menu_item_new_with_mnemonic(Q_("?diplomatic_state:Peace")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_peace_callback), pdialog); - gtk_widget_set_sensitive(item, ds != DS_PEACE && ds != DS_TEAM); - - item = gtk_menu_item_new_with_mnemonic(Q_("?diplomatic_state:Alliance")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(diplomacy_dialog_alliance_callback), pdialog); - gtk_widget_set_sensitive(item, ds != DS_ALLIANCE && ds != DS_TEAM); - - item = gtk_menu_item_new_with_mnemonic(_("_Pacts")); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - gtk_menu_shell_append(GTK_MENU_SHELL(parent), item); - gtk_widget_show_all(item); - } -} - -/************************************************************************//** - Some clause activated -****************************************************************************/ -static void row_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data) -{ - struct Diplomacy_dialog *pdialog = (struct Diplomacy_dialog *)data; - gint i; - gint *index; - - index = gtk_tree_path_get_indices(path); - - i = 0; - clause_list_iterate(pdialog->treaty.clauses, pclause) { - if (i == index[0]) { - dsend_packet_diplomacy_remove_clause_req(&client.conn, - player_number(pdialog->treaty.plr1), - player_number(pclause->from), - pclause->type, - pclause->value); - return; - } - i++; - } clause_list_iterate_end; -} - -/************************************************************************//** - Create the main tab for diplomatic meetings. -****************************************************************************/ -static struct Diplomacy_notebook *diplomacy_main_create(void) -{ - /* Collect all meetings in one main tab. */ - if (!dipl_main) { - GtkWidget *dipl_box, *dipl_sw; - - dipl_main = fc_malloc(sizeof(*dipl_main)); - gui_dialog_new(&(dipl_main->dialog), GTK_NOTEBOOK(top_notebook), - dipl_main->dialog, TRUE); - dipl_main->notebook = gtk_notebook_new(); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(dipl_main->notebook), - GTK_POS_RIGHT); - gtk_notebook_set_scrollable(GTK_NOTEBOOK(dipl_main->notebook), TRUE); - - dipl_sw = gtk_scrolled_window_new(NULL, NULL); - g_object_set(dipl_sw, "margin", 2, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dipl_sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(dipl_sw), dipl_main->notebook); - - /* Buttons */ - gui_dialog_add_stockbutton(dipl_main->dialog, GTK_STOCK_CANCEL, - _("Cancel _all meetings"), - RESPONSE_CANCEL_MEETING_ALL); - - /* Responces for _all_ meetings. */ - gui_dialog_response_set_callback(dipl_main->dialog, - diplomacy_main_response); - gui_dialog_set_default_response(dipl_main->dialog, - RESPONSE_CANCEL_MEETING_ALL); - - dipl_box = dipl_main->dialog->vbox; - gtk_container_add(GTK_CONTAINER(dipl_box), dipl_sw); - - gui_dialog_show_all(dipl_main->dialog); - gui_dialog_present(dipl_main->dialog); - } - - return dipl_main; -} - -/************************************************************************//** - Destroy main diplomacy dialog. -****************************************************************************/ -static void diplomacy_main_destroy(void) -{ - if (dipl_main->dialog) { - gui_dialog_destroy(dipl_main->dialog); - } - free(dipl_main); - dipl_main = NULL; -} - -/************************************************************************//** - User has responded to whole diplomacy dialog (main tab). -****************************************************************************/ -static void diplomacy_main_response(struct gui_dialog *dlg, int response, - gpointer data) -{ - if (!dipl_main) { - return; - } - - switch (response) { - default: - log_error("unhandled response in %s: %d", __FUNCTION__, response); - fc__fallthrough; /* No break. */ - case GTK_RESPONSE_DELETE_EVENT: /* GTK: delete the widget. */ - case RESPONSE_CANCEL_MEETING_ALL: /* Cancel all meetings. */ - dialog_list_iterate(dialog_list, adialog) { - /* This will do a round trip to the server ans close the diolag in the - * client. Closing the last dialog will also close the main tab.*/ - dsend_packet_diplomacy_cancel_meeting_req(&client.conn, - player_number( - adialog->treaty.plr1)); - } dialog_list_iterate_end; - break; - } -} - -/************************************************************************//** - Destroy diplomacy dialog -****************************************************************************/ -static void diplomacy_destroy(struct Diplomacy_dialog* pdialog) -{ - if (NULL != pdialog->dialog) { - /* pdialog->dialog may be NULL if the tab have been destroyed - * by an other way. */ - gui_dialog_destroy(pdialog->dialog); - } - dialog_list_remove(dialog_list, pdialog); - free(pdialog); - - if (dialog_list) { - /* Diplomatic meetings in one main tab. */ - if (dialog_list_size(dialog_list) > 0) { - if (dipl_main && dipl_main->dialog) { - gchar *buf; - - buf = g_strdup_printf(_("Diplomacy [%d]"), dialog_list_size(dialog_list)); - gui_dialog_set_title(dipl_main->dialog, buf); - g_free(buf); - } - } else if (dipl_main) { - /* No meeting left - destroy main tab. */ - diplomacy_main_destroy(); - } - } -} - -/************************************************************************//** - User has responded to whole diplomacy dialog (one meeting). -****************************************************************************/ -static void diplomacy_response(struct gui_dialog *dlg, int response, - gpointer data) -{ - struct Diplomacy_dialog *pdialog = NULL; - - fc_assert_ret(data); - pdialog = (struct Diplomacy_dialog *)data; - - switch (response) { - case GTK_RESPONSE_ACCEPT: /* Accept treaty. */ - dsend_packet_diplomacy_accept_treaty_req(&client.conn, - player_number( - pdialog->treaty.plr1)); - break; - - default: - log_error("unhandled response in %s: %d", __FUNCTION__, response); - fc__fallthrough; /* No break. */ - case GTK_RESPONSE_DELETE_EVENT: /* GTK: delete the widget. */ - case GTK_RESPONSE_CANCEL: /* GTK: cancel button. */ - case RESPONSE_CANCEL_MEETING: /* Cancel meetings. */ - dsend_packet_diplomacy_cancel_meeting_req(&client.conn, - player_number( - pdialog->treaty.plr1)); - break; - } -} - -/************************************************************************//** - Setups diplomacy dialog widgets. -****************************************************************************/ -static struct Diplomacy_dialog *create_diplomacy_dialog(struct player *plr0, - struct player *plr1) -{ - struct Diplomacy_notebook *dipl_dialog; - GtkWidget *vbox, *hbox, *table, *mainbox; - GtkWidget *label, *sw, *view, *image, *spin; - GtkWidget *menubar, *menuitem, *menu, *notebook; - struct sprite *flag_spr; - GtkListStore *store; - GtkCellRenderer *rend; - int i; - - struct Diplomacy_dialog *pdialog; - char plr_buf[4 * MAX_LEN_NAME]; - gchar *buf; - - pdialog = fc_malloc(sizeof(*pdialog)); - - dialog_list_prepend(dialog_list, pdialog); - init_treaty(&pdialog->treaty, plr0, plr1); - - /* Get main diplomacy tab. */ - dipl_dialog = diplomacy_main_create(); - - buf = g_strdup_printf(_("Diplomacy [%d]"), dialog_list_size(dialog_list)); - gui_dialog_set_title(dipl_dialog->dialog, buf); - g_free(buf); - - notebook = dipl_dialog->notebook; - - gui_dialog_new(&(pdialog->dialog), GTK_NOTEBOOK(notebook), pdialog, FALSE); - - /* Buttons */ - gui_dialog_add_stockbutton(pdialog->dialog, GTK_STOCK_DND, - _("Accept treaty"), GTK_RESPONSE_ACCEPT); - gui_dialog_add_stockbutton(pdialog->dialog, GTK_STOCK_CANCEL, - _("Cancel meeting"), RESPONSE_CANCEL_MEETING); - - /* Responces for one meeting. */ - gui_dialog_response_set_callback(pdialog->dialog, diplomacy_response); - gui_dialog_set_default_response(pdialog->dialog, RESPONSE_CANCEL_MEETING); - - /* Label for the new meeting. */ - buf = g_strdup_printf("%s", nation_plural_for_player(plr1)); - gui_dialog_set_title(pdialog->dialog, buf); - - /* Sort meeting tabs alphabetically by the tab label. */ - for (i = 0; i < gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)); i++) { - GtkWidget *prev_page - = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), i); - struct gui_dialog *prev_dialog - = g_object_get_data(G_OBJECT(prev_page), "gui-dialog-data"); - const char *prev_label - = gtk_label_get_text(GTK_LABEL(prev_dialog->v.tab.label)); - - if (fc_strcasecmp(buf, prev_label) < 0) { - gtk_notebook_reorder_child(GTK_NOTEBOOK(notebook), - pdialog->dialog->vbox, i); - break; - } - } - g_free(buf); - - /* Content. */ - mainbox = pdialog->dialog->vbox; - - /* us. */ - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 5); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); - gtk_container_add(GTK_CONTAINER(mainbox), vbox); - - /* Our nation. */ - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_CENTER); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - buf = g_strdup_printf("%s", - nation_plural_for_player(plr0)); - gtk_label_set_markup(GTK_LABEL(label), buf); - g_free(buf); - gtk_container_add(GTK_CONTAINER(vbox), label); - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 5); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - /* Our flag */ - flag_spr = get_nation_flag_sprite(tileset, nation_of_player(plr0)); - - image = gtk_image_new_from_surface(flag_spr->surface); - gtk_container_add(GTK_CONTAINER(hbox), image); - - /* Our name. */ - label = gtk_label_new(NULL); - gtk_widget_set_hexpand(label, TRUE); - gtk_widget_set_halign(label, GTK_ALIGN_CENTER); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - buf = g_strdup_printf("%s", - ruler_title_for_player(plr0, plr_buf, sizeof(plr_buf))); - gtk_label_set_markup(GTK_LABEL(label), buf); - g_free(buf); - gtk_container_add(GTK_CONTAINER(hbox), label); - - image = gtk_image_new(); - pdialog->image0 = image; - gtk_container_add(GTK_CONTAINER(hbox), image); - - /* Menu for clauses: we. */ - menubar = gtk_aux_menu_bar_new(); - - menu = gtk_menu_new(); - pdialog->menu0 = menu; - - menuitem = gtk_image_menu_item_new_with_label(_("Add Clause...")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), - gtk_image_new_from_stock(GTK_STOCK_ADD, - GTK_ICON_SIZE_MENU)); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem); - g_object_set_data(G_OBJECT(menu), "plr", plr0); - g_signal_connect(menu, "show", G_CALLBACK(popup_add_menu), pdialog); - - /* Main table for clauses and (if activated) gold trading: we. */ - table = gtk_grid_new(); - gtk_widget_set_halign(table, GTK_ALIGN_CENTER); - gtk_widget_set_valign(table, GTK_ALIGN_CENTER); - gtk_grid_set_column_spacing(GTK_GRID(table), 16); - gtk_container_add(GTK_CONTAINER(vbox), table); - - if (game.info.trading_gold) { - spin = gtk_spin_button_new_with_range(0.0, plr0->economic.gold + 0.1, - 1.0); - gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0); - gtk_entry_set_width_chars(GTK_ENTRY(spin), 16); - gtk_grid_attach(GTK_GRID(table), spin, 1, 0, 1, 1); - g_object_set_data(G_OBJECT(spin), "plr", plr0); - g_signal_connect_after(spin, "value-changed", - G_CALLBACK(diplo_dialog_returnkey), pdialog); - - label = g_object_new(GTK_TYPE_LABEL, "use-underline", TRUE, - "mnemonic-widget", spin, "label", _("Gold:"), - "xalign", 0.0, "yalign", 0.5, NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); - - gtk_grid_attach(GTK_GRID(table), menubar, 2, 0, 1, 1); - } else { - gtk_grid_attach(GTK_GRID(table), menubar, 0, 0, 1, 1); - } - - /* them. */ - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 5); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); - gtk_container_add(GTK_CONTAINER(mainbox), vbox); - - /* Their nation. */ - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_CENTER); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - buf = g_strdup_printf("%s", - nation_plural_for_player(plr1)); - gtk_label_set_markup(GTK_LABEL(label), buf); - g_free(buf); - gtk_container_add(GTK_CONTAINER(vbox), label); - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 5); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - /* Their flag */ - flag_spr = get_nation_flag_sprite(tileset, nation_of_player(plr1)); - - image = gtk_image_new_from_surface(flag_spr->surface); - gtk_container_add(GTK_CONTAINER(hbox), image); - - /* Their name. */ - label = gtk_label_new(NULL); - gtk_widget_set_hexpand(label, TRUE); - gtk_widget_set_halign(label, GTK_ALIGN_CENTER); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - buf = g_strdup_printf("%s", - ruler_title_for_player(plr1, plr_buf, sizeof(plr_buf))); - gtk_label_set_markup(GTK_LABEL(label), buf); - g_free(buf); - gtk_container_add(GTK_CONTAINER(hbox), label); - - image = gtk_image_new(); - pdialog->image1 = image; - gtk_container_add(GTK_CONTAINER(hbox), image); - - /* Menu for clauses: they. */ - menubar = gtk_aux_menu_bar_new(); - - menu = gtk_menu_new(); - pdialog->menu1 = menu; - - menuitem = gtk_image_menu_item_new_with_label(_("Add Clause...")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), - gtk_image_new_from_stock(GTK_STOCK_ADD, - GTK_ICON_SIZE_MENU)); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem); - g_object_set_data(G_OBJECT(menu), "plr", plr1); - g_signal_connect(menu, "show", G_CALLBACK(popup_add_menu), pdialog); - - /* Main table for clauses and (if activated) gold trading: they. */ - table = gtk_grid_new(); - gtk_widget_set_halign(table, GTK_ALIGN_CENTER); - gtk_widget_set_valign(table, GTK_ALIGN_CENTER); - gtk_grid_set_column_spacing(GTK_GRID(table), 16); - gtk_container_add(GTK_CONTAINER(vbox), table); - - if (game.info.trading_gold) { - spin = gtk_spin_button_new_with_range(0.0, plr1->economic.gold + 0.1, - 1.0); - gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0); - gtk_entry_set_width_chars(GTK_ENTRY(spin), 16); - gtk_grid_attach(GTK_GRID(table), spin, 1, 0, 1, 1); - g_object_set_data(G_OBJECT(spin), "plr", plr1); - g_signal_connect_after(spin, "value-changed", - G_CALLBACK(diplo_dialog_returnkey), pdialog); - - label = g_object_new(GTK_TYPE_LABEL, "use-underline", TRUE, - "mnemonic-widget", spin, "label", _("Gold:"), - "xalign", 0.0, "yalign", 0.5, NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); - - gtk_grid_attach(GTK_GRID(table), menubar, 2, 0, 1, 1); - } else { - gtk_grid_attach(GTK_GRID(table), menubar, 0, 0, 1, 1); - } - - /* Clauses. */ - mainbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(mainbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(pdialog->dialog->vbox), mainbox); - - store = gtk_list_store_new(1, G_TYPE_STRING); - pdialog->store = store; - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - g_object_unref(store); - gtk_widget_set_size_request(view, 320, 100); - - rend = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL, - rend, "text", 0, NULL); - - sw = gtk_scrolled_window_new(NULL, NULL); - g_object_set(sw, "margin", 2, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), view); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", view, - "label", _("C_lauses:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(mainbox), vbox); - gtk_container_add(GTK_CONTAINER(vbox), label); - gtk_container_add(GTK_CONTAINER(vbox), sw); - - gtk_widget_show_all(mainbox); - - g_signal_connect(view, "row_activated", G_CALLBACK(row_callback), pdialog); - - update_diplomacy_dialog(pdialog); - gui_dialog_show_all(pdialog->dialog); - - return pdialog; -} - -/************************************************************************//** - Update diplomacy dialog -****************************************************************************/ -static void update_diplomacy_dialog(struct Diplomacy_dialog *pdialog) -{ - GtkListStore *store; - GtkTreeIter it; - bool blank = TRUE; - GdkPixbuf *pixbuf; - - store = pdialog->store; - - gtk_list_store_clear(store); - clause_list_iterate(pdialog->treaty.clauses, pclause) { - char buf[128]; - - client_diplomacy_clause_string(buf, sizeof(buf), pclause); - - gtk_list_store_append(store, &it); - gtk_list_store_set(store, &it, 0, buf, -1); - blank = FALSE; - } clause_list_iterate_end; - - if (blank) { - gtk_list_store_append(store, &it); - gtk_list_store_set(store, &it, 0, - _("--- This treaty is blank. " - "Please add some clauses. ---"), -1); - } - - pixbuf = get_thumb_pixbuf(pdialog->treaty.accept0); - gtk_image_set_from_pixbuf(GTK_IMAGE(pdialog->image0), pixbuf); - g_object_unref(G_OBJECT(pixbuf)); - pixbuf = get_thumb_pixbuf(pdialog->treaty.accept1); - gtk_image_set_from_pixbuf(GTK_IMAGE(pdialog->image1), pixbuf); - g_object_unref(G_OBJECT(pixbuf)); -} - -/************************************************************************//** - Callback for the diplomatic dialog: give tech. -****************************************************************************/ -static void diplomacy_dialog_tech_callback(GtkWidget *w, gpointer data) -{ - int giver, dest, other, tech; - - giver = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w), "player_from")); - dest = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w), "player_to")); - tech = GPOINTER_TO_INT(data); - if (player_by_number(giver) == client_player()) { - other = dest; - } else { - other = giver; - } - - if (A_LAST == tech) { - /* All techs. */ - struct player *pgiver = player_by_number(giver); - struct player *pdest = player_by_number(dest); - const struct research *dresearch, *gresearch; - - fc_assert_ret(NULL != pgiver); - fc_assert_ret(NULL != pdest); - - dresearch = research_get(pdest); - gresearch = research_get(pgiver); - advance_iterate(A_FIRST, padvance) { - Tech_type_id i = advance_number(padvance); - - if (research_invention_state(gresearch, i) == TECH_KNOWN - && research_invention_gettable(dresearch, i, - game.info.tech_trade_allow_holes) - && (research_invention_state(dresearch, i) == TECH_UNKNOWN - || research_invention_state(dresearch, i) - == TECH_PREREQS_KNOWN)) { - dsend_packet_diplomacy_create_clause_req(&client.conn, other, giver, - CLAUSE_ADVANCE, i); - } - } advance_iterate_end; - } else { - /* Only one tech. */ - dsend_packet_diplomacy_create_clause_req(&client.conn, other, giver, - CLAUSE_ADVANCE, tech); - } -} - -/************************************************************************//** - Callback for trading cities - - Kris Bubendorfer -****************************************************************************/ -static void diplomacy_dialog_city_callback(GtkWidget *w, gpointer data) -{ - size_t choice = GPOINTER_TO_UINT(data); - int giver = (choice >> 24) & 0xff, dest = (choice >> 16) & 0xff, other; - int city = choice & 0xffff; - - if (player_by_number(giver) == client.conn.playing) { - other = dest; - } else { - other = giver; - } - - dsend_packet_diplomacy_create_clause_req(&client.conn, other, giver, - CLAUSE_CITY, city); -} - -/************************************************************************//** - Map menu item activated -****************************************************************************/ -static void diplomacy_dialog_map_callback(GtkWidget *w, gpointer data) -{ - struct Diplomacy_dialog *pdialog = (struct Diplomacy_dialog *)data; - struct player *pgiver; - - pgiver = (struct player *)g_object_get_data(G_OBJECT(w), "plr"); - - dsend_packet_diplomacy_create_clause_req(&client.conn, - player_number(pdialog->treaty.plr1), - player_number(pgiver), CLAUSE_MAP, 0); -} - -/************************************************************************//** - Seamap menu item activated -****************************************************************************/ -static void diplomacy_dialog_seamap_callback(GtkWidget *w, gpointer data) -{ - struct Diplomacy_dialog *pdialog = (struct Diplomacy_dialog *)data; - struct player *pgiver; - - pgiver = (struct player *)g_object_get_data(G_OBJECT(w), "plr"); - - dsend_packet_diplomacy_create_clause_req(&client.conn, - player_number(pdialog->treaty.plr1), - player_number(pgiver), CLAUSE_SEAMAP, - 0); -} - -/************************************************************************//** - Adding pact clause -****************************************************************************/ -static void diplomacy_dialog_add_pact_clause(GtkWidget *w, gpointer data, - int type) -{ - struct Diplomacy_dialog *pdialog = (struct Diplomacy_dialog *)data; - - dsend_packet_diplomacy_create_clause_req(&client.conn, - player_number(pdialog->treaty.plr1), - player_number(pdialog->treaty.plr0), - type, 0); -} - -/************************************************************************//** - Ceasefire pact menu item activated -****************************************************************************/ -static void diplomacy_dialog_ceasefire_callback(GtkWidget *w, gpointer data) -{ - diplomacy_dialog_add_pact_clause(w, data, CLAUSE_CEASEFIRE); -} - -/************************************************************************//** - Peace pact menu item activated -****************************************************************************/ -static void diplomacy_dialog_peace_callback(GtkWidget *w, gpointer data) -{ - diplomacy_dialog_add_pact_clause(w, data, CLAUSE_PEACE); -} - -/************************************************************************//** - Alliance pact menu item activated -****************************************************************************/ -static void diplomacy_dialog_alliance_callback(GtkWidget *w, gpointer data) -{ - diplomacy_dialog_add_pact_clause(w, data, CLAUSE_ALLIANCE); -} - -/************************************************************************//** - Shared vision menu item activated -****************************************************************************/ -static void diplomacy_dialog_vision_callback(GtkWidget *w, gpointer data) -{ - struct Diplomacy_dialog *pdialog = (struct Diplomacy_dialog *) data; - struct player *pgiver = - (struct player *) g_object_get_data(G_OBJECT(w), "plr"); - - dsend_packet_diplomacy_create_clause_req(&client.conn, - player_number(pdialog->treaty.plr1), - player_number(pgiver), CLAUSE_VISION, - 0); -} - -/************************************************************************//** - Embassy menu item activated -****************************************************************************/ -static void diplomacy_dialog_embassy_callback(GtkWidget *w, gpointer data) -{ - struct Diplomacy_dialog *pdialog = (struct Diplomacy_dialog *) data; - struct player *pgiver = - (struct player *) g_object_get_data(G_OBJECT(w), "plr"); - - dsend_packet_diplomacy_create_clause_req(&client.conn, - player_number(pdialog->treaty.plr1), - player_number(pgiver), CLAUSE_EMBASSY, - 0); -} - -/************************************************************************//** - Close diplomacy dialog -****************************************************************************/ -void close_diplomacy_dialog(struct Diplomacy_dialog *pdialog) -{ - diplomacy_destroy(pdialog); -} - -/************************************************************************//** - Initialize diplomacy dialog -****************************************************************************/ -void diplomacy_dialog_init(void) -{ - dialog_list = dialog_list_new(); - dipl_main = NULL; -} - -/************************************************************************//** - Free resources allocated for diplomacy dialog -****************************************************************************/ -void diplomacy_dialog_done(void) -{ - dialog_list_destroy(dialog_list); -} - -/************************************************************************//** - Find diplomacy dialog between player and other player -****************************************************************************/ -static struct Diplomacy_dialog *find_diplomacy_dialog(int other_player_id) -{ - struct player *plr0 = client.conn.playing; - struct player *plr1 = player_by_number(other_player_id); - - dialog_list_iterate(dialog_list, pdialog) { - if ((pdialog->treaty.plr0 == plr0 && pdialog->treaty.plr1 == plr1) - || (pdialog->treaty.plr0 == plr1 && pdialog->treaty.plr1 == plr0)) { - return pdialog; - } - } dialog_list_iterate_end; - - return NULL; -} - -/************************************************************************//** - User hit enter after entering gold amount -****************************************************************************/ -static void diplo_dialog_returnkey(GtkWidget *w, gpointer data) -{ - struct Diplomacy_dialog *pdialog = (struct Diplomacy_dialog *) data; - struct player *pgiver = - (struct player *) g_object_get_data(G_OBJECT(w), "plr"); - int amount = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w)); - - if (amount >= 0 && amount <= pgiver->economic.gold) { - dsend_packet_diplomacy_create_clause_req(&client.conn, - player_number(pdialog->treaty.plr1), - player_number(pgiver), - CLAUSE_GOLD, amount); - } else { - output_window_append(ftc_client, _("Invalid amount of gold specified.")); - } -} - -/************************************************************************//** - Close all dialogs, for when client disconnects from game. -****************************************************************************/ -void close_all_diplomacy_dialogs(void) -{ - while (dialog_list_size(dialog_list) > 0) { - close_diplomacy_dialog(dialog_list_get(dialog_list, 0)); - } -} diff --git a/client/gui-gtk-3.0/diplodlg.h b/client/gui-gtk-3.0/diplodlg.h deleted file mode 100644 index f713ecbb9f..0000000000 --- a/client/gui-gtk-3.0/diplodlg.h +++ /dev/null @@ -1,23 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__DIPLODLG_H -#define FC__DIPLODLG_H - -#include - -#include "diplodlg_g.h" - -void diplomacy_dialog_init(void); -void diplomacy_dialog_done(void); - -#endif /* FC__DIPLODLG_H */ diff --git a/client/gui-gtk-3.0/editgui.c b/client/gui-gtk-3.0/editgui.c deleted file mode 100644 index 5745fa20a4..0000000000 --- a/client/gui-gtk-3.0/editgui.c +++ /dev/null @@ -1,1922 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 2005 - The Freeciv Project - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "connection.h" -#include "game.h" -#include "government.h" -#include "packets.h" - -/* client */ -#include "chatline_common.h" -#include "client_main.h" -#include "dialogs_g.h" -#include "editor.h" -#include "mapview_common.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "canvas.h" -#include "editgui.h" -#include "editprop.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "plrdlg.h" -#include "sprite.h" - - -enum tool_value_selector_columns { - TVS_COL_IMAGE = 0, - TVS_COL_ID, - TVS_COL_NAME, - - TVS_NUM_COLS -}; - -enum player_pov_combo_columns { - PPV_COL_FLAG = 0, - PPV_COL_NAME, - PPV_COL_PLAYER_NO, - - PPV_NUM_COLS -}; - -enum tool_applied_player_columns { - TAP_COL_FLAG = 0, - TAP_COL_NAME, - TAP_COL_PLAYER_NO, - - TAP_NUM_COLS -}; - -enum spin_button_types { - SPIN_BUTTON_SIZE, - SPIN_BUTTON_COUNT -}; - -struct tool_value_selector { - struct editbar *editbar_parent; - - GtkWidget *dialog; - - GtkListStore *store; - GtkWidget *view; -}; - -static struct tool_value_selector * -create_tool_value_selector(struct editbar *eb_parent, - enum editor_tool_type ett); -static void editinfobox_refresh(struct editinfobox *ei); -static void editbar_player_pov_combobox_changed(GtkComboBox *combo, - gpointer user_data); -static void editbar_mode_button_toggled(GtkToggleButton *tb, - gpointer userdata); -static void editbar_tool_button_toggled(GtkToggleButton *tb, - gpointer userdata); -static void try_to_set_editor_tool(enum editor_tool_type ett); - -static struct editbar *editor_toolbar; -static struct editinfobox *editor_infobox; - -/************************************************************************//** - Refresh the buttons in the given editbar according to the current - editor state. -****************************************************************************/ -static void refresh_all_buttons(struct editbar *eb) -{ - enum editor_tool_type ett; - enum editor_tool_mode etm; - GtkWidget *tb = NULL; - int i; - - if (eb == NULL) { - return; - } - - ett = editor_get_tool(); - etm = editor_tool_get_mode(ett); - - for (i = 0; i < NUM_EDITOR_TOOL_MODES; i++) { - tb = eb->mode_buttons[i]; - if (tb == NULL) { - continue; - } - disable_gobject_callback(G_OBJECT(tb), - G_CALLBACK(editbar_mode_button_toggled)); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), i == etm); - enable_gobject_callback(G_OBJECT(tb), - G_CALLBACK(editbar_mode_button_toggled)); - gtk_widget_set_sensitive(tb, editor_tool_has_mode(ett, i)); - } - - if (ett < NUM_EDITOR_TOOL_TYPES - && eb->tool_buttons[ett] != NULL) { - tb = eb->tool_buttons[ett]; - disable_gobject_callback(G_OBJECT(tb), - G_CALLBACK(editbar_tool_button_toggled)); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), TRUE); - enable_gobject_callback(G_OBJECT(tb), - G_CALLBACK(editbar_tool_button_toggled)); - } -} - -/************************************************************************//** - Callback for all tool mode toggle buttons. -****************************************************************************/ -static void editbar_mode_button_toggled(GtkToggleButton *tb, - gpointer userdata) -{ - gboolean active; - enum editor_tool_mode etm; - enum editor_tool_type ett; - - etm = GPOINTER_TO_INT(userdata); - if (!(etm < NUM_EDITOR_TOOL_MODES)) { - return; - } - - active = gtk_toggle_button_get_active(tb); - ett = editor_get_tool(); - - editor_tool_set_mode(ett, active ? etm : ETM_PAINT); - editgui_refresh(); -} - -/************************************************************************//** - Try to set the given tool as the current editor tool. If the tool is - unavailable (editor_tool_is_usable) an error popup is displayed. -****************************************************************************/ -static void try_to_set_editor_tool(enum editor_tool_type ett) -{ - if (!(ett < NUM_EDITOR_TOOL_TYPES)) { - return; - } - - if (!editor_tool_is_usable(ett)) { - GtkWidget *dialog; - dialog = gtk_message_dialog_new(GTK_WINDOW(toplevel), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", - _("The current ruleset does not define any " - "objects corresponding to this editor tool.")); - gtk_window_set_title(GTK_WINDOW(dialog), editor_tool_get_name(ett)); - gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_destroy(dialog); - } else { - editor_set_tool(ett); - } -} - -/************************************************************************//** - Callback to handle toggling of any of the tool buttons. -****************************************************************************/ -static void editbar_tool_button_toggled(GtkToggleButton *tb, - gpointer userdata) -{ - gboolean active; - enum editor_tool_type ett; - - active = gtk_toggle_button_get_active(tb); - ett = GPOINTER_TO_INT(userdata); - - if (active) { - try_to_set_editor_tool(ett); - editgui_refresh(); - } -} - -/************************************************************************//** - Refresh the player point-of-view indicator based on the client and - editor state. - - NB: The convention is that the first entry (index 0) in the combo box - corresponds to the "global observer". -****************************************************************************/ -static void refresh_player_pov_indicator(struct editbar *eb) -{ - GtkListStore *store; - GdkPixbuf *flag; - GtkTreeIter iter; - GtkWidget *combo; - int index = -1, i; - - if (eb == NULL || eb->player_pov_store == NULL) { - return; - } - - store = eb->player_pov_store; - gtk_list_store_clear(store); - - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, PPV_COL_NAME, _("Global Observer"), - PPV_COL_PLAYER_NO, -1, -1); - - i = 1; - players_iterate(pplayer) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, PPV_COL_NAME, player_name(pplayer), - PPV_COL_PLAYER_NO, player_number(pplayer), -1); - - if (pplayer->nation != NO_NATION_SELECTED) { - flag = get_flag(pplayer->nation); - - if (flag != NULL) { - gtk_list_store_set(store, &iter, PPV_COL_FLAG, flag, -1); - g_object_unref(flag); - } - } - if (pplayer == client_player()) { - index = i; - } - i++; - } players_iterate_end; - - if (client_is_global_observer()) { - index = 0; - } - - combo = eb->player_pov_combobox; - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), index); -} - -/************************************************************************//** - Callback to handle selection of a player/global observer in the - player pov indicator. - - NB: The convention is that the first entry (index 0) in the combo box - corresponds to the "global observer". -****************************************************************************/ -static void editbar_player_pov_combobox_changed(GtkComboBox *combo, - gpointer user_data) -{ - struct editbar *eb = user_data; - int id; - struct player *pplayer; - GtkTreeIter iter; - GtkTreeModel *model; - - if (eb == NULL || eb->widget == NULL - || !gtk_widget_get_visible(eb->widget)) { - return; - } - - if (!gtk_combo_box_get_active_iter(combo, &iter)) { - return; - } - - model = gtk_combo_box_get_model(combo); - gtk_tree_model_get(model, &iter, PPV_COL_PLAYER_NO, &id, -1); - - if ((client_is_global_observer() && id == -1) - || client_player_number() == id) { - return; - } - - /* Ugh... hard-coded server command strings. :( */ - if (id == -1) { - send_chat("/observe"); - return; - } - - pplayer = player_by_number(id); - if (pplayer != NULL) { - send_chat_printf("/take \"%s\"", pplayer->name); - } -} - -/************************************************************************//** - Run the tool value selection dialog and return the value ID selected. - Returns -1 if cancelled. -****************************************************************************/ -static int tool_value_selector_run(struct tool_value_selector *tvs) -{ - gint res; - GtkWidget *dialog; - GtkTreeSelection *sel; - GtkTreeIter iter; - GtkTreeModel *model; - int id; - - if (tvs == NULL) { - return -1; - } - - dialog = tvs->dialog; - res = gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_hide(dialog); - - if (res != GTK_RESPONSE_ACCEPT) { - return -1; - } - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tvs->view)); - if (!gtk_tree_selection_get_selected(sel, &model, &iter)) { - return -1; - } - - gtk_tree_model_get(model, &iter, TVS_COL_ID, &id, -1); - - return id; -} - -/************************************************************************//** - Run the tool value selector for the given tool type. Sets the editor state - and refreshes the editor GUI depending on the user's choices. - - Returns FALSE if running the dialog is not possible. -****************************************************************************/ -static bool editgui_run_tool_selection(enum editor_tool_type ett) -{ - struct editbar *eb; - struct tool_value_selector *tvs; - int res = -1; - - eb = editgui_get_editbar(); - if (eb == NULL || !(ett < NUM_EDITOR_TOOL_TYPES)) { - return FALSE; - } - - if (!editor_tool_has_value(ett)) { - return FALSE; - } - - tvs = eb->tool_selectors[ett]; - if (!tvs) { - return FALSE; - } - - res = tool_value_selector_run(tvs); - - if (res >= 0) { - editor_tool_set_value(ett, res); - editinfobox_refresh(editgui_get_editinfobox()); - } - - return TRUE; -} - -/************************************************************************//** - Handle a mouse click on any of the tool buttons. -****************************************************************************/ -static gboolean editbar_tool_button_mouse_click(GtkWidget *w, - GdkEventButton *ev, - gpointer userdata) -{ - int ett; - - if (ev->button != 3) { - return FALSE; /* Propagate event further. */ - } - - ett = GPOINTER_TO_INT(userdata); - return editgui_run_tool_selection(ett); -} - -/************************************************************************//** - A helper function to create a toolbar button for the given editor tool. - Packs the newly created button into the hbox 'eb->widget'. -****************************************************************************/ -static void editbar_add_tool_button(struct editbar *eb, - enum editor_tool_type ett) -{ - GdkPixbuf *pixbuf; - GtkWidget *image, *button, *hbox; - GtkRadioButton *parent = NULL; - struct sprite *sprite; - int i; - - if (!eb || !(ett < NUM_EDITOR_TOOL_TYPES)) { - return; - } - - for (i = 0; i < NUM_EDITOR_TOOL_TYPES; i++) { - if (eb->tool_buttons[i] != NULL) { - parent = GTK_RADIO_BUTTON(eb->tool_buttons[i]); - break; - } - } - - if (parent == NULL) { - button = gtk_radio_button_new(NULL); - } else { - button = gtk_radio_button_new_from_widget(parent); - } - - sprite = editor_tool_get_sprite(ett); - fc_assert_ret(sprite != NULL); - pixbuf = sprite_get_pixbuf(sprite); - image = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(G_OBJECT(pixbuf)); - - gtk_container_add(GTK_CONTAINER(button), image); - gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(button), FALSE); - gtk_widget_set_tooltip_text(button, editor_tool_get_tooltip(ett)); - gtk_size_group_add_widget(eb->size_group, button); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); - - g_signal_connect(button, "toggled", - G_CALLBACK(editbar_tool_button_toggled), GINT_TO_POINTER(ett)); - g_signal_connect(button, "button_press_event", - G_CALLBACK(editbar_tool_button_mouse_click), GINT_TO_POINTER(ett)); - - hbox = eb->widget; - gtk_container_add(GTK_CONTAINER(hbox), button); - eb->tool_buttons[ett] = button; - - if (editor_tool_has_value(ett)) { - eb->tool_selectors[ett] = create_tool_value_selector(eb, ett); - } -} - -/************************************************************************//** - Handle a click on the player properties button in the editor toolbar. -****************************************************************************/ -static void editbar_player_properties_button_clicked(GtkButton *b, - gpointer userdata) -{ - struct property_editor *pe; - - pe = editprop_get_property_editor(); - property_editor_reload(pe, OBJTYPE_GAME); - property_editor_reload(pe, OBJTYPE_PLAYER); - property_editor_popup(pe, OBJTYPE_PLAYER); -} - -/************************************************************************//** - Helper function to add a tool mode button to the editor toolbar. The - button will be packed into the start of the hbox 'eb->widget'. -****************************************************************************/ -static void editbar_add_mode_button(struct editbar *eb, - enum editor_tool_mode etm) -{ - GdkPixbuf *pixbuf; - GtkWidget *image, *button, *hbox; - struct sprite *sprite; - const char *tooltip; - - if (!eb || !(etm < NUM_EDITOR_TOOL_MODES)) { - return; - } - - button = gtk_toggle_button_new(); - - sprite = editor_get_mode_sprite(etm); - fc_assert_ret(sprite != NULL); - pixbuf = sprite_get_pixbuf(sprite); - image = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(G_OBJECT(pixbuf)); - - gtk_container_add(GTK_CONTAINER(button), image); - gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(button), FALSE); - tooltip = editor_get_mode_tooltip(etm); - if (tooltip != NULL) { - gtk_widget_set_tooltip_text(button, tooltip); - } - gtk_size_group_add_widget(eb->size_group, button); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); - - g_signal_connect(button, "toggled", - G_CALLBACK(editbar_mode_button_toggled), GINT_TO_POINTER(etm)); - - hbox = eb->widget; - gtk_container_add(GTK_CONTAINER(hbox), button); - eb->mode_buttons[etm] = button; -} - -/************************************************************************//** - Create and return an editor toolbar. -****************************************************************************/ -static struct editbar *editbar_create(void) -{ - struct editbar *eb; - GtkWidget *hbox, *button, *combo, *image, *separator, *vbox; - GtkListStore *store; - GtkCellRenderer *cell; - GdkPixbuf *pixbuf; - const struct editor_sprites *sprites; - - eb = fc_calloc(1, sizeof(struct editbar)); - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - eb->widget = hbox; - eb->size_group = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); - - sprites = get_editor_sprites(tileset); - - editbar_add_mode_button(eb, ETM_ERASE); - - separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(hbox), separator); - - editbar_add_tool_button(eb, ETT_TERRAIN); - editbar_add_tool_button(eb, ETT_TERRAIN_RESOURCE); - editbar_add_tool_button(eb, ETT_TERRAIN_SPECIAL); - editbar_add_tool_button(eb, ETT_ROAD); - editbar_add_tool_button(eb, ETT_MILITARY_BASE); - editbar_add_tool_button(eb, ETT_UNIT); - editbar_add_tool_button(eb, ETT_CITY); - editbar_add_tool_button(eb, ETT_VISION); - editbar_add_tool_button(eb, ETT_STARTPOS); - editbar_add_tool_button(eb, ETT_COPYPASTE); - - separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(hbox), separator); - - /* Player POV indicator. */ - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(hbox), vbox); - - store = gtk_list_store_new(PPV_NUM_COLS, - GDK_TYPE_PIXBUF, - G_TYPE_STRING, - G_TYPE_INT); - eb->player_pov_store = store; - - combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); - - cell = gtk_cell_renderer_pixbuf_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, FALSE); - gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combo), - cell, "pixbuf", PPV_COL_FLAG); - - cell = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE); - gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combo), - cell, "text", PPV_COL_NAME); - - gtk_widget_set_size_request(combo, 140, -1); - g_signal_connect(combo, "changed", - G_CALLBACK(editbar_player_pov_combobox_changed), eb); - - gtk_widget_set_tooltip_text(combo, - _("Switch player point-of-view. Use this to edit " - "from the perspective of different players, or " - "even as a global observer.")); - gtk_container_add(GTK_CONTAINER(vbox), combo); - eb->player_pov_combobox = combo; - - /* Property editor button. */ - button = gtk_button_new(); - pixbuf = sprite_get_pixbuf(sprites->properties); - image = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(G_OBJECT(pixbuf)); - gtk_container_add(GTK_CONTAINER(button), image); - gtk_widget_set_tooltip_text(button, _("Show the property editor.")); - gtk_size_group_add_widget(eb->size_group, button); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); - g_signal_connect(button, "clicked", - G_CALLBACK(editbar_player_properties_button_clicked), eb); - gtk_container_add(GTK_CONTAINER(hbox), button); - eb->player_properties_button = button; - - return eb; -} - -/************************************************************************//** - Refresh the tool value selector in the given toolbar for the given tool - type with data from the editor state. -****************************************************************************/ -static void refresh_tool_value_selector(struct editbar *eb, - enum editor_tool_type ett) -{ - GtkTreeSelection *sel; - GtkTreeIter iter; - GtkTreeModel *model; - int value, store_value; - struct tool_value_selector *tvs; - - if (!editor_is_active() || !eb || !editor_tool_has_value(ett)) { - return; - } - - tvs = eb->tool_selectors[ett]; - - if (!tvs) { - return; - } - - value = editor_tool_get_value(ett); - model = GTK_TREE_MODEL(tvs->store); - if (!gtk_tree_model_get_iter_first(model, &iter)) { - return; - } - - do { - gtk_tree_model_get(model, &iter, TVS_COL_ID, &store_value, -1); - if (value == store_value) { - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tvs->view)); - gtk_tree_selection_select_iter(sel, &iter); - break; - } - } while (gtk_tree_model_iter_next(model, &iter)); -} - -/************************************************************************//** - Refresh all tool value selectors in the given toolbar according to the - current editor state. -****************************************************************************/ -static void refresh_all_tool_value_selectors(struct editbar *eb) -{ - int ett; - - if (!eb) { - return; - } - - for (ett = 0; ett < NUM_EDITOR_TOOL_TYPES; ett++) { - if (editor_tool_has_value(ett)) { - refresh_tool_value_selector(eb, ett); - } - } -} - -/************************************************************************//** - Refresh the given toolbar according to the current editor state. -****************************************************************************/ -static void editbar_refresh(struct editbar *eb) -{ - if (eb == NULL || eb->widget == NULL) { - return; - } - - if (!editor_is_active()) { - gtk_widget_hide(eb->widget); - return; - } - - refresh_all_buttons(eb); - refresh_all_tool_value_selectors(eb); - refresh_player_pov_indicator(eb); - - gtk_widget_show_all(eb->widget); -} - -/************************************************************************//** - Create a pixbuf containing a representative image for the given terrain - type, to be used as an icon in the GUI. - - May return NULL on error. - - NB: You must call g_object_unref on the non-NULL return value when you - no longer need it. -****************************************************************************/ -static GdkPixbuf *create_terrain_pixbuf(struct terrain *pterrain) -{ - int w, h, i; - GdkPixbuf *pixbuf; - struct canvas canvas = FC_STATIC_CANVAS_INIT; - cairo_t *cr; - - w = tileset_tile_width(tileset); - h = tileset_tile_height(tileset); - - canvas.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); - - cr = cairo_create(canvas.surface); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_paint(cr); - cairo_destroy(cr); - - for (i = 0; i < 3; i++) { - struct drawn_sprite sprs[80]; - int count = fill_basic_terrain_layer_sprite_array(tileset, sprs, - i, pterrain); - - put_drawn_sprites(&canvas, 1.0, 0, 0, count, sprs, FALSE); - } - - pixbuf = surface_get_pixbuf(canvas.surface, w, h); - cairo_surface_destroy(canvas.surface); - - return pixbuf; -} - -/************************************************************************//** - Clear icons from tool store, and the store itself. -****************************************************************************/ -static void clear_tool_store(GtkListStore *store) -{ - GtkTreeIter iter; - GtkTreeModel *model = GTK_TREE_MODEL(store); - - if (gtk_tree_model_get_iter_first(model, &iter)) { - do { - GdkPixbuf *pixbuf; - - gtk_tree_model_get(model, &iter, TVS_COL_IMAGE, &pixbuf, -1); - if (pixbuf != NULL) { - g_object_unref(pixbuf); - } - } while (gtk_tree_model_iter_next(model, &iter)); - } - - gtk_list_store_clear(store); -} - -/************************************************************************//** - Clears all stores from the editbar. -****************************************************************************/ -static void clear_tool_stores(struct editbar *eb) -{ - clear_tool_store(eb->tool_selectors[ETT_TERRAIN]->store); - clear_tool_store(eb->tool_selectors[ETT_TERRAIN_RESOURCE]->store); - clear_tool_store(eb->tool_selectors[ETT_TERRAIN_SPECIAL]->store); - clear_tool_store(eb->tool_selectors[ETT_ROAD]->store); - clear_tool_store(eb->tool_selectors[ETT_MILITARY_BASE]->store); - clear_tool_store(eb->tool_selectors[ETT_UNIT]->store); -} - -/************************************************************************//** - Reload all tool value data from the tileset for the given toolbar. -****************************************************************************/ -static void editbar_reload_tileset(struct editbar *eb) -{ - GtkTreeIter iter; - GtkListStore *store; - GdkPixbuf *pixbuf; - struct sprite *sprite; - struct tool_value_selector *tvs; - - if (eb == NULL || tileset == NULL) { - return; - } - - clear_tool_stores(eb); - - /* Reload terrains. */ - - tvs = eb->tool_selectors[ETT_TERRAIN]; - store = tvs->store; - - terrain_type_iterate(pterrain) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - TVS_COL_ID, terrain_number(pterrain), - TVS_COL_NAME, terrain_name_translation(pterrain), - -1); - pixbuf = create_terrain_pixbuf(pterrain); - if (pixbuf != NULL) { - gtk_list_store_set(store, &iter, TVS_COL_IMAGE, pixbuf, -1); - g_object_unref(pixbuf); - } - } terrain_type_iterate_end; - - - /* Reload terrain resources. */ - - tvs = eb->tool_selectors[ETT_TERRAIN_RESOURCE]; - store = tvs->store; - - extra_type_by_cause_iterate(EC_RESOURCE, pextra) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - TVS_COL_ID, extra_index(pextra), - TVS_COL_NAME, extra_name_translation(pextra), - -1); - pixbuf = create_extra_pixbuf(pextra); - if (pixbuf != NULL) { - gtk_list_store_set(store, &iter, TVS_COL_IMAGE, pixbuf, -1); - g_object_unref(pixbuf); - } - } extra_type_by_cause_iterate_end; - - /* Reload terrain specials. */ - - tvs = eb->tool_selectors[ETT_TERRAIN_SPECIAL]; - store = tvs->store; - - extra_type_by_cause_iterate(EC_SPECIAL, pextra) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - TVS_COL_ID, extra_index(pextra), - TVS_COL_NAME, extra_name_translation(pextra), - -1); - pixbuf = create_extra_pixbuf(pextra); - if (pixbuf != NULL) { - gtk_list_store_set(store, &iter, TVS_COL_IMAGE, pixbuf, -1); - g_object_unref(pixbuf); - } - } extra_type_by_cause_iterate_end; - - /* Reload roads. */ - - tvs = eb->tool_selectors[ETT_ROAD]; - store = tvs->store; - - extra_type_by_cause_iterate(EC_ROAD, pextra) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - TVS_COL_ID, extra_index(pextra), - TVS_COL_NAME, extra_name_translation(pextra), - -1); - pixbuf = create_extra_pixbuf(pextra); - if (pixbuf != NULL) { - gtk_list_store_set(store, &iter, TVS_COL_IMAGE, pixbuf, -1); - g_object_unref(pixbuf); - } - } extra_type_by_cause_iterate_end; - - /* Reload military bases. */ - - tvs = eb->tool_selectors[ETT_MILITARY_BASE]; - store = tvs->store; - - extra_type_by_cause_iterate(EC_BASE, pextra) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - TVS_COL_ID, extra_index(pextra), - TVS_COL_NAME, extra_name_translation(pextra), - -1); - pixbuf = create_extra_pixbuf(pextra); - if (pixbuf != NULL) { - gtk_list_store_set(store, &iter, TVS_COL_IMAGE, pixbuf, -1); - g_object_unref(pixbuf); - } - } extra_type_by_cause_iterate_end; - - - /* Reload unit types. */ - - tvs = eb->tool_selectors[ETT_UNIT]; - store = tvs->store; - - unit_type_iterate(putype) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - TVS_COL_ID, utype_number(putype), - TVS_COL_NAME, utype_name_translation(putype), - -1); - sprite = get_unittype_sprite(tileset, putype, direction8_invalid()); - if (sprite == NULL) { - continue; - } - pixbuf = sprite_get_pixbuf(sprite); - if (pixbuf != NULL) { - gtk_list_store_set(store, &iter, TVS_COL_IMAGE, pixbuf, -1); - g_object_unref(G_OBJECT(pixbuf)); - } - } unit_type_iterate_end; -} - -/************************************************************************//** - Convert gdk modifier values to editor modifier values. -****************************************************************************/ -static int convert_modifiers(int gdk_event_state) -{ - int modifiers = EKM_NONE; - - if (gdk_event_state & GDK_SHIFT_MASK) { - modifiers |= EKM_SHIFT; - } - if (gdk_event_state & GDK_CONTROL_MASK) { - modifiers |= EKM_CTRL; - } - if (gdk_event_state & GDK_MOD1_MASK) { - modifiers |= EKM_ALT; - } - - return modifiers; -} - -/************************************************************************//** - Convert gdk mouse button values to editor mouse button values. -****************************************************************************/ -static int convert_mouse_button(int gdk_mouse_button) -{ - switch (gdk_mouse_button) { - case 1: - return MOUSE_BUTTON_LEFT; - break; - case 2: - return MOUSE_BUTTON_MIDDLE; - break; - case 3: - return MOUSE_BUTTON_RIGHT; - break; - default: - break; - } - - return MOUSE_BUTTON_OTHER; -} - -/************************************************************************//** - Pass on the gdk mouse event to the editor's handler. -****************************************************************************/ -gboolean handle_edit_mouse_button_press(GdkEventButton *ev) -{ - if (ev->type != GDK_BUTTON_PRESS) { - return TRUE; - } - - editor_mouse_button_press(ev->x, ev->y, - convert_mouse_button(ev->button), - convert_modifiers(ev->state)); - - return TRUE; -} - -/************************************************************************//** - Pass on the gdk mouse event to the editor's handler. -****************************************************************************/ -gboolean handle_edit_mouse_button_release(GdkEventButton *ev) -{ - if (ev->type != GDK_BUTTON_RELEASE) { - return TRUE; - } - - editor_mouse_button_release(ev->x, ev->y, - convert_mouse_button(ev->button), - convert_modifiers(ev->state)); - return TRUE; -} - -/************************************************************************//** - Pass on the gdk mouse event to the editor's handler. -****************************************************************************/ -gboolean handle_edit_mouse_move(GdkEventMotion *ev) -{ - editor_mouse_move(ev->x, ev->y, convert_modifiers(ev->state)); - return TRUE; -} - -/************************************************************************//** - Handle a double-click on the tool value list. -****************************************************************************/ -static void tool_value_selector_treeview_row_activated(GtkTreeView *view, - GtkTreePath *path, - GtkTreeViewColumn *col, - gpointer user_data) -{ - struct tool_value_selector *tvs = user_data; - - gtk_dialog_response(GTK_DIALOG(tvs->dialog), GTK_RESPONSE_ACCEPT); -} - -/************************************************************************//** - Create a tool value selection dialog for the given toolbar. -****************************************************************************/ -static struct tool_value_selector * -create_tool_value_selector(struct editbar *eb, - enum editor_tool_type ett) -{ - struct tool_value_selector *tvs; - GtkWidget *vbox, *view, *scrollwin; - GtkCellRenderer *cell; - GtkListStore *store; - GtkTreeViewColumn *col; - GtkTreeSelection *sel; - - tvs = fc_calloc(1, sizeof(struct tool_value_selector)); - - tvs->editbar_parent = eb; - - tvs->dialog = gtk_dialog_new_with_buttons(_("Select Tool Value"), - GTK_WINDOW(toplevel), GTK_DIALOG_MODAL, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, - NULL); - vbox = gtk_dialog_get_content_area(GTK_DIALOG(tvs->dialog)); - - store = gtk_list_store_new(TVS_NUM_COLS, - GDK_TYPE_PIXBUF, - G_TYPE_INT, - G_TYPE_STRING); - tvs->store = store; - - scrollwin = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), - GTK_POLICY_NEVER, - GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), - 10 * tileset_tile_height(tileset)); - gtk_box_pack_start(GTK_BOX(vbox), scrollwin, TRUE, TRUE, 0); - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tvs->store)); - gtk_widget_set_size_request(view, -1, 10 * tileset_tile_height(tileset)); - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE); - gtk_tree_view_set_search_column(GTK_TREE_VIEW(view), TVS_COL_NAME); - g_signal_connect(view, "row-activated", - G_CALLBACK(tool_value_selector_treeview_row_activated), tvs); - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); - - cell = gtk_cell_renderer_pixbuf_new(); - col = gtk_tree_view_column_new_with_attributes(editor_tool_get_name(ett), - cell, "pixbuf", - TVS_COL_IMAGE, NULL); - gtk_tree_view_column_set_resizable(col, FALSE); - gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_column_set_reorderable(col, FALSE); - gtk_tree_view_column_set_clickable(col, FALSE); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - - cell = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes("ID", cell, - "text", TVS_COL_ID, NULL); - gtk_tree_view_column_set_resizable(col, FALSE); - gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_column_set_reorderable(col, FALSE); - gtk_tree_view_column_set_clickable(col, FALSE); - gtk_tree_view_column_set_sort_column_id(col, TVS_COL_ID); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - - cell = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes("Name", cell, - "text", TVS_COL_NAME, NULL); - gtk_tree_view_column_set_resizable(col, FALSE); - gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_column_set_reorderable(col, FALSE); - gtk_tree_view_column_set_clickable(col, FALSE); - gtk_tree_view_column_set_sort_column_id(col, TVS_COL_NAME); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - - gtk_container_add(GTK_CONTAINER(scrollwin), view); - tvs->view = view; - - /* Show everything but the window itself. */ - gtk_widget_show_all(vbox); - - return tvs; -} - -/************************************************************************//** - Handle a mouse click on the tool image area in the editor info box. -****************************************************************************/ -static gboolean editinfobox_handle_tool_image_button_press(GtkWidget *evbox, - GdkEventButton *ev, - gpointer data) -{ - editgui_run_tool_selection(editor_get_tool()); - return TRUE; -} - -/************************************************************************//** - Handle a mouse click on the mode image area in the editor info box. -****************************************************************************/ -static gboolean editinfobox_handle_mode_image_button_press(GtkWidget *evbox, - GdkEventButton *ev, - gpointer data) -{ - editor_tool_cycle_mode(editor_get_tool()); - editgui_refresh(); - - return TRUE; -} - -/************************************************************************//** - Callback for spin button changes in the editor info box. -****************************************************************************/ -static void editinfobox_spin_button_value_changed(GtkSpinButton *spinbutton, - gpointer userdata) -{ - struct editinfobox *ei; - int which, value; - enum editor_tool_type ett; - - ei = editgui_get_editinfobox(); - - if (!ei) { - return; - } - - value = gtk_spin_button_get_value_as_int(spinbutton); - which = GPOINTER_TO_INT(userdata); - ett = editor_get_tool(); - - switch (which) { - case SPIN_BUTTON_SIZE: - editor_tool_set_size(ett, value); - break; - case SPIN_BUTTON_COUNT: - editor_tool_set_count(ett, value); - break; - default: - return; - break; - } - - editinfobox_refresh(ei); -} - -/************************************************************************//** - Callback for changes in the applied player combobox in the editor - info box. -****************************************************************************/ -static void editinfobox_tool_applied_player_changed(GtkComboBox *combo, - gpointer userdata) -{ - struct editinfobox *ei = userdata; - GtkTreeIter iter; - GtkTreeModel *model; - int player_no = -1; - - if (ei == NULL) { - return; - } - - if (!gtk_combo_box_get_active_iter(combo, &iter)) { - return; - } - - model = gtk_combo_box_get_model(combo); - gtk_tree_model_get(model, &iter, TAP_COL_PLAYER_NO, &player_no, -1); - - editor_tool_set_applied_player(editor_get_tool(), player_no); -} - -/************************************************************************//** - Create and return an editor info box widget bundle. -****************************************************************************/ -static struct editinfobox *editinfobox_create(void) -{ - GtkWidget *label, *vbox, *frame, *hbox, *vbox2, *image, *evbox; - GtkWidget *spin, *combo; - GtkListStore *store; - GtkCellRenderer *cell; - struct editinfobox *ei; - char buf[128]; - - ei = fc_calloc(1, sizeof(*ei)); - - frame = gtk_frame_new(_("Editor Tool")); - gtk_container_set_border_width(GTK_CONTAINER(frame), 4); - ei->widget = frame; - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 8); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); - gtk_container_add(GTK_CONTAINER(frame), vbox); - - /* tool section */ - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 8); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - evbox = gtk_event_box_new(); - gtk_widget_set_tooltip_text(evbox, _("Click to change value if applicable.")); - g_signal_connect(evbox, "button_press_event", - G_CALLBACK(editinfobox_handle_tool_image_button_press), NULL); - gtk_container_add(GTK_CONTAINER(hbox), evbox); - - image = gtk_image_new(); - gtk_container_add(GTK_CONTAINER(evbox), image); - ei->tool_image = image; - - vbox2 = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox2), 4); - gtk_container_add(GTK_CONTAINER(hbox), vbox2); - - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(vbox2), label); - ei->tool_label = label; - - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(vbox2), label); - ei->tool_value_label = label; - - /* mode section */ - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 8); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - evbox = gtk_event_box_new(); - gtk_widget_set_tooltip_text(evbox, _("Click to change tool mode.")); - g_signal_connect(evbox, "button_press_event", - G_CALLBACK(editinfobox_handle_mode_image_button_press), NULL); - gtk_container_add(GTK_CONTAINER(hbox), evbox); - - image = gtk_image_new(); - gtk_container_add(GTK_CONTAINER(evbox), image); - ei->mode_image = image; - - vbox2 = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox2), 4); - gtk_container_add(GTK_CONTAINER(hbox), vbox2); - - label = gtk_label_new(NULL); - fc_snprintf(buf, sizeof(buf), "%s", - _("Mode")); - gtk_label_set_markup(GTK_LABEL(label), buf); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(vbox2), label); - - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(vbox2), label); - ei->mode_label = label; - - /* spinner section */ - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 8); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - ei->size_hbox = hbox; - spin = gtk_spin_button_new_with_range(1, 255, 1); - gtk_widget_set_tooltip_text(spin, - _("Use this to change the \"size\" parameter for the tool. " - "This parameter controls for example the half-width " - "of the square of tiles that will be affected by the " - "tool, or the size of a created city.")); - g_signal_connect(spin, "value-changed", - G_CALLBACK(editinfobox_spin_button_value_changed), - GINT_TO_POINTER(SPIN_BUTTON_SIZE)); - gtk_container_add(GTK_CONTAINER(hbox), spin); - ei->size_spin_button = spin; - label = gtk_label_new(_("Size")); - gtk_container_add(GTK_CONTAINER(hbox), label); - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 8); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - ei->count_hbox = hbox; - spin = gtk_spin_button_new_with_range(1, 255, 1); - gtk_widget_set_tooltip_text(spin, - _("Use this to change the tool's \"count\" parameter. " - "This controls for example how many units are placed " - "at once with the unit tool.")); - g_signal_connect(spin, "value-changed", - G_CALLBACK(editinfobox_spin_button_value_changed), - GINT_TO_POINTER(SPIN_BUTTON_COUNT)); - gtk_container_add(GTK_CONTAINER(hbox), spin); - ei->count_spin_button = spin; - label = gtk_label_new(_("Count")); - gtk_container_add(GTK_CONTAINER(hbox), label); - - /* combo section */ - store = gtk_list_store_new(TAP_NUM_COLS, - GDK_TYPE_PIXBUF, - G_TYPE_STRING, - G_TYPE_INT); - ei->tool_applied_player_store = store; - combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); - - cell = gtk_cell_renderer_pixbuf_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, FALSE); - gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combo), - cell, "pixbuf", TAP_COL_FLAG); - - cell = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE); - gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combo), - cell, "text", TAP_COL_NAME); - - gtk_widget_set_size_request(combo, 132, -1); - g_signal_connect(combo, "changed", - G_CALLBACK(editinfobox_tool_applied_player_changed), ei); - - gtk_widget_set_tooltip_text(combo, - _("Use this to change the \"applied player\" tool parameter. " - "This controls for example under which player units and cities " - "are created.")); - gtk_container_add(GTK_CONTAINER(vbox), combo); - ei->tool_applied_player_combobox = combo; - - /* We add a ref to the editinfobox widget so that it is - * not destroyed when replaced by the unit info box when - * we leave edit mode. See editinfobox_refresh(). */ - g_object_ref(ei->widget); - - /* The edit info box starts with no parent, so we have to - * show its internal widgets manually. */ - gtk_widget_show_all(ei->widget); - - return ei; -} - -/************************************************************************//** - Refresh the given editinfobox's applied player combobox according to the - current editor state. -****************************************************************************/ -static void refresh_tool_applied_player_combo(struct editinfobox *ei) -{ - enum editor_tool_type ett; - GtkListStore *store; - GtkWidget *combo; - GtkTreeIter iter; - GdkPixbuf *flag; - int apno, index, i; - - if (!ei) { - return; - } - - ett = editor_get_tool(); - combo = ei->tool_applied_player_combobox; - store = ei->tool_applied_player_store; - - if (!combo || !store) { - return; - } - - if (!editor_tool_has_applied_player(ett)) { - gtk_widget_hide(combo); - return; - } - - gtk_list_store_clear(store); - - apno = editor_tool_get_applied_player(ett); - index = -1; - i = 0; - - players_iterate(pplayer) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - TAP_COL_PLAYER_NO, player_number(pplayer), - TAP_COL_NAME, player_name(pplayer), -1); - - if (pplayer->nation != NO_NATION_SELECTED) { - flag = get_flag(pplayer->nation); - - if (flag != NULL) { - gtk_list_store_set(store, &iter, TAP_COL_FLAG, flag, -1); - g_object_unref(flag); - } - } - if (apno == player_number(pplayer)) { - index = i; - } - i++; - } players_iterate_end; - - if (index == -1) { - if (player_count() > 0) { - index = 0; - } - editor_tool_set_applied_player(ett, index); - } - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), index); - gtk_widget_show(combo); -} - -/************************************************************************//** - Return a pixbuf containing an image for the given editor tool sub-value, - if one exists. - - NB: Can return NULL. Must call g_object_unref on non-NULL when done. -****************************************************************************/ -static GdkPixbuf *get_tool_value_pixbuf(enum editor_tool_type ett, - int value) -{ - struct sprite *sprite = NULL; - GdkPixbuf *pixbuf = NULL; - struct terrain *pterrain; - struct unit_type *putype; - const struct editor_sprites *sprites; - - sprites = get_editor_sprites(tileset); - if (!sprites) { - return NULL; - } - - switch (ett) { - case ETT_TERRAIN: - pterrain = terrain_by_number(value); - if (pterrain) { - pixbuf = create_terrain_pixbuf(pterrain); - } - break; - case ETT_TERRAIN_RESOURCE: - case ETT_TERRAIN_SPECIAL: - case ETT_ROAD: - case ETT_MILITARY_BASE: - if (value >= 0) { - pixbuf = create_extra_pixbuf(extra_by_number(value)); - } - break; - case ETT_UNIT: - putype = utype_by_number(value); - if (putype) { - sprite = get_unittype_sprite(tileset, putype, direction8_invalid()); - } - break; - case ETT_CITY: - sprite = sprites->city; - break; - case ETT_VISION: - sprite = sprites->vision; - break; - case ETT_STARTPOS: - sprite = sprites->startpos; - break; - case ETT_COPYPASTE: - sprite = sprites->copypaste; - break; - default: - break; - } - - if (sprite) { - pixbuf = sprite_get_pixbuf(sprite); - /* - if (pixbuf) { - g_object_ref(pixbuf); - } - */ - sprite = NULL; - } - - return pixbuf; -} - -/************************************************************************//** - Return a pixbuf containing an image suitable for use as an icon - respresenting the given editor tool mode. - - NB: May return NULL. Must call g_object_unref on non-NULL when done. -****************************************************************************/ -static GdkPixbuf *get_tool_mode_pixbuf(enum editor_tool_mode etm) -{ - struct sprite *sprite = NULL; - GdkPixbuf *pixbuf = NULL; - - sprite = editor_get_mode_sprite(etm); - - if (sprite) { - pixbuf = sprite_get_pixbuf(sprite); - /* - if (pixbuf) { - g_object_ref(pixbuf); - } - */ - } - - return pixbuf; -} - -/************************************************************************//** - NB: Assumes that widget 'old' has enough references to not be destroyed - when removed from its parent container, and that the parent container - is a GtkBox (or is descended from it). -****************************************************************************/ -static void replace_widget(GtkWidget *old, GtkWidget *new) -{ - GtkWidget *parent; - - parent = gtk_widget_get_parent(old); - if (!parent) { - return; - } - - gtk_widget_hide(old); - gtk_container_remove(GTK_CONTAINER(parent), old); - gtk_container_add(GTK_CONTAINER(parent), new); - gtk_widget_show_all(new); -} - -/************************************************************************//** - Refresh the given editinfobox according to the current editor state. -****************************************************************************/ -static void editinfobox_refresh(struct editinfobox *ei) -{ - GdkPixbuf *pixbuf = NULL; - GtkLabel *label; - enum editor_tool_type ett; - enum editor_tool_mode etm; - int value; - char buf[256]; - - if (ei == NULL) { - return; - } - - if (!editor_is_active()) { - replace_widget(ei->widget, unit_info_box); - return; - } - - ett = editor_get_tool(); - etm = editor_tool_get_mode(ett); - value = editor_tool_get_value(ett); - - label = GTK_LABEL(ei->mode_label); - gtk_label_set_text(label, editor_tool_get_mode_name(ett, etm)); - - pixbuf = get_tool_mode_pixbuf(etm); - gtk_image_set_from_pixbuf(GTK_IMAGE(ei->mode_image), pixbuf); - if (pixbuf) { - g_object_unref(pixbuf); - pixbuf = NULL; - } - - fc_snprintf(buf, sizeof(buf), "%s", - editor_tool_get_name(ett)); - gtk_label_set_markup(GTK_LABEL(ei->tool_label), buf); - - if (etm == ETM_COPY || etm == ETM_PASTE) { - struct edit_buffer *ebuf; - struct sprite *spr; - char status[256]; - - ebuf = editor_get_copy_buffer(); - edit_buffer_get_status_string(ebuf, status, sizeof(status)); - gtk_label_set_text(GTK_LABEL(ei->tool_value_label), status); - - spr = editor_tool_get_sprite(ett); - pixbuf = spr ? sprite_get_pixbuf(spr) : NULL; - gtk_image_set_from_pixbuf(GTK_IMAGE(ei->tool_image), pixbuf); - if (pixbuf) { - g_object_unref(G_OBJECT(pixbuf)); - pixbuf = NULL; - } - } else { - pixbuf = get_tool_value_pixbuf(ett, value); - gtk_image_set_from_pixbuf(GTK_IMAGE(ei->tool_image), pixbuf); - if (pixbuf) { - g_object_unref(pixbuf); - pixbuf = NULL; - } - gtk_label_set_text(GTK_LABEL(ei->tool_value_label), - editor_tool_get_value_name(ett, value)); - } - - replace_widget(unit_info_box, ei->widget); - - if (editor_tool_has_size(ett)) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ei->size_spin_button), - editor_tool_get_size(ett)); - gtk_widget_show(ei->size_hbox); - } else { - gtk_widget_hide(ei->size_hbox); - } - - if (editor_tool_has_count(ett)) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(ei->count_spin_button), - editor_tool_get_count(ett)); - gtk_widget_show(ei->count_hbox); - } else { - gtk_widget_hide(ei->count_hbox); - } - - refresh_tool_applied_player_combo(ei); -} - -/************************************************************************//** - Handle ctrl+ combinations. -****************************************************************************/ -static gboolean handle_edit_key_press_with_ctrl(GdkEventKey *ev) -{ - return FALSE; /* Don't gobble. */ -} - -/************************************************************************//** - Handle shift+ combinations. -****************************************************************************/ -static gboolean handle_edit_key_press_with_shift(GdkEventKey *ev) -{ - enum editor_tool_type ett; - - ett = editor_get_tool(); - switch (ev->keyval) { - case GDK_KEY_D: - editor_tool_toggle_mode(ett, ETM_ERASE); - break; - case GDK_KEY_C: - editor_set_tool(ETT_COPYPASTE); - editor_tool_toggle_mode(ett, ETM_COPY); - break; - case GDK_KEY_V: - editor_set_tool(ETT_COPYPASTE); - editor_tool_toggle_mode(ett, ETM_PASTE); - break; - case GDK_KEY_T: - editgui_run_tool_selection(ETT_TERRAIN); - break; - case GDK_KEY_R: - editgui_run_tool_selection(ETT_TERRAIN_RESOURCE); - break; - case GDK_KEY_S: - editgui_run_tool_selection(ETT_TERRAIN_SPECIAL); - break; - case GDK_KEY_P: - editgui_run_tool_selection(ETT_ROAD); - break; - case GDK_KEY_M: - editgui_run_tool_selection(ETT_MILITARY_BASE); - break; - case GDK_KEY_U: - editgui_run_tool_selection(ETT_UNIT); - break; - default: - return TRUE; /* Gobble? */ - break; - } - - editgui_refresh(); - - return TRUE; -} - -/************************************************************************//** - Handle any kind of key press event. -****************************************************************************/ -gboolean handle_edit_key_press(GdkEventKey *ev) -{ - enum editor_tool_type ett, new_ett = NUM_EDITOR_TOOL_TYPES; - - if (ev->state & GDK_SHIFT_MASK) { - return handle_edit_key_press_with_shift(ev); - } - - if (ev->state & GDK_CONTROL_MASK) { - return handle_edit_key_press_with_ctrl(ev); - } - - ett = editor_get_tool(); - - switch (ev->keyval) { - case GDK_KEY_t: - new_ett = ETT_TERRAIN; - break; - case GDK_KEY_r: - new_ett = ETT_TERRAIN_RESOURCE; - break; - case GDK_KEY_s: - new_ett = ETT_TERRAIN_SPECIAL; - break; - case GDK_KEY_p: - new_ett = ETT_ROAD; - break; - case GDK_KEY_m: - new_ett = ETT_MILITARY_BASE; - break; - case GDK_KEY_u: - new_ett = ETT_UNIT; - break; - case GDK_KEY_c: - new_ett = ETT_CITY; - break; - case GDK_KEY_v: - new_ett = ETT_VISION; - break; - case GDK_KEY_b: - new_ett = ETT_STARTPOS; - break; - case GDK_KEY_plus: - case GDK_KEY_equal: - case GDK_KEY_KP_Add: - if (editor_tool_has_size(ett)) { - int size = editor_tool_get_size(ett); - editor_tool_set_size(ett, size + 1); - } else if (editor_tool_has_count(ett)) { - int count = editor_tool_get_count(ett); - editor_tool_set_count(ett, count + 1); - } - break; - case GDK_KEY_minus: - case GDK_KEY_underscore: - case GDK_KEY_KP_Subtract: - if (editor_tool_has_size(ett)) { - int size = editor_tool_get_size(ett); - editor_tool_set_size(ett, size - 1); - } else if (editor_tool_has_count(ett)) { - int count = editor_tool_get_count(ett); - editor_tool_set_count(ett, count - 1); - } - break; - case GDK_KEY_1: - case GDK_KEY_2: - case GDK_KEY_3: - case GDK_KEY_4: - case GDK_KEY_5: - case GDK_KEY_6: - case GDK_KEY_7: - case GDK_KEY_8: - case GDK_KEY_9: - if (editor_tool_has_size(ett)) { - editor_tool_set_size(ett, ev->keyval - GDK_KEY_1 + 1); - } else if (editor_tool_has_count(ett)) { - editor_tool_set_count(ett, ev->keyval - GDK_KEY_1 + 1); - } - break; - case GDK_KEY_space: - editor_apply_tool_to_selection(); - break; - case GDK_KEY_Tab: - editgui_run_tool_selection(ett); - break; - case GDK_KEY_F1: - case GDK_KEY_F2: - case GDK_KEY_F3: - case GDK_KEY_F4: - case GDK_KEY_F5: - case GDK_KEY_F6: - case GDK_KEY_F7: - case GDK_KEY_F8: - case GDK_KEY_F9: - case GDK_KEY_F10: - case GDK_KEY_F11: - case GDK_KEY_F12: - return FALSE; /* Allow default handler. */ - break; - default: - return TRUE; /* Gobbled... */ - break; - } - - if (new_ett != NUM_EDITOR_TOOL_TYPES) { - try_to_set_editor_tool(new_ett); - } - - editgui_refresh(); - - return TRUE; -} - -/************************************************************************//** - Key release handler. -****************************************************************************/ -gboolean handle_edit_key_release(GdkEventKey *ev) -{ - return FALSE; -} - -/************************************************************************//** - Get the pointer for the editbar embedded in the client's GUI. -****************************************************************************/ -struct editbar *editgui_get_editbar(void) -{ - return editor_toolbar; -} - -/************************************************************************//** - Refresh all editor GUI widgets according to the current editor state. -****************************************************************************/ -void editgui_refresh(void) -{ - struct property_editor *pe; - struct editbar *eb; - struct editinfobox *ei; - - eb = editgui_get_editbar(); - if (eb != NULL) { - editbar_refresh(eb); - } - - ei = editgui_get_editinfobox(); - if (ei != NULL) { - editinfobox_refresh(ei); - } - - pe = editprop_get_property_editor(); - if (!editor_is_active()) { - property_editor_clear(pe); - property_editor_popdown(pe); - } -} - -/************************************************************************//** - Create all editor GUI widgets. -****************************************************************************/ -void editgui_create_widgets(void) -{ - if (editor_toolbar == NULL) { - editor_toolbar = editbar_create(); - } - if (editor_infobox == NULL) { - editor_infobox = editinfobox_create(); - } -} - -/************************************************************************//** - Free everything allocated for the editgui. -****************************************************************************/ -void editgui_free(void) -{ - struct editbar *eb = editgui_get_editbar(); - - clear_tool_stores(eb); -} - -/************************************************************************//** - Return a pointer to the editor info box embedded in the client's GUI. -****************************************************************************/ -struct editinfobox *editgui_get_editinfobox(void) -{ - return editor_infobox; -} - -/************************************************************************//** - Update all editor widget internal data for the new tileset. Call this - after a new tileset has finished loading. -****************************************************************************/ -void editgui_tileset_changed(void) -{ - editbar_reload_tileset(editgui_get_editbar()); - editinfobox_refresh(editgui_get_editinfobox()); -} - -/************************************************************************//** - Popup the property editor. If 'tiles' is non-NULL, the tiles, units and - cities in those tiles are added to the property editor's object list. If - 'objtype' is a valid object type, the corresponding page of the property - editor notebook is focused. Otherwise which page is focused depends on - what was loaded from 'tiles'. -****************************************************************************/ -void editgui_popup_properties(const struct tile_list *tiles, int objtype) -{ - struct property_editor *pe; - - pe = editprop_get_property_editor(); - property_editor_clear(pe); - property_editor_reload(pe, OBJTYPE_PLAYER); - property_editor_reload(pe, OBJTYPE_GAME); - property_editor_load_tiles(pe, tiles); - property_editor_popup(pe, objtype); -} - -/************************************************************************//** - Popup all dialog window of the editor. -****************************************************************************/ -void editgui_popdown_all(void) -{ - struct property_editor *pe; - - pe = editprop_get_property_editor(); - property_editor_popdown(pe); - property_editor_clear(pe); /* And clear it. */ -} - -/************************************************************************//** - This is called to notify the editor GUI that some object (e.g. tile, unit, - etc.) has changed (usually because the corresponding packet was received) - and that widgets displaying the object should be updated. - - Currently this is used to notify the property editor that some object - has been removed or some property value has changed at the server. -****************************************************************************/ -void editgui_notify_object_changed(int objtype, int object_id, bool removal) -{ - struct property_editor *pe; - - if (!editor_is_active()) { - return; - } - - pe = editprop_get_property_editor(); - property_editor_handle_object_changed(pe, objtype, object_id, removal); -} - -/************************************************************************//** - Pass on the object creation notification to the property editor. -****************************************************************************/ -void editgui_notify_object_created(int tag, int id) -{ - struct property_editor *pe; - - if (!editor_is_active()) { - return; - } - - pe = editprop_get_property_editor(); - property_editor_handle_object_created(pe, tag, id); -} diff --git a/client/gui-gtk-3.0/editgui.h b/client/gui-gtk-3.0/editgui.h deleted file mode 100644 index 04405d6d85..0000000000 --- a/client/gui-gtk-3.0/editgui.h +++ /dev/null @@ -1,69 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 2005 - The Freeciv Project - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__EDITGUI_H -#define FC__EDITGUI_H - -#include - -/* client */ -#include "editor.h" -#include "editgui_g.h" - -struct tool_value_selector; - -struct editbar { - /* The widget holding the entire edit bar. */ - GtkWidget *widget; - - GtkSizeGroup *size_group; - - GtkWidget *mode_buttons[NUM_EDITOR_TOOL_MODES]; - GtkWidget *tool_buttons[NUM_EDITOR_TOOL_TYPES]; - GtkWidget *player_properties_button; - struct tool_value_selector *tool_selectors[NUM_EDITOR_TOOL_TYPES]; - - GtkListStore *player_pov_store; - GtkWidget *player_pov_combobox; -}; - -gboolean handle_edit_mouse_button_press(GdkEventButton *ev); -gboolean handle_edit_mouse_button_release(GdkEventButton *ev); -gboolean handle_edit_mouse_move(GdkEventMotion *ev); -gboolean handle_edit_key_press(GdkEventKey *ev); -gboolean handle_edit_key_release(GdkEventKey *ev); - -struct editinfobox { - GtkWidget *widget; - - GtkWidget *mode_image; - GtkWidget *mode_label; - - GtkWidget *tool_label; - GtkWidget *tool_value_label; - GtkWidget *tool_image; - - GtkWidget *size_hbox; - GtkWidget *size_spin_button; - GtkWidget *count_hbox; - GtkWidget *count_spin_button; - - GtkListStore *tool_applied_player_store; - GtkWidget *tool_applied_player_combobox; -}; - -void editgui_create_widgets(void); -void editgui_free(void); -struct editbar *editgui_get_editbar(void); -struct editinfobox *editgui_get_editinfobox(void); - -#endif /* FC__EDITGUI_H */ diff --git a/client/gui-gtk-3.0/editprop.c b/client/gui-gtk-3.0/editprop.c deleted file mode 100644 index 208fc31eaa..0000000000 --- a/client/gui-gtk-3.0/editprop.c +++ /dev/null @@ -1,6532 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 2005 - The Freeciv Project - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include /* USHRT_MAX */ - -#include -#include - -/* utility */ -#include "bitvector.h" -#include "fc_cmdline.h" -#include "fcintl.h" -#include "log.h" -#include "mem.h" - -/* common */ -#include "fc_interface.h" -#include "game.h" -#include "government.h" -#include "map.h" -#include "movement.h" -#include "research.h" -#include "tile.h" - -/* client */ -#include "client_main.h" -#include "climisc.h" -#include "editor.h" -#include "mapview_common.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "canvas.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "plrdlg.h" -#include "sprite.h" - -#include "editprop.h" - - -/* Forward declarations. */ -struct objprop; -struct objbind; -struct property_page; -struct property_editor; -struct extviewer; - -/**************************************************************************** - Miscellaneous helpers. -****************************************************************************/ -static GdkPixbuf *create_pixbuf_from_layers(const struct tile *ptile, - const struct unit *punit, - const struct city *pcity, - enum layer_category category); -static GdkPixbuf *create_tile_pixbuf(const struct tile *ptile); -static GdkPixbuf *create_unit_pixbuf(const struct unit *punit); -static GdkPixbuf *create_city_pixbuf(const struct city *pcity); - -static void add_column(GtkWidget *view, - int col_id, - const char *name, - GType gtype, - bool editable, - bool is_radio, - GCallback edit_callback, - gpointer callback_userdata); - -static bool can_create_unit_at_tile(struct tile *ptile); - -static int get_next_unique_tag(void); - -/* 'struct stored_tag_hash' and related functions. */ -#define SPECHASH_TAG stored_tag -#define SPECHASH_INT_KEY_TYPE -#define SPECHASH_INT_DATA_TYPE -#include "spechash.h" - -/* NB: If packet definitions change, be sure to - * update objbind_pack_current_values!!! */ -union packetdata { - struct { - gpointer v_pointer1; - gpointer v_pointer2; - } pointers; - struct packet_edit_tile *tile; - struct packet_edit_startpos_full *startpos; - struct packet_edit_city *city; - struct packet_edit_unit *unit; - struct packet_edit_player *player; - struct { - struct packet_edit_game *game; - struct packet_edit_scenario_desc *desc; - } game; -}; - -/* Helpers for the OPID_TILE_VISION property. */ -struct tile_vision_data { - bv_player tile_known, tile_seen[V_COUNT]; -}; -const char *vision_layer_get_name(enum vision_layer); - -#define PF_MAX_CLAUSES 16 -#define PF_DISJUNCTION_SEPARATOR "|" -#define PF_CONJUNCTION_SEPARATOR "&" - -struct pf_pattern { - bool negate; - char *text; -}; - -struct pf_conjunction { - struct pf_pattern conjunction[PF_MAX_CLAUSES]; - int count; -}; - -struct property_filter { - struct pf_conjunction disjunction[PF_MAX_CLAUSES]; - int count; -}; - -static struct property_filter *property_filter_new(const char *filter); -static bool property_filter_match(struct property_filter *pf, - const struct objprop *op); -static void property_filter_free(struct property_filter *pf); - - -/**************************************************************************** - Object type declarations. - - To add a new object type: - 1. Add a value in enum editor_object_type in client/editor.h. - 2. Add a string name to objtype_get_name. - 3. Add code in objtype_get_id_from_object. - 4. Add code in objtype_get_object_from_id. - 5. Add a case handler in objtype_is_conserved, and if - the object type is not conserved, then also in - objbind_request_destroy_object and property_page_create_objects. - 6. Add an if-block in objbind_get_value_from_object. - 7. Add an if-block in objbind_get_allowed_value_span. - 9. Add a case handler in property_page_setup_objprops. - 10. Add a case handler in property_page_add_objbinds_from_tile - if applicable. - - Furthermore, if the object type is to be editable: - 11. Define its edit packet in common/networking/packets.def. - 12. Add the packet handler in server/edithand.c. - 13. Add its edit packet type to union packetdata. - 14. Add an if-block in objbind_pack_current_values. - 15. Add an if-block in objbind_pack_modified_value. - 16. Add code in property_page_new_packet. - 17. Add code in property_page_send_packet. - 18. Add calls to editgui_notify_object_changed in - client/packhand.c or where applicable. - -****************************************************************************/ - -/* OBJTYPE_* enum values defined in client/editor.h */ - -static const char *objtype_get_name(enum editor_object_type objtype); -static int objtype_get_id_from_object(enum editor_object_type objtype, - gpointer object); -static gpointer objtype_get_object_from_id(enum editor_object_type objtype, - int id); -static bool objtype_is_conserved(enum editor_object_type objtype); - - -/**************************************************************************** - Value type declarations. - - To add a new value type: - 1. Add a value in enum value_types. - 2. Add its field in union propval_data. - 3. Add a case handler in valtype_get_name. - 4. Add a case handler in propval_copy if needed. - 5. Add a case handler in propval_free if needed. - 6. Add a case handler in propval_equal if needed. - 7. Add a case handler in objprop_get_gtype. - 8. Add a case handler in property_page_set_store_value. - 9. Add a case handler in propval_as_string if needed. -****************************************************************************/ -enum value_types { - VALTYPE_NONE = 0, - VALTYPE_INT, - VALTYPE_BOOL, - VALTYPE_STRING, - VALTYPE_PIXBUF, - VALTYPE_BUILT_ARRAY, /* struct built_status[B_LAST] */ - VALTYPE_INVENTIONS_ARRAY, /* bool[A_LAST] */ - VALTYPE_BV_SPECIAL, - VALTYPE_BV_ROADS, - VALTYPE_BV_BASES, - VALTYPE_NATION, - VALTYPE_NATION_HASH, /* struct nation_hash */ - VALTYPE_GOV, - VALTYPE_TILE_VISION_DATA /* struct tile_vision_data */ -}; - -static const char *valtype_get_name(enum value_types valtype); - - -/**************************************************************************** - Propstate and propval declarations. - - To add a new member to union propval_data, see the steps for adding a - new value type above. - - New property values are "constructed" by objbind_get_value_from_object. -****************************************************************************/ -union propval_data { - gpointer v_pointer; - int v_int; - bool v_bool; - char *v_string; - const char *v_const_string; - GdkPixbuf *v_pixbuf; - struct built_status *v_built; - bv_special v_bv_special; - bv_roads v_bv_roads; - bv_bases v_bv_bases; - struct nation_type *v_nation; - struct nation_hash *v_nation_hash; - struct government *v_gov; - bv_techs v_bv_inventions; - struct tile_vision_data *v_tile_vision; -}; - -struct propval { - union propval_data data; - enum value_types valtype; - bool must_free; -}; - -static void propval_free(struct propval *pv); -static void propval_free_data(struct propval *pv); -static struct propval *propval_copy(struct propval *pv); -static bool propval_equal(struct propval *pva, struct propval *pvb); - -struct propstate { - int property_id; - struct propval *property_value; -}; - -static struct propstate *propstate_new(struct objprop *op, - struct propval *pv); -static void propstate_destroy(struct propstate *ps); -static void propstate_clear_value(struct propstate *ps); -static void propstate_set_value(struct propstate *ps, - struct propval *pv); -static struct propval *propstate_get_value(struct propstate *ps); - -#define SPECHASH_TAG propstate -#define SPECHASH_INT_KEY_TYPE -#define SPECHASH_IDATA_TYPE struct propstate * -#define SPECHASH_IDATA_FREE propstate_destroy -#include "spechash.h" - - -/**************************************************************************** - Objprop declarations. - - To add a new object property: - 1. Add a value in enum object_property_ids (grouped by - object type). - 2. Define the property in property_page_setup_objprops. - 3. Add a case handler in objbind_get_value_from_object - in the appropriate object type block. - 4. Add a case handler in objprop_setup_widget. - 5. Add a case handler in objprop_refresh_widget. - - Furthermore, if the property is editable: - 5. Add a field for this property in the edit - packet for this object type in common/networking/packets.def. - 6. Add a case handler in objbind_pack_modified_value. - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !!! 7. Add code to set the packet field in !!! - !!! objbind_pack_current_values. !!! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - 8. Add code to handle changes in the packet field in - server/edithand.c handle_edit_. - - If the property makes use of an extviewer: - 9. Handle widget creation in extviewer_new. - 10. Handle refresh in extviewer_refresh_widgets. - 11. Handle clear in extviewer_clear_widgets. - 12. Handle any signal callbacks (e.g. toggled) if needed. - - TODO: Add more object properties. -****************************************************************************/ -enum object_property_ids { - OPID_TILE_IMAGE, - OPID_TILE_X, - OPID_TILE_Y, - OPID_TILE_NAT_X, - OPID_TILE_NAT_Y, - OPID_TILE_CONTINENT, -#ifdef FREECIV_DEBUG - OPID_TILE_ADDRESS, -#endif /* FREECIV_DEBUG */ - OPID_TILE_TERRAIN, - OPID_TILE_INDEX, - OPID_TILE_XY, - OPID_TILE_RESOURCE, - OPID_TILE_SPECIALS, - OPID_TILE_ROADS, - OPID_TILE_BASES, - OPID_TILE_VISION, /* tile_known and tile_seen */ - OPID_TILE_LABEL, - - OPID_STARTPOS_IMAGE, - OPID_STARTPOS_XY, - OPID_STARTPOS_EXCLUDE, - OPID_STARTPOS_NATIONS, - - OPID_UNIT_IMAGE, -#ifdef FREECIV_DEBUG - OPID_UNIT_ADDRESS, -#endif /* FREECIV_DEBUG */ - OPID_UNIT_TYPE, - OPID_UNIT_ID, - OPID_UNIT_XY, - OPID_UNIT_MOVES_LEFT, - OPID_UNIT_FUEL, - OPID_UNIT_MOVED, - OPID_UNIT_DONE_MOVING, - OPID_UNIT_HP, - OPID_UNIT_VETERAN, - OPID_UNIT_STAY, - - OPID_CITY_IMAGE, - OPID_CITY_NAME, -#ifdef FREECIV_DEBUG - OPID_CITY_ADDRESS, -#endif /* FREECIV_DEBUG */ - OPID_CITY_ID, - OPID_CITY_XY, - OPID_CITY_SIZE, - OPID_CITY_HISTORY, - OPID_CITY_BUILDINGS, - OPID_CITY_FOOD_STOCK, - OPID_CITY_SHIELD_STOCK, - - OPID_PLAYER_NAME, - OPID_PLAYER_NATION, - OPID_PLAYER_GOV, - OPID_PLAYER_AGE, -#ifdef FREECIV_DEBUG - OPID_PLAYER_ADDRESS, -#endif /* FREECIV_DEBUG */ - OPID_PLAYER_INVENTIONS, - OPID_PLAYER_SCENARIO_RESERVED, - OPID_PLAYER_SCIENCE, - OPID_PLAYER_GOLD, - - OPID_GAME_SCENARIO, - OPID_GAME_SCENARIO_NAME, - OPID_GAME_SCENARIO_AUTHORS, - OPID_GAME_SCENARIO_DESC, - OPID_GAME_SCENARIO_RANDSTATE, - OPID_GAME_SCENARIO_PLAYERS, - OPID_GAME_STARTPOS_NATIONS, - OPID_GAME_PREVENT_CITIES, - OPID_GAME_LAKE_FLOODING, - OPID_GAME_RULESET_LOCKED -}; - -enum object_property_flags { - OPF_NO_FLAGS = 0, - OPF_EDITABLE = 1 << 0, - OPF_IN_LISTVIEW = 1 << 1, - OPF_HAS_WIDGET = 1 << 2 -}; - -struct objprop { - int id; - const char *name; - const char *tooltip; - enum object_property_flags flags; - enum value_types valtype; - int column_id; - GtkTreeViewColumn *view_column; - GtkWidget *widget; - struct extviewer *extviewer; - struct property_page *parent_page; -}; - -static struct objprop *objprop_new(int id, - const char *name, - const char *tooltip, - enum object_property_flags flags, - enum value_types valtype, - struct property_page *parent); -static int objprop_get_id(const struct objprop *op); -static const char *objprop_get_name(const struct objprop *op); -static const char *objprop_get_tooltip(const struct objprop *op); -static enum value_types objprop_get_valtype(const struct objprop *op); -static struct property_page * -objprop_get_property_page(const struct objprop *op); - -static bool objprop_show_in_listview(const struct objprop *op); -static bool objprop_is_sortable(const struct objprop *op); -static bool objprop_is_readonly(const struct objprop *op); -static bool objprop_has_widget(const struct objprop *op); - -static GType objprop_get_gtype(const struct objprop *op); -static const char *objprop_get_attribute_type_string(const struct objprop *op); -static void objprop_set_column_id(struct objprop *op, int col_id); -static int objprop_get_column_id(const struct objprop *op); -static void objprop_set_treeview_column(struct objprop *op, - GtkTreeViewColumn *col); -static GtkTreeViewColumn *objprop_get_treeview_column(const struct objprop *op); -static GtkCellRenderer *objprop_create_cell_renderer(const struct objprop *op); - -static void objprop_setup_widget(struct objprop *op); -static GtkWidget *objprop_get_widget(struct objprop *op); -static void objprop_set_child_widget(struct objprop *op, - const char *widget_name, - GtkWidget *widget); -static GtkWidget *objprop_get_child_widget(struct objprop *op, - const char *widget_name); -static void objprop_set_extviewer(struct objprop *op, - struct extviewer *ev); -static struct extviewer *objprop_get_extviewer(struct objprop *op); -static void objprop_refresh_widget(struct objprop *op, - struct objbind *ob); -static void objprop_widget_entry_changed(GtkEntry *entry, gpointer userdata); -static void objprop_widget_spin_button_changed(GtkSpinButton *spin, - gpointer userdata); -static void objprop_widget_toggle_button_changed(GtkToggleButton *button, - gpointer userdata); - -#define SPECHASH_TAG objprop -#define SPECHASH_INT_KEY_TYPE -#define SPECHASH_IDATA_TYPE struct objprop * -#include "spechash.h" - - -/**************************************************************************** - Objbind declarations. -****************************************************************************/ -struct objbind { - enum editor_object_type objtype; - int object_id; - struct property_page *parent_property_page; - struct propstate_hash *propstate_table; - GtkTreeRowReference *rowref; -}; - -static struct objbind *objbind_new(enum editor_object_type objtype, - gpointer object); -static void objbind_destroy(struct objbind *ob); -static enum editor_object_type objbind_get_objtype(const struct objbind *ob); -static void objbind_bind_properties(struct objbind *ob, - struct property_page *pp); -static gpointer objbind_get_object(struct objbind *ob); -static int objbind_get_object_id(struct objbind *ob); -static void objbind_request_destroy_object(struct objbind *ob); -static struct propval *objbind_get_value_from_object(struct objbind *ob, - struct objprop *op); -static bool objbind_get_allowed_value_span(struct objbind *ob, - struct objprop *op, - double *pmin, - double *pmax, - double *pstep, - double *pbig_step); -static void objbind_set_modified_value(struct objbind *ob, - struct objprop *op, - struct propval *pv); -static struct propval *objbind_get_modified_value(struct objbind *ob, - struct objprop *op); -static void objbind_clear_modified_value(struct objbind *ob, - struct objprop *op); -static bool objbind_property_is_modified(struct objbind *ob, - struct objprop *op); -static bool objbind_has_modified_properties(struct objbind *ob); -static void objbind_clear_all_modified_values(struct objbind *ob); -static void objbind_pack_current_values(struct objbind *ob, - union packetdata packet); -static void objbind_pack_modified_value(struct objbind *ob, - struct objprop *op, - union packetdata packet); -static void objbind_set_rowref(struct objbind *ob, - GtkTreeRowReference *rr); -static GtkTreeRowReference *objbind_get_rowref(struct objbind *ob); - -#define SPECHASH_TAG objbind -#define SPECHASH_INT_KEY_TYPE -#define SPECHASH_IDATA_TYPE struct objbind * -#define SPECHASH_IDATA_FREE objbind_destroy -#include "spechash.h" - - -/**************************************************************************** - Extended property viewer declarations. This is a set of widgets used - for viewing and/or editing properties with complex values (e.g. arrays). -****************************************************************************/ -struct extviewer { - struct objprop *objprop; - struct propval *pv_cached; - - GtkWidget *panel_widget; - GtkWidget *panel_label; - GtkWidget *panel_button; - GtkWidget *panel_image; - - GtkWidget *view_widget; - GtkWidget *view_label; - - GtkListStore *store; - GtkTextBuffer *textbuf; -}; - -static struct extviewer *extviewer_new(struct objprop *op); -static struct objprop *extviewer_get_objprop(struct extviewer *ev); -static GtkWidget *extviewer_get_panel_widget(struct extviewer *ev); -static GtkWidget *extviewer_get_view_widget(struct extviewer *ev); -static void extviewer_refresh_widgets(struct extviewer *ev, - struct propval *pv); -static void extviewer_clear_widgets(struct extviewer *ev); -static void extviewer_panel_button_clicked(GtkButton *button, - gpointer userdata); -static void extviewer_view_cell_toggled(GtkCellRendererToggle *cell, - gchar *path, - gpointer userdata); -static void extviewer_textbuf_changed(GtkTextBuffer *textbuf, - gpointer userdata); - - -/**************************************************************************** - Property page declarations. -****************************************************************************/ -struct property_page { - enum editor_object_type objtype; - - GtkWidget *widget; - GtkListStore *object_store; - GtkWidget *object_view; - GtkWidget *extviewer_notebook; - - struct property_editor *pe_parent; - - struct objprop_hash *objprop_table; - struct objbind_hash *objbind_table; - struct stored_tag_hash *tag_table; - - struct objbind *focused_objbind; -}; - -static struct property_page * -property_page_new(enum editor_object_type objtype, - struct property_editor *parent); -static void property_page_setup_objprops(struct property_page *pp); -static const char *property_page_get_name(const struct property_page *pp); -static enum editor_object_type -property_page_get_objtype(const struct property_page *pp); -static void property_page_load_tiles(struct property_page *pp, - const struct tile_list *tiles); -static void property_page_add_objbinds_from_tile(struct property_page *pp, - const struct tile *ptile); -static int property_page_get_num_objbinds(const struct property_page *pp); -static void property_page_clear_objbinds(struct property_page *pp); -static void property_page_add_objbind(struct property_page *pp, - gpointer object_data); -static void property_page_fill_widgets(struct property_page *pp); -static struct objbind * -property_page_get_focused_objbind(struct property_page *pp); -static void property_page_set_focused_objbind(struct property_page *pp, - struct objbind *ob); -static struct objbind *property_page_get_objbind(struct property_page *pp, - int object_id); -static void property_page_selection_changed(GtkTreeSelection *sel, - gpointer userdata); -static gboolean property_page_selection_func(GtkTreeSelection *sel, - GtkTreeModel *model, - GtkTreePath *path, - gboolean currently_selected, - gpointer data); -static void property_page_quick_find_entry_changed(GtkWidget *entry, - gpointer userdata); -static void property_page_change_value(struct property_page *pp, - struct objprop *op, - struct propval *pv); -static void property_page_send_values(struct property_page *pp); -static void property_page_reset_objbinds(struct property_page *pp); -static void property_page_destroy_objects(struct property_page *pp); -static void property_page_create_objects(struct property_page *pp, - struct tile_list *hint_tiles); -static union packetdata property_page_new_packet(struct property_page *pp); -static void property_page_send_packet(struct property_page *pp, - union packetdata packet); -static void property_page_free_packet(struct property_page *pp, - union packetdata packet); -static void property_page_object_changed(struct property_page *pp, - int object_id, - bool remove); -static void property_page_object_created(struct property_page *pp, - int tag, int object_id); -static void property_page_add_extviewer(struct property_page *pp, - struct extviewer *ev); -static void property_page_show_extviewer(struct property_page *pp, - struct extviewer *ev); -static void property_page_store_creation_tag(struct property_page *pp, - int tag, int count); -static void property_page_remove_creation_tag(struct property_page *pp, - int tag); -static bool property_page_tag_is_known(struct property_page *pp, int tag); -static void property_page_clear_tags(struct property_page *pp); -static void property_page_apply_button_clicked(GtkButton *button, - gpointer userdata); -static void property_page_refresh_button_clicked(GtkButton *button, - gpointer userdata); -static void property_page_create_button_clicked(GtkButton *button, - gpointer userdata); -static void property_page_destroy_button_clicked(GtkButton *button, - gpointer userdata); - - -#define property_page_objprop_iterate(ARG_pp, NAME_op) \ - TYPED_HASH_DATA_ITERATE(struct objprop *, (ARG_pp)->objprop_table, NAME_op) -#define property_page_objprop_iterate_end HASH_DATA_ITERATE_END - -#define property_page_objbind_iterate(ARG_pp, NAME_ob) \ - TYPED_HASH_DATA_ITERATE(struct objbind *, (ARG_pp)->objbind_table, NAME_ob) -#define property_page_objbind_iterate_end HASH_DATA_ITERATE_END - - -/**************************************************************************** - Property editor declarations. -****************************************************************************/ -struct property_editor { - GtkWidget *widget; - GtkWidget *notebook; - - struct property_page *property_pages[NUM_OBJTYPES]; -}; - -static struct property_editor *property_editor_new(void); -static bool property_editor_add_page(struct property_editor *pe, - enum editor_object_type objtype); -static struct property_page * -property_editor_get_page(struct property_editor *pe, - enum editor_object_type objtype); - -static struct property_editor *the_property_editor; - - -/************************************************************************//** - Returns the translated name for the given object type. -****************************************************************************/ -static const char *objtype_get_name(enum editor_object_type objtype) -{ - switch (objtype) { - case OBJTYPE_TILE: - return _("Tile"); - case OBJTYPE_STARTPOS: - return _("Start Position"); - case OBJTYPE_UNIT: - return _("Unit"); - case OBJTYPE_CITY: - return _("City"); - case OBJTYPE_PLAYER: - return _("Player"); - case OBJTYPE_GAME: - return Q_("?play:Game"); - case NUM_OBJTYPES: - break; - } - - log_error("%s() Unhandled request to get name of object type %d.", - __FUNCTION__, objtype); - return "Unknown"; -} - -/************************************************************************//** - Returns the unique identifier value from the given object, assuming it - is of the 'objtype' object type. Valid IDs are always greater than or - equal to zero. -****************************************************************************/ -static int objtype_get_id_from_object(enum editor_object_type objtype, - gpointer object) -{ - switch (objtype) { - case OBJTYPE_TILE: - return tile_index((struct tile *) object); - case OBJTYPE_STARTPOS: - return startpos_number((struct startpos *) object); - case OBJTYPE_UNIT: - return ((struct unit *) object)->id; - case OBJTYPE_CITY: - return ((struct city *) object)->id; - case OBJTYPE_PLAYER: - return player_number((struct player *) object); - case OBJTYPE_GAME: - return 1; - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled request to get object ID from object %p of " - "type %d (%s).", __FUNCTION__, object, objtype, - objtype_get_name(objtype)); - return -1; -} - -/************************************************************************//** - Get the object of type 'objtype' uniquely identified by 'id'. -****************************************************************************/ -static gpointer objtype_get_object_from_id(enum editor_object_type objtype, - int id) -{ - switch (objtype) { - case OBJTYPE_TILE: - return index_to_tile(&(wld.map), id); - case OBJTYPE_STARTPOS: - return map_startpos_by_number(id); - case OBJTYPE_UNIT: - return game_unit_by_number(id); - case OBJTYPE_CITY: - return game_city_by_number(id); - case OBJTYPE_PLAYER: - return player_by_number(id); - case OBJTYPE_GAME: - return &game; - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled request to get object of type %d (%s) " - "with ID %d.", __FUNCTION__, objtype, - objtype_get_name(objtype), id); - return NULL; -} - -/************************************************************************//** - Returns TRUE if it does not make sense for the object of the given type to - be created and destroyed (e.g. tiles, game), as opposed to those that can - be (e.g. units, cities, players, etc.). -****************************************************************************/ -static bool objtype_is_conserved(enum editor_object_type objtype) -{ - switch (objtype) { - case OBJTYPE_TILE: - case OBJTYPE_GAME: - return TRUE; - case OBJTYPE_STARTPOS: - case OBJTYPE_UNIT: - case OBJTYPE_CITY: - case OBJTYPE_PLAYER: - return FALSE; - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled request for object type %d (%s)).", - __FUNCTION__, objtype, objtype_get_name(objtype)); - return TRUE; -} - -/************************************************************************//** - Returns the untranslated name for the given value type. -****************************************************************************/ -static const char *valtype_get_name(enum value_types valtype) -{ - switch (valtype) { - case VALTYPE_NONE: - return "none"; - case VALTYPE_STRING: - return "string"; - case VALTYPE_INT: - return "int"; - case VALTYPE_BOOL: - return "bool"; - case VALTYPE_PIXBUF: - return "pixbuf"; - case VALTYPE_BUILT_ARRAY: - return "struct built_status[B_LAST]"; - case VALTYPE_INVENTIONS_ARRAY: - return "bool[A_LAST]"; - case VALTYPE_BV_SPECIAL: - return "bv_special"; - case VALTYPE_BV_ROADS: - return "bv_roads"; - case VALTYPE_BV_BASES: - return "bv_bases"; - case VALTYPE_NATION: - return "nation"; - case VALTYPE_NATION_HASH: - return "struct nation_hash"; - case VALTYPE_GOV: - return "government"; - case VALTYPE_TILE_VISION_DATA: - return "struct tile_vision_data"; - } - - log_error("%s(): unhandled value type %d.", __FUNCTION__, valtype); - return "void"; -} - -/************************************************************************//** - Convenience function to add a column to a GtkTreeView. Used for the - view widget creation in extviewer_new(). -****************************************************************************/ -static void add_column(GtkWidget *view, - int col_id, - const char *name, - GType gtype, - bool editable, - bool is_radio, - GCallback edit_callback, - gpointer userdata) -{ - GtkCellRenderer *cell; - GtkTreeViewColumn *col; - const char *attr = NULL; - - if (gtype == G_TYPE_BOOLEAN) { - cell = gtk_cell_renderer_toggle_new(); - gtk_cell_renderer_toggle_set_radio(GTK_CELL_RENDERER_TOGGLE(cell), - is_radio); - if (editable) { - g_signal_connect(cell, "toggled", edit_callback, userdata); - } - attr = "active"; - } else if (gtype == GDK_TYPE_PIXBUF) { - cell = gtk_cell_renderer_pixbuf_new(); - attr = "pixbuf"; - } else { - cell = gtk_cell_renderer_text_new(); - if (editable) { - g_object_set(cell, "editable", TRUE, NULL); - g_signal_connect(cell, "edited", edit_callback, userdata); - } - attr = "text"; - } - - col = gtk_tree_view_column_new_with_attributes(name, cell, - attr, col_id, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); -} - -/************************************************************************//** - Fill the supplied buffer with a short string representation of the given - value. Returned value is g_strdup'd and must be g_free'd. -****************************************************************************/ -static gchar *propval_as_string(struct propval *pv) -{ - int count = 0; - - fc_assert_ret_val(NULL != pv, 0); - - switch (pv->valtype) { - case VALTYPE_NONE: - return g_strdup(""); - - case VALTYPE_INT: - return g_strdup_printf("%d", pv->data.v_int); - - case VALTYPE_BOOL: - return g_strdup_printf("%s", pv->data.v_bool ? _("TRUE") : _("FALSE")); - - case VALTYPE_NATION: - return g_strdup_printf("%s", nation_adjective_translation(pv->data.v_nation)); - - case VALTYPE_GOV: - return g_strdup_printf("%s", government_name_translation(pv->data.v_gov)); - - case VALTYPE_BUILT_ARRAY: - { - int great_wonder_count = 0, small_wonder_count = 0, building_count = 0; - int id; - - improvement_iterate(pimprove) { - id = improvement_index(pimprove); - if (pv->data.v_built[id].turn < 0) { - continue; - } - if (is_great_wonder(pimprove)) { - great_wonder_count++; - } else if (is_small_wonder(pimprove)) { - small_wonder_count++; - } else { - building_count++; - } - } improvement_iterate_end; - /* TRANS: "Number of buildings, number of small - * wonders (e.g. palace), number of great wonders." */ - return g_strdup_printf(_("%db %ds %dW"), - building_count, small_wonder_count, - great_wonder_count); - } - - case VALTYPE_INVENTIONS_ARRAY: - advance_index_iterate(A_FIRST, tech) { - if (BV_ISSET(pv->data.v_bv_inventions, tech)) { - count++; - } - } advance_index_iterate_end; - /* TRANS: "Number of technologies known". */ - return g_strdup_printf(_("%d known"), count); - - case VALTYPE_BV_SPECIAL: - extra_type_by_cause_iterate(EC_SPECIAL, spe) { - if (BV_ISSET(pv->data.v_bv_special, spe->data.special_idx)) { - count++; - } - } extra_type_by_cause_iterate_end; - /* TRANS: "The number of terrain specials (e.g. hut, - * river, pollution, etc.) present on a tile." */ - return g_strdup_printf(_("%d present"), count); - - case VALTYPE_BV_ROADS: - extra_type_by_cause_iterate(EC_ROAD, pextra) { - struct road_type *proad = extra_road_get(pextra); - - if (BV_ISSET(pv->data.v_bv_roads, road_number(proad))) { - count++; - } - } extra_type_by_cause_iterate_end; - return g_strdup_printf(_("%d present"), count); - - case VALTYPE_BV_BASES: - extra_type_by_cause_iterate(EC_BASE, pextra) { - struct base_type *pbase = extra_base_get(pextra); - - if (BV_ISSET(pv->data.v_bv_bases, base_number(pbase))) { - count++; - } - } extra_type_by_cause_iterate_end; - return g_strdup_printf(_("%d present"), count); - - case VALTYPE_NATION_HASH: - count = nation_hash_size(pv->data.v_nation_hash); - if (0 == count) { - return g_strdup(_("All nations")); - } else { - return g_strdup_printf(PL_("%d nation", "%d nations", - count), count); - } - - case VALTYPE_STRING: - /* Assume it is a very long string. */ - count = strlen(pv->data.v_const_string); - return g_strdup_printf(PL_("%d byte", "%d bytes", count), - count); - - case VALTYPE_PIXBUF: - case VALTYPE_TILE_VISION_DATA: - break; - } - - log_error("%s(): Unhandled value type %d for property value %p.", - __FUNCTION__, pv->valtype, pv); - return g_strdup(""); -} - -/************************************************************************//** - Convert the built_status information to a user viewable string. - Returned value is g_strdup'd and must be g_free'd. -****************************************************************************/ -static gchar *built_status_to_string(struct built_status *bs) -{ - int turn_built; - - turn_built = bs->turn; - - if (turn_built == I_NEVER) { - /* TRANS: Improvement never built. */ - return g_strdup(_("(never)")); - } else if (turn_built == I_DESTROYED) { - /* TRANS: Improvement was destroyed. */ - return g_strdup(_("(destroyed)")); - } else { - return g_strdup_printf("%d", turn_built); - } -} - -/************************************************************************//** - Returns TRUE if a unit can be created at the given tile based on the - state of the editor (see editor_unit_virtual_create). -****************************************************************************/ -static bool can_create_unit_at_tile(struct tile *ptile) -{ - struct unit *vunit; - struct city *pcity; - struct player *pplayer; - bool ret; - - if (!ptile) { - return FALSE; - } - - vunit = editor_unit_virtual_create(); - if (!vunit) { - return FALSE; - } - - pcity = tile_city(ptile); - pplayer = unit_owner(vunit); - - ret = (can_unit_exist_at_tile(&(wld.map), vunit, ptile) - && !is_non_allied_unit_tile(ptile, pplayer) - && (pcity == NULL - || pplayers_allied(city_owner(pcity), - unit_owner(vunit)))); - free(vunit); - - return ret; -} - -/************************************************************************//** - Return the next tag number in the sequence. -****************************************************************************/ -static int get_next_unique_tag(void) -{ - static int tag_series = 0; - - tag_series++; - return tag_series; -} - -/************************************************************************//** - Return a newly allocated deep copy of the given value. -****************************************************************************/ -static struct propval *propval_copy(struct propval *pv) -{ - struct propval *pv_copy; - size_t size; - - if (!pv) { - return NULL; - } - - pv_copy = fc_calloc(1, sizeof(*pv)); - pv_copy->valtype = pv->valtype; - - switch (pv->valtype) { - case VALTYPE_NONE: - return pv_copy; - case VALTYPE_INT: - pv_copy->data.v_int = pv->data.v_int; - return pv_copy; - case VALTYPE_BOOL: - pv_copy->data.v_bool = pv->data.v_bool; - return pv_copy; - case VALTYPE_STRING: - pv_copy->data.v_string = fc_strdup(pv->data.v_string); - pv_copy->must_free = TRUE; - return pv_copy; - case VALTYPE_PIXBUF: - g_object_ref(pv->data.v_pixbuf); - pv_copy->data.v_pixbuf = pv->data.v_pixbuf; - pv_copy->must_free = TRUE; - return pv_copy; - case VALTYPE_BUILT_ARRAY: - size = B_LAST * sizeof(struct built_status); - pv_copy->data.v_pointer = fc_malloc(size); - memcpy(pv_copy->data.v_pointer, pv->data.v_pointer, size); - pv_copy->must_free = TRUE; - return pv_copy; - case VALTYPE_BV_SPECIAL: - pv_copy->data.v_bv_special = pv->data.v_bv_special; - return pv_copy; - case VALTYPE_BV_ROADS: - pv_copy->data.v_bv_roads = pv->data.v_bv_roads; - return pv_copy; - case VALTYPE_BV_BASES: - pv_copy->data.v_bv_bases = pv->data.v_bv_bases; - return pv_copy; - case VALTYPE_NATION: - pv_copy->data.v_nation = pv->data.v_nation; - return pv_copy; - case VALTYPE_GOV: - pv_copy->data.v_gov = pv->data.v_gov; - return pv_copy; - case VALTYPE_NATION_HASH: - pv_copy->data.v_nation_hash - = nation_hash_copy(pv->data.v_nation_hash); - pv_copy->must_free = TRUE; - return pv_copy; - case VALTYPE_INVENTIONS_ARRAY: - pv_copy->data.v_bv_inventions = pv->data.v_bv_inventions; - return pv_copy; - case VALTYPE_TILE_VISION_DATA: - size = sizeof(struct tile_vision_data); - pv_copy->data.v_tile_vision = fc_malloc(size); - pv_copy->data.v_tile_vision->tile_known - = pv->data.v_tile_vision->tile_known; - vision_layer_iterate(v) { - pv_copy->data.v_tile_vision->tile_seen[v] - = pv->data.v_tile_vision->tile_seen[v]; - } vision_layer_iterate_end; - pv_copy->must_free = TRUE; - return pv_copy; - } - - log_error("%s(): Unhandled value type %d for property value %p.", - __FUNCTION__, pv->valtype, pv); - pv_copy->data = pv->data; - return pv_copy; -} - -/************************************************************************//** - Free all allocated memory used by this property value, including calling - the appropriate free function on the internal data according to its type. -****************************************************************************/ -static void propval_free(struct propval *pv) -{ - if (!pv) { - return; - } - - propval_free_data(pv); - free(pv); -} - -/************************************************************************//** - Frees the internal data held by the propval, without freeing the propval - struct itself. -****************************************************************************/ -static void propval_free_data(struct propval *pv) -{ - if (!pv || !pv->must_free) { - return; - } - - switch (pv->valtype) { - case VALTYPE_NONE: - case VALTYPE_INT: - case VALTYPE_BOOL: - case VALTYPE_BV_SPECIAL: - case VALTYPE_BV_ROADS: - case VALTYPE_BV_BASES: - case VALTYPE_NATION: - case VALTYPE_GOV: - return; - case VALTYPE_PIXBUF: - g_object_unref(pv->data.v_pixbuf); - return; - case VALTYPE_STRING: - case VALTYPE_BUILT_ARRAY: - case VALTYPE_INVENTIONS_ARRAY: - case VALTYPE_TILE_VISION_DATA: - free(pv->data.v_pointer); - return; - case VALTYPE_NATION_HASH: - nation_hash_destroy(pv->data.v_nation_hash); - return; - } - - log_error("%s(): Unhandled request to free data %p (type %s).", - __FUNCTION__, pv->data.v_pointer, valtype_get_name(pv->valtype)); -} - -/************************************************************************//** - Returns TRUE if the two values are equal, in a deep sense. -****************************************************************************/ -static bool propval_equal(struct propval *pva, - struct propval *pvb) -{ - if (!pva || !pvb) { - return pva == pvb; - } - - if (pva->valtype != pvb->valtype) { - return FALSE; - } - - switch (pva->valtype) { - case VALTYPE_NONE: - return TRUE; - case VALTYPE_INT: - return pva->data.v_int == pvb->data.v_int; - case VALTYPE_BOOL: - return pva->data.v_bool == pvb->data.v_bool; - case VALTYPE_STRING: - if (pva->data.v_const_string && pvb->data.v_const_string) { - return 0 == strcmp(pva->data.v_const_string, - pvb->data.v_const_string); - } - return pva->data.v_const_string == pvb->data.v_const_string; - case VALTYPE_PIXBUF: - return pva->data.v_pixbuf == pvb->data.v_pixbuf; - case VALTYPE_BUILT_ARRAY: - if (pva->data.v_pointer == pvb->data.v_pointer) { - return TRUE; - } else if (!pva->data.v_pointer || !pvb->data.v_pointer) { - return FALSE; - } - - improvement_iterate(pimprove) { - int id, vatb, vbtb; - id = improvement_index(pimprove); - vatb = pva->data.v_built[id].turn; - vbtb = pvb->data.v_built[id].turn; - if (vatb < 0 && vbtb < 0) { - continue; - } - if (vatb != vbtb) { - return FALSE; - } - } improvement_iterate_end; - return TRUE; - case VALTYPE_INVENTIONS_ARRAY: - return BV_ARE_EQUAL(pva->data.v_bv_inventions, pvb->data.v_bv_inventions); - case VALTYPE_BV_SPECIAL: - return BV_ARE_EQUAL(pva->data.v_bv_special, pvb->data.v_bv_special); - case VALTYPE_BV_ROADS: - return BV_ARE_EQUAL(pva->data.v_bv_roads, pvb->data.v_bv_roads); - case VALTYPE_BV_BASES: - return BV_ARE_EQUAL(pva->data.v_bv_bases, pvb->data.v_bv_bases); - case VALTYPE_NATION: - return pva->data.v_nation == pvb->data.v_nation; - case VALTYPE_NATION_HASH: - return nation_hashs_are_equal(pva->data.v_nation_hash, - pvb->data.v_nation_hash); - case VALTYPE_GOV: - return pva->data.v_gov == pvb->data.v_gov; - case VALTYPE_TILE_VISION_DATA: - if (!BV_ARE_EQUAL(pva->data.v_tile_vision->tile_known, - pvb->data.v_tile_vision->tile_known)) { - return FALSE; - } - vision_layer_iterate(v) { - if (!BV_ARE_EQUAL(pva->data.v_tile_vision->tile_seen[v], - pvb->data.v_tile_vision->tile_seen[v])) { - return FALSE; - } - } vision_layer_iterate_end; - return TRUE; - } - - log_error("%s(): Unhandled value type %d for property values %p and %p.", - __FUNCTION__, pva->valtype, pva, pvb); - return pva->data.v_pointer == pvb->data.v_pointer; -} - -/************************************************************************//** - Create a new "property state" record. It keeps track of the modified value - of a property bound to an object. - - NB: Does NOT make a copy of 'pv'. -****************************************************************************/ -static struct propstate *propstate_new(struct objprop *op, - struct propval *pv) -{ - struct propstate *ps; - - if (!op) { - return NULL; - } - - ps = fc_calloc(1, sizeof(*ps)); - ps->property_id = objprop_get_id(op); - ps->property_value = pv; - - return ps; -} - -/************************************************************************//** - Removes the stored value, freeing it if needed. -****************************************************************************/ -static void propstate_clear_value(struct propstate *ps) -{ - if (!ps) { - return; - } - - propval_free(ps->property_value); - ps->property_value = NULL; -} - -/************************************************************************//** - Free a property state and any associated resources. -****************************************************************************/ -static void propstate_destroy(struct propstate *ps) -{ - if (!ps) { - return; - } - propstate_clear_value(ps); - free(ps); -} - -/************************************************************************//** - Replace the stored property value with a new one. The old value will - be freed if needed. - - NB: Does NOT make a copy of 'pv'. -****************************************************************************/ -static void propstate_set_value(struct propstate *ps, - struct propval *pv) -{ - if (!ps) { - return; - } - propstate_clear_value(ps); - ps->property_value = pv; -} - -/************************************************************************//** - Returns the stored value. - - NB: NOT a copy of the value. -****************************************************************************/ -static struct propval *propstate_get_value(struct propstate *ps) -{ - if (!ps) { - return NULL; - } - return ps->property_value; -} - -/************************************************************************//** - Create a new object "bind". It serves to bind a set of object properties - to an object instance. -****************************************************************************/ -static struct objbind *objbind_new(enum editor_object_type objtype, - gpointer object) -{ - struct objbind *ob; - int id; - - if (object == NULL) { - return NULL; - } - - id = objtype_get_id_from_object(objtype, object); - if (id < 0) { - return NULL; - } - - ob = fc_calloc(1, sizeof(*ob)); - ob->object_id = id; - ob->objtype = objtype; - ob->propstate_table = propstate_hash_new(); - - return ob; -} - -/************************************************************************//** - Returns the bound object, if it still "exists". Returns NULL on error. -****************************************************************************/ -static gpointer objbind_get_object(struct objbind *ob) -{ - int id; - - if (!ob) { - return NULL; - } - - id = objbind_get_object_id(ob); - - return objtype_get_object_from_id(ob->objtype, id); -} - -/************************************************************************//** - Returns the ID of the bound object, or -1 if invalid. -****************************************************************************/ -static int objbind_get_object_id(struct objbind *ob) -{ - if (NULL == ob) { - return -1; - } - return ob->object_id; -} - -/************************************************************************//** - Sends a request to the server to have the bound object erased from - existence. Only makes sense for object types for which the function - objtype_is_conserved() returns FALSE. -****************************************************************************/ -static void objbind_request_destroy_object(struct objbind *ob) -{ - struct connection *my_conn = &client.conn; - enum editor_object_type objtype; - int id; - - if (!ob) { - return; - } - - objtype = objbind_get_objtype(ob); - if (objtype_is_conserved(objtype)) { - return; - } - - id = objbind_get_object_id(ob); - - switch (objtype) { - case OBJTYPE_STARTPOS: - dsend_packet_edit_startpos(my_conn, id, TRUE, 0); - return; - case OBJTYPE_UNIT: - dsend_packet_edit_unit_remove_by_id(my_conn, id); - return; - case OBJTYPE_CITY: - dsend_packet_edit_city_remove(my_conn, id); - return; - case OBJTYPE_PLAYER: - dsend_packet_edit_player_remove(my_conn, id); - return; - case OBJTYPE_TILE: - case OBJTYPE_GAME: - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled request to destroy object %p (ID %d) of type " - "%d (%s).", __FUNCTION__, objbind_get_object(ob), id, objtype, - objtype_get_name(objtype)); -} - -/************************************************************************//** - Returns a newly allocated property value for the given object property - on the object referenced by the given object bind, or NULL on failure. - - NB: You must call propval_free on the non-NULL return value when it - no longer needed. -****************************************************************************/ -static struct propval *objbind_get_value_from_object(struct objbind *ob, - struct objprop *op) -{ - enum editor_object_type objtype; - enum object_property_ids propid; - struct propval *pv; - - if (!ob || !op) { - return NULL; - } - - objtype = objbind_get_objtype(ob); - propid = objprop_get_id(op); - - pv = fc_calloc(1, sizeof(*pv)); - pv->valtype = objprop_get_valtype(op); - - switch (objtype) { - case OBJTYPE_TILE: - { - const struct tile *ptile = objbind_get_object(ob); - int tile_x, tile_y, nat_x, nat_y; - - if (NULL == ptile) { - goto FAILED; - } - - index_to_map_pos(&tile_x, &tile_y, tile_index(ptile)); - index_to_native_pos(&nat_x, &nat_y, tile_index(ptile)); - - switch (propid) { - case OPID_TILE_IMAGE: - pv->data.v_pixbuf = create_tile_pixbuf(ptile); - pv->must_free = TRUE; - break; -#ifdef FREECIV_DEBUG - case OPID_TILE_ADDRESS: - pv->data.v_string = g_strdup_printf("%p", ptile); - pv->must_free = TRUE; - break; -#endif /* FREECIV_DEBUG */ - case OPID_TILE_TERRAIN: - { - const struct terrain *pterrain = tile_terrain(ptile); - - if (NULL != pterrain) { - pv->data.v_const_string = terrain_name_translation(pterrain); - } else { - pv->data.v_const_string = ""; - } - } - break; - case OPID_TILE_RESOURCE: - { - const struct extra_type *presource = tile_resource(ptile); - - if (NULL != presource) { - pv->data.v_const_string = extra_name_translation(presource); - } else { - pv->data.v_const_string = ""; - } - } - break; - case OPID_TILE_XY: - pv->data.v_string = g_strdup_printf("(%d, %d)", tile_x, tile_y); - pv->must_free = TRUE; - break; - case OPID_TILE_INDEX: - pv->data.v_int = tile_index(ptile); - break; - case OPID_TILE_X: - pv->data.v_int = tile_x; - break; - case OPID_TILE_Y: - pv->data.v_int = tile_y; - break; - case OPID_TILE_NAT_X: - pv->data.v_int = nat_x; - break; - case OPID_TILE_NAT_Y: - pv->data.v_int = nat_y; - break; - case OPID_TILE_CONTINENT: - pv->data.v_int = ptile->continent; - break; - case OPID_TILE_SPECIALS: - BV_CLR_ALL(pv->data.v_bv_special); - extra_type_by_cause_iterate(EC_SPECIAL, pextra) { - if (tile_has_extra(ptile, pextra)) { - BV_SET(pv->data.v_bv_special, pextra->data.special_idx); - } - } extra_type_by_cause_iterate_end; - break; - case OPID_TILE_ROADS: - BV_CLR_ALL(pv->data.v_bv_roads); - extra_type_by_cause_iterate(EC_ROAD, pextra) { - if (tile_has_extra(ptile, pextra)) { - BV_SET(pv->data.v_bv_roads, road_number(extra_road_get(pextra))); - } - } extra_type_by_cause_iterate_end; - break; - case OPID_TILE_BASES: - BV_CLR_ALL(pv->data.v_bv_bases); - extra_type_by_cause_iterate(EC_BASE, pextra) { - if (tile_has_extra(ptile, pextra)) { - BV_SET(pv->data.v_bv_bases, base_number(extra_base_get(pextra))); - } - } extra_type_by_cause_iterate_end; - break; - case OPID_TILE_VISION: - pv->data.v_tile_vision = fc_malloc(sizeof(struct tile_vision_data)); - - /* The server saves the known tiles and the player vision in special - * bitvectors with the number of tiles as index. Here we want the - * information for one tile. Thus, the data is transformed to - * bitvectors with the number of player slots as index. */ - BV_CLR_ALL(pv->data.v_tile_vision->tile_known); - players_iterate(pplayer) { - if (dbv_isset(&pplayer->tile_known, tile_index(ptile))) { - BV_SET(pv->data.v_tile_vision->tile_known, - player_index(pplayer)); - } - } players_iterate_end; - - vision_layer_iterate(v) { - BV_CLR_ALL(pv->data.v_tile_vision->tile_seen[v]); - players_iterate(pplayer) { - if (fc_funcs->player_tile_vision_get(ptile, pplayer, v)) { - BV_SET(pv->data.v_tile_vision->tile_seen[v], - player_index(pplayer)); - } - } players_iterate_end; - } vision_layer_iterate_end; - pv->must_free = TRUE; - break; - case OPID_TILE_LABEL: - if (ptile->label != NULL) { - pv->data.v_const_string = ptile->label; - } else { - pv->data.v_const_string = ""; - } - break; - default: - log_error("%s(): Unhandled request for value of property %d " - "(%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - goto FAILED; - } - } - return pv; - - case OBJTYPE_STARTPOS: - { - const struct startpos *psp = objbind_get_object(ob); - const struct tile *ptile; - - if (NULL == psp) { - goto FAILED; - } - - switch (propid) { - case OPID_STARTPOS_IMAGE: - ptile = startpos_tile(psp); - pv->data.v_pixbuf = create_tile_pixbuf(ptile); - pv->must_free = TRUE; - break; - case OPID_STARTPOS_XY: - ptile = startpos_tile(psp); - pv->data.v_string = g_strdup_printf("(%d, %d)", TILE_XY(ptile)); - pv->must_free = TRUE; - break; - case OPID_STARTPOS_EXCLUDE: - pv->data.v_bool = startpos_is_excluding(psp); - break; - case OPID_STARTPOS_NATIONS: - pv->data.v_nation_hash = nation_hash_copy(startpos_raw_nations(psp)); - pv->must_free = TRUE; - break; - default: - log_error("%s(): Unhandled request for value of property %d " - "(%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - goto FAILED; - } - } - return pv; - - case OBJTYPE_UNIT: - { - struct unit *punit = objbind_get_object(ob); - - if (NULL == punit) { - goto FAILED; - } - - switch (propid) { - case OPID_UNIT_IMAGE: - pv->data.v_pixbuf = create_unit_pixbuf(punit); - pv->must_free = TRUE; - break; -#ifdef FREECIV_DEBUG - case OPID_UNIT_ADDRESS: - pv->data.v_string = g_strdup_printf("%p", punit); - pv->must_free = TRUE; - break; -#endif /* FREECIV_DEBUG */ - case OPID_UNIT_XY: - { - const struct tile *ptile = unit_tile(punit); - - pv->data.v_string = g_strdup_printf("(%d, %d)", TILE_XY(ptile)); - pv->must_free = TRUE; - } - break; - case OPID_UNIT_ID: - pv->data.v_int = punit->id; - break; - case OPID_UNIT_TYPE: - { - const struct unit_type *putype = unit_type_get(punit); - - pv->data.v_const_string = utype_name_translation(putype); - } - break; - case OPID_UNIT_MOVES_LEFT: - pv->data.v_int = punit->moves_left; - break; - case OPID_UNIT_FUEL: - pv->data.v_int = punit->fuel; - break; - case OPID_UNIT_MOVED: - pv->data.v_bool = punit->moved; - break; - case OPID_UNIT_DONE_MOVING: - pv->data.v_bool = punit->done_moving; - break; - case OPID_UNIT_HP: - pv->data.v_int = punit->hp; - break; - case OPID_UNIT_VETERAN: - pv->data.v_int = punit->veteran; - break; - case OPID_UNIT_STAY: - pv->data.v_bool = punit->stay; - break; - default: - log_error("%s(): Unhandled request for value of property %d " - "(%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - goto FAILED; - } - } - return pv; - - case OBJTYPE_CITY: - { - const struct city *pcity = objbind_get_object(ob); - - if (NULL == pcity) { - goto FAILED; - } - - switch (propid) { - case OPID_CITY_IMAGE: - pv->data.v_pixbuf = create_city_pixbuf(pcity); - pv->must_free = TRUE; - break; -#ifdef FREECIV_DEBUG - case OPID_CITY_ADDRESS: - pv->data.v_string = g_strdup_printf("%p", pcity); - pv->must_free = TRUE; - break; -#endif /* FREECIV_DEBUG */ - case OPID_CITY_XY: - { - const struct tile *ptile = city_tile(pcity); - - pv->data.v_string = g_strdup_printf("(%d, %d)", TILE_XY(ptile)); - pv->must_free = TRUE; - } - break; - case OPID_CITY_ID: - pv->data.v_int = pcity->id; - break; - case OPID_CITY_NAME: - pv->data.v_const_string = pcity->name; - break; - case OPID_CITY_SIZE: - pv->data.v_int = city_size_get(pcity); - break; - case OPID_CITY_HISTORY: - pv->data.v_int = pcity->history; - break; - case OPID_CITY_BUILDINGS: - pv->data.v_built = fc_malloc(sizeof(pcity->built)); - memcpy(pv->data.v_built, pcity->built, sizeof(pcity->built)); - pv->must_free = TRUE; - break; - case OPID_CITY_FOOD_STOCK: - pv->data.v_int = pcity->food_stock; - break; - case OPID_CITY_SHIELD_STOCK: - pv->data.v_int = pcity->shield_stock; - break; - default: - log_error("%s(): Unhandled request for value of property %d " - "(%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - goto FAILED; - } - } - return pv; - - case OBJTYPE_PLAYER: - { - const struct player *pplayer = objbind_get_object(ob); - const struct research *presearch; - - if (NULL == pplayer) { - goto FAILED; - } - - switch (propid) { - case OPID_PLAYER_NAME: - pv->data.v_const_string = pplayer->name; - break; - case OPID_PLAYER_NATION: - pv->data.v_nation = nation_of_player(pplayer); - break; - case OPID_PLAYER_GOV: - pv->data.v_gov = pplayer->government; - break; - case OPID_PLAYER_AGE: - pv->data.v_int = pplayer->turns_alive; - break; -#ifdef FREECIV_DEBUG - case OPID_PLAYER_ADDRESS: - pv->data.v_string = g_strdup_printf("%p", pplayer); - pv->must_free = TRUE; - break; -#endif /* FREECIV_DEBUG */ - case OPID_PLAYER_INVENTIONS: - presearch = research_get(pplayer); - BV_CLR_ALL(pv->data.v_bv_inventions); - advance_index_iterate(A_FIRST, tech) { - if (TECH_KNOWN == research_invention_state(presearch, tech)) { - BV_SET(pv->data.v_bv_inventions, tech); - } - } advance_index_iterate_end; - break; - case OPID_PLAYER_SCENARIO_RESERVED: - pv->data.v_bool = player_has_flag(pplayer, PLRF_SCENARIO_RESERVED); - break; - case OPID_PLAYER_SCIENCE: - presearch = research_get(pplayer); - pv->data.v_int = presearch->bulbs_researched; - break; - case OPID_PLAYER_GOLD: - pv->data.v_int = pplayer->economic.gold; - break; - default: - log_error("%s(): Unhandled request for value of property %d " - "(%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - goto FAILED; - } - } - return pv; - - case OBJTYPE_GAME: - { - const struct civ_game *pgame = objbind_get_object(ob); - - if (NULL == pgame) { - goto FAILED; - } - - switch (propid) { - case OPID_GAME_SCENARIO: - pv->data.v_bool = pgame->scenario.is_scenario; - break; - case OPID_GAME_SCENARIO_NAME: - pv->data.v_const_string = pgame->scenario.name; - break; - case OPID_GAME_SCENARIO_AUTHORS: - pv->data.v_const_string = pgame->scenario.authors; - break; - case OPID_GAME_SCENARIO_DESC: - pv->data.v_const_string = pgame->scenario_desc.description; - break; - case OPID_GAME_SCENARIO_RANDSTATE: - pv->data.v_bool = pgame->scenario.save_random; - break; - case OPID_GAME_SCENARIO_PLAYERS: - pv->data.v_bool = pgame->scenario.players; - break; - case OPID_GAME_STARTPOS_NATIONS: - pv->data.v_bool = pgame->scenario.startpos_nations; - break; - case OPID_GAME_PREVENT_CITIES: - pv->data.v_bool = pgame->scenario.prevent_new_cities; - break; - case OPID_GAME_LAKE_FLOODING: - pv->data.v_bool = pgame->scenario.lake_flooding; - break; - case OPID_GAME_RULESET_LOCKED: - pv->data.v_bool = pgame->scenario.ruleset_locked; - break; - default: - log_error("%s(): Unhandled request for value of property %d " - "(%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - goto FAILED; - } - } - return pv; - - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled request for object type \"%s\" (nb %d).", - __FUNCTION__, objtype_get_name(objtype), objtype); - -FAILED: - if (NULL != pv) { - free(pv); - } - return NULL; -} - -/************************************************************************//** - If applicable, sets the allowed range values of the given object property - as applied to the bound object. Returns TRUE if values were set. -****************************************************************************/ -static bool objbind_get_allowed_value_span(struct objbind *ob, - struct objprop *op, - double *pmin, - double *pmax, - double *pstep, - double *pbig_step) -{ - enum editor_object_type objtype; - enum object_property_ids propid; - double dummy; - - /* Fill the values with something. */ - if (NULL != pmin) { - *pmin = 0; - } else { - pmin = &dummy; - } - if (NULL != pmax) { - *pmax = 1; - } else { - pmax = &dummy; - } - if (NULL != pstep) { - *pstep = 1; - } else { - pstep = &dummy; - } - if (NULL != pbig_step) { - *pbig_step = 1; - } else { - pbig_step = &dummy; - } - - if (!ob || !op) { - return FALSE; - } - - propid = objprop_get_id(op); - objtype = objbind_get_objtype(ob); - - switch (objtype) { - case OBJTYPE_TILE: - case OBJTYPE_STARTPOS: - log_error("%s(): Unhandled request for value range of property %d (%s) " - "from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return FALSE; - - case OBJTYPE_UNIT: - { - const struct unit *punit = objbind_get_object(ob); - const struct unit_type *putype; - - if (NULL == punit) { - return FALSE; - } - - putype = unit_type_get(punit); - - switch (propid) { - case OPID_UNIT_MOVES_LEFT: - *pmin = 0; - *pmax = MAX_MOVE_FRAGS; - *pstep = 1; - *pbig_step = 5; - return TRUE; - case OPID_UNIT_FUEL: - *pmin = 0; - *pmax = utype_fuel(putype); - *pstep = 1; - *pbig_step = 5; - return TRUE; - case OPID_UNIT_HP: - *pmin = 1; - *pmax = putype->hp; - *pstep = 1; - *pbig_step = 10; - return TRUE; - case OPID_UNIT_VETERAN: - *pmin = 0; - *pmax = utype_veteran_levels(putype) - 1; - *pstep = 1; - *pbig_step = 3; - return TRUE; - default: - break; - } - } - log_error("%s(): Unhandled request for value range of property %d (%s) " - "from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return FALSE; - - case OBJTYPE_CITY: - { - const struct city *pcity = objbind_get_object(ob); - - if (NULL == pcity) { - return FALSE; - } - - switch (propid) { - case OPID_CITY_SIZE: - *pmin = 1; - *pmax = MAX_CITY_SIZE; - *pstep = 1; - *pbig_step = 5; - return TRUE; - case OPID_CITY_HISTORY: - *pmin = 0; - *pmax = USHRT_MAX; - *pstep = 1; - *pbig_step = 10; - return TRUE; - case OPID_CITY_FOOD_STOCK: - *pmin = 0; - *pmax = city_granary_size(city_size_get(pcity)); - *pstep = 1; - *pbig_step = 5; - return TRUE; - case OPID_CITY_SHIELD_STOCK: - *pmin = 0; - *pmax = USHRT_MAX; /* Limited to uint16 by city info packet. */ - *pstep = 1; - *pbig_step = 10; - return TRUE; - default: - break; - } - } - log_error("%s(): Unhandled request for value range of property %d (%s) " - "from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return FALSE; - - case OBJTYPE_PLAYER: - switch (propid) { - case OPID_PLAYER_SCIENCE: - *pmin = 0; - *pmax = 1000000; /* Arbitrary. */ - *pstep = 1; - *pbig_step = 100; - return TRUE; - case OPID_PLAYER_GOLD: - *pmin = 0; - *pmax = 1000000; /* Arbitrary. */ - *pstep = 1; - *pbig_step = 100; - return TRUE; - default: - break; - } - log_error("%s(): Unhandled request for value range of property %d (%s) " - "from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return FALSE; - - case OBJTYPE_GAME: - log_error("%s(): Unhandled request for value range of property %d (%s) " - "from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return FALSE; - - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled request for object type \"%s\" (nb %d).", - __FUNCTION__, objtype_get_name(objtype), objtype); - return FALSE; -} - -/************************************************************************//** - Remove a stored modified value, if it exists. -****************************************************************************/ -static void objbind_clear_modified_value(struct objbind *ob, - struct objprop *op) -{ - if (!ob || !op || !ob->propstate_table) { - return; - } - - propstate_hash_remove(ob->propstate_table, objprop_get_id(op)); -} - -/************************************************************************//** - Returns TRUE if a stored modified property value exists for this bound - object for the given property. -****************************************************************************/ -static bool objbind_property_is_modified(struct objbind *ob, - struct objprop *op) -{ - if (!ob || !op) { - return FALSE; - } - - if (objprop_is_readonly(op)) { - return FALSE; - } - - return propstate_hash_lookup(ob->propstate_table, - objprop_get_id(op), NULL); -} - -/************************************************************************//** - Returns TRUE if there are any stored modified values of any of the - properties of the bound object. -****************************************************************************/ -static bool objbind_has_modified_properties(struct objbind *ob) -{ - if (!ob) { - return FALSE; - } - - return (0 < propstate_hash_size(ob->propstate_table)); -} - -/************************************************************************//** - Deletes all stored modified property values. -****************************************************************************/ -static void objbind_clear_all_modified_values(struct objbind *ob) -{ - if (!ob) { - return; - } - propstate_hash_clear(ob->propstate_table); -} - -/************************************************************************//** - Store a modified property value, but only if it is different from the - current value. Always makes a copy of the given value when storing. -****************************************************************************/ -static void objbind_set_modified_value(struct objbind *ob, - struct objprop *op, - struct propval *pv) -{ - struct propstate *ps; - bool equal; - struct propval *pv_old, *pv_copy; - enum object_property_ids propid; - - if (!ob || !op) { - return; - } - - propid = objprop_get_id(op); - - pv_old = objbind_get_value_from_object(ob, op); - if (!pv_old) { - return; - } - - equal = propval_equal(pv, pv_old); - propval_free(pv_old); - - if (equal) { - objbind_clear_modified_value(ob, op); - return; - } - - pv_copy = propval_copy(pv); - - if (propstate_hash_lookup(ob->propstate_table, propid, &ps)) { - propstate_set_value(ps, pv_copy); - } else { - ps = propstate_new(op, pv_copy); - propstate_hash_insert(ob->propstate_table, propid, ps); - } -} - -/************************************************************************//** - Retrieve the stored property value for the bound object, or NULL if none - exists. - - NB: Does NOT return a copy. -****************************************************************************/ -static struct propval *objbind_get_modified_value(struct objbind *ob, - struct objprop *op) -{ - struct propstate *ps; - - if (!ob || !op) { - return FALSE; - } - - if (propstate_hash_lookup(ob->propstate_table, objprop_get_id(op), &ps)) { - return propstate_get_value(ps); - } else { - return NULL; - } -} - -/************************************************************************//** - Destroy the object bind and free any resources it might have been using. -****************************************************************************/ -static void objbind_destroy(struct objbind *ob) -{ - if (!ob) { - return; - } - if (ob->propstate_table) { - propstate_hash_destroy(ob->propstate_table); - ob->propstate_table = NULL; - } - if (ob->rowref) { - gtk_tree_row_reference_free(ob->rowref); - ob->rowref = NULL; - } - free(ob); -} - -/************************************************************************//** - Returns the object type of the bound object. -****************************************************************************/ -static enum editor_object_type objbind_get_objtype(const struct objbind *ob) -{ - if (!ob) { - return NUM_OBJTYPES; - } - return ob->objtype; -} - -/************************************************************************//** - Bind the object in the given objbind to the properties in the page. -****************************************************************************/ -static void objbind_bind_properties(struct objbind *ob, - struct property_page *pp) -{ - if (!ob) { - return; - } - ob->parent_property_page = pp; -} - -/************************************************************************//** - Fill the packet with the bound object's current state. - - NB: This must be updated if the packet_edit_ definitions change. -****************************************************************************/ -static void objbind_pack_current_values(struct objbind *ob, - union packetdata pd) -{ - enum editor_object_type objtype; - - if (!ob || !pd.pointers.v_pointer1) { - return; - } - - objtype = objbind_get_objtype(ob); - - switch (objtype) { - case OBJTYPE_TILE: - { - struct packet_edit_tile *packet = pd.tile; - const struct tile *ptile = objbind_get_object(ob); - - if (NULL == ptile) { - return; - } - - packet->tile = tile_index(ptile); - packet->extras = *tile_extras(ptile); - /* TODO: Set more packet fields. */ - } - return; - - case OBJTYPE_STARTPOS: - { - struct packet_edit_startpos_full *packet = pd.startpos; - const struct startpos *psp = objbind_get_object(ob); - - if (NULL != psp) { - startpos_pack(psp, packet); - } - } - return; - - case OBJTYPE_UNIT: - { - struct packet_edit_unit *packet = pd.unit; - const struct unit *punit = objbind_get_object(ob); - - if (NULL == punit) { - return; - } - - packet->id = punit->id; - packet->moves_left = punit->moves_left; - packet->fuel = punit->fuel; - packet->moved = punit->moved; - packet->done_moving = punit->done_moving; - packet->hp = punit->hp; - packet->veteran = punit->veteran; - packet->stay = punit->stay; - /* TODO: Set more packet fields. */ - } - return; - - case OBJTYPE_CITY: - { - struct packet_edit_city *packet = pd.city; - const struct city *pcity = objbind_get_object(ob); - int i; - - if (NULL == pcity) { - return; - } - - packet->id = pcity->id; - sz_strlcpy(packet->name, pcity->name); - packet->size = city_size_get(pcity); - packet->history = pcity->history; - for (i = 0; i < B_LAST; i++) { - packet->built[i] = pcity->built[i].turn; - } - packet->food_stock = pcity->food_stock; - packet->shield_stock = pcity->shield_stock; - /* TODO: Set more packet fields. */ - } - return; - - case OBJTYPE_PLAYER: - { - struct packet_edit_player *packet = pd.player; - const struct player *pplayer = objbind_get_object(ob); - const struct nation_type *pnation; - const struct research *presearch; - - if (NULL == pplayer) { - return; - } - - packet->id = player_number(pplayer); - sz_strlcpy(packet->name, pplayer->name); - pnation = nation_of_player(pplayer); - packet->nation = nation_index(pnation); - presearch = research_get(pplayer); - advance_index_iterate(A_FIRST, tech) { - packet->inventions[tech] - = TECH_KNOWN == research_invention_state(presearch, tech); - } advance_index_iterate_end; - packet->gold = pplayer->economic.gold; - packet->government = government_index(pplayer->government); - /* TODO: Set more packet fields. */ - } - return; - - case OBJTYPE_GAME: - { - struct packet_edit_game *packet = pd.game.game; - const struct civ_game *pgame = objbind_get_object(ob); - - if (NULL == pgame) { - return; - } - - packet->scenario = pgame->scenario.is_scenario; - sz_strlcpy(packet->scenario_name, pgame->scenario.name); - sz_strlcpy(packet->scenario_authors, pgame->scenario.authors); - sz_strlcpy(pd.game.desc->scenario_desc, pgame->scenario_desc.description); - packet->scenario_random = pgame->scenario.save_random; - packet->scenario_players = pgame->scenario.players; - packet->startpos_nations = pgame->scenario.startpos_nations; - packet->prevent_new_cities = pgame->scenario.prevent_new_cities; - packet->lake_flooding = pgame->scenario.lake_flooding; - } - return; - - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled object type %s (nb %d).", __FUNCTION__, - objtype_get_name(objtype), objtype); -} - -/************************************************************************//** - Package the modified property value into the supplied packet. -****************************************************************************/ -static void objbind_pack_modified_value(struct objbind *ob, - struct objprop *op, - union packetdata pd) -{ - struct propval *pv; - enum editor_object_type objtype; - enum object_property_ids propid; - - if (!op || !ob || !pd.pointers.v_pointer1) { - return; - } - - if (NULL == objbind_get_object(ob)) { - return; - } - - if (objprop_is_readonly(op) || !objbind_property_is_modified(ob, op)) { - return; - } - - pv = objbind_get_modified_value(ob, op); - if (!pv) { - return; - } - - objtype = objbind_get_objtype(ob); - propid = objprop_get_id(op); - - switch (objtype) { - case OBJTYPE_TILE: - { - struct packet_edit_tile *packet = pd.tile; - - switch (propid) { - case OPID_TILE_SPECIALS: - extra_type_by_cause_iterate(EC_SPECIAL, pextra) { - if (BV_ISSET(pv->data.v_bv_special, pextra->data.special_idx)) { - BV_SET(packet->extras, pextra->data.special_idx); - } else { - BV_CLR(packet->extras, pextra->data.special_idx); - } - } extra_type_by_cause_iterate_end; - return; - case OPID_TILE_ROADS: - extra_type_by_cause_iterate(EC_ROAD, pextra) { - int ridx = road_number(extra_road_get(pextra)); - - if (BV_ISSET(pv->data.v_bv_roads, ridx)) { - BV_SET(packet->extras, extra_index(pextra)); - } else { - BV_CLR(packet->extras, extra_index(pextra)); - } - } extra_type_by_cause_iterate_end; - return; - case OPID_TILE_BASES: - extra_type_by_cause_iterate(EC_BASE, pextra) { - int bidx = base_number(extra_base_get(pextra)); - - if (BV_ISSET(pv->data.v_bv_bases, bidx)) { - BV_SET(packet->extras, extra_index(pextra)); - } else { - BV_CLR(packet->extras, extra_index(pextra)); - } - } extra_type_by_cause_iterate_end; - return; - case OPID_TILE_LABEL: - sz_strlcpy(packet->label, pv->data.v_string); - return; - default: - break; - } - } - log_error("%s(): Unhandled request to pack value of property " - "%d (%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return; - - case OBJTYPE_STARTPOS: - { - struct packet_edit_startpos_full *packet = pd.startpos; - - switch (propid) { - case OPID_STARTPOS_EXCLUDE: - packet->exclude = pv->data.v_bool; - return; - case OPID_STARTPOS_NATIONS: - BV_CLR_ALL(packet->nations); - nation_hash_iterate(pv->data.v_nation_hash, pnation) { - BV_SET(packet->nations, nation_number(pnation)); - } nation_hash_iterate_end; - return; - default: - break; - } - } - log_error("%s(): Unhandled request to pack value of property " - "%d (%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return; - - case OBJTYPE_UNIT: - { - struct packet_edit_unit *packet = pd.unit; - - switch (propid) { - case OPID_UNIT_MOVES_LEFT: - packet->moves_left = pv->data.v_int; - return; - case OPID_UNIT_FUEL: - packet->fuel = pv->data.v_int; - return; - case OPID_UNIT_MOVED: - packet->moved = pv->data.v_bool; - return; - case OPID_UNIT_DONE_MOVING: - packet->done_moving = pv->data.v_bool; - return; - case OPID_UNIT_HP: - packet->hp = pv->data.v_int; - return; - case OPID_UNIT_VETERAN: - packet->veteran = pv->data.v_int; - return; - case OPID_UNIT_STAY: - packet->stay = pv->data.v_bool; - return; - default: - break; - } - } - log_error("%s(): Unhandled request to pack value of property " - "%d (%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return; - - case OBJTYPE_CITY: - { - struct packet_edit_city *packet = pd.city; - - switch (propid) { - case OPID_CITY_NAME: - sz_strlcpy(packet->name, pv->data.v_string); - return; - case OPID_CITY_SIZE: - packet->size = pv->data.v_int; - return; - case OPID_CITY_HISTORY: - packet->history = pv->data.v_int; - return; - case OPID_CITY_FOOD_STOCK: - packet->food_stock = pv->data.v_int; - return; - case OPID_CITY_SHIELD_STOCK: - packet->shield_stock = pv->data.v_int; - return; - case OPID_CITY_BUILDINGS: - { - int i; - - for (i = 0; i < B_LAST; i++) { - packet->built[i] = pv->data.v_built[i].turn; - } - } - return; - default: - break; - } - } - log_error("%s(): Unhandled request to pack value of property " - "%d (%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return; - - case OBJTYPE_PLAYER: - { - struct packet_edit_player *packet = pd.player; - - switch (propid) { - case OPID_PLAYER_NAME: - sz_strlcpy(packet->name, pv->data.v_string); - return; - case OPID_PLAYER_NATION: - packet->nation = nation_index(pv->data.v_nation); - return; - case OPID_PLAYER_GOV: - packet->government = government_index(pv->data.v_gov); - return; - case OPID_PLAYER_INVENTIONS: - advance_index_iterate(A_FIRST, tech) { - packet->inventions[tech] = BV_ISSET(pv->data.v_bv_inventions, tech); - } advance_index_iterate_end; - return; - case OPID_PLAYER_SCENARIO_RESERVED: - packet->scenario_reserved = pv->data.v_bool; - return; - case OPID_PLAYER_SCIENCE: - packet->bulbs_researched = pv->data.v_int; - return; - case OPID_PLAYER_GOLD: - packet->gold = pv->data.v_int; - return; - default: - break; - } - } - log_error("%s(): Unhandled request to pack value of property " - "%d (%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return; - - case OBJTYPE_GAME: - { - struct packet_edit_game *packet = pd.game.game; - - switch (propid) { - case OPID_GAME_SCENARIO: - packet->scenario = pv->data.v_bool; - return; - case OPID_GAME_SCENARIO_NAME: - sz_strlcpy(packet->scenario_name, pv->data.v_const_string); - return; - case OPID_GAME_SCENARIO_AUTHORS: - sz_strlcpy(packet->scenario_authors, pv->data.v_const_string); - return; - case OPID_GAME_SCENARIO_DESC: - sz_strlcpy(pd.game.desc->scenario_desc, pv->data.v_const_string); - return; - case OPID_GAME_SCENARIO_RANDSTATE: - packet->scenario_random = pv->data.v_bool; - return; - case OPID_GAME_SCENARIO_PLAYERS: - packet->scenario_players = pv->data.v_bool; - return; - case OPID_GAME_STARTPOS_NATIONS: - packet->startpos_nations = pv->data.v_bool; - return; - case OPID_GAME_PREVENT_CITIES: - packet->prevent_new_cities = pv->data.v_bool; - return; - case OPID_GAME_LAKE_FLOODING: - packet->lake_flooding = pv->data.v_bool; - return; - case OPID_GAME_RULESET_LOCKED: - packet->ruleset_locked = pv->data.v_bool; - return; - default: - break; - } - } - log_error("%s(): Unhandled request to pack value of property " - "%d (%s) from object of type \"%s\".", __FUNCTION__, - propid, objprop_get_name(op), objtype_get_name(objtype)); - return; - - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled request for object type \"%s\" (nb %d).", - __FUNCTION__, objtype_get_name(objtype), objtype); - -} - -/************************************************************************//** - Sets the row reference in a GtkTreeModel of this objbind. -****************************************************************************/ -static void objbind_set_rowref(struct objbind *ob, - GtkTreeRowReference *rr) -{ - if (!ob) { - return; - } - ob->rowref = rr; -} - -/************************************************************************//** - Returns the row reference of this objbind, or NULL if not applicable. -****************************************************************************/ -static GtkTreeRowReference *objbind_get_rowref(struct objbind *ob) -{ - if (!ob) { - return NULL; - } - return ob->rowref; -} - -/************************************************************************//** - Returns the unique property identifier for this object property. -****************************************************************************/ -static int objprop_get_id(const struct objprop *op) -{ - if (!op) { - return -1; - } - return op->id; -} - -/************************************************************************//** - Returns the GType that this object property renders as in a GtkTreeView. - Returning G_TYPE_NONE indicates that this property cannot be viewed - in a list. - - NB: This must correspond to the actions in property_page_set_store_value. -****************************************************************************/ -static GType objprop_get_gtype(const struct objprop *op) -{ - fc_assert_ret_val(NULL != op, G_TYPE_NONE); - - switch (op->valtype) { - case VALTYPE_NONE: - case VALTYPE_TILE_VISION_DATA: - return G_TYPE_NONE; - case VALTYPE_INT: - return G_TYPE_INT; - case VALTYPE_BOOL: - /* We want to show it as translated string, not as untranslated G_TYPE_BOOLEAN */ - return G_TYPE_STRING; - case VALTYPE_STRING: - case VALTYPE_BUILT_ARRAY: - case VALTYPE_INVENTIONS_ARRAY: - case VALTYPE_BV_SPECIAL: - case VALTYPE_BV_ROADS: - case VALTYPE_BV_BASES: - case VALTYPE_NATION_HASH: - return G_TYPE_STRING; - case VALTYPE_PIXBUF: - case VALTYPE_NATION: - case VALTYPE_GOV: - return GDK_TYPE_PIXBUF; - } - log_error("%s(): Unhandled value type %d.", __FUNCTION__, op->valtype); - return G_TYPE_NONE; -} - -/************************************************************************//** - Returns the value type of this property value (one of enum value_types). -****************************************************************************/ -static enum value_types objprop_get_valtype(const struct objprop *op) -{ - if (!op) { - return VALTYPE_NONE; - } - return op->valtype; -} - -/************************************************************************//** - Returns TRUE if this object property can be viewed in a GtkTreeView. -****************************************************************************/ -static bool objprop_show_in_listview(const struct objprop *op) -{ - if (!op) { - return FALSE; - } - return op->flags & OPF_IN_LISTVIEW; -} - -/************************************************************************//** - Returns TRUE if this object property can create and use a property widget. -****************************************************************************/ -static bool objprop_has_widget(const struct objprop *op) -{ - if (!op) { - return FALSE; - } - return op->flags & OPF_HAS_WIDGET; -} - -/************************************************************************//** - Returns a the string corresponding to the attribute type name required - for gtk_tree_view_column_new_with_attributes according to this objprop's - GType value. May return NULL if it does not make sense for this - object property. -****************************************************************************/ -static const char *objprop_get_attribute_type_string(const struct objprop *op) -{ - GType gtype; - - if (!op) { - return NULL; - } - - gtype = objprop_get_gtype(op); - if (gtype == G_TYPE_INT || gtype == G_TYPE_STRING - || gtype == G_TYPE_BOOLEAN) { - return "text"; - } else if (gtype == GDK_TYPE_PIXBUF) { - return "pixbuf"; - } - - return NULL; -} - -/************************************************************************//** - Store the column id of the list store that this object property can be - viewed in. This should generally only be changed set once, when the - object property's associated list view is created. - NB: This is the column id in the model, not the view. -****************************************************************************/ -static void objprop_set_column_id(struct objprop *op, int col_id) -{ - if (!op) { - return; - } - op->column_id = col_id; -} - -/************************************************************************//** - Returns the column id in the list store for this object property. - May return -1 if not applicable or if not yet set. - NB: This is the column id in the model, not the view. -****************************************************************************/ -static int objprop_get_column_id(const struct objprop *op) -{ - if (!op) { - return -1; - } - return op->column_id; -} - -/************************************************************************//** - Sets the view column reference for later convenience. -****************************************************************************/ -static void objprop_set_treeview_column(struct objprop *op, - GtkTreeViewColumn *col) -{ - if (!op) { - return; - } - op->view_column = col; -} - -/************************************************************************//** - Returns the previously stored tree view column reference, or NULL if none - exists. -****************************************************************************/ -static GtkTreeViewColumn *objprop_get_treeview_column(const struct objprop *op) -{ - if (!op) { - return NULL; - } - return op->view_column; -} - -/************************************************************************//** - Returns the string name of this object property. -****************************************************************************/ -static const char *objprop_get_name(const struct objprop *op) -{ - if (!op) { - return NULL; - } - return op->name; -} - -/************************************************************************//** - Return a description (translated) of the property. -****************************************************************************/ -static const char *objprop_get_tooltip(const struct objprop *op) -{ - if (!op) { - return NULL; - } - return op->tooltip; -} - -/************************************************************************//** - Create and return a cell renderer corresponding to this object property, - suitable to be used with a tree view. May return NULL if this object - property cannot exist in a list store. -****************************************************************************/ -static GtkCellRenderer *objprop_create_cell_renderer(const struct objprop *op) -{ - GtkCellRenderer *cell = NULL; - GType gtype; - - gtype = objprop_get_gtype(op); - - if (gtype == G_TYPE_INT || gtype == G_TYPE_STRING - || gtype == G_TYPE_BOOLEAN) { - cell = gtk_cell_renderer_text_new(); - } else if (gtype == GDK_TYPE_PIXBUF) { - cell = gtk_cell_renderer_pixbuf_new(); - } - - return cell; -} - -/************************************************************************//** - Return TRUE if the given object property can be sorted (i.e. in the list - view). -****************************************************************************/ -static bool objprop_is_sortable(const struct objprop *op) -{ - GType gtype; - if (!op) { - return FALSE; - } - gtype = objprop_get_gtype(op); - return gtype == G_TYPE_INT || gtype == G_TYPE_STRING; -} - -/************************************************************************//** - Return TRUE if the given object property cannot be edited (i.e. it is - displayed information only). -****************************************************************************/ -static bool objprop_is_readonly(const struct objprop *op) -{ - if (!op) { - return TRUE; - } - return !(op->flags & OPF_EDITABLE); -} - -/************************************************************************//** - Callback for entry widget 'changed' signal. -****************************************************************************/ -static void objprop_widget_entry_changed(GtkEntry *entry, gpointer userdata) -{ - struct objprop *op; - struct property_page *pp; - struct propval value = {{0,}, VALTYPE_STRING, FALSE}; - - op = userdata; - pp = objprop_get_property_page(op); - value.data.v_const_string = gtk_entry_get_text(entry); - - property_page_change_value(pp, op, &value); -} - -/************************************************************************//** - Callback for spin button widget 'value-changed' signal. -****************************************************************************/ -static void objprop_widget_spin_button_changed(GtkSpinButton *spin, - gpointer userdata) -{ - struct objprop *op; - struct property_page *pp; - struct propval value = {{0,}, VALTYPE_INT, FALSE}; - - op = userdata; - pp = objprop_get_property_page(op); - value.data.v_int = gtk_spin_button_get_value_as_int(spin); - - property_page_change_value(pp, op, &value); -} - -/************************************************************************//** - Callback for toggle type button widget 'toggled' signal. -****************************************************************************/ -static void objprop_widget_toggle_button_changed(GtkToggleButton *button, - gpointer userdata) -{ - struct objprop *op; - struct property_page *pp; - struct propval value = {{0,}, VALTYPE_BOOL, FALSE}; - - op = userdata; - pp = objprop_get_property_page(op); - value.data.v_bool = gtk_toggle_button_get_active(button); - - property_page_change_value(pp, op, &value); -} - -/************************************************************************//** - Create the gtk widget used to edit or display this object property. -****************************************************************************/ -static void objprop_setup_widget(struct objprop *op) -{ - GtkWidget *ebox, *hbox, *hbox2, *label, *image, *entry, *spin, *button; - struct extviewer *ev = NULL; - enum object_property_ids propid; - - if (!op) { - return; - } - - if (!objprop_has_widget(op)) { - return; - } - - ebox = gtk_event_box_new(); - op->widget = ebox; - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - - gtk_container_add(GTK_CONTAINER(ebox), hbox); - - label = gtk_label_new(objprop_get_name(op)); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), label); - objprop_set_child_widget(op, "name-label", label); - - propid = objprop_get_id(op); - - switch (propid) { - case OPID_TILE_INDEX: - case OPID_TILE_X: - case OPID_TILE_Y: - case OPID_TILE_NAT_X: - case OPID_TILE_NAT_Y: - case OPID_TILE_CONTINENT: - case OPID_TILE_TERRAIN: - case OPID_TILE_RESOURCE: - case OPID_TILE_XY: - case OPID_STARTPOS_XY: - case OPID_UNIT_ID: - case OPID_UNIT_XY: - case OPID_UNIT_TYPE: - case OPID_CITY_ID: - case OPID_CITY_XY: - case OPID_PLAYER_AGE: -#ifdef FREECIV_DEBUG - case OPID_TILE_ADDRESS: - case OPID_UNIT_ADDRESS: - case OPID_CITY_ADDRESS: - case OPID_PLAYER_ADDRESS: -#endif /* FREECIV_DEBUG */ - label = gtk_label_new(NULL); - gtk_widget_set_hexpand(label, TRUE); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), label); - objprop_set_child_widget(op, "value-label", label); - return; - - case OPID_TILE_IMAGE: - case OPID_STARTPOS_IMAGE: - case OPID_UNIT_IMAGE: - case OPID_CITY_IMAGE: - image = gtk_image_new(); - gtk_widget_set_hexpand(image, TRUE); - gtk_widget_set_halign(image, GTK_ALIGN_START); - gtk_widget_set_valign(image, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), image); - objprop_set_child_widget(op, "image", image); - return; - - case OPID_CITY_NAME: - case OPID_PLAYER_NAME: - case OPID_GAME_SCENARIO_NAME: - case OPID_TILE_LABEL: - entry = gtk_entry_new(); - gtk_widget_set_hexpand(entry, TRUE); - gtk_widget_set_halign(entry, GTK_ALIGN_END); - gtk_entry_set_width_chars(GTK_ENTRY(entry), 8); - g_signal_connect(entry, "changed", - G_CALLBACK(objprop_widget_entry_changed), op); - gtk_container_add(GTK_CONTAINER(hbox), entry); - objprop_set_child_widget(op, "entry", entry); - return; - - case OPID_UNIT_MOVES_LEFT: - case OPID_CITY_SIZE: - case OPID_CITY_HISTORY: - case OPID_CITY_SHIELD_STOCK: - case OPID_PLAYER_SCIENCE: - case OPID_PLAYER_GOLD: - spin = gtk_spin_button_new_with_range(0.0, 100.0, 1.0); - gtk_widget_set_hexpand(spin, TRUE); - gtk_widget_set_halign(spin, GTK_ALIGN_END); - g_signal_connect(spin, "value-changed", - G_CALLBACK(objprop_widget_spin_button_changed), op); - gtk_container_add(GTK_CONTAINER(hbox), spin); - objprop_set_child_widget(op, "spin", spin); - return; - - case OPID_UNIT_FUEL: - case OPID_UNIT_HP: - case OPID_UNIT_VETERAN: - case OPID_CITY_FOOD_STOCK: - hbox2 = gtk_grid_new(); - gtk_widget_set_hexpand(hbox2, TRUE); - gtk_widget_set_halign(hbox2, GTK_ALIGN_END); - gtk_grid_set_column_spacing(GTK_GRID(hbox2), 4); - gtk_container_add(GTK_CONTAINER(hbox), hbox2); - spin = gtk_spin_button_new_with_range(0.0, 100.0, 1.0); - g_signal_connect(spin, "value-changed", - G_CALLBACK(objprop_widget_spin_button_changed), op); - gtk_container_add(GTK_CONTAINER(hbox2), spin); - objprop_set_child_widget(op, "spin", spin); - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox2), label); - objprop_set_child_widget(op, "max-value-label", label); - return; - - case OPID_TILE_SPECIALS: - case OPID_TILE_ROADS: - case OPID_TILE_BASES: - case OPID_TILE_VISION: - case OPID_STARTPOS_NATIONS: - case OPID_CITY_BUILDINGS: - case OPID_PLAYER_NATION: - case OPID_PLAYER_GOV: - case OPID_PLAYER_INVENTIONS: - case OPID_GAME_SCENARIO_AUTHORS: - case OPID_GAME_SCENARIO_DESC: - ev = extviewer_new(op); - objprop_set_extviewer(op, ev); - gtk_widget_set_hexpand(extviewer_get_panel_widget(ev), TRUE); - gtk_widget_set_halign(extviewer_get_panel_widget(ev), GTK_ALIGN_END); - gtk_container_add(GTK_CONTAINER(hbox), extviewer_get_panel_widget(ev)); - property_page_add_extviewer(objprop_get_property_page(op), ev); - return; - - case OPID_STARTPOS_EXCLUDE: - case OPID_UNIT_MOVED: - case OPID_UNIT_DONE_MOVING: - case OPID_UNIT_STAY: - case OPID_GAME_SCENARIO: - case OPID_GAME_SCENARIO_RANDSTATE: - case OPID_GAME_SCENARIO_PLAYERS: - case OPID_GAME_STARTPOS_NATIONS: - case OPID_GAME_PREVENT_CITIES: - case OPID_GAME_LAKE_FLOODING: - case OPID_GAME_RULESET_LOCKED: - case OPID_PLAYER_SCENARIO_RESERVED: - button = gtk_check_button_new(); - gtk_widget_set_hexpand(button, TRUE); - gtk_widget_set_halign(button, GTK_ALIGN_END); - g_signal_connect(button, "toggled", - G_CALLBACK(objprop_widget_toggle_button_changed), op); - gtk_container_add(GTK_CONTAINER(hbox), button); - objprop_set_child_widget(op, "checkbutton", button); - return; - } - - log_error("%s(): Unhandled request to create widget for property %d (%s).", - __FUNCTION__, propid, objprop_get_name(op)); -} - -/************************************************************************//** - Refresh the display/edit widget corresponding to this object property - according to the value of the bound object. If a stored modified value - exists, then check it against the object's current value and remove it - if they are equal. - - If 'ob' is NULL, then clear the widget. -****************************************************************************/ -static void objprop_refresh_widget(struct objprop *op, - struct objbind *ob) -{ - GtkWidget *w, *label, *image, *entry, *spin, *button; - struct extviewer *ev; - struct propval *pv; - bool modified; - enum object_property_ids propid; - double min, max, step, big_step; - char buf[256]; - - if (!op || !objprop_has_widget(op)) { - return; - } - - w = objprop_get_widget(op); - if (!w) { - return; - } - - propid = objprop_get_id(op); - - /* NB: We must take care to propval_free the return value of - * objbind_get_value_from_object, since it always makes a - * copy, but to NOT free the result of objbind_get_modified_value - * since it returns its own stored value. */ - pv = objbind_get_value_from_object(ob, op); - modified = objbind_property_is_modified(ob, op); - - if (pv && modified) { - struct propval *pv_mod; - - pv_mod = objbind_get_modified_value(ob, op); - if (pv_mod) { - if (propval_equal(pv, pv_mod)) { - objbind_clear_modified_value(ob, op); - modified = FALSE; - } else { - propval_free(pv); - pv = pv_mod; - modified = TRUE; - } - } else { - modified = FALSE; - } - } - - switch (propid) { - case OPID_TILE_IMAGE: - case OPID_STARTPOS_IMAGE: - case OPID_UNIT_IMAGE: - case OPID_CITY_IMAGE: - image = objprop_get_child_widget(op, "image"); - if (pv) { - gtk_image_set_from_pixbuf(GTK_IMAGE(image), pv->data.v_pixbuf); - } else { - gtk_image_set_from_pixbuf(GTK_IMAGE(image), NULL); - } - break; - - case OPID_TILE_XY: - case OPID_TILE_TERRAIN: - case OPID_TILE_RESOURCE: - case OPID_STARTPOS_XY: - case OPID_UNIT_XY: - case OPID_UNIT_TYPE: - case OPID_CITY_XY: -#ifdef FREECIV_DEBUG - case OPID_TILE_ADDRESS: - case OPID_UNIT_ADDRESS: - case OPID_CITY_ADDRESS: - case OPID_PLAYER_ADDRESS: -#endif /* FREECIV_DEBUG */ - label = objprop_get_child_widget(op, "value-label"); - if (pv) { - gtk_label_set_text(GTK_LABEL(label), pv->data.v_string); - } else { - gtk_label_set_text(GTK_LABEL(label), NULL); - } - break; - - case OPID_TILE_INDEX: - case OPID_TILE_X: - case OPID_TILE_Y: - case OPID_TILE_NAT_X: - case OPID_TILE_NAT_Y: - case OPID_TILE_CONTINENT: - case OPID_UNIT_ID: - case OPID_CITY_ID: - case OPID_PLAYER_AGE: - label = objprop_get_child_widget(op, "value-label"); - if (pv) { - char agebuf[16]; - - fc_snprintf(agebuf, sizeof(agebuf), "%d", pv->data.v_int); - gtk_label_set_text(GTK_LABEL(label), agebuf); - } else { - gtk_label_set_text(GTK_LABEL(label), NULL); - } - break; - - case OPID_CITY_NAME: - case OPID_PLAYER_NAME: - case OPID_GAME_SCENARIO_NAME: - case OPID_TILE_LABEL: - entry = objprop_get_child_widget(op, "entry"); - if (pv) { - gtk_entry_set_text(GTK_ENTRY(entry), pv->data.v_string); - } else { - gtk_entry_set_text(GTK_ENTRY(entry), ""); - } - gtk_widget_set_sensitive(entry, pv != NULL); - break; - - case OPID_UNIT_MOVES_LEFT: - case OPID_CITY_SIZE: - case OPID_CITY_HISTORY: - case OPID_CITY_SHIELD_STOCK: - case OPID_PLAYER_SCIENCE: - case OPID_PLAYER_GOLD: - spin = objprop_get_child_widget(op, "spin"); - if (pv) { - disable_gobject_callback(G_OBJECT(spin), - G_CALLBACK(objprop_widget_spin_button_changed)); - if (objbind_get_allowed_value_span(ob, op, &min, &max, - &step, &big_step)) { - gtk_spin_button_set_range(GTK_SPIN_BUTTON(spin), min, max); - gtk_spin_button_set_increments(GTK_SPIN_BUTTON(spin), - step, big_step); - } - gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), pv->data.v_int); - enable_gobject_callback(G_OBJECT(spin), - G_CALLBACK(objprop_widget_spin_button_changed)); - } - gtk_widget_set_sensitive(spin, pv != NULL); - break; - - case OPID_UNIT_FUEL: - case OPID_UNIT_HP: - case OPID_UNIT_VETERAN: - case OPID_CITY_FOOD_STOCK: - spin = objprop_get_child_widget(op, "spin"); - label = objprop_get_child_widget(op, "max-value-label"); - if (pv) { - disable_gobject_callback(G_OBJECT(spin), - G_CALLBACK(objprop_widget_spin_button_changed)); - if (objbind_get_allowed_value_span(ob, op, &min, &max, - &step, &big_step)) { - gtk_spin_button_set_range(GTK_SPIN_BUTTON(spin), min, max); - gtk_spin_button_set_increments(GTK_SPIN_BUTTON(spin), - step, big_step); - fc_snprintf(buf, sizeof(buf), "/%d", (int) max); - gtk_label_set_text(GTK_LABEL(label), buf); - } else { - gtk_label_set_text(GTK_LABEL(label), NULL); - } - gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), pv->data.v_int); - enable_gobject_callback(G_OBJECT(spin), - G_CALLBACK(objprop_widget_spin_button_changed)); - } else { - gtk_label_set_text(GTK_LABEL(label), NULL); - } - gtk_widget_set_sensitive(spin, pv != NULL); - break; - - case OPID_TILE_SPECIALS: - case OPID_TILE_ROADS: - case OPID_TILE_BASES: - case OPID_TILE_VISION: - case OPID_STARTPOS_NATIONS: - case OPID_CITY_BUILDINGS: - case OPID_PLAYER_NATION: - case OPID_PLAYER_GOV: - case OPID_PLAYER_INVENTIONS: - case OPID_GAME_SCENARIO_AUTHORS: - case OPID_GAME_SCENARIO_DESC: - ev = objprop_get_extviewer(op); - if (pv) { - extviewer_refresh_widgets(ev, pv); - } else { - extviewer_clear_widgets(ev); - } - break; - - case OPID_STARTPOS_EXCLUDE: - case OPID_UNIT_MOVED: - case OPID_UNIT_DONE_MOVING: - case OPID_UNIT_STAY: - case OPID_GAME_SCENARIO: - case OPID_GAME_SCENARIO_RANDSTATE: - case OPID_GAME_SCENARIO_PLAYERS: - case OPID_GAME_STARTPOS_NATIONS: - case OPID_GAME_PREVENT_CITIES: - case OPID_GAME_LAKE_FLOODING: - case OPID_GAME_RULESET_LOCKED: - case OPID_PLAYER_SCENARIO_RESERVED: - button = objprop_get_child_widget(op, "checkbutton"); - disable_gobject_callback(G_OBJECT(button), - G_CALLBACK(objprop_widget_toggle_button_changed)); - if (pv) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), - pv->data.v_bool); - } else { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); - } - enable_gobject_callback(G_OBJECT(button), - G_CALLBACK(objprop_widget_toggle_button_changed)); - gtk_widget_set_sensitive(button, pv != NULL); - break; - } - - if (!modified) { - propval_free(pv); - } - - label = objprop_get_child_widget(op, "name-label"); - if (label) { - const char *name = objprop_get_name(op); - if (modified) { - char namebuf[128]; - - fc_snprintf(namebuf, sizeof(namebuf), - "%s", name); - gtk_label_set_markup(GTK_LABEL(label), namebuf); - } else { - gtk_label_set_text(GTK_LABEL(label), name); - } - } -} - -/************************************************************************//** - Returns the gtk widget used to display or edit this property, or NULL - if not applicable. -****************************************************************************/ -static GtkWidget *objprop_get_widget(struct objprop *op) -{ - if (!op) { - return NULL; - } - if (!op->widget) { - objprop_setup_widget(op); - } - return op->widget; -} - -/************************************************************************//** - Stores the given widget under the given name. This function will have no - effect if objprop_get_widget does not return a valid GtkWidget instance. -****************************************************************************/ -static void objprop_set_child_widget(struct objprop *op, - const char *widget_name, - GtkWidget *widget) -{ - GtkWidget *w; - - if (!op || !widget_name || !widget) { - return; - } - - w = objprop_get_widget(op); - if (!w) { - log_error("Cannot store child widget %p under name " - "\"%s\" using objprop_set_child_widget for object " - "property %d (%s) because objprop_get_widget does " - "not return a valid widget.", - widget, widget_name, objprop_get_id(op), objprop_get_name(op)); - return; - } - - g_object_set_data(G_OBJECT(w), widget_name, widget); -} - -/************************************************************************//** - Retrieves the widget stored under the given name, or returns NULL and - logs an error message if not found. -****************************************************************************/ -static GtkWidget *objprop_get_child_widget(struct objprop *op, - const char *widget_name) -{ - GtkWidget *w, *child; - - if (!op || !widget_name) { - return NULL; - } - - w = objprop_get_widget(op); - if (!w) { - log_error("Cannot retrieve child widget under name " - "\"%s\" using objprop_get_child_widget for object " - "property %d (%s) because objprop_get_widget does " - "not return a valid widget.", - widget_name, objprop_get_id(op), objprop_get_name(op)); - return NULL; - } - - child = g_object_get_data(G_OBJECT(w), widget_name); - if (!child) { - log_error("Child widget \"%s\" not found for object " - "property %d (%s) via objprop_get_child_widget.", - widget_name, objprop_get_id(op), objprop_get_name(op)); - return NULL; - } - - return child; -} - -/************************************************************************//** - Store the extviewer struct for later retrieval. -****************************************************************************/ -static void objprop_set_extviewer(struct objprop *op, - struct extviewer *ev) -{ - if (!op) { - return; - } - op->extviewer = ev; -} - -/************************************************************************//** - Return the stored extviewer, or NULL if none exists. -****************************************************************************/ -static struct extviewer *objprop_get_extviewer(struct objprop *op) -{ - if (!op) { - return NULL; - } - return op->extviewer; -} - -/************************************************************************//** - Get the property page in which this property is installed. -****************************************************************************/ -static struct property_page *objprop_get_property_page(const struct objprop *op) -{ - if (!op) { - return NULL; - } - return op->parent_page; -} - -/************************************************************************//** - Create a new object property. -****************************************************************************/ -static struct objprop *objprop_new(int id, - const char *name, - const char *tooltip, - enum object_property_flags flags, - enum value_types valtype, - struct property_page *parent) -{ - struct objprop *op; - - op = fc_calloc(1, sizeof(*op)); - op->id = id; - op->name = name; - op->tooltip = tooltip; - op->flags = flags; - op->valtype = valtype; - op->column_id = -1; - op->parent_page = parent; - - return op; -} - -/************************************************************************//** - Create "extended property viewer". This is used for viewing/editing - complex property values (e.g. arrays, bitvectors, etc.). -****************************************************************************/ -static struct extviewer *extviewer_new(struct objprop *op) -{ - struct extviewer *ev; - GtkWidget *hbox, *vbox, *label, *button, *scrollwin, *image; - GtkWidget *view = NULL; - GtkTreeSelection *sel; - GtkListStore *store = NULL; - GtkTextBuffer *textbuf = NULL; - GType *gtypes; - enum object_property_ids propid; - int num_cols; - - if (!op) { - return NULL; - } - - ev = fc_calloc(1, sizeof(*ev)); - ev->objprop = op; - - propid = objprop_get_id(op); - - - /* Create the panel widget. */ - - switch (propid) { - case OPID_TILE_SPECIALS: - case OPID_TILE_ROADS: - case OPID_TILE_BASES: - case OPID_STARTPOS_NATIONS: - case OPID_CITY_BUILDINGS: - case OPID_PLAYER_INVENTIONS: - case OPID_GAME_SCENARIO_AUTHORS: - case OPID_GAME_SCENARIO_DESC: - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - ev->panel_widget = hbox; - - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), label); - ev->panel_label = label; - break; - - case OPID_PLAYER_NATION: - case OPID_PLAYER_GOV: - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 4); - ev->panel_widget = vbox; - - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(vbox), label); - ev->panel_label = label; - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - image = gtk_image_new(); - gtk_widget_set_halign(image, GTK_ALIGN_START); - gtk_widget_set_valign(image, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), image); - ev->panel_image = image; - break; - - case OPID_TILE_VISION: - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - ev->panel_widget = hbox; - break; - - default: - log_error("Unhandled request to create panel widget " - "for property %d (%s) in extviewer_new().", - propid, objprop_get_name(op)); - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - ev->panel_widget = hbox; - break; - } - - if (objprop_is_readonly(op)) { - button = gtk_button_new_with_label(Q_("?verb:View")); - } else { - button = gtk_button_new_with_label(_("Edit")); - } - g_signal_connect(button, "clicked", - G_CALLBACK(extviewer_panel_button_clicked), ev); - gtk_container_add(GTK_CONTAINER(hbox), button); - ev->panel_button = button; - - - /* Create the data store. */ - - switch (propid) { - case OPID_TILE_SPECIALS: - case OPID_TILE_ROADS: - case OPID_TILE_BASES: - case OPID_PLAYER_INVENTIONS: - store = gtk_list_store_new(3, G_TYPE_BOOLEAN, G_TYPE_INT, - G_TYPE_STRING); - break; - case OPID_TILE_VISION: - num_cols = 3 + 1 + V_COUNT; - gtypes = fc_malloc(num_cols * sizeof(GType)); - gtypes[0] = G_TYPE_INT; /* player number */ - gtypes[1] = GDK_TYPE_PIXBUF; /* player flag */ - gtypes[2] = G_TYPE_STRING; /* player name */ - gtypes[3] = G_TYPE_BOOLEAN; /* tile_known */ - vision_layer_iterate(v) { - gtypes[4 + v] = G_TYPE_BOOLEAN; /* tile_seen[v] */ - } vision_layer_iterate_end; - store = gtk_list_store_newv(num_cols, gtypes); - free(gtypes); - break; - case OPID_CITY_BUILDINGS: - store = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_INT, - G_TYPE_STRING, G_TYPE_STRING); - break; - case OPID_STARTPOS_NATIONS: - case OPID_PLAYER_NATION: - case OPID_PLAYER_GOV: - store = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_INT, - GDK_TYPE_PIXBUF, G_TYPE_STRING); - break; - case OPID_GAME_SCENARIO_AUTHORS: - case OPID_GAME_SCENARIO_DESC: - textbuf = gtk_text_buffer_new(NULL); - break; - default: - log_error("Unhandled request to create data store " - "for property %d (%s) in extviewer_new().", - propid, objprop_get_name(op)); - break; - } - - ev->store = store; - ev->textbuf = textbuf; - - /* Create the view widget. */ - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 4); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); - ev->view_widget = vbox; - - label = gtk_label_new(objprop_get_name(op)); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(vbox), label); - ev->view_label = label; - - if (store || textbuf) { - scrollwin = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(vbox), scrollwin); - - if (store) { - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE); - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); - } else { - const bool editable = !objprop_is_readonly(op); - view = gtk_text_view_new_with_buffer(textbuf); - gtk_text_view_set_editable(GTK_TEXT_VIEW(view), editable); - gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), editable); - } - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - - gtk_container_add(GTK_CONTAINER(scrollwin), view); - } - - switch (propid) { - - case OPID_TILE_SPECIALS: - case OPID_TILE_ROADS: - case OPID_TILE_BASES: - /* TRANS: As in "this tile special is present". */ - add_column(view, 0, _("Present"), G_TYPE_BOOLEAN, TRUE, FALSE, - G_CALLBACK(extviewer_view_cell_toggled), ev); - add_column(view, 1, _("ID"), G_TYPE_INT, - FALSE, FALSE, NULL, NULL); - add_column(view, 2, _("Name"), G_TYPE_STRING, - FALSE, FALSE, NULL, NULL); - break; - - case OPID_TILE_VISION: - add_column(view, 0, _("ID"), G_TYPE_INT, - FALSE, FALSE, NULL, NULL); - add_column(view, 1, _("Nation"), GDK_TYPE_PIXBUF, - FALSE, FALSE, NULL, NULL); - add_column(view, 2, _("Name"), G_TYPE_STRING, - FALSE, FALSE, NULL, NULL); - add_column(view, 3, _("Known"), G_TYPE_BOOLEAN, - FALSE, FALSE, NULL, NULL); - vision_layer_iterate(v) { - add_column(view, 4 + v, vision_layer_get_name(v), - G_TYPE_BOOLEAN, FALSE, FALSE, NULL, NULL); - } vision_layer_iterate_end; - break; - - case OPID_CITY_BUILDINGS: - /* TRANS: As in "this building is present". */ - add_column(view, 0, _("Present"), G_TYPE_BOOLEAN, TRUE, FALSE, - G_CALLBACK(extviewer_view_cell_toggled), ev); - add_column(view, 1, _("ID"), G_TYPE_INT, - FALSE, FALSE, NULL, NULL); - add_column(view, 2, _("Name"), G_TYPE_STRING, - FALSE, FALSE, NULL, NULL); - /* TRANS: As in "the turn when this building was built". */ - add_column(view, 3, _("Turn Built"), G_TYPE_STRING, - FALSE, FALSE, NULL, NULL); - break; - - case OPID_STARTPOS_NATIONS: - /* TRANS: As in "the player has set this nation". */ - add_column(view, 0, _("Set"), G_TYPE_BOOLEAN, TRUE, FALSE, - G_CALLBACK(extviewer_view_cell_toggled), ev); - add_column(view, 1, _("ID"), G_TYPE_INT, - FALSE, FALSE, NULL, NULL); - add_column(view, 2, _("Flag"), GDK_TYPE_PIXBUF, - FALSE, FALSE, NULL, NULL); - add_column(view, 3, _("Name"), G_TYPE_STRING, - FALSE, FALSE, NULL, NULL); - break; - - case OPID_PLAYER_NATION: - case OPID_PLAYER_GOV: - /* TRANS: As in "the player has set this nation". */ - add_column(view, 0, _("Set"), G_TYPE_BOOLEAN, TRUE, TRUE, - G_CALLBACK(extviewer_view_cell_toggled), ev); - add_column(view, 1, _("ID"), G_TYPE_INT, - FALSE, FALSE, NULL, NULL); - add_column(view, 2, - propid == OPID_PLAYER_GOV ? _("Icon") : _("Flag"), - GDK_TYPE_PIXBUF, - FALSE, FALSE, NULL, NULL); - add_column(view, 3, _("Name"), G_TYPE_STRING, - FALSE, FALSE, NULL, NULL); - break; - - case OPID_PLAYER_INVENTIONS: - /* TRANS: As in "this invention is known". */ - add_column(view, 0, _("Known"), G_TYPE_BOOLEAN, TRUE, FALSE, - G_CALLBACK(extviewer_view_cell_toggled), ev); - add_column(view, 1, _("ID"), G_TYPE_INT, - FALSE, FALSE, NULL, NULL); - add_column(view, 2, _("Name"), G_TYPE_STRING, - FALSE, FALSE, NULL, NULL); - break; - - case OPID_GAME_SCENARIO_AUTHORS: - case OPID_GAME_SCENARIO_DESC: - g_signal_connect(textbuf, "changed", - G_CALLBACK(extviewer_textbuf_changed), ev); - break; - - default: - log_error("Unhandled request to configure view widget " - "for property %d (%s) in extviewer_new().", - propid, objprop_get_name(op)); - break; - } - - gtk_widget_show_all(ev->panel_widget); - gtk_widget_show_all(ev->view_widget); - - return ev; -} - -/************************************************************************//** - Returns the object property that is displayed by this extviewer. -****************************************************************************/ -static struct objprop *extviewer_get_objprop(struct extviewer *ev) -{ - if (!ev) { - return NULL; - } - return ev->objprop; -} - -/************************************************************************//** - Returns the "panel widget" for this extviewer, i.e. the widget the - is to be placed into the properties panel. -****************************************************************************/ -static GtkWidget *extviewer_get_panel_widget(struct extviewer *ev) -{ - if (!ev) { - return NULL; - } - return ev->panel_widget; -} - -/************************************************************************//** - Returns the "view widget" for this extviewer, i.e. the widget the - is to be used for viewing/editing a complex property. -****************************************************************************/ -static GtkWidget *extviewer_get_view_widget(struct extviewer *ev) -{ - if (!ev) { - return NULL; - } - return ev->view_widget; -} - -/************************************************************************//** - Set the widgets in the extended property viewer to display the given value. -****************************************************************************/ -static void extviewer_refresh_widgets(struct extviewer *ev, - struct propval *pv) -{ - struct objprop *op; - enum object_property_ids propid; - int id, turn_built; - bool present, all; - const char *name; - GdkPixbuf *pixbuf; - GtkListStore *store; - GtkTextBuffer *textbuf; - GtkTreeIter iter; - gchar *buf; - - if (!ev) { - return; - } - - op = extviewer_get_objprop(ev); - propid = objprop_get_id(op); - - if (propval_equal(pv, ev->pv_cached)) { - return; - } - propval_free(ev->pv_cached); - ev->pv_cached = propval_copy(pv); - store = ev->store; - textbuf = ev->textbuf; - - - /* NB: Remember to have -1 as the last argument to - * gtk_list_store_set() and to use the correct column - * number when inserting data. :) */ - switch (propid) { - - case OPID_TILE_SPECIALS: - gtk_list_store_clear(store); - extra_type_by_cause_iterate(EC_SPECIAL, spe) { - id = spe->data.special_idx; - name = extra_name_translation(spe); - present = BV_ISSET(pv->data.v_bv_special, id); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, present, 1, id, 2, name, -1); - } extra_type_by_cause_iterate_end; - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_TILE_ROADS: - gtk_list_store_clear(store); - extra_type_by_cause_iterate(EC_ROAD, pextra) { - struct road_type *proad = extra_road_get(pextra); - - id = road_number(proad); - name = extra_name_translation(pextra); - present = BV_ISSET(pv->data.v_bv_roads, id); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, present, 1, id, 2, name, -1); - } extra_type_by_cause_iterate_end; - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_TILE_BASES: - gtk_list_store_clear(store); - extra_type_by_cause_iterate(EC_BASE, pextra) { - struct base_type *pbase = extra_base_get(pextra); - - id = base_number(pbase); - name = extra_name_translation(pextra); - present = BV_ISSET(pv->data.v_bv_bases, id); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, present, 1, id, 2, name, -1); - } extra_type_by_cause_iterate_end; - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_TILE_VISION: - gtk_list_store_clear(store); - player_slots_iterate(pslot) { - id = player_slot_index(pslot); - if (player_slot_is_used(pslot)) { - struct player *pplayer = player_slot_get_player(pslot); - name = player_name(pplayer); - pixbuf = get_flag(pplayer->nation); - } else { - name = ""; - pixbuf = NULL; - } - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, id, 2, name, -1); - if (pixbuf) { - gtk_list_store_set(store, &iter, 1, pixbuf, -1); - g_object_unref(pixbuf); - pixbuf = NULL; - } - present = BV_ISSET(pv->data.v_tile_vision->tile_known, id); - gtk_list_store_set(store, &iter, 3, present, -1); - vision_layer_iterate(v) { - present = BV_ISSET(pv->data.v_tile_vision->tile_seen[v], id); - gtk_list_store_set(store, &iter, 4 + v, present, -1); - } vision_layer_iterate_end; - } player_slots_iterate_end; - break; - - case OPID_STARTPOS_NATIONS: - gtk_list_store_clear(store); - gtk_list_store_append(store, &iter); - all = (0 == nation_hash_size(pv->data.v_nation_hash)); - gtk_list_store_set(store, &iter, 0, all, 1, -1, 3, - _("All nations"), -1); - nations_iterate(pnation) { - if (client_nation_is_in_current_set(pnation) - && is_nation_playable(pnation)) { - present = (!all && nation_hash_lookup(pv->data.v_nation_hash, - pnation, NULL)); - id = nation_number(pnation); - pixbuf = get_flag(pnation); - name = nation_adjective_translation(pnation); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, present, 1, id, - 2, pixbuf, 3, name, -1); - if (pixbuf) { - g_object_unref(pixbuf); - } - } - } nations_iterate_end; - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_CITY_BUILDINGS: - gtk_list_store_clear(store); - improvement_iterate(pimprove) { - if (is_special_improvement(pimprove)) { - continue; - } - id = improvement_index(pimprove); - name = improvement_name_translation(pimprove); - turn_built = pv->data.v_built[id].turn; - present = turn_built >= 0; - buf = built_status_to_string(&pv->data.v_built[id]); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, present, 1, id, 2, name, - 3, buf, -1); - g_free(buf); - } improvement_iterate_end; - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_PLAYER_NATION: - { - enum barbarian_type barbarian_type = - nation_barbarian_type(pv->data.v_nation); - - gtk_list_store_clear(store); - nations_iterate(pnation) { - if (client_nation_is_in_current_set(pnation) - && nation_barbarian_type(pnation) == barbarian_type - && (barbarian_type != NOT_A_BARBARIAN - || is_nation_playable(pnation))) { - present = (pnation == pv->data.v_nation); - id = nation_index(pnation); - pixbuf = get_flag(pnation); - name = nation_adjective_translation(pnation); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, present, 1, id, - 2, pixbuf, 3, name, -1); - if (pixbuf) { - g_object_unref(pixbuf); - } - } - } nations_iterate_end; - gtk_label_set_text(GTK_LABEL(ev->panel_label), - nation_adjective_translation(pv->data.v_nation)); - pixbuf = get_flag(pv->data.v_nation); - gtk_image_set_from_pixbuf(GTK_IMAGE(ev->panel_image), pixbuf); - if (pixbuf) { - g_object_unref(pixbuf); - } - } - break; - - case OPID_PLAYER_GOV: - { - gtk_list_store_clear(store); - governments_iterate(pgov) { - present = (pgov == pv->data.v_gov); - id = government_index(pgov); - pixbuf = sprite_get_pixbuf(get_government_sprite(tileset, pgov)); - name = government_name_translation(pgov); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, present, 1, id, - 2, pixbuf, 3, name, -1); - if (pixbuf) { - g_object_unref(pixbuf); - } - } governments_iterate_end; - gtk_label_set_text(GTK_LABEL(ev->panel_label), - government_name_translation(pv->data.v_gov)); - pixbuf = sprite_get_pixbuf(get_government_sprite(tileset, pv->data.v_gov)); - gtk_image_set_from_pixbuf(GTK_IMAGE(ev->panel_image), pixbuf); - if (pixbuf) { - g_object_unref(pixbuf); - } - } - break; - - case OPID_PLAYER_INVENTIONS: - gtk_list_store_clear(store); - advance_iterate(A_FIRST, padvance) { - id = advance_index(padvance); - present = BV_ISSET(pv->data.v_bv_inventions, id); - name = advance_name_translation(padvance); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, present, 1, id, 2, name, -1); - } advance_iterate_end; - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_GAME_SCENARIO_AUTHORS: - case OPID_GAME_SCENARIO_DESC: - disable_gobject_callback(G_OBJECT(ev->textbuf), - G_CALLBACK(extviewer_textbuf_changed)); - { - GtkTextIter start, end; - char *oldtext; - - /* Don't re-set content if unchanged, to avoid moving cursor */ - gtk_text_buffer_get_bounds(textbuf, &start, &end); - oldtext = gtk_text_buffer_get_text(textbuf, &start, &end, TRUE); - if (strcmp(oldtext, pv->data.v_const_string) != 0) { - gtk_text_buffer_set_text(textbuf, pv->data.v_const_string, -1); - } - } - enable_gobject_callback(G_OBJECT(ev->textbuf), - G_CALLBACK(extviewer_textbuf_changed)); - gtk_widget_set_sensitive(ev->view_widget, TRUE); - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - default: - log_error("Unhandled request to refresh widgets " - "extviewer_refresh_widgets() for objprop id=%d " - "name \"%s\".", propid, objprop_get_name(op)); - break; - } -} - -/************************************************************************//** - Clear the display widgets. -****************************************************************************/ -static void extviewer_clear_widgets(struct extviewer *ev) -{ - struct objprop *op; - enum object_property_ids propid; - - if (!ev) { - return; - } - - op = extviewer_get_objprop(ev); - propid = objprop_get_id(op); - - propval_free(ev->pv_cached); - ev->pv_cached = NULL; - - if (ev->panel_label != NULL) { - gtk_label_set_text(GTK_LABEL(ev->panel_label), NULL); - } - - switch (propid) { - case OPID_TILE_SPECIALS: - case OPID_TILE_ROADS: - case OPID_TILE_BASES: - case OPID_TILE_VISION: - case OPID_STARTPOS_NATIONS: - case OPID_CITY_BUILDINGS: - case OPID_PLAYER_INVENTIONS: - gtk_list_store_clear(ev->store); - break; - case OPID_PLAYER_NATION: - case OPID_PLAYER_GOV: - gtk_list_store_clear(ev->store); - gtk_image_set_from_pixbuf(GTK_IMAGE(ev->panel_image), NULL); - break; - case OPID_GAME_SCENARIO_AUTHORS: - case OPID_GAME_SCENARIO_DESC: - disable_gobject_callback(G_OBJECT(ev->textbuf), - G_CALLBACK(extviewer_textbuf_changed)); - gtk_text_buffer_set_text(ev->textbuf, "", -1); - enable_gobject_callback(G_OBJECT(ev->textbuf), - G_CALLBACK(extviewer_textbuf_changed)); - gtk_widget_set_sensitive(ev->view_widget, FALSE); - break; - default: - log_error("Unhandled request to clear widgets " - "in extviewer_clear_widgets() for objprop id=%d " - "name \"%s\".", propid, objprop_get_name(op)); - break; - } -} - -/************************************************************************//** - Handle the extviewer's panel button click. This should set the property - page to display the view widget for this complex property. -****************************************************************************/ -static void extviewer_panel_button_clicked(GtkButton *button, - gpointer userdata) -{ - struct extviewer *ev; - struct property_page *pp; - struct objprop *op; - - ev = userdata; - if (!ev) { - return; - } - - op = extviewer_get_objprop(ev); - pp = objprop_get_property_page(op); - property_page_show_extviewer(pp, ev); -} - -/************************************************************************//** - Handle toggling of a boolean cell value in the extviewer's tree view. -****************************************************************************/ -static void extviewer_view_cell_toggled(GtkCellRendererToggle *cell, - gchar *path, - gpointer userdata) -{ - struct extviewer *ev; - struct objprop *op; - struct property_page *pp; - enum object_property_ids propid; - GtkTreeModel *model; - GtkTreeIter iter; - int id, old_id, turn_built; - struct propval *pv; - bool active, present; - gchar *buf; - GdkPixbuf *pixbuf = NULL; - - ev = userdata; - if (!ev) { - return; - } - - pv = ev->pv_cached; - if (!pv) { - return; - } - - op = extviewer_get_objprop(ev); - propid = objprop_get_id(op); - active = gtk_cell_renderer_toggle_get_active(cell); - pp = objprop_get_property_page(op); - - model = GTK_TREE_MODEL(ev->store); - if (!gtk_tree_model_get_iter_from_string(model, &iter, path)) { - return; - } - present = !active; - - - switch (propid) { - - case OPID_TILE_SPECIALS: - gtk_tree_model_get(model, &iter, 1, &id, -1); - if (id < 0 || id >= extra_type_list_size(extra_type_list_by_cause(EC_SPECIAL))) { - return; - } - if (present) { - BV_SET(pv->data.v_bv_special, id); - } else { - BV_CLR(pv->data.v_bv_special, id); - } - gtk_list_store_set(ev->store, &iter, 0, present, -1); - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_TILE_ROADS: - gtk_tree_model_get(model, &iter, 1, &id, -1); - if (!(0 <= id && id < road_count())) { - return; - } - if (present) { - BV_SET(pv->data.v_bv_roads, id); - } else { - BV_CLR(pv->data.v_bv_roads, id); - } - gtk_list_store_set(ev->store, &iter, 0, present, -1); - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_TILE_BASES: - gtk_tree_model_get(model, &iter, 1, &id, -1); - if (!(0 <= id && id < base_count())) { - return; - } - if (present) { - BV_SET(pv->data.v_bv_bases, id); - } else { - BV_CLR(pv->data.v_bv_bases, id); - } - gtk_list_store_set(ev->store, &iter, 0, present, -1); - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_STARTPOS_NATIONS: - gtk_tree_model_get(model, &iter, 1, &id, -1); - if (-1 > id && id >= nation_count()) { - return; - } - - if (-1 == id) { - gtk_list_store_set(ev->store, &iter, 0, present, -1); - gtk_tree_model_get_iter_first(model, &iter); - if (present) { - while (gtk_tree_model_iter_next(model, &iter)) { - gtk_list_store_set(ev->store, &iter, 0, FALSE, -1); - } - nation_hash_clear(pv->data.v_nation_hash); - } else { - const struct nation_type *pnation; - int id2; - - gtk_tree_model_iter_next(model, &iter); - gtk_tree_model_get(model, &iter, 0, &id2, -1); - gtk_list_store_set(ev->store, &iter, 0, TRUE, -1); - pnation = nation_by_number(id2); - nation_hash_insert(pv->data.v_nation_hash, pnation, NULL); - } - } else { - const struct nation_type *pnation; - bool all; - - gtk_list_store_set(ev->store, &iter, 0, present, -1); - pnation = nation_by_number(id); - if (present) { - nation_hash_insert(pv->data.v_nation_hash, pnation, NULL); - } else { - nation_hash_remove(pv->data.v_nation_hash, pnation); - } - gtk_tree_model_get_iter_first(model, &iter); - all = (0 == nation_hash_size(pv->data.v_nation_hash)); - gtk_list_store_set(ev->store, &iter, 0, all, -1); - } - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_CITY_BUILDINGS: - gtk_tree_model_get(model, &iter, 1, &id, -1); - if (!(0 <= id && id < B_LAST)) { - return; - } - turn_built = present ? game.info.turn : I_NEVER; - pv->data.v_built[id].turn = turn_built; - buf = built_status_to_string(&pv->data.v_built[id]); - gtk_list_store_set(ev->store, &iter, 0, present, 3, buf, -1); - g_free(buf); - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - case OPID_PLAYER_NATION: - gtk_tree_model_get(model, &iter, 1, &id, -1); - if (!(0 <= id && id < nation_count()) || !present) { - return; - } - old_id = nation_index(pv->data.v_nation); - pv->data.v_nation = nation_by_number(id); - gtk_list_store_set(ev->store, &iter, 0, TRUE, -1); - gtk_tree_model_iter_nth_child(model, &iter, NULL, old_id); - gtk_list_store_set(ev->store, &iter, 0, FALSE, -1); - gtk_label_set_text(GTK_LABEL(ev->panel_label), - nation_adjective_translation(pv->data.v_nation)); - pixbuf = get_flag(pv->data.v_nation); - gtk_image_set_from_pixbuf(GTK_IMAGE(ev->panel_image), pixbuf); - if (pixbuf) { - g_object_unref(pixbuf); - } - break; - - case OPID_PLAYER_GOV: - gtk_tree_model_get(model, &iter, 1, &id, -1); - if (!(0 <= id && id < government_count()) || !present) { - return; - } - old_id = government_index(pv->data.v_gov); - pv->data.v_gov = government_by_number(id); - gtk_list_store_set(ev->store, &iter, 0, TRUE, -1); - gtk_tree_model_iter_nth_child(model, &iter, NULL, old_id); - gtk_list_store_set(ev->store, &iter, 0, FALSE, -1); - gtk_label_set_text(GTK_LABEL(ev->panel_label), - government_name_translation(pv->data.v_gov)); - pixbuf = sprite_get_pixbuf(get_government_sprite(tileset, pv->data.v_gov)); - gtk_image_set_from_pixbuf(GTK_IMAGE(ev->panel_image), pixbuf); - if (pixbuf) { - g_object_unref(pixbuf); - } - break; - - case OPID_PLAYER_INVENTIONS: - gtk_tree_model_get(model, &iter, 1, &id, -1); - if (!(A_FIRST <= id && id < advance_count())) { - return; - } - if (present) { - BV_SET(pv->data.v_bv_inventions, id); - } else { - BV_CLR(pv->data.v_bv_inventions, id); - } - gtk_list_store_set(ev->store, &iter, 0, present, -1); - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - - default: - log_error("Unhandled widget toggled signal in " - "extviewer_view_cell_toggled() for objprop id=%d " - "name \"%s\".", propid, objprop_get_name(op)); - return; - break; - } - - property_page_change_value(pp, op, pv); -} - -/************************************************************************//** - Handle a change in the extviewer's text buffer. -****************************************************************************/ -static void extviewer_textbuf_changed(GtkTextBuffer *textbuf, - gpointer userdata) -{ - struct extviewer *ev; - struct objprop *op; - struct property_page *pp; - enum object_property_ids propid; - struct propval value = {{0,}, VALTYPE_STRING, FALSE}, *pv; - GtkTextIter start, end; - char *text; - gchar *buf; - - ev = userdata; - if (!ev) { - return; - } - - op = extviewer_get_objprop(ev); - propid = objprop_get_id(op); - pp = objprop_get_property_page(op); - - gtk_text_buffer_get_start_iter(textbuf, &start); - gtk_text_buffer_get_end_iter(textbuf, &end); - text = gtk_text_buffer_get_text(textbuf, &start, &end, FALSE); - value.data.v_const_string = text; - pv = &value; - - switch (propid) { - case OPID_GAME_SCENARIO_AUTHORS: - case OPID_GAME_SCENARIO_DESC: - buf = propval_as_string(pv); - gtk_label_set_text(GTK_LABEL(ev->panel_label), buf); - g_free(buf); - break; - default: - log_error("Unhandled widget modified signal in " - "extviewer_textbuf_changed() for objprop id=%d " - "name \"%s\".", propid, objprop_get_name(op)); - return; - break; - } - - property_page_change_value(pp, op, pv); - g_free(text); -} - -/************************************************************************//** - Install all object properties that this page type can view/edit. -****************************************************************************/ -static void property_page_setup_objprops(struct property_page *pp) -{ -#define ADDPROP(ARG_id, ARG_name, ARG_tooltip, ARG_flags, ARG_valtype) do { \ - struct objprop *MY_op = objprop_new(ARG_id, ARG_name, ARG_tooltip, \ - ARG_flags, ARG_valtype, pp); \ - objprop_hash_insert(pp->objprop_table, MY_op->id, MY_op); \ -} while (FALSE) - - switch (property_page_get_objtype(pp)) { - case OBJTYPE_TILE: - ADDPROP(OPID_TILE_IMAGE, _("Image"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_PIXBUF); - ADDPROP(OPID_TILE_TERRAIN, _("Terrain"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_STRING); - ADDPROP(OPID_TILE_RESOURCE, _("Resource"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_STRING); - ADDPROP(OPID_TILE_INDEX, _("Index"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_INT); - ADDPROP(OPID_TILE_X, Q_("?coordinate:X"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_INT); - ADDPROP(OPID_TILE_Y, Q_("?coordinate:Y"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_INT); - /* TRANS: The coordinate X in native coordinates. - * The freeciv coordinate system is described in doc/HACKING. */ - ADDPROP(OPID_TILE_NAT_X, _("NAT X"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_INT); - /* TRANS: The coordinate Y in native coordinates. - * The freeciv coordinate system is described in doc/HACKING. */ - ADDPROP(OPID_TILE_NAT_Y, _("NAT Y"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_INT); - ADDPROP(OPID_TILE_CONTINENT, _("Continent"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_INT); - ADDPROP(OPID_TILE_XY, Q_("?coordinates:X,Y"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_STRING); - ADDPROP(OPID_TILE_SPECIALS, _("Specials"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_BV_SPECIAL); - ADDPROP(OPID_TILE_ROADS, _("Roads"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_BV_ROADS); - ADDPROP(OPID_TILE_BASES, _("Bases"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_BV_BASES); -#ifdef FREECIV_DEBUG - ADDPROP(OPID_TILE_ADDRESS, _("Address"), NULL, - OPF_HAS_WIDGET, VALTYPE_STRING); -#endif /* FREECIV_DEBUG */ -#if 0 - /* Disabled entirely for now as server is not sending other - * players' vision information anyway. */ - ADDPROP(OPID_TILE_VISION, _("Vision"), NULL, - OPF_HAS_WIDGET, VALTYPE_TILE_VISION_DATA); -#endif - /* TRANS: Tile property "Label" label in editor */ - ADDPROP(OPID_TILE_LABEL, Q_("?property:Label"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_STRING); - return; - - case OBJTYPE_STARTPOS: - ADDPROP(OPID_STARTPOS_IMAGE, _("Image"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_PIXBUF); - ADDPROP(OPID_STARTPOS_XY, Q_("?coordinates:X,Y"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_STRING); - ADDPROP(OPID_STARTPOS_EXCLUDE, _("Exclude Nations"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - ADDPROP(OPID_STARTPOS_NATIONS, _("Nations"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_NATION_HASH); - return; - - case OBJTYPE_UNIT: - ADDPROP(OPID_UNIT_IMAGE, _("Image"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_PIXBUF); -#ifdef FREECIV_DEBUG - ADDPROP(OPID_UNIT_ADDRESS, _("Address"), NULL, - OPF_HAS_WIDGET, VALTYPE_STRING); -#endif /* FREECIV_DEBUG */ - ADDPROP(OPID_UNIT_TYPE, _("Type"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_STRING); - ADDPROP(OPID_UNIT_ID, _("ID"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_INT); - ADDPROP(OPID_UNIT_XY, Q_("?coordinates:X,Y"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_STRING); - ADDPROP(OPID_UNIT_MOVES_LEFT, _("Moves Left"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_INT); - ADDPROP(OPID_UNIT_FUEL, _("Fuel"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_INT); - ADDPROP(OPID_UNIT_MOVED, _("Moved"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - ADDPROP(OPID_UNIT_DONE_MOVING, _("Done Moving"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - /* TRANS: HP = Hit Points of a unit. */ - ADDPROP(OPID_UNIT_HP, _("HP"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_INT); - ADDPROP(OPID_UNIT_VETERAN, _("Veteran"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_INT); - ADDPROP(OPID_UNIT_STAY, _("Stay put"), NULL, - OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - return; - - case OBJTYPE_CITY: - ADDPROP(OPID_CITY_IMAGE, _("Image"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_PIXBUF); - ADDPROP(OPID_CITY_NAME, _("Name"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_STRING); -#ifdef FREECIV_DEBUG - ADDPROP(OPID_CITY_ADDRESS, _("Address"), NULL, - OPF_HAS_WIDGET, VALTYPE_STRING); -#endif /* FREECIV_DEBUG */ - ADDPROP(OPID_CITY_ID, _("ID"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_INT); - ADDPROP(OPID_CITY_XY, Q_("?coordinates:X,Y"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET, VALTYPE_STRING); - ADDPROP(OPID_CITY_SIZE, _("Size"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_INT); - ADDPROP(OPID_CITY_HISTORY, _("History"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_INT); - ADDPROP(OPID_CITY_BUILDINGS, _("Buildings"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_BUILT_ARRAY); - ADDPROP(OPID_CITY_FOOD_STOCK, _("Food Stock"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_INT); - ADDPROP(OPID_CITY_SHIELD_STOCK, _("Shield Stock"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_INT); - return; - - case OBJTYPE_PLAYER: - ADDPROP(OPID_PLAYER_NAME, _("Name"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_STRING); -#ifdef FREECIV_DEBUG - ADDPROP(OPID_PLAYER_ADDRESS, _("Address"), NULL, - OPF_HAS_WIDGET, VALTYPE_STRING); -#endif /* FREECIV_DEBUG */ - ADDPROP(OPID_PLAYER_NATION, _("Nation"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_NATION); - ADDPROP(OPID_PLAYER_GOV, _("Government"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_GOV); - ADDPROP(OPID_PLAYER_AGE, _("Age"), NULL, - OPF_HAS_WIDGET, VALTYPE_INT); - ADDPROP(OPID_PLAYER_INVENTIONS, _("Inventions"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_INVENTIONS_ARRAY); - ADDPROP(OPID_PLAYER_SCENARIO_RESERVED, _("Reserved"), NULL, - OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - ADDPROP(OPID_PLAYER_SCIENCE, _("Science"), NULL, - OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_INT); - ADDPROP(OPID_PLAYER_GOLD, _("Gold"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_INT); - return; - - case OBJTYPE_GAME: - ADDPROP(OPID_GAME_SCENARIO, _("Scenario"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_BOOL); - ADDPROP(OPID_GAME_SCENARIO_NAME, _("Scenario Name"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_STRING); - ADDPROP(OPID_GAME_SCENARIO_AUTHORS, - _("Scenario Authors"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_STRING); - ADDPROP(OPID_GAME_SCENARIO_DESC, - _("Scenario Description"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, - VALTYPE_STRING); - ADDPROP(OPID_GAME_SCENARIO_RANDSTATE, - _("Save Random Number State"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - ADDPROP(OPID_GAME_SCENARIO_PLAYERS, - _("Save Players"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - ADDPROP(OPID_GAME_STARTPOS_NATIONS, - _("Nation Start Positions"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - ADDPROP(OPID_GAME_PREVENT_CITIES, - _("Prevent New Cities"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - ADDPROP(OPID_GAME_LAKE_FLOODING, - _("Saltwater Flooding Lakes"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - ADDPROP(OPID_GAME_RULESET_LOCKED, - _("Lock to current Ruleset"), NULL, - OPF_IN_LISTVIEW | OPF_HAS_WIDGET | OPF_EDITABLE, VALTYPE_BOOL); - return; - - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled page object type %s (nb %d).", __FUNCTION__, - objtype_get_name(property_page_get_objtype(pp)), - property_page_get_objtype(pp)); -#undef ADDPROP -} - -/************************************************************************//** - Callback for when a property page's listview's selection changes. -****************************************************************************/ -static void property_page_selection_changed(GtkTreeSelection *sel, - gpointer userdata) -{ - struct property_page *pp; - struct objbind *ob = NULL; - - pp = userdata; - if (!pp) { - return; - } - - if (gtk_tree_selection_count_selected_rows(sel) < 1) { - property_page_set_focused_objbind(pp, NULL); - } - - ob = property_page_get_focused_objbind(pp); - property_page_objprop_iterate(pp, op) { - objprop_refresh_widget(op, ob); - } property_page_objprop_iterate_end; -} - -/************************************************************************//** - Monitor which rows are to be selected, so we know which objbind to display - in the properties panel. -****************************************************************************/ -static gboolean property_page_selection_func(GtkTreeSelection *sel, - GtkTreeModel *model, - GtkTreePath *sel_path, - gboolean currently_selected, - gpointer data) -{ - struct property_page *pp; - struct objbind *ob = NULL, *old_ob; - GtkTreeIter iter; - - pp = data; - if (!pp || !sel_path) { - return TRUE; - } - - if (!gtk_tree_model_get_iter(model, &iter, sel_path)) { - return TRUE; - } - - old_ob = property_page_get_focused_objbind(pp); - gtk_tree_model_get(model, &iter, 0, &ob, -1); - if (currently_selected) { - if (ob == old_ob) { - GList *rows, *p; - GtkTreePath *path; - struct objbind *new_ob = NULL; - - rows = gtk_tree_selection_get_selected_rows(sel, NULL); - for (p = rows; p != NULL; p = p->next) { - path = p->data; - if (gtk_tree_model_get_iter(model, &iter, path)) { - struct objbind *test_ob = NULL; - gtk_tree_model_get(model, &iter, 0, &test_ob, -1); - if (test_ob == ob) { - continue; - } - new_ob = test_ob; - break; - } - } - g_list_foreach(rows, (GFunc) gtk_tree_path_free, NULL); - g_list_free(rows); - - property_page_set_focused_objbind(pp, new_ob); - } - } else { - property_page_set_focused_objbind(pp, ob); - } - - return TRUE; -} - -/************************************************************************//** - Callback to handle text changing in the quick find entry widget. -****************************************************************************/ -static void property_page_quick_find_entry_changed(GtkWidget *entry, - gpointer userdata) -{ - struct property_page *pp; - const gchar *text; - GtkWidget *w; - GtkTreeViewColumn *col; - struct property_filter *pf; - bool matched; - - pp = userdata; - text = gtk_entry_get_text(GTK_ENTRY(entry)); - pf = property_filter_new(text); - - property_page_objprop_iterate(pp, op) { - if (!objprop_has_widget(op) - && !objprop_show_in_listview(op)) { - continue; - } - matched = property_filter_match(pf, op); - w = objprop_get_widget(op); - if (objprop_has_widget(op) && w != NULL) { - if (matched) { - gtk_widget_show(w); - } else { - gtk_widget_hide(w); - } - } - col = objprop_get_treeview_column(op); - if (objprop_show_in_listview(op) && col != NULL) { - gtk_tree_view_column_set_visible(col, matched); - } - } property_page_objprop_iterate_end; - - property_filter_free(pf); -} - -/************************************************************************//** - Create and return a property page of the given object type. - Returns NULL if the page could not be created. -****************************************************************************/ -static struct property_page * -property_page_new(enum editor_object_type objtype, - struct property_editor *pe) -{ - struct property_page *pp; - GtkWidget *vbox, *vbox2, *hbox, *hbox2, *paned, *frame, *w; - GtkWidget *scrollwin, *view, *label, *entry, *notebook; - GtkWidget *button, *hsep, *image; - GtkTreeSelection *sel; - GtkCellRenderer *cell; - GtkTreeViewColumn *col; - GtkSizeGroup *sizegroup; - int num_columns = 0; - GType *gtype_array; - int col_id = 1; - const char *attr_type_str, *name, *tooltip; - gchar *title; - - if (!(objtype < NUM_OBJTYPES)) { - return NULL; - } - - pp = fc_calloc(1, sizeof(struct property_page)); - pp->objtype = objtype; - pp->pe_parent = pe; - - sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); - - pp->objprop_table = objprop_hash_new(); - property_page_setup_objprops(pp); - - pp->objbind_table = objbind_hash_new(); - - pp->tag_table = stored_tag_hash_new(); - - property_page_objprop_iterate(pp, op) { - if (objprop_show_in_listview(op)) { - num_columns++; - } - } property_page_objprop_iterate_end; - - /* Column zero in the store holds an objbind - * pointer and is never displayed. */ - num_columns++; - gtype_array = fc_malloc(num_columns * sizeof(GType)); - gtype_array[0] = G_TYPE_POINTER; - - property_page_objprop_iterate(pp, op) { - if (objprop_show_in_listview(op)) { - gtype_array[col_id] = objprop_get_gtype(op); - objprop_set_column_id(op, col_id); - col_id++; - } - } property_page_objprop_iterate_end; - - pp->object_store = gtk_list_store_newv(num_columns, gtype_array); - free(gtype_array); - - paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); - gtk_paned_set_position(GTK_PANED(paned), 256); - pp->widget = paned; - - /* Left side object list view. */ - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 4); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); - gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE); - - scrollwin = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(vbox), scrollwin); - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pp->object_store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE); - - property_page_objprop_iterate(pp, op) { - if (!objprop_show_in_listview(op)) { - continue; - } - - attr_type_str = objprop_get_attribute_type_string(op); - if (!attr_type_str) { - continue; - } - col_id = objprop_get_column_id(op); - if (col_id < 0) { - continue; - } - name = objprop_get_name(op); - if (!name) { - continue; - } - cell = objprop_create_cell_renderer(op); - if (!cell) { - continue; - } - - col = gtk_tree_view_column_new_with_attributes(name, cell, - attr_type_str, col_id, - NULL); - - gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_GROW_ONLY); - gtk_tree_view_column_set_resizable(col, TRUE); - gtk_tree_view_column_set_reorderable(col, TRUE); - if (objprop_is_sortable(op)) { - gtk_tree_view_column_set_clickable(col, TRUE); - gtk_tree_view_column_set_sort_column_id(col, col_id); - } else { - gtk_tree_view_column_set_clickable(col, FALSE); - } - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - objprop_set_treeview_column(op, col); - - } property_page_objprop_iterate_end; - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); - g_signal_connect(sel, "changed", - G_CALLBACK(property_page_selection_changed), pp); - gtk_tree_selection_set_select_function(sel, - property_page_selection_func, pp, NULL); - - gtk_container_add(GTK_CONTAINER(scrollwin), view); - pp->object_view = view; - - if (!objtype_is_conserved(objtype)) { - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - - button = gtk_button_new(); - image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON); - gtk_button_set_image(GTK_BUTTON(button), image); - gtk_button_set_label(GTK_BUTTON(button), _("Create")); - gtk_size_group_add_widget(sizegroup, button); - gtk_widget_set_tooltip_text(button, - _("Pressing this button will create a new object of the " - "same type as the current property page and add it to " - "the page. The specific type and count of the objects " - "is taken from the editor tool state. So for example, " - "the \"tool value\" of the unit tool and its \"count\" " - "parameter affect unit creation.")); - g_signal_connect(button, "clicked", - G_CALLBACK(property_page_create_button_clicked), pp); - gtk_container_add(GTK_CONTAINER(hbox), button); - - button = gtk_button_new(); - image = gtk_image_new_from_stock(GTK_STOCK_REMOVE, - GTK_ICON_SIZE_BUTTON); - gtk_button_set_image(GTK_BUTTON(button), image); - gtk_button_set_label(GTK_BUTTON(button), _("Destroy")); - gtk_size_group_add_widget(sizegroup, button); - gtk_widget_set_tooltip_text(button, - _("Pressing this button will send a request to the server " - "to destroy (i.e. erase) the objects selected in the object " - "list.")); - g_signal_connect(button, "clicked", - G_CALLBACK(property_page_destroy_button_clicked), pp); - gtk_container_add(GTK_CONTAINER(hbox), button); - } - - /* Right side properties panel. */ - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - gtk_paned_pack2(GTK_PANED(paned), hbox, TRUE, TRUE); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 4); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); - gtk_container_add(GTK_CONTAINER(hbox), vbox); - - /* Extended property viewer to the right of the properties panel. - * This needs to be created before property widgets, since some - * might try to append themselves to this notebook. */ - - vbox2 = gtk_grid_new(); - gtk_widget_set_hexpand(vbox2, TRUE); - gtk_widget_set_vexpand(vbox2, TRUE); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox2), 4); - gtk_container_add(GTK_CONTAINER(hbox), vbox2); - - notebook = gtk_notebook_new(); - gtk_widget_set_vexpand(notebook, TRUE); - gtk_widget_set_size_request(notebook, 256, -1); - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); - gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE); - gtk_container_add(GTK_CONTAINER(vbox2), notebook); - pp->extviewer_notebook = notebook; - - hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); - gtk_container_add(GTK_CONTAINER(vbox2), hsep); - - hbox2 = gtk_grid_new(); - gtk_container_set_border_width(GTK_CONTAINER(hbox2), 4); - gtk_container_add(GTK_CONTAINER(vbox2), hbox2); - - button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - gtk_size_group_add_widget(sizegroup, button); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(gtk_widget_hide_on_delete), pe->widget); - gtk_container_add(GTK_CONTAINER(hbox2), button); - - /* Now create the properties panel. */ - - /* TRANS: %s is a type of object that can be edited, such as "Tile", - * "Unit", "Start Position", etc. */ - title = g_strdup_printf(_("%s Properties"), - objtype_get_name(objtype)); - frame = gtk_frame_new(title); - g_free(title); - gtk_widget_set_size_request(frame, 256, -1); - gtk_container_add(GTK_CONTAINER(vbox), frame); - - scrollwin = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin), - GTK_SHADOW_NONE); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(frame), scrollwin); - - vbox2 = gtk_grid_new(); - gtk_widget_set_vexpand(vbox2, TRUE); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox2), 4); - gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4); - gtk_container_add(GTK_CONTAINER(scrollwin), vbox2); - - property_page_objprop_iterate(pp, op) { - if (!objprop_has_widget(op)) { - continue; - } - w = objprop_get_widget(op); - if (!w) { - continue; - } - gtk_container_add(GTK_CONTAINER(vbox2), w); - tooltip = objprop_get_tooltip(op); - if (NULL != tooltip) { - gtk_widget_set_tooltip_text(w, tooltip); - } - } property_page_objprop_iterate_end; - - hbox2 = gtk_grid_new(); - gtk_widget_set_margin_top(hbox2, 4); - gtk_widget_set_margin_bottom(hbox2, 4); - gtk_grid_set_column_spacing(GTK_GRID(hbox2), 4); - gtk_container_add(GTK_CONTAINER(vbox), hbox2); - - label = gtk_label_new(_("Filter:")); - gtk_container_add(GTK_CONTAINER(hbox2), label); - - entry = gtk_entry_new(); - gtk_widget_set_tooltip_text(entry, - _("Enter a filter string to limit which properties are shown. " - "The filter is one or more text patterns separated by | " - "(\"or\") or & (\"and\"). The symbol & has higher precedence " - "than |. A pattern may also be negated by prefixing it with !.")); - g_signal_connect(entry, "changed", - G_CALLBACK(property_page_quick_find_entry_changed), pp); - gtk_container_add(GTK_CONTAINER(hbox2), entry); - - hbox2 = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox2), 4); - gtk_container_add(GTK_CONTAINER(vbox), hbox2); - - button = gtk_button_new_from_stock(GTK_STOCK_REFRESH); - gtk_size_group_add_widget(sizegroup, button); - gtk_widget_set_tooltip_text(button, - _("Pressing this button will reset all modified properties of " - "the selected objects to their current values (the values " - "they have on the server).")); - g_signal_connect(button, "clicked", - G_CALLBACK(property_page_refresh_button_clicked), pp); - gtk_container_add(GTK_CONTAINER(hbox2), button); - - button = gtk_button_new_from_stock(GTK_STOCK_APPLY); - gtk_size_group_add_widget(sizegroup, button); - gtk_widget_set_tooltip_text(button, - _("Pressing this button will send all modified properties of " - "the objects selected in the object list to the server. " - "Modified properties' names are shown in red in the properties " - "panel.")); - g_signal_connect(button, "clicked", - G_CALLBACK(property_page_apply_button_clicked), pp); - gtk_container_add(GTK_CONTAINER(hbox2), button); - - return pp; -} - -/************************************************************************//** - Returns the translated name of the property page's object type. -****************************************************************************/ -static const char *property_page_get_name(const struct property_page *pp) -{ - if (!pp) { - return ""; - } - return objtype_get_name(property_page_get_objtype(pp)); -} - -/************************************************************************//** - Returns the object type for this property page, or -1 if none. -****************************************************************************/ -static enum editor_object_type -property_page_get_objtype(const struct property_page *pp) -{ - if (!pp) { - return -1; - } - return pp->objtype; -} - -/************************************************************************//** - Create a pixbuf containing an image of the given tile. The image will - only be of the layers containing terrains, resources and specials. - - May return NULL on error or bad input. - NB: You must call g_object_unref on the non-NULL return value when you - no longer need it. -****************************************************************************/ -static GdkPixbuf *create_tile_pixbuf(const struct tile *ptile) -{ - return create_pixbuf_from_layers(ptile, NULL, NULL, LAYER_CATEGORY_TILE); -} - -/************************************************************************//** - Create a pixbuf containing an image of the given unit. - - May return NULL on error or bad input. - NB: You must call g_object_unref on the non-NULL return value when you - no longer need it. -****************************************************************************/ -static GdkPixbuf *create_unit_pixbuf(const struct unit *punit) -{ - return create_pixbuf_from_layers(NULL, punit, NULL, LAYER_CATEGORY_UNIT); -} - -/************************************************************************//** - Create a pixbuf containing an image of the given city. - - May return NULL on error or bad input. - NB: You must call g_object_unref on the non-NULL return value when you - no longer need it. -****************************************************************************/ -static GdkPixbuf *create_city_pixbuf(const struct city *pcity) -{ - return create_pixbuf_from_layers(city_tile(pcity), NULL, pcity, - LAYER_CATEGORY_CITY); -} - -/************************************************************************//** - Create a pixbuf containing an image of the given tile, unit or city - restricted to the layer category 'cat'. - - May return NULL on error or bad input. - NB: You must call g_object_unref on the non-NULL return value when you - no longer need it. -****************************************************************************/ -static GdkPixbuf *create_pixbuf_from_layers(const struct tile *ptile, - const struct unit *punit, - const struct city *pcity, - enum layer_category category) -{ - struct canvas canvas = FC_STATIC_CANVAS_INIT; - int h, fh, fw, canvas_x, canvas_y; - GdkPixbuf *pixbuf; - cairo_t *cr; - - fw = tileset_full_tile_width(tileset); - fh = tileset_full_tile_height(tileset); - h = tileset_tile_height(tileset); - - canvas.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, fw, fh); - - cr = cairo_create(canvas.surface); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_paint(cr); - cairo_destroy(cr); - - canvas_x = 0; - canvas_y = 0; - - canvas_y += (fh - h); - - mapview_layer_iterate(layer) { - if (tileset_layer_in_category(layer, category)) { - put_one_element(&canvas, 1.0, layer, - ptile, NULL, NULL, punit, pcity, - canvas_x, canvas_y, NULL, NULL); - } - } mapview_layer_iterate_end - pixbuf = surface_get_pixbuf(canvas.surface, fw, fh); - cairo_surface_destroy(canvas.surface); - - return pixbuf; -} - -/************************************************************************//** - Remove all object binds (i.e. objects listed) in the property page. -****************************************************************************/ -static void property_page_clear_objbinds(struct property_page *pp) -{ - if (!pp) { - return; - } - - gtk_list_store_clear(pp->object_store); - objbind_hash_clear(pp->objbind_table); - property_page_set_focused_objbind(pp, NULL); -} - -/************************************************************************//** - Create a new object bind to the given object and register it with the - given property page. -****************************************************************************/ -static void property_page_add_objbind(struct property_page *pp, - gpointer object_data) -{ - struct objbind *ob; - enum editor_object_type objtype; - int id; - - if (!pp) { - return; - } - - objtype = property_page_get_objtype(pp); - id = objtype_get_id_from_object(objtype, object_data); - if (id < 0) { - return; - } - - if (objbind_hash_lookup(pp->objbind_table, id, NULL)) { - /* Object already exists. */ - return; - } - - ob = objbind_new(objtype, object_data); - if (!ob) { - return; - } - - objbind_bind_properties(ob, pp); - - objbind_hash_insert(pp->objbind_table, ob->object_id, ob); -} - -/************************************************************************//** - Create zero or more object binds from the objects on the given tile to - the properties contained in the given property page. -****************************************************************************/ -static void property_page_add_objbinds_from_tile(struct property_page *pp, - const struct tile *ptile) -{ - - if (!pp || !ptile) { - return; - } - - switch (property_page_get_objtype(pp)) { - case OBJTYPE_TILE: - property_page_add_objbind(pp, (gpointer) ptile); - return; - - case OBJTYPE_STARTPOS: - { - struct startpos *psp = map_startpos_get(ptile); - - if (NULL != psp) { - property_page_add_objbind(pp, map_startpos_get(ptile)); - } - } - return; - - case OBJTYPE_UNIT: - unit_list_iterate(ptile->units, punit) { - property_page_add_objbind(pp, punit); - } unit_list_iterate_end; - return; - - case OBJTYPE_CITY: - if (tile_city(ptile)) { - property_page_add_objbind(pp, tile_city(ptile)); - } - return; - - case OBJTYPE_PLAYER: - case OBJTYPE_GAME: - return; - - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled page object type %s (nb %d).", __FUNCTION__, - objtype_get_name(property_page_get_objtype(pp)), - property_page_get_objtype(pp)); -} - -/************************************************************************//** - Set the column value in the list store of the property page. - Returns TRUE if data was enetered into the store. - - NB: This must match the conversion in objprop_get_gtype. -****************************************************************************/ -static bool property_page_set_store_value(struct property_page *pp, - struct objprop *op, - struct objbind *ob, - GtkTreeIter *iter) -{ - int col_id; - struct propval *pv; - enum value_types valtype; - char buf[128], *p; - GdkPixbuf *pixbuf = NULL; - GtkListStore *store; - gchar *buf2; - - if (!pp || !pp->object_store || !op || !ob) { - return FALSE; - } - - if (!objprop_show_in_listview(op)) { - return FALSE; - } - - col_id = objprop_get_column_id(op); - if (col_id < 0) { - return FALSE; - } - - pv = objbind_get_value_from_object(ob, op); - if (!pv) { - return FALSE; - } - - valtype = objprop_get_valtype(op); - store = pp->object_store; - - switch (valtype) { - case VALTYPE_NONE: - break; - case VALTYPE_INT: - gtk_list_store_set(store, iter, col_id, pv->data.v_int, -1); - break; - case VALTYPE_BOOL: - /* Set as translated string, not as untranslated G_TYPE_BOOLEAN */ - gtk_list_store_set(store, iter, col_id, propval_as_string(pv), -1); - break; - case VALTYPE_STRING: - if (fc_strlcpy(buf, pv->data.v_string, 28) >= 28) { - sz_strlcat(buf, "..."); - } - for (p = buf; *p; p++) { - if (*p == '\n' || *p == '\t' || *p == '\r') { - *p = ' '; - } - } - gtk_list_store_set(store, iter, col_id, buf, -1); - break; - case VALTYPE_PIXBUF: - gtk_list_store_set(store, iter, col_id, pv->data.v_pixbuf, -1); - break; - case VALTYPE_BUILT_ARRAY: - case VALTYPE_INVENTIONS_ARRAY: - case VALTYPE_BV_SPECIAL: - case VALTYPE_BV_ROADS: - case VALTYPE_BV_BASES: - case VALTYPE_NATION_HASH: - buf2 = propval_as_string(pv); - gtk_list_store_set(store, iter, col_id, buf2, -1); - g_free(buf2); - break; - case VALTYPE_NATION: - pixbuf = get_flag(pv->data.v_nation); - gtk_list_store_set(store, iter, col_id, pixbuf, -1); - if (pixbuf) { - g_object_unref(pixbuf); - } - break; - case VALTYPE_GOV: - pixbuf = sprite_get_pixbuf(get_government_sprite(tileset, pv->data.v_gov)); - gtk_list_store_set(store, iter, col_id, pixbuf, -1); - if (pixbuf) { - g_object_unref(pixbuf); - } - break; - case VALTYPE_TILE_VISION_DATA: - break; - } - - propval_free(pv); - - return TRUE; -} - -/************************************************************************//** - Inserts any objbinds owned by this proprety page into the page's list - store if they are not there already and refreshes all property widgets. -****************************************************************************/ -static void property_page_fill_widgets(struct property_page *pp) -{ - struct objbind *focused; - - if (!pp || !pp->objbind_table) { - return; - } - - if (pp->object_store) { - GtkTreeIter iter; - GtkTreeRowReference *rr; - GtkTreeModel *model; - GtkTreePath *path; - - model = GTK_TREE_MODEL(pp->object_store); - - property_page_objbind_iterate(pp, ob) { - if (objbind_get_rowref(ob)) { - continue; - } - gtk_list_store_append(pp->object_store, &iter); - gtk_list_store_set(pp->object_store, &iter, 0, ob, -1); - path = gtk_tree_model_get_path(model, &iter); - rr = gtk_tree_row_reference_new(model, path); - gtk_tree_path_free(path); - objbind_set_rowref(ob, rr); - - property_page_objprop_iterate(pp, op) { - property_page_set_store_value(pp, op, ob, &iter); - } property_page_objprop_iterate_end; - } property_page_objbind_iterate_end; - - if (gtk_tree_model_get_iter_first(model, &iter)) { - GtkTreeSelection *sel; - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pp->object_view)); - gtk_tree_selection_select_iter(sel, &iter); - } - } - - focused = property_page_get_focused_objbind(pp); - property_page_objprop_iterate(pp, op) { - objprop_refresh_widget(op, focused); - } property_page_objprop_iterate_end; -} - -/************************************************************************//** - Get the objbind corresponding to the object that is currently in view - (i.e. in the information/properties panels) or NULL if none. -****************************************************************************/ -static struct objbind *property_page_get_focused_objbind(struct property_page *pp) -{ - if (!pp) { - return NULL; - } - return pp->focused_objbind; -} - -/************************************************************************//** - Set the objbind that should be shown in the properties panel. Does not - refresh property widgets. -****************************************************************************/ -static void property_page_set_focused_objbind(struct property_page *pp, - struct objbind *ob) -{ - if (!pp) { - return; - } - pp->focused_objbind = ob; -} - -/************************************************************************//** - Returns the objbind whose object corresponds to the given id, or NULL - if no such objbind exists. -****************************************************************************/ -static struct objbind *property_page_get_objbind(struct property_page *pp, - int object_id) -{ - struct objbind *ob; - - if (!pp || !pp->objbind_table) { - return NULL; - } - - objbind_hash_lookup(pp->objbind_table, object_id, &ob); - return ob; -} - -/************************************************************************//** - Removes all of the current objbinds and extracts new ones from the - supplied list of tiles. -****************************************************************************/ -static void property_page_load_tiles(struct property_page *pp, - const struct tile_list *tiles) -{ - if (!pp || !tiles) { - return; - } - - tile_list_iterate(tiles, ptile) { - property_page_add_objbinds_from_tile(pp, ptile); - } tile_list_iterate_end; - - property_page_fill_widgets(pp); -} - -/************************************************************************//** - Return the number of current bound objects to this property page. -****************************************************************************/ -static int property_page_get_num_objbinds(const struct property_page *pp) -{ - if (!pp || !pp->objbind_table) { - return 0; - } - return objbind_hash_size(pp->objbind_table); -} - -/************************************************************************//** - Called when a user sets a new value for the given property via the GUI. - Refreshes the properties widget if anything changes. -****************************************************************************/ -static void property_page_change_value(struct property_page *pp, - struct objprop *op, - struct propval *pv) -{ - GtkTreeSelection *sel; - GtkTreeModel *model; - GList *rows, *p; - GtkTreePath *path; - GtkTreeIter iter; - struct objbind *ob; - - if (!pp || !op || !pp->object_view) { - return; - } - - if (objprop_is_readonly(op)) { - return; - } - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pp->object_view)); - rows = gtk_tree_selection_get_selected_rows(sel, &model); - - for (p = rows; p != NULL; p = p->next) { - path = p->data; - if (gtk_tree_model_get_iter(model, &iter, path)) { - gtk_tree_model_get(model, &iter, 0, &ob, -1); - objbind_set_modified_value(ob, op, pv); - } - gtk_tree_path_free(path); - } - g_list_free(rows); - - ob = property_page_get_focused_objbind(pp); - objprop_refresh_widget(op, ob); -} - -/************************************************************************//** - Send all modified values of all selected properties. -****************************************************************************/ -static void property_page_send_values(struct property_page *pp) -{ - GtkTreeSelection *sel; - GtkTreeModel *model; - GList *rows, *p; - GtkTreePath *path; - GtkTreeIter iter; - struct objbind *ob; - union packetdata packet; - struct connection *my_conn = &client.conn; - - if (!pp || !pp->object_view) { - return; - } - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pp->object_view)); - if (gtk_tree_selection_count_selected_rows(sel) < 1) { - return; - } - - packet = property_page_new_packet(pp); - if (!packet.pointers.v_pointer1) { - return; - } - - rows = gtk_tree_selection_get_selected_rows(sel, &model); - connection_do_buffer(my_conn); - for (p = rows; p != NULL; p = p->next) { - path = p->data; - if (gtk_tree_model_get_iter(model, &iter, path)) { - gtk_tree_model_get(model, &iter, 0, &ob, -1); - if (objbind_has_modified_properties(ob)) { - objbind_pack_current_values(ob, packet); - property_page_objprop_iterate(pp, op) { - if (objprop_is_readonly(op)) { - continue; - } - objbind_pack_modified_value(ob, op, packet); - } property_page_objprop_iterate_end; - property_page_send_packet(pp, packet); - } - } - gtk_tree_path_free(path); - } - connection_do_unbuffer(my_conn); - g_list_free(rows); - - property_page_free_packet(pp, packet); -} - -/************************************************************************//** - Returns pointer to a packet suitable for this page's object type. Result - should be freed using property_page_free_packet when no longer needed. -****************************************************************************/ -static union packetdata property_page_new_packet(struct property_page *pp) -{ - union packetdata packet; - - packet.pointers.v_pointer2 = NULL; - - if (!pp) { - packet.pointers.v_pointer1 = NULL; - return packet; - } - - switch (property_page_get_objtype(pp)) { - case OBJTYPE_TILE: - packet.tile = fc_calloc(1, sizeof(*packet.tile)); - break; - case OBJTYPE_STARTPOS: - packet.startpos = fc_calloc(1, sizeof(*packet.startpos)); - break; - case OBJTYPE_UNIT: - packet.unit = fc_calloc(1, sizeof(*packet.unit)); - break; - case OBJTYPE_CITY: - packet.city = fc_calloc(1, sizeof(*packet.city)); - break; - case OBJTYPE_PLAYER: - packet.player = fc_calloc(1, sizeof(*packet.player)); - break; - case OBJTYPE_GAME: - packet.game.game = fc_calloc(1, sizeof(*packet.game.game)); - packet.game.desc = fc_calloc(1, sizeof(*packet.game.desc)); - break; - case NUM_OBJTYPES: - break; - } - - return packet; -} - -/************************************************************************//** - Sends the given packet. -****************************************************************************/ -static void property_page_send_packet(struct property_page *pp, - union packetdata packet) -{ - struct connection *my_conn = &client.conn; - - if (!pp || !packet.pointers.v_pointer1) { - return; - } - - switch (property_page_get_objtype(pp)) { - case OBJTYPE_TILE: - send_packet_edit_tile(my_conn, packet.tile); - return; - case OBJTYPE_STARTPOS: - send_packet_edit_startpos_full(my_conn, packet.startpos); - return; - case OBJTYPE_UNIT: - send_packet_edit_unit(my_conn, packet.unit); - return; - case OBJTYPE_CITY: - send_packet_edit_city(my_conn, packet.city); - return; - case OBJTYPE_PLAYER: - send_packet_edit_player(my_conn, packet.player); - return; - case OBJTYPE_GAME: - send_packet_edit_game(my_conn, packet.game.game); - send_packet_edit_scenario_desc(my_conn, packet.game.desc); - return; - case NUM_OBJTYPES: - break; - } - - log_error("%s(): Unhandled object type %s (nb %d).", - __FUNCTION__, objtype_get_name(property_page_get_objtype(pp)), - property_page_get_objtype(pp)); -} - -/************************************************************************//** - Free any resources being used by the packet. -****************************************************************************/ -static void property_page_free_packet(struct property_page *pp, - union packetdata packet) -{ - if (!packet.pointers.v_pointer1) { - return; - } - - free(packet.pointers.v_pointer1); - packet.pointers.v_pointer1 = NULL; - - if (packet.pointers.v_pointer2 != NULL) { - free(packet.pointers.v_pointer2); - packet.pointers.v_pointer2 = NULL; - } -} - -/************************************************************************//** - Reload the displayed values of all properties for the selected bound - objects. Hence, deletes all their stored modified values. -****************************************************************************/ -static void property_page_reset_objbinds(struct property_page *pp) -{ - GtkTreeSelection *sel; - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreePath *path; - GList *rows, *p; - struct objbind *ob; - - if (!pp || !pp->object_view) { - return; - } - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pp->object_view)); - if (gtk_tree_selection_count_selected_rows(sel) < 1) { - return; - } - - rows = gtk_tree_selection_get_selected_rows(sel, &model); - for (p = rows; p != NULL; p = p->next) { - path = p->data; - if (gtk_tree_model_get_iter(model, &iter, path)) { - gtk_tree_model_get(model, &iter, 0, &ob, -1); - objbind_clear_all_modified_values(ob); - property_page_objprop_iterate(pp, op) { - property_page_set_store_value(pp, op, ob, &iter); - } property_page_objprop_iterate_end; - } - gtk_tree_path_free(path); - } - g_list_free(rows); - - ob = property_page_get_focused_objbind(pp); - property_page_objprop_iterate(pp, op) { - objprop_refresh_widget(op, ob); - } property_page_objprop_iterate_end; -} - -/************************************************************************//** - Destroy all selected objects in the current property page. -****************************************************************************/ -static void property_page_destroy_objects(struct property_page *pp) -{ - GtkTreeSelection *sel; - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreePath *path; - GList *rows, *p; - struct objbind *ob; - struct connection *my_conn = &client.conn; - - if (!pp || !pp->object_view) { - return; - } - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pp->object_view)); - if (gtk_tree_selection_count_selected_rows(sel) < 1) { - return; - } - - rows = gtk_tree_selection_get_selected_rows(sel, &model); - connection_do_buffer(my_conn); - for (p = rows; p != NULL; p = p->next) { - path = p->data; - if (gtk_tree_model_get_iter(model, &iter, path)) { - gtk_tree_model_get(model, &iter, 0, &ob, -1); - objbind_request_destroy_object(ob); - } - gtk_tree_path_free(path); - } - connection_do_unbuffer(my_conn); - g_list_free(rows); -} - -/************************************************************************//** - Create objects corresponding to the type of this property page. Parameters - such as the type, count, size and player owner are taken from the current - editor state. The 'hint_tiles' argument is a list of tiles where the - objects could be created. -****************************************************************************/ -static void property_page_create_objects(struct property_page *pp, - struct tile_list *hint_tiles) -{ - enum editor_object_type objtype; - int apno, value, count, size; - int tag; - struct connection *my_conn = &client.conn; - struct tile *ptile = NULL; - struct player *pplayer; - - if (!pp) { - return; - } - - objtype = property_page_get_objtype(pp); - if (objtype_is_conserved(objtype)) { - return; - } - - tag = get_next_unique_tag(); - count = 1; - - switch (objtype) { - case OBJTYPE_STARTPOS: - if (hint_tiles) { - tile_list_iterate(hint_tiles, atile) { - if (NULL == map_startpos_get(atile)) { - ptile = atile; - break; - } - } tile_list_iterate_end; - } - - if (NULL == ptile) { - ptile = get_center_tile_mapcanvas(); - } - - if (NULL == ptile) { - break; - } - - dsend_packet_edit_startpos(my_conn, tile_index(ptile), FALSE, tag); - break; - - case OBJTYPE_UNIT: - if (hint_tiles) { - tile_list_iterate(hint_tiles, atile) { - if (can_create_unit_at_tile(atile)) { - ptile = atile; - break; - } - } tile_list_iterate_end; - } - - if (!ptile) { - struct unit *punit; - property_page_objbind_iterate(pp, ob) { - punit = objbind_get_object(ob); - if (punit && can_create_unit_at_tile(unit_tile(punit))) { - ptile = unit_tile(punit); - break; - } - } property_page_objbind_iterate_end; - } - - if (!ptile) { - ptile = get_center_tile_mapcanvas(); - } - - if (!ptile) { - break; - } - - apno = editor_tool_get_applied_player(ETT_UNIT); - count = editor_tool_get_count(ETT_UNIT); - value = editor_tool_get_value(ETT_UNIT); - dsend_packet_edit_unit_create(my_conn, apno, tile_index(ptile), - value, count, tag); - break; - - case OBJTYPE_CITY: - apno = editor_tool_get_applied_player(ETT_CITY); - pplayer = player_by_number(apno); - if (pplayer && hint_tiles) { - tile_list_iterate(hint_tiles, atile) { - if (!is_enemy_unit_tile(atile, pplayer) - && city_can_be_built_here(atile, NULL)) { - ptile = atile; - break; - } - } tile_list_iterate_end; - } - - if (!ptile) { - ptile = get_center_tile_mapcanvas(); - } - - if (!ptile) { - break; - } - - size = editor_tool_get_size(ETT_CITY); - dsend_packet_edit_city_create(my_conn, apno, tile_index(ptile), - size, tag); - break; - - case OBJTYPE_PLAYER: - dsend_packet_edit_player_create(my_conn, tag); - break; - - case OBJTYPE_TILE: - case OBJTYPE_GAME: - case NUM_OBJTYPES: - break; - } - - property_page_store_creation_tag(pp, tag, count); -} - -/************************************************************************//** - Update objbinds and widgets according to how the object given by - 'object_id' has changed. If the object no longer exists then the - objbind is removed from the property page. -****************************************************************************/ -static void property_page_object_changed(struct property_page *pp, - int object_id, - bool removed) -{ - struct objbind *ob; - GtkTreeRowReference *rr; - - ob = property_page_get_objbind(pp, object_id); - if (!ob) { - return; - } - - rr = objbind_get_rowref(ob); - if (rr && gtk_tree_row_reference_valid(rr)) { - GtkTreePath *path; - GtkTreeIter iter; - GtkTreeModel *model; - - model = GTK_TREE_MODEL(pp->object_store); - path = gtk_tree_row_reference_get_path(rr); - - if (gtk_tree_model_get_iter(model, &iter, path)) { - if (removed) { - gtk_list_store_remove(pp->object_store, &iter); - } else { - property_page_objprop_iterate(pp, op) { - property_page_set_store_value(pp, op, ob, &iter); - } property_page_objprop_iterate_end; - } - } - - gtk_tree_path_free(path); - } - - if (removed) { - objbind_hash_remove(pp->objbind_table, object_id); - return; - } - - if (ob == property_page_get_focused_objbind(pp)) { - property_page_objprop_iterate(pp, op) { - objprop_refresh_widget(op, ob); - } property_page_objprop_iterate_end; - } -} - -/************************************************************************//** - Handle a notification of object creation sent back from the server. If - this is something we previously requested, then 'tag' should be found in - the tag table. In this case we create a new objbind for the object given - by 'object_id' and add it to this page. -****************************************************************************/ -static void property_page_object_created(struct property_page *pp, - int tag, int object_id) -{ - gpointer object; - enum editor_object_type objtype; - - if (!property_page_tag_is_known(pp, tag)) { - return; - } - property_page_remove_creation_tag(pp, tag); - - objtype = property_page_get_objtype(pp); - object = objtype_get_object_from_id(objtype, object_id); - - if (!object) { - return; - } - - property_page_add_objbind(pp, object); - property_page_fill_widgets(pp); -} - -/************************************************************************//** - Add the extviewer's view widget to the property page so that it can - be shown in the extended property view panel. -****************************************************************************/ -static void property_page_add_extviewer(struct property_page *pp, - struct extviewer *ev) -{ - GtkWidget *w; - - if (!pp || !ev) { - return; - } - - w = extviewer_get_view_widget(ev); - if (!w) { - return; - } - gtk_notebook_append_page(GTK_NOTEBOOK(pp->extviewer_notebook), w, NULL); -} - -/************************************************************************//** - Make the given extended property viewer's view widget visible in the - property page. -****************************************************************************/ -static void property_page_show_extviewer(struct property_page *pp, - struct extviewer *ev) -{ - GtkWidget *w; - GtkNotebook *notebook; - int page; - - if (!pp || !ev) { - return; - } - - w = extviewer_get_view_widget(ev); - if (!w) { - return; - } - - notebook = GTK_NOTEBOOK(pp->extviewer_notebook); - page = gtk_notebook_page_num(notebook, w); - gtk_notebook_set_current_page(notebook, page); -} - -/************************************************************************//** - Store the given object creation tag so that when the server notifies - us about it we know what to do, up to 'count' times. -****************************************************************************/ -static void property_page_store_creation_tag(struct property_page *pp, - int tag, int count) -{ - if (!pp || !pp->tag_table) { - return; - } - - if (stored_tag_hash_lookup(pp->tag_table, tag, NULL)) { - log_error("Attempted to insert object creation tag %d " - "twice into tag table for property page %p (%d %s).", - tag, pp, property_page_get_objtype(pp), - property_page_get_name(pp)); - return; - } - - stored_tag_hash_insert(pp->tag_table, tag, count); -} - -/************************************************************************//** - Decrease the tag count and remove the object creation tag if it is no - longer needed. -****************************************************************************/ -static void property_page_remove_creation_tag(struct property_page *pp, - int tag) -{ - int count; - - if (!pp || !pp->tag_table) { - return; - } - - if (stored_tag_hash_lookup(pp->tag_table, tag, &count)) { - if (0 >= --count) { - stored_tag_hash_remove(pp->tag_table, tag); - } - } -} - -/************************************************************************//** - Check if the given tag is one that we previously stored. -****************************************************************************/ -static bool property_page_tag_is_known(struct property_page *pp, int tag) -{ - if (!pp || !pp->tag_table) { - return FALSE; - } - return stored_tag_hash_lookup(pp->tag_table, tag, NULL); -} - -/************************************************************************//** - Remove all tags in the tag table. -****************************************************************************/ -static void property_page_clear_tags(struct property_page *pp) -{ - if (!pp || !pp->tag_table) { - return; - } - stored_tag_hash_clear(pp->tag_table); -} - -/************************************************************************//** - Handles the 'clicked' signal for the "Apply" button in the property page. -****************************************************************************/ -static void property_page_apply_button_clicked(GtkButton *button, - gpointer userdata) -{ - struct property_page *pp = userdata; - property_page_send_values(pp); -} - -/************************************************************************//** - Handles the 'clicked' signal for the "Refresh" button in the - property page. -****************************************************************************/ -static void property_page_refresh_button_clicked(GtkButton *button, - gpointer userdata) -{ - struct property_page *pp = userdata; - property_page_reset_objbinds(pp); -} - -/************************************************************************//** - Handle a request to create a new object in the property page. -****************************************************************************/ -static void property_page_create_button_clicked(GtkButton *button, - gpointer userdata) -{ - struct property_page *pp = userdata, *tile_pp; - struct tile_list *tiles = NULL; - struct tile *ptile; - - if (!pp) { - return; - } - - tile_pp = property_editor_get_page(pp->pe_parent, OBJTYPE_TILE); - tiles = tile_list_new(); - - property_page_objbind_iterate(tile_pp, ob) { - ptile = objbind_get_object(ob); - if (ptile) { - tile_list_append(tiles, ptile); - } - } property_page_objbind_iterate_end; - - property_page_create_objects(pp, tiles); - tile_list_destroy(tiles); -} - -/************************************************************************//** - Handle a click on the "destroy" button. -****************************************************************************/ -static void property_page_destroy_button_clicked(GtkButton *button, - gpointer userdata) -{ - struct property_page *pp = userdata; - property_page_destroy_objects(pp); -} - -/************************************************************************//** - Create and add a property page for the given object type - to the property editor. Returns TRUE if successful. -****************************************************************************/ -static bool property_editor_add_page(struct property_editor *pe, - enum editor_object_type objtype) -{ - struct property_page *pp; - GtkWidget *label; - const char *name; - - if (!pe || !pe->notebook) { - return FALSE; - } - - if (!(objtype < NUM_OBJTYPES)) { - return FALSE; - } - - pp = property_page_new(objtype, pe); - if (!pp) { - return FALSE; - } - - name = property_page_get_name(pp); - label = gtk_label_new(name); - gtk_notebook_append_page(GTK_NOTEBOOK(pe->notebook), - pp->widget, label); - - pe->property_pages[objtype] = pp; - - return TRUE; -} - -/************************************************************************//** - Returns the property page for the given object type. -****************************************************************************/ -static struct property_page * -property_editor_get_page(struct property_editor *pe, - enum editor_object_type objtype) -{ - if (!pe || !(objtype < NUM_OBJTYPES)) { - return NULL; - } - - return pe->property_pages[objtype]; -} - -/************************************************************************//** - Create and return the property editor widget bundle. -****************************************************************************/ -static struct property_editor *property_editor_new(void) -{ - struct property_editor *pe; - GtkWidget *win, *notebook, *vbox; - enum editor_object_type objtype; - - pe = fc_calloc(1, sizeof(*pe)); - - /* The property editor dialog window. */ - - win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(win), _("Property Editor")); - gtk_window_set_resizable(GTK_WINDOW(win), TRUE); - gtk_window_set_default_size(GTK_WINDOW(win), 780, 560); - gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT); - gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(toplevel)); - gtk_window_set_destroy_with_parent(GTK_WINDOW(win), TRUE); - gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DIALOG); - gtk_container_set_border_width(GTK_CONTAINER(win), 4); - g_signal_connect(win, "delete-event", - G_CALLBACK(gtk_widget_hide_on_delete), NULL); - pe->widget = win; - - vbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(win), vbox); - - /* Property pages. */ - - notebook = gtk_notebook_new(); - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), TRUE); - gtk_container_add(GTK_CONTAINER(vbox), notebook); - pe->notebook = notebook; - - for (objtype = 0; objtype < NUM_OBJTYPES; objtype++) { - property_editor_add_page(pe, objtype); - } - - return pe; -} - -/************************************************************************//** - Get the property editor for the client's GUI. -****************************************************************************/ -struct property_editor *editprop_get_property_editor(void) -{ - if (!the_property_editor) { - the_property_editor = property_editor_new(); - } - return the_property_editor; -} - -/************************************************************************//** - Refresh the given property editor according to the given list of tiles. -****************************************************************************/ -void property_editor_load_tiles(struct property_editor *pe, - const struct tile_list *tiles) -{ - struct property_page *pp; - enum editor_object_type objtype; - int i; - const enum editor_object_type preferred[] = { - OBJTYPE_CITY, - OBJTYPE_UNIT, - OBJTYPE_STARTPOS, - OBJTYPE_TILE - }; - - if (!pe || !tiles) { - return; - } - - for (objtype = 0; objtype < NUM_OBJTYPES; objtype++) { - pp = property_editor_get_page(pe, objtype); - property_page_load_tiles(pp, tiles); - } - - for (i = 0; i < ARRAY_SIZE(preferred) - 1; i++) { - pp = property_editor_get_page(pe, preferred[i]); - if (property_page_get_num_objbinds(pp) > 0) { - break; - } - } - objtype = preferred[i]; - gtk_notebook_set_current_page(GTK_NOTEBOOK(pe->notebook), objtype); -} - -/************************************************************************//** - Show the property editor to the user, with given page corresponding to - 'objtype' in front (if a valid object type). -****************************************************************************/ -void property_editor_popup(struct property_editor *pe, - enum editor_object_type objtype) -{ - if (!pe || !pe->widget) { - return; - } - - gtk_widget_show_all(pe->widget); - - gtk_window_present(GTK_WINDOW(pe->widget)); - if (objtype < NUM_OBJTYPES) { - gtk_notebook_set_current_page(GTK_NOTEBOOK(pe->notebook), objtype); - } -} - -/************************************************************************//** - Hide the property editor window. -****************************************************************************/ -void property_editor_popdown(struct property_editor *pe) -{ - if (!pe || !pe->widget) { - return; - } - gtk_widget_hide(pe->widget); -} - -/************************************************************************//** - Handle a notification from the client core that some object has changed - state at the server side (including being removed). -****************************************************************************/ -void property_editor_handle_object_changed(struct property_editor *pe, - enum editor_object_type objtype, - int object_id, - bool remove) -{ - struct property_page *pp; - - if (!pe) { - return; - } - - if (!(objtype < NUM_OBJTYPES)) { - return; - } - - pp = property_editor_get_page(pe, objtype); - property_page_object_changed(pp, object_id, remove); -} - -/************************************************************************//** - Handle a notification that an object was created under the given tag. -****************************************************************************/ -void property_editor_handle_object_created(struct property_editor *pe, - int tag, int object_id) -{ - enum editor_object_type objtype; - struct property_page *pp; - - for (objtype = 0; objtype < NUM_OBJTYPES; objtype++) { - if (objtype_is_conserved(objtype)) { - continue; - } - pp = property_editor_get_page(pe, objtype); - property_page_object_created(pp, tag, object_id); - } -} - -/************************************************************************//** - Clear all property pages in the given property editor. -****************************************************************************/ -void property_editor_clear(struct property_editor *pe) -{ - enum editor_object_type objtype; - struct property_page *pp; - - if (!pe) { - return; - } - - for (objtype = 0; objtype < NUM_OBJTYPES; objtype++) { - pp = property_editor_get_page(pe, objtype); - property_page_clear_objbinds(pp); - property_page_clear_tags(pp); - } -} - -/************************************************************************//** - Clear and load objects into the property page corresponding to the given - object type. Also, make it the current shown notebook page. -****************************************************************************/ -void property_editor_reload(struct property_editor *pe, - enum editor_object_type objtype) -{ - struct property_page *pp; - - if (!pe) { - return; - } - - pp = property_editor_get_page(pe, objtype); - if (!pp) { - return; - } - - property_page_clear_objbinds(pp); - - switch (objtype) { - case OBJTYPE_PLAYER: - players_iterate(pplayer) { - property_page_add_objbind(pp, pplayer); - } players_iterate_end; - break; - case OBJTYPE_GAME: - property_page_add_objbind(pp, &game); - break; - case OBJTYPE_TILE: - case OBJTYPE_STARTPOS: - case OBJTYPE_UNIT: - case OBJTYPE_CITY: - case NUM_OBJTYPES: - break; - } - - property_page_fill_widgets(pp); - gtk_notebook_set_current_page(GTK_NOTEBOOK(pe->notebook), objtype); -} - -/************************************************************************//** - Create a new property filter from the given filter string. Result - should be freed by property_filter_free when no longed needed. - - The filter string is '|' ("or") separated list of '&' ("and") separated - lists of patterns. A pattern may be preceeded by '!' to have its result - negated. - - NB: If you change the behaviour of this function, be sure to update - the filter tooltip in property_page_new(). -****************************************************************************/ -static struct property_filter *property_filter_new(const char *filter) -{ - struct property_filter *pf; - struct pf_conjunction *pfc; - struct pf_pattern *pfp; - int or_clause_count, and_clause_count; - char *or_clauses[PF_MAX_CLAUSES], *and_clauses[PF_MAX_CLAUSES]; - const char *pattern; - int i, j; - - pf = fc_calloc(1, sizeof(*pf)); - - if (!filter || filter[0] == '\0') { - return pf; - } - - or_clause_count = get_tokens(filter, or_clauses, - PF_MAX_CLAUSES, - PF_DISJUNCTION_SEPARATOR); - - for (i = 0; i < or_clause_count; i++) { - if (or_clauses[i][0] == '\0') { - continue; - } - pfc = &pf->disjunction[pf->count]; - - and_clause_count = get_tokens(or_clauses[i], and_clauses, - PF_MAX_CLAUSES, - PF_CONJUNCTION_SEPARATOR); - - for (j = 0; j < and_clause_count; j++) { - if (and_clauses[j][0] == '\0') { - continue; - } - pfp = &pfc->conjunction[pfc->count]; - pattern = and_clauses[j]; - - switch (pattern[0]) { - case '!': - pfp->negate = TRUE; - pfp->text = fc_strdup(pattern + 1); - break; - default: - pfp->text = fc_strdup(pattern); - break; - } - pfc->count++; - } - free_tokens(and_clauses, and_clause_count); - pf->count++; - } - - free_tokens(or_clauses, or_clause_count); - - return pf; -} - -/************************************************************************//** - Returns TRUE if the filter matches the given object property. - - The filter matches if its truth value is TRUE. That is, it has at least - one OR clause in which all AND clauses are TRUE. An AND clause is TRUE - if its pattern matches the name of the given object property (case is - ignored), or it is negated and does not match. For example: - - a - Matches all properties whose names contain "a" (or "A"). - !a - Matches all properties whose names do not contain "a". - a|b - Matches all properties whose names contain "a" or "b". - a|b&c - Matches all properties whose names contain either an "a", - or contain both "b" and "c". - - NB: If you change the behaviour of this function, be sure to update - the filter tooltip in property_page_new(). -****************************************************************************/ -static bool property_filter_match(struct property_filter *pf, - const struct objprop *op) -{ - struct pf_pattern *pfp; - struct pf_conjunction *pfc; - const char *name; - bool match, or_result, and_result; - int i, j; - - if (!pf) { - return TRUE; - } - if (!op) { - return FALSE; - } - - name = objprop_get_name(op); - if (!name) { - return FALSE; - } - - if (pf->count < 1) { - return TRUE; - } - - or_result = FALSE; - - for (i = 0; i < pf->count; i++) { - pfc = &pf->disjunction[i]; - and_result = TRUE; - for (j = 0; j < pfc->count; j++) { - pfp = &pfc->conjunction[j]; - match = (pfp->text[0] == '\0' - || fc_strcasestr(name, pfp->text)); - if (pfp->negate) { - match = !match; - } - and_result = and_result && match; - if (!and_result) { - break; - } - } - or_result = or_result || and_result; - if (or_result) { - break; - } - } - - return or_result; -} - -/************************************************************************//** - Frees all memory used by the property filter. -****************************************************************************/ -static void property_filter_free(struct property_filter *pf) -{ - struct pf_pattern *pfp; - struct pf_conjunction *pfc; - int i, j; - - if (!pf) { - return; - } - - for (i = 0; i < pf->count; i++) { - pfc = &pf->disjunction[i]; - for (j = 0; j < pfc->count; j++) { - pfp = &pfc->conjunction[j]; - if (pfp->text != NULL) { - free(pfp->text); - pfp->text = NULL; - } - } - pfc->count = 0; - } - pf->count = 0; - free(pf); -} - -/************************************************************************//** - Returns a translated string name for the given "vision layer". -****************************************************************************/ -const char *vision_layer_get_name(enum vision_layer vl) -{ - switch (vl) { - case V_MAIN: - /* TRANS: Vision layer name. Feel free to leave untranslated. */ - return _("Seen (Main)"); - case V_INVIS: - /* TRANS: Vision layer name. Feel free to leave untranslated. */ - return _("Seen (Invis)"); - case V_SUBSURFACE: - /* TRANS: Vision layer name. Feel free to leave untranslated. */ - return _("Seen (Subsurface)"); - case V_COUNT: - break; - } - - log_error("%s(): Unrecognized vision layer %d.", __FUNCTION__, vl); - return _("Unknown"); -} diff --git a/client/gui-gtk-3.0/editprop.h b/client/gui-gtk-3.0/editprop.h deleted file mode 100644 index d40a52c38c..0000000000 --- a/client/gui-gtk-3.0/editprop.h +++ /dev/null @@ -1,42 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 2005 - The Freeciv Project - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__EDITPROP_H -#define FC__EDITPROP_H - -/* client */ -#include "editor.h" - -/* client/include */ -#include "editgui_g.h" - -struct tile_list; -struct property_editor; - -void property_editor_clear(struct property_editor *pe); -void property_editor_load_tiles(struct property_editor *pe, - const struct tile_list *tiles); -void property_editor_reload(struct property_editor *pe, - enum editor_object_type objtype); -void property_editor_popup(struct property_editor *pe, - enum editor_object_type objtype); -void property_editor_popdown(struct property_editor *pe); -void property_editor_handle_object_changed(struct property_editor *pe, - enum editor_object_type objtype, - int object_id, - bool remove); -void property_editor_handle_object_created(struct property_editor *pe, - int tag, int object_id); - -struct property_editor *editprop_get_property_editor(void); - -#endif /* FC__EDITPROP_H */ diff --git a/client/gui-gtk-3.0/finddlg.c b/client/gui-gtk-3.0/finddlg.c deleted file mode 100644 index db6f10b6ae..0000000000 --- a/client/gui-gtk-3.0/finddlg.c +++ /dev/null @@ -1,214 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" - -/* common */ -#include "game.h" -#include "player.h" - -/* client */ -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "dialogs.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapview.h" - -#include "finddlg.h" - -static struct gui_dialog *find_dialog_shell; -static GtkWidget *find_view; - -static void update_find_dialog(GtkListStore *store); - -static void find_response(struct gui_dialog *dlg, int response, gpointer data); -static void find_destroy_callback(GtkWidget *w, gpointer data); -static void find_selection_callback(GtkTreeSelection *selection, - GtkTreeModel *model); - -static struct tile *pos; - -/**********************************************************************//** - Popup the dialog 10% inside the main-window -**************************************************************************/ -void popup_find_dialog(void) -{ - if (!find_dialog_shell) { - GtkWidget *label; - GtkWidget *sw; - GtkListStore *store; - GtkTreeSelection *selection; - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - - pos = get_center_tile_mapcanvas(); - - gui_dialog_new(&find_dialog_shell, GTK_NOTEBOOK(bottom_notebook), NULL, - TRUE); - gui_dialog_set_title(find_dialog_shell, _("Find City")); - gui_dialog_set_default_size(find_dialog_shell, -1, 240); - - gui_dialog_add_button(find_dialog_shell, - GTK_STOCK_FIND, GTK_RESPONSE_ACCEPT); - gui_dialog_add_button(find_dialog_shell, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); - - gui_dialog_set_default_response(find_dialog_shell, GTK_RESPONSE_ACCEPT); - - gui_dialog_response_set_callback(find_dialog_shell, find_response); - - g_signal_connect(find_dialog_shell->vbox, "destroy", - G_CALLBACK(find_destroy_callback), NULL); - - store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), - 0, GTK_SORT_ASCENDING); - - find_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(find_view)); - g_object_unref(store); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(find_view)); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(find_view), FALSE); - - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(NULL, renderer, - "text", 0, NULL); - gtk_tree_view_column_set_sort_order(column, GTK_SORT_ASCENDING); - gtk_tree_view_append_column(GTK_TREE_VIEW(find_view), column); - - sw = gtk_scrolled_window_new(NULL, NULL); - g_object_set(sw, "margin", 2, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(sw), find_view); - - gtk_widget_set_hexpand(GTK_WIDGET(find_view), TRUE); - gtk_widget_set_vexpand(GTK_WIDGET(find_view), TRUE); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", find_view, - "label", _("Ci_ties:"), - "xalign", 0.0, "yalign", 0.5, NULL); - gtk_container_add(GTK_CONTAINER(find_dialog_shell->vbox), label); - gtk_container_add(GTK_CONTAINER(find_dialog_shell->vbox), sw); - - g_signal_connect(selection, "changed", - G_CALLBACK(find_selection_callback), store); - - update_find_dialog(store); - gtk_tree_view_focus(GTK_TREE_VIEW(find_view)); - - gui_dialog_show_all(find_dialog_shell); - } - - gui_dialog_raise(find_dialog_shell); -} - -/**********************************************************************//** - Update find dialog with current cities -**************************************************************************/ -static void update_find_dialog(GtkListStore *store) -{ - GtkTreeIter it; - - gtk_list_store_clear(store); - - players_iterate(pplayer) { - city_list_iterate(pplayer->cities, pcity) { - GValue value = { 0, }; - - gtk_list_store_append(store, &it); - - g_value_init(&value, G_TYPE_STRING); - g_value_set_static_string(&value, city_name_get(pcity)); - gtk_list_store_set_value(store, &it, 0, &value); - g_value_unset(&value); - - gtk_list_store_set(store, &it, 1, pcity, -1); - } city_list_iterate_end; - } players_iterate_end; -} - -/**********************************************************************//** - User responded to find dialog -**************************************************************************/ -static void find_response(struct gui_dialog *dlg, int response, gpointer data) -{ - if (response == GTK_RESPONSE_ACCEPT) { - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter it; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(find_view)); - - if (gtk_tree_selection_get_selected(selection, &model, &it)) { - struct city *pcity; - - gtk_tree_model_get(model, &it, 1, &pcity, -1); - - if (pcity) { - pos = pcity->tile; - } - } - } - gui_dialog_destroy(dlg); -} - -/**********************************************************************//** - Find dialog destroyed -**************************************************************************/ -static void find_destroy_callback(GtkWidget *w, gpointer data) -{ - can_slide = FALSE; - center_tile_mapcanvas(pos); - can_slide = TRUE; -} - -/**********************************************************************//** - User selected city from find dialog -**************************************************************************/ -static void find_selection_callback(GtkTreeSelection *selection, - GtkTreeModel *model) -{ - GtkTreeIter it; - struct city *pcity; - - if (!gtk_tree_selection_get_selected(selection, NULL, &it)) { - return; - } - - gtk_tree_model_get(model, &it, 1, &pcity, -1); - - if (pcity) { - can_slide = FALSE; - center_tile_mapcanvas(pcity->tile); - can_slide = TRUE; - } -} diff --git a/client/gui-gtk-3.0/finddlg.h b/client/gui-gtk-3.0/finddlg.h deleted file mode 100644 index 24ade7d378..0000000000 --- a/client/gui-gtk-3.0/finddlg.h +++ /dev/null @@ -1,20 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 2003 - The Freeciv Project - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__FINDDLG_H -#define FC__FINDDLG_H - -#include "finddlg_g.h" - -/* nothing to add */ - -#endif /* FC__FINDDLG_H */ diff --git a/client/gui-gtk-3.0/gamedlgs.c b/client/gui-gtk-3.0/gamedlgs.c deleted file mode 100644 index 8a6c4deae1..0000000000 --- a/client/gui-gtk-3.0/gamedlgs.c +++ /dev/null @@ -1,561 +0,0 @@ - /********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include - -/* utility */ -#include "log.h" -#include "shared.h" -#include "string_vector.h" -#include "support.h" - -/* common */ -#include "events.h" -#include "fcintl.h" -#include "government.h" -#include "multipliers.h" - -/* client */ -#include "client_main.h" -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "cityrep.h" -#include "dialogs.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "ratesdlg.h" - -#include "gamedlgs.h" - -/******************************************************************/ -static GtkWidget *rates_dialog_shell; -static GtkWidget *rates_gov_label; -static GtkWidget *rates_tax_toggle, *rates_lux_toggle, *rates_sci_toggle; -static GtkWidget *rates_tax_label, *rates_lux_label, *rates_sci_label; -static GtkWidget *rates_tax_scale, *rates_lux_scale, *rates_sci_scale; - -static GtkWidget *multiplier_dialog_shell; -static GtkWidget *multipliers_scale[MAX_NUM_MULTIPLIERS]; - -static gulong rates_tax_sig, rates_lux_sig, rates_sci_sig; -/******************************************************************/ - -static int rates_tax_value, rates_lux_value, rates_sci_value; - -static void rates_changed_callback(GtkWidget *range); - -/**********************************************************************//** - Set tax values to display -**************************************************************************/ -static void rates_set_values(int tax, int no_tax_scroll, - int lux, int no_lux_scroll, - int sci, int no_sci_scroll) -{ - char buf[64]; - int tax_lock, lux_lock, sci_lock; - int maxrate; - - tax_lock = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rates_tax_toggle)); - lux_lock = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rates_lux_toggle)); - sci_lock = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rates_sci_toggle)); - - if (NULL != client.conn.playing) { - maxrate = get_player_bonus(client.conn.playing, EFT_MAX_RATES); - } else { - maxrate = 100; - } - - /* This's quite a simple-minded "double check".. */ - tax = MIN(tax, maxrate); - lux = MIN(lux, maxrate); - sci = MIN(sci, maxrate); - - if (tax + sci + lux != 100) { - if (tax != rates_tax_value) { - if (!lux_lock) { - lux = MIN(MAX(100 - tax - sci, 0), maxrate); - } - if (!sci_lock) { - sci = MIN(MAX(100 - tax - lux, 0), maxrate); - } - } else if (lux != rates_lux_value) { - if (!tax_lock) { - tax = MIN(MAX(100 - lux - sci, 0), maxrate); - } - if (!sci_lock) { - sci = MIN(MAX(100 - lux - tax, 0), maxrate); - } - } else if (sci != rates_sci_value) { - if (!lux_lock) { - lux = MIN(MAX(100 - tax - sci, 0), maxrate); - } - if (!tax_lock) { - tax = MIN(MAX(100 - lux - sci, 0), maxrate); - } - } - - if (tax + sci + lux != 100) { - tax = rates_tax_value; - lux = rates_lux_value; - sci = rates_sci_value; - - rates_tax_value = -1; - rates_lux_value = -1; - rates_sci_value = -1; - - no_tax_scroll = 0; - no_lux_scroll = 0; - no_sci_scroll = 0; - } - } - - if (tax != rates_tax_value) { - fc_snprintf(buf, sizeof(buf), "%3d%%", tax); - - if (strcmp(buf, gtk_label_get_text(GTK_LABEL(rates_tax_label))) != 0) { - gtk_label_set_text(GTK_LABEL(rates_tax_label), buf); - } - if (!no_tax_scroll) { - g_signal_handler_block(rates_tax_scale, rates_tax_sig); - gtk_range_set_value(GTK_RANGE(rates_tax_scale), tax / 10); - g_signal_handler_unblock(rates_tax_scale, rates_tax_sig); - } - rates_tax_value = tax; - } - - if (lux != rates_lux_value) { - fc_snprintf(buf, sizeof(buf), "%3d%%", lux); - - if (strcmp(buf, gtk_label_get_text(GTK_LABEL(rates_lux_label))) != 0) { - gtk_label_set_text(GTK_LABEL(rates_lux_label), buf); - } - if (!no_lux_scroll) { - g_signal_handler_block(rates_lux_scale, rates_lux_sig); - gtk_range_set_value(GTK_RANGE(rates_lux_scale), lux / 10); - g_signal_handler_unblock(rates_lux_scale, rates_lux_sig); - } - rates_lux_value = lux; - } - - if (sci != rates_sci_value) { - fc_snprintf(buf, sizeof(buf), "%3d%%", sci); - - if (strcmp(buf, gtk_label_get_text(GTK_LABEL(rates_sci_label))) != 0) { - gtk_label_set_text(GTK_LABEL(rates_sci_label), buf); - } - if (!no_sci_scroll) { - g_signal_handler_block(rates_sci_scale, rates_sci_sig); - gtk_range_set_value(GTK_RANGE(rates_sci_scale), sci / 10); - g_signal_handler_unblock(rates_sci_scale, rates_sci_sig); - } - rates_sci_value = sci; - } -} - -/**********************************************************************//** - User changes rates -**************************************************************************/ -static void rates_changed_callback(GtkWidget *range) -{ - int percent = gtk_range_get_value(GTK_RANGE(range)); - - if (range == rates_tax_scale) { - int tax_value; - - tax_value = 10 * percent; - tax_value = MIN(tax_value, 100); - rates_set_values(tax_value, 1, rates_lux_value, 0, rates_sci_value, 0); - } else if (range == rates_lux_scale) { - int lux_value; - - lux_value = 10 * percent; - lux_value = MIN(lux_value, 100); - rates_set_values(rates_tax_value, 0, lux_value, 1, rates_sci_value, 0); - } else { - int sci_value; - - sci_value = 10 * percent; - sci_value = MIN(sci_value, 100); - rates_set_values(rates_tax_value, 0, rates_lux_value, 0, sci_value, 1); - } -} - -/**********************************************************************//** - User has responded to rates dialog -**************************************************************************/ -static void rates_command_callback(GtkWidget *w, gint response_id) -{ - if (response_id == GTK_RESPONSE_OK) { - dsend_packet_player_rates(&client.conn, rates_tax_value, rates_lux_value, - rates_sci_value); - } - gtk_widget_destroy(rates_dialog_shell); -} - -/**********************************************************************//** - Convert real multiplier display value to scale value -**************************************************************************/ -static int mult_to_scale(const struct multiplier *pmul, int val) -{ - return (val - pmul->start) / pmul->step; -} - -/**********************************************************************//** - Convert scale units to real multiplier display value -**************************************************************************/ -static int scale_to_mult(const struct multiplier *pmul, int scale) -{ - return scale * pmul->step + pmul->start; -} - -/**********************************************************************//** - Format value for multiplier scales -**************************************************************************/ -static gchar *multiplier_value_callback(GtkScale *scale, gdouble value, - void *udata) -{ - const struct multiplier *pmul = udata; - - return g_strdup_printf("%d", scale_to_mult(pmul, value)); -} - -/**********************************************************************//** - User has responded to multipliers dialog -**************************************************************************/ -static void multipliers_command_callback(GtkWidget *w, gint response_id) -{ - struct packet_player_multiplier mul; - - if (response_id == GTK_RESPONSE_OK && can_client_issue_orders()) { - multipliers_iterate(m) { - Multiplier_type_id i = multiplier_index(m); - int value = gtk_range_get_value(GTK_RANGE(multipliers_scale[i])); - - mul.multipliers[i] = scale_to_mult(m, value); - } multipliers_iterate_end; - mul.count = multiplier_count(); - send_packet_player_multiplier(&client.conn, &mul); - } - gtk_widget_destroy(multiplier_dialog_shell); -} - -/**********************************************************************//** - Update values in multipliers dialog -**************************************************************************/ -static void multiplier_dialog_update_values(bool set_positions) -{ - multipliers_iterate(pmul) { - Multiplier_type_id multiplier = multiplier_index(pmul); - int val = player_multiplier_value(client_player(), pmul); - int target = player_multiplier_target_value(client_player(), pmul); - - fc_assert(multiplier < ARRAY_SIZE(multipliers_scale)); - fc_assert(scale_to_mult(pmul, mult_to_scale(pmul, val)) == val); - fc_assert(scale_to_mult(pmul, mult_to_scale(pmul, target)) == target); - - gtk_scale_clear_marks(GTK_SCALE(multipliers_scale[multiplier])); - gtk_scale_add_mark(GTK_SCALE(multipliers_scale[multiplier]), - mult_to_scale(pmul, val), GTK_POS_BOTTOM, - /* TRANS: current value of policy in force */ - Q_("?multiplier:Cur")); - - if (set_positions) { - gtk_range_set_value(GTK_RANGE(multipliers_scale[multiplier]), - mult_to_scale(pmul, target)); - } - } multipliers_iterate_end; -} - -/**********************************************************************//** - Callback when server indicates multiplier values have changed -**************************************************************************/ -void real_multipliers_dialog_update(void *unused) -{ - if (!multiplier_dialog_shell) { - return; - } - - /* If dialog belongs to a player rather than an observer, we don't - * want to lose any local changes made by the player. - * This dialog should be the only way the values can change, anyway. */ - multiplier_dialog_update_values(!can_client_issue_orders()); -} - -/**********************************************************************//** - Create multipliers dialog -**************************************************************************/ -static GtkWidget *create_multiplier_dialog(void) -{ - GtkWidget *shell, *content; - GtkWidget *label, *scale; - - if (can_client_issue_orders()) { - shell = gtk_dialog_new_with_buttons(_("Change policies"), - NULL, - 0, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, - GTK_RESPONSE_OK, - NULL); - } else { - shell = gtk_dialog_new_with_buttons(_("Policies"), - NULL, - 0, - GTK_STOCK_CLOSE, - GTK_RESPONSE_CLOSE, - NULL); - } - setup_dialog(shell, toplevel); - - gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_MOUSE); - content = gtk_dialog_get_content_area(GTK_DIALOG(shell)); - - if (can_client_issue_orders()) { - label = gtk_label_new(_("Changes will not take effect until next turn.")); - gtk_box_pack_start( GTK_BOX( content ), label, FALSE, FALSE, 0); - } - - multipliers_iterate(pmul) { - Multiplier_type_id multiplier = multiplier_index(pmul); - - fc_assert(multiplier < ARRAY_SIZE(multipliers_scale)); - label = gtk_label_new(multiplier_name_translation(pmul)); - /* Map each multiplier step to a single step on the UI, to enforce - * the step size. */ - scale = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, - mult_to_scale(pmul, pmul->start), - mult_to_scale(pmul, pmul->stop), 1); - multipliers_scale[multiplier] = scale; - gtk_range_set_increments(GTK_RANGE(multipliers_scale[multiplier]), - 1, MAX(2, mult_to_scale(pmul, pmul->stop) / 10)); - g_signal_connect(multipliers_scale[multiplier], "format-value", - G_CALLBACK(multiplier_value_callback), pmul); - g_signal_connect(multipliers_scale[multiplier], "destroy", - G_CALLBACK(gtk_widget_destroyed), - &multipliers_scale[multiplier]); - gtk_box_pack_start( GTK_BOX( content ), label, TRUE, TRUE, 5 ); - gtk_box_pack_start( GTK_BOX( content ), scale, TRUE, TRUE, 5 ); - gtk_widget_set_sensitive(multipliers_scale[multiplier], - can_client_issue_orders() - && multiplier_can_be_changed(pmul, client_player())); - } multipliers_iterate_end; - - multiplier_dialog_update_values(TRUE); - - g_signal_connect(shell, "destroy", - G_CALLBACK(gtk_widget_destroyed), &multiplier_dialog_shell); - - g_signal_connect(shell, "response", - G_CALLBACK(multipliers_command_callback), NULL); - - gtk_widget_show_all(content); - - return shell; -} - -/**********************************************************************//** - Popup multipliers dialog -**************************************************************************/ -void popup_multiplier_dialog(void) -{ - if (!multiplier_dialog_shell) { - multiplier_dialog_shell = create_multiplier_dialog(); - } - - if (!multiplier_dialog_shell) { - return; - } - - gtk_window_present(GTK_WINDOW(multiplier_dialog_shell)); -} - -/**********************************************************************//** - Create rates dialog -**************************************************************************/ -static GtkWidget *create_rates_dialog(void) -{ - GtkWidget *shell, *content; - GtkWidget *frame, *hgrid; - int i; - - if (!can_client_issue_orders()) { - return NULL; - } - - shell = gtk_dialog_new_with_buttons(_("Select tax, luxury and science rates"), - NULL, - 0, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, - GTK_RESPONSE_OK, - NULL); - setup_dialog(shell, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_OK); - gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_MOUSE); - content = gtk_dialog_get_content_area(GTK_DIALOG(shell)); - - rates_gov_label = gtk_label_new(""); - gtk_box_pack_start( GTK_BOX( content ), rates_gov_label, TRUE, TRUE, 5 ); - - frame = gtk_frame_new( _("Tax") ); - gtk_box_pack_start( GTK_BOX( content ), frame, TRUE, TRUE, 5 ); - - hgrid = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hgrid), 10); - gtk_container_add(GTK_CONTAINER(frame), hgrid); - - rates_tax_scale = - gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 10, 1); - gtk_range_set_increments(GTK_RANGE(rates_tax_scale), 1, 1); - for (i = 0; i <= 10; i++) { - gtk_scale_add_mark(GTK_SCALE(rates_tax_scale), i, GTK_POS_TOP, NULL); - } - gtk_widget_set_size_request(rates_tax_scale, 300, 40); - gtk_scale_set_digits(GTK_SCALE(rates_tax_scale), 0); - gtk_scale_set_draw_value(GTK_SCALE(rates_tax_scale), FALSE); - gtk_container_add(GTK_CONTAINER(hgrid), rates_tax_scale); - - rates_tax_label = gtk_label_new(" 0%"); - gtk_container_add(GTK_CONTAINER(hgrid), rates_tax_label); - gtk_widget_set_size_request(rates_tax_label, 40, -1); - - rates_tax_toggle = gtk_check_button_new_with_label( _("Lock") ); - gtk_container_add(GTK_CONTAINER(hgrid), rates_tax_toggle); - - frame = gtk_frame_new( _("Luxury") ); - gtk_box_pack_start( GTK_BOX( content ), frame, TRUE, TRUE, 5 ); - - hgrid = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hgrid), 10); - gtk_container_add(GTK_CONTAINER(frame), hgrid); - - rates_lux_scale = - gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 10, 1); - gtk_range_set_increments(GTK_RANGE(rates_lux_scale), 1, 1); - for (i = 0; i <= 10; i++) { - gtk_scale_add_mark(GTK_SCALE(rates_lux_scale), i, GTK_POS_TOP, NULL); - } - gtk_widget_set_size_request(rates_lux_scale, 300, 40); - gtk_scale_set_digits(GTK_SCALE(rates_lux_scale), 0); - gtk_scale_set_draw_value(GTK_SCALE(rates_lux_scale), FALSE); - gtk_container_add(GTK_CONTAINER(hgrid), rates_lux_scale); - - rates_lux_label = gtk_label_new(" 0%"); - gtk_container_add(GTK_CONTAINER(hgrid), rates_lux_label); - gtk_widget_set_size_request(rates_lux_label, 40, -1); - - rates_lux_toggle = gtk_check_button_new_with_label( _("Lock") ); - gtk_container_add(GTK_CONTAINER(hgrid), rates_lux_toggle); - - frame = gtk_frame_new( _("Science") ); - gtk_box_pack_start( GTK_BOX( content ), frame, TRUE, TRUE, 5 ); - - hgrid = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hgrid), 10); - gtk_container_add(GTK_CONTAINER(frame), hgrid); - - rates_sci_scale = - gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 10, 1); - gtk_range_set_increments(GTK_RANGE(rates_sci_scale), 1, 1); - for (i = 0; i <= 10; i++) { - gtk_scale_add_mark(GTK_SCALE(rates_sci_scale), i, GTK_POS_TOP, NULL); - } - gtk_widget_set_size_request(rates_sci_scale, 300, 40); - gtk_scale_set_digits(GTK_SCALE(rates_sci_scale), 0); - gtk_scale_set_draw_value(GTK_SCALE(rates_sci_scale), FALSE); - gtk_container_add(GTK_CONTAINER(hgrid), rates_sci_scale); - - rates_sci_label = gtk_label_new(" 0%"); - gtk_container_add(GTK_CONTAINER(hgrid), rates_sci_label); - gtk_widget_set_size_request(rates_sci_label, 40, -1); - - rates_sci_toggle = gtk_check_button_new_with_label( _("Lock") ); - gtk_container_add(GTK_CONTAINER(hgrid), rates_sci_toggle); - - - g_signal_connect(shell, "response", - G_CALLBACK(rates_command_callback), NULL); - g_signal_connect(shell, "destroy", - G_CALLBACK(gtk_widget_destroyed), &rates_dialog_shell); - - gtk_widget_show_all(shell); - - rates_tax_value=-1; - rates_lux_value=-1; - rates_sci_value=-1; - - rates_tax_sig = - g_signal_connect_after(rates_tax_scale, "value-changed", - G_CALLBACK(rates_changed_callback), NULL); - - rates_lux_sig = - g_signal_connect_after(rates_lux_scale, "value-changed", - G_CALLBACK(rates_changed_callback), NULL); - - rates_sci_sig = - g_signal_connect_after(rates_sci_scale, "value-changed", - G_CALLBACK(rates_changed_callback), NULL); - - rates_set_values(client.conn.playing->economic.tax, 0, - client.conn.playing->economic.luxury, 0, - client.conn.playing->economic.science, 0); - return shell; -} - -/**********************************************************************//** - Popup rates dialog -**************************************************************************/ -void popup_rates_dialog(void) -{ - if (!can_client_issue_orders()) { - return; - } - - if (!rates_dialog_shell) { - rates_dialog_shell = create_rates_dialog(); - } - if (!rates_dialog_shell) { - return; - } - - gchar *buf = g_strdup_printf(_("%s max rate: %d%%"), - government_name_for_player(client.conn.playing), - get_player_bonus(client.conn.playing, EFT_MAX_RATES)); - - gtk_label_set_text(GTK_LABEL(rates_gov_label), buf); - g_free(buf); - gtk_range_set_fill_level(GTK_RANGE(rates_tax_scale), - get_player_bonus(client.conn.playing, - EFT_MAX_RATES)/10); - gtk_range_set_fill_level(GTK_RANGE(rates_lux_scale), - get_player_bonus(client.conn.playing, - EFT_MAX_RATES)/10); - gtk_range_set_fill_level(GTK_RANGE(rates_sci_scale), - get_player_bonus(client.conn.playing, - EFT_MAX_RATES)/10); - - gtk_window_present(GTK_WINDOW(rates_dialog_shell)); -} diff --git a/client/gui-gtk-3.0/gamedlgs.h b/client/gui-gtk-3.0/gamedlgs.h deleted file mode 100644 index 76f88067f8..0000000000 --- a/client/gui-gtk-3.0/gamedlgs.h +++ /dev/null @@ -1,18 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__GAMEDLGS_H -#define FC__GAMEDLGS_H - -void popup_multiplier_dialog(void); - -#endif /* FC__GAMEDLGS_H */ diff --git a/client/gui-gtk-3.0/gotodlg.c b/client/gui-gtk-3.0/gotodlg.c deleted file mode 100644 index e29ab8f473..0000000000 --- a/client/gui-gtk-3.0/gotodlg.c +++ /dev/null @@ -1,549 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include - -/* utility */ -#include "astring.h" -#include "fcintl.h" -#include "log.h" -#include "support.h" - -/* common */ -#include "game.h" -#include "map.h" -#include "packets.h" -#include "player.h" -#include "unit.h" -#include "unitlist.h" - -/* client */ -#include "client_main.h" -#include "control.h" -#include "goto.h" -#include "options.h" -#include "text.h" - -/* clien/gui-gtk-3.0 */ -#include "plrdlg.h" -#include "dialogs.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapview.h" - -#include "gotodlg.h" - - -static GtkWidget *dshell = NULL; -static GtkWidget *view; -static GtkWidget *source; -static GtkWidget *all_toggle; -static GtkListStore *goto_list_store; -static GtkTreeSelection *goto_list_selection; -struct tile *original_tile; -static bool gotodlg_updating = FALSE; - -static void update_goto_dialog(GtkToggleButton *button); -static void update_source_label(void); -static void refresh_airlift_column(void); -static void refresh_airlift_button(void); -static void goto_selection_callback(GtkTreeSelection *selection, gpointer data); - -static struct city *get_selected_city(void); - -enum { - CMD_AIRLIFT = 1, CMD_GOTO -}; - -enum { - GD_COL_CITY_ID = 0, /* Not shown if not compiled with --enable-debug. */ - GD_COL_CITY_NAME, - GD_COL_FLAG, - GD_COL_NATION, - GD_COL_AIRLIFT, - - GD_COL_NUM -}; - -/**********************************************************************//** - User has responded to goto dialog -**************************************************************************/ -static void goto_cmd_callback(GtkWidget *dlg, gint arg) -{ - switch (arg) { - case GTK_RESPONSE_CANCEL: - center_tile_mapcanvas(original_tile); - break; - - case CMD_AIRLIFT: - { - struct city *pdestcity = get_selected_city(); - - if (pdestcity) { - unit_list_iterate(get_units_in_focus(), punit) { - if (unit_can_airlift_to(punit, pdestcity)) { - request_unit_airlift(punit, pdestcity); - } - } unit_list_iterate_end; - } - } - break; - - case CMD_GOTO: - { - struct city *pdestcity = get_selected_city(); - - if (pdestcity) { - unit_list_iterate(get_units_in_focus(), punit) { - send_goto_tile(punit, pdestcity->tile); - } unit_list_iterate_end; - } - } - break; - - default: - break; - } - - gtk_widget_destroy(dlg); - dshell = NULL; -} - -/**********************************************************************//** - Create goto -dialog for gotoing or airlifting unit -**************************************************************************/ -static void create_goto_dialog(void) -{ - GtkWidget *sw, *label, *frame, *vbox; - GtkCellRenderer *rend; - GtkTreeViewColumn *col; - - dshell = gtk_dialog_new_with_buttons(_("Goto/Airlift Unit"), - NULL, - 0, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - _("Air_lift"), - CMD_AIRLIFT, - _("_Goto"), - CMD_GOTO, - NULL); - setup_dialog(dshell, toplevel); - gtk_window_set_position(GTK_WINDOW(dshell), GTK_WIN_POS_MOUSE); - gtk_dialog_set_default_response(GTK_DIALOG(dshell), CMD_GOTO); - g_signal_connect(dshell, "destroy", - G_CALLBACK(gtk_widget_destroyed), &dshell); - g_signal_connect(dshell, "response", - G_CALLBACK(goto_cmd_callback), NULL); - - source = gtk_label_new("" /* filled in later */); - gtk_label_set_line_wrap(GTK_LABEL(source), TRUE); - gtk_label_set_justify(GTK_LABEL(source), GTK_JUSTIFY_CENTER); - gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dshell))), - source, FALSE, FALSE, 0); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "label", _("Select destination ci_ty"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - frame = gtk_frame_new(""); - gtk_frame_set_label_widget(GTK_FRAME(frame), label); - gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dshell))), - frame, TRUE, TRUE, 0); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 6); - gtk_container_add(GTK_CONTAINER(frame), vbox); - - goto_list_store = gtk_list_store_new(GD_COL_NUM, G_TYPE_INT, G_TYPE_STRING, - GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(goto_list_store), - GD_COL_CITY_NAME, GTK_SORT_ASCENDING); - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(goto_list_store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(goto_list_store); - goto_list_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE); - gtk_tree_view_set_search_column(GTK_TREE_VIEW(view), GD_COL_CITY_NAME); - gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), TRUE); - - /* Set the mnemonic in the frame label to focus the city list */ - gtk_label_set_mnemonic_widget(GTK_LABEL(label), view); - -#ifdef FREECIV_DEBUG - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(_("Id"), rend, - "text", GD_COL_CITY_ID, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - gtk_tree_view_column_set_sort_column_id(col, GD_COL_CITY_ID); -#endif /* FREECIV_DEBUG */ - - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(_("City"), rend, - "text", GD_COL_CITY_NAME, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - gtk_tree_view_column_set_sort_column_id(col, GD_COL_CITY_NAME); - - rend = gtk_cell_renderer_pixbuf_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, rend, - "pixbuf", GD_COL_FLAG, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(_("Nation"), rend, - "text", GD_COL_NATION, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - gtk_tree_view_column_set_sort_column_id(col, GD_COL_NATION); - - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(_("Airlift"), rend, - "text", GD_COL_AIRLIFT, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - gtk_tree_view_column_set_sort_column_id(col, GD_COL_AIRLIFT); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_container_add(GTK_CONTAINER(sw), view); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); - gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 200); - - gtk_container_add(GTK_CONTAINER(vbox), sw); - - all_toggle = gtk_check_button_new_with_mnemonic(_("Show _All Cities")); - gtk_container_add(GTK_CONTAINER(vbox), all_toggle); - - g_signal_connect(all_toggle, "toggled", G_CALLBACK(update_goto_dialog), NULL); - - g_signal_connect(goto_list_selection, "changed", - G_CALLBACK(goto_selection_callback), NULL); - - gtk_widget_show_all(dshell); - - original_tile = get_center_tile_mapcanvas(); - - update_source_label(); - update_goto_dialog(GTK_TOGGLE_BUTTON(all_toggle)); - gtk_tree_view_focus(GTK_TREE_VIEW(view)); -} - -/**********************************************************************//** - Popup the dialog -**************************************************************************/ -void popup_goto_dialog(void) -{ - if (!can_client_issue_orders() || get_num_units_in_focus() == 0) { - return; - } - - if (!dshell) { - create_goto_dialog(); - } - - gtk_window_present(GTK_WINDOW(dshell)); -} - -/**********************************************************************//** - Return currently selected city -**************************************************************************/ -static struct city *get_selected_city(void) -{ - GtkTreeModel *model; - GtkTreeIter it; - int city_id; - - if (!gtk_tree_selection_get_selected(goto_list_selection, NULL, &it)) { - return NULL; - } - - model = gtk_tree_view_get_model(GTK_TREE_VIEW(view)); - - gtk_tree_model_get(model, &it, GD_COL_CITY_ID, &city_id, -1); - - return game_city_by_number(city_id); -} - -/**********************************************************************//** - Appends the list of the city owned by the player in the goto dialog. -**************************************************************************/ -static bool list_store_append_player_cities(GtkListStore *store, - const struct player *pplayer) -{ - GtkTreeIter it; - struct nation_type *pnation = nation_of_player(pplayer); - const char *nation = nation_adjective_translation(pnation); - GdkPixbuf *pixbuf; - - if (city_list_size(pplayer->cities) == 0) { - return FALSE; - } - - pixbuf = get_flag(pnation); - - city_list_iterate(pplayer->cities, pcity) { - gtk_list_store_append(store, &it); - gtk_list_store_set(store, &it, - GD_COL_CITY_ID, pcity->id, - GD_COL_CITY_NAME, city_name_get(pcity), - GD_COL_FLAG, pixbuf, - GD_COL_NATION, nation, - /* GD_COL_AIRLIFT is populated later */ - -1); - } city_list_iterate_end; - g_object_unref(pixbuf); - - return TRUE; -} - -/**********************************************************************//** - Refresh the label that shows where the selected unit(s) currently are - (and the relevant cities' airlift capacities, if relevant). -**************************************************************************/ -static void update_source_label(void) -{ - /* Arbitrary limit to stop the label getting ridiculously long */ - static const int max_cities = 10; - struct { - const struct city *city; - struct unit_list *units; - } cities[max_cities]; - int ncities = 0; - bool too_many = FALSE; - bool no_city = FALSE; /* any units not in a city? */ - struct astring strs[max_cities]; - int nstrs; - char *last_str; - const char *descriptions[max_cities+1]; - int i; - - /* Sanity check: if no units selected, give up */ - if (unit_list_size(get_units_in_focus()) == 0) { - gtk_label_set_text(GTK_LABEL(source), _("No units selected.")); - return; - } - - /* Divide selected units up into a list of unique cities */ - unit_list_iterate(get_units_in_focus(), punit) { - const struct city *pcity = tile_city(unit_tile(punit)); - if (pcity) { - /* Inefficient, but it's not a long list */ - for (i = 0; i < ncities; i++) { - if (cities[i].city == pcity) { - unit_list_append(cities[i].units, punit); - break; - } - } - if (i == ncities) { - if (ncities < max_cities) { - cities[ncities].city = pcity; - cities[ncities].units = unit_list_new(); - unit_list_append(cities[ncities].units, punit); - ncities++; - } else { - too_many = TRUE; - break; - } - } - } else { - no_city = TRUE; - } - } unit_list_iterate_end; - - /* Describe the individual cities. */ - for (i = 0; i < ncities; i++) { - const char *air_text = get_airlift_text(cities[i].units, NULL); - - astr_init(&strs[i]); - if (air_text != NULL) { - astr_add(&strs[i], - /* TRANS: goto/airlift dialog. "Paris (airlift: 2/4)". - * A set of these appear in an "and"-separated list. */ - _("%s (airlift: %s)"), - city_name_get(cities[i].city), air_text); - } else { - astr_add(&strs[i], "%s", city_name_get(cities[i].city)); - } - descriptions[i] = astr_str(&strs[i]); - unit_list_destroy(cities[i].units); - } - if (too_many) { - /* TRANS: goto/airlift dialog. Too many cities to list, some omitted. - * Appears at the end of an "and"-separated list. */ - descriptions[ncities] = last_str = fc_strdup(Q_("?gotodlg:more")); - nstrs = ncities+1; - } else if (no_city) { - /* TRANS: goto/airlift dialog. For units not currently in a city. - * Appears at the end of an "and"-separated list. */ - descriptions[ncities] = last_str = fc_strdup(Q_("?gotodlg:no city")); - nstrs = ncities+1; - } else { - last_str = NULL; - nstrs = ncities; - } - - /* Finally, update the label. */ - { - struct astring label = ASTRING_INIT, list = ASTRING_INIT; - astr_set(&label, - /* TRANS: goto/airlift dialog. Current location of units; %s is an - * "and"-separated list of cities and associated info */ - _("Currently in: %s"), - astr_build_and_list(&list, descriptions, nstrs)); - astr_free(&list); - gtk_label_set_text(GTK_LABEL(source), astr_str(&label)); - astr_free(&label); - } - - /* Clear up. */ - for (i = 0; i < ncities; i++) { - astr_free(&strs[i]); - } - free(last_str); /* might have been NULL */ -} - -/**********************************************************************//** - Refresh city list (in response to "all cities" checkbox changing). -**************************************************************************/ -static void update_goto_dialog(GtkToggleButton *button) -{ - bool nonempty = FALSE; - - if (!client_has_player()) { - /* Case global observer. */ - return; - } - - gotodlg_updating = TRUE; - - gtk_list_store_clear(goto_list_store); - - if (gtk_toggle_button_get_active(button)) { - players_iterate(pplayer) { - nonempty |= list_store_append_player_cities(goto_list_store, pplayer); - } players_iterate_end; - } else { - nonempty |= list_store_append_player_cities(goto_list_store, client_player()); - } - - gotodlg_updating = FALSE; - - refresh_airlift_column(); - - if (!nonempty) { - /* No selection causes callbacks to fire, causing also Airlift button - * to update. Do it here. */ - refresh_airlift_button(); - } -} - -/**********************************************************************//** - Refresh airlift column in city list (without tearing everything down). -**************************************************************************/ -static void refresh_airlift_column(void) -{ - GtkTreeIter iter; - bool valid; - - valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(goto_list_store), &iter); - while (valid) { - int city_id; - const struct city *pcity; - const char *air_text; - - gtk_tree_model_get(GTK_TREE_MODEL(goto_list_store), &iter, - GD_COL_CITY_ID, &city_id, -1); - pcity = game_city_by_number(city_id); - fc_assert_ret(pcity != NULL); - air_text = get_airlift_text(get_units_in_focus(), pcity); - gtk_list_store_set(GTK_LIST_STORE(goto_list_store), &iter, - GD_COL_AIRLIFT, air_text ? air_text : "-", -1); - valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(goto_list_store), &iter); - } -} - -/**********************************************************************//** - Refresh the state of the "Airlift" button for the currently selected - unit(s) and city. -**************************************************************************/ -static void refresh_airlift_button(void) -{ - struct city *pdestcity = get_selected_city(); - - if (NULL != pdestcity) { - bool can_airlift = FALSE; - - /* Allow action if any of the selected units can airlift. */ - unit_list_iterate(get_units_in_focus(), punit) { - if (unit_can_airlift_to(punit, pdestcity)) { - can_airlift = TRUE; - break; - } - } unit_list_iterate_end; - - if (can_airlift) { - gtk_dialog_set_response_sensitive(GTK_DIALOG(dshell), - CMD_AIRLIFT, TRUE); - return; - } - } - gtk_dialog_set_response_sensitive(GTK_DIALOG(dshell), CMD_AIRLIFT, FALSE); -} - -/**********************************************************************//** - Update goto dialog. button tells if cities of all players or just - client's player should be listed. -**************************************************************************/ -static void goto_selection_callback(GtkTreeSelection *selection, - gpointer data) -{ - struct city *pdestcity; - - if (gotodlg_updating) { - return; - } - - pdestcity = get_selected_city(); - - if (NULL != pdestcity) { - center_tile_mapcanvas(city_tile(pdestcity)); - } - refresh_airlift_button(); -} - -/**********************************************************************//** - Called when the set of units in focus has changed; updates airlift info -**************************************************************************/ -void goto_dialog_focus_units_changed(void) -{ - /* Is the dialog currently being displayed? */ - if (dshell) { - /* Location of current units and ability to airlift may have changed */ - update_source_label(); - refresh_airlift_column(); - refresh_airlift_button(); - } -} diff --git a/client/gui-gtk-3.0/gotodlg.h b/client/gui-gtk-3.0/gotodlg.h deleted file mode 100644 index 42b2a48eac..0000000000 --- a/client/gui-gtk-3.0/gotodlg.h +++ /dev/null @@ -1,20 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__GOTODLG_H -#define FC__GOTODLG_H - -#include "gotodlg_g.h" - -void goto_dialog_focus_units_changed(void); - -#endif /* FC__GOTODLG_H */ diff --git a/client/gui-gtk-3.0/graphics.c b/client/gui-gtk-3.0/graphics.c deleted file mode 100644 index 1f2d6154db..0000000000 --- a/client/gui-gtk-3.0/graphics.c +++ /dev/null @@ -1,112 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include - -/* utility */ -#include "log.h" -#include "mem.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "game.h" -#include "movement.h" -#include "unit.h" -#include "version.h" - -/* client */ -#include "climisc.h" -#include "colors.h" -#include "mapview_g.h" -#include "options.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "gui_main.h" - -#include "graphics.h" - -struct sprite *intro_gfx_sprite; - -GdkCursor *fc_cursors[CURSOR_LAST][NUM_CURSOR_FRAMES]; - -/***********************************************************************//** - Returns TRUE to indicate that gtk3-client supports given view type -***************************************************************************/ -bool is_view_supported(enum ts_type type) -{ - switch (type) { - case TS_ISOMETRIC: - case TS_OVERHEAD: - return TRUE; - case TS_3D: - return FALSE; - } - - return FALSE; -} - -/***********************************************************************//** - Loading tileset of the specified type -***************************************************************************/ -void tileset_type_set(enum ts_type type) -{ -} - -#define COLOR_MOTTO_FACE_R 0x2D -#define COLOR_MOTTO_FACE_G 0x71 -#define COLOR_MOTTO_FACE_B 0xE3 - -/***********************************************************************//** - Load cursor sprites -***************************************************************************/ -void load_cursors(void) -{ - enum cursor_type cursor; - GdkDisplay *display = gdk_display_get_default(); - int frame; - - for (cursor = 0; cursor < CURSOR_LAST; cursor++) { - for (frame = 0; frame < NUM_CURSOR_FRAMES; frame++) { - int hot_x, hot_y; - struct sprite *sprite - = get_cursor_sprite(tileset, cursor, &hot_x, &hot_y, frame); - GdkPixbuf *pixbuf = sprite_get_pixbuf(sprite); - - fc_cursors[cursor][frame] = gdk_cursor_new_from_pixbuf(display, pixbuf, - hot_x, hot_y); - g_object_unref(G_OBJECT(pixbuf)); - } - } -} - -/***********************************************************************//** - This function is so that packhand.c can be gui-independent, and - not have to deal with Sprites itself. -***************************************************************************/ -void free_intro_radar_sprites(void) -{ - if (intro_gfx_sprite) { - free_sprite(intro_gfx_sprite); - intro_gfx_sprite = NULL; - } -} diff --git a/client/gui-gtk-3.0/graphics.h b/client/gui-gtk-3.0/graphics.h deleted file mode 100644 index 2e706fda90..0000000000 --- a/client/gui-gtk-3.0/graphics.h +++ /dev/null @@ -1,32 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__GRAPHICS_H -#define FC__GRAPHICS_H - -#include - -/* client */ -#include "graphics_g.h" -#include "mapview_common.h" - -/* client/gui-gtk-3.0 */ -#include "canvas.h" -#include "sprite.h" - -extern struct sprite *intro_gfx_sprite; - -/* This name is to avoid a naming conflict with a global 'cursors' - * variable in GTK+-2.6. See PR#12459. */ -extern GdkCursor *fc_cursors[CURSOR_LAST][NUM_CURSOR_FRAMES]; - -#endif /* FC__GRAPHICS_H */ diff --git a/client/gui-gtk-3.0/gui_main.c b/client/gui-gtk-3.0/gui_main.c deleted file mode 100644 index 4c60594e8e..0000000000 --- a/client/gui-gtk-3.0/gui_main.c +++ /dev/null @@ -1,2424 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef AUDIO_SDL -/* Though it would happily compile without this include, - * it is needed for sound to work. - * It defines "main" macro to rename our main() so that - * it can install SDL's own. */ -#ifdef SDL2_PLAIN_INCLUDE -#include -#else /* PLAIN_INCLUDE */ -#include -#endif /* PLAIN_INCLUDE */ -#endif /* AUDIO_SDL */ - -#ifdef HAVE_LOCALE_H -#include -#endif -#include -#include -#include -#include -#include - -#ifdef HAVE_UNISTD_H -#include -#endif - -#include -#include - -/* utility */ -#include "fc_cmdline.h" -#include "fciconv.h" -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "support.h" - -/* common */ -#include "dataio.h" -#include "featured_text.h" -#include "game.h" -#include "government.h" -#include "map.h" -#include "unitlist.h" -#include "version.h" - -/* client */ -#include "client_main.h" -#include "climisc.h" -#include "clinet.h" -#include "colors.h" -#include "connectdlg_common.h" -#include "control.h" -#include "editor.h" -#include "options.h" -#include "text.h" -#include "tilespec.h" -#include "zoom.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "citizensinfo.h" -#include "connectdlg.h" -#include "cma_fe.h" -#include "dialogs.h" -#include "diplodlg.h" -#include "editgui.h" -#include "gotodlg.h" -#include "graphics.h" -#include "gui_stuff.h" -#include "happiness.h" -#include "inteldlg.h" -#include "mapctrl.h" -#include "mapview.h" -#include "menu.h" -#include "messagewin.h" -#include "optiondlg.h" -#include "pages.h" -#include "plrdlg.h" -#include "luaconsole.h" -#include "spaceshipdlg.h" -#include "repodlgs.h" -#include "voteinfo_bar.h" - -#include "gui_main.h" - -const char *client_string = "gui-gtk-3.0"; - -GtkWidget *map_canvas; /* GtkDrawingArea */ -GtkWidget *map_horizontal_scrollbar; -GtkWidget *map_vertical_scrollbar; - -GtkWidget *overview_canvas; /* GtkDrawingArea */ -GtkWidget *overview_scrolled_window; /* GtkScrolledWindow */ -/* The two values below define the width and height of the map overview. The - * first set of values (2*62, 2*46) define the size for a netbook display. For - * bigger displays the values are doubled (default). */ -#define OVERVIEW_CANVAS_STORE_WIDTH_NETBOOK (2 * 64) -#define OVERVIEW_CANVAS_STORE_HEIGHT_NETBOOK (2 * 46) -#define OVERVIEW_CANVAS_STORE_WIDTH \ - (2 * OVERVIEW_CANVAS_STORE_WIDTH_NETBOOK) -#define OVERVIEW_CANVAS_STORE_HEIGHT \ - (2 * OVERVIEW_CANVAS_STORE_HEIGHT_NETBOOK) -int overview_canvas_store_width = OVERVIEW_CANVAS_STORE_WIDTH; -int overview_canvas_store_height = OVERVIEW_CANVAS_STORE_HEIGHT; - -GtkWidget *toplevel; -GdkWindow *root_window; -GtkWidget *toplevel_tabs; -GtkWidget *top_vbox; -GtkWidget *top_notebook, *bottom_notebook, *right_notebook; -GtkWidget *map_widget; -static GtkWidget *bottom_hpaned; - -PangoFontDescription *city_names_style = NULL; -PangoFontDescription *city_productions_style = NULL; -PangoFontDescription *reqtree_text_style = NULL; - -GtkWidget *main_frame_civ_name; -GtkWidget *main_label_info; - -GtkWidget *avbox, *ahbox, *conn_box; -GtkWidget* scroll_panel; - -GtkWidget *econ_label[10]; -GtkWidget *bulb_label; -GtkWidget *sun_label; -GtkWidget *flake_label; -GtkWidget *government_label; -GtkWidget *timeout_label; -GtkWidget *turn_done_button; - -GtkWidget *unit_info_label; -GtkWidget *unit_info_box; -GtkWidget *unit_info_frame; - -GtkWidget *econ_ebox; -GtkWidget *bulb_ebox; -GtkWidget *sun_ebox; -GtkWidget *flake_ebox; -GtkWidget *government_ebox; - -const char *const gui_character_encoding = "UTF-8"; -const bool gui_use_transliteration = FALSE; - -static GtkWidget *main_menubar; -static GtkWidget *unit_image_table; -static GtkWidget *unit_image; -static GtkWidget *unit_image_button; -static GtkWidget *unit_below_image[MAX_NUM_UNITS_BELOW]; -static GtkWidget *unit_below_image_button[MAX_NUM_UNITS_BELOW]; -static GtkWidget *more_arrow_pixmap; -static GtkWidget *more_arrow_pixmap_button; -static GtkWidget *more_arrow_pixmap_container; - -static int unit_id_top; -static int unit_ids[MAX_NUM_UNITS_BELOW]; /* ids of the units icons in - * information display: (or 0) */ -GtkTextView *main_message_area; -GtkTextBuffer *message_buffer = NULL; -static GtkWidget *allied_chat_toggle_button; - -static enum Display_color_type display_color_type; /* practically unused */ -static gint timer_id; /* ditto */ -static GIOChannel *srv_channel; -static guint srv_id; -gint cur_x, cur_y; - -static bool gui_up = FALSE; - -static struct video_mode vmode = { -1, -1 }; - -static gboolean show_info_button_release(GtkWidget *w, GdkEventButton *ev, gpointer data); -static gboolean show_info_popup(GtkWidget *w, GdkEventButton *ev, gpointer data); - -static void end_turn_callback(GtkWidget *w, gpointer data); -static gboolean get_net_input(GIOChannel *source, GIOCondition condition, - gpointer data); -static void set_wait_for_writable_socket(struct connection *pc, - bool socket_writable); - -static void print_usage(void); -static void parse_options(int argc, char **argv); -static gboolean toplevel_key_press_handler(GtkWidget *w, GdkEventKey *ev, gpointer data); -static gboolean toplevel_key_release_handler(GtkWidget *w, GdkEventKey *ev, gpointer data); -static gboolean mouse_scroll_mapcanvas(GtkWidget *w, GdkEventScroll *ev); - -static void tearoff_callback(GtkWidget *b, gpointer data); -static GtkWidget *detached_widget_new(void); -static GtkWidget *detached_widget_fill(GtkWidget *tearbox); - -static gboolean select_unit_image_callback(GtkWidget *w, GdkEvent *ev, - gpointer data); -static gboolean select_more_arrow_pixmap_callback(GtkWidget *w, GdkEvent *ev, - gpointer data); -static gboolean quit_dialog_callback(void); - -static void allied_chat_button_toggled(GtkToggleButton *button, - gpointer user_data); - -static void free_unit_table(void); - -static void adjust_default_options(void); - -/**********************************************************************//** - Callback for freelog -**************************************************************************/ -static void log_callback_utf8(enum log_level level, const char *message, - bool file_too) -{ - if (!file_too || level <= LOG_FATAL) { - fc_fprintf(stderr, "%d: %s\n", level, message); - } -} - -/**********************************************************************//** - Called while in gtk_main() (which is all of the time) - TIMER_INTERVAL is now set by real_timer_callback() -**************************************************************************/ -static gboolean timer_callback(gpointer data) -{ - double seconds = real_timer_callback(); - - timer_id = g_timeout_add(seconds * 1000, timer_callback, NULL); - - return FALSE; -} - -/**********************************************************************//** - Print extra usage information, including one line help on each option, - to stderr. -**************************************************************************/ -static void print_usage(void) -{ - /* add client-specific usage information here */ - fc_fprintf(stderr, - _("This client accepts the standard Gtk command-line options\n" - "after '--'. See the Gtk documentation.\n\n")); - - fc_fprintf(stderr, - _("Other gui-specific options are:\n")); - - fc_fprintf(stderr, - _("-g, --gtk-warnings\tAllow Gtk+ to print warnings " - "to console\n")); - - fc_fprintf(stderr, - _("-r, --resolution WIDTHxHEIGHT\tAssume given resolution " - "screen\n")); - -#ifdef GTK3_ZOOM_ENABLED - fc_fprintf(stderr, - /* TRANS: Keep word 'default' untranslated */ - _("-z, --zoom LEVEL\tSet zoom level; use value 'default' " - "to reset\n\n")); -#else - fc_fprintf(stderr, "\n"); -#endif /* GTK3_ZOOM_ENABLED */ - - /* TRANS: No full stop after the URL, could cause confusion. */ - fc_fprintf(stderr, _("Report bugs at %s\n"), BUG_URL); -} - -/**********************************************************************//** - Dummy gtk error printer -**************************************************************************/ -static void log_gtk_warns(const gchar *log_domain, GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) -{ - log_verbose("%s", message); -} - -/**********************************************************************//** - Search for command line options. right now, it's just help - semi-useless until we have options that aren't the same across all clients. -**************************************************************************/ -static void parse_options(int argc, char **argv) -{ - int i = 1; - bool gtk_warns_enabled = FALSE; - - while (i < argc) { - char *option = NULL; - - if (is_option("--help", argv[i])) { - print_usage(); - exit(EXIT_SUCCESS); - } else if (is_option("--gtk-warnings", argv[i])) { - gtk_warns_enabled = TRUE; - -#ifdef GTK3_ZOOM_ENABLED - } else if ((option = get_option_malloc("--zoom", argv, &i, argc, FALSE))) { - char *endptr; - - if (strcmp("default", option)) { - gui_options.zoom_set = TRUE; - gui_options.zoom_default_level = strtof(option, &endptr); - } else { - gui_options.zoom_set = FALSE; - } - free(option); -#endif /* GTK3_ZOOM_ENABLED */ - - } else if ((option = get_option_malloc("--resolution", argv, &i, argc, FALSE))) { - if (!string_to_video_mode(option, &vmode)) { - fc_fprintf(stderr, _("Illegal video mode '%s'"), option); - exit(EXIT_FAILURE); - } - free(option); - } - /* Can't check against unknown options, as those might be gtk options */ - - i++; - } - - if (!gtk_warns_enabled) { - g_log_set_handler("Gtk", G_LOG_LEVEL_WARNING, log_gtk_warns, NULL); - } -} - -/**********************************************************************//** - Focus on widget. Returns whether focus was really changed. -**************************************************************************/ -static gboolean toplevel_focus(GtkWidget *w, GtkDirectionType arg) -{ - switch (arg) { - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_TAB_BACKWARD: - - if (!gtk_widget_get_can_focus(w)) { - return FALSE; - } - - if (!gtk_widget_is_focus(w)) { - gtk_widget_grab_focus(w); - return TRUE; - } - break; - - default: - break; - } - return FALSE; -} - -/**********************************************************************//** - When the chatline text view is resized, scroll it to the bottom. This - prevents users from accidentally missing messages when the chatline - gets scrolled up a small amount and stops scrolling down automatically. -**************************************************************************/ -static void main_message_area_size_allocate(GtkWidget *widget, - GtkAllocation *allocation, - gpointer data) -{ - static int old_width = 0, old_height = 0; - - if (allocation->width != old_width - || allocation->height != old_height) { - chatline_scroll_to_bottom(TRUE); - old_width = allocation->width; - old_height = allocation->height; - } -} - -/**********************************************************************//** - Focus on map canvas -**************************************************************************/ -gboolean map_canvas_focus(void) -{ - gtk_window_present(GTK_WINDOW(toplevel)); - gtk_notebook_set_current_page(GTK_NOTEBOOK(top_notebook), 0); - gtk_widget_grab_focus(map_canvas); - return TRUE; -} - -/**********************************************************************//** - In GTK+ keyboard events are recursively propagated from the hierarchy - parent down to its children. Sometimes this is not what we want. - E.g. The inputline is active, the user presses the 's' key, we want it - to be sent to the inputline, but because the main menu is further up - the hierarchy, it wins and the inputline never gets anything! - This function ensures an entry widget (like the inputline) always gets - first dibs at handling a keyboard event. -**************************************************************************/ -static gboolean toplevel_handler(GtkWidget *w, GdkEventKey *ev, gpointer data) -{ - GtkWidget *focus; - - focus = gtk_window_get_focus(GTK_WINDOW(toplevel)); - if (focus) { - if (GTK_IS_ENTRY(focus) - || (GTK_IS_TEXT_VIEW(focus) - && gtk_text_view_get_editable(GTK_TEXT_VIEW(focus)))) { - /* Propagate event to currently focused entry widget. */ - if (gtk_widget_event(focus, (GdkEvent *) ev)) { - /* Do not propagate event to our children. */ - return TRUE; - } - } - } - - /* Continue propagating event to our children. */ - return FALSE; -} - -/**********************************************************************//** - Handle keypress events when map canvas is in focus -**************************************************************************/ -static gboolean key_press_map_canvas(GtkWidget *w, GdkEventKey *ev, - gpointer data) -{ - if ((ev->state & GDK_SHIFT_MASK)) { - switch (ev->keyval) { - - case GDK_KEY_Left: - scroll_mapview(DIR8_WEST); - return TRUE; - - case GDK_KEY_Right: - scroll_mapview(DIR8_EAST); - return TRUE; - - case GDK_KEY_Up: - scroll_mapview(DIR8_NORTH); - return TRUE; - - case GDK_KEY_Down: - scroll_mapview(DIR8_SOUTH); - return TRUE; - - case GDK_KEY_Home: - key_center_capital(); - return TRUE; - - case GDK_KEY_Page_Up: - g_signal_emit_by_name(main_message_area, "move_cursor", - GTK_MOVEMENT_PAGES, -1, FALSE); - return TRUE; - - case GDK_KEY_Page_Down: - g_signal_emit_by_name(main_message_area, "move_cursor", - GTK_MOVEMENT_PAGES, 1, FALSE); - return TRUE; - - default: - break; - } - } else if (!(ev->state & GDK_CONTROL_MASK)) { - switch (ev->keyval) { - default: - break; - } - } - -#ifdef GTK3_ZOOM_ENABLED - if (!(ev->state & GDK_CONTROL_MASK)) { - switch (ev->keyval) { - case GDK_KEY_plus: - zoom_step_up(); - return TRUE; - - case GDK_KEY_minus: - zoom_step_down(); - return TRUE; - - default: - break; - } - } -#endif /* GTK3_ZOOM_ENABLED */ - - /* Return here if observer */ - if (client_is_observer()) { - return FALSE; - } - - fc_assert(MAX_NUM_BATTLEGROUPS == 4); - - if ((ev->state & GDK_CONTROL_MASK)) { - switch (ev->keyval) { - - case GDK_KEY_F1: - key_unit_assign_battlegroup(0, (ev->state & GDK_SHIFT_MASK)); - return TRUE; - - case GDK_KEY_F2: - key_unit_assign_battlegroup(1, (ev->state & GDK_SHIFT_MASK)); - return TRUE; - - case GDK_KEY_F3: - key_unit_assign_battlegroup(2, (ev->state & GDK_SHIFT_MASK)); - return TRUE; - - case GDK_KEY_F4: - key_unit_assign_battlegroup(3, (ev->state & GDK_SHIFT_MASK)); - return TRUE; - - default: - break; - }; - } else if ((ev->state & GDK_SHIFT_MASK)) { - switch (ev->keyval) { - - case GDK_KEY_F1: - key_unit_select_battlegroup(0, FALSE); - return TRUE; - - case GDK_KEY_F2: - key_unit_select_battlegroup(1, FALSE); - return TRUE; - - case GDK_KEY_F3: - key_unit_select_battlegroup(2, FALSE); - return TRUE; - - case GDK_KEY_F4: - key_unit_select_battlegroup(3, FALSE); - return TRUE; - - default: - break; - }; - } - - switch (ev->keyval) { - - case GDK_KEY_KP_Up: - case GDK_KEY_KP_8: - case GDK_KEY_Up: - case GDK_KEY_8: - key_unit_move(DIR8_NORTH); - return TRUE; - - case GDK_KEY_KP_Page_Up: - case GDK_KEY_KP_9: - case GDK_KEY_Page_Up: - case GDK_KEY_9: - key_unit_move(DIR8_NORTHEAST); - return TRUE; - - case GDK_KEY_KP_Right: - case GDK_KEY_KP_6: - case GDK_KEY_Right: - case GDK_KEY_6: - key_unit_move(DIR8_EAST); - return TRUE; - - case GDK_KEY_KP_Page_Down: - case GDK_KEY_KP_3: - case GDK_KEY_Page_Down: - case GDK_KEY_3: - key_unit_move(DIR8_SOUTHEAST); - return TRUE; - - case GDK_KEY_KP_Down: - case GDK_KEY_KP_2: - case GDK_KEY_Down: - case GDK_KEY_2: - key_unit_move(DIR8_SOUTH); - return TRUE; - - case GDK_KEY_KP_End: - case GDK_KEY_KP_1: - case GDK_KEY_End: - case GDK_KEY_1: - key_unit_move(DIR8_SOUTHWEST); - return TRUE; - - case GDK_KEY_KP_Left: - case GDK_KEY_KP_4: - case GDK_KEY_Left: - case GDK_KEY_4: - key_unit_move(DIR8_WEST); - return TRUE; - - case GDK_KEY_KP_Home: - case GDK_KEY_KP_7: - case GDK_KEY_Home: - case GDK_KEY_7: - key_unit_move(DIR8_NORTHWEST); - return TRUE; - - case GDK_KEY_KP_Begin: - case GDK_KEY_KP_5: - case GDK_KEY_5: - key_recall_previous_focus_unit(); - return TRUE; - - case GDK_KEY_Escape: - key_cancel_action(); - return TRUE; - - case GDK_KEY_b: - if (tiles_hilited_cities) { - buy_production_in_selected_cities(); - return TRUE; - } - break; - - default: - break; - }; - - return FALSE; -} - -/**********************************************************************//** - Handler for "key release" for toplevel window -**************************************************************************/ -static gboolean toplevel_key_release_handler(GtkWidget *w, GdkEventKey *ev, - gpointer data) -{ - /* inputline history code */ - if (!gtk_widget_get_mapped(top_vbox) || inputline_has_focus()) { - return FALSE; - } - - if (editor_is_active()) { - return handle_edit_key_release(ev); - } - - return FALSE; -} - -/**********************************************************************//** - Handle a keyboard key press made in the client's toplevel window. -**************************************************************************/ -static gboolean toplevel_key_press_handler(GtkWidget *w, GdkEventKey *ev, - gpointer data) -{ - if (inputline_has_focus()) { - return FALSE; - } - - switch (ev->keyval) { - - case GDK_KEY_apostrophe: - /* Allow this even if not in main map view; chatline is present on - * some other pages too */ - - /* Make the chatline visible if it's not currently. - * FIXME: should find the correct window, even when detached, from any - * other window; should scroll to the bottom automatically showing the - * latest text from other players; MUST NOT make spurious text windows - * at the bottom of other dialogs. */ - if (gtk_widget_get_mapped(top_vbox)) { - /* The main game view is visible. May need to switch notebook. */ - if (GUI_GTK_OPTION(message_chat_location) == GUI_GTK_MSGCHAT_MERGED) { - gtk_notebook_set_current_page(GTK_NOTEBOOK(top_notebook), 1); - } else { - gtk_notebook_set_current_page(GTK_NOTEBOOK(bottom_notebook), 0); - } - } - - /* If the chatline is (now) visible, focus it. */ - if (inputline_is_visible()) { - inputline_grab_focus(); - return TRUE; - } else { - break; - } - - default: - break; - } - - if (!gtk_widget_get_mapped(top_vbox) - || !can_client_change_view()) { - return FALSE; - } - - if (editor_is_active()) { - if (handle_edit_key_press(ev)) { - return TRUE; - } - } - - if (ev->state & GDK_SHIFT_MASK) { - switch (ev->keyval) { - - case GDK_KEY_Return: - case GDK_KEY_KP_Enter: - key_end_turn(); - return TRUE; - - default: - break; - } - } - - if (0 == gtk_notebook_get_current_page(GTK_NOTEBOOK(top_notebook))) { - /* 0 means the map view is focused. */ - return key_press_map_canvas(w, ev, data); - } - -#if 0 - /* We are focused some other dialog, tab, or widget. */ - if ((ev->state & GDK_CONTROL_MASK)) { - } else if ((ev->state & GDK_SHIFT_MASK)) { - } else { - switch (ev->keyval) { - - case GDK_KEY_F4: - map_canvas_focus(); - return TRUE; - - default: - break; - }; - } -#endif /* 0 */ - - return FALSE; -} - -/**********************************************************************//** - Mouse/touchpad scrolling over the mapview -**************************************************************************/ -static gboolean mouse_scroll_mapcanvas(GtkWidget *w, GdkEventScroll *ev) -{ - int scroll_x, scroll_y, xstep, ystep; - - if (!can_client_change_view()) { - return FALSE; - } - - get_mapview_scroll_pos(&scroll_x, &scroll_y); - get_mapview_scroll_step(&xstep, &ystep); - - switch (ev->direction) { - case GDK_SCROLL_UP: - scroll_y -= ystep*2; - break; - case GDK_SCROLL_DOWN: - scroll_y += ystep*2; - break; - case GDK_SCROLL_RIGHT: - scroll_x += xstep*2; - break; - case GDK_SCROLL_LEFT: - scroll_x -= xstep*2; - break; - default: - return FALSE; - }; - - set_mapview_scroll_pos(scroll_x, scroll_y); - - /* Emulating mouse move now */ - if (!gtk_widget_has_focus(map_canvas)) { - gtk_widget_grab_focus(map_canvas); - } - - update_line(ev->x, ev->y); - if (rbutton_down && (ev->state & GDK_BUTTON3_MASK)) { - update_selection_rectangle(ev->x, ev->y); - } - - if (keyboardless_goto_button_down && hover_state == HOVER_NONE) { - maybe_activate_keyboardless_goto(cur_x, cur_y); - } - - control_mouse_cursor(canvas_pos_to_tile(cur_x, cur_y)); - - return TRUE; -} - -/**********************************************************************//** - Reattaches the detached widget when the user destroys it. -**************************************************************************/ -static void tearoff_destroy(GtkWidget *w, gpointer data) -{ - GtkWidget *p, *b, *box; - - box = GTK_WIDGET(data); - p = g_object_get_data(G_OBJECT(w), "parent"); - b = g_object_get_data(G_OBJECT(w), "toggle"); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b), FALSE); - - gtk_widget_hide(w); - gtk_widget_reparent(box, p); -} - -/**********************************************************************//** - Propagates a keypress in a tearoff back to the toplevel window. -**************************************************************************/ -static gboolean propagate_keypress(GtkWidget *w, GdkEventKey *ev) -{ - gtk_widget_event(toplevel, (GdkEvent *)ev); - - return FALSE; -} - -/**********************************************************************//** - Callback for the toggle button in the detachable widget: causes the - widget to detach or reattach. -**************************************************************************/ -static void tearoff_callback(GtkWidget *b, gpointer data) -{ - GtkWidget *box = GTK_WIDGET(data); - GtkWidget *w; - - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b))) { - GtkWidget *temp_hide; - - w = gtk_window_new(GTK_WINDOW_TOPLEVEL); - setup_dialog(w, toplevel); - gtk_widget_set_name(w, "Freeciv"); - gtk_window_set_title(GTK_WINDOW(w), _("Freeciv")); - gtk_window_set_position(GTK_WINDOW(w), GTK_WIN_POS_MOUSE); - g_signal_connect(w, "destroy", G_CALLBACK(tearoff_destroy), box); - g_signal_connect(w, "key_press_event", - G_CALLBACK(propagate_keypress), NULL); - - g_object_set_data(G_OBJECT(w), "parent", gtk_widget_get_parent(box)); - g_object_set_data(G_OBJECT(w), "toggle", b); - temp_hide = g_object_get_data(G_OBJECT(box), "hide-over-reparent"); - if (temp_hide != NULL) { - gtk_widget_hide(temp_hide); - } - gtk_widget_reparent(box, w); - gtk_widget_show(w); - if (temp_hide != NULL) { - gtk_widget_show(temp_hide); - } - } else { - gtk_widget_destroy(gtk_widget_get_parent(box)); - } -} - -/**********************************************************************//** - Create the container for the widget that's able to be detached -**************************************************************************/ -static GtkWidget *detached_widget_new(void) -{ - GtkWidget *hgrid = gtk_grid_new(); - - gtk_grid_set_column_spacing(GTK_GRID(hgrid), 2); - return hgrid; -} - -/**********************************************************************//** - Creates the toggle button necessary to detach and reattach the widget - and returns a vbox in which you fill your goodies. -**************************************************************************/ -static GtkWidget *detached_widget_fill(GtkWidget *tearbox) -{ - GtkWidget *b, *fillbox; - - b = gtk_toggle_button_new(); - gtk_container_add(GTK_CONTAINER(tearbox), b); - g_signal_connect(b, "toggled", G_CALLBACK(tearoff_callback), tearbox); - - fillbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(fillbox), - GTK_ORIENTATION_VERTICAL); - - gtk_container_add(GTK_CONTAINER(tearbox), fillbox); - - return fillbox; -} - -/**********************************************************************//** - Called to build the unit_below pixmap table. This is the table on the - left of the screen that shows all of the inactive units in the current - tile. - - It may be called again if the tileset changes. -**************************************************************************/ -static void populate_unit_image_table(void) -{ - int i, width; - GtkWidget *table = unit_image_table; - GdkPixbuf *pix; - - /* get width of the overview window */ - width = (overview_canvas_store_width > GUI_GTK_OVERVIEW_MIN_XSIZE) ? overview_canvas_store_width - : GUI_GTK_OVERVIEW_MIN_XSIZE; - - if (GUI_GTK_OPTION(small_display_layout)) { - /* We want arrow to appear if there is other units in addition - to active one in tile. Active unit is not counted, so there - can be 0 other units to not to display arrow. */ - num_units_below = 1 - 1; - } else { - num_units_below = width / (int) tileset_tile_width(tileset); - num_units_below = CLIP(1, num_units_below, MAX_NUM_UNITS_BELOW); - } - - /* Top row: the active unit. */ - /* Note, we ref this and other widgets here so that we can unref them - * in reset_unit_table. */ - unit_image = gtk_image_new(); - gtk_widget_add_events(unit_image, GDK_BUTTON_PRESS_MASK); - g_object_ref(unit_image); - unit_image_button = gtk_event_box_new(); - gtk_widget_set_size_request(unit_image_button, - tileset_tile_width(tileset), -1); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(unit_image_button), FALSE); - g_object_ref(unit_image_button); - gtk_container_add(GTK_CONTAINER(unit_image_button), unit_image); - gtk_grid_attach(GTK_GRID(table), unit_image_button, 0, 0, 1, 1); - g_signal_connect(unit_image_button, "button_press_event", - G_CALLBACK(select_unit_image_callback), - GINT_TO_POINTER(-1)); - - if (!GUI_GTK_OPTION(small_display_layout)) { - /* Bottom row: other units in the same tile. */ - for (i = 0; i < num_units_below; i++) { - unit_below_image[i] = gtk_image_new(); - g_object_ref(unit_below_image[i]); - gtk_widget_add_events(unit_below_image[i], GDK_BUTTON_PRESS_MASK); - unit_below_image_button[i] = gtk_event_box_new(); - g_object_ref(unit_below_image_button[i]); - gtk_widget_set_size_request(unit_below_image_button[i], - tileset_tile_width(tileset), -1); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(unit_below_image_button[i]), FALSE); - gtk_container_add(GTK_CONTAINER(unit_below_image_button[i]), - unit_below_image[i]); - g_signal_connect(unit_below_image_button[i], - "button_press_event", - G_CALLBACK(select_unit_image_callback), - GINT_TO_POINTER(i)); - - gtk_grid_attach(GTK_GRID(table), unit_below_image_button[i], - i, 1, 1, 1); - } - } - - /* create arrow (popup for all units on the selected tile) */ - pix = sprite_get_pixbuf(get_arrow_sprite(tileset, ARROW_RIGHT)); - more_arrow_pixmap = gtk_image_new_from_pixbuf(pix); - g_object_ref(more_arrow_pixmap); - more_arrow_pixmap_button = gtk_event_box_new(); - g_object_ref(more_arrow_pixmap_button); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(more_arrow_pixmap_button), - FALSE); - gtk_container_add(GTK_CONTAINER(more_arrow_pixmap_button), - more_arrow_pixmap); - g_signal_connect(more_arrow_pixmap_button, - "button_press_event", - G_CALLBACK(select_more_arrow_pixmap_callback), NULL); - /* An extra layer so that we can hide the clickable button but keep - * an explicit size request to avoid the layout jumping around */ - more_arrow_pixmap_container = gtk_alignment_new(0.5, 0.5, 0, 0); - g_object_ref(more_arrow_pixmap_container); - gtk_container_add(GTK_CONTAINER(more_arrow_pixmap_container), - more_arrow_pixmap_button); - gtk_widget_set_size_request(more_arrow_pixmap_container, - gdk_pixbuf_get_width(pix), -1); - g_object_unref(G_OBJECT(pix)); - - if (!GUI_GTK_OPTION(small_display_layout)) { - /* Display on bottom row. */ - gtk_grid_attach(GTK_GRID(table), more_arrow_pixmap_container, - MAX_NUM_UNITS_BELOW, 1, 1, 1); - } else { - /* Display on top row (there is no bottom row). */ - gtk_grid_attach(GTK_GRID(table), more_arrow_pixmap_container, - MAX_NUM_UNITS_BELOW, 0, 1, 1); - } - - gtk_widget_show_all(table); -} - -/**********************************************************************//** - Free unit image table. -**************************************************************************/ -static void free_unit_table(void) -{ - if (unit_image_button) { - gtk_container_remove(GTK_CONTAINER(unit_image_table), - unit_image_button); - g_object_unref(unit_image); - g_object_unref(unit_image_button); - if (!GUI_GTK_OPTION(small_display_layout)) { - int i; - - for (i = 0; i < num_units_below; i++) { - gtk_container_remove(GTK_CONTAINER(unit_image_table), - unit_below_image_button[i]); - g_object_unref(unit_below_image[i]); - g_object_unref(unit_below_image_button[i]); - } - } - gtk_container_remove(GTK_CONTAINER(unit_image_table), - more_arrow_pixmap_container); - g_object_unref(more_arrow_pixmap); - g_object_unref(more_arrow_pixmap_button); - g_object_unref(more_arrow_pixmap_container); - } -} - -/**********************************************************************//** - Called when the tileset is changed to reset the unit pixmap table. -**************************************************************************/ -void reset_unit_table(void) -{ - /* Unreference all of the widgets that we're about to reallocate, thus - * avoiding a memory leak. Remove them from the container first, just - * to be safe. Note, the widgets are ref'd in - * populatate_unit_image_table. */ - free_unit_table(); - - populate_unit_image_table(); - - /* We have to force a redraw of the units. And we explicitly have - * to force a redraw of the focus unit, which is normally only - * redrawn when the focus changes. We also have to force the 'more' - * arrow to go away, both by expicitly hiding it and telling it to - * do so (this will be reset immediately afterwards if necessary, - * but we have to make the *internal* state consistent). */ - gtk_widget_hide(more_arrow_pixmap_button); - set_unit_icons_more_arrow(FALSE); - if (get_num_units_in_focus() == 1) { - set_unit_icon(-1, head_of_units_in_focus()); - } else { - set_unit_icon(-1, NULL); - } - update_unit_pix_label(get_units_in_focus()); -} - -/**********************************************************************//** - Enable/Disable the game page menu bar. -**************************************************************************/ -void enable_menus(bool enable) -{ - if (enable) { - main_menubar = setup_menus(toplevel); - /* Ensure the menus are really created before performing any operations - * on them. */ - while (gtk_events_pending()) { - gtk_main_iteration(); - } - gtk_grid_attach_next_to(GTK_GRID(top_vbox), main_menubar, NULL, GTK_POS_TOP, 1, 1); - menus_init(); - gtk_widget_show_all(main_menubar); - } else { - gtk_widget_destroy(main_menubar); - } -} - -/**********************************************************************//** - Workaround for a crash that occurs when a button release event is - emitted for a notebook with no pages. See PR#40743. - FIXME: Remove this hack once gtk_notebook_button_release() in - gtk/gtknotebook.c checks for NULL notebook->cur_page. -**************************************************************************/ -static gboolean right_notebook_button_release(GtkWidget *widget, - GdkEventButton *event) -{ - if (event->type != GDK_BUTTON_RELEASE) { - return FALSE; - } - - if (!GTK_IS_NOTEBOOK(widget) - || -1 == gtk_notebook_get_current_page(GTK_NOTEBOOK(widget))) { - /* Make sure the default gtk handler - * does NOT get called in this case. */ - return TRUE; - } - - return FALSE; -} - -/**********************************************************************//** - Override background color for canvases -**************************************************************************/ -static void setup_canvas_color_for_state(GtkStateFlags state) -{ - gtk_widget_override_background_color(GTK_WIDGET(overview_canvas), state, - &get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color); - gtk_widget_override_background_color(GTK_WIDGET(map_canvas), state, - &get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color); -} - -/**********************************************************************//** - Do the heavy lifting for the widget setup. -**************************************************************************/ -static void setup_widgets(void) -{ - GtkWidget *page, *ebox, *hgrid, *hgrid2, *label; - GtkWidget *frame, *table, *table2, *paned, *hpaned, *sw, *text; - GtkWidget *button, *view, *vgrid, *right_vbox = NULL; - int i; - char buf[256]; - struct sprite *spr; - GtkWidget *notebook, *statusbar; - GtkWidget *dtach_lowbox = NULL; - - message_buffer = gtk_text_buffer_new(NULL); - - notebook = gtk_notebook_new(); - - /* stop mouse wheel notebook page switching. */ - g_signal_connect(notebook, "scroll_event", - G_CALLBACK(gtk_true), NULL); - - toplevel_tabs = notebook; - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); - gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE); - vgrid = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vgrid), 4); - gtk_container_add(GTK_CONTAINER(toplevel), vgrid); - gtk_container_add(GTK_CONTAINER(vgrid), notebook); - statusbar = create_statusbar(); - gtk_container_add(GTK_CONTAINER(vgrid), statusbar); - - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), - create_main_page(), NULL); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), - create_start_page(), NULL); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), - create_scenario_page(), NULL); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), - create_load_page(), NULL); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), - create_network_page(), NULL); - - editgui_create_widgets(); - - ingame_votebar = voteinfo_bar_new(FALSE); - g_object_set(ingame_votebar, "margin", 2, NULL); - - /* *** everything in the top *** */ - - page = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(page), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(page), - GTK_SHADOW_ETCHED_IN); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, NULL); - - top_vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(top_vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(top_vbox), 5); - hgrid = gtk_grid_new(); - - if (GUI_GTK_OPTION(small_display_layout)) { - /* The window is divided into two horizontal panels: overview + - * civinfo + unitinfo, main view + message window. */ - right_vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(right_vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(hgrid), right_vbox); - - paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); - gtk_container_add(GTK_CONTAINER(page), top_vbox); - gtk_container_add(GTK_CONTAINER(top_vbox), hgrid); - gtk_container_add(GTK_CONTAINER(right_vbox), paned); - gtk_container_add(GTK_CONTAINER(right_vbox), ingame_votebar); - - /* Overview size designed for small displays (netbooks). */ - overview_canvas_store_width = OVERVIEW_CANVAS_STORE_WIDTH_NETBOOK; - overview_canvas_store_height = OVERVIEW_CANVAS_STORE_HEIGHT_NETBOOK; - } else { - /* The window is divided into two vertical panes: overview + - * + civinfo + unitinfo + main view, message window. */ - paned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(page), paned); - gtk_paned_pack1(GTK_PANED(paned), top_vbox, TRUE, FALSE); - gtk_container_add(GTK_CONTAINER(top_vbox), hgrid); - - /* Overview size designed for big displays (desktops). */ - overview_canvas_store_width = OVERVIEW_CANVAS_STORE_WIDTH; - overview_canvas_store_height = OVERVIEW_CANVAS_STORE_HEIGHT; - } - - /* this holds the overview canvas, production info, etc. */ - vgrid = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_column_spacing(GTK_GRID(vgrid), 3); - /* Put vgrid to the left of anything else in hgrid -- right_vbox is either - * the chat/messages pane, or NULL which is OK */ - gtk_grid_attach_next_to(GTK_GRID(hgrid), vgrid, right_vbox, - GTK_POS_LEFT, 1, 1); - - /* overview canvas */ - ahbox = detached_widget_new(); - gtk_widget_set_hexpand(ahbox, FALSE); - gtk_widget_set_vexpand(ahbox, FALSE); - gtk_container_add(GTK_CONTAINER(vgrid), ahbox); - avbox = detached_widget_fill(ahbox); - - overview_scrolled_window = gtk_scrolled_window_new(NULL, NULL); - gtk_container_set_border_width(GTK_CONTAINER (overview_scrolled_window), 1); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (overview_scrolled_window), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - - overview_canvas = gtk_drawing_area_new(); - gtk_widget_set_halign(overview_canvas, GTK_ALIGN_CENTER); - gtk_widget_set_valign(overview_canvas, GTK_ALIGN_CENTER); - gtk_widget_set_size_request(overview_canvas, overview_canvas_store_width, - overview_canvas_store_height); - gtk_widget_set_size_request(overview_scrolled_window, overview_canvas_store_width, - overview_canvas_store_height); - gtk_widget_set_hexpand(overview_canvas, TRUE); - gtk_widget_set_vexpand(overview_canvas, TRUE); - - gtk_widget_add_events(overview_canvas, GDK_EXPOSURE_MASK - |GDK_BUTTON_PRESS_MASK - |GDK_POINTER_MOTION_MASK); - gtk_container_add(GTK_CONTAINER(avbox), overview_scrolled_window); - - gtk_container_add(GTK_CONTAINER(overview_scrolled_window), - overview_canvas); - - g_signal_connect(overview_canvas, "draw", - G_CALLBACK(overview_canvas_draw), NULL); - - g_signal_connect(overview_canvas, "motion_notify_event", - G_CALLBACK(move_overviewcanvas), NULL); - - g_signal_connect(overview_canvas, "button_press_event", - G_CALLBACK(butt_down_overviewcanvas), NULL); - - /* The rest */ - - ahbox = detached_widget_new(); - gtk_container_add(GTK_CONTAINER(vgrid), ahbox); - gtk_widget_set_hexpand(ahbox, FALSE); - avbox = detached_widget_fill(ahbox); - gtk_widget_set_vexpand(avbox, TRUE); - gtk_widget_set_valign(avbox, GTK_ALIGN_FILL); - - /* Info on player's civilization, when game is running. */ - frame = gtk_frame_new(""); - gtk_container_add(GTK_CONTAINER(avbox), frame); - - main_frame_civ_name = frame; - - vgrid = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(frame), vgrid); - gtk_widget_set_hexpand(vgrid, TRUE); - - ebox = gtk_event_box_new(); - gtk_widget_add_events(ebox, GDK_BUTTON_PRESS_MASK); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); - g_signal_connect(ebox, "button_press_event", - G_CALLBACK(show_info_popup), NULL); - gtk_container_add(GTK_CONTAINER(vgrid), ebox); - - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_widget_set_margin_left(label, 2); - gtk_widget_set_margin_right(label, 2); - gtk_widget_set_margin_top(label, 2); - gtk_widget_set_margin_bottom(label, 2); - gtk_container_add(GTK_CONTAINER(ebox), label); - main_label_info = label; - - /* Production status */ - table = gtk_grid_new(); - gtk_widget_set_halign(table, GTK_ALIGN_CENTER); - gtk_grid_set_column_homogeneous(GTK_GRID(table), TRUE); - gtk_container_add(GTK_CONTAINER(avbox), table); - - /* citizens for taxrates */ - ebox = gtk_event_box_new(); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); - gtk_grid_attach(GTK_GRID(table), ebox, 0, 0, 10, 1); - econ_ebox = ebox; - - table2 = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(ebox), table2); - - for (i = 0; i < 10; i++) { - ebox = gtk_event_box_new(); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); - gtk_widget_add_events(ebox, GDK_BUTTON_PRESS_MASK); - - gtk_grid_attach(GTK_GRID(table2), ebox, i, 0, 1, 1); - - g_signal_connect(ebox, "button_press_event", - G_CALLBACK(taxrates_callback), GINT_TO_POINTER(i)); - - spr = i < 5 ? get_tax_sprite(tileset, O_SCIENCE) : get_tax_sprite(tileset, O_GOLD); - econ_label[i] = gtk_image_new_from_surface(spr->surface); - gtk_container_add(GTK_CONTAINER(ebox), econ_label[i]); - } - - /* science, environmental, govt, timeout */ - spr = client_research_sprite(); - if (spr != NULL) { - bulb_label = gtk_image_new_from_surface(spr->surface); - } - spr = client_warming_sprite(); - if (spr != NULL) { - sun_label = gtk_image_new_from_surface(spr->surface); - } - spr = client_cooling_sprite(); - if (spr != NULL) { - flake_label = gtk_image_new_from_surface(spr->surface); - } - spr = client_government_sprite(); - if (spr != NULL) { - government_label = gtk_image_new_from_surface(spr->surface); - } - - for (i = 0; i < 4; i++) { - GtkWidget *w; - - ebox = gtk_event_box_new(); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); - - switch (i) { - case 0: - w = bulb_label; - bulb_ebox = ebox; - break; - case 1: - w = sun_label; - sun_ebox = ebox; - break; - case 2: - w = flake_label; - flake_ebox = ebox; - break; - default: - case 3: - w = government_label; - government_ebox = ebox; - break; - } - - gtk_widget_set_halign(w, GTK_ALIGN_START); - gtk_widget_set_valign(w, GTK_ALIGN_START); - gtk_widget_set_margin_left(w, 0); - gtk_widget_set_margin_right(w, 0); - gtk_widget_set_margin_top(w, 0); - gtk_widget_set_margin_bottom(w, 0); - gtk_container_add(GTK_CONTAINER(ebox), w); - gtk_grid_attach(GTK_GRID(table), ebox, i, 1, 1, 1); - } - - timeout_label = gtk_label_new(""); - - frame = gtk_frame_new(NULL); - gtk_grid_attach(GTK_GRID(table), frame, 4, 1, 6, 1); - gtk_container_add(GTK_CONTAINER(frame), timeout_label); - - - /* turn done */ - turn_done_button = gtk_button_new_with_label(_("Turn Done")); - - gtk_grid_attach(GTK_GRID(table), turn_done_button, 0, 2, 10, 1); - - g_signal_connect(turn_done_button, "clicked", - G_CALLBACK(end_turn_callback), NULL); - - fc_snprintf(buf, sizeof(buf), "%s:\n%s", - _("Turn Done"), _("Shift+Return")); - gtk_widget_set_tooltip_text(turn_done_button, buf); - - /* Selected unit status */ - - unit_info_box = gtk_grid_new(); - gtk_widget_set_hexpand(unit_info_box, FALSE); - gtk_orientable_set_orientation(GTK_ORIENTABLE(unit_info_box), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(avbox), unit_info_box); - - /* In edit mode the unit_info_box widget is replaced by the - * editinfobox, so we need to add a ref here so that it is - * not destroyed when removed from its container. - * See editinfobox_refresh(). */ - g_object_ref(unit_info_box); - - unit_info_frame = gtk_frame_new(""); - gtk_container_add(GTK_CONTAINER(unit_info_box), unit_info_frame); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_OUT); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); - gtk_container_add(GTK_CONTAINER(unit_info_frame), sw); - - label = gtk_label_new(NULL); - gtk_widget_set_hexpand(label, TRUE); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_widget_set_margin_left(label, 2); - gtk_widget_set_margin_right(label, 2); - gtk_widget_set_margin_top(label, 2); - gtk_widget_set_margin_bottom(label, 2); - gtk_container_add(GTK_CONTAINER(sw), label); - unit_info_label = label; - - hgrid2 = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(unit_info_box), hgrid2); - - table = gtk_grid_new(); - g_object_set(table, "margin", 5, NULL); - gtk_container_add(GTK_CONTAINER(hgrid2), table); - - gtk_grid_set_row_spacing(GTK_GRID(table), 2); - gtk_grid_set_column_spacing(GTK_GRID(table), 2); - - unit_image_table = table; - - /* Map canvas, editor toolbar, and scrollbars */ - - /* The top notebook containing the map view and dialogs. */ - - top_notebook = gtk_notebook_new(); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(top_notebook), GTK_POS_BOTTOM); - gtk_notebook_set_scrollable(GTK_NOTEBOOK(top_notebook), TRUE); - - - if (GUI_GTK_OPTION(small_display_layout)) { - gtk_paned_pack1(GTK_PANED(paned), top_notebook, TRUE, FALSE); - } else if (GUI_GTK_OPTION(message_chat_location) == GUI_GTK_MSGCHAT_MERGED) { - right_vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(right_vbox), - GTK_ORIENTATION_VERTICAL); - - gtk_container_add(GTK_CONTAINER(right_vbox), top_notebook); - gtk_container_add(GTK_CONTAINER(right_vbox), ingame_votebar); - gtk_container_add(GTK_CONTAINER(hgrid), right_vbox); - } else { - gtk_container_add(GTK_CONTAINER(hgrid), top_notebook); - } - - map_widget = gtk_grid_new(); - - vgrid = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(vgrid), map_widget); - - gtk_container_add(GTK_CONTAINER(vgrid), editgui_get_editbar()->widget); - g_object_set(editgui_get_editbar()->widget, "margin", 4, NULL); - - label = gtk_label_new(Q_("?noun:View")); - gtk_notebook_append_page(GTK_NOTEBOOK(top_notebook), vgrid, label); - - frame = gtk_frame_new(NULL); - gtk_grid_attach(GTK_GRID(map_widget), frame, 0, 0, 1, 1); - - map_canvas = gtk_drawing_area_new(); - gtk_widget_set_hexpand(map_canvas, TRUE); - gtk_widget_set_vexpand(map_canvas, TRUE); - gtk_widget_set_size_request(map_canvas, 300, 300); - gtk_widget_set_can_focus(map_canvas, TRUE); - - setup_canvas_color_for_state(GTK_STATE_FLAG_NORMAL); - setup_canvas_color_for_state(GTK_STATE_FLAG_ACTIVE); - setup_canvas_color_for_state(GTK_STATE_FLAG_PRELIGHT); - setup_canvas_color_for_state(GTK_STATE_FLAG_SELECTED); - setup_canvas_color_for_state(GTK_STATE_FLAG_INSENSITIVE); - setup_canvas_color_for_state(GTK_STATE_FLAG_INCONSISTENT); - setup_canvas_color_for_state(GTK_STATE_FLAG_FOCUSED); - setup_canvas_color_for_state(GTK_STATE_FLAG_BACKDROP); - - gtk_widget_add_events(map_canvas, GDK_EXPOSURE_MASK - |GDK_BUTTON_PRESS_MASK - |GDK_BUTTON_RELEASE_MASK - |GDK_KEY_PRESS_MASK - |GDK_POINTER_MOTION_MASK - |GDK_SCROLL_MASK); - - gtk_container_add(GTK_CONTAINER(frame), map_canvas); - - map_horizontal_scrollbar = - gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL); - gtk_grid_attach(GTK_GRID(map_widget), map_horizontal_scrollbar, 0, 1, 1, 1); - - map_vertical_scrollbar = - gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL); - gtk_grid_attach(GTK_GRID(map_widget), map_vertical_scrollbar, 1, 0, 1, 1); - - g_signal_connect(map_canvas, "draw", - G_CALLBACK(map_canvas_draw), NULL); - - g_signal_connect(map_canvas, "configure_event", - G_CALLBACK(map_canvas_configure), NULL); - - g_signal_connect(map_canvas, "motion_notify_event", - G_CALLBACK(move_mapcanvas), NULL); - - g_signal_connect(toplevel, "enter_notify_event", - G_CALLBACK(leave_mapcanvas), NULL); - - g_signal_connect(map_canvas, "button_press_event", - G_CALLBACK(butt_down_mapcanvas), NULL); - - g_signal_connect(map_canvas, "button_release_event", - G_CALLBACK(butt_release_mapcanvas), NULL); - - g_signal_connect(map_canvas, "scroll_event", - G_CALLBACK(mouse_scroll_mapcanvas), NULL); - - g_signal_connect(toplevel, "key_press_event", - G_CALLBACK(toplevel_key_press_handler), NULL); - - g_signal_connect(toplevel, "key_release_event", - G_CALLBACK(toplevel_key_release_handler), NULL); - - /* *** The message window -- this is a detachable widget *** */ - - if (GUI_GTK_OPTION(message_chat_location) == GUI_GTK_MSGCHAT_MERGED) { - bottom_hpaned = hpaned = paned; - right_notebook = bottom_notebook = top_notebook; - } else { - dtach_lowbox = detached_widget_new(); - gtk_paned_pack2(GTK_PANED(paned), dtach_lowbox, FALSE, TRUE); - avbox = detached_widget_fill(dtach_lowbox); - - vgrid = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid), - GTK_ORIENTATION_VERTICAL); - if (!GUI_GTK_OPTION(small_display_layout)) { - gtk_container_add(GTK_CONTAINER(vgrid), ingame_votebar); - } - gtk_container_add(GTK_CONTAINER(avbox), vgrid); - - if (GUI_GTK_OPTION(small_display_layout)) { - hpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); - } else { - hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); - } - gtk_container_add(GTK_CONTAINER(vgrid), hpaned); - g_object_set(hpaned, "margin", 4, NULL); - bottom_hpaned = hpaned; - - bottom_notebook = gtk_notebook_new(); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(bottom_notebook), GTK_POS_TOP); - gtk_notebook_set_scrollable(GTK_NOTEBOOK(bottom_notebook), TRUE); - gtk_paned_pack1(GTK_PANED(hpaned), bottom_notebook, TRUE, TRUE); - - right_notebook = gtk_notebook_new(); - g_object_ref(right_notebook); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(right_notebook), GTK_POS_TOP); - gtk_notebook_set_scrollable(GTK_NOTEBOOK(right_notebook), TRUE); - g_signal_connect(right_notebook, "button-release-event", - G_CALLBACK(right_notebook_button_release), NULL); - if (GUI_GTK_OPTION(message_chat_location) == GUI_GTK_MSGCHAT_SPLIT) { - gtk_paned_pack2(GTK_PANED(hpaned), right_notebook, TRUE, TRUE); - } - } - - vgrid = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid), - GTK_ORIENTATION_VERTICAL); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, - GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(vgrid), sw); - - label = gtk_label_new(_("Chat")); - gtk_notebook_append_page(GTK_NOTEBOOK(bottom_notebook), vgrid, label); - - text = gtk_text_view_new_with_buffer(message_buffer); - gtk_widget_set_hexpand(text, TRUE); - gtk_widget_set_vexpand(text, TRUE); - set_message_buffer_view_link_handlers(text); - gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); - gtk_container_add(GTK_CONTAINER(sw), text); - g_signal_connect(text, "size-allocate", - G_CALLBACK(main_message_area_size_allocate), NULL); - - gtk_widget_set_name(text, "chatline"); - - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); - gtk_widget_realize(text); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 5); - - main_message_area = GTK_TEXT_VIEW(text); - if (dtach_lowbox != NULL) { - g_object_set_data(G_OBJECT(dtach_lowbox), "hide-over-reparent", main_message_area); - } - - chat_welcome_message(TRUE); - - /* the chat line */ - view = inputline_toolkit_view_new(); - gtk_container_add(GTK_CONTAINER(vgrid), view); - g_object_set(view, "margin", 3, NULL); - - button = gtk_check_button_new_with_label(_("Allies Only")); - gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), - GUI_GTK_OPTION(allied_chat_only)); - g_signal_connect(button, "toggled", - G_CALLBACK(allied_chat_button_toggled), NULL); - inputline_toolkit_view_append_button(view, button); - allied_chat_toggle_button = button; - - button = gtk_button_new_with_label(_("Clear links")); - g_signal_connect(button, "clicked", - G_CALLBACK(link_marks_clear_all), NULL); - inputline_toolkit_view_append_button(view, button); - - /* Other things to take care of */ - - gtk_widget_show_all(gtk_bin_get_child(GTK_BIN(toplevel))); - - if (GUI_GTK_OPTION(enable_tabs)) { - meswin_dialog_popup(FALSE); - } - - gtk_notebook_set_current_page(GTK_NOTEBOOK(top_notebook), 0); - gtk_notebook_set_current_page(GTK_NOTEBOOK(bottom_notebook), 0); - - if (!GUI_GTK_OPTION(map_scrollbars)) { - gtk_widget_hide(map_horizontal_scrollbar); - gtk_widget_hide(map_vertical_scrollbar); - } -} - -/**********************************************************************//** - Called from main(). -**************************************************************************/ -void ui_init(void) -{ - log_set_callback(log_callback_utf8); - set_frame_by_frame_animation(); -} - -/**********************************************************************//** - Entry point for whole freeciv client program. -**************************************************************************/ -int main(int argc, char **argv) -{ - return client_main(argc, argv); -} - -/**********************************************************************//** - Migrate gtk3 client specific options from gtk2 client options. -**************************************************************************/ -static void migrate_options_from_gtk2(void) -{ - log_normal(_("Migrating options from gtk2 to gtk3 client")); - -#define MIGRATE_OPTION(opt) GUI_GTK_OPTION(opt) = gui_options.gui_gtk2_##opt; -#define MIGRATE_STR_OPTION(opt) \ - strncpy(GUI_GTK_OPTION(opt), gui_options.gui_gtk2_##opt, \ - sizeof(GUI_GTK_OPTION(opt))); - - /* Default theme name is never migrated */ - /* 'fullscreen', 'small_display_layout', and 'message_chat_location' - * not migrated, as (unlike Gtk2), Gtk3-client tries to pick better - * defaults for these in fresh installations based on screen size (see - * adjust_default_options()); so user is probably better served by - * getting these adaptive defaults than whatever they had for Gtk2. - * Since 'fullscreen' isn't migrated, we don't need to worry about - * preserving gui_gtk2_migrated_from_2_5 either. */ - MIGRATE_OPTION(map_scrollbars); - MIGRATE_OPTION(dialogs_on_top); - MIGRATE_OPTION(show_task_icons); - MIGRATE_OPTION(enable_tabs); - MIGRATE_OPTION(show_chat_message_time); - MIGRATE_OPTION(new_messages_go_to_top); - MIGRATE_OPTION(show_message_window_buttons); - MIGRATE_OPTION(metaserver_tab_first); - MIGRATE_OPTION(allied_chat_only); - MIGRATE_OPTION(mouse_over_map_focus); - MIGRATE_OPTION(chatline_autocompletion); - MIGRATE_OPTION(citydlg_xsize); - MIGRATE_OPTION(citydlg_ysize); - MIGRATE_OPTION(popup_tech_help); - - MIGRATE_STR_OPTION(font_city_label); - MIGRATE_STR_OPTION(font_notify_label); - MIGRATE_STR_OPTION(font_spaceship_label); - MIGRATE_STR_OPTION(font_help_label); - MIGRATE_STR_OPTION(font_help_link); - MIGRATE_STR_OPTION(font_help_text); - MIGRATE_STR_OPTION(font_chatline); - MIGRATE_STR_OPTION(font_beta_label); - MIGRATE_STR_OPTION(font_small); - MIGRATE_STR_OPTION(font_comment_label); - MIGRATE_STR_OPTION(font_city_names); - MIGRATE_STR_OPTION(font_city_productions); - MIGRATE_STR_OPTION(font_reqtree_text); - -#undef MIGRATE_OPTION -#undef MIGRATE_STR_OPTION - - GUI_GTK_OPTION(migrated_from_gtk2) = TRUE; -} - -/**********************************************************************//** - Migrate gtk3 client specific options from freeciv-2.5 options -**************************************************************************/ -static void migrate_options_from_2_5(void) -{ - log_normal(_("Migrating gtk3-client options from freeciv-2.5 options.")); - - GUI_GTK_OPTION(fullscreen) = gui_options.migrate_fullscreen; - - GUI_GTK_OPTION(migrated_from_2_5) = TRUE; -} - -/**********************************************************************//** - Called from client_main(), is what it's named. -**************************************************************************/ -void ui_main(int argc, char **argv) -{ - PangoFontDescription *toplevel_font_name; - guint sig; - - parse_options(argc, argv); - - /* the locale has already been set in init_nls() and the Win32-specific - * locale logic in gtk_init() causes problems with zh_CN (see PR#39475) */ - gtk_disable_setlocale(); - - /* GTK withdraw gtk options. Process GTK arguments */ - gtk_init(&argc, &argv); - - toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_position(GTK_WINDOW(toplevel), GTK_WIN_POS_CENTER); - if (vmode.width > 0 && vmode.height > 0) { - gtk_window_resize(GTK_WINDOW(toplevel), vmode.width, vmode.height); - } - g_signal_connect(toplevel, "key_press_event", - G_CALLBACK(toplevel_handler), NULL); - - gtk_window_set_role(GTK_WINDOW(toplevel), "toplevel"); - gtk_widget_realize(toplevel); - gtk_widget_set_name(toplevel, "Freeciv"); - root_window = gtk_widget_get_window(toplevel); - - if (gui_options.first_boot) { - adjust_default_options(); - /* We're using fresh defaults for this version of this client, - * so prevent any future migrations from other clients / versions */ - GUI_GTK_OPTION(migrated_from_gtk2) = TRUE; - GUI_GTK_OPTION(migrated_from_2_5) = TRUE; - } else { - if (!GUI_GTK_OPTION(migrated_from_gtk2)) { - migrate_options_from_gtk2(); - /* We want a fresh look at screen-size-related defaults */ - adjust_default_options(); - /* We don't ever want to consider pre-2.6 fullscreen option again */ - GUI_GTK_OPTION(migrated_from_2_5) = TRUE; - } else if (!GUI_GTK_OPTION(migrated_from_2_5)) { - /* This only affects the fullscreen option, which we don't want - * to touch if adjust_default_options() just adjusted it. */ - migrate_options_from_2_5(); - } - } - - if (GUI_GTK_OPTION(fullscreen)) { - gtk_window_fullscreen(GTK_WINDOW(toplevel)); - } - - gtk_window_set_title(GTK_WINDOW (toplevel), _("Freeciv")); - - g_signal_connect(toplevel, "delete_event", - G_CALLBACK(quit_dialog_callback), NULL); - - /* Disable GTK+ cursor key focus movement */ - sig = g_signal_lookup("focus", GTK_TYPE_WIDGET); - g_signal_handlers_disconnect_matched(toplevel, G_SIGNAL_MATCH_ID, sig, - 0, 0, 0, 0); - g_signal_connect(toplevel, "focus", G_CALLBACK(toplevel_focus), NULL); - - - display_color_type = get_visual(); - - options_iterate(client_optset, poption) { - if (OT_FONT == option_type(poption)) { - /* Force to call the appropriated callback. */ - option_changed(poption); - } - } options_iterate_end; - - toplevel_font_name = pango_context_get_font_description( - gtk_widget_get_pango_context(toplevel)); - - if (NULL == city_names_style) { - city_names_style = pango_font_description_copy(toplevel_font_name); - log_error("city_names_style should have been set by options."); - } - if (NULL == city_productions_style) { - city_productions_style = pango_font_description_copy(toplevel_font_name); - log_error("city_productions_style should have been set by options."); - } - if (NULL == reqtree_text_style) { - reqtree_text_style = pango_font_description_copy(toplevel_font_name); - log_error("reqtree_text_style should have been set by options."); - } - - tileset_init(tileset); - tileset_load_tiles(tileset); - - /* keep the icon of the executable on Windows (see PR#36491) */ -#ifndef FREECIV_MSWINDOWS - { - GdkPixbuf *pixbuf = sprite_get_pixbuf(get_icon_sprite(tileset, ICON_FREECIV)); - - /* Only call this after tileset_load_tiles is called. */ - gtk_window_set_icon(GTK_WINDOW(toplevel), pixbuf); - g_object_unref(pixbuf); - } -#endif /* FREECIV_MSWINDOWS */ - - setup_widgets(); - load_cursors(); - cma_fe_init(); - diplomacy_dialog_init(); - luaconsole_dialog_init(); - happiness_dialog_init(); - citizens_dialog_init(); - intel_dialog_init(); - spaceship_dialog_init(); - chatline_init(); - init_mapcanvas_and_overview(); - - tileset_use_preferred_theme(tileset); - - gtk_widget_show(toplevel); - - /* assumes toplevel showing */ - set_client_state(C_S_DISCONNECTED); - - /* assumes client_state is set */ - timer_id = g_timeout_add(TIMER_INTERVAL, timer_callback, NULL); - - gui_up = TRUE; - gtk_main(); - gui_up = FALSE; - - destroy_server_scans(); - free_mapcanvas_and_overview(); - spaceship_dialog_done(); - intel_dialog_done(); - citizens_dialog_done(); - luaconsole_dialog_done(); - happiness_dialog_done(); - diplomacy_dialog_done(); - cma_fe_done(); - free_unit_table(); - editgui_free(); - gtk_widget_destroy(toplevel_tabs); - message_buffer = NULL; /* Result of destruction of everything */ - tileset_free_tiles(tileset); -} - -/**********************************************************************//** - Return whether gui is currently running. -**************************************************************************/ -bool is_gui_up(void) -{ - return gui_up; -} - -/**********************************************************************//** - Do any necessary UI-specific cleanup -**************************************************************************/ -void ui_exit(void) -{ - if (message_buffer != NULL) { - g_object_unref(message_buffer); - message_buffer = NULL; - } -} - -/**********************************************************************//** - Return our GUI type -**************************************************************************/ -enum gui_type get_gui_type(void) -{ - return GUI_GTK3; -} - -/**********************************************************************//** - Obvious... -**************************************************************************/ -void sound_bell(void) -{ - gdk_display_beep(gdk_display_get_default()); -} - -/**********************************************************************//** - Set one of the unit icons in information area based on punit. - Use punit == NULL to clear icon. - Index 'idx' is -1 for "active unit", or 0 to (num_units_below - 1) for - units below. Also updates unit_ids[idx] for idx >= 0. -**************************************************************************/ -void set_unit_icon(int idx, struct unit *punit) -{ - GtkWidget *w; - - fc_assert_ret(idx >= -1 && idx < num_units_below); - - if (idx == -1) { - w = unit_image; - unit_id_top = punit ? punit->id : 0; - } else { - w = unit_below_image[idx]; - unit_ids[idx] = punit ? punit->id : 0; - } - - if (!w) { - return; - } - - if (punit) { - put_unit_image(punit, GTK_IMAGE(w), -1); - } else { - gtk_image_clear(GTK_IMAGE(w)); - } -} - -/**********************************************************************//** - Set the "more arrow" for the unit icons to on(1) or off(0). - Maintains a static record of current state to avoid unnecessary redraws. - Note initial state should match initial gui setup (off). -**************************************************************************/ -void set_unit_icons_more_arrow(bool onoff) -{ - static bool showing = FALSE; - - if (!more_arrow_pixmap_button) { - return; - } - - if (onoff && !showing) { - gtk_widget_show(more_arrow_pixmap_button); - showing = TRUE; - } else if (!onoff && showing) { - gtk_widget_hide(more_arrow_pixmap_button); - showing = FALSE; - } -} - -/**********************************************************************//** - Called when the set of units in focus (get_units_in_focus()) changes. - Standard updates like update_unit_info_label() are handled in the platform- - independent code; we use this to keep the goto/airlift dialog up to date, - if it's visible. -**************************************************************************/ -void real_focus_units_changed(void) -{ - goto_dialog_focus_units_changed(); -} - -/**********************************************************************//** - Callback for clicking a unit icon underneath unit info box. - these are the units on the same tile as the focus unit. -**************************************************************************/ -static gboolean select_unit_image_callback(GtkWidget *w, GdkEvent *ev, - gpointer data) -{ - int i = GPOINTER_TO_INT(data); - struct unit *punit; - - if (i == -1) { - punit = game_unit_by_number(unit_id_top); - if (punit && unit_is_in_focus(punit)) { - /* Clicking on the currently selected unit will center it. */ - center_tile_mapcanvas(unit_tile(punit)); - } - return TRUE; - } - - if (unit_ids[i] == 0) /* no unit displayed at this place */ - return TRUE; - - punit = game_unit_by_number(unit_ids[i]); - if (NULL != punit && unit_owner(punit) == client.conn.playing) { - /* Unit shouldn't be NULL but may be owned by an ally. */ - unit_focus_set(punit); - } - - return TRUE; -} - -/**********************************************************************//** - Callback for clicking a unit icon underneath unit info box. - these are the units on the same tile as the focus unit. -**************************************************************************/ -static gboolean select_more_arrow_pixmap_callback(GtkWidget *w, GdkEvent *ev, - gpointer data) -{ - struct unit *punit = game_unit_by_number(unit_id_top); - - if (punit) { - unit_select_dialog_popup(unit_tile(punit)); - } - - return TRUE; -} - -/**********************************************************************//** - Button released when showing info popup -**************************************************************************/ -static gboolean show_info_button_release(GtkWidget *w, GdkEventButton *ev, gpointer data) -{ - gtk_grab_remove(w); - gdk_device_ungrab(ev->device, ev->time); - gtk_widget_destroy(w); - return FALSE; -} - -/**********************************************************************//** - Popup info box -**************************************************************************/ -static gboolean show_info_popup(GtkWidget *w, GdkEventButton *ev, gpointer data) -{ - if (ev->button == 1) { - GtkWidget *p; - - p = gtk_window_new(GTK_WINDOW_POPUP); - gtk_container_set_border_width(GTK_CONTAINER(p), 4); - gtk_window_set_transient_for(GTK_WINDOW(p), GTK_WINDOW(toplevel)); - gtk_window_set_position(GTK_WINDOW(p), GTK_WIN_POS_MOUSE); - - gtk_widget_new(GTK_TYPE_LABEL, "GtkWidget::parent", p, - "GtkLabel::label", get_info_label_text_popup(), - "GtkWidget::visible", TRUE, - NULL); - gtk_widget_show(p); - - gdk_device_grab(ev->device, gtk_widget_get_window(p), - GDK_OWNERSHIP_NONE, TRUE, GDK_BUTTON_RELEASE_MASK, NULL, - ev->time); - gtk_grab_add(p); - - g_signal_connect_after(p, "button_release_event", - G_CALLBACK(show_info_button_release), NULL); - } - - return TRUE; -} - -/**********************************************************************//** - User clicked "Turn Done" button -**************************************************************************/ -static void end_turn_callback(GtkWidget *w, gpointer data) -{ - gtk_widget_set_sensitive(turn_done_button, FALSE); - user_ended_turn(); -} - -/**********************************************************************//** - Read input from server socket -**************************************************************************/ -static gboolean get_net_input(GIOChannel *source, GIOCondition condition, - gpointer data) -{ - input_from_server(g_io_channel_unix_get_fd(source)); - - return TRUE; -} - -/**********************************************************************//** - Set socket writability state -**************************************************************************/ -static void set_wait_for_writable_socket(struct connection *pc, - bool socket_writable) -{ - static bool previous_state = FALSE; - - fc_assert_ret(pc == &client.conn); - - if (previous_state == socket_writable) { - return; - } - - log_debug("set_wait_for_writable_socket(%d)", socket_writable); - - g_source_remove(srv_id); - srv_id = g_io_add_watch(srv_channel, - G_IO_IN | (socket_writable ? G_IO_OUT : 0) | G_IO_ERR, - get_net_input, - NULL); - - previous_state = socket_writable; -} - -/**********************************************************************//** - This function is called after the client succesfully - has connected to the server -**************************************************************************/ -void add_net_input(int sock) -{ -#ifdef FREECIV_MSWINDOWS - srv_channel = g_io_channel_win32_new_socket(sock); -#else - srv_channel = g_io_channel_unix_new(sock); -#endif - srv_id = g_io_add_watch(srv_channel, - G_IO_IN | G_IO_ERR, - get_net_input, - NULL); - client.conn.notify_of_writable_data = set_wait_for_writable_socket; -} - -/**********************************************************************//** - This function is called if the client disconnects - from the server -**************************************************************************/ -void remove_net_input(void) -{ - g_source_remove(srv_id); - g_io_channel_unref(srv_channel); - gdk_window_set_cursor(root_window, NULL); -} - -/**********************************************************************//** - This is the response callback for the dialog with the message: - Are you sure you want to quit? -**************************************************************************/ -static void quit_dialog_response(GtkWidget *dialog, gint response) -{ - gtk_widget_destroy(dialog); - if (response == GTK_RESPONSE_YES) { - start_quitting(); - if (client.conn.used) { - disconnect_from_server(); - } - quit_gtk_main(); - } -} - -/**********************************************************************//** - Exit gtk main loop. -**************************************************************************/ -void quit_gtk_main(void) -{ - /* Quit gtk main loop. After this it will return to finish - * ui_main() */ - - gtk_main_quit(); -} - -/**********************************************************************//** - Popups the dialog with the message: - Are you sure you want to quit? -**************************************************************************/ -void popup_quit_dialog(void) -{ - static GtkWidget *dialog; - - if (!dialog) { - dialog = gtk_message_dialog_new(NULL, - 0, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_YES_NO, - _("Are you sure you want to quit?")); - setup_dialog(dialog, toplevel); - - gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); - - g_signal_connect(dialog, "response", - G_CALLBACK(quit_dialog_response), NULL); - g_signal_connect(dialog, "destroy", - G_CALLBACK(gtk_widget_destroyed), &dialog); - } - - gtk_window_present(GTK_WINDOW(dialog)); -} - -/**********************************************************************//** - Popups the quit dialog. -**************************************************************************/ -static gboolean quit_dialog_callback(void) -{ - popup_quit_dialog(); - /* Stop emission of event. */ - return TRUE; -} - -struct callback { - void (*callback)(void *data); - void *data; -}; - -/**********************************************************************//** - A wrapper for the callback called through add_idle_callback. -**************************************************************************/ -static gboolean idle_callback_wrapper(gpointer data) -{ - struct callback *cb = data; - - (cb->callback)(cb->data); - free(cb); - - return FALSE; -} - -/**********************************************************************//** - Enqueue a callback to be called during an idle moment. The 'callback' - function should be called sometimes soon, and passed the 'data' pointer - as its data. -**************************************************************************/ -void add_idle_callback(void (callback)(void *), void *data) -{ - struct callback *cb = fc_malloc(sizeof(*cb)); - - cb->callback = callback; - cb->data = data; - g_idle_add(idle_callback_wrapper, cb); -} - -/**********************************************************************//** - Option callback for the 'allied_chat_only' gtk-gui option. - This updates the state of the associated toggle button. -**************************************************************************/ -static void allied_chat_only_callback(struct option *poption) -{ - GtkWidget *button; - - button = allied_chat_toggle_button; - fc_assert_ret(button != NULL); - fc_assert_ret(GTK_IS_TOGGLE_BUTTON(button)); - - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), - option_bool_get(poption)); -} - -/**********************************************************************//** - Change the city names font. -**************************************************************************/ -static void apply_city_names_font(struct option *poption) -{ - gui_update_font_full(option_font_target(poption), - option_font_get(poption), - &city_names_style); - update_city_descriptions(); -} - -/**********************************************************************//** - Change the city productions font. -**************************************************************************/ -static void apply_city_productions_font(struct option *poption) -{ - gui_update_font_full(option_font_target(poption), - option_font_get(poption), - &city_productions_style); - update_city_descriptions(); -} - -/**********************************************************************//** - Change the city productions font. -**************************************************************************/ -static void apply_reqtree_text_font(struct option *poption) -{ - gui_update_font_full(option_font_target(poption), - option_font_get(poption), - &reqtree_text_style); - science_report_dialog_redraw(); -} - -/**********************************************************************//** - Extra initializers for client options. Here we make set the callback - for the specific gui-gtk-3.0 options. -**************************************************************************/ -void options_extra_init(void) -{ - struct option *poption; - -#define option_var_set_callback(var, callback) \ - if ((poption = optset_option_by_name(client_optset, GUI_GTK_OPTION_STR(var)))) { \ - option_set_changed_callback(poption, callback); \ - } else { \ - log_error("Didn't find option %s!", GUI_GTK_OPTION_STR(var)); \ - } - - option_var_set_callback(allied_chat_only, - allied_chat_only_callback); - - option_var_set_callback(font_city_names, - apply_city_names_font); - option_var_set_callback(font_city_productions, - apply_city_productions_font); - option_var_set_callback(font_reqtree_text, - apply_reqtree_text_font); -#undef option_var_set_callback -} - -/**********************************************************************//** - Set the chatline buttons to reflect the state of the game and current - client options. This function should be called on game start. -**************************************************************************/ -void refresh_chat_buttons(void) -{ - GtkWidget *button; - - button = allied_chat_toggle_button; - fc_assert_ret(button != NULL); - fc_assert_ret(GTK_IS_TOGGLE_BUTTON(button)); - - /* Hide the "Allies Only" button for local games. */ - if (is_server_running()) { - gtk_widget_hide(button); - } else { - gtk_widget_show(button); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), - GUI_GTK_OPTION(allied_chat_only)); - } -} - -/**********************************************************************//** - Handle a toggle of the "Allies Only" chat button. -**************************************************************************/ -static void allied_chat_button_toggled(GtkToggleButton *button, - gpointer user_data) -{ - GUI_GTK_OPTION(allied_chat_only) = gtk_toggle_button_get_active(button); -} - -/**********************************************************************//** - Insert build information to help -**************************************************************************/ -void insert_client_build_info(char *outbuf, size_t outlen) -{ - cat_snprintf(outbuf, outlen, _("\nBuilt against gtk+ %d.%d.%d, using %d.%d.%d" - "\nBuilt against glib %d.%d.%d, using %d.%d.%d"), - GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION, - gtk_get_major_version(), gtk_get_minor_version(), gtk_get_micro_version(), - GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION, - glib_major_version, glib_minor_version, glib_micro_version); -} - -/**********************************************************************//** - Return width of the default screen -**************************************************************************/ -int screen_width(void) -{ - GdkScreen *screen; - - if (vmode.width > 0) { - return vmode.width; - } - - screen = gdk_screen_get_default(); - - if (screen == NULL) { - return 0; - } - - return gdk_screen_get_width(screen); -} - -/**********************************************************************//** - Return height of the default screen -**************************************************************************/ -int screen_height(void) -{ - GdkScreen *screen; - - if (vmode.height > 0) { - return vmode.height; - } - - screen = gdk_screen_get_default(); - - if (screen == NULL) { - return 0; - } - - return gdk_screen_get_height(screen); -} - -/**********************************************************************//** - Give resolution requested by user, if any. -**************************************************************************/ -struct video_mode *resolution_request_get(void) -{ - if (vmode.width > 0 && vmode.height > 0) { - return &vmode; - } - - return NULL; -} - -/**********************************************************************//** - Make dynamic adjustments to first-launch default options. -**************************************************************************/ -static void adjust_default_options(void) -{ - int scr_height = screen_height(); - - if (scr_height > 0) { - /* Adjust these options only if we do know the screen height. */ - - if (scr_height <= 480) { - /* Freeciv is practically unusable outside fullscreen mode in so - * small display */ - log_verbose("Changing default to fullscreen due to very small screen"); - GUI_GTK_OPTION(fullscreen) = TRUE; - } - if (scr_height < 1024) { - /* This is a small display */ - log_verbose("Defaulting to small widget layout due to small screen"); - GUI_GTK_OPTION(small_display_layout) = TRUE; - log_verbose("Defaulting to merged messages/chat due to small screen"); - GUI_GTK_OPTION(message_chat_location) = GUI_GTK_MSGCHAT_MERGED; - } - } -} diff --git a/client/gui-gtk-3.0/gui_main.h b/client/gui-gtk-3.0/gui_main.h deleted file mode 100644 index 9991ac8589..0000000000 --- a/client/gui-gtk-3.0/gui_main.h +++ /dev/null @@ -1,88 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__GUI_MAIN_H -#define FC__GUI_MAIN_H - -#include - -/* client */ -#include "gui_main_g.h" - -#define GUI_GTK_OPTION(optname) gui_options.gui_gtk3_##optname -#define GUI_GTK_OPTION_STR(optname) "gui_gtk3_" #optname -#define GUI_GTK_DEFAULT_THEME_NAME FC_GTK3_DEFAULT_THEME_NAME - -/* network string charset conversion */ -gchar *ntoh_str(const gchar *netstr); - -extern PangoFontDescription *city_names_style; -extern PangoFontDescription *city_productions_style; -extern PangoFontDescription *reqtree_text_style; - -#define single_tile_pixmap (mapview.single_tile->pixmap) - -extern GtkTextView * main_message_area; -extern GtkWidget * text_scrollbar; -extern GtkWidget * toplevel; -extern GtkWidget * top_vbox; -extern GtkWidget * main_frame_civ_name; -extern GtkWidget * main_label_info; -extern GtkWidget * econ_label[10]; -extern GtkWidget * bulb_label; -extern GtkWidget * sun_label; -extern GtkWidget * flake_label; -extern GtkWidget * government_label; -extern GtkWidget * econ_ebox; -extern GtkWidget * bulb_ebox; -extern GtkWidget * sun_ebox; -extern GtkWidget * flake_ebox; -extern GtkWidget * government_ebox; -extern GtkWidget * map_canvas; /* GtkDrawingArea */ -extern GtkWidget * overview_canvas; /* GtkDrawingArea */ -extern GtkWidget * overview_scrolled_window; /* GtkScrolledWindow */ -extern GtkWidget * timeout_label; -extern GtkWidget * turn_done_button; -extern GtkWidget * unit_info_box; -extern GtkWidget * unit_info_label; -extern GtkWidget * unit_info_frame; -extern GtkWidget * map_horizontal_scrollbar; -extern GtkWidget * map_vertical_scrollbar; -extern GdkWindow * root_window; - -extern GtkWidget * toplevel_tabs; -extern GtkWidget * top_notebook; -extern GtkWidget * map_widget; -extern GtkWidget * bottom_notebook; -extern GtkWidget * right_notebook; -extern GtkTextBuffer * message_buffer; - -extern int overview_canvas_store_width; -extern int overview_canvas_store_height; - - -void enable_menus(bool enable); - -gboolean map_canvas_focus(void); - -void reset_unit_table(void); -void popup_quit_dialog(void); -void quit_gtk_main(void); -void refresh_chat_buttons(void); - -int screen_width(void); -int screen_height(void); -struct video_mode *resolution_request_get(void); - -bool is_gui_up(void); - -#endif /* FC__GUI_MAIN_H */ diff --git a/client/gui-gtk-3.0/gui_stuff.c b/client/gui-gtk-3.0/gui_stuff.c deleted file mode 100644 index ac17d1056a..0000000000 --- a/client/gui-gtk-3.0/gui_stuff.c +++ /dev/null @@ -1,1141 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "support.h" - -/* client */ -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "colors.h" -#include "gui_main.h" - -#include "gui_stuff.h" - - -static GList *dialog_list; - -static GtkSizeGroup *gui_action; - - -/**********************************************************************//** - Draw widget now -**************************************************************************/ -void gtk_expose_now(GtkWidget *w) -{ - gtk_widget_queue_draw(w); -} - -/**********************************************************************//** - Set window position relative to reference window -**************************************************************************/ -void set_relative_window_position(GtkWindow *ref, GtkWindow *w, int px, int py) -{ - gint x, y, width, height; - - gtk_window_get_position(ref, &x, &y); - gtk_window_get_size(ref, &width, &height); - - x += px * width / 100; - y += py * height / 100; - - gtk_window_move(w, x, y); -} - -/**********************************************************************//** - Create new stock button -**************************************************************************/ -GtkWidget *gtk_stockbutton_new(const gchar *stock, const gchar *label_text) -{ - GtkWidget *button; - GtkWidget *image; - - button = gtk_button_new_with_mnemonic(label_text); - image = gtk_image_new_from_stock(stock, GTK_ICON_SIZE_BUTTON); - gtk_button_set_image(GTK_BUTTON(button), image); - - return button; -} - -/**********************************************************************//** - Changes the label (with mnemonic) on an existing stockbutton. See - gtk_stockbutton_new. -**************************************************************************/ -void gtk_stockbutton_set_label(GtkWidget *button, const gchar *label_text) -{ - gtk_button_set_label(GTK_BUTTON(button), label_text); -} - -/**********************************************************************//** - Returns gettext-converted list of n strings. The individual strings - in the list are as returned by gettext(). In case of no NLS, the strings - will be the original strings, so caller should ensure that the originals - persist for as long as required. (For no NLS, still allocate the - list, for consistency.) - - (This is not directly gui/gtk related, but it fits in here - because so far it is used for doing i18n for gtk titles...) -**************************************************************************/ -void intl_slist(int n, const char **s, bool *done) -{ - int i; - - if (!*done) { - for (i = 0; i < n; i++) { - s[i] = Q_(s[i]); - } - - *done = TRUE; - } -} - -/**********************************************************************//** - Set itree to the beginning -**************************************************************************/ -void itree_begin(GtkTreeModel *model, ITree *it) -{ - it->model = model; - it->end = !gtk_tree_model_get_iter_first(it->model, &it->it); -} - -/**********************************************************************//** - Return whether itree end has been reached -**************************************************************************/ -gboolean itree_end(ITree *it) -{ - return it->end; -} - -/**********************************************************************//** - Make itree to go forward one step -**************************************************************************/ -void itree_next(ITree *it) -{ - it->end = !gtk_tree_model_iter_next(it->model, &it->it); -} - -/**********************************************************************//** - Store values to itree -**************************************************************************/ -void itree_set(ITree *it, ...) -{ - va_list ap; - - va_start(ap, it); - gtk_tree_store_set_valist(GTK_TREE_STORE(it->model), &it->it, ap); - va_end(ap); -} - -/**********************************************************************//** - Get values from itree -**************************************************************************/ -void itree_get(ITree *it, ...) -{ - va_list ap; - - va_start(ap, it); - gtk_tree_model_get_valist(it->model, &it->it, ap); - va_end(ap); -} - -/**********************************************************************//** - Append one item to the end of tree store -**************************************************************************/ -void tstore_append(GtkTreeStore *store, ITree *it, ITree *parent) -{ - it->model = GTK_TREE_MODEL(store); - if (parent) { - gtk_tree_store_append(GTK_TREE_STORE(it->model), &it->it, &parent->it); - } else { - gtk_tree_store_append(GTK_TREE_STORE(it->model), &it->it, NULL); - } - it->end = FALSE; -} - -/**********************************************************************//** - Return whether current itree item is selected -**************************************************************************/ -gboolean itree_is_selected(GtkTreeSelection *selection, ITree *it) -{ - return gtk_tree_selection_iter_is_selected(selection, &it->it); -} - -/**********************************************************************//** - Add current itree item to selection -**************************************************************************/ -void itree_select(GtkTreeSelection *selection, ITree *it) -{ - gtk_tree_selection_select_iter(selection, &it->it); -} - -/**********************************************************************//** - Remove current itree item from selection -**************************************************************************/ -void itree_unselect(GtkTreeSelection *selection, ITree *it) -{ - gtk_tree_selection_unselect_iter(selection, &it->it); -} - -/**********************************************************************//** - Return the selected row in a GtkTreeSelection. - If no row is selected return -1. -**************************************************************************/ -gint gtk_tree_selection_get_row(GtkTreeSelection *selection) -{ - GtkTreeModel *model; - GtkTreeIter it; - gint row = -1; - - if (gtk_tree_selection_get_selected(selection, &model, &it)) { - GtkTreePath *path; - gint *idx; - - path = gtk_tree_model_get_path(model, &it); - idx = gtk_tree_path_get_indices(path); - row = idx[0]; - gtk_tree_path_free(path); - } - return row; -} - -/**********************************************************************//** - Give focus to view -**************************************************************************/ -void gtk_tree_view_focus(GtkTreeView *view) -{ - GtkTreeModel *model; - GtkTreePath *path; - GtkTreeIter iter; - - if ((model = gtk_tree_view_get_model(view)) - && gtk_tree_model_get_iter_first(model, &iter) - && (path = gtk_tree_model_get_path(model, &iter))) { - gtk_tree_view_set_cursor(view, path, NULL, FALSE); - gtk_tree_path_free(path); - gtk_widget_grab_focus(GTK_WIDGET(view)); - } -} - -/**********************************************************************//** - Create an auxiliary menubar (i.e., not the main menubar at the top of - the window). -**************************************************************************/ -GtkWidget *gtk_aux_menu_bar_new(void) -{ - GtkWidget *menubar = gtk_menu_bar_new(); - - /* - * Ubuntu Linux's Ayatana/Unity desktop environment likes to steal the - * application's main menu bar from its window and put it at the top of - * the screen. It needs a hint in order not to steal menu bars other - * than the main one. Gory details at - * https://bugs.launchpad.net/ubuntu/+source/freeciv/+bug/743265 - */ - if (g_object_class_find_property( - G_OBJECT_CLASS(GTK_MENU_BAR_GET_CLASS(menubar)), "ubuntu-local")) { - g_object_set(G_OBJECT(menubar), "ubuntu-local", TRUE, NULL); - } - - return menubar; -} - -/**********************************************************************//** - Generic close callback for all widgets -**************************************************************************/ -static void close_callback(GtkDialog *dialog, gpointer data) -{ - gtk_widget_destroy(GTK_WIDGET(dialog)); -} - -/**********************************************************************//** - This function handles new windows which are subwindows to the - toplevel window. It must be called on every dialog in the game, - so fullscreen windows are handled properly by the window manager. -**************************************************************************/ -void setup_dialog(GtkWidget *shell, GtkWidget *parent) -{ - if (GUI_GTK_OPTION(dialogs_on_top) || GUI_GTK_OPTION(fullscreen)) { - gtk_window_set_transient_for(GTK_WINDOW(shell), - GTK_WINDOW(parent)); - gtk_window_set_type_hint(GTK_WINDOW(shell), - GDK_WINDOW_TYPE_HINT_DIALOG); - } else { - gtk_window_set_type_hint(GTK_WINDOW(shell), - GDK_WINDOW_TYPE_HINT_NORMAL); - } - - /* Close dialog window on Escape keypress. */ - if (GTK_IS_DIALOG(shell)) { - g_signal_connect_after(shell, "close", G_CALLBACK(close_callback), shell); - } -} - -/**********************************************************************//** - Emit a dialog response. -**************************************************************************/ -static void gui_dialog_response(struct gui_dialog *dlg, int response) -{ - if (dlg->response_callback) { - (*dlg->response_callback)(dlg, response, dlg->user_data); - } -} - -/**********************************************************************//** - Default dialog response handler. Destroys the dialog. -**************************************************************************/ -static void gui_dialog_destroyed(struct gui_dialog *dlg, int response, - gpointer data) -{ - gui_dialog_destroy(dlg); -} - -/**********************************************************************//** - Cleanups the leftovers after a dialog is destroyed. -**************************************************************************/ -static void gui_dialog_destroy_handler(GtkWidget *w, struct gui_dialog *dlg) -{ - if (dlg->type == GUI_DIALOG_TAB) { - GtkWidget *notebook = dlg->v.tab.notebook; - gulong handler_id = dlg->v.tab.handler_id; - - g_signal_handler_disconnect(notebook, handler_id); - } - - g_object_unref(dlg->gui_button); - - if (*(dlg->source)) { - *(dlg->source) = NULL; - } - - dialog_list = g_list_remove(dialog_list, dlg); - - /* Raise the return dialog set by gui_dialog_set_return_dialog() */ - if (dlg->return_dialog_id != -1) { - GList *it; - - for (it = dialog_list; it; it = g_list_next(it)) { - struct gui_dialog * adialog = (struct gui_dialog *)it->data; - - if (adialog->id == dlg->return_dialog_id) { - gui_dialog_raise(adialog); - break; - } - } - } - - if (dlg->title) { - free(dlg->title); - } - - free(dlg); -} - -/**********************************************************************//** - Emit a delete event response on dialog deletion in case the end-user - needs to know when a deletion took place. - Popup dialog version -**************************************************************************/ -static gint gui_dialog_delete_handler(GtkWidget *widget, - GdkEventAny *ev, gpointer data) -{ - struct gui_dialog *dlg = data; - - /* emit response signal. */ - gui_dialog_response(dlg, GTK_RESPONSE_DELETE_EVENT); - - /* do the destroy by default. */ - return FALSE; -} - -/**********************************************************************//** - Emit a delete event response on dialog deletion in case the end-user - needs to know when a deletion took place. - TAB version -**************************************************************************/ -static gint gui_dialog_delete_tab_handler(struct gui_dialog* dlg) -{ - GtkWidget* notebook; - int n; - - notebook = dlg->v.tab.notebook; - n = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)); - if (gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), n) - != dlg->v.tab.child) { - gui_dialog_set_return_dialog(dlg, NULL); - } - - /* emit response signal. */ - gui_dialog_response(dlg, GTK_RESPONSE_DELETE_EVENT); - - /* do the destroy by default. */ - return FALSE; -} - -/**********************************************************************//** - Allow the user to close a dialog using Escape or CTRL+W. -**************************************************************************/ -static gboolean gui_dialog_key_press_handler(GtkWidget *w, GdkEventKey *ev, - gpointer data) -{ - struct gui_dialog *dlg = data; - - if (ev->keyval == GDK_KEY_Escape - || ((ev->state & GDK_CONTROL_MASK) && ev->keyval == GDK_KEY_w)) { - /* emit response signal. */ - gui_dialog_response(dlg, GTK_RESPONSE_DELETE_EVENT); - } - - /* propagate event further. */ - return FALSE; -} - -/**********************************************************************//** - Resets tab colour on tab activation. -**************************************************************************/ -static void gui_dialog_switch_page_handler(GtkNotebook *notebook, - GtkWidget *page, - guint num, - struct gui_dialog *dlg) -{ - gint n; - - n = gtk_notebook_page_num(GTK_NOTEBOOK(dlg->v.tab.notebook), dlg->vbox); - - if (n == num) { - gtk_widget_override_color(dlg->v.tab.label, GTK_STATE_FLAG_NORMAL, NULL); - } -} - -/**********************************************************************//** - Changes a tab into a window. -**************************************************************************/ -static void gui_dialog_detach(struct gui_dialog* dlg) -{ - gint n; - GtkWidget *window, *notebook; - gulong handler_id; - - if (dlg->type != GUI_DIALOG_TAB) { - return; - } - dlg->type = GUI_DIALOG_WINDOW; - - /* Create a new reference to the main widget, so it won't be - * destroyed in gtk_notebook_remove_page() */ - g_object_ref(dlg->vbox); - - /* Remove widget from the notebook */ - notebook = dlg->v.tab.notebook; - handler_id = dlg->v.tab.handler_id; - g_signal_handler_disconnect(notebook, handler_id); - - n = gtk_notebook_page_num(GTK_NOTEBOOK(dlg->v.tab.notebook), dlg->vbox); - gtk_notebook_remove_page(GTK_NOTEBOOK(dlg->v.tab.notebook), n); - - - /* Create window and put the widget inside */ - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(window), dlg->title); - setup_dialog(window, toplevel); - - gtk_container_add(GTK_CONTAINER(window), dlg->vbox); - dlg->v.window = window; - g_signal_connect(window, "delete_event", - G_CALLBACK(gui_dialog_delete_handler), dlg); - - gtk_window_set_default_size(GTK_WINDOW(dlg->v.window), - dlg->default_width, - dlg->default_height); - gtk_widget_show_all(window); -} - -/**********************************************************************//** - Someone has clicked on a label in a notebook -**************************************************************************/ -static gboolean click_on_tab_callback(GtkWidget* w, - GdkEventButton* button, - gpointer data) -{ - if (button->type != GDK_2BUTTON_PRESS) { - return FALSE; - } - if (button->button != 1) { - return FALSE; - } - gui_dialog_detach((struct gui_dialog*) data); - return TRUE; -} - - -/**********************************************************************//** - Creates a new dialog. It will be a tab or a window depending on the - current user setting of 'enable_tabs' gtk-gui option. - Sets pdlg to point to the dialog once it is create, Zeroes pdlg on - dialog destruction. - user_data will be passed through response function - check_top indicates if the layout deision should depend on the parent. -**************************************************************************/ -void gui_dialog_new(struct gui_dialog **pdlg, GtkNotebook *notebook, - gpointer user_data, bool check_top) -{ - struct gui_dialog *dlg; - GtkWidget *vbox, *action_area; - static int dialog_id_counter; - - dlg = fc_malloc(sizeof(*dlg)); - dialog_list = g_list_prepend(dialog_list, dlg); - - dlg->source = pdlg; - *pdlg = dlg; - dlg->user_data = user_data; - dlg->title = NULL; - - dlg->default_width = 200; - dlg->default_height = 300; - - if (GUI_GTK_OPTION(enable_tabs)) { - dlg->type = GUI_DIALOG_TAB; - } else { - dlg->type = GUI_DIALOG_WINDOW; - } - - if (!gui_action) { - gui_action = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL); - } - dlg->gui_button = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); - - vbox = gtk_grid_new(); - action_area = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(action_area), 4); - gtk_grid_set_column_spacing(GTK_GRID(action_area), 4); - if (GUI_GTK_OPTION(enable_tabs) - && (check_top && notebook != GTK_NOTEBOOK(top_notebook)) - && !GUI_GTK_OPTION(small_display_layout)) { - /* We expect this to be short (as opposed to tall); maximise usable - * height by putting buttons down the right hand side */ - gtk_orientable_set_orientation(GTK_ORIENTABLE(action_area), - GTK_ORIENTATION_VERTICAL); - } else { - /* We expect this to be reasonably tall; maximise usable width by - * putting buttons along the bottom */ - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - } - - gtk_widget_show(vbox); - gtk_container_add(GTK_CONTAINER(vbox), action_area); - gtk_widget_show(action_area); - - gtk_container_set_border_width(GTK_CONTAINER(vbox), 2); - gtk_container_set_border_width(GTK_CONTAINER(action_area), 2); - - switch (dlg->type) { - case GUI_DIALOG_WINDOW: - { - GtkWidget *window; - - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_widget_set_name(window, "Freeciv"); - gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE); - setup_dialog(window, toplevel); - - gtk_container_add(GTK_CONTAINER(window), vbox); - dlg->v.window = window; - g_signal_connect(window, "delete_event", - G_CALLBACK(gui_dialog_delete_handler), dlg); - - } - break; - case GUI_DIALOG_TAB: - { - GtkWidget *hbox, *label, *image, *button, *event_box; - gchar *buf; - - hbox = gtk_grid_new(); - - label = gtk_label_new(NULL); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_widget_set_margin_left(label, 4); - gtk_widget_set_margin_right(label, 4); - gtk_widget_set_margin_top(label, 0); - gtk_widget_set_margin_bottom(label, 0); - gtk_container_add(GTK_CONTAINER(hbox), label); - - button = gtk_button_new(); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(gui_dialog_delete_tab_handler), dlg); - - buf = g_strdup_printf(_("Close Tab:\n%s"), _("Ctrl+W")); - gtk_widget_set_tooltip_text(button, buf); - g_free(buf); - - image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); - gtk_widget_set_margin_left(image, 0); - gtk_widget_set_margin_right(image, 0); - gtk_widget_set_margin_top(image, 0); - gtk_widget_set_margin_bottom(image, 0); - gtk_button_set_image(GTK_BUTTON(button), image); - - gtk_container_add(GTK_CONTAINER(hbox), button); - - gtk_widget_show_all(hbox); - - event_box = gtk_event_box_new(); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), FALSE); - gtk_container_add(GTK_CONTAINER(event_box), hbox); - - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, event_box); - dlg->v.tab.handler_id = - g_signal_connect(notebook, "switch-page", - G_CALLBACK(gui_dialog_switch_page_handler), dlg); - dlg->v.tab.child = vbox; - - dlg->v.tab.label = label; - dlg->v.tab.notebook = GTK_WIDGET(notebook); - - gtk_widget_add_events(event_box, GDK_BUTTON2_MOTION_MASK); - g_signal_connect(event_box, "button-press-event", - G_CALLBACK(click_on_tab_callback), dlg); - } - break; - } - - dlg->vbox = vbox; - dlg->action_area = action_area; - - dlg->response_callback = gui_dialog_destroyed; - - dlg->id = dialog_id_counter; - dialog_id_counter++; - dlg->return_dialog_id = -1; - - g_signal_connect(vbox, "destroy", - G_CALLBACK(gui_dialog_destroy_handler), dlg); - g_signal_connect(vbox, "key_press_event", - G_CALLBACK(gui_dialog_key_press_handler), dlg); - - g_object_set_data(G_OBJECT(vbox), "gui-dialog-data", dlg); -} - -/**********************************************************************//** - Called when a dialog button is activated. -**************************************************************************/ -static void action_widget_activated(GtkWidget *button, GtkWidget *vbox) -{ - struct gui_dialog *dlg = - g_object_get_data(G_OBJECT(vbox), "gui-dialog-data"); - gpointer arg2 = - g_object_get_data(G_OBJECT(button), "gui-dialog-response-data"); - - gui_dialog_response(dlg, GPOINTER_TO_INT(arg2)); -} - -/**********************************************************************//** - Places a button into a dialog, taking care of setting up signals, etc. -**************************************************************************/ -static void gui_dialog_pack_button(struct gui_dialog *dlg, GtkWidget *button, - int response) -{ - gint signal_id; - - fc_assert_ret(GTK_IS_BUTTON(button)); - - g_object_set_data(G_OBJECT(button), "gui-dialog-response-data", - GINT_TO_POINTER(response)); - - if ((signal_id = g_signal_lookup("clicked", GTK_TYPE_BUTTON))) { - GClosure *closure; - - closure = g_cclosure_new_object(G_CALLBACK(action_widget_activated), - G_OBJECT(dlg->vbox)); - g_signal_connect_closure_by_id(button, signal_id, 0, closure, FALSE); - } - - gtk_container_add(GTK_CONTAINER(dlg->action_area), button); - gtk_size_group_add_widget(gui_action, button); - gtk_size_group_add_widget(dlg->gui_button, button); -} - -/**********************************************************************//** - Adds a button to a dialog, allowing the choice of a special stock item. -**************************************************************************/ -GtkWidget *gui_dialog_add_stockbutton(struct gui_dialog *dlg, - const char *stock, - const char *text, int response) -{ - GtkWidget *button; - - button = gtk_stockbutton_new(stock, text); - gtk_widget_set_can_default(button, TRUE); - gui_dialog_pack_button(dlg, button, response); - - return button; -} - -/**********************************************************************//** - Adds a button to a dialog. -**************************************************************************/ -GtkWidget *gui_dialog_add_button(struct gui_dialog *dlg, - const char *text, int response) -{ - GtkWidget *button; - - button = gtk_button_new_from_stock(text); - gtk_widget_set_can_default(button, TRUE); - gui_dialog_pack_button(dlg, button, response); - - return button; -} - -/**********************************************************************//** - Adds a widget to a dialog. -**************************************************************************/ -GtkWidget *gui_dialog_add_widget(struct gui_dialog *dlg, - GtkWidget *widget) -{ - gtk_container_add(GTK_CONTAINER(dlg->action_area), widget); - gtk_size_group_add_widget(gui_action, widget); - - return widget; -} - -/**********************************************************************//** - Changes the default dialog response. -**************************************************************************/ -void gui_dialog_set_default_response(struct gui_dialog *dlg, int response) -{ - GList *children; - GList *list; - - children = gtk_container_get_children(GTK_CONTAINER(dlg->action_area)); - - for (list = children; list; list = g_list_next(list)) { - GtkWidget *button = list->data; - - if (GTK_IS_BUTTON(button)) { - gpointer data = g_object_get_data(G_OBJECT(button), - "gui-dialog-response-data"); - - if (response == GPOINTER_TO_INT(data)) { - gtk_widget_grab_default(button); - } - } - } - - g_list_free(children); -} - -/**********************************************************************//** - Change the sensitivity of a dialog button. -**************************************************************************/ -void gui_dialog_set_response_sensitive(struct gui_dialog *dlg, - int response, bool setting) -{ - GList *children; - GList *list; - - children = gtk_container_get_children(GTK_CONTAINER(dlg->action_area)); - - for (list = children; list; list = g_list_next(list)) { - GtkWidget *button = list->data; - - if (GTK_IS_BUTTON(button)) { - gpointer data = g_object_get_data(G_OBJECT(button), - "gui-dialog-response-data"); - - if (response == GPOINTER_TO_INT(data)) { - gtk_widget_set_sensitive(button, setting); - } - } - } - - g_list_free(children); -} - -/**********************************************************************//** - Get the dialog's toplevel window. -**************************************************************************/ -GtkWidget *gui_dialog_get_toplevel(struct gui_dialog *dlg) -{ - return gtk_widget_get_toplevel(dlg->vbox); -} - -/**********************************************************************//** - Show the dialog contents, but not the dialog per se. -**************************************************************************/ -void gui_dialog_show_all(struct gui_dialog *dlg) -{ - gtk_widget_show_all(dlg->vbox); - - if (dlg->type == GUI_DIALOG_TAB) { - GList *children; - GList *list; - gint num_visible = 0; - - children = gtk_container_get_children(GTK_CONTAINER(dlg->action_area)); - - for (list = children; list; list = g_list_next(list)) { - GtkWidget *button = list->data; - - if (!GTK_IS_BUTTON(button)) { - num_visible++; - } else { - gpointer data = g_object_get_data(G_OBJECT(button), - "gui-dialog-response-data"); - int response = GPOINTER_TO_INT(data); - - if (response != GTK_RESPONSE_CLOSE - && response != GTK_RESPONSE_CANCEL) { - num_visible++; - } else { - gtk_widget_hide(button); - } - } - } - g_list_free(children); - - if (num_visible == 0) { - gtk_widget_hide(dlg->action_area); - } - } -} - -/**********************************************************************//** - Notify the user the dialog has changed. -**************************************************************************/ -void gui_dialog_present(struct gui_dialog *dlg) -{ - fc_assert_ret(NULL != dlg); - - switch (dlg->type) { - case GUI_DIALOG_WINDOW: - gtk_widget_show(dlg->v.window); - break; - case GUI_DIALOG_TAB: - { - GtkNotebook *notebook = GTK_NOTEBOOK(dlg->v.tab.notebook); - gint current, n; - - current = gtk_notebook_get_current_page(notebook); - n = gtk_notebook_page_num(notebook, dlg->vbox); - - if (current != n) { - GtkWidget *label = dlg->v.tab.label; - GdkRGBA color = {.red = 1.0, .green = 0, .blue = 0, .alpha = 1.0}; - - gtk_widget_override_color(label, GTK_STATE_FLAG_NORMAL, &color); - } - } - break; - } -} - -/**********************************************************************//** - Raise dialog to top. -**************************************************************************/ -void gui_dialog_raise(struct gui_dialog *dlg) -{ - fc_assert_ret(NULL != dlg); - - switch (dlg->type) { - case GUI_DIALOG_WINDOW: - gtk_window_present(GTK_WINDOW(dlg->v.window)); - break; - case GUI_DIALOG_TAB: - { - GtkNotebook *notebook = GTK_NOTEBOOK(dlg->v.tab.notebook); - gint n; - - n = gtk_notebook_page_num(notebook, dlg->vbox); - gtk_notebook_set_current_page(notebook, n); - } - break; - } -} - -/**********************************************************************//** - Alert the user to an important event. -**************************************************************************/ -void gui_dialog_alert(struct gui_dialog *dlg) -{ - fc_assert_ret(NULL != dlg); - - switch (dlg->type) { - case GUI_DIALOG_WINDOW: - break; - case GUI_DIALOG_TAB: - { - GtkNotebook *notebook = GTK_NOTEBOOK(dlg->v.tab.notebook); - gint current, n; - - current = gtk_notebook_get_current_page(notebook); - n = gtk_notebook_page_num(notebook, dlg->vbox); - - if (current != n) { - GtkWidget *label = dlg->v.tab.label; - GdkRGBA color = {.red = 0, .green = 0, .blue =1.0, .alpha = 1.0}; - - gtk_widget_override_color(label, GTK_STATE_FLAG_NORMAL, &color); - } - } - break; - } -} - -/**********************************************************************//** - Sets the dialog's default size (applies to toplevel windows only). -**************************************************************************/ -void gui_dialog_set_default_size(struct gui_dialog *dlg, int width, int height) -{ - dlg->default_width = width; - dlg->default_height = height; - switch (dlg->type) { - case GUI_DIALOG_WINDOW: - gtk_window_set_default_size(GTK_WINDOW(dlg->v.window), width, height); - break; - case GUI_DIALOG_TAB: - break; - } -} - -/**********************************************************************//** - Changes a dialog's title. -**************************************************************************/ -void gui_dialog_set_title(struct gui_dialog *dlg, const char *title) -{ - if (dlg->title) { - free(dlg->title); - } - dlg->title = fc_strdup(title); - switch (dlg->type) { - case GUI_DIALOG_WINDOW: - gtk_window_set_title(GTK_WINDOW(dlg->v.window), title); - break; - case GUI_DIALOG_TAB: - gtk_label_set_text_with_mnemonic(GTK_LABEL(dlg->v.tab.label), title); - break; - } -} - -/**********************************************************************//** - Destroy a dialog. -**************************************************************************/ -void gui_dialog_destroy(struct gui_dialog *dlg) -{ - switch (dlg->type) { - case GUI_DIALOG_WINDOW: - gtk_widget_destroy(dlg->v.window); - break; - case GUI_DIALOG_TAB: - { - gint n; - - n = gtk_notebook_page_num(GTK_NOTEBOOK(dlg->v.tab.notebook), dlg->vbox); - gtk_notebook_remove_page(GTK_NOTEBOOK(dlg->v.tab.notebook), n); - } - break; - } -} - -/**********************************************************************//** - Destroy all dialogs. -**************************************************************************/ -void gui_dialog_destroy_all(void) -{ - GList *it, *it_next; - - for (it = dialog_list; it; it = it_next) { - it_next = g_list_next(it); - - gui_dialog_destroy((struct gui_dialog *)it->data); - } -} - -/**********************************************************************//** - Set the response callback for a dialog. -**************************************************************************/ -void gui_dialog_response_set_callback(struct gui_dialog *dlg, - GUI_DIALOG_RESPONSE_FUN fun) -{ - dlg->response_callback = fun; -} - -/**********************************************************************//** - When the dlg dialog is destroyed the return_dialog will be raised -**************************************************************************/ -void gui_dialog_set_return_dialog(struct gui_dialog *dlg, - struct gui_dialog *return_dialog) -{ - if (return_dialog == NULL) { - dlg->return_dialog_id = -1; - } else { - dlg->return_dialog_id = return_dialog->id; - } -} - -/**********************************************************************//** - Updates a gui font style. -**************************************************************************/ -void gui_update_font(const char *font_name, const char *font_value) -{ - char *str; - GtkCssProvider *provider; - PangoFontDescription *desc; - int size; - const char *fam; - const char *style; - const char *weight; - - desc = pango_font_description_from_string(font_value); - - if (desc == NULL) { - return; - } - - fam = pango_font_description_get_family(desc); - - if (fam == NULL) { - return; - } - - if (pango_font_description_get_style(desc) == PANGO_STYLE_ITALIC) { - style = "\n font-style: italic;"; - } else { - style = ""; - } - - if (pango_font_description_get_weight(desc) >= 700) { - weight = "\n font-weight: bold;"; - } else { - weight = ""; - } - - size = pango_font_description_get_size(desc); - - if (size != 0) { - str = g_strdup_printf("#Freeciv #%s { font-family: %s; font-size: %dpx;%s%s}", - font_name, fam, size / PANGO_SCALE, style, weight); - } else { - str = g_strdup_printf("#Freeciv #%s { font-family: %s;%s%s}", - font_name, fam, style, weight); - } - - pango_font_description_free(desc); - - provider = gtk_css_provider_new(); - gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(provider), - str, -1, NULL); - gtk_style_context_add_provider_for_screen( - gtk_widget_get_screen(toplevel), GTK_STYLE_PROVIDER(provider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - g_free(str); -} - -/**********************************************************************//** - Update a font option which is not attached to a widget. -**************************************************************************/ -void gui_update_font_full(const char *font_name, const char *font_value, - PangoFontDescription **font_desc) -{ - PangoFontDescription *f_desc; - - gui_update_font(font_name, font_value); - - f_desc = pango_font_description_from_string(font_value); - pango_font_description_free(*font_desc); - - *font_desc = f_desc; -} - -/**********************************************************************//** - Temporarily disable signal invocation of the given callback for the given - GObject. Re-enable the signal with enable_gobject_callback. -**************************************************************************/ -void disable_gobject_callback(GObject *obj, GCallback cb) -{ - gulong hid; - - if (!obj || !cb) { - return; - } - - hid = g_signal_handler_find(obj, G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, cb, NULL); - g_signal_handler_block(obj, hid); -} - -/**********************************************************************//** - Re-enable a signal callback blocked by disable_gobject_callback. -**************************************************************************/ -void enable_gobject_callback(GObject *obj, GCallback cb) -{ - gulong hid; - - if (!obj || !cb) { - return; - } - - hid = g_signal_handler_find(obj, G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, cb, NULL); - g_signal_handler_unblock(obj, hid); -} - -/**********************************************************************//** - Convenience function to add a column to a GtkTreeView. Returns the added - column, or NULL if an error occurred. -**************************************************************************/ -GtkTreeViewColumn *add_treeview_column(GtkWidget *view, const char *title, - GType gtype, int model_index) -{ - GtkTreeViewColumn *col; - GtkCellRenderer *rend; - const char *attr; - - fc_assert_ret_val(view != NULL, NULL); - fc_assert_ret_val(GTK_IS_TREE_VIEW(view), NULL); - fc_assert_ret_val(title != NULL, NULL); - - if (gtype == G_TYPE_BOOLEAN) { - rend = gtk_cell_renderer_toggle_new(); - attr = "active"; - } else if (gtype == GDK_TYPE_PIXBUF) { - rend = gtk_cell_renderer_pixbuf_new(); - attr = "pixbuf"; - } else { - rend = gtk_cell_renderer_text_new(); - attr = "text"; - } - - col = gtk_tree_view_column_new_with_attributes(title, rend, attr, - model_index, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - return col; -} diff --git a/client/gui-gtk-3.0/gui_stuff.h b/client/gui-gtk-3.0/gui_stuff.h deleted file mode 100644 index c339749bfd..0000000000 --- a/client/gui-gtk-3.0/gui_stuff.h +++ /dev/null @@ -1,133 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__GUI_STUFF_H -#define FC__GUI_STUFF_H - -#include - -/* utility */ -#include "shared.h" - -GtkWidget *gtk_stockbutton_new(const gchar *stock, const gchar *label_text); -void gtk_stockbutton_set_label(GtkWidget *button, const gchar *label_text); -void gtk_expose_now(GtkWidget *w); -void set_relative_window_position(GtkWindow *ref, GtkWindow *w, int px, int py); - -void intl_slist(int n, const char **s, bool *done); - -/* the standard GTK+ 2.0 API is braindamaged. this is slightly better! */ - -typedef struct -{ - GtkTreeModel *model; - gboolean end; - GtkTreeIter it; -} ITree; - -#define TREE_ITER_PTR(x) (&(x).it) - -void itree_begin(GtkTreeModel *model, ITree *it); -gboolean itree_end(ITree *it); -void itree_next(ITree *it); -void itree_get(ITree *it, ...); -void itree_set(ITree *it, ...); - -void tstore_append(GtkTreeStore *store, ITree *it, ITree *parent); - -gboolean itree_is_selected(GtkTreeSelection *selection, ITree *it); -void itree_select(GtkTreeSelection *selection, ITree *it); -void itree_unselect(GtkTreeSelection *selection, ITree *it); - -gint gtk_tree_selection_get_row(GtkTreeSelection *selection); -void gtk_tree_view_focus(GtkTreeView *view); -void setup_dialog(GtkWidget *shell, GtkWidget *parent); -GtkTreeViewColumn *add_treeview_column(GtkWidget *view, const char *title, - GType gtype, int model_index); - -GtkWidget *gtk_aux_menu_bar_new(void); - -enum gui_dialog_type { - GUI_DIALOG_WINDOW, - GUI_DIALOG_TAB -}; - -struct gui_dialog; - -typedef void (*GUI_DIALOG_RESPONSE_FUN)(struct gui_dialog *, int, gpointer); - -struct gui_dialog -{ - /* public. */ - GtkWidget *vbox; - GtkWidget *action_area; - - /* private. */ - char *title; - enum gui_dialog_type type; - int id; - int return_dialog_id; - - int default_width; - int default_height; - - union { - GtkWidget *window; - struct { - GtkWidget *label; - GtkWidget *notebook; - gulong handler_id; - GtkWidget *child; - } tab; - } v; - - struct gui_dialog **source; - - GUI_DIALOG_RESPONSE_FUN response_callback; - gpointer user_data; - - GtkSizeGroup *gui_button; -}; - -void gui_dialog_new(struct gui_dialog **pdlg, GtkNotebook *notebook, - gpointer user_data, bool check_top); -void gui_dialog_set_default_response(struct gui_dialog *dlg, int response); -GtkWidget *gui_dialog_add_button(struct gui_dialog *dlg, - const char *text, int response); -GtkWidget *gui_dialog_add_stockbutton(struct gui_dialog *dlg, - const char *stock, const char *text, int response); -GtkWidget *gui_dialog_add_widget(struct gui_dialog *dlg, - GtkWidget *widget); -void gui_dialog_set_default_size(struct gui_dialog *dlg, - int width, int height); -void gui_dialog_set_title(struct gui_dialog *dlg, const char *title); -void gui_dialog_set_response_sensitive(struct gui_dialog *dlg, - int response, bool setting); -void gui_dialog_show_all(struct gui_dialog *dlg); -void gui_dialog_present(struct gui_dialog *dlg); -void gui_dialog_raise(struct gui_dialog *dlg); -void gui_dialog_alert(struct gui_dialog *dlg); -void gui_dialog_destroy(struct gui_dialog *dlg); -void gui_dialog_destroy_all(void); -GtkWidget *gui_dialog_get_toplevel(struct gui_dialog *dlg); -void gui_dialog_response_set_callback(struct gui_dialog *dlg, - GUI_DIALOG_RESPONSE_FUN fun); -void gui_dialog_set_return_dialog(struct gui_dialog *dlg, - struct gui_dialog *return_dialog); - -void gui_update_font_full(const char *font_name, const char *font_value, - PangoFontDescription **font_desc); - -void disable_gobject_callback(GObject *obj, GCallback cb); -void enable_gobject_callback(GObject *obj, GCallback cb); - -#endif /* FC__GUI_STUFF_H */ diff --git a/client/gui-gtk-3.0/happiness.c b/client/gui-gtk-3.0/happiness.c deleted file mode 100644 index 2a6ae97951..0000000000 --- a/client/gui-gtk-3.0/happiness.c +++ /dev/null @@ -1,360 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "support.h" - -/* common */ -#include "city.h" -#include "game.h" -#include "government.h" - -/* client */ -#include "text.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "graphics.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "happiness.h" -#include "mapview.h" - -/* semi-arbitrary number that controls the width of the happiness widget */ -#define HAPPINESS_PIX_WIDTH 30 - -#define FEELING_WIDTH (HAPPINESS_PIX_WIDTH * tileset_small_sprite_width(tileset)) -#define FEELING_HEIGHT (tileset_small_sprite_height(tileset)) - -#define NUM_HAPPINESS_MODIFIERS 6 - -enum { CITIES, LUXURIES, BUILDINGS, NATIONALITY, UNITS, WONDERS }; - -struct happiness_dialog { - struct city *pcity; - GtkWidget *win; - GtkWidget *shell; - GtkWidget *cityname_label; - cairo_surface_t *feeling_surfaces[NUM_HAPPINESS_MODIFIERS]; - GtkWidget *happiness_ebox[NUM_HAPPINESS_MODIFIERS]; - GtkWidget *happiness_label[NUM_HAPPINESS_MODIFIERS]; - GtkWidget *close; -}; - -#define SPECLIST_TAG dialog -#define SPECLIST_TYPE struct happiness_dialog -#include "speclist.h" - -#define dialog_list_iterate(dialoglist, pdialog) \ - TYPED_LIST_ITERATE(struct happiness_dialog, dialoglist, pdialog) -#define dialog_list_iterate_end LIST_ITERATE_END - -static struct dialog_list *dialog_list; -static struct happiness_dialog *get_happiness_dialog(struct city *pcity); -static struct happiness_dialog *create_happiness_dialog(struct city *pcity, - bool low_dlg, - GtkWidget *win); -static gboolean show_happiness_popup(GtkWidget *w, - GdkEventButton *ev, - gpointer data); -static gboolean show_happiness_button_release(GtkWidget *w, - GdkEventButton *ev, - gpointer data); - -/**********************************************************************//** - Create happiness dialog -**************************************************************************/ -void happiness_dialog_init(void) -{ - dialog_list = dialog_list_new(); -} - -/**********************************************************************//** - Remove happiness dialog -**************************************************************************/ -void happiness_dialog_done(void) -{ - dialog_list_destroy(dialog_list); -} - -/**********************************************************************//** - Return happiness dialog for a city -**************************************************************************/ -static struct happiness_dialog *get_happiness_dialog(struct city *pcity) -{ - dialog_list_iterate(dialog_list, pdialog) { - if (pdialog->pcity == pcity) { - return pdialog; - } - } dialog_list_iterate_end; - - return NULL; -} - -/**********************************************************************//** - Popup for the happiness display. -**************************************************************************/ -static gboolean show_happiness_popup(GtkWidget *w, - GdkEventButton *ev, - gpointer data) -{ - struct happiness_dialog *pdialog = g_object_get_data(G_OBJECT(w), - "pdialog"); - - if (ev->button == 1) { - GtkWidget *p, *label, *frame; - char buf[1024]; - - switch (GPOINTER_TO_UINT(data)) { - case CITIES: - sz_strlcpy(buf, text_happiness_cities(pdialog->pcity)); - break; - case LUXURIES: - sz_strlcpy(buf, text_happiness_luxuries(pdialog->pcity)); - break; - case BUILDINGS: - sz_strlcpy(buf, text_happiness_buildings(pdialog->pcity)); - break; - case NATIONALITY: - sz_strlcpy(buf, text_happiness_nationality(pdialog->pcity)); - break; - case UNITS: - sz_strlcpy(buf, text_happiness_units(pdialog->pcity)); - break; - case WONDERS: - sz_strlcpy(buf, text_happiness_wonders(pdialog->pcity)); - break; - default: - return TRUE; - } - - p = gtk_window_new(GTK_WINDOW_POPUP); - gtk_widget_set_name(p, "Freeciv"); - gtk_container_set_border_width(GTK_CONTAINER(p), 2); - gtk_window_set_transient_for(GTK_WINDOW(p), GTK_WINDOW(pdialog->win)); - gtk_window_set_position(GTK_WINDOW(p), GTK_WIN_POS_MOUSE); - - frame = gtk_frame_new(NULL); - gtk_container_add(GTK_CONTAINER(p), frame); - - label = gtk_label_new(buf); - /* FIXME: there is no font option corresponding to this style name. - * Remove?: */ - gtk_widget_set_name(label, "city_happiness_label"); - gtk_widget_set_margin_left(label, 4); - gtk_widget_set_margin_right(label, 4); - gtk_widget_set_margin_top(label, 4); - gtk_widget_set_margin_bottom(label, 4); - gtk_container_add(GTK_CONTAINER(frame), label); - gtk_widget_show_all(p); - - gdk_device_grab(ev->device, gtk_widget_get_window(p), - GDK_OWNERSHIP_NONE, TRUE, GDK_BUTTON_RELEASE_MASK, NULL, - ev->time); - gtk_grab_add(p); - - g_signal_connect_after(p, "button_release_event", - G_CALLBACK(show_happiness_button_release), NULL); - } - - return TRUE; -} - -/**********************************************************************//** - Clear the happiness popup. -**************************************************************************/ -static gboolean show_happiness_button_release(GtkWidget *w, - GdkEventButton *ev, - gpointer data) -{ - gtk_grab_remove(w); - gdk_device_ungrab(ev->device, ev->time); - gtk_widget_destroy(w); - return FALSE; -} - -/**********************************************************************//** - Create the happiness notebook page. -**************************************************************************/ -static struct happiness_dialog *create_happiness_dialog(struct city *pcity, - bool low_dlg, - GtkWidget *win) -{ - int i; - struct happiness_dialog *pdialog; - GtkWidget *ebox, *label, *table; - char buf[700]; - - static const char *happiness_label_str[NUM_HAPPINESS_MODIFIERS] = { - N_("Cities:"), - N_("Luxuries:"), - N_("Buildings:"), - N_("Nationality:"), - N_("Units:"), - N_("Wonders:"), - }; - static bool happiness_label_str_done; - - pdialog = fc_malloc(sizeof(struct happiness_dialog)); - pdialog->pcity = pcity; - - pdialog->shell = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(pdialog->shell), - GTK_ORIENTATION_VERTICAL); - - pdialog->cityname_label = gtk_frame_new(_("Happiness")); - gtk_container_add(GTK_CONTAINER(pdialog->shell), pdialog->cityname_label); - - table = gtk_grid_new(); - g_object_set(table, "margin", 4, NULL); - gtk_grid_set_row_spacing(GTK_GRID(table), 10); - - intl_slist(ARRAY_SIZE(happiness_label_str), happiness_label_str, - &happiness_label_str_done); - - gtk_container_add(GTK_CONTAINER(pdialog->cityname_label), table); - - for (i = 0; i < NUM_HAPPINESS_MODIFIERS; i++) { - GtkWidget *img; - - /* set spacing between lines of citizens*/ - - /* happiness labels */ - label = gtk_label_new(happiness_label_str[i]); - pdialog->happiness_label[i] = label; - gtk_widget_set_name(label, "city_label"); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - - gtk_grid_attach(GTK_GRID(table), label, 0, i, 1, 1); - - /* list of citizens */ - ebox = gtk_event_box_new(); - gtk_widget_set_margin_left(ebox, 5); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); - g_object_set_data(G_OBJECT(ebox), "pdialog", pdialog); - g_signal_connect(ebox, "button_press_event", - G_CALLBACK(show_happiness_popup), GUINT_TO_POINTER(i)); - pdialog->happiness_ebox[i] = ebox; - - pdialog->feeling_surfaces[i] = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - FEELING_WIDTH, FEELING_HEIGHT); - img = gtk_image_new_from_surface(pdialog->feeling_surfaces[i]); - gtk_container_add(GTK_CONTAINER(ebox), img); - gtk_widget_set_halign(img, GTK_ALIGN_START); - gtk_widget_set_valign(img, GTK_ALIGN_START); - - gtk_grid_attach(GTK_GRID(table), ebox, 1, i, 1, 1); - } - - /* TRANS: the width of this text defines the width of the city dialog. - * '%s' is either space or newline depending on screen real estate. */ - fc_snprintf(buf, sizeof(buf), - _("Additional information is available%svia left " - "click on the citizens."), low_dlg ? "\n" : " "); - label = gtk_label_new(buf); - gtk_widget_set_name(label, "city_label"); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_grid_attach(GTK_GRID(table), label, 0, NUM_HAPPINESS_MODIFIERS, 2, 1); - - gtk_widget_show_all(pdialog->shell); - dialog_list_prepend(dialog_list, pdialog); - refresh_happiness_dialog(pcity); - - pdialog->win = win; - - return pdialog; -} - -/**********************************************************************//** - Refresh citizens surface -**************************************************************************/ -static void refresh_feeling_surface(cairo_surface_t *dst, struct city *pcity, - enum citizen_feeling index) -{ - enum citizen_category categories[MAX_CITY_SIZE]; - int i; - int num_citizens = get_city_citizen_types(pcity, index, categories); - int offset = MIN(tileset_small_sprite_width(tileset), FEELING_WIDTH / num_citizens); - cairo_t *cr; - - cr = cairo_create(dst); - - for (i = 0; i < num_citizens; i++) { - cairo_set_source_surface(cr, - get_citizen_sprite(tileset, categories[i], i, pcity)->surface, - i * offset, 0); - cairo_rectangle(cr, i * offset, 0, offset, FEELING_HEIGHT); - cairo_fill(cr); - } - - cairo_destroy(cr); -} - -/**********************************************************************//** - Refresh whole happiness dialog -**************************************************************************/ -void refresh_happiness_dialog(struct city *pcity) -{ - int i; - struct happiness_dialog *pdialog = get_happiness_dialog(pcity); - - for (i = 0; i < FEELING_LAST; i++) { - refresh_feeling_surface(pdialog->feeling_surfaces[i], pdialog->pcity, i); - } -} - -/**********************************************************************//** - Close happiness dialog of given city -**************************************************************************/ -void close_happiness_dialog(struct city *pcity) -{ - struct happiness_dialog *pdialog = get_happiness_dialog(pcity); - int i; - - if (pdialog == NULL) { - /* City which is being investigated doesn't contain happiness tab */ - return; - } - - gtk_widget_hide(pdialog->shell); - - dialog_list_remove(dialog_list, pdialog); - - gtk_widget_destroy(pdialog->shell); - - for (i = 0; i < NUM_HAPPINESS_MODIFIERS; i++) { - cairo_surface_destroy(pdialog->feeling_surfaces[i]); - } - - free(pdialog); -} - -/**********************************************************************//** - Create happiness dialog and get its widget -**************************************************************************/ -GtkWidget *get_top_happiness_display(struct city *pcity, bool low_dlg, - GtkWidget *win) -{ - return create_happiness_dialog(pcity, low_dlg, win)->shell; -} diff --git a/client/gui-gtk-3.0/happiness.h b/client/gui-gtk-3.0/happiness.h deleted file mode 100644 index 4778906756..0000000000 --- a/client/gui-gtk-3.0/happiness.h +++ /dev/null @@ -1,29 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__HAPPINESS_H -#define FC__HAPPINESS_H - -#include - -/* client/gui-gtk-3.0 */ -#include "citydlg.h" - -void happiness_dialog_init(void); -void happiness_dialog_done(void); -GtkWidget *get_top_happiness_display(struct city *pcity, - bool low_dlg, - GtkWidget *win); -void close_happiness_dialog(struct city *pcity); -void refresh_happiness_dialog(struct city *pcity); - -#endif /* FC__HAPPINESS_H */ diff --git a/client/gui-gtk-3.0/helpdlg.c b/client/gui-gtk-3.0/helpdlg.c deleted file mode 100644 index 48d24d93c6..0000000000 --- a/client/gui-gtk-3.0/helpdlg.c +++ /dev/null @@ -1,1674 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include /* sqrt */ - -#include - -/* utility */ -#include "fcintl.h" -#include "mem.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "city.h" -#include "game.h" -#include "government.h" -#include "movement.h" -#include "specialist.h" -#include "tech.h" -#include "unit.h" -#include "map.h" -#include "research.h" -#include "version.h" - -/* client */ -#include "client_main.h" -#include "climisc.h" -#include "helpdata.h" -#include "options.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "colors.h" -#include "graphics.h" -#include "gui_main.h" -#include "gui_stuff.h" - -#include "helpdlg.h" - -#define TECH_TREE_DEPTH 20 - -/* - * Globals. - */ -static GtkWidget *help_dialog_shell; -static GtkWidget *help_view_sw; - -static GtkWidget *help_view; - -static GtkWidget *help_frame; -static GtkTextBuffer *help_text; -static GtkWidget *help_text_sw; -static GtkWidget *help_vbox; -static GtkWidget *help_tile; -static GtkWidget *help_box; -static GtkWidget *help_itable; -static GtkWidget *help_wtable; -static GtkWidget *help_utable; -static GtkWidget *help_ttable; -static GtkWidget *help_etable; -static GtkWidget *help_tree; -static GtkTreeStore *tstore; - -static GtkWidget *help_tree_sw; -static GtkWidget *help_tree_expand; -static GtkWidget *help_tree_collapse; -static GtkWidget *help_tree_buttons_hbox; -static GtkWidget *help_ilabel[6]; -static GtkWidget *help_wlabel[6]; -static GtkWidget *help_ulabel[5][5]; -static GtkWidget *help_tlabel[2][5]; -static GtkWidget *help_elabel[6]; - -static bool help_advances[A_LAST]; - -static GPtrArray *help_history; -static int help_history_pos; - - -static const char *help_ilabel_name[6] = -{ N_("Base Cost:"), NULL, N_("Upkeep:"), NULL, N_("Requirement:"), NULL }; - -static const char *help_wlabel_name[6] = -{ N_("Base Cost:"), NULL, N_("Requirement:"), NULL, N_("Obsolete by:"), NULL }; - -static const char *help_ulabel_name[5][5] = -{ - { N_("Cost:"), NULL, NULL, N_("Attack:"), NULL }, - { N_("Defense:"), NULL, NULL, N_("Move:") , NULL }, - { N_("FirePower:"), NULL, NULL, N_("Hitpoints:"), NULL }, - { N_("Basic Upkeep:"), NULL, NULL, N_("Vision:"), NULL }, - { N_("Requirement:"), NULL, NULL, N_("Obsolete by:"), NULL } -}; - -static const char *help_tlabel_name[2][5] = -{ - { N_("Move/Defense:"), NULL, NULL, N_("Food/Res/Trade:"), NULL }, - { N_("Resources:"), NULL, NULL, NULL, NULL } -}; - -static const char *help_elabel_name[6] = -/* TRANS: Label for build cost for extras in help. Will be followed by - * something like "3 MP" (where MP = Movement Points) */ -{ N_("Build:"), NULL, -/* TRANS: Extra conflicts in help. Will be followed by a list of extras - * that can't be built on the same tile as this one. */ - N_("Conflicts with:"), NULL, -/* TRANS: Extra bonus in help. Will be followed by food/production/trade - * stats like "0/0/+1", "0/+50%/0" */ - N_("Bonus (F/P/T):"), NULL }; - -#define REQ_LABEL_NONE _("?tech:None") -#define REQ_LABEL_NEVER _("(Never)") - -static void create_help_dialog(void); -static void help_update_dialog(const struct help_item *pitem); -static void create_help_page(enum help_page_type type); - -static void select_help_item_string(const char *item, - enum help_page_type htype); -static void help_command_update(void); -static void help_command_callback(GtkWidget *w, gint response_id); - -/**********************************************************************//** - Set topic specific title for help_frame -**************************************************************************/ -static void set_title_topic(char *topic) -{ - if (strcmp(topic, _(HELP_ABOUT_ITEM)) == 0) { - gtk_frame_set_label(GTK_FRAME(help_frame), freeciv_name_version()); - } else { - gtk_frame_set_label(GTK_FRAME(help_frame), topic); - } - return; -} - -/**********************************************************************//** - Close help dialog -**************************************************************************/ -void popdown_help_dialog(void) -{ - if (help_dialog_shell) { - gtk_widget_destroy(help_dialog_shell); - } -} - -/**********************************************************************//** - Popup help dialog for given item of given type. -**************************************************************************/ -void popup_help_dialog_typed(const char *item, enum help_page_type htype) -{ - if (!help_dialog_shell) { - create_help_dialog(); - set_relative_window_position(GTK_WINDOW(toplevel), - GTK_WINDOW(help_dialog_shell), 10, 10); - } - gtk_window_present(GTK_WINDOW(help_dialog_shell)); - - select_help_item_string(item, htype); -} - -/**********************************************************************//** - Not sure if this should call Q_(item) as it does, or whether all - callers of this function should do so themselves... --dwp -**************************************************************************/ -void popup_help_dialog_string(const char *item) -{ - popup_help_dialog_typed(Q_(item), HELP_ANY); -} - -/**********************************************************************//** - Called by help_update_tech and itself - Creates a node in the given tree for the given tech, and creates child - nodes for any children it has up to levels deep. These are then expanded - if they are less than expanded_levels deep. Avoids generating redundant - subtrees, so that if Literacy occurs twice in a tech tree, only the first - will have children. Color codes the node based on when it will be - discovered: red >2 turns, yellow 1 turn, green 0 turns (discovered). -**************************************************************************/ -static void create_tech_tree(int tech, int levels, GtkTreeIter *parent) -{ - const struct research *presearch; - int bg; - int turns_to_tech; - bool original; - GtkTreeIter l; - GValue value = { 0, }; - - if (advance_required(tech, AR_ONE) == A_LAST - && advance_required(tech, AR_TWO) == A_LAST) { - bg = COLOR_REQTREE_UNKNOWN; - - gtk_tree_store_append(tstore, &l, parent); - help_advances[tech] = TRUE; - - g_value_init(&value, G_TYPE_STRING); - g_value_set_static_string(&value, _("Removed")); - gtk_tree_store_set_value(tstore, &l, 0, &value); - g_value_unset(&value); - - gtk_tree_store_set(tstore, &l, - 1, -1, - 2, tech, - 3, &get_color(tileset, bg)->color - -1); - return; - } - - presearch = research_get(client_player()); - - bg = COLOR_REQTREE_BACKGROUND; - switch (research_invention_state(presearch, tech)) { - case TECH_UNKNOWN: - bg = COLOR_REQTREE_UNKNOWN; - break; - case TECH_KNOWN: - bg = COLOR_REQTREE_KNOWN; - break; - case TECH_PREREQS_KNOWN: - bg = COLOR_REQTREE_PREREQS_KNOWN; - break; - } - turns_to_tech = research_goal_unknown_techs(presearch, tech); - - /* l is the original in the tree. */ - original = !help_advances[tech]; - - gtk_tree_store_append(tstore, &l, parent); - help_advances[tech] = TRUE; - - g_value_init(&value, G_TYPE_STRING); - g_value_set_static_string(&value, - research_advance_name_translation(presearch, - tech)); - gtk_tree_store_set_value(tstore, &l, 0, &value); - g_value_unset(&value); - - gtk_tree_store_set(tstore, &l, - 1, turns_to_tech, - 2, tech, - 3, &get_color(tileset, bg)->color, - -1); - - if (--levels <= 0) - return; - - if (original) { - /* only add children to orginals */ - if (advance_required(tech, AR_ONE) != A_NONE) - create_tech_tree(advance_required(tech, AR_ONE), levels, &l); - if (advance_required(tech, AR_TWO) != A_NONE) - create_tech_tree(advance_required(tech, AR_TWO), levels, &l); - } - return; -} - -/**********************************************************************//** - Selects the help page for the tech in the tree that was double clicked. -**************************************************************************/ -static void help_tech_tree_activated_callback(GtkTreeView *view, - GtkTreePath *path, - GtkTreeViewColumn *col, - gpointer data) -{ - GtkTreeIter it; - gint tech; - - gtk_tree_model_get_iter(GTK_TREE_MODEL(tstore), &it, path); - gtk_tree_model_get(GTK_TREE_MODEL(tstore), &it, 2, &tech, -1); - select_help_item_string(advance_name_translation(advance_by_number(tech)), - HELP_TECH); -} - -/**********************************************************************//** - Called when "Expand All" button is clicked -**************************************************************************/ -static void help_tech_tree_expand_callback(GtkWidget *w, gpointer data) -{ - gtk_tree_view_expand_all(GTK_TREE_VIEW(data)); -} - -/**********************************************************************//** - Called when "Collapse All" button is clicked -**************************************************************************/ -static void help_tech_tree_collapse_callback(GtkWidget *w, gpointer data) -{ - gtk_tree_view_collapse_all(GTK_TREE_VIEW(data)); -} - -/**********************************************************************//** - Hyperlink clicked -**************************************************************************/ -static void help_hyperlink_callback(GtkWidget *w) -{ - const char *s; - enum help_page_type type; - - s=gtk_label_get_text(GTK_LABEL(w)); - type=GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(w), "page_type")); - - /* FIXME: May be able to skip, or may need to modify, advances[A_NONE] - below, depending on which i18n is done elsewhere. - */ - if (strcmp(s, REQ_LABEL_NEVER) != 0 - && strcmp(s, skip_intl_qualifier_prefix(REQ_LABEL_NONE)) != 0 - && strcmp(s, advance_name_translation(advance_by_number(A_NONE))) != 0) - select_help_item_string(s, type); -} - -/**********************************************************************//** - Create new hyperlink button -**************************************************************************/ -static GtkWidget *help_hyperlink_new(GtkWidget *label, enum help_page_type type) -{ - GtkWidget *button; - - button = gtk_button_new(); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_widget_set_halign(label, GTK_ALIGN_CENTER); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_widget_set_name(label, "help_link"); - gtk_container_add(GTK_CONTAINER(button), label); - gtk_widget_show(button); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(help_hyperlink_callback), label); - g_object_set_data(G_OBJECT(label), "page_type", GUINT_TO_POINTER(type)); - return button; -} - -/**********************************************************************//** - Create new hyperlink button with text -**************************************************************************/ -static GtkWidget *help_slink_new(const gchar *txt, enum help_page_type type) -{ - GtkWidget *button, *label; - - label = gtk_label_new(txt); - gtk_widget_set_halign(label, GTK_ALIGN_CENTER); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - button = help_hyperlink_new(label, type); - - return button; -} - -/**********************************************************************//** - Hide help box -**************************************************************************/ -static void help_box_hide(void) -{ - gtk_widget_hide(help_box); - - gtk_widget_hide(help_tile); - - gtk_widget_hide(help_itable); - gtk_widget_hide(help_wtable); - gtk_widget_hide(help_utable); - gtk_widget_hide(help_ttable); - gtk_widget_hide(help_etable); - - gtk_widget_hide(help_tile); /* FIXME: twice? */ - - gtk_widget_hide(help_vbox); - gtk_widget_hide(help_text_sw); - - gtk_widget_hide(help_tree_sw); - gtk_widget_hide(help_tree_buttons_hbox); -} - -/**********************************************************************//** - Completely destory help dialog -**************************************************************************/ -static void help_destroy_callback(GtkWidget *w, gpointer data) -{ - g_ptr_array_free(help_history, TRUE); - help_dialog_shell = NULL; -} - -/**********************************************************************//** - New topic activated from help dialog -**************************************************************************/ -static void activated_topic(GtkTreeView *view, gpointer data) -{ - GtkTreePath *path; - GtkTreeViewColumn *col; - GtkTreeModel *model; - GtkTreeIter it; - struct help_item *pitem; - - model = gtk_tree_view_get_model(view); - - gtk_tree_view_get_cursor(view, &path, &col); - gtk_tree_model_get_iter(model, &it, path); - gtk_tree_path_free(path); - - if (!path) { - return; - } - - gtk_tree_model_get(model, &it, 1, &pitem, -1); - - if (help_history_pos >= 0 - && g_ptr_array_index(help_history, help_history_pos) - == (gpointer) pitem) { - return; - } - - help_update_dialog(pitem); - - /* add to history. */ - if (help_history_pos < help_history->len - 1) { - g_ptr_array_set_size(help_history, help_history_pos + 1); - } - help_history_pos++; - - g_ptr_array_add(help_history, (gpointer)pitem); - help_command_update(); -} - -/**********************************************************************//** - Create help dialog -**************************************************************************/ -static void create_help_dialog(void) -{ - GtkWidget *hbox; - GtkWidget *button; - GtkWidget *text; - int i, j; - GtkCellRenderer *rend; - GtkTreeViewColumn *col; - GArray *array; - GtkTreeStore *store; - GtkTreeSelection *selection; - - help_history = g_ptr_array_new(); - help_history_pos = -1; - - help_dialog_shell = gtk_dialog_new_with_buttons(_("Freeciv Help Browser"), - NULL, - 0, - GTK_STOCK_GO_BACK, - 1, - GTK_STOCK_GO_FORWARD, - 2, - GTK_STOCK_CLOSE, - GTK_RESPONSE_CLOSE, - NULL); - setup_dialog(help_dialog_shell, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(help_dialog_shell), - GTK_RESPONSE_CLOSE); - gtk_widget_set_name(help_dialog_shell, "Freeciv"); - - g_signal_connect(help_dialog_shell, "response", - G_CALLBACK(help_command_callback), NULL); - g_signal_connect(help_dialog_shell, "destroy", - G_CALLBACK(help_destroy_callback), NULL); - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 5); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(help_dialog_shell))), hbox); - gtk_widget_show(hbox); - - /* build tree store. */ - store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); - - array = g_array_new(FALSE, FALSE, sizeof(GtkTreeIter)); - help_items_iterate(pitem) { - GtkTreeIter *it, *parent; - const char *s; - int depth; - - for (s = pitem->topic; *s == ' '; s++) { - /* nothing */ - } - depth = s - pitem->topic; - - array = g_array_set_size(array, depth+1); - - if (depth > 0) { - parent = &g_array_index(array, GtkTreeIter, depth-1); - } else { - parent = NULL; - } - - it = &g_array_index(array, GtkTreeIter, depth); - gtk_tree_store_append(store, it, parent); - - gtk_tree_store_set(store, it, 0, pitem->topic, 1, pitem, -1); - } help_items_iterate_end; - - - /* create tree view. */ - help_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - g_object_unref(store); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(help_view)); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(help_view), FALSE); - - g_signal_connect(help_view, "cursor-changed", - G_CALLBACK(activated_topic), NULL); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(help_view)); - gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); - - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, rend, "text", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(help_view), col); - - help_view_sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(help_view_sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_widget_set_size_request(help_view_sw, 190, -1); - gtk_container_add(GTK_CONTAINER(help_view_sw), help_view); - gtk_widget_show(help_view); - gtk_container_add(GTK_CONTAINER(hbox), help_view_sw); - gtk_widget_show(help_view_sw); - - help_frame = gtk_frame_new(""); - gtk_container_add(GTK_CONTAINER(hbox), help_frame); - gtk_widget_set_size_request(help_frame, 520, 350); - gtk_widget_show(help_frame); - - help_box = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(help_box), 5); - gtk_orientable_set_orientation(GTK_ORIENTABLE(help_box), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(help_frame), help_box); - - help_tile = gtk_image_new(); - - gtk_container_add(GTK_CONTAINER(help_box), help_tile); - - help_itable = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_box), help_itable); - - for (i = 0; i < 6; i++) { - help_ilabel[i] = - gtk_label_new(help_ilabel_name[i] ? _(help_ilabel_name[i]) : ""); - gtk_widget_set_hexpand(help_ilabel[i], TRUE); - - if (i == 5) { - button = help_hyperlink_new(help_ilabel[i], HELP_TECH); - gtk_grid_attach(GTK_GRID(help_itable), button, i, 0, 1, 1); - } else { - gtk_grid_attach(GTK_GRID(help_itable), help_ilabel[i], i, 0, 1, 1); - gtk_widget_set_name(help_ilabel[i], "help_label"); - } - gtk_widget_show(help_ilabel[i]); - } - - help_wtable = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_box), help_wtable); - - for (i = 0; i < 6; i++) { - help_wlabel[i] = - gtk_label_new(help_wlabel_name[i] ? _(help_wlabel_name[i]) : ""); - gtk_widget_set_hexpand(help_wlabel[i], TRUE); - - if (i == 3 || i == 5) { - button = help_hyperlink_new(help_wlabel[i], HELP_TECH); - gtk_grid_attach(GTK_GRID(help_wtable), button, i, 0, 1, 1); - } else { - gtk_grid_attach(GTK_GRID(help_wtable), help_wlabel[i], i, 0, 1, 1); - gtk_widget_set_name(help_wlabel[i], "help_label"); - } - gtk_widget_show(help_wlabel[i]); - } - - - help_utable = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_box), help_utable); - - for (i = 0; i < 5; i++) { - for (j = 0; j < 5; j++) { - help_ulabel[j][i] = - gtk_label_new(help_ulabel_name[j][i] ? _(help_ulabel_name[j][i]) : ""); - gtk_widget_set_hexpand(help_ulabel[j][i], TRUE); - - if (j == 4 && (i == 1 || i == 4)) { - if (i == 1) { - button = help_hyperlink_new(help_ulabel[j][i], HELP_TECH); - } else { - button = help_hyperlink_new(help_ulabel[j][i], HELP_UNIT); - } - - gtk_grid_attach(GTK_GRID(help_utable), button, i, j, 1, 1); - } else { - gtk_grid_attach(GTK_GRID(help_utable), help_ulabel[j][i], - i, j, 1, 1); - gtk_widget_set_name(help_ulabel[j][i], "help_label"); - } - gtk_widget_show(help_ulabel[j][i]); - } - } - - help_ttable = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_box), help_ttable); - - for (j = 0; j < 2; j++) { - for (i = 0; i < 5; i++) { - help_tlabel[j][i] = - gtk_label_new(help_tlabel_name[j][i] ? _(help_tlabel_name[j][i]) : ""); - gtk_widget_set_hexpand(help_tlabel[j][i], TRUE); - gtk_widget_set_name(help_tlabel[j][i], "help_label"); - - /* Ugly (but these numbers are hardcoded in help_update_terrain() too) */ - if (j == 1 && i == 1) { - /* Extra wide cell for terrain specials */ - gtk_grid_attach(GTK_GRID(help_ttable), help_tlabel[j][i], - i, j, 4, 1); - gtk_widget_show(help_tlabel[j][i]); - break; /* skip rest of row */ - } else { - gtk_grid_attach(GTK_GRID(help_ttable), help_tlabel[j][i], - i, j, 1, 1); - gtk_widget_show(help_tlabel[j][i]); - } - } - } - - help_etable = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_box), help_etable); - - for (i = 0; i < 6; i++) { - help_elabel[i] = - gtk_label_new(help_elabel_name[i] ? _(help_elabel_name[i]) : ""); - gtk_widget_set_hexpand(help_elabel[i], TRUE); - gtk_grid_attach(GTK_GRID(help_etable), help_elabel[i], i % 4, i / 4, 1, 1); - gtk_widget_set_name(help_elabel[i], "help_label"); - gtk_widget_show(help_elabel[i]); - } - - help_vbox = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(help_vbox), 1); - gtk_orientable_set_orientation(GTK_ORIENTABLE(help_vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_set_border_width(GTK_CONTAINER(help_vbox), 5); - gtk_container_add(GTK_CONTAINER(help_box), help_vbox); - - text = gtk_text_view_new(); - gtk_widget_set_hexpand(text, TRUE); - gtk_widget_set_vexpand(text, TRUE); - gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE); - gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); - gtk_container_set_border_width(GTK_CONTAINER(text), 5); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); - gtk_widget_set_name(text, "help_text"); - help_text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); - gtk_widget_show(text); - - help_text_sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(help_text_sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(help_text_sw), text); - gtk_container_add(GTK_CONTAINER(help_box), help_text_sw); - - /* build tech store. */ - tstore = gtk_tree_store_new(4, - G_TYPE_STRING, /* tech name */ - G_TYPE_INT, /* turns to tech */ - G_TYPE_INT, /* tech id */ - GDK_TYPE_RGBA); /* color */ - help_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tstore)); - gtk_widget_set_hexpand(help_tree, TRUE); - gtk_widget_set_vexpand(help_tree, TRUE); - g_object_unref(tstore); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(help_tree), FALSE); - - g_signal_connect(help_tree, "row_activated", - G_CALLBACK(help_tech_tree_activated_callback), NULL); - - - col = gtk_tree_view_column_new(); - - rend = gtk_cell_renderer_text_new(); - g_object_set(rend, "weight", PANGO_WEIGHT_BOLD, NULL); - gtk_tree_view_column_pack_start(col, rend, TRUE); - gtk_tree_view_column_set_attributes(col, rend, - "text", 0, - "background-rgba", 3, - NULL); - rend = gtk_cell_renderer_text_new(); - g_object_set(rend, "weight", PANGO_WEIGHT_BOLD, "xalign", 1.0, NULL); - gtk_tree_view_column_pack_start(col, rend, FALSE); - gtk_tree_view_column_set_attributes(col, rend, - "text", 1, - "background-rgba", 3, - NULL); - - gtk_tree_view_append_column(GTK_TREE_VIEW(help_tree), col); - - - help_tree_sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(help_tree_sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(help_tree_sw), help_tree); - gtk_widget_show(help_tree); - gtk_container_add(GTK_CONTAINER(help_box), help_tree_sw); - - help_tree_expand = - gtk_button_new_with_label(_("Expand All")); - help_tree_collapse = gtk_button_new_with_label(_("Collapse All")); - - g_signal_connect(help_tree_expand, "clicked", - G_CALLBACK(help_tech_tree_expand_callback), help_tree); - g_signal_connect(help_tree_collapse, "clicked", - G_CALLBACK(help_tech_tree_collapse_callback), help_tree); - - help_tree_buttons_hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); - gtk_container_add(GTK_CONTAINER(help_tree_buttons_hbox), help_tree_expand); - gtk_container_add(GTK_CONTAINER(help_tree_buttons_hbox), help_tree_collapse); - gtk_container_add(GTK_CONTAINER(help_box), help_tree_buttons_hbox); - gtk_widget_show_all(help_tree_buttons_hbox); - - create_help_page(HELP_TEXT); -} - -/**********************************************************************//** - Create page for help type -**************************************************************************/ -static void create_help_page(enum help_page_type type) -{ -} - -/**********************************************************************//** - Set sprite to show for current help item. -**************************************************************************/ -static void set_help_tile_from_sprite(struct sprite *spr) -{ - if (spr == NULL) { - return; - } - - gtk_image_set_from_surface(GTK_IMAGE(help_tile), spr->surface); - gtk_widget_show(help_tile); -} - -/**********************************************************************//** - Set sprite to show for current terrain. -**************************************************************************/ -static void set_help_tile_from_terrain(struct terrain *pterr) -{ - struct canvas canvas = FC_STATIC_CANVAS_INIT; - cairo_t *cr; - int i; - - canvas.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - tileset_tile_width(tileset), - tileset_tile_height(tileset)); - - cr = cairo_create(canvas.surface); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_paint(cr); - cairo_destroy(cr); - - for (i = 0; i < 3; i++) { - struct drawn_sprite sprs[80]; - int count = fill_basic_terrain_layer_sprite_array(tileset, sprs, - i, pterr); - - put_drawn_sprites(&canvas, 1.0, 0, 0, count, sprs, FALSE); - } - - gtk_image_set_from_surface(GTK_IMAGE(help_tile), canvas.surface); - gtk_widget_show(help_tile); - cairo_surface_destroy(canvas.surface); -} - -/**********************************************************************//** - Display updated help about improvement -**************************************************************************/ -static void help_update_improvement(const struct help_item *pitem, - char *title) -{ - char buf[8192]; - struct impr_type *imp = improvement_by_translated_name(title); - - create_help_page(HELP_IMPROVEMENT); - - if (imp && !is_great_wonder(imp)) { - const char *req = skip_intl_qualifier_prefix(REQ_LABEL_NONE); - char req_buf[512]; - - sprintf(buf, "%d", impr_base_build_shield_cost(imp)); - gtk_label_set_text(GTK_LABEL(help_ilabel[1]), buf); - sprintf(buf, "%d", imp->upkeep); - gtk_label_set_text(GTK_LABEL(help_ilabel[3]), buf); - - /* FIXME: this should show ranges, negated reqs, and all the - * MAX_NUM_REQS reqs. - * Currently it's limited to 1 req but this code is partially prepared - * to be extended. Remember MAX_NUM_REQS is a compile-time - * definition. */ - requirement_vector_iterate(&imp->reqs, preq) { - if (!preq->present) { - continue; - } - req = universal_name_translation(&preq->source, req_buf, sizeof(req_buf)); - break; - } requirement_vector_iterate_end; - gtk_label_set_text(GTK_LABEL(help_ilabel[5]), req); -/* create_tech_tree(help_improvement_tree, 0, imp->tech_req, 3);*/ - } else { - gtk_label_set_text(GTK_LABEL(help_ilabel[1]), "0"); - gtk_label_set_text(GTK_LABEL(help_ilabel[3]), "0"); - gtk_label_set_text(GTK_LABEL(help_ilabel[5]), REQ_LABEL_NEVER); -/* create_tech_tree(help_improvement_tree, 0, advance_count(), 3);*/ - } - - set_help_tile_from_sprite(get_building_sprite(tileset, imp)); - - gtk_widget_show(help_itable); - - helptext_building(buf, sizeof(buf), client.conn.playing, pitem->text, imp); - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); -} - -/**********************************************************************//** - Display updated help about wonder -**************************************************************************/ -static void help_update_wonder(const struct help_item *pitem, - char *title) -{ - char buf[8192]; - struct impr_type *imp = improvement_by_translated_name(title); - - create_help_page(HELP_WONDER); - - if (imp && is_great_wonder(imp)) { - int i; - char req_buf[512]; - - sprintf(buf, "%d", impr_base_build_shield_cost(imp)); - gtk_label_set_text(GTK_LABEL(help_wlabel[1]), buf); - - /* FIXME: this should show ranges, negated reqs, and all the - * MAX_NUM_REQS reqs. - * Currently it's limited to 1 req but this code is partially prepared - * to be extended. Remember MAX_NUM_REQS is a compile-time - * definition. */ - i = 0; - requirement_vector_iterate(&imp->reqs, preq) { - if (!preq->present) { - continue; - } - gtk_label_set_text(GTK_LABEL(help_wlabel[3 + i]), - universal_name_translation(&preq->source, - req_buf, sizeof(req_buf))); - i++; - break; - } requirement_vector_iterate_end; - gtk_label_set_text(GTK_LABEL(help_wlabel[5]), REQ_LABEL_NEVER); - requirement_vector_iterate(&imp->obsolete_by, pobs) { - if (pobs->source.kind == VUT_ADVANCE && pobs->present) { - gtk_label_set_text(GTK_LABEL(help_wlabel[5]), - advance_name_translation - (pobs->source.value.advance)); - break; - } - } requirement_vector_iterate_end; -/* create_tech_tree(help_improvement_tree, 0, imp->tech_req, 3);*/ - } else { - /* can't find wonder */ - gtk_label_set_text(GTK_LABEL(help_wlabel[1]), "0"); - gtk_label_set_text(GTK_LABEL(help_wlabel[3]), REQ_LABEL_NEVER); - gtk_label_set_text(GTK_LABEL(help_wlabel[5]), skip_intl_qualifier_prefix(REQ_LABEL_NONE)); -/* create_tech_tree(help_improvement_tree, 0, advance_count(), 3); */ - } - - set_help_tile_from_sprite(get_building_sprite(tileset, imp)); - - gtk_widget_show(help_wtable); - - helptext_building(buf, sizeof(buf), client.conn.playing, pitem->text, imp); - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); -} - -/**********************************************************************//** - Display updated help about unit type -**************************************************************************/ -static void help_update_unit_type(const struct help_item *pitem, - char *title) -{ - char buf[8192]; - struct unit_type *utype = unit_type_by_translated_name(title); - - create_help_page(HELP_UNIT); - - if (utype) { - sprintf(buf, "%d", utype_build_shield_cost_base(utype)); - gtk_label_set_text(GTK_LABEL(help_ulabel[0][1]), buf); - sprintf(buf, "%d", utype->attack_strength); - gtk_label_set_text(GTK_LABEL(help_ulabel[0][4]), buf); - sprintf(buf, "%d", utype->defense_strength); - gtk_label_set_text(GTK_LABEL(help_ulabel[1][1]), buf); - sprintf(buf, "%s", move_points_text(utype->move_rate, TRUE)); - gtk_label_set_text(GTK_LABEL(help_ulabel[1][4]), buf); - sprintf(buf, "%d", utype->firepower); - gtk_label_set_text(GTK_LABEL(help_ulabel[2][1]), buf); - sprintf(buf, "%d", utype->hp); - gtk_label_set_text(GTK_LABEL(help_ulabel[2][4]), buf); - gtk_label_set_text(GTK_LABEL(help_ulabel[3][1]), - helptext_unit_upkeep_str(utype)); - sprintf(buf, "%d", (int)sqrt((double)utype->vision_radius_sq)); - gtk_label_set_text(GTK_LABEL(help_ulabel[3][4]), buf); - if (A_NEVER == utype->require_advance) { - gtk_label_set_text(GTK_LABEL(help_ulabel[4][1]), REQ_LABEL_NEVER); - } else { - gtk_label_set_text(GTK_LABEL(help_ulabel[4][1]), - advance_name_translation(utype->require_advance)); - } -/* create_tech_tree(help_improvement_tree, 0, advance_number(utype->require_advance), 3);*/ - if (U_NOT_OBSOLETED == utype->obsoleted_by) { - gtk_label_set_text(GTK_LABEL(help_ulabel[4][4]), skip_intl_qualifier_prefix(REQ_LABEL_NONE)); - } else { - gtk_label_set_text(GTK_LABEL(help_ulabel[4][4]), - utype_name_translation(utype->obsoleted_by)); - } - - helptext_unit(buf, sizeof(buf), client.conn.playing, pitem->text, utype); - - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); - - set_help_tile_from_sprite(get_unittype_sprite(tileset, utype, - direction8_invalid())); - } else { - gtk_label_set_text(GTK_LABEL(help_ulabel[0][1]), "0"); - gtk_label_set_text(GTK_LABEL(help_ulabel[0][4]), "0"); - gtk_label_set_text(GTK_LABEL(help_ulabel[1][1]), "0"); - gtk_label_set_text(GTK_LABEL(help_ulabel[1][4]), "0"); - gtk_label_set_text(GTK_LABEL(help_ulabel[2][1]), "0"); - gtk_label_set_text(GTK_LABEL(help_ulabel[2][4]), "0"); - gtk_label_set_text(GTK_LABEL(help_ulabel[3][1]), "0"); - gtk_label_set_text(GTK_LABEL(help_ulabel[3][4]), "0"); - - gtk_label_set_text(GTK_LABEL(help_ulabel[4][1]), REQ_LABEL_NEVER); -/* create_tech_tree(help_improvement_tree, 0, A_LAST, 3);*/ - gtk_label_set_text(GTK_LABEL(help_ulabel[4][4]), skip_intl_qualifier_prefix(REQ_LABEL_NONE)); - - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); - } - gtk_widget_show(help_utable); -} - -/**********************************************************************//** - Cut str to at max len bytes in a utf8 friendly way -**************************************************************************/ -static char *fc_chomp(char *str, size_t len) -{ - gchar *i; - - if (!str || !*str) - return str; - - i = str + len; - for (i = g_utf8_find_prev_char(str, i); - (i && g_unichar_isspace(g_utf8_get_char(i))); - i = g_utf8_find_prev_char(str, i)) { - *i = '\0'; - } - return str; -} - -/**********************************************************************//** - Display updated help about tech -**************************************************************************/ -static void help_update_tech(const struct help_item *pitem, char *title) -{ - int i, j; - GtkWidget *w, *hbox; - char buf[8192]; - struct advance *padvance = advance_by_translated_name(title); - - create_help_page(HELP_TECH); - - if (padvance && !is_future_tech(i = advance_number(padvance))) { - GtkTextBuffer *txt; - size_t len; - - gtk_container_foreach(GTK_CONTAINER(help_vbox), (GtkCallback)gtk_widget_destroy, NULL); - - for (j = 0; j < ARRAY_SIZE(help_advances); j++) { - help_advances[j] = FALSE; - } - gtk_tree_store_clear(tstore); - create_tech_tree(i, TECH_TREE_DEPTH, NULL); - gtk_widget_show(help_tree_sw); - gtk_widget_show(help_tree_buttons_hbox); - - helptext_advance(buf, sizeof(buf), client.conn.playing, pitem->text, i); - len = strlen(buf); - fc_chomp(buf, len); - - set_help_tile_from_sprite(get_tech_sprite(tileset, i)); - - w = gtk_text_view_new(); - gtk_widget_set_hexpand(w, TRUE); - gtk_widget_set_vexpand(w, TRUE); - gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(w), FALSE); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(w), GTK_WRAP_WORD); - gtk_widget_set_name(w, "help_text"); - gtk_container_set_border_width(GTK_CONTAINER(w), 5); - gtk_text_view_set_editable(GTK_TEXT_VIEW(w), FALSE); - gtk_container_add(GTK_CONTAINER(help_vbox), w); - gtk_widget_show(w); - - txt = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w)); - if (txt) { - gtk_text_buffer_set_text(txt, buf, -1); - } - - w = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); - g_object_set(w, "margin", 5, NULL); - gtk_widget_set_hexpand(w, TRUE); - gtk_widget_set_vexpand(w, TRUE); - gtk_container_add(GTK_CONTAINER(help_vbox), w); - gtk_widget_show(w); - - governments_iterate(pgov) { - /* FIXME: need a more general mechanism for this, since this - * helptext needs to be shown in all possible req source types. */ - requirement_vector_iterate(&pgov->reqs, preq) { - if (VUT_ADVANCE == preq->source.kind - && preq->source.value.advance == padvance) { - hbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_vbox), hbox); - w = gtk_label_new(_("Allows")); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(government_name_translation(pgov), - HELP_GOVERNMENT); - gtk_container_add(GTK_CONTAINER(hbox), w); - gtk_widget_show_all(hbox); - } - } requirement_vector_iterate_end; - } governments_iterate_end; - - improvement_iterate(pimprove) { - requirement_vector_iterate(&pimprove->reqs, preq) { - if (VUT_ADVANCE == preq->source.kind - && preq->source.value.advance == padvance) { - hbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_vbox), hbox); - w = gtk_label_new(_("Allows")); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(improvement_name_translation(pimprove), - is_great_wonder(pimprove) - ? HELP_WONDER - : HELP_IMPROVEMENT); - gtk_container_add(GTK_CONTAINER(hbox), w); - gtk_widget_show_all(hbox); - } - } requirement_vector_iterate_end; - requirement_vector_iterate(&pimprove->obsolete_by, pobs) { - if (pobs->source.kind == VUT_ADVANCE - && pobs->source.value.advance == padvance - && pobs->present) { - hbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_vbox), hbox); - w = gtk_label_new(_("Obsoletes")); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(improvement_name_translation(pimprove), - is_great_wonder(pimprove) - ? HELP_WONDER - : HELP_IMPROVEMENT); - gtk_container_add(GTK_CONTAINER(hbox), w); - gtk_widget_show_all(hbox); - } - } requirement_vector_iterate_end; - } improvement_iterate_end; - - unit_type_iterate(punittype) { - if (padvance != punittype->require_advance) { - continue; - } - hbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_vbox), hbox); - w = gtk_label_new(_("Allows")); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(utype_name_translation(punittype), HELP_UNIT); - gtk_container_add(GTK_CONTAINER(hbox), w); - gtk_widget_show_all(hbox); - } unit_type_iterate_end; - - advance_iterate(A_NONE, ptest) { - if (padvance == advance_requires(ptest, AR_ONE)) { - if (advance_by_number(A_NONE) == advance_requires(ptest, AR_TWO)) { - hbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_vbox), hbox); - w = gtk_label_new(_("Allows")); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(advance_name_translation(ptest), HELP_TECH); - gtk_container_add(GTK_CONTAINER(hbox), w); - gtk_widget_show_all(hbox); - } else { - hbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_vbox), hbox); - w = gtk_label_new(_("Allows")); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(advance_name_translation(ptest), HELP_TECH); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = gtk_label_new(_("with")); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(advance_name_translation(advance_requires(ptest, AR_TWO)), - HELP_TECH); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = gtk_label_new(Q_("?techhelp:")); - gtk_container_add(GTK_CONTAINER(hbox), w); - gtk_widget_show_all(hbox); - } - } - if (padvance == advance_requires(ptest, AR_TWO)) { - hbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_vbox), hbox); - w = gtk_label_new(_("Allows")); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(advance_name_translation(ptest), HELP_TECH); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = gtk_label_new(_("with")); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(advance_name_translation(advance_requires(ptest, AR_ONE)), - HELP_TECH); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = gtk_label_new(Q_("?techhelp:")); - gtk_container_add(GTK_CONTAINER(hbox), w); - gtk_widget_show_all(hbox); - } - } advance_iterate_end; - gtk_widget_show(help_vbox); - } -} - -/**********************************************************************//** - Add a line for an activity linking to help for result -**************************************************************************/ -static void add_act_help_for_terrain(const char *act_label, - const char *result_link_label, - enum help_page_type result_link_type, - const char *descr_label) -{ - GtkWidget *w; - GtkWidget *hbox; - - hbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(help_vbox), hbox); - w = gtk_label_new(act_label); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = help_slink_new(result_link_label, result_link_type); - gtk_container_add(GTK_CONTAINER(hbox), w); - w = gtk_label_new(descr_label); - gtk_container_add(GTK_CONTAINER(hbox), w); - - gtk_widget_show_all(hbox); -} - -/**********************************************************************//** - Create widgets about all extras of one cause activity to the terrain. -**************************************************************************/ -static void help_extras_of_act_for_terrain(struct terrain *pterr, - enum unit_activity act, - char *label) -{ - enum extra_cause cause = activity_to_extra_cause(act); - - extra_type_by_cause_iterate(cause, pextra) { - if (pextra->buildable - && requirement_fulfilled_by_terrain(pterr, &(pextra->reqs))) { - add_act_help_for_terrain(label, - extra_name_translation(pextra), HELP_EXTRA, - helptext_extra_for_terrain_str(pextra, pterr, - act)); - } - } extra_type_by_cause_iterate_end; -} - -/**********************************************************************//** - Display updated help about terrain -**************************************************************************/ -static void help_update_terrain(const struct help_item *pitem, - char *title) -{ - char buf[8192]; - struct terrain *pterrain = terrain_by_translated_name(title); - - create_help_page(HELP_TERRAIN); - - if (pterrain) { - struct universal for_terr = { .kind = VUT_TERRAIN, .value = { .terrain = pterrain }}; - - set_help_tile_from_terrain(pterrain); - - { - /* 25 => "1.25"; 50 => "1.5"; 100 => "2.0" */ - int defbonus = pterrain->defense_bonus + 100; - int frac = defbonus % 100; - - if ((frac % 10) == 0) { - frac /= 10; - } - sprintf(buf, "%d/%d.%d", - pterrain->movement_cost, defbonus / 100, frac); - } - gtk_label_set_text(GTK_LABEL(help_tlabel[0][1]), buf); - - sprintf(buf, "%d/%d/%d", - pterrain->output[O_FOOD], - pterrain->output[O_SHIELD], - pterrain->output[O_TRADE]); - gtk_label_set_text(GTK_LABEL(help_tlabel[0][4]), buf); - - buf[0] = '\0'; - if (*(pterrain->resources)) { - struct extra_type **r; - - for (r = pterrain->resources; *r; r++) { - /* TRANS: " Whales (2/1/2)," */ - sprintf (buf + strlen (buf), " %s (%d/%d/%d),", - extra_name_translation(*r), - pterrain->output[O_FOOD] + (*r)->data.resource->output[O_FOOD], - pterrain->output[O_SHIELD] + (*r)->data.resource->output[O_SHIELD], - pterrain->output[O_TRADE] + (*r)->data.resource->output[O_TRADE]); - } - buf[strlen (buf) - 1] = '.'; - } else { - /* TRANS: "Resources: (none)" */ - sprintf (buf + strlen (buf), _("(none)")); - } - gtk_label_set_text(GTK_LABEL(help_tlabel[1][1]), buf); - - gtk_container_foreach(GTK_CONTAINER(help_vbox), (GtkCallback)gtk_widget_destroy, NULL); - - if (pterrain->cultivate_result != T_NONE - && pterrain->cultivate_time != 0 - && action_id_univs_not_blocking(ACTION_CULTIVATE, - NULL, &for_terr)) { - fc_snprintf(buf, sizeof(buf), - PL_("%d turn", "%d turns", pterrain->cultivate_time), - pterrain->cultivate_time); - add_act_help_for_terrain(_("Cultivate. Rslt/Time"), - terrain_name_translation(pterrain->cultivate_result), - HELP_TERRAIN, buf); - } - - if (pterrain->plant_result != T_NONE - && pterrain->plant_time != 0 - && action_id_univs_not_blocking(ACTION_PLANT, NULL, &for_terr)) { - fc_snprintf(buf, sizeof(buf), - PL_("%d turn", "%d turns", pterrain->plant_time), - pterrain->plant_time); - add_act_help_for_terrain(_("Plant Rslt/Time"), - terrain_name_translation(pterrain->plant_result), - HELP_TERRAIN, buf); - } - - if (pterrain->transform_result != T_NONE - && pterrain->transform_time != 0 - && action_id_univs_not_blocking(ACTION_TRANSFORM_TERRAIN, - NULL, &for_terr)) { - fc_snprintf(buf, sizeof(buf), - PL_("%d turn", "%d turns", pterrain->transform_time), - pterrain->transform_time); - add_act_help_for_terrain(_("Trans. Rslt/Time"), - terrain_name_translation(pterrain->transform_result), - HELP_TERRAIN, buf); - } - - if (pterrain->irrigation_time != 0 - && action_id_univs_not_blocking(ACTION_IRRIGATE, NULL, &for_terr)) { - help_extras_of_act_for_terrain(pterrain, ACTIVITY_IRRIGATE, _("Build as irrigation")); - } - if (pterrain->mining_time != 0 - && action_id_univs_not_blocking(ACTION_MINE, NULL, &for_terr)) { - help_extras_of_act_for_terrain(pterrain, ACTIVITY_MINE, _("Build as mine")); - } - if (pterrain->road_time != 0) { - help_extras_of_act_for_terrain(pterrain, ACTIVITY_GEN_ROAD, _("Build as road")); - } - if (pterrain->base_time != 0) { - help_extras_of_act_for_terrain(pterrain, ACTIVITY_BASE, _("Build as base")); - } - gtk_widget_show(help_vbox); - } - - helptext_terrain(buf, sizeof(buf), client.conn.playing, pitem->text, pterrain); - - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); - - gtk_widget_show(help_ttable); -} - -/**********************************************************************//** - Help page for extras. -**************************************************************************/ -static void help_update_extra(const struct help_item *pitem, char *title) -{ - char buf[8192]; - struct extra_type *pextra = extra_type_by_translated_name(title); - - create_help_page(HELP_EXTRA); - - buf[0] = '\0'; - if (pextra == NULL) { - strcat(buf, pitem->text); - } else { - struct road_type *proad = extra_road_get(pextra); - bool is_resource = is_extra_caused_by(pextra, EC_RESOURCE); - - /* Cost to build */ - if (pextra->buildable) { - if (pextra->build_time != 0) { - /* TRANS: "MP" = movement points */ - sprintf(buf, _("%d MP"), pextra->build_time); - } else { - /* TRANS: Build time depends on terrain. */ - sprintf(buf, _("Terrain specific")); - } - } else { - sprintf(buf, "-"); - } - gtk_label_set_text(GTK_LABEL(help_elabel[1]), buf); - /* Conflicting extras */ - buf[0] = '\0'; - if (is_resource) { - /* TRANS: (Resource extra) Conflicts with: */ - strcat(buf, _("Other Resources")); - } - extra_type_iterate(pextra2) { - if (!can_extras_coexist(pextra, pextra2) - && (!is_resource || !is_extra_caused_by(pextra2, EC_RESOURCE))) { - if (buf[0] != '\0') { - strcat(buf, "/"); - } - strcat(buf, extra_name_translation(pextra2)); - } - } extra_type_iterate_end; - /* TRANS: "Conflicts with: (none)" (extras) */ - gtk_label_set_text(GTK_LABEL(help_elabel[3]), buf[0] ? buf : _("(none)")); - - /* Bonus */ - if (proad != NULL) { - const char *bonus = NULL; - - output_type_iterate(o) { - if (proad->tile_incr[o] > 0) { - /* TRANS: Road bonus depends on terrain. */ - bonus = _("Terrain specific"); - break; - } - } output_type_iterate_end; - if (!bonus) { - bonus = helptext_road_bonus_str(NULL, proad); - } - if (!bonus) { - /* TRANS: No output bonus from a road */ - bonus = Q_("?bonus:None"); - } - gtk_label_set_text(GTK_LABEL(help_elabel[5]), bonus); - } else { - gtk_label_set_text(GTK_LABEL(help_elabel[5]), Q_("?bonus:None")); - } - - helptext_extra(buf, sizeof(buf), client.conn.playing, pitem->text, pextra); - } - gtk_widget_show(help_etable); - - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); -} - -/**********************************************************************//** - This is currently just a text page, with special text: -**************************************************************************/ -static void help_update_goods(const struct help_item *pitem, - char *title) -{ - char buf[8192]; - struct goods_type *pgood = goods_by_translated_name(title); - - create_help_page(HELP_GOODS); - - if (!pgood) { - strcat(buf, pitem->text); - } else { - helptext_goods(buf, sizeof(buf), client.conn.playing, pitem->text, - pgood); - } - create_help_page(HELP_GOODS); - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); -} - -/**********************************************************************//** - This is currently just a text page, with special text: -**************************************************************************/ -static void help_update_specialist(const struct help_item *pitem, - char *title) -{ - char buf[8192]; - struct specialist *pspec = specialist_by_translated_name(title); - - if (!pspec) { - strcat(buf, pitem->text); - } else { - helptext_specialist(buf, sizeof(buf), client.conn.playing, pitem->text, - pspec); - } - create_help_page(HELP_TEXT); - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); -} - -/**********************************************************************//** - This is currently just a text page, with special text: -**************************************************************************/ -static void help_update_government(const struct help_item *pitem, - char *title) -{ - char buf[8192]; - struct government *gov = government_by_translated_name(title); - - if (!gov) { - strcat(buf, pitem->text); - } else { - helptext_government(buf, sizeof(buf), client.conn.playing, pitem->text, gov); - } - create_help_page(HELP_TEXT); - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); -} - -/**********************************************************************//** - This is currently just a text page, with special text -**************************************************************************/ -static void help_update_nation(const struct help_item *pitem, char *title, - struct nation_type *pnation) -{ - char buf[4096]; - - if (!pnation) { - strcat(buf, pitem->text); - } else { - helptext_nation(buf, sizeof(buf), pnation, pitem->text); - } - create_help_page(HELP_TEXT); - gtk_text_buffer_set_text(help_text, buf, -1); - gtk_widget_show(help_text_sw); -} - -/**********************************************************************//** - Display updated help dialog -**************************************************************************/ -static void help_update_dialog(const struct help_item *pitem) -{ - char *top; - - /* figure out what kind of item is required for pitem ingo */ - - for (top = pitem->topic; *top == ' '; top++) { - /* nothing */ - } - - help_box_hide(); - gtk_text_buffer_set_text(help_text, "", -1); - - switch (pitem->type) { - case HELP_IMPROVEMENT: - help_update_improvement(pitem, top); - break; - case HELP_WONDER: - help_update_wonder(pitem, top); - break; - case HELP_UNIT: - help_update_unit_type(pitem, top); - break; - case HELP_TECH: - help_update_tech(pitem, top); - break; - case HELP_TERRAIN: - help_update_terrain(pitem, top); - break; - case HELP_EXTRA: - help_update_extra(pitem, top); - break; - case HELP_GOODS: - help_update_goods(pitem, top); - break; - case HELP_SPECIALIST: - help_update_specialist(pitem, top); - break; - case HELP_GOVERNMENT: - help_update_government(pitem, top); - break; - case HELP_NATIONS: - help_update_nation(pitem, top, nation_by_translated_plural(top)); - break; - case HELP_TEXT: - default: - /* it was a pure text item */ - create_help_page(HELP_TEXT); - - gtk_text_buffer_set_text(help_text, pitem->text, -1); - gtk_widget_show(help_text_sw); - break; - } - set_title_topic(pitem->topic); - - gtk_widget_show(help_box); -} - -/**********************************************************************//** - Add item at path to selection and scroll to its cell -**************************************************************************/ -static void help_item_zoom(GtkTreePath *path) -{ - GtkTreeModel *model; - GtkTreeIter it, child, item; - GtkTreeSelection *selection; - - model = gtk_tree_view_get_model(GTK_TREE_VIEW(help_view)); - gtk_tree_model_get_iter(model, &item, path); - - for (child=item; gtk_tree_model_iter_parent(model, &it, &child); child=it) { - GtkTreePath *it_path; - - it_path = gtk_tree_model_get_path(model, &it); - gtk_tree_view_expand_row(GTK_TREE_VIEW(help_view), it_path, TRUE); - gtk_tree_path_free(it_path); - } - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(help_view)); - gtk_tree_selection_select_iter(selection, &item); - gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(help_view), path, NULL, - TRUE, 0.0, 0.0); -} - -/**********************************************************************//** - Return path to help item. -**************************************************************************/ -static GtkTreePath *help_item_path(const struct help_item *pitem) -{ - GtkTreePath *path; - bool next; - - path = gtk_tree_path_new_first(); - next = FALSE; - help_items_iterate(pitem2) { - const char *s; - int depth; - - for (s = pitem2->topic; *s == ' '; s++) { - /* nothing */ - } - depth = s - pitem2->topic + 1; - - while (depth < gtk_tree_path_get_depth(path)) { - gtk_tree_path_up(path); - gtk_tree_path_next(path); - next = FALSE; - } - while (depth > gtk_tree_path_get_depth(path)) { - gtk_tree_path_down(path); - next = FALSE; - } - - if (next) { - gtk_tree_path_next(path); - } - - if (pitem == pitem2) - break; - - next = TRUE; - } help_items_iterate_end; - - return path; -} - -/**********************************************************************//** - Add item to selection -**************************************************************************/ -static void select_help_item_string(const char *item, enum help_page_type htype) -{ - const struct help_item *pitem; - int idx; - GtkTreePath *path; - GtkTreeViewColumn *col; - - if (!(pitem = get_help_item_spec(item, htype, &idx))) { - return; - } - - path = help_item_path(pitem); - help_item_zoom(path); - - col = gtk_tree_view_get_column(GTK_TREE_VIEW(help_view), 0); - gtk_tree_view_set_cursor(GTK_TREE_VIEW(help_view), path, col, FALSE); - gtk_tree_path_free(path); -} - -/**********************************************************************//** - Set sensitivity of help dialog response buttons. -**************************************************************************/ -static void help_command_update(void) -{ - GtkDialog *dialog = GTK_DIALOG(help_dialog_shell); - - if (help_history_pos < 0) { - gtk_dialog_set_response_sensitive(dialog, 1, FALSE); - gtk_dialog_set_response_sensitive(dialog, 2, FALSE); - } else { - gtk_dialog_set_response_sensitive(dialog, 1, TRUE); - gtk_dialog_set_response_sensitive(dialog, 2, TRUE); - - if (help_history_pos == 0) { - gtk_dialog_set_response_sensitive(dialog, 1, FALSE); - } - if (help_history_pos >= help_history->len - 1) { - gtk_dialog_set_response_sensitive(dialog, 2, FALSE); - } - } -} - -/**********************************************************************//** - User gave response to help dialog -**************************************************************************/ -static void help_command_callback(GtkWidget *w, gint response_id) -{ - GtkTreePath *path; - const struct help_item *pitem; - - if (response_id == 1) { - if (help_history_pos > 0) { - help_history_pos--; - - pitem = g_ptr_array_index(help_history, help_history_pos); - path = help_item_path(pitem); - help_item_zoom(path); - help_update_dialog(pitem); - help_command_update(); - } - } else if (response_id == 2) { - if (help_history_pos < help_history->len - 1) { - help_history_pos++; - - pitem = g_ptr_array_index(help_history, help_history_pos); - path = help_item_path(pitem); - help_item_zoom(path); - help_update_dialog(pitem); - help_command_update(); - } - } else { - gtk_widget_destroy(help_dialog_shell); - } -} diff --git a/client/gui-gtk-3.0/helpdlg.h b/client/gui-gtk-3.0/helpdlg.h deleted file mode 100644 index 672aa164ab..0000000000 --- a/client/gui-gtk-3.0/helpdlg.h +++ /dev/null @@ -1,20 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__HELPDLG_H -#define FC__HELPDLG_H - -#include "helpdlg_g.h" - -/* nothing else */ - -#endif /* FC__HELPDLG_H */ diff --git a/client/gui-gtk-3.0/infradlg.c b/client/gui-gtk-3.0/infradlg.c deleted file mode 100644 index 1f0ec80992..0000000000 --- a/client/gui-gtk-3.0/infradlg.c +++ /dev/null @@ -1,26 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -/* client */ -#include "dialogs_g.h" - -/************************************************************************//** - Refresh infra dialog -****************************************************************************/ -void update_infra_dialog(void) -{ -} diff --git a/client/gui-gtk-3.0/inputdlg.c b/client/gui-gtk-3.0/inputdlg.c deleted file mode 100644 index 491336b713..0000000000 --- a/client/gui-gtk-3.0/inputdlg.c +++ /dev/null @@ -1,104 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "mem.h" - -/* client/gui-gtk-3.0 */ -#include "gui_main.h" -#include "gui_stuff.h" - -#include "inputdlg.h" - -struct input_dialog_data { - input_dialog_callback_t response_callback; - gpointer response_cli_data; -}; - -/**********************************************************************//** - Called when user dismisses dialog -- either to accept or to cancel. -**************************************************************************/ -static void input_dialog_response(GtkDialog *shell, gint response, - gpointer data) -{ - GtkWidget *winput = g_object_get_data(G_OBJECT(shell), "iinput"); - struct input_dialog_data *cb = data; - - cb->response_callback(cb->response_cli_data, - response, gtk_entry_get_text(GTK_ENTRY(winput))); - - /* Any response is final */ - gtk_widget_destroy(GTK_WIDGET(shell)); - FC_FREE(cb); -} - -/**********************************************************************//** - Called when user closes dialog with key (Esc). -**************************************************************************/ -static void input_dialog_close(GtkDialog *shell, gpointer data) -{ - input_dialog_response(shell, GTK_RESPONSE_CANCEL, data); -} - -/**********************************************************************//** - Create a popup with a text entry box and "OK" and "Cancel" buttons. -**************************************************************************/ -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) -{ - GtkWidget *shell, *label, *input; - struct input_dialog_data *cb = fc_malloc(sizeof(struct input_dialog_data)); - - cb->response_callback = response_callback; - cb->response_cli_data = response_cli_data; - - shell = gtk_dialog_new_with_buttons(dialogname, - parent, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_OK, - NULL); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_OK); - setup_dialog(shell, GTK_WIDGET(parent)); - g_signal_connect(shell, "response", G_CALLBACK(input_dialog_response), cb); - g_signal_connect(shell, "close", G_CALLBACK(input_dialog_close), cb); - gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT); - - label = gtk_frame_new(text); - /* Should use gtk_dialog_get_content_area() instead of ->vbox, but that - * requires at least gtk+-2.14.0 */ - gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(shell))), label, TRUE, TRUE, 0); - - input = gtk_entry_new(); - gtk_container_add(GTK_CONTAINER(label), input); - gtk_entry_set_text(GTK_ENTRY(input), postinputtest); - gtk_entry_set_activates_default(GTK_ENTRY(input), TRUE); - g_object_set_data(G_OBJECT(shell), "iinput", input); - - gtk_widget_show_all(GTK_WIDGET(shell)); - gtk_window_present(GTK_WINDOW(shell)); - - return shell; -} diff --git a/client/gui-gtk-3.0/inputdlg.h b/client/gui-gtk-3.0/inputdlg.h deleted file mode 100644 index a10151f47f..0000000000 --- a/client/gui-gtk-3.0/inputdlg.h +++ /dev/null @@ -1,26 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__INPUTDLG_H -#define FC__INPUTDLG_H - -#include - -typedef void (*input_dialog_callback_t)(gpointer response_cli_data, - gint response, const char *input); - -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); - -#endif /* FC__INPUTDLG_H */ diff --git a/client/gui-gtk-3.0/inteldlg.c b/client/gui-gtk-3.0/inteldlg.c deleted file mode 100644 index 538e6b8516..0000000000 --- a/client/gui-gtk-3.0/inteldlg.c +++ /dev/null @@ -1,470 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "government.h" -#include "packets.h" -#include "player.h" -#include "research.h" - -/* client */ -#include "client_main.h" -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapview.h" - -#include "inteldlg.h" - -/******************************************************************/ -static const char *table_text[] = { - N_("Ruler:"), - N_("Government:"), - N_("Capital:"), - N_("Gold:"), - NULL, - N_("Tax:"), - N_("Science:"), - N_("Luxury:"), - NULL, - N_("Researching:"), - N_("Culture:") -}; - -enum table_label { - LABEL_RULER, - LABEL_GOVERNMENT, - LABEL_CAPITAL, - LABEL_GOLD, - LABEL_SEP1, - LABEL_TAX, - LABEL_SCIENCE, - LABEL_LUXURY, - LABEL_SEP2, - LABEL_RESEARCHING, - LABEL_CULTURE, - LABEL_LAST -}; - -/******************************************************************/ -struct intel_dialog { - struct player *pplayer; - GtkWidget *shell; - - GtkTreeStore *diplstates; - GtkListStore *techs; - GtkWidget *table_labels[LABEL_LAST]; -}; - -#define SPECLIST_TAG dialog -#define SPECLIST_TYPE struct intel_dialog -#include "speclist.h" - -#define dialog_list_iterate(dialoglist, pdialog) \ - TYPED_LIST_ITERATE(struct intel_dialog, dialoglist, pdialog) -#define dialog_list_iterate_end LIST_ITERATE_END - -static struct dialog_list *dialog_list; -static struct intel_dialog *create_intel_dialog(struct player *p); - -/**********************************************************************//** - Initialize intelligence dialogs -**************************************************************************/ -void intel_dialog_init(void) -{ - dialog_list = dialog_list_new(); -} - -/**********************************************************************//** - Free resources allocated for intelligence dialogs -**************************************************************************/ -void intel_dialog_done(void) -{ - dialog_list_destroy(dialog_list); -} - -/**********************************************************************//** - Get intelligence dialog between client user and other player - passed as parameter. -**************************************************************************/ -static struct intel_dialog *get_intel_dialog(struct player *pplayer) -{ - dialog_list_iterate(dialog_list, pdialog) { - if (pdialog->pplayer == pplayer) { - return pdialog; - } - } dialog_list_iterate_end; - - return NULL; -} - -/**********************************************************************//** - Open intelligence dialog -**************************************************************************/ -void popup_intel_dialog(struct player *p) -{ - struct intel_dialog *pdialog; - - if (!(pdialog = get_intel_dialog(p))) { - pdialog = create_intel_dialog(p); - } - - update_intel_dialog(p); - - gtk_window_present(GTK_WINDOW(pdialog->shell)); -} - -/**********************************************************************//** - Intelligence dialog destruction requested -**************************************************************************/ -static void intel_destroy_callback(GtkWidget *w, gpointer data) -{ - struct intel_dialog *pdialog = (struct intel_dialog *)data; - - dialog_list_remove(dialog_list, pdialog); - - free(pdialog); -} - -/**********************************************************************//** - Close an intelligence dialog for the given player. -**************************************************************************/ -void close_intel_dialog(struct player *p) -{ - struct intel_dialog *pdialog = get_intel_dialog(p); - intel_destroy_callback(NULL, pdialog); -} - -/**********************************************************************//** - Create new intelligence dialog between client user and player - given as parameter. -**************************************************************************/ -static struct intel_dialog *create_intel_dialog(struct player *p) -{ - struct intel_dialog *pdialog; - - GtkWidget *shell, *notebook, *label, *sw, *view, *table; - GtkCellRenderer *rend; - GtkTreeViewColumn *col; - - int i; - - pdialog = fc_malloc(sizeof(*pdialog)); - pdialog->pplayer = p; - - shell = gtk_dialog_new_with_buttons(NULL, - NULL, - 0, - GTK_STOCK_CLOSE, - GTK_RESPONSE_CLOSE, - NULL); - pdialog->shell = shell; - gtk_window_set_default_size(GTK_WINDOW(shell), 350, 350); - setup_dialog(shell, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CLOSE); - - g_signal_connect(shell, "destroy", - G_CALLBACK(intel_destroy_callback), pdialog); - g_signal_connect(shell, "response", - G_CALLBACK(gtk_widget_destroy), NULL); - - notebook = gtk_notebook_new(); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))), notebook); - - /* overview tab. */ - table = gtk_grid_new(); - g_object_set(table, "margin", 6, NULL); - - gtk_grid_set_row_spacing(GTK_GRID(table), 2); - gtk_grid_set_column_spacing(GTK_GRID(table), 12); - - /* TRANS: Overview tab of foreign intelligence report dialog */ - label = gtk_label_new_with_mnemonic(_("_Overview")); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table, label); - - for (i = 0; i < ARRAY_SIZE(table_text); i++) { - if (table_text[i]) { - label = gtk_label_new(_(table_text[i])); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_grid_attach(GTK_GRID(table), label, 0, i, 1, 1); - - label = gtk_label_new(NULL); - pdialog->table_labels[i] = label; - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_grid_attach(GTK_GRID(table), label, 1, i, 1, 1); - } else { - pdialog->table_labels[i] = NULL; - } - } - - /* diplomacy tab. */ - pdialog->diplstates = gtk_tree_store_new(1, G_TYPE_STRING); - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pdialog->diplstates)); - g_object_set(view, "margin", 6, NULL); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(pdialog->diplstates); - gtk_container_set_border_width(GTK_CONTAINER(view), 6); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, rend, - "text", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - - gtk_tree_view_expand_all(GTK_TREE_VIEW(view)); - - sw = gtk_scrolled_window_new(NULL,NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_container_add(GTK_CONTAINER(sw), view); - - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - - label = gtk_label_new_with_mnemonic(_("_Diplomacy")); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, label); - - /* techs tab. */ - pdialog->techs = gtk_list_store_new(2, G_TYPE_BOOLEAN, G_TYPE_STRING); - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(pdialog->techs), - 1, GTK_SORT_ASCENDING); - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pdialog->techs)); - g_object_set(view, "margin", 6, NULL); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(pdialog->techs); - gtk_container_set_border_width(GTK_CONTAINER(view), 6); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - - rend = gtk_cell_renderer_toggle_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, rend, - "active", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, rend, - "text", 1, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - - sw = gtk_scrolled_window_new(NULL,NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_container_add(GTK_CONTAINER(sw), view); - - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - - label = gtk_label_new_with_mnemonic(_("_Techs")); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, label); - - gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(shell))); - - dialog_list_prepend(dialog_list, pdialog); - - return pdialog; -} - -/**********************************************************************//** - Update the intelligence dialog for the given player. This is called by - the core client code when that player's information changes. -**************************************************************************/ -void update_intel_dialog(struct player *p) -{ - struct intel_dialog *pdialog = get_intel_dialog(p); - - if (pdialog) { - const struct research *mresearch, *presearch; - GtkTreeIter diplstates[DS_LAST]; - int i; - bool global_observer = client_is_global_observer(); - struct player *me = client_player(); - bool embassy_knowledge = global_observer || team_has_embassy(me->team, p); - bool trade_knowledge = global_observer || p == me || could_intel_with_player(me, p); - - /* window title. */ - gchar *title = g_strdup_printf(_("Foreign Intelligence: %s Empire"), - nation_adjective_for_player(p)); - gtk_window_set_title(GTK_WINDOW(pdialog->shell), title); - g_free(title); - - /* diplomacy tab. */ - gtk_tree_store_clear(pdialog->diplstates); - - for (i = 0; i < ARRAY_SIZE(diplstates); i++) { - GtkTreeIter it; - GValue v = { 0, }; - - gtk_tree_store_append(pdialog->diplstates, &it, NULL); - g_value_init(&v, G_TYPE_STRING); - g_value_set_static_string(&v, diplstate_type_translated_name(i)); - gtk_tree_store_set_value(pdialog->diplstates, &it, 0, &v); - g_value_unset(&v); - diplstates[i] = it; - } - - players_iterate(other) { - const struct player_diplstate *state; - GtkTreeIter it; - GValue v = { 0, }; - - if (other == p || !other->is_alive) { - continue; - } - state = player_diplstate_get(p, other); - gtk_tree_store_append(pdialog->diplstates, &it, - &diplstates[state->type]); - g_value_init(&v, G_TYPE_STRING); - g_value_set_static_string(&v, player_name(other)); - gtk_tree_store_set_value(pdialog->diplstates, &it, 0, &v); - g_value_unset(&v); - } players_iterate_end; - - /* techs tab. */ - gtk_list_store_clear(pdialog->techs); - - mresearch = research_get(client_player()); - presearch = research_get(p); - advance_index_iterate(A_FIRST, advi) { - if (research_invention_state(presearch, advi) == TECH_KNOWN) { - GtkTreeIter it; - - gtk_list_store_append(pdialog->techs, &it); - - gtk_list_store_set(pdialog->techs, &it, - 0, research_invention_state(mresearch, advi) - != TECH_KNOWN, - 1, research_advance_name_translation(presearch, - advi), - -1); - } - } advance_index_iterate_end; - - /* table labels. */ - for (i = 0; i < ARRAY_SIZE(pdialog->table_labels); i++) { - if (pdialog->table_labels[i]) { - struct city *pcity; - gchar *buf = NULL; - char tbuf[256]; - - switch (i) { - case LABEL_RULER: - ruler_title_for_player(p, tbuf, sizeof(tbuf)); - buf = g_strdup(tbuf); - break; - case LABEL_GOVERNMENT: - if (trade_knowledge) { - buf = g_strdup(government_name_for_player(p)); - } else { - buf = g_strdup(_("(Unknown)")); - } - break; - case LABEL_CAPITAL: - pcity = player_primary_capital(p); - /* TRANS: "unknown" location */ - buf = g_strdup((!pcity) ? _("(Unknown)") : city_name_get(pcity)); - break; - case LABEL_GOLD: - if (trade_knowledge) { - buf = g_strdup_printf("%d", p->economic.gold); - } else { - buf = g_strdup(_("(Unknown)")); - } - break; - case LABEL_TAX: - if (embassy_knowledge) { - buf = g_strdup_printf("%d%%", p->economic.tax); - } else { - buf = g_strdup(_("(Unknown)")); - } - break; - case LABEL_SCIENCE: - if (embassy_knowledge) { - buf = g_strdup_printf("%d%%", p->economic.science); - } else { - buf = g_strdup(_("(Unknown)")); - } - break; - case LABEL_LUXURY: - if (embassy_knowledge) { - buf = g_strdup_printf("%d%%", p->economic.luxury); - } else { - buf = g_strdup(_("(Unknown)")); - } - break; - case LABEL_RESEARCHING: - { - struct research *research = research_get(p); - - switch (research->researching) { - case A_UNKNOWN: - /* TRANS: "Unknown" advance/technology */ - buf = g_strdup(_("(Unknown)")); - break; - case A_UNSET: - if (embassy_knowledge) { - /* TRANS: missing value */ - buf = g_strdup(_("(none)")); - } else { - buf = g_strdup(_("(Unknown)")); - } - break; - default: - buf = g_strdup_printf("%s(%d/%d)", - research_advance_name_translation - (research, research->researching), - research->bulbs_researched, - research->client.researching_cost); - break; - } - break; - } - case LABEL_CULTURE: - if (embassy_knowledge) { - buf = g_strdup_printf("%d", p->client.culture); - } else { - buf = g_strdup(_("(Unknown)")); - } - break; - } - - if (buf) { - gtk_label_set_text(GTK_LABEL(pdialog->table_labels[i]), buf); - g_free(buf); - } - } - } - } -} diff --git a/client/gui-gtk-3.0/inteldlg.h b/client/gui-gtk-3.0/inteldlg.h deleted file mode 100644 index 50321d368b..0000000000 --- a/client/gui-gtk-3.0/inteldlg.h +++ /dev/null @@ -1,21 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 2003 - The Freeciv Project - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__INTELDLG_H -#define FC__INTELDLG_H - -#include "inteldlg_g.h" - -void intel_dialog_init(void); -void intel_dialog_done(void); - -#endif /* FC__INTELDLG_H */ diff --git a/client/gui-gtk-3.0/luaconsole.c b/client/gui-gtk-3.0/luaconsole.c deleted file mode 100644 index d1a225bac3..0000000000 --- a/client/gui-gtk-3.0/luaconsole.c +++ /dev/null @@ -1,495 +0,0 @@ -/***************************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -*****************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "shared.h" - -/* common */ -#include "featured_text.h" -#include "game.h" - -/* client */ -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "gui_main.h" -#include "gui_stuff.h" - -/* client/luascript */ -#include "script_client.h" - -#include "luaconsole.h" - -enum luaconsole_res { - LUACONSOLE_RES_OPEN -}; - -#define MAX_LUACONSOLE_HISTORY 20 - -struct luaconsole_data { - struct gui_dialog *shell; - GtkTextBuffer *message_buffer; - GtkTextView *message_area; - GtkWidget *entry; - - struct genlist *history_list; - int history_pos; -}; - -static struct luaconsole_data *luaconsole = NULL; - - -static struct luaconsole_data *luaconsole_dialog_get(void); -static void luaconsole_dialog_create(struct luaconsole_data *pdialog); -static void luaconsole_dialog_refresh(struct luaconsole_data *pdialog); -static void luaconsole_dialog_destroy(struct luaconsole_data *pdialog); - -static void luaconsole_dialog_area_size_allocate(GtkWidget *widget, - GtkAllocation *allocation, - gpointer data); -static void luaconsole_dialog_scroll_to_bottom(void); - -static void luaconsole_input_return(GtkEntry *w, gpointer data); -static gboolean luaconsole_input_handler(GtkWidget *w, GdkEventKey *ev); - -static void luaconsole_response_callback(struct gui_dialog *pgui_dialog, - int response, gpointer data); -static void luaconsole_load_file_popup(void); -static void luaconsole_load_file_callback(GtkWidget *widget, gint response, - gpointer data); - -/*************************************************************************//** - Create a lua console. -*****************************************************************************/ -void luaconsole_dialog_init(void) -{ - fc_assert_ret(luaconsole == NULL); - - /* Create a container for the dialog. */ - luaconsole = fc_calloc(1, sizeof(*luaconsole)); - luaconsole->message_buffer = gtk_text_buffer_new(NULL); - luaconsole->shell = NULL; - - luaconsole->history_list = genlist_new(); - luaconsole->history_pos = -1; - - luaconsole_welcome_message(); -} - -/*************************************************************************//** - Free a script lua console. -*****************************************************************************/ -void luaconsole_dialog_done(void) -{ - if (luaconsole != NULL) { - if (luaconsole->history_list) { - genlist_destroy(luaconsole->history_list); - } - - luaconsole_dialog_popdown(); - free(luaconsole); - - luaconsole = NULL; - } -} - -/*************************************************************************//** - Get the data for the lua console. -*****************************************************************************/ -static struct luaconsole_data *luaconsole_dialog_get(void) -{ - return luaconsole; -} - -/*************************************************************************//** - Popup the lua console inside the main-window, and optionally raise it. -*****************************************************************************/ -void luaconsole_dialog_popup(bool raise) -{ - struct luaconsole_data *pdialog = luaconsole_dialog_get(); - - fc_assert_ret(pdialog); - if (pdialog->shell == NULL) { - luaconsole_dialog_create(pdialog); - } - - gui_dialog_present(pdialog->shell); - if (raise) { - gui_dialog_raise(pdialog->shell); - } -} - -/*************************************************************************//** - Closes the lua console; the content is saved till the client is done. -*****************************************************************************/ -void luaconsole_dialog_popdown(void) -{ - struct luaconsole_data *pdialog = luaconsole_dialog_get(); - - fc_assert_ret(pdialog); - if (pdialog->shell == NULL) { - luaconsole_dialog_destroy(pdialog); - } -} - -/*************************************************************************//** - Return TRUE iff the lua console is open. -*****************************************************************************/ -bool luaconsole_dialog_is_open(void) -{ - struct luaconsole_data *pdialog = luaconsole_dialog_get(); - - fc_assert_ret_val(pdialog, FALSE); - - return (NULL != pdialog->shell); -} - -/*************************************************************************//** - Update the lua console. -*****************************************************************************/ -void real_luaconsole_dialog_update(void) -{ - struct luaconsole_data *pdialog = luaconsole_dialog_get(); - - fc_assert_ret(pdialog); - - if (NULL != pdialog->shell) { - luaconsole_dialog_refresh(pdialog); - } -} - -/*************************************************************************//** - Initialize a lua console. -*****************************************************************************/ -static void luaconsole_dialog_create(struct luaconsole_data *pdialog) -{ - GtkWidget *entry, *box, *vbox, *sw, *text, *notebook; - - fc_assert_ret(NULL != pdialog); - - if (GUI_GTK_OPTION(message_chat_location) == GUI_GTK_MSGCHAT_SPLIT) { - notebook = right_notebook; - } else { - notebook = bottom_notebook; - } - - gui_dialog_new(&pdialog->shell, GTK_NOTEBOOK(notebook), pdialog, TRUE); - gui_dialog_set_title(pdialog->shell, _("Client Lua Console")); - - box = pdialog->shell->vbox; - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(box), vbox); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(vbox), sw); - - text = gtk_text_view_new_with_buffer(pdialog->message_buffer); - gtk_widget_set_hexpand(text, TRUE); - gtk_widget_set_vexpand(text, TRUE); - set_message_buffer_view_link_handlers(text); - gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); - gtk_container_add(GTK_CONTAINER(sw), text); - g_signal_connect(text, "size-allocate", - G_CALLBACK(luaconsole_dialog_area_size_allocate), NULL); - - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); - gtk_widget_realize(text); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 5); - - pdialog->message_area = GTK_TEXT_VIEW(text); - g_signal_connect(text, "destroy", G_CALLBACK(gtk_widget_destroyed), - &pdialog->message_area); - - /* The lua console input line. */ - entry = gtk_entry_new(); - g_object_set(entry, "margin", 2, NULL); - gtk_container_add(GTK_CONTAINER(vbox), entry); - g_signal_connect(entry, "activate", G_CALLBACK(luaconsole_input_return), - NULL); - g_signal_connect(entry, "key_press_event", - G_CALLBACK(luaconsole_input_handler), NULL); - pdialog->entry = entry; - g_signal_connect(entry, "destroy", G_CALLBACK(gtk_widget_destroyed), - &pdialog->entry); - - /* Load lua script command button. */ - gui_dialog_add_button(pdialog->shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - - gui_dialog_add_stockbutton(pdialog->shell, GTK_STOCK_OPEN, - _("Load Lua Script"), LUACONSOLE_RES_OPEN); - gui_dialog_response_set_callback(pdialog->shell, - luaconsole_response_callback); - - luaconsole_dialog_refresh(pdialog); - gui_dialog_show_all(pdialog->shell); -} - -/*************************************************************************//** - Called when the return key is pressed. -*****************************************************************************/ -static void luaconsole_input_return(GtkEntry *w, gpointer data) -{ - const char *theinput; - struct luaconsole_data *pdialog = luaconsole_dialog_get(); - - fc_assert_ret(pdialog); - fc_assert_ret(pdialog->history_list); - - theinput = gtk_entry_get_text(w); - - if (*theinput) { - luaconsole_printf(ftc_luaconsole_input, "(input)> %s", theinput); - script_client_do_string(theinput); - - if (genlist_size(pdialog->history_list) >= MAX_LUACONSOLE_HISTORY) { - void *history_data; - - history_data = genlist_get(pdialog->history_list, -1); - genlist_remove(pdialog->history_list, history_data); - free(history_data); - } - - genlist_prepend(pdialog->history_list, fc_strdup(theinput)); - pdialog->history_pos = -1; - } - - gtk_entry_set_text(w, ""); -} - -/*************************************************************************//** - Dialog response callback. -*****************************************************************************/ -static void luaconsole_response_callback(struct gui_dialog *pgui_dialog, - int response, gpointer data) -{ - switch (response) { - case LUACONSOLE_RES_OPEN: - break; - default: - gui_dialog_destroy(pgui_dialog); - return; - } - - switch (response) { - case LUACONSOLE_RES_OPEN: - luaconsole_load_file_popup(); - break; - } -} - -/*************************************************************************//** - Create a file selector for loading a lua file.. -*****************************************************************************/ -static void luaconsole_load_file_popup(void) -{ - GtkWidget *filesel; - - /* Create the selector */ - filesel = gtk_file_chooser_dialog_new("Load Lua file", GTK_WINDOW(toplevel), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_OK, - NULL); - setup_dialog(filesel, toplevel); - gtk_window_set_position(GTK_WINDOW(filesel), GTK_WIN_POS_MOUSE); - - g_signal_connect(filesel, "response", - G_CALLBACK(luaconsole_load_file_callback), NULL); - - /* Display that dialog */ - gtk_window_present(GTK_WINDOW(filesel)); -} - -/*************************************************************************//** - Callback for luaconsole_load_file_popup(). -*****************************************************************************/ -static void luaconsole_load_file_callback(GtkWidget *widget, gint response, - gpointer data) -{ - if (response == GTK_RESPONSE_OK) { - gchar *filename = g_filename_to_utf8(gtk_file_chooser_get_filename - (GTK_FILE_CHOOSER(widget)), - -1, NULL, NULL, NULL); - - if (NULL != filename) { - luaconsole_printf(ftc_luaconsole_input, "(file)> %s", filename); - script_client_do_file(filename); - g_free(filename); - } - } - gtk_widget_destroy(widget); -} - -/*************************************************************************//** - Called when a key is pressed. -*****************************************************************************/ -static gboolean luaconsole_input_handler(GtkWidget *w, GdkEventKey *ev) -{ - struct luaconsole_data *pdialog = luaconsole_dialog_get(); - - fc_assert_ret_val(pdialog, FALSE); - fc_assert_ret_val(pdialog->history_list, FALSE); - - switch (ev->keyval) { - case GDK_KEY_Up: - if (pdialog->history_pos < genlist_size(pdialog->history_list) - 1) { - gtk_entry_set_text(GTK_ENTRY(w), genlist_get(pdialog->history_list, - ++pdialog->history_pos)); - gtk_editable_set_position(GTK_EDITABLE(w), -1); - } - return TRUE; - - case GDK_KEY_Down: - if (pdialog->history_pos >= 0) { - pdialog->history_pos--; - } - - if (pdialog->history_pos >= 0) { - gtk_entry_set_text(GTK_ENTRY(w), genlist_get(pdialog->history_list, - pdialog->history_pos)); - } else { - gtk_entry_set_text(GTK_ENTRY(w), ""); - } - gtk_editable_set_position(GTK_EDITABLE(w), -1); - return TRUE; - - default: - break; - } - - return FALSE; -} - -/*************************************************************************//** - When the luaconsole text view is resized, scroll it to the bottom. This - prevents users from accidentally missing messages when the chatline - gets scrolled up a small amount and stops scrolling down automatically. -*****************************************************************************/ -static void luaconsole_dialog_area_size_allocate(GtkWidget *widget, - GtkAllocation *allocation, - gpointer data) -{ - static int old_width = 0, old_height = 0; - if (allocation->width != old_width - || allocation->height != old_height) { - luaconsole_dialog_scroll_to_bottom(); - old_width = allocation->width; - old_height = allocation->height; - } -} - -/*************************************************************************//** - Scrolls the luaconsole all the way to the bottom. - If delayed is TRUE, it will be done in a idle_callback. - - Modified copy of chatline_scroll_to_bottom(). -*****************************************************************************/ -static void luaconsole_dialog_scroll_to_bottom(void) -{ - struct luaconsole_data *pdialog = luaconsole_dialog_get(); - - fc_assert_ret(pdialog); - - if (pdialog->shell) { - GtkTextIter end; - - fc_assert_ret(NULL != pdialog->message_buffer); - fc_assert_ret(NULL != pdialog->message_area); - - gtk_text_buffer_get_end_iter(pdialog->message_buffer, &end); - gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(pdialog->message_area), - &end, 0.0, TRUE, 1.0, 0.0); - } -} - -/*************************************************************************//** - Refresh a lua console. -*****************************************************************************/ -static void luaconsole_dialog_refresh(struct luaconsole_data *pdialog) -{ - fc_assert_ret(NULL != pdialog); -} - -/*************************************************************************//** - Closes a lua console. -*****************************************************************************/ -static void luaconsole_dialog_destroy(struct luaconsole_data *pdialog) -{ - fc_assert_ret(NULL != pdialog); - - if (pdialog->shell) { - gui_dialog_destroy(pdialog->shell); - fc_assert(NULL == pdialog->shell); - } - fc_assert(NULL == pdialog->message_area); - fc_assert(NULL == pdialog->entry); -} - -/*************************************************************************//** - Appends the string to the chat output window. The string should be - inserted on its own line, although it will have no newline. -*****************************************************************************/ -void real_luaconsole_append(const char *astring, - const struct text_tag_list *tags) -{ - GtkTextBuffer *buf; - GtkTextIter iter; - GtkTextMark *mark; - ft_offset_t text_start_offset; - struct luaconsole_data *pdialog = luaconsole_dialog_get(); - - fc_assert_ret(pdialog); - - buf = pdialog->message_buffer; - gtk_text_buffer_get_end_iter(buf, &iter); - gtk_text_buffer_insert(buf, &iter, "\n", -1); - mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE); - - if (GUI_GTK_OPTION(show_chat_message_time)) { - char timebuf[64]; - time_t now; - struct tm *now_tm; - - now = time(NULL); - now_tm = localtime(&now); - strftime(timebuf, sizeof(timebuf), "[%H:%M:%S] ", now_tm); - gtk_text_buffer_insert(buf, &iter, timebuf, -1); - } - - text_start_offset = gtk_text_iter_get_offset(&iter); - gtk_text_buffer_insert(buf, &iter, astring, -1); - text_tag_list_iterate(tags, ptag) { - apply_text_tag(ptag, buf, text_start_offset, astring); - } text_tag_list_iterate_end; - - if (pdialog->message_area) { - scroll_if_necessary(GTK_TEXT_VIEW(pdialog->message_area), mark); - } - gtk_text_buffer_delete_mark(buf, mark); -} diff --git a/client/gui-gtk-3.0/luaconsole.h b/client/gui-gtk-3.0/luaconsole.h deleted file mode 100644 index 7da96d1e8b..0000000000 --- a/client/gui-gtk-3.0/luaconsole.h +++ /dev/null @@ -1,23 +0,0 @@ -/***************************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -*****************************************************************************/ -#ifndef FC__LUACONSOLE_H -#define FC__LUACONSOLE_H - -#include "luaconsole_g.h" - -void luaconsole_dialog_init(void); -void luaconsole_dialog_done(void); - -void luaconsole_dialog_popdown(void); - -#endif /* FC__LUACONSOLE_H */ diff --git a/client/gui-gtk-3.0/mapctrl.c b/client/gui-gtk-3.0/mapctrl.c deleted file mode 100644 index 7349a33654..0000000000 --- a/client/gui-gtk-3.0/mapctrl.c +++ /dev/null @@ -1,490 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" -#include "support.h" - -/* common */ -#include "combat.h" -#include "game.h" -#include "map.h" -#include "player.h" -#include "unit.h" - -#include "overview_common.h" - -/* client */ -#include "client_main.h" -#include "climap.h" -#include "climisc.h" -#include "control.h" -#include "editor.h" -#include "tilespec.h" -#include "text.h" - -/* client/agents */ -#include "cma_core.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "citydlg.h" -#include "colors.h" -#include "dialogs.h" -#include "editgui.h" -#include "graphics.h" -#include "gui_main.h" -#include "inputdlg.h" -#include "mapview.h" -#include "menu.h" - -#include "mapctrl.h" - -struct tmousepos { int x, y; }; -extern gint cur_x, cur_y; - -/**********************************************************************//** - Button released when showing info label -**************************************************************************/ -static gboolean popit_button_release(GtkWidget *w, GdkEventButton *event) -{ - gtk_grab_remove(w); - gdk_device_ungrab(event->device, event->time); - gtk_widget_destroy(w); - return FALSE; -} - -/**********************************************************************//** - Put the popup on a smart position, after the real size of the widget is - known: left of the cursor if within the right half of the map, and vice - versa; displace the popup so as not to obscure it by the mouse cursor; - stay always within the map if possible. -**************************************************************************/ -static void popupinfo_positioning_callback(GtkWidget *w, GtkAllocation *alloc, - gpointer data) -{ - struct tmousepos *mousepos = data; - float x, y; - struct tile *ptile; - - ptile = canvas_pos_to_tile(mousepos->x, mousepos->y); - if (tile_to_canvas_pos(&x, &y, ptile)) { - gint minx, miny, maxy; - - gdk_window_get_origin(gtk_widget_get_window(map_canvas), &minx, &miny); - maxy = miny + gtk_widget_get_allocated_height(map_canvas); - - if (x > mapview.width/2) { - /* right part of the map */ - x += minx; - y += miny + (tileset_tile_height(tileset) - alloc->height)/2; - - y = CLIP(miny, y, maxy - alloc->height); - - gtk_window_move(GTK_WINDOW(w), x - alloc->width, y); - } else { - /* left part of the map */ - x += minx + tileset_tile_width(tileset); - y += miny + (tileset_tile_height(tileset) - alloc->height)/2; - - y = CLIP(miny, y, maxy - alloc->height); - - gtk_window_move(GTK_WINDOW(w), x, y); - } - } -} - -/**********************************************************************//** - Popup a label with information about the tile, unit, city, when the user - used the middle mouse button on the map. -**************************************************************************/ -static void popit(GdkEventButton *event, struct tile *ptile) -{ - GtkWidget *p; - static struct tmousepos mousepos; - struct unit *punit; - - if (TILE_UNKNOWN != client_tile_get_known(ptile)) { - p = gtk_window_new(GTK_WINDOW_POPUP); - gtk_container_set_border_width(GTK_CONTAINER(p), 4); - gtk_window_set_transient_for(GTK_WINDOW(p), GTK_WINDOW(toplevel)); - gtk_container_add(GTK_CONTAINER(p), gtk_label_new(popup_info_text(ptile))); - - punit = find_visible_unit(ptile); - - if (punit) { - mapdeco_set_gotoroute(punit); - if (punit->goto_tile) { - mapdeco_set_crosshair(punit->goto_tile, TRUE); - } - } - mapdeco_set_crosshair(ptile, TRUE); - - g_signal_connect(p, "destroy", - G_CALLBACK(popupinfo_popdown_callback), NULL); - - mousepos.x = event->x; - mousepos.y = event->y; - - g_signal_connect(p, "size-allocate", - G_CALLBACK(popupinfo_positioning_callback), - &mousepos); - - gtk_widget_show_all(p); - gdk_device_grab(event->device, gtk_widget_get_window(p), - GDK_OWNERSHIP_NONE, TRUE, GDK_BUTTON_RELEASE_MASK, NULL, - event->time); - gtk_grab_add(p); - - g_signal_connect_after(p, "button_release_event", - G_CALLBACK(popit_button_release), NULL); - } -} - -/**********************************************************************//** - Information label destruction requested -**************************************************************************/ -void popupinfo_popdown_callback(GtkWidget *w, gpointer data) -{ - mapdeco_clear_crosshairs(); - mapdeco_clear_gotoroutes(); -} - -/**********************************************************************//** - Callback from city name dialog for new city. -**************************************************************************/ -static void name_new_city_popup_callback(gpointer data, gint response, - const char *input) -{ - int idx = GPOINTER_TO_INT(data); - - switch (response) { - case GTK_RESPONSE_OK: - finish_city(index_to_tile(&(wld.map), idx), input); - break; - case GTK_RESPONSE_CANCEL: - case GTK_RESPONSE_DELETE_EVENT: - cancel_city(index_to_tile(&(wld.map), idx)); - break; - } -} - -/**********************************************************************//** - Popup dialog where the user choose the name of the new city - punit = (settler) unit which builds the city - suggestname = suggetion of the new city's name -**************************************************************************/ -void popup_newcity_dialog(struct unit *punit, const char *suggestname) -{ - input_dialog_create(GTK_WINDOW(toplevel), /*"shellnewcityname" */ - _("Build New City"), - _("What should we call our new city?"), suggestname, - name_new_city_popup_callback, - GINT_TO_POINTER(tile_index(unit_tile(punit)))); -} - -/**********************************************************************//** - Enable or disable the turn done button. - Should probably some where else. -**************************************************************************/ -void set_turn_done_button_state(bool state) -{ - gtk_widget_set_sensitive(turn_done_button, state); -} - -/**********************************************************************//** - Handle 'Mouse button released'. Because of the quickselect feature, - the release of both left and right mousebutton can launch the goto. -**************************************************************************/ -gboolean butt_release_mapcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data) -{ - if (editor_is_active()) { - return handle_edit_mouse_button_release(ev); - } - - if (ev->button == 1 || ev->button == 3) { - release_goto_button(ev->x, ev->y); - } - if (ev->button == 3 && (rbutton_down || hover_state != HOVER_NONE)) { - release_right_button(ev->x, ev->y, - (ev->state & GDK_SHIFT_MASK) != 0); - } - - return TRUE; -} - -/**********************************************************************//** - Handle all mouse button press on canvas. - Future feature: User-configurable mouse clicks. -**************************************************************************/ -gboolean butt_down_mapcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data) -{ - struct city *pcity = NULL; - struct tile *ptile = NULL; - - if (editor_is_active()) { - return handle_edit_mouse_button_press(ev); - } - - if (!can_client_change_view()) { - return TRUE; - } - - gtk_widget_grab_focus(map_canvas); - ptile = canvas_pos_to_tile(ev->x, ev->y); - pcity = ptile ? tile_city(ptile) : NULL; - - switch (ev->button) { - - case 1: /* LEFT mouse button */ - - /* + + LMB : Adjust workers. */ - if ((ev->state & GDK_SHIFT_MASK) && (ev->state & GDK_CONTROL_MASK)) { - adjust_workers_button_pressed(ev->x, ev->y); - } else if (ev->state & GDK_CONTROL_MASK) { - /* + LMB : Quickselect a sea unit. */ - action_button_pressed(ev->x, ev->y, SELECT_SEA); - } else if (ptile && (ev->state & GDK_SHIFT_MASK)) { - /* + LMB: Append focus unit. */ - action_button_pressed(ev->x, ev->y, SELECT_APPEND); - } else if (ptile && (ev->state & GDK_MOD1_MASK)) { - /* + LMB: popit (same as middle-click) */ - /* FIXME: we need a general mechanism for letting freeciv work with - * 1- or 2-button mice. */ - popit(ev, ptile); - } else if (tiles_hilited_cities) { - /* LMB in Area Selection mode. */ - if (ptile) { - toggle_tile_hilite(ptile); - } - } else { - /* Plain LMB click. */ - action_button_pressed(ev->x, ev->y, SELECT_POPUP); - } - break; - - case 2: /* MIDDLE mouse button */ - - /* + MMB: Wake up sentries. */ - if (ev->state & GDK_CONTROL_MASK) { - wakeup_button_pressed(ev->x, ev->y); - } else if (ptile) { - /* Plain Middle click. */ - popit(ev, ptile); - } - break; - - case 3: /* RIGHT mouse button */ - - /* + + RMB : insert city or tile chat link. */ - /* + + + RMB : insert unit chat link. */ - if (ptile && (ev->state & GDK_MOD1_MASK) - && (ev->state & GDK_CONTROL_MASK)) { - inputline_make_chat_link(ptile, (ev->state & GDK_SHIFT_MASK) != 0); - } else if ((ev->state & GDK_SHIFT_MASK) && (ev->state & GDK_MOD1_MASK)) { - /* + + RMB : Show/hide workers. */ - key_city_overlay(ev->x, ev->y); - } else if ((ev->state & GDK_SHIFT_MASK) && (ev->state & GDK_CONTROL_MASK) - && pcity != NULL) { - /* + RMB: Paste Production. */ - clipboard_paste_production(pcity); - cancel_tile_hiliting(); - } else if (ev->state & GDK_SHIFT_MASK - && clipboard_copy_production(ptile)) { - /* + RMB on city/unit: Copy Production. */ - /* If nothing to copy, fall through to rectangle selection. */ - - /* Already done the copy */ - } else if (ev->state & GDK_CONTROL_MASK) { - /* + RMB : Quickselect a land unit. */ - action_button_pressed(ev->x, ev->y, SELECT_LAND); - } else { - /* Plain RMB click. Area selection. */ - - /* A foolproof user will depress button on canvas, - * release it on another widget, and return to canvas - * to find rectangle still active. - */ - if (rectangle_active) { - release_right_button(ev->x, ev->y, - (ev->state & GDK_SHIFT_MASK) != 0); - return TRUE; - } - if (hover_state == HOVER_NONE) { - anchor_selection_rectangle(ev->x, ev->y); - rbutton_down = TRUE; /* causes rectangle updates */ - } - } - break; - - default: - break; - } - - return TRUE; -} - -/**********************************************************************//** - Update goto line so that destination is at current mouse pointer location. -**************************************************************************/ -void create_line_at_mouse_pos(void) -{ - int x, y; - GdkWindow *window; - GdkDeviceManager *manager = - gdk_display_get_device_manager(gtk_widget_get_display(toplevel)); - GdkDevice *pointer = gdk_device_manager_get_client_pointer(manager); - - if (!pointer) { - return; - } - - window = gdk_device_get_window_at_position(pointer, &x, &y); - if (window) { - if (window == gtk_widget_get_window(map_canvas)) { - update_line(x, y); - } else if (window == gtk_widget_get_window(overview_canvas)) { - overview_update_line(x, y); - } - } -} - -/**********************************************************************//** - The Area Selection rectangle. Called by center_tile_mapcanvas() and - when the mouse pointer moves. -**************************************************************************/ -void update_rect_at_mouse_pos(void) -{ - int x, y; - GdkWindow *window; - GdkDevice *pointer; - GdkModifierType mask; - GdkDeviceManager *manager = - gdk_display_get_device_manager(gtk_widget_get_display(toplevel)); - - pointer = gdk_device_manager_get_client_pointer(manager); - if (!pointer) { - return; - } - - window = gdk_device_get_window_at_position(pointer, &x, &y); - if (window && window == gtk_widget_get_window(map_canvas)) { - gdk_device_get_state(pointer, window, NULL, &mask); - if (mask & GDK_BUTTON3_MASK) { - update_selection_rectangle(x, y); - } - } -} - -/**********************************************************************//** - Triggered by the mouse moving on the mapcanvas, this function will - update the mouse cursor and goto lines. -**************************************************************************/ -gboolean move_mapcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data) -{ - if (GUI_GTK_OPTION(mouse_over_map_focus) - && !gtk_widget_has_focus(map_canvas)) { - gtk_widget_grab_focus(map_canvas); - } - - if (editor_is_active()) { - return handle_edit_mouse_move(ev); - } - - cur_x = ev->x; - cur_y = ev->y; - update_line(ev->x, ev->y); - if (rbutton_down && (ev->state & GDK_BUTTON3_MASK)) { - update_selection_rectangle(ev->x, ev->y); - } - - if (keyboardless_goto_button_down && hover_state == HOVER_NONE) { - maybe_activate_keyboardless_goto(ev->x, ev->y); - } - control_mouse_cursor(canvas_pos_to_tile(ev->x, ev->y)); - - return TRUE; -} - -/**********************************************************************//** - This function will reset the mouse cursor if it leaves the map. -**************************************************************************/ -gboolean leave_mapcanvas(GtkWidget *widget, GdkEventCrossing *event) -{ - if (gtk_notebook_get_current_page(GTK_NOTEBOOK(top_notebook)) - != gtk_notebook_page_num(GTK_NOTEBOOK(top_notebook), map_widget)) { - /* Map is not currently topmost tab. Do not use tile specific cursors. */ - update_mouse_cursor(CURSOR_DEFAULT); - return TRUE; - } - - /* Bizarrely, this function can be called even when we don't "leave" - * the map canvas, for instance, it gets called any time the mouse is - * clicked. */ - if (!map_is_empty() - && event->x >= 0 && event->y >= 0 - && event->x < mapview.width && event->y < mapview.height) { - control_mouse_cursor(canvas_pos_to_tile(event->x, event->y)); - } else { - update_mouse_cursor(CURSOR_DEFAULT); - } - - update_unit_info_label(get_units_in_focus()); - return TRUE; -} - -/**********************************************************************//** - Overview canvas moved -**************************************************************************/ -gboolean move_overviewcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data) -{ - overview_update_line(ev->x, ev->y); - return TRUE; -} - -/**********************************************************************//** - Button pressed at overview -**************************************************************************/ -gboolean butt_down_overviewcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data) -{ - int xtile, ytile; - - if (ev->type != GDK_BUTTON_PRESS) { - return TRUE; /* Double-clicks? Triple-clicks? No thanks! */ - } - - overview_to_map_pos(&xtile, &ytile, ev->x, ev->y); - - if (can_client_change_view() && (ev->button == 3)) { - center_tile_mapcanvas(map_pos_to_tile(&(wld.map), xtile, ytile)); - } else if (can_client_issue_orders() && (ev->button == 1)) { - do_map_click(map_pos_to_tile(&(wld.map), xtile, ytile), - (ev->state & GDK_SHIFT_MASK) ? SELECT_APPEND : SELECT_POPUP); - } - - return TRUE; -} - -/**********************************************************************//** - Best effort to center the map on the currently selected unit(s) -**************************************************************************/ -void center_on_unit(void) -{ - request_center_focus_unit(); -} diff --git a/client/gui-gtk-3.0/mapctrl.h b/client/gui-gtk-3.0/mapctrl.h deleted file mode 100644 index a7aaf62206..0000000000 --- a/client/gui-gtk-3.0/mapctrl.h +++ /dev/null @@ -1,33 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__MAPCTRL_H -#define FC__MAPCTRL_H - -#include - -#include "fc_types.h" - -#include "mapctrl_g.h" - -gboolean butt_release_mapcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data); -gboolean butt_down_mapcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data); -gboolean butt_down_overviewcanvas(GtkWidget *w, GdkEventButton *ev, gpointer data); -gboolean move_mapcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data); -gboolean leave_mapcanvas(GtkWidget *widget, GdkEventCrossing *event); -gboolean move_overviewcanvas(GtkWidget *w, GdkEventMotion *ev, gpointer data); - -void center_on_unit(void); - -void popupinfo_popdown_callback(GtkWidget *w, gpointer data); - -#endif /* FC__MAPCTRL_H */ diff --git a/client/gui-gtk-3.0/mapview.c b/client/gui-gtk-3.0/mapview.c deleted file mode 100644 index 91ac7f9d3c..0000000000 --- a/client/gui-gtk-3.0/mapview.c +++ /dev/null @@ -1,765 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#ifdef HAVE_UNISTD_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "rand.h" -#include "support.h" -#include "timing.h" - -/* common */ -#include "game.h" -#include "government.h" /* government_graphic() */ -#include "map.h" -#include "player.h" - -/* client */ -#include "client_main.h" -#include "climap.h" -#include "climisc.h" -#include "colors.h" -#include "control.h" /* get_unit_in_focus() */ -#include "editor.h" -#include "options.h" -#include "overview_common.h" -#include "tilespec.h" -#include "text.h" -#include "zoom.h" - -/* client/gui-gtk-3.0 */ -#include "citydlg.h" /* For reset_city_dialogs() */ -#include "editgui.h" -#include "graphics.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapctrl.h" -#include "repodlgs.h" -#include "wldlg.h" - -#include "mapview.h" - -static GtkAdjustment *map_hadj, *map_vadj; -static int cursor_timer_id = 0, cursor_type = -1, cursor_frame = 0; -static int mapview_frozen_level = 0; - -/**********************************************************************//** - If do_restore is FALSE it will invert the turn done button style. If - called regularly from a timer this will give a blinking turn done - button. If do_restore is TRUE this will reset the turn done button - to the default style. -**************************************************************************/ -void update_turn_done_button(bool do_restore) -{ - static bool flip = FALSE; - - if (!get_turn_done_button_state()) { - return; - } - - if ((do_restore && flip) || !do_restore) { - GdkRGBA fore; - GdkRGBA back; - GtkStyleContext *context = gtk_widget_get_style_context(turn_done_button); - - gtk_style_context_get_color(context, GTK_STATE_FLAG_NORMAL, &fore); - gtk_style_context_get_background_color(context, GTK_STATE_FLAG_NORMAL, &back); - - gtk_widget_override_color(turn_done_button, GTK_STATE_FLAG_NORMAL, &back); - gtk_widget_override_background_color(turn_done_button, GTK_STATE_FLAG_NORMAL, &fore); - - flip = !flip; - } -} - -/**********************************************************************//** - Timeout label requires refreshing -**************************************************************************/ -void update_timeout_label(void) -{ - gtk_label_set_text(GTK_LABEL(timeout_label), get_timeout_label_text()); - - if (current_turn_timeout() > 0) { - gtk_widget_set_tooltip_text(timeout_label, - _("Time to forced turn change,\n" - "or estimated time to finish turn change " - "processing.")); - } else { - gtk_widget_set_tooltip_text(timeout_label, - _("Turn timeout disabled.\n" - "Between turns this shows estimated time " - "to finish turn change processing.")); - } -} - -/**********************************************************************//** - Refresh info label -**************************************************************************/ -void update_info_label(void) -{ - GtkWidget *label; - const struct player *pplayer = client.conn.playing; - - label = gtk_frame_get_label_widget(GTK_FRAME(main_frame_civ_name)); - if (pplayer != NULL) { - const gchar *name; - gunichar c; - - /* Capitalize the first character of the translated nation - * plural name so that the frame label looks good. */ - name = nation_plural_for_player(pplayer); - c = g_utf8_get_char_validated(name, -1); - if ((gunichar) -1 != c && (gunichar) -2 != c) { - gchar nation[MAX_LEN_NAME]; - gchar *next; - gint len; - - len = g_unichar_to_utf8(g_unichar_toupper(c), nation); - nation[len] = '\0'; - next = g_utf8_find_next_char(name, NULL); - if (NULL != next) { - sz_strlcat(nation, next); - } - gtk_label_set_text(GTK_LABEL(label), nation); - } else { - gtk_label_set_text(GTK_LABEL(label), name); - } - } else { - gtk_label_set_text(GTK_LABEL(label), "-"); - } - - gtk_label_set_text(GTK_LABEL(main_label_info), - get_info_label_text(!GUI_GTK_OPTION(small_display_layout))); - - set_indicator_icons(client_research_sprite(), - client_warming_sprite(), - client_cooling_sprite(), - client_government_sprite()); - - if (NULL != client.conn.playing) { - int d = 0; - - for (; d < client.conn.playing->economic.luxury /10; d++) { - struct sprite *spr = get_tax_sprite(tileset, O_LUXURY); - - gtk_image_set_from_surface(GTK_IMAGE(econ_label[d]), spr->surface); - } - - for (; d < (client.conn.playing->economic.science - + client.conn.playing->economic.luxury) / 10; d++) { - struct sprite *spr = get_tax_sprite(tileset, O_SCIENCE); - - gtk_image_set_from_surface(GTK_IMAGE(econ_label[d]), spr->surface); - } - - for (; d < 10; d++) { - struct sprite *spr = get_tax_sprite(tileset, O_GOLD); - - gtk_image_set_from_surface(GTK_IMAGE(econ_label[d]), spr->surface); - } - } - - update_timeout_label(); - - /* update tooltips. */ - gtk_widget_set_tooltip_text(econ_ebox, - _("Shows your current luxury/science/tax rates; " - "click to toggle them.")); - - gtk_widget_set_tooltip_text(bulb_ebox, get_bulb_tooltip()); - gtk_widget_set_tooltip_text(sun_ebox, get_global_warming_tooltip()); - gtk_widget_set_tooltip_text(flake_ebox, get_nuclear_winter_tooltip()); - gtk_widget_set_tooltip_text(government_ebox, get_government_tooltip()); -} - -/**********************************************************************//** - This function is used to animate the mouse cursor. -**************************************************************************/ -static gboolean anim_cursor_cb(gpointer data) -{ - if (!cursor_timer_id) { - return FALSE; - } - - cursor_frame++; - if (cursor_frame == NUM_CURSOR_FRAMES) { - cursor_frame = 0; - } - - if (cursor_type == CURSOR_DEFAULT) { - gdk_window_set_cursor(root_window, NULL); - cursor_timer_id = 0; - return FALSE; - } - - gdk_window_set_cursor(root_window, - fc_cursors[cursor_type][cursor_frame]); - control_mouse_cursor(NULL); - return TRUE; -} - -/**********************************************************************//** - This function will change the current mouse cursor. -**************************************************************************/ -void update_mouse_cursor(enum cursor_type new_cursor_type) -{ - cursor_type = new_cursor_type; - if (!cursor_timer_id) { - cursor_timer_id = g_timeout_add(CURSOR_INTERVAL, anim_cursor_cb, NULL); - } -} - -/**********************************************************************//** - Update the information label which gives info on the current unit and the - square under the current unit, for specified unit. Note that in practice - punit is always the focus unit. - Clears label if punit is NULL. - Also updates the cursor for the map_canvas (this is related because the - info label includes a "select destination" prompt etc). - Also calls update_unit_pix_label() to update the icons for units on this - square. -**************************************************************************/ -void update_unit_info_label(struct unit_list *punits) -{ - GtkWidget *label; - - label = gtk_frame_get_label_widget(GTK_FRAME(unit_info_frame)); - gtk_label_set_text(GTK_LABEL(label), - get_unit_info_label_text1(punits)); - - gtk_label_set_text(GTK_LABEL(unit_info_label), - get_unit_info_label_text2(punits, 0)); - - update_unit_pix_label(punits); -} - -/**********************************************************************//** - Get sprite for treaty acceptance or rejection. -**************************************************************************/ -GdkPixbuf *get_thumb_pixbuf(int onoff) -{ - return sprite_get_pixbuf(get_treaty_thumb_sprite(tileset, BOOL_VAL(onoff))); -} - -/**********************************************************************//** - Set information for the indicator icons typically shown in the main - client window. The parameters tell which sprite to use for the - indicator. -**************************************************************************/ -void set_indicator_icons(struct sprite *bulb, struct sprite *sol, - struct sprite *flake, struct sprite *gov) -{ - gtk_image_set_from_surface(GTK_IMAGE(bulb_label), bulb->surface); - gtk_image_set_from_surface(GTK_IMAGE(sun_label), sol->surface); - gtk_image_set_from_surface(GTK_IMAGE(flake_label), flake->surface); - gtk_image_set_from_surface(GTK_IMAGE(government_label), gov->surface); -} - -/**********************************************************************//** - Return the maximum dimensions of the area (container widget) for the - overview. Due to the fact that the scaling factor is at least 1, the real - size could be larger. The calculation in calculate_overview_dimensions() - limit it to the smallest possible size. -**************************************************************************/ -void get_overview_area_dimensions(int *width, int *height) -{ - *width = GUI_GTK_OVERVIEW_MIN_XSIZE; - *height = GUI_GTK_OVERVIEW_MIN_YSIZE; -} - -/**********************************************************************//** - Size of overview changed -**************************************************************************/ -void overview_size_changed(void) -{ - gtk_widget_set_size_request(overview_canvas, - gui_options.overview.width, - gui_options.overview.height); - update_map_canvas_scrollbars_size(); -} - -/**********************************************************************//** - Return a canvas that is the overview window. -**************************************************************************/ -struct canvas *get_overview_window(void) -{ -#if 0 - static struct canvas store; - - store.surface = NULL; - store.drawable = gdk_cairo_create(gtk_widget_get_window(overview_canvas)); - - return &store; -#endif /* 0 */ - if (can_client_change_view()) { - gtk_widget_queue_draw(overview_canvas); - } - return NULL; -} - -/**********************************************************************//** - Redraw overview canvas -**************************************************************************/ -gboolean overview_canvas_draw(GtkWidget *w, cairo_t *cr, gpointer data) -{ - gpointer source = (can_client_change_view()) ? - (gpointer)gui_options.overview.window : NULL; - - if (source) { - cairo_surface_t *surface = gui_options.overview.window->surface; - - cairo_set_source_surface(cr, surface, 0, 0); - cairo_paint(cr); - } - return TRUE; -} - -/**********************************************************************//** - Freeze the drawing of the map. -**************************************************************************/ -void mapview_freeze(void) -{ - mapview_frozen_level++; -} - -/**********************************************************************//** - Thaw the drawing of the map. -**************************************************************************/ -void mapview_thaw(void) -{ - if (1 < mapview_frozen_level) { - mapview_frozen_level--; - } else { - fc_assert(0 < mapview_frozen_level); - mapview_frozen_level = 0; - dirty_all(); - } -} - -/**********************************************************************//** - Return whether the map should be drawn or not. -**************************************************************************/ -bool mapview_is_frozen(void) -{ - return (0 < mapview_frozen_level); -} - -/**********************************************************************//** - Update on canvas widget size change -**************************************************************************/ -gboolean map_canvas_configure(GtkWidget *w, GdkEventConfigure *ev, - gpointer data) -{ - map_canvas_resized(ev->width, ev->height); - - return TRUE; -} - -/**********************************************************************//** - Redraw map canvas. -**************************************************************************/ -gboolean map_canvas_draw(GtkWidget *w, cairo_t *cr, gpointer data) -{ - if (can_client_change_view() && !map_is_empty() && !mapview_is_frozen()) { - /* First we mark the area to be updated as dirty. Then we unqueue - * any pending updates, to make sure only the most up-to-date data - * is written (otherwise drawing bugs happen when old data is copied - * to screen). Then we draw all changed areas to the screen. */ - update_animation(); - unqueue_mapview_updates(FALSE); - cairo_set_source_surface(cr, mapview.store->surface, 0, 0); - cairo_paint(cr); - } - return TRUE; -} - -/**********************************************************************//** - Mark the rectangular region as "dirty" so that we know to flush it - later. -**************************************************************************/ -void dirty_rect(int canvas_x, int canvas_y, - int pixel_width, int pixel_height) -{ - GdkRectangle rectangle = {canvas_x, canvas_y, pixel_width, pixel_height}; - if (gtk_widget_get_realized(map_canvas)) { - gdk_window_invalidate_rect(gtk_widget_get_window(map_canvas), &rectangle, FALSE); - } -} - -/**********************************************************************//** - Mark the entire screen area as "dirty" so that we can flush it later. -**************************************************************************/ -void dirty_all(void) -{ - if (gtk_widget_get_realized(map_canvas)) { - gdk_window_invalidate_rect(gtk_widget_get_window(map_canvas), NULL, FALSE); - } -} - -/**********************************************************************//** - Flush all regions that have been previously marked as dirty. See - dirty_rect and dirty_all. This function is generally called after we've - processed a batch of drawing operations. -**************************************************************************/ -void flush_dirty(void) -{ - if (map_canvas != NULL && gtk_widget_get_realized(map_canvas)) { - gdk_window_process_updates(gtk_widget_get_window(map_canvas), FALSE); - } -} - -/**********************************************************************//** - Do any necessary synchronization to make sure the screen is up-to-date. - The canvas should have already been flushed to screen via flush_dirty - - all this function does is make sure the hardware has caught up. -**************************************************************************/ -void gui_flush(void) -{ - cairo_surface_flush(mapview.store->surface); -} - -/**********************************************************************//** - Update display of descriptions associated with cities on the main map. -**************************************************************************/ -void update_city_descriptions(void) -{ - update_map_canvas_visible(); -} - -/**********************************************************************//** - Fill image with unit gfx -**************************************************************************/ -void put_unit_image(struct unit *punit, GtkImage *p, int height) -{ - struct canvas store = FC_STATIC_CANVAS_INIT; - int width; - - if (height <= 0) { - height = tileset_full_tile_height(tileset); - } - width = tileset_full_tile_width(tileset); - - store.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - width, height); - - put_unit(punit, &store, 1.0, 0, 0); - - gtk_image_set_from_surface(p, store.surface); - cairo_surface_destroy(store.surface); -} - -/**********************************************************************//** - FIXME: - For now only two food, two gold one shield and two masks can be drawn per - unit, the proper way to do this is probably something like what Civ II does. - (One food/shield/mask drawn N times, possibly one top of itself. -- SKi -**************************************************************************/ -void put_unit_image_city_overlays(struct unit *punit, GtkImage *p, - int height, - int *upkeep_cost, int happy_cost) -{ - struct canvas store = FC_STATIC_CANVAS_INIT; - int width = tileset_full_tile_width(tileset); - - store.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - width, height); - - put_unit(punit, &store, 1.0, 0, 0); - - put_unit_city_overlays(punit, &store, 0, tileset_unit_layout_offset_y(tileset), - upkeep_cost, happy_cost); - - gtk_image_set_from_surface(p, store.surface); - cairo_surface_destroy(store.surface); -} - -/**********************************************************************//** - Put overlay tile to pixmap -**************************************************************************/ -void pixmap_put_overlay_tile(GdkWindow *pixmap, float zoom, - int canvas_x, int canvas_y, - struct sprite *ssprite) -{ - cairo_t *cr; - - if (!ssprite) { - return; - } - - cr = gdk_cairo_create(pixmap); - cairo_scale(cr, zoom, zoom); - cairo_set_source_surface(cr, ssprite->surface, canvas_x, canvas_y); - cairo_paint(cr); - cairo_destroy(cr); -} - -/**********************************************************************//** - Only used for isometric view. -**************************************************************************/ -void pixmap_put_overlay_tile_draw(struct canvas *pcanvas, - int canvas_x, int canvas_y, - struct sprite *ssprite, - bool fog) -{ - cairo_t *cr; - int sswidth, ssheight; - const double bright = 0.65; /* Fogged brightness compared to unfogged */ - - if (!ssprite) { - return; - } - - get_sprite_dimensions(ssprite, &sswidth, &ssheight); - - if (fog) { - struct color *fogcol = color_alloc(0.0, 0.0, 0.0); /* black */ - cairo_surface_t *fog_surface; - struct sprite *fogged; - unsigned char *mask_in; - unsigned char *mask_out; - int i, j; - - /* Create sprites initially fully transparent */ - fogcol->color.alpha = 0.0; - fogged = create_sprite(sswidth, ssheight, fogcol); - fog_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sswidth, ssheight); - - /* Calculate black fog mask from the original sprite's alpha channel; - * we don't want to blacken transparent parts of the sprite. */ - mask_in = cairo_image_surface_get_data(ssprite->surface); - mask_out = cairo_image_surface_get_data(fog_surface); - - for (i = 0; i < sswidth; i++) { - for (j = 0; j < ssheight; j++) { - /* In order to darken pixels of ssprite to 'bright' fraction of - * their original value, we need to overlay blackness of - * (1-bright) transparency. */ - if (!is_bigendian()) { - mask_out[(j * sswidth + i) * 4 + 3] - = (1-bright) * mask_in[(j * sswidth + i) * 4 + 3]; - } else { - mask_out[(j * sswidth + i) * 4 + 0] - = (1-bright) * mask_in[(j * sswidth + i) * 4 + 0]; - } - } - } - - cairo_surface_mark_dirty(fog_surface); - - /* First copy original sprite canvas to intermediate sprite canvas */ - cr = cairo_create(fogged->surface); - cairo_set_source_surface(cr, ssprite->surface, 0, 0); - cairo_paint(cr); - - /* Then apply created fog to the intermediate sprite to darken it */ - cairo_set_source_surface(cr, fog_surface, 0, 0); - cairo_paint(cr); - cairo_destroy(cr); - - /* Put intermediate sprite to the target canvas */ - canvas_put_sprite(pcanvas, canvas_x, canvas_y, - fogged, 0, 0, sswidth, ssheight); - - /* Free intermediate stuff */ - cairo_surface_destroy(fog_surface); - free_sprite(fogged); - color_free(fogcol); - } else { - canvas_put_sprite(pcanvas, canvas_x, canvas_y, - ssprite, 0, 0, sswidth, ssheight); - } -} - -/**********************************************************************//** - Draws a cross-hair overlay on a tile -**************************************************************************/ -void put_cross_overlay_tile(struct tile *ptile) -{ - float canvas_x, canvas_y; - - if (tile_to_canvas_pos(&canvas_x, &canvas_y, ptile)) { - pixmap_put_overlay_tile(gtk_widget_get_window(map_canvas), map_zoom, - canvas_x / map_zoom, canvas_y / map_zoom, - get_attention_crosshair_sprite(tileset)); - } -} - -/**********************************************************************//** - Sets the position of the overview scroll window based on mapview position. -**************************************************************************/ -void update_overview_scroll_window_pos(int x, int y) -{ - gdouble ov_scroll_x, ov_scroll_y; - GtkAdjustment *ov_hadj, *ov_vadj; - - ov_hadj = gtk_scrolled_window_get_hadjustment( - GTK_SCROLLED_WINDOW(overview_scrolled_window)); - ov_vadj = gtk_scrolled_window_get_vadjustment( - GTK_SCROLLED_WINDOW(overview_scrolled_window)); - - ov_scroll_x = MIN(x - (overview_canvas_store_width / 2), - gtk_adjustment_get_upper(ov_hadj) - - gtk_adjustment_get_page_size(ov_hadj)); - ov_scroll_y = MIN(y - (overview_canvas_store_height / 2), - gtk_adjustment_get_upper(ov_vadj) - - gtk_adjustment_get_page_size(ov_vadj)); - - gtk_adjustment_set_value(ov_hadj, ov_scroll_x); - gtk_adjustment_set_value(ov_vadj, ov_scroll_y); -} - -/**********************************************************************//** - Refresh map canvas scrollbars -**************************************************************************/ -void update_map_canvas_scrollbars(void) -{ - int scroll_x, scroll_y; - - get_mapview_scroll_pos(&scroll_x, &scroll_y); - gtk_adjustment_set_value(map_hadj, scroll_x); - gtk_adjustment_set_value(map_vadj, scroll_y); - if (can_client_change_view()) { - gtk_widget_queue_draw(overview_canvas); - } -} - -/**********************************************************************//** - Refresh map canvas scrollbar as canvas size changes -**************************************************************************/ -void update_map_canvas_scrollbars_size(void) -{ - float xmin, ymin, xmax, ymax; - int xsize, ysize, xstep, ystep; - - get_mapview_scroll_window(&xmin, &ymin, &xmax, &ymax, &xsize, &ysize); - get_mapview_scroll_step(&xstep, &ystep); - - map_hadj = gtk_adjustment_new(-1, xmin, xmax, xstep, xsize, xsize); - map_vadj = gtk_adjustment_new(-1, ymin, ymax, ystep, ysize, ysize); - - gtk_range_set_adjustment(GTK_RANGE(map_horizontal_scrollbar), map_hadj); - gtk_range_set_adjustment(GTK_RANGE(map_vertical_scrollbar), map_vadj); - - g_signal_connect(map_hadj, "value_changed", - G_CALLBACK(scrollbar_jump_callback), - GINT_TO_POINTER(TRUE)); - g_signal_connect(map_vadj, "value_changed", - G_CALLBACK(scrollbar_jump_callback), - GINT_TO_POINTER(FALSE)); -} - -/**********************************************************************//** - Scrollbar has moved -**************************************************************************/ -void scrollbar_jump_callback(GtkAdjustment *adj, gpointer hscrollbar) -{ - int scroll_x, scroll_y; - - if (!can_client_change_view()) { - return; - } - - get_mapview_scroll_pos(&scroll_x, &scroll_y); - - if (hscrollbar) { - scroll_x = gtk_adjustment_get_value(adj); - } else { - scroll_y = gtk_adjustment_get_value(adj); - } - - set_mapview_scroll_pos(scroll_x, scroll_y); -} - -/**********************************************************************//** - Draws a rectangle with top left corner at (canvas_x, canvas_y), and - width 'w' and height 'h'. It is drawn using the 'selection_gc' context, - so the pixel combining function is XOR. This means that drawing twice - in the same place will restore the image to its original state. - - NB: A side effect of this function is to set the 'selection_gc' color - to COLOR_MAPVIEW_SELECTION. -**************************************************************************/ -void draw_selection_rectangle(int canvas_x, int canvas_y, int w, int h) -{ - double dashes[2] = {4.0, 4.0}; - struct color *pcolor; - cairo_t *cr; - - if (w == 0 || h == 0) { - return; - } - - pcolor = get_color(tileset, COLOR_MAPVIEW_SELECTION); - if (!pcolor) { - return; - } - - cr = gdk_cairo_create(gtk_widget_get_window(map_canvas)); - gdk_cairo_set_source_rgba(cr, &pcolor->color); - cairo_set_line_width(cr, 2.0); - cairo_set_dash(cr, dashes, 2, 0); -#ifdef FREECIV_MSWINDOWS - if (cairo_version() < CAIRO_VERSION_ENCODE(1, 12, 0)) { - /* Cairo has crashing CAIRO_OPERATOR_DIFFERENCE on win32 surface */ - cairo_set_operator(cr, CAIRO_OPERATOR_XOR); - } else -#endif /* FREECIV_MSWINDOWS */ - { - cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE); - } - cairo_rectangle(cr, canvas_x, canvas_y, w, h); - cairo_stroke(cr); - cairo_destroy(cr); -} - -/**********************************************************************//** - This function is called when the tileset is changed. -**************************************************************************/ -void tileset_changed(void) -{ - science_report_dialog_redraw(); - reset_city_dialogs(); - reset_unit_table(); - blank_max_unit_size(); - editgui_tileset_changed(); - - /* keep the icon of the executable on Windows (see PR#36491) */ -#ifndef FREECIV_MSWINDOWS - { - GdkPixbuf *pixbuf = sprite_get_pixbuf(get_icon_sprite(tileset, ICON_FREECIV)); - - /* Only call this after tileset_load_tiles is called. */ - gtk_window_set_icon(GTK_WINDOW(toplevel), pixbuf); - g_object_unref(pixbuf); - } -#endif /* FREECIV_MSWINDOWS */ -} - -/**********************************************************************//** - New turn callback -**************************************************************************/ -void start_turn(void) -{} diff --git a/client/gui-gtk-3.0/mapview.h b/client/gui-gtk-3.0/mapview.h deleted file mode 100644 index 503cda3137..0000000000 --- a/client/gui-gtk-3.0/mapview.h +++ /dev/null @@ -1,60 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__MAPVIEW_H -#define FC__MAPVIEW_H - -#include - -/* common */ -#include "fc_types.h" - -/* client */ -#include "citydlg_common.h" -#include "mapview_g.h" -#include "mapview_common.h" - -/* client/gui-gtk-3.0 */ -#include "canvas.h" -#include "graphics.h" - -GdkPixbuf *get_thumb_pixbuf(int onoff); - -#define CURSOR_INTERVAL 200 /* milliseconds */ - -gboolean overview_canvas_draw(GtkWidget *w, cairo_t *cr, gpointer data); -gboolean map_canvas_draw(GtkWidget *w, cairo_t *cr, gpointer data); -gboolean map_canvas_configure(GtkWidget *w, GdkEventConfigure *ev, - gpointer data); - -void put_unit_image(struct unit *punit, GtkImage *p, int height); - -void put_unit_image_city_overlays(struct unit *punit, GtkImage *p, - int height, int *upkeep_cost, int happy_cost); - -void scrollbar_jump_callback(GtkAdjustment *adj, gpointer hscrollbar); -void update_map_canvas_scrollbars_size(void); - -void pixmap_put_overlay_tile(GdkWindow *pixmap, float zoom, - int canvas_x, int canvas_y, - struct sprite *ssprite); - -void pixmap_put_overlay_tile_draw(struct canvas *pcanvas, - int canvas_x, int canvas_y, - struct sprite *ssprite, - bool fog); - -void mapview_freeze(void); -void mapview_thaw(void); -bool mapview_is_frozen(void); - -#endif /* FC__MAPVIEW_H */ diff --git a/client/gui-gtk-3.0/menu.c b/client/gui-gtk-3.0/menu.c deleted file mode 100644 index 7b7d3d28b0..0000000000 --- a/client/gui-gtk-3.0/menu.c +++ /dev/null @@ -1,2928 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "game.h" -#include "government.h" -#include "road.h" -#include "unit.h" - -/* client */ -#include "client_main.h" -#include "clinet.h" -#include "connectdlg_common.h" -#include "control.h" -#include "mapview_common.h" -#include "options.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "cityrep.h" -#include "dialogs.h" -#include "editgui.h" -#include "editprop.h" -#include "finddlg.h" -#include "gamedlgs.h" -#include "gotodlg.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "helpdlg.h" -#include "luaconsole.h" -#include "mapctrl.h" /* center_on_unit(). */ -#include "messagedlg.h" -#include "messagewin.h" -#include "optiondlg.h" -#include "pages.h" -#include "plrdlg.h" -#include "ratesdlg.h" -#include "repodlgs.h" -#include "sprite.h" -#include "spaceshipdlg.h" -#include "transportdlg.h" -#include "unitselect.h" -#include "wldlg.h" - -#include "menu.h" - -#ifndef GTK_STOCK_EDIT -#define GTK_STOCK_EDIT NULL -#endif - -static GtkBuilder *ui_builder = NULL; - -static void menu_entry_set_active(const char *key, - gboolean is_active); -static void menu_entry_set_sensitive(const char *key, - gboolean is_sensitive); -#ifndef FREECIV_DEBUG -static void menu_entry_set_visible(const char *key, - gboolean is_visible, - gboolean is_sensitive); -#endif /* FREECIV_DEBUG */ - -static void view_menu_update_sensitivity(void); - -enum menu_entry_grouping { MGROUP_SAFE, MGROUP_EDIT, MGROUP_PLAYING, - MGROUP_UNIT, MGROUP_PLAYER, MGROUP_ALL }; - -static void menu_entry_group_set_sensitive(enum menu_entry_grouping group, - gboolean is_sensitive); - -struct menu_entry_info { - const char *key; - const char *name; - guint accel; - GdkModifierType accel_mod; - GCallback cb; - enum menu_entry_grouping grouping; -}; - -static void clear_chat_logs_callback(GtkMenuItem *item, gpointer data); -static void save_chat_logs_callback(GtkMenuItem *item, gpointer data); -static void local_options_callback(GtkMenuItem *item, gpointer data); -static void message_options_callback(GtkMenuItem *item, gpointer data); -static void server_options_callback(GtkMenuItem *item, gpointer data); -static void save_options_callback(GtkMenuItem *item, gpointer data); -static void reload_tileset_callback(GtkMenuItem *item, gpointer data); -static void save_game_callback(GtkMenuItem *item, gpointer data); -static void save_game_as_callback(GtkMenuItem *item, gpointer data); -static void save_mapimg_callback(GtkMenuItem *item, gpointer data); -static void save_mapimg_as_callback(GtkMenuItem *item, gpointer data); -static void find_city_callback(GtkMenuItem *item, gpointer data); -static void worklists_callback(GtkMenuItem *item, gpointer data); -static void client_lua_script_callback(GtkMenuItem *item, gpointer data); -static void leave_callback(GtkMenuItem *item, gpointer data); -static void quit_callback(GtkMenuItem *item, gpointer data); -static void map_view_callback(GtkMenuItem *item, gpointer data); -static void report_units_callback(GtkMenuItem *item, gpointer data); -static void report_nations_callback(GtkMenuItem *item, gpointer data); -static void report_cities_callback(GtkMenuItem *item, gpointer data); -static void report_wow_callback(GtkMenuItem *item, gpointer data); -static void report_top_cities_callback(GtkMenuItem *item, gpointer data); -static void report_messages_callback(GtkMenuItem *item, gpointer data); -static void report_demographic_callback(GtkMenuItem *item, gpointer data); -static void help_overview_callback(GtkMenuItem *item, gpointer data); -static void help_playing_callback(GtkMenuItem *item, gpointer data); -static void help_policies_callback(GtkMenuItem *item, gpointer data); -static void help_terrain_callback(GtkMenuItem *item, gpointer data); -static void help_economy_callback(GtkMenuItem *item, gpointer data); -static void help_cities_callback(GtkMenuItem *item, gpointer data); -static void help_improvements_callback(GtkMenuItem *item, gpointer data); -static void help_wonders_callback(GtkMenuItem *item, gpointer data); -static void help_units_callback(GtkMenuItem *item, gpointer data); -static void help_combat_callback(GtkMenuItem *item, gpointer data); -static void help_zoc_callback(GtkMenuItem *item, gpointer data); -static void help_government_callback(GtkMenuItem *item, gpointer data); -static void help_diplomacy_callback(GtkMenuItem *item, gpointer data); -static void help_tech_callback(GtkMenuItem *item, gpointer data); -static void help_space_race_callback(GtkMenuItem *item, gpointer data); -static void help_ruleset_callback(GtkMenuItem *item, gpointer data); -static void help_tileset_callback(GtkMenuItem *item, gpointer data); -static void help_nations_callback(GtkMenuItem *item, gpointer data); -static void help_connecting_callback(GtkMenuItem *item, gpointer data); -static void help_controls_callback(GtkMenuItem *item, gpointer data); -static void help_cma_callback(GtkMenuItem *item, gpointer data); -static void help_chatline_callback(GtkMenuItem *item, gpointer data); -static void help_worklist_editor_callback(GtkMenuItem *item, gpointer data); -static void help_language_callback(GtkMenuItem *item, gpointer data); -static void help_copying_callback(GtkMenuItem *item, gpointer data); -static void help_about_callback(GtkMenuItem *item, gpointer data); -static void save_options_on_exit_callback(GtkCheckMenuItem *item, - gpointer data); -static void edit_mode_callback(GtkCheckMenuItem *item, gpointer data); -static void show_city_outlines_callback(GtkCheckMenuItem *item, - gpointer data); -static void show_city_output_callback(GtkCheckMenuItem *item, gpointer data); -static void show_map_grid_callback(GtkCheckMenuItem *item, gpointer data); -static void show_national_borders_callback(GtkCheckMenuItem *item, - gpointer data); -static void show_native_tiles_callback(GtkCheckMenuItem *item, - gpointer data); -static void show_city_full_bar_callback(GtkCheckMenuItem *item, - gpointer data); -static void show_city_names_callback(GtkCheckMenuItem *item, gpointer data); -static void show_city_growth_callback(GtkCheckMenuItem *item, gpointer data); -static void show_city_productions_callback(GtkCheckMenuItem *item, - gpointer data); -static void show_city_buy_cost_callback(GtkCheckMenuItem *item, - gpointer data); -static void show_city_trade_routes_callback(GtkCheckMenuItem *item, - gpointer data); -static void show_terrain_callback(GtkCheckMenuItem *item, gpointer data); -static void show_coastline_callback(GtkCheckMenuItem *item, gpointer data); -static void show_road_rails_callback(GtkCheckMenuItem *item, gpointer data); -static void show_irrigation_callback(GtkCheckMenuItem *item, gpointer data); -static void show_mine_callback(GtkCheckMenuItem *item, gpointer data); -static void show_bases_callback(GtkCheckMenuItem *item, gpointer data); -static void show_resources_callback(GtkCheckMenuItem *item, gpointer data); -static void show_huts_callback(GtkCheckMenuItem *item, gpointer data); -static void show_pollution_callback(GtkCheckMenuItem *item, gpointer data); -static void show_cities_callback(GtkCheckMenuItem *item, gpointer data); -static void show_units_callback(GtkCheckMenuItem *item, gpointer data); -static void show_unit_solid_bg_callback(GtkCheckMenuItem *item, - gpointer data); -static void show_unit_shields_callback(GtkCheckMenuItem *item, - gpointer data); -static void show_focus_unit_callback(GtkCheckMenuItem *item, gpointer data); -static void show_fog_of_war_callback(GtkCheckMenuItem *item, gpointer data); -static void full_screen_callback(GtkCheckMenuItem *item, gpointer data); -static void recalc_borders_callback(GtkMenuItem *item, gpointer data); -static void toggle_fog_callback(GtkMenuItem *item, gpointer data); -static void scenario_properties_callback(GtkMenuItem *item, gpointer data); -static void save_scenario_callback(GtkMenuItem *item, gpointer data); -static void center_view_callback(GtkMenuItem *item, gpointer data); -static void report_economy_callback(GtkMenuItem *item, gpointer data); -static void report_research_callback(GtkMenuItem *item, gpointer data); -static void multiplier_callback(GtkMenuItem *item, gpointer data); -static void report_spaceship_callback(GtkMenuItem *item, gpointer data); -static void report_achievements_callback(GtkMenuItem *item, gpointer data); -static void government_callback(GtkMenuItem *item, gpointer data); -static void tax_rate_callback(GtkMenuItem *item, gpointer data); -static void select_single_callback(GtkMenuItem *item, gpointer data); -static void select_all_on_tile_callback(GtkMenuItem *item, gpointer data); -static void select_same_type_tile_callback(GtkMenuItem *item, gpointer data); -static void select_same_type_cont_callback(GtkMenuItem *item, gpointer data); -static void select_same_type_callback(GtkMenuItem *item, gpointer data); -static void select_dialog_callback(GtkMenuItem *item, gpointer data); -static void unit_wait_callback(GtkMenuItem *item, gpointer data); -static void unit_done_callback(GtkMenuItem *item, gpointer data); -static void unit_goto_callback(GtkMenuItem *item, gpointer data); -static void unit_goto_city_callback(GtkMenuItem *item, gpointer data); -static void unit_return_callback(GtkMenuItem *item, gpointer data); -static void unit_explore_callback(GtkMenuItem *item, gpointer data); -static void unit_patrol_callback(GtkMenuItem *item, gpointer data); -static void unit_sentry_callback(GtkMenuItem *item, gpointer data); -static void unit_unsentry_callback(GtkMenuItem *item, gpointer data); -static void unit_load_callback(GtkMenuItem *item, gpointer data); -static void unit_unload_callback(GtkMenuItem *item, gpointer data); -static void unit_unload_transporter_callback(GtkMenuItem *item, - gpointer data); -static void unit_homecity_callback(GtkMenuItem *item, gpointer data); -static void unit_upgrade_callback(GtkMenuItem *item, gpointer data); -static void unit_convert_callback(GtkMenuItem *item, gpointer data); -static void unit_disband_callback(GtkMenuItem *item, gpointer data); -static void build_city_callback(GtkMenuItem *item, gpointer data); -static void auto_settle_callback(GtkMenuItem *item, gpointer data); -static void build_road_callback(GtkMenuItem *item, gpointer data); -static void build_irrigation_callback(GtkMenuItem *item, gpointer data); -static void cultivate_callback(GtkMenuItem *item, gpointer data); -static void build_mine_callback(GtkMenuItem *item, gpointer data); -static void plant_callback(GtkMenuItem *item, gpointer data); -static void connect_road_callback(GtkMenuItem *item, gpointer data); -static void connect_rail_callback(GtkMenuItem *item, gpointer data); -static void connect_irrigation_callback(GtkMenuItem *item, gpointer data); -static void transform_terrain_callback(GtkMenuItem *item, gpointer data); -static void clean_pollution_callback(GtkMenuItem *item, gpointer data); -static void clean_fallout_callback(GtkMenuItem *item, gpointer data); -static void fortify_callback(GtkMenuItem *item, gpointer data); -static void build_fortress_callback(GtkMenuItem *item, gpointer data); -static void build_airbase_callback(GtkMenuItem *item, gpointer data); -static void do_pillage_callback(GtkMenuItem *item, gpointer data); -static void diplomat_action_callback(GtkMenuItem *item, gpointer data); - -static struct menu_entry_info menu_entries[] = -{ - { "MENU_GAME", N_("_Game"), 0, 0, NULL, MGROUP_SAFE }, - { "MENU_OPTIONS", N_("_Options"), 0, 0, NULL, MGROUP_SAFE }, - { "MENU_EDIT", N_("_Edit"), 0, 0, NULL, MGROUP_SAFE }, - { "MENU_VIEW", N_("?verb:_View"), 0, 0, NULL, MGROUP_SAFE }, - { "MENU_IMPROVEMENTS", N_("_Improvements"), 0, 0, - NULL, MGROUP_SAFE }, - { "MENU_CIVILIZATION", N_("C_ivilization"), 0, 0, - NULL, MGROUP_SAFE }, - { "MENU_HELP", N_("_Help"), 0, 0, NULL, MGROUP_SAFE }, - { "CLEAR_CHAT_LOGS", N_("_Clear Chat Log"), 0, 0, - G_CALLBACK(clear_chat_logs_callback), MGROUP_SAFE }, - { "SAVE_CHAT_LOGS", N_("Save Chat _Log"), 0, 0, - G_CALLBACK(save_chat_logs_callback), MGROUP_SAFE }, - { "LOCAL_OPTIONS", N_("_Local Client"), 0, 0, - G_CALLBACK(local_options_callback), MGROUP_SAFE }, - { "MESSAGE_OPTIONS", N_("_Message"), 0, 0, - G_CALLBACK(message_options_callback), MGROUP_SAFE }, - { "SERVER_OPTIONS", N_("_Remote Server"), 0, 0, - G_CALLBACK(server_options_callback), MGROUP_SAFE }, - { "SAVE_OPTIONS", N_("Save Options _Now"), 0, 0, - G_CALLBACK(save_options_callback), MGROUP_SAFE }, - { "RELOAD_TILESET", N_("_Reload Tileset"), - GDK_KEY_r, GDK_MOD1_MASK | GDK_CONTROL_MASK, - G_CALLBACK(reload_tileset_callback), MGROUP_SAFE }, - { "GAME_SAVE", N_("_Save Game"), GDK_KEY_s, GDK_CONTROL_MASK, - G_CALLBACK(save_game_callback), MGROUP_SAFE }, - { "GAME_SAVE_AS", N_("Save Game _As..."), 0, 0, - G_CALLBACK(save_game_as_callback), MGROUP_SAFE }, - { "MAPIMG_SAVE", N_("Save Map _Image"), 0, 0, - G_CALLBACK(save_mapimg_callback), MGROUP_SAFE }, - { "MAPIMG_SAVE_AS", N_("Save _Map Image As..."), 0, 0, - G_CALLBACK(save_mapimg_as_callback), MGROUP_SAFE }, - { "LEAVE", N_("_Leave"), 0, 0, G_CALLBACK(leave_callback), MGROUP_SAFE }, - { "QUIT", N_("_Quit"), GDK_KEY_q, GDK_CONTROL_MASK, - G_CALLBACK(quit_callback), MGROUP_SAFE }, - { "FIND_CITY", N_("_Find City"), GDK_KEY_f, GDK_CONTROL_MASK, - G_CALLBACK(find_city_callback), MGROUP_SAFE }, - { "WORKLISTS", N_("Work_lists"), GDK_KEY_l, GDK_CONTROL_MASK, - G_CALLBACK(worklists_callback), MGROUP_SAFE }, - { "CLIENT_LUA_SCRIPT", N_("Client _Lua Script"), 0, 0, - G_CALLBACK(client_lua_script_callback), MGROUP_SAFE }, - { "MAP_VIEW", N_("?noun:_View"), GDK_KEY_F1, 0, - G_CALLBACK(map_view_callback), MGROUP_SAFE }, - { "REPORT_UNITS", N_("_Units"), GDK_KEY_F2, 0, - G_CALLBACK(report_units_callback), MGROUP_SAFE }, - { "REPORT_NATIONS", N_("_Nations"), GDK_KEY_F3, 0, - G_CALLBACK(report_nations_callback), MGROUP_SAFE }, - { "REPORT_CITIES", N_("_Cities"), GDK_KEY_F4, 0, - G_CALLBACK(report_cities_callback), MGROUP_SAFE }, - { "REPORT_WOW", N_("_Wonders of the World"), GDK_KEY_F7, 0, - G_CALLBACK(report_wow_callback), MGROUP_SAFE }, - { "REPORT_TOP_CITIES", N_("Top _Five Cities"), GDK_KEY_F8, 0, - G_CALLBACK(report_top_cities_callback), MGROUP_SAFE }, - { "REPORT_MESSAGES", N_("_Messages"), GDK_KEY_F9, 0, - G_CALLBACK(report_messages_callback), MGROUP_SAFE }, - { "REPORT_DEMOGRAPHIC", N_("_Demographics"), GDK_KEY_F11, 0, - G_CALLBACK(report_demographic_callback), MGROUP_SAFE }, - { "HELP_OVERVIEW", N_("?help:Overview"), 0, 0, - G_CALLBACK(help_overview_callback), MGROUP_SAFE }, - { "HELP_PLAYING", N_("Strategy and Tactics"), 0, 0, - G_CALLBACK(help_playing_callback), MGROUP_SAFE }, - { "HELP_POLICIES", N_("Policies"), 0, 0, - G_CALLBACK(help_policies_callback), MGROUP_SAFE }, - { "HELP_TERRAIN", N_("Terrain"), 0, 0, - G_CALLBACK(help_terrain_callback), MGROUP_SAFE }, - { "HELP_ECONOMY", N_("Economy"), 0, 0, - G_CALLBACK(help_economy_callback), MGROUP_SAFE }, - { "HELP_CITIES", N_("Cities"), 0, 0, - G_CALLBACK(help_cities_callback), MGROUP_SAFE }, - { "HELP_IMPROVEMENTS", N_("City Improvements"), 0, 0, - G_CALLBACK(help_improvements_callback), MGROUP_SAFE }, - { "HELP_WONDERS", N_("Wonders of the World"), 0, 0, - G_CALLBACK(help_wonders_callback), MGROUP_SAFE }, - { "HELP_UNITS", N_("Units"), 0, 0, - G_CALLBACK(help_units_callback), MGROUP_SAFE }, - { "HELP_COMBAT", N_("Combat"), 0, 0, - G_CALLBACK(help_combat_callback), MGROUP_SAFE }, - { "HELP_ZOC", N_("Zones of Control"), 0, 0, - G_CALLBACK(help_zoc_callback), MGROUP_SAFE }, - { "HELP_GOVERNMENT", N_("Government"), 0, 0, - G_CALLBACK(help_government_callback), MGROUP_SAFE }, - { "HELP_DIPLOMACY", N_("Diplomacy"), 0, 0, - G_CALLBACK(help_diplomacy_callback), MGROUP_SAFE }, - { "HELP_TECH", N_("Technology"), 0, 0, - G_CALLBACK(help_tech_callback), MGROUP_SAFE }, - { "HELP_SPACE_RACE", N_("Space Race"), 0, 0, - G_CALLBACK(help_space_race_callback), MGROUP_SAFE }, - { "HELP_RULESET", N_("About Current Ruleset"), 0, 0, - G_CALLBACK(help_ruleset_callback), MGROUP_SAFE }, - { "HELP_TILESET", N_("About Current Tileset"), 0, 0, - G_CALLBACK(help_tileset_callback), MGROUP_SAFE }, - { "HELP_NATIONS", N_("About Nations"), 0, 0, - G_CALLBACK(help_nations_callback), MGROUP_SAFE }, - { "HELP_CONNECTING", N_("Connecting"), 0, 0, - G_CALLBACK(help_connecting_callback), MGROUP_SAFE }, - { "HELP_CONTROLS", N_("Controls"), 0, 0, - G_CALLBACK(help_controls_callback), MGROUP_SAFE }, - { "HELP_CMA", N_("Citizen Governor"), 0, 0, - G_CALLBACK(help_cma_callback), MGROUP_SAFE }, - { "HELP_CHATLINE", N_("Chatline"), 0, 0, - G_CALLBACK(help_chatline_callback), MGROUP_SAFE }, - { "HELP_WORKLIST_EDITOR", N_("Worklist Editor"), 0, 0, - G_CALLBACK(help_worklist_editor_callback), MGROUP_SAFE }, - { "HELP_LANGUAGES", N_("Languages"), 0, 0, - G_CALLBACK(help_language_callback), MGROUP_SAFE }, - { "HELP_COPYING", N_("Copying"), 0, 0, - G_CALLBACK(help_copying_callback), MGROUP_SAFE }, - { "HELP_ABOUT", N_("About Freeciv"), 0, 0, - G_CALLBACK(help_about_callback), MGROUP_SAFE }, - { "SAVE_OPTIONS_ON_EXIT", N_("Save Options on _Exit"), 0, 0, - G_CALLBACK(save_options_on_exit_callback), MGROUP_SAFE }, - { "EDIT_MODE", N_("_Editing Mode"), GDK_KEY_e, GDK_CONTROL_MASK, - G_CALLBACK(edit_mode_callback), MGROUP_SAFE }, - { "SHOW_CITY_OUTLINES", N_("Cit_y Outlines"), GDK_KEY_y, GDK_CONTROL_MASK, - G_CALLBACK(show_city_outlines_callback), MGROUP_SAFE }, - { "SHOW_CITY_OUTPUT", N_("City Output"), GDK_KEY_w, GDK_CONTROL_MASK, - G_CALLBACK(show_city_output_callback), MGROUP_SAFE }, - { "SHOW_MAP_GRID", N_("Map _Grid"), GDK_KEY_g, GDK_CONTROL_MASK, - G_CALLBACK(show_map_grid_callback), MGROUP_SAFE }, - { "SHOW_NATIONAL_BORDERS", N_("National _Borders"), GDK_KEY_b, GDK_CONTROL_MASK, - G_CALLBACK(show_national_borders_callback), MGROUP_SAFE }, - { "SHOW_NATIVE_TILES", N_("Native Tiles"), GDK_KEY_n, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - G_CALLBACK(show_native_tiles_callback), MGROUP_SAFE }, - { "SHOW_CITY_FULL_BAR", N_("City Full Bar"), 0, 0, - G_CALLBACK(show_city_full_bar_callback), MGROUP_SAFE }, - { "SHOW_CITY_NAMES", N_("City _Names"), GDK_KEY_n, GDK_CONTROL_MASK, - G_CALLBACK(show_city_names_callback), MGROUP_SAFE }, - { "SHOW_CITY_GROWTH", N_("City G_rowth"), GDK_KEY_o, GDK_CONTROL_MASK, - G_CALLBACK(show_city_growth_callback), MGROUP_SAFE }, - { "SHOW_CITY_PRODUCTIONS", N_("City _Production Levels"), GDK_KEY_p, GDK_CONTROL_MASK, - G_CALLBACK(show_city_productions_callback), MGROUP_SAFE }, - { "SHOW_CITY_BUY_COST", N_("City Buy Cost"), 0, 0, - G_CALLBACK(show_city_buy_cost_callback), MGROUP_SAFE }, - { "SHOW_CITY_TRADE_ROUTES", N_("City Tra_deroutes"), GDK_KEY_d, GDK_CONTROL_MASK, - G_CALLBACK(show_city_trade_routes_callback), MGROUP_SAFE }, - { "SHOW_TERRAIN", N_("_Terrain"), 0, 0, - G_CALLBACK(show_terrain_callback), MGROUP_SAFE }, - { "SHOW_COASTLINE", N_("C_oastline"), 0, 0, - G_CALLBACK(show_coastline_callback), MGROUP_SAFE }, - { "SHOW_PATHS", N_("_Paths"), 0, 0, - G_CALLBACK(show_road_rails_callback), MGROUP_SAFE }, - { "SHOW_IRRIGATION", N_("_Irrigation"), 0, 0, - G_CALLBACK(show_irrigation_callback), MGROUP_SAFE }, - { "SHOW_MINES", N_("_Mines"), 0, 0, - G_CALLBACK(show_mine_callback), MGROUP_SAFE }, - { "SHOW_BASES", N_("_Bases"), 0, 0, - G_CALLBACK(show_bases_callback), MGROUP_SAFE }, - { "SHOW_RESOURCES", N_("_Resources"), 0, 0, - G_CALLBACK(show_resources_callback), MGROUP_SAFE }, - { "SHOW_HUTS", N_("_Huts"), 0, 0, - G_CALLBACK(show_huts_callback), MGROUP_SAFE }, - { "SHOW_POLLUTION", N_("Po_llution & Fallout"), 0, 0, - G_CALLBACK(show_pollution_callback), MGROUP_SAFE }, - { "SHOW_CITIES", N_("Citi_es"), 0, 0, - G_CALLBACK(show_cities_callback), MGROUP_SAFE }, - { "SHOW_UNITS", N_("_Units"), 0, 0, - G_CALLBACK(show_units_callback), MGROUP_SAFE }, - { "SHOW_UNIT_SOLID_BG", N_("Unit Solid Background"), 0, 0, - G_CALLBACK(show_unit_solid_bg_callback), MGROUP_SAFE }, - { "SHOW_UNIT_SHIELDS", N_("Unit shields"), 0, 0, - G_CALLBACK(show_unit_shields_callback), MGROUP_SAFE }, - { "SHOW_FOCUS_UNIT", N_("Focu_s Unit"), 0, 0, - G_CALLBACK(show_focus_unit_callback), MGROUP_SAFE }, - { "SHOW_FOG_OF_WAR", N_("Fog of _War"), 0, 0, - G_CALLBACK(show_fog_of_war_callback), MGROUP_SAFE }, - { "FULL_SCREEN", N_("_Fullscreen"), GDK_KEY_Return, GDK_MOD1_MASK, - G_CALLBACK(full_screen_callback), MGROUP_SAFE }, - - { "RECALC_BORDERS", N_("Recalculate _Borders"), 0, 0, - G_CALLBACK(recalc_borders_callback), MGROUP_EDIT }, - { "TOGGLE_FOG", N_("Toggle Fog of _War"), GDK_KEY_m, GDK_CONTROL_MASK, - G_CALLBACK(toggle_fog_callback), MGROUP_EDIT }, - { "SCENARIO_PROPERTIES", N_("Game/Scenario Properties"), 0, 0, - G_CALLBACK(scenario_properties_callback), MGROUP_EDIT }, - { "SAVE_SCENARIO", N_("Save Scenario"), 0, 0, - G_CALLBACK(save_scenario_callback), MGROUP_EDIT }, - - { "CENTER_VIEW", N_("_Center View"), GDK_KEY_c, 0, - G_CALLBACK(center_view_callback), MGROUP_PLAYER }, - { "REPORT_ECONOMY", N_("_Economy"), GDK_KEY_F5, 0, - G_CALLBACK(report_economy_callback), MGROUP_PLAYER }, - { "REPORT_RESEARCH", N_("_Research"), GDK_KEY_F6, 0, - G_CALLBACK(report_research_callback), MGROUP_PLAYER }, - { "POLICIES", N_("_Policies..."), - GDK_KEY_p, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - G_CALLBACK(multiplier_callback), MGROUP_PLAYER }, - { "REPORT_SPACESHIP", N_("_Spaceship"), GDK_KEY_F12, 0, - G_CALLBACK(report_spaceship_callback), MGROUP_PLAYER }, - { "REPORT_ACHIEVEMENTS", N_("_Achievements"), GDK_KEY_asterisk, 0, - G_CALLBACK(report_achievements_callback), MGROUP_PLAYER }, - - { "MENU_SELECT", N_("_Select"), 0, 0, NULL, MGROUP_UNIT }, - { "MENU_UNIT", N_("_Unit"), 0, 0, NULL, MGROUP_UNIT }, - { "MENU_WORK", N_("_Work"), 0, 0, NULL, MGROUP_UNIT }, - { "MENU_COMBAT", N_("_Combat"), 0, 0, NULL, MGROUP_UNIT }, - { "MENU_BUILD_BASE", N_("Build _Base"), 0, 0, NULL, MGROUP_UNIT }, - { "MENU_BUILD_PATH", N_("Build _Path"), 0, 0, NULL, MGROUP_UNIT }, - { "SELECT_SINGLE", N_("_Single Unit (Unselect Others)"), GDK_KEY_z, 0, - G_CALLBACK(select_single_callback), MGROUP_UNIT }, - { "SELECT_ALL_ON_TILE", N_("_All On Tile"), GDK_KEY_v, 0, - G_CALLBACK(select_all_on_tile_callback), MGROUP_UNIT }, - { "SELECT_SAME_TYPE_TILE", N_("Same Type on _Tile"), - GDK_KEY_v, GDK_SHIFT_MASK, - G_CALLBACK(select_same_type_tile_callback), MGROUP_UNIT }, - { "SELECT_SAME_TYPE_CONT", N_("Same Type on _Continent"), - GDK_KEY_c, GDK_SHIFT_MASK, - G_CALLBACK(select_same_type_cont_callback), MGROUP_UNIT }, - { "SELECT_SAME_TYPE", N_("Same Type _Everywhere"), GDK_KEY_x, GDK_SHIFT_MASK, - G_CALLBACK(select_same_type_callback), MGROUP_UNIT }, - { "SELECT_DLG", N_("Unit Selection Dialog"), 0, 0, - G_CALLBACK(select_dialog_callback), MGROUP_UNIT }, - { "UNIT_WAIT", N_("_Wait"), GDK_KEY_w, 0, - G_CALLBACK(unit_wait_callback), MGROUP_UNIT }, - { "UNIT_DONE", N_("_Done"), GDK_KEY_space, 0, - G_CALLBACK(unit_done_callback), MGROUP_UNIT }, - { "UNIT_GOTO", N_("_Go to"), GDK_KEY_g, 0, - G_CALLBACK(unit_goto_callback), MGROUP_UNIT }, - { "MENU_GOTO_AND", N_("Go to a_nd..."), 0, 0, NULL, MGROUP_UNIT }, - { "UNIT_GOTO_CITY", N_("Go _to/Airlift to City..."), GDK_KEY_t, 0, - G_CALLBACK(unit_goto_city_callback), MGROUP_UNIT }, - { "UNIT_RETURN", N_("_Return to Nearest City"), GDK_KEY_g, GDK_SHIFT_MASK, - G_CALLBACK(unit_return_callback), MGROUP_UNIT }, - { "UNIT_EXPLORE", N_("Auto E_xplore"), GDK_KEY_x, 0, - G_CALLBACK(unit_explore_callback), MGROUP_UNIT }, - { "UNIT_PATROL", N_("_Patrol"), GDK_KEY_q, 0, - G_CALLBACK(unit_patrol_callback), MGROUP_UNIT }, - { "UNIT_SENTRY", N_("_Sentry"), GDK_KEY_s, 0, - G_CALLBACK(unit_sentry_callback), MGROUP_UNIT }, - { "UNIT_UNSENTRY", N_("Uns_entry All On Tile"), GDK_KEY_s, GDK_SHIFT_MASK, - G_CALLBACK(unit_unsentry_callback), MGROUP_UNIT }, - { "UNIT_LOAD", N_("_Load"), GDK_KEY_l, 0, - G_CALLBACK(unit_load_callback), MGROUP_UNIT }, - { "UNIT_UNLOAD", N_("_Unload"), GDK_KEY_u, 0, - G_CALLBACK(unit_unload_callback), MGROUP_UNIT }, - { "UNIT_UNLOAD_TRANSPORTER", N_("U_nload All From Transporter"), - GDK_KEY_t, GDK_SHIFT_MASK, - G_CALLBACK(unit_unload_transporter_callback), MGROUP_UNIT }, - { "UNIT_HOMECITY", N_("Set _Home City"), GDK_KEY_h, 0, - G_CALLBACK(unit_homecity_callback), MGROUP_UNIT }, - { "UNIT_UPGRADE", N_("Upgr_ade"), GDK_KEY_u, GDK_SHIFT_MASK, - G_CALLBACK(unit_upgrade_callback), MGROUP_UNIT }, - { "UNIT_CONVERT", N_("C_onvert"), GDK_KEY_o, GDK_SHIFT_MASK, - G_CALLBACK(unit_convert_callback), MGROUP_UNIT }, - { "UNIT_DISBAND", N_("_Disband"), GDK_KEY_d, GDK_SHIFT_MASK, - G_CALLBACK(unit_disband_callback), MGROUP_UNIT }, - { "BUILD_CITY", N_("_Build City"), GDK_KEY_b, 0, - G_CALLBACK(build_city_callback), MGROUP_UNIT }, - { "AUTO_SETTLER", N_("_Auto Settler"), GDK_KEY_a, 0, - G_CALLBACK(auto_settle_callback), MGROUP_UNIT }, - { "BUILD_ROAD", N_("Build _Road"), GDK_KEY_r, 0, - G_CALLBACK(build_road_callback), MGROUP_UNIT }, - { "BUILD_IRRIGATION", N_("Build _Irrigation"), GDK_KEY_i, 0, - G_CALLBACK(build_irrigation_callback), MGROUP_UNIT }, - { "CULTIVATE", N_("Cultivate"), GDK_KEY_i, GDK_SHIFT_MASK, - G_CALLBACK(cultivate_callback), MGROUP_UNIT }, - { "BUILD_MINE", N_("Build _Mine"), GDK_KEY_m, 0, - G_CALLBACK(build_mine_callback), MGROUP_UNIT }, - { "PLANT", N_("Plant"), GDK_KEY_m, GDK_SHIFT_MASK, - G_CALLBACK(plant_callback), MGROUP_UNIT }, - { "CONNECT_ROAD", N_("Connect With Roa_d"), GDK_KEY_r, GDK_CONTROL_MASK, - G_CALLBACK(connect_road_callback), MGROUP_UNIT }, - { "CONNECT_RAIL", N_("Connect With Rai_l"), GDK_KEY_l, GDK_CONTROL_MASK, - G_CALLBACK(connect_rail_callback), MGROUP_UNIT }, - { "CONNECT_IRRIGATION", N_("Connect With Irri_gation"), - GDK_KEY_i, GDK_CONTROL_MASK, - G_CALLBACK(connect_irrigation_callback), MGROUP_UNIT }, - { "TRANSFORM_TERRAIN", N_("Transf_orm Terrain"), GDK_KEY_o, 0, - G_CALLBACK(transform_terrain_callback), MGROUP_UNIT }, - { "CLEAN_POLLUTION", N_("Clean _Pollution"), GDK_KEY_p, 0, - G_CALLBACK(clean_pollution_callback), MGROUP_UNIT }, - { "CLEAN_FALLOUT", N_("Clean _Nuclear Fallout"), GDK_KEY_n, 0, - G_CALLBACK(clean_fallout_callback), MGROUP_UNIT }, - { "FORTIFY", N_("Fortify"), GDK_KEY_f, 0, - G_CALLBACK(fortify_callback), MGROUP_UNIT }, - { "BUILD_FORTRESS", N_("Build Fortress"), GDK_KEY_f, GDK_SHIFT_MASK, - G_CALLBACK(build_fortress_callback), MGROUP_UNIT }, - { "BUILD_AIRBASE", N_("Build Airbase"), GDK_KEY_e, GDK_SHIFT_MASK, - G_CALLBACK(build_airbase_callback), MGROUP_UNIT }, - { "DO_PILLAGE", N_("_Pillage"), GDK_KEY_p, GDK_SHIFT_MASK, - G_CALLBACK(do_pillage_callback), MGROUP_UNIT }, - { "DIPLOMAT_ACTION", N_("_Do..."), GDK_KEY_d, 0, - G_CALLBACK(diplomat_action_callback), MGROUP_UNIT }, - - { "MENU_GOVERNMENT", N_("_Government"), 0, 0, - NULL, MGROUP_PLAYING }, - { "TAX_RATE", N_("_Tax Rates..."), GDK_KEY_t, GDK_CONTROL_MASK, - G_CALLBACK(tax_rate_callback), MGROUP_PLAYING }, - { "START_REVOLUTION", N_("_Revolution..."), - GDK_KEY_r, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - G_CALLBACK(government_callback), MGROUP_PLAYING }, - { NULL } -}; - -static struct menu_entry_info *menu_entry_info_find(const char *key); - -/************************************************************************//** - Item "CLEAR_CHAT_LOGS" callback. -****************************************************************************/ -static void clear_chat_logs_callback(GtkMenuItem *item, gpointer data) -{ - clear_output_window(); -} - -/************************************************************************//** - Item "SAVE_CHAT_LOGS" callback. -****************************************************************************/ -static void save_chat_logs_callback(GtkMenuItem *item, gpointer data) -{ - log_output_window(); -} - -/************************************************************************//** - Item "LOCAL_OPTIONS" callback. -****************************************************************************/ -static void local_options_callback(GtkMenuItem *item, gpointer data) -{ - option_dialog_popup(_("Set local options"), client_optset); -} - -/************************************************************************//** - Item "MESSAGE_OPTIONS" callback. -****************************************************************************/ -static void message_options_callback(GtkMenuItem *item, gpointer data) -{ - popup_messageopt_dialog(); -} - -/************************************************************************//** - Item "SERVER_OPTIONS" callback. -****************************************************************************/ -static void server_options_callback(GtkMenuItem *item, gpointer data) -{ - option_dialog_popup(_("Game Settings"), server_optset); -} - -/************************************************************************//** - Item "SAVE_OPTIONS" callback. -****************************************************************************/ -static void save_options_callback(GtkMenuItem *item, gpointer data) -{ - options_save(NULL); -} - -/************************************************************************//** - Item "RELOAD_TILESET" callback. -****************************************************************************/ -static void reload_tileset_callback(GtkMenuItem *item, gpointer data) -{ - tilespec_reread(NULL, TRUE, 1.0); -} - -/************************************************************************//** - Item "SAVE_GAME" callback. -****************************************************************************/ -static void save_game_callback(GtkMenuItem *item, gpointer data) -{ - send_save_game(NULL); -} - -/************************************************************************//** - Item "SAVE_GAME_AS" callback. -****************************************************************************/ -static void save_game_as_callback(GtkMenuItem *item, gpointer data) -{ - save_game_dialog_popup(); -} - -/************************************************************************//** - Item "SAVE_MAPIMG" callback. -****************************************************************************/ -static void save_mapimg_callback(GtkMenuItem *item, gpointer data) -{ - mapimg_client_save(NULL); -} - -/************************************************************************//** - Item "SAVE_MAPIMG_AS" callback. -****************************************************************************/ -static void save_mapimg_as_callback(GtkMenuItem *item, gpointer data) -{ - save_mapimg_dialog_popup(); -} - -/************************************************************************//** - This is the response callback for the dialog with the message: - Leaving a local game will end it! -****************************************************************************/ -static void leave_local_game_response(GtkWidget *dialog, gint response) -{ - gtk_widget_destroy(dialog); - if (response == GTK_RESPONSE_OK) { - /* It might be killed already */ - if (client.conn.used) { - /* It will also kill the server */ - disconnect_from_server(); - } - } -} - -/************************************************************************//** - Item "LEAVE" callback. -****************************************************************************/ -static void leave_callback(GtkMenuItem *item, gpointer data) -{ - if (is_server_running()) { - GtkWidget* dialog = - gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_WARNING, - GTK_BUTTONS_OK_CANCEL, - _("Leaving a local game will end it!")); - setup_dialog(dialog, toplevel); - gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); - g_signal_connect(dialog, "response", - G_CALLBACK(leave_local_game_response), NULL); - gtk_window_present(GTK_WINDOW(dialog)); - } else { - disconnect_from_server(); - } -} - -/************************************************************************//** - Item "QUIT" callback. -****************************************************************************/ -static void quit_callback(GtkMenuItem *item, gpointer data) -{ - popup_quit_dialog(); -} - -/************************************************************************//** - Item "FIND_CITY" callback. -****************************************************************************/ -static void find_city_callback(GtkMenuItem *item, gpointer data) -{ - popup_find_dialog(); -} - -/************************************************************************//** - Item "WORKLISTS" callback. -****************************************************************************/ -static void worklists_callback(GtkMenuItem *item, gpointer data) -{ - popup_worklists_report(); -} - -/************************************************************************//** - Item "MAP_VIEW" callback. -****************************************************************************/ -static void map_view_callback(GtkMenuItem *item, gpointer data) -{ - map_canvas_focus(); -} - -/************************************************************************//** - Item "REPORT_NATIONS" callback. -****************************************************************************/ -static void report_nations_callback(GtkMenuItem *item, gpointer data) -{ - popup_players_dialog(TRUE); -} - -/************************************************************************//** - Item "REPORT_WOW" callback. -****************************************************************************/ -static void report_wow_callback(GtkMenuItem *item, gpointer data) -{ - send_report_request(REPORT_WONDERS_OF_THE_WORLD); -} - -/************************************************************************//** - Item "REPORT_TOP_CITIES" callback. -****************************************************************************/ -static void report_top_cities_callback(GtkMenuItem *item, gpointer data) -{ - send_report_request(REPORT_TOP_5_CITIES); -} - -/************************************************************************//** - Item "REPORT_MESSAGES" callback. -****************************************************************************/ -static void report_messages_callback(GtkMenuItem *item, gpointer data) -{ - meswin_dialog_popup(TRUE); -} - -/************************************************************************//** - Item "CLIENT_LUA_SCRIPT" callback. -****************************************************************************/ -static void client_lua_script_callback(GtkMenuItem *item, gpointer data) -{ - luaconsole_dialog_popup(TRUE); -} - -/************************************************************************//** - Item "REPORT_DEMOGRAPHIC" callback. -****************************************************************************/ -static void report_demographic_callback(GtkMenuItem *item, gpointer data) -{ - send_report_request(REPORT_DEMOGRAPHIC); -} - -/************************************************************************//** - Item "REPORT_ACHIEVEMENTS" callback. -****************************************************************************/ -static void report_achievements_callback(GtkMenuItem *item, gpointer data) -{ - send_report_request(REPORT_ACHIEVEMENTS); -} - -/************************************************************************//** - Item "HELP_LANGUAGE" callback. -****************************************************************************/ -static void help_language_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_LANGUAGES_ITEM); -} - -/************************************************************************//** - Item "HELP_POLICIES" callback. -****************************************************************************/ -static void help_policies_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_MULTIPLIER_ITEM); -} - -/************************************************************************//** - Item "HELP_CONNECTING" callback. -****************************************************************************/ -static void help_connecting_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_CONNECTING_ITEM); -} - -/************************************************************************//** - Item "HELP_CONTROLS" callback. -****************************************************************************/ -static void help_controls_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_CONTROLS_ITEM); -} - -/************************************************************************//** - Item "HELP_CHATLINE" callback. -****************************************************************************/ -static void help_chatline_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_CHATLINE_ITEM); -} - -/************************************************************************//** - Item "HELP_WORKLIST_EDITOR" callback. -****************************************************************************/ -static void help_worklist_editor_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_WORKLIST_EDITOR_ITEM); -} - -/************************************************************************//** - Item "HELP_CMA" callback. -****************************************************************************/ -static void help_cma_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_CMA_ITEM); -} - -/************************************************************************//** - Item "HELP_OVERVIEW" callback. -****************************************************************************/ -static void help_overview_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_OVERVIEW_ITEM); -} - -/************************************************************************//** - Item "HELP_PLAYING" callback. -****************************************************************************/ -static void help_playing_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_PLAYING_ITEM); -} - -/************************************************************************//** - Item "HELP_RULESET" callback. -****************************************************************************/ -static void help_ruleset_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_RULESET_ITEM); -} - -/************************************************************************//** - Item "HELP_TILESET" callback. -****************************************************************************/ -static void help_tileset_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_TILESET_ITEM); -} - -/************************************************************************//** - Item "HELP_ECONOMY" callback. -****************************************************************************/ -static void help_economy_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_ECONOMY_ITEM); -} - -/************************************************************************//** - Item "HELP_CITIES" callback. -****************************************************************************/ -static void help_cities_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_CITIES_ITEM); -} - -/************************************************************************//** - Item "HELP_IMPROVEMENTS" callback. -****************************************************************************/ -static void help_improvements_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_IMPROVEMENTS_ITEM); -} - -/************************************************************************//** - Item "HELP_UNITS" callback. -****************************************************************************/ -static void help_units_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_UNITS_ITEM); -} - -/************************************************************************//** - Item "HELP_COMBAT" callback. -****************************************************************************/ -static void help_combat_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_COMBAT_ITEM); -} - -/************************************************************************//** - Item "HELP_ZOC" callback. -****************************************************************************/ -static void help_zoc_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_ZOC_ITEM); -} - -/************************************************************************//** - Item "HELP_TECH" callback. -****************************************************************************/ -static void help_tech_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_TECHS_ITEM); -} - -/************************************************************************//** - Item "HELP_TERRAIN" callback. -****************************************************************************/ -static void help_terrain_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_TERRAIN_ITEM); -} - -/************************************************************************//** - Item "HELP_WONDERS" callback. -****************************************************************************/ -static void help_wonders_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_WONDERS_ITEM); -} - -/************************************************************************//** - Item "HELP_GOVERNMENT" callback. -****************************************************************************/ -static void help_government_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_GOVERNMENT_ITEM); -} - -/************************************************************************//** - Item "HELP_DIPLOMACY" callback. -****************************************************************************/ -static void help_diplomacy_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_DIPLOMACY_ITEM); -} - -/************************************************************************//** - Item "HELP_SPACE_RACE" callback. -****************************************************************************/ -static void help_space_race_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_SPACE_RACE_ITEM); -} - -/************************************************************************//** - Item "HELP_NATIONS" callback. -****************************************************************************/ -static void help_nations_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_NATIONS_ITEM); -} - -/************************************************************************//** - Item "HELP_COPYING" callback. -****************************************************************************/ -static void help_copying_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_COPYING_ITEM); -} - -/************************************************************************//** - Item "HELP_ABOUT" callback. -****************************************************************************/ -static void help_about_callback(GtkMenuItem *item, gpointer data) -{ - popup_help_dialog_string(HELP_ABOUT_ITEM); -} - -/************************************************************************//** - Item "SAVE_OPTIONS_ON_EXIT" callback. -****************************************************************************/ -static void save_options_on_exit_callback(GtkCheckMenuItem *item, - gpointer data) -{ - gui_options.save_options_on_exit = gtk_check_menu_item_get_active(item); -} - -/************************************************************************//** - Item "EDIT_MODE" callback. -****************************************************************************/ -static void edit_mode_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (game.info.is_edit_mode ^ gtk_check_menu_item_get_active(item)) { - key_editor_toggle(); - /* Unreachbale techs in reqtree on/off */ - science_report_dialog_popdown(); - } -} - -/************************************************************************//** - Item "SHOW_CITY_OUTLINES" callback. -****************************************************************************/ -static void show_city_outlines_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_city_outlines ^ gtk_check_menu_item_get_active(item)) { - key_city_outlines_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_CITY_OUTPUT" callback. -****************************************************************************/ -static void show_city_output_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_city_output ^ gtk_check_menu_item_get_active(item)) { - key_city_output_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_MAP_GRID" callback. -****************************************************************************/ -static void show_map_grid_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_map_grid ^ gtk_check_menu_item_get_active(item)) { - key_map_grid_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_NATIONAL_BORDERS" callback. -****************************************************************************/ -static void show_national_borders_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_borders ^ gtk_check_menu_item_get_active(item)) { - key_map_borders_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_NATIVE_TILES" callback. -****************************************************************************/ -static void show_native_tiles_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_native ^ gtk_check_menu_item_get_active(item)) { - key_map_native_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_CITY_FULL_BAR" callback. -****************************************************************************/ -static void show_city_full_bar_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_full_citybar ^ gtk_check_menu_item_get_active(item)) { - key_city_full_bar_toggle(); - view_menu_update_sensitivity(); - } -} - -/************************************************************************//** - Item "SHOW_CITY_NAMES" callback. -****************************************************************************/ -static void show_city_names_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_city_names ^ gtk_check_menu_item_get_active(item)) { - key_city_names_toggle(); - view_menu_update_sensitivity(); - } -} - -/************************************************************************//** - Item "SHOW_CITY_GROWTH" callback. -****************************************************************************/ -static void show_city_growth_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_city_growth ^ gtk_check_menu_item_get_active(item)) { - key_city_growth_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_CITY_PRODUCTIONS" callback. -****************************************************************************/ -static void show_city_productions_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_city_productions ^ gtk_check_menu_item_get_active(item)) { - key_city_productions_toggle(); - view_menu_update_sensitivity(); - } -} - -/************************************************************************//** - Item "SHOW_CITY_BUY_COST" callback. -****************************************************************************/ -static void show_city_buy_cost_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_city_buycost ^ gtk_check_menu_item_get_active(item)) { - key_city_buycost_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_CITY_TRADE_ROUTES" callback. -****************************************************************************/ -static void show_city_trade_routes_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_city_trade_routes ^ gtk_check_menu_item_get_active(item)) { - key_city_trade_routes_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_TERRAIN" callback. -****************************************************************************/ -static void show_terrain_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_terrain ^ gtk_check_menu_item_get_active(item)) { - key_terrain_toggle(); - view_menu_update_sensitivity(); - } -} - -/************************************************************************//** - Item "SHOW_COASTLINE" callback. -****************************************************************************/ -static void show_coastline_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_coastline ^ gtk_check_menu_item_get_active(item)) { - key_coastline_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_ROAD_RAILS" callback. -****************************************************************************/ -static void show_road_rails_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_roads_rails ^ gtk_check_menu_item_get_active(item)) { - key_roads_rails_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_IRRIGATION" callback. -****************************************************************************/ -static void show_irrigation_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_irrigation ^ gtk_check_menu_item_get_active(item)) { - key_irrigation_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_MINE" callback. -****************************************************************************/ -static void show_mine_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_mines ^ gtk_check_menu_item_get_active(item)) { - key_mines_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_BASES" callback. -****************************************************************************/ -static void show_bases_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_fortress_airbase ^ gtk_check_menu_item_get_active(item)) { - key_bases_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_RESOURCES" callback. -****************************************************************************/ -static void show_resources_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_specials ^ gtk_check_menu_item_get_active(item)) { - key_resources_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_HUTS" callback. -****************************************************************************/ -static void show_huts_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_huts ^ gtk_check_menu_item_get_active(item)) { - key_huts_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_POLLUTION" callback. -****************************************************************************/ -static void show_pollution_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_pollution ^ gtk_check_menu_item_get_active(item)) { - key_pollution_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_CITIES" callback. -****************************************************************************/ -static void show_cities_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_cities ^ gtk_check_menu_item_get_active(item)) { - key_cities_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_UNITS" callback. -****************************************************************************/ -static void show_units_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_units ^ gtk_check_menu_item_get_active(item)) { - key_units_toggle(); - view_menu_update_sensitivity(); - } -} - -/************************************************************************//** - Item "SHOW_UNIT_SOLID_BG" callback. -****************************************************************************/ -static void show_unit_solid_bg_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.solid_color_behind_units ^ gtk_check_menu_item_get_active(item)) { - key_unit_solid_bg_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_UNIT_SHIELDS" callback. -****************************************************************************/ -static void show_unit_shields_callback(GtkCheckMenuItem *item, - gpointer data) -{ - if (gui_options.draw_unit_shields ^ gtk_check_menu_item_get_active(item)) { - key_unit_shields_toggle(); - } -} - -/************************************************************************//** - Item "SHOW_FOCUS_UNIT" callback. -****************************************************************************/ -static void show_focus_unit_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_focus_unit ^ gtk_check_menu_item_get_active(item)) { - key_focus_unit_toggle(); - view_menu_update_sensitivity(); - } -} - -/************************************************************************//** - Item "SHOW_FOG_OF_WAR" callback. -****************************************************************************/ -static void show_fog_of_war_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (gui_options.draw_fog_of_war ^ gtk_check_menu_item_get_active(item)) { - key_fog_of_war_toggle(); - view_menu_update_sensitivity(); - } -} - -/************************************************************************//** - Item "FULL_SCREEN" callback. -****************************************************************************/ -static void full_screen_callback(GtkCheckMenuItem *item, gpointer data) -{ - if (GUI_GTK_OPTION(fullscreen) ^ gtk_check_menu_item_get_active(item)) { - GUI_GTK_OPTION(fullscreen) ^= 1; - - if (GUI_GTK_OPTION(fullscreen)) { - gtk_window_fullscreen(GTK_WINDOW(toplevel)); - } else { - gtk_window_unfullscreen(GTK_WINDOW(toplevel)); - } - } -} - -/************************************************************************//** - Item "RECALC_BORDERS" callback. -****************************************************************************/ -static void recalc_borders_callback(GtkMenuItem *item, gpointer data) -{ - key_editor_recalculate_borders(); -} - -/************************************************************************//** - Item "TOGGLE_FOG" callback. -****************************************************************************/ -static void toggle_fog_callback(GtkMenuItem *item, gpointer data) -{ - key_editor_toggle_fogofwar(); -} - -/************************************************************************//** - Item "SCENARIO_PROPERTIES" callback. -****************************************************************************/ -static void scenario_properties_callback(GtkMenuItem *item, gpointer data) -{ - struct property_editor *pe; - - pe = editprop_get_property_editor(); - property_editor_reload(pe, OBJTYPE_GAME); - property_editor_popup(pe, OBJTYPE_GAME); -} - -/************************************************************************//** - Item "SAVE_SCENARIO" callback. -****************************************************************************/ -static void save_scenario_callback(GtkMenuItem *item, gpointer data) -{ - save_scenario_dialog_popup(); -} - -/************************************************************************//** - Item "SELECT_SINGLE" callback. -****************************************************************************/ -static void select_single_callback(GtkMenuItem *item, gpointer data) -{ - request_unit_select(get_units_in_focus(), SELTYPE_SINGLE, SELLOC_TILE); -} - -/************************************************************************//** - Item "SELECT_ALL_ON_TILE" callback. -****************************************************************************/ -static void select_all_on_tile_callback(GtkMenuItem *item, gpointer data) -{ - request_unit_select(get_units_in_focus(), SELTYPE_ALL, SELLOC_TILE); -} - -/************************************************************************//** - Item "SELECT_SAME_TYPE_TILE" callback. -****************************************************************************/ -static void select_same_type_tile_callback(GtkMenuItem *item, gpointer data) -{ - request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_TILE); -} - -/************************************************************************//** - Item "SELECT_SAME_TYPE_CONT" callback. -****************************************************************************/ -static void select_same_type_cont_callback(GtkMenuItem *item, gpointer data) -{ - request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_CONT); -} - -/************************************************************************//** - Item "SELECT_SAME_TYPE" callback. -****************************************************************************/ -static void select_same_type_callback(GtkMenuItem *item, gpointer data) -{ - request_unit_select(get_units_in_focus(), SELTYPE_SAME, SELLOC_WORLD); -} - -/************************************************************************//** - Open unit selection dialog. -****************************************************************************/ -static void select_dialog_callback(GtkMenuItem *item, gpointer data) -{ - unit_select_dialog_popup(NULL); -} - -/************************************************************************//** - Item "UNIT_WAIT" callback. -****************************************************************************/ -static void unit_wait_callback(GtkMenuItem *item, gpointer data) -{ - key_unit_wait(); -} - -/************************************************************************//** - Item "UNIT_DONE" callback. -****************************************************************************/ -static void unit_done_callback(GtkMenuItem *item, gpointer data) -{ - key_unit_done(); -} - -/************************************************************************//** - Item "UNIT_GOTO" callback. -****************************************************************************/ -static void unit_goto_callback(GtkMenuItem *item, gpointer data) -{ - key_unit_goto(); -} - -/************************************************************************//** - Activate the goto system with an action to perform once there. -****************************************************************************/ -static void unit_goto_and_callback(GtkMenuItem *item, gpointer data) -{ - int sub_target = NO_TARGET; - struct action *paction = g_object_get_data(G_OBJECT(item), "end_action"); - - fc_assert_ret(paction != NULL); - - switch (action_get_sub_target_kind(paction)) { - case ASTK_BUILDING: - { - struct impr_type *pbuilding = g_object_get_data(G_OBJECT(item), - "end_building"); - fc_assert_ret(pbuilding != NULL); - sub_target = improvement_number(pbuilding); - } - break; - case ASTK_TECH: - { - struct advance *ptech = g_object_get_data(G_OBJECT(item), - "end_tech"); - fc_assert_ret(ptech != NULL); - sub_target = advance_number(ptech); - } - break; - case ASTK_EXTRA: - case ASTK_EXTRA_NOT_THERE: - { - struct extra_type *pextra = g_object_get_data(G_OBJECT(item), - "end_extra"); - fc_assert_ret(pextra != NULL); - sub_target = extra_number(pextra); - } - break; - case ASTK_NONE: - sub_target = NO_TARGET; - break; - case ASTK_COUNT: - /* Should not exits */ - fc_assert(action_get_sub_target_kind(paction) != ASTK_COUNT); - break; - } - - request_unit_goto(ORDER_PERFORM_ACTION, paction->id, sub_target); -} - -/************************************************************************//** - Item "UNIT_GOTO_CITY" callback. -****************************************************************************/ -static void unit_goto_city_callback(GtkMenuItem *item, gpointer data) -{ - if (get_num_units_in_focus() > 0) { - popup_goto_dialog(); - } -} - -/************************************************************************//** - Item "UNIT_RETURN" callback. -****************************************************************************/ -static void unit_return_callback(GtkMenuItem *item, gpointer data) -{ - unit_list_iterate(get_units_in_focus(), punit) { - request_unit_return(punit); - } unit_list_iterate_end; -} - -/************************************************************************//** - Item "UNIT_EXPLORE" callback. -****************************************************************************/ -static void unit_explore_callback(GtkMenuItem *item, gpointer data) -{ - key_unit_auto_explore(); -} - -/************************************************************************//** - Item "UNIT_PATROL" callback. -****************************************************************************/ -static void unit_patrol_callback(GtkMenuItem *item, gpointer data) -{ - key_unit_patrol(); -} - -/************************************************************************//** - Item "UNIT_SENTRY" callback. -****************************************************************************/ -static void unit_sentry_callback(GtkMenuItem *item, gpointer data) -{ - key_unit_sentry(); -} - -/************************************************************************//** - Item "UNIT_UNSENTRY" callback. -****************************************************************************/ -static void unit_unsentry_callback(GtkMenuItem *item, gpointer data) -{ - key_unit_wakeup_others(); -} - -/************************************************************************//** - Item "UNIT_LOAD" callback. -****************************************************************************/ -static void unit_load_callback(GtkMenuItem *item, gpointer data) -{ - unit_list_iterate(get_units_in_focus(), punit) { - request_transport(punit, unit_tile(punit)); - } unit_list_iterate_end; -} - -/************************************************************************//** - Item "UNIT_UNLOAD" callback. -****************************************************************************/ -static void unit_unload_callback(GtkMenuItem *item, gpointer data) -{ - unit_list_iterate(get_units_in_focus(), punit) { - request_unit_unload(punit); - } unit_list_iterate_end; -} - -/************************************************************************//** - Item "UNIT_UNLOAD_TRANSPORTER" callback. -****************************************************************************/ -static void unit_unload_transporter_callback(GtkMenuItem *item, - gpointer data) -{ - key_unit_unload_all(); -} - -/************************************************************************//** - Item "UNIT_HOMECITY" callback. -****************************************************************************/ -static void unit_homecity_callback(GtkMenuItem *item, gpointer data) -{ - key_unit_homecity(); -} - -/************************************************************************//** - Item "UNIT_UPGRADE" callback. -****************************************************************************/ -static void unit_upgrade_callback(GtkMenuItem *item, gpointer data) -{ - popup_upgrade_dialog(get_units_in_focus()); -} - -/************************************************************************//** - Item "UNIT_CONVERT" callback. -****************************************************************************/ -static void unit_convert_callback(GtkMenuItem *item, gpointer data) -{ - key_unit_convert(); -} - -/************************************************************************//** - Item "UNIT_DISBAND" callback. -****************************************************************************/ -static void unit_disband_callback(GtkMenuItem *item, gpointer data) -{ - popup_disband_dialog(get_units_in_focus()); -} - -/************************************************************************//** - Item "BUILD_CITY" callback. -****************************************************************************/ -static void build_city_callback(GtkMenuItem *item, gpointer data) -{ - unit_list_iterate(get_units_in_focus(), punit) { - /* FIXME: this can provide different items for different units... - * not good! */ - /* Enable the button for adding to a city in all cases, so we - get an eventual error message from the server if we try. */ - if (unit_can_add_or_build_city(punit)) { - request_unit_build_city(punit); - } else if (utype_can_do_action(unit_type_get(punit), - ACTION_HELP_WONDER)) { - request_unit_caravan_action(punit, ACTION_HELP_WONDER); - } - } unit_list_iterate_end; -} - -/************************************************************************//** - Action "AUTO_SETTLE" callback. -****************************************************************************/ -static void auto_settle_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_auto_settle(); -} - -/************************************************************************//** - Action "BUILD_ROAD" callback. -****************************************************************************/ -static void build_road_callback(GtkMenuItem *action, gpointer data) -{ - unit_list_iterate(get_units_in_focus(), punit) { - /* FIXME: this can provide different actions for different units... - * not good! */ - struct extra_type *tgt = next_extra_for_tile(unit_tile(punit), - EC_ROAD, - unit_owner(punit), - punit); - bool building_road = FALSE; - - if (tgt != NULL - && can_unit_do_activity_targeted(punit, ACTIVITY_GEN_ROAD, tgt)) { - request_new_unit_activity_targeted(punit, ACTIVITY_GEN_ROAD, tgt); - building_road = TRUE; - } - - if (!building_road && unit_can_est_trade_route_here(punit)) { - request_unit_caravan_action(punit, ACTION_TRADE_ROUTE); - } - } unit_list_iterate_end; -} - -/************************************************************************//** - Action "BUILD_IRRIGATION" callback. -****************************************************************************/ -static void build_irrigation_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_irrigate(); -} - -/************************************************************************//** - Action "CULTIVATE" callback. -****************************************************************************/ -static void cultivate_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_cultivate(); -} - -/************************************************************************//** - Action "BUILD_MINE" callback. -****************************************************************************/ -static void build_mine_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_mine(); -} - -/************************************************************************//** - Action "PLANT" callback. -****************************************************************************/ -static void plant_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_plant(); -} - -/************************************************************************//** - Action "CONNECT_ROAD" callback. -****************************************************************************/ -static void connect_road_callback(GtkMenuItem *action, gpointer data) -{ - struct road_type *proad = road_by_compat_special(ROCO_ROAD); - - if (proad != NULL) { - struct extra_type *tgt; - - tgt = road_extra_get(proad); - - key_unit_connect(ACTIVITY_GEN_ROAD, tgt); - } -} - -/************************************************************************//** - Action "CONNECT_RAIL" callback. -****************************************************************************/ -static void connect_rail_callback(GtkMenuItem *action, gpointer data) -{ - struct road_type *prail = road_by_compat_special(ROCO_RAILROAD); - - if (prail != NULL) { - struct extra_type *tgt; - - tgt = road_extra_get(prail); - - key_unit_connect(ACTIVITY_GEN_ROAD, tgt); - } -} - -/************************************************************************//** - Action "CONNECT_IRRIGATION" callback. -****************************************************************************/ -static void connect_irrigation_callback(GtkMenuItem *action, gpointer data) -{ - struct extra_type_list *extras = extra_type_list_by_cause(EC_IRRIGATION); - - if (extra_type_list_size(extras) > 0) { - struct extra_type *pextra; - - pextra = extra_type_list_get(extra_type_list_by_cause(EC_IRRIGATION), 0); - - key_unit_connect(ACTIVITY_IRRIGATE, pextra); - } -} - -/************************************************************************//** - Action "TRANSFORM_TERRAIN" callback. -****************************************************************************/ -static void transform_terrain_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_transform(); -} - -/************************************************************************//** - Action "CLEAN_POLLUTION" callback. -****************************************************************************/ -static void clean_pollution_callback(GtkMenuItem *action, gpointer data) -{ - unit_list_iterate(get_units_in_focus(), punit) { - /* FIXME: this can provide different actions for different units... - * not good! */ - struct extra_type *pextra; - - pextra = prev_extra_in_tile(unit_tile(punit), ERM_CLEANPOLLUTION, - unit_owner(punit), punit); - - if (pextra != NULL) { - request_new_unit_activity_targeted(punit, ACTIVITY_POLLUTION, pextra); - } else if (can_unit_paradrop(punit)) { - /* FIXME: This is getting worse, we use a key_unit_*() function - * which assign the order for all units! Very bad! */ - key_unit_paradrop(); - } - } unit_list_iterate_end; -} - -/************************************************************************//** - Action "CLEAN_FALLOUT" callback. -****************************************************************************/ -static void clean_fallout_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_fallout(); -} - -/************************************************************************//** - Action "BUILD_FORTRESS" callback. -****************************************************************************/ -static void build_fortress_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_fortress(); -} - -/************************************************************************//** - Action "FORTIFY" callback. -****************************************************************************/ -static void fortify_callback(GtkMenuItem *action, gpointer data) -{ - unit_list_iterate(get_units_in_focus(), punit) { - request_unit_fortify(punit); - } unit_list_iterate_end; -} - -/************************************************************************//** - Action "BUILD_AIRBASE" callback. -****************************************************************************/ -static void build_airbase_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_airbase(); -} - -/************************************************************************//** - Action "DO_PILLAGE" callback. -****************************************************************************/ -static void do_pillage_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_pillage(); -} - -/************************************************************************//** - Action "DIPLOMAT_ACTION" callback. -****************************************************************************/ -static void diplomat_action_callback(GtkMenuItem *action, gpointer data) -{ - key_unit_action_select_tgt(); -} - -/************************************************************************//** - Action "TAX_RATE" callback. -****************************************************************************/ -static void tax_rate_callback(GtkMenuItem *action, gpointer data) -{ - popup_rates_dialog(); -} - -/************************************************************************//** - Action "MULTIPLIERS" callback. -****************************************************************************/ -static void multiplier_callback(GtkMenuItem *action, gpointer data) -{ - popup_multiplier_dialog(); -} - -/************************************************************************//** - The player has chosen a government from the menu. -****************************************************************************/ -static void government_callback(GtkMenuItem *item, gpointer data) -{ - popup_revolution_dialog((struct government *) data); -} - -/************************************************************************//** - The player has chosen a base to build from the menu. -****************************************************************************/ -static void base_callback(GtkMenuItem *item, gpointer data) -{ - struct extra_type *pextra = data; - - unit_list_iterate(get_units_in_focus(), punit) { - request_new_unit_activity_targeted(punit, ACTIVITY_BASE, pextra); - } unit_list_iterate_end; -} - -/************************************************************************//** - The player has chosen a road to build from the menu. -****************************************************************************/ -static void road_callback(GtkMenuItem *item, gpointer data) -{ - struct extra_type *pextra = data; - - unit_list_iterate(get_units_in_focus(), punit) { - request_new_unit_activity_targeted(punit, ACTIVITY_GEN_ROAD, - pextra); - } unit_list_iterate_end; -} - -/************************************************************************//** - Action "CENTER_VIEW" callback. -****************************************************************************/ -static void center_view_callback(GtkMenuItem *action, gpointer data) -{ - center_on_unit(); -} - -/************************************************************************//** - Action "REPORT_UNITS" callback. -****************************************************************************/ -static void report_units_callback(GtkMenuItem *action, gpointer data) -{ - units_report_dialog_popup(TRUE); -} - -/************************************************************************//** - Action "REPORT_CITIES" callback. -****************************************************************************/ -static void report_cities_callback(GtkMenuItem *action, gpointer data) -{ - city_report_dialog_popup(TRUE); -} - -/************************************************************************//** - Action "REPORT_ECONOMY" callback. -****************************************************************************/ -static void report_economy_callback(GtkMenuItem *action, gpointer data) -{ - economy_report_dialog_popup(TRUE); -} - -/************************************************************************//** - Action "REPORT_RESEARCH" callback. -****************************************************************************/ -static void report_research_callback(GtkMenuItem *action, gpointer data) -{ - science_report_dialog_popup(TRUE); -} - -/************************************************************************//** - Action "REPORT_SPACESHIP" callback. -****************************************************************************/ -static void report_spaceship_callback(GtkMenuItem *action, gpointer data) -{ - if (NULL != client.conn.playing) { - popup_spaceship_dialog(client.conn.playing); - } -} - -/************************************************************************//** - Set name of the menu item. -****************************************************************************/ -static void menu_entry_init(GtkBuildable *item) -{ - const char *key = gtk_buildable_get_name(item); - struct menu_entry_info *info = menu_entry_info_find(key); - - if (info != NULL) { - gtk_menu_item_set_label(GTK_MENU_ITEM(item), - Q_(info->name)); - gtk_menu_item_set_use_underline(GTK_MENU_ITEM(item), TRUE); - if (info->cb != NULL) { - g_signal_connect(item, "activate", info->cb, NULL); - } - - if (info->accel != 0) { - const char *path = gtk_menu_item_get_accel_path(GTK_MENU_ITEM(item)); - - if (path == NULL) { - char buf[512]; - - fc_snprintf(buf, sizeof(buf), "/%s", key); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), buf); - path = buf; /* Not NULL, but not usable either outside this block */ - } - - if (path != NULL) { - gtk_accel_map_add_entry(gtk_menu_item_get_accel_path(GTK_MENU_ITEM(item)), - info->accel, info->accel_mod); - } - } - - return; - } - - /* temporary naming solution */ - gtk_menu_item_set_label(GTK_MENU_ITEM(item), key); -} - -/************************************************************************//** - Returns the name of the file readable by the GtkUIManager. -****************************************************************************/ -static const gchar *get_ui_filename(void) -{ - static char filename[256]; - const char *name; - - if ((name = getenv("FREECIV_MENUS")) - || (name = fileinfoname(get_data_dirs(), "gtk3_menus.xml"))) { - sz_strlcpy(filename, name); - } else { - log_error("Gtk menus: file definition not found"); - filename[0] = '\0'; - } - - log_verbose("ui menu file is \"%s\".", filename); - return filename; -} - -/************************************************************************//** - Creates the menu bar. -****************************************************************************/ -GtkWidget *setup_menus(GtkWidget *window) -{ - GtkWidget *menubar = NULL; - GError *error = NULL; - - ui_builder = gtk_builder_new(); - if (!gtk_builder_add_from_file(ui_builder, get_ui_filename(), &error)) { - log_error("Gtk menus: %s", error->message); - g_error_free(error); - } else { - GSList *entries; - GSList *next; - - entries = gtk_builder_get_objects(ui_builder); - next = entries; - - while (next != NULL) { - GObject *obj = next->data; - - if (GTK_IS_MENU_ITEM(obj)) { - if (!GTK_IS_SEPARATOR_MENU_ITEM(obj)) { - menu_entry_init(GTK_BUILDABLE(obj)); - } - } else if (GTK_IS_MENU(obj)) { - const char *key = gtk_buildable_get_name(GTK_BUILDABLE(obj)); - - if (key[0] == '<') { - GtkAccelGroup *ac_group = gtk_menu_get_accel_group(GTK_MENU(obj)); - - if (ac_group == NULL) { - ac_group = gtk_accel_group_new(); - gtk_menu_set_accel_group(GTK_MENU(obj), ac_group); - } - - gtk_window_add_accel_group(GTK_WINDOW(window), ac_group); - - gtk_menu_set_accel_path(GTK_MENU(obj), key); - } - } - - next = next->next; - } - - g_slist_free(entries); - - menubar = GTK_WIDGET(gtk_builder_get_object(ui_builder, "MENU")); - gtk_widget_set_visible(menubar, TRUE); - gtk_widget_show_all(menubar); - } - -#ifndef FREECIV_DEBUG - menu_entry_set_visible("RELOAD_TILESET", FALSE, FALSE); -#endif /* FREECIV_DEBUG */ - - return menubar; -} - -/************************************************************************//** - Find menu entry constrution data -****************************************************************************/ -static struct menu_entry_info *menu_entry_info_find(const char *key) -{ - int i; - - for (i = 0; menu_entries[i].key != NULL; i++) { - if (!strcmp(key, menu_entries[i].key)) { - return &(menu_entries[i]); - } - } - - return NULL; -} - -/************************************************************************//** - Sets an menu entry sensitive. -****************************************************************************/ -static void menu_entry_set_active(const char *key, - gboolean is_active) -{ - GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM(gtk_builder_get_object(ui_builder, key)); - - if (item != NULL) { - gtk_check_menu_item_set_active(item, is_active); - } -} - -/************************************************************************//** - Sets sensitivity of an menu entry. -****************************************************************************/ -static void menu_entry_set_sensitive(const char *key, - gboolean is_sensitive) -{ - GtkWidget *item = GTK_WIDGET(gtk_builder_get_object(ui_builder, key)); - - if (item != NULL) { - gtk_widget_set_sensitive(item, is_sensitive); - } -} - -/************************************************************************//** - Set sensitivity of all entries in the group. -****************************************************************************/ -static void menu_entry_group_set_sensitive(enum menu_entry_grouping group, - gboolean is_sensitive) -{ - int i; - - for (i = 0; menu_entries[i].key != NULL; i++) { - if (menu_entries[i].grouping == group || group == MGROUP_ALL) { - menu_entry_set_sensitive(menu_entries[i].key, is_sensitive); - } - } -} - -/************************************************************************//** - Sets an action visible. -****************************************************************************/ -#ifndef FREECIV_DEBUG -static void menu_entry_set_visible(const char *key, - gboolean is_visible, - gboolean is_sensitive) -{ - GtkWidget *item = GTK_WIDGET(gtk_builder_get_object(ui_builder, key)); - - if (item != NULL) { - gtk_widget_set_visible(item, is_visible); - gtk_widget_set_sensitive(item, is_sensitive); - } -} -#endif /* FREECIV_DEBUG */ - -/************************************************************************//** - Renames an action. -****************************************************************************/ -static void menus_rename(const char *key, - const gchar *new_label) -{ - GtkWidget *item = GTK_WIDGET(gtk_builder_get_object(ui_builder, key)); - - if (item != NULL) { - gtk_menu_item_set_label(GTK_MENU_ITEM(item), new_label); - } -} - -/************************************************************************//** - Find the child menu of an action. -****************************************************************************/ -static GtkMenu *find_menu(const char *key) -{ - return GTK_MENU(gtk_builder_get_object(ui_builder, key)); -} - -/************************************************************************//** - Update the sensitivity of the items in the view menu. -****************************************************************************/ -static void view_menu_update_sensitivity(void) -{ - /* The "full" city bar (i.e. the new way of drawing the - * city name), can draw the city growth even without drawing - * the city name. But the old method cannot. */ - if (gui_options.draw_full_citybar) { - menu_entry_set_sensitive("SHOW_CITY_GROWTH", TRUE); - menu_entry_set_sensitive("SHOW_CITY_TRADE_ROUTES", TRUE); - } else { - menu_entry_set_sensitive("SHOW_CITY_GROWTH", gui_options.draw_city_names); - menu_entry_set_sensitive("SHOW_CITY_TRADE_ROUTES", - gui_options.draw_city_names); - } - - menu_entry_set_sensitive("SHOW_CITY_BUY_COST", - gui_options.draw_city_productions); - menu_entry_set_sensitive("SHOW_COASTLINE", !gui_options.draw_terrain); - menu_entry_set_sensitive("SHOW_UNIT_SOLID_BG", - gui_options.draw_units || gui_options.draw_focus_unit); - menu_entry_set_sensitive("SHOW_UNIT_SHIELDS", - gui_options.draw_units || gui_options.draw_focus_unit); - menu_entry_set_sensitive("SHOW_FOCUS_UNIT", !gui_options.draw_units); -} - -/************************************************************************//** - Return the text for the tile, changed by the activity. - - Should only be called for irrigation, mining, or transformation, and - only when the activity changes the base terrain type. -****************************************************************************/ -static const char *get_tile_change_menu_text(struct tile *ptile, - enum unit_activity activity) -{ - struct tile *newtile = tile_virtual_new(ptile); - const char *text; - - tile_apply_activity(newtile, activity, NULL); - text = tile_get_info_text(newtile, FALSE, 0); - tile_virtual_destroy(newtile); - return text; -} - -/************************************************************************//** - Updates the menus. -****************************************************************************/ -void real_menus_update(void) -{ - struct unit_list *punits = NULL; - bool units_all_same_tile = TRUE, units_all_same_type = TRUE; - GtkMenu *menu; - char acttext[128], irrtext[128], mintext[128], transtext[128]; - char cultext[128], plantext[128]; - struct terrain *pterrain; - bool conn_possible; - struct road_type *proad; - struct extra_type_list *extras; - - if (ui_builder == NULL || !can_client_change_view()) { - return; - } - - if (get_num_units_in_focus() > 0) { - const struct tile *ptile = NULL; - const struct unit_type *ptype = NULL; - punits = get_units_in_focus(); - unit_list_iterate(punits, punit) { - fc_assert((ptile==NULL) == (ptype==NULL)); - if (ptile || ptype) { - if (unit_tile(punit) != ptile) { - units_all_same_tile = FALSE; - } - if (unit_type_get(punit) != ptype) { - units_all_same_type = FALSE; - } - } else { - ptile = unit_tile(punit); - ptype = unit_type_get(punit); - } - } unit_list_iterate_end; - } - - menu_entry_group_set_sensitive(MGROUP_EDIT, editor_is_active()); - menu_entry_group_set_sensitive(MGROUP_PLAYING, can_client_issue_orders() - && !editor_is_active()); - menu_entry_group_set_sensitive(MGROUP_UNIT, can_client_issue_orders() - && !editor_is_active() && punits != NULL); - - menu_entry_set_active("EDIT_MODE", game.info.is_edit_mode); - menu_entry_set_sensitive("EDIT_MODE", - can_conn_enable_editing(&client.conn)); - editgui_refresh(); - - { - char road_buf[500]; - - proad = road_by_compat_special(ROCO_ROAD); - if (proad != NULL) { - /* TRANS: Connect with some road type (Road/Railroad) */ - snprintf(road_buf, sizeof(road_buf), _("Connect With %s"), - extra_name_translation(road_extra_get(proad))); - menus_rename("CONNECT_ROAD", road_buf); - } - - proad = road_by_compat_special(ROCO_RAILROAD); - if (proad != NULL) { - snprintf(road_buf, sizeof(road_buf), _("Connect With %s"), - extra_name_translation(road_extra_get(proad))); - menus_rename("CONNECT_RAIL", road_buf); - } - } - - if (!can_client_issue_orders()) { - return; - } - - /* Set government sensitivity. */ - if ((menu = find_menu("/GOVERNMENT"))) { - GList *list, *iter; - struct government *pgov; - - list = gtk_container_get_children(GTK_CONTAINER(menu)); - for (iter = list; NULL != iter; iter = g_list_next(iter)) { - pgov = g_object_get_data(G_OBJECT(iter->data), "government"); - if (NULL != pgov) { - gtk_widget_set_sensitive(GTK_WIDGET(iter->data), - can_change_to_government(client_player(), - pgov)); - } else { - /* Revolution without target government */ - gtk_widget_set_sensitive(GTK_WIDGET(iter->data), - untargeted_revolution_allowed()); - } - } - g_list_free(list); - } - - if (!punits) { - return; - } - - /* Remaining part of this function: Update Unit, Work, and Combat menus */ - - /* Set base sensitivity. */ - if ((menu = find_menu("/BUILD_BASE"))) { - GList *list, *iter; - struct extra_type *pextra; - - list = gtk_container_get_children(GTK_CONTAINER(menu)); - for (iter = list; NULL != iter; iter = g_list_next(iter)) { - pextra = g_object_get_data(G_OBJECT(iter->data), "base"); - if (NULL != pextra) { - gtk_widget_set_sensitive(GTK_WIDGET(iter->data), - can_units_do_activity_targeted(punits, - ACTIVITY_BASE, - pextra)); - } - } - g_list_free(list); - } - - /* Set road sensitivity. */ - if ((menu = find_menu("/BUILD_PATH"))) { - GList *list, *iter; - struct extra_type *pextra; - - list = gtk_container_get_children(GTK_CONTAINER(menu)); - for (iter = list; NULL != iter; iter = g_list_next(iter)) { - pextra = g_object_get_data(G_OBJECT(iter->data), "road"); - if (NULL != pextra) { - gtk_widget_set_sensitive(GTK_WIDGET(iter->data), - can_units_do_activity_targeted(punits, - ACTIVITY_GEN_ROAD, - pextra)); - } - } - g_list_free(list); - } - - /* Set Go to and... action visibility. */ - if ((menu = find_menu("/GOTO_AND"))) { - GList *list, *iter; - struct action *paction; - - bool can_do_something = FALSE; - - /* Enable a menu item if it is theoretically possible that one of the - * selected units can perform it. Checking if the action can be performed - * at the current tile is pointless since it should be performed at the - * target tile. */ - list = gtk_container_get_children(GTK_CONTAINER(menu)); - for (iter = list; NULL != iter; iter = g_list_next(iter)) { - paction = g_object_get_data(G_OBJECT(iter->data), "end_action"); - if (NULL != paction) { - if (units_can_do_action(get_units_in_focus(), - paction->id, TRUE)) { - gtk_widget_set_visible(GTK_WIDGET(iter->data), TRUE); - gtk_widget_set_sensitive(GTK_WIDGET(iter->data), TRUE); - can_do_something = TRUE; - } else { - gtk_widget_set_visible(GTK_WIDGET(iter->data), FALSE); - gtk_widget_set_sensitive(GTK_WIDGET(iter->data), FALSE); - } - } - } - g_list_free(list); - - /* Only sensitive if an action may be possible. */ - menu_entry_set_sensitive("MENU_GOTO_AND", can_do_something); - } - - /* Enable the button for adding to a city in all cases, so we - * get an eventual error message from the server if we try. */ - menu_entry_set_sensitive("BUILD_CITY", - (can_units_do(punits, unit_can_add_or_build_city) - || can_units_do(punits, unit_can_help_build_wonder_here))); - menu_entry_set_sensitive("BUILD_ROAD", - (can_units_do_any_road(punits) - || can_units_do(punits, - unit_can_est_trade_route_here))); - menu_entry_set_sensitive("BUILD_IRRIGATION", - can_units_do_activity(punits, ACTIVITY_IRRIGATE)); - menu_entry_set_sensitive("CULTIVATE", - can_units_do_activity(punits, ACTIVITY_CULTIVATE)); - menu_entry_set_sensitive("BUILD_MINE", - can_units_do_activity(punits, ACTIVITY_MINE)); - menu_entry_set_sensitive("PLANT", - can_units_do_activity(punits, ACTIVITY_PLANT)); - menu_entry_set_sensitive("TRANSFORM_TERRAIN", - can_units_do_activity(punits, ACTIVITY_TRANSFORM)); - menu_entry_set_sensitive("FORTIFY", - can_units_do_activity(punits, - ACTIVITY_FORTIFYING)); - menu_entry_set_sensitive("BUILD_FORTRESS", - can_units_do_base_gui(punits, BASE_GUI_FORTRESS)); - menu_entry_set_sensitive("BUILD_AIRBASE", - can_units_do_base_gui(punits, BASE_GUI_AIRBASE)); - menu_entry_set_sensitive("CLEAN_POLLUTION", - (can_units_do_activity(punits, ACTIVITY_POLLUTION) - || can_units_do(punits, can_unit_paradrop))); - menu_entry_set_sensitive("CLEAN_FALLOUT", - can_units_do_activity(punits, ACTIVITY_FALLOUT)); - menu_entry_set_sensitive("UNIT_SENTRY", - can_units_do_activity(punits, ACTIVITY_SENTRY)); - /* FIXME: should conditionally rename "Pillage" to "Pillage..." in cases where - * selecting the command results in a dialog box listing options of what to pillage */ - menu_entry_set_sensitive("DO_PILLAGE", - can_units_do_activity(punits, ACTIVITY_PILLAGE)); - menu_entry_set_sensitive("UNIT_DISBAND", - units_can_do_action(punits, ACTION_DISBAND_UNIT, - TRUE)); - menu_entry_set_sensitive("UNIT_UPGRADE", - units_can_upgrade(punits)); - /* "UNIT_CONVERT" dealt with below */ - menu_entry_set_sensitive("UNIT_HOMECITY", - can_units_do(punits, can_unit_change_homecity)); - menu_entry_set_sensitive("UNIT_UNLOAD_TRANSPORTER", - units_are_occupied(punits)); - menu_entry_set_sensitive("UNIT_LOAD", - units_can_load(punits)); - menu_entry_set_sensitive("UNIT_UNLOAD", - units_can_unload(punits)); - menu_entry_set_sensitive("UNIT_UNSENTRY", - units_have_activity_on_tile(punits, - ACTIVITY_SENTRY)); - menu_entry_set_sensitive("AUTO_SETTLER", - can_units_do(punits, can_unit_do_autosettlers)); - menu_entry_set_sensitive("UNIT_EXPLORE", - can_units_do_activity(punits, ACTIVITY_EXPLORE)); - - proad = road_by_compat_special(ROCO_ROAD); - if (proad != NULL) { - struct extra_type *tgt; - - tgt = road_extra_get(proad); - - conn_possible = can_units_do_connect(punits, ACTIVITY_GEN_ROAD, tgt); - } else { - conn_possible = FALSE; - } - menu_entry_set_sensitive("CONNECT_ROAD", conn_possible); - - proad = road_by_compat_special(ROCO_RAILROAD); - if (proad != NULL) { - struct extra_type *tgt; - - tgt = road_extra_get(proad); - - conn_possible = can_units_do_connect(punits, ACTIVITY_GEN_ROAD, tgt); - } else { - conn_possible = FALSE; - } - menu_entry_set_sensitive("CONNECT_RAIL", conn_possible); - - extras = extra_type_list_by_cause(EC_IRRIGATION); - - if (extra_type_list_size(extras) > 0) { - struct extra_type *tgt; - - tgt = extra_type_list_get(extras, 0); - conn_possible = can_units_do_connect(punits, ACTIVITY_IRRIGATE, tgt); - } else { - conn_possible = FALSE; - } - menu_entry_set_sensitive("CONNECT_IRRIGATION", conn_possible); - - menu_entry_set_sensitive("DIPLOMAT_ACTION", - units_can_do_action(punits, ACTION_ANY, TRUE)); - - if (units_can_do_action(punits, ACTION_HELP_WONDER, TRUE)) { - menus_rename("BUILD_CITY", - action_get_ui_name_mnemonic(ACTION_HELP_WONDER, "_")); - } else { - bool city_on_tile = FALSE; - - /* FIXME: this overloading doesn't work well with multiple focus - * units. */ - unit_list_iterate(punits, punit) { - if (tile_city(unit_tile(punit))) { - city_on_tile = TRUE; - break; - } - } unit_list_iterate_end; - - if (city_on_tile && units_can_do_action(punits, ACTION_JOIN_CITY, - TRUE)) { - menus_rename("BUILD_CITY", - action_get_ui_name_mnemonic(ACTION_JOIN_CITY, "_")); - } else { - /* refresh default order */ - menus_rename("BUILD_CITY", - action_get_ui_name_mnemonic(ACTION_FOUND_CITY, "_")); - } - } - - if (units_can_do_action(punits, ACTION_TRADE_ROUTE, TRUE)) { - menus_rename("BUILD_ROAD", - action_get_ui_name_mnemonic(ACTION_TRADE_ROUTE, "_")); - } else if (units_have_type_flag(punits, UTYF_SETTLERS, TRUE)) { - char road_item[500]; - struct extra_type *pextra = NULL; - - /* FIXME: this overloading doesn't work well with multiple focus - * units. */ - unit_list_iterate(punits, punit) { - pextra = next_extra_for_tile(unit_tile(punit), EC_ROAD, - unit_owner(punit), punit); - if (pextra != NULL) { - break; - } - } unit_list_iterate_end; - - if (pextra != NULL) { - /* TRANS: Build road of specific type (Road/Railroad) */ - snprintf(road_item, sizeof(road_item), _("Build %s"), - extra_name_translation(pextra)); - menus_rename("BUILD_ROAD", road_item); - } - } else { - menus_rename("BUILD_ROAD", _("Build _Road")); - } - - if (units_all_same_type) { - struct unit *punit = unit_list_get(punits, 0); - const struct unit_type *to_unittype = - can_upgrade_unittype(client_player(), unit_type_get(punit)); - if (to_unittype) { - /* TRANS: %s is a unit type. */ - fc_snprintf(acttext, sizeof(acttext), _("Upgr_ade to %s"), - utype_name_translation( - can_upgrade_unittype(client_player(), - unit_type_get(punit)))); - } else { - acttext[0] = '\0'; - } - } else { - acttext[0] = '\0'; - } - if ('\0' != acttext[0]) { - menus_rename("UNIT_UPGRADE", acttext); - } else { - menus_rename("UNIT_UPGRADE", _("Upgr_ade")); - } - - if (units_can_convert(punits)) { - menu_entry_set_sensitive("UNIT_CONVERT", TRUE); - if (units_all_same_type) { - struct unit *punit = unit_list_get(punits, 0); - /* TRANS: %s is a unit type. */ - fc_snprintf(acttext, sizeof(acttext), _("C_onvert to %s"), - utype_name_translation(unit_type_get(punit)->converted_to)); - } else { - acttext[0] = '\0'; - } - } else { - menu_entry_set_sensitive("UNIT_CONVERT", FALSE); - acttext[0] = '\0'; - } - if ('\0' != acttext[0]) { - menus_rename("UNIT_CONVERT", acttext); - } else { - menus_rename("UNIT_CONVERT", _("C_onvert")); - } - - if (units_all_same_tile) { - struct unit *first = unit_list_get(punits, 0); - - pterrain = tile_terrain(unit_tile(first)); - - if (units_have_type_flag(punits, UTYF_SETTLERS, TRUE)) { - struct extra_type *pextra = NULL; - - /* FIXME: this overloading doesn't work well with multiple focus - * units. */ - unit_list_iterate(punits, punit) { - pextra = next_extra_for_tile(unit_tile(punit), EC_IRRIGATION, - unit_owner(punit), punit); - if (pextra != NULL) { - break; - } - } unit_list_iterate_end; - - if (pextra != NULL) { - /* TRANS: Build irrigation of specific type */ - snprintf(irrtext, sizeof(irrtext), _("Build %s"), - extra_name_translation(pextra)); - } else { - sz_strlcpy(irrtext, _("Build _Irrigation")); - } - } else { - sz_strlcpy(irrtext, _("Build _Irrigation")); - } - - if (pterrain->cultivate_result != T_NONE) { - fc_snprintf(cultext, sizeof(cultext), _("Change to %s"), - get_tile_change_menu_text(unit_tile(first), - ACTIVITY_CULTIVATE)); - } else { - sz_strlcpy(cultext, _("_Cultivate")); - } - - if (units_have_type_flag(punits, UTYF_SETTLERS, TRUE)) { - struct extra_type *pextra = NULL; - - /* FIXME: this overloading doesn't work well with multiple focus - * units. */ - unit_list_iterate(punits, punit) { - pextra = next_extra_for_tile(unit_tile(punit), EC_MINE, - unit_owner(punit), punit); - if (pextra != NULL) { - break; - } - } unit_list_iterate_end; - - if (pextra != NULL) { - /* TRANS: Build mine of specific type */ - snprintf(mintext, sizeof(mintext), _("Build %s"), - extra_name_translation(pextra)); - } else { - sz_strlcpy(mintext, _("Build _Mine")); - } - } else { - sz_strlcpy(mintext, _("Build _Mine")); - } - - if (pterrain->plant_result != T_NONE) { - fc_snprintf(plantext, sizeof(plantext), _("Change to %s"), - get_tile_change_menu_text(unit_tile(first), ACTIVITY_PLANT)); - } else { - sz_strlcpy(plantext, _("_Plant")); - } - - if (pterrain->transform_result != T_NONE - && pterrain->transform_result != pterrain) { - fc_snprintf(transtext, sizeof(transtext), _("Transf_orm to %s"), - get_tile_change_menu_text(unit_tile(first), - ACTIVITY_TRANSFORM)); - } else { - sz_strlcpy(transtext, _("Transf_orm Terrain")); - } - } else { - sz_strlcpy(irrtext, _("Build _Irrigation")); - sz_strlcpy(cultext, _("_Cultivate")); - sz_strlcpy(mintext, _("Build _Mine")); - sz_strlcpy(plantext, _("_Plant")); - sz_strlcpy(transtext, _("Transf_orm Terrain")); - } - - menus_rename("BUILD_IRRIGATION", irrtext); - menus_rename("CULTIVATE", cultext); - menus_rename("BUILD_MINE", mintext); - menus_rename("PLANT", plantext); - menus_rename("TRANSFORM_TERRAIN", transtext); - - if (units_can_do_action(punits, ACTION_PARADROP, TRUE)) { - menus_rename("CLEAN_POLLUTION", - action_get_ui_name_mnemonic(ACTION_PARADROP, "_")); - } else { - menus_rename("CLEAN_POLLUTION", _("Clean _Pollution")); - } - - menus_rename("UNIT_HOMECITY", - action_get_ui_name_mnemonic(ACTION_HOME_CITY, "_")); -} - -/************************************************************************//** - Add an accelerator to an item in the "Go to and..." menu. -****************************************************************************/ -static void menu_unit_goto_and_add_accel(GtkWidget *item, action_id act_id, - const guint accel_key, - const GdkModifierType accel_mods) -{ - const char *path = gtk_menu_item_get_accel_path(GTK_MENU_ITEM(item)); - - if (path == NULL) { - char buf[MAX_LEN_NAME + strlen("/GOTO_AND/")]; - - fc_snprintf(buf, sizeof(buf), "/GOTO_AND/%s", - action_id_rule_name(act_id)); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), buf); - path = buf; /* Not NULL, but not usable either outside this block */ - } - - if (path != NULL) { - gtk_accel_map_add_entry(gtk_menu_item_get_accel_path(GTK_MENU_ITEM(item)), - accel_key, accel_mods); - } -} - -/************************************************************************//** - Recursively remove previous entries in a menu and its sub menus. -****************************************************************************/ -static void menu_remove_previous_entries(GtkMenu *menu) -{ - GList *list, *iter; - GtkWidget *sub_menu; - - list = gtk_container_get_children(GTK_CONTAINER(menu)); - for (iter = list; NULL != iter; iter = g_list_next(iter)) { - if ((sub_menu = gtk_menu_item_get_submenu(iter->data)) != NULL) { - menu_remove_previous_entries(GTK_MENU(sub_menu)); - gtk_widget_destroy(sub_menu); - } - gtk_widget_destroy(GTK_WIDGET(iter->data)); - } - g_list_free(list); -} - -/************************************************************************//** - Initialize menus (sensitivity, name, etc.) based on the - current state and current ruleset, etc. Call menus_update(). -****************************************************************************/ -void real_menus_init(void) -{ - GtkMenu *menu; - - if (ui_builder == NULL) { - return; - } - - menu_entry_set_sensitive("GAME_SAVE_AS", - can_client_access_hack() - && C_S_RUNNING <= client_state()); - menu_entry_set_sensitive("GAME_SAVE", - can_client_access_hack() - && C_S_RUNNING <= client_state()); - - menu_entry_set_active("SAVE_OPTIONS_ON_EXIT", - gui_options.save_options_on_exit); - menu_entry_set_sensitive("SERVER_OPTIONS", client.conn.established); - - menu_entry_set_sensitive("LEAVE", - client.conn.established); - - if (!can_client_change_view()) { - menu_entry_group_set_sensitive(MGROUP_ALL, FALSE); - - return; - } - - menus_rename("BUILD_FORTRESS", Q_(terrain_control.gui_type_base0)); - menus_rename("BUILD_AIRBASE", Q_(terrain_control.gui_type_base1)); - - if ((menu = find_menu("/GOVERNMENT"))) { - GList *list, *iter; - GtkWidget *item; - char buf[256]; - - /* Remove previous government entries. */ - list = gtk_container_get_children(GTK_CONTAINER(menu)); - for (iter = list; NULL != iter; iter = g_list_next(iter)) { - if (g_object_get_data(G_OBJECT(iter->data), "government") != NULL - || GTK_IS_SEPARATOR_MENU_ITEM(iter->data)) { - gtk_widget_destroy(GTK_WIDGET(iter->data)); - } - } - g_list_free(list); - - /* Add new government entries. */ - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - - governments_iterate(g) { - if (g != game.government_during_revolution) { - struct sprite *gsprite; - - /* TRANS: %s is a government name */ - fc_snprintf(buf, sizeof(buf), _("%s..."), - government_name_translation(g)); - item = gtk_image_menu_item_new_with_label(buf); - g_object_set_data(G_OBJECT(item), "government", g); - - if ((gsprite = get_government_sprite(tileset, g))) { - GtkWidget *image; - GdkPixbuf *pb = sprite_get_pixbuf(gsprite); - - image = gtk_image_new_from_pixbuf(pb); - g_object_unref(pb); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); - gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE); - gtk_widget_show(image); - } - - g_signal_connect(item, "activate", - G_CALLBACK(government_callback), g); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - } - } governments_iterate_end; - } - - if ((menu = find_menu("/BUILD_BASE"))) { - GList *list, *iter; - GtkWidget *item; - - /* Remove previous base entries. */ - list = gtk_container_get_children(GTK_CONTAINER(menu)); - for (iter = list; NULL != iter; iter = g_list_next(iter)) { - gtk_widget_destroy(GTK_WIDGET(iter->data)); - } - g_list_free(list); - - /* Add new base entries. */ - extra_type_by_cause_iterate(EC_BASE, pextra) { - if (pextra->buildable) { - item = gtk_menu_item_new_with_label(extra_name_translation(pextra)); - g_object_set_data(G_OBJECT(item), "base", pextra); - g_signal_connect(item, "activate", G_CALLBACK(base_callback), pextra); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - } - } extra_type_by_cause_iterate_end; - } - - if ((menu = find_menu("/BUILD_PATH"))) { - GList *list, *iter; - GtkWidget *item; - - /* Remove previous road entries. */ - list = gtk_container_get_children(GTK_CONTAINER(menu)); - for (iter = list; NULL != iter; iter = g_list_next(iter)) { - gtk_widget_destroy(GTK_WIDGET(iter->data)); - } - g_list_free(list); - - /* Add new road entries. */ - extra_type_by_cause_iterate(EC_ROAD, pextra) { - if (pextra->buildable) { - item = gtk_menu_item_new_with_label(extra_name_translation(pextra)); - g_object_set_data(G_OBJECT(item), "road", pextra); - g_signal_connect(item, "activate", G_CALLBACK(road_callback), pextra); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - } - } extra_type_by_cause_iterate_end; - } - - /* Initialize the Go to and... actions. */ - if ((menu = find_menu("/GOTO_AND"))) { - GtkWidget *item; - int tgt_kind_group; - - /* Remove previous action entries. */ - menu_remove_previous_entries(menu); - - /* Add the new action entries grouped by target kind. */ - for (tgt_kind_group = 0; tgt_kind_group < ATK_COUNT; tgt_kind_group++) { - action_iterate(act_id) { - struct action *paction = action_by_number(act_id); - - if (action_id_get_actor_kind(act_id) != AAK_UNIT) { - /* This action isn't performed by a unit. */ - continue; - } - - if (action_id_get_target_kind(act_id) != tgt_kind_group) { - /* Wrong group. */ - continue; - } - - /* Create and add the menu item. It will be hidden or shown based on - * unit type. */ - item = gtk_menu_item_new_with_mnemonic( - action_get_ui_name_mnemonic(act_id, "_")); - g_object_set_data(G_OBJECT(item), "end_action", paction); - - if (action_id_has_complex_target(act_id)) { - GtkWidget *sub_target_menu = gtk_menu_new(); - -#define CREATE_SUB_ITEM(_sub_target_, _sub_target_key_, _sub_target_name_) \ - GtkWidget *sub_item = gtk_menu_item_new_with_label(_sub_target_name_); \ - g_object_set_data(G_OBJECT(sub_item), "end_action", paction); \ - g_object_set_data(G_OBJECT(sub_item), _sub_target_key_, _sub_target_); \ - g_signal_connect(sub_item, "activate", \ - G_CALLBACK(unit_goto_and_callback), paction); \ - gtk_menu_shell_append(GTK_MENU_SHELL(sub_target_menu), sub_item); \ - gtk_widget_show(sub_item); - - switch (action_get_sub_target_kind(paction)) { - case ASTK_BUILDING: - improvement_iterate(pimpr) { - CREATE_SUB_ITEM(pimpr, "end_building", - improvement_name_translation(pimpr)); - } improvement_iterate_end; - break; - case ASTK_TECH: - advance_iterate(A_FIRST, ptech) { - CREATE_SUB_ITEM(ptech, "end_tech", - advance_name_translation(ptech)); - } advance_iterate_end; - break; - case ASTK_EXTRA: - case ASTK_EXTRA_NOT_THERE: - extra_type_iterate(pextra) { - if (!(action_creates_extra(paction, pextra) - || action_removes_extra(paction, pextra))) { - /* Not relevant */ - continue; - } - CREATE_SUB_ITEM(pextra, "end_extra", - extra_name_translation(pextra)); - } extra_type_iterate_end; - break; - case ASTK_NONE: - /* Should not be here. */ - fc_assert(action_get_sub_target_kind(paction) != ASTK_NONE); - break; - case ASTK_COUNT: - /* Should not exits */ - fc_assert(action_get_sub_target_kind(paction) != ASTK_COUNT); - break; - } - -#undef CREATE_SUB_ITEM - - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), sub_target_menu); - } else { - g_signal_connect(item, "activate", - G_CALLBACK(unit_goto_and_callback), paction); - -#define ADD_OLD_ACCELERATOR(wanted_action_id, accel_key, accel_mods) \ - if (act_id == wanted_action_id) { \ - menu_unit_goto_and_add_accel(item, act_id, accel_key, accel_mods); \ - } - - /* Add the keyboard shortcuts for "Go to and..." menu items that - * existed independently before the "Go to and..." menu arrived. - */ - ADD_OLD_ACCELERATOR(ACTION_FOUND_CITY, GDK_KEY_b, GDK_SHIFT_MASK); - ADD_OLD_ACCELERATOR(ACTION_JOIN_CITY, GDK_KEY_j, GDK_SHIFT_MASK); - ADD_OLD_ACCELERATOR(ACTION_NUKE, GDK_KEY_n, GDK_SHIFT_MASK); - } - - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - } action_iterate_end; - } - } - - menu_entry_group_set_sensitive(MGROUP_SAFE, TRUE); - menu_entry_group_set_sensitive(MGROUP_PLAYER, client_has_player()); - - menu_entry_set_sensitive("TAX_RATE", - game.info.changable_tax - && can_client_issue_orders()); - menu_entry_set_sensitive("POLICIES", - multiplier_count() > 0); - - menu_entry_set_active("SHOW_CITY_OUTLINES", - gui_options.draw_city_outlines); - menu_entry_set_active("SHOW_CITY_OUTPUT", - gui_options.draw_city_output); - menu_entry_set_active("SHOW_MAP_GRID", - gui_options.draw_map_grid); - menu_entry_set_active("SHOW_NATIONAL_BORDERS", - gui_options.draw_borders); - menu_entry_set_sensitive("SHOW_NATIONAL_BORDERS", - BORDERS_DISABLED != game.info.borders); - menu_entry_set_active("SHOW_NATIVE_TILES", - gui_options.draw_native); - menu_entry_set_active("SHOW_CITY_FULL_BAR", - gui_options.draw_full_citybar); - menu_entry_set_active("SHOW_CITY_NAMES", - gui_options.draw_city_names); - menu_entry_set_active("SHOW_CITY_GROWTH", - gui_options.draw_city_growth); - menu_entry_set_active("SHOW_CITY_PRODUCTIONS", - gui_options.draw_city_productions); - menu_entry_set_active("SHOW_CITY_BUY_COST", - gui_options.draw_city_buycost); - menu_entry_set_active("SHOW_CITY_TRADE_ROUTES", - gui_options.draw_city_trade_routes); - menu_entry_set_active("SHOW_TERRAIN", - gui_options.draw_terrain); - menu_entry_set_active("SHOW_COASTLINE", - gui_options.draw_coastline); - menu_entry_set_active("SHOW_PATHS", - gui_options.draw_roads_rails); - menu_entry_set_active("SHOW_IRRIGATION", - gui_options.draw_irrigation); - menu_entry_set_active("SHOW_MINES", - gui_options.draw_mines); - menu_entry_set_active("SHOW_BASES", - gui_options.draw_fortress_airbase); - menu_entry_set_active("SHOW_RESOURCES", - gui_options.draw_specials); - menu_entry_set_active("SHOW_HUTS", - gui_options.draw_huts); - menu_entry_set_active("SHOW_POLLUTION", - gui_options.draw_pollution); - menu_entry_set_active("SHOW_CITIES", - gui_options.draw_cities); - menu_entry_set_active("SHOW_UNITS", - gui_options.draw_units); - menu_entry_set_active("SHOW_UNIT_SOLID_BG", - gui_options.solid_color_behind_units); - menu_entry_set_active("SHOW_UNIT_SHIELDS", - gui_options.draw_unit_shields); - menu_entry_set_active("SHOW_FOCUS_UNIT", - gui_options.draw_focus_unit); - menu_entry_set_active("SHOW_FOG_OF_WAR", - gui_options.draw_fog_of_war); - - view_menu_update_sensitivity(); - - menu_entry_set_active("FULL_SCREEN", GUI_GTK_OPTION(fullscreen)); -} diff --git a/client/gui-gtk-3.0/menu.h b/client/gui-gtk-3.0/menu.h deleted file mode 100644 index 914b97552b..0000000000 --- a/client/gui-gtk-3.0/menu.h +++ /dev/null @@ -1,24 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__MENU_H -#define FC__MENU_H - -#include - -#include "menu_g.h" - -GtkWidget *setup_menus(GtkWidget *window); - -extern GtkAccelGroup *toplevel_accel; - -#endif /* FC__MENU_H */ diff --git a/client/gui-gtk-3.0/messagedlg.c b/client/gui-gtk-3.0/messagedlg.c deleted file mode 100644 index ca33989eea..0000000000 --- a/client/gui-gtk-3.0/messagedlg.c +++ /dev/null @@ -1,224 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" - -/* common */ -#include "events.h" - -/* client */ -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "colors.h" -#include "gui_main.h" -#include "gui_stuff.h" - -#include "messagedlg.h" - -#define NUM_LISTS 1 - -/*************************************************************************/ -static struct gui_dialog *shell; -static GtkListStore *models[NUM_LISTS]; - -static void create_messageopt_dialog(void); -static void messageopt_response(struct gui_dialog *dlg, int response, - gpointer data); -static void item_toggled(GtkCellRendererToggle *cell, - gchar *spath, gpointer data); - -/**********************************************************************//** - Open messageoptions dialog -**************************************************************************/ -void popup_messageopt_dialog(void) -{ - if (!shell) - create_messageopt_dialog(); - - gui_dialog_raise(shell); -} - -/**********************************************************************//** - Create messageoptions dialog -**************************************************************************/ -static void create_messageopt_dialog(void) -{ - GtkWidget *form, *explanation; - int n, i = 0, j; - - gui_dialog_new(&shell, GTK_NOTEBOOK(top_notebook), NULL, TRUE); - gui_dialog_set_title(shell, _("Message Options")); - - gui_dialog_set_default_size(shell, -1, 450); - - gui_dialog_add_button(shell, GTK_STOCK_OK, GTK_RESPONSE_OK); - gui_dialog_add_button(shell, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); - - explanation = gtk_label_new(NULL); - g_object_set(explanation, "margin", 4, NULL); - gtk_label_set_markup(GTK_LABEL(explanation), - _("Where to display messages?\n" - "Output window ; " - "Messages window ; " - "Popup individual window")); - gtk_widget_set_name(explanation, "comment_label"); - gtk_container_add(GTK_CONTAINER(shell->vbox), explanation); - gtk_widget_show(explanation); - - form = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(shell->vbox), form); - - for (n = 0; n < NUM_LISTS; n++) { - models[n] = gtk_list_store_new(5, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_INT); - } - - sorted_event_iterate(ev) { - GtkTreeIter it; - GValue value = { 0, }; - - n = (i++ % NUM_LISTS); - - gtk_list_store_append(models[n], &it); - - g_value_init(&value, G_TYPE_STRING); - g_value_set_static_string(&value, get_event_message_text(ev)); - gtk_list_store_set_value(models[n], &it, 3, &value); - g_value_unset(&value); - - gtk_list_store_set(models[n], &it, 4, ev, -1); - - for (j = 0; j < NUM_MW; j++) { - gtk_list_store_set(models[n], &it, j, messages_where[ev] & (1< -#endif - -#include -#include -#include - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" - -/* common */ -#include "events.h" -#include "game.h" -#include "map.h" -#include "player.h" - -/* client */ -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "citydlg.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapview.h" - -#include "messagewin.h" - - -struct meswin_dialog { - struct gui_dialog *shell; - GtkTreeView *tree_view; -}; - -/* Those values must match meswin_dialog_store_new(). */ -enum meswin_columns { - MESWIN_COL_ICON, - MESWIN_COL_MESSAGE, - - /* Not visible. */ - MESWIN_COL_WEIGHT, - MESWIN_COL_STYLE, - MESWIN_COL_ID, - - MESWIN_COL_NUM -}; - -enum meswin_responses { - MESWIN_RES_GOTO = 1, - MESWIN_RES_POPUP_CITY -}; - -static struct meswin_dialog meswin = { NULL, }; - -/************************************************************************//** - Create a tree model for the message window. -****************************************************************************/ -static GtkListStore *meswin_dialog_store_new(void) -{ - return gtk_list_store_new(MESWIN_COL_NUM, - GDK_TYPE_PIXBUF, /* MESWIN_COL_ICON */ - G_TYPE_STRING, /* MESWIN_COL_MESSAGE */ - G_TYPE_INT, /* MESWIN_COL_WEIGHT */ - G_TYPE_INT, /* MESWIN_COL_STYLE */ - G_TYPE_INT); /* MESWIN_COL_ID */ -} - -/************************************************************************//** - Get the pango attributes for the visited state. -****************************************************************************/ -static void meswin_dialog_visited_get_attr(bool visited, gint *weight, - gint *style) -{ - if (NULL != weight) { - *weight = (visited ? PANGO_WEIGHT_NORMAL : PANGO_WEIGHT_BOLD); - } - if (NULL != style) { - *style = (visited ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); - } -} - -/************************************************************************//** - Set the visited state of the store. -****************************************************************************/ -static void meswin_dialog_set_visited(GtkTreeModel *model, - GtkTreeIter *iter, bool visited) -{ - gint row, weight, style; - - gtk_tree_model_get(model, iter, MESWIN_COL_ID, &row, -1); - meswin_dialog_visited_get_attr(visited, &weight, &style); - gtk_list_store_set(GTK_LIST_STORE(model), iter, - MESWIN_COL_WEIGHT, weight, - MESWIN_COL_STYLE, style, - -1); - meswin_set_visited_state(row, visited); -} - -/************************************************************************//** - Refresh a message window dialog. -****************************************************************************/ -static void meswin_dialog_refresh(struct meswin_dialog *pdialog) -{ - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkListStore *store; - GtkTreeIter iter; - const struct message *pmsg; - gint weight, style; - int selected, i, num; - bool need_alert = FALSE; - - fc_assert_ret(NULL != pdialog); - - /* Save the selection. */ - selection = gtk_tree_view_get_selection(pdialog->tree_view); - if (gtk_tree_selection_get_selected(selection, &model, &iter)) { - gtk_tree_model_get(model, &iter, MESWIN_COL_ID, &selected, -1); - } else { - selected = -1; - } - - model = gtk_tree_view_get_model(pdialog->tree_view); - store = GTK_LIST_STORE(model); - num = meswin_get_num_messages(); - - gtk_list_store_clear(store); - for (i = 0; i < num; i++) { - GdkPixbuf *pb; - struct sprite *icon; - int x0, y0, x1, y1, w, h; - GdkPixbuf *pixbuf; - - pmsg = meswin_get_message(i); - - if (GUI_GTK_OPTION(new_messages_go_to_top)) { - gtk_list_store_prepend(store, &iter); - } else { - gtk_list_store_append(store, &iter); - } - - icon = get_event_sprite(tileset, pmsg->event); - sprite_get_bounding_box(icon, &x0, &y0, &x1, &y1); - w = (x1 - x0) + 1; - h = (y1 - y0) + 1; - pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h); - pixbuf = sprite_get_pixbuf(icon); - gdk_pixbuf_copy_area(pixbuf, x0, y0, w, h, - pb, 0, 0); - g_object_unref(G_OBJECT(pixbuf)); - - meswin_dialog_visited_get_attr(pmsg->visited, &weight, &style); - gtk_list_store_set(store, &iter, - MESWIN_COL_ICON, pb, - MESWIN_COL_MESSAGE, pmsg->descr, - MESWIN_COL_WEIGHT, weight, - MESWIN_COL_STYLE, style, - MESWIN_COL_ID, i, - -1); - g_object_unref(pb); - if (i == selected) { - /* Restore the selection. */ - gtk_tree_selection_select_iter(selection, &iter); - } - - if (!pmsg->visited) { - need_alert = TRUE; - } - } - - if (need_alert) { - gui_dialog_alert(pdialog->shell); - } -} - -/************************************************************************//** - Selection changed callback. -****************************************************************************/ -static void meswin_dialog_selection_callback(GtkTreeSelection *selection, - gpointer data) -{ - struct meswin_dialog *pdialog = data; - const struct message *pmsg; - GtkTreeModel *model; - GtkTreeIter iter; - gint row; - - if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { - return; - } - - gtk_tree_model_get(model, &iter, MESWIN_COL_ID, &row, -1); - pmsg = meswin_get_message(row); - - gui_dialog_set_response_sensitive(pdialog->shell, MESWIN_RES_GOTO, - NULL != pmsg && pmsg->location_ok); - gui_dialog_set_response_sensitive(pdialog->shell, MESWIN_RES_POPUP_CITY, - NULL != pmsg && pmsg->city_ok); -} - -/************************************************************************//** - A row has been activated by the user. -****************************************************************************/ -static void meswin_dialog_row_activated_callback(GtkTreeView *view, - GtkTreePath *path, - GtkTreeViewColumn *col, - gpointer data) -{ - GtkTreeModel *model = gtk_tree_view_get_model(view); - GtkTreeIter iter; - gint row; - - if (!gtk_tree_model_get_iter(model, &iter, path)) { - return; - } - - gtk_tree_model_get(model, &iter, MESWIN_COL_ID, &row, -1); - - if (NULL != meswin_get_message(row)) { - meswin_double_click(row); - meswin_dialog_set_visited(model, &iter, TRUE); - } -} - -/************************************************************************//** - Mouse button press handler for the message window treeview. We only - care about right clicks on a row; this action centers on the tile - associated with the event at that row (if applicable). -****************************************************************************/ -static gboolean meswin_dialog_button_press_callback(GtkWidget *widget, - GdkEventButton *ev, - gpointer data) -{ - GtkTreePath *path = NULL; - GtkTreeModel *model; - GtkTreeIter iter; - gint row; - - fc_assert_ret_val(GTK_IS_TREE_VIEW(widget), FALSE); - - if (GDK_BUTTON_PRESS != ev->type || 3 != ev->button) { - return FALSE; - } - - if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), ev->x, ev->y, - &path, NULL, NULL, NULL)) { - return TRUE; - } - - model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); - if (gtk_tree_model_get_iter(model, &iter, path)) { - gtk_tree_model_get(model, &iter, MESWIN_COL_ID, &row, -1); - meswin_goto(row); - } - gtk_tree_path_free(path); - - return TRUE; -} - -/************************************************************************//** - Dialog response callback. -****************************************************************************/ -static void meswin_dialog_response_callback(struct gui_dialog *pgui_dialog, - int response, gpointer data) -{ - struct meswin_dialog *pdialog = data; - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - gint row; - - switch (response) { - case MESWIN_RES_GOTO: - case MESWIN_RES_POPUP_CITY: - break; - default: - gui_dialog_destroy(pgui_dialog); - return; - } - - selection = gtk_tree_view_get_selection(pdialog->tree_view); - if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { - return; - } - - gtk_tree_model_get(model, &iter, MESWIN_COL_ID, &row, -1); - - switch (response) { - case MESWIN_RES_GOTO: - meswin_goto(row); - break; - case MESWIN_RES_POPUP_CITY: - meswin_popup_city(row); - break; - } - meswin_dialog_set_visited(model, &iter, TRUE); -} - -/************************************************************************//** - Initilialize a message window dialog. -****************************************************************************/ -static void meswin_dialog_init(struct meswin_dialog *pdialog) -{ - GtkWidget *view, *sw, *cmd, *notebook; - GtkContainer *vbox; - GtkListStore *store; - GtkTreeSelection *selection; - GtkCellRenderer *renderer; - GtkTreeViewColumn *col; - - fc_assert_ret(NULL != pdialog); - - if (GUI_GTK_OPTION(message_chat_location) == GUI_GTK_MSGCHAT_SPLIT) { - notebook = right_notebook; - } else { - notebook = bottom_notebook; - } - - gui_dialog_new(&pdialog->shell, GTK_NOTEBOOK(notebook), pdialog, TRUE); - gui_dialog_set_title(pdialog->shell, _("Messages")); - vbox = GTK_CONTAINER(pdialog->shell->vbox); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_container_add(vbox, sw); - - store = meswin_dialog_store_new(); - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(store); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view)); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - g_signal_connect(view, "row_activated", - G_CALLBACK(meswin_dialog_row_activated_callback), NULL); - g_signal_connect(view, "button-press-event", - G_CALLBACK(meswin_dialog_button_press_callback), NULL); - pdialog->tree_view = GTK_TREE_VIEW(view); - - renderer = gtk_cell_renderer_pixbuf_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, renderer, - "pixbuf", MESWIN_COL_ICON, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - gtk_tree_view_column_set_visible(col, !GUI_GTK_OPTION(small_display_layout)); - - renderer = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(NULL, renderer, - "text", MESWIN_COL_MESSAGE, - "weight", MESWIN_COL_WEIGHT, - "style", MESWIN_COL_STYLE, - NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - gtk_container_add(GTK_CONTAINER(sw), view); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - g_signal_connect(selection, "changed", - G_CALLBACK(meswin_dialog_selection_callback), pdialog); - - gui_dialog_add_button(pdialog->shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - - if (GUI_GTK_OPTION(show_message_window_buttons)) { - cmd = gui_dialog_add_stockbutton(pdialog->shell, GTK_STOCK_ZOOM_IN, - _("I_nspect City"), - MESWIN_RES_POPUP_CITY); - gtk_widget_set_sensitive(cmd, FALSE); - - cmd = gui_dialog_add_stockbutton(pdialog->shell, GTK_STOCK_JUMP_TO, - _("Goto _Location"), MESWIN_RES_GOTO); - gtk_widget_set_sensitive(cmd, FALSE); - } - - gui_dialog_response_set_callback(pdialog->shell, - meswin_dialog_response_callback); - gui_dialog_set_default_size(pdialog->shell, 520, 300); - - meswin_dialog_refresh(pdialog); - gui_dialog_show_all(pdialog->shell); -} - -/************************************************************************//** - Closes a message window dialog. -****************************************************************************/ -static void meswin_dialog_free(struct meswin_dialog *pdialog) -{ - fc_assert_ret(NULL != pdialog); - - gui_dialog_destroy(pdialog->shell); - fc_assert(NULL == pdialog->shell); - - memset(pdialog, 0, sizeof(*pdialog)); -} - -/************************************************************************//** - Popup the dialog inside the main-window, and optionally raise it. -****************************************************************************/ -void meswin_dialog_popup(bool raise) -{ - if (NULL == meswin.shell) { - meswin_dialog_init(&meswin); - } - - gui_dialog_present(meswin.shell); - if (raise) { - gui_dialog_raise(meswin.shell); - } -} - -/************************************************************************//** - Closes the message window dialog. -****************************************************************************/ -void meswin_dialog_popdown(void) -{ - if (NULL != meswin.shell) { - meswin_dialog_free(&meswin); - fc_assert(NULL == meswin.shell); - } -} - -/************************************************************************//** - Return TRUE iff the message window is open. -****************************************************************************/ -bool meswin_dialog_is_open(void) -{ - return (NULL != meswin.shell); -} - -/************************************************************************//** - Update the message window dialog. -****************************************************************************/ -void real_meswin_dialog_update(void *unused) -{ - if (NULL != meswin.shell) { - meswin_dialog_refresh(&meswin); - } -} diff --git a/client/gui-gtk-3.0/messagewin.h b/client/gui-gtk-3.0/messagewin.h deleted file mode 100644 index ff23253687..0000000000 --- a/client/gui-gtk-3.0/messagewin.h +++ /dev/null @@ -1,20 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__MESSAGEWIN_H -#define FC__MESSAGEWIN_H - -#include "messagewin_g.h" - -void meswin_dialog_popdown(void); - -#endif /* FC__MESSAGEWIN_H */ diff --git a/client/gui-gtk-3.0/optiondlg.c b/client/gui-gtk-3.0/optiondlg.c deleted file mode 100644 index 99e834dd1d..0000000000 --- a/client/gui-gtk-3.0/optiondlg.c +++ /dev/null @@ -1,1055 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -/* utility */ -#include "log.h" -#include "mem.h" -#include "string_vector.h" - -/* client */ -#include "options.h" - -/* client/gui-gtk-3.0 */ -#include "colors.h" -#include "dialogs.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "pages.h" - -#include "optiondlg.h" - - -/* The option dialog data. */ -struct option_dialog { - const struct option_set *poptset; /* The option set. */ - GtkWidget *shell; /* The main widget. */ - GtkWidget *notebook; /* The notebook. */ - GtkWidget **vboxes; /* Category boxes. */ - int *box_children; /* The number of children for - * each category. */ -}; - -#define SPECLIST_TAG option_dialog -#define SPECLIST_TYPE struct option_dialog -#include "speclist.h" -#define option_dialogs_iterate(pdialog) \ - TYPED_LIST_ITERATE(struct option_dialog, option_dialogs, pdialog) -#define option_dialogs_iterate_end LIST_ITERATE_END - -/* All option dialog are set on this list. */ -static struct option_dialog_list *option_dialogs = NULL; - -enum { - RESPONSE_CANCEL, - RESPONSE_OK, - RESPONSE_APPLY, - RESPONSE_RESET, - RESPONSE_REFRESH, - RESPONSE_SAVE -}; - - -/* Option dialog main functions. */ -static struct option_dialog * -option_dialog_get(const struct option_set *poptset); -static struct option_dialog * -option_dialog_new(const char *name, const struct option_set *poptset); -static void option_dialog_destroy(struct option_dialog *pdialog); - -static void option_dialog_reorder_notebook(struct option_dialog *pdialog); -static inline void option_dialog_foreach(struct option_dialog *pdialog, - void (*option_action) - (struct option *)); - -/* Option dialog option-specific functions. */ -static void option_dialog_option_add(struct option_dialog *pdialog, - struct option *poption, - bool reorder_notebook); -static void option_dialog_option_remove(struct option_dialog *pdialog, - struct option *poption); - -static void option_dialog_option_refresh(struct option *poption); -static void option_dialog_option_reset(struct option *poption); -static void option_dialog_option_apply(struct option *poption); - - -/************************************************************************//** - Option dialog widget response callback. -****************************************************************************/ -static void option_dialog_reponse_callback(GtkDialog *dialog, - gint response_id, gpointer data) -{ - struct option_dialog *pdialog = (struct option_dialog *) data; - - switch (response_id) { - case RESPONSE_CANCEL: - gtk_widget_destroy(GTK_WIDGET(dialog)); - break; - case RESPONSE_OK: - option_dialog_foreach(pdialog, option_dialog_option_apply); - gtk_widget_destroy(GTK_WIDGET(dialog)); - break; - case RESPONSE_APPLY: - option_dialog_foreach(pdialog, option_dialog_option_apply); - break; - case RESPONSE_RESET: - option_dialog_foreach(pdialog, option_dialog_option_reset); - break; - case RESPONSE_REFRESH: - option_dialog_foreach(pdialog, option_dialog_option_refresh); - break; - case RESPONSE_SAVE: - desired_settable_options_update(); - options_save(NULL); - break; - } -} - -/************************************************************************//** - Option dialog widget destroyed callback. -****************************************************************************/ -static void option_dialog_destroy_callback(GtkWidget *object, gpointer data) -{ - struct option_dialog *pdialog = (struct option_dialog *) data; - - if (NULL != pdialog->shell) { - /* Mark as destroyed, see also option_dialog_destroy(). */ - pdialog->shell = NULL; - option_dialog_destroy(pdialog); - } -} - -/************************************************************************//** - Option refresh requested from menu. -****************************************************************************/ -static void option_refresh_callback(GtkMenuItem *menuitem, gpointer data) -{ - struct option *poption = (struct option *) data; - struct option_dialog *pdialog = option_dialog_get(option_optset(poption)); - - if (NULL != pdialog) { - option_dialog_option_refresh(poption); - } -} - -/************************************************************************//** - Option reset requested from menu. -****************************************************************************/ -static void option_reset_callback(GtkMenuItem *menuitem, gpointer data) -{ - struct option *poption = (struct option *) data; - struct option_dialog *pdialog = option_dialog_get(option_optset(poption)); - - if (NULL != pdialog) { - option_dialog_option_reset(poption); - } -} - -/************************************************************************//** - Option apply requested from menu. -****************************************************************************/ -static void option_apply_callback(GtkMenuItem *menuitem, gpointer data) -{ - struct option *poption = (struct option *) data; - struct option_dialog *pdialog = option_dialog_get(option_optset(poption)); - - if (NULL != pdialog) { - option_dialog_option_apply(poption); - } -} - -/************************************************************************//** - Called when a button is pressed on a option. -****************************************************************************/ -static gboolean option_button_press_callback(GtkWidget *widget, - GdkEventButton *event, - gpointer data) -{ - struct option *poption = (struct option *) data; - GtkWidget *menu, *item; - - if (3 != event->button || !option_is_changeable(poption)) { - /* Only right button please! */ - return FALSE; - } - - menu = gtk_menu_new(); - - item = gtk_image_menu_item_new_with_label(_("Refresh this option")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), - gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(option_refresh_callback), poption); - - item = gtk_image_menu_item_new_with_label(_("Reset this option")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), - gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(option_reset_callback), poption); - - item = gtk_image_menu_item_new_with_label( - _("Apply the changes for this option")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), - gtk_image_new_from_stock(GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(option_apply_callback), poption); - - gtk_widget_show_all(menu); - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, 0); - - return TRUE; -} - -/************************************************************************//** - Returns the option dialog which fit the option set. -****************************************************************************/ -static struct option_dialog * -option_dialog_get(const struct option_set *poptset) -{ - if (NULL != option_dialogs) { - option_dialogs_iterate(pdialog) { - if (pdialog->poptset == poptset) { - return pdialog; - } - } option_dialogs_iterate_end; - } - return NULL; -} - -/************************************************************************//** - GDestroyNotify callback. -****************************************************************************/ -static void option_color_destroy_notify(gpointer data) -{ - GdkRGBA *color = (GdkRGBA *) data; - - if (NULL != color) { - gdk_rgba_free(color); - } -} - -/************************************************************************//** - Set the color of a button. -****************************************************************************/ -static void option_color_set_button_color(GtkButton *button, - const GdkRGBA *new_color) -{ - GdkRGBA *current_color = g_object_get_data(G_OBJECT(button), "color"); - GtkWidget *child; - - if (NULL == new_color) { - if (NULL != current_color) { - g_object_set_data(G_OBJECT(button), "color", NULL); - if ((child = gtk_bin_get_child(GTK_BIN(button)))) { - gtk_widget_destroy(child); - } - } - } else { - GdkPixbuf *pixbuf; - - /* Apply the new color. */ - if (NULL != current_color) { - /* We already have a GdkRGBA pointer. */ - *current_color = *new_color; - } else { - /* We need to make a GdkRGBA pointer. */ - current_color = gdk_rgba_copy(new_color); - g_object_set_data_full(G_OBJECT(button), "color", current_color, - option_color_destroy_notify); - } - if ((child = gtk_bin_get_child(GTK_BIN(button)))) { - gtk_widget_destroy(child); - } - - /* Update the button. */ - { - cairo_surface_t *surface = cairo_image_surface_create( - CAIRO_FORMAT_RGB24, 16, 16); - cairo_t *cr = cairo_create(surface); - gdk_cairo_set_source_rgba(cr, current_color); - cairo_paint(cr); - cairo_destroy(cr); - pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, 16, 16); - cairo_surface_destroy(surface); - } - child = gtk_image_new_from_pixbuf(pixbuf); - gtk_container_add(GTK_CONTAINER(button), child); - gtk_widget_show(child); - g_object_unref(G_OBJECT(pixbuf)); - } -} - -/************************************************************************//** - "response" signal callback. -****************************************************************************/ -static void color_selector_response_callback(GtkDialog *dialog, - gint res, gpointer data) -{ - if (res == GTK_RESPONSE_REJECT) { - /* Clears the current color. */ - option_color_set_button_color(GTK_BUTTON(data), NULL); - } else if (res == GTK_RESPONSE_OK) { - /* Apply the new color. */ - GtkColorChooser *chooser = - GTK_COLOR_CHOOSER(g_object_get_data(G_OBJECT(dialog), "chooser")); - GdkRGBA new_color; - - gtk_color_chooser_get_rgba(chooser, &new_color); - option_color_set_button_color(GTK_BUTTON(data), &new_color); - } - - gtk_widget_destroy(GTK_WIDGET(dialog)); -} - -/************************************************************************//** - Called when the user press a color button. -****************************************************************************/ -static void option_color_select_callback(GtkButton *button, gpointer data) -{ - GtkWidget *dialog, *chooser; - GdkRGBA *current_color = g_object_get_data(G_OBJECT(button), "color"); - - dialog = gtk_dialog_new_with_buttons(_("Select a color"), NULL, - GTK_DIALOG_MODAL, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_CLEAR, GTK_RESPONSE_REJECT, - GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); - setup_dialog(dialog, toplevel); - g_signal_connect(dialog, "response", - G_CALLBACK(color_selector_response_callback), button); - - chooser = gtk_color_chooser_widget_new(); - g_object_set_data(G_OBJECT(dialog), "chooser", chooser); - gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), chooser, - FALSE, FALSE, 0); - if (current_color) { - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(chooser), current_color); - } - - gtk_widget_show_all(dialog); -} - - -/************************************************************************//** - Creates a new option dialog. -****************************************************************************/ -static struct option_dialog * -option_dialog_new(const char *name, const struct option_set *poptset) -{ - struct option_dialog *pdialog; - const int CATEGORY_NUM = optset_category_number(poptset); - - /* Create the dialog structure. */ - pdialog = fc_malloc(sizeof(*pdialog)); - pdialog->poptset = poptset; - pdialog->shell = gtk_dialog_new_with_buttons(name, NULL, 0, - GTK_STOCK_CANCEL, RESPONSE_CANCEL, - GTK_STOCK_SAVE, RESPONSE_SAVE, - GTK_STOCK_REFRESH, RESPONSE_REFRESH, - _("Reset"), RESPONSE_RESET, - GTK_STOCK_APPLY, RESPONSE_APPLY, - GTK_STOCK_OK, RESPONSE_OK, NULL); - pdialog->notebook = gtk_notebook_new(); - pdialog->vboxes = fc_calloc(CATEGORY_NUM, sizeof(*pdialog->vboxes)); - pdialog->box_children = fc_calloc(CATEGORY_NUM, - sizeof(*pdialog->box_children)); - - /* Append to the option dialog list. */ - if (NULL == option_dialogs) { - option_dialogs = option_dialog_list_new(); - } - option_dialog_list_append(option_dialogs, pdialog); - - /* Shell */ - setup_dialog(pdialog->shell, toplevel); - gtk_window_set_position(GTK_WINDOW(pdialog->shell), GTK_WIN_POS_MOUSE); - gtk_window_set_default_size(GTK_WINDOW(pdialog->shell), -1, 480); - g_signal_connect(pdialog->shell, "response", - G_CALLBACK(option_dialog_reponse_callback), pdialog); - g_signal_connect(pdialog->shell, "destroy", - G_CALLBACK(option_dialog_destroy_callback), pdialog); - - gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(pdialog->shell))), - pdialog->notebook, TRUE, TRUE, 0); - - /* Add the options. */ - options_iterate(poptset, poption) { - option_dialog_option_add(pdialog, poption, FALSE); - } options_iterate_end; - - option_dialog_reorder_notebook(pdialog); - - /* Show the widgets. */ - gtk_widget_show_all(pdialog->shell); - - return pdialog; -} - -/************************************************************************//** - Destroys an option dialog. -****************************************************************************/ -static void option_dialog_destroy(struct option_dialog *pdialog) -{ - GtkWidget *shell = pdialog->shell; - - if (NULL != option_dialogs) { - option_dialog_list_remove(option_dialogs, pdialog); - } - - options_iterate(pdialog->poptset, poption) { - option_set_gui_data(poption, NULL); - } options_iterate_end; - - if (NULL != shell) { - /* Maybe already destroyed, see also option_dialog_destroy_callback(). */ - pdialog->shell = NULL; - gtk_widget_destroy(shell); - } - - free(pdialog->vboxes); - free(pdialog->box_children); - free(pdialog); -} - -/************************************************************************//** - Utility for sorting the pages of a option dialog. -****************************************************************************/ -static int option_dialog_pages_sort_func(const void *w1, const void *w2) -{ - GObject *obj1 = G_OBJECT(*(GtkWidget **) w1); - GObject *obj2 = G_OBJECT(*(GtkWidget **) w2); - - return (GPOINTER_TO_INT(g_object_get_data(obj1, "category")) - - GPOINTER_TO_INT(g_object_get_data(obj2, "category"))); -} - -/************************************************************************//** - Reoder the pages of the notebook of the option dialog. -****************************************************************************/ -static void option_dialog_reorder_notebook(struct option_dialog *pdialog) -{ - GtkNotebook *notebook = GTK_NOTEBOOK(pdialog->notebook); - const int pages_num = gtk_notebook_get_n_pages(notebook); - - if (0 < pages_num) { - GtkWidget *pages[pages_num]; - int i; - - for (i = 0; i < pages_num; i++) { - pages[i] = gtk_notebook_get_nth_page(notebook, i); - } - qsort(pages, pages_num, sizeof(*pages), option_dialog_pages_sort_func); - for (i = 0; i < pages_num; i++) { - gtk_notebook_reorder_child(notebook, pages[i], i); - } - } -} - -/************************************************************************//** - Do an action for all options of the option dialog. -****************************************************************************/ -static inline void option_dialog_foreach(struct option_dialog *pdialog, - void (*option_action) - (struct option *)) -{ - fc_assert_ret(NULL != pdialog); - - options_iterate(pdialog->poptset, poption) { - option_action(poption); - } options_iterate_end; -} - -/************************************************************************//** - Add an option to the option dialog. -****************************************************************************/ -static void option_dialog_option_add(struct option_dialog *pdialog, - struct option *poption, - bool reorder_notebook) -{ - const int category = option_category(poption); - GtkWidget *main_hbox, *label, *ebox, *w = NULL; - - fc_assert(NULL == option_get_gui_data(poption)); - - /* Add category if needed. */ - if (NULL == pdialog->vboxes[category]) { - GtkWidget *sw; - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, - GTK_POLICY_AUTOMATIC); - g_object_set_data(G_OBJECT(sw), "category", GINT_TO_POINTER(category)); - gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), sw, - gtk_label_new_with_mnemonic - (option_category_name(poption))); - - if (reorder_notebook) { - option_dialog_reorder_notebook(pdialog); - } - - pdialog->vboxes[category] = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(pdialog->vboxes[category]), - GTK_ORIENTATION_VERTICAL); - g_object_set(pdialog->vboxes[category], "margin", 8, NULL); - gtk_widget_set_hexpand(pdialog->vboxes[category], TRUE); - gtk_container_add(GTK_CONTAINER(sw), pdialog->vboxes[category]); - - gtk_widget_show_all(sw); - } - pdialog->box_children[category]++; - - ebox = gtk_event_box_new(); - gtk_widget_set_tooltip_text(ebox, option_help_text(poption)); - gtk_container_add(GTK_CONTAINER(pdialog->vboxes[category]), ebox); - g_signal_connect(ebox, "button_press_event", - G_CALLBACK(option_button_press_callback), poption); - - main_hbox = gtk_grid_new(); - label = gtk_label_new(option_description(poption)); - g_object_set(label, "margin", 2, NULL); - gtk_container_add(GTK_CONTAINER(main_hbox), label); - gtk_container_add(GTK_CONTAINER(ebox), main_hbox); - - switch (option_type(poption)) { - case OT_BOOLEAN: - w = gtk_check_button_new(); - break; - - case OT_INTEGER: - { - int min = option_int_min(poption), max = option_int_max(poption); - - w = gtk_spin_button_new_with_range(min, max, MAX((max - min) / 50, 1)); - } - break; - - case OT_STRING: - { - const struct strvec *values = option_str_values(poption); - - if (NULL != values) { - w = gtk_combo_box_text_new_with_entry(); - strvec_iterate(values, value) { - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(w), value); - } strvec_iterate_end; - } else { - w = gtk_entry_new(); - } - } - break; - - case OT_ENUM: - { - int i; - const char *str; - GtkListStore *model; - GtkCellRenderer *renderer; - GtkTreeIter iter; - - /* 0: enum index, 1: translated enum name. */ - model = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING); - w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model)); - g_object_unref(model); - - renderer = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), renderer, FALSE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), renderer, - "text", 1, NULL); - for (i = 0; (str = option_enum_int_to_str(poption, i)); i++) { - gtk_list_store_append(model, &iter); - gtk_list_store_set(model, &iter, 0, i, 1, _(str), -1); - } - } - break; - - case OT_BITWISE: - { - GList *list = NULL; - GtkWidget *grid, *check; - const struct strvec *values = option_bitwise_values(poption); - int i; - - w = gtk_frame_new(NULL); - grid = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(grid), 4); - gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE); - gtk_container_add(GTK_CONTAINER(w), grid); - for (i = 0; i < strvec_size(values); i++) { - check = gtk_check_button_new(); - gtk_grid_attach(GTK_GRID(grid), check, 0, i, 1, 1); - label = gtk_label_new(_(strvec_get(values, i))); - gtk_grid_attach(GTK_GRID(grid), label, 1, i, 1, 1); - list = g_list_append(list, check); - } - g_object_set_data_full(G_OBJECT(w), "check_buttons", list, - (GDestroyNotify) g_list_free); - } - break; - - case OT_FONT: - w = gtk_font_button_new(); - g_object_set(G_OBJECT(w), "use-font", TRUE, NULL); - break; - - case OT_COLOR: - { - GtkWidget *button; - - w = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(w), 4); - gtk_grid_set_row_homogeneous(GTK_GRID(w), TRUE); - - /* Foreground color selector button. */ - button = gtk_button_new(); - gtk_container_add(GTK_CONTAINER(w), button); - gtk_widget_set_tooltip_text(GTK_WIDGET(button), - _("Select the text color")); - g_object_set_data(G_OBJECT(w), "fg_button", button); - g_signal_connect(button, "clicked", - G_CALLBACK(option_color_select_callback), NULL); - - /* Background color selector button. */ - button = gtk_button_new(); - gtk_container_add(GTK_CONTAINER(w), button); - gtk_widget_set_tooltip_text(GTK_WIDGET(button), - _("Select the background color")); - g_object_set_data(G_OBJECT(w), "bg_button", button); - g_signal_connect(button, "clicked", - G_CALLBACK(option_color_select_callback), NULL); - } - break; - - case OT_VIDEO_MODE: - log_error("Option type %s (%d) not supported yet.", - option_type_name(option_type(poption)), - option_type(poption)); - break; - } - - option_set_gui_data(poption, w); - if (NULL == w) { - log_error("Failed to create a widget for option %d \"%s\".", - option_number(poption), option_name(poption)); - } else { - g_object_set_data(G_OBJECT(w), "main_widget", ebox); - gtk_widget_set_hexpand(w, TRUE); - gtk_widget_set_halign(w, GTK_ALIGN_END); - gtk_container_add(GTK_CONTAINER(main_hbox), w); - } - - gtk_widget_show_all(ebox); - - /* Set as current value. */ - option_dialog_option_refresh(poption); -} - -/************************************************************************//** - Remove an option from the option dialog. -****************************************************************************/ -static void option_dialog_option_remove(struct option_dialog *pdialog, - struct option *poption) -{ - GObject *object = G_OBJECT(option_get_gui_data(poption)); - - if (NULL != object) { - const int category = option_category(poption); - - option_set_gui_data(poption, NULL); - gtk_widget_destroy(GTK_WIDGET(g_object_get_data(object, "main_widget"))); - - /* Remove category if needed. */ - if (0 == --pdialog->box_children[category]) { - gtk_notebook_remove_page(GTK_NOTEBOOK(pdialog->notebook), category); - pdialog->vboxes[category] = NULL; - } - } -} - -/************************************************************************//** - Set the boolean value of the option. -****************************************************************************/ -static inline void option_dialog_option_bool_set(struct option *poption, - bool value) -{ - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON - (option_get_gui_data(poption)), - value); -} - -/************************************************************************//** - Set the integer value of the option. -****************************************************************************/ -static inline void option_dialog_option_int_set(struct option *poption, - int value) -{ - gtk_spin_button_set_value(GTK_SPIN_BUTTON(option_get_gui_data(poption)), - value); -} - -/************************************************************************//** - Set the string value of the option. -****************************************************************************/ -static inline void option_dialog_option_str_set(struct option *poption, - const char *string) -{ - if (NULL != option_str_values(poption)) { - gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN - (option_get_gui_data(poption)))), string); - } else { - gtk_entry_set_text(GTK_ENTRY(option_get_gui_data(poption)), string); - } -} - -/************************************************************************//** - Set the enum value of the option. -****************************************************************************/ -static inline void option_dialog_option_enum_set(struct option *poption, - int value) -{ - GtkComboBox *combo = GTK_COMBO_BOX(option_get_gui_data(poption)); - GtkTreeModel *model = gtk_combo_box_get_model(combo); - GtkTreeIter iter; - int i; - - if (gtk_tree_model_get_iter_first(model, &iter)) { - do { - gtk_tree_model_get(model, &iter, 0, &i, -1); - if (i == value) { - gtk_combo_box_set_active_iter(combo, &iter); - return; - } - } while (gtk_tree_model_iter_next(model, &iter)); - } - - log_error("Didn't find the value %d for option \"%s\" (nb %d).", - value, option_name(poption), option_number(poption)); -} - -/************************************************************************//** - Set the enum value of the option. -****************************************************************************/ -static inline void option_dialog_option_bitwise_set(struct option *poption, - unsigned value) -{ - GObject *data = option_get_gui_data(poption); - GList *iter = g_object_get_data(data, "check_buttons"); - int bit; - - for (bit = 0; NULL != iter; iter = g_list_next(iter), bit++) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(iter->data), - value & (1 << bit)); - } -} - -/************************************************************************//** - Set the font value of the option. -****************************************************************************/ -static inline void option_dialog_option_font_set(struct option *poption, - const char *font) -{ - gtk_font_chooser_set_font(GTK_FONT_CHOOSER - (option_get_gui_data(poption)), font); -} - -/************************************************************************//** - Set the font value of the option. -****************************************************************************/ -static inline void option_dialog_option_color_set(struct option *poption, - struct ft_color color) -{ - GtkWidget *w = option_get_gui_data(poption); - GdkRGBA gdk_color; - - /* Update the foreground button. */ - if (NULL != color.foreground - && '\0' != color.foreground[0] - && gdk_rgba_parse(&gdk_color, color.foreground)) { - option_color_set_button_color(g_object_get_data(G_OBJECT(w), - "fg_button"), - &gdk_color); - } else { - option_color_set_button_color(g_object_get_data(G_OBJECT(w), - "fg_button"), NULL); - } - - /* Update the background button. */ - if (NULL != color.background - && '\0' != color.background[0] - && gdk_rgba_parse(&gdk_color, color.background)) { - option_color_set_button_color(g_object_get_data(G_OBJECT(w), - "bg_button"), - &gdk_color); - } else { - option_color_set_button_color(g_object_get_data(G_OBJECT(w), - "bg_button"), NULL); - } -} - -/************************************************************************//** - Update an option in the option dialog. -****************************************************************************/ -static void option_dialog_option_refresh(struct option *poption) -{ - switch (option_type(poption)) { - case OT_BOOLEAN: - option_dialog_option_bool_set(poption, option_bool_get(poption)); - break; - case OT_INTEGER: - option_dialog_option_int_set(poption, option_int_get(poption)); - break; - case OT_STRING: - option_dialog_option_str_set(poption, option_str_get(poption)); - break; - case OT_ENUM: - option_dialog_option_enum_set(poption, option_enum_get_int(poption)); - break; - case OT_BITWISE: - option_dialog_option_bitwise_set(poption, option_bitwise_get(poption)); - break; - case OT_FONT: - option_dialog_option_font_set(poption, option_font_get(poption)); - break; - case OT_COLOR: - option_dialog_option_color_set(poption, option_color_get(poption)); - break; - case OT_VIDEO_MODE: - log_error("Option type %s (%d) not supported yet.", - option_type_name(option_type(poption)), - option_type(poption)); - break; - } - - gtk_widget_set_sensitive(option_get_gui_data(poption), - option_is_changeable(poption)); -} - -/************************************************************************//** - Reset the option. -****************************************************************************/ -static void option_dialog_option_reset(struct option *poption) -{ - switch (option_type(poption)) { - case OT_BOOLEAN: - option_dialog_option_bool_set(poption, option_bool_def(poption)); - break; - case OT_INTEGER: - option_dialog_option_int_set(poption, option_int_def(poption)); - break; - case OT_STRING: - option_dialog_option_str_set(poption, option_str_def(poption)); - break; - case OT_ENUM: - option_dialog_option_enum_set(poption, option_enum_def_int(poption)); - break; - case OT_BITWISE: - option_dialog_option_bitwise_set(poption, option_bitwise_def(poption)); - break; - case OT_FONT: - option_dialog_option_font_set(poption, option_font_def(poption)); - break; - case OT_COLOR: - option_dialog_option_color_set(poption, option_color_def(poption)); - break; - case OT_VIDEO_MODE: - log_error("Option type %s (%d) not supported yet.", - option_type_name(option_type(poption)), - option_type(poption)); - break; - } -} - -/************************************************************************//** - Apply the option change. -****************************************************************************/ -static void option_dialog_option_apply(struct option *poption) -{ - GtkWidget *w = GTK_WIDGET(option_get_gui_data(poption)); - - switch (option_type(poption)) { - case OT_BOOLEAN: - (void) option_bool_set(poption, gtk_toggle_button_get_active - (GTK_TOGGLE_BUTTON(w))); - break; - - case OT_INTEGER: - (void) option_int_set(poption, gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(w))); - break; - - case OT_STRING: - if (NULL != option_str_values(poption)) { - (void) option_str_set(poption, gtk_entry_get_text - (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(w))))); - } else { - (void) option_str_set(poption, gtk_entry_get_text(GTK_ENTRY(w))); - } - break; - - case OT_ENUM: - { - GtkTreeIter iter; - int value; - - if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(w), &iter)) { - break; - } - - gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(w)), - &iter, 0, &value, -1); - (void) option_enum_set_int(poption, value); - } - break; - - case OT_BITWISE: - { - GList *iter = g_object_get_data(G_OBJECT(w), "check_buttons"); - unsigned value = 0; - int bit; - - for (bit = 0; NULL != iter; iter = g_list_next(iter), bit++) { - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iter->data))) { - value |= 1 << bit; - } - } - (void) option_bitwise_set(poption, value); - } - break; - - case OT_FONT: - (void) option_font_set(poption, gtk_font_chooser_get_font - (GTK_FONT_CHOOSER(w))); - break; - - case OT_COLOR: - { - gchar *fg_color_text = NULL, *bg_color_text = NULL; - GObject *button; - GdkRGBA *color; - - /* Get foreground color. */ - button = g_object_get_data(G_OBJECT(w), "fg_button"); - color = g_object_get_data(button, "color"); - if (color) fg_color_text = gdk_rgba_to_string(color); - - /* Get background color. */ - button = g_object_get_data(G_OBJECT(w), "bg_button"); - color = g_object_get_data(button, "color"); - if (color) bg_color_text = gdk_rgba_to_string(color); - - (void) option_color_set(poption, - ft_color_construct(fg_color_text, bg_color_text)); - g_free(fg_color_text); - g_free(bg_color_text); - } - break; - - case OT_VIDEO_MODE: - log_error("Option type %s (%d) not supported yet.", - option_type_name(option_type(poption)), - option_type(poption)); - break; - } -} - -/************************************************************************//** - Popup the option dialog for the option set. -****************************************************************************/ -void option_dialog_popup(const char *name, const struct option_set *poptset) -{ - struct option_dialog *pdialog = option_dialog_get(poptset); - - if (NULL != pdialog) { - option_dialog_foreach(pdialog, option_dialog_option_refresh); - } else { - (void) option_dialog_new(name, poptset); - } -} - -/************************************************************************//** - Popdown the option dialog for the option set. -****************************************************************************/ -void option_dialog_popdown(const struct option_set *poptset) -{ - struct option_dialog *pdialog = option_dialog_get(poptset); - - if (NULL != pdialog) { - option_dialog_destroy(pdialog); - } -} - -/************************************************************************//** - Pass on updated option values to controls outside the main option - dialogs. -****************************************************************************/ -static void option_gui_update_extra(struct option *poption) -{ - if (option_optset(poption) == server_optset) { - if (strcmp(option_name(poption), "aifill") == 0) { - ai_fill_changed_by_server(option_int_get(poption)); - } else if (strcmp(option_name(poption), "nationset") == 0) { - nationset_sync_to_server(option_str_get(poption)); - } - } -} - -/************************************************************************//** - Update the GUI for the option. -****************************************************************************/ -void option_gui_update(struct option *poption) -{ - struct option_dialog *pdialog = option_dialog_get(option_optset(poption)); - - if (NULL != pdialog) { - option_dialog_option_refresh(poption); - } - - option_gui_update_extra(poption); -} - -/************************************************************************//** - Add the GUI for the option. -****************************************************************************/ -void option_gui_add(struct option *poption) -{ - struct option_dialog *pdialog = option_dialog_get(option_optset(poption)); - - if (NULL != pdialog) { - option_dialog_option_add(pdialog, poption, TRUE); - } - - option_gui_update_extra(poption); -} - -/************************************************************************//** - Remove the GUI for the option. -****************************************************************************/ -void option_gui_remove(struct option *poption) -{ - struct option_dialog *pdialog = option_dialog_get(option_optset(poption)); - - if (NULL != pdialog) { - option_dialog_option_remove(pdialog, poption); - } -} diff --git a/client/gui-gtk-3.0/optiondlg.h b/client/gui-gtk-3.0/optiondlg.h deleted file mode 100644 index 3019bda60e..0000000000 --- a/client/gui-gtk-3.0/optiondlg.h +++ /dev/null @@ -1,18 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__OPTIONDLG_H -#define FC__OPTIONDLG_H - -#include "optiondlg_g.h" - -#endif /* FC__OPTIONDLG_H */ diff --git a/client/gui-gtk-3.0/pages.c b/client/gui-gtk-3.0/pages.c deleted file mode 100644 index 7649d481f2..0000000000 --- a/client/gui-gtk-3.0/pages.c +++ /dev/null @@ -1,3570 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "dataio.h" -#include "game.h" -#include "mapimg.h" -#include "version.h" - -/* client */ -#include "client_main.h" -#include "climisc.h" -#include "clinet.h" -#include "connectdlg_common.h" -#include "packhand.h" -#include "servers.h" -#include "update_queue.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "connectdlg.h" -#include "dialogs.h" -#include "graphics.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "mapview.h" -#include "optiondlg.h" -#include "plrdlg.h" /* get_flag() */ -#include "repodlgs.h" -#include "voteinfo_bar.h" - -#include "pages.h" - - -static GtkWidget *scenario_description; -static GtkWidget *scenario_authors; -static GtkWidget *scenario_filename; -static GtkWidget *scenario_version; - -static GtkListStore *load_store, *scenario_store, *meta_store, *lan_store; - -static GtkListStore *server_playerlist_store; -static GtkWidget *server_playerlist_view; - -static GtkTreeSelection *load_selection, *scenario_selection; -static GtkTreeSelection *meta_selection, *lan_selection; - -/* This is the current page. Invalid value at start, to be sure that it won't - * be catch throught a switch() statement. */ -static enum client_pages current_page = -1; - -struct server_scan_timer_data -{ - struct server_scan *scan; - guint timer; -}; - -static struct server_scan_timer_data meta_scan = { NULL, 0 }; -static struct server_scan_timer_data lan_scan = { NULL, 0 }; - -static GtkWidget *statusbar, *statusbar_frame; -static GQueue *statusbar_queue; -static guint statusbar_timer = 0; - -static GtkWidget *ruleset_combo; - -static bool holding_srv_list_mutex = FALSE; - -static void connection_state_reset(void); - -/**********************************************************************//** - Spawn a server, if there isn't one, using the default settings. -**************************************************************************/ -static void start_new_game_callback(GtkWidget *w, gpointer data) -{ - if (is_server_running() || client_start_server()) { - /* saved settings are sent in client/options.c load_settable_options() */ - } -} - -/**********************************************************************//** - Go to the scenario page, spawning a server, -**************************************************************************/ -static void start_scenario_callback(GtkWidget *w, gpointer data) -{ - output_window_append(ftc_client, _("Compiling scenario list.")); - client_start_server_and_set_page(PAGE_SCENARIO); -} - -/**********************************************************************//** - Go to the load page, spawning a server. -**************************************************************************/ -static void load_saved_game_callback(GtkWidget *w, gpointer data) -{ - client_start_server_and_set_page(PAGE_LOAD); -} - -/**********************************************************************//** - Reset the connection status and switch to network page. -**************************************************************************/ -static void connect_network_game_callback(GtkWidget *w, gpointer data) -{ - connection_state_reset(); - set_client_page(PAGE_NETWORK); -} - -/**********************************************************************//** - Callback to open settings dialog. -**************************************************************************/ -static void open_settings(void) -{ - option_dialog_popup(_("Set local options"), client_optset); -} - -/**********************************************************************//** - Cancel, by terminating the connection and going back to main page. -**************************************************************************/ -static void main_callback(GtkWidget *w, gpointer data) -{ - enum client_pages page = PAGE_MAIN; - - if (client.conn.used) { - disconnect_from_server(); - } - if (page != get_client_page()) { - set_client_page(page); - } -} - -/**********************************************************************//** - This is called whenever the intro graphic needs a graphics refresh. -**************************************************************************/ -static gboolean intro_expose(GtkWidget *w, cairo_t *cr, gpointer *data) -{ - static PangoLayout *layout; - static int width, height; - static bool left = FALSE; - GtkAllocation allocation; - struct sprite *intro = (struct sprite *)data; - - cairo_set_source_surface(cr, intro->surface, 0, 0); - cairo_paint(cr); - - if (!layout) { - char msgbuf[128]; - const char *rev_ver; - - layout = pango_layout_new(gtk_widget_create_pango_context(w)); - pango_layout_set_font_description(layout, - pango_font_description_from_string("Sans Bold 10")); - - rev_ver = fc_git_revision(); - - if (rev_ver == NULL) { - /* TRANS: "version 2.6.0, gui-gtk-3.0 client" */ - fc_snprintf(msgbuf, sizeof(msgbuf), _("%s%s, %s client"), - word_version(), VERSION_STRING, client_string); - } else { - /* TRANS: "version 2.6.0 - * commit: [modified] - * gui-gtk-3.0 client" */ - fc_snprintf(msgbuf, sizeof(msgbuf), _("%s%s\ncommit: %s\n%s client"), - word_version(), VERSION_STRING, rev_ver, client_string); - left = TRUE; - } - pango_layout_set_text(layout, msgbuf, -1); - - pango_layout_get_pixel_size(layout, &width, &height); - } - gtk_widget_get_allocation(w, &allocation); - - cairo_set_source_rgb(cr, 0, 0, 0); - cairo_move_to(cr, left ? 4 : allocation.width - width - 3, - allocation.height - height - 3); - pango_cairo_show_layout(cr, layout); - - cairo_set_source_rgb(cr, 1, 1, 1); - cairo_move_to(cr, left ? 3 : allocation.width - width - 4, - allocation.height - height - 4); - pango_cairo_show_layout(cr, layout); - - return TRUE; -} - -/**********************************************************************//** - This is called when main page is getting destroyed. -**************************************************************************/ -static void intro_free(GtkWidget *w, gpointer *data) -{ - struct sprite *intro = (struct sprite *)data; - - free_sprite(intro); -} - -/**********************************************************************//** - Create the main page. -**************************************************************************/ -GtkWidget *create_main_page(void) -{ - GtkWidget *widget, *vbox, *frame, *darea, *button, *table; - GtkSizeGroup *size; - struct sprite *intro_in, *intro; - int width, height; - int sh; - int space_needed; - - size = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - widget = vbox; - - frame = gtk_frame_new(NULL); - g_object_set(frame, "margin", 18, NULL); - gtk_widget_set_halign(frame, GTK_ALIGN_CENTER); - gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT); - gtk_container_add(GTK_CONTAINER(vbox), frame); - - intro_in = load_gfxfile(tileset_main_intro_filename(tileset)); - get_sprite_dimensions(intro_in, &width, &height); - sh = screen_height(); - - if (sh <= 0) { - /* Assume some minimum height */ - sh = 600; - } - - space_needed = 250; -#if IS_BETA_VERSION - /* Beta notice takes extra space */ - space_needed += 50; -#endif - - if (sh - height < space_needed) { - float scale; - - if (sh < (space_needed + 0.2 * height)) { - /* Screen is simply too small, use minimum scale */ - scale = 0.2; - } else { - scale = (double)(sh - space_needed) / height; - } - height *= scale; - width *= scale; - intro = sprite_scale(intro_in, width, height); - free_sprite(intro_in); - } else { - intro = intro_in; - } - darea = gtk_drawing_area_new(); - gtk_widget_set_size_request(darea, width, height); - g_signal_connect(darea, "draw", - G_CALLBACK(intro_expose), intro); - g_signal_connect(widget, "destroy", - G_CALLBACK(intro_free), intro); - gtk_container_add(GTK_CONTAINER(frame), darea); - -#if IS_BETA_VERSION - { - GtkWidget *label; - - label = gtk_label_new(beta_message()); - gtk_widget_set_name(label, "beta_label"); - gtk_widget_set_halign(label, GTK_ALIGN_CENTER); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); - gtk_container_add(GTK_CONTAINER(vbox), label); - } -#endif /* IS_BETA_VERSION */ - - table = gtk_grid_new(); - g_object_set(table, "margin", 12, NULL); - gtk_widget_set_hexpand(table, TRUE); - gtk_widget_set_vexpand(table, TRUE); - gtk_widget_set_halign(table, GTK_ALIGN_CENTER); - gtk_widget_set_valign(table, GTK_ALIGN_CENTER); - - gtk_grid_set_row_spacing(GTK_GRID(table), 8); - gtk_grid_set_column_spacing(GTK_GRID(table), 18); - gtk_container_add(GTK_CONTAINER(vbox), table); - - button = gtk_button_new_with_mnemonic(_("Start _New Game")); - gtk_size_group_add_widget(size, button); - gtk_grid_attach(GTK_GRID(table), button, 0, 0, 1, 1); - g_signal_connect(button, "clicked", - G_CALLBACK(start_new_game_callback), NULL); - - button = gtk_button_new_with_mnemonic(_("Start _Scenario Game")); - gtk_size_group_add_widget(size, button); - gtk_grid_attach(GTK_GRID(table), button, 0, 1, 1, 1); - g_signal_connect(button, "clicked", - G_CALLBACK(start_scenario_callback), NULL); - - button = gtk_button_new_with_mnemonic(_("_Load Saved Game")); - gtk_size_group_add_widget(size, button); - gtk_grid_attach(GTK_GRID(table), button, 0, 2, 1, 1); - g_signal_connect(button, "clicked", - G_CALLBACK(load_saved_game_callback), NULL); - - button = gtk_button_new_with_mnemonic(_("C_onnect to Network Game")); - gtk_size_group_add_widget(size, button); - gtk_grid_attach(GTK_GRID(table), button, 1, 0, 1, 1); - g_signal_connect(button, "clicked", - G_CALLBACK(connect_network_game_callback), NULL); - - button = gtk_button_new_with_mnemonic(_("Client Settings")); - gtk_size_group_add_widget(size, button); - gtk_grid_attach(GTK_GRID(table), button, 1, 1, 1, 1); - g_signal_connect(button, "clicked", open_settings, NULL); - - button = gtk_button_new_from_stock(GTK_STOCK_QUIT); - gtk_size_group_add_widget(size, button); - g_object_unref(size); - gtk_grid_attach(GTK_GRID(table), button, 1, 2, 1, 1); - g_signal_connect(button, "clicked", - G_CALLBACK(quit_gtk_main), NULL); - - return widget; -} - -/**************************************************************************** - GENERIC SAVE DIALOG -****************************************************************************/ -typedef void (*save_dialog_action_fn_t) (const char *filename); -typedef struct fileinfo_list * (*save_dialog_files_fn_t) (void); - -struct save_dialog { - GtkDialog *shell; - GtkTreeView *tree_view; - GtkEntry *entry; - save_dialog_action_fn_t action; - save_dialog_files_fn_t files; -}; - -enum save_dialog_columns { - SD_COL_PRETTY_NAME = 0, - SD_COL_FULL_PATH, - - SD_COL_NUM -}; - -enum save_dialog_response { - SD_RES_BROWSE, - SD_RES_DELETE, - SD_RES_SAVE -}; - -/**********************************************************************//** - Create a new file list store. -**************************************************************************/ -static inline GtkListStore *save_dialog_store_new(void) -{ - return gtk_list_store_new(SD_COL_NUM, - G_TYPE_STRING, /* SD_COL_PRETTY_NAME */ - G_TYPE_STRING); /* SD_COL_FULL_PATH */ -} - -/**********************************************************************//** - Fill a file list store with 'files'. -**************************************************************************/ -static void save_dialog_store_update(GtkListStore *store, - const struct fileinfo_list *files) -{ - GtkTreeIter iter; - - gtk_list_store_clear(store); - fileinfo_list_iterate(files, pfile) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - SD_COL_PRETTY_NAME, pfile->name, - SD_COL_FULL_PATH, pfile->fullname, - -1); - } fileinfo_list_iterate_end; -} - -/**********************************************************************//** - Update a save dialog. -**************************************************************************/ -static void save_dialog_update(struct save_dialog *pdialog) -{ - struct fileinfo_list *files; - - fc_assert_ret(NULL != pdialog); - - /* Update the store. */ - files = pdialog->files(); - save_dialog_store_update(GTK_LIST_STORE - (gtk_tree_view_get_model(pdialog->tree_view)), - files); - fileinfo_list_destroy(files); -} - -/**********************************************************************//** - Callback for save_dialog_file_chooser_new(). -**************************************************************************/ -static void save_dialog_file_chooser_callback(GtkWidget *widget, - gint response, gpointer data) -{ - if (response == GTK_RESPONSE_OK) { - save_dialog_action_fn_t action = data; - gchar *filename = g_filename_to_utf8(gtk_file_chooser_get_filename - (GTK_FILE_CHOOSER(widget)), - -1, NULL, NULL, NULL); - - if (NULL != filename) { - action(filename); - g_free(filename); - } - } - gtk_widget_destroy(widget); -} - -/**********************************************************************//** - Create a file chooser for both the load and save commands. -**************************************************************************/ -static void save_dialog_file_chooser_popup(const char *title, - GtkFileChooserAction action, - save_dialog_action_fn_t cb) -{ - GtkWidget *filechoose; - - /* Create the chooser */ - filechoose = gtk_file_chooser_dialog_new(title, GTK_WINDOW(toplevel), action, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - action == GTK_FILE_CHOOSER_ACTION_SAVE ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, - GTK_RESPONSE_OK, NULL); - setup_dialog(filechoose, toplevel); - gtk_window_set_position(GTK_WINDOW(filechoose), GTK_WIN_POS_MOUSE); - - g_signal_connect(filechoose, "response", - G_CALLBACK(save_dialog_file_chooser_callback), cb); - - /* Display that dialog */ - gtk_window_present(GTK_WINDOW(filechoose)); -} - -/**********************************************************************//** - Handle save dialog response. -**************************************************************************/ -static void save_dialog_response_callback(GtkWidget *w, gint response, - gpointer data) -{ - struct save_dialog *pdialog = data; - - switch (response) { - case SD_RES_BROWSE: - save_dialog_file_chooser_popup(_("Select Location to Save"), - GTK_FILE_CHOOSER_ACTION_SAVE, - pdialog->action); - break; - case SD_RES_DELETE: - { - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - const gchar *full_path; - - selection = gtk_tree_view_get_selection(pdialog->tree_view); - if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { - return; - } - - gtk_tree_model_get(model, &iter, SD_COL_FULL_PATH, &full_path, -1); - fc_remove(full_path); - save_dialog_update(pdialog); - } - return; - case SD_RES_SAVE: - { - const char *text = gtk_entry_get_text(pdialog->entry); - gchar *filename = g_filename_from_utf8(text, -1, NULL, NULL, NULL); - - if (NULL == filename) { - return; - } - pdialog->action(filename); - g_free(filename); - } - break; - default: - break; - } - gtk_widget_destroy(GTK_WIDGET(pdialog->shell)); -} - -/**********************************************************************//** - Handle save list double click. -**************************************************************************/ -static void save_dialog_row_callback(GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gpointer data) -{ - save_dialog_response_callback(NULL, SD_RES_SAVE, data); -} - -/**********************************************************************//** - Handle save filename entry activation. -**************************************************************************/ -static void save_dialog_entry_callback(GtkEntry *entry, gpointer data) -{ - save_dialog_response_callback(NULL, SD_RES_SAVE, data); -} - -/**********************************************************************//** - Handle the save list selection changes. -**************************************************************************/ -static void save_dialog_list_callback(GtkTreeSelection *selection, - gpointer data) -{ - struct save_dialog *pdialog = data; - GtkTreeModel *model; - GtkTreeIter iter; - const gchar *filename; - - if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { - gtk_dialog_set_response_sensitive(pdialog->shell, SD_RES_DELETE, FALSE); - return; - } - - gtk_dialog_set_response_sensitive(pdialog->shell, SD_RES_DELETE, TRUE); - gtk_tree_model_get(model, &iter, SD_COL_PRETTY_NAME, &filename, -1); - gtk_entry_set_text(pdialog->entry, filename); -} - -/**********************************************************************//** - Create a new save dialog. -**************************************************************************/ -static GtkWidget *save_dialog_new(const char *title, const char *savelabel, - const char *savefilelabel, - save_dialog_action_fn_t action, - save_dialog_files_fn_t files) -{ - GtkWidget *shell, *sbox, *sw, *label, *view, *entry; - GtkContainer *vbox; - GtkListStore *store; - GtkCellRenderer *rend; - GtkTreeSelection *selection; - struct save_dialog *pdialog; - - fc_assert_ret_val(NULL != action, NULL); - fc_assert_ret_val(NULL != files, NULL); - - /* Save dialog structure. */ - pdialog = fc_malloc(sizeof(*pdialog)); - pdialog->action = action; - pdialog->files = files; - - /* Shell. */ - shell = gtk_dialog_new_with_buttons(title, NULL, 0, - _("_Browse..."), SD_RES_BROWSE, - GTK_STOCK_DELETE, SD_RES_DELETE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, SD_RES_SAVE, - NULL); - g_object_set_data_full(G_OBJECT(shell), "save_dialog", pdialog, - (GDestroyNotify) free); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CANCEL); - gtk_dialog_set_response_sensitive(GTK_DIALOG(shell), SD_RES_DELETE, FALSE); - setup_dialog(shell, toplevel); - g_signal_connect(shell, "response", - G_CALLBACK(save_dialog_response_callback), pdialog); - pdialog->shell = GTK_DIALOG(shell); - vbox = GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))); - - /* Tree view. */ - store = save_dialog_store_new(); - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(store); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - g_signal_connect(view, "row-activated", - G_CALLBACK(save_dialog_row_callback), pdialog); - pdialog->tree_view = GTK_TREE_VIEW(view); - - sbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(sbox), 2); - gtk_container_add(vbox, sbox); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", view, - "label", savelabel, - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_container_add(GTK_CONTAINER(sbox), label); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw), 300); - gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 300); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), view); - gtk_container_add(GTK_CONTAINER(sbox), sw); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); - g_signal_connect(selection, "changed", - G_CALLBACK(save_dialog_list_callback), pdialog); - - rend = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), - -1, NULL, rend, "text", - SD_COL_PRETTY_NAME, NULL); - - /* Entry. */ - entry = gtk_entry_new(); - gtk_widget_set_hexpand(entry, TRUE); - g_signal_connect(entry, "activate", - G_CALLBACK(save_dialog_entry_callback), pdialog); - pdialog->entry = GTK_ENTRY(entry); - - sbox = gtk_grid_new(); - g_object_set(sbox, "margin", 12, NULL); - gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(sbox), 2); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", entry, - "label", savefilelabel, - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_container_add(GTK_CONTAINER(sbox), label); - - gtk_container_add(GTK_CONTAINER(sbox), entry); - gtk_container_add(vbox, sbox); - - save_dialog_update(pdialog); - gtk_window_set_focus(GTK_WINDOW(shell), entry); - gtk_widget_show_all(GTK_WIDGET(vbox)); - return shell; -} - -/**************************************************************************** - NETWORK PAGE -****************************************************************************/ -static GtkWidget *network_login_label, *network_login; -static GtkWidget *network_host_label, *network_host; -static GtkWidget *network_port_label, *network_port; -static GtkWidget *network_password_label, *network_password; -static GtkWidget *network_confirm_password_label, *network_confirm_password; - -/**********************************************************************//** - Update a server list. -**************************************************************************/ -static void update_server_list(enum server_scan_type sstype, - const struct server_list *list) -{ - GtkTreeSelection *sel = NULL; - GtkTreeView *view; - GtkTreeIter it; - GtkListStore *store; - const gchar *host, *portstr; - int port; - - switch (sstype) { - case SERVER_SCAN_LOCAL: - sel = lan_selection; - break; - case SERVER_SCAN_GLOBAL: - sel = meta_selection; - break; - default: - break; - } - - if (!sel) { - return; - } - - view = gtk_tree_selection_get_tree_view(sel); - store = GTK_LIST_STORE(gtk_tree_view_get_model(view)); - gtk_list_store_clear(store); - - if (!list) { - return; - } - - host = gtk_entry_get_text(GTK_ENTRY(network_host)); - portstr = gtk_entry_get_text(GTK_ENTRY(network_port)); - port = atoi(portstr); - - server_list_iterate(list, pserver) { - char buf[20]; - - if (pserver->humans >= 0) { - fc_snprintf(buf, sizeof(buf), "%d", pserver->humans); - } else { - sz_strlcpy(buf, _("Unknown")); - } - gtk_list_store_append(store, &it); - gtk_list_store_set(store, &it, - 0, pserver->host, - 1, pserver->port, - 2, pserver->version, - 3, _(pserver->state), - 4, pserver->nplayers, - 5, buf, - 6, pserver->message, - -1); - if (strcmp(host, pserver->host) == 0 && port == pserver->port) { - gtk_tree_selection_select_iter(sel, &it); - } - } server_list_iterate_end; -} - -/**********************************************************************//** - Free the server scans. -**************************************************************************/ -void destroy_server_scans(void) -{ - if (meta_scan.scan) { - server_scan_finish(meta_scan.scan); - meta_scan.scan = NULL; - } - if (meta_scan.timer != 0) { - g_source_remove(meta_scan.timer); - meta_scan.timer = 0; - } - if (lan_scan.scan) { - server_scan_finish(lan_scan.scan); - lan_scan.scan = NULL; - } - if (lan_scan.timer != 0) { - g_source_remove(lan_scan.timer); - lan_scan.timer = 0; - } -} - -/**********************************************************************//** - This function updates the list of servers every so often. -**************************************************************************/ -static gboolean check_server_scan(gpointer data) -{ - struct server_scan_timer_data *scan_data = data; - struct server_scan *scan = scan_data->scan; - enum server_scan_status stat; - - if (!scan) { - return FALSE; - } - - stat = server_scan_poll(scan); - if (stat >= SCAN_STATUS_PARTIAL) { - enum server_scan_type type; - struct srv_list *srvrs; - - type = server_scan_get_type(scan); - srvrs = server_scan_get_list(scan); - fc_allocate_mutex(&srvrs->mutex); - holding_srv_list_mutex = TRUE; - update_server_list(type, srvrs->servers); - holding_srv_list_mutex = FALSE; - fc_release_mutex(&srvrs->mutex); - } - - if (stat == SCAN_STATUS_ERROR || stat == SCAN_STATUS_DONE) { - scan_data->timer = 0; - return FALSE; - } - return TRUE; -} - -/**********************************************************************//** - Callback function for when there's an error in the server scan. -**************************************************************************/ -static void server_scan_error(struct server_scan *scan, - const char *message) -{ - output_window_append(ftc_client, message); - log_error("%s", message); - - /* Main thread will finalize the scan later (or even concurrently) - - * do not do anything here to cause double free or raze condition. */ -} - -/**********************************************************************//** - Stop and restart the metaserver and lan server scans. -**************************************************************************/ -static void update_network_lists(void) -{ - destroy_server_scans(); - - meta_scan.scan = server_scan_begin(SERVER_SCAN_GLOBAL, server_scan_error); - meta_scan.timer = g_timeout_add(200, check_server_scan, &meta_scan); - - lan_scan.scan = server_scan_begin(SERVER_SCAN_LOCAL, server_scan_error); - lan_scan.timer = g_timeout_add(500, check_server_scan, &lan_scan); -} - -/************************************************************************** - Network connection state defines. -**************************************************************************/ -enum connection_state { - LOGIN_TYPE, - NEW_PASSWORD_TYPE, - ENTER_PASSWORD_TYPE, - WAITING_TYPE -}; - -static enum connection_state connection_status; - -/**********************************************************************//** - Update statusbar label text. -**************************************************************************/ -static gboolean update_network_statusbar(gpointer data) -{ - if (!g_queue_is_empty(statusbar_queue)) { - char *txt; - - txt = g_queue_pop_head(statusbar_queue); - gtk_label_set_text(GTK_LABEL(statusbar), txt); - free(txt); - } - - return TRUE; -} - -/**********************************************************************//** - Clear statusbar queue. -**************************************************************************/ -static void clear_network_statusbar(void) -{ - while (!g_queue_is_empty(statusbar_queue)) { - char *txt; - - txt = g_queue_pop_head(statusbar_queue); - free(txt); - } - gtk_label_set_text(GTK_LABEL(statusbar), ""); -} - -/**********************************************************************//** - Queue statusbar label text change. -**************************************************************************/ -void append_network_statusbar(const char *text, bool force) -{ - if (gtk_widget_get_visible(statusbar_frame)) { - if (force) { - clear_network_statusbar(); - gtk_label_set_text(GTK_LABEL(statusbar), text); - } else { - g_queue_push_tail(statusbar_queue, fc_strdup(text)); - } - } -} - -/**********************************************************************//** - Create statusbar. -**************************************************************************/ -GtkWidget *create_statusbar(void) -{ - statusbar_frame = gtk_frame_new(NULL); - gtk_frame_set_shadow_type(GTK_FRAME(statusbar_frame), GTK_SHADOW_IN); - - statusbar = gtk_label_new(""); - gtk_widget_set_margin_left(statusbar, 2); - gtk_widget_set_margin_right(statusbar, 2); - gtk_widget_set_margin_top(statusbar, 2); - gtk_widget_set_margin_bottom(statusbar, 2); - gtk_container_add(GTK_CONTAINER(statusbar_frame), statusbar); - - statusbar_queue = g_queue_new(); - statusbar_timer = g_timeout_add(2000, update_network_statusbar, NULL); - - return statusbar_frame; -} - -/**********************************************************************//** - Update network page connection state. -**************************************************************************/ -static void set_connection_state(enum connection_state state) -{ - switch (state) { - case LOGIN_TYPE: - append_network_statusbar("", FALSE); - - gtk_entry_set_text(GTK_ENTRY(network_password), ""); - gtk_entry_set_text(GTK_ENTRY(network_confirm_password), ""); - - gtk_widget_set_sensitive(network_host, TRUE); - gtk_widget_set_sensitive(network_port, TRUE); - gtk_widget_set_sensitive(network_login, TRUE); - gtk_widget_set_sensitive(network_password_label, FALSE); - gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("Pass_word:")); - gtk_widget_set_sensitive(network_password, FALSE); - gtk_widget_set_sensitive(network_confirm_password_label, FALSE); - gtk_widget_set_sensitive(network_confirm_password, FALSE); - break; - case NEW_PASSWORD_TYPE: - set_client_page(PAGE_NETWORK); - gtk_entry_set_text(GTK_ENTRY(network_password), ""); - gtk_entry_set_text(GTK_ENTRY(network_confirm_password), ""); - - gtk_widget_set_sensitive(network_host, FALSE); - gtk_widget_set_sensitive(network_port, FALSE); - gtk_widget_set_sensitive(network_login, FALSE); - gtk_widget_set_sensitive(network_password_label, TRUE); - gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("New Pass_word:")); - gtk_widget_set_sensitive(network_password, TRUE); - gtk_widget_set_sensitive(network_confirm_password_label, TRUE); - gtk_widget_set_sensitive(network_confirm_password, TRUE); - - gtk_widget_grab_focus(network_password); - break; - case ENTER_PASSWORD_TYPE: - set_client_page(PAGE_NETWORK); - gtk_entry_set_text(GTK_ENTRY(network_password), ""); - gtk_entry_set_text(GTK_ENTRY(network_confirm_password), ""); - - gtk_widget_set_sensitive(network_host, FALSE); - gtk_widget_set_sensitive(network_port, FALSE); - gtk_widget_set_sensitive(network_login, FALSE); - gtk_widget_set_sensitive(network_password_label, TRUE); - gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("Pass_word:")); - gtk_widget_set_sensitive(network_password, TRUE); - gtk_widget_set_sensitive(network_confirm_password_label, FALSE); - gtk_widget_set_sensitive(network_confirm_password, FALSE); - - gtk_widget_grab_focus(network_password); - break; - case WAITING_TYPE: - append_network_statusbar("", TRUE); - - gtk_widget_set_sensitive(network_login, FALSE); - gtk_widget_set_sensitive(network_password_label, FALSE); - gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("Pass_word:")); - gtk_widget_set_sensitive(network_password, FALSE); - gtk_widget_set_sensitive(network_confirm_password_label, FALSE); - gtk_widget_set_sensitive(network_confirm_password, FALSE); - break; - } - - connection_status = state; -} - -/**********************************************************************//** - Reset the connection state. -**************************************************************************/ -static void connection_state_reset(void) -{ - set_connection_state(LOGIN_TYPE); -} - -/**********************************************************************//** - Configure the dialog depending on what type of authentication request the - server is making. -**************************************************************************/ -void handle_authentication_req(enum authentication_type type, - const char *message) -{ - append_network_statusbar(message, TRUE); - - switch (type) { - case AUTH_NEWUSER_FIRST: - case AUTH_NEWUSER_RETRY: - set_connection_state(NEW_PASSWORD_TYPE); - return; - case AUTH_LOGIN_FIRST: - /* if we magically have a password already present in 'password' - * then, use that and skip the password entry dialog */ - if (password[0] != '\0') { - struct packet_authentication_reply reply; - - sz_strlcpy(reply.password, password); - send_packet_authentication_reply(&client.conn, &reply); - return; - } else { - set_connection_state(ENTER_PASSWORD_TYPE); - } - return; - case AUTH_LOGIN_RETRY: - set_connection_state(ENTER_PASSWORD_TYPE); - return; - } - - log_error("Unsupported authentication type %d: %s.", type, message); -} - -/**********************************************************************//** - If on the network page, switch page to the login page (with new server - and port). if on the login page, send connect and/or authentication - requests to the server. -**************************************************************************/ -static void connect_callback(GtkWidget *w, gpointer data) -{ - char errbuf [512]; - struct packet_authentication_reply reply; - - switch (connection_status) { - case LOGIN_TYPE: - sz_strlcpy(user_name, gtk_entry_get_text(GTK_ENTRY(network_login))); - sz_strlcpy(server_host, gtk_entry_get_text(GTK_ENTRY(network_host))); - server_port = atoi(gtk_entry_get_text(GTK_ENTRY(network_port))); - - if (connect_to_server(user_name, server_host, server_port, - errbuf, sizeof(errbuf)) != -1) { - } else { - append_network_statusbar(errbuf, TRUE); - - output_window_append(ftc_client, errbuf); - } - return; - case NEW_PASSWORD_TYPE: - if (w != network_password) { - sz_strlcpy(password, - gtk_entry_get_text(GTK_ENTRY(network_password))); - sz_strlcpy(reply.password, - gtk_entry_get_text(GTK_ENTRY(network_confirm_password))); - if (strncmp(reply.password, password, MAX_LEN_NAME) == 0) { - password[0] = '\0'; - send_packet_authentication_reply(&client.conn, &reply); - - set_connection_state(WAITING_TYPE); - } else { - append_network_statusbar(_("Passwords don't match, enter password."), - TRUE); - - set_connection_state(NEW_PASSWORD_TYPE); - } - } - return; - case ENTER_PASSWORD_TYPE: - sz_strlcpy(reply.password, - gtk_entry_get_text(GTK_ENTRY(network_password))); - send_packet_authentication_reply(&client.conn, &reply); - - set_connection_state(WAITING_TYPE); - return; - case WAITING_TYPE: - return; - } - - log_error("Unsupported connection status: %d", connection_status); -} - -/**********************************************************************//** - Connect on list item double-click. -**************************************************************************/ -static void network_activate_callback(GtkTreeView *view, - GtkTreePath *arg1, - GtkTreeViewColumn *arg2, - gpointer data) -{ - connect_callback(NULL, data); -} - -/**********************************************************************//** - Fills the server player list with the players in the given server, or - clears it if there is no player data. -**************************************************************************/ -static void update_server_playerlist(const struct server *pserver) -{ - GtkListStore *store; - GtkTreeIter iter; - int n, i; - - store = server_playerlist_store; - fc_assert_ret(store != NULL); - - gtk_list_store_clear(store); - if (!pserver || !pserver->players) { - return; - } - - n = pserver->nplayers; - for (i = 0; i < n; i++) { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - 0, pserver->players[i].name, - 1, pserver->players[i].type, - 2, pserver->players[i].host, - 3, pserver->players[i].nation, - -1); - } -} - -/**********************************************************************//** - Sets the host, port and player list of the selected server. -**************************************************************************/ -static void network_list_callback(GtkTreeSelection *select, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - const char *host; - int port; - char portstr[32]; - const struct server *pserver = NULL; - - if (!gtk_tree_selection_get_selected(select, &model, &it)) { - return; - } - - if (select == meta_selection) { - GtkTreePath *path; - struct srv_list *srvrs; - - srvrs = server_scan_get_list(meta_scan.scan); - path = gtk_tree_model_get_path(model, &it); - if (!holding_srv_list_mutex) { - /* We are not yet inside mutex protected block */ - fc_allocate_mutex(&srvrs->mutex); - } - if (srvrs->servers && path) { - gint pos = gtk_tree_path_get_indices(path)[0]; - pserver = server_list_get(srvrs->servers, pos); - } - if (!holding_srv_list_mutex) { - /* We are not yet inside mutex protected block */ - fc_release_mutex(&srvrs->mutex); - } - gtk_tree_path_free(path); - } - update_server_playerlist(pserver); - - gtk_tree_model_get(model, &it, 0, &host, 1, &port, -1); - - gtk_entry_set_text(GTK_ENTRY(network_host), host); - fc_snprintf(portstr, sizeof(portstr), "%d", port); - gtk_entry_set_text(GTK_ENTRY(network_port), portstr); -} - -/**********************************************************************//** - Update the network page. -**************************************************************************/ -static void update_network_page(void) -{ - char buf[256]; - - gtk_tree_selection_unselect_all(lan_selection); - gtk_tree_selection_unselect_all(meta_selection); - - gtk_entry_set_text(GTK_ENTRY(network_login), user_name); - gtk_entry_set_text(GTK_ENTRY(network_host), server_host); - fc_snprintf(buf, sizeof(buf), "%d", server_port); - gtk_entry_set_text(GTK_ENTRY(network_port), buf); -} - -/**********************************************************************//** - Create the network page. -**************************************************************************/ -GtkWidget *create_network_page(void) -{ - GtkWidget *box, *sbox, *bbox, *hbox, *notebook; - GtkWidget *button, *label, *view, *sw, *table; - GtkTreeSelection *selection; - GtkListStore *store; - - box = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(box), - GTK_ORIENTATION_VERTICAL); - gtk_container_set_border_width(GTK_CONTAINER(box), 4); - - notebook = gtk_notebook_new(); - gtk_container_add(GTK_CONTAINER(box), notebook); - - /* LAN pane. */ - lan_store = gtk_list_store_new(7, G_TYPE_STRING, /* host */ - G_TYPE_INT, /* port */ - G_TYPE_STRING, /* version */ - G_TYPE_STRING, /* state */ - G_TYPE_INT, /* nplayers */ - G_TYPE_STRING, /* humans */ - G_TYPE_STRING); /* message */ - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(lan_store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(lan_store); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view)); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - lan_selection = selection; - gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); - g_signal_connect(view, "focus", - G_CALLBACK(gtk_true), NULL); - g_signal_connect(view, "row-activated", - G_CALLBACK(network_activate_callback), NULL); - g_signal_connect(selection, "changed", - G_CALLBACK(network_list_callback), NULL); - - add_treeview_column(view, _("Server Name"), G_TYPE_STRING, 0); - add_treeview_column(view, _("Port"), G_TYPE_INT, 1); - add_treeview_column(view, _("Version"), G_TYPE_STRING, 2); - add_treeview_column(view, _("Status"), G_TYPE_STRING, 3); - add_treeview_column(view, Q_("?count:Players"), G_TYPE_INT, 4); - add_treeview_column(view, _("Humans"), G_TYPE_STRING, 5); - add_treeview_column(view, _("Comment"), G_TYPE_STRING, 6); - - label = gtk_label_new_with_mnemonic(_("Local _Area Network")); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_container_set_border_width(GTK_CONTAINER(sw), 4); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), view); - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, label); - - - /* Metaserver pane. */ - meta_store = gtk_list_store_new(7, G_TYPE_STRING, /* host */ - G_TYPE_INT, /* port */ - G_TYPE_STRING, /* version */ - G_TYPE_STRING, /* state */ - G_TYPE_INT, /* nplayers */ - G_TYPE_STRING, /* humans */ - G_TYPE_STRING); /* message */ - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(meta_store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(meta_store); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view)); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - meta_selection = selection; - gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); - g_signal_connect(view, "focus", - G_CALLBACK(gtk_true), NULL); - g_signal_connect(view, "row-activated", - G_CALLBACK(network_activate_callback), NULL); - g_signal_connect(selection, "changed", - G_CALLBACK(network_list_callback), NULL); - - add_treeview_column(view, _("Server Name"), G_TYPE_STRING, 0); - add_treeview_column(view, _("Port"), G_TYPE_INT, 1); - add_treeview_column(view, _("Version"), G_TYPE_STRING, 2); - add_treeview_column(view, _("Status"), G_TYPE_STRING, 3); - add_treeview_column(view, Q_("?count:Players"), G_TYPE_INT, 4); - add_treeview_column(view, _("Humans"), G_TYPE_STRING, 5); - add_treeview_column(view, _("Comment"), G_TYPE_STRING, 6); - - label = gtk_label_new_with_mnemonic(_("Internet _Metaserver")); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_container_set_border_width(GTK_CONTAINER(sw), 4); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), view); - if (GUI_GTK_OPTION(metaserver_tab_first)) { - gtk_notebook_prepend_page(GTK_NOTEBOOK(notebook), sw, label); - } else { - gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, label); - } - - /* Bottom part of the page, outside the inner notebook. */ - sbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(box), sbox); - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 12); - g_object_set(hbox, "margin", 8, NULL); - gtk_container_add(GTK_CONTAINER(sbox), hbox); - - table = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(table), 2); - gtk_grid_set_column_spacing(GTK_GRID(table), 12); - gtk_container_add(GTK_CONTAINER(hbox), table); - - network_host = gtk_entry_new(); - g_signal_connect(network_host, "activate", - G_CALLBACK(connect_callback), NULL); - gtk_grid_attach(GTK_GRID(table), network_host, 1, 0, 1, 1); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", network_host, - "label", _("_Host:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - network_host_label = label; - gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); - - network_port = gtk_entry_new(); - g_signal_connect(network_port, "activate", - G_CALLBACK(connect_callback), NULL); - gtk_grid_attach(GTK_GRID(table), network_port, 1, 1, 1, 1); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", network_port, - "label", _("_Port:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - network_port_label = label; - gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1); - - network_login = gtk_entry_new(); - gtk_widget_set_margin_top(network_login, 10); - g_signal_connect(network_login, "activate", - G_CALLBACK(connect_callback), NULL); - gtk_grid_attach(GTK_GRID(table), network_login, 1, 3, 1, 1); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", network_login, - "label", _("_Login:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_widget_set_margin_top(label, 10); - network_login_label = label; - gtk_grid_attach(GTK_GRID(table), label, 0, 3, 1, 1); - - network_password = gtk_entry_new(); - g_signal_connect(network_password, "activate", - G_CALLBACK(connect_callback), NULL); - gtk_entry_set_visibility(GTK_ENTRY(network_password), FALSE); - gtk_grid_attach(GTK_GRID(table), network_password, 1, 4, 1, 1); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", network_password, - "label", _("Pass_word:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - network_password_label = label; - gtk_grid_attach(GTK_GRID(table), label, 0, 4, 1, 1); - - network_confirm_password = gtk_entry_new(); - g_signal_connect(network_confirm_password, "activate", - G_CALLBACK(connect_callback), NULL); - gtk_entry_set_visibility(GTK_ENTRY(network_confirm_password), FALSE); - gtk_grid_attach(GTK_GRID(table), network_confirm_password, 1, 5, 1, 1); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", network_confirm_password, - "label", _("Conf_irm Password:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - network_confirm_password_label = label; - gtk_grid_attach(GTK_GRID(table), label, 0, 5, 1, 1); - - /* Server player list. */ - store = gtk_list_store_new(4, G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING); - server_playerlist_store = store; - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(view, TRUE); - add_treeview_column(view, _("Name"), G_TYPE_STRING, 0); - add_treeview_column(view, _("Type"), G_TYPE_STRING, 1); - add_treeview_column(view, _("Host"), G_TYPE_STRING, 2); - add_treeview_column(view, _("Nation"), G_TYPE_STRING, 3); - server_playerlist_view = view; - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), view); - gtk_container_add(GTK_CONTAINER(hbox), sw); - - - bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); - g_object_set(bbox, "margin", 2, NULL); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_set_spacing(GTK_BOX(bbox), 12); - gtk_container_add(GTK_CONTAINER(sbox), bbox); - - button = gtk_button_new_from_stock(GTK_STOCK_REFRESH); - gtk_container_add(GTK_CONTAINER(bbox), button); - gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), button, TRUE); - g_signal_connect(button, "clicked", - G_CALLBACK(update_network_lists), NULL); - - button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); - gtk_container_add(GTK_CONTAINER(bbox), button); - g_signal_connect(button, "clicked", - G_CALLBACK(main_callback), NULL); - - button = gtk_button_new_with_mnemonic(_("C_onnect")); - gtk_container_add(GTK_CONTAINER(bbox), button); - g_signal_connect(button, "clicked", - G_CALLBACK(connect_callback), NULL); - - return box; -} - - -/**************************************************************************** - START PAGE -****************************************************************************/ -GtkWidget *start_message_area; - -static GtkWidget *start_options_table; -static GtkWidget *observe_button, *ready_button, *nation_button; -static GtkTreeStore *connection_list_store; -static GtkTreeView *connection_list_view; -static GtkWidget *start_aifill_spin = NULL; -static GtkWidget *ai_lvl_combobox = NULL; - - -/* NB: Must match creation arguments in connection_list_store_new(). */ -enum connection_list_columns { - CL_COL_PLAYER_NUMBER = 0, - CL_COL_USER_NAME, - CL_COL_READY_STATE, - CL_COL_PLAYER_NAME, - CL_COL_FLAG, - CL_COL_COLOR, - CL_COL_NATION, - CL_COL_TEAM, - CL_COL_CONN_ID, - CL_COL_STYLE, - CL_COL_WEIGHT, - CL_COL_COLLAPSED, - - CL_NUM_COLUMNS -}; - -/**********************************************************************//** - Create a new tree store for connection list. -**************************************************************************/ -static inline GtkTreeStore *connection_list_store_new(void) -{ - return gtk_tree_store_new(CL_NUM_COLUMNS, - G_TYPE_INT, /* CL_COL_PLAYER_NUMBER */ - G_TYPE_STRING, /* CL_COL_USER_NAME */ - G_TYPE_BOOLEAN, /* CL_COL_READY_STATE */ - G_TYPE_STRING, /* CL_COL_PLAYER_NAME */ - GDK_TYPE_PIXBUF, /* CL_COL_FLAG */ - GDK_TYPE_PIXBUF, /* CL_COL_COLOR */ - G_TYPE_STRING, /* CL_COL_NATION */ - G_TYPE_STRING, /* CL_COL_TEAM */ - G_TYPE_INT, /* CL_COL_CONN_ID */ - G_TYPE_INT, /* CL_COL_STYLE */ - G_TYPE_INT, /* CL_COL_WEIGHT */ - G_TYPE_BOOLEAN); /* CL_COL_COLLAPSED */ -} - -/**********************************************************************//** - Maybe toggle AI of the player if the client could take the player. This - function shouldn't be used directly, see in client_take_player(). -**************************************************************************/ -static void client_aitoggle_player(void *data) -{ - struct player *pplayer = player_by_number(FC_PTR_TO_INT(data)); - - if (NULL != pplayer - && pplayer == client_player() - && !is_human(pplayer)) { - send_chat("/away"); - } -} - -/**********************************************************************//** - Send the /take command by chat and toggle AI if needed (after that). -**************************************************************************/ -static void client_take_player(struct player *pplayer) -{ - int request_id = send_chat_printf("/take \"%s\"", player_name(pplayer)); - void *data = FC_INT_TO_PTR(player_number(pplayer)); - - update_queue_connect_processing_finished(request_id, - client_aitoggle_player, data); -} - -/**********************************************************************//** - Connect the object to the player and the connection. -**************************************************************************/ -static void object_put(GObject *object, struct player *pplayer, - struct connection *pconn) -{ - /* Note that passing -1 to GINT_TO_POINTER() is buggy with some versions - * of gcc. player_slot_count() is not a valid player number. 0 is not - * a valid connection id (see comment in server/sernet.c: - * makeup_connection_name()). */ - g_object_set_data(object, "player_id", - GINT_TO_POINTER(NULL != pplayer - ? player_number(pplayer) - : player_slot_count())); - g_object_set_data(object, "connection_id", - GINT_TO_POINTER(NULL != pconn ? pconn->id : 0)); -} - -/**********************************************************************//** - Extract the player and the connection set with object_put(). Returns TRUE - if at least one of them isn't NULL. -**************************************************************************/ -static bool object_extract(GObject *object, struct player **ppplayer, - struct connection **ppconn) -{ - bool ret = FALSE; - int id; - - if (NULL != ppplayer) { - id = GPOINTER_TO_INT(g_object_get_data(object, "player_id")); - *ppplayer = player_by_number(id); - if (NULL != *ppplayer) { - ret = TRUE; - } - } - if (NULL != ppconn) { - id = GPOINTER_TO_INT(g_object_get_data(object, "connection_id")); - *ppconn = conn_by_number(id); - if (NULL != *ppconn) { - ret = TRUE; - } - } - - return ret; -} - -/**********************************************************************//** - Request the game options dialog. -**************************************************************************/ -static void game_options_callback(GtkWidget *w, gpointer data) -{ - option_dialog_popup(_("Game Settings"), server_optset); -} - -/**********************************************************************//** - AI skill setting callback. -**************************************************************************/ -static void ai_skill_callback(GtkWidget *w, gpointer data) -{ - enum ai_level *levels = (enum ai_level *)data; - const char *name; - int i; - - i = gtk_combo_box_get_active(GTK_COMBO_BOX(w)); - - if (i != -1) { - enum ai_level level = levels[i]; - - /* Suppress changes provoked by server rather than local user */ - if (server_ai_level() != level) { - name = ai_level_cmd(level); - send_chat_printf("/%s", name); - } - } -} - -/* HACK: sometimes when creating the ruleset combo the value is set without - * the user's control. In this case we don't want to do a /read. */ -static bool no_ruleset_callback = FALSE; - -/**********************************************************************//** - Ruleset name has been given -**************************************************************************/ -static void ruleset_selected(const char *name) -{ - if (name && name[0] != '\0' && !no_ruleset_callback) { - set_ruleset(name); - } -} - -/**********************************************************************//** - Ruleset selection callback. Note that this gets also called when ever - user types to entry box. In that case we don't want to set_ruleset() - after each letter. -**************************************************************************/ -static void ruleset_entry_changed(GtkWidget *w, gpointer data) -{ - const char *name = NULL; - - name = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(ruleset_combo)); - - if (name != NULL) { - ruleset_selected(name); - } -} - -/**********************************************************************//** - User changed AI fill setting. -**************************************************************************/ -static bool send_new_aifill_to_server = TRUE; -static void ai_fill_changed_by_user(GtkWidget *w, gpointer data) -{ - if (send_new_aifill_to_server) { - option_int_set(optset_option_by_name(server_optset, "aifill"), - gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w))); - } -} - -/**********************************************************************//** - Server changed AI fill setting. -**************************************************************************/ -void ai_fill_changed_by_server(int aifill) -{ - if (start_aifill_spin) { - bool old = send_new_aifill_to_server; - /* Suppress callback from this change to avoid a loop. */ - send_new_aifill_to_server = FALSE; - /* HACK: this GUI control doesn't have quite the same semantics as the - * server 'aifill' option, in that it claims to represent the minimum - * number of players _including humans_. The GUI control has a minimum - * value of 1, so aifill == 0 will not be represented correctly. - * But there's generally exactly one human player because the control - * only shows up for a locally spawned server, so we more or less - * get away with this. */ - gtk_spin_button_set_value(GTK_SPIN_BUTTON(start_aifill_spin), aifill); - send_new_aifill_to_server = old; - } -} - -/**********************************************************************//** - Update the start page. -**************************************************************************/ -void update_start_page(void) -{ - conn_list_dialog_update(); -} - -/**********************************************************************//** - Callback for when a team is chosen from the conn menu. -**************************************************************************/ -static void conn_menu_team_chosen(GObject *object, gpointer data) -{ - struct player *pplayer; - struct team_slot *tslot = data; - - if (object_extract(object, &pplayer, NULL) - && NULL != tslot - && team_slot_index(tslot) != team_number(pplayer->team)) { - send_chat_printf("/team \"%s\" \"%s\"", - player_name(pplayer), - team_slot_rule_name(tslot)); - } -} - -/**********************************************************************//** - Callback for when the "ready" entry is chosen from the conn menu. -**************************************************************************/ -static void conn_menu_ready_chosen(GObject *object, gpointer data) -{ - struct player *pplayer; - - if (object_extract(object, &pplayer, NULL)) { - dsend_packet_player_ready(&client.conn, - player_number(pplayer), !pplayer->is_ready); - } -} - -/**********************************************************************//** - Callback for when the pick-nation entry is chosen from the conn menu. -**************************************************************************/ -static void conn_menu_nation_chosen(GObject *object, gpointer data) -{ - struct player *pplayer; - - if (object_extract(object, &pplayer, NULL)) { - popup_races_dialog(pplayer); - } -} - -/**********************************************************************//** - Miscellaneous callback for the conn menu that allows an arbitrary command - (/observe, /remove, /hard, etc.) to be run on the player. -**************************************************************************/ -static void conn_menu_player_command(GObject *object, gpointer data) -{ - struct player *pplayer; - - if (object_extract(object, &pplayer, NULL)) { - send_chat_printf("/%s \"%s\"", - (char *) g_object_get_data(G_OBJECT(data), "command"), - player_name(pplayer)); - } -} - -/**********************************************************************//** - Take command in the conn menu. -**************************************************************************/ -static void conn_menu_player_take(GObject *object, gpointer data) -{ - struct player *pplayer; - - if (object_extract(object, &pplayer, NULL)) { - client_take_player(pplayer); - } -} - -/**********************************************************************//** - Miscellaneous callback for the conn menu that allows an arbitrary command - (/cmdlevel, /cut, etc.) to be run on the connection. -**************************************************************************/ -static void conn_menu_connection_command(GObject *object, gpointer data) -{ - struct connection *pconn; - - if (object_extract(object, NULL, &pconn)) { - send_chat_printf("/%s \"%s\"", - (char *) g_object_get_data(G_OBJECT(data), "command"), - pconn->username); - } -} - -/**********************************************************************//** - Show details about a user in the Connected Users dialog in a popup. -**************************************************************************/ -static void show_conn_popup(struct player *pplayer, struct connection *pconn) -{ - GtkWidget *popup; - char buf[4096] = ""; - - if (pconn) { - cat_snprintf(buf, sizeof(buf), _("Connection name: %s"), - pconn->username); - } else { - cat_snprintf(buf, sizeof(buf), _("Player name: %s"), - player_name(pplayer)); - } - cat_snprintf(buf, sizeof(buf), "\n"); - if (pconn) { - cat_snprintf(buf, sizeof(buf), _("Host: %s"), pconn->addr); - } - cat_snprintf(buf, sizeof(buf), "\n"); - - /* Show popup. */ - popup = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, - "%s", buf); - gtk_window_set_title(GTK_WINDOW(popup), _("Player/conn info")); - setup_dialog(popup, toplevel); - g_signal_connect(popup, "response", G_CALLBACK(gtk_widget_destroy), NULL); - gtk_window_present(GTK_WINDOW(popup)); -} - -/**********************************************************************//** - Callback for when the "info" entry is chosen from the conn menu. -**************************************************************************/ -static void conn_menu_info_chosen(GObject *object, gpointer data) -{ - struct player *pplayer; - struct connection *pconn; - - if (object_extract(object, &pplayer, &pconn)) { - show_conn_popup(pplayer, pconn); - } -} - -/**********************************************************************//** - Called when you click on a player; this function pops up a menu - to allow changing the team. -**************************************************************************/ -static GtkWidget *create_conn_menu(struct player *pplayer, - struct connection *pconn) -{ - GtkWidget *menu; - GtkWidget *item; - gchar *buf; - - menu = gtk_menu_new(); - object_put(G_OBJECT(menu), pplayer, pconn); - - buf = g_strdup_printf(_("%s info"), - pconn ? pconn->username : player_name(pplayer)); - item = gtk_menu_item_new_with_label(buf); - g_free(buf); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_info_chosen), menu); - - if (NULL != pplayer) { - item = gtk_menu_item_new_with_label(_("Toggle player ready")); - gtk_widget_set_sensitive(item, is_human(pplayer)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_ready_chosen), menu); - - item = gtk_menu_item_new_with_label(_("Pick nation")); - gtk_widget_set_sensitive(item, - can_conn_edit_players_nation(&client.conn, - pplayer)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_nation_chosen), menu); - - item = gtk_menu_item_new_with_label(_("Observe this player")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data_full(G_OBJECT(item), "command", g_strdup("observe"), - (GDestroyNotify) g_free); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_player_command), menu); - - item = gtk_menu_item_new_with_label(_("Take this player")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_player_take), menu); - } - - if (ALLOW_CTRL <= client.conn.access_level && NULL != pconn - && (pconn->id != client.conn.id || NULL != pplayer)) { - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - if (pconn->id != client.conn.id) { - item = gtk_menu_item_new_with_label(_("Cut connection")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data_full(G_OBJECT(item), "command", g_strdup("cut"), - (GDestroyNotify) g_free); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_connection_command), - menu); - } - } - - if (ALLOW_CTRL <= client.conn.access_level && NULL != pplayer) { - item = gtk_menu_item_new_with_label(_("Aitoggle player")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data_full(G_OBJECT(item), "command", g_strdup("aitoggle"), - (GDestroyNotify) g_free); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_player_command), menu); - - if (pplayer != client.conn.playing && game.info.is_new_game) { - item = gtk_menu_item_new_with_label(_("Remove player")); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data_full(G_OBJECT(item), "command", g_strdup("remove"), - (GDestroyNotify) g_free); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_player_command), menu); - } - } - - if (ALLOW_ADMIN <= client.conn.access_level && NULL != pconn - && pconn->id != client.conn.id) { - enum cmdlevel level; - - /* No item for hack access; that would be a serious security hole. */ - for (level = cmdlevel_min(); level < client.conn.access_level; level++) { - /* TRANS: Give access level to a connection. */ - buf = g_strdup_printf(_("Give %s access"), - cmdlevel_name(level)); - item = gtk_menu_item_new_with_label(buf); - g_free(buf); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data_full(G_OBJECT(item), "command", - g_strdup_printf("cmdlevel %s", - cmdlevel_name(level)), - (GDestroyNotify) g_free); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_connection_command), - menu); - } - } - - if (ALLOW_CTRL <= client.conn.access_level - && NULL != pplayer && is_ai(pplayer)) { - enum ai_level level; - - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - for (level = 0; level < AI_LEVEL_COUNT; level++) { - if (is_settable_ai_level(level)) { - const char *level_name = ai_level_translated_name(level); - const char *level_cmd = ai_level_cmd(level); - - item = gtk_menu_item_new_with_label(level_name); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_object_set_data_full(G_OBJECT(item), "command", - g_strdup(level_cmd), (GDestroyNotify) g_free); - g_signal_connect_swapped(item, "activate", - G_CALLBACK(conn_menu_player_command), menu); - } - } - } - - if (pplayer && game.info.is_new_game) { - const int count = pplayer->team - ? player_list_size(team_members(pplayer->team)) : 0; - bool need_empty_team = (count != 1); - - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - /* Can't use team_iterate here since it skips empty teams. */ - team_slots_iterate(tslot) { - if (!team_slot_is_used(tslot)) { - if (!need_empty_team) { - continue; - } - need_empty_team = FALSE; - } - - /* TRANS: e.g., "Put on Team 5" */ - buf = g_strdup_printf(_("Put on %s"), - team_slot_name_translation(tslot)); - item = gtk_menu_item_new_with_label(buf); - g_free(buf); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - object_put(G_OBJECT(item), pplayer, NULL); - g_signal_connect(item, "activate", G_CALLBACK(conn_menu_team_chosen), - tslot); - } team_slots_iterate_end; - } - - gtk_widget_show_all(menu); - - return menu; -} - -/**********************************************************************//** - Unselect a tree path. -**************************************************************************/ -static gboolean delayed_unselect_path(gpointer data) -{ - if (NULL != connection_list_view) { - GtkTreeSelection *selection = - gtk_tree_view_get_selection(connection_list_view); - GtkTreePath *path = data; - - gtk_tree_selection_unselect_path(selection, path); - gtk_tree_path_free(path); - } - return FALSE; -} - -/**********************************************************************//** - Called on a button event on the pregame player list. -**************************************************************************/ -static gboolean connection_list_event(GtkWidget *widget, - GdkEventButton *event, - gpointer data) -{ - GtkTreeView *tree = GTK_TREE_VIEW(widget); - GtkTreePath *path = NULL; - GtkTreeSelection *selection = gtk_tree_view_get_selection(tree); - gboolean ret = FALSE; - - if ((1 != event->button && 3 != event->button) - || GDK_BUTTON_PRESS != event->type - || !gtk_tree_view_get_path_at_pos(tree, - event->x, event->y, - &path, NULL, NULL, NULL)) { - return FALSE; - } - - if (1 == event->button) { - if (gtk_tree_selection_path_is_selected(selection, path)) { - /* Need to delay to avoid problem with the expander. */ - g_idle_add(delayed_unselect_path, path); - return FALSE; /* Return now, don't free the path. */ - } - } else if (3 == event->button) { - GtkTreeModel *model = gtk_tree_view_get_model(tree); - GtkTreeIter iter; - GtkWidget *menu; - int player_no, conn_id; - struct player *pplayer; - struct connection *pconn; - - if (!gtk_tree_selection_path_is_selected(selection, path)) { - gtk_tree_selection_select_path(selection, path); - } - gtk_tree_model_get_iter(model, &iter, path); - - gtk_tree_model_get(model, &iter, CL_COL_PLAYER_NUMBER, &player_no, -1); - pplayer = player_by_number(player_no); - - gtk_tree_model_get(model, &iter, CL_COL_CONN_ID, &conn_id, -1); - pconn = conn_by_number(conn_id); - - menu = create_conn_menu(pplayer, pconn); - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, - NULL, NULL, event->button, event->time); - ret = TRUE; - } - - gtk_tree_path_free(path); - return ret; -} - -/**********************************************************************//** - Mark a row as collapsed or expanded. -**************************************************************************/ -static void connection_list_row_callback(GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path, - gpointer data) -{ - GtkTreeStore *store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view)); - - gtk_tree_store_set(store, iter, - CL_COL_COLLAPSED, GPOINTER_TO_INT(data), -1); -} - -/**********************************************************************//** - Returns TRUE if a row is selected in the connection/player list. Fills - the not null data. -**************************************************************************/ -static bool conn_list_selection(struct player **ppplayer, - struct connection **ppconn) -{ - if (NULL != connection_list_view) { - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeSelection *selection = - gtk_tree_view_get_selection(connection_list_view); - - if (gtk_tree_selection_get_selected(selection, &model, &iter)) { - int id; - - if (NULL != ppplayer) { - gtk_tree_model_get(model, &iter, CL_COL_PLAYER_NUMBER, &id, -1); - *ppplayer = player_by_number(id); - } - if (NULL != ppconn) { - gtk_tree_model_get(model, &iter, CL_COL_CONN_ID, &id, -1); - *ppconn = conn_by_number(id); - } - return TRUE; - } - } - - if (NULL != ppplayer) { - *ppplayer = NULL; - } - if (NULL != ppconn) { - *ppconn = NULL; - } - return FALSE; -} - -/**********************************************************************//** - Returns TRUE if a row is selected in the connection/player list. Fills - the not null data. -**************************************************************************/ -static void conn_list_select_conn(struct connection *pconn) -{ - GtkTreeModel *model; - GtkTreeIter parent, child, *iter = NULL; - GtkTreeSelection *selection; - gboolean valid; - const int search_id = pconn->id; - int id; - - if (NULL == connection_list_view) { - return; - } - - model = gtk_tree_view_get_model(connection_list_view); - selection = gtk_tree_view_get_selection(connection_list_view); - - /* Main iteration. */ - valid = gtk_tree_model_get_iter_first(model, &parent); - while (valid && NULL == iter) { - gtk_tree_model_get(model, &parent, CL_COL_CONN_ID, &id, -1); - if (search_id == id) { - iter = &parent; - break; - } - - /* Node children iteration. */ - valid = gtk_tree_model_iter_children(model, &child, &parent); - while (valid && NULL == iter) { - gtk_tree_model_get(model, &child, CL_COL_CONN_ID, &id, -1); - if (search_id == id) { - iter = &child; - break; - } - valid = gtk_tree_model_iter_next(model, &child); - } - - valid = gtk_tree_model_iter_next(model, &parent); - } - - /* Select iterator. */ - if (NULL != iter) { - gtk_tree_selection_select_iter(selection, iter); - } else { - log_error("%s(): connection %s not found.", - __FUNCTION__, conn_description(pconn)); - } -} - -/**********************************************************************//** - 'ready_button' clicked callback. -**************************************************************************/ -static void ready_button_callback(GtkWidget *w, gpointer data) -{ - if (can_client_control()) { - dsend_packet_player_ready(&client.conn, - player_number(client_player()), - !client_player()->is_ready); - } else { - dsend_packet_player_ready(&client.conn, 0, TRUE); - } -} - -/**********************************************************************//** - 'nation_button' clicked callback. -**************************************************************************/ -static void nation_button_callback(GtkWidget *w, gpointer data) -{ - struct player *selected_plr; - bool row_selected = conn_list_selection(&selected_plr, NULL); - - if (row_selected && NULL != selected_plr) { - /* "Take " */ - client_take_player(selected_plr); - } else if (can_client_control()) { - /* "Pick Nation" */ - popup_races_dialog(client_player()); - } else { - /* "Take a Player" */ - send_chat("/take -"); - } -} - -/**********************************************************************//** - 'observe_button' clicked callback. -**************************************************************************/ -static void observe_button_callback(GtkWidget *w, gpointer data) -{ - struct player *selected_plr; - bool row_selected = conn_list_selection(&selected_plr, NULL); - - if (row_selected && NULL != selected_plr) { - /* "Observe " */ - send_chat_printf("/observe \"%s\"", player_name(selected_plr)); - } else if (!client_is_global_observer()) { - /* "Observe" */ - send_chat("/observe"); - } else { - /* "Do not observe" */ - send_chat("/detach"); - } -} - -/**********************************************************************//** - Update the buttons of the start page. -**************************************************************************/ -static void update_start_page_buttons(void) -{ - char buf[2 * MAX_LEN_NAME]; - const char *text; - struct player *selected_plr; - bool row_selected = conn_list_selection(&selected_plr, NULL); - bool sensitive; - - /*** Ready button. ***/ - if (can_client_control()) { - sensitive = TRUE; - if (client_player()->is_ready) { - text = _("Not _ready"); - } else { - int num_unready = 0; - - players_iterate(pplayer) { - if (is_human(pplayer) && !pplayer->is_ready) { - num_unready++; - } - } players_iterate_end; - - if (num_unready > 1) { - text = _("_Ready"); - } else { - /* We are the last unready player so clicking here will - * immediately start the game. */ - text = _("_Start"); - } - } - } else { - text = _("_Start"); - if (can_client_access_hack()) { - sensitive = TRUE; - players_iterate(plr) { - if (is_human(plr)) { - /* There's human controlled player(s) in game, so it's their - * job to start the game. */ - sensitive = FALSE; - break; - } - } players_iterate_end; - } else { - sensitive = FALSE; - } - } - gtk_stockbutton_set_label(ready_button, text); - gtk_widget_set_sensitive(ready_button, sensitive); - - /*** Nation button. ***/ - if (row_selected && NULL != selected_plr) { - fc_snprintf(buf, sizeof(buf), _("_Take %s"), player_name(selected_plr)); - text = buf; - sensitive = (client_is_observer() || selected_plr != client_player()); - } else if (can_client_control()) { - text = _("Pick _Nation"); - sensitive = game.info.is_new_game; - } else { - text = _("_Take a Player"); - sensitive = game.info.is_new_game; - } - gtk_stockbutton_set_label(nation_button, text); - gtk_widget_set_sensitive(nation_button, sensitive); - - /*** Observe button. ***/ - if (row_selected && NULL != selected_plr) { - fc_snprintf(buf, sizeof(buf), _("_Observe %s"), - player_name(selected_plr)); - text = buf; - sensitive = (!client_is_observer() || selected_plr != client_player()); - } else if (!client_is_global_observer()) { - text = _("_Observe"); - sensitive = TRUE; - } else { - text = _("Do not _observe"); - sensitive = TRUE; - } - gtk_stockbutton_set_label(observe_button, text); - gtk_widget_set_sensitive(observe_button, sensitive); -} - -/**********************************************************************//** - Search a player iterator in the model. Begin the iteration at 'start' or - at the start of the model if 'start' is set to NULL. -**************************************************************************/ -static bool model_get_player_iter(GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *start, - const struct player *pplayer) -{ - const int search_id = player_number(pplayer); - int id; - - if (NULL != start) { - *iter = *start; - if (!gtk_tree_model_iter_next(model, iter)) { - return FALSE; - } - } else if (!gtk_tree_model_get_iter_first(model, iter)) { - return FALSE; - } - - do { - gtk_tree_model_get(model, iter, CL_COL_PLAYER_NUMBER, &id, -1); - if (id == search_id) { - return TRUE; - } - } while (gtk_tree_model_iter_next(model, iter)); - - return FALSE; -} - -/**********************************************************************//** - Search a connection iterator in the model. Begin the iteration at 'start' - or at the start of the model if 'start' is set to NULL. -**************************************************************************/ -static bool model_get_conn_iter(GtkTreeModel *model, GtkTreeIter *iter, - GtkTreeIter *parent, GtkTreeIter *start, - const struct connection *pconn) -{ - const int search_id = pconn->id; - int id; - - if (NULL != start) { - *iter = *start; - if (!gtk_tree_model_iter_next(model, iter)) { - return FALSE; - } - } else if (!gtk_tree_model_iter_children(model, iter, parent)) { - return FALSE; - } - - do { - gtk_tree_model_get(model, iter, CL_COL_CONN_ID, &id, -1); - if (id == search_id) { - return TRUE; - } - } while (gtk_tree_model_iter_next(model, iter)); - - return FALSE; -} - -/**********************************************************************//** - Update the connected users list at pregame state. -**************************************************************************/ -void real_conn_list_dialog_update(void *unused) -{ - if (client_state() == C_S_PREPARING - && get_client_page() == PAGE_START - && connection_list_store != NULL) { - GtkTreeStore *store = connection_list_store; - GtkTreeModel *model = GTK_TREE_MODEL(store); - GtkTreePath *path; - GtkTreeIter child, prev_child, *pprev_child; - GtkTreeIter parent, prev_parent, *pprev_parent = NULL; - GdkPixbuf *flag, *color; - gboolean collapsed; - struct player *pselected_player; - struct connection *pselected_conn; - bool is_ready; - const char *nation, *plr_name, *team; - char name[MAX_LEN_NAME + 8]; - enum cmdlevel access_level; - int conn_id; - - /* Refresh the AI skill level control */ - if (ai_lvl_combobox) { - enum ai_level new_level = server_ai_level(), level; - int i = 0; - - for (level = 0; level < AI_LEVEL_COUNT; level++) { - if (is_settable_ai_level(level)) { - if (level == new_level) { - gtk_combo_box_set_active(GTK_COMBO_BOX(ai_lvl_combobox), i); - break; - } - i++; - } - } - if (level == AI_LEVEL_COUNT) { - /* Probably ai_level_invalid() */ - gtk_combo_box_set_active(GTK_COMBO_BOX(ai_lvl_combobox), -1); - } - } - - /* Save the selected connection. */ - (void) conn_list_selection(&pselected_player, &pselected_conn); - - /* Insert players into the connection list. */ - players_iterate(pplayer) { - if (!player_has_flag(pplayer, PLRF_SCENARIO_RESERVED)) { - conn_id = -1; - access_level = ALLOW_NONE; - flag = pplayer->nation ? get_flag(pplayer->nation) : NULL; - color = create_player_icon(pplayer); - - conn_list_iterate(pplayer->connections, pconn) { - if (pconn->playing == pplayer && !pconn->observer) { - conn_id = pconn->id; - access_level = pconn->access_level; - break; - } - } conn_list_iterate_end; - - if (is_ai(pplayer) && !pplayer->was_created - && !pplayer->is_connected) { - /* TRANS: "" */ - fc_snprintf(name, sizeof(name), _("<%s AI>"), - ai_level_translated_name(pplayer->ai_common.skill_level)); - } else { - sz_strlcpy(name, pplayer->username); - if (access_level > ALLOW_BASIC) { - sz_strlcat(name, "*"); - } - } - - is_ready = !is_human(pplayer) ? TRUE : pplayer->is_ready; - - if (pplayer->nation == NO_NATION_SELECTED) { - nation = _("Random"); - if (pplayer->was_created) { - plr_name = player_name(pplayer); - } else { - plr_name = ""; - } - } else { - nation = nation_adjective_for_player(pplayer); - plr_name = player_name(pplayer); - } - - team = pplayer->team ? team_name_translation(pplayer->team) : ""; - - if (model_get_player_iter(model, &parent, pprev_parent, pplayer)) { - gtk_tree_store_move_after(store, &parent, pprev_parent); - } else { - gtk_tree_store_insert_after(store, &parent, NULL, pprev_parent); - } - - gtk_tree_store_set(store, &parent, - CL_COL_PLAYER_NUMBER, player_number(pplayer), - CL_COL_USER_NAME, name, - CL_COL_READY_STATE, is_ready, - CL_COL_PLAYER_NAME, plr_name, - CL_COL_FLAG, flag, - CL_COL_COLOR, color, - CL_COL_NATION, nation, - CL_COL_TEAM, team, - CL_COL_CONN_ID, conn_id, - CL_COL_STYLE, PANGO_STYLE_NORMAL, - CL_COL_WEIGHT, PANGO_WEIGHT_BOLD, - -1); - - /* Insert observers of this player as child nodes. */ - pprev_child = NULL; - conn_list_iterate(pplayer->connections, pconn) { - if (pconn->id == conn_id) { - continue; - } - if (model_get_conn_iter(model, &child, &parent, - pprev_child, pconn)) { - gtk_tree_store_move_after(store, &child, pprev_child); - } else { - gtk_tree_store_insert_after(store, &child, &parent, pprev_child); - } - - gtk_tree_store_set(store, &child, - CL_COL_PLAYER_NUMBER, -1, - CL_COL_USER_NAME, pconn->username, - CL_COL_TEAM, _("Observer"), - CL_COL_CONN_ID, pconn->id, - CL_COL_STYLE, PANGO_STYLE_NORMAL, - CL_COL_WEIGHT, PANGO_WEIGHT_NORMAL, - -1); - - prev_child = child; - pprev_child = &prev_child; - } conn_list_iterate_end; - - /* Expand node? */ - if (NULL != pprev_child) { - gtk_tree_model_get(model, &parent, CL_COL_COLLAPSED, &collapsed, -1); - if (!collapsed) { - path = gtk_tree_model_get_path(model, &parent); - gtk_tree_view_expand_row(GTK_TREE_VIEW(connection_list_view), - path, FALSE); - gtk_tree_path_free(path); - } - } - - /* Remove trailing rows. */ - if (NULL != pprev_child) { - child = prev_child; - if (gtk_tree_model_iter_next(model, &child)) { - while (gtk_tree_store_remove(store, &child)) { - /* Do nothing more. */ - } - } - } else if (gtk_tree_model_iter_children(model, &child, &parent)) { - while (gtk_tree_store_remove(store, &child)) { - /* Do nothing more. */ - } - } - - prev_parent = parent; - pprev_parent = &prev_parent; - if (flag) { - g_object_unref(flag); - } - if (color) { - g_object_unref(color); - } - } - } players_iterate_end; - - /* Finally, insert global observers... */ - conn_list_iterate(game.est_connections, pconn) { - if (NULL != pconn->playing || !pconn->observer) { - continue; - } - - if (model_get_conn_iter(model, &parent, NULL, pprev_parent, pconn)) { - gtk_tree_store_move_after(store, &parent, pprev_parent); - } else { - gtk_tree_store_insert_after(store, &parent, NULL, pprev_parent); - } - - gtk_tree_store_set(store, &parent, - CL_COL_PLAYER_NUMBER, -1, - CL_COL_USER_NAME, pconn->username, - CL_COL_TEAM, _("Observer"), - CL_COL_CONN_ID, pconn->id, - CL_COL_STYLE, PANGO_STYLE_NORMAL, - CL_COL_WEIGHT, PANGO_WEIGHT_NORMAL, - -1); - - prev_parent = parent; - pprev_parent = &prev_parent; - } conn_list_iterate_end; - - /* ...and detached connections. */ - conn_list_iterate(game.est_connections, pconn) { - if (NULL != pconn->playing || pconn->observer) { - continue; - } - - if (model_get_conn_iter(model, &parent, NULL, pprev_parent, pconn)) { - gtk_tree_store_move_after(store, &parent, pprev_parent); - } else { - gtk_tree_store_insert_after(store, &parent, NULL, pprev_parent); - } - - gtk_tree_store_set(store, &parent, - CL_COL_PLAYER_NUMBER, -1, - CL_COL_USER_NAME, pconn->username, - CL_COL_TEAM, _("Detached"), - CL_COL_CONN_ID, pconn->id, - CL_COL_STYLE, PANGO_STYLE_ITALIC, - CL_COL_WEIGHT, PANGO_WEIGHT_NORMAL, - -1); - - prev_parent = parent; - pprev_parent = &prev_parent; - } conn_list_iterate_end; - - /* Remove trailing rows. */ - if (NULL != pprev_parent) { - parent = prev_parent; - if (gtk_tree_model_iter_next(model, &parent)) { - while (gtk_tree_store_remove(store, &parent)) { - /* Do nothing more. */ - } - } - } else { - gtk_tree_store_clear(store); - } - - /* If we were selecting a single connection, let's try to reselect it. */ - if (NULL == pselected_player && NULL != pselected_conn) { - conn_list_select_conn(pselected_conn); - } - } - - update_start_page_buttons(); -} - -/**********************************************************************//** - Helper function for adding columns to a tree view. If 'key' is not NULL - then the added column is added to the object data of the treeview using - g_object_set_data under 'key'. -**************************************************************************/ -static void add_tree_col(GtkWidget *treeview, GType gtype, - const char *title, int colnum, const char *key) -{ - GtkCellRenderer *rend; - GtkTreeViewColumn *col; - - if (gtype == G_TYPE_BOOLEAN) { - rend = gtk_cell_renderer_toggle_new(); - col = gtk_tree_view_column_new_with_attributes(title, rend, - "active", colnum, NULL); - } else if (gtype == GDK_TYPE_PIXBUF) { - rend = gtk_cell_renderer_pixbuf_new(); - col = gtk_tree_view_column_new_with_attributes(title, rend, - "pixbuf", colnum, NULL); - } else { - rend = gtk_cell_renderer_text_new(); - col = gtk_tree_view_column_new_with_attributes(title, rend, - "text", colnum, - "style", CL_COL_STYLE, - "weight", CL_COL_WEIGHT, - NULL); - } - - gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col); - - if (key != NULL) { - g_object_set_data(G_OBJECT(treeview), key, col); - } -} - -/**********************************************************************//** - Create start page. -**************************************************************************/ -GtkWidget *create_start_page(void) -{ - GtkWidget *box, *sbox, *table, *vbox; - GtkWidget *view, *sw, *text, *toolkit_view, *button, *spin; - GtkWidget *label; - GtkTreeSelection *selection; - enum ai_level level; - /* There's less than AI_LEVEL_COUNT entries as not all levels have - entries (that's the whole point of this array: index != value), - but this is set safely to the max */ - static enum ai_level levels[AI_LEVEL_COUNT]; - int i = 0; - - box = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(box), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(box), 8); - gtk_container_set_border_width(GTK_CONTAINER(box), 4); - - sbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(sbox), 12); - gtk_container_add(GTK_CONTAINER(box), sbox); - - vbox = gtk_grid_new(); - g_object_set(vbox, "margin", 12, NULL); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 2); - gtk_widget_set_halign(vbox, GTK_ALIGN_CENTER); - gtk_widget_set_valign(vbox, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(sbox), vbox); - - table = gtk_grid_new(); - start_options_table = table; - gtk_grid_set_row_spacing(GTK_GRID(table), 2); - gtk_grid_set_column_spacing(GTK_GRID(table), 12); - gtk_container_add(GTK_CONTAINER(vbox), table); - - spin = gtk_spin_button_new_with_range(1, MAX_NUM_PLAYERS, 1); - start_aifill_spin = spin; - gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0); - gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), - GTK_UPDATE_IF_VALID); - if (server_optset != NULL) { - struct option *paifill = optset_option_by_name(server_optset, "aifill"); - if (paifill) { - gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), - option_int_get(paifill)); - } /* else it'll be updated later */ - } - g_signal_connect_after(spin, "value_changed", - G_CALLBACK(ai_fill_changed_by_user), NULL); - - gtk_grid_attach(GTK_GRID(table), spin, 1, 0, 1, 1); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", spin, - /* TRANS: Keep individual lines short */ - "label", _("Number of _Players\n(including AI):"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); - - ai_lvl_combobox = gtk_combo_box_text_new(); - - for (level = 0; level < AI_LEVEL_COUNT; level++) { - if (is_settable_ai_level(level)) { - const char *level_name = ai_level_translated_name(level); - - gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(ai_lvl_combobox), i, level_name); - levels[i] = level; - i++; - } - } - gtk_combo_box_set_active(GTK_COMBO_BOX(ai_lvl_combobox), -1); - g_signal_connect(ai_lvl_combobox, "changed", - G_CALLBACK(ai_skill_callback), levels); - - gtk_grid_attach(GTK_GRID(table), ai_lvl_combobox, 1, 1, 1, 1); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", ai_lvl_combobox, - "label", _("AI Skill _Level:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1); - - ruleset_combo = gtk_combo_box_text_new(); - g_signal_connect(G_OBJECT(ruleset_combo), "changed", - G_CALLBACK(ruleset_entry_changed), NULL); - - gtk_grid_attach(GTK_GRID(table), ruleset_combo, 1, 2, 1, 1); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", GTK_COMBO_BOX_TEXT(ruleset_combo), - "label", _("Ruleset:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 2, 1, 1); - - button = gtk_stockbutton_new(GTK_STOCK_PREFERENCES, - _("_More Game Options...")); - g_object_set(button, "margin", 8, NULL); - gtk_widget_set_halign(button, GTK_ALIGN_CENTER); - gtk_widget_set_valign(button, GTK_ALIGN_CENTER); - g_signal_connect(button, "clicked", - G_CALLBACK(game_options_callback), NULL); - gtk_container_add(GTK_CONTAINER(vbox), button); - - connection_list_store = connection_list_store_new(); - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(connection_list_store)); - gtk_widget_set_hexpand(view, TRUE); - g_object_unref(G_OBJECT(connection_list_store)); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE); - connection_list_view = GTK_TREE_VIEW(view); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); - g_signal_connect(selection, "changed", - G_CALLBACK(update_start_page_buttons), NULL); - - add_tree_col(view, G_TYPE_STRING, _("Name"), - CL_COL_USER_NAME, NULL); - add_tree_col(view, G_TYPE_BOOLEAN, _("Ready"), - CL_COL_READY_STATE, NULL); - add_tree_col(view, G_TYPE_STRING, Q_("?player:Leader"), - CL_COL_PLAYER_NAME, NULL); - add_tree_col(view, GDK_TYPE_PIXBUF, _("Flag"), - CL_COL_FLAG, NULL); - add_tree_col(view, GDK_TYPE_PIXBUF, _("Border"), - CL_COL_COLOR, NULL); - add_tree_col(view, G_TYPE_STRING, _("Nation"), - CL_COL_NATION, NULL); - add_tree_col(view, G_TYPE_STRING, _("Team"), - CL_COL_TEAM, NULL); - - g_signal_connect(view, "button-press-event", - G_CALLBACK(connection_list_event), NULL); - g_signal_connect(view, "row-collapsed", - G_CALLBACK(connection_list_row_callback), - GINT_TO_POINTER(TRUE)); - g_signal_connect(view, "row-expanded", - G_CALLBACK(connection_list_row_callback), - GINT_TO_POINTER(FALSE)); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_ALWAYS); - gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 200); - gtk_container_add(GTK_CONTAINER(sw), view); - gtk_container_add(GTK_CONTAINER(sbox), sw); - - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(box), sw); - - text = gtk_text_view_new_with_buffer(message_buffer); - gtk_widget_set_hexpand(text, TRUE); - gtk_widget_set_vexpand(text, TRUE); - start_message_area = text; - gtk_widget_set_name(text, "chatline"); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 5); - gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); - gtk_container_add(GTK_CONTAINER(sw), text); - - - /* Vote widgets. */ - if (pregame_votebar == NULL) { - pregame_votebar = voteinfo_bar_new(TRUE); - } - gtk_container_add(GTK_CONTAINER(box), pregame_votebar); - - - toolkit_view = inputline_toolkit_view_new(); - gtk_container_add(GTK_CONTAINER(box), toolkit_view); - - button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); - inputline_toolkit_view_append_button(toolkit_view, button); - g_signal_connect(button, "clicked", G_CALLBACK(main_callback), NULL); - - nation_button = gtk_stockbutton_new(GTK_STOCK_PROPERTIES, - _("Pick _Nation")); - inputline_toolkit_view_append_button(toolkit_view, nation_button); - g_signal_connect(nation_button, "clicked", - G_CALLBACK(nation_button_callback), NULL); - - observe_button = gtk_stockbutton_new(GTK_STOCK_ZOOM_IN, _("_Observe")); - inputline_toolkit_view_append_button(toolkit_view, observe_button); - g_signal_connect(observe_button, "clicked", - G_CALLBACK(observe_button_callback), NULL); - - ready_button = gtk_stockbutton_new(GTK_STOCK_EXECUTE, _("_Ready")); - inputline_toolkit_view_append_button(toolkit_view, ready_button); - g_signal_connect(ready_button, "clicked", - G_CALLBACK(ready_button_callback), NULL); - - return box; -} - - -/**********************************************************************//** - This regenerates the player information from a loaded game on the server. -**************************************************************************/ -void handle_game_load(bool load_successful, const char *filename) -{ - if (load_successful) { - set_client_page(PAGE_START); - - if (game.info.is_new_game) { - /* It's pregame. Create a player and connect to him */ - send_chat("/take -"); - } - } -} - -/**********************************************************************//** - Load a savegame/scenario. -**************************************************************************/ -static void load_filename(const char *filename) -{ - send_chat_printf("/load %s", filename); -} - -/**********************************************************************//** - Loads the currently selected game. -**************************************************************************/ -static void load_callback(void) -{ - GtkTreeIter it; - const gchar *filename; - - if (!gtk_tree_selection_get_selected(load_selection, NULL, &it)) { - return; - } - - gtk_tree_model_get(GTK_TREE_MODEL(load_store), &it, - SD_COL_FULL_PATH, &filename, -1); - load_filename(filename); -} - -/**********************************************************************//** - Call the default GTK+ requester for saved game loading. -**************************************************************************/ -static void load_browse_callback(GtkWidget *w, gpointer data) -{ - save_dialog_file_chooser_popup(_("Choose Saved Game to Load"), - GTK_FILE_CHOOSER_ACTION_OPEN, - load_filename); -} - -/**********************************************************************//** - Update the load page. -**************************************************************************/ -static void update_load_page(void) -{ - /* Search for user saved games. */ - struct fileinfo_list *files = fileinfolist_infix(get_save_dirs(), - ".sav", FALSE); - - save_dialog_store_update(load_store, files); - fileinfo_list_destroy(files); -} - -/**********************************************************************//** - Create the load page. -**************************************************************************/ -GtkWidget *create_load_page(void) -{ - GtkWidget *box, *sbox, *bbox; - - GtkWidget *button, *label, *view, *sw; - GtkCellRenderer *rend; - - box = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(box), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(box), 18); - gtk_container_set_border_width(GTK_CONTAINER(box), 4); - - load_store = save_dialog_store_new(); - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(load_store)); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(load_store); - - rend = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), - -1, NULL, rend, "text", 0, NULL); - - load_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - - gtk_tree_selection_set_mode(load_selection, GTK_SELECTION_SINGLE); - - g_signal_connect(view, "row-activated", - G_CALLBACK(load_callback), NULL); - - sbox = gtk_grid_new(); - gtk_widget_set_halign(sbox, GTK_ALIGN_CENTER); - gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(sbox), 2); - gtk_container_add(GTK_CONTAINER(box), sbox); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", view, - "label", _("Choose Saved Game to _Load:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_container_add(GTK_CONTAINER(sbox), label); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw), 300); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), view); - gtk_container_add(GTK_CONTAINER(sbox), sw); - - bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); - gtk_widget_set_hexpand(bbox, TRUE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_set_spacing(GTK_BOX(bbox), 12); - gtk_container_add(GTK_CONTAINER(box), bbox); - - button = gtk_button_new_with_mnemonic(_("_Browse...")); - gtk_container_add(GTK_CONTAINER(bbox), button); - gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), button, TRUE); - g_signal_connect(button, "clicked", - G_CALLBACK(load_browse_callback), NULL); - - button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); - gtk_container_add(GTK_CONTAINER(bbox), button); - g_signal_connect(button, "clicked", - G_CALLBACK(main_callback), NULL); - - button = gtk_button_new_from_stock(GTK_STOCK_OK); - gtk_container_add(GTK_CONTAINER(bbox), button); - g_signal_connect(button, "clicked", - G_CALLBACK(load_callback), NULL); - - return box; -} - - -/**********************************************************************//** - Updates the info for the currently selected scenario. -**************************************************************************/ -static void scenario_list_callback(void) -{ - GtkTreeIter it; - GtkTextBuffer *buffer; - char *description; - char *authors; - char *filename; - int ver; - char vername[50]; - - if (gtk_tree_selection_get_selected(scenario_selection, NULL, &it)) { - gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it, - 2, &description, -1); - gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it, - 3, &authors, -1); - gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it, - 1, &filename, -1); - gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it, - 4, &ver, -1); - filename = skip_to_basename(filename); - if (ver > 0) { - int maj; - int min; - - maj = ver / 1000000; - ver %= 1000000; - min = ver / 10000; - fc_snprintf(vername, sizeof(vername), "%d.%d", maj, min); - } else { - /* TRANS: Old scenario format version */ - fc_snprintf(vername, sizeof(vername), _("pre-2.6")); - } - } else { - description = ""; - authors = ""; - filename = ""; - vername[0] = '\0'; - } - - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(scenario_description)); - gtk_text_buffer_set_text(buffer, description, -1); - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(scenario_authors)); - gtk_text_buffer_set_text(buffer, authors, -1); - gtk_label_set_text(GTK_LABEL(scenario_filename), filename); - gtk_label_set_text(GTK_LABEL(scenario_version), vername); -} - -/**********************************************************************//** - Loads the currently selected scenario. -**************************************************************************/ -static void scenario_callback(void) -{ - GtkTreeIter it; - char *filename; - - if (!gtk_tree_selection_get_selected(scenario_selection, NULL, &it)) { - return; - } - - gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it, 1, &filename, -1); - load_filename(filename); -} - -/**********************************************************************//** - Call the default GTK+ requester for scenario loading. -**************************************************************************/ -static void scenario_browse_callback(GtkWidget *w, gpointer data) -{ - save_dialog_file_chooser_popup(_("Choose a Scenario"), - GTK_FILE_CHOOSER_ACTION_OPEN, - load_filename); -} - -/**********************************************************************//** - Update the scenario page. -**************************************************************************/ -static void update_scenario_page(void) -{ - struct fileinfo_list *files; - - gtk_list_store_clear(scenario_store); - - /* search for scenario files. */ - files = fileinfolist_infix(get_scenario_dirs(), ".sav", TRUE); - fileinfo_list_iterate(files, pfile) { - struct section_file *sf; - - if ((sf = secfile_load_section(pfile->fullname, "scenario", TRUE)) - && secfile_lookup_bool_default(sf, TRUE, "scenario.is_scenario")) { - const char *sname, *sdescription, *sauthors; - int fcver; - int current_ver = MAJOR_VERSION * 1000000 + MINOR_VERSION * 10000; - - fcver = secfile_lookup_int_default(sf, 0, "scenario.game_version"); - if (fcver < 30000) { - /* Pre-3.0 versions stored version number without emergency version - * part in the end. To get comparable version number stored, - * multiply by 100. */ - fcver *= 100; - } - fcver -= (fcver % 10000); /* Patch level does not affect compatibility */ - sname = secfile_lookup_str_default(sf, NULL, "scenario.name"); - sdescription = secfile_lookup_str_default(sf, NULL, - "scenario.description"); - sauthors = secfile_lookup_str_default(sf, NULL, "scenario.authors"); - log_debug("scenario file: %s from %s", sname, pfile->fullname); - - /* Ignore scenarios for newer freeciv versions than we are */ - if (fcver <= current_ver) { - bool add_new = TRUE; - - if (sname != NULL) { - GtkTreeIter it; - bool valid; - - valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(scenario_store), &it); - while (valid) { - char *oname; - - gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it, - 0, &oname, -1); - - if (!strcmp(sname, oname)) { - /* Already listed scenario has the same name as the one we just found */ - int existing; - - gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it, - 4, &existing, -1); - log_debug("Duplicate %s (%d vs %d)", sname, existing, fcver); - - if (existing > fcver) { - /* Already listed one has higher version number */ - add_new = FALSE; - } else if (existing < fcver) { - /* New one has higher version number */ - add_new = FALSE; - - gtk_list_store_set(scenario_store, &it, - 0, sname && strlen(sname) ? Q_(sname) : pfile->name, - 1, pfile->fullname, - 2, (NULL != sdescription && '\0' != sdescription[0] - ? Q_(sdescription) : ""), - 3, (NULL != sauthors && sauthors[0] != '\0' - ? Q_(sauthors) : ""), - 4, fcver, - -1); - } else { - /* Same version number -> list both */ - } - } - valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(scenario_store), &it); - } - } - - if (add_new) { - GtkTreeIter it; - - gtk_list_store_append(scenario_store, &it); - gtk_list_store_set(scenario_store, &it, - 0, sname && strlen(sname) ? Q_(sname) : pfile->name, - 1, pfile->fullname, - 2, (NULL != sdescription && '\0' != sdescription[0] - ? Q_(sdescription) : ""), - 3, (NULL != sauthors && sauthors[0] != '\0' - ? Q_(sauthors) : ""), - 4, fcver, - -1); - } - } - - secfile_destroy(sf); - } - } fileinfo_list_iterate_end; - - fileinfo_list_destroy(files); -} - -/**********************************************************************//** - Create the scenario page. -**************************************************************************/ -GtkWidget *create_scenario_page(void) -{ - GtkWidget *vbox, *hbox, *sbox, *bbox, *filenamebox, *descbox; - GtkWidget *versionbox, *vertext; - GtkWidget *button, *label, *view, *sw, *swa, *text; - GtkCellRenderer *rend; - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 18); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); - - scenario_store = gtk_list_store_new(5, G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_INT); - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(scenario_store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(scenario_store); - - rend = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), - -1, NULL, rend, "text", 0, NULL); - - scenario_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - g_signal_connect(scenario_selection, "changed", - G_CALLBACK(scenario_list_callback), NULL); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); - - gtk_tree_selection_set_mode(scenario_selection, GTK_SELECTION_SINGLE); - - g_signal_connect(view, "row-activated", - G_CALLBACK(scenario_callback), NULL); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", view, - "label", _("Choose a _Scenario:"), - "xalign", 0.0, - "yalign", 0.5, - NULL); - gtk_container_add(GTK_CONTAINER(vbox), label); - - sbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(sbox), 12); - gtk_grid_set_row_homogeneous(GTK_GRID(sbox), TRUE); - gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(sbox), 2); - gtk_container_add(GTK_CONTAINER(vbox), sbox); - - hbox = gtk_grid_new(); - gtk_grid_set_column_homogeneous(GTK_GRID(hbox), TRUE); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 12); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), view); - gtk_grid_attach(GTK_GRID(sbox), sw, 0, 0, 1, 2); - - text = gtk_text_view_new(); - gtk_widget_set_hexpand(text, TRUE); - gtk_widget_set_vexpand(text, TRUE); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 2); - gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); - scenario_description = text; - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), text); - - text = gtk_text_view_new(); - gtk_widget_set_hexpand(text, TRUE); - gtk_widget_set_vexpand(text, TRUE); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 2); - gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); - scenario_authors = text; - - swa = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swa), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swa), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(swa), text); - - text = gtk_label_new(_("Filename:")); - scenario_filename = gtk_label_new(""); - gtk_widget_set_halign(scenario_filename, GTK_ALIGN_START); - gtk_widget_set_valign(scenario_filename, GTK_ALIGN_CENTER); - gtk_label_set_selectable(GTK_LABEL(scenario_filename), TRUE); - - filenamebox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 12); - g_object_set(filenamebox, "margin", 5, NULL); - - gtk_container_add(GTK_CONTAINER(filenamebox), text); - gtk_container_add(GTK_CONTAINER(filenamebox), scenario_filename); - - /* TRANS: Scenario format version */ - vertext = gtk_label_new(_("Format:")); - scenario_version = gtk_label_new(""); - gtk_widget_set_halign(scenario_version, GTK_ALIGN_START); - gtk_widget_set_valign(scenario_version, GTK_ALIGN_CENTER); - gtk_label_set_selectable(GTK_LABEL(scenario_version), TRUE); - - versionbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 12); - g_object_set(versionbox, "margin", 5, NULL); - - gtk_container_add(GTK_CONTAINER(versionbox), vertext); - gtk_container_add(GTK_CONTAINER(versionbox), scenario_version); - - descbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(descbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(descbox), 6); - gtk_container_add(GTK_CONTAINER(descbox), sw); - gtk_container_add(GTK_CONTAINER(descbox), swa); - gtk_container_add(GTK_CONTAINER(descbox), filenamebox); - gtk_container_add(GTK_CONTAINER(descbox), versionbox); - gtk_grid_attach(GTK_GRID(sbox), descbox, 1, 0, 1, 2); - - bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_set_spacing(GTK_BOX(bbox), 12); - gtk_container_add(GTK_CONTAINER(vbox), bbox); - - button = gtk_button_new_with_mnemonic(_("_Browse...")); - gtk_container_add(GTK_CONTAINER(bbox), button); - gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), button, TRUE); - g_signal_connect(button, "clicked", - G_CALLBACK(scenario_browse_callback), NULL); - - button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); - gtk_container_add(GTK_CONTAINER(bbox), button); - g_signal_connect(button, "clicked", - G_CALLBACK(main_callback), NULL); - - button = gtk_button_new_from_stock(GTK_STOCK_OK); - gtk_container_add(GTK_CONTAINER(bbox), button); - g_signal_connect(button, "clicked", - G_CALLBACK(scenario_callback), NULL); - - return vbox; -} - - -/**********************************************************************//** - Returns current client page -**************************************************************************/ -enum client_pages get_current_client_page(void) -{ - return current_page; -} - -/**********************************************************************//** - Changes the current page. The action is delayed. -**************************************************************************/ -void real_set_client_page(enum client_pages new_page) -{ - /* Don't use current_page directly here because maybe it could be modified - * before we reach the end of this function. */ - enum client_pages old_page = current_page; - - /* If the page remains the same, don't do anything. */ - if (old_page == new_page) { - return; - } - - log_debug("Switching client page from %s to %s.", - -1 == old_page ? "(no page)" : client_pages_name(old_page), - client_pages_name(new_page)); - - current_page = new_page; - - switch (old_page) { - case PAGE_SCENARIO: - case PAGE_LOAD: - break; - case PAGE_GAME: - { - struct video_mode *vmode = resolution_request_get(); - - enable_menus(FALSE); - if (vmode == NULL) { - gtk_window_unmaximize(GTK_WINDOW(toplevel)); - } - } - break; - default: - break; - } - - switch (new_page) { - case PAGE_MAIN: - case PAGE_START: - if (is_server_running()) { - if (game.info.is_new_game) { - gtk_widget_show(start_options_table); - } else { - gtk_widget_hide(start_options_table); - } - update_start_page(); - } else { - gtk_widget_hide(start_options_table); - } - voteinfo_gui_update(); - overview_size_changed(); - conn_list_dialog_update(); - break; - case PAGE_GAME: - { - struct video_mode *vmode = resolution_request_get(); - - reset_unit_table(); - enable_menus(TRUE); - if (vmode == NULL) { - gtk_window_maximize(GTK_WINDOW(toplevel)); - } - voteinfo_gui_update(); - mapview_freeze(); - } - break; - case PAGE_LOAD: - update_load_page(); - break; - case PAGE_SCENARIO: - update_scenario_page(); - break; - case PAGE_NETWORK: - update_network_page(); - break; - } - - /* hide/show statusbar. */ - if (new_page == PAGE_START || new_page == PAGE_GAME) { - clear_network_statusbar(); - gtk_widget_hide(statusbar_frame); - } else { - gtk_widget_show(statusbar_frame); - } - - gtk_notebook_set_current_page(GTK_NOTEBOOK(toplevel_tabs), new_page); - - /* Update the GUI. */ - while (gtk_events_pending()) { - gtk_main_iteration(); - } - - switch (new_page) { - case PAGE_MAIN: - break; - case PAGE_START: - chatline_scroll_to_bottom(FALSE); - inputline_grab_focus(); - break; - case PAGE_LOAD: - gtk_tree_view_focus(gtk_tree_selection_get_tree_view(load_selection)); - break; - case PAGE_SCENARIO: - gtk_tree_view_focus(gtk_tree_selection_get_tree_view(scenario_selection)); - break; - case PAGE_GAME: - chatline_scroll_to_bottom(FALSE); - refresh_chat_buttons(); - center_on_something(); - mapview_thaw(); - break; - case PAGE_NETWORK: - update_network_lists(); - gtk_widget_grab_focus(network_login); - gtk_editable_set_position(GTK_EDITABLE(network_login), 0); - set_connection_state(connection_status); - break; - } -} - -/**************************************************************************** - SAVE GAME DIALOGs -****************************************************************************/ - -/**********************************************************************//** - Save game 'save_dialog_files_fn_t' implementation. -**************************************************************************/ -static struct fileinfo_list *save_dialog_savegame_list(void) -{ - return fileinfolist_infix(get_save_dirs(), ".sav", FALSE); -} - -/**********************************************************************//** - Save game dialog. -**************************************************************************/ -void save_game_dialog_popup(void) -{ - static GtkWidget *shell = NULL; - - if (NULL != shell) { - return; - } - - shell = save_dialog_new(_("Save Game"), _("Saved _Games:"), - _("Save _Filename:"), send_save_game, - save_dialog_savegame_list); - g_signal_connect(shell, "destroy", G_CALLBACK(gtk_widget_destroyed), - &shell); - gtk_window_present(GTK_WINDOW(shell)); -} - -/**********************************************************************//** - Save scenario 'save_dialog_action_fn_t' implementation. -**************************************************************************/ -static void save_dialog_save_scenario(const char *filename) -{ - dsend_packet_save_scenario(&client.conn, filename); -} - -/**********************************************************************//** - Save scenario 'save_dialog_files_fn_t' implementation. -**************************************************************************/ -static struct fileinfo_list *save_dialog_scenario_list(void) -{ - return fileinfolist_infix(get_scenario_dirs(), ".sav", FALSE); -} - -/**********************************************************************//** - Save scenario dialog. -**************************************************************************/ -void save_scenario_dialog_popup(void) -{ - static GtkWidget *shell = NULL; - - if (NULL != shell) { - return; - } - - shell = save_dialog_new(_("Save Scenario"), _("Saved Sce_narios:"), - _("Save Sc_enario:"), save_dialog_save_scenario, - save_dialog_scenario_list); - g_signal_connect(shell, "destroy", G_CALLBACK(gtk_widget_destroyed), - &shell); - gtk_window_present(GTK_WINDOW(shell)); -} - -/**********************************************************************//** - Save mapimg 'save_dialog_files_fn_t' implementation. If possible, only the - current directory is used. As fallback, the files in the save directories - are listed. -**************************************************************************/ -static struct fileinfo_list *save_dialog_mapimg_list(void) -{ - return fileinfolist_infix(get_save_dirs(), ".map", FALSE); -} - -/**********************************************************************//** - Save scenario dialog. -**************************************************************************/ -void save_mapimg_dialog_popup(void) -{ - static GtkWidget *shell = NULL; - - if (NULL != shell) { - return; - } - - shell = save_dialog_new(_("Save Map Image"), _("Saved Map _Images:"), - _("Save _Map Images:"), mapimg_client_save, - save_dialog_mapimg_list); - g_signal_connect(shell, "destroy", G_CALLBACK(gtk_widget_destroyed), - &shell); - gtk_window_present(GTK_WINDOW(shell)); -} - -/**********************************************************************//** - Save map image. On error popup a message window for the user. -**************************************************************************/ -void mapimg_client_save(const char *filename) -{ - if (!mapimg_client_createmap(filename)) { - char msg[512]; - - fc_snprintf(msg, sizeof(msg), "(%s)", mapimg_error()); - popup_notify_dialog("Error", "Error Creating the Map Image!", msg); - } -} - - -/**********************************************************************//** - Set the list of available rulesets. The default ruleset should be - "default", and if the user changes this then set_ruleset() should be - called. -**************************************************************************/ -void set_rulesets(int num_rulesets, char **rulesets) -{ - int i; - int def_idx = -1; - - gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(ruleset_combo)); - for (i = 0; i < num_rulesets; i++) { - - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ruleset_combo), rulesets[i]); - if (!strcmp("default", rulesets[i])) { - def_idx = i; - } - } - - no_ruleset_callback = TRUE; - - /* HACK: server should tell us the current ruleset. */ - gtk_combo_box_set_active(GTK_COMBO_BOX(ruleset_combo), def_idx); - - no_ruleset_callback = FALSE; -} diff --git a/client/gui-gtk-3.0/pages.h b/client/gui-gtk-3.0/pages.h deleted file mode 100644 index 79128b1048..0000000000 --- a/client/gui-gtk-3.0/pages.h +++ /dev/null @@ -1,43 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__PAGES_H -#define FC__PAGES_H - -#include - -/* utility */ -#include "support.h" /* bool type */ - -#include "pages_g.h" - -extern GtkWidget *start_message_area; - -GtkWidget *create_main_page(void); -GtkWidget *create_start_page(void); -GtkWidget *create_scenario_page(void); -GtkWidget *create_load_page(void); -GtkWidget *create_network_page(void); - -GtkWidget *create_statusbar(void); -void append_network_statusbar(const char *text, bool force); - -void save_game_dialog_popup(void); -void save_scenario_dialog_popup(void); -void save_mapimg_dialog_popup(void); -void mapimg_client_save(const char *filename); - -void ai_fill_changed_by_server(int aifill); - -void destroy_server_scans(void); - -#endif /* FC__PAGES_H */ diff --git a/client/gui-gtk-3.0/plrdlg.c b/client/gui-gtk-3.0/plrdlg.c deleted file mode 100644 index ee7bd89028..0000000000 --- a/client/gui-gtk-3.0/plrdlg.c +++ /dev/null @@ -1,943 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include - -/* utility */ -#include "astring.h" -#include "fcintl.h" -#include "support.h" - -/* common */ -#include "diptreaty.h" -#include "packets.h" -#include "nation.h" -#include "player.h" - -/* client */ -#include "client_main.h" -#include "climisc.h" -#include "connectdlg_common.h" -#include "tilespec.h" -#include "colors.h" -#include "graphics.h" -#include "options.h" -#include "text.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "dialogs.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "inteldlg.h" -#include "spaceshipdlg.h" -#include "colors.h" -#include "graphics.h" - -#include "plrdlg.h" - -struct gui_dialog *players_dialog_shell; -static GtkWidget *players_list; -static GtkTreeSelection *players_selection; -static GtkWidget *players_int_command; -static GtkWidget *players_meet_command; -static GtkWidget *players_war_command; -static GtkWidget *players_vision_command; -static GtkWidget *players_sship_command; - -static GtkListStore *players_dialog_store; -#define PLR_DLG_COL_STYLE (0 + num_player_dlg_columns) -#define PLR_DLG_COL_WEIGHT (1 + num_player_dlg_columns) -#define PLR_DLG_COL_ID (2 + num_player_dlg_columns) - -static void create_players_dialog(void); -static void players_meet_callback(GtkMenuItem *item, gpointer data); -static void players_war_callback(GtkMenuItem *item, gpointer data); -static void players_vision_callback(GtkMenuItem *item, gpointer data); -static void players_intel_callback(GtkMenuItem *item, gpointer data); -static void players_sship_callback(GtkMenuItem *item, gpointer data); -static void players_ai_toggle_callback(GtkMenuItem *item, gpointer data); -static void players_ai_skill_callback(GtkMenuItem *item, gpointer data); - - -static void update_views(void); - -/**********************************************************************//** - Popup the dialog 10% inside the main-window, and optionally raise it. -**************************************************************************/ -void popup_players_dialog(bool raise) -{ - if (!players_dialog_shell) { - create_players_dialog(); - } - gui_dialog_present(players_dialog_shell); - if (raise) { - gui_dialog_raise(players_dialog_shell); - } -} - -/**********************************************************************//** - Closes the players dialog. -**************************************************************************/ -void popdown_players_dialog(void) -{ - if (players_dialog_shell) { - gui_dialog_destroy(players_dialog_shell); - } -} - -/**********************************************************************//** - Create a small colored square representing the player color, for use - in player lists. - May return NULL if the player has no color yet. -**************************************************************************/ -GdkPixbuf *create_player_icon(const struct player *plr) -{ - int width, height; - GdkPixbuf *tmp; - cairo_surface_t *surface; - struct color *color; - cairo_t *cr; - - if (!player_has_color(tileset, plr)) { - return NULL; - } - - gtk_icon_size_lookup_for_settings( - gtk_settings_get_for_screen(gtk_widget_get_screen(top_notebook)), - GTK_ICON_SIZE_MENU, &width, &height); - surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - - cr = cairo_create(surface); - - color = get_color(tileset, COLOR_PLAYER_COLOR_BACKGROUND); - gdk_cairo_set_source_rgba(cr, &color->color); - cairo_rectangle(cr, 0, 0, width, height); - cairo_fill(cr); - - color = get_player_color(tileset, plr); - gdk_cairo_set_source_rgba(cr, &color->color); - cairo_rectangle(cr, 1, 1, width - 2, height - 2); - cairo_fill(cr); - - cairo_destroy(cr); - tmp = surface_get_pixbuf(surface, width, height); - cairo_surface_destroy(surface); - - return tmp; -} - -/**********************************************************************//** - Refresh player menu -**************************************************************************/ -static void update_players_menu(void) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(players_selection, &model, &it)) { - struct player *plr; - gint plrno; - - gtk_tree_model_get(model, &it, PLR_DLG_COL_ID, &plrno, -1); - plr = player_by_number(plrno); - - if (plr->spaceship.state != SSHIP_NONE) { - gtk_widget_set_sensitive(players_sship_command, TRUE); - } else { - gtk_widget_set_sensitive(players_sship_command, FALSE); - } - - if (NULL != client.conn.playing) { - /* We keep button sensitive in case of DIPL_SENATE_BLOCKING, so that player - * can request server side to check requirements of those effects with omniscience */ - gtk_widget_set_sensitive(players_war_command, - can_client_issue_orders() - && pplayer_can_cancel_treaty(client_player(), - player_by_number(plrno)) - != DIPL_ERROR); - } else { - gtk_widget_set_sensitive(players_war_command, FALSE); - } - - gtk_widget_set_sensitive(players_vision_command, - can_client_issue_orders() - && gives_shared_vision(client.conn.playing, plr) - && !players_on_same_team(client.conn.playing, plr)); - - gtk_widget_set_sensitive(players_meet_command, can_meet_with_player(plr)); - gtk_widget_set_sensitive(players_int_command, can_intel_with_player(plr)); - return; - } - - gtk_widget_set_sensitive(players_meet_command, FALSE); - gtk_widget_set_sensitive(players_int_command, FALSE); -} - -/**********************************************************************//** - Something selected from player menu -**************************************************************************/ -static void selection_callback(GtkTreeSelection *selection, gpointer data) -{ - update_players_menu(); -} - -/**********************************************************************//** - Button pressed on player list -**************************************************************************/ -static gboolean button_press_callback(GtkTreeView *view, GdkEventButton *ev) -{ - if (ev->type == GDK_2BUTTON_PRESS) { - GtkTreePath *path; - - gtk_tree_view_get_cursor(view, &path, NULL); - if (path) { - GtkTreeModel *model = gtk_tree_view_get_model(view); - GtkTreeIter it; - gint id; - struct player *plr; - - gtk_tree_model_get_iter(model, &it, path); - gtk_tree_path_free(path); - - gtk_tree_model_get(model, &it, PLR_DLG_COL_ID, &id, -1); - plr = player_by_number(id); - - if (ev->button == 1) { - if (can_intel_with_player(plr)) { - popup_intel_dialog(plr); - } - } else if (can_meet_with_player(plr)) { - dsend_packet_diplomacy_init_meeting_req(&client.conn, id); - } - } - } - return FALSE; -} - -/**********************************************************************//** - Sorting function for plr dlg. -**************************************************************************/ -static gint plrdlg_sort_func(GtkTreeModel *model, - GtkTreeIter *a, GtkTreeIter *b, gpointer data) -{ - GValue value = { 0, }; - struct player *player1; - struct player *player2; - gint n; - - n = GPOINTER_TO_INT(data); - - gtk_tree_model_get_value(model, a, PLR_DLG_COL_ID, &value); - player1 = player_by_number(g_value_get_int(&value)); - g_value_unset(&value); - - gtk_tree_model_get_value(model, b, PLR_DLG_COL_ID, &value); - player2 = player_by_number(g_value_get_int(&value)); - g_value_unset(&value); - - return player_dlg_columns[n].sort_func(player1, player2); -} - -/**********************************************************************//** - Create a player dialog store. -**************************************************************************/ -static GtkListStore *players_dialog_store_new(void) -{ - GtkListStore *store; - GType model_types[num_player_dlg_columns + 3]; - int i; - - for (i = 0; i < num_player_dlg_columns; i++) { - switch (player_dlg_columns[i].type) { - case COL_FLAG: - model_types[i] = GDK_TYPE_PIXBUF; - break; - case COL_COLOR: - model_types[i] = GDK_TYPE_PIXBUF; - break; - case COL_BOOLEAN: - model_types[i] = G_TYPE_BOOLEAN; - break; - case COL_TEXT: - case COL_RIGHT_TEXT: - model_types[i] = G_TYPE_STRING; - break; - } - } - /* special (invisible rows) - Text style, weight and player id */ - model_types[i++] = G_TYPE_INT; /* PLR_DLG_COL_STYLE. */ - model_types[i++] = G_TYPE_INT; /* PLR_DLG_COL_WEIGHT. */ - model_types[i++] = G_TYPE_INT; /* PLR_DLG_COL_ID. */ - - store = gtk_list_store_newv(i, model_types); - - /* Set sort order */ - for (i = 0; i < num_player_dlg_columns; i++) { - if (player_dlg_columns[i].sort_func != NULL) { - gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), i, - plrdlg_sort_func, GINT_TO_POINTER(i), - NULL); - } - } - - return store; -} - -/**********************************************************************//** - Toggled column visibility -**************************************************************************/ -static void toggle_view(GtkCheckMenuItem* item, gpointer data) -{ - struct player_dlg_column* pcol = data; - - pcol->show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)); - update_views(); -} - -/**********************************************************************//** - Called whenever player toggles the 'Show/Dead Players' menu item -**************************************************************************/ -static void toggle_dead_players(GtkCheckMenuItem* item, gpointer data) -{ - gui_options.player_dlg_show_dead_players = - gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)); - real_players_dialog_update(NULL); -} - -/**********************************************************************//** - Create and return the "diplomacy" menu for the player report. This menu - contains diplomacy actions the current player can use on other nations. -**************************************************************************/ -static GtkWidget *create_diplomacy_menu(void) -{ - GtkWidget *menu, *item; - - menu = gtk_menu_new(); - - item = gtk_menu_item_new_with_mnemonic(_("_Meet")); - g_signal_connect(item, "activate", - G_CALLBACK(players_meet_callback), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - players_meet_command = item; - - item = gtk_menu_item_new_with_mnemonic(_("Cancel _Treaty")); - g_signal_connect(item, "activate", - G_CALLBACK(players_war_callback), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - players_war_command = item; - - item = gtk_menu_item_new_with_mnemonic(_("_Withdraw Vision")); - g_signal_connect(item, "activate", - G_CALLBACK(players_vision_callback), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - players_vision_command = item; - - return menu; -} - -/**********************************************************************//** - Create and return the "intelligence" menu. The items in this menu are - used by the player to see more detailed information about other nations. -**************************************************************************/ -static GtkWidget *create_intelligence_menu(void) -{ - GtkWidget *menu, *item; - - menu = gtk_menu_new(); - - item = gtk_menu_item_new_with_mnemonic(_("_Report")); - g_signal_connect(item, "activate", - G_CALLBACK(players_intel_callback), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - players_int_command = item; - - item = gtk_menu_item_new_with_mnemonic(_("_Spaceship")); - g_signal_connect(item, "activate", - G_CALLBACK(players_sship_callback), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - players_sship_command = item; - - return menu; -} - -/**********************************************************************//** - Create 'show' menu for player dialog -**************************************************************************/ -static GtkWidget* create_show_menu(void) -{ - int i; - GtkWidget *menu = gtk_menu_new(); - GtkWidget *item; - - /* index starting at one (1) here to force playername to always be shown */ - for (i = 1; i < num_player_dlg_columns; i++) { - struct player_dlg_column *pcol; - - pcol = &player_dlg_columns[i]; - item = gtk_check_menu_item_new_with_label(pcol->title); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), pcol->show); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "toggled", G_CALLBACK(toggle_view), pcol); - } - - item = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - item = gtk_check_menu_item_new_with_label(Q_("?show:Dead Players")); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), - gui_options.player_dlg_show_dead_players); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - g_signal_connect(item, "toggled", G_CALLBACK(toggle_dead_players), NULL); - - return menu; -} - -/**********************************************************************//** - Create all of player dialog -**************************************************************************/ -void create_players_dialog(void) -{ - int i; - GtkWidget *sep, *sw; - GtkWidget *menubar, *menu, *item, *vbox; - enum ai_level level; - - gui_dialog_new(&players_dialog_shell, GTK_NOTEBOOK(top_notebook), NULL, - TRUE); - /* TRANS: Nations report title */ - gui_dialog_set_title(players_dialog_shell, _("Nations")); - - gui_dialog_add_button(players_dialog_shell, - GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - - gui_dialog_set_default_size(players_dialog_shell, -1, 270); - - players_dialog_store = players_dialog_store_new(); - - players_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL - (players_dialog_store)); - gtk_widget_set_hexpand(players_list, TRUE); - gtk_widget_set_vexpand(players_list, TRUE); - g_object_unref(players_dialog_store); - gtk_widget_set_name(players_list, "small_font"); - - players_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(players_list)); - g_signal_connect(players_selection, "changed", - G_CALLBACK(selection_callback), NULL); - g_signal_connect(players_list, "button_press_event", - G_CALLBACK(button_press_callback), NULL); - - for (i = 0; i < num_player_dlg_columns; i++) { - struct player_dlg_column *pcol; - GtkCellRenderer *renderer; - GtkTreeViewColumn *col; - - pcol = &player_dlg_columns[i]; - col = NULL; - - switch (pcol->type) { - case COL_FLAG: - renderer = gtk_cell_renderer_pixbuf_new(); - - col = gtk_tree_view_column_new_with_attributes(pcol->title, - renderer, "pixbuf", i, NULL); - break; - case COL_BOOLEAN: - renderer = gtk_cell_renderer_toggle_new(); - - col = gtk_tree_view_column_new_with_attributes(pcol->title, renderer, - "active", i, NULL); - break; - case COL_COLOR: - renderer = gtk_cell_renderer_pixbuf_new(); - - col = gtk_tree_view_column_new_with_attributes(pcol->title, renderer, - "pixbuf", i, NULL); - break; - case COL_TEXT: - renderer = gtk_cell_renderer_text_new(); - g_object_set(renderer, "style-set", TRUE, "weight-set", TRUE, NULL); - - col = gtk_tree_view_column_new_with_attributes(pcol->title, renderer, - "text", i, - "style", PLR_DLG_COL_STYLE, - "weight", PLR_DLG_COL_WEIGHT, - NULL); - gtk_tree_view_column_set_sort_column_id(col, i); - break; - case COL_RIGHT_TEXT: - renderer = gtk_cell_renderer_text_new(); - g_object_set(renderer, "style-set", TRUE, "weight-set", TRUE, NULL); - - col = gtk_tree_view_column_new_with_attributes(pcol->title, renderer, - "text", i, - "style", PLR_DLG_COL_STYLE, - "weight", PLR_DLG_COL_WEIGHT, - NULL); - gtk_tree_view_column_set_sort_column_id(col, i); - g_object_set(renderer, "xalign", 1.0, NULL); - gtk_tree_view_column_set_alignment(col, 1.0); - break; - } - - if (col) { - gtk_tree_view_append_column(GTK_TREE_VIEW(players_list), col); - } - } - - gtk_tree_view_set_search_column(GTK_TREE_VIEW(players_list), - player_dlg_default_sort_column()); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(sw), players_list); - - gtk_container_add(GTK_CONTAINER(players_dialog_shell->vbox), sw); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - - sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); - gtk_container_add(GTK_CONTAINER(vbox), sep); - - menubar = gtk_aux_menu_bar_new(); - gtk_container_add(GTK_CONTAINER(vbox), menubar); - - - gui_dialog_add_widget(players_dialog_shell, vbox); - - item = gtk_menu_item_new_with_mnemonic(_("Di_plomacy")); - menu = create_diplomacy_menu(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - - item = gtk_menu_item_new_with_mnemonic(_("_Intelligence")); - menu = create_intelligence_menu(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - - item = gtk_menu_item_new_with_mnemonic(_("_Display")); - menu = create_show_menu(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - - item = gtk_menu_item_new_with_mnemonic(_("_AI")); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - - menu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - - item = gtk_menu_item_new_with_mnemonic(_("_Toggle AI Mode")); - g_signal_connect(item, "activate", - G_CALLBACK(players_ai_toggle_callback), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - - sep = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep); - - for (level = 0; level < AI_LEVEL_COUNT; level++) { - if (is_settable_ai_level(level)) { - const char *level_name = ai_level_translated_name(level); - - item = gtk_menu_item_new_with_label(level_name); - g_signal_connect(item, "activate", - G_CALLBACK(players_ai_skill_callback), - GUINT_TO_POINTER(level)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - } - } - gtk_widget_show_all(menu); - - gui_dialog_show_all(players_dialog_shell); - - real_players_dialog_update(NULL); - - gui_dialog_set_default_response(players_dialog_shell, - GTK_RESPONSE_CLOSE); - - gtk_tree_view_focus(GTK_TREE_VIEW(players_list)); -} - - -/************************************************************************** -... -**************************************************************************/ -#define MIN_DIMENSION 5 - -/**********************************************************************//** - Builds the flag pixmap. May return NULL if there is not enough memory. - You must call g_object_unref on the returned pixbuf when it is no - longer needed. -**************************************************************************/ -GdkPixbuf *get_flag(const struct nation_type *nation) -{ - int x0, y0, x1, y1, w, h; - GdkPixbuf *im; - struct sprite *flag; - - flag = get_nation_flag_sprite(tileset, nation); - - /* calculate the bounding box ... */ - sprite_get_bounding_box(flag, &x0, &y0, &x1, &y1); - - fc_assert_ret_val(x0 != -1, NULL); - fc_assert_ret_val(y0 != -1, NULL); - fc_assert_ret_val(x1 != -1, NULL); - fc_assert_ret_val(y1 != -1, NULL); - - w = (x1 - x0) + 1; - h = (y1 - y0) + 1; - - /* if the flag is smaller then 5 x 5, something is wrong */ - fc_assert_ret_val(w >= MIN_DIMENSION && h >= MIN_DIMENSION, NULL); - - /* croping */ - im = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h); - if (im != NULL) { - GdkPixbuf *pixbuf = sprite_get_pixbuf(flag); - - gdk_pixbuf_copy_area(pixbuf, x0, y0, w, h, - im, 0, 0); - g_object_unref(G_OBJECT(pixbuf)); - } - - /* and finally store the scaled flag pixbuf in the static flags array */ - return im; -} - - -/**********************************************************************//** - Fills the player list with the information for 'pplayer' at the row - given by 'it'. -**************************************************************************/ -static void fill_row(GtkListStore *store, GtkTreeIter *it, - const struct player *pplayer) -{ - struct player_dlg_column* pcol; - GdkPixbuf *pixbuf; - int style = PANGO_STYLE_NORMAL, weight = PANGO_WEIGHT_NORMAL; - int k; - - for (k = 0; k < num_player_dlg_columns; k++) { - pcol = &player_dlg_columns[k]; - switch (pcol->type) { - case COL_TEXT: - case COL_RIGHT_TEXT: - gtk_list_store_set(store, it, k, pcol->func(pplayer), -1); - break; - case COL_FLAG: - pixbuf = get_flag(nation_of_player(pplayer)); - if (pixbuf != NULL) { - gtk_list_store_set(store, it, k, pixbuf, -1); - g_object_unref(pixbuf); - } - break; - case COL_COLOR: - pixbuf = create_player_icon(pplayer); - if (pixbuf != NULL) { - gtk_list_store_set(store, it, k, pixbuf, -1); - g_object_unref(pixbuf); - } - break; - case COL_BOOLEAN: - gtk_list_store_set(store, it, k, pcol->bool_func(pplayer), -1); - break; - } - } - - /* now add some eye candy ... */ - if (client_has_player()) { - switch (player_diplstate_get(client_player(), pplayer)->type) { - case DS_WAR: - weight = PANGO_WEIGHT_NORMAL; - style = PANGO_STYLE_ITALIC; - break; - case DS_ALLIANCE: - case DS_TEAM: - weight = PANGO_WEIGHT_BOLD; - style = PANGO_STYLE_NORMAL; - break; - case DS_ARMISTICE: - case DS_CEASEFIRE: - case DS_PEACE: - case DS_NO_CONTACT: - weight = PANGO_WEIGHT_NORMAL; - style = PANGO_STYLE_NORMAL; - break; - case DS_LAST: - break; - } - } - - gtk_list_store_set(store, it, - PLR_DLG_COL_STYLE, style, - PLR_DLG_COL_WEIGHT, weight, - PLR_DLG_COL_ID, player_number(pplayer), - -1); -} - -/**********************************************************************//** - Return TRUE if the player should be shown in the player list. -**************************************************************************/ -static bool player_should_be_shown(const struct player *pplayer) -{ - return NULL != pplayer && (gui_options.player_dlg_show_dead_players - || pplayer->is_alive) - && (!is_barbarian(pplayer)); -} - -/**********************************************************************//** - Clear and refill the entire player list. -**************************************************************************/ -void real_players_dialog_update(void *unused) -{ - GtkTreeModel *model; - GtkTreeIter iter; - int selected; - - if (NULL == players_dialog_shell) { - return; - } - - /* Save the selection. */ - if (gtk_tree_selection_get_selected(players_selection, &model, &iter)) { - gtk_tree_model_get(model, &iter, PLR_DLG_COL_ID, &selected, -1); - } else { - selected = -1; - } - - gtk_list_store_clear(players_dialog_store); - players_iterate(pplayer) { - if (!player_should_be_shown(pplayer)) { - continue; - } - gtk_list_store_append(players_dialog_store, &iter); - fill_row(players_dialog_store, &iter, pplayer); - if (player_number(pplayer) == selected) { - /* Restore the selection. */ - gtk_tree_selection_select_iter(players_selection, &iter); - } - } players_iterate_end; - - update_views(); -} - -/**********************************************************************//** - Callback for diplomatic meetings button. This button is enabled iff - we can meet with the other player. -**************************************************************************/ -void players_meet_callback(GtkMenuItem *item, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(players_selection, &model, &it)) { - gint plrno; - - gtk_tree_model_get(model, &it, PLR_DLG_COL_ID, &plrno, -1); - - dsend_packet_diplomacy_init_meeting_req(&client.conn, plrno); - } -} - -/**********************************************************************//** - Confirm pact/treaty cancellation. - Frees strings passed in. -**************************************************************************/ -static void confirm_cancel_pact(enum clause_type clause, int plrno, - char *title, char *question) -{ - GtkWidget *shell; - - shell = gtk_message_dialog_new(NULL, 0, - GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - "%s", question); - gtk_window_set_title(GTK_WINDOW(shell), title); - setup_dialog(shell, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_NO); - - if (gtk_dialog_run(GTK_DIALOG(shell)) == GTK_RESPONSE_YES) { - dsend_packet_diplomacy_cancel_pact(&client.conn, plrno, clause); - } - gtk_widget_destroy(shell); - FC_FREE(title); - FC_FREE(question); -} - -/**********************************************************************//** - Pact cancellation requested -**************************************************************************/ -void players_war_callback(GtkMenuItem *item, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(players_selection, &model, &it)) { - struct astring title = ASTRING_INIT, question = ASTRING_INIT; - gint plrno; - struct player *aplayer; - enum diplstate_type oldstate, newstate; - - gtk_tree_model_get(model, &it, PLR_DLG_COL_ID, &plrno, -1); - aplayer = player_by_number(plrno); - fc_assert_ret(aplayer != NULL); - - oldstate = player_diplstate_get(client_player(), aplayer)->type; - newstate = cancel_pact_result(oldstate); - - /* TRANS: %s is a diplomatic state: "Cancel Cease-fire" */ - astr_set(&title, _("Cancel %s"), diplstate_type_translated_name(oldstate)); - - if (newstate == DS_WAR) { - astr_set(&question, _("Really declare war on the %s?"), - nation_plural_for_player(aplayer)); - } else { - /* TRANS: "Cancel Belgian Alliance? ... will be Armistice." */ - astr_set(&question, _("Cancel %s %s? New diplomatic state will be %s."), - nation_adjective_for_player(aplayer), - diplstate_type_translated_name(oldstate), - diplstate_type_translated_name(newstate)); - } - - /* can be any pact clause */ - confirm_cancel_pact(CLAUSE_CEASEFIRE, plrno, - astr_to_str(&title), astr_to_str(&question)); - } -} - -/**********************************************************************//** - Withdrawing shared vision -**************************************************************************/ -void players_vision_callback(GtkMenuItem *item, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(players_selection, &model, &it)) { - struct astring question = ASTRING_INIT; - gint plrno; - struct player *aplayer; - - gtk_tree_model_get(model, &it, PLR_DLG_COL_ID, &plrno, -1); - aplayer = player_by_number(plrno); - fc_assert_ret(aplayer != NULL); - - /* TRANS: "...from the Belgians?" */ - astr_set(&question, _("Withdraw shared vision from the %s?"), - nation_plural_for_player(aplayer)); - - confirm_cancel_pact(CLAUSE_VISION, plrno, - fc_strdup(_("Withdraw Shared Vision")), - astr_to_str(&question)); - } -} - -/**********************************************************************//** - Intelligence report query -**************************************************************************/ -void players_intel_callback(GtkMenuItem *item, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(players_selection, &model, &it)) { - gint plrno; - - gtk_tree_model_get(model, &it, PLR_DLG_COL_ID, &plrno, -1); - - if (can_intel_with_player(player_by_number(plrno))) { - popup_intel_dialog(player_by_number(plrno)); - } - } -} - -/**********************************************************************//** - Spaceship query callback -**************************************************************************/ -void players_sship_callback(GtkMenuItem *item, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(players_selection, &model, &it)) { - gint plrno; - - gtk_tree_model_get(model, &it, PLR_DLG_COL_ID, &plrno, -1); - popup_spaceship_dialog(player_by_number(plrno)); - } -} - -/**********************************************************************//** - AI toggle callback. -**************************************************************************/ -static void players_ai_toggle_callback(GtkMenuItem *item, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(players_selection, &model, &it)) { - gint plrno; - - gtk_tree_model_get(model, &it, PLR_DLG_COL_ID, &plrno, -1); - - send_chat_printf("/aitoggle \"%s\"", player_name(player_by_number(plrno))); - } -} - -/**********************************************************************//** - AI skill level setting callback. -**************************************************************************/ -static void players_ai_skill_callback(GtkMenuItem *item, gpointer data) -{ - GtkTreeModel *model; - GtkTreeIter it; - - if (gtk_tree_selection_get_selected(players_selection, &model, &it)) { - gint plrno; - - gtk_tree_model_get(model, &it, PLR_DLG_COL_ID, &plrno, -1); - - send_chat_printf("/%s %s", - ai_level_cmd(GPOINTER_TO_UINT(data)), - player_name(player_by_number(plrno))); - } -} - -/**********************************************************************//** - Refresh players dialog views. -**************************************************************************/ -static void update_views(void) -{ - int i; - - for (i = 0; i < num_player_dlg_columns; i++) { - GtkTreeViewColumn *col; - - col = gtk_tree_view_get_column(GTK_TREE_VIEW(players_list), i); - gtk_tree_view_column_set_visible(col, player_dlg_columns[i].show); - } -} diff --git a/client/gui-gtk-3.0/plrdlg.h b/client/gui-gtk-3.0/plrdlg.h deleted file mode 100644 index bed2acb4fd..0000000000 --- a/client/gui-gtk-3.0/plrdlg.h +++ /dev/null @@ -1,26 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__PLRDLG_H -#define FC__PLRDLG_H - -#include "plrdlg_g.h" - -void popdown_players_dialog(void); - -/* Misc helper functions */ -GdkPixbuf *get_flag(const struct nation_type *pnation); -GdkPixbuf *create_player_icon(const struct player *plr); - -extern struct gui_dialog *players_dialog_shell; - -#endif /* FC__PLRDLG_H */ diff --git a/client/gui-gtk-3.0/ratesdlg.h b/client/gui-gtk-3.0/ratesdlg.h deleted file mode 100644 index 62d22445f8..0000000000 --- a/client/gui-gtk-3.0/ratesdlg.h +++ /dev/null @@ -1,20 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 2003 - The Freeciv Project - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__RATESDLG_H -#define FC__RATESDLG_H - -#include "ratesdlg_g.h" - -/* nothing to add */ - -#endif /* FC__RATESDLG_H */ diff --git a/client/gui-gtk-3.0/repodlgs.c b/client/gui-gtk-3.0/repodlgs.c deleted file mode 100644 index e1acbf867f..0000000000 --- a/client/gui-gtk-3.0/repodlgs.c +++ /dev/null @@ -1,1926 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "fc_types.h" /* LINE_BREAK */ -#include "game.h" -#include "government.h" -#include "packets.h" -#include "research.h" -#include "tech.h" -#include "unitlist.h" - -/* client */ -#include "chatline_common.h" -#include "client_main.h" -#include "climisc.h" -#include "control.h" -#include "mapview_common.h" -#include "options.h" -#include "packhand_gen.h" -#include "control.h" -#include "reqtree.h" -#include "text.h" - -/* client/gui-gtk-3.0 */ -#include "canvas.h" -#include "cityrep.h" -#include "dialogs.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "helpdlg.h" -#include "plrdlg.h" -#include "sprite.h" - -#include "repodlgs.h" - - -/**************************************************************************** - RESEARCH REPORT DIALOG -****************************************************************************/ -struct science_report { - struct gui_dialog *shell; - GtkComboBox *reachable_techs; - GtkComboBox *reachable_goals; - GtkWidget *button_show_all; - GtkLabel *main_label; /* Gets science_dialog_text(). */ - GtkProgressBar *progress_bar; - GtkLabel *goal_label; - GtkLayout *drawing_area; -}; - -static GtkListStore *science_report_store_new(void); -static inline void science_report_store_set(GtkListStore *store, - GtkTreeIter *iter, - Tech_type_id tech); -static bool science_report_combo_get_active(GtkComboBox *combo, - Tech_type_id *tech, - const char **name); -static void science_report_combo_set_active(GtkComboBox *combo, - Tech_type_id tech); -static gboolean science_diagram_button_release_callback(GtkWidget *widget, - GdkEventButton *event, gpointer data); -static gboolean science_diagram_update(GtkWidget *widget, - cairo_t *cr, - gpointer data); -static GtkWidget *science_diagram_new(void); -static void science_diagram_data(GtkWidget *widget, bool show_all); -static void science_diagram_center(GtkWidget *diagram, Tech_type_id tech); -static void science_report_redraw(struct science_report *preport); -static gint cmp_func(gconstpointer a_p, gconstpointer b_p); -static void science_report_update(struct science_report *preport); -static void science_report_current_callback(GtkComboBox *combo, - gpointer data); -static void science_report_show_all_callback(GtkComboBox *combo, - gpointer data); -static void science_report_goal_callback(GtkComboBox *combo, gpointer data); -static void science_report_init(struct science_report *preport); -static void science_report_free(struct science_report *preport); - -static struct science_report science_report = { NULL, }; -static bool science_report_no_combo_callback = FALSE; - -/* Those values must match the function science_report_store_new(). */ -enum science_report_columns { - SRD_COL_NAME, - SRD_COL_STEPS, - - /* Not visible. */ - SRD_COL_ID, /* Tech_type_id */ - - SRD_COL_NUM -}; - -/************************************************************************//** - Create a science report list store. -****************************************************************************/ -static GtkListStore *science_report_store_new(void) -{ - return gtk_list_store_new(SRD_COL_NUM, - G_TYPE_STRING, /* SRD_COL_NAME */ - G_TYPE_INT, /* SRD_COL_STEPS */ - G_TYPE_INT); /* SRD_COL_ID */ -} - -/************************************************************************//** - Append a technology to the list store. -****************************************************************************/ -static inline void science_report_store_set(GtkListStore *store, - GtkTreeIter *iter, - Tech_type_id tech) -{ - const struct research *presearch = research_get(client_player()); - - gtk_list_store_set(store, iter, - SRD_COL_NAME, - research_advance_name_translation(presearch, tech), - SRD_COL_STEPS, - research_goal_unknown_techs(presearch, tech), - SRD_COL_ID, tech, - -1); -} - -/************************************************************************//** - Get the active tech of the combo. -****************************************************************************/ -static bool science_report_combo_get_active(GtkComboBox *combo, - Tech_type_id *tech, - const char **name) -{ - GtkTreeIter iter; - - if (science_report_no_combo_callback - || !gtk_combo_box_get_active_iter(combo, &iter)) { - return FALSE; - } - - gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, - SRD_COL_NAME, name, - SRD_COL_ID, tech, - -1); - return TRUE; -} - -/************************************************************************//** - Set the active tech of the combo. -****************************************************************************/ -static void science_report_combo_set_active(GtkComboBox *combo, - Tech_type_id tech) -{ - ITree iter; - Tech_type_id iter_tech; - - for (itree_begin(gtk_combo_box_get_model(combo), &iter); - !itree_end(&iter); itree_next(&iter)) { - itree_get(&iter, SRD_COL_ID, &iter_tech, -1); - if (iter_tech == tech) { - science_report_no_combo_callback = TRUE; - gtk_combo_box_set_active_iter(combo, &iter.it); - science_report_no_combo_callback = FALSE; - return; - } - } - log_error("%s(): Tech %d not found in the combo.", __FUNCTION__, tech); -} - -/************************************************************************//** - Change tech goal, research or open help dialog. -****************************************************************************/ -static gboolean science_diagram_button_release_callback(GtkWidget *widget, - GdkEventButton *event, gpointer data) -{ - const struct research *presearch = research_get(client_player()); - struct reqtree *reqtree = g_object_get_data(G_OBJECT(widget), "reqtree"); - Tech_type_id tech = get_tech_on_reqtree(reqtree, event->x, event->y); - - if (tech == A_NONE) { - return TRUE; - } - - if (event->button == 3) { - /* RMB: get help */ - popup_help_dialog_typed(research_advance_name_translation(presearch, - tech), - HELP_TECH); - } else { - if (event->button == 1 && can_client_issue_orders()) { - /* LMB: set research or research goal */ - switch (research_invention_state(research_get(client_player()), - tech)) { - case TECH_PREREQS_KNOWN: - dsend_packet_player_research(&client.conn, tech); - break; - case TECH_UNKNOWN: - dsend_packet_player_tech_goal(&client.conn, tech); - break; - case TECH_KNOWN: - break; - } - } - } - return TRUE; -} - -/************************************************************************//** - Draw the invalidated portion of the reqtree. -****************************************************************************/ -static gboolean science_diagram_update(GtkWidget *widget, cairo_t *cr, gpointer data) -{ - /* FIXME: this currently redraws everything! */ - struct canvas canvas = FC_STATIC_CANVAS_INIT; - struct reqtree *reqtree = g_object_get_data(G_OBJECT(widget), "reqtree"); - int width, height; - GtkAdjustment *hadjustment; - GtkAdjustment *vadjustment; - gint hadjustment_value; - gint vadjustment_value; - - if (!tileset_is_fully_loaded()) { - return TRUE; - } - - hadjustment = gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(widget)); - vadjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(widget)); - - hadjustment_value = (gint)gtk_adjustment_get_value(hadjustment); - vadjustment_value = (gint)gtk_adjustment_get_value(vadjustment); - - cairo_translate(cr, -hadjustment_value, -vadjustment_value); - - canvas.drawable = cr; - - get_reqtree_dimensions(reqtree, &width, &height); - draw_reqtree(reqtree, &canvas, 0, 0, 0, 0, width, height); - - return TRUE; -} - -/************************************************************************//** - Return the drawing area widget of new technology diagram. Set in 'x' the - position of the current tech to center to it. -****************************************************************************/ -static GtkWidget *science_diagram_new(void) -{ - GtkWidget *diagram; - - diagram = gtk_layout_new(NULL, NULL); - gtk_widget_add_events(diagram, - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK); - g_signal_connect(diagram, "draw", - G_CALLBACK(science_diagram_update), NULL); - g_signal_connect(diagram, "button-release-event", - G_CALLBACK(science_diagram_button_release_callback), - NULL); - - return diagram; -} - -/************************************************************************//** - Recreate the req tree. -****************************************************************************/ -static void science_diagram_data(GtkWidget *widget, bool show_all) -{ - struct reqtree *reqtree; - int width, height; - - if (can_conn_edit(&client.conn)) { - /* Show all techs in editor mode, not only currently reachable ones */ - reqtree = create_reqtree(NULL, TRUE); - } else { - /* Show only at some point reachable techs */ - reqtree = create_reqtree(client_player(), show_all); - } - - get_reqtree_dimensions(reqtree, &width, &height); - gtk_layout_set_size(GTK_LAYOUT(widget), width, height); - g_object_set_data_full(G_OBJECT(widget), "reqtree", reqtree, - (GDestroyNotify) destroy_reqtree); -} - -/************************************************************************//** - Set the diagram parent to point to 'tech' location. -****************************************************************************/ -static void science_diagram_center(GtkWidget *diagram, Tech_type_id tech) -{ - GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(gtk_widget_get_parent(diagram)); - struct reqtree *reqtree; - int x, y, width, height; - - if (!GTK_IS_SCROLLED_WINDOW(sw)) { - return; - } - - reqtree = g_object_get_data(G_OBJECT(diagram), "reqtree"); - get_reqtree_dimensions(reqtree, &width, &height); - if (find_tech_on_reqtree(reqtree, tech, &x, &y, NULL, NULL)) { - GtkAdjustment *adjust = NULL; - gdouble value; - - adjust = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw)); - value = (gtk_adjustment_get_lower(adjust) - + gtk_adjustment_get_upper(adjust) - - gtk_adjustment_get_page_size(adjust)) / width * x; - gtk_adjustment_set_value(adjust, value); - gtk_adjustment_value_changed(adjust); - - adjust = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)); - value = (gtk_adjustment_get_lower(adjust) - + gtk_adjustment_get_upper(adjust) - - gtk_adjustment_get_page_size(adjust)) / height * y; - gtk_adjustment_set_value(adjust, value); - gtk_adjustment_value_changed(adjust); - } -} - -/************************************************************************//** - Resize and redraw the requirement tree. -****************************************************************************/ -static void science_report_redraw(struct science_report *preport) -{ - Tech_type_id researching; - - fc_assert_ret(NULL != preport); - - science_diagram_data(GTK_WIDGET(preport->drawing_area), - gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( - preport->button_show_all))); - - if (client_has_player()) { - researching = research_get(client_player())->researching; - } else { - researching = A_UNSET; - } - science_diagram_center(GTK_WIDGET(preport->drawing_area), researching); - - gtk_widget_queue_draw(GTK_WIDGET(preport->drawing_area)); -} - -/************************************************************************//** - Utility for g_list_sort. -****************************************************************************/ -static gint cmp_func(gconstpointer a_p, gconstpointer b_p) -{ - const gchar *a_str, *b_str; - gint a = GPOINTER_TO_INT(a_p), b = GPOINTER_TO_INT(b_p); - const struct research *presearch = research_get(client_player()); - - a_str = research_advance_name_translation(presearch, a); - b_str = research_advance_name_translation(presearch, b); - - return fc_strcoll(a_str, b_str); -} - -/************************************************************************//** - Update a science report dialog. -****************************************************************************/ -static void science_report_update(struct science_report *preport) -{ - GtkListStore *store; - GtkTreeIter iter; - GList *sorting_list, *item; - struct research *presearch = research_get(client_player()); - const char *text; - double pct; - Tech_type_id tech; - - fc_assert_ret(NULL != preport); - fc_assert_ret(NULL != presearch); - - /* Disable callbacks. */ - science_report_no_combo_callback = TRUE; - - gtk_widget_queue_draw(GTK_WIDGET(preport->drawing_area)); - - gtk_label_set_text(preport->main_label, science_dialog_text()); - - /* Update the progress bar. */ - text = get_science_target_text(&pct); - gtk_progress_bar_set_text(preport->progress_bar, text); - gtk_progress_bar_set_fraction(preport->progress_bar, pct); - /* Work around GTK+ refresh bug? */ - gtk_widget_queue_resize(GTK_WIDGET(preport->progress_bar)); - - /* Update reachable techs. */ - store = GTK_LIST_STORE(gtk_combo_box_get_model(preport->reachable_techs)); - gtk_list_store_clear(store); - sorting_list = NULL; - if (A_UNSET == presearch->researching - || is_future_tech(presearch->researching)) { - gtk_list_store_append(store, &iter); - science_report_store_set(store, &iter, presearch->researching); - gtk_combo_box_set_active_iter(preport->reachable_techs, &iter); - } - - /* Collect all techs which are reachable in the next step. */ - advance_index_iterate(A_FIRST, i) { - if (TECH_PREREQS_KNOWN == presearch->inventions[i].state) { - sorting_list = g_list_prepend(sorting_list, GINT_TO_POINTER(i)); - } - } advance_index_iterate_end; - - /* Sort the list, append it to the store. */ - sorting_list = g_list_sort(sorting_list, cmp_func); - for (item = sorting_list; NULL != item; item = g_list_next(item)) { - tech = GPOINTER_TO_INT(item->data); - gtk_list_store_append(store, &iter); - science_report_store_set(store, &iter, tech); - if (tech == presearch->researching) { - gtk_combo_box_set_active_iter(preport->reachable_techs, &iter); - } - } - - /* Free, re-init. */ - g_list_free(sorting_list); - sorting_list = NULL; - store = GTK_LIST_STORE(gtk_combo_box_get_model(preport->reachable_goals)); - gtk_list_store_clear(store); - - /* Update the tech goal. */ - gtk_label_set_text(preport->goal_label, - get_science_goal_text(presearch->tech_goal)); - - if (A_UNSET == presearch->tech_goal) { - gtk_list_store_append(store, &iter); - science_report_store_set(store, &iter, A_UNSET); - gtk_combo_box_set_active_iter(preport->reachable_goals, &iter); - } - - /* Collect all techs which are reachable in next 10 steps. */ - advance_index_iterate(A_FIRST, i) { - if (research_invention_reachable(presearch, i) - && TECH_KNOWN != presearch->inventions[i].state - && (i == presearch->tech_goal - || 10 >= presearch->inventions[i].num_required_techs)) { - sorting_list = g_list_prepend(sorting_list, GINT_TO_POINTER(i)); - } - } advance_index_iterate_end; - - /* Sort the list, append it to the store. */ - sorting_list = g_list_sort(sorting_list, cmp_func); - for (item = sorting_list; NULL != item; item = g_list_next(item)) { - tech = GPOINTER_TO_INT(item->data); - gtk_list_store_append(store, &iter); - science_report_store_set(store, &iter, tech); - if (tech == presearch->tech_goal) { - gtk_combo_box_set_active_iter(preport->reachable_goals, &iter); - } - } - - /* Free. */ - g_list_free(sorting_list); - - /* Re-enable callbacks. */ - science_report_no_combo_callback = FALSE; -} - -/************************************************************************//** - Actived item in the reachable techs combo box. -****************************************************************************/ -static void science_report_current_callback(GtkComboBox *combo, - gpointer data) -{ - Tech_type_id tech; - const char *tech_name; - - if (!science_report_combo_get_active(combo, &tech, &tech_name)) { - return; - } - - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data))) { - popup_help_dialog_typed(tech_name, HELP_TECH); - } else if (can_client_issue_orders()) { - dsend_packet_player_research(&client.conn, tech); - } - /* Revert, or we will be not synchron with the server. */ - science_report_combo_set_active(combo, research_get - (client_player())->researching); -} - -/************************************************************************//** - Show or hide unreachable techs. -****************************************************************************/ -static void science_report_show_all_callback(GtkComboBox *combo, - gpointer data) -{ - struct science_report *preport = (struct science_report *) data; - - science_report_redraw(preport); -} - -/************************************************************************//** - Actived item in the reachable goals combo box. -****************************************************************************/ -static void science_report_goal_callback(GtkComboBox *combo, gpointer data) -{ - Tech_type_id tech; - const char *tech_name; - - if (!science_report_combo_get_active(combo, &tech, &tech_name)) { - return; - } - - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data))) { - popup_help_dialog_typed(tech_name, HELP_TECH); - } else if (can_client_issue_orders()) { - dsend_packet_player_tech_goal(&client.conn, tech); - } - /* Revert, or we will be not synchron with the server. */ - science_report_combo_set_active(combo, research_get - (client_player())->tech_goal); -} - -/************************************************************************//** - Initialize a science report. -****************************************************************************/ -static void science_report_init(struct science_report *preport) -{ - GtkWidget *frame, *table, *help_button, *show_all_button, *sw, *w; - GtkSizeGroup *group; - GtkContainer *vbox; - GtkListStore *store; - GtkCellRenderer *renderer; - - fc_assert_ret(NULL != preport); - - gui_dialog_new(&preport->shell, GTK_NOTEBOOK(top_notebook), NULL, TRUE); - /* TRANS: Research report title */ - gui_dialog_set_title(preport->shell, _("Research")); - - gui_dialog_add_button(preport->shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - gui_dialog_set_default_response(preport->shell, GTK_RESPONSE_CLOSE); - - vbox = GTK_CONTAINER(preport->shell->vbox); - group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); - - w = gtk_label_new(NULL); - gtk_container_add(vbox, w); - preport->main_label = GTK_LABEL(w); - - /* Current research target line. */ - frame = gtk_frame_new(_("Researching")); - gtk_container_add(vbox, frame); - - table = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(table), 4); - gtk_container_add(GTK_CONTAINER(frame), table); - - help_button = gtk_check_button_new_with_label(_("Help")); - gtk_grid_attach(GTK_GRID(table), help_button, 5, 0, 1, 1); - - store = science_report_store_new(); - w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); - gtk_size_group_add_widget(group, w); - g_object_unref(G_OBJECT(store)); - renderer = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), renderer, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), renderer, "text", - SRD_COL_NAME, NULL); - gtk_widget_set_sensitive(w, can_client_issue_orders()); - g_signal_connect(w, "changed", G_CALLBACK(science_report_current_callback), - help_button); - gtk_grid_attach(GTK_GRID(table), w, 0, 0, 1, 1); - preport->reachable_techs = GTK_COMBO_BOX(w); - - w = gtk_progress_bar_new(); - gtk_widget_set_hexpand(w, TRUE); - gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(w), TRUE); - gtk_grid_attach(GTK_GRID(table), w, 2, 0, 1, 1); - gtk_widget_set_size_request(w, -1, 25); - preport->progress_bar = GTK_PROGRESS_BAR(w); - - /* Research goal line. */ - frame = gtk_frame_new( _("Goal")); - gtk_container_add(vbox, frame); - - table = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(table), 4); - gtk_container_add(GTK_CONTAINER(frame),table); - - store = science_report_store_new(); - w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); - gtk_size_group_add_widget(group, w); - g_object_unref(G_OBJECT(store)); - renderer = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), renderer, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), renderer, "text", - SRD_COL_NAME, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(w), renderer, FALSE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), renderer, "text", - SRD_COL_STEPS, NULL); - gtk_widget_set_sensitive(w, can_client_issue_orders()); - g_signal_connect(w, "changed", G_CALLBACK(science_report_goal_callback), - help_button); - gtk_grid_attach(GTK_GRID(table), w, 0, 0, 1, 1); - preport->reachable_goals = GTK_COMBO_BOX(w); - - w = gtk_label_new(NULL); - gtk_widget_set_hexpand(w, TRUE); - gtk_grid_attach(GTK_GRID(table), w, 2, 0, 1, 1); - gtk_widget_set_size_request(w, -1, 25); - preport->goal_label = GTK_LABEL(w); - - /* Toggle "Show All" button. */ - /* TRANS: As in 'Show all (even not reachable) techs'. */ - show_all_button = gtk_toggle_button_new_with_label(_("Show all")); - gtk_grid_attach(GTK_GRID(table), show_all_button, 5, 0, 1, 1); - g_signal_connect(show_all_button, "toggled", - G_CALLBACK(science_report_show_all_callback), preport); - gtk_widget_set_sensitive(show_all_button, can_client_issue_orders() - && !client_is_global_observer()); - preport->button_show_all = show_all_button; - - /* Science diagram. */ - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add(vbox, sw); - - w = science_diagram_new(); - gtk_widget_set_hexpand(w, TRUE); - gtk_widget_set_vexpand(w, TRUE); - gtk_container_add(GTK_CONTAINER(sw), w); - preport->drawing_area = GTK_LAYOUT(w); - - science_report_update(preport); - gui_dialog_show_all(preport->shell); - gtk_widget_queue_draw(GTK_WIDGET(preport->drawing_area)); - g_object_unref(group); - - /* This must be _after_ the dialog is drawn to really center it ... */ - science_report_redraw(preport); -} - -/************************************************************************//** - Free a science report. -****************************************************************************/ -static void science_report_free(struct science_report *preport) -{ - fc_assert_ret(NULL != preport); - - gui_dialog_destroy(preport->shell); - fc_assert(NULL == preport->shell); - - memset(preport, 0, sizeof(*preport)); -} - -/************************************************************************//** - Create the science report is needed. -****************************************************************************/ -void science_report_dialog_popup(bool raise) -{ - struct research *presearch = research_get(client_player()); - - if (NULL == science_report.shell) { - science_report_init(&science_report); - } - - if (NULL != presearch - && A_UNSET == presearch->tech_goal - && A_UNSET == presearch->researching) { - gui_dialog_alert(science_report.shell); - } else { - gui_dialog_present(science_report.shell); - } - - if (raise) { - gui_dialog_raise(science_report.shell); - } -} - -/************************************************************************//** - Closes the science report dialog. -****************************************************************************/ -void science_report_dialog_popdown(void) -{ - if (NULL != science_report.shell) { - science_report_free(&science_report); - fc_assert(NULL == science_report.shell); - } -} - -/************************************************************************//** - Update the science report dialog. -****************************************************************************/ -void real_science_report_dialog_update(void *unused) -{ - if (NULL != science_report.shell) { - science_report_update(&science_report); - } -} - -/************************************************************************//** - Resize and redraw the requirement tree. -****************************************************************************/ -void science_report_dialog_redraw(void) -{ - if (NULL != science_report.shell) { - science_report_redraw(&science_report); - } -} - - -/**************************************************************************** - ECONOMY REPORT DIALOG -****************************************************************************/ -struct economy_report { - struct gui_dialog *shell; - GtkTreeView *tree_view; - GtkLabel *label; -}; - -static struct economy_report economy_report = { NULL, NULL, NULL }; - -enum economy_report_response { - ERD_RES_SELL_REDUNDANT = 1, - ERD_RES_SELL_ALL, - ERD_RES_DISBAND_UNITS -}; - -/* Those values must match the functions economy_report_store_new() and - * economy_report_column_name(). */ -enum economy_report_columns { - ERD_COL_SPRITE, - ERD_COL_NAME, - ERD_COL_REDUNDANT, - ERD_COL_COUNT, - ERD_COL_COST, - ERD_COL_TOTAL_COST, - - /* Not visible. */ - ERD_COL_IS_IMPROVEMENT, - ERD_COL_CID, - - ERD_COL_NUM -}; - -/************************************************************************//** - Create a new economy report list store. -****************************************************************************/ -static GtkListStore *economy_report_store_new(void) -{ - return gtk_list_store_new(ERD_COL_NUM, - GDK_TYPE_PIXBUF, /* ERD_COL_SPRITE */ - G_TYPE_STRING, /* ERD_COL_NAME */ - G_TYPE_INT, /* ERD_COL_REDUNDANT */ - G_TYPE_INT, /* ERD_COL_COUNT */ - G_TYPE_INT, /* ERD_COL_COST */ - G_TYPE_INT, /* ERD_COL_TOTAL_COST */ - G_TYPE_BOOLEAN, /* ERD_COL_IS_IMPROVEMENT */ - G_TYPE_INT, /* ERD_COL_UNI_KIND */ - G_TYPE_INT); /* ERD_COL_UNI_VALUE_ID */ -} - -/************************************************************************//** - Returns the title of the column (translated). -****************************************************************************/ -static const char * -economy_report_column_name(enum economy_report_columns col) -{ - switch (col) { - case ERD_COL_SPRITE: - /* TRANS: Image header */ - return _("Type"); - case ERD_COL_NAME: - return Q_("?Building or Unit type:Name"); - case ERD_COL_REDUNDANT: - return _("Redundant"); - case ERD_COL_COUNT: - return _("Count"); - case ERD_COL_COST: - return _("Cost"); - case ERD_COL_TOTAL_COST: - /* TRANS: Upkeep total, count*cost. */ - return _("U Total"); - case ERD_COL_IS_IMPROVEMENT: - case ERD_COL_CID: - case ERD_COL_NUM: - break; - } - - return NULL; -} - -/************************************************************************//** - Update the economy report dialog. -****************************************************************************/ -static void economy_report_update(struct economy_report *preport) -{ - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkListStore *store; - GtkTreeIter iter; - GdkPixbuf *pix; - struct improvement_entry building_entries[B_LAST]; - struct unit_entry unit_entries[U_LAST]; - int entries_used, building_total, unit_total, tax, i; - char buf[256]; - cid selected; - - fc_assert_ret(NULL != preport); - - /* Save the selection. */ - selection = gtk_tree_view_get_selection(preport->tree_view); - if (gtk_tree_selection_get_selected(selection, &model, &iter)) { - gtk_tree_model_get(model, &iter, ERD_COL_CID, &selected, -1); - } else { - selected = -1; - } - - model = gtk_tree_view_get_model(preport->tree_view); - store = GTK_LIST_STORE(model); - gtk_list_store_clear(store); - - /* Buildings. */ - get_economy_report_data(building_entries, &entries_used, - &building_total, &tax); - for (i = 0; i < entries_used; i++) { - struct improvement_entry *pentry = building_entries + i; - struct impr_type *pimprove = pentry->type; - struct sprite *sprite = get_building_sprite(tileset, pimprove); - cid id = cid_encode_building(pimprove); - - pix = sprite_get_pixbuf(sprite); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - ERD_COL_SPRITE, pix, - ERD_COL_NAME, improvement_name_translation(pimprove), - ERD_COL_REDUNDANT, pentry->redundant, - ERD_COL_COUNT, pentry->count, - ERD_COL_COST, pentry->cost, - ERD_COL_TOTAL_COST, pentry->total_cost, - ERD_COL_IS_IMPROVEMENT, TRUE, - ERD_COL_CID, id, - -1); - g_object_unref(G_OBJECT(pix)); - if (selected == id) { - /* Restore the selection. */ - gtk_tree_selection_select_iter(selection, &iter); - } - } - - /* Units. */ - get_economy_report_units_data(unit_entries, &entries_used, &unit_total); - for (i = 0; i < entries_used; i++) { - struct unit_entry *pentry = unit_entries + i; - struct unit_type *putype = pentry->type; - struct sprite *sprite = get_unittype_sprite(tileset, putype, - direction8_invalid()); - cid id = cid_encode_unit(putype); - - pix = sprite_get_pixbuf(sprite); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - ERD_COL_SPRITE, pix, - ERD_COL_NAME, utype_name_translation(putype), - ERD_COL_REDUNDANT, 0, - ERD_COL_COUNT, pentry->count, - ERD_COL_COST, pentry->cost, - ERD_COL_TOTAL_COST, pentry->total_cost, - ERD_COL_IS_IMPROVEMENT, FALSE, - ERD_COL_CID, id, - -1); - g_object_unref(G_OBJECT(pix)); - if (selected == id) { - /* Restore the selection. */ - gtk_tree_selection_select_iter(selection, &iter); - } - } - - /* Update the label. */ - fc_snprintf(buf, sizeof(buf), _("Income: %d Total Costs: %d"), - tax, building_total + unit_total); - gtk_label_set_text(preport->label, buf); -} - -/************************************************************************//** - Issue a command on the economy report. -****************************************************************************/ -static void economy_report_command_callback(struct gui_dialog *pdialog, - int response, - gpointer data) -{ - struct economy_report *preport = data; - GtkTreeSelection *selection = gtk_tree_view_get_selection(preport->tree_view); - GtkTreeModel *model; - GtkTreeIter iter; - GtkWidget *shell; - struct universal selected; - cid id; - char buf[256] = ""; - - switch (response) { - case ERD_RES_SELL_REDUNDANT: - case ERD_RES_SELL_ALL: - case ERD_RES_DISBAND_UNITS: - break; - default: - gui_dialog_destroy(pdialog); - return; - } - - if (!can_client_issue_orders() - || !gtk_tree_selection_get_selected(selection, &model, &iter)) { - return; - } - - gtk_tree_model_get(model, &iter, ERD_COL_CID, &id, -1); - selected = cid_decode(id); - - switch (selected.kind) { - case VUT_IMPROVEMENT: - { - const struct impr_type *pimprove = selected.value.building; - - if (can_sell_building(pimprove) - && (ERD_RES_SELL_ALL == response - || (ERD_RES_SELL_REDUNDANT == response))) { - bool redundant = (ERD_RES_SELL_REDUNDANT == response); - gint count; - gtk_tree_model_get(model, &iter, - redundant ? ERD_COL_REDUNDANT : ERD_COL_COUNT, - &count, -1); - if (count == 0) { - break; - } - shell = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL - | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, - redundant - /* TRANS: %s is an improvement */ - ? _("Do you really wish to sell " - "every redundant %s (%d total)?") - /* TRANS: %s is an improvement */ - : _("Do you really wish to sell " - "every %s (%d total)?"), - improvement_name_translation(pimprove), - count); - setup_dialog(shell, gui_dialog_get_toplevel(pdialog)); - gtk_window_set_title(GTK_WINDOW(shell), _("Sell Improvements")); - - if (GTK_RESPONSE_YES == gtk_dialog_run(GTK_DIALOG(shell))) { - sell_all_improvements(pimprove, redundant, buf, sizeof(buf)); - } - gtk_widget_destroy(shell); - } - } - break; - case VUT_UTYPE: - { - if (ERD_RES_DISBAND_UNITS == response) { - const struct unit_type *putype = selected.value.utype; - gint count; - - gtk_tree_model_get(model, &iter, ERD_COL_COUNT, &count, -1); - - shell = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL - | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, - /* TRANS: %s is a unit */ - _("Do you really wish to disband " - "every %s (%d total)?"), - utype_name_translation(putype), - count); - setup_dialog(shell, gui_dialog_get_toplevel(pdialog)); - gtk_window_set_title(GTK_WINDOW(shell), _("Disband Units")); - - if (GTK_RESPONSE_YES == gtk_dialog_run(GTK_DIALOG(shell))) { - disband_all_units(putype, FALSE, buf, sizeof(buf)); - } - gtk_widget_destroy(shell); - } - } - break; - default: - log_error("Not supported type: %d.", selected.kind); - } - - if ('\0' != buf[0]) { - shell = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, - "%s", buf); - setup_dialog(shell, gui_dialog_get_toplevel(pdialog)); - g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy), - NULL); - gtk_window_set_title(GTK_WINDOW(shell), _("Sell-Off: Results")); - gtk_window_present(GTK_WINDOW(shell)); - } -} - -/************************************************************************//** - Called when a building or a unit type is selected in the economy list. -****************************************************************************/ -static void economy_report_selection_callback(GtkTreeSelection *selection, - gpointer data) -{ - struct gui_dialog *pdialog = ((struct economy_report *)data)->shell; - GtkTreeModel *model; - GtkTreeIter iter; - - if (can_client_issue_orders() - && gtk_tree_selection_get_selected(selection, &model, &iter)) { - struct universal selected; - cid id; - - gtk_tree_model_get(model, &iter, ERD_COL_CID, &id, -1); - selected = cid_decode(id); - switch (selected.kind) { - case VUT_IMPROVEMENT: - { - bool can_sell = can_sell_building(selected.value.building); - gint redundant; - gtk_tree_model_get(model, &iter, ERD_COL_REDUNDANT, &redundant, -1); - - gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_REDUNDANT, - can_sell && redundant > 0); - gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_ALL, can_sell); - gui_dialog_set_response_sensitive(pdialog, ERD_RES_DISBAND_UNITS, - FALSE); - } - return; - case VUT_UTYPE: - gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_REDUNDANT, - FALSE); - gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_ALL, FALSE); - gui_dialog_set_response_sensitive(pdialog, ERD_RES_DISBAND_UNITS, - TRUE); - return; - default: - log_error("Not supported type: %d.", selected.kind); - break; - } - } - - gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_REDUNDANT, FALSE); - gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_ALL, FALSE); - gui_dialog_set_response_sensitive(pdialog, ERD_RES_DISBAND_UNITS, FALSE); -} - -/************************************************************************//** - Create a new economy report. -****************************************************************************/ -static void economy_report_init(struct economy_report *preport) -{ - GtkWidget *view, *sw, *label, *button; - GtkListStore *store; - GtkTreeSelection *selection; - GtkContainer *vbox; - const char *title; - enum economy_report_columns i; - - fc_assert_ret(NULL != preport); - - gui_dialog_new(&preport->shell, GTK_NOTEBOOK(top_notebook), preport, TRUE); - gui_dialog_set_title(preport->shell, _("Economy")); - vbox = GTK_CONTAINER(preport->shell->vbox); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_widget_set_halign(sw, GTK_ALIGN_CENTER); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(vbox), sw); - - store = economy_report_store_new(); - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(store); - gtk_widget_set_name(view, "small_font"); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view)); - gtk_container_add(GTK_CONTAINER(sw), view); - preport->tree_view = GTK_TREE_VIEW(view); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - g_signal_connect(selection, "changed", - G_CALLBACK(economy_report_selection_callback), preport); - - for (i = 0; (title = economy_report_column_name(i)); i++) { - GtkCellRenderer *renderer; - GtkTreeViewColumn *col; - GType type = gtk_tree_model_get_column_type(GTK_TREE_MODEL(store), i); - - if (GDK_TYPE_PIXBUF == type) { - renderer = gtk_cell_renderer_pixbuf_new(); - col = gtk_tree_view_column_new_with_attributes(title, renderer, - "pixbuf", i, NULL); -#if 0 - } else if (G_TYPE_BOOLEAN == type) { - renderer = gtk_cell_renderer_toggle_new(); - col = gtk_tree_view_column_new_with_attributes(title, renderer, - "active", i, NULL); -#endif - } else { - bool is_redundant = (i == ERD_COL_REDUNDANT); - renderer = gtk_cell_renderer_text_new(); - if (is_redundant) { - /* Special treatment: hide "Redundant" column for units */ - col = gtk_tree_view_column_new_with_attributes(title, renderer, - "text", i, - "visible", - ERD_COL_IS_IMPROVEMENT, - NULL); - } else { - col = gtk_tree_view_column_new_with_attributes(title, renderer, - "text", i, NULL); - } - } - - if (i > 1) { - g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); - gtk_tree_view_column_set_alignment(col, 1.0); - } - - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - } - - label = gtk_label_new(NULL); - gtk_container_add(vbox, label); - gtk_widget_set_margin_left(label, 5); - gtk_widget_set_margin_right(label, 5); - gtk_widget_set_margin_top(label, 5); - gtk_widget_set_margin_bottom(label, 5); - preport->label = GTK_LABEL(label); - - gui_dialog_add_button(preport->shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - - button = gui_dialog_add_button(preport->shell, _("_Disband"), - ERD_RES_DISBAND_UNITS); - gtk_widget_set_sensitive(button, FALSE); - - button = gui_dialog_add_button(preport->shell, _("Sell _All"), - ERD_RES_SELL_ALL); - gtk_widget_set_sensitive(button, FALSE); - - button = gui_dialog_add_button(preport->shell, _("Sell _Redundant"), - ERD_RES_SELL_REDUNDANT); - gtk_widget_set_sensitive(button, FALSE); - - gui_dialog_set_default_response(preport->shell, GTK_RESPONSE_CLOSE); - gui_dialog_response_set_callback(preport->shell, - economy_report_command_callback); - - gui_dialog_set_default_size(preport->shell, -1, 350); - gui_dialog_show_all(preport->shell); - - economy_report_update(preport); - - gtk_tree_view_focus(GTK_TREE_VIEW(view)); -} - -/************************************************************************//** - Free an economy report. -****************************************************************************/ -static void economy_report_free(struct economy_report *preport) -{ - fc_assert_ret(NULL != preport); - - gui_dialog_destroy(preport->shell); - fc_assert(NULL == preport->shell); - - memset(preport, 0, sizeof(*preport)); -} - -/************************************************************************//** - Create the economy report if needed. -****************************************************************************/ -void economy_report_dialog_popup(bool raise) -{ - if (NULL == economy_report.shell) { - economy_report_init(&economy_report); - } - - gui_dialog_present(economy_report.shell); - if (raise) { - gui_dialog_raise(economy_report.shell); - } -} - -/************************************************************************//** - Close the economy report dialog. -****************************************************************************/ -void economy_report_dialog_popdown(void) -{ - if (NULL != economy_report.shell) { - economy_report_free(&economy_report); - } -} - -/************************************************************************//** - Update the economy report dialog. -****************************************************************************/ -void real_economy_report_dialog_update(void *unused) -{ - if (NULL != economy_report.shell) { - economy_report_update(&economy_report); - } -} - - -/**************************************************************************** - UNITS REPORT DIALOG -****************************************************************************/ -struct units_report { - struct gui_dialog *shell; - GtkTreeView *tree_view; -}; - -static struct units_report units_report = { NULL, NULL }; - -enum units_report_response { - URD_RES_NEAREST = 1, - URD_RES_UPGRADE -}; - -/* Those values must match the order of unit_report_columns[]. */ -enum units_report_columns { - URD_COL_UTYPE_NAME, - URD_COL_UPGRADABLE, - URD_COL_N_UPGRADABLE, - URD_COL_IN_PROGRESS, - URD_COL_ACTIVE, - URD_COL_SHIELD, - URD_COL_FOOD, - URD_COL_GOLD, - - /* Not visible. */ - URD_COL_TEXT_WEIGHT, - URD_COL_UPG_VISIBLE, - URD_COL_NUPG_VISIBLE, - URD_COL_UTYPE_ID, - - URD_COL_NUM -}; - -static const struct { - GType type; - const char *title; - const char *tooltip; - bool rightalign; - int visible_col; -} unit_report_columns[] = { - { /* URD_COL_UTYPE_NAME */ G_TYPE_STRING, N_("Unit Type"), - NULL, FALSE, -1 }, - { /* URD_COL_UPGRADABLE */ G_TYPE_BOOLEAN, N_("?Upgradable unit [short]:U"), - N_("Upgradable"), TRUE, URD_COL_UPG_VISIBLE }, - { /* URD_COL_N_UPGRADABLE */ G_TYPE_INT, "" /* merge with previous col */, - NULL, TRUE, URD_COL_NUPG_VISIBLE }, - /* TRANS: "In progress" abbreviation. */ - { /* URD_COL_IN_PROGRESS */ G_TYPE_INT, N_("In-Prog"), - N_("In progress"), TRUE, -1 }, - { /* URD_COL_ACTIVE */ G_TYPE_INT, N_("Active"), - NULL, TRUE, -1 }, - { /* URD_COL_SHIELD */ G_TYPE_INT, N_("Shield"), - N_("Total shield upkeep"), TRUE, -1 }, - { /* URD_COL_FOOD */ G_TYPE_INT, N_("Food"), - N_("Total food upkeep"), TRUE, -1 }, - { /* URD_COL_GOLD */ G_TYPE_INT, N_("Gold"), - N_("Total gold upkeep"), TRUE, -1 }, - { /* URD_COL_TEXT_WEIGHT */ G_TYPE_INT, NULL /* ... */ }, - { /* URD_COL_UPG_VISIBLE */ G_TYPE_BOOLEAN, NULL /* ... */ }, - { /* URD_COL_NUPG_VISIBLE */ G_TYPE_BOOLEAN, NULL /* ... */ }, - { /* URD_COL_UTYPE_ID */ G_TYPE_INT, NULL /* ... */ } -}; - -/************************************************************************//** - Create a new units report list store. -****************************************************************************/ -static GtkListStore *units_report_store_new(void) -{ - int i; - GType cols[URD_COL_NUM]; - fc_assert(ARRAY_SIZE(unit_report_columns) == URD_COL_NUM); - - for (i=0; iunits, punit) { - info = unit_array + utype_index(unit_type_get(punit)); - - if (0 != punit->homecity) { - output_type_iterate(o) { - info->upkeep[o] += punit->upkeep[o]; - } output_type_iterate_end; - } - info->active_count++; - } unit_list_iterate_end; - city_list_iterate(pplayer->cities, pcity) { - if (VUT_UTYPE == pcity->production.kind) { - int num_units; - info = unit_array + utype_index(pcity->production.value.utype); - /* Account for build slots in city */ - (void) city_production_build_units(pcity, TRUE, &num_units); - /* Unit is in progress even if it won't be done this turn */ - num_units = MAX(num_units, 1); - info->building_count += num_units; - } - } city_list_iterate_end; - } players_iterate_end; - - /* Save selection. */ - selection = gtk_tree_view_get_selection(preport->tree_view); - if (gtk_tree_selection_get_selected(selection, &model, &iter)) { - gtk_tree_model_get(model, &iter, URD_COL_UTYPE_ID, &selected, -1); - } else { - selected = -1; - } - - /* Make the store. */ - model = gtk_tree_view_get_model(preport->tree_view); - store = GTK_LIST_STORE(model); - gtk_list_store_clear(store); - - unit_type_iterate(utype) { - bool upgradable; - - utype_id = utype_index(utype); - info = unit_array + utype_id; - - if (0 == info->active_count && 0 == info->building_count) { - continue; /* We don't need a row for this type. */ - } - - upgradable = client_has_player() - && NULL != can_upgrade_unittype(client_player(), utype); - - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - URD_COL_UTYPE_NAME, utype_name_translation(utype), - URD_COL_UPGRADABLE, upgradable, - URD_COL_N_UPGRADABLE, 0, /* never displayed */ - URD_COL_IN_PROGRESS, info->building_count, - URD_COL_ACTIVE, info->active_count, - URD_COL_SHIELD, info->upkeep[O_SHIELD], - URD_COL_FOOD, info->upkeep[O_FOOD], - URD_COL_GOLD, info->upkeep[O_GOLD], - URD_COL_TEXT_WEIGHT, PANGO_WEIGHT_NORMAL, - URD_COL_UPG_VISIBLE, TRUE, - URD_COL_NUPG_VISIBLE, FALSE, - URD_COL_UTYPE_ID, utype_id, - -1); - if (selected == utype_id) { - /* Restore the selection. */ - gtk_tree_selection_select_iter(selection, &iter); - } - - /* Update totals. */ - unit_totals.active_count += info->active_count; - output_type_iterate(o) { - unit_totals.upkeep[o] += info->upkeep[o]; - } output_type_iterate_end; - unit_totals.building_count += info->building_count; - if (upgradable) { - total_upgradable_count += info->active_count; - } - } unit_type_iterate_end; - - /* Add the total row. */ - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - URD_COL_UTYPE_NAME, _("Totals:"), - URD_COL_UPGRADABLE, FALSE, /* never displayed */ - URD_COL_N_UPGRADABLE, total_upgradable_count, - URD_COL_IN_PROGRESS, unit_totals.building_count, - URD_COL_ACTIVE, unit_totals.active_count, - URD_COL_SHIELD, unit_totals.upkeep[O_SHIELD], - URD_COL_FOOD, unit_totals.upkeep[O_FOOD], - URD_COL_GOLD, unit_totals.upkeep[O_GOLD], - URD_COL_TEXT_WEIGHT, PANGO_WEIGHT_BOLD, - URD_COL_UPG_VISIBLE, FALSE, - URD_COL_NUPG_VISIBLE, TRUE, - URD_COL_UTYPE_ID, U_LAST, - -1); - if (selected == U_LAST) { - /* Restore the selection. */ - gtk_tree_selection_select_iter(selection, &iter); - } -} - -/************************************************************************//** - GtkTreeSelection "changed" signal handler. -****************************************************************************/ -static void units_report_selection_callback(GtkTreeSelection *selection, - gpointer data) -{ - struct units_report *preport = data; - GtkTreeModel *model; - GtkTreeIter it; - int active_count; - struct unit_type *utype = NULL; - - if (gtk_tree_selection_get_selected(selection, &model, &it)) { - int ut; - - gtk_tree_model_get(model, &it, - URD_COL_ACTIVE, &active_count, - URD_COL_UTYPE_ID, &ut, - -1); - if (0 < active_count) { - utype = utype_by_number(ut); - } - } - - if (NULL == utype) { - gui_dialog_set_response_sensitive(preport->shell, URD_RES_NEAREST, - FALSE); - gui_dialog_set_response_sensitive(preport->shell, URD_RES_UPGRADE, - FALSE); - } else { - gui_dialog_set_response_sensitive(preport->shell, URD_RES_NEAREST, TRUE); - gui_dialog_set_response_sensitive(preport->shell, URD_RES_UPGRADE, - (can_client_issue_orders() - && NULL != can_upgrade_unittype(client_player(), utype))); - } -} - -/************************************************************************//** - Returns the nearest unit of the type 'utype'. -****************************************************************************/ -static struct unit *find_nearest_unit(const struct unit_type *utype, - struct tile *ptile) -{ - struct unit *best_candidate = NULL; - int best_dist = FC_INFINITY, dist; - - players_iterate(pplayer) { - if (client_has_player() && pplayer != client_player()) { - continue; - } - - unit_list_iterate(pplayer->units, punit) { - if (utype == unit_type_get(punit) - && FOCUS_AVAIL == punit->client.focus_status - && 0 < punit->moves_left - && !punit->done_moving - && punit->ssa_controller == SSA_NONE) { - dist = sq_map_distance(unit_tile(punit), ptile); - if (dist < best_dist) { - best_candidate = punit; - best_dist = dist; - } - } - } unit_list_iterate_end; - } players_iterate_end; - - return best_candidate; -} - -/************************************************************************//** - Gui dialog handler. -****************************************************************************/ -static void units_report_command_callback(struct gui_dialog *pdialog, - int response, - gpointer data) -{ - struct units_report *preport = data; - struct unit_type *utype = NULL; - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter it; - - switch (response) { - case URD_RES_NEAREST: - case URD_RES_UPGRADE: - break; - default: - gui_dialog_destroy(pdialog); - return; - } - - /* Nearest & upgrade commands. */ - selection = gtk_tree_view_get_selection(preport->tree_view); - if (gtk_tree_selection_get_selected(selection, &model, &it)) { - int ut; - - gtk_tree_model_get(model, &it, URD_COL_UTYPE_ID, &ut, -1); - utype = utype_by_number(ut); - } - - if (response == URD_RES_NEAREST) { - struct tile *ptile; - struct unit *punit; - - ptile = get_center_tile_mapcanvas(); - if ((punit = find_nearest_unit(utype, ptile))) { - center_tile_mapcanvas(unit_tile(punit)); - - if (ACTIVITY_IDLE == punit->activity - || ACTIVITY_SENTRY == punit->activity) { - if (can_unit_do_activity(punit, ACTIVITY_IDLE)) { - unit_focus_set_and_select(punit); - } - } - } - } else if (can_client_issue_orders()) { - GtkWidget *shell; - const struct unit_type *upgrade = can_upgrade_unittype(client_player(), utype); - char buf[1024]; - int price = unit_upgrade_price(client_player(), utype, upgrade); - - fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.", - "Treasury contains %d gold.", - client_player()->economic.gold), - client_player()->economic.gold); - - shell = gtk_message_dialog_new(NULL, - GTK_DIALOG_MODAL - | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - /* TRANS: Last %s is pre-pluralised - * "Treasury contains %d gold." */ - PL_("Upgrade as many %s to %s as possible " - "for %d gold each?\n%s", - "Upgrade as many %s to %s as possible " - "for %d gold each?\n%s", price), - utype_name_translation(utype), - utype_name_translation(upgrade), - price, buf); - setup_dialog(shell, gui_dialog_get_toplevel(preport->shell)); - - gtk_window_set_title(GTK_WINDOW(shell), _("Upgrade Obsolete Units")); - - if (GTK_RESPONSE_YES == gtk_dialog_run(GTK_DIALOG(shell))) { - dsend_packet_unit_type_upgrade(&client.conn, utype_number(utype)); - } - - gtk_widget_destroy(shell); - } -} - -/************************************************************************//** - Create a units report. -****************************************************************************/ -static void units_report_init(struct units_report *preport) -{ - GtkWidget *view, *sw, *button; - GtkListStore *store; - GtkTreeSelection *selection; - GtkContainer *vbox; - GtkTreeViewColumn *col = NULL; - enum units_report_columns i; - - fc_assert_ret(NULL != preport); - - gui_dialog_new(&preport->shell, GTK_NOTEBOOK(top_notebook), preport, TRUE); - gui_dialog_set_title(preport->shell, _("Units")); - vbox = GTK_CONTAINER(preport->shell->vbox); - - sw = gtk_scrolled_window_new(NULL,NULL); - gtk_widget_set_halign(sw, GTK_ALIGN_CENTER); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(vbox), sw); - - store = units_report_store_new(); - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_vexpand(view, TRUE); - g_object_unref(store); - gtk_widget_set_name(view, "small_font"); - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view)); - gtk_container_add(GTK_CONTAINER(sw), view); - preport->tree_view = GTK_TREE_VIEW(view); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); - g_signal_connect(selection, "changed", - G_CALLBACK(units_report_selection_callback), preport); - - for (i = 0; unit_report_columns[i].title != NULL; i++) { - GtkCellRenderer *renderer; - - if (strlen(unit_report_columns[i].title) > 0) { - GtkWidget *header = gtk_label_new(Q_(unit_report_columns[i].title)); - if (unit_report_columns[i].tooltip) { - gtk_widget_set_tooltip_text(header, - Q_(unit_report_columns[i].tooltip)); - } - gtk_widget_show(header); - col = gtk_tree_view_column_new(); - gtk_tree_view_column_set_widget(col, header); - if (unit_report_columns[i].rightalign) { - gtk_tree_view_column_set_alignment(col, 1.0); - } - gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); - } /* else add new renderer to previous TreeViewColumn */ - - fc_assert(col != NULL); - if (G_TYPE_BOOLEAN == unit_report_columns[i].type) { - renderer = gtk_cell_renderer_toggle_new(); - gtk_tree_view_column_pack_start(col, renderer, FALSE); - gtk_tree_view_column_add_attribute(col, renderer, "active", i); - } else { - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(col, renderer, TRUE); - gtk_tree_view_column_add_attribute(col, renderer, "text", i); - gtk_tree_view_column_add_attribute(col, renderer, - "weight", URD_COL_TEXT_WEIGHT); - } - - if (unit_report_columns[i].visible_col >= 0) { - gtk_tree_view_column_add_attribute(col, renderer, "visible", - unit_report_columns[i].visible_col); - } - - if (unit_report_columns[i].rightalign) { - g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); - } - } - - gui_dialog_add_button(preport->shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - - button = gui_dialog_add_button(preport->shell, _("_Upgrade"), - URD_RES_UPGRADE); - gtk_widget_set_sensitive(button, FALSE); - - button = gui_dialog_add_stockbutton(preport->shell, GTK_STOCK_FIND, - _("Find _Nearest"), URD_RES_NEAREST); - gtk_widget_set_sensitive(button, FALSE); - - gui_dialog_set_default_response(preport->shell, GTK_RESPONSE_CLOSE); - gui_dialog_response_set_callback(preport->shell, - units_report_command_callback); - - gui_dialog_set_default_size(preport->shell, -1, 350); - gui_dialog_show_all(preport->shell); - - units_report_update(preport); - gtk_tree_view_focus(GTK_TREE_VIEW(view)); -} - -/************************************************************************//** - Free an units report. -****************************************************************************/ -static void units_report_free(struct units_report *preport) -{ - fc_assert_ret(NULL != preport); - - gui_dialog_destroy(preport->shell); - fc_assert(NULL == preport->shell); - - memset(preport, 0, sizeof(*preport)); -} - -/************************************************************************//** - Create the units report if needed. -****************************************************************************/ -void units_report_dialog_popup(bool raise) -{ - if (NULL == units_report.shell) { - units_report_init(&units_report); - } - - gui_dialog_present(units_report.shell); - if (raise) { - gui_dialog_raise(units_report.shell); - } -} - -/************************************************************************//** - Closes the units report dialog. -****************************************************************************/ -void units_report_dialog_popdown(void) -{ - if (NULL != units_report.shell) { - units_report_free(&units_report); - fc_assert(NULL == units_report.shell); - } -} - -/************************************************************************//** - Update the units report dialog. -****************************************************************************/ -void real_units_report_dialog_update(void *unused) -{ - if (NULL != units_report.shell) { - units_report_update(&units_report); - } -} - - -/**************************************************************************** - FINAL REPORT DIALOG -****************************************************************************/ -struct endgame_report { - struct gui_dialog *shell; - GtkTreeView *tree_view; - GtkListStore *store; - int player_count; - int players_received; -}; - -enum endgame_report_columns { - FRD_COL_PLAYER, - FRD_COL_NATION, - FRD_COL_SCORE, - - FRD_COL_NUM -}; - -static struct endgame_report endgame_report = { NULL, NULL }; - -/************************************************************************//** - Returns the title of the column (translated). -****************************************************************************/ -static const char * -endgame_report_column_name(enum endgame_report_columns col) -{ - switch (col) { - case FRD_COL_PLAYER: - return _("Player\n"); - case FRD_COL_NATION: - return _("Nation\n"); - case FRD_COL_SCORE: - return _("Score\n"); - case FRD_COL_NUM: - break; - } - - return NULL; -} - -/************************************************************************//** - Fill a final report with statistics for each player. -****************************************************************************/ -static void endgame_report_update(struct endgame_report *preport, - const struct packet_endgame_report *packet) -{ - const size_t col_num = packet->category_num + FRD_COL_NUM; - GType col_types[col_num]; - GtkListStore *store; - GtkTreeViewColumn *col; - int i; - - fc_assert_ret(NULL != preport); - - /* Remove the old columns. */ - while ((col = gtk_tree_view_get_column(preport->tree_view, 0))) { - gtk_tree_view_remove_column(preport->tree_view, col); - } - - /* Create the new model. */ - col_types[FRD_COL_PLAYER] = G_TYPE_STRING; - col_types[FRD_COL_NATION] = GDK_TYPE_PIXBUF; - col_types[FRD_COL_SCORE] = G_TYPE_INT; - for (i = FRD_COL_NUM; (guint)i < col_num; i++) { - col_types[i] = G_TYPE_INT; - } - store = gtk_list_store_newv(col_num, col_types); - gtk_tree_view_set_model(preport->tree_view, GTK_TREE_MODEL(store)); - g_object_unref(G_OBJECT(store)); - - /* Create the new columns. */ - for (i = 0; (guint)i < col_num; i++) { - GtkCellRenderer *renderer; - const char *title; - const char *attribute; - - if (GDK_TYPE_PIXBUF == col_types[i]) { - renderer = gtk_cell_renderer_pixbuf_new(); - attribute = "pixbuf"; - } else { - renderer = gtk_cell_renderer_text_new(); - attribute = "text"; - } - - if (i < FRD_COL_NUM) { - title = endgame_report_column_name(i); - } else { - title = packet->category_name[i - FRD_COL_NUM]; - } - - col = gtk_tree_view_column_new_with_attributes(Q_(title), renderer, - attribute, i, NULL); - gtk_tree_view_append_column(preport->tree_view, col); - if (GDK_TYPE_PIXBUF != col_types[i]) { - gtk_tree_view_column_set_sort_column_id(col, i); - } - } - - preport->store = store; - preport->player_count = packet->player_num; - preport->players_received = 0; -} - -/************************************************************************//** - Handle endgame report information about one player. -****************************************************************************/ -void endgame_report_dialog_player(const struct packet_endgame_player *packet) -{ - /* Fill the model with player stats. */ - struct endgame_report *preport = &endgame_report; - const struct player *pplayer = player_by_number(packet->player_id); - GtkTreeIter iter; - int i; - - gtk_list_store_append(preport->store, &iter); - gtk_list_store_set(preport->store, &iter, - FRD_COL_PLAYER, player_name(pplayer), - FRD_COL_NATION, get_flag(nation_of_player(pplayer)), - FRD_COL_SCORE, packet->score, - -1); - for (i = 0; i < packet->category_num; i++) { - gtk_list_store_set(preport->store, &iter, - i + FRD_COL_NUM, packet->category_score[i], - -1); - } - - preport->players_received++; - - if (preport->players_received == preport->player_count) { - gui_dialog_present(preport->shell); - } -} - -/************************************************************************//** - Prepare a final report. -****************************************************************************/ -static void endgame_report_init(struct endgame_report *preport) -{ - GtkWidget *sw, *view; - - fc_assert_ret(NULL != preport); - - gui_dialog_new(&preport->shell, GTK_NOTEBOOK(top_notebook), NULL, TRUE); - gui_dialog_set_title(preport->shell, _("Score")); - - gui_dialog_set_default_size(preport->shell, 700, 420); - - /* Setup the layout. */ - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(preport->shell->vbox), sw); - - view = gtk_tree_view_new(); - gtk_widget_set_name(view, "small_font"); - gtk_container_add(GTK_CONTAINER(sw), view); - preport->tree_view = GTK_TREE_VIEW(view); - - if (preport->shell->type == GUI_DIALOG_TAB) { - gtk_widget_set_hexpand(GTK_WIDGET(view), TRUE); - gtk_widget_set_vexpand(GTK_WIDGET(view), TRUE); - } - - gui_dialog_show_all(preport->shell); -} - -/************************************************************************//** - Start building a dialog with player statistics at endgame. -****************************************************************************/ -void endgame_report_dialog_start(const struct packet_endgame_report *packet) -{ - if (NULL == endgame_report.shell) { - endgame_report_init(&endgame_report); - } - endgame_report_update(&endgame_report, packet); -} diff --git a/client/gui-gtk-3.0/repodlgs.h b/client/gui-gtk-3.0/repodlgs.h deleted file mode 100644 index 5b0c337c20..0000000000 --- a/client/gui-gtk-3.0/repodlgs.h +++ /dev/null @@ -1,22 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__REPODLGS_H -#define FC__REPODLGS_H - -#include "repodlgs_g.h" - -void science_report_dialog_popdown(void); -void economy_report_dialog_popdown(void); -void units_report_dialog_popdown(void); - -#endif /* FC__REPODLGS_H */ diff --git a/client/gui-gtk-3.0/soundset_dlg.c b/client/gui-gtk-3.0/soundset_dlg.c deleted file mode 100644 index 0ff0674b11..0000000000 --- a/client/gui-gtk-3.0/soundset_dlg.c +++ /dev/null @@ -1,145 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" - -/* common */ -#include "game.h" - -/* client */ -#include "audio.h" -#include "client_main.h" - -/* client/gui-gtk-3.0 */ -#include "gui_main.h" -#include "gui_stuff.h" - -#include "dialogs_g.h" - -static void soundset_suggestion_callback(GtkWidget *dlg, gint arg); -static void musicset_suggestion_callback(GtkWidget *dlg, gint arg); - -/************************************************************************//** - Callback either loading suggested soundset or doing nothing -****************************************************************************/ -static void soundset_suggestion_callback(GtkWidget *dlg, gint arg) -{ - if (arg == GTK_RESPONSE_YES) { - /* User accepted soundset loading */ - audio_restart(game.control.preferred_soundset, - music_set_name); - } -} - -/************************************************************************//** - Popup dialog asking if ruleset suggested soundset should be - used. -****************************************************************************/ -void popup_soundset_suggestion_dialog(void) -{ - GtkWidget *dialog, *label; - char buf[1024]; - - dialog = gtk_dialog_new_with_buttons(_("Preferred soundset"), - NULL, - 0, - _("_Load soundset"), - GTK_RESPONSE_YES, - _("_Keep current soundset"), - GTK_RESPONSE_NO, - NULL); - setup_dialog(dialog, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES); - gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); - - fc_snprintf(buf, sizeof(buf), - _("Modpack suggests using %s soundset.\n" - "It might not work with other soundsets.\n" - "You are currently using soundset %s."), - game.control.preferred_soundset, sound_set_name); - - label = gtk_label_new(buf); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label); - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); - gtk_widget_show(label); - - g_signal_connect(dialog, "response", - G_CALLBACK(soundset_suggestion_callback), NULL); - - /* In case incoming rulesets are incompatible with current soundset - * we need to block their receive before user has accepted loading - * of the correct soundset. */ - gtk_dialog_run(GTK_DIALOG(dialog)); - - gtk_widget_destroy(dialog); -} - -/************************************************************************//** - Callback either loading suggested musicset or doing nothing -****************************************************************************/ -static void musicset_suggestion_callback(GtkWidget *dlg, gint arg) -{ - if (arg == GTK_RESPONSE_YES) { - /* User accepted musicset loading */ - audio_restart(sound_set_name, game.control.preferred_musicset); - } -} - -/************************************************************************//** - Popup dialog asking if ruleset suggested musicset should be - used. -****************************************************************************/ -void popup_musicset_suggestion_dialog(void) -{ - GtkWidget *dialog, *label; - char buf[1024]; - - dialog = gtk_dialog_new_with_buttons(_("Preferred musicset"), - NULL, - 0, - _("_Load musicset"), - GTK_RESPONSE_YES, - _("_Keep current musicset"), - GTK_RESPONSE_NO, - NULL); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES); - gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); - - fc_snprintf(buf, sizeof(buf), - _("Modpack suggests using %s musicset.\n" - "It might not work with other musicsets.\n" - "You are currently using musicset %s."), - game.control.preferred_musicset, music_set_name); - - label = gtk_label_new(buf); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label); - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); - gtk_widget_show(label); - - g_signal_connect(dialog, "response", - G_CALLBACK(musicset_suggestion_callback), NULL); - - /* In case incoming rulesets are incompatible with current musicset - * we need to block their receive before user has accepted loading - * of the correct soundset. */ - gtk_dialog_run(GTK_DIALOG(dialog)); - - gtk_widget_destroy(dialog); -} diff --git a/client/gui-gtk-3.0/spaceshipdlg.c b/client/gui-gtk-3.0/spaceshipdlg.c deleted file mode 100644 index 6c0c56de5e..0000000000 --- a/client/gui-gtk-3.0/spaceshipdlg.c +++ /dev/null @@ -1,294 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "shared.h" -#include "support.h" - -/* common */ -#include "game.h" -#include "map.h" -#include "packets.h" -#include "player.h" -#include "spaceship.h" -#include "victory.h" - -/* client */ -#include "client_main.h" -#include "climisc.h" -#include "colors.h" -#include "options.h" -#include "text.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "dialogs.h" -#include "graphics.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "helpdlg.h" -#include "inputdlg.h" -#include "mapctrl.h" -#include "mapview.h" -#include "repodlgs.h" - -#include "spaceshipdlg.h" - -struct spaceship_dialog { - struct player *pplayer; - struct gui_dialog *shell; - GtkWidget *main_form; - GtkWidget *info_label; - GtkWidget *image_canvas; -}; - -#define SPECLIST_TAG dialog -#define SPECLIST_TYPE struct spaceship_dialog -#include "speclist.h" - -#define dialog_list_iterate(dialoglist, pdialog) \ - TYPED_LIST_ITERATE(struct spaceship_dialog, dialoglist, pdialog) -#define dialog_list_iterate_end LIST_ITERATE_END - -static struct dialog_list *dialog_list; - -static struct spaceship_dialog *get_spaceship_dialog(struct player *pplayer); -static struct spaceship_dialog *create_spaceship_dialog(struct player - *pplayer); - -static void spaceship_dialog_update_image(struct spaceship_dialog *pdialog); -static void spaceship_dialog_update_info(struct spaceship_dialog *pdialog); - -/************************************************************************//** - Initialize spaceship dialogs -****************************************************************************/ -void spaceship_dialog_init(void) -{ - dialog_list = dialog_list_new(); -} - -/************************************************************************//** - Free resources allocated for spaceship dialogs -****************************************************************************/ -void spaceship_dialog_done(void) -{ - dialog_list_destroy(dialog_list); -} - -/************************************************************************//** - Get spaceship dialog about certain player -****************************************************************************/ -struct spaceship_dialog *get_spaceship_dialog(struct player *pplayer) -{ - dialog_list_iterate(dialog_list, pdialog) { - if (pdialog->pplayer == pplayer) { - return pdialog; - } - } dialog_list_iterate_end; - - return NULL; -} - -/************************************************************************//** - Refresh spaceship dialog of certain player -****************************************************************************/ -void refresh_spaceship_dialog(struct player *pplayer) -{ - struct spaceship_dialog *pdialog; - struct player_spaceship *pship; - - if (!(pdialog = get_spaceship_dialog(pplayer))) { - return; - } - - pship = &(pdialog->pplayer->spaceship); - - if (victory_enabled(VC_SPACERACE) - && pplayer == client.conn.playing - && pship->state == SSHIP_STARTED - && pship->success_rate > 0.0) { - gui_dialog_set_response_sensitive(pdialog->shell, - GTK_RESPONSE_ACCEPT, TRUE); - } else { - gui_dialog_set_response_sensitive(pdialog->shell, - GTK_RESPONSE_ACCEPT, FALSE); - } - - spaceship_dialog_update_info(pdialog); - spaceship_dialog_update_image(pdialog); -} - -/************************************************************************//** - Popup the dialog 10% inside the main-window -****************************************************************************/ -void popup_spaceship_dialog(struct player *pplayer) -{ - struct spaceship_dialog *pdialog; - - if (!(pdialog = get_spaceship_dialog(pplayer))) { - pdialog = create_spaceship_dialog(pplayer); - } - - gui_dialog_raise(pdialog->shell); -} - -/************************************************************************//** - Popdown the dialog -****************************************************************************/ -void popdown_spaceship_dialog(struct player *pplayer) -{ - struct spaceship_dialog *pdialog; - - if ((pdialog = get_spaceship_dialog(pplayer))) { - gui_dialog_destroy(pdialog->shell); - } -} - -/************************************************************************//** - Spaceship dialog canvas got exposed -****************************************************************************/ -static gboolean spaceship_image_canvas_expose(GtkWidget *widget, - cairo_t *cr, - gpointer data) -{ - struct spaceship_dialog *pdialog = (struct spaceship_dialog *)data; - struct canvas store = FC_STATIC_CANVAS_INIT; - - store.drawable = cr; - - put_spaceship(&store, 0, 0, pdialog->pplayer); - - return TRUE; -} - -/************************************************************************//** - Spaceship dialog being destroyed -****************************************************************************/ -static void spaceship_destroy_callback(GtkWidget *w, gpointer data) -{ - struct spaceship_dialog *pdialog = (struct spaceship_dialog *)data; - - dialog_list_remove(dialog_list, pdialog); - - free(pdialog); -} - -/************************************************************************//** - User has responded to spaceship dialog -****************************************************************************/ -static void spaceship_response(struct gui_dialog *dlg, int response, - gpointer data) -{ - switch (response) { - case GTK_RESPONSE_ACCEPT: - send_packet_spaceship_launch(&client.conn); - break; - - default: - gui_dialog_destroy(dlg); - break; - } -} - -/************************************************************************//** - Create new spaceship dialog -****************************************************************************/ -struct spaceship_dialog *create_spaceship_dialog(struct player *pplayer) -{ - struct spaceship_dialog *pdialog; - GtkWidget *hbox, *frame; - int w, h; - - pdialog = fc_malloc(sizeof(struct spaceship_dialog)); - pdialog->pplayer = pplayer; - - gui_dialog_new(&pdialog->shell, GTK_NOTEBOOK(top_notebook), NULL, TRUE); - gui_dialog_set_title(pdialog->shell, player_name(pplayer)); - - gui_dialog_add_button(pdialog->shell, - GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - gui_dialog_add_button(pdialog->shell, - _("_Launch"), GTK_RESPONSE_ACCEPT); - - g_signal_connect(pdialog->shell->vbox, "destroy", - G_CALLBACK(spaceship_destroy_callback), pdialog); - gui_dialog_response_set_callback(pdialog->shell, spaceship_response); - - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 5); - gtk_container_add(GTK_CONTAINER(pdialog->shell->vbox), hbox); - - frame = gtk_frame_new(NULL); - gtk_container_add(GTK_CONTAINER(hbox), frame); - - pdialog->image_canvas = gtk_drawing_area_new(); - gtk_widget_set_can_focus(pdialog->image_canvas, TRUE); - get_spaceship_dimensions(&w, &h); - gtk_widget_set_size_request(pdialog->image_canvas, w, h); - - gtk_widget_set_events(pdialog->image_canvas, GDK_EXPOSURE_MASK); - gtk_container_add(GTK_CONTAINER(frame), pdialog->image_canvas); - gtk_widget_realize(pdialog->image_canvas); - - g_signal_connect(pdialog->image_canvas, "draw", - G_CALLBACK(spaceship_image_canvas_expose), pdialog); - - pdialog->info_label = gtk_label_new(get_spaceship_descr(NULL)); - - gtk_label_set_justify(GTK_LABEL(pdialog->info_label), GTK_JUSTIFY_LEFT); - gtk_widget_set_halign(pdialog->info_label, GTK_ALIGN_START); - gtk_widget_set_valign(pdialog->info_label, GTK_ALIGN_START); - - gtk_container_add(GTK_CONTAINER(hbox), pdialog->info_label); - gtk_widget_set_name(pdialog->info_label, "spaceship_label"); - - dialog_list_prepend(dialog_list, pdialog); - - gtk_widget_grab_focus(pdialog->image_canvas); - - gui_dialog_show_all(pdialog->shell); - - refresh_spaceship_dialog(pdialog->pplayer); - - return pdialog; -} - -/************************************************************************//** - Update spaceship dialog info label text -****************************************************************************/ -void spaceship_dialog_update_info(struct spaceship_dialog *pdialog) -{ - gtk_label_set_text(GTK_LABEL(pdialog->info_label), - get_spaceship_descr(&pdialog->pplayer->spaceship)); -} - -/************************************************************************//** - Should also check connectedness, and show non-connected - parts differently. -****************************************************************************/ -void spaceship_dialog_update_image(struct spaceship_dialog *pdialog) -{ - gtk_widget_queue_draw(pdialog->image_canvas); -} diff --git a/client/gui-gtk-3.0/spaceshipdlg.h b/client/gui-gtk-3.0/spaceshipdlg.h deleted file mode 100644 index 781d5c264a..0000000000 --- a/client/gui-gtk-3.0/spaceshipdlg.h +++ /dev/null @@ -1,23 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__SPACESHIPDLG_H -#define FC__SPACESHIPDLG_H - -#include - -#include "spaceshipdlg_g.h" - -void spaceship_dialog_init(void); -void spaceship_dialog_done(void); - -#endif /* FC__SPACESHIPDLG_H */ diff --git a/client/gui-gtk-3.0/sprite.c b/client/gui-gtk-3.0/sprite.c deleted file mode 100644 index 9ac7bf8820..0000000000 --- a/client/gui-gtk-3.0/sprite.c +++ /dev/null @@ -1,512 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -/* utility */ -#include "log.h" -#include "mem.h" -#include "shared.h" - -/* client/gui-gtk-3.0 */ -#include "colors.h" -#include "mapview.h" - -#include "sprite.h" - -#define MAX_FILE_EXTENSIONS 50 - -/************************************************************************//** - Create a new sprite by cropping and taking only the given portion of - the image. - - source gives the sprite that is to be cropped. - - x,y, width, height gives the rectangle to be cropped. The pixel at - position of the source sprite will be at (0,0) in the new sprite, and - the new sprite will have dimensions (width, height). - - mask gives an additional mask to be used for clipping the new sprite. - - mask_offset_x, mask_offset_y is the offset of the mask relative to the - origin of the source image. The pixel at (mask_offset_x,mask_offset_y) - in the mask image will be used to clip pixel (0,0) in the source image - which is pixel (-x,-y) in the new image. - - scale gives scale of new tileset - smooth means if scaling might be bilinear, if set to false use nearest - neighbor -****************************************************************************/ -struct sprite *crop_sprite(struct sprite *source, - int x, int y, - int width, int height, - struct sprite *mask, int mask_offset_x, int mask_offset_y, - float scale, bool smooth) -{ - struct sprite *new = fc_malloc(sizeof(*new)); - cairo_t *cr; - - fc_assert_ret_val(source, NULL); - - new->surface = cairo_surface_create_similar(source->surface, - CAIRO_CONTENT_COLOR_ALPHA, width, height); - cr = cairo_create(new->surface); - cairo_rectangle(cr, 0, 0, width, height); - cairo_clip(cr); - - cairo_set_source_surface(cr, source->surface, -x, -y); - cairo_paint(cr); - if (mask) { - cairo_set_operator(cr, CAIRO_OPERATOR_DEST_IN); - cairo_set_source_surface(cr, mask->surface, mask_offset_x-x, mask_offset_y-y); - cairo_paint(cr); - } - cairo_destroy(cr); - - return new; -} - -/************************************************************************//** - Create a sprite with the given height, width and color. -****************************************************************************/ -struct sprite *create_sprite(int width, int height, struct color *pcolor) -{ - struct sprite *sprite = fc_malloc(sizeof(*sprite)); - cairo_t *cr; - - fc_assert_ret_val(width > 0, NULL); - fc_assert_ret_val(height > 0, NULL); - fc_assert_ret_val(pcolor != NULL, NULL); - - sprite->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - width, height); - - cr = cairo_create(sprite->surface); - gdk_cairo_set_source_rgba(cr, &pcolor->color); - cairo_paint(cr); - cairo_destroy(cr); - - return sprite; -} - -/************************************************************************//** - Find the dimensions of the sprite. -****************************************************************************/ -void get_sprite_dimensions(struct sprite *sprite, int *width, int *height) -{ - *width = cairo_image_surface_get_width(sprite->surface); - *height = cairo_image_surface_get_height(sprite->surface); -} - -/************************************************************************//** - Returns the filename extensions the client supports - Order is important. -****************************************************************************/ -const char **gfx_fileextensions(void) -{ - /* Includes space for hardcoded 'png' and termination NULL */ - static const char *ext[MAX_FILE_EXTENSIONS + 2] = - { - NULL - }; - - if (ext[0] == NULL) { - int count = 0; - GSList *formats = gdk_pixbuf_get_formats(); - GSList *next = formats; - - while ((next = g_slist_next(next)) != NULL && count < MAX_FILE_EXTENSIONS) { - GdkPixbufFormat *format = g_slist_nth_data(next, 0); - gchar **mimes = gdk_pixbuf_format_get_mime_types(format); - int i; - - /* Consider .png to be supported even when there's no mime-type called "png" */ - ext[count++] = fc_strdup("png"); - - for (i = 0; mimes[i] != NULL && count < MAX_FILE_EXTENSIONS; i++) { - char *end = strstr(mimes[i], "/"); - - if (end != NULL) { - ext[count++] = fc_strdup(end + 1); - } - } - - g_strfreev(mimes); - } - - g_slist_free(formats); - - ext[count] = NULL; - } - - return ext; -} - -/************************************************************************//** - Called when the cairo surface with freeciv allocated data is destroyed. -****************************************************************************/ -static void surf_destroy_callback(void *data) -{ - free(data); -} - -/************************************************************************//** - Load the given graphics file into a sprite. This function loads an - entire image file, which may later be broken up into individual sprites - with crop_sprite. -****************************************************************************/ -struct sprite *load_gfxfile(const char *filename) -{ - struct sprite *spr; - GError *err = NULL;; - GdkPixbuf *pb = gdk_pixbuf_new_from_file(filename, &err); - int width; - int height; - unsigned char *pbdata; - int rs; - unsigned char *cairo_data; - unsigned char *data; - int i, j; - int cairo_stride; - bool has_alpha; - int channels; - - if (pb == NULL) { - log_error(_("Can't load %s: %s"), filename, err->message); - return NULL; - } - - spr = fc_malloc(sizeof(*spr)); - width = gdk_pixbuf_get_width(pb); - height = gdk_pixbuf_get_height(pb); - pbdata = gdk_pixbuf_get_pixels(pb); - rs = gdk_pixbuf_get_rowstride(pb); - has_alpha = gdk_pixbuf_get_has_alpha(pb); - channels = gdk_pixbuf_get_n_channels(pb); - - cairo_stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - if (cairo_stride <= 0) { - log_error("Cairo does not give stride for width %d", width); - free(spr); - return NULL; - } - - cairo_data = fc_malloc(height * cairo_stride * 4); - data = cairo_data; - - for (i = 0; i < height; i++) { - for (j = 0; j < width; j++) { - if (has_alpha) { - unsigned char tmp; - -#define MULTI_UNc(a,b) ((a * b - (b / 2)) / 0xFF) - - if (is_bigendian()) { - tmp = pbdata[j * channels + 3]; - data[j * 4 + 3] = MULTI_UNc(pbdata[j * channels + 2], tmp); - data[j * 4 + 2] = MULTI_UNc(pbdata[j * channels + 1], tmp); - data[j * 4 + 1] = MULTI_UNc(pbdata[j * channels + 0], tmp); - data[j * 4 + 0] = tmp; - } else { - tmp = MULTI_UNc(pbdata[j * channels + 2], pbdata[j * channels + 3]); - data[j * 4 + 1] = MULTI_UNc(pbdata[j * channels + 1], pbdata[j * channels + 3]); - data[j * 4 + 2] = MULTI_UNc(pbdata[j * channels + 0], pbdata[j * channels + 3]); - data[j * 4 + 0] = tmp; - data[j * 4 + 3] = pbdata[j * channels + 3]; - } - -#undef MULTI_UNc - - } else { - data[j * 4 + 3] = 255; - data[j * 4 + 0] = pbdata[j * channels + 2]; - data[j * 4 + 1] = pbdata[j * channels + 1]; - data[j * 4 + 2] = pbdata[j * channels + 0]; - } - } - - data += cairo_stride; - pbdata += rs; - } - - g_object_unref(pb); - - spr->surface = cairo_image_surface_create_for_data(cairo_data, CAIRO_FORMAT_ARGB32, - width, height, cairo_stride); - if (spr->surface == NULL || cairo_surface_status(spr->surface) != CAIRO_STATUS_SUCCESS) { - log_error("Cairo image surface creation error"); - free(spr); - free(cairo_data); - - return NULL; - } - - cairo_surface_set_user_data(spr->surface, NULL, cairo_data, surf_destroy_callback); - - fc_assert(cairo_image_surface_get_format(spr->surface) == CAIRO_FORMAT_ARGB32); - - if (cairo_surface_status(spr->surface) != CAIRO_STATUS_SUCCESS) { - log_fatal("Failed reading graphics file: \"%s\"", filename); - - exit(EXIT_FAILURE); - } - - return spr; -} - -/************************************************************************//** - Free a sprite and all associated image data. -****************************************************************************/ -void free_sprite(struct sprite * s) -{ - cairo_surface_destroy(s->surface); - free(s); -} - -/************************************************************************//** - Scales a sprite. If the sprite contains a mask, the mask is scaled - as as well. -****************************************************************************/ -struct sprite *sprite_scale(struct sprite *src, int new_w, int new_h) -{ - cairo_t *cr; - struct sprite *new = fc_malloc(sizeof(*new)); - int width, height; - - get_sprite_dimensions(src, &width, &height); - - new->surface = cairo_surface_create_similar(src->surface, - CAIRO_CONTENT_COLOR_ALPHA, new_w, new_h); - - cr = cairo_create(new->surface); - cairo_save(cr); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_paint(cr); - cairo_restore(cr); - cairo_scale(cr, (double) new_w / (double) width, (double) new_h / (double) height); - cairo_set_source_surface(cr, src->surface, 0, 0); - cairo_paint(cr); - - cairo_destroy(cr); - - return new; -} - -/************************************************************************//** - Method returns the bounding box of a sprite. It assumes a rectangular - object/mask. The bounding box contains the border (pixel which have - unset pixel as neighbours) pixel. -****************************************************************************/ -void sprite_get_bounding_box(struct sprite * sprite, int *start_x, - int *start_y, int *end_x, int *end_y) -{ - unsigned char *data = cairo_image_surface_get_data(sprite->surface); - int width = cairo_image_surface_get_width(sprite->surface); - int height = cairo_image_surface_get_height(sprite->surface); - int i, j; - int endian; - - if (is_bigendian()) { - endian = 0; - } else { - endian = 3; - } - - fc_assert(cairo_image_surface_get_format(sprite->surface) == CAIRO_FORMAT_ARGB32); - - /* parses mask image for the first column that contains a visible pixel */ - *start_x = -1; - for (i = 0; i < width && *start_x == -1; i++) { - for (j = 0; j < height; j++) { - if (data[(j * width + i) * 4 + endian]) { - *start_x = i; - break; - } - } - } - - /* parses mask image for the last column that contains a visible pixel */ - *end_x = -1; - for (i = width - 1; i >= *start_x && *end_x == -1; i--) { - for (j = 0; j < height; j++) { - if (data[(j * width + i) * 4 + endian]) { - *end_x = i; - break; - } - } - } - - /* parses mask image for the first row that contains a visible pixel */ - *start_y = -1; - for (i = 0; i < height && *start_y == -1; i++) { - for (j = *start_x; j <= *end_x; j++) { - if (data[(i * width + j) * 4 + endian]) { - *start_y = i; - break; - } - } - } - - /* parses mask image for the last row that contains a visible pixel */ - *end_y = -1; - for (i = height - 1; i >= *end_y && *end_y == -1; i--) { - for (j = *start_x; j <= *end_x; j++) { - if (data[(i * width + j) * 4 + endian]) { - *end_y = i; - break; - } - } - } -} - -/************************************************************************//** - Crops all blankspace from a sprite (insofar as is possible as a rectangle) -****************************************************************************/ -struct sprite *crop_blankspace(struct sprite *s) -{ - int x1, y1, x2, y2; - - sprite_get_bounding_box(s, &x1, &y1, &x2, &y2); - - return crop_sprite(s, x1, y1, x2 - x1 + 1, y2 - y1 + 1, NULL, -1, -1, - 1.0, FALSE); -} - -/************************************************************************//** - Render a pixbuf from the sprite. - - NOTE: the pixmap and mask of a sprite must not change after this - function is called! -****************************************************************************/ -GdkPixbuf *sprite_get_pixbuf(struct sprite *sprite) -{ - int width, height; - - if (!sprite) { - return NULL; - } - - get_sprite_dimensions(sprite, &width, &height); - - return surface_get_pixbuf(sprite->surface, width, height); -} - -/************************************************************************//** - Render a pixbuf from the cairo surface -****************************************************************************/ -GdkPixbuf *surface_get_pixbuf(cairo_surface_t *surf, int width, int height) -{ - cairo_t *cr; - cairo_surface_t *tmpsurf; - GdkPixbuf *pb; - unsigned char *pixels; - int rowstride; - int i; - - pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); - pixels = gdk_pixbuf_get_pixels(pb); - rowstride = gdk_pixbuf_get_rowstride(pb); - - tmpsurf = cairo_image_surface_create_for_data(pixels, CAIRO_FORMAT_ARGB32, - width, height, rowstride); - - cr = cairo_create(tmpsurf); - cairo_save(cr); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_paint(cr); - cairo_restore(cr); - cairo_set_source_surface(cr, surf, 0, 0); - cairo_paint(cr); - cairo_destroy(cr); - - for (i = height; i > 0; i--) { - unsigned char *p = pixels; - unsigned char *end = p + 4 * width; - unsigned char tmp; - -#define DIV_UNc(a,b) (((guint16) (a) * 0xFF + ((b) / 2)) / (b)) - - while (p < end) { - tmp = p[0]; - - if (is_bigendian()) { - if (tmp != 0) { - p[0] = DIV_UNc(p[1], tmp); - p[1] = DIV_UNc(p[2], tmp); - p[2] = DIV_UNc(p[3], tmp); - p[3] = tmp; - } else { - p[1] = p[2] = p[3] = 0; - } - } else { - if (p[3] != 0) { - p[0] = DIV_UNc(p[2], p[3]); - p[1] = DIV_UNc(p[1], p[3]); - p[2] = DIV_UNc(tmp, p[3]); - } else { - p[0] = p[1] = p[2] = 0; - } - } - - p += 4; - } - -#undef DIV_UNc - - pixels += rowstride; - } - - cairo_surface_destroy(tmpsurf); - - return pb; -} - -/************************************************************************//** - Create a pixbuf containing a representative image for the given extra - type, to be used as an icon in the GUI. - - May return NULL on error. - - NB: You must call g_object_unref on the non-NULL return value when you - no longer need it. -****************************************************************************/ -GdkPixbuf *create_extra_pixbuf(const struct extra_type *pextra) -{ - struct drawn_sprite sprs[80]; - int count, w, h, canvas_x, canvas_y; - GdkPixbuf *pixbuf; - struct canvas canvas = FC_STATIC_CANVAS_INIT; - cairo_t *cr; - - w = tileset_tile_width(tileset); - h = tileset_tile_height(tileset); - - canvas.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); - canvas_x = 0; - canvas_y = 0; - - cr = cairo_create(canvas.surface); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_paint(cr); - cairo_destroy(cr); - - count = fill_basic_extra_sprite_array(tileset, sprs, pextra); - put_drawn_sprites(&canvas, 1.0, canvas_x, canvas_y, count, sprs, FALSE); - - pixbuf = surface_get_pixbuf(canvas.surface, w, h); - cairo_surface_destroy(canvas.surface); - - return pixbuf; -} diff --git a/client/gui-gtk-3.0/sprite.h b/client/gui-gtk-3.0/sprite.h deleted file mode 100644 index 54203800ee..0000000000 --- a/client/gui-gtk-3.0/sprite.h +++ /dev/null @@ -1,39 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__SPRITE_H -#define FC__SPRITE_H - -#include - -/* client */ -#include "sprite_g.h" - -struct sprite -{ - cairo_surface_t *surface; -}; - -struct sprite *sprite_scale(struct sprite *src, int new_w, int new_h); -void sprite_get_bounding_box(struct sprite *sprite, int *start_x, - int *start_y, int *end_x, int *end_y); -struct sprite *crop_blankspace(struct sprite *s); - -/******************************************************************** - Note: a sprite cannot be changed after these functions are called! -********************************************************************/ -GdkPixbuf *sprite_get_pixbuf(struct sprite *sprite); -GdkPixbuf *surface_get_pixbuf(cairo_surface_t *surf, int width, int height); - -GdkPixbuf *create_extra_pixbuf(const struct extra_type *pextra); - -#endif /* FC__SPRITE_H */ diff --git a/client/gui-gtk-3.0/theme_dlg.c b/client/gui-gtk-3.0/theme_dlg.c deleted file mode 100644 index 441601ab64..0000000000 --- a/client/gui-gtk-3.0/theme_dlg.c +++ /dev/null @@ -1,88 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" - -/* client */ -#include "dialogs_g.h" -#include "options.h" - -/* gui-gtk-3.0 */ -#include "gui_main.h" - -static bool load_theme = FALSE; - -static void theme_suggestion_callback(GtkWidget *dlg, gint arg); - -/************************************************************************//** - Callback deciding if the theme may be loaded or not -****************************************************************************/ -static void theme_suggestion_callback(GtkWidget *dlg, gint arg) -{ - load_theme = (arg == GTK_RESPONSE_YES); -} - -/************************************************************************//** - Popup dialog asking if tileset suggested theme should be - used. -****************************************************************************/ -bool popup_theme_suggestion_dialog(const char *theme_name) -{ - GtkWidget *dialog, *label; - char buf[1024]; - char *current_name = GUI_GTK_OPTION(default_theme_name); - - if (current_name == NULL) { - /* gui option default_theme_name is not yet set. - * This can happen when we load tileset requested at command line and - * user has not saved theme information to .freeciv-client-rc.A.B. */ - current_name = FC_GTK3_DEFAULT_THEME_NAME; - } - - dialog = gtk_dialog_new_with_buttons(_("Theme suggested"), - NULL, - 0, - _("Load theme"), - GTK_RESPONSE_YES, - _("Keep current theme"), - GTK_RESPONSE_NO, - NULL); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES); - gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); - - fc_snprintf(buf, sizeof(buf), - _("Tileset suggests using %s theme.\n" - "You are currently using %s."), - theme_name, current_name); - - label = gtk_label_new(buf); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label); - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); - gtk_widget_show(label); - - g_signal_connect(dialog, "response", - G_CALLBACK(theme_suggestion_callback), NULL); - - gtk_dialog_run(GTK_DIALOG(dialog)); - - gtk_widget_destroy(dialog); - - return load_theme; -} diff --git a/client/gui-gtk-3.0/themes.c b/client/gui-gtk-3.0/themes.c deleted file mode 100644 index 57d50ddfdc..0000000000 --- a/client/gui-gtk-3.0/themes.c +++ /dev/null @@ -1,203 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 2005 The Freeciv Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef FREECIV_HAVE_DIRENT_H -#include -#endif - -#include -#include -#include - -#include - -/* utility */ -#include "fc_dirent.h" -#include "mem.h" -#include "string_vector.h" - -/* client */ -#include "themes_common.h" - -/* gui-gtk-3.0 */ -#include "gui_main.h" - -#include "themes_g.h" - -/*************************************************************************//** - Loads a gtk theme directory/theme_name -*****************************************************************************/ -void gui_load_theme(const char *directory, const char *theme_name) -{ - static GtkCssProvider *fc_css_provider = NULL; - GError *error = NULL; - char buf[strlen(directory) + strlen(theme_name) + 32]; - - if (fc_css_provider == NULL) { - fc_css_provider = gtk_css_provider_new(); - gtk_style_context_add_provider(gtk_widget_get_style_context(toplevel), - GTK_STYLE_PROVIDER(fc_css_provider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - } - - /* Gtk theme is a directory containing gtk-3.0/gtk.css file */ - fc_snprintf(buf, sizeof(buf), "%s/%s/gtk-3.0/gtk.css", directory, - theme_name); - - gtk_css_provider_load_from_file(fc_css_provider, g_file_new_for_path(buf), &error); - - if (error) { - g_warning("%s\n", error->message); - } - - gtk_style_context_invalidate(gtk_widget_get_style_context(toplevel)); -} - -/*************************************************************************//** - Clears a theme (sets default system theme) -*****************************************************************************/ -void gui_clear_theme(void) -{ - bool theme_loaded; - - /* try to load user defined theme */ - theme_loaded = load_theme(GUI_GTK_OPTION(default_theme_name)); - - /* no user defined theme loaded -> try to load Freeciv default theme */ - if (!theme_loaded) { - theme_loaded = load_theme(GUI_GTK_DEFAULT_THEME_NAME); - if (theme_loaded) { - sz_strlcpy(GUI_GTK_OPTION(default_theme_name), GUI_GTK_DEFAULT_THEME_NAME); - } - } - - /* still no theme loaded -> load system default theme */ - if (!theme_loaded) { - static GtkCssProvider *default_provider = NULL; - - if (default_provider == NULL) { - default_provider = gtk_css_provider_new(); - } - gtk_style_context_add_provider_for_screen( - gtk_widget_get_screen(toplevel), - GTK_STYLE_PROVIDER(default_provider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - } -} - -/*************************************************************************//** - Each gui has its own themes directories. - For gtk3 these are: - - /usr/share/themes - - ~/.themes - Returns an array containing these strings and sets array size in count. - The caller is responsible for freeing the array and the paths. -*****************************************************************************/ -char **get_gui_specific_themes_directories(int *count) -{ - gchar *standard_dir; - char *home_dir; - const struct strvec *data_dirs = get_data_dirs(); - char **directories = fc_malloc((2 + strvec_size(data_dirs)) - * sizeof(char *)); - - *count = 0; - - /* Freeciv-specific GTK3 themes directories */ - strvec_iterate(data_dirs, dir_name) { - char buf[strlen(dir_name) + strlen("/themes/gtk3") + 1]; - - fc_snprintf(buf, sizeof(buf), "%s/themes/gtk3", dir_name); - - directories[(*count)++] = fc_strdup(buf); - } strvec_iterate_end; - - /* standard GTK+ themes directory */ -#ifdef CROSSER - standard_dir = "../share/themes"; -#else /* CROSSER */ - standard_dir = "/usr/share/themes"; -#endif /* CROSSER */ - directories[(*count)++] = fc_strdup(standard_dir); - - /* user GTK+ themes directory (~/.themes) */ - home_dir = user_home_dir(); - if (home_dir) { - char buf[strlen(home_dir) + 16]; - - fc_snprintf(buf, sizeof(buf), "%s/.themes/", home_dir); - directories[(*count)++] = fc_strdup(buf); - } - - return directories; -} - -/*************************************************************************//** - Return an array of names of usable themes in the given directory. - Array size is stored in count. - Useable theme for gtk+ is a directory which contains file gtk-3.0/gtk.css. - The caller is responsible for freeing the array and the names -*****************************************************************************/ -char **get_useable_themes_in_directory(const char *directory, int *count) -{ - DIR *dir; - struct dirent *entry; - char **theme_names = fc_malloc(sizeof(char *) * 2); - /* Allocated memory size */ - int t_size = 2; - - - *count = 0; - - dir = fc_opendir(directory); - if (!dir) { - /* This isn't directory or we can't list it */ - return theme_names; - } - - while ((entry = readdir(dir))) { - char buf[strlen(directory) + strlen(entry->d_name) + 32]; - struct stat stat_result; - - fc_snprintf(buf, sizeof(buf), - "%s/%s/gtk-3.0/gtk.css", directory, entry->d_name); - - if (fc_stat(buf, &stat_result) != 0) { - /* File doesn't exist */ - continue; - } - - if (!S_ISREG(stat_result.st_mode)) { - /* Not a regular file */ - continue; - } - - /* Otherwise it's ok */ - - /* Increase array size if needed */ - if (*count == t_size) { - theme_names = fc_realloc(theme_names, t_size * 2 * sizeof(char *)); - t_size *= 2; - } - - theme_names[*count] = fc_strdup(entry->d_name); - (*count)++; - } - - closedir(dir); - - return theme_names; -} diff --git a/client/gui-gtk-3.0/tileset_dlg.c b/client/gui-gtk-3.0/tileset_dlg.c deleted file mode 100644 index 8a5a42135d..0000000000 --- a/client/gui-gtk-3.0/tileset_dlg.c +++ /dev/null @@ -1,94 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" - -/* common */ -#include "game.h" -#include "unitlist.h" - -/* client */ -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "gui_main.h" -#include "gui_stuff.h" - -#include "dialogs_g.h" -extern char forced_tileset_name[512]; -static void tileset_suggestion_callback(GtkWidget *dlg, gint arg); - -/************************************************************************//** - Callback either loading suggested tileset or doing nothing -****************************************************************************/ -static void tileset_suggestion_callback(GtkWidget *dlg, gint arg) -{ - if (arg == GTK_RESPONSE_YES) { - /* User accepted tileset loading */ - sz_strlcpy(forced_tileset_name, game.control.preferred_tileset); - if (!tilespec_reread(game.control.preferred_tileset, TRUE, 1.0)) { - tileset_error(LOG_ERROR, _("Can't load requested tileset %s."), - game.control.preferred_tileset); - } - } -} - -/************************************************************************//** - Popup dialog asking if ruleset suggested tileset should be - used. -****************************************************************************/ -void popup_tileset_suggestion_dialog(void) -{ - GtkWidget *dialog, *label; - char buf[1024]; - - dialog = gtk_dialog_new_with_buttons(_("Preferred tileset"), - NULL, - 0, - _("_Load tileset"), - GTK_RESPONSE_YES, - _("_Keep current tileset"), - GTK_RESPONSE_NO, - NULL); - setup_dialog(dialog, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES); - gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); - - fc_snprintf(buf, sizeof(buf), - _("Modpack suggests using %s tileset.\n" - "It might not work with other tilesets.\n" - "You are currently using tileset %s."), - game.control.preferred_tileset, tileset_basename(tileset)); - - label = gtk_label_new(buf); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label); - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); - gtk_widget_show(label); - - g_signal_connect(dialog, "response", - G_CALLBACK(tileset_suggestion_callback), NULL); - - /* In case incoming rulesets are incompatible with current tileset - * we need to block their receive before user has accepted loading - * of the correct tileset. */ - gtk_dialog_run(GTK_DIALOG(dialog)); - - gtk_widget_destroy(dialog); -} diff --git a/client/gui-gtk-3.0/transportdlg.c b/client/gui-gtk-3.0/transportdlg.c deleted file mode 100644 index f64247e790..0000000000 --- a/client/gui-gtk-3.0/transportdlg.c +++ /dev/null @@ -1,118 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" - -/* common */ -#include "game.h" -#include "movement.h" -#include "unit.h" - -/* client */ -#include "control.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "gui_main.h" -#include "gui_stuff.h" -#include "sprite.h" -#include "unitselunitdlg.h" - -#include "transportdlg.h" - -struct transport_radio_cb_data { - GtkWidget *dlg; - int tp_id; -}; - -/************************************************************************//** - Handle user response to transport dialog. -****************************************************************************/ -static void transport_response_callback(GtkWidget *dlg, gint arg) -{ - if (arg == GTK_RESPONSE_YES) { - struct unit *pcargo = - game_unit_by_number(GPOINTER_TO_INT( - g_object_get_data(G_OBJECT(dlg), - "actor"))); - - if (pcargo != NULL) { - int tp_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg), - "target")); - struct tile *ptile = g_object_get_data(G_OBJECT(dlg), "tile"); - - if (tp_id == 0) { - /* Load to any */ - request_unit_load(pcargo, NULL, ptile); - } else { - struct unit *ptransport = game_unit_by_number(tp_id); - - if (ptransport != NULL) { - /* Still exist */ - request_unit_load(pcargo, ptransport, ptile); - } - } - } - } - - gtk_widget_destroy(dlg); -} - -/************************************************************************//** - Handle transport request automatically when there's nothing to - choose from. Otherwise open up transport dialog for the unit -****************************************************************************/ -bool request_transport(struct unit *cargo, struct tile *ptile) -{ - int tcount; - struct unit_list *potential_transports = unit_list_new(); - struct unit *best_transport = transporter_for_unit_at(cargo, ptile); - - unit_list_iterate(ptile->units, ptransport) { - if (can_unit_transport(ptransport, cargo) - && get_transporter_occupancy(ptransport) < get_transporter_capacity(ptransport)) { - unit_list_append(potential_transports, ptransport); - } - } unit_list_iterate_end; - - tcount = unit_list_size(potential_transports); - - if (tcount == 0) { - fc_assert(best_transport == NULL); - unit_list_destroy(potential_transports); - - return FALSE; /* Unit was not handled here. */ - } else if (tcount == 1) { - /* There's exactly one potential transport - use it automatically */ - fc_assert(unit_list_get(potential_transports, 0) == best_transport); - request_unit_load(cargo, unit_list_get(potential_transports, 0), ptile); - - unit_list_destroy(potential_transports); - - return TRUE; - } - - return select_tgt_unit(cargo, ptile, potential_transports, best_transport, - _("Transport selection"), - _("Looking for transport:"), - _("Transports available:"), - _("Load"), - G_CALLBACK(transport_response_callback)); -} diff --git a/client/gui-gtk-3.0/transportdlg.h b/client/gui-gtk-3.0/transportdlg.h deleted file mode 100644 index 2625585525..0000000000 --- a/client/gui-gtk-3.0/transportdlg.h +++ /dev/null @@ -1,18 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__TRANSPORTDLG_H -#define FC__TRANSPORTDLG_H - -#include "dialogs_g.h" - -#endif /* FC__TRANSPORTDLG_H */ diff --git a/client/gui-gtk-3.0/unitselect.c b/client/gui-gtk-3.0/unitselect.c deleted file mode 100644 index e9d0faccc2..0000000000 --- a/client/gui-gtk-3.0/unitselect.c +++ /dev/null @@ -1,1294 +0,0 @@ -/***************************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -*****************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" - -/* common */ -#include "fc_types.h" -#include "game.h" -#include "player.h" -#include "unit.h" -#include "unitlist.h" -#include "unittype.h" - -/* client */ -#include "client_main.h" -#include "control.h" -#include "goto.h" -#include "tilespec.h" -#include "unitselect_common.h" - -/* client/gui-gtk-3.0 */ -#include "graphics.h" -#include "gui_stuff.h" -#include "gui_main.h" - -#include "unitselect.h" - -/* Activate this to get more columns (see below) */ -#undef DEBUG_USDLG - -enum usdlg_column_types { - COL_PIXBUF, - COL_TEXT, - COL_INT -}; - -enum usdlg_row_types { - ROW_UNITTYPE, - ROW_ACTIVITY, - ROW_UNIT, - ROW_UNIT_TRANSPORTED -}; - -/* Basic data (Unit, description, count) */ -#define USDLG_COLUMNS_DEFAULT 3 -/* Additional data; shown if DEBUG_USDLG */ -#define USDLG_COL_UTID USDLG_COLUMNS_DEFAULT + 0 /* Unit type ID */ -#define USDLG_COL_UID USDLG_COLUMNS_DEFAULT + 1 /* Unit ID */ -#define USDLG_COL_LOCATION USDLG_COLUMNS_DEFAULT + 2 /* Unit location */ -#define USDLG_COL_ACTIVITY USDLG_COLUMNS_DEFAULT + 3 /* Unit activity */ -#define USDLG_COL_ROW_TYPE USDLG_COLUMNS_DEFAULT + 4 /* Row type */ -#define USDLG_COLUMNS_DEBUG USDLG_COLUMNS_DEFAULT + 5 -/* Layout options; never shown */ -#define USDLG_COL_STYLE USDLG_COLUMNS_DEBUG + 0 -#define USDLG_COL_WEIGHT USDLG_COLUMNS_DEBUG + 1 -#define USDLG_COLUMNS_ALL USDLG_COLUMNS_DEBUG + 2 - -#ifdef DEBUG_USDLG - #define USDLG_COLUMNS_SHOW USDLG_COLUMNS_DEBUG -#else - #define USDLG_COLUMNS_SHOW USDLG_COLUMNS_DEFAULT -#endif /* DEBUG_USDLG */ - -enum usdlg_column_types usdlg_col_types[USDLG_COLUMNS_ALL] = { - COL_PIXBUF, /* Unit */ - COL_TEXT, /* Description */ - COL_INT, /* Count */ - COL_INT, /* Debug: unit type */ - COL_INT, /* Debug: unit ID */ - COL_INT, /* Debug: location */ - COL_INT, /* Debug: activity */ - COL_INT, /* Debug: row type */ - COL_INT, /* Layout: style */ - COL_INT /* Layout: width */ -}; - -static const char *usdlg_col_titles[USDLG_COLUMNS_ALL] = { - N_("Unit"), - N_("Description"), - N_("Count"), - "[Unittype]", /* Only for debug, no translation! */ - "[Unit ID]", - "[Location]", - "[Activity]", - "[Row type]", - "[Style]", - "[Width]" -}; - -enum usdlg_cmd { - USDLG_CMD_SELECT, - USDLG_CMD_DESELECT, - USDLG_CMD_READY, - USDLG_CMD_SENTRY, - USDLG_CMD_CENTER, - USDLG_CMD_FOCUS, - USDLG_CMD_LAST -}; - -struct unit_select_dialog { - struct tile *ptile; - int unit_id_focus; - - GtkWidget *shell; - GtkWidget *notebook; - - struct { - GtkTreeStore *store; - GtkWidget *view; - GtkTreePath *path; - } units; - - struct { - GtkTreeStore *store; - GtkWidget *page; - GtkWidget *view; - GtkTreePath *path; - - GtkWidget *cmd[USDLG_CMD_LAST]; - } tabs[SELLOC_COUNT]; -}; - -/* The unit selection dialog; should only be used in usdlg_get(). */ -static struct unit_select_dialog *unit_select_dlg = NULL; - -static struct unit_select_dialog *usdlg_get(bool create); -static struct unit_select_dialog *usdlg_create(void); -static void usdlg_destroy(void); -static void usdlg_destroy_callback(GObject *object, gpointer data); -static void usdlg_tile(struct unit_select_dialog *pdialog, - struct tile *ptile); -static void usdlg_refresh(struct unit_select_dialog *pdialog); - -static void usdlg_tab_select(struct unit_select_dialog *pdialog, - const char *title, - enum unit_select_location_mode loc); -static GtkTreeStore *usdlg_tab_store_new(void); -static bool usdlg_tab_update(struct unit_select_dialog *pdialog, - struct usdata_hash *ushash, - enum unit_select_location_mode loc); -static void usdlg_tab_append_utype(GtkTreeStore *store, - enum unit_select_location_mode loc, - const struct unit_type *putype, - GtkTreeIter *it); -static void usdlg_tab_append_activity(GtkTreeStore *store, - enum unit_select_location_mode loc, - const struct unit_type *putype, - enum unit_activity act, - int count, GtkTreeIter *it, - GtkTreeIter *parent); -static void usdlg_tab_append_units(struct unit_select_dialog *pdialog, - enum unit_select_location_mode loc, - enum unit_activity act, - const struct unit *punit, - bool transported, GtkTreeIter *it, - GtkTreeIter *parent); - -static void usdlg_cmd_ready(GObject *object, gpointer data); -static void usdlg_cmd_sentry(GObject *object, gpointer data); -static void usdlg_cmd_select(GObject *object, gpointer data); -static void usdlg_cmd_deselect(GObject *object, gpointer data); -static void usdlg_cmd_exec(GObject *object, gpointer mode_data, - enum usdlg_cmd cmd); -static void usdlg_cmd_exec_unit(struct unit *punit, enum usdlg_cmd cmd); -static void usdlg_cmd_center(GObject *object, gpointer data); -static void usdlg_cmd_focus(GObject *object, gpointer data); -static void usdlg_cmd_focus_real(GtkTreeView *view); -static void usdlg_cmd_row_activated(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data); -static void usdlg_cmd_cursor_changed(GtkTreeView *view, gpointer data); - - -/*************************************************************************//** - Popup the unit selection dialog. -*****************************************************************************/ -void unit_select_dialog_popup_main(struct tile *ptile, bool create) -{ - struct unit_select_dialog *pdialog; - - /* Create the dialog if it is requested. */ - pdialog = usdlg_get(create); - - /* Present the unit selection dialog if it exists. */ - if (pdialog) { - /* Show all. */ - gtk_widget_show_all(GTK_WIDGET(pdialog->shell)); - /* Update tile. */ - usdlg_tile(pdialog, ptile); - /* Refresh data and hide unused tabs. */ - usdlg_refresh(pdialog); - } -} - -/*************************************************************************//** - Popdown the unit selection dialog. -*****************************************************************************/ -void unit_select_dialog_popdown(void) -{ - usdlg_destroy(); -} - -/*************************************************************************//** - Get the current unit selection dialog. Create it if needed and 'create' is - TRUE. -*****************************************************************************/ -static struct unit_select_dialog *usdlg_get(bool create) -{ - if (unit_select_dlg) { - /* Return existing dialog. */ - return unit_select_dlg; - } else if (create) { - /* Create new dialog. */ - unit_select_dlg = usdlg_create(); - return unit_select_dlg; - } else { - /* Nothing. */ - return NULL; - } -} - -/*************************************************************************//** - Create a new unit selection dialog. -*****************************************************************************/ -static struct unit_select_dialog *usdlg_create(void) -{ - GtkWidget *vbox; - GtkWidget *close_cmd; - struct unit_select_dialog *pdialog; - - /* Create a container for the dialog. */ - pdialog = fc_calloc(1, sizeof(*pdialog)); - - /* No tile defined. */ - pdialog->ptile = NULL; - - /* Create the dialog. */ - pdialog->shell = gtk_dialog_new(); - gtk_window_set_title(GTK_WINDOW(pdialog->shell), _("Unit selection")); - setup_dialog(pdialog->shell, toplevel); - g_signal_connect(pdialog->shell, "destroy", - G_CALLBACK(usdlg_destroy_callback), pdialog); - gtk_window_set_position(GTK_WINDOW(pdialog->shell), GTK_WIN_POS_MOUSE); - gtk_widget_realize(pdialog->shell); - - vbox = gtk_dialog_get_content_area(GTK_DIALOG(pdialog->shell)); - - /* Notebook. */ - pdialog->notebook = gtk_notebook_new(); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(pdialog->notebook), - GTK_POS_BOTTOM); - gtk_box_pack_start(GTK_BOX(vbox), pdialog->notebook, TRUE, TRUE, 0); - - /* Append pages. */ - usdlg_tab_select(pdialog, _("_Units"), SELLOC_UNITS); - usdlg_tab_select(pdialog, _("_Tile"), SELLOC_TILE); - usdlg_tab_select(pdialog, _("C_ontinent"), SELLOC_CONT); - usdlg_tab_select(pdialog, _("_Land"), SELLOC_LAND); - usdlg_tab_select(pdialog, _("_Sea"), SELLOC_SEA); - usdlg_tab_select(pdialog, _("_Both"), SELLOC_BOTH); - usdlg_tab_select(pdialog, _("_World"), SELLOC_WORLD); - - /* Buttons. */ - close_cmd = gtk_dialog_add_button(GTK_DIALOG(pdialog->shell), - GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - gtk_dialog_set_default_response(GTK_DIALOG(pdialog->shell), - GTK_RESPONSE_CLOSE); - g_signal_connect(close_cmd, "clicked", - G_CALLBACK(usdlg_destroy_callback), pdialog); - - return pdialog; -} - -/*************************************************************************//** - Destroy a unit selection dialog. -*****************************************************************************/ -static void usdlg_destroy(void) -{ - if (unit_select_dlg) { - gtk_widget_destroy(GTK_WIDGET(unit_select_dlg->shell)); - free(unit_select_dlg); - } - unit_select_dlg = NULL; -} - -/*************************************************************************//** - Callback for the destruction of the dialog. -*****************************************************************************/ -static void usdlg_destroy_callback(GObject *object, gpointer data) -{ - usdlg_destroy(); -} - -/*************************************************************************//** - Set the reference tile. -*****************************************************************************/ -static void usdlg_tile(struct unit_select_dialog *pdialog, - struct tile *ptile) -{ - if (!pdialog) { - return; - } - - /* Check for a valid tile. */ - if (ptile != NULL) { - pdialog->ptile = ptile; - } else if (pdialog->ptile == NULL) { - struct unit *punit = head_of_units_in_focus(); - - if (punit) { - pdialog->ptile = unit_tile(punit); - center_tile_mapcanvas(pdialog->ptile); - } else { - pdialog->ptile = get_center_tile_mapcanvas(); - } - } -} - -/*************************************************************************//** - Refresh the dialog. -*****************************************************************************/ -static void usdlg_refresh(struct unit_select_dialog *pdialog) -{ - struct usdata_hash *ushash = NULL; - enum unit_select_location_mode loc; - - if (!pdialog) { - return; - } - - /* Sort units into the hash. */ - ushash = usdlg_data_new(pdialog->ptile); - /* Update all tabs. */ - for (loc = unit_select_location_mode_begin(); - loc != unit_select_location_mode_end(); - loc = unit_select_location_mode_next(loc)) { - bool show = usdlg_tab_update(pdialog, ushash, loc); - - if (!show) { - gtk_widget_hide(pdialog->tabs[loc].page); - } else { - gtk_widget_show(pdialog->tabs[loc].page); - - if (pdialog->tabs[loc].path) { - gtk_tree_view_expand_row(GTK_TREE_VIEW(pdialog->tabs[loc].view), - pdialog->tabs[loc].path,FALSE); - gtk_tree_view_set_cursor(GTK_TREE_VIEW(pdialog->tabs[loc].view), - pdialog->tabs[loc].path, NULL, FALSE); - gtk_tree_path_free(pdialog->tabs[loc].path); - pdialog->tabs[loc].path = NULL; - } - } - } - /* Destroy the hash. */ - usdlg_data_destroy(ushash); -} - -/*************************************************************************//** - +--------------------------------+ - | +-----------------+----------+ | - | | (unit list) | select | | - | | | deselect | | - | | | | | - | | | center | | - | | | focus | | - | +-----------------+----------+ | - | | tabs | ... | | - | close | - +--------------------------------+ -*****************************************************************************/ -static void usdlg_tab_select(struct unit_select_dialog *pdialog, - const char *title, - enum unit_select_location_mode loc) -{ - GtkWidget *page, *label, *hbox, *vbox, *view, *sw; - GtkTreeStore *store; - static bool titles_done; - int i; - - page = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(page), - GTK_ORIENTATION_VERTICAL); - gtk_container_set_border_width(GTK_CONTAINER(page), 8); - pdialog->tabs[loc].page = page; - - label = gtk_label_new_with_mnemonic(title); - gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label); - - hbox = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(page), hbox); - - store = usdlg_tab_store_new(); - pdialog->tabs[loc].store = store; - - view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); - gtk_widget_set_hexpand(view, TRUE); - gtk_widget_set_vexpand(view, TRUE); - pdialog->tabs[loc].view = view; - g_object_unref(store); - - g_signal_connect(view, "row-activated", G_CALLBACK(usdlg_cmd_row_activated), - (gpointer *)loc); - g_signal_connect(view, "cursor-changed", - G_CALLBACK(usdlg_cmd_cursor_changed), (gpointer *)loc); - - /* Translate titles. */ - intl_slist(ARRAY_SIZE(usdlg_col_titles), usdlg_col_titles, &titles_done); - - for (i = 0; i < USDLG_COLUMNS_SHOW; i++) { - GtkTreeViewColumn *column = NULL; - GtkCellRenderer *renderer = NULL; - - switch (usdlg_col_types[i]) { - case COL_PIXBUF: - renderer = gtk_cell_renderer_pixbuf_new(); - column = gtk_tree_view_column_new_with_attributes( - usdlg_col_titles[i], renderer, "pixbuf", i, NULL); - gtk_tree_view_column_set_expand(column, FALSE); - break; - case COL_TEXT: - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes( - usdlg_col_titles[i], renderer, "text", i, - "style", USDLG_COL_STYLE, "weight", USDLG_COL_WEIGHT, NULL); - gtk_tree_view_column_set_expand(column, TRUE); - break; - case COL_INT: - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes( - usdlg_col_titles[i], renderer, "text", i, - "style", USDLG_COL_STYLE, "weight", USDLG_COL_WEIGHT, NULL); - g_object_set(renderer, "xalign", 1.0, NULL); - gtk_tree_view_column_set_alignment(column, 1.0); - gtk_tree_view_column_set_expand(column, FALSE); - break; - } - - fc_assert_ret(column != NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); - } - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 300); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(sw), view); - gtk_container_add(GTK_CONTAINER(hbox), sw); - - vbox = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(hbox), vbox); - - /* button box 1: ready, sentry */ - - pdialog->tabs[loc].cmd[USDLG_CMD_READY] - = gtk_button_new_with_mnemonic(_("Ready")); - gtk_container_add(GTK_CONTAINER(vbox), - pdialog->tabs[loc].cmd[USDLG_CMD_READY]); - g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_READY], "clicked", - G_CALLBACK(usdlg_cmd_ready), (gpointer *)loc); - gtk_widget_set_sensitive( - GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_READY]), FALSE); - - pdialog->tabs[loc].cmd[USDLG_CMD_SENTRY] - = gtk_button_new_with_mnemonic(_("Sentry")); - gtk_widget_set_margin_bottom( - GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_SENTRY]), 10); - gtk_container_add(GTK_CONTAINER(vbox), - pdialog->tabs[loc].cmd[USDLG_CMD_SENTRY]); - g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_SENTRY], "clicked", - G_CALLBACK(usdlg_cmd_sentry), (gpointer *)loc); - gtk_widget_set_sensitive( - GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_SENTRY]), FALSE); - - /* button box 2: select, deselect */ - - pdialog->tabs[loc].cmd[USDLG_CMD_SELECT] - = gtk_button_new_with_mnemonic(_("_Select")); - gtk_container_add(GTK_CONTAINER(vbox), - pdialog->tabs[loc].cmd[USDLG_CMD_SELECT]); - g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_SELECT], "clicked", - G_CALLBACK(usdlg_cmd_select), (gpointer *)loc); - gtk_widget_set_sensitive( - GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_SELECT]), FALSE); - - pdialog->tabs[loc].cmd[USDLG_CMD_DESELECT] - = gtk_button_new_with_mnemonic(_("_Deselect")); - gtk_widget_set_margin_bottom( - GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_DESELECT]), 10); - gtk_container_add(GTK_CONTAINER(vbox), - pdialog->tabs[loc].cmd[USDLG_CMD_DESELECT]); - g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_DESELECT], "clicked", - G_CALLBACK(usdlg_cmd_deselect), (gpointer *)loc); - gtk_widget_set_sensitive( - GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_DESELECT]), FALSE); - - /* button box 3: center, focus */ - - pdialog->tabs[loc].cmd[USDLG_CMD_CENTER] - = gtk_button_new_with_mnemonic(_("C_enter")); - gtk_container_add(GTK_CONTAINER(vbox), - pdialog->tabs[loc].cmd[USDLG_CMD_CENTER]); - g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_CENTER], "clicked", - G_CALLBACK(usdlg_cmd_center), (gpointer *)loc); - gtk_widget_set_sensitive( - GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_CENTER]), FALSE); - - pdialog->tabs[loc].cmd[USDLG_CMD_FOCUS] - = gtk_button_new_with_mnemonic(_("_Focus")); - gtk_container_add(GTK_CONTAINER(vbox), - pdialog->tabs[loc].cmd[USDLG_CMD_FOCUS]); - g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_FOCUS], "clicked", - G_CALLBACK(usdlg_cmd_focus), (gpointer *)loc); - gtk_widget_set_sensitive( - GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_FOCUS]), FALSE); -} - -/*************************************************************************//** - Create a player dialog store. -*****************************************************************************/ -static GtkTreeStore *usdlg_tab_store_new(void) -{ - GtkTreeStore *store; - GType model_types[USDLG_COLUMNS_ALL]; - int i; - - for (i = 0; i < USDLG_COLUMNS_ALL; i++) { - switch (usdlg_col_types[i]) { - case COL_PIXBUF: - model_types[i] = GDK_TYPE_PIXBUF; - break; - case COL_TEXT: - model_types[i] = G_TYPE_STRING; - break; - case COL_INT: - model_types[i] = G_TYPE_INT; - break; - } - } - - store = gtk_tree_store_newv(i, model_types); - - return store; -} - -/*************************************************************************//** - Update on tab of the dialog. -*****************************************************************************/ -static bool usdlg_tab_update(struct unit_select_dialog *pdialog, - struct usdata_hash *ushash, - enum unit_select_location_mode loc) -{ - bool show = FALSE; - GtkTreeStore *store; - - fc_assert_ret_val(ushash, FALSE); - fc_assert_ret_val(pdialog != NULL, FALSE); - - store = pdialog->tabs[loc].store; - - /* clear current store. */ - gtk_tree_store_clear(GTK_TREE_STORE(store)); - - /* Iterate over all unit types. */ - if (loc == SELLOC_UNITS) { - /* Special case - show all units on this tile in their transports. */ - unit_type_iterate(utype) { - struct usdata *data; - - usdata_hash_lookup(ushash, utype_index(utype), &data); - - if (!data) { - continue; - } - - activity_type_iterate(act) { - if (unit_list_size(data->units[loc][act]) == 0) { - continue; - } - - unit_list_iterate(data->units[loc][act], punit) { - GtkTreeIter it_unit; - - usdlg_tab_append_units(pdialog, loc, act, punit, FALSE, - &it_unit, NULL); - } unit_list_iterate_end; - - /* Show this tab. */ - show = TRUE; - } activity_type_iterate_end; - } unit_type_iterate_end; - } else { - unit_type_iterate(utype) { - struct usdata *data; - bool first = TRUE; - GtkTreeIter it_utype; - GtkTreePath *path; - int count = 0; - - usdata_hash_lookup(ushash, utype_index(utype), &data); - - if (!data) { - continue; - } - - activity_type_iterate(act) { - GtkTreeIter it_act; - - if (unit_list_size(data->units[loc][act]) == 0) { - continue; - } - - /* Level 1: Display unit type. */ - if (first) { - usdlg_tab_append_utype(GTK_TREE_STORE(store), loc, data->utype, - &it_utype); - first = FALSE; - } - - /* Level 2: Display unit activities. */ - usdlg_tab_append_activity(GTK_TREE_STORE(store), loc, data->utype, - act, unit_list_size(data->units[loc][act]), - &it_act, &it_utype); - - /* Level 3: Display all units with this activitiy - * (and transported units in further level(s)). */ - unit_list_iterate(data->units[loc][act], punit) { - GtkTreeIter it_unit; - - usdlg_tab_append_units(pdialog, loc, act, punit, FALSE, - &it_unit, &it_act); - } unit_list_iterate_end; - - count += unit_list_size(data->units[loc][act]); - - /* Update sum of units with this type. */ - gtk_tree_store_set(GTK_TREE_STORE(store), &it_utype, 2, count, -1); - - /* Expand to the activities. */ - path - = gtk_tree_model_get_path(GTK_TREE_MODEL(pdialog->tabs[loc].store), - &it_utype); - gtk_tree_view_expand_row(GTK_TREE_VIEW(pdialog->tabs[loc].view), path, - FALSE); - gtk_tree_path_free(path); - - /* Show this tab. */ - show = TRUE; - } activity_type_iterate_end; - } unit_type_iterate_end; - } - - return show; -} - -/*************************************************************************//** - Append the data for one unit type. -*****************************************************************************/ -static void usdlg_tab_append_utype(GtkTreeStore *store, - enum unit_select_location_mode loc, - const struct unit_type *putype, - GtkTreeIter *it) -{ - GdkPixbuf *pix; - char buf[128]; - - fc_assert_ret(store != NULL); - fc_assert_ret(putype != NULL); - - /* Add this item. */ - gtk_tree_store_append(GTK_TREE_STORE(store), it, NULL); - - /* Create a icon */ - { - struct canvas canvas_store = FC_STATIC_CANVAS_INIT; - - canvas_store.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - tileset_full_tile_width(tileset), tileset_full_tile_height(tileset)); - - put_unittype(putype, &canvas_store, 1.0, 0, 0); - pix = surface_get_pixbuf(canvas_store.surface, tileset_full_tile_width(tileset), - tileset_full_tile_height(tileset)); - cairo_surface_destroy(canvas_store.surface); - } - - /* The name of the unit. */ - fc_snprintf(buf, sizeof(buf), "%s", utype_name_translation(putype)); - - /* Add it to the tree. */ - gtk_tree_store_set(GTK_TREE_STORE(store), it, - 0, pix, /* Unit pixmap */ - 1, buf, /* Text */ - 2, -1, /* will be set later */ /* Number of units */ - 3, utype_index(putype), /* Unit type ID */ - /* 4: not set */ /* Unit ID */ - 5, loc, /* Unit location */ - /* 6: not set */ /* Unit activity */ - 7, ROW_UNITTYPE, /* Row type */ - 8, PANGO_STYLE_NORMAL, /* Style */ - 9, PANGO_WEIGHT_BOLD, /* Weight */ - -1); - g_object_unref(pix); -} - -/*************************************************************************//** - Append the unit activity. -*****************************************************************************/ -static void usdlg_tab_append_activity(GtkTreeStore *store, - enum unit_select_location_mode loc, - const struct unit_type *putype, - enum unit_activity act, - int count, GtkTreeIter *it, - GtkTreeIter *parent) -{ - char buf[128] = ""; - - fc_assert_ret(store != NULL); - fc_assert_ret(putype != NULL); - - /* Add this item. */ - gtk_tree_store_append(GTK_TREE_STORE(store), it, parent); - - /* The activity. */ - fc_snprintf(buf, sizeof(buf), "%s", get_activity_text(act)); - - /* Add it to the tree. */ - gtk_tree_store_set(GTK_TREE_STORE(store), it, - /* 0: not set */ /* Unit pixmap */ - 1, buf, /* Text */ - 2, count, /* Number of units */ - 3, utype_index(putype), /* Unit type ID */ - /* 4: not set */ /* Unit ID */ - 5, loc, /* Unit location */ - 6, act, /* Unit activity */ - 7, ROW_ACTIVITY, /* Row type */ - 8, PANGO_STYLE_NORMAL, /* Style */ - 9, PANGO_WEIGHT_NORMAL, /* Weight */ - -1); -} - -/*************************************************************************//** - Get an unit selection list item suitable image of the specified unit. - - Caller is responsible for getting rid of the returned image after use. -*****************************************************************************/ -GdkPixbuf *usdlg_get_unit_image(const struct unit *punit) -{ - GdkPixbuf *out; - struct canvas canvas_store = FC_STATIC_CANVAS_INIT; - - canvas_store.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - tileset_full_tile_width(tileset), tileset_full_tile_height(tileset)); - - put_unit(punit, &canvas_store, 1.0, 0, 0); - out = surface_get_pixbuf(canvas_store.surface, - tileset_full_tile_width(tileset), - tileset_full_tile_height(tileset)); - cairo_surface_destroy(canvas_store.surface); - - return out; -} - -/*************************************************************************//** - Get an unit selection list item suitable description of the specified - unit. -*****************************************************************************/ -const char *usdlg_get_unit_descr(const struct unit *punit) -{ - static char buf[248] = ""; - char buf2[248] = ""; - struct city *phome; - - phome = game_city_by_number(punit->homecity); - if (phome) { - fc_snprintf(buf2, sizeof(buf2), "%s", city_name_get(phome)); - } else if (unit_owner(punit) == client_player() - || client_is_global_observer()) { - /* TRANS: used in place of unit home city name */ - sz_strlcpy(buf2, _("no home city")); - } else { - /* TRANS: used in place of unit home city name */ - sz_strlcpy(buf2, _("unknown")); - } -#ifdef FREECIV_DEBUG - /* Strings only used in debug builds, don't bother with i18n */ - fc_snprintf(buf, sizeof(buf), "%s [Unit ID %d]\n(%s)\nCoordinates: (%d,%d)", - unit_name_translation(punit), punit->id, buf2, - TILE_XY(unit_tile(punit))); - { - struct unit *ptrans = unit_transport_get(punit); - - if (ptrans) { - cat_snprintf(buf, sizeof(buf), "\nTransported by unit ID %d", - ptrans->id); - } - } -#else /* FREECIV_DEBUG */ - /* TRANS: unit type and home city, e.g. "Transport\n(New Orleans)" */ - fc_snprintf(buf, sizeof(buf), _("%s\n(%s)"), unit_name_translation(punit), - buf2); -#endif /* FREECIV_DEBUG */ - - return buf; -} - -/*************************************************************************//** - Append units (recursively). -*****************************************************************************/ -static void usdlg_tab_append_units(struct unit_select_dialog *pdialog, - enum unit_select_location_mode loc, - enum unit_activity act, - const struct unit *punit, - bool transported, GtkTreeIter *it, - GtkTreeIter *parent) -{ - const char *text; - GdkPixbuf *pix; - enum usdlg_row_types row = ROW_UNIT; - int style = PANGO_STYLE_NORMAL; - int weight = PANGO_WEIGHT_NORMAL; - GtkTreeStore *store; - - fc_assert_ret(pdialog != NULL); - fc_assert_ret(punit != NULL); - - store = pdialog->tabs[loc].store; - - - /* Add this item. */ - gtk_tree_store_append(GTK_TREE_STORE(store), it, parent); - - /* Unit gfx */ - pix = usdlg_get_unit_image(punit); - - text = usdlg_get_unit_descr(punit); - - if (transported) { - weight = PANGO_WEIGHT_NORMAL; - style = PANGO_STYLE_ITALIC; - row = ROW_UNIT_TRANSPORTED; - } - - /* Add it to the tree. */ - gtk_tree_store_set(GTK_TREE_STORE(store), it, - 0, pix, /* Unit pixmap */ - 1, text, /* Text */ - 2, 1, /* Number of units */ - 3, utype_index(unit_type_get(punit)), /* Unit type ID */ - 4, punit->id, /* Unit ID */ - 5, loc, /* Unit location */ - 6, act, /* Unit activity */ - 7, row, /* Row type */ - 8, style, /* Style */ - 9, weight, /* Weight */ - -1); - g_object_unref(pix); - - if (get_transporter_occupancy(punit) > 0) { - unit_list_iterate(unit_transport_cargo(punit), pcargo) { - GtkTreeIter it_cargo; - - usdlg_tab_append_units(pdialog, loc, act, pcargo, TRUE, &it_cargo, it); - } unit_list_iterate_end; - } - - if (!transported && unit_is_in_focus(punit) - && get_num_units_in_focus() == 1) { - /* Put the keyboard focus on the selected unit. It isn't transported. - * Selection maps to keyboard focus since it alone is selected. */ - fc_assert_action(pdialog->tabs[loc].path == NULL, - /* Don't leak memory. */ - gtk_tree_path_free(pdialog->tabs[loc].path)); - - pdialog->tabs[loc].path - = gtk_tree_model_get_path(GTK_TREE_MODEL(store), it); - } -} - -/*************************************************************************//** - Callback for the ready button. -*****************************************************************************/ -static void usdlg_cmd_ready(GObject *object, gpointer data) -{ - usdlg_cmd_exec(object, data, USDLG_CMD_READY); -} - -/*************************************************************************//** - Callback for the sentry button. -*****************************************************************************/ -static void usdlg_cmd_sentry(GObject *object, gpointer data) -{ - usdlg_cmd_exec(object, data, USDLG_CMD_SENTRY); -} - -/*************************************************************************//** - Callback for the select button. -*****************************************************************************/ -static void usdlg_cmd_select(GObject *object, gpointer data) -{ - usdlg_cmd_exec(object, data, USDLG_CMD_SELECT); -} - -/*************************************************************************//** - Callback for the deselect button. -*****************************************************************************/ -static void usdlg_cmd_deselect(GObject *object, gpointer data) -{ - usdlg_cmd_exec(object, data, USDLG_CMD_DESELECT); -} - -/*************************************************************************//** - Main function for the callbacks. -*****************************************************************************/ -static void usdlg_cmd_exec(GObject *object, gpointer mode_data, - enum usdlg_cmd cmd) -{ - enum unit_select_location_mode loc_mode - = (enum unit_select_location_mode) GPOINTER_TO_INT(mode_data); - GtkTreeView *view; - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter it; - gint row; - struct unit_select_dialog *pdialog = usdlg_get(FALSE); - - fc_assert_ret(pdialog != NULL); - fc_assert_ret(unit_select_location_mode_is_valid(loc_mode)); - - if (!can_client_change_view() || !can_client_control()) { - return; - } - - view = GTK_TREE_VIEW(pdialog->tabs[loc_mode].view); - selection = gtk_tree_view_get_selection(view); - - if (!gtk_tree_selection_get_selected(selection, &model, &it)) { - log_debug("No selection"); - return; - } - gtk_tree_model_get(model, &it, USDLG_COL_ROW_TYPE, &row, -1); - - switch (row) { - case ROW_UNITTYPE: - { - gint loc, utid; - struct usdata_hash *ushash; - struct usdata *data; - - gtk_tree_model_get(model, &it, USDLG_COL_LOCATION, &loc, - USDLG_COL_UTID, &utid, -1); - - /* We can't be sure that all units still exists - recalc the data. */ - ushash = usdlg_data_new(pdialog->ptile); - - usdata_hash_lookup(ushash, utid, &data); - if (data != NULL) { - activity_type_iterate(act) { - if (unit_list_size(data->units[loc][act]) == 0) { - continue; - } - - unit_list_iterate(data->units[loc][act], punit) { - usdlg_cmd_exec_unit(punit, cmd); - } unit_list_iterate_end; - } activity_type_iterate_end; - } - - /* Destroy the hash. */ - usdlg_data_destroy(ushash); - } - break; - case ROW_ACTIVITY: - { - gint loc, act, utid; - struct usdata_hash *ushash; - struct usdata *data; - - gtk_tree_model_get(model, &it, USDLG_COL_ACTIVITY, &act, - USDLG_COL_LOCATION, &loc, USDLG_COL_UTID, &utid, -1); - - /* We can't be sure that all units still exists - recalc the data. */ - ushash = usdlg_data_new(pdialog->ptile); - - usdata_hash_lookup(ushash, utid, &data); - if (data != NULL - && unit_list_size(data->units[loc][act]) != 0) { - unit_list_iterate(data->units[loc][act], punit) { - usdlg_cmd_exec_unit(punit, cmd); - } unit_list_iterate_end; - } - - /* Destroy the hash. */ - usdlg_data_destroy(ushash); - } - break; - case ROW_UNIT: - case ROW_UNIT_TRANSPORTED: - { - gint uid; - struct unit *punit; - - gtk_tree_model_get(model, &it, USDLG_COL_UID, &uid, -1); - - punit = game_unit_by_number(uid); - - if (!punit) { - log_debug("Unit vanished (Unit ID %d)!", uid); - return; - } - - usdlg_cmd_exec_unit(punit, cmd); - } - break; - } - - /* Update focus. */ - unit_focus_update(); - /* Refresh dialog. */ - usdlg_refresh(pdialog); -} - -/*************************************************************************//** - Update one unit (select/deselect/ready/sentry). -*****************************************************************************/ -static void usdlg_cmd_exec_unit(struct unit *punit, enum usdlg_cmd cmd) -{ - fc_assert_ret(punit); - - switch (cmd) { - case USDLG_CMD_SELECT: - if (!unit_is_in_focus(punit)) { - unit_focus_add(punit); - } - break; - case USDLG_CMD_DESELECT: - if (unit_is_in_focus(punit)) { - unit_focus_remove(punit); - } - break; - case USDLG_CMD_READY: - if (punit->activity != ACTIVITY_IDLE) { - request_new_unit_activity(punit, ACTIVITY_IDLE); - } - break; - case USDLG_CMD_SENTRY: - if (punit->activity != ACTIVITY_SENTRY) { - request_new_unit_activity(punit, ACTIVITY_SENTRY); - } - break; - case USDLG_CMD_CENTER: - case USDLG_CMD_FOCUS: - /* Nothing here. It is done in its own functions. */ - break; - case USDLG_CMD_LAST: - /* Should never happen. */ - fc_assert_ret(cmd != USDLG_CMD_LAST); - break; - } -} - -/*************************************************************************//** - Callback for the center button. -*****************************************************************************/ -static void usdlg_cmd_center(GObject *object, gpointer data) -{ - enum unit_select_location_mode loc - = (enum unit_select_location_mode) GPOINTER_TO_INT(data); - GtkTreeView *view; - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter it; - gint row; - struct unit_select_dialog *pdialog = usdlg_get(FALSE); - - fc_assert_ret(pdialog != NULL); - fc_assert_ret(unit_select_location_mode_is_valid(loc)); - - view = GTK_TREE_VIEW(pdialog->tabs[loc].view); - selection = gtk_tree_view_get_selection(view); - - if (!gtk_tree_selection_get_selected(selection, &model, &it)) { - log_debug("No selection"); - return; - } - gtk_tree_model_get(model, &it, USDLG_COL_ROW_TYPE, &row, -1); - - if (row == ROW_UNIT || row == ROW_UNIT_TRANSPORTED) { - gint uid; - struct unit *punit; - - gtk_tree_model_get(model, &it, USDLG_COL_UID, &uid, -1); - - punit = player_unit_by_number(client_player(), uid); - if (punit) { - center_tile_mapcanvas(unit_tile(punit)); - } - } -} - -/*************************************************************************//** - Callback for the focus button. -*****************************************************************************/ -static void usdlg_cmd_focus(GObject *object, gpointer data) -{ - enum unit_select_location_mode loc - = (enum unit_select_location_mode) GPOINTER_TO_INT(data); - struct unit_select_dialog *pdialog = usdlg_get(FALSE); - - fc_assert_ret(pdialog != NULL); - fc_assert_ret(unit_select_location_mode_is_valid(loc)); - - usdlg_cmd_focus_real(GTK_TREE_VIEW(pdialog->tabs[loc].view)); -} - -/*************************************************************************//** - Callback if a row is activated. -*****************************************************************************/ -static void usdlg_cmd_row_activated(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data) -{ - usdlg_cmd_focus_real(view); -} - -/*************************************************************************//** - Focus to the currently selected unit. -*****************************************************************************/ -static void usdlg_cmd_focus_real(GtkTreeView *view) -{ - GtkTreeSelection *selection = gtk_tree_view_get_selection(view); - GtkTreeModel *model; - GtkTreeIter it; - gint row; - - if (!can_client_change_view() || !can_client_control()) { - return; - } - - if (!gtk_tree_selection_get_selected(selection, &model, &it)) { - log_debug("No selection"); - return; - } - gtk_tree_model_get(model, &it, USDLG_COL_ROW_TYPE, &row, -1); - - if (row == ROW_UNIT || row == ROW_UNIT_TRANSPORTED) { - gint uid; - struct unit *punit; - - gtk_tree_model_get(model, &it, USDLG_COL_UID, &uid, -1); - - punit = player_unit_by_number(client_player(), uid); - if (punit && unit_owner(punit) == client_player()) { - unit_focus_set(punit); - usdlg_destroy(); - } - } -} - -/*************************************************************************//** - Callback if the row is changed. -*****************************************************************************/ -static void usdlg_cmd_cursor_changed(GtkTreeView *view, gpointer data) -{ - enum unit_select_location_mode loc - = (enum unit_select_location_mode) GPOINTER_TO_INT(data); - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter it; - gint row, uid; - struct unit_select_dialog *pdialog = usdlg_get(FALSE); - struct unit *punit; - bool cmd_status[USDLG_CMD_LAST]; - int cmd_id; - - fc_assert_ret(unit_select_location_mode_is_valid(loc)); - - if (pdialog == NULL) { - /* Dialog closed, nothing we can do */ - return; - } - - selection = gtk_tree_view_get_selection(view); - if (!gtk_tree_selection_get_selected(selection, &model, &it)) { - log_debug("No selection"); - return; - } - gtk_tree_model_get(model, &it, USDLG_COL_ROW_TYPE, &row, USDLG_COL_UID, - &uid, -1); - - switch (row) { - case ROW_UNITTYPE: - case ROW_ACTIVITY: - /* Button status for rows unittype and activity: - * player observer - * ready TRUE FALSE - * sentry TRUE FALSE - * select TRUE FALSE - * deselect TRUE FALSE - * center FALSE FALSE - * focus FALSE FALSE */ - if (can_client_change_view() && can_client_control()) { - cmd_status[USDLG_CMD_READY] = TRUE; - cmd_status[USDLG_CMD_SENTRY] = TRUE; - cmd_status[USDLG_CMD_SELECT] = TRUE; - cmd_status[USDLG_CMD_DESELECT] = TRUE; - } else { - cmd_status[USDLG_CMD_READY] = FALSE; - cmd_status[USDLG_CMD_SENTRY] = FALSE; - cmd_status[USDLG_CMD_SELECT] = FALSE; - cmd_status[USDLG_CMD_DESELECT] = FALSE; - } - - cmd_status[USDLG_CMD_CENTER] = FALSE; - cmd_status[USDLG_CMD_FOCUS] = FALSE; - break; - case ROW_UNIT: - case ROW_UNIT_TRANSPORTED: - /* Button status for rows unit and unit (transported): - * player observer - * ready !IDLE FALSE - * sentry !SENTRY FALSE - * select !FOCUS FALSE - * deselect FOCUS FALSE - * center TRUE TRUE - * focus !FOCUS FALSE */ - punit = player_unit_by_number(client_player(), uid); - - if (punit && can_client_change_view() && can_client_control()) { - if (punit->activity == ACTIVITY_IDLE) { - cmd_status[USDLG_CMD_READY] = FALSE; - } else { - cmd_status[USDLG_CMD_READY] = TRUE; - } - - if (punit->activity == ACTIVITY_SENTRY) { - cmd_status[USDLG_CMD_SENTRY] = FALSE; - } else { - cmd_status[USDLG_CMD_SENTRY] = TRUE; - } - - if (!unit_is_in_focus(punit)) { - cmd_status[USDLG_CMD_SELECT] = TRUE; - cmd_status[USDLG_CMD_DESELECT] = FALSE; - cmd_status[USDLG_CMD_FOCUS] = TRUE; - } else { - cmd_status[USDLG_CMD_SELECT] = FALSE; - cmd_status[USDLG_CMD_DESELECT] = TRUE; - cmd_status[USDLG_CMD_FOCUS] = FALSE; - } - } else { - cmd_status[USDLG_CMD_READY] = FALSE; - cmd_status[USDLG_CMD_SENTRY] = FALSE; - - cmd_status[USDLG_CMD_SELECT] = FALSE; - cmd_status[USDLG_CMD_DESELECT] = FALSE; - - cmd_status[USDLG_CMD_FOCUS] = FALSE; - } - - cmd_status[USDLG_CMD_CENTER] = TRUE; - break; - - default: - fc_assert(FALSE); - for (cmd_id = 0; cmd_id < USDLG_CMD_LAST; cmd_id++) { - cmd_status[cmd_id] = FALSE; - } - break; - } - - /* Set widget status. */ - for (cmd_id = 0; cmd_id < USDLG_CMD_LAST; cmd_id++) { - gtk_widget_set_sensitive(GTK_WIDGET(pdialog->tabs[loc].cmd[cmd_id]), - cmd_status[cmd_id]); - } -} diff --git a/client/gui-gtk-3.0/unitselect.h b/client/gui-gtk-3.0/unitselect.h deleted file mode 100644 index 069d2b1bc7..0000000000 --- a/client/gui-gtk-3.0/unitselect.h +++ /dev/null @@ -1,27 +0,0 @@ -/***************************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -*****************************************************************************/ -#ifndef FC__UNITSELECT_H -#define FC__UNITSELECT_H - -#include - -struct tile; - -void unit_select_dialog_popup_main(struct tile *ptile, bool create); - -void unit_select_dialog_popdown(void); - -GdkPixbuf *usdlg_get_unit_image(const struct unit *punit); -const char *usdlg_get_unit_descr(const struct unit *punit); - -#endif /* FC__UNITSELECT_H */ diff --git a/client/gui-gtk-3.0/unitselextradlg.c b/client/gui-gtk-3.0/unitselextradlg.c deleted file mode 100644 index 8a235751c2..0000000000 --- a/client/gui-gtk-3.0/unitselextradlg.c +++ /dev/null @@ -1,236 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" - -/* common */ -#include "extras.h" -#include "game.h" -#include "movement.h" -#include "unit.h" - -/* client */ -#include "control.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "gui_main.h" -#include "gui_stuff.h" -#include "sprite.h" - -#include "unitselextradlg.h" - -struct unit_sel_extra_cb_data { - GtkWidget *dlg; - int tp_id; -}; - -/***********************************************************************//** - Get an extra selection list item suitable description of the specified - extra at the specified tile. -***************************************************************************/ -static const char *tgt_extra_descr(const struct extra_type *tgt_extra, - const struct tile *tgt_tile) -{ - static char buf[248] = ""; - static char buf2[248] = ""; - - if (tile_has_extra(tgt_tile, tgt_extra)) { - if (extra_owner(tgt_tile)) { - /* TRANS: nation adjective for extra owner used below if the target - * tile has the target extra and it has an owner. */ - fc_snprintf(buf2, sizeof(buf2), Q_("?eowner:%s"), - nation_adjective_for_player(extra_owner(tgt_tile))); - } else { - /* TRANS: used below if the target tile has the target extra but it - * doesn't have an owner. */ - sz_strlcpy(buf2, _("target")); - } - } else { - /* TRANS: used below if the target tile doesn't have the target - * extra (so it is assumed that it will be created). */ - sz_strlcpy(buf2, _("create")); - } - - /* TRANS: extra name ... one of the above strings depending on if the - * target extra currently exists at the target tile and if it has an - * owner. */ - fc_snprintf(buf, sizeof(buf), _("%s\n(%s)"), - extra_name_translation(tgt_extra), buf2); - - return buf; -} - -/************************************************************************//** - Callback to handle toggling of one of the target extra buttons. -****************************************************************************/ -static void unit_sel_extra_toggled(GtkToggleButton *tb, gpointer userdata) -{ - struct unit_sel_extra_cb_data *cbdata - = (struct unit_sel_extra_cb_data *)userdata; - - if (gtk_toggle_button_get_active(tb)) { - g_object_set_data(G_OBJECT(cbdata->dlg), "target", - GINT_TO_POINTER(cbdata->tp_id)); - } -} - -/************************************************************************//** - Callback to handle destruction of one of the target extra buttons. -****************************************************************************/ -static void unit_sel_extra_destroyed(GtkWidget *radio, gpointer userdata) -{ - free(userdata); -} - -/************************************************************************//** - Create a dialog where a unit select what extra to act on. -****************************************************************************/ -bool select_tgt_extra(struct unit *actor, struct tile *ptile, - bv_extras potential_tgt_extras, - struct extra_type *suggested_tgt_extra, - const gchar *dlg_title, - const gchar *actor_label, - const gchar *tgt_label, - const gchar *do_label, - GCallback do_callback) -{ - GtkWidget *dlg; - GtkWidget *main_box; - GtkWidget *box; - GtkWidget *icon; - GtkWidget *lbl; - GtkWidget *sep; - GtkWidget *radio; - GtkWidget *default_option = NULL; - GtkWidget *first_option = NULL; - struct sprite *spr; - const struct unit_type *actor_type = unit_type_get(actor); - int tcount; - - dlg = gtk_dialog_new_with_buttons(dlg_title, NULL, 0, - _("Close"), GTK_RESPONSE_NO, - do_label, GTK_RESPONSE_YES, - NULL); - setup_dialog(dlg, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_NO); - gtk_window_set_destroy_with_parent(GTK_WINDOW(dlg), TRUE); - - main_box = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(main_box), - GTK_ORIENTATION_VERTICAL); - box = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(box), - GTK_ORIENTATION_HORIZONTAL); - - lbl = gtk_label_new(actor_label); - gtk_grid_attach(GTK_GRID(box), lbl, 0, 0, 1, 1); - - spr = get_unittype_sprite(tileset, actor_type, direction8_invalid()); - if (spr != NULL) { - icon = gtk_image_new_from_pixbuf(sprite_get_pixbuf(spr)); - } else { - icon = gtk_image_new(); - } - gtk_grid_attach(GTK_GRID(box), icon, 1, 0, 1, 1); - - lbl = gtk_label_new(utype_name_translation(actor_type)); - gtk_grid_attach(GTK_GRID(box), lbl, 2, 0, 1, 1); - - gtk_container_add(GTK_CONTAINER(main_box), box); - - sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); - gtk_container_add(GTK_CONTAINER(main_box), sep); - - lbl = gtk_label_new(tgt_label); - gtk_container_add(GTK_CONTAINER(main_box), lbl); - - box = gtk_grid_new(); - - tcount = 0; - extra_type_re_active_iterate(ptgt) { - GdkPixbuf *tubuf; - - if (!BV_ISSET(potential_tgt_extras, extra_number(ptgt))) { - continue; - } - - struct unit_sel_extra_cb_data *cbdata - = fc_malloc(sizeof(struct unit_sel_extra_cb_data)); - - cbdata->tp_id = ptgt->id; - cbdata->dlg = dlg; - - radio = gtk_radio_button_new_from_widget( - GTK_RADIO_BUTTON(first_option)); - if (first_option == NULL) { - first_option = radio; - default_option = first_option; - } - /* The lists must be the same length if they contain the same - * elements. */ - fc_assert_msg(g_slist_length(gtk_radio_button_get_group( - GTK_RADIO_BUTTON(radio))) - == g_slist_length(gtk_radio_button_get_group( - GTK_RADIO_BUTTON(first_option))), - "The radio button for '%s' is broken.", - extra_rule_name(ptgt)); - g_signal_connect(radio, "toggled", - G_CALLBACK(unit_sel_extra_toggled), cbdata); - g_signal_connect(radio, "destroy", - G_CALLBACK(unit_sel_extra_destroyed), cbdata); - if (ptgt == suggested_tgt_extra) { - default_option = radio; - } - gtk_grid_attach(GTK_GRID(box), radio, 0, tcount, 1, 1); - - tubuf = create_extra_pixbuf(ptgt); - if (tubuf != NULL) { - icon = gtk_image_new_from_pixbuf(tubuf); - g_object_unref(tubuf); - } else { - icon = gtk_image_new(); - } - gtk_grid_attach(GTK_GRID(box), icon, 1, tcount, 1, 1); - - lbl = gtk_label_new(tgt_extra_descr(ptgt, ptile)); - gtk_grid_attach(GTK_GRID(box), lbl, 2, tcount, 1, 1); - - tcount++; - } extra_type_re_active_iterate_end; - gtk_container_add(GTK_CONTAINER(main_box), box); - - fc_assert_ret_val(default_option, FALSE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(default_option), TRUE); - - gtk_container_add( - GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), - main_box); - - g_object_set_data(G_OBJECT(dlg), "actor", GINT_TO_POINTER(actor->id)); - g_object_set_data(G_OBJECT(dlg), "tile", ptile); - - g_signal_connect(dlg, "response", do_callback, actor); - - gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dlg))); - gtk_widget_show(dlg); - - return TRUE; -} diff --git a/client/gui-gtk-3.0/unitselextradlg.h b/client/gui-gtk-3.0/unitselextradlg.h deleted file mode 100644 index 790eeca39a..0000000000 --- a/client/gui-gtk-3.0/unitselextradlg.h +++ /dev/null @@ -1,25 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996-2018 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__UNITSELEXTRADLG_H -#define FC__UNITSELEXTRADLG_H - -bool select_tgt_extra(struct unit *actor, struct tile *ptile, - bv_extras potential_tgt_extras, - struct extra_type *suggested_tgt_extra, - const gchar *dlg_title, - const gchar *actor_label, - const gchar *tgt_label, - const gchar *do_label, - GCallback do_callback); - -#endif /* FC__UNITSELEXTRADLG_H */ diff --git a/client/gui-gtk-3.0/unitselunitdlg.c b/client/gui-gtk-3.0/unitselunitdlg.c deleted file mode 100644 index e0c8bd1683..0000000000 --- a/client/gui-gtk-3.0/unitselunitdlg.c +++ /dev/null @@ -1,196 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" - -/* common */ -#include "game.h" -#include "movement.h" -#include "unit.h" - -/* client */ -#include "control.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "gui_main.h" -#include "gui_stuff.h" -#include "sprite.h" -#include "unitselect.h" - -#include "unitselunitdlg.h" - -struct unit_sel_unit_cb_data { - GtkWidget *dlg; - int tp_id; -}; - -/************************************************************************//** - Callback to handle toggling of one of the target unit buttons. -****************************************************************************/ -static void unit_sel_unit_toggled(GtkToggleButton *tb, gpointer userdata) -{ - struct unit_sel_unit_cb_data *cbdata - = (struct unit_sel_unit_cb_data *)userdata; - - if (gtk_toggle_button_get_active(tb)) { - g_object_set_data(G_OBJECT(cbdata->dlg), "target", - GINT_TO_POINTER(cbdata->tp_id)); - } -} - -/************************************************************************//** - Callback to handle destruction of one of the target unit buttons. -****************************************************************************/ -static void unit_sel_unit_destroyed(GtkWidget *radio, gpointer userdata) -{ - free(userdata); -} - -/************************************************************************//** - Create a dialog where a unit select what other unit to act on. -****************************************************************************/ -bool select_tgt_unit(struct unit *actor, struct tile *ptile, - struct unit_list *potential_tgt_units, - struct unit *suggested_tgt_unit, - const gchar *dlg_title, - const gchar *actor_label, - const gchar *tgt_label, - const gchar *do_label, - GCallback do_callback) -{ - GtkWidget *dlg; - GtkWidget *main_box; - GtkWidget *box; - GtkWidget *icon; - GtkWidget *lbl; - GtkWidget *sep; - GtkWidget *radio; - GtkWidget *default_option = NULL; - GtkWidget *first_option = NULL; - struct sprite *spr; - const struct unit_type *actor_type = unit_type_get(actor); - int tcount; - - dlg = gtk_dialog_new_with_buttons(dlg_title, NULL, 0, - _("Close"), GTK_RESPONSE_NO, - do_label, GTK_RESPONSE_YES, - NULL); - setup_dialog(dlg, toplevel); - gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_NO); - gtk_window_set_destroy_with_parent(GTK_WINDOW(dlg), TRUE); - - main_box = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(main_box), - GTK_ORIENTATION_VERTICAL); - box = gtk_grid_new(); - gtk_orientable_set_orientation(GTK_ORIENTABLE(box), - GTK_ORIENTATION_HORIZONTAL); - - lbl = gtk_label_new(actor_label); - gtk_grid_attach(GTK_GRID(box), lbl, 0, 0, 1, 1); - - spr = get_unittype_sprite(tileset, actor_type, direction8_invalid()); - if (spr != NULL) { - icon = gtk_image_new_from_pixbuf(sprite_get_pixbuf(spr)); - } else { - icon = gtk_image_new(); - } - gtk_grid_attach(GTK_GRID(box), icon, 1, 0, 1, 1); - - lbl = gtk_label_new(utype_name_translation(actor_type)); - gtk_grid_attach(GTK_GRID(box), lbl, 2, 0, 1, 1); - - gtk_container_add(GTK_CONTAINER(main_box), box); - - sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); - gtk_container_add(GTK_CONTAINER(main_box), sep); - - lbl = gtk_label_new(tgt_label); - gtk_container_add(GTK_CONTAINER(main_box), lbl); - - box = gtk_grid_new(); - - tcount = 0; - unit_list_iterate(potential_tgt_units, ptgt) { - GdkPixbuf *tubuf; - - struct unit_sel_unit_cb_data *cbdata - = fc_malloc(sizeof(struct unit_sel_unit_cb_data)); - - cbdata->tp_id = ptgt->id; - cbdata->dlg = dlg; - - radio = gtk_radio_button_new_from_widget( - GTK_RADIO_BUTTON(first_option)); - if (first_option == NULL) { - first_option = radio; - default_option = first_option; - } - /* The lists must be the same length if they contain the same - * elements. */ - fc_assert_msg(g_slist_length(gtk_radio_button_get_group( - GTK_RADIO_BUTTON(radio))) - == g_slist_length(gtk_radio_button_get_group( - GTK_RADIO_BUTTON(first_option))), - "The radio button for '%s' is broken.", - unit_rule_name(ptgt)); - g_signal_connect(radio, "toggled", - G_CALLBACK(unit_sel_unit_toggled), cbdata); - g_signal_connect(radio, "destroy", - G_CALLBACK(unit_sel_unit_destroyed), cbdata); - if (ptgt == suggested_tgt_unit) { - default_option = radio; - } - gtk_grid_attach(GTK_GRID(box), radio, 0, tcount, 1, 1); - - tubuf = usdlg_get_unit_image(ptgt); - if (tubuf != NULL) { - icon = gtk_image_new_from_pixbuf(tubuf); - g_object_unref(tubuf); - } else { - icon = gtk_image_new(); - } - gtk_grid_attach(GTK_GRID(box), icon, 1, tcount, 1, 1); - - lbl = gtk_label_new(usdlg_get_unit_descr(ptgt)); - gtk_grid_attach(GTK_GRID(box), lbl, 2, tcount, 1, 1); - - tcount++; - } unit_list_iterate_end; - gtk_container_add(GTK_CONTAINER(main_box), box); - - fc_assert_ret_val(default_option, FALSE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(default_option), TRUE); - - gtk_container_add( - GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), - main_box); - - g_object_set_data(G_OBJECT(dlg), "actor", GINT_TO_POINTER(actor->id)); - g_object_set_data(G_OBJECT(dlg), "tile", ptile); - - g_signal_connect(dlg, "response", do_callback, actor); - - gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dlg))); - gtk_widget_show(dlg); - - return TRUE; -} diff --git a/client/gui-gtk-3.0/unitselunitdlg.h b/client/gui-gtk-3.0/unitselunitdlg.h deleted file mode 100644 index d872de86c5..0000000000 --- a/client/gui-gtk-3.0/unitselunitdlg.h +++ /dev/null @@ -1,25 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__UNITSELUNITDLG_H -#define FC__UNITSELUNITDLG_H - -bool select_tgt_unit(struct unit *actor, struct tile *ptile, - struct unit_list *potential_tgt_units, - struct unit *suggested_tgt_unit, - const gchar *dlg_title, - const gchar *actor_label, - const gchar *tgt_label, - const gchar *do_label, - GCallback do_callback); - -#endif /* FC__UNITSELUNITDLG_H */ diff --git a/client/gui-gtk-3.0/voteinfo_bar.c b/client/gui-gtk-3.0/voteinfo_bar.c deleted file mode 100644 index 2e6fe7a33f..0000000000 --- a/client/gui-gtk-3.0/voteinfo_bar.c +++ /dev/null @@ -1,322 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -/* utility */ -#include "fcintl.h" -#include "mem.h" -#include "support.h" - -/* client */ -#include "options.h" -#include "voteinfo.h" - -/* client/gui-gtk-3.0 */ -#include "chatline.h" -#include "pages.h" - -#include "voteinfo_bar.h" - -/* A set of widgets. */ -struct voteinfo_bar { - GtkWidget *box; - GtkWidget *next_button; - GtkWidget *label; - GtkWidget *yes_button; - GtkWidget *no_button; - GtkWidget *abstain_button; - GtkWidget *yes_count_label; - GtkWidget *no_count_label; - GtkWidget *abstain_count_label; - GtkWidget *voter_count_label; -}; - -GtkWidget *pregame_votebar = NULL; /* PAGE_START voteinfo bar. */ -GtkWidget *ingame_votebar = NULL; /* PAGE_GAME voteinfo bar. */ - -/**********************************************************************//** - Called after a click on a vote button. -**************************************************************************/ -static void voteinfo_bar_do_vote_callback(GtkWidget *w, gpointer userdata) -{ - enum client_vote_type vote; - struct voteinfo *vi; - - vote = GPOINTER_TO_INT(userdata); - vi = voteinfo_queue_get_current(NULL); - - if (vi == NULL) { - return; - } - - voteinfo_do_vote(vi->vote_no, vote); -} - -/**********************************************************************//** - Switch to the next vote. -**************************************************************************/ -static void voteinfo_bar_next_callback(GtkWidget *w, gpointer userdata) -{ - voteinfo_queue_next(); - voteinfo_gui_update(); -} - -/**********************************************************************//** - Destroy the voteinfo_bar data structure. -**************************************************************************/ -static void voteinfo_bar_destroy(GtkWidget *w, gpointer userdata) -{ - free((struct voteinfo_bar *) userdata); -} - -/**********************************************************************//** - Create a voteinfo_bar structure. "split_bar" controls whether to split - voteinfo bar over two lines (for narrow windows) or put on a single line - to save vertical space. -**************************************************************************/ -GtkWidget *voteinfo_bar_new(bool split_bar) -{ - GtkWidget *label, *button, *vbox, *hbox, *arrow; - struct voteinfo_bar *vib; - const int BUTTON_HEIGHT = 12; - - vib = fc_calloc(1, sizeof(struct voteinfo_bar)); - - if (!split_bar) { - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - g_object_set_data(G_OBJECT(hbox), "voteinfo_bar", vib); - g_signal_connect(hbox, "destroy", G_CALLBACK(voteinfo_bar_destroy), vib); - vib->box = hbox; - vbox = NULL; /* The compiler may require it. */ - } else { - vbox = gtk_grid_new(); - gtk_grid_set_row_homogeneous(GTK_GRID(vbox), TRUE); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 4); - g_object_set_data(G_OBJECT(vbox), "voteinfo_bar", vib); - g_signal_connect(vbox, "destroy", G_CALLBACK(voteinfo_bar_destroy), vib); - vib->box = vbox; - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - } - - label = gtk_label_new(""); - gtk_widget_set_hexpand(label, TRUE); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_widget_set_margin_left(label, 8); - gtk_widget_set_margin_right(label, 8); - gtk_widget_set_margin_top(label, 4); - gtk_widget_set_margin_bottom(label, 4); - gtk_label_set_max_width_chars(GTK_LABEL(label), 80); - gtk_container_add(GTK_CONTAINER(hbox), label); - gtk_widget_set_name(label, "vote label"); - vib->label = label; - - arrow = gtk_image_new_from_stock(GTK_STOCK_MEDIA_REWIND, - GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_widget_set_halign(arrow, GTK_ALIGN_CENTER); - gtk_widget_set_valign(arrow, GTK_ALIGN_START); - - if (split_bar) { - hbox = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(hbox), 4); - gtk_container_add(GTK_CONTAINER(vbox), hbox); - } - - button = gtk_button_new(); - gtk_widget_set_margin_right(button, 16); - g_signal_connect(button, "clicked", - G_CALLBACK(voteinfo_bar_next_callback), NULL); - gtk_button_set_image(GTK_BUTTON(button), arrow); - gtk_widget_set_size_request(button, -1, BUTTON_HEIGHT); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); - gtk_container_add(GTK_CONTAINER(hbox), button); - vib->next_button = button; - - button = gtk_button_new_with_mnemonic(_("_YES")); - g_signal_connect(button, "clicked", - G_CALLBACK(voteinfo_bar_do_vote_callback), - GINT_TO_POINTER(CVT_YES)); - gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); - gtk_container_add(GTK_CONTAINER(hbox), button); - gtk_widget_set_name(button, "vote yes button"); - vib->yes_button = button; - - label = gtk_label_new("0"); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), label); - vib->yes_count_label = label; - - button = gtk_button_new_with_mnemonic(_("_NO")); - g_signal_connect(button, "clicked", - G_CALLBACK(voteinfo_bar_do_vote_callback), - GINT_TO_POINTER(CVT_NO)); - gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); - gtk_container_add(GTK_CONTAINER(hbox), button); - gtk_widget_set_name(button, "vote no button"); - vib->no_button = button; - - label = gtk_label_new("0"); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), label); - vib->no_count_label = label; - - button = gtk_button_new_with_mnemonic(_("_ABSTAIN")); - g_signal_connect(button, "clicked", - G_CALLBACK(voteinfo_bar_do_vote_callback), - GINT_TO_POINTER(CVT_ABSTAIN)); - gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE); - gtk_container_add(GTK_CONTAINER(hbox), button); - gtk_widget_set_name(button, "vote abstain button"); - vib->abstain_button = button; - - label = gtk_label_new("0"); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), label); - vib->abstain_count_label = label; - - label = gtk_label_new("/0"); - gtk_widget_set_halign(label, GTK_ALIGN_START); - gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - gtk_container_add(GTK_CONTAINER(hbox), label); - vib->voter_count_label = label; - - return vib->box; -} - -/**********************************************************************//** - Refresh all vote related GUI widgets. Called by the voteinfo module when - the client receives new vote information from the server. -**************************************************************************/ -void voteinfo_gui_update(void) -{ - int vote_count, index; - struct voteinfo_bar *vib = NULL; - struct voteinfo *vi = NULL; - char buf[1024], status[1024], ordstr[128], color[32]; - bool running, need_scroll; - gchar *escaped_desc, *escaped_user; - - if (get_client_page() == PAGE_START && NULL != pregame_votebar) { - vib = g_object_get_data(G_OBJECT(pregame_votebar), "voteinfo_bar"); - } else if (get_client_page() == PAGE_GAME && NULL != ingame_votebar) { - vib = g_object_get_data(G_OBJECT(ingame_votebar), "voteinfo_bar"); - } - - if (vib == NULL) { - return; - } - - if (!voteinfo_bar_can_be_shown()) { - gtk_widget_hide(vib->box); - return; - } - - vote_count = voteinfo_queue_size(); - vi = voteinfo_queue_get_current(&index); - - if (vi != NULL && vi->resolved && vi->passed) { - /* TRANS: Describing a vote that passed. */ - fc_snprintf(status, sizeof(status), _("[passed]")); - sz_strlcpy(color, "green"); - } else if (vi != NULL && vi->resolved && !vi->passed) { - /* TRANS: Describing a vote that failed. */ - fc_snprintf(status, sizeof(status), _("[failed]")); - sz_strlcpy(color, "red"); - } else if (vi != NULL && vi->remove_time > 0) { - /* TRANS: Describing a vote that was removed. */ - fc_snprintf(status, sizeof(status), _("[removed]")); - sz_strlcpy(color, "grey"); - } else { - status[0] = '\0'; - } - - if (vote_count > 1) { - fc_snprintf(ordstr, sizeof(ordstr), - "(%d/%d) ", - index + 1, vote_count); - } else { - ordstr[0] = '\0'; - } - - if (status[0] != '\0') { - fc_snprintf(buf, sizeof(buf), - "%s ", - color, status); - sz_strlcpy(status, buf); - } - - if (vi != NULL) { - escaped_desc = g_markup_escape_text(vi->desc, -1); - escaped_user = g_markup_escape_text(vi->user, -1); - /* TRANS: "Vote" as a process */ - fc_snprintf(buf, sizeof(buf), _("%sVote %d by %s: %s%s"), - ordstr, vi->vote_no, escaped_user, status, - escaped_desc); - g_free(escaped_desc); - g_free(escaped_user); - } else { - buf[0] = '\0'; - } - gtk_label_set_markup(GTK_LABEL(vib->label), buf); - - if (vi != NULL) { - fc_snprintf(buf, sizeof(buf), "%d", vi->yes); - gtk_label_set_text(GTK_LABEL(vib->yes_count_label), buf); - fc_snprintf(buf, sizeof(buf), "%d", vi->no); - gtk_label_set_text(GTK_LABEL(vib->no_count_label), buf); - fc_snprintf(buf, sizeof(buf), "%d", vi->abstain); - gtk_label_set_text(GTK_LABEL(vib->abstain_count_label), buf); - fc_snprintf(buf, sizeof(buf), "/%d", vi->num_voters); - gtk_label_set_text(GTK_LABEL(vib->voter_count_label), buf); - } else { - gtk_label_set_text(GTK_LABEL(vib->yes_count_label), "-"); - gtk_label_set_text(GTK_LABEL(vib->no_count_label), "-"); - gtk_label_set_text(GTK_LABEL(vib->abstain_count_label), "-"); - gtk_label_set_text(GTK_LABEL(vib->voter_count_label), "/-"); - } - - running = vi != NULL && !vi->resolved && vi->remove_time == 0; - - gtk_widget_set_sensitive(vib->yes_button, running); - gtk_widget_set_sensitive(vib->no_button, running); - gtk_widget_set_sensitive(vib->abstain_button, running); - - need_scroll = !gtk_widget_get_visible(vib->box) - && chatline_is_scrolled_to_bottom(); - - gtk_widget_show_all(vib->box); - - if (vote_count <= 1) { - gtk_widget_hide(vib->next_button); - } - - if (need_scroll) { - /* Showing the votebar when it was hidden - * previously makes the chatline scroll up. */ - chatline_scroll_to_bottom(TRUE); - } -} diff --git a/client/gui-gtk-3.0/voteinfo_bar.h b/client/gui-gtk-3.0/voteinfo_bar.h deleted file mode 100644 index aeb1214c9e..0000000000 --- a/client/gui-gtk-3.0/voteinfo_bar.h +++ /dev/null @@ -1,26 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__VOTEBAR_H -#define FC__VOTEBAR_H - -#include - -/* include */ -#include "voteinfo_bar_g.h" - -extern GtkWidget *pregame_votebar; -extern GtkWidget *ingame_votebar; - -GtkWidget *voteinfo_bar_new(bool split_bar); - -#endif /* FC__VOTEBAR_H */ diff --git a/client/gui-gtk-3.0/wldlg.c b/client/gui-gtk-3.0/wldlg.c deleted file mode 100644 index 7ec29ba66a..0000000000 --- a/client/gui-gtk-3.0/wldlg.c +++ /dev/null @@ -1,1511 +0,0 @@ -/*********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include -#include - -/* utility */ -#include "fcintl.h" -#include "log.h" -#include "mem.h" -#include "support.h" - -/* common */ -#include "city.h" -#include "packets.h" -#include "worklist.h" - -/* client */ -#include "citydlg_common.h" -#include "client_main.h" -#include "climisc.h" -#include "global_worklist.h" -#include "options.h" -#include "tilespec.h" - -/* client/gui-gtk-3.0 */ -#include "canvas.h" -#include "citydlg.h" -#include "graphics.h" -#include "gui_main.h" -#include "gui_stuff.h" -#include "helpdlg.h" -#include "inputdlg.h" - -#include "wldlg.h" - -static GtkWidget *worklists_shell; -static GtkWidget *worklists_list; - -enum { - WORKLISTS_NEW, - WORKLISTS_DELETE, - WORKLISTS_PROPERTIES, - WORKLISTS_CLOSE -}; - -static GtkListStore *worklists_store; - -static int max_unit_height = -1, max_unit_width = -1; - -static void reset_global_worklist(GtkWidget *editor, - struct global_worklist *pgwl); -static void popup_worklist(struct global_worklist *pgwl); -static void popdown_worklist(struct global_worklist *pgwl); -static void dst_row_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data); - -/************************************************************************//** - Illegal initialization value for max unit size variables -****************************************************************************/ -void blank_max_unit_size(void) -{ - max_unit_height = -1; - max_unit_width = -1; -} - -/************************************************************************//** - Setup max unit sprite size. -****************************************************************************/ -static void update_max_unit_size(void) -{ - max_unit_height = 0; - max_unit_width = 0; - - unit_type_iterate(i) { - int x1, x2, y1, y2; - struct sprite *sprite = get_unittype_sprite(tileset, i, - direction8_invalid()); - - sprite_get_bounding_box(sprite, &x1, &y1, &x2, &y2); - max_unit_width = MAX(max_unit_width, x2 - x1); - max_unit_height = MAX(max_unit_height, y2 - y1); - } unit_type_iterate_end; -} - -/************************************************************************//** - Worklists dialog being destroyed -****************************************************************************/ -static void worklists_destroy_callback(GtkWidget *w, gpointer data) -{ - worklists_shell = NULL; -} - -/************************************************************************//** - Refresh global worklists list -****************************************************************************/ -void update_worklist_report_dialog(void) -{ - GtkTreeIter it; - - gtk_list_store_clear(worklists_store); - global_worklists_iterate(pgwl) { - gtk_list_store_append(worklists_store, &it); - - gtk_list_store_set(worklists_store, &it, - 0, global_worklist_name(pgwl), - 1, global_worklist_id(pgwl), - -1); - } global_worklists_iterate_end; -} - -/************************************************************************//** - User has responded to worklist report -****************************************************************************/ -static void worklists_response(GtkWidget *w, gint response) -{ - struct global_worklist *pgwl; - int id; - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter it; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(worklists_list)); - - if (gtk_tree_selection_get_selected(selection, &model, &it)) { - gtk_tree_model_get(model, &it, 1, &id, -1); - pgwl = global_worklist_by_id(id); - } else { - pgwl = NULL; - id = -1; - } - - switch (response) { - case WORKLISTS_NEW: - global_worklist_new(_("new")); - update_worklist_report_dialog(); - return; - - case WORKLISTS_DELETE: - if (!pgwl) { - return; - } - - popdown_worklist(pgwl); - global_worklist_destroy(pgwl); - update_worklist_report_dialog(); - return; - - case WORKLISTS_PROPERTIES: - if (!pgwl) { - return; - } - - popup_worklist(pgwl); - return; - - default: - gtk_widget_destroy(worklists_shell); - return; - } -} - -/************************************************************************//** - Worklist cell edited -****************************************************************************/ -static void cell_edited(GtkCellRendererText *cell, - const gchar *spath, - const gchar *text, gpointer data) -{ - GtkTreePath *path; - GtkTreeIter it; - struct global_worklist *pgwl; - int id; - - path = gtk_tree_path_new_from_string(spath); - gtk_tree_model_get_iter(GTK_TREE_MODEL(worklists_store), &it, path); - gtk_tree_path_free(path); - - gtk_tree_model_get(GTK_TREE_MODEL(worklists_store), &it, 1, &id, -1); - pgwl = global_worklist_by_id(id); - - if (!pgwl) { - gtk_list_store_remove(worklists_store, &it); - return; - } - - global_worklist_set_name(pgwl, text); - gtk_list_store_set(worklists_store, &it, 0, text, -1); -} - -/************************************************************************//** - Bring up the global worklist report. -****************************************************************************/ -static GtkWidget *create_worklists_report(void) -{ - GtkWidget *shell, *list; - GtkWidget *vbox, *label, *sw; - GtkCellRenderer *rend; - - shell = gtk_dialog_new_with_buttons(_("Edit worklists"), - NULL, - 0, - GTK_STOCK_NEW, - WORKLISTS_NEW, - GTK_STOCK_DELETE, - WORKLISTS_DELETE, - GTK_STOCK_PROPERTIES, - WORKLISTS_PROPERTIES, - GTK_STOCK_CLOSE, - WORKLISTS_CLOSE, - NULL); - setup_dialog(shell, toplevel); - gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_MOUSE); - - g_signal_connect(shell, "response", - G_CALLBACK(worklists_response), NULL); - g_signal_connect(shell, "destroy", - G_CALLBACK(worklists_destroy_callback), NULL); - - vbox = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(vbox), 2); - gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox), - GTK_ORIENTATION_VERTICAL); - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))), vbox); - - worklists_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); - - list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(worklists_store)); - gtk_widget_set_hexpand(list, TRUE); - gtk_widget_set_vexpand(list, TRUE); - - g_object_unref(worklists_store); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE); - - worklists_list = list; - - rend = gtk_cell_renderer_text_new(); - g_object_set(rend, "editable", TRUE, NULL); - g_signal_connect(rend, "edited", - G_CALLBACK(cell_edited), NULL); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list), -1, NULL, - rend, "text", 0, NULL); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 200); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); - gtk_container_add(GTK_CONTAINER(sw), list); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", list, - "label", _("_Worklists:"), - "xalign", 0.0, "yalign", 0.5, NULL); - - gtk_container_add(GTK_CONTAINER(vbox), label); - gtk_container_add(GTK_CONTAINER(vbox), sw); - gtk_widget_show_all(vbox); - - return shell; -} - -/************************************************************************//** - Open worklists report -****************************************************************************/ -void popup_worklists_report(void) -{ - if (!worklists_shell) { - worklists_shell = create_worklists_report(); - - update_worklist_report_dialog(); - } - - gtk_window_present(GTK_WINDOW(worklists_shell)); -} - - - -/**************************************************************************** - ... -****************************************************************************/ -struct worklist_data { - int global_worklist_id; - struct city *pcity; - - GtkWidget *editor; - - GtkListStore *src, *dst; - GtkWidget *src_view, *dst_view; - GtkTreeSelection *src_selection, *dst_selection; - - GtkTreeViewColumn *src_col, *dst_col; - - GtkWidget *add_cmd, *change_cmd, *help_cmd; - GtkWidget *up_cmd, *down_cmd, *prepend_cmd, *append_cmd, *remove_cmd; - - bool future; -}; - -static GHashTable *hash; - -static void commit_worklist(struct worklist_data *ptr); - - -enum { - TARGET_GTK_TREE_MODEL_ROW -}; - -static GtkTargetEntry wl_dnd_targets[] = { - { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_GTK_TREE_MODEL_ROW }, -}; - - -/************************************************************************//** - Add drag&drop target -****************************************************************************/ -void add_worklist_dnd_target(GtkWidget *w) -{ - gtk_drag_dest_set(w, GTK_DEST_DEFAULT_ALL, - wl_dnd_targets, G_N_ELEMENTS(wl_dnd_targets), - GDK_ACTION_COPY); -} - -/************************************************************************//** - Get worklist by id -****************************************************************************/ -static GtkWidget *get_worklist(int global_worklist_id) -{ - if (hash) { - gpointer ret; - - ret = g_hash_table_lookup(hash, GINT_TO_POINTER(global_worklist_id)); - return ret; - } else { - return NULL; - } -} - -/************************************************************************//** - Insert worklist to editor -****************************************************************************/ -static void insert_worklist(int global_worklist_id, GtkWidget *editor) -{ - if (!hash) { - hash = g_hash_table_new(g_direct_hash, g_direct_equal); - } - g_hash_table_insert(hash, GINT_TO_POINTER(global_worklist_id), editor); -} - -/************************************************************************//** - Remove worklist from hash -****************************************************************************/ -static void delete_worklist(int global_worklist_id) -{ - if (hash) { - g_hash_table_remove(hash, GINT_TO_POINTER(global_worklist_id)); - } -} - -/************************************************************************//** - User responded to worklist report -****************************************************************************/ -static void worklist_response(GtkWidget *shell, gint response) -{ - gtk_widget_destroy(shell); -} - -/************************************************************************//** - Worklist editor window used by the global worklist report. -****************************************************************************/ -static void popup_worklist(struct global_worklist *pgwl) -{ - GtkWidget *shell; - - if (!(shell = get_worklist(global_worklist_id(pgwl)))) { - GtkWidget *editor; - - shell = gtk_dialog_new_with_buttons(global_worklist_name(pgwl), - GTK_WINDOW(worklists_shell), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_STOCK_CLOSE, - GTK_RESPONSE_CLOSE, - NULL); - gtk_window_set_role(GTK_WINDOW(shell), "worklist"); - gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_MOUSE); - g_signal_connect(shell, "response", G_CALLBACK(worklist_response), NULL); - gtk_window_set_default_size(GTK_WINDOW(shell), 500, 400); - - editor = create_worklist(); - reset_global_worklist(editor, pgwl); - insert_worklist(global_worklist_id(pgwl), editor); - - gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))), editor); - gtk_widget_show(editor); - - refresh_worklist(editor); - } - - gtk_window_present(GTK_WINDOW(shell)); -} - -/************************************************************************//** - Close worklist -****************************************************************************/ -static void popdown_worklist(struct global_worklist *pgwl) -{ - GtkWidget *shell; - - if ((shell = get_worklist(global_worklist_id(pgwl)))) { - GtkWidget *parent; - - parent = gtk_widget_get_toplevel(shell); - gtk_widget_destroy(parent); - } -} - -/************************************************************************//** - Destroy worklist -****************************************************************************/ -static void worklist_destroy(GtkWidget *editor, gpointer data) -{ - struct worklist_data *ptr; - - ptr = data; - - if (ptr->global_worklist_id != -1) { - delete_worklist(ptr->global_worklist_id); - } - - free(ptr); -} - -/************************************************************************//** - Item activated from menu -****************************************************************************/ -static void menu_item_callback(GtkMenuItem *item, struct worklist_data *ptr) -{ - struct global_worklist *pgwl; - const struct worklist *pwl; - size_t i; - - if (NULL == client.conn.playing) { - return; - } - - pgwl = global_worklist_by_id(GPOINTER_TO_INT - (g_object_get_data(G_OBJECT(item), "id"))); - if (!pgwl) { - return; - } - pwl = global_worklist_get(pgwl); - - for (i = 0; i < (size_t) worklist_length(pwl); i++) { - GtkTreeIter it; - cid id; - - id = cid_encode(pwl->entries[i]); - - gtk_list_store_append(ptr->dst, &it); - gtk_list_store_set(ptr->dst, &it, 0, (gint) id, -1); - } - - commit_worklist(ptr); -} - -/************************************************************************//** - Open menu for adding items to worklist -****************************************************************************/ -static void popup_add_menu(GtkMenuShell *menu, gpointer data) -{ - GtkWidget *item; - - gtk_container_foreach(GTK_CONTAINER(menu), - (GtkCallback) gtk_widget_destroy, NULL); - - global_worklists_iterate(pgwl) { - item = gtk_menu_item_new_with_label(global_worklist_name(pgwl)); - g_object_set_data(G_OBJECT(item), "id", - GINT_TO_POINTER(global_worklist_id(pgwl))); - gtk_widget_show(item); - - gtk_container_add(GTK_CONTAINER(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(menu_item_callback), data); - } global_worklists_iterate_end; - - item = gtk_separator_menu_item_new(); - gtk_widget_show(item); - - gtk_container_add(GTK_CONTAINER(menu), item); - - item = gtk_menu_item_new_with_mnemonic(_("Edit Global _Worklists")); - gtk_widget_show(item); - - gtk_container_add(GTK_CONTAINER(menu), item); - g_signal_connect(item, "activate", - G_CALLBACK(popup_worklists_report), NULL); -} - -/************************************************************************//** - Help button clicked -****************************************************************************/ -static void help_callback(GtkWidget *w, gpointer data) -{ - struct worklist_data *ptr; - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter it; - - ptr = data; - selection = ptr->src_selection; - - if (gtk_tree_selection_get_selected(selection, &model, &it)) { - gint id; - struct universal target; - - gtk_tree_model_get(model, &it, 0, &id, -1); - target = cid_decode(id); - - if (VUT_UTYPE == target.kind) { - popup_help_dialog_typed(utype_name_translation(target.value.utype), - HELP_UNIT); - } else if (is_great_wonder(target.value.building)) { - popup_help_dialog_typed(improvement_name_translation(target.value.building), - HELP_WONDER); - } else { - popup_help_dialog_typed(improvement_name_translation(target.value.building), - HELP_IMPROVEMENT); - } - } else { - popup_help_dialog_string(HELP_WORKLIST_EDITOR_ITEM); - } -} - -/************************************************************************//** - "Change Production" clicked -****************************************************************************/ -static void change_callback(GtkWidget *w, gpointer data) -{ - struct worklist_data *ptr; - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter it; - - ptr = data; - selection = ptr->src_selection; - - if (gtk_tree_selection_get_selected(selection, &model, &it)) { - gint id; - struct universal univ; - - gtk_tree_model_get(model, &it, 0, &id, -1); - univ = cid_production(id); - city_change_production(ptr->pcity, &univ); - } -} - -/************************************************************************//** - Showing of future targets toggled -****************************************************************************/ -static void future_callback(GtkToggleButton *toggle, gpointer data) -{ - struct worklist_data *ptr; - - ptr = data; - ptr->future = !ptr->future; - - refresh_worklist(ptr->editor); -} - -/************************************************************************//** - Move item up in worklist -****************************************************************************/ -static void queue_bubble_up(struct worklist_data *ptr) -{ - GtkTreePath *path; - GtkTreeViewColumn *col; - GtkTreeModel *model; - - if (!gtk_widget_is_sensitive(ptr->dst_view)) { - return; - } - - model = GTK_TREE_MODEL(ptr->dst); - gtk_tree_view_get_cursor(GTK_TREE_VIEW(ptr->dst_view), &path, &col); - if (path) { - GtkTreeIter it, it_prev; - - if (gtk_tree_path_prev(path)) { - gtk_tree_model_get_iter(model, &it_prev, path); - it = it_prev; - gtk_tree_model_iter_next(model, &it); - - gtk_list_store_swap(GTK_LIST_STORE(model), &it, &it_prev); - - gtk_tree_view_set_cursor(GTK_TREE_VIEW(ptr->dst_view), path, col, FALSE); - commit_worklist(ptr); - } - } - gtk_tree_path_free(path); -} - -/************************************************************************//** - Removal of the item requested -****************************************************************************/ -static void queue_remove(struct worklist_data *ptr) -{ - GtkTreePath *path; - GtkTreeViewColumn *col; - - gtk_tree_view_get_cursor(GTK_TREE_VIEW(ptr->dst_view), &path, &col); - if (path) { - dst_row_callback(GTK_TREE_VIEW(ptr->dst_view), path, col, ptr); - gtk_tree_path_free(path); - } -} - -/************************************************************************//** - Move item down in queue -****************************************************************************/ -static void queue_bubble_down(struct worklist_data *ptr) -{ - GtkTreePath *path; - GtkTreeViewColumn *col; - GtkTreeModel *model; - - if (!gtk_widget_is_sensitive(ptr->dst_view)) { - return; - } - - model = GTK_TREE_MODEL(ptr->dst); - gtk_tree_view_get_cursor(GTK_TREE_VIEW(ptr->dst_view), &path, &col); - if (path) { - GtkTreeIter it, it_next; - - gtk_tree_model_get_iter(model, &it, path); - it_next = it; - if (gtk_tree_model_iter_next(model, &it_next)) { - gtk_list_store_swap(GTK_LIST_STORE(model), &it, &it_next); - - gtk_tree_path_next(path); - gtk_tree_view_set_cursor(GTK_TREE_VIEW(ptr->dst_view), path, col, FALSE); - commit_worklist(ptr); - } - } - gtk_tree_path_free(path); -} - -/************************************************************************//** - Insert item to queue -****************************************************************************/ -static void queue_insert(struct worklist_data *ptr, bool prepend) -{ - GtkTreeModel *model; - GtkTreeIter it; - GtkTreePath *path; - - GtkTreeModel *src_model, *dst_model; - GtkTreeIter src_it, dst_it; - gint i, ncols; - - if (!gtk_widget_is_sensitive(ptr->dst_view)) { - return; - } - - if (!gtk_tree_selection_get_selected(ptr->src_selection, &model, &it)) { - return; - } - - path = gtk_tree_model_get_path(model, &it); - - src_model = GTK_TREE_MODEL(ptr->src); - dst_model = GTK_TREE_MODEL(ptr->dst); - - gtk_tree_model_get_iter(src_model, &src_it, path); - if (prepend) { - gtk_list_store_prepend(GTK_LIST_STORE(dst_model), &dst_it); - } else { - gtk_list_store_append(GTK_LIST_STORE(dst_model), &dst_it); - } - - ncols = gtk_tree_model_get_n_columns(src_model); - - for (i = 0; i < ncols; i++) { - GValue value = { 0, }; - - gtk_tree_model_get_value(src_model, &src_it, i, &value); - gtk_list_store_set_value(GTK_LIST_STORE(dst_model), &dst_it, i, &value); - } - commit_worklist(ptr); - - gtk_tree_path_free(path); -} - -/************************************************************************//** - Prepend item to worklist -****************************************************************************/ -static void queue_prepend(struct worklist_data *ptr) -{ - queue_insert(ptr, TRUE); -} - -/************************************************************************//** - Append item to worklist -****************************************************************************/ -static void queue_append(struct worklist_data *ptr) -{ - queue_insert(ptr, FALSE); -} - -/************************************************************************//** - Source row activated -****************************************************************************/ -static void src_row_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data) -{ - struct worklist_data *ptr; - GtkTreeModel *src_model, *dst_model; - GtkTreeIter src_it, dst_it; - gint i, ncols; - - ptr = data; - - if (!gtk_widget_is_sensitive(ptr->dst_view)) { - return; - } - - src_model = GTK_TREE_MODEL(ptr->src); - dst_model = GTK_TREE_MODEL(ptr->dst); - - gtk_tree_model_get_iter(src_model, &src_it, path); - gtk_list_store_append(GTK_LIST_STORE(dst_model), &dst_it); - - ncols = gtk_tree_model_get_n_columns(src_model); - - for (i = 0; i < ncols; i++) { - GValue value = { 0, }; - - gtk_tree_model_get_value(src_model, &src_it, i, &value); - gtk_list_store_set_value(GTK_LIST_STORE(dst_model), &dst_it, i, &value); - } - commit_worklist(ptr); -} - -/************************************************************************//** - Destination row activated -****************************************************************************/ -static void dst_row_callback(GtkTreeView *view, GtkTreePath *path, - GtkTreeViewColumn *col, gpointer data) -{ - struct worklist_data *ptr; - GtkTreeModel *dst_model; - GtkTreeIter it; - - ptr = data; - dst_model = GTK_TREE_MODEL(ptr->dst); - - gtk_tree_model_get_iter(dst_model, &it, path); - - gtk_list_store_remove(GTK_LIST_STORE(dst_model), &it); - commit_worklist(ptr); -} - -/************************************************************************//** - Key press for source -****************************************************************************/ -static gboolean src_key_press_callback(GtkWidget *w, GdkEventKey *ev, - gpointer data) -{ - struct worklist_data *ptr; - - ptr = data; - - if (!gtk_widget_is_sensitive(ptr->dst_view)) { - return FALSE; - } - - if ((ev->state & GDK_SHIFT_MASK) && ev->keyval == GDK_KEY_Insert) { - queue_prepend(ptr); - return TRUE; - } else if (ev->keyval == GDK_KEY_Insert) { - queue_append(ptr); - return TRUE; - } else { - return FALSE; - } -} - -/************************************************************************//** - Key press for destination -****************************************************************************/ -static gboolean dst_key_press_callback(GtkWidget *w, GdkEventKey *ev, - gpointer data) -{ - GtkTreeModel *model; - struct worklist_data *ptr; - - ptr = data; - model = GTK_TREE_MODEL(ptr->dst); - - if (ev->keyval == GDK_KEY_Delete) { - GtkTreeIter it, it_next; - bool deleted = FALSE; - - if (gtk_tree_model_get_iter_first(model, &it)) { - bool more; - - do { - it_next = it; - more = gtk_tree_model_iter_next(model, &it_next); - - if (gtk_tree_selection_iter_is_selected(ptr->dst_selection, &it)) { - gtk_list_store_remove(GTK_LIST_STORE(model), &it); - deleted = TRUE; - } - it = it_next; - - } while (more); - } - - if (deleted) { - commit_worklist(ptr); - } - return TRUE; - - } else if ((ev->state & GDK_MOD1_MASK) && ev->keyval == GDK_KEY_Up) { - queue_bubble_up(ptr); - return TRUE; - - } else if ((ev->state & GDK_MOD1_MASK) && ev->keyval == GDK_KEY_Down) { - queue_bubble_down(ptr); - return TRUE; - - } else { - return FALSE; - } -} - -/************************************************************************//** - Selection from source -****************************************************************************/ -static void src_selection_callback(GtkTreeSelection *selection, gpointer data) -{ - struct worklist_data *ptr; - - ptr = data; - - /* update widget sensitivity. */ - if (gtk_tree_selection_get_selected(selection, NULL, NULL)) { - if (can_client_issue_orders() - && (!ptr->pcity || city_owner(ptr->pcity) == client.conn.playing)) { - /* if ptr->pcity is NULL, this is a global worklist */ - gtk_widget_set_sensitive(ptr->change_cmd, TRUE); - gtk_widget_set_sensitive(ptr->prepend_cmd, TRUE); - gtk_widget_set_sensitive(ptr->append_cmd, TRUE); - } else { - gtk_widget_set_sensitive(ptr->change_cmd, FALSE); - gtk_widget_set_sensitive(ptr->prepend_cmd, FALSE); - gtk_widget_set_sensitive(ptr->append_cmd, FALSE); - } - gtk_widget_set_sensitive(ptr->help_cmd, TRUE); - } else { - gtk_widget_set_sensitive(ptr->change_cmd, FALSE); - gtk_widget_set_sensitive(ptr->help_cmd, FALSE); - gtk_widget_set_sensitive(ptr->prepend_cmd, FALSE); - gtk_widget_set_sensitive(ptr->append_cmd, FALSE); - } -} - -/************************************************************************//** - Selection from destination -****************************************************************************/ -static void dst_selection_callback(GtkTreeSelection *selection, gpointer data) -{ - struct worklist_data *ptr; - - ptr = data; - - /* update widget sensitivity. */ - if (gtk_tree_selection_count_selected_rows(selection) > 0) { - int num_rows = 0; - GtkTreeIter it; - - gtk_widget_set_sensitive(ptr->up_cmd, TRUE); - gtk_widget_set_sensitive(ptr->down_cmd, TRUE); - if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ptr->dst), &it)) { - do { - num_rows++; - } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ptr->dst), &it)); - } - if (num_rows > 1) { - gtk_widget_set_sensitive(ptr->remove_cmd, TRUE); - } else { - gtk_widget_set_sensitive(ptr->remove_cmd, FALSE); - } - } else { - gtk_widget_set_sensitive(ptr->up_cmd, FALSE); - gtk_widget_set_sensitive(ptr->down_cmd, FALSE); - gtk_widget_set_sensitive(ptr->remove_cmd, FALSE); - } -} - -/************************************************************************//** - Drag&drop to destination -****************************************************************************/ -static gboolean dst_dnd_callback(GtkWidget *w, GdkDragContext *context, - struct worklist_data *ptr) -{ - commit_worklist(ptr); - return FALSE; -} - -/************************************************************************//** - Render worklist cell -****************************************************************************/ -static void cell_render_func(GtkTreeViewColumn *col, GtkCellRenderer *rend, - GtkTreeModel *model, GtkTreeIter *it, - gpointer data) -{ - gint id; - struct universal target; - - gtk_tree_model_get(model, it, 0, &id, -1); - target = cid_production(id); - - if (GTK_IS_CELL_RENDERER_PIXBUF(rend)) { - GdkPixbuf *pix; - struct sprite *sprite; - - if (VUT_UTYPE == target.kind) { - sprite = sprite_scale(get_unittype_sprite(tileset, target.value.utype, - direction8_invalid()), - max_unit_width, max_unit_height); - - } else { - sprite = get_building_sprite(tileset, target.value.building); - - } - pix = sprite_get_pixbuf(sprite); - g_object_set(rend, "pixbuf", pix, NULL); - g_object_unref(G_OBJECT(pix)); - if (VUT_UTYPE == target.kind) { - free_sprite(sprite); - } - } else { - struct city **pcity = data; - gint column; - char *row[4]; - char buf[4][64]; - guint i; - gboolean useless; - - for (i = 0; i < ARRAY_SIZE(row); i++) { - row[i] = buf[i]; - } - column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(rend), "column")); - - get_city_dialog_production_row(row, sizeof(buf[0]), &target, *pcity); - g_object_set(rend, "text", row[column], NULL); - - if (NULL != *pcity && VUT_IMPROVEMENT == target.kind) { - useless = is_improvement_redundant(*pcity, target.value.building); - /* Mark building redundant if we are really certain that there is - * no use for it. */ - g_object_set(rend, "strikethrough", useless, NULL); - } else { - g_object_set(rend, "strikethrough", FALSE, NULL); - } - } -} - -/************************************************************************//** - Populate view with buildable item information -****************************************************************************/ -static void populate_view(GtkTreeView *view, struct city **ppcity, - GtkTreeViewColumn **pcol) -{ - static const char *titles[] = - { N_("Type"), N_("Name"), N_("Info"), N_("Cost"), N_("Turns") }; - - static bool titles_done; - guint i; - GtkCellRenderer *rend; - GtkTreeViewColumn *col; - - intl_slist(ARRAY_SIZE(titles), titles, &titles_done); - - /* Case i == 0 taken out of the loop to workaround gcc-4.2.1 bug - * http://gcc.gnu.org/PR33381 - * Some values would 'stick' from i == 0 round. */ - i = 0; - - rend = gtk_cell_renderer_pixbuf_new(); - - gtk_tree_view_insert_column_with_data_func(view, - i, titles[i], rend, cell_render_func, ppcity, NULL); - col = gtk_tree_view_get_column(view, i); - - if (GUI_GTK_OPTION(show_task_icons)) { - if (max_unit_width == -1 || max_unit_height == -1) { - update_max_unit_size(); - } - } else { - g_object_set(col, "visible", FALSE, NULL); - } - if (GUI_GTK_OPTION(show_task_icons)) { - g_object_set(rend, "height", max_unit_height, NULL); - } - - for (i = 1; i < ARRAY_SIZE(titles); i++) { - - gint pos = i-1; - - rend = gtk_cell_renderer_text_new(); - g_object_set_data(G_OBJECT(rend), "column", GINT_TO_POINTER(pos)); - - gtk_tree_view_insert_column_with_data_func(view, - i, titles[i], rend, cell_render_func, ppcity, NULL); - col = gtk_tree_view_get_column(view, i); - - if (pos >= 2) { - g_object_set(G_OBJECT(rend), "xalign", 1.0, NULL); - gtk_tree_view_column_set_alignment(col, 1.0); - } - - if (pos == 3) { - *pcol = col; - } - if (GUI_GTK_OPTION(show_task_icons)) { - g_object_set(rend, "height", max_unit_height, NULL); - } - } -} - -/************************************************************************//** - Worklist editor shell. -****************************************************************************/ -GtkWidget *create_worklist(void) -{ - GtkWidget *editor, *table, *sw, *bbox; - GtkWidget *src_view, *dst_view, *label, *button; - GtkWidget *menubar, *item, *menu, *image; - GtkWidget *table2, *arrow, *check; - GtkSizeGroup *group; - GtkListStore *src_store, *dst_store; - struct worklist_data *ptr; - - ptr = fc_malloc(sizeof(*ptr)); - - src_store = gtk_list_store_new(1, G_TYPE_INT); - dst_store = gtk_list_store_new(1, G_TYPE_INT); - - ptr->global_worklist_id = -1; - ptr->pcity = NULL; - ptr->src = src_store; - ptr->dst = dst_store; - ptr->future = FALSE; - - /* create shell. */ - editor = gtk_grid_new(); - gtk_grid_set_row_spacing(GTK_GRID(editor), 6); - gtk_orientable_set_orientation(GTK_ORIENTABLE(editor), - GTK_ORIENTATION_VERTICAL); - g_signal_connect(editor, "destroy", G_CALLBACK(worklist_destroy), ptr); - g_object_set_data(G_OBJECT(editor), "data", ptr); - - ptr->editor = editor; - - /* add source and target lists. */ - table = gtk_grid_new(); - gtk_container_add(GTK_CONTAINER(editor), table); - - group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_grid_attach(GTK_GRID(table), sw, 3, 1, 2, 1); - - src_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(src_store)); - gtk_widget_set_hexpand(src_view, TRUE); - gtk_widget_set_vexpand(src_view, TRUE); - g_object_unref(src_store); - gtk_size_group_add_widget(group, src_view); - gtk_widget_set_name(src_view, "small_font"); - - populate_view(GTK_TREE_VIEW(src_view), &ptr->pcity, &ptr->src_col); - gtk_container_add(GTK_CONTAINER(sw), src_view); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", src_view, - "label", _("Source _Tasks:"), - "xalign", 0.0, "yalign", 0.5, NULL); - gtk_grid_attach(GTK_GRID(table), label, 3, 0, 1, 1); - - check = gtk_check_button_new_with_mnemonic(_("Show _Future Targets")); - gtk_grid_attach(GTK_GRID(table), check, 4, 0, 1, 1); - g_signal_connect(check, "toggled", G_CALLBACK(future_callback), ptr); - - table2 = gtk_grid_new(); - gtk_grid_attach(GTK_GRID(table), table2, 2, 1, 1, 1); - - button = gtk_button_new(); - gtk_widget_set_margin_top(button, 24); - gtk_widget_set_margin_bottom(button, 24); - ptr->prepend_cmd = button; - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_grid_attach(GTK_GRID(table2), button, 0, 0, 1, 1); - - arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE); - gtk_container_add(GTK_CONTAINER(button), arrow); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(queue_prepend), ptr); - gtk_widget_set_sensitive(ptr->prepend_cmd, FALSE); - - button = gtk_button_new(); - ptr->up_cmd = button; - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_grid_attach(GTK_GRID(table2), button, 0, 1, 1, 1); - - arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE); - gtk_container_add(GTK_CONTAINER(button), arrow); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(queue_bubble_up), ptr); - gtk_widget_set_sensitive(ptr->up_cmd, FALSE); - - button = gtk_button_new(); - ptr->down_cmd = button; - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_grid_attach(GTK_GRID(table2), button, 0, 2, 1, 1); - - arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_IN); - gtk_container_add(GTK_CONTAINER(button), arrow); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(queue_bubble_down), ptr); - gtk_widget_set_sensitive(ptr->down_cmd, FALSE); - - button = gtk_button_new(); - gtk_widget_set_margin_top(button, 24); - gtk_widget_set_margin_bottom(button, 24); - ptr->append_cmd = button; - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_grid_attach(GTK_GRID(table2), button, 0, 3, 1, 1); - - arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE); - gtk_container_add(GTK_CONTAINER(button), arrow); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(queue_append), ptr); - gtk_widget_set_sensitive(ptr->append_cmd, FALSE); - - button = gtk_button_new(); - gtk_widget_set_margin_top(button, 24); - gtk_widget_set_margin_bottom(button, 24); - ptr->remove_cmd = button; - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_grid_attach(GTK_GRID(table2), button, 0, 4, 1, 1); - - arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_IN); - gtk_container_add(GTK_CONTAINER(button), arrow); - g_signal_connect_swapped(button, "clicked", - G_CALLBACK(queue_remove), ptr); - gtk_widget_set_sensitive(ptr->remove_cmd, FALSE); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), - GTK_SHADOW_ETCHED_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_grid_attach(GTK_GRID(table), sw, 0, 1, 2, 1); - - dst_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dst_store)); - gtk_widget_set_hexpand(dst_view, TRUE); - gtk_widget_set_vexpand(dst_view, TRUE); - g_object_unref(dst_store); - gtk_size_group_add_widget(group, dst_view); - gtk_widget_set_name(dst_view, "small_font"); - - populate_view(GTK_TREE_VIEW(dst_view), &ptr->pcity, &ptr->dst_col); - gtk_container_add(GTK_CONTAINER(sw), dst_view); - - label = g_object_new(GTK_TYPE_LABEL, - "use-underline", TRUE, - "mnemonic-widget", dst_view, - "label", _("Target _Worklist:"), - "xalign", 0.0, "yalign", 0.5, NULL); - gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); - - /* add bottom menu and buttons. */ - bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - gtk_box_set_spacing(GTK_BOX(bbox), 10); - gtk_container_add(GTK_CONTAINER(editor), bbox); - - menubar = gtk_aux_menu_bar_new(); - gtk_container_add(GTK_CONTAINER(bbox), menubar); - gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), menubar, TRUE); - - menu = gtk_menu_new(); - - image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); - item = gtk_image_menu_item_new_with_mnemonic(_("_Add Global Worklist")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); - g_signal_connect(menu, "show", - G_CALLBACK(popup_add_menu), ptr); - ptr->add_cmd = item; - gtk_widget_set_sensitive(ptr->add_cmd, FALSE); - - button = gtk_button_new_from_stock(GTK_STOCK_HELP); - gtk_container_add(GTK_CONTAINER(bbox), button); - g_signal_connect(button, "clicked", - G_CALLBACK(help_callback), ptr); - ptr->help_cmd = button; - gtk_widget_set_sensitive(ptr->help_cmd, FALSE); - - button = gtk_button_new_with_mnemonic(_("Change Prod_uction")); - gtk_container_add(GTK_CONTAINER(bbox), button); - g_signal_connect(button, "clicked", - G_CALLBACK(change_callback), ptr); - ptr->change_cmd = button; - gtk_widget_set_sensitive(ptr->change_cmd, FALSE); - - ptr->src_view = src_view; - ptr->dst_view = dst_view; - ptr->src_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(src_view)); - ptr->dst_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dst_view)); - gtk_tree_selection_set_mode(ptr->dst_selection, GTK_SELECTION_MULTIPLE); - - /* DND and other state changing callbacks. */ - gtk_tree_view_set_reorderable(GTK_TREE_VIEW(dst_view), TRUE); - g_signal_connect(dst_view, "drag_end", - G_CALLBACK(dst_dnd_callback), ptr); - - g_signal_connect(src_view, "row_activated", - G_CALLBACK(src_row_callback), ptr); - g_signal_connect(src_view, "key_press_event", - G_CALLBACK(src_key_press_callback), ptr); - - g_signal_connect(dst_view, "row_activated", - G_CALLBACK(dst_row_callback), ptr); - g_signal_connect(dst_view, "key_press_event", - G_CALLBACK(dst_key_press_callback), ptr); - - g_signal_connect(ptr->src_selection, "changed", - G_CALLBACK(src_selection_callback), ptr); - g_signal_connect(ptr->dst_selection, "changed", - G_CALLBACK(dst_selection_callback), ptr); - - - gtk_widget_show_all(table); - gtk_widget_show_all(bbox); - - return editor; -} - -/************************************************************************//** - Reset worklist for city -****************************************************************************/ -void reset_city_worklist(GtkWidget *editor, struct city *pcity) -{ - struct worklist_data *ptr; - - ptr = g_object_get_data(G_OBJECT(editor), "data"); - - ptr->global_worklist_id = -1; - ptr->pcity = pcity; - - gtk_list_store_clear(ptr->src); - gtk_list_store_clear(ptr->dst); - - g_object_set(ptr->src_col, "visible", TRUE, NULL); - g_object_set(ptr->dst_col, "visible", TRUE, NULL); - - gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(ptr->src_view), - GDK_BUTTON1_MASK, - wl_dnd_targets, - G_N_ELEMENTS(wl_dnd_targets), - GDK_ACTION_COPY); -} - -/************************************************************************//** - Reset one of the global worklists -****************************************************************************/ -static void reset_global_worklist(GtkWidget *editor, - struct global_worklist *pgwl) -{ - struct worklist_data *ptr; - - ptr = g_object_get_data(G_OBJECT(editor), "data"); - - ptr->global_worklist_id = global_worklist_id(pgwl); - ptr->pcity = NULL; - - gtk_list_store_clear(ptr->src); - gtk_list_store_clear(ptr->dst); - - gtk_widget_hide(ptr->change_cmd); - g_object_set(ptr->src_col, "visible", FALSE, NULL); - g_object_set(ptr->dst_col, "visible", FALSE, NULL); - - gtk_tree_view_unset_rows_drag_source(GTK_TREE_VIEW(ptr->src_view)); -} - -/************************************************************************//** - Refresh worklist info -****************************************************************************/ -void refresh_worklist(GtkWidget *editor) -{ - struct worklist_data *ptr; - struct worklist queue; - struct universal targets[MAX_NUM_PRODUCTION_TARGETS]; - int i, targets_used; - struct item items[MAX_NUM_PRODUCTION_TARGETS]; - bool selected; - gint id; - GtkTreeIter it; - GtkTreePath *path; - GtkTreeModel *model; - gboolean exists; - - ptr = g_object_get_data(G_OBJECT(editor), "data"); - - /* refresh source tasks. */ - if (gtk_tree_selection_get_selected(ptr->src_selection, NULL, &it)) { - gtk_tree_model_get(GTK_TREE_MODEL(ptr->src), &it, 0, &id, -1); - selected = TRUE; - } else { - selected = FALSE; - } - - /* These behave just right if ptr->pcity is NULL -> in case of global - * worklist. */ - targets_used = collect_eventually_buildable_targets(targets, ptr->pcity, - ptr->future); - name_and_sort_items(targets, targets_used, items, FALSE, ptr->pcity); - - /* Re-purpose existing items in the list store -- this avoids the - * UI jumping around (especially as the set of source tasks doesn't - * actually change much in practice). */ - model = GTK_TREE_MODEL(ptr->src); - exists = gtk_tree_model_get_iter_first(model, &it); - - path = NULL; - for (i = 0; i < targets_used; i++) { - if (!exists) { - gtk_list_store_append(ptr->src, &it); - } - - gtk_list_store_set(ptr->src, &it, 0, (gint) cid_encode(items[i].item), -1); - - if (selected && cid_encode(items[i].item) == id) { - path = gtk_tree_model_get_path(GTK_TREE_MODEL(ptr->src), &it); - } - - if (exists) { - exists = gtk_tree_model_iter_next(model, &it); - } - } - - /* If the list got shorter, delete any excess items. */ - if (exists) { - GtkTreeIter it_next; - bool more; - - do { - it_next = it; - more = gtk_tree_model_iter_next(model, &it_next); - - gtk_list_store_remove(ptr->src, &it); - it = it_next; - } while (more); - } - - /* Select the same item that was previously selected, if any. */ - if (path) { - gtk_tree_view_set_cursor(GTK_TREE_VIEW(ptr->src_view), path, NULL, FALSE); - gtk_tree_path_free(path); - } - - /* refresh target worklist. */ - model = GTK_TREE_MODEL(ptr->dst); - exists = gtk_tree_model_get_iter_first(model, &it); - - /* dance around worklist braindamage. */ - if (ptr->pcity != NULL) { - city_get_queue(ptr->pcity, &queue); - } else { - const struct global_worklist *pgwl; - - pgwl = global_worklist_by_id(ptr->global_worklist_id); - - fc_assert(NULL != pgwl); - - worklist_copy(&queue, global_worklist_get(pgwl)); - } - - for (i = 0; i < worklist_length(&queue); i++) { - struct universal target = queue.entries[i]; - - if (!exists) { - gtk_list_store_append(ptr->dst, &it); - } - - gtk_list_store_set(ptr->dst, &it, 0, (gint) cid_encode(target), -1); - - if (exists) { - exists = gtk_tree_model_iter_next(model, &it); - } - } - - if (exists) { - GtkTreeIter it_next; - bool more; - - do { - it_next = it; - more = gtk_tree_model_iter_next(model, &it_next); - - gtk_list_store_remove(ptr->dst, &it); - it = it_next; - } while (more); - } - - /* update widget sensitivity. */ - if (ptr->pcity) { - if ((can_client_issue_orders() - && city_owner(ptr->pcity) == client.conn.playing)) { - gtk_widget_set_sensitive(ptr->add_cmd, TRUE); - gtk_widget_set_sensitive(ptr->dst_view, TRUE); - } else { - gtk_widget_set_sensitive(ptr->add_cmd, FALSE); - gtk_widget_set_sensitive(ptr->dst_view, FALSE); - } - } else { - gtk_widget_set_sensitive(ptr->add_cmd, TRUE); - gtk_widget_set_sensitive(ptr->dst_view, TRUE); - } -} - -/************************************************************************//** - Commit worklist data to worklist -****************************************************************************/ -static void commit_worklist(struct worklist_data *ptr) -{ - struct worklist queue; - GtkTreeModel *model; - GtkTreeIter it; - size_t i; - - model = GTK_TREE_MODEL(ptr->dst); - - worklist_init(&queue); - - i = 0; - if (gtk_tree_model_get_iter_first(model, &it)) { - do { - gint id; - struct universal univ; - - /* oops, the player has a worklist longer than what we can store. */ - if (i >= MAX_LEN_WORKLIST) { - break; - } - - gtk_tree_model_get(model, &it, 0, &id, -1); - univ = cid_production(id); - worklist_append(&queue, &univ); - - i++; - } while (gtk_tree_model_iter_next(model, &it)); - } - - /* dance around worklist braindamage. */ - if (ptr->pcity) { - if (!city_set_queue(ptr->pcity, &queue)) { - /* Failed to change worklist. This means worklist visible - * on screen is not true. */ - refresh_worklist(ptr->editor); - } - } else { - struct global_worklist *pgwl; - - pgwl = global_worklist_by_id(ptr->global_worklist_id); - if (pgwl) { - global_worklist_set(pgwl, &queue); - } - } -} diff --git a/client/gui-gtk-3.0/wldlg.h b/client/gui-gtk-3.0/wldlg.h deleted file mode 100644 index 2ee9e2978e..0000000000 --- a/client/gui-gtk-3.0/wldlg.h +++ /dev/null @@ -1,40 +0,0 @@ -/********************************************************************** - Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -***********************************************************************/ -#ifndef FC__WLDLG_H -#define FC__WLDLG_H - -#include - -/* common */ -#include "improvement.h" -#include "unittype.h" -#include "worklist.h" - -/* client */ -#include "climisc.h" - -#include "wldlg_g.h" - -/* the global worklist view. */ -void popup_worklists_report(void); - -/* an individual worklist. */ -GtkWidget *create_worklist(void); -void reset_city_worklist(GtkWidget *editor, struct city *pcity); -void refresh_worklist(GtkWidget *editor); - -void add_worklist_dnd_target(GtkWidget *w); - -void blank_max_unit_size(void); - -#endif /* FC__WLDLG_H */ diff --git a/configure.ac b/configure.ac index f15ec9ec39..e445c67fed 100644 --- a/configure.ac +++ b/configure.ac @@ -439,7 +439,7 @@ dnl auto: Autodetect one. dnl all: Autodetect as many as possible. dnl comma-separated-list: Detect these or abort. AC_ARG_ENABLE([client], - AS_HELP_STRING([--enable-client=auto/all/gtk3/gtk3.22/sdl2/qt/stub], + AS_HELP_STRING([--enable-client=auto/all/gtk3.22/sdl2/qt/stub], [clients to compile [auto](list for multiple)]), [clients=${enableval}], [client=auto]) @@ -448,7 +448,6 @@ if test "x$fcweb" = "xtrue" && test "x$client" = "xauto" ; then clients=no fi -gui_gtk3=no gui_gtk3_22=no gui_gtk3x=no gui_sdl2=no @@ -463,12 +462,7 @@ for gui in $(echo $clients | $SED 's/,/ /g') ; do elif test "x$gui" = "xall" ; then client=all else - if test "x$gui" = "xgtk3" || - test "x$gui" = "xgtk3.0" || - test "x$gui" = "xgtk30" || - test "x$gui" = "xgtk-3.0" ; then - gui_gtk3=yes - elif test "x$gui" = "xgtk3.22" ; then + if test "x$gui" = "xgtk3.22" ; then gui_gtk3_22=yes elif test "x$gui" = "xgtk3x" ; then gui_gtk3x=yes @@ -1048,9 +1042,6 @@ if test "x$client" != "xno"; then dnl Gtk-3.22-specific overrides FC_GTK3_22_CLIENT - dnl Gtk-3.0-specific overrides - FC_GTK3_CLIENT - dnl QT-specific overrides FC_QT_CLIENT @@ -1076,8 +1067,7 @@ if test "x$client" != "xno"; then fi if test "x$client" = "xall" ; then - if test "x$gui_gtk3" = "xyes" || - test "x$gui_gtk3_22" = "xyes" || + if test "x$gui_gtk3_22" = "xyes" || test "x$gui_gtk3x" = "xyes" || test "x$gui_sdl2" = "xyes" || test "x$gui_qt" = "xyes" || @@ -1313,9 +1303,6 @@ AM_CONDITIONAL([SRV_LIB], [test "x$server" = "xyes" || test "x$fcmanual" = "xyes" || test "x$ruledit" = "xyes" || test "x$fcruleup" = "xyes"]) AC_SUBST([gui_3d_libs]) -AC_SUBST([gui_gtk3_cflags]) -AC_SUBST([gui_gtk3_libs]) -AC_SUBST([gui_gtk3_ldflags]) AC_SUBST([gui_gtk3_22_cflags]) AC_SUBST([gui_gtk3_22_libs]) AC_SUBST([gui_gtk3_22_ldflags]) @@ -1356,7 +1343,6 @@ AC_SUBST([HOST_DIR_SEPARATOR]) AC_SUBST([FREECIV_STORAGE_DIR]) AM_CONDITIONAL(AUDIO_SDL, test "x$SDL_mixer" != "xno") AM_CONDITIONAL(CLIENT_GUI_SDL2, test "x$gui_sdl2" = "xyes") -AM_CONDITIONAL(CLIENT_GUI_GTK_3_0, test "x$gui_gtk3" = "xyes") AM_CONDITIONAL(CLIENT_GUI_GTK_3_22, test "x$gui_gtk3_22" = "xyes") AM_CONDITIONAL(CLIENT_GUI_GTK_3X, test "x$gui_gtk3x" = "xyes") AM_CONDITIONAL(CLIENT_GUI_QT, test "x$gui_qt" = "xyes") @@ -1719,9 +1705,6 @@ AC_CONFIG_FILES([Makefile data/nation/Makefile data/ruledit/Makefile data/themes/Makefile - data/themes/gtk3/Makefile - data/themes/gtk3/Freeciv/Makefile - data/themes/gtk3/Freeciv/gtk-3.0/Makefile data/themes/gtk3.22/Makefile data/themes/gtk3.22/Freeciv/Makefile data/themes/gtk3.22/Freeciv/gtk-3.0/Makefile @@ -1801,7 +1784,6 @@ AC_CONFIG_FILES([Makefile doc/sv/Makefile bootstrap/Makefile lua/Makefile - client/org.freeciv.gtk3.desktop:bootstrap/org.freeciv.gtk3.desktop.in client/org.freeciv.gtk322.desktop:bootstrap/org.freeciv.gtk322.desktop.in client/org.freeciv.sdl2.desktop:bootstrap/org.freeciv.sdl2.desktop.in client/org.freeciv.qt.desktop:bootstrap/org.freeciv.qt.desktop.in @@ -1810,7 +1792,6 @@ AC_CONFIG_FILES([Makefile tools/fcmp/org.freeciv.mp.gtk4.desktop:bootstrap/org.freeciv.mp.gtk4.desktop.in tools/fcmp/org.freeciv.mp.qt.desktop:bootstrap/org.freeciv.mp.qt.desktop.in tools/ruledit/org.freeciv.ruledit.desktop:bootstrap/org.freeciv.ruledit.desktop.in - client/freeciv-gtk3.appdata.xml:bootstrap/freeciv-gtk3.appdata.xml.in client/freeciv-gtk3.22.appdata.xml:bootstrap/freeciv-gtk3.22.appdata.xml.in client/freeciv-sdl2.appdata.xml:bootstrap/freeciv-sdl2.appdata.xml.in client/freeciv-qt.appdata.xml:bootstrap/freeciv-qt.appdata.xml.in @@ -1860,7 +1841,6 @@ AC_MSG_NOTICE([ Maintained client frontends: Gtk-3.22 $gui_gtk3_22 - Gtk-3.0: $gui_gtk3 SDL2: $gui_sdl2 QT: $gui_qt Stub: $gui_stub diff --git a/data/Makefile.am b/data/Makefile.am index f0814e6082..ffedf9fa74 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -14,10 +14,6 @@ CLIENT_FILES = \ stdmusic.musicspec \ stdsounds.soundspec \ helpdata.txt -if CLIENT_GUI_GTK_3_0 -CLIENT_FILES += \ - gtk3_menus.xml -endif if CLIENT_GUI_GTK_3_22 CLIENT_FILES += \ gtk3.22_menus.xml diff --git a/data/gtk3_menus.xml b/data/gtk3_menus.xml deleted file mode 100644 index 676b4846b4..0000000000 --- a/data/gtk3_menus.xml +++ /dev/null @@ -1,577 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/themes/Makefile.am b/data/themes/Makefile.am index db5629d7e4..4d672df1c5 100644 --- a/data/themes/Makefile.am +++ b/data/themes/Makefile.am @@ -3,9 +3,6 @@ SUBDIRS = -if CLIENT_GUI_GTK_3_0 -SUBDIRS += gtk3 -endif if CLIENT_GUI_GTK_3_22 SUBDIRS += gtk3.22 endif diff --git a/data/themes/gtk3/.gitignore b/data/themes/gtk3/.gitignore deleted file mode 100644 index dc1ebd209a..0000000000 --- a/data/themes/gtk3/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/Makefile -/Makefile.in -/.deps diff --git a/data/themes/gtk3/Freeciv/.gitignore b/data/themes/gtk3/Freeciv/.gitignore deleted file mode 100644 index dc1ebd209a..0000000000 --- a/data/themes/gtk3/Freeciv/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/Makefile -/Makefile.in -/.deps diff --git a/data/themes/gtk3/Freeciv/Makefile.am b/data/themes/gtk3/Freeciv/Makefile.am deleted file mode 100644 index 2799ebb111..0000000000 --- a/data/themes/gtk3/Freeciv/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -## Process this file with automake to produce Makefile.in - -SUBDIRS = gtk-3.0 diff --git a/data/themes/gtk3/Freeciv/gtk-3.0/.gitignore b/data/themes/gtk3/Freeciv/gtk-3.0/.gitignore deleted file mode 100644 index dc1ebd209a..0000000000 --- a/data/themes/gtk3/Freeciv/gtk-3.0/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/Makefile -/Makefile.in -/.deps diff --git a/data/themes/gtk3/Freeciv/gtk-3.0/Makefile.am b/data/themes/gtk3/Freeciv/gtk-3.0/Makefile.am deleted file mode 100644 index 3ec65a845f..0000000000 --- a/data/themes/gtk3/Freeciv/gtk-3.0/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -## Process this file with automake to produce Makefile.in - -## Override automake so that "make install" puts these in proper place: -pkgdatadir = $(datadir)/$(PACKAGE)/themes/gtk3/Freeciv/gtk-3.0 - -pkgdata_DATA = \ - bg.png \ - gtk.css \ - menubar.css - -EXTRA_DIST = $(pkgdata_DATA) diff --git a/data/themes/gtk3/Freeciv/gtk-3.0/bg.png b/data/themes/gtk3/Freeciv/gtk-3.0/bg.png deleted file mode 100644 index efd4bf9663a926ff73d46b43739f5c4028d262b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 657164 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%0Anb(h*!uJ3d{ey_UNPcD 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�UuA}L^+nCj5a=` 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

`4o$5eVc11a80?hb>6YBB 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

!u2| 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 -a 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

d~3>e(oo(bFj?eXTOhQ&LJ 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 3.10.0], - [ - GTK3_CFLAGS="$GTK3_CFLAGS -DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_8 -DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_3_10" - GTK3_CFLAGS="$GTK3_CFLAGS -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36" - gui_gtk3=yes - if test "x$client" = "xauto" ; then - client=yes - fi - gui_gtk3_cflags="$GTK3_CFLAGS" - gui_gtk3_libs="$GTK3_LIBS" - if test "x$MINGW" = "xyes"; then - dnl Required to compile gtk3 on Windows platform - gui_gtk3_cflags="$gui_gtk3_cflags -mms-bitfields" - gui_gtk3_ldflags="$gui_gtk3_ldflags $MWINDOWS_FLAG" - fi - ], - [ - FC_NO_CLIENT([gtk3], [GTK+-3.0 libraries not found]) - ]) - fi -]) diff --git a/translations/core/POTFILES.in b/translations/core/POTFILES.in index 040595aa2a..0603de5538 100644 --- a/translations/core/POTFILES.in +++ b/translations/core/POTFILES.in @@ -20,45 +20,6 @@ client/connectdlg_common.c client/control.c client/editor.c client/global_worklist.c -client/gui-gtk-3.0/action_dialog.c -client/gui-gtk-3.0/chatline.c -client/gui-gtk-3.0/citizensinfo.c -client/gui-gtk-3.0/citydlg.c -client/gui-gtk-3.0/cityrep.c -client/gui-gtk-3.0/cma_fe.c -client/gui-gtk-3.0/dialogs.c -client/gui-gtk-3.0/diplodlg.c -client/gui-gtk-3.0/editgui.c -client/gui-gtk-3.0/editprop.c -client/gui-gtk-3.0/finddlg.c -client/gui-gtk-3.0/gamedlgs.c -client/gui-gtk-3.0/gotodlg.c -client/gui-gtk-3.0/gui_main.c -client/gui-gtk-3.0/gui_stuff.c -client/gui-gtk-3.0/happiness.c -client/gui-gtk-3.0/helpdlg.c -client/gui-gtk-3.0/inteldlg.c -client/gui-gtk-3.0/luaconsole.c -client/gui-gtk-3.0/mapctrl.c -client/gui-gtk-3.0/mapview.c -client/gui-gtk-3.0/menu.c -client/gui-gtk-3.0/messagedlg.c -client/gui-gtk-3.0/messagewin.c -client/gui-gtk-3.0/optiondlg.c -client/gui-gtk-3.0/pages.c -client/gui-gtk-3.0/plrdlg.c -client/gui-gtk-3.0/repodlgs.c -client/gui-gtk-3.0/soundset_dlg.c -client/gui-gtk-3.0/spaceshipdlg.c -client/gui-gtk-3.0/sprite.c -client/gui-gtk-3.0/theme_dlg.c -client/gui-gtk-3.0/tileset_dlg.c -client/gui-gtk-3.0/transportdlg.c -client/gui-gtk-3.0/unitselect.c -client/gui-gtk-3.0/unitselextradlg.c -client/gui-gtk-3.0/unitselunitdlg.c -client/gui-gtk-3.0/voteinfo_bar.c -client/gui-gtk-3.0/wldlg.c client/gui-gtk-3.22/action_dialog.c client/gui-gtk-3.22/chatline.c client/gui-gtk-3.22/citizensinfo.c diff --git a/windows/installer_legacy/Makefile b/windows/installer_legacy/Makefile index 1ef48ba58a..56290b971d 100644 --- a/windows/installer_legacy/Makefile +++ b/windows/installer_legacy/Makefile @@ -33,10 +33,7 @@ endif # by default build all installers -all: gtk3-installer sdl2-installer qt-installer ruledit-installer - -gtk3-installer: - make GUI=gtk3 FCMP=gtk3 EXTRA_CONFIG="--disable-ruledit $(EXTRA_CONFIG)" wrap-gtk3 +all: sdl2-installer qt-installer ruledit-installer sdl2-installer: make GUI=sdl2 FCMP=gtk3 EXTRA_CONFIG="--disable-ruledit $(EXTRA_CONFIG)" wrap-sdl2 @@ -74,8 +71,6 @@ build-freeciv-ruledit: make -C build-ruledit/translations/ruledit update-po make -C build-ruledit/bootstrap langstat_ruledit.txt -build-freeciv-gtk3: build-freeciv-common - build-freeciv-sdl2: build-freeciv-common build-freeciv-qt: build-freeciv-common @@ -103,8 +98,6 @@ install-freeciv-common: # add start menu files cp freeciv-server.cmd freeciv-mp-$(FCMP).cmd freeciv-$(GUI).cmd Freeciv.url install-$(GUI)/ -install-freeciv-gtk3: install-freeciv-common - install-freeciv-sdl2: install-freeciv-common # add CJK fonts cp /opt/fireflysung-1.3.0/fireflysung.ttf install-sdl2/data/themes/gui-sdl2/human/ @@ -265,8 +258,6 @@ installer-ruledit: clean-install-ruledit install-ruledit install-env-ruledit mkdir -p Output makensis Freeciv-$(FREECIV_VERSION)-ruledit.nsi -wrap-gtk3: build-freeciv-gtk3 installer-common - wrap-sdl2: build-freeciv-sdl2 installer-common wrap-qt: build-freeciv-qt installer-common @@ -299,7 +290,6 @@ clean-installer-ruledit: rm -f Freeciv-*-ruledit.nsi clean: - make GUI=gtk3 clean-build-common clean-install-common clean-installer-common make GUI=sdl2 clean-build-common clean-install-common clean-installer-common make GUI=qt clean-build-common clean-install-common clean-installer-common make clean-build-ruledit clean-install-ruledit clean-installer-ruledit -- 2.30.0