@@ -88,7 +88,9 @@ class MenuItem::Impl {
8888 type_(type),
8989 state_(MenuItemState::Unchecked),
9090 radio_group_(-1 ),
91- accelerator_(" " , KeyboardAccelerator::None) {}
91+ accelerator_(" " , KeyboardAccelerator::None),
92+ activate_handler_id_(0 ),
93+ toggled_handler_id_(0 ) {}
9294
9395 void ApplyRadioGroup () {
9496 if (!gtk_menu_item_ || type_ != MenuItemType::Radio || radio_group_ < 0 ) {
@@ -123,6 +125,10 @@ class MenuItem::Impl {
123125 int radio_group_;
124126 KeyboardAccelerator accelerator_;
125127 std::shared_ptr<Menu> submenu_;
128+
129+ // Signal handler IDs for cleanup
130+ gulong activate_handler_id_;
131+ gulong toggled_handler_id_;
126132
127133 // Shared map from logical group id to GTK group list
128134 static std::unordered_map<int , GSList*> s_group_map_;
@@ -165,9 +171,11 @@ MenuItem::MenuItem(const std::string& label, MenuItemType type) {
165171 // Connect signals for click/toggle events (except separators)
166172 if (gtk_item && type != MenuItemType::Separator) {
167173 if (type == MenuItemType::Checkbox || type == MenuItemType::Radio) {
168- g_signal_connect (G_OBJECT (gtk_item), " toggled" , G_CALLBACK (OnGtkCheckMenuItemToggled), this );
174+ pimpl_->toggled_handler_id_ = g_signal_connect (G_OBJECT (gtk_item), " toggled" ,
175+ G_CALLBACK (OnGtkCheckMenuItemToggled), this );
169176 } else {
170- g_signal_connect (G_OBJECT (gtk_item), " activate" , G_CALLBACK (OnGtkMenuItemActivate), this );
177+ pimpl_->activate_handler_id_ = g_signal_connect (G_OBJECT (gtk_item), " activate" ,
178+ G_CALLBACK (OnGtkMenuItemActivate), this );
171179 }
172180 }
173181}
@@ -186,16 +194,39 @@ MenuItem::MenuItem(void* menu_item) {
186194
187195 if (pimpl_->gtk_menu_item_ ) {
188196 if (GTK_IS_CHECK_MENU_ITEM (pimpl_->gtk_menu_item_ )) {
189- g_signal_connect (G_OBJECT (pimpl_->gtk_menu_item_ ), " toggled" ,
190- G_CALLBACK (OnGtkCheckMenuItemToggled), this );
197+ pimpl_-> toggled_handler_id_ = g_signal_connect (G_OBJECT (pimpl_->gtk_menu_item_ ), " toggled" ,
198+ G_CALLBACK (OnGtkCheckMenuItemToggled), this );
191199 } else {
192- g_signal_connect (G_OBJECT (pimpl_->gtk_menu_item_ ), " activate" ,
193- G_CALLBACK (OnGtkMenuItemActivate), this );
200+ pimpl_-> activate_handler_id_ = g_signal_connect (G_OBJECT (pimpl_->gtk_menu_item_ ), " activate" ,
201+ G_CALLBACK (OnGtkMenuItemActivate), this );
194202 }
195203 }
196204}
197205
198- MenuItem::~MenuItem () {}
206+ MenuItem::~MenuItem () {
207+ // Disconnect signal handlers before destruction to prevent accessing freed memory
208+ if (pimpl_->gtk_menu_item_ ) {
209+ if (pimpl_->activate_handler_id_ > 0 ) {
210+ g_signal_handler_disconnect (G_OBJECT (pimpl_->gtk_menu_item_ ), pimpl_->activate_handler_id_ );
211+ pimpl_->activate_handler_id_ = 0 ;
212+ }
213+ if (pimpl_->toggled_handler_id_ > 0 ) {
214+ g_signal_handler_disconnect (G_OBJECT (pimpl_->gtk_menu_item_ ), pimpl_->toggled_handler_id_ );
215+ pimpl_->toggled_handler_id_ = 0 ;
216+ }
217+
218+ // Disconnect submenu map/unmap handlers if they exist
219+ if (pimpl_->submenu_ && pimpl_->submenu_ ->GetNativeObject ()) {
220+ GtkWidget* submenu_widget = (GtkWidget*)pimpl_->submenu_ ->GetNativeObject ();
221+ if (submenu_widget) {
222+ g_signal_handlers_disconnect_by_func (G_OBJECT (submenu_widget),
223+ (gpointer)OnGtkSubmenuMap, this );
224+ g_signal_handlers_disconnect_by_func (G_OBJECT (submenu_widget),
225+ (gpointer)OnGtkSubmenuUnmap, this );
226+ }
227+ }
228+ }
229+ }
199230
200231MenuItemId MenuItem::GetId () const {
201232 return pimpl_->id_ ;
@@ -424,34 +455,53 @@ void* MenuItem::GetNativeObjectInternal() const {
424455// Private implementation class for Menu
425456class Menu ::Impl {
426457 public:
427- Impl (MenuId id, GtkWidget* menu) : id_(id), gtk_menu_(menu) {}
458+ Impl (MenuId id, GtkWidget* menu)
459+ : id_(id), gtk_menu_(menu), map_handler_id_(0 ), unmap_handler_id_(0 ) {}
428460
429461 MenuId id_;
430462 GtkWidget* gtk_menu_;
431463 std::vector<std::shared_ptr<MenuItem>> items_;
464+
465+ // Signal handler IDs for cleanup
466+ gulong map_handler_id_;
467+ gulong unmap_handler_id_;
432468};
433469
434470Menu::Menu () {
435471 MenuId id = IdAllocator::Allocate<Menu>();
436472 pimpl_ = std::unique_ptr<Impl>(new Impl (id, gtk_menu_new ()));
437473 // Connect menu map/unmap to emit open/close events when actually visible
438474 if (pimpl_->gtk_menu_ ) {
439- g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " map" , G_CALLBACK (OnGtkMenuMap), this );
440- g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " unmap" , G_CALLBACK (OnGtkMenuUnmap), this );
475+ pimpl_->map_handler_id_ = g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " map" ,
476+ G_CALLBACK (OnGtkMenuMap), this );
477+ pimpl_->unmap_handler_id_ = g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " unmap" ,
478+ G_CALLBACK (OnGtkMenuUnmap), this );
441479 }
442480}
443481
444482Menu::Menu (void * menu) {
445483 MenuId id = IdAllocator::Allocate<Menu>();
446484 pimpl_ = std::unique_ptr<Impl>(new Impl (id, (GtkWidget*)menu));
447485 if (pimpl_->gtk_menu_ ) {
448- g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " map" , G_CALLBACK (OnGtkMenuMap), this );
449- g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " unmap" , G_CALLBACK (OnGtkMenuUnmap), this );
486+ pimpl_->map_handler_id_ = g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " map" ,
487+ G_CALLBACK (OnGtkMenuMap), this );
488+ pimpl_->unmap_handler_id_ = g_signal_connect (G_OBJECT (pimpl_->gtk_menu_ ), " unmap" ,
489+ G_CALLBACK (OnGtkMenuUnmap), this );
450490 }
451491}
452492
453493Menu::~Menu () {
494+ // Disconnect signal handlers before unreferencing to prevent accessing freed memory
454495 if (pimpl_->gtk_menu_ ) {
496+ if (pimpl_->map_handler_id_ > 0 ) {
497+ g_signal_handler_disconnect (G_OBJECT (pimpl_->gtk_menu_ ), pimpl_->map_handler_id_ );
498+ pimpl_->map_handler_id_ = 0 ;
499+ }
500+ if (pimpl_->unmap_handler_id_ > 0 ) {
501+ g_signal_handler_disconnect (G_OBJECT (pimpl_->gtk_menu_ ), pimpl_->unmap_handler_id_ );
502+ pimpl_->unmap_handler_id_ = 0 ;
503+ }
504+
455505 g_object_unref (pimpl_->gtk_menu_ );
456506 }
457507}
0 commit comments