Browse Source

Fix concurrency problems in OSD plugin

Change-Id: Iae88af8028e55d45540c4272324dd792233a5d91
Reviewed-on: http://gerrit.dmdirc.com/651
Reviewed-by: Shane Mc Cormack <shane@dmdirc.com>
Automatic-Compile: Gregory Holmes <greg@dmdirc.com>
Reviewed-by: Gregory Holmes <greg@dmdirc.com>
tags/0.6.3
Chris Smith 14 years ago
parent
commit
7c10d0b14a
2 changed files with 102 additions and 28 deletions
  1. 37
    25
      src/com/dmdirc/addons/osd/OsdManager.java
  2. 65
    3
      src/com/dmdirc/addons/osd/OsdWindow.java

+ 37
- 25
src/com/dmdirc/addons/osd/OsdManager.java View File

@@ -83,10 +83,15 @@ public class OsdManager {
83 83
 
84 84
     /**
85 85
      * Create a new OSD window with "message".
86
+     * <p>
87
+     * This method needs to be synchronised to ensure that the window list is
88
+     * not modified in between the invocation of {@link #getYPosition()} and
89
+     * the point at which the {@link OSDWindow} is added to the windowList.
86 90
      *
91
+     * @see #getYPosition()
87 92
      * @param message Text to display in the OSD window.
88 93
      */
89
-    private void displayWindow(final String message) {
94
+    private synchronized void displayWindow(final String message) {
90 95
         windowList.add(UIUtilities.invokeAndWait(
91 96
                 new ReturnableThread<OsdWindow>() {
92 97
 
@@ -97,7 +102,8 @@ public class OsdManager {
97 102
                         IdentityManager.getGlobalConfig().getOptionInt(
98 103
                         plugin.getDomain(), "locationX"), getYPosition(),
99 104
                         plugin, OsdManager.this));
100
-            }}));
105
+            }
106
+        }));
101 107
     }
102 108
 
103 109
     /**
@@ -106,38 +112,37 @@ public class OsdManager {
106 112
      *
107 113
      * @param window The window that we are destroying.
108 114
      */
109
-    public void closeWindow(final OsdWindow window) {
115
+    public synchronized void closeWindow(final OsdWindow window) {
110 116
         final String policy = IdentityManager.getGlobalConfig().getOption(
111 117
                 plugin.getDomain(), "newbehaviour");
112 118
 
113
-        synchronized (OsdManager.this) {
114
-            UIUtilities.invokeAndWait(new Runnable() {
115
-                /** {@inheritDoc} */
116
-                @Override
117
-                public void run() {
118
-                    int oldY = window.getY();
119
-                    final int closedIndex = windowList.indexOf(window);
119
+        int oldY = window.getDesiredY();
120
+        final int closedIndex = windowList.indexOf(window);
120 121
 
121
-                    if (closedIndex == -1) {
122
-                        return;
123
-                    }
122
+        if (closedIndex == -1) {
123
+            return;
124
+        }
124 125
 
125
-                    windowList.remove(window);
126
-                    window.dispose();
126
+        windowList.remove(window);
127 127
 
128
-                    final List<OsdWindow> newList = getWindowList();
128
+        UIUtilities.invokeLater(new Runnable() {
129
+            /** {@inheritDoc} */
130
+            @Override
131
+            public void run() {
132
+                window.dispose();
133
+            }
134
+        });
129 135
 
130
-                    for (OsdWindow otherWindow : newList.subList(closedIndex, newList.size())) {
131
-                        final int currentY = otherWindow.getY();
136
+        final List<OsdWindow> newList = getWindowList();
137
+        for (OsdWindow otherWindow : newList.subList(closedIndex, newList.size())) {
138
+            final int currentY = otherWindow.getDesiredY();
132 139
 
133
-                        if ("down".equals(policy) || "up".equals(policy)) {
134
-                                otherWindow.setLocation(otherWindow.getX(), oldY);
135
-                                oldY = currentY;
136
-                        }
137
-                    }
138
-                }
139
-            });
140
+            if ("down".equals(policy) || "up".equals(policy)) {
141
+                otherWindow.setDesiredLocation(otherWindow.getDesiredX(), oldY);
142
+                oldY = currentY;
143
+            }
140 144
         }
145
+
141 146
         displayWindows();
142 147
     }
143 148
 
@@ -170,6 +175,13 @@ public class OsdManager {
170 175
 
171 176
     /**
172 177
      * Get the Y position for the next window.
178
+     * <p>
179
+     * In order to ensure that windows are displayed at the correct position,
180
+     * the calling party MUST ensure that the window list is not altered between
181
+     * this method's invocation and the time at which the window is displayed.
182
+     * If the window list is altered, multiple windows may appear on top of
183
+     * each other instead of stacking correctly, or there may be gaps in up/down
184
+     * policy layouts.
173 185
      *
174 186
      * @return the Y position for the next window.
175 187
      */

+ 65
- 3
src/com/dmdirc/addons/osd/OsdWindow.java View File

@@ -24,6 +24,7 @@ package com.dmdirc.addons.osd;
24 24
 
25 25
 import com.dmdirc.Main;
26 26
 import com.dmdirc.addons.ui_swing.MainFrame;
27
+import com.dmdirc.addons.ui_swing.UIUtilities;
27 28
 import com.dmdirc.config.IdentityManager;
28 29
 import com.dmdirc.ui.messages.ColourManager;
29 30
 
@@ -70,6 +71,9 @@ public final class OsdWindow extends JDialog implements MouseListener,
70 71
     
71 72
     /** Starting positions of the mouse. */
72 73
     private int startX, startY;
74
+
75
+    /** Desired position. */
76
+    private volatile int desiredX, desiredY;
73 77
     
74 78
     /** Is this a config instance? */
75 79
     private final boolean config;
@@ -99,6 +103,9 @@ public final class OsdWindow extends JDialog implements MouseListener,
99 103
 
100 104
         setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
101 105
 
106
+        desiredX = x;
107
+        desiredY = y;
108
+
102 109
         setLocation(x, y);
103 110
 
104 111
         panel = new JPanel();
@@ -124,8 +131,8 @@ public final class OsdWindow extends JDialog implements MouseListener,
124 131
         pack();
125 132
 
126 133
         if (config) {
127
-            this.addMouseMotionListener(this);
128
-            this.addMouseListener(this);
134
+            addMouseMotionListener(this);
135
+            addMouseListener(this);
129 136
         } else {
130 137
             addMouseListener(this);
131 138
             new Timer("OSD Display Timer").schedule(new TimerTask() {
@@ -255,8 +262,63 @@ public final class OsdWindow extends JDialog implements MouseListener,
255 262
         }
256 263
     }
257 264
 
265
+    /**
266
+     * Retrieves the desired x offset of this OSD window.
267
+     *
268
+     * @since 0.6.3
269
+     * @see #setDesiredLocation(int, int)
270
+     * @return The desired offset of this window
271
+     */
272
+    public int getDesiredX() {
273
+        return desiredX;
274
+    }
275
+
276
+    /**
277
+     * Retrieves the desired y offset of this OSD window.
278
+     *
279
+     * @since 0.6.3
280
+     * @see #setDesiredLocation(int, int)
281
+     * @return The desired offset of this window
282
+     */
283
+    public int getDesiredY() {
284
+        return desiredY;
285
+    }
286
+
287
+    /**
288
+     * Sets the desired location of this OSD window, and queues an event to
289
+     * move the window to the desired location at some point in the future.
290
+     * <p>
291
+     * This method WILL NOT alter the location immediately, but will schedule
292
+     * an event in the AWT event despatch thread which will be executed in
293
+     * the future.
294
+     * <p>
295
+     * This method will immediately update the values returned by the
296
+     * {@link #getDesiredX()} and {@link #getDesiredY()} methods, but the
297
+     * {@link #getX()} and {@link #getY()} methods will continue to reflect the
298
+     * actual location of the window.
299
+     * <p>
300
+     * This method is thread safe.
301
+     *
302
+     * @param x The desired x offset of this window
303
+     * @param y The desired y offset of this window
304
+     * @since 0.6.3
305
+     */
306
+    public void setDesiredLocation(final int x, final int y) {
307
+        this.desiredX = x;
308
+        this.desiredY = y;
309
+
310
+        UIUtilities.invokeLater(new Runnable() {
311
+            /** {@inheritDoc} */
312
+            @Override
313
+            public void run() {
314
+                setLocation(getDesiredX(), getDesiredY());
315
+            }
316
+        });
317
+    }
318
+
258 319
     /** {@inheritDoc} */
259
-    @Override  public String toString() {
320
+    @Override
321
+    public String toString() {
260 322
         return label.getText();
261 323
     }
262 324
 }

Loading…
Cancel
Save