/*
* 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.wizard;
import static de.dal33t.powerfolder.ui.wizard.WizardContextAttributes.FOLDERINFO_ATTRIBUTE;
import static de.dal33t.powerfolder.ui.wizard.WizardContextAttributes.INITIAL_FOLDER_NAME;
import static de.dal33t.powerfolder.ui.wizard.WizardContextAttributes.PROMPT_TEXT_ATTRIBUTE;
import static de.dal33t.powerfolder.ui.wizard.WizardContextAttributes.SEND_INVIATION_AFTER_ATTRIBUTE;
import static de.dal33t.powerfolder.ui.wizard.WizardContextAttributes.SYNC_PROFILE_ATTRIBUTE;
import java.awt.Color;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import de.dal33t.powerfolder.ui.dialog.GenericDialogType;
import de.dal33t.powerfolder.ui.widget.JButtonMini;
import jwf.WizardPanel;
import com.jgoodies.binding.value.ValueHolder;
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.ConfigurationEntry;
import de.dal33t.powerfolder.Controller;
import de.dal33t.powerfolder.PreferencesEntry;
import de.dal33t.powerfolder.disk.Folder;
import de.dal33t.powerfolder.disk.SyncProfile;
import de.dal33t.powerfolder.light.FolderInfo;
import de.dal33t.powerfolder.ui.dialog.DialogFactory;
import de.dal33t.powerfolder.ui.util.Icons;
import de.dal33t.powerfolder.ui.util.SimpleComponentFactory;
import de.dal33t.powerfolder.util.FileUtils;
import de.dal33t.powerfolder.util.Format;
import de.dal33t.powerfolder.util.IdGenerator;
import de.dal33t.powerfolder.util.Reject;
import de.dal33t.powerfolder.util.StringUtils;
import de.dal33t.powerfolder.util.Translation;
/**
* A wizard panel for choosing a disk location for a single folder, like when processing a join invite.
*
* @author <a href="mailto:totmacher@powerfolder.com">Christian Sprajc </a>
* @version $Revision: 1.9 $
* @deprecated use ChooseMultiDiskLocationPanel
*/
@Deprecated
public class ChooseDiskLocationPanel extends PFWizardPanel {
/**
* Used to hold initial dir and any chooser selection changes.
*/
private WizardPanel next;
private final String initialLocation;
private ValueModel locationModel; // <String (directory absolute path)>
private JTextField locationTF;
private JCheckBox backupByOnlineStorageBox;
private JCheckBox manualSyncCheckBox;
private JCheckBox sendInviteAfterCB;
private JComponent locationField;
private JLabel folderSizeLabel;
/**
* Creates a new disk location wizard panel. Name of new folder is
* automatically generated, folder will be secret
*
* @param controller
* @param initialLocation
* @param next
* the next panel after selecting the directory.
*/
public ChooseDiskLocationPanel(Controller controller,
String initialLocation, WizardPanel next)
{
super(controller);
Reject.ifNull(next, "Next wizard panel is null");
this.initialLocation = initialLocation;
this.next = next;
}
// From WizardPanel *******************************************************
public WizardPanel next() {
return next;
}
public boolean hasNext() {
return locationModel.getValue() != null && !StringUtils.isBlank(locationModel.getValue().toString());
}
public boolean validateNext() {
File location = new File((String) locationModel.getValue());
// Have to do this here as well as on choose directory
// in case the incoming dir is bad and user does not change anything!
if (!validDirectory(location)) {
return false;
}
getWizardContext().setAttribute(
WizardContextAttributes.FOLDER_LOCAL_BASE, location);
getWizardContext().setAttribute(
WizardContextAttributes.BACKUP_ONLINE_STOARGE,
backupByOnlineStorageBox.isSelected());
getWizardContext().setAttribute(SEND_INVIATION_AFTER_ATTRIBUTE,
sendInviteAfterCB.isSelected());
getWizardContext().setAttribute(INITIAL_FOLDER_NAME,
FileUtils.getSuggestedFolderName(location));
// Change to manual sync if requested.
if (manualSyncCheckBox.isSelected()) {
getWizardContext().setAttribute(SYNC_PROFILE_ATTRIBUTE,
SyncProfile.MANUAL_SYNCHRONIZATION);
}
return true;
}
protected JPanel buildContent() {
String verticalLayout = "3dlu, pref, 3dlu, pref, 3dlu, pref, 3dlu, pref, 3dlu, pref";
FormLayout layout = new FormLayout("pref, 0:grow", verticalLayout);
PanelBuilder builder = new PanelBuilder(layout);
CellConstraints cc = new CellConstraints();
String infoText = (String) getWizardContext().getAttribute(PROMPT_TEXT_ATTRIBUTE);
if (infoText == null) {
infoText = Translation.getTranslation("wizard.choose_disk_location.select");
}
int row = 2;
builder.addLabel(infoText, cc.xy(1, row));
row += 2;
builder.add(locationField, cc.xy(1, row));
row += 2;
builder.add(folderSizeLabel, cc.xy(1, row));
if (getController().getOSClient().isBackupByDefault()
&& PreferencesEntry.EXPERT_MODE.getValueBoolean(getController())) {
row += 2;
builder.add(backupByOnlineStorageBox, cc.xy(1, row));
}
// Send Invite
if (getController().isBackupOnly()
|| !ConfigurationEntry.SERVER_INVITE_ENABLED.getValueBoolean(getController())) {
sendInviteAfterCB.setSelected(false);
} else {
row += 2;
builder.add(sendInviteAfterCB, cc.xy(1, row));
}
return builder.getPanel();
}
/**
* Initializes all necessary components
*/
protected void initComponents() {
FolderInfo folderInfo = (FolderInfo) getWizardContext().getAttribute(
FOLDERINFO_ATTRIBUTE);
String dir;
if (folderInfo == null) {
dir = getController().getFolderRepository().getFoldersBasedirString();
} else {
Folder folder1 = folderInfo.getFolder(getController());
if (folder1 == null) {
dir = getController().getFolderRepository()
.getFoldersBasedirString();
} else {
dir = folder1.getLocalBase().getAbsolutePath();
}
}
locationModel = new ValueHolder(dir);
if (initialLocation != null) {
locationModel.setValue(initialLocation);
}
locationModel.addValueChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
updateLocationComponents();
updateButtons();
startFolderSizeCalculator();
}
});
locationField = createLocationField();
locationField.setBackground(Color.WHITE);
folderSizeLabel = new JLabel();
startFolderSizeCalculator();
// Online Storage integration
boolean backupByOS = getController().getOSClient().isBackupByDefault()
&& Boolean.TRUE.equals(getWizardContext().getAttribute(WizardContextAttributes.BACKUP_ONLINE_STOARGE));
backupByOnlineStorageBox = new JCheckBox(Translation.getTranslation(
"wizard.choose_disk_location.backup_by_online_storage"));
// Is backup suggested?
if (backupByOS) {
backupByOnlineStorageBox.setSelected(true);
}
backupByOnlineStorageBox.getModel().addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (backupByOnlineStorageBox.isSelected()) {
getController().getUIController().getApplicationModel()
.getServerClientModel().checkAndSetupAccount();
}
}
});
backupByOnlineStorageBox.setOpaque(false);
// Create manual sync cb
manualSyncCheckBox = new JCheckBox(Translation.getTranslation(
"wizard.choose_disk_location.maual_sync"));
manualSyncCheckBox.setOpaque(false);
// Send Invite
boolean sendInvite = Boolean.TRUE.equals(getWizardContext()
.getAttribute(SEND_INVIATION_AFTER_ATTRIBUTE));
sendInviteAfterCB = SimpleComponentFactory.createCheckBox(Translation
.getTranslation("wizard.choose_disk_location.send_invitation"));
sendInviteAfterCB.setOpaque(false);
sendInviteAfterCB.setSelected(sendInvite);
}
private void startFolderSizeCalculator() {
SwingWorker<Object, Object> worker = new MySwingWorker((String) locationModel.getValue());
worker.execute();
}
protected String getTitle() {
return Translation.getTranslation("wizard.choose_disk_location.title");
}
/**
* Try to create the original folder if it does not exists.
*/
protected void afterDisplay() {
Object value = locationModel.getValue();
if (value != null) {
if (value instanceof String) {
try {
File f = new File((String) value);
// Try to create the directory if it does not exist.
if (!f.exists()) {
if (f.mkdirs()) {
startFolderSizeCalculator();
}
}
// If dir does not exist or is not writable,
// give user a choice to relocate in an alternate location.
boolean ok = true;
if (!f.exists()) {
ok = false;
} else if (!f.canWrite()) {
ok = false;
}
if (!ok) {
String baseDir = getController().getFolderRepository()
.getFoldersBasedirString();
String name = f.getName();
if (name.length() == 0) { // Like f == 'E:/'
name = "new";
}
File alternate = new File(baseDir, name);
// Check alternate is unique.
int x = 1;
while (alternate.exists()) {
alternate = new File(baseDir, name + '-' + x++);
}
alternate.mkdirs();
startFolderSizeCalculator();
locationModel.setValue(alternate.getAbsolutePath());
}
} catch (Exception e) {
// Ignore
}
}
}
}
/**
* Called when the location model changes value. Sets the location text
* field value and enables the location button.
*/
private void updateLocationComponents() {
String value = (String) locationModel.getValue();
locationTF.setText(value);
}
/**
* Creates a pair of location text field and button.
*
* @return
*/
private JComponent createLocationField() {
FormLayout layout = new FormLayout("140dlu, 3dlu, pref", "pref");
PanelBuilder builder = new PanelBuilder(layout);
CellConstraints cc = new CellConstraints();
locationTF = new JTextField();
locationTF.setEditable(false);
locationTF.setText((String) locationModel.getValue());
builder.add(locationTF, cc.xy(1, 1));
JButtonMini locationButton = new JButtonMini(Icons.getIconById(Icons.DIRECTORY),
Translation.getTranslation("wizard.choose_disk_location.select_directory"));
locationButton.addActionListener(new MyActionListener());
builder.add(locationButton, cc.xy(3, 1));
JPanel panel = builder.getPanel();
panel.setOpaque(false);
return panel;
}
private void displayChooseDirectory() {
String initial = (String) locationModel.getValue();
List<File> files = DialogFactory.chooseDirectory(getController().getUIController(), initial, false);
if (!files.isEmpty()) {
File location = files.get(0);
if (!validDirectory(location)) {
return;
}
locationModel.setValue(location.getAbsolutePath());
}
}
private boolean validDirectory(File location) {
// Do not allow user to select folder base dir.
if (location.equals(getController().getFolderRepository().getFoldersBasedir())) {
String title = Translation.getTranslation("general.directory");
String message = Translation.getTranslation("general.basedir_error.text");
DialogFactory.genericDialog(getController(), title, message, GenericDialogType.ERROR);
return false;
}
// Don't allow non-user dir folders if not allowed.
if (ConfigurationEntry.FOLDER_CREATE_IN_BASEDIR_ONLY
.getValueBoolean(getController()))
{
if (!location.getParentFile().equals(
getController().getFolderRepository().getFoldersBasedir()))
{
String title = Translation.getTranslation("general.directory");
String message = Translation.getTranslation(
"general.outside_basedir_error.text", getController()
.getFolderRepository().getFoldersBasedirString());
DialogFactory.genericDialog(getController(), title, message,
GenericDialogType.ERROR);
return false;
}
}
return true;
}
/**
* Action listener for the location button. Opens a choose dir dialog and
* sets the location model with the result.
*/
private class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
displayChooseDirectory();
}
}
/**
* Worker to display info of the folder in the background.
*/
private class MySwingWorker extends SwingWorker<Object, Object> {
private final String initial;
private long directorySize;
private boolean nonExistent;
private boolean noWrite;
private boolean checkNoWrite = true;
private MySwingWorker(String initial) {
this.initial = initial;
}
protected Object doInBackground() throws Exception {
// Show something while working.
folderSizeLabel.setText(Translation.getTranslation("wizard.choose_disk_location.calculating_directory_size"));
folderSizeLabel.setForeground(SystemColor.textText);
try {
File f = new File(initial);
if (!f.exists()) {
nonExistent = true;
} else if (checkNoWrite && !canWriteDirectory(f)) {
noWrite = true;
} else {
directorySize = FileUtils.calculateDirectorySizeAndCount(f)[0];
}
} catch (Exception e) {
Logger.getAnonymousLogger().log(Level.WARNING, e.toString(), e);
}
return null;
}
@Override
protected void done() {
try {
// Has the value changed?
// In that case, another worker is going to update the value later.
if (initial.equals(locationModel.getValue())) {
if (nonExistent) {
folderSizeLabel.setText(Translation.getTranslation(
"wizard.choose_disk_location.directory_non_existent"));
folderSizeLabel.setForeground(Color.red);
} else if (noWrite) {
folderSizeLabel.setText(Translation.getTranslation(
"wizard.choose_disk_location.directory_no_write"));
folderSizeLabel.setForeground(Color.red);
} else {
folderSizeLabel.setText(Translation.getTranslation(
"wizard.choose_disk_location.directory_size",
Format.formatBytes(directorySize)));
folderSizeLabel.setForeground(SystemColor.textText);
}
}
} catch (Exception e) {
Logger.getAnonymousLogger().log(Level.WARNING, e.toString(), e);
}
}
private boolean canWriteDirectory(File dir) {
File testFile;
do {
testFile = new File(dir, FileUtils.removeInvalidFilenameChars(IdGenerator.makeId()));
} while (testFile.exists());
try {
testFile.createNewFile();
boolean canWrite = testFile.canWrite();
canWrite = canWrite && testFile.delete();
return canWrite;
} catch (IOException e) {
return false;
}
}
}
}