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