/*
* This file is part of DRBD Management Console by LINBIT HA-Solutions GmbH
* written by Rasto Levrinc.
*
* Copyright (C) 2009, LINBIT HA-Solutions GmbH.
* Copyright (C) 2011-2012, Rastislav Levrinc.
*
* DRBD Management Console 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; either version 2, or (at your option)
* any later version.
*
* DRBD Management Console 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 drbd; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package lcmc.cluster.ui.wizard;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import lcmc.common.ui.GUIData;
import lcmc.common.domain.Application;
import lcmc.host.domain.Host;
import lcmc.host.domain.Hosts;
import lcmc.common.ui.WizardDialog;
import lcmc.common.domain.util.Tools;
/**
* An implementation of a dialog where user can choose which hosts belong to
* the cluster.
*/
@Named
final class ClusterHosts extends DialogCluster {
private static final ImageIcon HOST_CHECKED_ICON = Tools.createImageIcon(
Tools.getDefault("Dialog.Cluster.ClusterHosts.HostCheckedIcon"));
private static final ImageIcon HOST_UNCHECKED_ICON = Tools.createImageIcon(
Tools.getDefault("Dialog.Cluster.ClusterHosts.HostUncheckedIcon"));
/** Map from checkboxes to the host, which they choose. */
private final Map<JCheckBox, Host> checkBoxToHost = new LinkedHashMap<JCheckBox, Host>();
@Inject
private CommStack commStackDialog;
@Inject
private Connect connectDialog;
@Inject
private GUIData guiData;
@Inject
private Application application;
@Inject
private Hosts allHosts;
/** It is executed after the dialog is applied. */
@Override
protected void finishDialog() {
getCluster().removeAllHosts();
for (final Map.Entry<JCheckBox, Host> checkBoxEntry : checkBoxToHost.entrySet()) {
if (checkBoxEntry.getKey().isSelected()) {
final Host host = checkBoxEntry.getValue();
host.setCluster(getCluster());
getCluster().addHost(host);
}
}
guiData.refreshClustersPanel();
}
@Override
public WizardDialog nextDialog() {
boolean allConnected = true;
for (final Host host : getCluster().getHosts()) {
if (!host.isConnected()) {
allConnected = false;
}
}
DialogCluster nextDialog;
if (allConnected) {
nextDialog = commStackDialog;
} else {
nextDialog = connectDialog;
}
nextDialog.init(this, getCluster());
return nextDialog;
}
/** Checks whether at least two hosts are selected for the cluster. */
protected void checkCheckBoxes() {
allHosts.removeHostsFromCluster(getCluster());
int selected = 0;
for (final JCheckBox button : checkBoxToHost.keySet()) {
if (button.isSelected()) {
selected++;
}
}
boolean enable = true;
final Collection<String> hostNames = new ArrayList<String>();
if (selected < 1 || (selected == 1 && !application.isOneHostCluster())) {
enable = false;
} else {
/* check if some of the hosts are the same. It will not work all
* the time if hops are used. */
for (final Map.Entry<JCheckBox, Host> checkBoxEntry : checkBoxToHost.entrySet()) {
if (checkBoxEntry.getKey().isSelected() && checkBoxEntry.getKey().isEnabled()) {
final Host host = checkBoxEntry.getValue();
final String hostname = host.getHostname();
if (hostNames.contains(hostname)) {
enable = false;
break;
}
hostNames.add(hostname);
}
}
}
final boolean enableButton = enable;
application.invokeLater(new Runnable() {
@Override
public void run() {
buttonClass(nextButton()).setEnabled(enableButton);
}
});
if (!application.getAutoClusters().isEmpty()) {
Tools.sleep(1000);
pressNextButton();
}
}
@Override
protected String getClusterDialogTitle() {
return Tools.getString("Dialog.Cluster.ClusterHosts.Title");
}
@Override
protected String getDescription() {
return Tools.getString("Dialog.Cluster.ClusterHosts.Description");
}
@Override
protected void initDialogBeforeVisible() {
super.initDialogBeforeVisible();
enableComponentsLater(new JComponent[]{buttonClass(nextButton())});
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
checkCheckBoxes();
}
});
thread.start();
}
@Override
protected void initDialogAfterVisible() {
enableComponents();
}
@Override
protected JComponent getInputPane() {
/* Hosts */
final ScrollableFlowPanel p1 = new ScrollableFlowPanel(new FlowLayout(FlowLayout.LEADING, 1, 1));
final Hosts hosts = allHosts;
final ItemListener chListener = new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent e) {
checkCheckBoxes();
}
};
Host lastHost1 = null;
Host lastHost2 = null;
if (getCluster().getHosts().isEmpty()) {
/* mark last two available hosts */
for (final Host host : hosts.getHostsArray()) {
if (!getCluster().getHosts().contains(host)
&& !host.isInCluster()) {
if (lastHost2 != null
&& lastHost2.getIpAddress() != null
&& lastHost2.getIpAddress().equals(host.getIpAddress())) {
lastHost2 = host;
} else {
lastHost1 = lastHost2;
lastHost2 = host;
}
}
}
}
final JScrollPane sp = new JScrollPane(p1,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
for (final Host host : hosts.getHostsArray()) {
final JCheckBox button = new JCheckBox(host.getName(), HOST_UNCHECKED_ICON);
button.setBackground(Tools.getDefaultColor("ConfigDialog.Background.Light"));
button.setSelectedIcon(HOST_CHECKED_ICON);
if (getCluster().getBrowser() != null && getCluster() == host.getCluster()) {
/* once we have browser the cluster members cannot be removed.
* TODO: make it possible
*/
button.setEnabled(false);
} else if (host.isInCluster(getCluster())) {
button.setEnabled(false);
}
checkBoxToHost.put(button, host);
if (getCluster().getHosts().contains(host)) {
button.setSelected(true);
} else {
if (host == lastHost1 || host == lastHost2) {
button.setSelected(true);
} else {
button.setSelected(false);
}
}
button.addItemListener(chListener);
p1.add(button);
}
p1.setBackground(Color.WHITE);
return sp;
}
/** Workaround so that flow layout scrolls right. */
private class ScrollableFlowPanel extends JPanel implements Scrollable {
ScrollableFlowPanel(final LayoutManager layout) {
super(layout);
}
@Override
public void setBounds(final int x, final int y, final int width, final int height) {
super.setBounds(x, y, getParent().getWidth(), height);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(getWidth(), getPreferredHeight());
}
@Override
public Dimension getPreferredScrollableViewportSize() {
return super.getPreferredSize();
}
@Override
public int getScrollableUnitIncrement(final Rectangle visibleRect, final int orientation, final int direction) {
final int hundredth = (orientation == SwingConstants.VERTICAL
? getParent().getHeight() : getParent().getWidth()) / 100;
return (hundredth == 0 ? 1 : hundredth);
}
@Override
public int getScrollableBlockIncrement(final Rectangle visibleRect,
final int orientation,
final int direction) {
return orientation == SwingConstants.VERTICAL ? getParent().getHeight() : getParent().getWidth();
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
private int getPreferredHeight() {
int rv = 0;
final int count = getComponentCount();
for (int k = 0; k < count; k++) {
final java.awt.Component comp = getComponent(k);
final Rectangle r = comp.getBounds();
final int height = r.y + r.height;
if (height > rv) {
rv = height;
}
}
rv += ((FlowLayout) getLayout()).getVgap();
return rv;
}
}
}