linux-menubar: fixes rubymine/191.624
authorArtem Bochkarev <artem.bochkarev@jetbrains.com>
Fri, 19 Oct 2018 08:06:39 +0000 (15:06 +0700)
committerArtem Bochkarev <artem.bochkarev@jetbrains.com>
Fri, 19 Oct 2018 10:02:13 +0000 (17:02 +0700)
fixed IDEA-200379 linux Native menu bar on Ubuntu Linux: the View menu actions have incorrect state
fixed IDEA-151200 Unity menu bar is not available for detached editor tabs and floating tool windows
fixed IDEA-200724 IntelliJ IDEA does not fully start after update, ends with window with menubar and nothing else
fixed IDEA-200374 Throwable at com.intellij.openapi.wm.impl.GlobalMenuLinux.lambda$handleEvent$4
fixed IDEA-200768 Start Splash window doesn't hide
fixed IDEA-200822 Opening any Markdown file freezes recent IntelliJ Ultimate EAP 183.3795.13

fixed GLIB-WARN (child/parent mismatches)
add traces
return checking of registry key linux.native.menu
fix loading of class GlobalMenuLib

bin/linux/libdbm64.so
native/LinuxGlobalMenu/DbusMenuWrapper.c
native/LinuxGlobalMenu/DbusMenuWrapper.h
platform/platform-impl/src/com/intellij/openapi/wm/impl/GlobalMenuLinux.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeMenuBar.java

index e3368f40339c99a0617bbeca6ebd1d7f1315a690..f28323c7bd36a2bde85eb30383c9e33f520527bb 100755 (executable)
Binary files a/bin/linux/libdbm64.so and b/bin/linux/libdbm64.so differ
index ce62b421e9a87a6b7aa8c0c9279f1e8bdf3f5f06..b131494e291347a038017ac49706234c4981c316 100644 (file)
@@ -317,21 +317,19 @@ void bindNewWindow(WndInfo * wi, long windowXid) {
     return;
   }
 
