package org.limewire.ui.swing.statusbar; 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.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.AbstractCellEditor; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.Timer; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import net.miginfocom.swing.MigLayout; import org.jdesktop.application.Resource; import org.jdesktop.swingx.JXPanel; import org.limewire.collection.glazedlists.GlazedListsFactory; import org.limewire.core.api.library.SharedFileList; import org.limewire.core.api.library.SharedFileListManager; 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.ListenerSupport; import org.limewire.listener.SwingEDTEvent; import org.limewire.ui.swing.action.AbstractAction; import org.limewire.ui.swing.components.HyperlinkButton; import org.limewire.ui.swing.components.PopupHeaderBar; import org.limewire.ui.swing.components.Resizable; import org.limewire.ui.swing.components.decorators.ButtonDecorator; import org.limewire.ui.swing.friends.login.AutoLoginService; import org.limewire.ui.swing.friends.login.LoginPopupPanel; import org.limewire.ui.swing.library.LibraryMediator; import org.limewire.ui.swing.painter.StatusBarPopupButtonPainter.DrawMode; import org.limewire.ui.swing.painter.StatusBarPopupButtonPainter.PopupVisibilityChecker; import org.limewire.ui.swing.table.MouseableTable; import org.limewire.ui.swing.util.GuiUtils; import org.limewire.ui.swing.util.I18n; import org.limewire.ui.swing.util.PainterUtils; import ca.odell.glazedlists.FilterList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.gui.TableFormat; import ca.odell.glazedlists.impl.swing.SwingThreadProxyEventList; import ca.odell.glazedlists.matchers.Matcher; import ca.odell.glazedlists.swing.DefaultEventTableModel; import com.google.inject.Inject; import com.google.inject.Provider; public class SharedFileCountPopupPanel extends Panel implements Resizable { private static final String ICON_COLUMN_ID = "Icon"; private static final String NAME_COLUMN_ID = "Name"; private static final String BLANK_COLUMN_ID = "Blank"; private static final String FILES_COLUMN_ID = "Files"; private static final int ROW_HEIGHT = 18; @Resource private Color background = PainterUtils.TRASPARENT; @Resource private Color border = PainterUtils.TRASPARENT; @Resource private Icon publicIcon; @Resource private Icon listSharedIcon; @Resource private Font listTextFont; @Resource private Color listTextForeground; @Resource private Font signInTextFont; private final SharedFileCountPanel sharedFileCountPanel; private final SharedFileListManager shareListManager; private final Provider<LoginPopupPanel> loginPanelProvider; private final ListenerSupport<FriendConnectionEvent> connectionSupport; private final LibraryMediator libraryMediator; private final Provider<AutoLoginService> autoLoginServiceProvider; private Timer repaintTimer = null; private JXPanel frame = null; private MouseableTable table = null; private VisiblityMatcher matcher = null; private FilterList<SharedFileList> filteredSharedFileLists; private final AbstractAction closeAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { setVisible(false); sharedFileCountPanel.repaint(); } }; private HyperlinkButton signIntoFriendsButton = null; private final EventBean<FriendConnectionEvent> friendConnectionBean; @Inject public SharedFileCountPopupPanel(SharedFileCountPanel sharedFileCountPanel, SharedFileListManager shareListManager, Provider<LoginPopupPanel> loginPanelProvider, ListenerSupport<FriendConnectionEvent> connectionSupport, LibraryMediator libraryMediator, Provider<AutoLoginService> autoLoginServiceProvider, EventBean<FriendConnectionEvent> friendConnectionBean, ButtonDecorator buttonDecorator) { super(new BorderLayout()); this.sharedFileCountPanel = sharedFileCountPanel; this.shareListManager = shareListManager; this.loginPanelProvider = loginPanelProvider; this.connectionSupport = connectionSupport; this.libraryMediator = libraryMediator; this.autoLoginServiceProvider = autoLoginServiceProvider; this.friendConnectionBean = friendConnectionBean; GuiUtils.assignResources(this); setUpButton(buttonDecorator); setVisible(false); } @Inject public void register() { sharedFileCountPanel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { setVisible(!isVisible() && shareListManager.getSharedFileCount() != 0); sharedFileCountPanel.repaint(); } }); sharedFileCountPanel.addComponentListener(new ComponentAdapter() { @Override public void componentMoved(ComponentEvent e) { if (isVisible()) { resize(); } } }); } @SuppressWarnings("unchecked") private void initContent() { frame = new JXPanel(new BorderLayout()); frame.setPreferredSize(new Dimension(225, 160)); JPanel topBarPanel = new PopupHeaderBar(I18n.tr("Lists I'm Sharing"), closeAction); frame.add(topBarPanel, BorderLayout.NORTH); JPanel contentPanel = new JPanel(new BorderLayout()); JScrollPane scrollPane = new JScrollPane(contentPanel); scrollPane.setOpaque(false); scrollPane.setBorder(BorderFactory.createEmptyBorder()); contentPanel.setOpaque(false); contentPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(1, 1, 0, 1, border), BorderFactory.createEmptyBorder(5,4,4,4))); filteredSharedFileLists = new FilterList<SharedFileList>(shareListManager.getModel()); matcher = new VisiblityMatcher(); filteredSharedFileLists.setMatcher(matcher); SwingThreadProxyEventList<SharedFileList> stpl = GlazedListsFactory.swingThreadProxyEventList(filteredSharedFileLists); table = new MouseableTable(new DefaultEventTableModel<SharedFileList>(stpl, new TableFormat<SharedFileList>() { @Override public int getColumnCount() { return 4; } @Override public String getColumnName(int column) { if (column == 0) { return ICON_COLUMN_ID; } else if (column == 1) { return NAME_COLUMN_ID; } else if (column == 2) { return BLANK_COLUMN_ID; } else { return FILES_COLUMN_ID; } } @Override public Object getColumnValue(SharedFileList baseObject, int column) { if (column == 0) { if (baseObject.isPublic()) { return publicIcon; } else { return listSharedIcon; } } else if (column == 1) { return baseObject.getCollectionName(); } else if (column == 2) { return null; } else { return I18n.trn("{0} file", "{0} files", baseObject.size()); } } })); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); TableColumn iconColumn = table.getColumn(ICON_COLUMN_ID); iconColumn.setCellRenderer(new IconRenderer()); iconColumn.setPreferredWidth(publicIcon.getIconWidth()); iconColumn.setMaxWidth(publicIcon.getIconWidth()); LinkRenderer linkRenderer = new LinkRenderer(); linkRenderer.setFont(listTextFont); LinkEditor linkEditor = new LinkEditor(); linkEditor.setFont(listTextFont); TableColumn nameColumn = table.getColumn(NAME_COLUMN_ID); nameColumn.setCellRenderer(linkRenderer); nameColumn.setCellEditor(linkEditor); TableColumn blankColumn = table.getColumn(BLANK_COLUMN_ID); blankColumn.setPreferredWidth(20); blankColumn.setMinWidth(20); blankColumn.setMaxWidth(20); table.getColumn(FILES_COLUMN_ID).setPreferredWidth(0); table.setFont(listTextFont); table.setForeground(listTextForeground); table.setOpaque(false); table.setShowGrid(false); table.setFocusable(false); table.setCellSelectionEnabled(false); table.setRowHeight(ROW_HEIGHT); table.setStripeHighlighterEnabled(false); table.setEmptyRowsPainted(false); repaintTimer = new Timer(50, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { repaint(); } }); final Timer resizeTimer = new Timer(50, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { resize(); } }); repaintTimer.setRepeats(false); final ListEventListener repaintListener = new RepaintListener(); for ( SharedFileList item : shareListManager.getModel() ) { item.getSwingModel().addListEventListener(repaintListener); } shareListManager.getModel().addListEventListener(new ListEventListener<SharedFileList>() { @Override public void listChanged(ListEvent<SharedFileList> listChanges) { while(listChanges.next()) { if (listChanges.getType() == ListEvent.INSERT) { SharedFileList newList = listChanges.getSourceList().get(listChanges.getIndex()); newList.getModel().addListEventListener(repaintListener); } } resizeTimer.start(); } }); contentPanel.add(table, BorderLayout.CENTER); JPanel bottomPanel = new JPanel(new MigLayout("gap 0, insets 0, align center")); bottomPanel.setOpaque(false); setUpAndAddSignInButton(bottomPanel); contentPanel.add(bottomPanel, BorderLayout.SOUTH); frame.add(scrollPane, BorderLayout.CENTER); add(frame, BorderLayout.CENTER); } private void setUpAndAddSignInButton(final JPanel panel) { if (shouldShowSignInButton()) { signIntoFriendsButton = new HyperlinkButton(new AbstractAction(I18n.tr("Sign in to share with friends")) { @Override public void actionPerformed(ActionEvent e) { loginPanelProvider.get().setVisible(true); } }); signIntoFriendsButton.setFont(signInTextFont); panel.add(signIntoFriendsButton); connectionSupport.addListener(new EventListener<FriendConnectionEvent>() { @SwingEDTEvent @Override public void handleEvent(FriendConnectionEvent event) { if (!shouldShowSignInButton()) { connectionSupport.removeListener(this); panel.remove(signIntoFriendsButton); signIntoFriendsButton = null; validate(); resize(); repaint(); } } }); } } private boolean shouldShowSignInButton() { FriendConnectionEvent lastEvent = friendConnectionBean.getLastEvent(); FriendConnection friendConnection = lastEvent != null ? lastEvent.getSource() : null; if ((friendConnection != null && (friendConnection.isLoggedIn() || friendConnection.isLoggingIn())) || autoLoginServiceProvider.get().isAttemptingLogin()) { return false; } if (shareListManager.getModel().size() == 1) { return true; } for ( SharedFileList list : shareListManager.getModel() ) { if (!list.isPublic()) { if (list.getFriendIds().size() != 0) { return false; } } } return true; } @Override public void setVisible(boolean visible) { super.setVisible(visible); if (table == null && visible) { initContent(); } if (visible) { resize(); validate(); frame.repaint(); } } private int getRowsToShow() { int rows = filteredSharedFileLists.size(); return (rows > 10) ? 10 : rows; } @Override public void resize() { Rectangle parentBounds = getParent().getBounds(); Dimension childPreferredSize = frame.getPreferredSize(); int w = (int) childPreferredSize.getWidth(); int h = 32 + ROW_HEIGHT*getRowsToShow() + (signIntoFriendsButton == null ? 0 : 15); setBounds((int)sharedFileCountPanel.getBounds().getX(), (int)parentBounds.getHeight() - h, w, h); } private void setUpButton(ButtonDecorator buttonDecorator) { buttonDecorator.decorateStatusPopupButton(sharedFileCountPanel, new PopupVisibilityChecker() { @Override public boolean isPopupVisible() { return isVisible(); } }, background, border, DrawMode.RIGHT_CONNECTING); sharedFileCountPanel.setEnabled(shareListManager.getSharedFileCount() != 0); shareListManager.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if(evt.getPropertyName().equals(SharedFileListManager.SHARED_FILE_COUNT)) { sharedFileCountPanel.setEnabled(shareListManager.getSharedFileCount() != 0); } } }); } private class RepaintListener implements ListEventListener { @Override public void listChanged(ListEvent listChanges) { repaintTimer.start(); } } private static class IconRenderer extends JLabel implements TableCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (!(value instanceof Icon)) { return null; } setIcon((Icon) value); return this; } } private static class LinkRenderer extends HyperlinkButton implements TableCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value == null) { return null; } setText(value.toString()); return this; } /** * Used to filter out evil foreground calls from the default JXTable Highlighter */ @Override public void setForeground(Color c) { } } private class LinkEditor extends AbstractCellEditor implements TableCellEditor { private final SharedFileListHyperLinkButton button = new SharedFileListHyperLinkButton(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { libraryMediator.showSharedFileList(filteredSharedFileLists.get(button.getFilteredListIndex())); } }); @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { if (value == null) { return null; } button.setText(value.toString()); button.setFilteredListIndex(row); return button; } @Override public Object getCellEditorValue() { return button; } public void setFont(Font f) { button.setFont(f); } } private static class SharedFileListHyperLinkButton extends HyperlinkButton { private int filteredListIndex = -1; public SharedFileListHyperLinkButton(Action a) { super(a); } public void setFilteredListIndex(int i) { filteredListIndex = i; } public int getFilteredListIndex() { return filteredListIndex; } /** * Used to filter out evil foreground calls from the default JXTable Highlighter */ @Override public void setForeground(Color c) { } } private static class VisiblityMatcher implements Matcher<SharedFileList> { @Override public boolean matches(SharedFileList item) { return item.isPublic() || item.getFriendIds().size() != 0; } } }