package org.limewire.ui.swing.mainframe;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import net.miginfocom.swing.MigLayout;
import org.jdesktop.application.Resource;
import org.jdesktop.swingx.JXPanel;
import org.limewire.friend.api.FriendConnection;
import org.limewire.friend.api.FriendConnectionEvent;
import org.limewire.listener.EventBean;
import org.limewire.listener.EventListener;
import org.limewire.listener.EventListenerList;
import org.limewire.listener.EventUtils;
import org.limewire.listener.SwingEDTEvent;
import org.limewire.ui.swing.components.MessageComponent;
import org.limewire.ui.swing.components.PanelResizer;
import org.limewire.ui.swing.components.Resizable;
import org.limewire.ui.swing.components.RolloverCursorListener;
import org.limewire.ui.swing.components.decorators.MessageDecorator;
import org.limewire.ui.swing.library.LibraryMediator;
import org.limewire.ui.swing.library.LibraryTableRect;
import org.limewire.ui.swing.library.navigator.LibraryNavItem;
import org.limewire.ui.swing.library.navigator.LibraryNavigatorPanel;
import org.limewire.ui.swing.library.navigator.LibraryNavItem.NavType;
import org.limewire.ui.swing.library.sharing.LibrarySharingEvent;
import org.limewire.ui.swing.nav.NavCategory;
import org.limewire.ui.swing.nav.NavItem;
import org.limewire.ui.swing.nav.NavMediator;
import org.limewire.ui.swing.nav.NavSelectable;
import org.limewire.ui.swing.nav.NavigationListener;
import org.limewire.ui.swing.nav.Navigator;
import org.limewire.ui.swing.settings.SwingUiSettings;
import org.limewire.ui.swing.util.GuiUtils;
import org.limewire.ui.swing.util.I18n;
import com.google.inject.Inject;
import com.google.inject.Provider;
/**
* Container for overlay messages that are displayed when the user signs on
* for private sharing.
*/
class SignOnMessageLayer {
/** Message type definitions. */
private enum Type {
NONE, LIBRARY, SHARING;
}
@Resource private Color messageForeground;
@Resource private Font libraryFont;
@Resource private Font sharingFont;
@Resource private Icon addFileIcon;
@Resource private Icon checkedIcon;
@Resource private Icon closeIcon;
@Resource private Icon closeHoverIcon;
@Resource private Icon upArrowIcon;
@Resource private Icon leftArrowIcon;
private final JLayeredPane layeredPane;
private final TopPanel topPanel;
private final Navigator navigator;
private final Provider<LibraryNavigatorPanel> libraryNavProvider;
private final LibraryMediator libraryMediator;
private final EventListenerList<LibrarySharingEvent> libraryListenerList;
private final Provider<Rectangle> libraryTableRect;
private final EventBean<FriendConnectionEvent> connectionEventBean;
private final Provider<MessageDecorator> messageDecoratorProvider;
/** Current message component. */
private Component messageComponent;
/** Type of current message. */
private Type messageType = Type.NONE;
/** Listener to handle layered pane resizing. */
private PanelResizer panelResizer;
/** Listener to handle library sharing events. */
private EventListener<LibrarySharingEvent> libraryListener;
/**
* Constructs a SignOnMessagePanel with the specified services.
*/
@Inject
public SignOnMessageLayer(
@GlobalLayeredPane JLayeredPane limeWireLayeredPane,
TopPanel topPanel,
Navigator navigator,
Provider<LibraryNavigatorPanel> libraryNavProvider,
LibraryMediator libraryMediator,
EventListenerList<LibrarySharingEvent> libraryListenerList,
@LibraryTableRect Provider<Rectangle> libraryTableRect,
EventBean<FriendConnectionEvent> connectionEventBean,
Provider<MessageDecorator> messageDecoratorProvider) {
this.layeredPane = limeWireLayeredPane;
this.topPanel = topPanel;
this.navigator = navigator;
this.libraryMediator = libraryMediator;
this.libraryNavProvider = libraryNavProvider;
this.libraryListenerList = libraryListenerList;
this.libraryTableRect = libraryTableRect;
this.connectionEventBean = connectionEventBean;
this.messageDecoratorProvider = messageDecoratorProvider;
GuiUtils.assignResources(this);
}
/**
* Registers listeners for service events. This method should be called
* post-construction.
*/
@Inject
void register() {
// Add listener to handle Navigator selection events.
navigator.addNavigationListener(new NavigationListener() {
@Override
public void categoryAdded(NavCategory category) {
}
@Override
public void categoryRemoved(NavCategory category, boolean wasSelected) {
}
@Override
public void itemAdded(NavCategory category, NavItem navItem) {
}
@Override
public void itemRemoved(NavCategory category, NavItem navItem, boolean wasSelected) {
}
@Override
public void itemSelected(NavCategory category, NavItem navItem,
NavSelectable selectable, NavMediator navMediator) {
//TODO: all this logic is extremely brittle
switch (messageType) {
case LIBRARY:
// If library message showing and Library selected, close message.
if (category == NavCategory.LIBRARY) {
hideMessage();
}
break;
case SHARING:
// If sharing message showing and Library de-selected, close message.
if (category != NavCategory.LIBRARY) {
hideMessage();
}
break;
}
// If Private Shared selected and signed on, show message.
NavType selectedType = getSelectedNavType();
if ((category == NavCategory.LIBRARY) &&
(selectedType == NavType.LIST) && isSignedOn()) {
showMessage();
}
}
});
// Add listener to handle LibraryNavigator selection events.
libraryNavProvider.get().addTableSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
NavType navType = getSelectedNavType();
//TODO: all this logic is extremely brittle
switch (messageType) {
case LIBRARY:
// If library message showing and Private Shared selected, close message.
if (navType != NavType.LIBRARY) {
hideMessage();
}
// If Private Shared selected and signed on, show message.
if ((navType == NavType.LIST) && isSignedOn()) {
showMessage();
}
break;
case SHARING:
// If sharing message showing and Private Shared not selected, close message.
if (navType != NavType.LIST) {
hideMessage();
}
break;
case NONE:
// If Private Shared selected and signed on, show message.
if ((navType == NavType.LIST) && isSignedOn()) {
showMessage();
}
break;
}
}
});
}
/**
* Returns true if there are any pending sign-on messages.
*/
public static boolean isSignOnMessageEnabled() {
return SwingUiSettings.SHOW_LIBRARY_OVERLAY_MESSAGE.getValue() ||
SwingUiSettings.SHOW_SHARING_OVERLAY_MESSAGE.getValue();
}
/**
* Returns true if the user is signed on to any service.
*/
private boolean isSignedOn() {
FriendConnection connection = EventUtils.getSource(connectionEventBean);
return ((connection != null) && connection.isLoggedIn());
}
/**
* Displays a sign-on message using the specified layered pane.
*/
public void showMessage() {
// Retrieve message settings.
boolean showLibraryMsg = SwingUiSettings.SHOW_LIBRARY_OVERLAY_MESSAGE.getValue();
boolean showSharingMsg = SwingUiSettings.SHOW_SHARING_OVERLAY_MESSAGE.getValue();
// Get Library item from navigator.
NavItem libraryItem = navigator.getNavItem(NavCategory.LIBRARY, LibraryMediator.NAME);
// Get selected collection in library.
NavType selectedType = getSelectedNavType();
// Remove old message components.
if (panelResizer != null) {
layeredPane.removeComponentListener(panelResizer);
panelResizer = null;
}
if (messageComponent != null) {
layeredPane.remove(messageComponent);
messageComponent = null;
}
if ((libraryItem == null) || !libraryItem.isSelected()) {
// Library not selected so show message pointing to Library.
if (showLibraryMsg) {
Point location = new Point(16, topPanel.getSize().height - 4);
messageComponent = new LibraryMessagePanel(location, false);
messageType = Type.LIBRARY;
}
} else if ((selectedType == NavType.LIBRARY) || (selectedType == NavType.PUBLIC_SHARED)) {
// Library selected so show message pointing to Private Shared.
if (showLibraryMsg) {
LibraryNavigatorPanel libraryNavPanel = libraryNavProvider.get();
Point location = new Point(libraryNavPanel.getSize().width - 6, 75);
messageComponent = new LibraryMessagePanel(location, true);
messageType = Type.LIBRARY;
}
} else {
// Disable library overlay message.
SwingUiSettings.SHOW_LIBRARY_OVERLAY_MESSAGE.setValue(false);
// Private Shared selected so show message about sharing files.
if (showSharingMsg) {
messageComponent = new SharingMessagePanel();
messageType = Type.SHARING;
// Add listener for Library events to dismiss message.
libraryListener = new EventListener<LibrarySharingEvent>() {
@Override
@SwingEDTEvent
public void handleEvent(LibrarySharingEvent event) {
hideMessage();
}
};
libraryListenerList.addListener(libraryListener);
}
}
// Exit if no message to display.
if (messageComponent == null) {
messageType = Type.NONE;
return;
}
// Add new message to layered pane.
panelResizer = new PanelResizer((Resizable) messageComponent);
layeredPane.add(messageComponent, JLayeredPane.PALETTE_LAYER);
layeredPane.addComponentListener(panelResizer);
layeredPane.validate();
// Show message.
messageComponent.setVisible(true);
((Resizable) messageComponent).resize();
}
private NavType getSelectedNavType() {
// Get selected collection in library.
LibraryNavItem selectedItem = libraryMediator.getSelectedNavItem();
NavType selectedType;
if(selectedItem == null)
selectedType = NavType.LIBRARY;
else
selectedType = selectedItem.getType();
return selectedType;
}
/**
* Hides the current sign-on message, and disables the message so it will
* not be displayed again.
*/
public void hideMessage() {
// Hide message and validate layered pane.
if(panelResizer != null) {
layeredPane.removeComponentListener(panelResizer);
panelResizer = null;
}
if(messageComponent != null) {
messageComponent.setVisible(false);
layeredPane.remove(messageComponent);
messageComponent = null;
}
layeredPane.validate();
// Remove library listener.
if (libraryListener != null) {
libraryListenerList.removeListener(libraryListener);
libraryListener = null;
}
// Disable overlay messages.
switch (messageType) {
case LIBRARY:
SwingUiSettings.SHOW_LIBRARY_OVERLAY_MESSAGE.setValue(false);
break;
case SHARING:
SwingUiSettings.SHOW_LIBRARY_OVERLAY_MESSAGE.setValue(false);
SwingUiSettings.SHOW_SHARING_OVERLAY_MESSAGE.setValue(false);
break;
}
messageType = Type.NONE;
}
/**
* Creates a close button.
*/
private JButton createCloseButton() {
JButton closeButton = new JButton();
closeButton.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
closeButton.setContentAreaFilled(false);
closeButton.setFocusPainted(false);
closeButton.setIcon(closeIcon);
closeButton.setRolloverIcon(closeHoverIcon);
closeButton.setRolloverEnabled(true);
closeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
hideMessage();
}
});
// Install listener to display rollover cursor.
new RolloverCursorListener().install(closeButton);
return closeButton;
}
/**
* Container for Library overlay message that may be displayed when the
* user signs on for private sharing. LibraryMessagePanel extends
* java.awt.Panel because the message may be displayed on top of the
* browser component.
*/
private class LibraryMessagePanel extends Panel implements Resizable {
private final Point messageLocation;
private JXPanel messagePanel;
private JButton closeButton;
public LibraryMessagePanel(Point messageLocation, boolean librarySelected) {
super(new BorderLayout());
this.messageLocation = messageLocation;
messagePanel = new JXPanel(new MigLayout("insets 3 3 3 3, gap 0!"));
messagePanel.setOpaque(false);
messageDecoratorProvider.get().decorateGreenRectangleMessage(messagePanel);
// Create message components.
closeButton = createCloseButton();
JLabel arrowLabel = new JLabel();
arrowLabel.setIcon(librarySelected ? leftArrowIcon : upArrowIcon);
JLabel messageLabel = new JLabel();
messageLabel.setFont(libraryFont);
messageLabel.setForeground(messageForeground);
messageLabel.setText(I18n.tr("Share with friends using your Private Shared list"));
// Add components to container.
if (librarySelected) {
messagePanel.add(arrowLabel, "gap 6 0 0 0, aligny center");
} else {
messagePanel.add(arrowLabel, "gap 6 0 6 0, aligny top");
}
messagePanel.add(messageLabel, "gap 6 6 10 10, aligny center");
messagePanel.add(closeButton , "aligny top");
add(messagePanel, BorderLayout.CENTER);
}
@Override
public void resize() {
// Get preferred size.
Dimension preferredSize = getPreferredSize();
// Set bounds to position message in layered pane.
setBounds(messageLocation.x, messageLocation.y, preferredSize.width, preferredSize.height);
}
}
/**
* Container for Sharing overlay message that may be displayed when the
* user signs on for private sharing. SharingMessagePanel extends
* javax.swing.JPanel because it is displayed over the library table with
* a transparent background.
*/
private class SharingMessagePanel extends JPanel implements Resizable {
private MessageComponent messageComponent;
private JButton closeButton;
private JLabel leftMessageLabel;
private JLabel rightMessageLabel;
public SharingMessagePanel() {
setLayout(new MigLayout("insets 0 0 0 0"));
setOpaque(false);
// Create message component.
messageComponent = new MessageComponent(5, 5, 18, 8);
messageDecoratorProvider.get().decorateGreenMessage(messageComponent);
// Create message elements.
closeButton = createCloseButton();
leftMessageLabel = new JLabel();
leftMessageLabel.setFont(sharingFont);
leftMessageLabel.setForeground(messageForeground);
leftMessageLabel.setIcon(checkedIcon);
leftMessageLabel.setText(I18n.tr("Share this list with friends"));
rightMessageLabel = new JLabel();
rightMessageLabel.setFont(sharingFont);
rightMessageLabel.setForeground(messageForeground);
rightMessageLabel.setIcon(addFileIcon);
rightMessageLabel.setText(I18n.tr("Add files to it"));
// Add message elements to component.
messageComponent.addComponent(closeButton, "span 2, align right, push, wrap");
//messageComponent.addComponent(titleLabel, "gap 15 15, span 2, align left, wrap");
messageComponent.addComponent(leftMessageLabel , "gap 18 6, align left, wrap");
messageComponent.addComponent(rightMessageLabel, "gap 15 15, align left");
//messageComponent.addComponent(moreMessageLabel , "gap 15 15, span 2, align left");
// Add message component to container.
add(messageComponent, "pos 0.5al 0.5al");
}
@Override
public void resize() {
// Post event to perform delayed resizing. This corrects an issue
// with component sizes when the window is maximized or unmaximized.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Determine message location and size.
int y0 = topPanel.getSize().height;
Rectangle tableRect = libraryTableRect.get();
// Set bounds to position message in layered pane.
setBounds(tableRect.x, y0 + tableRect.y, tableRect.width, tableRect.height);
// Revalidate layered pane.
layeredPane.revalidate();
}
});
}
}
}