-  if (wi->linkedXids == NULL) {
-    wi->linkedXids = g_list_alloc();
-    wi->linkedXids->data = windowXid;
-  } else
-    g_list_append(wi->linkedXids, windowXid);
+  // _logmsg(LOG_LEVEL_INFO, "bind new window 0x%lx", windowXid);
+  wi->linkedXids = g_list_append(wi->linkedXids, windowXid);
 }
 
 void unbindWindow(WndInfo * wi, long windowXid) {
   if (wi == NULL || wi->server == NULL || wi->menuPath == NULL)
     return;
 
+  // _logmsg(LOG_LEVEL_INFO, "unbind window 0x%lx", windowXid);
   _unregisterWindow(windowXid, wi->registrar);
 
   if (wi->linkedXids != NULL)
-    g_list_remove(wi->linkedXids, windowXid);
+    wi->linkedXids = g_list_remove(wi->linkedXids, windowXid);
 }
 
 static gboolean _execReleaseWindow(gpointer user_data) {
@@ -536,6 +534,16 @@ void setItemShortcut(DbusmenuMenuitem *item, int jmodifiers, int jkeycode) {
   dbusmenu_menuitem_property_set_variant(item, DBUSMENU_MENUITEM_PROP_SHORTCUT, outsideArr);
 }
 
+void toggleItemStateChecked(DbusmenuMenuitem *item, bool isChecked) {
+  const int nOn = DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED;
+  const int nOff = DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED;
+  const int ncheck = isChecked ? nOn : nOff;
+  if (dbusmenu_menuitem_property_get_int(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE) != ncheck) {
+    // _logmsg(LOG_LEVEL_INFO, "item %s changes checked-state: %d -> %d", _getItemLabel(item), dbusmenu_menuitem_property_get_int(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE), ncheck);
+    dbusmenu_menuitem_property_set_int(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, ncheck);
+  }
+}
+
 static gboolean _execJRunnable(gpointer user_data) {
   (*((jrunnable) user_data))();
   return FALSE;
index 1673ce94a12173d593cfc2ff6201bda4706abdf1..1aa7b91a91d13581b9c889aef6a80fe64f2ac238 100644 (file)
@@ -62,6 +62,8 @@ void setItemEnabled(DbusmenuMenuitem* item, bool isEnabled);
 void setItemIcon(DbusmenuMenuitem* item, const char * iconBytesPng, int iconBytesCount);
 void setItemShortcut(DbusmenuMenuitem *item, int jmodifiers, int jkeycode);
 
+void toggleItemStateChecked(DbusmenuMenuitem *item, bool isChecked);
+
 #ifdef __cplusplus
 }
 #endif
index a55def5883262c287770b1a4f8213f2072b99674..568ee58337b43c9d34c53b4bf34bfebe6937bc06 100644 (file)
@@ -2,6 +2,7 @@
 package com.intellij.openapi.wm.impl;
 
 import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.ActionManager;
 import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.impl.ActionMenu;
 import com.intellij.openapi.actionSystem.impl.ActionMenuItem;
@@ -29,14 +30,10 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.*;
 import java.text.SimpleDateFormat;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.Date;
 
 interface GlobalMenuLib extends Library {
   void runDbusServer(JLogger jlogger, JRunnable onAppmenuServiceAppeared, JRunnable onAppmenuServiceVanished);
@@ -46,8 +43,8 @@ interface GlobalMenuLib extends Library {
   Pointer registerWindow(long windowXid, EventHandler handler);
   void releaseWindowOnMainLoop(Pointer wi);
 
-  void bindNewWindow(Pointer wi, long windowXid);
-  void unbindWindow(Pointer wi, long windowXid);
+  void bindNewWindow(Pointer wi, long windowXid); // can be called from EDT (invokes only g_dbus_proxy_call, stateless)
+  void unbindWindow(Pointer wi, long windowXid);  // can be called from EDT (invokes only g_dbus_proxy_call, stateless)
 
   void clearRootMenu(Pointer wi);
   void clearMenu(Pointer dbmi);
@@ -63,6 +60,8 @@ interface GlobalMenuLib extends Library {
   void setItemIcon(Pointer item, byte[] iconBytesPng, int iconBytesCount);
   void setItemShortcut(Pointer item, int jmodifiers, int jkeycode);
 
+  void toggleItemStateChecked(Pointer item, boolean isChecked);
+
   interface EventHandler extends Callback {
     void handleEvent(int uid, int eventType);
   }
@@ -98,16 +97,22 @@ interface GlobalMenuLib extends Library {
 
 public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
   private static final SimpleDateFormat ourDtf = new SimpleDateFormat("hhmmss.SSS"); // for debug only
-  private static final boolean TRACE_SYSOUT       = Registry.is("linux.native.menu.debug.trace.sysout", false);
-  private static final boolean TRACE_DISABLED     = Registry.is("linux.native.menu.debug.trace.disabled", true);
-  private static final boolean TRACE_SYNC_STATS   = Registry.is("linux.native.menu.debug.trace.sync-stats", false);
-  private static final boolean TRACE_EVENTS       = Registry.is("linux.native.menu.debug.trace.events", false);
-  private static final boolean TRACE_EVENT_FILTER = Registry.is("linux.native.menu.debug.trace.event-filter", false);
+  private static final boolean TRACE_SYSOUT               = Registry.is("linux.native.menu.debug.trace.sysout", false);
+  private static final boolean TRACE_DISABLED             = Registry.is("linux.native.menu.debug.trace.disabled", true);
+  private static final boolean TRACE_SYNC_STATS           = Registry.is("linux.native.menu.debug.trace.sync-stats", false);
+  private static final boolean TRACE_EVENTS               = Registry.is("linux.native.menu.debug.trace.events", false);
+  private static final boolean TRACE_EVENT_FILTER         = Registry.is("linux.native.menu.debug.trace.event-filter", false);
+  private static final boolean TRACE_CLEARING             = Registry.is("linux.native.menu.debug.trace.clearing", false);
+  private static final boolean TRACE_HIERARCHY_MISMATCHES = Registry.is("linux.native.menu.debug.trace.hierarchy.mismatches", false);
+  private static final boolean SHOW_SWING_MENU            = Registry.is("linux.native.menu.debug.show.frame.menu", false);
 
   private static final Logger LOG = Logger.getInstance(GlobalMenuLinux.class);
   private static final GlobalMenuLib ourLib;
   private static final GlobalMenuLib.JLogger ourGLogger;
   private static final Thread ourGlibMainLoopThread;
+  private static final GlobalMenuLib.JRunnable ourProcessQueue;
+  private static final GlobalMenuLib.JRunnable ourOnAppmenuServiceAppeared;
+  private static final GlobalMenuLib.JRunnable ourOnAppmenuServiceVanished;
   private static final Map<Long, GlobalMenuLinux> ourInstances = new ConcurrentHashMap<>();
   private static boolean ourIsServiceAvailable = false;
 
@@ -124,18 +129,47 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
     if (ourLib != null) {
       ourGLogger = (level, msg) -> {
         if (level == GlobalMenuLib.LOG_LEVEL_INFO) {
-          // System.out.println("INFO: " + msg);
-          LOG.info(msg);
+          if (TRACE_SYSOUT)
+            _trace(msg);
+          else
+            LOG.info(msg);
         } else {
           // System.out.println("ERROR: " + msg);
           LOG.error(msg);
         }
       };
-      ourGlibMainLoopThread = new Thread(()->ourLib.runDbusServer(ourGLogger, GlobalMenuLinux::_onAppmenuServiceAppeared, GlobalMenuLinux::_onAppmenuServiceVanished), "Glib-main-loop");
+      ourProcessQueue = () -> {
+        // exec at glib-thread
+        if (!ourIsServiceAvailable)
+          return;
+
+        for (GlobalMenuLinux gml: ourInstances.values())
+          gml._processRoots();
+      };
+      ourOnAppmenuServiceAppeared = () -> {
+        // exec at glib-thread
+        _trace("Appeared dbus-service 'com.canonical.AppMenu.Registrar'");
+        ourIsServiceAvailable = true;
+        ourProcessQueue.run();
+      };
+      ourOnAppmenuServiceVanished = () -> {
+        // exec at glib-thread
+        _trace("Closed dbus-service 'com.canonical.AppMenu.Registrar'");
+        ourIsServiceAvailable = false;
+        for (GlobalMenuLinux gml: ourInstances.values()) {
+          gml.myWindowHandle = null;
+          ApplicationManager.getApplication().invokeLater(()->gml.myFrame.getJMenuBar().setVisible(true));
+        }
+      };
+
+      ourGlibMainLoopThread = new Thread(()->ourLib.runDbusServer(ourGLogger, ourOnAppmenuServiceAppeared, ourOnAppmenuServiceVanished), "Glib-main-loop");
       ourGlibMainLoopThread.start();
     } else {
       ourGLogger = null;
       ourGlibMainLoopThread = null;
+      ourProcessQueue = null;
+      ourOnAppmenuServiceAppeared = null;
+      ourOnAppmenuServiceVanished = null;
     }
   }
 
@@ -163,17 +197,34 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
   }
 
   public void bindNewWindow(@NotNull Window frame) {
+    // exec at EDT
     final long xid = _getX11WindowXid(frame);
-    if (xid == 0)
+    if (xid == 0) {
+      LOG.warn("can't obtain XID of window: " + frame + ", skip global menu binding");
       return;
-    ourLib.bindNewWindow(myWindowHandle, xid);
+    }
+    if (myWindowHandle != null) {
+      _trace("bind new window 0x%X", xid);
+      ourLib.bindNewWindow(myWindowHandle, xid);
+      if (frame instanceof JFrame) {
+        final JFrame jfr = (JFrame)frame;
+        if (jfr.getJMenuBar() != null)
+          jfr.getJMenuBar().setVisible(false);
+      }
+    }
   }
 
-  public void unbindNewWindow(@NotNull Window frame) {
+  public void unbindWindow(@NotNull Window frame) {
+    // exec at EDT
     final long xid = _getX11WindowXid(frame);
-    if (xid == 0)
+    if (xid == 0) {
+      LOG.warn("can't obtain XID of window: " + frame + ", skip global menu unbinding");
       return;
-    ourLib.unbindWindow(myWindowHandle, xid);
+    }
+    if (myWindowHandle != null) {
+      _trace("unbind window 0x%X", xid);
+      ourLib.unbindWindow(myWindowHandle, xid);
+    }
   }
 
   public void setRoots(List<ActionMenu> roots) {
@@ -187,8 +238,9 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
     if (roots != null) {
       for (ActionMenu am: roots) {
         final int uid = System.identityHashCode(am);
-        final MenuItemInternal mi = new MenuItemInternal(newRoots.size(), uid, GlobalMenuLib.ITEM_SUBMENU, _buildMnemonicLabel(am), am.getText(), null, true, am.getAnAction());
+        final MenuItemInternal mi = new MenuItemInternal(newRoots.size(), uid, GlobalMenuLib.ITEM_SUBMENU, am.getAnAction());
         mi.jitem = am;
+        mi.setLabelFromSwingPeer(am);
         newRoots.add(mi);
       }
     }
@@ -223,7 +275,7 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
     for (MenuItemInternal mi: croots)
       mi.nativePeer = ourLib.addRootMenu(myWindowHandle, mi.uid, mi.txt);
 
-    if (!Registry.is("linux.native.menu.debug.show.frame.menu", false))
+    if (!SHOW_SWING_MENU)
       ApplicationManager.getApplication().invokeLater(()->myFrame.getJMenuBar().setVisible(false));
   }
 
@@ -291,14 +343,14 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
     MenuItemInternal result = null;
     if (each instanceof ActionMenuItem) {
       final ActionMenuItem ami = (ActionMenuItem)each;
-      result = new MenuItemInternal(-1, System.identityHashCode(ami), ami.isToggleable() ? GlobalMenuLib.ITEM_CHECK : GlobalMenuLib.ITEM_SIMPLE, ami.getText(), ami.getText(), _icon2png(ami.getIcon()), ami.isEnabled(), ami.getAnAction());
+      result = new MenuItemInternal(-1, System.identityHashCode(ami), ami.isToggleable() ? GlobalMenuLib.ITEM_CHECK : GlobalMenuLib.ITEM_SIMPLE, ami.getAnAction());
       result.jitem = ami;
     } else if (each instanceof ActionMenu) {
       final ActionMenu am2 = (ActionMenu)each;
-      result = new MenuItemInternal(-1, System.identityHashCode(am2), GlobalMenuLib.ITEM_SUBMENU, am2.getText(), am2.getText(), null, am2.isEnabled(), am2.getAnAction());
+      result = new MenuItemInternal(-1, System.identityHashCode(am2), GlobalMenuLib.ITEM_SUBMENU, am2.getAnAction());
       result.jitem = am2;
     } else if (each instanceof JSeparator) {
-      result = new MenuItemInternal(-1, System.identityHashCode(each), GlobalMenuLib.ITEM_SIMPLE, null, null, null, true, null);
+      result = new MenuItemInternal(-1, System.identityHashCode(each), GlobalMenuLib.ITEM_SIMPLE, null);
     } else if (each instanceof StubItem) {
       // System.out.println("skip StubItem");
     } else {
@@ -334,6 +386,8 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
         if (cmi != null) {
           mi.children.add(cmi);
           if (stats != null) ++stats[STAT_CREATED];
+          if (each instanceof JMenuItem)
+            cmi.updateBySwingPeer((JMenuItem)each);
         }
       } else {
         cmi.toDelete = false;
@@ -359,11 +413,9 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
       if (child.nativePeer != null) {
         if (child.toDelete) {
           ourLib.removeMenuItem(mi.nativePeer, child.nativePeer);
+          child.nativePeer = null;
         } else {
-          // update states and icons
-          // NOTE: probably it's better to use sync flags, to avoid frequent calls, to avoid applet destabilization)
-          ourLib.setItemEnabled(child.nativePeer, child.isEnabled);
-          ourLib.setItemIcon(child.nativePeer, child.iconPngBytes, child.iconPngBytes != null ? child.iconPngBytes.length : 0);
+          child.updateNative();
         }
       } else {
         if (child.action == null) {
@@ -372,11 +424,7 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
         }
 
         child.nativePeer = ourLib.addMenuItem(mi.nativePeer, child.uid, child.txt, child.type);
-
-        if (!child.isEnabled)
-          ourLib.setItemEnabled(child.nativePeer, false);
-        if (child.iconPngBytes != null && child.iconPngBytes.length > 0)
-          ourLib.setItemIcon(child.nativePeer, child.iconPngBytes, child.iconPngBytes.length);
+        child.updateNative();
       }
 
       _processChildren(child);
@@ -416,7 +464,7 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
           // ETD-start
           final JMenuItem jmi = mi.jitem;
           if (jmi == null) {
-            LOG.error("can't find corresponding (opening) ActionMenu, event source: " + mi + ", swing menu hierarchy:\n" + _dumpSwingHierarchy());
+            if (TRACE_HIERARCHY_MISMATCHES) _trace("corresponding (opening) swing item is null, event source: " + mi + ", swing menu hierarchy:\n" + _dumpSwingHierarchy());
             return;
           }
           if (!(jmi instanceof ActionMenu)) {
@@ -437,26 +485,7 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
         _processChildren(mi);
       } else if (eventType == GlobalMenuLib.EVENT_CLOSED) {
         // glib main-loop thread
-        final Timer timer = new Timer(100, (e) -> {
-          // ETD-start
-          final JMenuItem jmi = mi.jitem;
-          if (jmi == null) {
-            LOG.error("can't find corresponding (closing) ActionMenu, event source: " + mi + ", swing menu hierarchy:\n" + _dumpSwingHierarchy());
-            return;
-          }
-          if (!(jmi instanceof ActionMenu)) {
-            LOG.error("corresponding (closing) swing item isn't instance of ActionMenu, class=" + jmi.getClass().getName() + ", event source: " + mi);
-            return;
-          }
-
-          final ActionMenu am = (ActionMenu)jmi;
-          am.clearItems();
-          mi.clearChildrenSwingRefs();
-        });
-        timer.setRepeats(false);
-        timer.start();
-
-        _trace("\t scheduled (100 ms later) to clear menu '%s'", mi.txt);
+        mi.scheduleClearSwing();
       }
 
       return;
@@ -483,22 +512,22 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
   public static boolean isAvailable() { return ourLib != null; }
 
   private static GlobalMenuLib _loadLibrary() {
-    if (!SystemInfo.isLinux)
+    if (!SystemInfo.isLinux || !Registry.is("linux.native.menu"))
       return null;
 
-    UrlClassLoader.loadPlatformLibrary("dbm");
+    try {
+      UrlClassLoader.loadPlatformLibrary("dbm");
 
-    // Set JNA to convert java.lang.String to char* using UTF-8, and match that with
-    // the way we tell CF to interpret our char*
-    // May be removed if we use toStringViaUTF16
-    System.setProperty("jna.encoding", "UTF8");
+      // Set JNA to convert java.lang.String to char* using UTF-8, and match that with
+      // the way we tell CF to interpret our char*
+      // May be removed if we use toStringViaUTF16
+      System.setProperty("jna.encoding", "UTF8");
 
-    final Map<String, Object> options = new HashMap<>();
-    try {
+      final Map<String, Object> options = new HashMap<>();
       return Native.loadLibrary("dbm", GlobalMenuLib.class, options);
     } catch (UnsatisfiedLinkError ule) {
-      LOG.error(ule);
-    } catch (RuntimeException e) {
+      LOG.info("disable global-menu integration because some of shared libraries isn't installed: " + ule);
+    } catch (Throwable e) {
       LOG.error(e);
     }
     return null;
@@ -514,28 +543,27 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
 
     String txt;
     String originTxt;
-    boolean isEnabled;
+    boolean isEnabled = true;
+    boolean isChecked = false;
     byte[] iconPngBytes;
 
     JMenuItem jitem;
     Pointer nativePeer;
     boolean toDelete = false;
 
-    long lastOpenedMs = 0;
     long lastClosedMs = 0;
 
-    MenuItemInternal(int rootPos, int uid, int type, String txt, String originTxt, byte[] iconPngBytes, boolean isEnabled, AnAction action) {
+    Timer timerClearSwing;
+
+    MenuItemInternal(int rootPos, int uid, int type, AnAction action) {
       this.rootPos = rootPos;
       this.uid = uid;
       this.type = type;
-      this.txt = txt;
-      this.originTxt = originTxt;
-      this.iconPngBytes = iconPngBytes;
-      this.isEnabled = isEnabled;
       this.action = action;
     }
 
     boolean isRoot() { return rootPos >= 0; }
+    boolean isToggleable() { return type == GlobalMenuLib.ITEM_CHECK || type == GlobalMenuLib.ITEM_RADIO; }
 
     void clearChildrenSwingRefs() {
       for (MenuItemInternal cmi: children) {
@@ -545,16 +573,44 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
     }
 
     // returns true when changed
-    boolean updateBySwingPeer(JMenuItem peer) {
+    boolean updateBySwingPeer(@NotNull JMenuItem peer) {
       // exec at EDT
       jitem = peer;
-      // probably need to calc sync flags like: if (cmi.isEnabled != each.isEnabled()) cmi.needUpdate = true;
+      // NOTE: probably is's better to use sync flags like: if (cmi.isEnabled != each.isEnabled()) cmi.needUpdate = true;
       boolean res = isEnabled != peer.isEnabled();
       isEnabled = peer.isEnabled();
-      // TODO: update icons (text must be the same)
+      if (isToggleable()) {
+        if (isChecked != peer.isSelected()) res = true;
+        isChecked = peer.isSelected();
+      }
+      if (!Objects.equals(originTxt, peer.getText())) {
+        // _trace("label changes: '%s' -> '%s'", originTxt, peer.getText());
+        setLabelFromSwingPeer(peer);
+        res = true;
+      }
+      iconPngBytes = isToggleable() ? null : _icon2png(peer.getIcon());
       return res;
     }
 
+    void setLabelFromSwingPeer(@NotNull JMenuItem peer) {
+      // exec at EDT
+      originTxt = peer.getText();
+      txt = _buildMnemonicLabel(peer);
+    }
+
+    void updateNative() {
+      // exec at glib-loop thread
+      // NOTE: probably it's better to use sync flags, to avoid frequent calls, to avoid applet destabilization)
+      if (nativePeer == null)
+        return;
+
+      ourLib.setItemLabel(nativePeer, txt);
+      ourLib.setItemEnabled(nativePeer, isEnabled);
+      ourLib.setItemIcon(nativePeer, iconPngBytes, iconPngBytes != null ? iconPngBytes.length : 0);
+      if (isToggleable())
+        ourLib.toggleItemStateChecked(nativePeer, isChecked);
+    }
+
     MenuItemInternal findCorrespondingChild(@NotNull Component target) {
       if (target == null)
         return null;
@@ -638,6 +694,55 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
         res = res + " [toDelele]";
       return res;
     }
+
+    String toStringShort() {
+      String res = String.format("'%s'", txt);
+      if (toDelete)
+        res = res + " [D]";
+      if (isRoot())
+        res = "Root " + res;
+      else
+        res = "Submenu " + res;
+      return res;
+    }
+
+    synchronized void scheduleClearSwing() {
+      // exec at glib main-loop thread
+      if (timerClearSwing != null)
+        timerClearSwing.stop();
+
+      timerClearSwing = new Timer(300, (e) -> {
+        _clearSwing();
+      });
+      timerClearSwing.setRepeats(false);
+      timerClearSwing.start();
+      if (TRACE_CLEARING) _trace("\t scheduled (300 ms later) to clear '%s'", toStringShort());
+    }
+
+    synchronized void cancelClearSwing() {
+      // exec at glib main-loop thread
+      timerClearSwing = null;
+    }
+
+    private void _clearSwing() {
+      // exec at ETD
+      if (timerClearSwing == null)
+        return;
+
+      if (jitem == null) {
+        if (TRACE_CLEARING) _trace("corresponding (closing) swing item is null - nothing to clear, event source: ", this);
+        return;
+      }
+      if (!(jitem instanceof ActionMenu)) {
+        LOG.error("corresponding (closing) swing item isn't instance of ActionMenu, class=" + jitem.getClass().getName() + ", event source: " + toString());
+        return;
+      }
+
+      final ActionMenu am = (ActionMenu)jitem;
+      am.clearItems();
+      clearChildrenSwingRefs();
+      if (TRACE_CLEARING) _trace("\t cleared '%s'", toStringShort());
+    }
   }
 
   private class EventFilter {
@@ -666,6 +771,8 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
             if (TRACE_EVENT_FILTER) _trace("EventFilter: start timer to process 'about-to-show' of first-root later");
             return false;
           }
+
+          myTimer = null;
         } else if (mi.rootPos > 0) {
           if ((timeMs - myLastFirstRootEventMs) < 50) {
             if (TRACE_EVENT_FILTER) _trace("EventFilter: skip fake 'about-to-show' of root[%d]%s", mi.rootPos, myTimer != null ? " (reset timer)" : "");
@@ -685,8 +792,6 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
         }
       }
 
-      if (mi.rootPos == 0)
-        myTimer = null;
       return true;
     }
   }
@@ -821,28 +926,4 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
     else
       LOG.info(msg);
   }
-
-  private static final GlobalMenuLib.JRunnable ourProcessQueue = () -> {
-    // exec at glib-thread
-    if (!ourIsServiceAvailable)
-      return;
-
-    for (GlobalMenuLinux gml: ourInstances.values())
-      gml._processRoots();
-  };
-
-  private static void _onAppmenuServiceAppeared() {
-    // exec at glib-thread
-    ourIsServiceAvailable = true;
-    ourProcessQueue.run();
-  }
-
-  private static void _onAppmenuServiceVanished() {
-    // exec at glib-thread
-    ourIsServiceAvailable = false;
-    for (GlobalMenuLinux gml: ourInstances.values()) {
-      gml.myWindowHandle = null;
-      ApplicationManager.getApplication().invokeLater(()->gml.myFrame.getJMenuBar().setVisible(true));
-    }
-  }
 }
\ No newline at end of file
index 955a29054ee9efb53db885a3908d4928cada5c4b..1735b1840d47f856b5459669150d07a1b969852d 100644 (file)
@@ -16,7 +16,6 @@ import com.intellij.openapi.actionSystem.impl.WeakTimerListener;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.ui.FrameWrapper;
 import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.registry.Registry;
@@ -575,8 +574,9 @@ public class IdeMenuBar extends JMenuBar implements IdeEventQueue.EventDispatche
         frame.addWindowListener(new WindowAdapter() {
           @Override
           public void windowClosing(WindowEvent e) {
-            frameMenuBar.myGlobalMenuLinux.unbindNewWindow(frame);
+            frameMenuBar.myGlobalMenuLinux.unbindWindow(frame);
           }
+          @Override
           public void windowOpened(WindowEvent e) {
             frameMenuBar.myGlobalMenuLinux.bindNewWindow(frame);
           }