/*
* Copyright 2004 - 2008 Christian Sprajc. All rights reserved.
*
* This file is part of PowerFolder.
*
* PowerFolder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* PowerFolder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PowerFolder. If not, see <http://www.gnu.org/licenses/>.
*
* $Id$
*/
package de.dal33t.powerfolder.ui.friends;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.InetSocketAddress;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import de.dal33t.powerfolder.Controller;
import de.dal33t.powerfolder.Member;
import de.dal33t.powerfolder.PreferencesEntry;
import de.dal33t.powerfolder.net.ConnectionListener;
import de.dal33t.powerfolder.net.NodeSearcher;
import de.dal33t.powerfolder.ui.action.BaseAction;
import de.dal33t.powerfolder.ui.action.ConnectAction;
import de.dal33t.powerfolder.ui.model.SearchNodeTableModel;
import de.dal33t.powerfolder.ui.widget.FilterTextField;
import de.dal33t.powerfolder.ui.PFUIComponent;
import de.dal33t.powerfolder.util.Translation;
import de.dal33t.powerfolder.ui.util.UIUtil;
import de.dal33t.powerfolder.ui.util.PopupMenuOpener;
import de.dal33t.powerfolder.ui.util.SimpleComponentFactory;
/**
* Search for members, use to "make friends".
*
* @author <A HREF="mailto:schaatser@powerfolder.com">Jan van Oosterom</A>
* @version $Revision: 1.7 $
*/
public class FindComputersPanel extends PFUIComponent {
/** input field for search text */
private FilterTextField searchInput;
private ValueModel searchInputVM;
/** the ui of the list of users that matches the search. */
private JTable searchResultTable;
private JScrollPane searchResultScroller;
/** the table model holding the search results */
private SearchNodeTableModel searchNodeTableModel;
/** this panel */
private JComponent panel;
/** add friend */
private Action addFriendAction;
/** to connect */
private Action connectAction;
/** The Thread performing the search */
private NodeSearcher searcher;
/** Label to show when there are no results */
private JLabel noResultsLabel;
/**
* the toggle button that indicates if the offline users should be hidden or
* not
*/
private JCheckBox hideOffline;
/**
* create a FriendsPanel
*
* @param controller
*/
public FindComputersPanel(Controller controller) {
super(controller);
addFriendAction = new AddFriendAction();
connectAction = new MyConnectAction(controller);
noResultsLabel = new JLabel(Translation
.getTranslation("friend_search.no_computers_found"),
SwingConstants.CENTER);
noResultsLabel.setEnabled(false);
}
private void showResults(final Boolean valuesDisplayed) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
searchResultScroller.setVisible(valuesDisplayed);
noResultsLabel.setVisible(!valuesDisplayed);
}
});
}
/**
* returns this ui component, creates it if not available
*/
public JComponent getUIComponent() {
if (panel == null) {
initComponents();
panel = createContentPanel();
searchInput.requestFocus();
showResults(false);
}
return panel;
}
public void cancelSearch() {
// Block until the search has been killed if there's one running
if (searcher != null && searcher.isSearching()) {
searcher.cancelSearch();
}
}
private void initComponents() {
searchInput = new FilterTextField(
15,
Translation
.getTranslation("find_computers_panel.search_for_computer.hint"),
Translation
.getTranslation("find_computers_panel.search_for_computer.tooltip"));
searchInputVM = searchInput.getValueModel();
searchInputVM.addValueChangeListener(new MySearchInputVMListener());
searchNodeTableModel = new SearchNodeTableModel(getController());
addFriendAction.setEnabled(false);
searchResultTable = new FindComputersTable(searchNodeTableModel);
searchResultTable.getSelectionModel().addListSelectionListener(
new SearchResultSelectionListener());
searchResultTable.addMouseListener(new PopupMenuOpener(
createPopupMenu()));
searchResultTable.addMouseListener(new DoubleClickAction(
addFriendAction));
searchResultTable.registerKeyboardAction(new SelectAllAction(),
KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK),
JComponent.WHEN_FOCUSED);
searchResultScroller = new JScrollPane(searchResultTable);
UIUtil.whiteStripTable(searchResultTable);
UIUtil.removeBorder(searchResultScroller);
searchNodeTableModel.getListModel().addListDataListener(
new ListDataListener() {
public void contentsChanged(ListDataEvent e) {
showResults(!searchNodeTableModel.getListModel().isEmpty());
}
public void intervalAdded(ListDataEvent e) {
showResults(!searchNodeTableModel.getListModel().isEmpty());
}
public void intervalRemoved(ListDataEvent e) {
showResults(!searchNodeTableModel.getListModel().isEmpty());
}
});
}
private JComponent createContentPanel() {
FormLayout layout = new FormLayout("fill:600:grow",
"pref, 3dlu, pref, 3dlu, fill:300:grow");
PanelBuilder builder = new PanelBuilder(layout);
CellConstraints cc = new CellConstraints();
builder.add(createSearchPanel(), cc.xy(1, 1));
builder.addSeparator(null, cc.xy(1, 3));
// searchResultScroller and noResultsLabel share the same slot.
builder.add(searchResultScroller, cc.xy(1, 5));
builder.add(noResultsLabel, cc.xy(1, 5));
updateActions();
return builder.getPanel();
}
/**
* Creates the popup menu
*/
private JPopupMenu createPopupMenu() {
JPopupMenu popupMenu = SimpleComponentFactory.createPopupMenu();
popupMenu.add(addFriendAction);
return popupMenu;
}
private JPanel createSearchPanel() {
hideOffline = new JCheckBox(new HideOfflineAction());
hideOffline.setSelected(PreferencesEntry.FRIEND_SEARCH_HIDE_OFFLINE
.getValueBoolean(getController()));
hideOffline.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
// save to pref if hide off line checkbox clicked
PreferencesEntry.FRIEND_SEARCH_HIDE_OFFLINE.setValue(
getController(), hideOffline.isSelected());
}
});
FormLayout layout = new FormLayout("0:grow, pref, 3dlu, pref", "pref");
PanelBuilder builder = new PanelBuilder(layout);
CellConstraints cc = new CellConstraints();
builder.add(hideOffline, cc.xy(2, 1));
builder.add(searchInput.getUIComponent(), cc.xy(4, 1));
return builder.getPanel();
}
/** perform a search, interrupts a search if still running */
private void search() {
Object o = searchInputVM.getValue();
if (o == null) {
return;
}
String searchText = ((String) o).trim().toLowerCase();
if (searchText.length() < 3) {
return;
}
cancelSearch();
searcher = new NodeSearcher(getController(), searchText,
searchNodeTableModel.getListModel(), false, hideOffline
.isSelected());
searcher.start();
}
/** called if button addFriend clicked or if selected in popupmenu */
private void addFriend() {
synchronized (searchNodeTableModel) {
int[] selectedIndexes = searchResultTable.getSelectedRows();
if (selectedIndexes != null && selectedIndexes.length > 0) {
for (int index : selectedIndexes) {
Object item = searchNodeTableModel.getDataAt(index);
if (item instanceof Member) {
Member newFriend = (Member) item;
newFriend.setFriend(true, null);
}
}
}
}
// Update actions
updateActions();
// refresh search (removes the new friend)
search();
}
// UI Helper code *********************************************************
/**
* Updates the state of all actions upon the current selection
*/
private void updateActions() {
addFriendAction.setEnabled(false);
int[] selectedIndexes = searchResultTable.getSelectedRows();
// if at least one member selected
boolean enabled = false;
for (int index : selectedIndexes) {
Object item = searchNodeTableModel.getDataAt(index);
if (item instanceof Member) {
Member user = (Member) item;
if (!user.isFriend()) {
enabled = true;
break;
}
}
}
addFriendAction.setEnabled(enabled);
}
private class MyConnectAction extends ConnectAction {
private MyConnectAction(Controller controller) {
super(controller);
}
@Override
public void actionPerformed(ActionEvent e) {
int row = searchResultTable.getSelectedRow();
if (row >= 0) {
Member node = (Member) searchNodeTableModel.getValueAt(row, 0);
if (node != null) {
InetSocketAddress addr = node.getReconnectAddress();
String conStr = addr.getAddress().getHostAddress();
if (addr.getPort() != ConnectionListener.DEFAULT_PORT) {
conStr += ':';
conStr += addr.getPort();
}
setInputConnect(conStr);
}
}
super.actionPerformed(e);
}
}
/** The hide offline user to perform on click on checkbox */
private class HideOfflineAction extends BaseAction {
private HideOfflineAction() {
super("action_hide_offline", FindComputersPanel.this
.getController());
}
public void actionPerformed(ActionEvent e) {
search();
}
}
/** The Add friends action for button and popup menu Item */
private class AddFriendAction extends BaseAction {
private AddFriendAction() {
super("action_add_friend", FindComputersPanel.this.getController());
}
public void actionPerformed(ActionEvent e) {
addFriend();
}
}
/**
* listens to keys in the search input updates the searchAction state if
* enough chars are available and preforms a search on enter key
*
* @author <A HREF="mailto:schaatser@powerfolder.com">Jan van Oosterom</A>
*/
private class MySearchInputVMListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
search();
}
}
/**
* Helper class which tracks the selections int the search Results and
* updates the actions to the correct state
*
* @author <A HREF="mailto:schaatser@powerfolder.com">Jan van Oosterom</A>
*/
private class SearchResultSelectionListener implements
ListSelectionListener
{
public void valueChanged(ListSelectionEvent e) {
updateActions();
}
}
public Action getAddFriendAction() {
return addFriendAction;
}
public Action getConnectAction() {
return connectAction;
}
private class SelectAllAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
searchResultTable.selectAll();
}
}
private static class DoubleClickAction extends MouseAdapter {
private Action action;
DoubleClickAction(Action action) {
if (action == null) {
throw new NullPointerException("Action is null");
}
this.action = action;
}
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2
&& action.isEnabled())
{
action.actionPerformed(null);
}
}
}
}