mirror of
https://github.com/rozniak/xfce-winxp-tc.git
synced 2026-05-01 03:31:33 +00:00
Enhancement: Fixes #435, comctl - alternative GMenu binding object
This commit is contained in:
82
private/play/menutest/CMakeLists.txt
Normal file
82
private/play/menutest/CMakeLists.txt
Normal file
@@ -0,0 +1,82 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(
|
||||
wintc-menutest
|
||||
VERSION 1.0
|
||||
DESCRIPTION "Windows Total Conversion menu binding test application."
|
||||
LANGUAGES C
|
||||
)
|
||||
|
||||
set(PROJECT_ANYARCH false)
|
||||
set(PROJECT_FREESTATUS true)
|
||||
set(PROJECT_MAINTAINER "Rory Fewell <roryf@oddmatics.uk>")
|
||||
|
||||
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_compile_resources()
|
||||
|
||||
add_executable(
|
||||
wintc-menutest
|
||||
src/application.c
|
||||
src/application.h
|
||||
src/main.c
|
||||
src/resources.c
|
||||
src/window.c
|
||||
src/window.h
|
||||
)
|
||||
|
||||
target_compile_options(
|
||||
wintc-menutest
|
||||
PRIVATE ${WINTC_COMPILE_OPTIONS}
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
wintc-menutest
|
||||
SYSTEM
|
||||
PRIVATE ${GLIB_INCLUDE_DIRS}
|
||||
PRIVATE ${GTK3_INCLUDE_DIRS}
|
||||
PRIVATE ${WINTC_COMCTL_INCLUDE_DIRS}
|
||||
PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_directories(
|
||||
wintc-menutest
|
||||
PRIVATE ${GLIB_LIBRARY_DIRS}
|
||||
PRIVATE ${GTK3_LIBRARY_DIRS}
|
||||
PRIVATE ${WINTC_COMCTL_LIBRARY_DIRS}
|
||||
PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
wintc-menutest
|
||||
PRIVATE ${GLIB_LIBRARIES}
|
||||
PRIVATE ${GTK3_LIBRARIES}
|
||||
PRIVATE ${WINTC_COMCTL_LIBRARIES}
|
||||
PRIVATE ${WINTC_COMGTK_LIBRARIES}
|
||||
)
|
||||
|
||||
# Installation
|
||||
#
|
||||
wintc_configure_and_install_packaging()
|
||||
wintc_install_icons_into_package()
|
||||
|
||||
install(
|
||||
FILES wintc-menutest.desktop
|
||||
DESTINATION share/applications
|
||||
)
|
||||
install(
|
||||
TARGETS wintc-menutest
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
4
private/play/menutest/README.MD
Normal file
4
private/play/menutest/README.MD
Normal file
@@ -0,0 +1,4 @@
|
||||
# hello
|
||||
This directory contains the source code for the *Hello* sample program.
|
||||
|
||||

|
||||
4
private/play/menutest/deps
Normal file
4
private/play/menutest/deps
Normal file
@@ -0,0 +1,4 @@
|
||||
bt,rt:glib2
|
||||
bt,rt:gtk3
|
||||
bt,rt:wintc-comctl
|
||||
bt,rt:wintc-comgtk
|
||||
BIN
private/play/menutest/icons/16x16/wintc-menutest.png
Normal file
BIN
private/play/menutest/icons/16x16/wintc-menutest.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 204 B |
BIN
private/play/menutest/icons/32x32/wintc-menutest.png
Normal file
BIN
private/play/menutest/icons/32x32/wintc-menutest.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 256 B |
75
private/play/menutest/src/application.c
Normal file
75
private/play/menutest/src/application.c
Normal file
@@ -0,0 +1,75 @@
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <wintc/comgtk.h>
|
||||
|
||||
#include "application.h"
|
||||
#include "window.h"
|
||||
|
||||
//
|
||||
// GTK OOP CLASS/INSTANCE DEFINITIONS
|
||||
//
|
||||
struct _WinTCMenuTestApplicationClass
|
||||
{
|
||||
GtkApplicationClass __parent__;
|
||||
};
|
||||
|
||||
struct _WinTCMenuTestApplication
|
||||
{
|
||||
GtkApplication __parent__;
|
||||
};
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static void wintc_menu_test_application_activate(
|
||||
GApplication* application
|
||||
);
|
||||
|
||||
//
|
||||
// GTK TYPE DEFINITIONS & CTORS
|
||||
//
|
||||
G_DEFINE_TYPE(
|
||||
WinTCMenuTestApplication,
|
||||
wintc_menu_test_application,
|
||||
GTK_TYPE_APPLICATION
|
||||
)
|
||||
|
||||
static void wintc_menu_test_application_class_init(
|
||||
WinTCMenuTestApplicationClass* klass
|
||||
)
|
||||
{
|
||||
GApplicationClass* application_class = G_APPLICATION_CLASS(klass);
|
||||
|
||||
application_class->activate = wintc_menu_test_application_activate;
|
||||
}
|
||||
|
||||
static void wintc_menu_test_application_init(
|
||||
WINTC_UNUSED(WinTCMenuTestApplication* self)
|
||||
) { }
|
||||
|
||||
//
|
||||
// CLASS VIRTUAL METHODS
|
||||
//
|
||||
static void wintc_menu_test_application_activate(
|
||||
GApplication* application
|
||||
)
|
||||
{
|
||||
GtkWidget* new_window =
|
||||
wintc_menu_test_window_new(WINTC_MENU_TEST_APPLICATION(application));
|
||||
|
||||
gtk_widget_show_all(new_window);
|
||||
}
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
WinTCMenuTestApplication* wintc_menu_test_application_new(void)
|
||||
{
|
||||
return WINTC_MENU_TEST_APPLICATION(
|
||||
g_object_new(
|
||||
wintc_menu_test_application_get_type(),
|
||||
"application-id", "uk.co.oddmatics.wintc.play.menu-test",
|
||||
NULL
|
||||
)
|
||||
);
|
||||
}
|
||||
27
private/play/menutest/src/application.h
Normal file
27
private/play/menutest/src/application.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef __APPLICATION_H__
|
||||
#define __APPLICATION_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
typedef struct _WinTCMenuTestApplicationClass WinTCMenuTestApplicationClass;
|
||||
typedef struct _WinTCMenuTestApplication WinTCMenuTestApplication;
|
||||
|
||||
#define WINTC_TYPE_MENU_TEST_APPLICATION (wintc_menu_test_application_get_type())
|
||||
#define WINTC_MENU_TEST_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WINTC_TYPE_MENU_TEST_APPLICATION, WinTCMenuTestApplication))
|
||||
#define WINTC_MENU_TEST_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WINTC_TYPE_MENU_TEST_APPLICATION, WinTCMenuTestApplicationClass))
|
||||
#define IS_WINTC_MENU_TEST_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WINTC_TYPE_MENU_TEST_APPLICATION))
|
||||
#define IS_WINTC_MENU_TEST_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WINTC_TYPE_MENU_TEST_APPLICATION))
|
||||
#define WINTC_MENU_TEST_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WINTC_TYPE_MENU_TEST_APPLICATION, WinTCMenuTestApplicationClass))
|
||||
|
||||
GType wintc_menu_test_application_get_type(void) G_GNUC_CONST;
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
WinTCMenuTestApplication* wintc_menu_test_application_new(void);
|
||||
|
||||
#endif
|
||||
20
private/play/menutest/src/main.c
Normal file
20
private/play/menutest/src/main.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include "application.h"
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char* argv[]
|
||||
)
|
||||
{
|
||||
WinTCMenuTestApplication* app = wintc_menu_test_application_new();
|
||||
int status;
|
||||
|
||||
g_set_application_name("Menu Test");
|
||||
|
||||
status = g_application_run(G_APPLICATION(app), argc, argv);
|
||||
|
||||
g_object_unref(app);
|
||||
|
||||
return status;
|
||||
}
|
||||
71
private/play/menutest/src/res/menubar.ui
Normal file
71
private/play/menutest/src/res/menubar.ui
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<menu id="menu">
|
||||
<submenu>
|
||||
<attribute name="label">Example menu</attribute>
|
||||
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label">Item 1</attribute>
|
||||
<attribute name="icon">add</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Item 2</attribute>
|
||||
<attribute name="icon">find</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Item 3</attribute>
|
||||
<attribute name="icon">paste</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label">Item 4</attribute>
|
||||
<attribute name="icon">computer</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Item 5</attribute>
|
||||
<attribute name="icon">printer</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Item 6</attribute>
|
||||
<attribute name="icon">drive-optical</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="label">Second menu</attribute>
|
||||
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label">Item 1</attribute>
|
||||
<attribute name="icon">add</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Item 2</attribute>
|
||||
<attribute name="icon">find</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Item 3</attribute>
|
||||
<attribute name="icon">paste</attribute>
|
||||
</item>
|
||||
</section>
|
||||
|
||||
<submenu>
|
||||
<attribute name="label">More</attribute>
|
||||
<attribute name="icon">applications-other</attribute>
|
||||
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label">Item 1</attribute>
|
||||
<attribute name="icon">add</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Item 2</attribute>
|
||||
<attribute name="icon">copy</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
</submenu>
|
||||
</menu>
|
||||
</interface>
|
||||
6
private/play/menutest/src/res/resources.xml
Normal file
6
private/play/menutest/src/res/resources.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/uk/oddmatics/wintc/samples/menutest">
|
||||
<file>menubar.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
142
private/play/menutest/src/window.c
Normal file
142
private/play/menutest/src/window.c
Normal file
@@ -0,0 +1,142 @@
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <wintc/comctl.h>
|
||||
#include <wintc/comgtk.h>
|
||||
|
||||
#include "application.h"
|
||||
#include "window.h"
|
||||
|
||||
// Change this to use either our binding or GTK
|
||||
//
|
||||
#define TEST_USE_CTL_BINDING 1
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static void wintc_menu_test_window_dispose(
|
||||
GObject* object
|
||||
);
|
||||
|
||||
//
|
||||
// GTK OOP CLASS/INSTANCE DEFINITIONS
|
||||
//
|
||||
struct _WinTCMenuTestWindowClass
|
||||
{
|
||||
GtkApplicationWindowClass __parent__;
|
||||
};
|
||||
|
||||
struct _WinTCMenuTestWindow
|
||||
{
|
||||
GtkApplicationWindow __parent__;
|
||||
|
||||
WinTCCtlMenuBinding* menu_binding;
|
||||
};
|
||||
|
||||
//
|
||||
// GTK TYPE DEFINITION & CTORS
|
||||
//
|
||||
G_DEFINE_TYPE(
|
||||
WinTCMenuTestWindow,
|
||||
wintc_menu_test_window,
|
||||
GTK_TYPE_APPLICATION_WINDOW
|
||||
)
|
||||
|
||||
static void wintc_menu_test_window_class_init(
|
||||
WinTCMenuTestWindowClass* klass
|
||||
)
|
||||
{
|
||||
GObjectClass* object_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
object_class->dispose = wintc_menu_test_window_dispose;
|
||||
}
|
||||
|
||||
static void wintc_menu_test_window_init(
|
||||
WinTCMenuTestWindow* self
|
||||
)
|
||||
{
|
||||
GtkWidget* box_container = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||
|
||||
gtk_window_set_default_size(
|
||||
GTK_WINDOW(self),
|
||||
320,
|
||||
200
|
||||
);
|
||||
|
||||
// Retrieve menu model
|
||||
//
|
||||
GtkBuilder* builder =
|
||||
gtk_builder_new_from_resource(
|
||||
"/uk/oddmatics/wintc/samples/menutest/menubar.ui"
|
||||
);
|
||||
|
||||
GMenuModel* model =
|
||||
G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
|
||||
|
||||
g_object_ref(model);
|
||||
|
||||
g_object_unref(builder);
|
||||
|
||||
// Create a boring menu strip
|
||||
//
|
||||
GtkWidget* menu_bar = gtk_menu_bar_new();
|
||||
|
||||
if (TEST_USE_CTL_BINDING)
|
||||
{
|
||||
self->menu_binding =
|
||||
wintc_ctl_menu_binding_new(
|
||||
GTK_MENU_SHELL(menu_bar),
|
||||
model
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_menu_shell_bind_model(
|
||||
GTK_MENU_SHELL(menu_bar),
|
||||
model,
|
||||
NULL,
|
||||
FALSE
|
||||
);
|
||||
}
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(box_container), menu_bar);
|
||||
|
||||
// Show!
|
||||
//
|
||||
gtk_container_add(GTK_CONTAINER(self), box_container);
|
||||
gtk_widget_show_all(box_container);
|
||||
}
|
||||
|
||||
//
|
||||
// CLASS VIRTUAL METHODS
|
||||
//
|
||||
static void wintc_menu_test_window_dispose(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
WinTCMenuTestWindow* wnd = WINTC_MENU_TEST_WINDOW(object);
|
||||
|
||||
if (wnd->menu_binding)
|
||||
{
|
||||
g_clear_object(&(wnd->menu_binding));
|
||||
}
|
||||
|
||||
(G_OBJECT_CLASS(wintc_menu_test_window_parent_class))
|
||||
->dispose(object);
|
||||
}
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
GtkWidget* wintc_menu_test_window_new(
|
||||
WinTCMenuTestApplication* app
|
||||
)
|
||||
{
|
||||
return GTK_WIDGET(
|
||||
g_object_new(
|
||||
WINTC_TYPE_MENU_TEST_WINDOW,
|
||||
"application", GTK_APPLICATION(app),
|
||||
"title", "Menu Binding Test",
|
||||
NULL
|
||||
)
|
||||
);
|
||||
}
|
||||
31
private/play/menutest/src/window.h
Normal file
31
private/play/menutest/src/window.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef __WINDOW_H__
|
||||
#define __WINDOW_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "application.h"
|
||||
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
typedef struct _WinTCMenuTestWindowClass WinTCMenuTestWindowClass;
|
||||
typedef struct _WinTCMenuTestWindow WinTCMenuTestWindow;
|
||||
|
||||
#define WINTC_TYPE_MENU_TEST_WINDOW (wintc_menu_test_window_get_type())
|
||||
#define WINTC_MENU_TEST_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WINTC_TYPE_MENU_TEST_WINDOW, WinTCMenuTestWindow))
|
||||
#define WINTC_MENU_TEST_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WINTC_TYPE_MENU_TEST_WINDOW, WinTCMenuTestWindowClass))
|
||||
#define IS_WINTC_MENU_TEST_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WINTC_TYPE_MENU_TEST_WINDOW))
|
||||
#define IS_WINTC_MENU_TEST_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WINTC_TYPE_MENU_TEST_WINDOW))
|
||||
#define WINTC_MENU_TEST_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WINTC_TYPE_MENU_TEST_WINDOW, WinTCMenuTestWindowClass))
|
||||
|
||||
GType wintc_menu_test_window_get_type(void) G_GNUC_CONST;
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
GtkWidget* wintc_menu_test_window_new(
|
||||
WinTCMenuTestApplication* app
|
||||
);
|
||||
|
||||
#endif
|
||||
9
private/play/menutest/wintc-menutest.desktop
Normal file
9
private/play/menutest/wintc-menutest.desktop
Normal file
@@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Name=Menu Test (WinTC Test)
|
||||
Comment=WinTC menu binding test program.
|
||||
Exec=wintc-menutest
|
||||
Icon=wintc-menutest
|
||||
Terminal=false
|
||||
StartupNotify=false
|
||||
Type=Application
|
||||
Categories=Utility;GTK;
|
||||
@@ -37,6 +37,8 @@ add_library(
|
||||
public/animctl.h
|
||||
src/cpl.c
|
||||
public/cpl.h
|
||||
src/menubind.c
|
||||
public/menubind.h
|
||||
src/style.c
|
||||
public/style.h
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "@LIB_HEADER_DIR@/animctl.h"
|
||||
#include "@LIB_HEADER_DIR@/cpl.h"
|
||||
#include "@LIB_HEADER_DIR@/menubind.h"
|
||||
#include "@LIB_HEADER_DIR@/style.h"
|
||||
|
||||
#endif
|
||||
|
||||
28
shared/comctl/public/menubind.h
Normal file
28
shared/comctl/public/menubind.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef __COMCTL_MENUBIND_H__
|
||||
#define __COMCTL_MENUBIND_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
//
|
||||
// GTK OOP BOILERPLATE
|
||||
//
|
||||
#define WINTC_TYPE_CTL_MENU_BINDING (wintc_ctl_menu_binding_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE(
|
||||
WinTCCtlMenuBinding,
|
||||
wintc_ctl_menu_binding,
|
||||
WINTC,
|
||||
CTL_MENU_BINDING,
|
||||
GObject
|
||||
)
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
WinTCCtlMenuBinding* wintc_ctl_menu_binding_new(
|
||||
GtkMenuShell* menu_shell,
|
||||
GMenuModel* menu_model
|
||||
);
|
||||
|
||||
#endif
|
||||
755
shared/comctl/src/menubind.c
Normal file
755
shared/comctl/src/menubind.c
Normal file
@@ -0,0 +1,755 @@
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <wintc/comgtk.h>
|
||||
|
||||
#include "../public/menubind.h"
|
||||
|
||||
//
|
||||
// PRIVATE ENUMS
|
||||
//
|
||||
enum
|
||||
{
|
||||
PROP_MENU_SHELL = 1,
|
||||
PROP_MENU_MODEL
|
||||
};
|
||||
|
||||
//
|
||||
// PRIVATE STRUCTURES
|
||||
//
|
||||
typedef struct _WinTCCtlMenuBindingMenu
|
||||
{
|
||||
WinTCCtlMenuBinding* menu_binding;
|
||||
|
||||
GtkMenuShell* menu_shell;
|
||||
GMenuModel* menu_model;
|
||||
|
||||
GList* sections;
|
||||
} WinTCCtlMenuBindingMenu;
|
||||
|
||||
typedef struct _WinTCCtlMenuBindingSection
|
||||
{
|
||||
WinTCCtlMenuBindingMenu* parent_menu;
|
||||
|
||||
GMenuModel* menu_model;
|
||||
gint item_count;
|
||||
} WinTCCtlMenuBindingSection;
|
||||
|
||||
//
|
||||
// FORWARD DECLARATIONS
|
||||
//
|
||||
static void wintc_ctl_menu_binding_constructed(
|
||||
GObject* object
|
||||
);
|
||||
static void wintc_ctl_menu_binding_dispose(
|
||||
GObject* object
|
||||
);
|
||||
static void wintc_ctl_menu_binding_finalize(
|
||||
GObject* object
|
||||
);
|
||||
static void wintc_ctl_menu_binding_get_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
GValue* value,
|
||||
GParamSpec* pspec
|
||||
);
|
||||
static void wintc_ctl_menu_binding_set_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
const GValue* value,
|
||||
GParamSpec* pspec
|
||||
);
|
||||
|
||||
static GList* wintc_ctl_menu_binding_find_section_pos(
|
||||
WinTCCtlMenuBindingMenu* menu,
|
||||
gint dst_pos,
|
||||
gint* mid_pos
|
||||
);
|
||||
static void wintc_ctl_menu_binding_insert_item(
|
||||
WinTCCtlMenuBindingMenu* menu,
|
||||
GMenuModel* menu_model,
|
||||
gint src_pos,
|
||||
gint dst_pos
|
||||
);
|
||||
static void wintc_ctl_menu_binding_track_menu(
|
||||
WinTCCtlMenuBinding* menu_binding,
|
||||
GtkMenuShell* menu_shell,
|
||||
GMenuModel* menu_model
|
||||
);
|
||||
|
||||
static void wintc_ctl_menu_binding_menu_free(
|
||||
WinTCCtlMenuBindingMenu* menu
|
||||
);
|
||||
|
||||
//
|
||||
// GTK OOP CLASS/INSTANCE DEFINITIONS
|
||||
//
|
||||
typedef struct _WinTCCtlMenuBinding
|
||||
{
|
||||
GObject __parent__;
|
||||
|
||||
GtkMenuShell* menu_shell;
|
||||
GMenuModel* menu_model;
|
||||
|
||||
GSList* tracked_menus;
|
||||
} WinTCCtlMenuBinding;
|
||||
|
||||
//
|
||||
// GTK TYPE DEFINITIONS & CTORS
|
||||
//
|
||||
G_DEFINE_TYPE(
|
||||
WinTCCtlMenuBinding,
|
||||
wintc_ctl_menu_binding,
|
||||
G_TYPE_OBJECT
|
||||
)
|
||||
|
||||
static void wintc_ctl_menu_binding_class_init(
|
||||
WinTCCtlMenuBindingClass* klass
|
||||
)
|
||||
{
|
||||
GObjectClass* object_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
object_class->constructed = wintc_ctl_menu_binding_constructed;
|
||||
object_class->dispose = wintc_ctl_menu_binding_dispose;
|
||||
object_class->finalize = wintc_ctl_menu_binding_finalize;
|
||||
object_class->get_property = wintc_ctl_menu_binding_get_property;
|
||||
object_class->set_property = wintc_ctl_menu_binding_set_property;
|
||||
|
||||
g_object_class_install_property(
|
||||
object_class,
|
||||
PROP_MENU_SHELL,
|
||||
g_param_spec_object(
|
||||
"menu-shell",
|
||||
"MenuShell",
|
||||
"The GTK menu shell to manage.",
|
||||
GTK_TYPE_MENU_SHELL,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY
|
||||
)
|
||||
);
|
||||
g_object_class_install_property(
|
||||
object_class,
|
||||
PROP_MENU_MODEL,
|
||||
g_param_spec_object(
|
||||
"menu-model",
|
||||
"MenuModel",
|
||||
"The menu model to bind to.",
|
||||
G_TYPE_MENU_MODEL,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void wintc_ctl_menu_binding_init(
|
||||
WINTC_UNUSED(WinTCCtlMenuBinding* self)
|
||||
) {}
|
||||
|
||||
//
|
||||
// CLASS VIRTUAL METHODS
|
||||
//
|
||||
static void wintc_ctl_menu_binding_constructed(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
WinTCCtlMenuBinding* menu_binding = WINTC_CTL_MENU_BINDING(object);
|
||||
|
||||
wintc_container_clear(
|
||||
GTK_CONTAINER(menu_binding->menu_shell)
|
||||
);
|
||||
|
||||
wintc_ctl_menu_binding_track_menu(
|
||||
menu_binding,
|
||||
menu_binding->menu_shell,
|
||||
menu_binding->menu_model
|
||||
);
|
||||
|
||||
(G_OBJECT_CLASS(wintc_ctl_menu_binding_parent_class))
|
||||
->constructed(object);
|
||||
}
|
||||
|
||||
static void wintc_ctl_menu_binding_dispose(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
WinTCCtlMenuBinding* menu_binding = WINTC_CTL_MENU_BINDING(object);
|
||||
|
||||
g_clear_object(&(menu_binding->menu_shell));
|
||||
g_clear_object(&(menu_binding->menu_model));
|
||||
|
||||
(G_OBJECT_CLASS(wintc_ctl_menu_binding_parent_class))
|
||||
->dispose(object);
|
||||
}
|
||||
|
||||
static void wintc_ctl_menu_binding_finalize(
|
||||
GObject* object
|
||||
)
|
||||
{
|
||||
WinTCCtlMenuBinding* menu_binding = WINTC_CTL_MENU_BINDING(object);
|
||||
|
||||
g_clear_slist(
|
||||
&(menu_binding->tracked_menus),
|
||||
(GDestroyNotify) wintc_ctl_menu_binding_menu_free
|
||||
);
|
||||
|
||||
(G_OBJECT_CLASS(wintc_ctl_menu_binding_parent_class))
|
||||
->finalize(object);
|
||||
}
|
||||
|
||||
static void wintc_ctl_menu_binding_get_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
WINTC_UNUSED(GValue* value),
|
||||
GParamSpec* pspec
|
||||
)
|
||||
{
|
||||
switch (prop_id)
|
||||
{
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void wintc_ctl_menu_binding_set_property(
|
||||
GObject* object,
|
||||
guint prop_id,
|
||||
const GValue* value,
|
||||
GParamSpec* pspec
|
||||
)
|
||||
{
|
||||
WinTCCtlMenuBinding* menu_binding = WINTC_CTL_MENU_BINDING(object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MENU_SHELL:
|
||||
menu_binding->menu_shell =
|
||||
g_value_dup_object(value);
|
||||
|
||||
break;
|
||||
|
||||
case PROP_MENU_MODEL:
|
||||
menu_binding->menu_model =
|
||||
g_value_dup_object(value);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// PUBLIC FUNCTIONS
|
||||
//
|
||||
WinTCCtlMenuBinding* wintc_ctl_menu_binding_new(
|
||||
GtkMenuShell* menu_shell,
|
||||
GMenuModel* menu_model
|
||||
)
|
||||
{
|
||||
return WINTC_CTL_MENU_BINDING(
|
||||
g_object_new(
|
||||
WINTC_TYPE_CTL_MENU_BINDING,
|
||||
"menu-shell", menu_shell,
|
||||
"menu-model", menu_model,
|
||||
NULL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// PRIVATE FUNCTIONS
|
||||
//
|
||||
static GList* wintc_ctl_menu_binding_find_section_pos(
|
||||
WinTCCtlMenuBindingMenu* menu,
|
||||
gint dst_pos,
|
||||
gint* mid_pos
|
||||
)
|
||||
{
|
||||
gint i = 0;
|
||||
GList* iter = menu->sections;
|
||||
|
||||
if (!iter)
|
||||
{
|
||||
*mid_pos = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (; iter; iter = iter->next)
|
||||
{
|
||||
WinTCCtlMenuBindingSection* section =
|
||||
(WinTCCtlMenuBindingSection*) iter->data;
|
||||
|
||||
// Have we hit our target?
|
||||
//
|
||||
if (i == dst_pos)
|
||||
{
|
||||
*mid_pos = 0;
|
||||
return iter;
|
||||
}
|
||||
|
||||
if (!(section->menu_model))
|
||||
{
|
||||
// Check if section should be inserted in the middle of a fake
|
||||
// section
|
||||
//
|
||||
if (i + section->item_count > dst_pos)
|
||||
{
|
||||
*mid_pos = dst_pos - i;
|
||||
return iter;
|
||||
}
|
||||
|
||||
// Progress num of items in the fake section
|
||||
//
|
||||
i += section->item_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Progress only 1 as a real section is considered 1 item
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
*mid_pos = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void wintc_ctl_menu_binding_insert_item(
|
||||
WinTCCtlMenuBindingMenu* menu,
|
||||
GMenuModel* menu_model,
|
||||
gint src_pos,
|
||||
gint dst_pos
|
||||
)
|
||||
{
|
||||
gboolean is_menubar = GTK_IS_MENU_BAR(menu->menu_shell);
|
||||
|
||||
// If this is a section, get it set up - find where to insert it in the
|
||||
// list... it could be in the middle of normal menu items
|
||||
//
|
||||
GMenuModel* section =
|
||||
g_menu_model_get_item_link(
|
||||
menu_model,
|
||||
src_pos,
|
||||
G_MENU_LINK_SECTION
|
||||
);
|
||||
|
||||
if (section)
|
||||
{
|
||||
// Create new section
|
||||
//
|
||||
WinTCCtlMenuBindingSection* new_section =
|
||||
g_new(WinTCCtlMenuBindingSection, 1);
|
||||
|
||||
new_section->parent_menu = menu;
|
||||
new_section->menu_model = section;
|
||||
new_section->item_count = 0;
|
||||
|
||||
// Find where to insert
|
||||
//
|
||||
gint mid_pos = 0;
|
||||
GList* target_li =
|
||||
wintc_ctl_menu_binding_find_section_pos(menu, dst_pos, &mid_pos);
|
||||
|
||||
if (!target_li)
|
||||
{
|
||||
menu->sections =
|
||||
g_list_append(menu->sections, new_section);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mid_pos)
|
||||
{
|
||||
WinTCCtlMenuBindingSection* target =
|
||||
(WinTCCtlMenuBindingSection*) target_li->data;
|
||||
|
||||
// Need to split up 'fake' section
|
||||
//
|
||||
WinTCCtlMenuBindingSection* new_subsection =
|
||||
g_new(WinTCCtlMenuBindingSection, 1);
|
||||
|
||||
new_subsection->parent_menu = menu;
|
||||
new_subsection->menu_model = NULL;
|
||||
new_subsection->item_count = target->item_count - mid_pos;
|
||||
|
||||
target->item_count -= new_subsection->item_count;
|
||||
|
||||
// Append the new section + the latter half of the split fake
|
||||
// section
|
||||
//
|
||||
menu->sections =
|
||||
g_list_insert_before(
|
||||
menu->sections,
|
||||
target_li->next,
|
||||
new_section
|
||||
);
|
||||
menu->sections =
|
||||
g_list_insert_before(
|
||||
menu->sections,
|
||||
target_li->next,
|
||||
new_subsection
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu->sections =
|
||||
g_list_insert_before(
|
||||
menu->sections,
|
||||
target_li,
|
||||
new_section
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert items from the section
|
||||
//
|
||||
gint n_items = g_menu_model_get_n_items(section);
|
||||
|
||||
for (gint i = 0; i < n_items; i++)
|
||||
{
|
||||
wintc_ctl_menu_binding_insert_item(
|
||||
menu,
|
||||
section,
|
||||
i,
|
||||
i
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Actually have an item - pull its data
|
||||
//
|
||||
GIcon* icon = NULL;
|
||||
GVariant* icon_var;
|
||||
gchar* label = NULL;
|
||||
|
||||
g_menu_model_get_item_attribute(
|
||||
menu_model,
|
||||
src_pos,
|
||||
G_MENU_ATTRIBUTE_LABEL,
|
||||
"s",
|
||||
&label
|
||||
);
|
||||
|
||||
icon_var =
|
||||
g_menu_model_get_item_attribute_value(
|
||||
menu_model,
|
||||
src_pos,
|
||||
G_MENU_ATTRIBUTE_ICON,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (icon_var)
|
||||
{
|
||||
icon = g_icon_deserialize(icon_var);
|
||||
g_variant_unref(icon_var);
|
||||
}
|
||||
|
||||
// Get action information
|
||||
//
|
||||
gchar* action_name = NULL;
|
||||
GVariant* action_target;
|
||||
|
||||
if (
|
||||
g_menu_model_get_item_attribute(
|
||||
menu_model,
|
||||
src_pos,
|
||||
G_MENU_ATTRIBUTE_ACTION,
|
||||
"s",
|
||||
&action_name
|
||||
)
|
||||
)
|
||||
{
|
||||
action_target =
|
||||
g_menu_model_get_item_attribute_value(
|
||||
menu_model,
|
||||
src_pos,
|
||||
G_MENU_ATTRIBUTE_TARGET,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
// Create the item
|
||||
//
|
||||
GtkWidget* box_container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
GtkWidget* menu_item = gtk_menu_item_new();
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(menu_item), box_container);
|
||||
|
||||
if (!is_menubar) // Only add icons to submenus
|
||||
{
|
||||
GtkWidget* img_icon = gtk_image_new();
|
||||
|
||||
gtk_widget_set_margin_end(img_icon, 6); // Got this from Mousepad
|
||||
gtk_widget_set_size_request(img_icon, 16, 16);
|
||||
|
||||
if (icon)
|
||||
{
|
||||
gtk_image_set_from_gicon(
|
||||
GTK_IMAGE(img_icon),
|
||||
icon,
|
||||
GTK_ICON_SIZE_MENU
|
||||
);
|
||||
gtk_image_set_pixel_size(
|
||||
GTK_IMAGE(img_icon),
|
||||
16
|
||||
);
|
||||
|
||||
g_object_unref(icon);
|
||||
}
|
||||
|
||||
gtk_box_pack_start(
|
||||
GTK_BOX(box_container),
|
||||
img_icon,
|
||||
FALSE,
|
||||
FALSE,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
if (action_name)
|
||||
{
|
||||
gtk_actionable_set_action_name(
|
||||
GTK_ACTIONABLE(menu_item),
|
||||
action_name
|
||||
);
|
||||
gtk_actionable_set_action_target_value(
|
||||
GTK_ACTIONABLE(menu_item),
|
||||
action_target
|
||||
);
|
||||
|
||||
g_free(action_name);
|
||||
g_variant_unref(action_target);
|
||||
}
|
||||
|
||||
if (label)
|
||||
{
|
||||
GtkWidget* label_title = gtk_label_new(label);
|
||||
|
||||
gtk_label_set_xalign(GTK_LABEL(label_title), 0.0f);
|
||||
|
||||
gtk_box_pack_start(
|
||||
GTK_BOX(box_container),
|
||||
label_title,
|
||||
TRUE,
|
||||
TRUE,
|
||||
0
|
||||
);
|
||||
|
||||
g_free(label);
|
||||
}
|
||||
|
||||
// Do we have a submenu for this item?
|
||||
//
|
||||
GMenuModel* submenu_model =
|
||||
g_menu_model_get_item_link(
|
||||
menu_model,
|
||||
src_pos,
|
||||
G_MENU_LINK_SUBMENU
|
||||
);
|
||||
|
||||
if (submenu_model)
|
||||
{
|
||||
GtkWidget* submenu = gtk_menu_new();
|
||||
|
||||
gtk_menu_set_reserve_toggle_size(GTK_MENU(submenu), FALSE);
|
||||
|
||||
wintc_ctl_menu_binding_track_menu(
|
||||
menu->menu_binding,
|
||||
GTK_MENU_SHELL(submenu),
|
||||
submenu_model
|
||||
);
|
||||
|
||||
gtk_menu_item_set_submenu(
|
||||
GTK_MENU_ITEM(menu_item),
|
||||
submenu
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Insert the item
|
||||
//
|
||||
GList* iter = menu->sections;
|
||||
gint real_pos = 0;
|
||||
|
||||
// menu_model could either be the menu itself or one of its child sections
|
||||
//
|
||||
gboolean is_subsection = menu_model != menu->menu_model;
|
||||
|
||||
// If its a subsection then locate it and append the item
|
||||
//
|
||||
if (is_subsection)
|
||||
{
|
||||
for (; iter; iter = iter->next)
|
||||
{
|
||||
WinTCCtlMenuBindingSection* check_section =
|
||||
(WinTCCtlMenuBindingSection*) iter->data;
|
||||
|
||||
// Is this the correct section? If so, insert and move on
|
||||
//
|
||||
if (menu_model == check_section->menu_model)
|
||||
{
|
||||
check_section->item_count++;
|
||||
real_pos += dst_pos; // Location within the section
|
||||
|
||||
gtk_menu_shell_insert(menu->menu_shell, menu_item, real_pos);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// No? Increment real_pos as needed
|
||||
//
|
||||
real_pos += check_section->item_count;
|
||||
}
|
||||
|
||||
// This should never happen - an attempt to add an item within a
|
||||
// section into a menu which does not contain the section
|
||||
//
|
||||
g_critical(
|
||||
"%s",
|
||||
"ctl - menubind - section not found in menu for item?"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Continue - add the item directly into the menu
|
||||
//
|
||||
if (!iter)
|
||||
{
|
||||
// If there isn't yet any sections, then this must be the first item
|
||||
//
|
||||
if (dst_pos > 0)
|
||||
{
|
||||
g_warning(
|
||||
"ctl - menubind - first item not at 0? (wants %d)",
|
||||
dst_pos
|
||||
);
|
||||
}
|
||||
|
||||
// Create fake section
|
||||
//
|
||||
WinTCCtlMenuBindingSection* fake_section =
|
||||
g_new(WinTCCtlMenuBindingSection, 1);
|
||||
|
||||
fake_section->parent_menu = menu;
|
||||
fake_section->menu_model = NULL;
|
||||
fake_section->item_count = 1;
|
||||
|
||||
menu->sections =
|
||||
g_list_append(menu->sections, fake_section);
|
||||
|
||||
gtk_menu_shell_append(menu->menu_shell, menu_item);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to iterate through sections to find where to add this item
|
||||
//
|
||||
gint i = 0;
|
||||
|
||||
while (i < dst_pos)
|
||||
{
|
||||
WinTCCtlMenuBindingSection* subsection =
|
||||
(WinTCCtlMenuBindingSection*) iter->data;
|
||||
|
||||
// Check we're not overshooting - could be within a 'fake' section...
|
||||
//
|
||||
if (
|
||||
!(subsection->menu_model) &&
|
||||
i + subsection->item_count >= dst_pos
|
||||
)
|
||||
{
|
||||
real_pos += (dst_pos - i);
|
||||
break;
|
||||
}
|
||||
|
||||
// No where else to go?
|
||||
//
|
||||
if (!(iter->next))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Moving on...
|
||||
//
|
||||
iter = iter->next;
|
||||
real_pos += subsection->item_count;
|
||||
|
||||
if (subsection->menu_model)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
i += subsection->item_count;
|
||||
}
|
||||
}
|
||||
|
||||
// If we fell out, we need to add the item to the end of the menu
|
||||
//
|
||||
WinTCCtlMenuBindingSection* last_section =
|
||||
(WinTCCtlMenuBindingSection*) iter->data;
|
||||
|
||||
if (last_section->menu_model)
|
||||
{
|
||||
// If the last section is not fake, then we need to append a
|
||||
// fake section now with the item in it
|
||||
//
|
||||
last_section = g_new(WinTCCtlMenuBindingSection, 1);
|
||||
|
||||
last_section->parent_menu = menu;
|
||||
last_section->menu_model = NULL;
|
||||
last_section->item_count = 0; // Will be incremented in a moment
|
||||
|
||||
menu->sections =
|
||||
g_list_append(menu->sections, last_section);
|
||||
|
||||
// FIXME: Probably append a separator here first
|
||||
}
|
||||
|
||||
last_section->item_count++;
|
||||
gtk_menu_shell_append(menu->menu_shell, menu_item);
|
||||
}
|
||||
|
||||
static void wintc_ctl_menu_binding_track_menu(
|
||||
WinTCCtlMenuBinding* menu_binding,
|
||||
GtkMenuShell* menu_shell,
|
||||
GMenuModel* menu_model
|
||||
)
|
||||
{
|
||||
// Initialise new tracker
|
||||
//
|
||||
WinTCCtlMenuBindingMenu* tracker =
|
||||
g_new(WinTCCtlMenuBindingMenu, 1);
|
||||
|
||||
tracker->menu_binding = menu_binding;
|
||||
tracker->menu_shell = menu_shell;
|
||||
tracker->menu_model = menu_model;
|
||||
tracker->sections = NULL;
|
||||
|
||||
menu_binding->tracked_menus =
|
||||
g_slist_append(
|
||||
menu_binding->tracked_menus,
|
||||
tracker
|
||||
);
|
||||
|
||||
// Iterate over the menu model
|
||||
//
|
||||
gint n_menu_items = g_menu_model_get_n_items(menu_model);
|
||||
|
||||
for (gint i = 0; i < n_menu_items; i++)
|
||||
{
|
||||
wintc_ctl_menu_binding_insert_item(
|
||||
tracker,
|
||||
tracker->menu_model,
|
||||
i,
|
||||
i
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void wintc_ctl_menu_binding_menu_free(
|
||||
WinTCCtlMenuBindingMenu* menu
|
||||
)
|
||||
{
|
||||
g_clear_list(&(menu->sections), (GDestroyNotify) g_free);
|
||||
g_free(menu);
|
||||
}
|
||||
Reference in New Issue
Block a user