forked from Mirrors/openclonk
Remove the GTK platform (USE_GTK)
The GTK code does not work with the Qt editor. The simpler SDL2 platform does, so we have little reason to keep the GTK code. Note that GTK is still a dependency for mape. Discussion: http://forum.openclonk.org/topic_show.pl?tid=3328qteditor
parent
c12c51b44d
commit
b7359e0c27
|
@ -46,7 +46,6 @@ option(PROJECT_FOLDERS "Put source files into subfolders in project file" ON)
|
||||||
CMAKE_DEPENDENT_OPTION(USE_COCOA "Use Apple Cocoa widgets." ON "APPLE" OFF)
|
CMAKE_DEPENDENT_OPTION(USE_COCOA "Use Apple Cocoa widgets." ON "APPLE" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(USE_WIN32_WINDOWS "Use Microsoft Desktop App User Interface widgets." ON "WIN32" OFF)
|
CMAKE_DEPENDENT_OPTION(USE_WIN32_WINDOWS "Use Microsoft Desktop App User Interface widgets." ON "WIN32" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(USE_SDL_MAINLOOP "Use SDL to create windows etc. Qt editor." ON "NOT USE_COCOA AND NOT USE_WIN32_WINDOWS" OFF)
|
CMAKE_DEPENDENT_OPTION(USE_SDL_MAINLOOP "Use SDL to create windows etc. Qt editor." ON "NOT USE_COCOA AND NOT USE_WIN32_WINDOWS" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(USE_GTK "Use GTK+ widgets." ON "NOT USE_COCOA AND NOT USE_WIN32_WINDOWS AND NOT USE_SDL_MAINLOOP" OFF)
|
|
||||||
option(WITH_AUTOMATIC_UPDATE "Automatic updates are downloaded from the project website." OFF)
|
option(WITH_AUTOMATIC_UPDATE "Automatic updates are downloaded from the project website." OFF)
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ${PROJECT_FOLDERS})
|
set_property(GLOBAL PROPERTY USE_FOLDERS ${PROJECT_FOLDERS})
|
||||||
|
@ -298,9 +297,6 @@ CHECK_CXX_SOURCE_COMPILES("#include <GL/glew.h>\nvoid GLAPIENTRY OpenGLDebugProc
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
FINDLIB(X11_LIBRARIES X11)
|
FINDLIB(X11_LIBRARIES X11)
|
||||||
if(USE_GTK)
|
|
||||||
FINDLIB(XRANDR_LIBRARIES Xrandr)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
@ -323,13 +319,6 @@ endif()
|
||||||
SET(HAVE_LIBREADLINE ${READLINE_FOUND} CACHE INTERNAL "libreadline available")
|
SET(HAVE_LIBREADLINE ${READLINE_FOUND} CACHE INTERNAL "libreadline available")
|
||||||
|
|
||||||
find_package(GTK3 COMPONENTS gthread gio gobject glib OPTIONAL_COMPONENTS gtksourceview)
|
find_package(GTK3 COMPONENTS gthread gio gobject glib OPTIONAL_COMPONENTS gtksourceview)
|
||||||
if(USE_GTK)
|
|
||||||
SET(WITH_GLIB ${GTK3_FOUND})
|
|
||||||
include_directories(SYSTEM ${GTK3_INCLUDE_DIRS})
|
|
||||||
# Set GTK link directory. This needs to be done before add_executable,
|
|
||||||
# otherwise the path is not used for linking clonk
|
|
||||||
link_directories(${GTK3_LIBRARY_DIRS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Select an audio library
|
# Select an audio library
|
||||||
find_package("Audio")
|
find_package("Audio")
|
||||||
|
@ -384,31 +373,6 @@ add_custom_command(
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT engine-resource.c
|
|
||||||
COMMAND
|
|
||||||
${GLIB_COMPILE_RESOURCES} "--internal" "--generate"
|
|
||||||
"--target" "engine-resource.c"
|
|
||||||
"--sourcedir" ${CMAKE_CURRENT_SOURCE_DIR}/src/res
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/res/engine.xml"
|
|
||||||
MAIN_DEPENDENCY src/res/engine.xml
|
|
||||||
DEPENDS
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Brush_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Halt_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Picker_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Cursor_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Play_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Dynamic_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Line_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Rect_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Exact_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Mouse_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Static_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/Fill_Trans.png
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/res/oc.ico
|
|
||||||
VERBATIM
|
|
||||||
)
|
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# Mac OS bundle setup
|
# Mac OS bundle setup
|
||||||
############################################################################
|
############################################################################
|
||||||
|
@ -966,16 +930,7 @@ elseif(UPNP_STYLE STREQUAL "miniupnpc")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# source files specific to a GUI library
|
# source files specific to a GUI library
|
||||||
if(USE_GTK)
|
if(USE_SDL_MAINLOOP)
|
||||||
list(APPEND OC_GUI_SOURCES
|
|
||||||
src/editor/C4ConsoleGTK.cpp
|
|
||||||
src/editor/C4ConsoleGTKDlg.cpp
|
|
||||||
src/editor/C4ConsoleGTKDlg.h
|
|
||||||
src/platform/C4AppGTK.cpp
|
|
||||||
src/platform/C4WindowGTK.cpp
|
|
||||||
engine-resource.c
|
|
||||||
)
|
|
||||||
elseif(USE_SDL_MAINLOOP)
|
|
||||||
list(APPEND OC_GUI_SOURCES
|
list(APPEND OC_GUI_SOURCES
|
||||||
src/platform/C4AppSDL.cpp
|
src/platform/C4AppSDL.cpp
|
||||||
src/platform/C4WindowSDL.cpp
|
src/platform/C4WindowSDL.cpp
|
||||||
|
@ -1188,9 +1143,6 @@ target_link_libraries(openclonk
|
||||||
libc4script
|
libc4script
|
||||||
libmisc
|
libmisc
|
||||||
)
|
)
|
||||||
if(USE_GTK)
|
|
||||||
target_link_libraries(openclonk ${GTK3_LIBRARIES})
|
|
||||||
endif()
|
|
||||||
if(USE_COCOA)
|
if(USE_COCOA)
|
||||||
target_link_libraries(openclonk "-framework Cocoa -framework AppKit -framework Quartz -framework OpenAL -framework AudioToolbox")
|
target_link_libraries(openclonk "-framework Cocoa -framework AppKit -framework Quartz -framework OpenAL -framework AudioToolbox")
|
||||||
endif()
|
endif()
|
||||||
|
@ -1217,7 +1169,6 @@ target_link_libraries(openclonk-server
|
||||||
${READLINE_LIBRARIES}
|
${READLINE_LIBRARIES}
|
||||||
${Audio_LIBRARIES}
|
${Audio_LIBRARIES}
|
||||||
${GETOPT_LIBRARIES}
|
${GETOPT_LIBRARIES}
|
||||||
${GTK3_glib_LIBRARIES}
|
|
||||||
${TinyXML_LIBRARIES}
|
${TinyXML_LIBRARIES}
|
||||||
${DBGHELP_LIBRARIES}
|
${DBGHELP_LIBRARIES}
|
||||||
${UPNP_LIBRARIES}
|
${UPNP_LIBRARIES}
|
||||||
|
|
|
@ -583,7 +583,7 @@ void C4Console::RegisterRecentInput(const char *input, RecentScriptInputLists se
|
||||||
mru.erase(mru.begin());
|
mru.erase(mru.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !(defined(USE_WIN32_WINDOWS) || defined(USE_COCOA) || defined(USE_GTK) || defined(WITH_QT_EDITOR))
|
#if !(defined(USE_WIN32_WINDOWS) || defined(USE_COCOA) || defined(WITH_QT_EDITOR))
|
||||||
class C4ConsoleGUI::State: public C4ConsoleGUI::InternalState<class C4ConsoleGUI>
|
class C4ConsoleGUI::State: public C4ConsoleGUI::InternalState<class C4ConsoleGUI>
|
||||||
{
|
{
|
||||||
public: State(C4ConsoleGUI *console): Super(console) {}
|
public: State(C4ConsoleGUI *console): Super(console) {}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,127 +0,0 @@
|
||||||
/*
|
|
||||||
* OpenClonk, http://www.openclonk.org
|
|
||||||
*
|
|
||||||
* Copyright (c) 2007-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
||||||
* Copyright (c) 2010-2016, The OpenClonk Team and contributors
|
|
||||||
*
|
|
||||||
* Distributed under the terms of the ISC license; see accompanying file
|
|
||||||
* "COPYING" for details.
|
|
||||||
*
|
|
||||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
||||||
* See accompanying file "TRADEMARK" for details.
|
|
||||||
*
|
|
||||||
* To redistribute this file separately, substitute the full license texts
|
|
||||||
* for the above references.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Common window for drawing and property tool dialogs in console mode */
|
|
||||||
|
|
||||||
#include "C4Include.h"
|
|
||||||
#include "editor/C4ConsoleGTKDlg.h"
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
|
|
||||||
GtkWidget* C4DevmodeDlg::window = NULL;
|
|
||||||
GtkWidget* C4DevmodeDlg::notebook = NULL;
|
|
||||||
|
|
||||||
int C4DevmodeDlg::x = -1;
|
|
||||||
int C4DevmodeDlg::y = -1;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
gboolean OnDeleteEvent(GtkWidget* widget, gpointer user_data)
|
|
||||||
{
|
|
||||||
// Just hide the window, don't destroy it
|
|
||||||
C4DevmodeDlg::SwitchPage(NULL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4DevmodeDlg::OnDestroy(GtkWidget* window, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4DevmodeDlg::window = NULL;
|
|
||||||
C4DevmodeDlg::notebook = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4DevmodeDlg::AddPage(GtkWidget* widget, GtkWindow* parent, const char* title)
|
|
||||||
{
|
|
||||||
// Create Window if necessary
|
|
||||||
if (window == NULL)
|
|
||||||
{
|
|
||||||
notebook = gtk_notebook_new();
|
|
||||||
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), false);
|
|
||||||
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), false);
|
|
||||||
gtk_widget_show(GTK_WIDGET(notebook));
|
|
||||||
|
|
||||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
||||||
gtk_container_add(GTK_CONTAINER(window), notebook);
|
|
||||||
|
|
||||||
gtk_window_set_resizable(GTK_WINDOW(window), true);
|
|
||||||
gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_UTILITY);
|
|
||||||
gtk_window_set_default_size(GTK_WINDOW(window), 320, 320);
|
|
||||||
gtk_window_set_role(GTK_WINDOW(window), "toolbox");
|
|
||||||
|
|
||||||
gtk_window_set_transient_for(GTK_WINDOW(window), parent);
|
|
||||||
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT);
|
|
||||||
|
|
||||||
g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(OnDeleteEvent), NULL);
|
|
||||||
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(OnDestroy), NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add page to notebook
|
|
||||||
GtkWidget* label = gtk_label_new(title);
|
|
||||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4DevmodeDlg::RemovePage(GtkWidget* widget)
|
|
||||||
{
|
|
||||||
int page_num = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), widget);
|
|
||||||
assert(page_num != -1); // Make sure it is contained
|
|
||||||
|
|
||||||
gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), page_num);
|
|
||||||
|
|
||||||
if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)) == 0)
|
|
||||||
gtk_widget_destroy(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4DevmodeDlg::SwitchPage(GtkWidget* widget)
|
|
||||||
{
|
|
||||||
bool is_visible = gtk_widget_get_visible(GTK_WIDGET(window));
|
|
||||||
|
|
||||||
// Remember window position
|
|
||||||
if (window != NULL && is_visible)
|
|
||||||
gtk_window_get_position(GTK_WINDOW(window), &x, &y);
|
|
||||||
|
|
||||||
if (widget != NULL)
|
|
||||||
{
|
|
||||||
assert(window != NULL);
|
|
||||||
|
|
||||||
// Show required page
|
|
||||||
int page_num = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), widget);
|
|
||||||
assert(page_num != -1); // Make sure it is contained
|
|
||||||
|
|
||||||
gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page_num);
|
|
||||||
gtk_window_set_title(GTK_WINDOW(window), gtk_notebook_get_tab_label_text(GTK_NOTEBOOK(notebook), widget));
|
|
||||||
|
|
||||||
// Show window if not visible
|
|
||||||
if (!is_visible)
|
|
||||||
{
|
|
||||||
gtk_widget_show(window);
|
|
||||||
if (x != -1 || y != -1)
|
|
||||||
gtk_window_move(GTK_WINDOW(window), x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (window != NULL && is_visible)
|
|
||||||
gtk_widget_hide(window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4DevmodeDlg::SetTitle(GtkWidget* widget, const char* title)
|
|
||||||
{
|
|
||||||
gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(notebook), widget, title);
|
|
||||||
int page_num = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
|
|
||||||
if (gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num) == widget)
|
|
||||||
gtk_window_set_title(GTK_WINDOW(window), title);
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* OpenClonk, http://www.openclonk.org
|
|
||||||
*
|
|
||||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
||||||
* Copyright (c) 2013-2016, The OpenClonk Team and contributors
|
|
||||||
*
|
|
||||||
* Distributed under the terms of the ISC license; see accompanying file
|
|
||||||
* "COPYING" for details.
|
|
||||||
*
|
|
||||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
||||||
* See accompanying file "TRADEMARK" for details.
|
|
||||||
*
|
|
||||||
* To redistribute this file separately, substitute the full license texts
|
|
||||||
* for the above references.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Common window for drawing and property tool dialogs in console mode */
|
|
||||||
|
|
||||||
#ifndef INC_C4DevmodeDlg
|
|
||||||
#define INC_C4DevmodeDlg
|
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#endif // USE_GTK
|
|
||||||
|
|
||||||
// TODO: Threadsafety?
|
|
||||||
class C4DevmodeDlg
|
|
||||||
{
|
|
||||||
// Make sure all developer tools are held in the same window
|
|
||||||
#ifdef USE_GTK
|
|
||||||
private:
|
|
||||||
static GtkWidget* window;
|
|
||||||
static GtkWidget* notebook;
|
|
||||||
|
|
||||||
static int x, y;
|
|
||||||
|
|
||||||
static void OnDestroy(GtkWidget* widget, gpointer user_data);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static GtkWidget* GetWindow() { return window; }
|
|
||||||
static void AddPage(GtkWidget* widget, GtkWindow* parent, const char* title);
|
|
||||||
static void RemovePage(GtkWidget* widget);
|
|
||||||
static void SwitchPage(GtkWidget* widget);
|
|
||||||
|
|
||||||
static void SetTitle(GtkWidget* widget, const char* title);
|
|
||||||
#endif // USE_GTK
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //INC_C4DevmodeDlg
|
|
|
@ -41,9 +41,6 @@
|
||||||
#include "res/resource.h"
|
#include "res/resource.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
StdStrBuf C4EditCursorSelection::GetDataString() const
|
StdStrBuf C4EditCursorSelection::GetDataString() const
|
||||||
{
|
{
|
||||||
|
@ -176,22 +173,6 @@ bool C4EditCursor::Init()
|
||||||
#ifdef USE_WIN32_WINDOWS
|
#ifdef USE_WIN32_WINDOWS
|
||||||
if (!(hMenu = LoadMenu(Application.GetInstance(),MAKEINTRESOURCE(IDR_CONTEXTMENUS))))
|
if (!(hMenu = LoadMenu(Application.GetInstance(),MAKEINTRESOURCE(IDR_CONTEXTMENUS))))
|
||||||
return false;
|
return false;
|
||||||
#elif defined(USE_GTK)
|
|
||||||
menuContext = gtk_menu_new();
|
|
||||||
|
|
||||||
itemDelete = gtk_menu_item_new_with_label(LoadResStr("IDS_MNU_DELETE"));
|
|
||||||
itemDuplicate = gtk_menu_item_new_with_label(LoadResStr("IDS_MNU_DUPLICATE"));
|
|
||||||
itemGrabContents = gtk_menu_item_new_with_label(LoadResStr("IDS_MNU_CONTENTS"));
|
|
||||||
|
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(menuContext), itemDelete);
|
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(menuContext), itemDuplicate);
|
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(menuContext), itemGrabContents);
|
|
||||||
|
|
||||||
g_signal_connect(G_OBJECT(itemDelete), "activate", G_CALLBACK(OnDelete), this);
|
|
||||||
g_signal_connect(G_OBJECT(itemDuplicate), "activate", G_CALLBACK(OnDuplicate), this);
|
|
||||||
g_signal_connect(G_OBJECT(itemGrabContents), "activate", G_CALLBACK(OnGrabContents), this);
|
|
||||||
|
|
||||||
gtk_widget_show_all(menuContext);
|
|
||||||
#endif
|
#endif
|
||||||
Console.UpdateModeCtrls(Mode);
|
Console.UpdateModeCtrls(Mode);
|
||||||
|
|
||||||
|
@ -1142,16 +1123,6 @@ void C4EditCursor::AppendMenuItem(int num, const StdStrBuf & label)
|
||||||
AppendMenu(GetSubMenu(hMenu,0), MF_STRING, IDM_VPORTDYN_FIRST + num, label.GetWideChar());
|
AppendMenu(GetSubMenu(hMenu,0), MF_STRING, IDM_VPORTDYN_FIRST + num, label.GetWideChar());
|
||||||
else
|
else
|
||||||
AppendMenu(GetSubMenu(hMenu,0), MF_SEPARATOR, IDM_VPORTDYN_FIRST, NULL);
|
AppendMenu(GetSubMenu(hMenu,0), MF_SEPARATOR, IDM_VPORTDYN_FIRST, NULL);
|
||||||
#elif defined(USE_GTK)
|
|
||||||
GtkWidget * wdg;
|
|
||||||
if (num)
|
|
||||||
wdg = gtk_menu_item_new_with_label(label.getData());
|
|
||||||
else
|
|
||||||
wdg = gtk_separator_menu_item_new();
|
|
||||||
itemsObjselect[num].MenuItem = wdg;
|
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(menuContext), wdg);
|
|
||||||
if (num)
|
|
||||||
g_signal_connect(G_OBJECT(wdg), "activate", G_CALLBACK(OnObjselect), &itemsObjselect[num]);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1167,10 +1138,6 @@ bool C4EditCursor::DoContextMenu(DWORD dwKeyState)
|
||||||
SetMenuItemText(hContext,IDM_VIEWPORT_DELETE,LoadResStr("IDS_MNU_DELETE"));
|
SetMenuItemText(hContext,IDM_VIEWPORT_DELETE,LoadResStr("IDS_MNU_DELETE"));
|
||||||
SetMenuItemText(hContext,IDM_VIEWPORT_DUPLICATE,LoadResStr("IDS_MNU_DUPLICATE"));
|
SetMenuItemText(hContext,IDM_VIEWPORT_DUPLICATE,LoadResStr("IDS_MNU_DUPLICATE"));
|
||||||
SetMenuItemText(hContext,IDM_VIEWPORT_CONTENTS,LoadResStr("IDS_MNU_CONTENTS"));
|
SetMenuItemText(hContext,IDM_VIEWPORT_CONTENTS,LoadResStr("IDS_MNU_CONTENTS"));
|
||||||
#elif defined(USE_GTK)
|
|
||||||
gtk_widget_set_sensitive(itemDelete, fObjectSelected && Console.Editing);
|
|
||||||
gtk_widget_set_sensitive(itemDuplicate, fObjectSelected && Console.Editing);
|
|
||||||
gtk_widget_set_sensitive(itemGrabContents, fObjectSelected && Selection.GetObject()->Contents.ObjectCount() && Console.Editing);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Add selection and custom command entries for any objects at the cursor
|
// Add selection and custom command entries for any objects at the cursor
|
||||||
|
@ -1274,9 +1241,6 @@ bool C4EditCursor::DoContextMenu(DWORD dwKeyState)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ObjselectDelItems();
|
ObjselectDelItems();
|
||||||
#elif defined(USE_GTK)
|
|
||||||
gtk_widget_show_all(menuContext);
|
|
||||||
gtk_menu_popup(GTK_MENU(menuContext), NULL, NULL, NULL, NULL, 3, gtk_get_current_event_time());
|
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1439,53 +1403,12 @@ void C4EditCursor::EMControl(C4PacketType eCtrlType, C4ControlPacket *pCtrl)
|
||||||
::Control.DoInput(eCtrlType, pCtrl, CDT_Decide);
|
::Control.DoInput(eCtrlType, pCtrl, CDT_Decide);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
// GTK+ callbacks
|
|
||||||
void C4EditCursor::OnDelete(GtkWidget* widget, gpointer data)
|
|
||||||
{
|
|
||||||
static_cast<C4EditCursor*>(data)->Delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4EditCursor::OnDuplicate(GtkWidget* widget, gpointer data)
|
|
||||||
{
|
|
||||||
static_cast<C4EditCursor*>(data)->Duplicate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4EditCursor::OnGrabContents(GtkWidget* widget, gpointer data)
|
|
||||||
{
|
|
||||||
static_cast<C4EditCursor*>(data)->GrabContents();
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4EditCursor::OnObjselect(GtkWidget* widget, gpointer data)
|
|
||||||
{
|
|
||||||
bool IsShiftDown = false;
|
|
||||||
GdkEvent* event = gtk_get_current_event();
|
|
||||||
if(event)
|
|
||||||
{
|
|
||||||
if(event->type == GDK_BUTTON_PRESS)
|
|
||||||
IsShiftDown = ( ((GdkEventButton*)event)->state & MK_SHIFT) != 0;
|
|
||||||
else if(event->type == GDK_KEY_PRESS)
|
|
||||||
IsShiftDown = ( ((GdkEventKey*)event)->state & MK_SHIFT) != 0;
|
|
||||||
|
|
||||||
gdk_event_free(event);
|
|
||||||
}
|
|
||||||
ObjselItemDt* it = static_cast<ObjselItemDt*>(data);
|
|
||||||
if (it->Command.getLength())
|
|
||||||
it->EditCursor->DoContextObjCommand(it->Object, it->Command.getData());
|
|
||||||
else
|
|
||||||
it->EditCursor->DoContextObjsel(it->Object, !IsShiftDown);
|
|
||||||
it->EditCursor->ObjselectDelItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void C4EditCursor::ObjselectDelItems() {
|
void C4EditCursor::ObjselectDelItems() {
|
||||||
if(!itemsObjselect.size()) return;
|
if(!itemsObjselect.size()) return;
|
||||||
std::vector<ObjselItemDt>::iterator it = itemsObjselect.begin();
|
std::vector<ObjselItemDt>::iterator it = itemsObjselect.begin();
|
||||||
while(it != itemsObjselect.end()) {
|
while(it != itemsObjselect.end()) {
|
||||||
#if defined(USE_GTK)
|
#if defined(USE_WIN32_WINDOWS)
|
||||||
gtk_widget_destroy(it->MenuItem);
|
|
||||||
#elif defined(USE_WIN32_WINDOWS)
|
|
||||||
if(!it->ItemId) { ++it; continue; }
|
if(!it->ItemId) { ++it; continue; }
|
||||||
HMENU hContext = GetSubMenu(hMenu,0);
|
HMENU hContext = GetSubMenu(hMenu,0);
|
||||||
DeleteMenu(hContext, it->ItemId, MF_BYCOMMAND);
|
DeleteMenu(hContext, it->ItemId, MF_BYCOMMAND);
|
||||||
|
|
|
@ -27,9 +27,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "object/C4DefGraphics.h"
|
#include "object/C4DefGraphics.h"
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Currently selected elements in editcursor. May be objects and other prop lists.
|
// Currently selected elements in editcursor. May be objects and other prop lists.
|
||||||
class C4EditCursorSelection : public std::list<C4Value>
|
class C4EditCursorSelection : public std::list<C4Value>
|
||||||
|
@ -69,19 +66,11 @@ protected:
|
||||||
StdCopyStrBuf Command;
|
StdCopyStrBuf Command;
|
||||||
#if defined(USE_WIN32_WINDOWS)
|
#if defined(USE_WIN32_WINDOWS)
|
||||||
UINT_PTR ItemId;
|
UINT_PTR ItemId;
|
||||||
#elif defined(USE_GTK)
|
|
||||||
GtkWidget* MenuItem;
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
std::vector<ObjselItemDt> itemsObjselect;
|
std::vector<ObjselItemDt> itemsObjselect;
|
||||||
#ifdef USE_WIN32_WINDOWS
|
#ifdef USE_WIN32_WINDOWS
|
||||||
HMENU hMenu;
|
HMENU hMenu;
|
||||||
#elif defined(USE_GTK)
|
|
||||||
GtkWidget* menuContext;
|
|
||||||
GtkWidget* itemDelete;
|
|
||||||
GtkWidget* itemDuplicate;
|
|
||||||
GtkWidget* itemGrabContents;
|
|
||||||
GtkWidget* itemProperties;
|
|
||||||
#endif
|
#endif
|
||||||
// Selection may either be any number of objects or a single non-object prop list
|
// Selection may either be any number of objects or a single non-object prop list
|
||||||
C4EditCursorSelection selection;
|
C4EditCursorSelection selection;
|
||||||
|
@ -151,12 +140,6 @@ public:
|
||||||
void PerformDuplicationLegacy(int32_t *pObjects, int32_t iObjectNum, bool fLocalCall);
|
void PerformDuplicationLegacy(int32_t *pObjects, int32_t iObjectNum, bool fLocalCall);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#ifdef USE_GTK
|
|
||||||
static void OnDelete(GtkWidget* widget, gpointer data);
|
|
||||||
static void OnDuplicate(GtkWidget* widget, gpointer data);
|
|
||||||
static void OnGrabContents(GtkWidget* widget, gpointer data);
|
|
||||||
static void OnObjselect(GtkWidget* widget, gpointer data);
|
|
||||||
#endif
|
|
||||||
public:
|
public:
|
||||||
void AddToSelection(C4PropList *add_obj); // add object to selection and do script callback. Doesn't do OnSelectionChanged().
|
void AddToSelection(C4PropList *add_obj); // add object to selection and do script callback. Doesn't do OnSelectionChanged().
|
||||||
bool RemoveFromSelection(C4PropList *remove_obj); // remove object from selection and do script callback. return true if object was in selection before. Doesn't do OnSelectionChanged().
|
bool RemoveFromSelection(C4PropList *remove_obj); // remove object from selection and do script callback. return true if object was in selection before. Doesn't do OnSelectionChanged().
|
||||||
|
|
|
@ -23,819 +23,6 @@
|
||||||
#include "object/C4GameObjects.h"
|
#include "object/C4GameObjects.h"
|
||||||
#include "script/C4Effect.h"
|
#include "script/C4Effect.h"
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
|
|
||||||
|
|
||||||
/* Some boilerplate GObject defines. 'klass' is used instead of 'class', because 'class' is a C++ keyword */
|
|
||||||
|
|
||||||
#define C4_TYPE_LIST (c4_list_get_type ())
|
|
||||||
#define C4_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), C4_TYPE_LIST, C4List))
|
|
||||||
#define C4_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), C4_TYPE_LIST, C4ListClass))
|
|
||||||
#define C4_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), C4_TYPE_LIST))
|
|
||||||
#define C4_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), C4_TYPE_LIST))
|
|
||||||
#define C4_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), C4_TYPE_LIST, C4ListClass))
|
|
||||||
|
|
||||||
/* The data columns that we export via the tree model interface */
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
C4_LIST_COL_OBJECT,
|
|
||||||
C4_LIST_N_COLUMNS
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _C4List C4List;
|
|
||||||
typedef struct _C4ListClass C4ListClass;
|
|
||||||
|
|
||||||
struct _C4List
|
|
||||||
{
|
|
||||||
GObject parent; /* this MUST be the first member */
|
|
||||||
|
|
||||||
C4ObjectList * data;
|
|
||||||
|
|
||||||
gint stamp; /* A random integer to check if an iter belongs to this model */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* more boilerplate GObject stuff */
|
|
||||||
|
|
||||||
struct _C4ListClass
|
|
||||||
{
|
|
||||||
GObjectClass parent_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
static GObjectClass *parent_class = NULL;
|
|
||||||
|
|
||||||
GType c4_list_get_type (void);
|
|
||||||
|
|
||||||
// Initialise an instance
|
|
||||||
static void
|
|
||||||
c4_list_init (C4List *c4_list)
|
|
||||||
{
|
|
||||||
c4_list->data = &::Objects;
|
|
||||||
|
|
||||||
c4_list->stamp = g_random_int(); /* Random int to check whether iters belong to this model */
|
|
||||||
}
|
|
||||||
|
|
||||||
// destructor
|
|
||||||
static void
|
|
||||||
c4_list_finalize (GObject *object)
|
|
||||||
{
|
|
||||||
/* must chain up - finalize parent */
|
|
||||||
(* parent_class->finalize) (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static GtkTreeModelFlags
|
|
||||||
c4_list_get_flags (GtkTreeModel *tree_model)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (C4_IS_LIST(tree_model), (GtkTreeModelFlags)0);
|
|
||||||
|
|
||||||
// neither is this a flat list nor do the iters persist changes in the model
|
|
||||||
//return GtkTreeModelFlags(GTK_TREE_MODEL_ITERS_PERSIST);
|
|
||||||
return GtkTreeModelFlags(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// converts 'path' into an iterator and stores that in 'iter'
|
|
||||||
static gboolean
|
|
||||||
c4_list_get_iter (GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreePath * path)
|
|
||||||
{
|
|
||||||
gint *indices, depth;
|
|
||||||
|
|
||||||
g_assert(C4_IS_LIST(tree_model));
|
|
||||||
g_assert(path!=NULL);
|
|
||||||
|
|
||||||
C4List * c4_list = C4_LIST(tree_model);
|
|
||||||
|
|
||||||
indices = gtk_tree_path_get_indices(path);
|
|
||||||
depth = gtk_tree_path_get_depth(path);
|
|
||||||
|
|
||||||
iter->stamp = c4_list->stamp;
|
|
||||||
|
|
||||||
|
|
||||||
C4ObjectLink * pLnk = C4_LIST(tree_model)->data->First;
|
|
||||||
// Skip Contained Objects in the main list
|
|
||||||
while (pLnk && pLnk->Obj->Contained) pLnk = pLnk->Next;
|
|
||||||
for (int i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
if (!pLnk)
|
|
||||||
return false;
|
|
||||||
if (indices[i] < 0)
|
|
||||||
return false;
|
|
||||||
for (int j = 0; j < indices[i]; ++j)
|
|
||||||
{
|
|
||||||
pLnk = pLnk->Next;
|
|
||||||
// Skip Contained Objects in the main list
|
|
||||||
while (i == 0 && pLnk && pLnk->Obj->Contained) pLnk = pLnk->Next;
|
|
||||||
if (!pLnk)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
iter->user_data = pLnk;
|
|
||||||
iter->user_data2 = pLnk->Obj->Contained;
|
|
||||||
pLnk = pLnk->Obj->Contents.First;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// converts 'iter' into a new tree path and returns that.
|
|
||||||
// Note: This is called by OnObjectRemove with an iter which is not in the
|
|
||||||
// list anymore, but with the object and prev still usable.
|
|
||||||
static GtkTreePath *
|
|
||||||
c4_list_get_path (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (C4_IS_LIST(tree_model), NULL);
|
|
||||||
g_return_val_if_fail (iter != NULL, NULL);
|
|
||||||
g_return_val_if_fail (iter->user_data != NULL, NULL);
|
|
||||||
|
|
||||||
GtkTreePath *path = gtk_tree_path_new();
|
|
||||||
|
|
||||||
C4List * c4_list = C4_LIST(tree_model);
|
|
||||||
|
|
||||||
C4Object * pObj = ((C4ObjectLink *) iter->user_data)->Obj;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (C4ObjectLink * pLnk = ((C4ObjectLink *) iter->user_data)->Prev; pLnk; pLnk = pLnk->Prev)
|
|
||||||
{
|
|
||||||
// Skip Contained Objects in the main list
|
|
||||||
if (pObj->Contained != pLnk->Obj->Contained) continue;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
gtk_tree_path_prepend_index(path, i);
|
|
||||||
|
|
||||||
pObj = (C4Object *) iter->user_data2;
|
|
||||||
while (pObj)
|
|
||||||
{
|
|
||||||
i = 0;
|
|
||||||
C4ObjectList * pList = c4_list->data;
|
|
||||||
if (pObj->Contained)
|
|
||||||
pList = &pObj->Contained->Contents;
|
|
||||||
for (C4ObjectLink * pLnk = pList->First; pLnk && pLnk->Obj != pObj; pLnk = pLnk->Next)
|
|
||||||
{
|
|
||||||
// Skip Contained Objects in the main list
|
|
||||||
if (pObj->Contained != pLnk->Obj->Contained) continue;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
gtk_tree_path_prepend_index(path, i);
|
|
||||||
pObj = pObj->Contained;
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ++iter
|
|
||||||
static gboolean
|
|
||||||
c4_list_iter_next (GtkTreeModel * tree_model, GtkTreeIter * iter)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (C4_IS_LIST (tree_model), false);
|
|
||||||
|
|
||||||
if (iter == NULL || iter->user_data == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
C4ObjectLink * pLnk = (C4ObjectLink *) iter->user_data;
|
|
||||||
|
|
||||||
pLnk = pLnk->Next;
|
|
||||||
|
|
||||||
// Skip Contained Objects in the main list
|
|
||||||
if (!(C4Object *)iter->user_data2)
|
|
||||||
while (pLnk && pLnk->Obj->Contained)
|
|
||||||
pLnk = pLnk->Next;
|
|
||||||
if (!pLnk)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
iter->user_data = pLnk;
|
|
||||||
iter->user_data2 = pLnk->Obj->Contained;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set 'iter' to the first child of 'parent', or the first top-level row if
|
|
||||||
// 'parent' is 0, or return false if there aren't any children.
|
|
||||||
static gboolean
|
|
||||||
c4_list_iter_children (GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (parent == NULL || parent->user_data != NULL, false);
|
|
||||||
g_return_val_if_fail (C4_IS_LIST (tree_model), false);
|
|
||||||
|
|
||||||
C4List *c4_list = C4_LIST(tree_model);
|
|
||||||
|
|
||||||
C4ObjectLink * pLnk;
|
|
||||||
if (parent)
|
|
||||||
{
|
|
||||||
C4ObjectList * pList = &((C4ObjectLink *)parent->user_data)->Obj->Contents;
|
|
||||||
pLnk = pList->First;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pLnk = c4_list->data->First;
|
|
||||||
// Skip...
|
|
||||||
while (pLnk && pLnk->Obj->Contained) pLnk = pLnk->Next;
|
|
||||||
}
|
|
||||||
if (!pLnk)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Set iter to first item in list */
|
|
||||||
iter->stamp = c4_list->stamp;
|
|
||||||
iter->user_data = pLnk;
|
|
||||||
iter->user_data2 = pLnk->Obj->Contained;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if 'parent' has children.
|
|
||||||
static gboolean
|
|
||||||
c4_list_iter_has_child (GtkTreeModel *tree_model,
|
|
||||||
GtkTreeIter *parent)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (parent == NULL || parent->user_data != NULL, false);
|
|
||||||
g_return_val_if_fail (C4_IS_LIST (tree_model), false);
|
|
||||||
|
|
||||||
C4List *c4_list = C4_LIST(tree_model);
|
|
||||||
|
|
||||||
C4ObjectList * pList = c4_list->data;
|
|
||||||
if (parent)
|
|
||||||
pList = &((C4ObjectLink *)parent->user_data)->Obj->Contents;
|
|
||||||
return pList->First != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Counts the children 'parent' has.
|
|
||||||
static gint
|
|
||||||
c4_list_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *parent)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (C4_IS_LIST (tree_model), -1);
|
|
||||||
g_return_val_if_fail (parent == NULL || parent->user_data != NULL, -1);
|
|
||||||
|
|
||||||
C4List *c4_list = C4_LIST(tree_model);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
if (parent)
|
|
||||||
{
|
|
||||||
C4ObjectList * pList = &((C4ObjectLink *)parent->user_data)->Obj->Contents;
|
|
||||||
C4ObjectLink * pLnk = pList->First;
|
|
||||||
while (pLnk)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
pLnk = pLnk->Next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
C4ObjectLink * pLnk = c4_list->data->First;
|
|
||||||
while (pLnk)
|
|
||||||
{
|
|
||||||
if (!pLnk->Obj->Contained)
|
|
||||||
++i;
|
|
||||||
pLnk = pLnk->Next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets 'iter' to the 'n'-th child of 'parent'.
|
|
||||||
static gboolean
|
|
||||||
c4_list_iter_nth_child (GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent, gint n)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (C4_IS_LIST (tree_model), false);
|
|
||||||
g_return_val_if_fail (parent == NULL || parent->user_data != NULL, false);
|
|
||||||
|
|
||||||
C4List *c4_list = C4_LIST(tree_model);
|
|
||||||
|
|
||||||
C4ObjectLink * pLnk;
|
|
||||||
if (parent)
|
|
||||||
{
|
|
||||||
C4ObjectList * pList = &((C4ObjectLink *)parent->user_data)->Obj->Contents;
|
|
||||||
pLnk = pList->First;
|
|
||||||
for (int i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
if (!pLnk)
|
|
||||||
return false;
|
|
||||||
pLnk = pLnk->Next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pLnk = c4_list->data->First;
|
|
||||||
for (int i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
if (!pLnk)
|
|
||||||
return false;
|
|
||||||
pLnk = pLnk->Next;
|
|
||||||
// Skip...
|
|
||||||
while (pLnk && pLnk->Obj->Contained) pLnk = pLnk->Next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!pLnk)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
iter->stamp = c4_list->stamp;
|
|
||||||
iter->user_data = pLnk;
|
|
||||||
iter->user_data2 = pLnk->Obj->Contained;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function.
|
|
||||||
static gboolean c4_list_iter_for_C4Object (GtkTreeModel * tree_model, GtkTreeIter * iter, C4ObjectList * pList, C4Object * pObj)
|
|
||||||
{
|
|
||||||
if (!pObj)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
C4List * c4_list = C4_LIST(tree_model);
|
|
||||||
|
|
||||||
for (C4ObjectLink * pLnk = pList->First; pLnk; pLnk = pLnk->Next)
|
|
||||||
{
|
|
||||||
if (pLnk->Obj == pObj)
|
|
||||||
{
|
|
||||||
iter->stamp = c4_list->stamp;
|
|
||||||
iter->user_data = pLnk;
|
|
||||||
iter->user_data2 = pLnk->Obj->Contained;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_return_val_if_reached(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets 'iter' to the parent row of 'child'.
|
|
||||||
static gboolean
|
|
||||||
c4_list_iter_parent (GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * child)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (C4_IS_LIST (tree_model), false);
|
|
||||||
g_return_val_if_fail (child == NULL || child->user_data != NULL, false);
|
|
||||||
|
|
||||||
C4List * c4_list = C4_LIST(tree_model);
|
|
||||||
|
|
||||||
C4Object * pObj = (C4Object *) child->user_data2;
|
|
||||||
|
|
||||||
C4ObjectList * pList = c4_list->data;
|
|
||||||
if (pObj->Contained)
|
|
||||||
pList = &pObj->Contained->Contents;
|
|
||||||
return c4_list_iter_for_C4Object(tree_model, iter, pList, pObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static C4Object *
|
|
||||||
c4_list_iter_get_C4Object(GtkTreeModel * tree_model, GtkTreeIter * iter)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (C4_IS_LIST (tree_model), NULL);
|
|
||||||
g_return_val_if_fail (iter != NULL && iter->user_data != NULL, NULL);
|
|
||||||
|
|
||||||
return ((C4ObjectLink *) iter->user_data)->Obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How many columns does this model have?
|
|
||||||
static gint
|
|
||||||
c4_list_get_n_columns (GtkTreeModel * tree_model)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (C4_IS_LIST(tree_model), 0);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// What sort of data is in the column?
|
|
||||||
static GType
|
|
||||||
c4_list_get_column_type (GtkTreeModel * tree_model, gint index)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (C4_IS_LIST(tree_model), G_TYPE_INVALID);
|
|
||||||
g_return_val_if_fail (index < 1 && index >= 0, G_TYPE_INVALID);
|
|
||||||
|
|
||||||
return G_TYPE_POINTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
// gets the data for the 'column' in the row at 'iter' and stores that in 'value'.
|
|
||||||
static void
|
|
||||||
c4_list_get_value (GtkTreeModel * tree_model, GtkTreeIter * iter, gint column, GValue * value)
|
|
||||||
{
|
|
||||||
g_return_if_fail (C4_IS_LIST (tree_model));
|
|
||||||
g_return_if_fail (iter != NULL);
|
|
||||||
g_return_if_fail (column == 0);
|
|
||||||
|
|
||||||
C4Object * pObj = ((C4ObjectLink *) iter->user_data)->Obj;
|
|
||||||
g_return_if_fail (pObj != NULL);
|
|
||||||
|
|
||||||
g_value_init (value, G_TYPE_POINTER);
|
|
||||||
g_value_set_pointer(value, pObj);
|
|
||||||
|
|
||||||
// g_value_set_string(value, pObj->GetName());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper around g_object_new.
|
|
||||||
static C4List *
|
|
||||||
c4_list_new (void)
|
|
||||||
{
|
|
||||||
C4List * list;
|
|
||||||
|
|
||||||
list = (C4List *) g_object_new (C4_TYPE_LIST, NULL);
|
|
||||||
|
|
||||||
g_assert(list != NULL);
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called once for the class.
|
|
||||||
static void
|
|
||||||
c4_list_class_init (C4ListClass * klass)
|
|
||||||
{
|
|
||||||
GObjectClass * object_class;
|
|
||||||
|
|
||||||
parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
|
|
||||||
object_class = (GObjectClass*) klass;
|
|
||||||
|
|
||||||
object_class->finalize = c4_list_finalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill in the GtkTreeModel interface with the functions above.
|
|
||||||
static void
|
|
||||||
c4_list_tree_model_init (GtkTreeModelIface *iface)
|
|
||||||
{
|
|
||||||
iface->get_flags = c4_list_get_flags;
|
|
||||||
iface->get_n_columns = c4_list_get_n_columns;
|
|
||||||
iface->get_column_type = c4_list_get_column_type;
|
|
||||||
iface->get_iter = c4_list_get_iter;
|
|
||||||
iface->get_path = c4_list_get_path;
|
|
||||||
iface->get_value = c4_list_get_value;
|
|
||||||
iface->iter_next = c4_list_iter_next;
|
|
||||||
iface->iter_children = c4_list_iter_children;
|
|
||||||
iface->iter_has_child = c4_list_iter_has_child;
|
|
||||||
iface->iter_n_children = c4_list_iter_n_children;
|
|
||||||
iface->iter_nth_child = c4_list_iter_nth_child;
|
|
||||||
iface->iter_parent = c4_list_iter_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the type, registering it on first call.
|
|
||||||
GType
|
|
||||||
c4_list_get_type (void)
|
|
||||||
{
|
|
||||||
static GType c4_list_type = 0;
|
|
||||||
|
|
||||||
if (c4_list_type == 0)
|
|
||||||
{
|
|
||||||
// Some boilerplate type registration stuff
|
|
||||||
static const GTypeInfo c4_list_info =
|
|
||||||
{
|
|
||||||
sizeof (C4ListClass), /* class_size */
|
|
||||||
NULL, /* base_init */
|
|
||||||
NULL, /* base_finalize */
|
|
||||||
(GClassInitFunc) c4_list_class_init, /* class_init */
|
|
||||||
NULL, /* class finalize */
|
|
||||||
NULL, /* class_data */
|
|
||||||
sizeof (C4List), /* instance_size */
|
|
||||||
0, /* n_preallocs */
|
|
||||||
(GInstanceInitFunc) c4_list_init, /* instance_init */
|
|
||||||
NULL /* value_table */
|
|
||||||
};
|
|
||||||
|
|
||||||
c4_list_type = g_type_register_static (G_TYPE_OBJECT, "C4List",
|
|
||||||
&c4_list_info, (GTypeFlags)0);
|
|
||||||
|
|
||||||
/* register the GtkTreeModel interface with the type system */
|
|
||||||
static const GInterfaceInfo tree_model_info =
|
|
||||||
{
|
|
||||||
(GInterfaceInitFunc) c4_list_tree_model_init,
|
|
||||||
NULL,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
g_type_add_interface_static (c4_list_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
return c4_list_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::OnObjectRemove(C4ObjectList * pList, C4ObjectLink * pLnk)
|
|
||||||
{
|
|
||||||
if (!model) return;
|
|
||||||
|
|
||||||
C4List * c4_list = C4_LIST(model);
|
|
||||||
C4Object * Contained = pLnk->Obj->Contained;
|
|
||||||
GtkTreeIter iter;
|
|
||||||
iter.stamp = c4_list->stamp;
|
|
||||||
iter.user_data = pLnk;
|
|
||||||
iter.user_data2 = Contained;
|
|
||||||
|
|
||||||
// While pLnk is not in the list anymore, with pLnk->Prev and Contained a path can still be made
|
|
||||||
GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &iter);
|
|
||||||
|
|
||||||
gtk_tree_model_row_deleted(GTK_TREE_MODEL(model), path);
|
|
||||||
gtk_tree_path_free(path);
|
|
||||||
|
|
||||||
// Removed from a now empty container?
|
|
||||||
if (Contained && !Contained->Contents.First)
|
|
||||||
{
|
|
||||||
printf("Removed from a now empty container\n");
|
|
||||||
GtkTreeIter parent;
|
|
||||||
C4ObjectList * pList = c4_list->data;
|
|
||||||
if (Contained->Contained)
|
|
||||||
pList = &Contained->Contained->Contents;
|
|
||||||
c4_list_iter_for_C4Object(GTK_TREE_MODEL(model), &parent, pList, Contained);
|
|
||||||
|
|
||||||
GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &parent);
|
|
||||||
|
|
||||||
gtk_tree_model_row_has_child_toggled(GTK_TREE_MODEL(model), path, &parent);
|
|
||||||
gtk_tree_path_free(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Cheat: For the signals it must look as if the object had it's parent removed already
|
|
||||||
pLnk->Obj->Contained = 0;
|
|
||||||
// if removed from contents, it get's added to main list
|
|
||||||
if (pList != c4_list->data)
|
|
||||||
{
|
|
||||||
printf("Removed from a container\n");
|
|
||||||
GtkTreeIter iter;
|
|
||||||
C4ObjectList * pList = c4_list->data;
|
|
||||||
c4_list_iter_for_C4Object(GTK_TREE_MODEL(model), &iter, pList, pLnk->Obj);
|
|
||||||
|
|
||||||
GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &iter);
|
|
||||||
|
|
||||||
gtk_tree_model_row_inserted(GTK_TREE_MODEL(model), path, &iter);
|
|
||||||
gtk_tree_path_free(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// End-of-cheat
|
|
||||||
pLnk->Obj->Contained = Contained;
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::OnObjectAdded(C4ObjectList * pList, C4ObjectLink * pLnk)
|
|
||||||
{
|
|
||||||
if (!model) return;
|
|
||||||
|
|
||||||
C4List * c4_list = C4_LIST(model);
|
|
||||||
|
|
||||||
// Inserted into a container? Remove from main list
|
|
||||||
if (pList != c4_list->data)
|
|
||||||
{
|
|
||||||
printf("Inserted into a container\n");
|
|
||||||
GtkTreePath *path = gtk_tree_path_new();
|
|
||||||
int i = 0;
|
|
||||||
C4ObjectList * pList = c4_list->data;
|
|
||||||
C4Object * pObj = pLnk->Obj;
|
|
||||||
for (C4ObjectLink * pLnk2 = pList->First; pLnk2 && pLnk2->Obj != pObj; pLnk2 = pLnk2->Next)
|
|
||||||
{
|
|
||||||
// Skip Contained Objects in the main list
|
|
||||||
if (pLnk2->Obj->Contained) continue;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
gtk_tree_path_prepend_index(path, i);
|
|
||||||
|
|
||||||
gtk_tree_model_row_deleted(GTK_TREE_MODEL(model), path);
|
|
||||||
gtk_tree_path_free(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkTreeIter iter;
|
|
||||||
iter.stamp = c4_list->stamp;
|
|
||||||
iter.user_data = pLnk;
|
|
||||||
iter.user_data2 = pLnk->Obj->Contained;
|
|
||||||
|
|
||||||
GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &iter);
|
|
||||||
|
|
||||||
gtk_tree_model_row_inserted(GTK_TREE_MODEL(model), path, &iter);
|
|
||||||
gtk_tree_path_free(path);
|
|
||||||
|
|
||||||
// Inserted into a previously empty container?
|
|
||||||
if (pLnk->Obj->Contained &&
|
|
||||||
pLnk->Obj->Contained->Contents.First == pLnk->Obj->Contained->Contents.Last)
|
|
||||||
{
|
|
||||||
printf("Inserted into a previously empty container\n");
|
|
||||||
GtkTreeIter parent;
|
|
||||||
c4_list_iter_parent(GTK_TREE_MODEL(model), &parent, &iter);
|
|
||||||
|
|
||||||
GtkTreePath * path = c4_list_get_path(GTK_TREE_MODEL(model), &parent);
|
|
||||||
|
|
||||||
gtk_tree_model_row_has_child_toggled(GTK_TREE_MODEL(model), path, &parent);
|
|
||||||
gtk_tree_path_free(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::OnObjectRename(C4ObjectList * pList, C4ObjectLink * pLnk)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::OnDestroy(GtkWidget* widget, C4ObjectListDlg* dlg)
|
|
||||||
{
|
|
||||||
dlg->window = 0;
|
|
||||||
dlg->model = 0;
|
|
||||||
dlg->treeview = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::OnRowActivated(GtkTreeView * tree_view, GtkTreePath * path, GtkTreeViewColumn * column, C4ObjectListDlg * dlg)
|
|
||||||
{
|
|
||||||
Console.EditCursor.SetMode(C4CNS_ModeEdit);
|
|
||||||
Console.EditCursor.OpenPropTools();
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::OnSelectionChanged(GtkTreeSelection* selection, C4ObjectListDlg* dlg)
|
|
||||||
{
|
|
||||||
if (dlg->updating_selection) return;
|
|
||||||
dlg->updating_selection = true;
|
|
||||||
|
|
||||||
GList* list = gtk_tree_selection_get_selected_rows(selection, NULL);
|
|
||||||
|
|
||||||
Console.EditCursor.GetSelection().Clear();
|
|
||||||
for (GList * i = list; i; i = i->next)
|
|
||||||
{
|
|
||||||
GtkTreePath * path = (GtkTreePath *)i->data;
|
|
||||||
GtkTreeIter iter;
|
|
||||||
c4_list_get_iter(GTK_TREE_MODEL(dlg->model), &iter, path);
|
|
||||||
Console.EditCursor.GetSelection().Add(((C4ObjectLink *)iter.user_data)->Obj, C4ObjectList::stNone);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL);
|
|
||||||
g_list_free (list);
|
|
||||||
|
|
||||||
Console.EditCursor.OnSelectionChanged();
|
|
||||||
dlg->updating_selection = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::Update(C4EditCursorSelection &rSelection)
|
|
||||||
{
|
|
||||||
if (updating_selection) return;
|
|
||||||
if (!window) return;
|
|
||||||
updating_selection = true;
|
|
||||||
|
|
||||||
GtkTreeSelection *selection;
|
|
||||||
|
|
||||||
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
|
|
||||||
|
|
||||||
gtk_tree_selection_unselect_all(selection);
|
|
||||||
|
|
||||||
for (auto val : rSelection)
|
|
||||||
{
|
|
||||||
C4Object *obj = val.getObj();
|
|
||||||
if (!obj) continue; // it's a non-object proplist or deleted object
|
|
||||||
GtkTreeIter iter;
|
|
||||||
C4List * c4_list = C4_LIST(model);
|
|
||||||
C4ObjectList * pList = c4_list->data;
|
|
||||||
if (obj->Contained)
|
|
||||||
pList = &obj->Contained->Contents;
|
|
||||||
c4_list_iter_for_C4Object(GTK_TREE_MODEL(model), &iter, pList, obj);
|
|
||||||
gtk_tree_selection_select_iter(selection, &iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
updating_selection = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
C4ObjectListDlg::C4ObjectListDlg():
|
|
||||||
window(0),
|
|
||||||
treeview(0),
|
|
||||||
model(0),
|
|
||||||
updating_selection(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
C4ObjectListDlg::~C4ObjectListDlg()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::Execute()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void name_cell_data_func(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, gpointer data)
|
|
||||||
{
|
|
||||||
C4Object* object = c4_list_iter_get_C4Object(model, iter);
|
|
||||||
|
|
||||||
g_object_set(G_OBJECT(renderer), "text", object->GetName(), (gpointer)NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum { ICON_SIZE = 24 };
|
|
||||||
|
|
||||||
#ifdef _UNUSED_
|
|
||||||
static void icon_cell_data_func(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, gpointer data)
|
|
||||||
{
|
|
||||||
C4Object* object = c4_list_iter_get_C4Object(model, iter);
|
|
||||||
|
|
||||||
// Icons for objects with ColorByOwner are cached by object, others by Def
|
|
||||||
// FIXME: Invalidate cache when objects change color, and redraw.
|
|
||||||
gpointer key = object->Def;
|
|
||||||
if (object->Def->ColorByOwner) key = object;
|
|
||||||
|
|
||||||
GHashTable* table = static_cast<GHashTable*>(data);
|
|
||||||
GdkPixbuf* pixbuf = GDK_PIXBUF(g_hash_table_lookup(table, key));
|
|
||||||
|
|
||||||
if (pixbuf == NULL)
|
|
||||||
{
|
|
||||||
/* Not yet cached, create from Graphics */
|
|
||||||
C4Surface* surface = object->Def->Graphics.Bmp.Bitmap;
|
|
||||||
if (object->Def->Graphics.Bmp.BitmapClr) surface = object->Def->Graphics.Bmp.BitmapClr;
|
|
||||||
|
|
||||||
const C4Rect& picture = object->Def->PictureRect;
|
|
||||||
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, picture.Wdt, picture.Hgt);
|
|
||||||
guchar* pixels = gdk_pixbuf_get_pixels(pixbuf);
|
|
||||||
surface->Lock();
|
|
||||||
for (int y = 0; y < picture.Hgt; ++ y) for (int x = 0; x < picture.Wdt; ++ x)
|
|
||||||
{
|
|
||||||
DWORD dw = surface->GetPixDw(picture.x + x, picture.y + y, true);
|
|
||||||
*pixels = (dw >> 16) & 0xff; ++ pixels;
|
|
||||||
*pixels = (dw >> 8 ) & 0xff; ++ pixels;
|
|
||||||
*pixels = (dw ) & 0xff; ++ pixels;
|
|
||||||
*pixels = 0xff - ((dw >> 24) & 0xff); ++ pixels;
|
|
||||||
}
|
|
||||||
surface->Unlock();
|
|
||||||
|
|
||||||
// Scale down to ICON_SIZE, keeping aspect ratio
|
|
||||||
guint dest_width, dest_height;
|
|
||||||
if (picture.Wdt >= picture.Hgt)
|
|
||||||
{
|
|
||||||
double factor = static_cast<double>(picture.Hgt) / static_cast<double>(picture.Wdt);
|
|
||||||
dest_width = ICON_SIZE;
|
|
||||||
dest_height = dest_width * factor;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double factor = static_cast<double>(picture.Wdt) / static_cast<double>(picture.Hgt);
|
|
||||||
dest_height = ICON_SIZE;
|
|
||||||
dest_width = dest_height * factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
GdkPixbuf* scaled = gdk_pixbuf_scale_simple(pixbuf, dest_width, dest_height, GDK_INTERP_HYPER);
|
|
||||||
g_object_unref(G_OBJECT(pixbuf));
|
|
||||||
pixbuf = scaled;
|
|
||||||
|
|
||||||
g_hash_table_insert(table, key, pixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_object_set(G_OBJECT(renderer), "pixbuf", pixbuf, NULL);
|
|
||||||
}
|
|
||||||
#endif // _UNUSED_
|
|
||||||
|
|
||||||
void C4ObjectListDlg::Open()
|
|
||||||
{
|
|
||||||
// Create Window if necessary
|
|
||||||
if (window == NULL)
|
|
||||||
{
|
|
||||||
// The Windows
|
|
||||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
||||||
|
|
||||||
gtk_window_set_resizable(GTK_WINDOW(window), true);
|
|
||||||
gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_UTILITY);
|
|
||||||
gtk_window_set_role(GTK_WINDOW(window), "objectlist");
|
|
||||||
gtk_window_set_title(GTK_WINDOW(window), "Objects");
|
|
||||||
gtk_window_set_default_size(GTK_WINDOW(window), 180, 300);
|
|
||||||
|
|
||||||
gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(Console.window));
|
|
||||||
|
|
||||||
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(OnDestroy), this);
|
|
||||||
|
|
||||||
// The Tree
|
|
||||||
GtkWidget* scrolled_wnd = gtk_scrolled_window_new(NULL, NULL);
|
|
||||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_wnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
||||||
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_wnd), GTK_SHADOW_IN);
|
|
||||||
|
|
||||||
model = G_OBJECT(c4_list_new());
|
|
||||||
|
|
||||||
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
|
|
||||||
|
|
||||||
g_object_unref(model); /* destroy store automatically with view */
|
|
||||||
|
|
||||||
g_signal_connect(G_OBJECT(treeview), "row-activated", G_CALLBACK(OnRowActivated), this);
|
|
||||||
|
|
||||||
GtkTreeViewColumn * col = gtk_tree_view_column_new();
|
|
||||||
GtkCellRenderer * renderer = gtk_cell_renderer_text_new();
|
|
||||||
gtk_tree_view_column_pack_start(col, renderer, true);
|
|
||||||
gtk_tree_view_column_set_cell_data_func(col, renderer, name_cell_data_func, NULL, NULL);
|
|
||||||
|
|
||||||
gtk_tree_view_column_set_title(col, "Name");
|
|
||||||
gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
|
|
||||||
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview),col);
|
|
||||||
|
|
||||||
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), false);
|
|
||||||
gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), true);
|
|
||||||
|
|
||||||
GtkTreeSelection * selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
|
|
||||||
gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
|
|
||||||
|
|
||||||
g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(OnSelectionChanged), this);
|
|
||||||
|
|
||||||
gtk_container_add(GTK_CONTAINER(scrolled_wnd), treeview);
|
|
||||||
gtk_widget_set_vexpand(scrolled_wnd, true);
|
|
||||||
gtk_widget_set_hexpand(scrolled_wnd, true);
|
|
||||||
|
|
||||||
gtk_container_add(GTK_CONTAINER(window), scrolled_wnd);
|
|
||||||
|
|
||||||
gtk_widget_show_all(window);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gtk_window_present_with_time(GTK_WINDOW(window), gtk_get_current_event_time());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::OnObjectContainerChanged(C4Object *obj, C4Object *old_container, C4Object *new_container)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::OnEffectAdded(C4Effect *fx)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4ObjectListDlg::OnEffectRemoved(C4Effect *fx)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
C4ObjectListDlg::C4ObjectListDlg()
|
C4ObjectListDlg::C4ObjectListDlg()
|
||||||
{
|
{
|
||||||
|
@ -928,6 +115,5 @@ void C4ObjectListDlg::OnEffectRemoved(C4Effect *fx)
|
||||||
|
|
||||||
#endif WITH_QT_EDITOR
|
#endif WITH_QT_EDITOR
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
C4ObjectListChangeListener & ObjectListChangeListener = Console.ObjectListDlg;
|
C4ObjectListChangeListener & ObjectListChangeListener = Console.ObjectListDlg;
|
||||||
|
|
|
@ -19,9 +19,6 @@
|
||||||
#ifndef INC_C4ObjectListDlg
|
#ifndef INC_C4ObjectListDlg
|
||||||
#define INC_C4ObjectListDlg
|
#define INC_C4ObjectListDlg
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#endif // USE_GTK
|
|
||||||
|
|
||||||
#include "object/C4ObjectList.h"
|
#include "object/C4ObjectList.h"
|
||||||
|
|
||||||
|
@ -48,17 +45,6 @@ public:
|
||||||
virtual void OnEffectAdded(class C4Effect *fx) override;
|
virtual void OnEffectAdded(class C4Effect *fx) override;
|
||||||
virtual void OnEffectRemoved(class C4Effect *fx) override;
|
virtual void OnEffectRemoved(class C4Effect *fx) override;
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
private:
|
|
||||||
GtkWidget * window;
|
|
||||||
GtkWidget * treeview;
|
|
||||||
GObject * model;
|
|
||||||
bool updating_selection;
|
|
||||||
|
|
||||||
static void OnDestroy(GtkWidget * widget, C4ObjectListDlg * dlg);
|
|
||||||
static void OnRowActivated(GtkTreeView * tree_view, GtkTreePath * path, GtkTreeViewColumn * column, C4ObjectListDlg * dlg);
|
|
||||||
static void OnSelectionChanged(GtkTreeSelection * selection, C4ObjectListDlg * dlg);
|
|
||||||
#endif // USE_GTK
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //INC_C4ObjectListDlg
|
#endif //INC_C4ObjectListDlg
|
||||||
|
|
|
@ -20,9 +20,6 @@
|
||||||
#ifndef INC_C4ToolsDlg
|
#ifndef INC_C4ToolsDlg
|
||||||
#define INC_C4ToolsDlg
|
#define INC_C4ToolsDlg
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "config/C4Constants.h"
|
#include "config/C4Constants.h"
|
||||||
#include "landscape/C4Landscape.h"
|
#include "landscape/C4Landscape.h"
|
||||||
|
|
|
@ -25,9 +25,6 @@
|
||||||
#include "landscape/C4Landscape.h"
|
#include "landscape/C4Landscape.h"
|
||||||
#include "player/C4PlayerList.h"
|
#include "player/C4PlayerList.h"
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef WITH_QT_EDITOR
|
#ifdef WITH_QT_EDITOR
|
||||||
#include "editor/C4ConsoleQtViewport.h"
|
#include "editor/C4ConsoleQtViewport.h"
|
||||||
|
@ -117,70 +114,7 @@ bool C4Viewport::ScrollBarsByViewPosition()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(USE_GTK)
|
#endif
|
||||||
bool C4Viewport::TogglePlayerLock()
|
|
||||||
{
|
|
||||||
if (PlayerLock)
|
|
||||||
{
|
|
||||||
PlayerLock = false;
|
|
||||||
gtk_widget_show(pWindow->h_scrollbar);
|
|
||||||
gtk_widget_show(pWindow->v_scrollbar);
|
|
||||||
ScrollBarsByViewPosition();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PlayerLock = true;
|
|
||||||
gtk_widget_hide(pWindow->h_scrollbar);
|
|
||||||
gtk_widget_hide(pWindow->v_scrollbar);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C4Viewport::ScrollBarsByViewPosition()
|
|
||||||
{
|
|
||||||
if (PlayerLock) return false;
|
|
||||||
|
|
||||||
GtkAllocation allocation;
|
|
||||||
gtk_widget_get_allocation(GTK_WIDGET(pWindow->render_widget), &allocation);
|
|
||||||
|
|
||||||
GtkAdjustment* adjustment = gtk_range_get_adjustment(GTK_RANGE(pWindow->h_scrollbar));
|
|
||||||
|
|
||||||
gtk_adjustment_configure(adjustment,
|
|
||||||
GetViewX(), // value
|
|
||||||
0, // lower
|
|
||||||
::Landscape.GetWidth(), // upper
|
|
||||||
ViewportScrollSpeed, // step_increment
|
|
||||||
allocation.width / Zoom, // page_increment
|
|
||||||
allocation.width / Zoom // page_size
|
|
||||||
);
|
|
||||||
|
|
||||||
adjustment = gtk_range_get_adjustment(GTK_RANGE(pWindow->v_scrollbar));
|
|
||||||
gtk_adjustment_configure(adjustment,
|
|
||||||
GetViewY(), // value
|
|
||||||
0, // lower
|
|
||||||
::Landscape.GetHeight(), // upper
|
|
||||||
ViewportScrollSpeed, // step_increment
|
|
||||||
allocation.height / Zoom, // page_increment
|
|
||||||
allocation.height / Zoom // page_size
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C4Viewport::ViewPositionByScrollBars()
|
|
||||||
{
|
|
||||||
if (PlayerLock) return false;
|
|
||||||
|
|
||||||
GtkAdjustment* adjustment = gtk_range_get_adjustment(GTK_RANGE(pWindow->h_scrollbar));
|
|
||||||
SetViewX(gtk_adjustment_get_value(adjustment));
|
|
||||||
|
|
||||||
adjustment = gtk_range_get_adjustment(GTK_RANGE(pWindow->v_scrollbar));
|
|
||||||
SetViewY(gtk_adjustment_get_value(adjustment));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // USE_GTK
|
|
||||||
|
|
||||||
void C4ViewportWindow::PerformUpdate()
|
void C4ViewportWindow::PerformUpdate()
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,9 +22,6 @@
|
||||||
|
|
||||||
#include "platform/C4Window.h"
|
#include "platform/C4Window.h"
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#endif
|
|
||||||
#ifdef WITH_QT_EDITOR
|
#ifdef WITH_QT_EDITOR
|
||||||
#define C4ViewportWindowStyle (WS_VISIBLE)
|
#define C4ViewportWindowStyle (WS_VISIBLE)
|
||||||
#else
|
#else
|
||||||
|
@ -37,10 +34,6 @@ class C4ViewportWindow: public C4Window
|
||||||
public:
|
public:
|
||||||
C4Viewport * cvp;
|
C4Viewport * cvp;
|
||||||
C4ViewportWindow(C4Viewport * cvp): cvp(cvp) { }
|
C4ViewportWindow(C4Viewport * cvp): cvp(cvp) { }
|
||||||
#if defined(USE_GTK)
|
|
||||||
GtkWidget* h_scrollbar;
|
|
||||||
GtkWidget* v_scrollbar;
|
|
||||||
#endif
|
|
||||||
void EditCursorMove(int X, int Y, uint32_t);
|
void EditCursorMove(int X, int Y, uint32_t);
|
||||||
using C4Window::Init;
|
using C4Window::Init;
|
||||||
C4Window * Init(int32_t iPlayer);
|
C4Window * Init(int32_t iPlayer);
|
||||||
|
|
|
@ -1855,19 +1855,6 @@ bool C4Game::SaveGameTitle(C4Group &hGroup)
|
||||||
|
|
||||||
bool C4Game::DoKeyboardInput(C4KeyCode vk_code, C4KeyEventType eEventType, bool fAlt, bool fCtrl, bool fShift, bool fRepeated, class C4GUI::Dialog *pForDialog, bool fPlrCtrlOnly, int32_t iStrength)
|
bool C4Game::DoKeyboardInput(C4KeyCode vk_code, C4KeyEventType eEventType, bool fAlt, bool fCtrl, bool fShift, bool fRepeated, class C4GUI::Dialog *pForDialog, bool fPlrCtrlOnly, int32_t iStrength)
|
||||||
{
|
{
|
||||||
#ifdef USE_GTK
|
|
||||||
static std::map<C4KeyCode, bool> PressedKeys;
|
|
||||||
// Keyrepeats are send as down, down, ..., down, up, where all downs are not distinguishable from the first.
|
|
||||||
if (eEventType == KEYEV_Down)
|
|
||||||
{
|
|
||||||
if (PressedKeys[vk_code]) fRepeated = true;
|
|
||||||
else PressedKeys[vk_code] = true;
|
|
||||||
}
|
|
||||||
else if (eEventType == KEYEV_Up)
|
|
||||||
{
|
|
||||||
PressedKeys[vk_code] = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// compose key
|
// compose key
|
||||||
C4KeyCodeEx Key(vk_code, C4KeyShiftState(fAlt*KEYS_Alt + fCtrl*KEYS_Control + fShift*KEYS_Shift), fRepeated);
|
C4KeyCodeEx Key(vk_code, C4KeyShiftState(fAlt*KEYS_Alt + fCtrl*KEYS_Control + fShift*KEYS_Shift), fRepeated);
|
||||||
return DoKeyboardInput(Key, eEventType, pForDialog, fPlrCtrlOnly, iStrength);
|
return DoKeyboardInput(Key, eEventType, pForDialog, fPlrCtrlOnly, iStrength);
|
||||||
|
|
|
@ -60,16 +60,7 @@ bool C4Viewport::UpdateOutputSize(int32_t new_width, int32_t new_height)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef USE_GTK
|
#if defined(WITH_QT_EDITOR)
|
||||||
GtkAllocation allocation;
|
|
||||||
gtk_widget_get_allocation(GTK_WIDGET(pWindow->render_widget), &allocation);
|
|
||||||
|
|
||||||
// Use only size of drawing area without scrollbars
|
|
||||||
rect.x = allocation.x;
|
|
||||||
rect.y = allocation.y;
|
|
||||||
rect.Wdt = allocation.width;
|
|
||||||
rect.Hgt = allocation.height;
|
|
||||||
#elif defined(WITH_QT_EDITOR)
|
|
||||||
// Never query the window - size is always passed from Qt.
|
// Never query the window - size is always passed from Qt.
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -127,8 +127,6 @@ protected:
|
||||||
C4Window * pWindow; // window to draw in
|
C4Window * pWindow; // window to draw in
|
||||||
#ifdef USE_WGL
|
#ifdef USE_WGL
|
||||||
HDC hDC; // device context handle
|
HDC hDC; // device context handle
|
||||||
#elif defined(USE_GTK)
|
|
||||||
/*GLXContext*/void * ctx;
|
|
||||||
#elif defined(USE_SDL_MAINLOOP)
|
#elif defined(USE_SDL_MAINLOOP)
|
||||||
void * ctx;
|
void * ctx;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -438,153 +438,6 @@ bool CStdGLCtx::PageFlip()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(USE_GTK)
|
|
||||||
#include <GL/glxew.h>
|
|
||||||
#include <GL/glx.h>
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gdk/gdkx.h>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
void InitGLXPointers()
|
|
||||||
{
|
|
||||||
glXGetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC)(glXGetProcAddress((const GLubyte*)"glXGetVisualFromFBConfig"));
|
|
||||||
glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)(glXGetProcAddress((const GLubyte*)"glXChooseFBConfig"));
|
|
||||||
glXCreateNewContext = (PFNGLXCREATENEWCONTEXTPROC)(glXGetProcAddress((const GLubyte*)"glXCreateNewContext"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CStdGLCtx::CStdGLCtx(): pWindow(0), ctx(0), this_context(contexts.end()) { }
|
|
||||||
|
|
||||||
void CStdGLCtx::Clear(bool multisample_change)
|
|
||||||
{
|
|
||||||
Deselect();
|
|
||||||
if (ctx)
|
|
||||||
{
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
glXDestroyContext(dpy, (GLXContext)ctx);
|
|
||||||
ctx = 0;
|
|
||||||
}
|
|
||||||
pWindow = 0;
|
|
||||||
|
|
||||||
if (this_context != contexts.end())
|
|
||||||
{
|
|
||||||
contexts.erase(this_context);
|
|
||||||
this_context = contexts.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
|
|
||||||
{
|
|
||||||
// safety
|
|
||||||
if (!pGL) return false;
|
|
||||||
// store window
|
|
||||||
this->pWindow = pWindow;
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
InitGLXPointers();
|
|
||||||
if (!glXGetVisualFromFBConfig || !glXChooseFBConfig || !glXCreateNewContext)
|
|
||||||
{
|
|
||||||
return pGL->Error(" gl: Unable to retrieve GLX 1.4 entry points");
|
|
||||||
}
|
|
||||||
XVisualInfo *vis_info = glXGetVisualFromFBConfig(dpy, pWindow->Info);
|
|
||||||
// Create base context so we can initialize GLEW
|
|
||||||
GLXContext dummy_ctx = glXCreateContext(dpy, vis_info, 0, True);
|
|
||||||
XFree(vis_info);
|
|
||||||
glXMakeCurrent(dpy, pWindow->renderwnd, dummy_ctx);
|
|
||||||
glewExperimental = GL_TRUE;
|
|
||||||
GLenum err = glewInit();
|
|
||||||
if (err != GLEW_OK)
|
|
||||||
{
|
|
||||||
return pGL->Error((const char*)glewGetErrorString(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Context with sharing (if this is the main context, our ctx will be 0, so no sharing)
|
|
||||||
const int attribs[] = {
|
|
||||||
GLX_CONTEXT_MAJOR_VERSION_ARB, REQUESTED_GL_CTX_MAJOR,
|
|
||||||
GLX_CONTEXT_MINOR_VERSION_ARB, REQUESTED_GL_CTX_MINOR,
|
|
||||||
GLX_CONTEXT_FLAGS_ARB, (Config.Graphics.DebugOpenGL ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
|
|
||||||
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
||||||
None
|
|
||||||
};
|
|
||||||
GLXContext share_context = (pGL->pMainCtx != this) ? static_cast<GLXContext>(pGL->pMainCtx->ctx) : 0;
|
|
||||||
|
|
||||||
if (glXCreateContextAttribsARB)
|
|
||||||
{
|
|
||||||
gdk_x11_display_error_trap_push(gdk_display_get_default());
|
|
||||||
ctx = glXCreateContextAttribsARB(dpy, pWindow->Info, share_context, True, attribs);
|
|
||||||
gdk_x11_display_error_trap_pop_ignored(gdk_display_get_default());
|
|
||||||
}
|
|
||||||
if(!ctx) {
|
|
||||||
Log(" gl: falling back to attribute-less context creation.");
|
|
||||||
ctx = glXCreateNewContext(dpy, pWindow->Info, GLX_RGBA_TYPE, share_context, True);
|
|
||||||
}
|
|
||||||
|
|
||||||
glXMakeCurrent(dpy, None, NULL);
|
|
||||||
glXDestroyContext(dpy, dummy_ctx);
|
|
||||||
|
|
||||||
// No luck?
|
|
||||||
if (!ctx) return pGL->Error(" gl: Unable to create context");
|
|
||||||
if (!Select(true)) return pGL->Error(" gl: Unable to select context");
|
|
||||||
// init extensions
|
|
||||||
glewExperimental = GL_TRUE;
|
|
||||||
err = glewInit();
|
|
||||||
if (GLEW_OK != err)
|
|
||||||
{
|
|
||||||
// Problem: glewInit failed, something is seriously wrong.
|
|
||||||
return pGL->Error(reinterpret_cast<const char*>(glewGetErrorString(err)));
|
|
||||||
}
|
|
||||||
|
|
||||||
this_context = contexts.insert(contexts.end(), this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStdGLCtx::Select(bool verbose)
|
|
||||||
{
|
|
||||||
// safety
|
|
||||||
if (!pGL || !ctx)
|
|
||||||
{
|
|
||||||
if (verbose) pGL->Error(" gl: pGL is zero");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
// make context current
|
|
||||||
if (!pWindow->renderwnd || !glXMakeCurrent(dpy, pWindow->renderwnd, (GLXContext)ctx))
|
|
||||||
{
|
|
||||||
if (verbose) pGL->Error(" gl: glXMakeCurrent failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SelectCommon();
|
|
||||||
// update clipper - might have been done by UpdateSize
|
|
||||||
// however, the wrong size might have been assumed
|
|
||||||
if (!pGL->UpdateClipper())
|
|
||||||
{
|
|
||||||
if (verbose) pGL->Error(" gl: UpdateClipper failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// success
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStdGLCtx::Deselect()
|
|
||||||
{
|
|
||||||
if (pGL && pGL->pCurrCtx == this)
|
|
||||||
{
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
glXMakeCurrent(dpy, None, NULL);
|
|
||||||
pGL->pCurrCtx = 0;
|
|
||||||
pGL->RenderTarget = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStdGLCtx::PageFlip()
|
|
||||||
{
|
|
||||||
// flush GL buffer
|
|
||||||
glFlush();
|
|
||||||
if (!pWindow || !pWindow->renderwnd) return false;
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
glXSwapBuffers(dpy, pWindow->renderwnd);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(USE_SDL_MAINLOOP)
|
#elif defined(USE_SDL_MAINLOOP)
|
||||||
|
|
||||||
CStdGLCtx::CStdGLCtx(): pWindow(0), this_context(contexts.end()) { ctx = NULL; }
|
CStdGLCtx::CStdGLCtx(): pWindow(0), this_context(contexts.end()) { ctx = NULL; }
|
||||||
|
@ -663,7 +516,7 @@ bool CStdGLCtx::PageFlip()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //USE_GTK/USE_SDL_MAINLOOP
|
#endif // USE_*
|
||||||
|
|
||||||
#ifdef WITH_QT_EDITOR
|
#ifdef WITH_QT_EDITOR
|
||||||
#undef LineFeed // conflicts with Qt
|
#undef LineFeed // conflicts with Qt
|
||||||
|
|
|
@ -22,13 +22,6 @@
|
||||||
#include "game/C4Game.h"
|
#include "game/C4Game.h"
|
||||||
#include "platform/C4Window.h"
|
#include "platform/C4Window.h"
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
#include <gdk/gdkx.h>
|
|
||||||
#include <X11/XKBlib.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
@ -412,7 +405,7 @@ StdStrBuf C4KeyCodeEx::KeyCode2String(C4KeyCode wCode, bool fHumanReadable, bool
|
||||||
// for config files and such: dump scancode
|
// for config files and such: dump scancode
|
||||||
return FormatString("$%x", static_cast<unsigned int>(wCode));
|
return FormatString("$%x", static_cast<unsigned int>(wCode));
|
||||||
}
|
}
|
||||||
#if defined(USE_WIN32_WINDOWS) || (defined(_WIN32) && defined(USE_GTK))
|
#if defined(USE_WIN32_WINDOWS)
|
||||||
|
|
||||||
// Query map
|
// Query map
|
||||||
const C4KeyCodeMapEntry *pCheck = KeyCodeMap;
|
const C4KeyCodeMapEntry *pCheck = KeyCodeMap;
|
||||||
|
@ -442,20 +435,6 @@ StdStrBuf C4KeyCodeEx::KeyCode2String(C4KeyCode wCode, bool fHumanReadable, bool
|
||||||
if (wCode == pCheck->wCode) return StdStrBuf((pCheck->szShortName && fShort) ? pCheck->szShortName : pCheck->szName); else ++pCheck;
|
if (wCode == pCheck->wCode) return StdStrBuf((pCheck->szShortName && fShort) ? pCheck->szShortName : pCheck->szName); else ++pCheck;
|
||||||
// not found: Compose as direct code
|
// not found: Compose as direct code
|
||||||
return FormatString("\\x%x", static_cast<unsigned int>(wCode));
|
return FormatString("\\x%x", static_cast<unsigned int>(wCode));
|
||||||
#elif defined(USE_GTK)
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
KeySym keysym = (KeySym)XkbKeycodeToKeysym(dpy,wCode+8,0,0);
|
|
||||||
char* name = NULL;
|
|
||||||
if (keysym != NoSymbol) { // is the keycode without shift modifiers mapped to a symbol?
|
|
||||||
name = gtk_accelerator_get_label_with_keycode(gdk_display_get_default(), keysym, wCode+8, (GdkModifierType)0);
|
|
||||||
}
|
|
||||||
if (name) { // is there a string representation of the keysym?
|
|
||||||
// prevent memleak
|
|
||||||
StdStrBuf buf;
|
|
||||||
buf.Copy(name);
|
|
||||||
g_free(name);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
#elif defined(USE_SDL_MAINLOOP)
|
#elif defined(USE_SDL_MAINLOOP)
|
||||||
StdStrBuf buf;
|
StdStrBuf buf;
|
||||||
buf.Copy(SDL_GetScancodeName(static_cast<SDL_Scancode>(wCode)));
|
buf.Copy(SDL_GetScancodeName(static_cast<SDL_Scancode>(wCode)));
|
||||||
|
|
|
@ -122,11 +122,7 @@ public:
|
||||||
pthread_t MainThread;
|
pthread_t MainThread;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(USE_GTK)
|
#if defined(USE_SDL_MAINLOOP)
|
||||||
protected:
|
|
||||||
class C4X11AppImpl * Priv;
|
|
||||||
|
|
||||||
#elif defined(USE_SDL_MAINLOOP)
|
|
||||||
public:
|
public:
|
||||||
void HandleSDLEvent(SDL_Event& event);
|
void HandleSDLEvent(SDL_Event& event);
|
||||||
|
|
||||||
|
|
|
@ -1,378 +0,0 @@
|
||||||
/*
|
|
||||||
* OpenClonk, http://www.openclonk.org
|
|
||||||
*
|
|
||||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
||||||
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
||||||
*
|
|
||||||
* Distributed under the terms of the ISC license; see accompanying file
|
|
||||||
* "COPYING" for details.
|
|
||||||
*
|
|
||||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
||||||
* See accompanying file "TRADEMARK" for details.
|
|
||||||
*
|
|
||||||
* To redistribute this file separately, substitute the full license texts
|
|
||||||
* for the above references.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* A wrapper class to OS dependent event and window interfaces, X11 version */
|
|
||||||
|
|
||||||
#include "C4Include.h"
|
|
||||||
#include "platform/C4App.h"
|
|
||||||
|
|
||||||
#include "platform/C4Window.h"
|
|
||||||
#include "graphics/C4DrawGL.h"
|
|
||||||
#include "graphics/C4Draw.h"
|
|
||||||
#include "platform/StdFile.h"
|
|
||||||
#include "lib/StdBuf.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <X11/extensions/Xrandr.h>
|
|
||||||
#include <gdk/gdkx.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class C4GLibProc: public StdSchedulerProc
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
C4GLibProc(GMainContext *context): context(context), query_time(C4TimeMilliseconds::NegativeInfinity) { fds.resize(1); g_main_context_ref(context); }
|
|
||||||
~C4GLibProc()
|
|
||||||
{
|
|
||||||
g_main_context_unref(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
GMainContext *context;
|
|
||||||
#ifdef STDSCHEDULER_USE_EVENTS
|
|
||||||
std::vector<GPollFD> fds;
|
|
||||||
#else
|
|
||||||
std::vector<pollfd> fds;
|
|
||||||
#endif
|
|
||||||
C4TimeMilliseconds query_time;
|
|
||||||
int timeout;
|
|
||||||
int max_priority;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Obtain the timeout and FDs from the glib mainloop. We then pass them
|
|
||||||
// to the StdScheduler in GetFDs() and GetNextTick() so that it can
|
|
||||||
// poll the file descriptors, along with the file descriptors from
|
|
||||||
// other sources that it might have.
|
|
||||||
void query(C4TimeMilliseconds Now)
|
|
||||||
{
|
|
||||||
// If Execute() has not yet been called, then finish the current iteration first.
|
|
||||||
// Note that we cannot simply ignore the query() call, as new
|
|
||||||
// FDs or Timeouts may have been added to the Glib loop in the meanwhile
|
|
||||||
if (!query_time.IsInfinite())
|
|
||||||
{
|
|
||||||
//g_main_context_check(context, max_priority, fds.empty() ? NULL : (GPollFD*) &fds[0], fds.size());
|
|
||||||
Execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
g_main_context_prepare (context, &max_priority);
|
|
||||||
unsigned int fd_count;
|
|
||||||
if (fds.empty()) fds.resize(1);
|
|
||||||
while ((fd_count = g_main_context_query(context, max_priority, &timeout, (GPollFD*) &fds[0], fds.size())) > fds.size())
|
|
||||||
{
|
|
||||||
fds.resize(fd_count);
|
|
||||||
}
|
|
||||||
// Make sure we don't report more FDs than there are available
|
|
||||||
fds.resize(fd_count);
|
|
||||||
query_time = Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Iterate the Glib main loop until all pending events have been
|
|
||||||
// processed. Don't use g_main_context_pending() directly as the
|
|
||||||
// C4GLibProc might have initiated a loop iteration already.
|
|
||||||
// This is mainly used to update the log in the editor window while
|
|
||||||
// a scenario is being loaded.
|
|
||||||
void IteratePendingEvents()
|
|
||||||
{
|
|
||||||
// TODO: I think we can also iterate the context manually,
|
|
||||||
// without g_main_context_iteration. This might be less hacky.
|
|
||||||
|
|
||||||
// Finish current iteration first
|
|
||||||
C4TimeMilliseconds old_query_time = C4TimeMilliseconds::NegativeInfinity;
|
|
||||||
if (!query_time.IsInfinite())
|
|
||||||
{
|
|
||||||
old_query_time = query_time;
|
|
||||||
//g_main_context_check(context, max_priority, fds.empty() ? NULL : (GPollFD*) &fds[0], fds.size());
|
|
||||||
//query_time = C4TimeMilliseconds::NegativeInfinity;
|
|
||||||
Execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the loop
|
|
||||||
while (g_main_context_pending(context))
|
|
||||||
g_main_context_iteration(context, false);
|
|
||||||
|
|
||||||
// Return to original state
|
|
||||||
if (!old_query_time.IsInfinite())
|
|
||||||
query(old_query_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
// StdSchedulerProc override
|
|
||||||
#ifdef STDSCHEDULER_USE_EVENTS
|
|
||||||
virtual HANDLE GetEvent()
|
|
||||||
{
|
|
||||||
return reinterpret_cast<HANDLE>(fds[0].fd);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
virtual void GetFDs(std::vector<struct pollfd> & rfds)
|
|
||||||
{
|
|
||||||
if (query_time.IsInfinite()) query(C4TimeMilliseconds::Now());
|
|
||||||
rfds.insert(rfds.end(), fds.begin(), fds.end());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
virtual C4TimeMilliseconds GetNextTick(C4TimeMilliseconds Now)
|
|
||||||
{
|
|
||||||
query(Now);
|
|
||||||
if (timeout < 0) return C4TimeMilliseconds::PositiveInfinity;
|
|
||||||
return query_time + timeout;
|
|
||||||
}
|
|
||||||
virtual bool Execute(int iTimeout = -1, pollfd * readyfds = 0)
|
|
||||||
{
|
|
||||||
if (query_time.IsInfinite()) return true;
|
|
||||||
g_main_context_check(context, max_priority, fds.empty() ? NULL : readyfds ? (GPollFD*) readyfds : (GPollFD*) &fds[0], fds.size());
|
|
||||||
|
|
||||||
// g_main_context_dispatch makes callbacks from the main loop.
|
|
||||||
// We allow the callback to iterate the mainloop via
|
|
||||||
// IteratePendingEvents so reset query_time before to not call
|
|
||||||
// g_main_context_check() twice for the current iteration.
|
|
||||||
// This would otherwise lead to a freeze since
|
|
||||||
// g_main_context_check() seems to block when called twice.
|
|
||||||
query_time = C4TimeMilliseconds::NegativeInfinity;
|
|
||||||
g_main_context_dispatch(context);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class C4X11AppImpl
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
C4GLibProc GLibProc;
|
|
||||||
C4X11AppImpl(C4AbstractApp *pApp):
|
|
||||||
GLibProc(g_main_context_default()),
|
|
||||||
xrandr_major_version(-1), xrandr_minor_version(-1),
|
|
||||||
xrandr_oldmode(-1),
|
|
||||||
xrandr_rot(0),
|
|
||||||
xrandr_event(-1),
|
|
||||||
argc(0), argv(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int xrandr_major_version, xrandr_minor_version;
|
|
||||||
int xrandr_oldmode;
|
|
||||||
unsigned short xrandr_rot;
|
|
||||||
int xrandr_event;
|
|
||||||
|
|
||||||
int argc; char ** argv;
|
|
||||||
};
|
|
||||||
|
|
||||||
C4AbstractApp::C4AbstractApp(): Active(false), fQuitMsgReceived(false),
|
|
||||||
// main thread
|
|
||||||
#ifdef _WIN32
|
|
||||||
hInstance(NULL),
|
|
||||||
idMainThread(::GetCurrentThreadId()),
|
|
||||||
#elif defined(HAVE_PTHREAD)
|
|
||||||
MainThread (pthread_self()),
|
|
||||||
#endif
|
|
||||||
Priv(new C4X11AppImpl(this)), fDspModeSet(false)
|
|
||||||
{
|
|
||||||
Add(&Priv->GLibProc);
|
|
||||||
}
|
|
||||||
|
|
||||||
C4AbstractApp::~C4AbstractApp()
|
|
||||||
{
|
|
||||||
Remove(&Priv->GLibProc);
|
|
||||||
delete Priv;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C4AbstractApp::Init(int argc, char * argv[])
|
|
||||||
{
|
|
||||||
// Set locale
|
|
||||||
setlocale(LC_ALL,"");
|
|
||||||
gtk_init(&argc, &argv);
|
|
||||||
|
|
||||||
GdkPixbuf* icon = gdk_pixbuf_new_from_resource("/org/openclonk/engine/oc.ico", NULL);
|
|
||||||
gtk_window_set_default_icon(icon);
|
|
||||||
g_object_unref(icon);
|
|
||||||
// Try to figure out the location of the executable
|
|
||||||
Priv->argc=argc; Priv->argv=argv;
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
int xrandr_error_base;
|
|
||||||
if (!XRRQueryExtension(dpy, &Priv->xrandr_event, &xrandr_error_base)
|
|
||||||
|| !XRRQueryVersion(dpy, &Priv->xrandr_major_version, &Priv->xrandr_minor_version))
|
|
||||||
{
|
|
||||||
Priv->xrandr_major_version = -1;
|
|
||||||
Priv->xrandr_minor_version = 0;
|
|
||||||
}
|
|
||||||
if (Priv->xrandr_major_version >= 0)
|
|
||||||
{
|
|
||||||
XRRSelectInput(dpy, DefaultRootWindow(dpy), RRScreenChangeNotifyMask);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Log("The Xrandr extension is missing. Resolution switching will not work.");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Custom initialization
|
|
||||||
return DoInit (argc, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_clipboard_store_all (void)
|
|
||||||
{
|
|
||||||
GtkClipboard *clipboard;
|
|
||||||
GSList *displays, *list;
|
|
||||||
|
|
||||||
displays = gdk_display_manager_list_displays (gdk_display_manager_get ());
|
|
||||||
|
|
||||||
list = displays;
|
|
||||||
while (list)
|
|
||||||
{
|
|
||||||
GdkDisplay *display = static_cast<GdkDisplay *>(list->data);
|
|
||||||
|
|
||||||
clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
|
|
||||||
|
|
||||||
if (clipboard)
|
|
||||||
gtk_clipboard_store (clipboard);
|
|
||||||
|
|
||||||
list = list->next;
|
|
||||||
}
|
|
||||||
g_slist_free (displays);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4AbstractApp::Clear()
|
|
||||||
{
|
|
||||||
gtk_clipboard_store_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4AbstractApp::Quit()
|
|
||||||
{
|
|
||||||
fQuitMsgReceived = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C4AbstractApp::FlushMessages()
|
|
||||||
{
|
|
||||||
// Always fail after quit message
|
|
||||||
if (fQuitMsgReceived)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Priv->GLibProc.IteratePendingEvents();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C4AbstractApp::SetVideoMode(int iXRes, int iYRes, unsigned int iRefreshRate, unsigned int iMonitor, bool fFullScreen)
|
|
||||||
{
|
|
||||||
if (!fFullScreen)
|
|
||||||
{
|
|
||||||
RestoreVideoMode();
|
|
||||||
if (iXRes != -1)
|
|
||||||
pWindow->SetSize(iXRes, iYRes);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
if (Priv->xrandr_major_version >= 0 && !(iXRes == -1 && iYRes == -1))
|
|
||||||
{
|
|
||||||
// randr spec says to always get fresh info, so don't cache.
|
|
||||||
XRRScreenConfiguration * conf = XRRGetScreenInfo (dpy, pWindow->renderwnd);
|
|
||||||
if (Priv->xrandr_oldmode == -1)
|
|
||||||
Priv->xrandr_oldmode = XRRConfigCurrentConfiguration (conf, &Priv->xrandr_rot);
|
|
||||||
int n;
|
|
||||||
XRRScreenSize * sizes = XRRConfigSizes(conf, &n);
|
|
||||||
for (int i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
if (int(sizes[i].width) == iXRes && int(sizes[i].height) == iYRes)
|
|
||||||
{
|
|
||||||
#ifdef _DEBUG
|
|
||||||
LogF("XRRSetScreenConfig %d", i);
|
|
||||||
#endif
|
|
||||||
fDspModeSet = XRRSetScreenConfig(dpy, conf, pWindow->renderwnd, i, Priv->xrandr_rot, CurrentTime) == RRSetConfigSuccess;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XRRFreeScreenConfigInfo(conf);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
gtk_window_fullscreen(GTK_WINDOW(pWindow->window));
|
|
||||||
return fDspModeSet || (iXRes == -1 && iYRes == -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4AbstractApp::RestoreVideoMode()
|
|
||||||
{
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
// Restore resolution
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
if (fDspModeSet && Priv->xrandr_major_version >= 0 && Priv->xrandr_oldmode != -1)
|
|
||||||
{
|
|
||||||
XRRScreenConfiguration * conf = XRRGetScreenInfo (dpy, pWindow->renderwnd);
|
|
||||||
#ifdef _DEBUG
|
|
||||||
LogF("XRRSetScreenConfig %d (back)", Priv->xrandr_oldmode);
|
|
||||||
#endif
|
|
||||||
XRRSetScreenConfig (dpy, conf, pWindow->renderwnd, Priv->xrandr_oldmode, Priv->xrandr_rot, CurrentTime);
|
|
||||||
Priv->xrandr_oldmode = -1;
|
|
||||||
XRRFreeScreenConfigInfo(conf);
|
|
||||||
fDspModeSet = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// pWindow may be unset when C4AbstractApp gets destroyed during the
|
|
||||||
// initialization code, before a window has been created
|
|
||||||
if (pWindow)
|
|
||||||
gtk_window_unfullscreen(GTK_WINDOW(pWindow->window));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C4AbstractApp::GetIndexedDisplayMode(int32_t iIndex, int32_t *piXRes, int32_t *piYRes, int32_t *piBitDepth, int32_t *piRefreshRate, uint32_t iMonitor)
|
|
||||||
{
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
int n;
|
|
||||||
XRRScreenSize * sizes = XRRSizes(dpy, XDefaultScreen(dpy), &n);
|
|
||||||
if (iIndex < n && iIndex >= 0)
|
|
||||||
{
|
|
||||||
*piXRes = sizes[iIndex].width;
|
|
||||||
*piYRes = sizes[iIndex].height;
|
|
||||||
*piBitDepth = 32;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the text to the clipboard or the primary selection
|
|
||||||
bool C4AbstractApp::Copy(const StdStrBuf & text, bool fClipboard)
|
|
||||||
{
|
|
||||||
gtk_clipboard_set_text(gtk_clipboard_get(fClipboard ? GDK_SELECTION_CLIPBOARD : GDK_SELECTION_PRIMARY),
|
|
||||||
text.getData(), text.getLength());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Paste the text from the clipboard or the primary selection
|
|
||||||
StdStrBuf C4AbstractApp::Paste(bool fClipboard)
|
|
||||||
{
|
|
||||||
char * r = gtk_clipboard_wait_for_text(gtk_clipboard_get(fClipboard ? GDK_SELECTION_CLIPBOARD : GDK_SELECTION_PRIMARY));
|
|
||||||
// gtk_clipboard_request_text(gtk_clipboard_get(fClipboard ? GDK_SELECTION_CLIPBOARD : GDK_SELECTION_PRIMARY),
|
|
||||||
// GtkClipboardTextReceivedFunc callback, gpointer user_data);
|
|
||||||
StdStrBuf rbuf;
|
|
||||||
rbuf.Copy(r);
|
|
||||||
g_free(r);
|
|
||||||
return rbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is there something in the clipboard?
|
|
||||||
bool C4AbstractApp::IsClipboardFull(bool fClipboard)
|
|
||||||
{
|
|
||||||
return gtk_clipboard_wait_is_text_available(gtk_clipboard_get(fClipboard ? GDK_SELECTION_CLIPBOARD : GDK_SELECTION_PRIMARY));
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4AbstractApp::MessageDialog(const char * message)
|
|
||||||
{
|
|
||||||
GtkWidget * dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", message);
|
|
||||||
gtk_window_set_title(GTK_WINDOW(dialog), "OpenClonk Error");
|
|
||||||
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
||||||
gtk_widget_destroy (dialog);
|
|
||||||
}
|
|
|
@ -21,18 +21,7 @@
|
||||||
|
|
||||||
#include "lib/StdBuf.h"
|
#include "lib/StdBuf.h"
|
||||||
|
|
||||||
#if defined(USE_GTK)
|
#if defined(USE_SDL_MAINLOOP)
|
||||||
#ifdef _WIN32
|
|
||||||
#undef MK_CONTROL
|
|
||||||
#undef MK_SHIFT
|
|
||||||
#endif
|
|
||||||
// from X.h:
|
|
||||||
//#define ShiftMask (1<<0)
|
|
||||||
//#define ControlMask (1<<2)
|
|
||||||
#define MK_CONTROL (1<<2)
|
|
||||||
#define MK_SHIFT (1<<0)
|
|
||||||
#define MK_ALT (1<<3)
|
|
||||||
#elif defined(USE_SDL_MAINLOOP)
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#define MK_SHIFT (KMOD_LSHIFT | KMOD_RSHIFT)
|
#define MK_SHIFT (KMOD_LSHIFT | KMOD_RSHIFT)
|
||||||
#define MK_CONTROL (KMOD_LCTRL | KMOD_RCTRL)
|
#define MK_CONTROL (KMOD_LCTRL | KMOD_RCTRL)
|
||||||
|
@ -59,7 +48,7 @@ extern int MK_ALT;
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(USE_WIN32_WINDOWS) || defined(USE_GTK) || defined(USE_CONSOLE) || defined(USE_SDL_MAINLOOP)
|
#if defined(USE_WIN32_WINDOWS) || defined(USE_CONSOLE) || defined(USE_SDL_MAINLOOP)
|
||||||
#define K_ESCAPE 1
|
#define K_ESCAPE 1
|
||||||
#define K_1 2
|
#define K_1 2
|
||||||
#define K_2 3
|
#define K_2 3
|
||||||
|
@ -267,10 +256,6 @@ extern C4KeyCode K_PRINT;
|
||||||
extern C4KeyCode K_CENTER;
|
extern C4KeyCode K_CENTER;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_GTK
|
|
||||||
// Forward declaration because xlib.h is evil
|
|
||||||
typedef struct __GLXFBConfigRec *GLXFBConfig;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class C4Window
|
class C4Window
|
||||||
#ifdef USE_COCOA
|
#ifdef USE_COCOA
|
||||||
|
@ -325,33 +310,17 @@ public:
|
||||||
#if defined(USE_WIN32_WINDOWS)
|
#if defined(USE_WIN32_WINDOWS)
|
||||||
HWND hWindow;
|
HWND hWindow;
|
||||||
virtual bool Win32DialogMessageHandling(MSG * msg) { return false; };
|
virtual bool Win32DialogMessageHandling(MSG * msg) { return false; };
|
||||||
#elif defined(USE_GTK)
|
|
||||||
/*GtkWidget*/void * window;
|
|
||||||
// Set by Init to the widget which is used as a
|
|
||||||
// render target, which can be the whole window.
|
|
||||||
/*GtkWidget*/void * render_widget;
|
|
||||||
// Mouse grabbing has to be restored when the window gains focus.
|
|
||||||
bool mouse_was_grabbed = false;
|
|
||||||
#elif defined(USE_SDL_MAINLOOP)
|
#elif defined(USE_SDL_MAINLOOP)
|
||||||
SDL_Window * window;
|
SDL_Window * window;
|
||||||
void HandleSDLEvent(SDL_WindowEvent &e);
|
void HandleSDLEvent(SDL_WindowEvent &e);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_WGL
|
#ifdef USE_WGL
|
||||||
HWND renderwnd;
|
HWND renderwnd;
|
||||||
#elif defined(USE_GTK)
|
|
||||||
unsigned long renderwnd;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef WITH_QT_EDITOR
|
#ifdef WITH_QT_EDITOR
|
||||||
class QOpenGLWidget *glwidget;
|
class QOpenGLWidget *glwidget;
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
#if defined(USE_GTK)
|
|
||||||
bool FindFBConfig(int samples, GLXFBConfig *info);
|
|
||||||
|
|
||||||
// The GLXFBConfig the window was created with
|
|
||||||
GLXFBConfig Info;
|
|
||||||
unsigned long handlerDestroy;
|
|
||||||
#endif
|
|
||||||
virtual C4Window * Init(WindowKind windowKind, C4AbstractApp * pApp, const char * Title, const C4Rect * size);
|
virtual C4Window * Init(WindowKind windowKind, C4AbstractApp * pApp, const char * Title, const C4Rect * size);
|
||||||
friend class C4Draw;
|
friend class C4Draw;
|
||||||
friend class CStdGL;
|
friend class CStdGL;
|
||||||
|
|
|
@ -1,949 +0,0 @@
|
||||||
/*
|
|
||||||
* OpenClonk, http://www.openclonk.org
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
||||||
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
||||||
*
|
|
||||||
* Distributed under the terms of the ISC license; see accompanying file
|
|
||||||
* "COPYING" for details.
|
|
||||||
*
|
|
||||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
||||||
* See accompanying file "TRADEMARK" for details.
|
|
||||||
*
|
|
||||||
* To redistribute this file separately, substitute the full license texts
|
|
||||||
* for the above references.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* A wrapper class to OS dependent event and window interfaces, GTK+ version */
|
|
||||||
|
|
||||||
#include "C4Include.h"
|
|
||||||
#include "platform/C4Window.h"
|
|
||||||
|
|
||||||
#include "platform/C4App.h"
|
|
||||||
#include "C4Version.h"
|
|
||||||
#include "config/C4Config.h"
|
|
||||||
|
|
||||||
#include "graphics/C4DrawGL.h"
|
|
||||||
#include "graphics/C4Draw.h"
|
|
||||||
#include "platform/StdFile.h"
|
|
||||||
#include "lib/StdBuf.h"
|
|
||||||
|
|
||||||
#include "lib/C4Rect.h"
|
|
||||||
|
|
||||||
#include "editor/C4Console.h"
|
|
||||||
#include "editor/C4ViewportWindow.h"
|
|
||||||
#include "game/C4Viewport.h"
|
|
||||||
#include "gui/C4MouseControl.h"
|
|
||||||
|
|
||||||
#include <gdk/gdk.h>
|
|
||||||
#include <gdk/gdkkeysyms.h>
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
#include <gdk/gdkx.h>
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <GL/glx.h>
|
|
||||||
|
|
||||||
// Some helper functions for choosing a proper visual
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
static const std::map<int, int> base_attrib_map {
|
|
||||||
{GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT},
|
|
||||||
{GLX_X_RENDERABLE, True},
|
|
||||||
{GLX_RED_SIZE, 4},
|
|
||||||
{GLX_GREEN_SIZE, 4},
|
|
||||||
{GLX_BLUE_SIZE, 4},
|
|
||||||
{GLX_DEPTH_SIZE, 8}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Turns an int->int map into an attribute list suitable for any GLX calls.
|
|
||||||
std::unique_ptr<int[]> MakeGLXAttribList(const std::map<int, int> &map)
|
|
||||||
{
|
|
||||||
// We need two ints for every attribute, plus one as a sentinel
|
|
||||||
auto list = std::make_unique<int[]>(map.size() * 2 + 1);
|
|
||||||
int *cursor = list.get();
|
|
||||||
for(const auto &attrib : map)
|
|
||||||
{
|
|
||||||
*cursor++ = attrib.first;
|
|
||||||
*cursor++ = attrib.second;
|
|
||||||
}
|
|
||||||
*cursor = None;
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function picks an acceptable GLXFBConfig. To do this, we first
|
|
||||||
// request a list of framebuffer configs with no less than 4 bits per color;
|
|
||||||
// no less than 8 bits of depth buffer; if multisampling is not -1,
|
|
||||||
// with at least the requested number of samples; and with double buffering.
|
|
||||||
// If that returns no suitable configs, we retry with only a single buffer.
|
|
||||||
GLXFBConfig PickGLXFBConfig(Display* dpy, int multisampling)
|
|
||||||
{
|
|
||||||
std::map<int, int> attrib_map = base_attrib_map;
|
|
||||||
|
|
||||||
if (multisampling >= 0)
|
|
||||||
{
|
|
||||||
attrib_map[GLX_SAMPLE_BUFFERS] = multisampling > 0 ? 1 : 0;
|
|
||||||
attrib_map[GLX_SAMPLES] = multisampling;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLXFBConfig *configs = NULL;
|
|
||||||
int config_count;
|
|
||||||
// Find a double-buffered FB config
|
|
||||||
attrib_map[GLX_DOUBLEBUFFER] = True;
|
|
||||||
std::unique_ptr<int[]> attribs = MakeGLXAttribList(attrib_map);
|
|
||||||
configs = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs.get(), &config_count);
|
|
||||||
if (config_count == 0)
|
|
||||||
{
|
|
||||||
// If none exists, try to find a single-buffered one
|
|
||||||
if (configs != NULL)
|
|
||||||
XFree(configs);
|
|
||||||
attrib_map[GLX_DOUBLEBUFFER] = False;
|
|
||||||
attribs = MakeGLXAttribList(attrib_map);
|
|
||||||
configs = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs.get(), &config_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLXFBConfig config = NULL;
|
|
||||||
if (config_count > 0)
|
|
||||||
{
|
|
||||||
config = configs[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
XFree(configs);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif defined(GDK_WINDOWING_WIN32)
|
|
||||||
#include "platform/C4windowswrapper.h"
|
|
||||||
#include <gdk/gdkwin32.h>
|
|
||||||
#endif // GDK_WINDOWING_X11
|
|
||||||
|
|
||||||
static void OnDestroyStatic(GtkWidget* widget, gpointer data)
|
|
||||||
{
|
|
||||||
C4Window* wnd = static_cast<C4Window*>(data);
|
|
||||||
wnd->Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnDelete(GtkWidget* widget, GdkEvent* event, gpointer data)
|
|
||||||
{
|
|
||||||
C4Window* wnd = static_cast<C4Window*>(data);
|
|
||||||
wnd->Close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
static constexpr int x11scancodeoffset = 8;
|
|
||||||
#else
|
|
||||||
static constexpr int x11scancodeoffset = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static gboolean OnKeyPress(GtkWidget* widget, GdkEventKey* event, gpointer data)
|
|
||||||
{
|
|
||||||
C4Window* wnd = static_cast<C4Window*>(data);
|
|
||||||
if (event->hardware_keycode <= x11scancodeoffset) return false;
|
|
||||||
Game.DoKeyboardInput(event->hardware_keycode - x11scancodeoffset, KEYEV_Down,
|
|
||||||
!!(event->state & GDK_MOD1_MASK),
|
|
||||||
!!(event->state & GDK_CONTROL_MASK),
|
|
||||||
!!(event->state & GDK_SHIFT_MASK), false, NULL);
|
|
||||||
wnd->CharIn(event->string); // FIXME: Use GtkIMContext somehow
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnKeyRelease(GtkWidget* widget, GdkEventKey* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
if (event->hardware_keycode <= x11scancodeoffset) return false;
|
|
||||||
Game.DoKeyboardInput(event->hardware_keycode - x11scancodeoffset, KEYEV_Up,
|
|
||||||
!!(event->state & GDK_MOD1_MASK),
|
|
||||||
!!(event->state & GDK_CONTROL_MASK),
|
|
||||||
!!(event->state & GDK_SHIFT_MASK), false, NULL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnDragDataReceivedStatic(GtkWidget* widget, GdkDragContext* context, gint x, gint y, GtkSelectionData* data, guint info, guint time, gpointer user_data)
|
|
||||||
{
|
|
||||||
if (!Console.Editing) { Console.Message(LoadResStr("IDS_CNS_NONETEDIT")); return; }
|
|
||||||
C4ViewportWindow* window = static_cast<C4ViewportWindow*>(user_data);
|
|
||||||
|
|
||||||
gchar** uris = gtk_selection_data_get_uris(data);
|
|
||||||
if (!uris) return;
|
|
||||||
|
|
||||||
for (gchar** uri = uris; *uri != NULL; ++ uri)
|
|
||||||
{
|
|
||||||
gchar* file = g_filename_from_uri(*uri, NULL, NULL);
|
|
||||||
if (!file) continue;
|
|
||||||
|
|
||||||
window->cvp->DropFile(file, x, y);
|
|
||||||
g_free(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev(uris);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnExposeStatic(GtkWidget* widget, void *, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4Viewport* cvp = static_cast<C4ViewportWindow*>(user_data)->cvp;
|
|
||||||
cvp->Execute();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnRealizeStatic(GtkWidget* widget, gpointer user_data)
|
|
||||||
{
|
|
||||||
// Initial PlayerLock
|
|
||||||
if (static_cast<C4ViewportWindow*>(user_data)->cvp->GetPlayerLock())
|
|
||||||
{
|
|
||||||
gtk_widget_hide(static_cast<C4ViewportWindow*>(user_data)->h_scrollbar);
|
|
||||||
gtk_widget_hide(static_cast<C4ViewportWindow*>(user_data)->v_scrollbar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnKeyPressStatic(GtkWidget* widget, GdkEventKey* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
if (event->keyval == GDK_KEY_Scroll_Lock)
|
|
||||||
{
|
|
||||||
static_cast<C4ViewportWindow*>(user_data)->cvp->TogglePlayerLock();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (event->hardware_keycode <= x11scancodeoffset) return false;
|
|
||||||
Console.EditCursor.KeyDown(event->hardware_keycode - x11scancodeoffset, event->state);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnKeyReleaseStatic(GtkWidget* widget, GdkEventKey* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
if (event->hardware_keycode <= x11scancodeoffset) return false;
|
|
||||||
Console.EditCursor.KeyUp(event->hardware_keycode - x11scancodeoffset, event->state);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnScrollVW(GtkWidget* widget, GdkEventScroll* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4ViewportWindow* window = static_cast<C4ViewportWindow*>(user_data);
|
|
||||||
|
|
||||||
if (::MouseControl.IsViewport(window->cvp) && (Console.EditCursor.GetMode()==C4CNS_ModePlay))
|
|
||||||
{
|
|
||||||
switch (event->direction)
|
|
||||||
{
|
|
||||||
case GDK_SCROLL_UP:
|
|
||||||
C4GUI::MouseMove(C4MC_Button_Wheel, (int32_t)event->x, (int32_t)event->y, event->state + (short(1) << 16), window->cvp);
|
|
||||||
break;
|
|
||||||
case GDK_SCROLL_DOWN:
|
|
||||||
C4GUI::MouseMove(C4MC_Button_Wheel, (int32_t)event->x, (int32_t)event->y, event->state + (short(-1) << 16), window->cvp);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnButtonPressStatic(GtkWidget* widget, GdkEventButton* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4ViewportWindow* window = static_cast<C4ViewportWindow*>(user_data);
|
|
||||||
|
|
||||||
if (::MouseControl.IsViewport(window->cvp) && (Console.EditCursor.GetMode()==C4CNS_ModePlay))
|
|
||||||
{
|
|
||||||
switch (event->button)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
if (event->type == GDK_BUTTON_PRESS)
|
|
||||||
C4GUI::MouseMove(C4MC_Button_LeftDown, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp);
|
|
||||||
else if (event->type == GDK_2BUTTON_PRESS)
|
|
||||||
C4GUI::MouseMove(C4MC_Button_LeftDouble, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
C4GUI::MouseMove(C4MC_Button_MiddleDown, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
if (event->type == GDK_BUTTON_PRESS)
|
|
||||||
C4GUI::MouseMove(C4MC_Button_RightDown, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp);
|
|
||||||
else if (event->type == GDK_2BUTTON_PRESS)
|
|
||||||
C4GUI::MouseMove(C4MC_Button_RightDouble, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (event->button)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
Console.EditCursor.LeftButtonDown(event->state);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
Console.EditCursor.RightButtonDown(event->state);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnButtonReleaseStatic(GtkWidget* widget, GdkEventButton* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4ViewportWindow* window = static_cast<C4ViewportWindow*>(user_data);
|
|
||||||
|
|
||||||
if (::MouseControl.IsViewport(window->cvp) && (Console.EditCursor.GetMode()==C4CNS_ModePlay))
|
|
||||||
{
|
|
||||||
switch (event->button)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
C4GUI::MouseMove(C4MC_Button_LeftUp, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
C4GUI::MouseMove(C4MC_Button_MiddleUp, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
C4GUI::MouseMove(C4MC_Button_RightUp, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (event->button)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
Console.EditCursor.LeftButtonUp(event->state);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
Console.EditCursor.RightButtonUp(event->state);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnMotionNotifyStatic(GtkWidget* widget, GdkEventMotion* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4ViewportWindow* window = static_cast<C4ViewportWindow*>(user_data);
|
|
||||||
|
|
||||||
if (::MouseControl.IsViewport(window->cvp) && (Console.EditCursor.GetMode()==C4CNS_ModePlay))
|
|
||||||
{
|
|
||||||
C4GUI::MouseMove(C4MC_Button_None, (int32_t)event->x, (int32_t)event->y, event->state, window->cvp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
window->EditCursorMove(event->x, event->y, event->state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnConfigureStatic(GtkWidget* widget, GdkEventConfigure* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4ViewportWindow* window = static_cast<C4ViewportWindow*>(user_data);
|
|
||||||
C4Viewport* cvp = window->cvp;
|
|
||||||
|
|
||||||
//cvp->UpdateOutputSize();
|
|
||||||
cvp->ScrollBarsByViewPosition();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnConfigureDareaStatic(GtkWidget* widget, GdkEventConfigure* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4ViewportWindow* window = static_cast<C4ViewportWindow*>(user_data);
|
|
||||||
C4Viewport* cvp = window->cvp;
|
|
||||||
|
|
||||||
cvp->UpdateOutputSize();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnVScrollStatic(GtkAdjustment* adjustment, gpointer user_data)
|
|
||||||
{
|
|
||||||
static_cast<C4ViewportWindow*>(user_data)->cvp->ViewPositionByScrollBars();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnHScrollStatic(GtkAdjustment* adjustment, gpointer user_data)
|
|
||||||
{
|
|
||||||
static_cast<C4ViewportWindow*>(user_data)->cvp->ViewPositionByScrollBars();
|
|
||||||
}
|
|
||||||
|
|
||||||
static GtkTargetEntry drag_drop_entries[] =
|
|
||||||
{
|
|
||||||
{ const_cast<gchar*>("text/uri-list"), 0, 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
static gboolean OnConfigureNotify(GtkWidget *widget, GdkEvent *event, gpointer user_data)
|
|
||||||
{
|
|
||||||
Application.OnResolutionChanged(event->configure.width, event->configure.height);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool fullscreen_needs_restore = false;
|
|
||||||
static gboolean fullscreen_restore(gpointer data)
|
|
||||||
{
|
|
||||||
if (fullscreen_needs_restore)
|
|
||||||
Application.SetVideoMode(Application.GetConfigWidth(), Application.GetConfigHeight(), Config.Graphics.RefreshRate, Config.Graphics.Monitor, Application.FullScreenMode());
|
|
||||||
fullscreen_needs_restore = false;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool grab_mouse(GtkWidget *widget)
|
|
||||||
{
|
|
||||||
// This should be possible with pure GTK code as well, using
|
|
||||||
// gdk_device_grab(). However, while gdk_device_grab() will prevent clicks
|
|
||||||
// outside of the game window, the mouse gets stuck near the edge of the
|
|
||||||
// window when trying to move outside.
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
Window xwindow = gdk_x11_window_get_xid(gtk_widget_get_window(widget));
|
|
||||||
int result = XGrabPointer(dpy, xwindow, true, 0, GrabModeAsync, GrabModeAsync, xwindow, None, CurrentTime);
|
|
||||||
return result == GrabSuccess;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ungrab_mouse()
|
|
||||||
{
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
XUngrabPointer(dpy, CurrentTime);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean grab_mouse_fn(gpointer user_data)
|
|
||||||
{
|
|
||||||
// Grabbing may not be possible immediately after focusing the window, so
|
|
||||||
// try again if we don't succeed.
|
|
||||||
return !grab_mouse((GtkWidget*) user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnFocusInFS(GtkWidget *widget, GdkEvent *event, gpointer user_data)
|
|
||||||
{
|
|
||||||
Application.Active = true;
|
|
||||||
if (Application.FullScreenMode())
|
|
||||||
{
|
|
||||||
fullscreen_needs_restore = true;
|
|
||||||
gdk_threads_add_idle(fullscreen_restore, NULL);
|
|
||||||
}
|
|
||||||
C4Window *window = (C4Window*) user_data;
|
|
||||||
if (window->mouse_was_grabbed)
|
|
||||||
gdk_threads_add_timeout(50, grab_mouse_fn, widget);
|
|
||||||
// Reset urgency hint (does nothing if unset)
|
|
||||||
assert(widget == window->window && "OnFocusInFS callback used for something other than the main window.");
|
|
||||||
gtk_window_set_urgency_hint(GTK_WINDOW(window->window), false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static gboolean OnFocusOutFS(GtkWidget *widget, GdkEvent *event, gpointer user_data)
|
|
||||||
{
|
|
||||||
Application.Active = false;
|
|
||||||
if (Application.FullScreenMode() && Application.GetConfigWidth() != -1)
|
|
||||||
{
|
|
||||||
Application.RestoreVideoMode();
|
|
||||||
gtk_window_iconify(GTK_WINDOW(widget));
|
|
||||||
fullscreen_needs_restore = false;
|
|
||||||
}
|
|
||||||
ungrab_mouse();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnButtonPressFS(GtkWidget* widget, GdkEventButton* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
switch (event->button)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
if (event->type == GDK_BUTTON_PRESS)
|
|
||||||
C4GUI::MouseMove(C4MC_Button_LeftDown, (int32_t)event->x, (int32_t)event->y, event->state, NULL);
|
|
||||||
else if (event->type == GDK_2BUTTON_PRESS)
|
|
||||||
C4GUI::MouseMove(C4MC_Button_LeftDouble, (int32_t)event->x, (int32_t)event->y, event->state, NULL);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
C4GUI::MouseMove(C4MC_Button_MiddleDown, (int32_t)event->x, (int32_t)event->y, event->state, NULL);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
if (event->type == GDK_BUTTON_PRESS)
|
|
||||||
C4GUI::MouseMove(C4MC_Button_RightDown, (int32_t)event->x, (int32_t)event->y, event->state, NULL);
|
|
||||||
else if (event->type == GDK_2BUTTON_PRESS)
|
|
||||||
C4GUI::MouseMove(C4MC_Button_RightDouble, (int32_t)event->x, (int32_t)event->y, event->state, NULL);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean OnButtonRelease(GtkWidget* widget, GdkEventButton* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
int b;
|
|
||||||
switch (event->button)
|
|
||||||
{
|
|
||||||
case 1: b = C4MC_Button_LeftUp; break;
|
|
||||||
case 2: b = C4MC_Button_MiddleUp; break;
|
|
||||||
case 3: b = C4MC_Button_RightUp; break;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
C4GUI::MouseMove(b, (int32_t)event->x, (int32_t)event->y, event->state, NULL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnMotionNotify(GtkWidget* widget, GdkEventMotion* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4GUI::MouseMove(C4MC_Button_None, (int32_t)event->x, (int32_t)event->y, event->state, NULL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnScroll(GtkWidget* widget, GdkEventScroll* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4GUI::DialogWindow * window = static_cast<C4GUI::DialogWindow*>(user_data);
|
|
||||||
C4GUI::Dialog *pDlg = ::pGUI->GetDialog(window);
|
|
||||||
int idy;
|
|
||||||
switch (event->direction)
|
|
||||||
{
|
|
||||||
case GDK_SCROLL_UP: idy = 32; break;
|
|
||||||
case GDK_SCROLL_DOWN: idy = -32; break;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: make the GUI api less insane here
|
|
||||||
if (pDlg)
|
|
||||||
::pGUI->MouseInput(C4MC_Button_Wheel, event->x, event->y, event->state + (idy << 16), pDlg, NULL);
|
|
||||||
else
|
|
||||||
C4GUI::MouseMove(C4MC_Button_Wheel, event->x, event->y, event->state + (idy << 16), NULL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnButtonPressGD(GtkWidget* widget, GdkEventButton* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4GUI::DialogWindow * window = static_cast<C4GUI::DialogWindow*>(user_data);
|
|
||||||
C4GUI::Dialog *pDlg = ::pGUI->GetDialog(window);
|
|
||||||
|
|
||||||
switch (event->button)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
if (event->type == GDK_2BUTTON_PRESS)
|
|
||||||
{
|
|
||||||
::pGUI->MouseInput(C4MC_Button_LeftDouble, event->x, event->y, event->state, pDlg, NULL);
|
|
||||||
}
|
|
||||||
else if (event->type == GDK_BUTTON_PRESS)
|
|
||||||
{
|
|
||||||
::pGUI->MouseInput(C4MC_Button_LeftDown,event->x, event->y, event->state, pDlg, NULL);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if (event->type == GDK_BUTTON_PRESS)
|
|
||||||
::pGUI->MouseInput(C4MC_Button_MiddleDown, event->x, event->y, event->state, pDlg, NULL);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
if (event->type == GDK_2BUTTON_PRESS)
|
|
||||||
{
|
|
||||||
::pGUI->MouseInput(C4MC_Button_RightDouble, event->x, event->y, event->state, pDlg, NULL);
|
|
||||||
}
|
|
||||||
else if (event->type == GDK_BUTTON_PRESS)
|
|
||||||
{
|
|
||||||
::pGUI->MouseInput(C4MC_Button_RightDown, event->x, event->y, event->state, pDlg, NULL);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnButtonReleaseGD(GtkWidget* widget, GdkEventButton* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4GUI::DialogWindow * window = static_cast<C4GUI::DialogWindow*>(user_data);
|
|
||||||
C4GUI::Dialog *pDlg = ::pGUI->GetDialog(window);
|
|
||||||
|
|
||||||
switch (event->button)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
::pGUI->MouseInput(C4MC_Button_LeftUp, event->x, event->y, event->state, pDlg, NULL);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
::pGUI->MouseInput(C4MC_Button_MiddleUp, event->x, event->y, event->state, pDlg, NULL);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
::pGUI->MouseInput(C4MC_Button_RightUp, event->x, event->y, event->state, pDlg, NULL);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnMotionNotifyGD(GtkWidget* widget, GdkEventMotion* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4GUI::DialogWindow * window = static_cast<C4GUI::DialogWindow*>(user_data);
|
|
||||||
C4GUI::Dialog *pDlg = ::pGUI->GetDialog(window);
|
|
||||||
|
|
||||||
::pGUI->MouseInput(C4MC_Button_None, event->x, event->y, event->state, pDlg, NULL);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean OnConfigureGD(GtkWidget* widget, GdkEventConfigure* event, gpointer user_data)
|
|
||||||
{
|
|
||||||
C4GUI::DialogWindow * window = static_cast<C4GUI::DialogWindow*>(user_data);
|
|
||||||
|
|
||||||
window->pSurface->UpdateSize(event->width, event->height);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
C4Window::C4Window ():
|
|
||||||
Active(false), pSurface(0),
|
|
||||||
renderwnd(0), Info(0), window(NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
C4Window::~C4Window ()
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
bool C4Window::FindFBConfig(int samples, GLXFBConfig *info)
|
|
||||||
{
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
GLXFBConfig config = PickGLXFBConfig(dpy, samples);
|
|
||||||
if (info)
|
|
||||||
{
|
|
||||||
*info = config;
|
|
||||||
}
|
|
||||||
return config != NULL;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void C4Window::EnumerateMultiSamples(std::vector<int>& samples) const
|
|
||||||
{
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
std::map<int, int> attribs = base_attrib_map;
|
|
||||||
attribs[GLX_SAMPLE_BUFFERS_ARB] = 1;
|
|
||||||
|
|
||||||
int config_count = 0;
|
|
||||||
GLXFBConfig *configs = glXChooseFBConfig(dpy, DefaultScreen(dpy), MakeGLXAttribList(attribs).get(), &config_count);
|
|
||||||
|
|
||||||
std::set<int> multisamples;
|
|
||||||
for(int i = 0; i < config_count; ++i)
|
|
||||||
{
|
|
||||||
int v_samples;
|
|
||||||
glXGetFBConfigAttrib(dpy, configs[i], GLX_SAMPLES, &v_samples);
|
|
||||||
multisamples.insert(v_samples);
|
|
||||||
}
|
|
||||||
|
|
||||||
XFree(configs);
|
|
||||||
samples.assign(multisamples.cbegin(), multisamples.cend());
|
|
||||||
#else
|
|
||||||
if(pGL && pGL->pMainCtx)
|
|
||||||
samples = pGL->pMainCtx->EnumerateMultiSamples();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C4Window::StorePosition(const char *, const char *, bool) { return true; }
|
|
||||||
|
|
||||||
bool C4Window::RestorePosition(const char *, const char *, bool)
|
|
||||||
{
|
|
||||||
// The Windowmanager is responsible for window placement.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4Window::FlashWindow()
|
|
||||||
{
|
|
||||||
gtk_window_set_urgency_hint(GTK_WINDOW(window), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4Window::GrabMouse(bool grab)
|
|
||||||
{
|
|
||||||
if (grab)
|
|
||||||
{
|
|
||||||
// Don't grab the mouse while the game window isn't focused.
|
|
||||||
if (Application.Active)
|
|
||||||
grab_mouse(GTK_WIDGET(window));
|
|
||||||
mouse_was_grabbed = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ungrab_mouse();
|
|
||||||
mouse_was_grabbed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
C4Window* C4Window::Init(WindowKind windowKind, C4AbstractApp * pApp, const char * Title, const C4Rect * size)
|
|
||||||
{
|
|
||||||
Active = true;
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
if(!FindFBConfig(Config.Graphics.MultiSampling, &Info))
|
|
||||||
{
|
|
||||||
// Disable multisampling if we don't find a visual which
|
|
||||||
// supports the currently configured setting.
|
|
||||||
if(!FindFBConfig(0, &Info)) return NULL;
|
|
||||||
Config.Graphics.MultiSampling = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assert(!window);
|
|
||||||
|
|
||||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
||||||
if (windowKind == W_Viewport)
|
|
||||||
{
|
|
||||||
C4ViewportWindow * vw = static_cast<C4ViewportWindow *>(this);
|
|
||||||
|
|
||||||
// Cannot just use ScrolledWindow because this would just move
|
|
||||||
// the GdkWindow of the DrawingArea.
|
|
||||||
GtkWidget* table = gtk_grid_new();
|
|
||||||
render_widget = gtk_drawing_area_new();
|
|
||||||
gtk_widget_set_hexpand(GTK_WIDGET(render_widget), true);
|
|
||||||
gtk_widget_set_vexpand(GTK_WIDGET(render_widget), true);
|
|
||||||
gtk_grid_attach(GTK_GRID(table), GTK_WIDGET(render_widget), 0, 0, 1, 1);
|
|
||||||
vw->h_scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL);
|
|
||||||
gtk_grid_attach(GTK_GRID(table), vw->h_scrollbar, 0, 1, 1, 1);
|
|
||||||
vw->v_scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL);
|
|
||||||
gtk_grid_attach(GTK_GRID(table), vw->v_scrollbar, 1, 0, 1, 1);
|
|
||||||
|
|
||||||
GtkAdjustment* adjustment = gtk_range_get_adjustment(GTK_RANGE(vw->h_scrollbar));
|
|
||||||
|
|
||||||
g_signal_connect(
|
|
||||||
G_OBJECT(adjustment),
|
|
||||||
"value-changed",
|
|
||||||
G_CALLBACK(OnHScrollStatic),
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
adjustment = gtk_range_get_adjustment(GTK_RANGE(vw->v_scrollbar));
|
|
||||||
|
|
||||||
g_signal_connect(
|
|
||||||
G_OBJECT(adjustment),
|
|
||||||
"value-changed",
|
|
||||||
G_CALLBACK(OnVScrollStatic),
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
gtk_container_add(GTK_CONTAINER(window), table);
|
|
||||||
|
|
||||||
gtk_widget_add_events(GTK_WIDGET(window), GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK | GDK_POINTER_MOTION_MASK);
|
|
||||||
|
|
||||||
gtk_drag_dest_set(GTK_WIDGET(render_widget), GTK_DEST_DEFAULT_ALL, drag_drop_entries, 1, GDK_ACTION_COPY);
|
|
||||||
g_signal_connect(G_OBJECT(render_widget), "drag-data-received", G_CALLBACK(OnDragDataReceivedStatic), this);
|
|
||||||
g_signal_connect(G_OBJECT(render_widget), "draw", G_CALLBACK(OnExposeStatic), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(OnKeyPressStatic), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "key-release-event", G_CALLBACK(OnKeyReleaseStatic), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "scroll-event", G_CALLBACK(OnScrollVW), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(OnButtonPressStatic), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "button-release-event", G_CALLBACK(OnButtonReleaseStatic), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "motion-notify-event", G_CALLBACK(OnMotionNotifyStatic), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(OnKeyPress), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "key-release-event", G_CALLBACK(OnKeyRelease), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(OnConfigureStatic), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "realize", G_CALLBACK(OnRealizeStatic), this);
|
|
||||||
|
|
||||||
g_signal_connect_after(G_OBJECT(render_widget), "configure-event", G_CALLBACK(OnConfigureDareaStatic), this);
|
|
||||||
|
|
||||||
#if !GTK_CHECK_VERSION(3,10,0)
|
|
||||||
// do not draw the default background
|
|
||||||
gtk_widget_set_double_buffered (GTK_WIDGET(render_widget), false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(Console.window));
|
|
||||||
#if !GTK_CHECK_VERSION(3,14,0)
|
|
||||||
gtk_window_set_has_resize_grip(GTK_WINDOW(window), false);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (windowKind == W_Fullscreen)
|
|
||||||
{
|
|
||||||
render_widget = gtk_drawing_area_new();
|
|
||||||
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(render_widget));
|
|
||||||
|
|
||||||
g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(OnConfigureNotify), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "focus-in-event", G_CALLBACK(OnFocusInFS), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "focus-out-event", G_CALLBACK(OnFocusOutFS), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "unmap-event", G_CALLBACK(OnFocusOutFS), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(OnButtonPressFS), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "button-release-event", G_CALLBACK(OnButtonRelease), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "motion-notify-event", G_CALLBACK(OnMotionNotify), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(OnKeyPress), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "key-release-event", G_CALLBACK(OnKeyRelease), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "scroll-event", G_CALLBACK(OnScroll), this);
|
|
||||||
gtk_widget_add_events(GTK_WIDGET(window), GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
|
|
||||||
#if !GTK_CHECK_VERSION(3,10,0)
|
|
||||||
gtk_widget_set_double_buffered (GTK_WIDGET(render_widget), false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GValue val = {0,{{0}}};
|
|
||||||
g_value_init (&val, G_TYPE_BOOLEAN);
|
|
||||||
g_value_set_boolean (&val, true);
|
|
||||||
g_object_set_property (G_OBJECT (render_widget), "can-focus", &val);
|
|
||||||
g_object_set_property (G_OBJECT (window), "can-focus", &val);
|
|
||||||
g_value_unset (&val);
|
|
||||||
#if !GTK_CHECK_VERSION(3,14,0)
|
|
||||||
gtk_window_set_has_resize_grip(GTK_WINDOW(window), false);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (windowKind == W_GuiWindow)
|
|
||||||
{
|
|
||||||
render_widget = window;
|
|
||||||
g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(OnButtonPressGD), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "button-release-event", G_CALLBACK(OnButtonReleaseGD), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "motion-notify-event", G_CALLBACK(OnMotionNotifyGD), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(OnConfigureGD), this);
|
|
||||||
g_signal_connect(G_OBJECT(window), "scroll-event", G_CALLBACK(OnScroll), this);
|
|
||||||
|
|
||||||
gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(Console.window));
|
|
||||||
#if !GTK_CHECK_VERSION(3,14,0)
|
|
||||||
gtk_window_set_has_resize_grip(GTK_WINDOW(window), false);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (windowKind == W_Console)
|
|
||||||
{
|
|
||||||
render_widget = window;
|
|
||||||
}
|
|
||||||
assert(window);
|
|
||||||
assert(render_widget);
|
|
||||||
// Override gtk's default to match name/class of the XLib windows
|
|
||||||
gtk_window_set_wmclass(GTK_WINDOW(window), C4ENGINENAME, C4ENGINENAME);
|
|
||||||
gtk_window_set_default_size(GTK_WINDOW(window), size->Wdt, size->Hgt);
|
|
||||||
|
|
||||||
g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(OnDelete), this);
|
|
||||||
handlerDestroy = g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(OnDestroyStatic), this);
|
|
||||||
gtk_widget_add_events(GTK_WIDGET(window), GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_SCROLL_MASK);
|
|
||||||
|
|
||||||
// TODO: It would be nice to support GDK_SCROLL_SMOOTH_MASK and
|
|
||||||
// smooth scrolling for scrolling in menus, however that should not
|
|
||||||
// change the scroll wheel behaviour ingame for zooming or
|
|
||||||
// inventory change. Note that when both GDK_SCROLL_MASK and
|
|
||||||
// GDK_SMOOTH_SCROLL_MASK are enabled, both type of scroll events
|
|
||||||
// are reported, so one needs to make sure to not double-process them.
|
|
||||||
// It would be nice to have smooth scrolling also e.g. for zooming
|
|
||||||
// ingame, but it probably requires the notion of smooth scrolling
|
|
||||||
// other parts of the engine as well.
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
GdkScreen * scr = gtk_widget_get_screen(GTK_WIDGET(render_widget));
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
XVisualInfo *vis_info = glXGetVisualFromFBConfig(dpy, Info);
|
|
||||||
assert(vis_info);
|
|
||||||
GdkVisual * vis = gdk_x11_screen_lookup_visual(scr, vis_info->visualid);
|
|
||||||
XFree(vis_info);
|
|
||||||
gtk_widget_set_visual(GTK_WIDGET(render_widget),vis);
|
|
||||||
#endif
|
|
||||||
gtk_widget_show_all(GTK_WIDGET(window));
|
|
||||||
|
|
||||||
// XVisualInfo vitmpl; int blub;
|
|
||||||
// vitmpl.visual = gdk_x11_visual_get_xvisual(gtk_widget_get_visual(window));
|
|
||||||
// vitmpl.visualid = XVisualIDFromVisual(vitmpl.visual);
|
|
||||||
// Info = XGetVisualInfo(dpy, VisualIDMask, &vitmpl, &blub);
|
|
||||||
|
|
||||||
// printf("%p\n", gtk_widget_get_visual(render_widget));
|
|
||||||
// Info = gdk_x11_visual_get_xvisual(gtk_widget_get_visual(render_widget));
|
|
||||||
|
|
||||||
// Default icon has been set right after gtk_init(),
|
|
||||||
// so we don't need to take care about setting the icon here.
|
|
||||||
|
|
||||||
SetTitle(Title);
|
|
||||||
|
|
||||||
// Wait until window is mapped to get the window's XID
|
|
||||||
gtk_widget_show_now(GTK_WIDGET(window));
|
|
||||||
GdkWindow* render_gdk_wnd;
|
|
||||||
if (GTK_IS_LAYOUT(render_widget))
|
|
||||||
render_gdk_wnd = gtk_layout_get_bin_window(GTK_LAYOUT(render_widget));
|
|
||||||
else
|
|
||||||
render_gdk_wnd = gtk_widget_get_window(GTK_WIDGET(render_widget));
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
renderwnd = GDK_WINDOW_XID(render_gdk_wnd);
|
|
||||||
#elif defined(GDK_WINDOWING_WIN32)
|
|
||||||
renderwnd = reinterpret_cast<HWND>(gdk_win32_window_get_handle(render_gdk_wnd));
|
|
||||||
#endif
|
|
||||||
// Make sure the window is shown and ready to be rendered into,
|
|
||||||
// this avoids an async X error.
|
|
||||||
gdk_flush();
|
|
||||||
|
|
||||||
if (windowKind == W_Fullscreen)
|
|
||||||
gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(render_widget)),
|
|
||||||
gdk_cursor_new_for_display(gdk_display_get_default(), GDK_BLANK_CURSOR));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C4Window::ReInit(C4AbstractApp* pApp)
|
|
||||||
{
|
|
||||||
// Check whether multisampling settings was changed. If not then we
|
|
||||||
// don't need to ReInit anything.
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
|
||||||
int value;
|
|
||||||
Display * const dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
|
|
||||||
glXGetFBConfigAttrib(dpy, Info, GLX_SAMPLES, &value);
|
|
||||||
if(value == Config.Graphics.MultiSampling) return true;
|
|
||||||
|
|
||||||
// Check whether we have a visual with the requested number of samples
|
|
||||||
GLXFBConfig new_info;
|
|
||||||
if(!FindFBConfig(Config.Graphics.MultiSampling, &new_info)) return false;
|
|
||||||
|
|
||||||
GdkScreen * scr = gtk_widget_get_screen(GTK_WIDGET(render_widget));
|
|
||||||
XVisualInfo *vis_info = glXGetVisualFromFBConfig(dpy, new_info);
|
|
||||||
assert(vis_info);
|
|
||||||
GdkVisual * vis = gdk_x11_screen_lookup_visual(scr, vis_info->visualid);
|
|
||||||
XFree(vis_info);
|
|
||||||
|
|
||||||
// Un- and re-realizing the render_widget does not work, the window
|
|
||||||
// remains hidden afterwards. So we re-create it from scratch.
|
|
||||||
gtk_widget_destroy(GTK_WIDGET(render_widget));
|
|
||||||
render_widget = gtk_drawing_area_new();
|
|
||||||
#if !GTK_CHECK_VERSION(3,10,0)
|
|
||||||
gtk_widget_set_double_buffered (GTK_WIDGET(render_widget), false);
|
|
||||||
#endif
|
|
||||||
g_object_set(G_OBJECT(render_widget), "can-focus", TRUE, NULL);
|
|
||||||
|
|
||||||
gtk_widget_set_visual(GTK_WIDGET(render_widget),vis);
|
|
||||||
|
|
||||||
Info = new_info;
|
|
||||||
|
|
||||||
// Wait until window is mapped to get the window's XID
|
|
||||||
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(render_widget));
|
|
||||||
gtk_widget_show_now(GTK_WIDGET(render_widget));
|
|
||||||
|
|
||||||
if (GTK_IS_LAYOUT(render_widget))
|
|
||||||
{
|
|
||||||
GdkWindow* bin_wnd = gtk_layout_get_bin_window(GTK_LAYOUT(render_widget));
|
|
||||||
renderwnd = GDK_WINDOW_XID(bin_wnd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GdkWindow* render_wnd = gtk_widget_get_window(GTK_WIDGET(render_widget));
|
|
||||||
renderwnd = GDK_WINDOW_XID(render_wnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
gdk_flush();
|
|
||||||
gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(render_widget)),
|
|
||||||
gdk_cursor_new_for_display(gdk_display_get_default(), GDK_BLANK_CURSOR));
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4Window::Clear()
|
|
||||||
{
|
|
||||||
if (window != NULL)
|
|
||||||
{
|
|
||||||
g_signal_handler_disconnect(window, handlerDestroy);
|
|
||||||
gtk_widget_destroy(GTK_WIDGET(window));
|
|
||||||
handlerDestroy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid that the base class tries to free these
|
|
||||||
renderwnd = 0;
|
|
||||||
|
|
||||||
window = NULL;
|
|
||||||
Active = false;
|
|
||||||
|
|
||||||
Info = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4Window::SetSize(unsigned int width, unsigned int height)
|
|
||||||
{
|
|
||||||
gtk_window_resize(GTK_WINDOW(window), width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C4Window::GetSize(C4Rect * r)
|
|
||||||
{
|
|
||||||
r->x = 0; r->y = 0;
|
|
||||||
gtk_window_get_size(GTK_WINDOW(window), &r->Wdt, &r->Hgt);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4Window::SetTitle(char const * Title)
|
|
||||||
{
|
|
||||||
gtk_window_set_title(GTK_WINDOW(window), Title);
|
|
||||||
}
|
|
||||||
|
|
||||||
void C4Window::RequestUpdate()
|
|
||||||
{
|
|
||||||
// just invoke directly
|
|
||||||
PerformUpdate();
|
|
||||||
}
|
|
|
@ -62,42 +62,7 @@ bool EraseItemSafe(const char *szFilename)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_GTK
|
#if defined(WITH_QT_EDITOR)
|
||||||
#include <gtk/gtk.h>
|
|
||||||
bool OpenURL(const char *szURL)
|
|
||||||
{
|
|
||||||
GError *error = 0;
|
|
||||||
if (gtk_show_uri(NULL, szURL, GDK_CURRENT_TIME, &error))
|
|
||||||
return true;
|
|
||||||
if (error != NULL)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "Unable to open URL: %s\n", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
}
|
|
||||||
const char * argv[][3] =
|
|
||||||
{
|
|
||||||
{ "xdg-open", szURL, 0 },
|
|
||||||
{ "sensible-browser", szURL, 0 },
|
|
||||||
{ "firefox", szURL, 0 },
|
|
||||||
{ "mozilla", szURL, 0 },
|
|
||||||
{ "konqueror", szURL, 0 },
|
|
||||||
{ "epiphany", szURL, 0 },
|
|
||||||
{ 0, 0, 0 }
|
|
||||||
};
|
|
||||||
for (int i = 0; argv[i][0]; ++i)
|
|
||||||
{
|
|
||||||
error = 0;
|
|
||||||
if (g_spawn_async (g_get_home_dir(), const_cast<char**>(argv[i]), 0, G_SPAWN_SEARCH_PATH, 0, 0, 0, &error))
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s\n", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#elif defined(WITH_QT_EDITOR)
|
|
||||||
#undef LineFeed
|
#undef LineFeed
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif // HAVE_CONFIG_H
|
#endif // HAVE_CONFIG_H
|
||||||
|
|
||||||
#if defined(USE_WIN32_WINDOWS) || (defined(_WIN32) && defined(USE_GTK))
|
#if defined(USE_WIN32_WINDOWS)
|
||||||
#define USE_WGL
|
#define USE_WGL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue