diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java
index e79b01b35..a4a6eac4e 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java
@@ -175,6 +175,13 @@ public class CallPanel
*/
private DialpadDialog dialpadDialog;
+ /**
+ * The indicator which determines whether {@link #dispose()} has already
+ * been invoked on this instance. If true, this instance is
+ * considered non-functional and is to be left to the garbage collector.
+ */
+ private boolean disposed = false;
+
/**
* The handler for DTMF tones.
*/
@@ -295,7 +302,17 @@ public void update(Observable o, Object arg)
{
public void run()
{
- updateViewFromModelInEventDispatchThread();
+ /*
+ * We receive events/notifications from various threads and we
+ * respond to them in the AWT event dispatching thread. It is
+ * possible to first schedule an event to be brought to the AWT
+ * event dispatching thread, then to have #dispose() invoked on
+ * this instance and, finally, to receive the scheduled event in
+ * the AWT event dispatching thread. In such a case, this
+ * disposed instance should not respond to the event.
+ */
+ if (!disposed)
+ updateViewFromModelInEventDispatchThread();
}
};
@@ -585,6 +602,8 @@ private JComponent createBottomBar()
*/
void dispose()
{
+ disposed = true;
+
callConference.removeCallChangeListener(callConferenceListener);
callConference.removeCallPeerConferenceListener(callConferenceListener);
callConference.removePropertyChangeListener(callConferenceListener);
@@ -1940,12 +1959,23 @@ private void updateSettingsPanelInEventDispatchThread(
*/
private void updateViewFromModel()
{
- if (SwingUtilities.isEventDispatchThread())
- updateViewFromModelInEventDispatchThread();
- else
+ /*
+ * We receive events/notifications from various threads and we respond
+ * to them in the AWT event dispatching thread. It is possible to first
+ * schedule an event to be brought to the AWT event dispatching thread,
+ * then to have #dispose() invoked on this instance and, finally, to
+ * receive the scheduled event in the AWT event dispatching thread. In
+ * such a case, this disposed instance should not respond to the event.
+ */
+ if (!disposed)
{
- SwingUtilities.invokeLater(
- updateViewFromModelInEventDispatchThread);
+ if (SwingUtilities.isEventDispatchThread())
+ updateViewFromModelInEventDispatchThread();
+ else
+ {
+ SwingUtilities.invokeLater(
+ updateViewFromModelInEventDispatchThread);
+ }
}
}
@@ -1956,6 +1986,17 @@ private void updateViewFromModel()
*/
private void updateViewFromModelInEventDispatchThread()
{
+ /*
+ * We receive events/notifications from various threads and we respond
+ * to them in the AWT event dispatching thread. It is possible to first
+ * schedule an event to be brought to the AWT event dispatching thread,
+ * then to have #dispose() invoked on this instance and, finally, to
+ * receive the scheduled event in the AWT event dispatching thread. In
+ * such a case, this disposed instance should not respond to the event.
+ */
+ if (disposed)
+ return;
+
/*
* We may add, remove, show, and hide various Components of the user
* interface hierarchy of this instance bellow. Consequently, this view
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/DTMFHandler.java b/src/net/java/sip/communicator/impl/gui/main/call/DTMFHandler.java
index 687ce7e46..1632d9439 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/DTMFHandler.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/DTMFHandler.java
@@ -18,7 +18,6 @@
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
-import org.jitsi.service.audionotifier.*;
import org.jitsi.service.protocol.*;
/**
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java
index 4897908cf..caf549093 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java
@@ -44,6 +44,13 @@ public class OneToOneCallPeerPanel
implements CallPeerRenderer,
Skinnable
{
+ /**
+ * The Logger used by the OneToOneCallPeerPanel class and
+ * its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(OneToOneCallPeerPanel.class);
+
/**
* Serial version UID.
*/
@@ -82,6 +89,13 @@ public class OneToOneCallPeerPanel
*/
private Component closeLocalVisualComponentButton;
+ /**
+ * The indicator which determines whether {@link #dispose()} has already
+ * been invoked on this instance. If true, this instance is
+ * considered non-functional and is to be left to the garbage collector.
+ */
+ private boolean disposed = false;
+
/**
* The DTMF label.
*/
@@ -203,7 +217,17 @@ public void update(Observable o, Object arg)
{
public void run()
{
- updateViewFromModelInEventDispatchThread();
+ /*
+ * We receive events/notifications from various threads and we
+ * respond to them in the AWT event dispatching thread. It is
+ * possible to first schedule an event to be brought to the AWT
+ * event dispatching thread, then to have #dispose() invoked on
+ * this instance and, finally, to receive the scheduled event in
+ * the AWT event dispatching thread. In such a case, this
+ * disposed instance should not respond to the event.
+ */
+ if (!disposed)
+ updateViewFromModelInEventDispatchThread();
}
};
@@ -451,6 +475,7 @@ private VideoContainer createVideoContainer(Component noVideoComponent)
*/
public void dispose()
{
+ disposed = true;
callPeerAdapter.dispose();
uiVideoHandler.deleteObserver(uiVideoHandlerObserver);
}
@@ -1173,12 +1198,26 @@ public void componentResized(ComponentEvent e)
*/
private void updateViewFromModel()
{
- if (SwingUtilities.isEventDispatchThread())
- updateViewFromModelInEventDispatchThread();
- else
+ /*
+ * We receive events/notifications from various threads and we respond
+ * to them in the AWT event dispatching thread. It is possible to first
+ * schedule an event to be brought to the AWT event dispatching thread,
+ * then to have #dispose() invoked on this instance and, finally, to
+ * receive the scheduled event in the AWT event dispatching thread. In
+ * such a case, this disposed instance should not respond to the event
+ * because it may, for example, steal a visual Components depicting
+ * video (which cannot belong to more than one parent at a time) from
+ * another non-disposed OneToOneCallPeerPanel.
+ */
+ if (!disposed)
{
- SwingUtilities.invokeLater(
- updateViewFromModelInEventDispatchThread);
+ if (SwingUtilities.isEventDispatchThread())
+ updateViewFromModelInEventDispatchThread();
+ else
+ {
+ SwingUtilities.invokeLater(
+ updateViewFromModelInEventDispatchThread);
+ }
}
}
@@ -1189,6 +1228,20 @@ private void updateViewFromModel()
*/
private void updateViewFromModelInEventDispatchThread()
{
+ /*
+ * We receive events/notifications from various threads and we respond
+ * to them in the AWT event dispatching thread. It is possible to first
+ * schedule an event to be brought to the AWT event dispatching thread,
+ * then to have #dispose() invoked on this instance and, finally, to
+ * receive the scheduled event in the AWT event dispatching thread. In
+ * such a case, this disposed instance should not respond to the event
+ * because it may, for example, steal a visual Components depicting
+ * video (which cannot belong to more than one parent at a time) from
+ * another non-disposed OneToOneCallPeerPanel.
+ */
+ if (disposed)
+ return;
+
/*
* Update the display of visual Components depicting video
* streaming between the local peer/user and the remote peer(s).
@@ -1238,6 +1291,9 @@ private void updateViewFromModelInEventDispatchThread()
* Well, we cannot do much about the exception. We'll just
* not display the local video.
*/
+ logger.warn(
+ "Failed to retrieve local video to be displayed.",
+ ofe);
}
}
@@ -1279,13 +1335,19 @@ private void updateViewFromModelInEventDispatchThread()
else
{
center.removeAll();
+ this.localVideo = null;
+ this.remoteVideo = null;
+
if (remoteVideo != null)
+ {
center.add(remoteVideo, VideoLayout.CENTER_REMOTE, -1);
- this.remoteVideo = remoteVideo;
+ this.remoteVideo = remoteVideo;
+ }
if (localVideo != null)
{
center.add(localVideo, VideoLayout.LOCAL, -1);
+ this.localVideo = localVideo;
if (closeLocalVisualComponentButton == null)
{
@@ -1298,7 +1360,6 @@ private void updateViewFromModelInEventDispatchThread()
VideoLayout.CLOSE_LOCAL_BUTTON,
-1);
}
- this.localVideo = localVideo;
}
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/SelectScreenDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/SelectScreenDialog.java
index e08eb0ca9..5333b68fd 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/SelectScreenDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/SelectScreenDialog.java
@@ -290,6 +290,7 @@ private static JComponent createVideoContainer(Component noVideoComponent)
private static class ComboRenderer
extends DefaultListCellRenderer
{
+ @Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus)
{
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/TransferActiveCallsMenu.java b/src/net/java/sip/communicator/impl/gui/main/call/TransferActiveCallsMenu.java
index 00bc48c7c..b0e95b243 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/TransferActiveCallsMenu.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/TransferActiveCallsMenu.java
@@ -175,7 +175,7 @@ public void loadSkin()
/**
* A custom menu item corresponding to a specific CallPeer.
*/
- private class CallPeerMenuItem
+ private static class CallPeerMenuItem
extends JMenuItem
implements Skinnable
{
@@ -202,16 +202,6 @@ public CallPeerMenuItem(CallPeer peer)
loadSkin();
}
- /**
- * Get the CallPeer of this instance.
- *
- * @return CallPeer
- */
- public CallPeer getCallPeer()
- {
- return callPeer;
- }
-
/**
* Reloads icon.
*/
@@ -222,6 +212,5 @@ public void loadSkin()
if (peerIcon != null)
this.setIcon(new ImageIcon(peerIcon));
}
-
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/ZrtpSecurityPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/ZrtpSecurityPanel.java
index 8412c8d94..02ac19f41 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/ZrtpSecurityPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/ZrtpSecurityPanel.java
@@ -275,6 +275,7 @@ private JPanel createSasPanel()
TransparentPanel sasPanel = new TransparentPanel()
{
+ @Override
public void paintComponent(Graphics g)
{
g = g.create();
@@ -428,7 +429,8 @@ private void initZidNameButton()
{
public void actionPerformed(ActionEvent e)
{
- // Set ZID name only for verfied peers (SAS compared and confirmed)
+ // Set ZID name only for verified peers (SAS compared and
+ // confirmed).
if (!sasVerified)
return;
@@ -505,10 +507,7 @@ public void securityOff(CallPeerSecurityOffEvent evt)
repaint();
}
- public void securityTimeout(CallPeerSecurityTimeoutEvent evt)
- {
-
- }
+ public void securityTimeout(CallPeerSecurityTimeoutEvent ev) {}
/**
* Reloads icons and components.
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceCallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceCallPanel.java
index ce249893a..b0521f1c7 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceCallPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceCallPanel.java
@@ -55,6 +55,13 @@ public abstract class BasicConferenceCallPanel
private final Map callPeerPanels
= new HashMap();
+ /**
+ * The indicator which determines whether {@link #dispose()} has already
+ * been invoked on this instance. If true, this instance is
+ * considered non-functional and is to be left to the garbage collector.
+ */
+ private boolean disposed = false;
+
/**
* The Runnable which is scheduled by
* {@link #updateViewFromModel()} for execution in the AWT event dispatching
@@ -66,7 +73,17 @@ public abstract class BasicConferenceCallPanel
{
public void run()
{
- updateViewFromModelInEventDispatchThread();
+ /*
+ * We receive events/notifications from various threads and we
+ * respond to them in the AWT event dispatching thread. It is
+ * possible to first schedule an event to be brought to the AWT
+ * event dispatching thread, then to have #dispose() invoked on
+ * this instance and, finally, to receive the scheduled event in
+ * the AWT event dispatching thread. In such a case, this
+ * disposed instance should not respond to the event.
+ */
+ if (!disposed)
+ updateViewFromModelInEventDispatchThread();
}
};
@@ -182,6 +199,8 @@ protected void conferenceMemberRemoved(CallPeerConferenceEvent ev)
*/
public void dispose()
{
+ disposed = true;
+
callConference.removeCallChangeListener(callConferenceListener);
callConference.removeCallPeerConferenceListener(callConferenceListener);
@@ -259,6 +278,18 @@ protected void initializeComplete()
updateViewFromModel();
}
+ /**
+ * Returns true if {@link #dispose()} has already been invoked on
+ * this instance; otherwise, false.
+ *
+ * @return true if dispose() has already been invoked on
+ * this instance; otherwise, false
+ */
+ protected final boolean isDisposed()
+ {
+ return disposed;
+ }
+
/**
* Notifies this instance about a specific CallPeerConferenceEvent
* fired in the telephony conference depicted by this instance.
@@ -315,12 +346,23 @@ protected void onCallPeerEvent(CallPeerEvent ev)
*/
protected void updateViewFromModel()
{
- if (SwingUtilities.isEventDispatchThread())
- updateViewFromModelInEventDispatchThread();
- else
+ /*
+ * We receive events/notifications from various threads and we respond
+ * to them in the AWT event dispatching thread. It is possible to first
+ * schedule an event to be brought to the AWT event dispatching thread,
+ * then to have #dispose() invoked on this instance and, finally, to
+ * receive the scheduled event in the AWT event dispatching thread. In
+ * such a case, this disposed instance should not respond to the event.
+ */
+ if (!disposed)
{
- SwingUtilities.invokeLater(
- updateViewFromModelInEventDispatchThread);
+ if (SwingUtilities.isEventDispatchThread())
+ updateViewFromModelInEventDispatchThread();
+ else
+ {
+ SwingUtilities.invokeLater(
+ updateViewFromModelInEventDispatchThread);
+ }
}
}
@@ -401,6 +443,17 @@ protected abstract ConferenceCallPeerRenderer updateViewFromModel(
*/
protected void updateViewFromModelInEventDispatchThread()
{
+ /*
+ * We receive events/notifications from various threads and we respond
+ * to them in the AWT event dispatching thread. It is possible to first
+ * schedule an event to be brought to the AWT event dispatching thread,
+ * then to have #dispose() invoked on this instance and, finally, to
+ * receive the scheduled event in the AWT event dispatching thread. In
+ * such a case, this disposed instance should not respond to the event.
+ */
+ if (disposed)
+ return;
+
/* Update the view of the local peer/user. */
updateViewFromModel(null);
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java
index b7231ba73..bec0333a8 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java
@@ -13,8 +13,6 @@
import javax.swing.*;
-import com.sun.org.apache.bcel.internal.generic.*;
-
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.call.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
@@ -24,7 +22,6 @@
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
-// disambiguation
/**
* The invite dialog is the one shown when the user clicks on the conference