From b80d7b807bd6c5a7d266bda87b1ab9872b47d3dd Mon Sep 17 00:00:00 2001 From: Rory Fewell Date: Sat, 28 Dec 2024 00:52:06 +0000 Subject: [PATCH] Enhancement: Fixes #404, UI resource preview tool --- shared/comctl/src/cpl.c | 2 +- shared/comgtk/CMakeLists.txt | 2 + shared/comgtk/public/accelerator.h | 41 +++ shared/comgtk/public/libapi.h.in | 1 + shared/comgtk/src/accelerator.c | 23 ++ shared/comgtk/src/builder.c | 10 +- shell/cpl/desk/src/res/page-appearance.ui | 2 +- shell/cpl/desk/src/res/page-desktop.ui | 2 +- shell/cpl/desk/src/res/page-scrnsave.ui | 2 +- shell/cpl/desk/src/res/page-settings.ui | 2 +- shell/cpl/desk/src/res/page-themes.ui | 2 +- tools/resvwr/CMakeLists.txt | 90 +++++ tools/resvwr/README.MD | 20 + tools/resvwr/deps | 5 + tools/resvwr/icons/16x16/wintc-resvwr.png | Bin 0 -> 637 bytes tools/resvwr/icons/32x32/wintc-resvwr.png | Bin 0 -> 1790 bytes tools/resvwr/src/application.c | 124 +++++++ tools/resvwr/src/application.h | 25 ++ tools/resvwr/src/main.c | 20 + tools/resvwr/src/res/menubar.ui | 30 ++ tools/resvwr/src/res/resources.xml | 7 + tools/resvwr/src/res/resvwr.ui | 32 ++ tools/resvwr/src/window.c | 422 ++++++++++++++++++++++ tools/resvwr/src/window.h | 29 ++ tools/resvwr/wintc-resvwr.desktop | 9 + 25 files changed, 891 insertions(+), 11 deletions(-) create mode 100644 shared/comgtk/public/accelerator.h create mode 100644 shared/comgtk/src/accelerator.c create mode 100644 tools/resvwr/CMakeLists.txt create mode 100644 tools/resvwr/README.MD create mode 100644 tools/resvwr/deps create mode 100644 tools/resvwr/icons/16x16/wintc-resvwr.png create mode 100644 tools/resvwr/icons/32x32/wintc-resvwr.png create mode 100644 tools/resvwr/src/application.c create mode 100644 tools/resvwr/src/application.h create mode 100644 tools/resvwr/src/main.c create mode 100644 tools/resvwr/src/res/menubar.ui create mode 100644 tools/resvwr/src/res/resources.xml create mode 100644 tools/resvwr/src/res/resvwr.ui create mode 100644 tools/resvwr/src/window.c create mode 100644 tools/resvwr/src/window.h create mode 100644 tools/resvwr/wintc-resvwr.desktop diff --git a/shared/comctl/src/cpl.c b/shared/comctl/src/cpl.c index 99b46e3..f03b712 100644 --- a/shared/comctl/src/cpl.c +++ b/shared/comctl/src/cpl.c @@ -25,7 +25,7 @@ void wintc_ctl_cpl_notebook_append_page_from_resource( // wintc_lc_builder_preprocess_widget_text(builder); - box_page = GTK_WIDGET(gtk_builder_get_object(builder, "page-box")); + box_page = GTK_WIDGET(gtk_builder_get_object(builder, "page")); label_title = GTK_WIDGET(gtk_builder_get_object(builder, "label-title")); gtk_notebook_append_page( diff --git a/shared/comgtk/CMakeLists.txt b/shared/comgtk/CMakeLists.txt index 71d6787..e415e3c 100644 --- a/shared/comgtk/CMakeLists.txt +++ b/shared/comgtk/CMakeLists.txt @@ -33,6 +33,8 @@ configure_file(src/version.c.in ${PROJECT_ROOT}/src/version.c @ONLY) add_library( libwintc-comgtk + src/accelerator.c + public/accelerator.h src/application.c public/application.h public/assets.h diff --git a/shared/comgtk/public/accelerator.h b/shared/comgtk/public/accelerator.h new file mode 100644 index 0000000..d1b7133 --- /dev/null +++ b/shared/comgtk/public/accelerator.h @@ -0,0 +1,41 @@ +/** @file */ + +#ifndef __COMGTK_ACCELERATOR_H__ +#define __COMGTK_ACCELERATOR_H__ + +#include +#include + +// +// PUBLIC STRUCTURES +// + +/** + * Convenience structure for defining accelerators for use with + * wintc_application_set_accelerators. + */ +typedef struct _WinTCAccelEntry +{ + const gchar* action_name; + const gchar* accelerator[2]; +} WinTCAccelEntry; + +// +// PUBLIC FUNCTIONS +// + +/** + * Convenience function for setting up many accelerators at once with + * gtk_application_set_accels_for_action(). + * + * @param application The application. + * @param accelerators The array of accelerator entries. + * @param n_accelerators The number of accelerators in the array. + */ +void wintc_application_set_accelerators( + GtkApplication* application, + const WinTCAccelEntry* accelerators, + guint n_accelerators +); + +#endif diff --git a/shared/comgtk/public/libapi.h.in b/shared/comgtk/public/libapi.h.in index ba983dd..93820d1 100644 --- a/shared/comgtk/public/libapi.h.in +++ b/shared/comgtk/public/libapi.h.in @@ -1,6 +1,7 @@ #ifndef __WINTC_COMGTK_H__ #define __WINTC_COMGTK_H__ +#include "@LIB_HEADER_DIR@/accelerator.h" #include "@LIB_HEADER_DIR@/application.h" #include "@LIB_HEADER_DIR@/assets.h" #include "@LIB_HEADER_DIR@/builder.h" diff --git a/shared/comgtk/src/accelerator.c b/shared/comgtk/src/accelerator.c new file mode 100644 index 0000000..3ac93f9 --- /dev/null +++ b/shared/comgtk/src/accelerator.c @@ -0,0 +1,23 @@ +#include +#include + +#include "../public/accelerator.h" + +// +// PUBLIC FUNCTIONS +// +void wintc_application_set_accelerators( + GtkApplication* application, + const WinTCAccelEntry* accelerators, + guint n_accelerators +) +{ + for (guint i = 0; i < n_accelerators; i++) + { + gtk_application_set_accels_for_action( + application, + accelerators[i].action_name, + accelerators[i].accelerator + ); + } +} diff --git a/shared/comgtk/src/builder.c b/shared/comgtk/src/builder.c index e0c6111..359a591 100644 --- a/shared/comgtk/src/builder.c +++ b/shared/comgtk/src/builder.c @@ -12,9 +12,9 @@ void wintc_builder_get_objects( ... ) { - va_list ap; - GtkWidget** next_dst; - gchar* next_name; + va_list ap; + GObject** next_dst; + gchar* next_name; va_start(ap, builder); @@ -22,8 +22,8 @@ void wintc_builder_get_objects( while (next_name) { - next_dst = va_arg(ap, GtkWidget**); - *next_dst = GTK_WIDGET(gtk_builder_get_object(builder, next_name)); + next_dst = va_arg(ap, GObject**); + *next_dst = gtk_builder_get_object(builder, next_name); next_name = va_arg(ap, gchar*); } diff --git a/shell/cpl/desk/src/res/page-appearance.ui b/shell/cpl/desk/src/res/page-appearance.ui index 7c9517d..b483d01 100644 --- a/shell/cpl/desk/src/res/page-appearance.ui +++ b/shell/cpl/desk/src/res/page-appearance.ui @@ -6,7 +6,7 @@ True Appearance - + True False vertical diff --git a/shell/cpl/desk/src/res/page-desktop.ui b/shell/cpl/desk/src/res/page-desktop.ui index 9bc237c..5f8b172 100644 --- a/shell/cpl/desk/src/res/page-desktop.ui +++ b/shell/cpl/desk/src/res/page-desktop.ui @@ -29,7 +29,7 @@ True Desktop - + True False vertical diff --git a/shell/cpl/desk/src/res/page-scrnsave.ui b/shell/cpl/desk/src/res/page-scrnsave.ui index 40156ba..e4039e3 100644 --- a/shell/cpl/desk/src/res/page-scrnsave.ui +++ b/shell/cpl/desk/src/res/page-scrnsave.ui @@ -6,7 +6,7 @@ True Screen Saver - + True False vertical diff --git a/shell/cpl/desk/src/res/page-settings.ui b/shell/cpl/desk/src/res/page-settings.ui index f4e1b17..3d3f571 100644 --- a/shell/cpl/desk/src/res/page-settings.ui +++ b/shell/cpl/desk/src/res/page-settings.ui @@ -6,7 +6,7 @@ True Settings - + True False vertical diff --git a/shell/cpl/desk/src/res/page-themes.ui b/shell/cpl/desk/src/res/page-themes.ui index 3730cdf..ac00034 100644 --- a/shell/cpl/desk/src/res/page-themes.ui +++ b/shell/cpl/desk/src/res/page-themes.ui @@ -6,7 +6,7 @@ True Themes - + True False vertical diff --git a/tools/resvwr/CMakeLists.txt b/tools/resvwr/CMakeLists.txt new file mode 100644 index 0000000..3aebaf5 --- /dev/null +++ b/tools/resvwr/CMakeLists.txt @@ -0,0 +1,90 @@ +cmake_minimum_required(VERSION 3.5) + +project( + wintc-resvwr + VERSION 1.0 + DESCRIPTION "Windows Total Conversion resource viewer tool." + LANGUAGES C +) + +set(PROJECT_ANYARCH false) +set(PROJECT_FREESTATUS true) +set(PROJECT_MAINTAINER "Rory Fewell ") + +set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR}) + +include(GNUInstallDirs) + +include(../../packaging/cmake-inc/common/CMakeLists.txt) +include(../../packaging/cmake-inc/linking/CMakeLists.txt) +include(../../packaging/cmake-inc/packaging/CMakeLists.txt) +include(../../packaging/cmake-inc/resources/CMakeLists.txt) + +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(wintc-comctl WINTC_COMCTL) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) +wintc_resolve_library(wintc-shcommon WINTC_SHCOMMON) +wintc_resolve_library(wintc-shlang WINTC_SHLANG) + +wintc_compile_resources() + +add_executable( + wintc-resvwr + src/application.c + src/application.h + src/main.c + src/resources.c + src/window.c + src/window.h +) + +target_compile_options( + wintc-resvwr + PRIVATE ${WINTC_COMPILE_OPTIONS} +) + +target_include_directories( + wintc-resvwr + SYSTEM + PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${WINTC_COMCTL_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} + PRIVATE ${WINTC_SHCOMMON_INCLUDE_DIRS} + PRIVATE ${WINTC_SHLANG_INCLUDE_DIRS} +) + +target_link_directories( + wintc-resvwr + PRIVATE ${GLIB_LIBRARY_DIRS} + PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${WINTC_COMCTL_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} + PRIVATE ${WINTC_SHCOMMON_LIBRARY_DIRS} + PRIVATE ${WINTC_SHLANG_LIBRARY_DIRS} +) + +target_link_libraries( + wintc-resvwr + PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${WINTC_COMCTL_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} + PRIVATE ${WINTC_SHCOMMON_LIBRARIES} + PRIVATE ${WINTC_SHLANG_LIBRARIES} +) + +# Installation +# +wintc_configure_and_install_packaging() +wintc_install_icons_into_package() + +install( + FILES wintc-resvwr.desktop + DESTINATION share/applications +) +install( + TARGETS wintc-resvwr + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/tools/resvwr/README.MD b/tools/resvwr/README.MD new file mode 100644 index 0000000..a853740 --- /dev/null +++ b/tools/resvwr/README.MD @@ -0,0 +1,20 @@ +# Resource Viewer +This directory contains the source code for the *Resource Viewer* tool. + +![image](https://github.com/user-attachments/assets/a2c67868-29d2-4f4d-9b47-fa7b6723307f) + +## Installation +This tool must be compiled before it can be used. Build, package, and install it the same way you would with any other program in this repo, eg: +``` +(in the /packaging dir) +./build.sh tools/resvwr +./package.sh tools/resvwr +sudo dpkg -i local-out/wintc-resvwr.deb +``` + +Adjust the `dpkg` command to whatever the package manager is on your distro (eg. for Fedora do `sudo dnf install ./local-out/wintc-resvwr.rpm`) + +# Usage +The tool allows you to view most of the GTK builder XML resources in this project. Some of course require custom widgets which may not be available. + +It's a viewer only, to aid with development. It is not a fully featured editor like Glade. diff --git a/tools/resvwr/deps b/tools/resvwr/deps new file mode 100644 index 0000000..3f751a1 --- /dev/null +++ b/tools/resvwr/deps @@ -0,0 +1,5 @@ +bt,rt:glib2 +bt,rt:gtk3 +bt,rt:wintc-comctl +bt,rt:wintc-comgtk +bt,rt:wintc-shlang diff --git a/tools/resvwr/icons/16x16/wintc-resvwr.png b/tools/resvwr/icons/16x16/wintc-resvwr.png new file mode 100644 index 0000000000000000000000000000000000000000..06234ed5f9bb625e2af30a962364da25d0ba6b99 GIT binary patch literal 637 zcmV-@0)qXCP)CyOft#jJ;_WanLLi)Jklm&b>oEt!*I_z_nz+>40=6yn?x&yW|A0q$)WE> z0=>`T=zh%N>~IVx??rLU9L3>228V9aFtiF}3RQ498EmS8NTQ6bn1ldbf_GbllM-Ry zC}P=HgwYZLpsZMy=mKds{TY7o?F z4OHX?M5zv5s3OBxk;uwmk`;6u?}1_7>W6}Ni(WLpTmMz6b=)7F1uxcM8kqz~$X5rR zBS_ZU5s>n8f5gGg3y>;x6bL!1=M&TO3DCO0`)^*gdBL{z*GW>jD%i9}?z%n#Us%)x z-h5eul%0m%$F%LQ?5?=;LQQRqr)AJw89`Qi8COsjn3`XK7#W6plf{+0V=B4-;S|Jj z9hc3cNMvMn6`F(=iW4$Z!l@oJ9GikBFfnIA(RCi*9h927Oq>OhxN;M5LJqNIwNW8c zjD+=Y3GR@HALJ%ff$^EI$Xhz#+@Nvk_EQu~HKfT?MM+pj(-Zi%wNa_4gcDsRc*y8S z&)mSWR~Hz2^%?08`(SfYYTBv1jA*i~5o$DQh)ulD=g`G>B{kViMc_M$Lgf&WlZM_JKlgENR*WS2SH7EI})Bmnt XmAR~%Z!e;-00000NkvXXu0mjf`vD?k literal 0 HcmV?d00001 diff --git a/tools/resvwr/icons/32x32/wintc-resvwr.png b/tools/resvwr/icons/32x32/wintc-resvwr.png new file mode 100644 index 0000000000000000000000000000000000000000..04cb48ae15e0aea40a2949a15a995a7d75157700 GIT binary patch literal 1790 zcmVQK4m^A7A3wT@O>!E$&adlV~Q-{UT zwFru;!Ssk~d=y%V4}&Z5!SafC^1U$tmwOZr*C_1DD6I62*nKeDyco84F)-pmzsZB1 zAvazQy3sY@Mth$NZ9PsHx}A8@si3Jt!Si+p8d@EwGuTn9x1+k*hRP-z%5_$hG+I&g z%rbER47WLbuo9TzCaV`i77qr@9`p@+(9LiHcl5hp>~*1)!1|X?Gu{j4&4GG? zKP7k#!K(;fLEy6I1aDxt6_4xQ5`b0l!QxO=6MS&QD^&L~oK$x*JV3R+i&T^H=SBt3 z7;YeV3#ooVstI1Ev!S%nhGGIgskaFD!`hLF1NdKi;7s~$yy=aN0i&a1@X_b>j=}92 z6TPC)XXldnv*r_78yZndW^()crK8_1OGcS%p_2Wu@z6gDfY=i5lW>oB6fO!tA+W}?Z@?47#cls0NMGZd`wL{ z!@U7;;SoNe++z3o;e#rCd|F(uO^Xw|W7CC4rIh&M@ zOp1Nt#@!msUAa>RU}*1$b0lZ{GiO5?&V(|Q4rTBHl>QVrde6eqeH!+zI7FYw5X#L~ z>5jrD1l&!^J$^WMJS%VMQca4KN5!NHueh3Dg;_stmjUPuJpu5`P)wO{`r*k0Payay zI0)|0@58QRX~LvT>bMEG+VO#K9yh%o&PL_HGu56i=T%_liYOUCovzal|2O66W98KG zU4yT}dDgYv&}fvqA#z`$*bSF*%ka(eZ8CtW`ZfV~4rhSNG0__pQvrWdI^AQU91`xL zj(3S!_rpnjQ@gb2hBkFEkww?Mup|9?F}_;5MFvn@-2$iS64m@7xE%a=e);{HaD~Qk z4Es)A65w4&(}WLfJD4I;%H{AIjoR@NE|qiL1)P@?DG%FyS}dmvIYszlaik1@U5Vk! zz&UU^UiSg75B@P!T;tBi8;_gn@5&kOq;ZoSuPrB5UfFCHEBRdZW6W3-Ap^Mcq=De6 zQ2G+VNu@s8Ay zO<)u7QaVTyGw$QF`5R;a*9)ozoX3pI!Irz@=mnveZ-Wta!wp)-uc4J&kBT1(+Z7K3 z1t7;wgDY~}TC{73;7wL>7X(Kh5eLbci+Azqy!A4GY??`i+dB?}%i&V{FBs^~GcE*c~X!TEV;uH(s zd>BOpUmAW;TtvrHZ{g$FYh?fzu0Da?xF5FGJ>YVPttPDBUG(dCj zwbOa4cA=osjO7~+BIA!zq+csW+SMYQyHbc$`n{ygN7QH!=svhd^}Zugej+s=aVa-( zEa^`iPRzxWnISTOBfnk4p)=WtKA8b7hnVX=b*{U#Gn*UBE{{#URClY)gvE4Je7$T7 zzFe{ypD&8UjPEzmctv3Pf^gC0pUexRTCc~nIqO8qr_Ne~DM6u_92A1dS^$BM%fZLP z3Q8^4c1hTNq4>LyJ))+wX@jwmd`mu_S8Bq7&|mOQ|1;upFt~(jR>&vxQwtu{jNmTS z`wpc%zub(QWo7}-Eg8lfZKuDh7g&xR +#include +#include +#include + +#include "application.h" +#include "window.h" + +// +// FORWARD DECLARATIONS +// +static void wintc_resvwr_application_activate( + GApplication* application +); +static void wintc_resvwr_application_startup( + GApplication* application +); + +// +// STATIC DATA +// +static const WinTCAccelEntry S_ACCELS[] = { + { + "win.open", + { + "O", + NULL + } + }, + { + "win.refresh", + { + "F5", + NULL + } + }, + { + "win.exit", + { + "F4", + NULL + } + } +}; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +typedef struct _WinTCResvwrApplication +{ + GtkApplication __parent__; +} WinTCResvwrApplication; + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCResvwrApplication, + wintc_resvwr_application, + GTK_TYPE_APPLICATION +) + +static void wintc_resvwr_application_class_init( + WinTCResvwrApplicationClass* klass +) +{ + GApplicationClass* application_class = G_APPLICATION_CLASS(klass); + + application_class->activate = wintc_resvwr_application_activate; + application_class->startup = wintc_resvwr_application_startup; +} + +static void wintc_resvwr_application_init( + WINTC_UNUSED(WinTCResvwrApplication* self) +) { } + +// +// CLASS VIRTUAL METHODS +// +static void wintc_resvwr_application_activate( + GApplication* application +) +{ + GtkWidget* new_window = + wintc_resvwr_window_new(WINTC_RESVWR_APPLICATION(application)); + + gtk_widget_show_all(new_window); +} + +static void wintc_resvwr_application_startup( + GApplication* application +) +{ + (G_APPLICATION_CLASS(wintc_resvwr_application_parent_class)) + ->startup(application); + + // Init comctl + // + wintc_ctl_install_default_styles(); + + g_type_ensure(WINTC_TYPE_CTL_ANIMATION); + + // Init accelerators + // + wintc_application_set_accelerators( + GTK_APPLICATION(application), + S_ACCELS, + G_N_ELEMENTS(S_ACCELS) + ); +} + +// +// PUBLIC FUNCTIONS +// +WinTCResvwrApplication* wintc_resvwr_application_new(void) +{ + return WINTC_RESVWR_APPLICATION( + g_object_new( + wintc_resvwr_application_get_type(), + "application-id", "uk.oddmatics.wintc.tools.resvwr", + NULL + ) + ); +} diff --git a/tools/resvwr/src/application.h b/tools/resvwr/src/application.h new file mode 100644 index 0000000..aa0655e --- /dev/null +++ b/tools/resvwr/src/application.h @@ -0,0 +1,25 @@ +#ifndef __APPLICATION_H__ +#define __APPLICATION_H__ + +#include +#include + +// +// GTK OOP BOILERPLATE +// +#define WINTC_TYPE_RESVWR_APPLICATION (wintc_resvwr_application_get_type()) + +G_DECLARE_FINAL_TYPE( + WinTCResvwrApplication, + wintc_resvwr_application, + WINTC, + RESVWR_APPLICATION, + GtkApplication +) + +// +// PUBLIC FUNCTIONS +// +WinTCResvwrApplication* wintc_resvwr_application_new(void); + +#endif diff --git a/tools/resvwr/src/main.c b/tools/resvwr/src/main.c new file mode 100644 index 0000000..a48cca0 --- /dev/null +++ b/tools/resvwr/src/main.c @@ -0,0 +1,20 @@ +#include + +#include "application.h" + +int main( + int argc, + char* argv[] +) +{ + WinTCResvwrApplication* app = wintc_resvwr_application_new(); + int status; + + g_set_application_name("Resvwr"); + + status = g_application_run(G_APPLICATION(app), argc, argv); + + g_object_unref(app); + + return status; +} diff --git a/tools/resvwr/src/res/menubar.ui b/tools/resvwr/src/res/menubar.ui new file mode 100644 index 0000000..944113f --- /dev/null +++ b/tools/resvwr/src/res/menubar.ui @@ -0,0 +1,30 @@ + + + + + File + +
+ + <Control>O + win.open + Open + +
+
+ + F5 + win.refresh + Refresh + +
+
+ + <Alt>F4 + win.exit + Exit + +
+
+
+
diff --git a/tools/resvwr/src/res/resources.xml b/tools/resvwr/src/res/resources.xml new file mode 100644 index 0000000..4e66e8a --- /dev/null +++ b/tools/resvwr/src/res/resources.xml @@ -0,0 +1,7 @@ + + + + menubar.ui + resvwr.ui + + diff --git a/tools/resvwr/src/res/resvwr.ui b/tools/resvwr/src/res/resvwr.ui new file mode 100644 index 0000000..356ba21 --- /dev/null +++ b/tools/resvwr/src/res/resvwr.ui @@ -0,0 +1,32 @@ + + + + True + False + vertical + + + + True + False + + + False + False + 0 + + + + + True + True + in + + + True + True + 1 + + + + diff --git a/tools/resvwr/src/window.c b/tools/resvwr/src/window.c new file mode 100644 index 0000000..d740763 --- /dev/null +++ b/tools/resvwr/src/window.c @@ -0,0 +1,422 @@ +#include +#include +#include +#include + +#include "application.h" +#include "window.h" + +// +// FORWARD DECLARATIONS +// +static void wintc_resvwr_window_finalize( + GObject* object +); + +static void wintc_resvwr_window_refresh( + WinTCResvwrWindow* wnd +); + +static void action_exit( + GSimpleAction* action, + GVariant* parameter, + gpointer user_data +); +static void action_open( + GSimpleAction* action, + GVariant* parameter, + gpointer user_data +); +static void action_refresh( + GSimpleAction* action, + GVariant* parameter, + gpointer user_data +); + +// +// STATIC DATA +// +static GActionEntry s_window_actions[] = { + { + .name = "exit", + .activate = action_exit, + .parameter_type = NULL, + .state = NULL, + .change_state = NULL + }, + { + .name = "open", + .activate = action_open, + .parameter_type = NULL, + .state = NULL, + .change_state = NULL + }, + { + .name = "refresh", + .activate = action_refresh, + .parameter_type = NULL, + .state = NULL, + .change_state = NULL + }, +}; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +typedef struct _WinTCResvwrWindow +{ + GtkApplicationWindow __parent__; + + // State + // + gchar* path_resource; + GtkWidget* scrollwnd_view; +} WinTCResvwrWindow; + +// +// GTK TYPE DEFINITION & CTORS +// +G_DEFINE_TYPE( + WinTCResvwrWindow, + wintc_resvwr_window, + GTK_TYPE_APPLICATION_WINDOW +) + +static void wintc_resvwr_window_class_init( + WinTCResvwrWindowClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = wintc_resvwr_window_finalize; +} + +static void wintc_resvwr_window_init( + WinTCResvwrWindow* self +) +{ + GtkBuilder* builder; + + // Set up action map + // + g_action_map_add_action_entries( + G_ACTION_MAP(self), + s_window_actions, + G_N_ELEMENTS(s_window_actions), + self + ); + + // Build menu + // + GMenuModel* menubar; + + builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/tools/resvwr/menubar.ui" + ); + + menubar = + G_MENU_MODEL( + g_object_ref( + gtk_builder_get_object(builder, "menubar") + ) + ); + + g_object_unref(builder); + + // Initialize UI + // + GtkWidget* menubar_main; + + builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/tools/resvwr/resvwr.ui" + ); + + wintc_lc_builder_preprocess_widget_text(builder); + + wintc_builder_get_objects( + builder, + "menubar-main", &menubar_main, + "scrollwnd-view", &(self->scrollwnd_view), + NULL + ); + + gtk_menu_shell_bind_model( + GTK_MENU_SHELL(menubar_main), + menubar, + NULL, + FALSE + ); + + gtk_container_add( + GTK_CONTAINER(self), + GTK_WIDGET(gtk_builder_get_object(builder, "main-box")) + ); + + g_object_unref(builder); + g_object_unref(menubar); + + gtk_window_set_default_size( + GTK_WINDOW(self), + 320, + 260 + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_resvwr_window_finalize( + GObject* object +) +{ + WinTCResvwrWindow* wnd = WINTC_RESVWR_WINDOW(object); + + g_free(wnd->path_resource); + + (G_OBJECT_CLASS(wintc_resvwr_window_parent_class)) + ->finalize(object); +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_resvwr_window_new( + WinTCResvwrApplication* app +) +{ + return GTK_WIDGET( + g_object_new( + WINTC_TYPE_RESVWR_WINDOW, + "application", GTK_APPLICATION(app), + "title", "Resource Viewer", + NULL + ) + ); +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_resvwr_window_refresh( + WinTCResvwrWindow* wnd +) +{ + // Have we got any selection? + // + if (!(wnd->path_resource)) + { + return; + } + + // Clear existing stuff (if any) + // + GtkWidget* child = gtk_bin_get_child(GTK_BIN(wnd->scrollwnd_view)); + + if (child) + { + gtk_container_remove(GTK_CONTAINER(wnd->scrollwnd_view), child); + } + + // Create new from file + // + GtkBuilder* builder = gtk_builder_new(); + GError* error = NULL; + + if ( + gtk_builder_add_from_file( + builder, + wnd->path_resource, + &error + ) + ) + { + // Based on what we find in the builder, construct the UI in the + // scrolled window + // + GtkWidget* label_title; + GMenuModel* menu; + GMenuModel* menubar; + GtkWidget* main_box; + GtkWidget* page; + GtkWidget* toolbar; + + wintc_builder_get_objects( + builder, + "label-title", &label_title, + "main-box", &main_box, + "menu", &menu, + "menubar", &menubar, + "page", &page, + "toolbar", &toolbar, + NULL + ); + + if (main_box) + { + gtk_container_add( + GTK_CONTAINER(wnd->scrollwnd_view), + main_box + ); + } + else if (menu || menubar) + { + GtkWidget* box_wrapper = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + GtkWidget* menu_built; + + if (menu) // Floating menu - create menubar to attach to + { + GtkWidget* submenu_built = gtk_menu_new_from_model(menu); + GtkWidget* submenu_item = gtk_menu_item_new_with_label("_"); + + menu_built = gtk_menu_bar_new(); + + gtk_menu_item_set_submenu( + GTK_MENU_ITEM(submenu_item), + submenu_built + ); + gtk_menu_shell_append( + GTK_MENU_SHELL(menu_built), + submenu_item + ); + } + else // Menubar - attach immediately + { + menu_built = gtk_menu_bar_new_from_model(menubar); + } + + gtk_container_add( + GTK_CONTAINER(box_wrapper), + menu_built + ); + gtk_container_add( + GTK_CONTAINER(wnd->scrollwnd_view), + box_wrapper + ); + } + else if (page) + { + GtkWidget* notebook = gtk_notebook_new(); + + gtk_notebook_append_page( + GTK_NOTEBOOK(notebook), + page, + label_title + ); + + gtk_container_add( + GTK_CONTAINER(wnd->scrollwnd_view), + notebook + ); + } + else if (toolbar) + { + GtkWidget* box_wrapper = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + + gtk_container_add( + GTK_CONTAINER(box_wrapper), + toolbar + ); + gtk_container_add( + GTK_CONTAINER(wnd->scrollwnd_view), + box_wrapper + ); + } + else + { + // FIXME: Probably just append the first item? Who knows... + // + wintc_messagebox_show( + GTK_WINDOW(wnd), + "Don't know how to deal with this widget tree.", + "Unrecognised Widget Tree", + GTK_BUTTONS_OK, + GTK_MESSAGE_ERROR + ); + } + + gtk_widget_show_all(wnd->scrollwnd_view); + } + else + { + wintc_display_error_and_clear(&error); + } + + g_object_unref(builder); +} + +// +// CALLBACKS +// +static void action_exit( + WINTC_UNUSED(GSimpleAction* action), + WINTC_UNUSED(GVariant* parameter), + gpointer user_data +) +{ + gtk_window_close(GTK_WINDOW(user_data)); +} + +static void action_open( + WINTC_UNUSED(GSimpleAction* action), + WINTC_UNUSED(GVariant* parameter), + gpointer user_data +) +{ + WinTCResvwrWindow* wnd = WINTC_RESVWR_WINDOW(user_data); + + // Open file dialog stuff + // + GtkWidget* dlg; + GtkFileFilter* filter_xml; + + filter_xml = gtk_file_filter_new(); + + gtk_file_filter_set_name(filter_xml, "XML Files"); + gtk_file_filter_add_mime_type(filter_xml, "text/xml"); + + dlg = + gtk_file_chooser_dialog_new( + wintc_lc_get_control_text(WINTC_CTLTXT_OPEN, WINTC_PUNC_NONE), + GTK_WINDOW(wnd), + GTK_FILE_CHOOSER_ACTION_OPEN, + wintc_lc_get_control_text(WINTC_CTLTXT_CANCEL, WINTC_PUNC_NONE), + GTK_RESPONSE_CANCEL, + wintc_lc_get_control_text(WINTC_CTLTXT_OPEN, WINTC_PUNC_NONE), + GTK_RESPONSE_ACCEPT, + NULL + ); + + gtk_file_chooser_add_filter( + GTK_FILE_CHOOSER(dlg), + filter_xml + ); + + // Execute dialog and handle result + // + gint result = gtk_dialog_run(GTK_DIALOG(dlg)); + + if (result == GTK_RESPONSE_ACCEPT) + { + wnd->path_resource = + gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg)); + + wintc_resvwr_window_refresh(wnd); + } + + gtk_widget_destroy(dlg); +} + +static void action_refresh( + WINTC_UNUSED(GSimpleAction* action), + WINTC_UNUSED(GVariant* parameter), + gpointer user_data +) +{ + WinTCResvwrWindow* wnd = WINTC_RESVWR_WINDOW(user_data); + + WINTC_LOG_DEBUG("resvwr: refreshing..."); + + wintc_resvwr_window_refresh(wnd); +} diff --git a/tools/resvwr/src/window.h b/tools/resvwr/src/window.h new file mode 100644 index 0000000..9860309 --- /dev/null +++ b/tools/resvwr/src/window.h @@ -0,0 +1,29 @@ +#ifndef __WINDOW_H__ +#define __WINDOW_H__ + +#include +#include + +#include "application.h" + +// +// GTK OOP BOILERPLATE +// +#define WINTC_TYPE_RESVWR_WINDOW (wintc_resvwr_window_get_type()) + +G_DECLARE_FINAL_TYPE( + WinTCResvwrWindow, + wintc_resvwr_window, + WINTC, + RESVWR_WINDOW, + GtkApplicationWindow +) + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_resvwr_window_new( + WinTCResvwrApplication* app +); + +#endif diff --git a/tools/resvwr/wintc-resvwr.desktop b/tools/resvwr/wintc-resvwr.desktop new file mode 100644 index 0000000..3f980d5 --- /dev/null +++ b/tools/resvwr/wintc-resvwr.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=Resource Viewer (WinTC) +Comment=Development tool for viewing resources in the WinTC project. +Exec=wintc-resvwr +Icon=wintc-resvwr +Terminal=false +StartupNotify=false +Type=Application +Categories=Development;GTK;