org.bidib.wizard.mvc.backup.view.BackupView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-client Show documentation
Show all versions of bidibwizard-client Show documentation
jBiDiB BiDiB Wizard Client Application POM
package org.bidib.wizard.mvc.backup.view;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.text.Document;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bidib.wizard.api.locale.Resources;
import org.bidib.wizard.client.common.table.AbstractEmptyTable;
import org.bidib.wizard.client.common.table.ProgressCellRenderer;
import org.bidib.wizard.client.common.text.WizardComponentFactory;
import org.bidib.wizard.client.common.view.DockKeys;
import org.bidib.wizard.client.common.view.statusbar.StatusBarPublisher;
import org.bidib.wizard.common.model.settings.WizardSettingsInterface;
import org.bidib.wizard.common.service.SettingsService;
import org.bidib.wizard.core.dialog.FileDialog;
import org.bidib.wizard.mvc.backup.controller.listener.BackupControllerListener;
import org.bidib.wizard.mvc.backup.model.BackupTableModel;
import org.bidib.wizard.mvc.backup.model.NodeBackupModel;
import org.bidib.wizard.mvc.common.view.text.ChangeDocumentListener;
import org.bidib.wizard.mvc.debug.view.FileStringConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jgoodies.binding.adapter.SingleListSelectionAdapter;
import com.jgoodies.binding.beans.PropertyAdapter;
import com.jgoodies.binding.beans.PropertyConnector;
import com.jgoodies.binding.list.SelectionInList;
import com.jgoodies.binding.value.ConverterValueModel;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.forms.builder.FormBuilder;
import com.jgoodies.forms.debug.FormDebugPanel;
import com.jgoodies.forms.factories.Paddings;
import com.jidesoft.grid.AutoFilterTableHeader;
import com.jidesoft.grid.CellRendererManager;
import com.jidesoft.grid.CellStyle;
import com.jidesoft.grid.CellStyleTableHeader;
import com.jidesoft.grid.FilterableTableModel;
import com.jidesoft.grid.HeaderStyleModel;
import com.jidesoft.grid.IFilterableTableModel;
import com.jidesoft.grid.JideTable;
import com.jidesoft.grid.NestedTableHeader;
import com.jidesoft.grid.SortableTableModel;
import com.jidesoft.grid.TableColumnChooserPopupMenuCustomizer;
import com.jidesoft.grid.TableHeaderPopupMenuInstaller;
import com.jidesoft.grid.TableModelWrapperUtils;
import com.jidesoft.icons.CheckBoxIcon;
import com.jidesoft.swing.DefaultOverlayable;
import com.jidesoft.swing.JideSwingUtilities;
import com.jidesoft.swing.StyledLabelBuilder;
import com.jidesoft.swing.TristateCheckBox;
import com.vlsolutions.swing.docking.DockKey;
import com.vlsolutions.swing.docking.Dockable;
public class BackupView implements Dockable {
private static final Logger LOGGER = LoggerFactory.getLogger(BackupView.class);
private static final String ENCODED_DIALOG_COLUMN_SPECS = "pref, 3dlu, fill:pref:grow, 3dlu, pref";
private static final String ENCODED_DIALOG_ROW_SPECS = "p, 3dlu, fill:50dlu:grow, 3dlu, p, 3dlu, p";
private final JPanel contentPanel;
private SelectionInList nodeBackupSelection;
private TableModelListener tableModelListener;
private ValueModel backupDirectoryValueModel;
private JTextField backupDirectoryText;
private final JButton selectDirectoryButton = new JButton(Resources.getString(getClass(), "select"));
private final JButton startBackupButton = new JButton(Resources.getString(getClass(), "startBackup"));
private final BackupTableModel backupTableModel;
private final SettingsService settingsService;
private final BackupControllerListener backupController;
private final StatusBarPublisher statusBarPublisher;
public BackupView(final BackupControllerListener backupController, final BackupTableModel backupTableModel,
final SettingsService settingsService, final StatusBarPublisher statusBarPublisher) {
this.backupController = backupController;
this.backupTableModel = backupTableModel;
this.settingsService = settingsService;
this.statusBarPublisher = statusBarPublisher;
DockKeys.DOCKKEY_BACKUP_VIEW.setName(Resources.getString(getClass(), "title"));
DockKeys.DOCKKEY_BACKUP_VIEW.setFloatEnabled(true);
DockKeys.DOCKKEY_BACKUP_VIEW.setAutoHideEnabled(false);
LOGGER.info("Create new BackupView");
final WizardSettingsInterface wizardSettings = this.settingsService.getWizardSettings();
String storedBackupDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_BACKUP_KEY);
if (StringUtils.isNotBlank(storedBackupDirectory)) {
try {
this.backupTableModel.setBackupDirectory(new File(storedBackupDirectory));
}
catch (Exception ex) {
LOGGER.warn("Set the backup directory in the model failed.", ex);
}
}
// create form builder
FormBuilder dialogBuilder = null;
boolean debugDialog = false;
if (debugDialog) {
JPanel panel = new FormDebugPanel();
dialogBuilder =
FormBuilder.create().columns(ENCODED_DIALOG_COLUMN_SPECS).rows(ENCODED_DIALOG_ROW_SPECS).panel(panel);
}
else {
JPanel panel = new JPanel(new BorderLayout());
dialogBuilder =
FormBuilder.create().columns(ENCODED_DIALOG_COLUMN_SPECS).rows(ENCODED_DIALOG_ROW_SPECS).panel(panel);
}
dialogBuilder.border(Paddings.DIALOG);
dialogBuilder.add(Resources.getString(getClass(), "select-nodes-to-backup")).xyw(1, 1, 3);
nodeBackupSelection =
new SelectionInList(
(ListModel) this.backupTableModel.getNodeBackupListModel());
final SortableTableModel tableModel = new SortableTableModel(new BackupTableTableModel(nodeBackupSelection));
// create a booster table
final AbstractEmptyTable backupTable =
new AbstractEmptyTable(tableModel, Resources.getString(getClass(), "empty_table")) {
private static final long serialVersionUID = 1L;
};
backupTable.adjustRowHeight();
backupTable.setSelectionModel(new SingleListSelectionAdapter(nodeBackupSelection.getSelectionIndexHolder()));
backupTable.setAutoResizeMode(JideTable.AUTO_RESIZE_FILL | JideTable.AUTO_RESIZE_LAST_COLUMN);
// backupTable.setFillsGrids(true);
CellRendererManager
.registerRenderer(Integer.class, new ProgressCellRenderer(true), ProgressCellRenderer.CONTEXT);
TableColumn tc = backupTable.getColumnModel().getColumn(BackupTableTableModel.COLUMN_SELECTED);
tc.setPreferredWidth(30);
tc.setMaxWidth(50);
tc = backupTable.getColumnModel().getColumn(BackupTableTableModel.COLUMN_UNIQUE_ID);
tc.setMinWidth(120);
tc.setMaxWidth(150);
tc.setPreferredWidth(150);
tc = backupTable.getColumnModel().getColumn(BackupTableTableModel.COLUMN_DESCRIPTION);
tc.setPreferredWidth(300);
tc = backupTable.getColumnModel().getColumn(BackupTableTableModel.COLUMN_PROGRESS);
this.tableModelListener = new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
int viewColumn = backupTable.convertColumnIndexToView(0);
if (viewColumn < 0) {
return;
}
boolean hasUnselected = false;
boolean hasSelected = false;
for (int row = 0; row < backupTable.getRowCount(); row++) {
Object value = backupTable.getValueAt(row, viewColumn);
if (value instanceof Boolean) {
if ((Boolean) value) {
hasSelected = true;
}
else {
hasUnselected = true;
}
if (hasSelected && hasUnselected) {
break;
}
}
}
int state;
if (hasSelected && hasUnselected) {
state = TristateCheckBox.STATE_MIXED;
}
else if (hasSelected) {
state = TristateCheckBox.STATE_SELECTED;
}
else {
state = TristateCheckBox.STATE_UNSELECTED;
}
final HeaderStyleModel headerStyleModel =
(HeaderStyleModel) TableModelWrapperUtils
.getActualTableModel(backupTable.getModel(), HeaderStyleModel.class);
CellStyle style = headerStyleModel.getHeaderStyleAt(0, 0);
if (style.getIcon() instanceof CheckBoxIcon) {
((CheckBoxIcon) style.getIcon()).setState(state);
backupTable.getTableHeader().repaint();
}
}
};
tableModel.addTableModelListener(tableModelListener);
final SortableTableModel sortableTableModel =
(SortableTableModel) TableModelWrapperUtils
.getActualTableModel(backupTable.getModel(), SortableTableModel.class);
sortableTableModel.setColumnSortable(0, false);
sortableTableModel.sortColumn(BackupTableTableModel.COLUMN_DESCRIPTION, true, true);
AutoFilterTableHeader header = new AutoFilterTableHeader(backupTable) {
private static final long serialVersionUID = 1L;
@Override
protected IFilterableTableModel createFilterableTableModel(TableModel model) {
return new FilterableTableModel(model) {
private static final long serialVersionUID = 1L;
@Override
public boolean isColumnAutoFilterable(int column) {
return column != 0;
}
};
}
};
header.setAutoFilterEnabled(true);
header.setUseNativeHeaderRenderer(true);
backupTable.setTableHeader(header);
JideSwingUtilities.insertMouseListener(header, new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getSource() instanceof CellStyleTableHeader) {
CellStyleTableHeader _header = (CellStyleTableHeader) e.getSource();
Point p = e.getPoint();
TableColumnModel columnModel = _header.getColumnModel();
int index = _header.originalColumnAtPoint(p);
if (_header.getTable() != null && index >= 0 && index < columnModel.getColumnCount()) {
TableModel actualTableModel =
TableModelWrapperUtils
.getActualTableModel(_header.getTable().getModel(), HeaderStyleModel.class);
if (actualTableModel instanceof HeaderStyleModel
&& ((HeaderStyleModel) actualTableModel).isHeaderStyleOn()) {
int actualColumnIndex =
TableModelWrapperUtils
.getActualColumnAt(_header.getTable().getModel(),
_header.getTable().convertColumnIndexToModel(index), actualTableModel);
int rowIndex =
_header instanceof NestedTableHeader ? ((NestedTableHeader) _header).getRowCount() - 1
: 0;
CellStyle style =
((HeaderStyleModel) actualTableModel).getHeaderStyleAt(rowIndex, actualColumnIndex);
Rectangle headerRect = _header.getHeaderRect(index);
Point centerPoint =
new Point(headerRect.x + headerRect.width / 2, headerRect.y + headerRect.height / 2);
if (style != null && style.getIcon() instanceof CheckBoxIcon
&& p.x >= centerPoint.x - style.getIcon().getIconWidth() / 2
&& p.x <= centerPoint.x + style.getIcon().getIconWidth() / 2
&& p.y >= centerPoint.y - style.getIcon().getIconHeight() / 2
&& p.y <= centerPoint.y + style.getIcon().getIconHeight() / 2) {
int state = ((CheckBoxIcon) style.getIcon()).getState();
if (state == TristateCheckBox.STATE_MIXED
|| state == TristateCheckBox.STATE_UNSELECTED) {
state = TristateCheckBox.STATE_SELECTED;
}
else if (state == TristateCheckBox.STATE_SELECTED) {
state = TristateCheckBox.STATE_UNSELECTED;
}
((CheckBoxIcon) style.getIcon()).setState(state);
tableModel.removeTableModelListener(tableModelListener);
for (int row = 0; row < tableModel.getRowCount(); row++) {
tableModel.setValueAt(state == TristateCheckBox.STATE_SELECTED, row, 0);
}
tableModel.addTableModelListener(tableModelListener);
e.consume();
}
}
}
}
}
}, 0);
TableHeaderPopupMenuInstaller installer = new TableHeaderPopupMenuInstaller(backupTable);
installer.addTableHeaderPopupMenuCustomizer(new TableColumnChooserPopupMenuCustomizer());
final DefaultOverlayable overlayTable = new DefaultOverlayable(new JScrollPane(backupTable));
tableModel.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
overlayTable.setOverlayVisible(tableModel.getRowCount() == 0);
}
});
overlayTable
.addOverlayComponent(
StyledLabelBuilder.createStyledLabel("{" + backupTable.getEmptyTableText() + ":f:gray}"));
dialogBuilder.add(overlayTable).xyw(1, 3, 5);
// create the textfield for backup directory
backupDirectoryValueModel =
new PropertyAdapter(this.backupTableModel, BackupTableModel.PROPERTY_BACKUP_DIRECTORY,
true);
final ValueModel backupDirectoryConverterModel =
new ConverterValueModel(backupDirectoryValueModel, new FileStringConverter());
backupDirectoryText = WizardComponentFactory.createTextField(backupDirectoryConverterModel, true);
backupDirectoryText.setEditable(false);
final DefaultOverlayable backupDirectoryOverlayable = new DefaultOverlayable(backupDirectoryText);
backupDirectoryOverlayable
.addOverlayComponent(
StyledLabelBuilder
.createStyledLabel("{" + Resources.getString(getClass(), "backupDirectory.prompt") + ":f:gray}"),
SwingConstants.WEST);
backupDirectoryOverlayable.setOverlayLocationInsets(new Insets(0, -5, 0, 5));
final Document backupDirectoryDoc = backupDirectoryText.getDocument();
backupDirectoryDoc
.addDocumentListener(
new ChangeDocumentListener(doc -> backupDirectoryOverlayable.setOverlayVisible(doc.getLength() < 1)));
backupDirectoryOverlayable.setOverlayVisible(backupDirectoryDoc.getLength() < 1);
dialogBuilder.add(Resources.getString(getClass(), "backupDirectory")).xy(1, 5);
dialogBuilder.add(backupDirectoryOverlayable).xy(3, 5);
dialogBuilder.add(selectDirectoryButton).xy(5, 5);
selectDirectoryButton.addActionListener(evt -> fireSelectDirectory());
selectDirectoryButton.setEnabled(true);
dialogBuilder.add(startBackupButton).xy(1, 7);
startBackupButton.addActionListener(evt -> fireStartBackup());
startBackupButton.setEnabled(false);
this.contentPanel = dialogBuilder.build();
PropertyConnector
.connect(this.backupTableModel, BackupTableModel.PROPERTY_BACKUP_ENABLED, startBackupButton, "enabled");
this.startBackupButton.setEnabled(this.backupTableModel.isBackupEnabled());
}
@Override
public DockKey getDockKey() {
return DockKeys.DOCKKEY_BACKUP_VIEW;
}
@Override
public Component getComponent() {
return contentPanel;
}
private static final String WORKING_DIR_BACKUP_KEY = "backup";
private void fireSelectDirectory() {
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
String storedBackupDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_BACKUP_KEY);
final FileFilter[] ff = null;
final FileDialog dialog = new FileDialog(contentPanel, FileDialog.SAVE, storedBackupDirectory, null, ff) {
@Override
public void approve(final String selectedFile) {
File file = new File(selectedFile);
if (file != null && file.isDirectory()) {
backupTableModel.setBackupDirectory(file);
final String backupDir = Paths.get(selectedFile).toString();
LOGGER.info("Save current backupDir: {}", backupDir);
wizardSettings.setWorkingDirectory(WORKING_DIR_BACKUP_KEY, backupDir);
}
}
@Override
protected boolean checkOverrideExisting(File file) {
// do not ask for override
return true;
}
};
dialog.showDialog();
}
private void fireStartBackup() {
// JOptionPane
// .showMessageDialog(this.contentPanel, "Under construction! No CV values loaded from node!",
// Resources.getString(getClass(), "title"), JOptionPane.WARNING_MESSAGE);
final List nodeBackupModels = new LinkedList<>();
// collect the selected nodes
for (NodeBackupModel nodeBackupModel : this.backupTableModel.getNodes()) {
if (nodeBackupModel.isSelected()) {
// perform backup
nodeBackupModels.add(nodeBackupModel);
}
// reset the progress on start
nodeBackupModel.setProgress(0);
}
if (CollectionUtils.isEmpty(nodeBackupModels)) {
JOptionPane
.showMessageDialog(this.contentPanel, Resources.getString(getClass(), "backup.no-nodes-selected"),
Resources.getString(getClass(), "title"), JOptionPane.WARNING_MESSAGE);
return;
}
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
final String storedBackupDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_BACKUP_KEY);
boolean nodeExportAppendDateEnabled = wizardSettings.isNodeExportAppendDateEnabled();
final File backupDir;
if (nodeExportAppendDateEnabled) {
// append the current date and time to the backup dir
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
String currentDateTime = now.format(formatter);
backupDir = new File(storedBackupDirectory, currentDateTime);
LOGGER.info("Use storedBackupDirectory with timestamp as backupDir: {}", backupDir);
}
else {
backupDir = new File(storedBackupDirectory);
LOGGER.info("Use storedBackupDirectory as backupDir: {}", backupDir);
}
final String storedBackupDirectoryWithTime = backupDir.getPath();
try {
FileUtils.forceMkdir(backupDir);
}
catch (IOException ex) {
LOGGER.warn("Create backup directory with date and time failed.", ex);
JOptionPane
.showMessageDialog(this.contentPanel,
Resources.getString(getClass(), "backup.create-backup-dir-failed", storedBackupDirectoryWithTime),
Resources.getString(getClass(), "title"), JOptionPane.WARNING_MESSAGE);
return;
}
this.statusBarPublisher
.publish(Resources.getString(getClass(), "backup.started", storedBackupDirectoryWithTime), null);
LOGGER.info("Perform the backup async.");
CompletableFuture