package org.ovirt.engine.ui.common.widget.uicommon.popup.vm;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.ovirt.engine.core.common.businessentities.Snapshot;
import org.ovirt.engine.core.common.businessentities.comparators.DiskByDiskAliasComparator;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.ImageStatus;
import org.ovirt.engine.ui.common.CommonApplicationConstants;
import org.ovirt.engine.ui.common.CommonApplicationResources;
import org.ovirt.engine.ui.common.CommonApplicationTemplates;
import org.ovirt.engine.ui.common.editor.UiCommonEditorDriver;
import org.ovirt.engine.ui.common.gin.AssetProvider;
import org.ovirt.engine.ui.common.idhandler.ElementIdHandler;
import org.ovirt.engine.ui.common.widget.editor.EntityModelCellTable;
import org.ovirt.engine.ui.common.widget.table.cell.RadioboxCell;
import org.ovirt.engine.ui.common.widget.table.column.AbstractCheckboxColumn;
import org.ovirt.engine.ui.common.widget.table.column.AbstractFullDateTimeColumn;
import org.ovirt.engine.ui.common.widget.table.column.AbstractTextColumn;
import org.ovirt.engine.ui.common.widget.table.header.ImageResourceHeader;
import org.ovirt.engine.ui.common.widget.table.header.SafeHtmlHeader;
import org.ovirt.engine.ui.common.widget.uicommon.popup.AbstractModelBoundPopupWidget;
import org.ovirt.engine.ui.common.widget.uicommon.vm.VmSnapshotInfoPanel;
import org.ovirt.engine.ui.uicommonweb.models.EntityModel;
import org.ovirt.engine.ui.uicommonweb.models.ListModel;
import org.ovirt.engine.ui.uicommonweb.models.vms.PreviewSnapshotModel;
import org.ovirt.engine.ui.uicommonweb.models.vms.SnapshotModel;
import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.SplitLayoutPanel;
import com.google.gwt.view.client.CellPreviewEvent;
import com.google.gwt.view.client.NoSelectionModel;
public class VmSnapshotCustomPreviewPopupWidget extends AbstractModelBoundPopupWidget<PreviewSnapshotModel> {
interface Driver extends UiCommonEditorDriver<PreviewSnapshotModel, VmSnapshotCustomPreviewPopupWidget> {
}
interface ViewUiBinder extends UiBinder<SplitLayoutPanel, VmSnapshotCustomPreviewPopupWidget> {
ViewUiBinder uiBinder = GWT.create(ViewUiBinder.class);
}
interface ViewIdHandler extends ElementIdHandler<VmSnapshotCustomPreviewPopupWidget> {
ViewIdHandler idHandler = GWT.create(ViewIdHandler.class);
}
@UiField
@Ignore
Label previewTableLabel;
@UiField(provided = true)
@Ignore
EntityModelCellTable<ListModel<SnapshotModel>> previewTable;
@UiField(provided = true)
SplitLayoutPanel splitLayoutPanel;
@UiField
SimplePanel snapshotInfoContainer;
@UiField
FlowPanel warningPanel;
private PreviewSnapshotModel previewSnapshotModel;
private VmSnapshotInfoPanel vmSnapshotInfoPanel;
private static final CommonApplicationTemplates templates = AssetProvider.getTemplates();
private static final CommonApplicationResources resources = AssetProvider.getResources();
private static final CommonApplicationConstants constants = AssetProvider.getConstants();
private final Driver driver = GWT.create(Driver.class);
public VmSnapshotCustomPreviewPopupWidget() {
initTables();
initWidget(ViewUiBinder.uiBinder.createAndBindUi(this));
localize();
ViewIdHandler.idHandler.generateAndSetIds(this);
driver.initialize(this);
}
private void initTables() {
// Create custom preview table
previewTable = new EntityModelCellTable<>(false, true);
previewTable.enableColumnResizing();
// Create Snapshot information tab panel
vmSnapshotInfoPanel = new VmSnapshotInfoPanel();
// Create split layout panel
splitLayoutPanel = new SplitLayoutPanel(4);
}
private void createPreviewTable() {
previewTable.addColumn(new AbstractFullDateTimeColumn<SnapshotModel>() {
@Override
protected Date getRawValue(SnapshotModel snapshotModel) {
return snapshotModel.getEntity().getCreationDate();
}
}, constants.dateSnapshot(), "140px"); //$NON-NLS-1$
previewTable.addColumn(new AbstractTextColumn<SnapshotModel>() {
@Override
public String getValue(SnapshotModel snapshotModel) {
return snapshotModel.getEntity().getDescription();
}
}, constants.descriptionSnapshot(), "100px"); //$NON-NLS-1$
previewTable.setSelectionModel(new NoSelectionModel());
Column<SnapshotModel, Boolean> vmConfColumn = new Column<SnapshotModel, Boolean>(new RadioboxCell()) {
@Override
public Boolean getValue(SnapshotModel model) {
Snapshot snapshotVmConf = model.getEntity();
Snapshot toPreviewVmConf = previewSnapshotModel.getSnapshotModel().getEntity();
if (snapshotVmConf == null && toPreviewVmConf == null) {
return true;
}
return snapshotVmConf != null && snapshotVmConf.equals(toPreviewVmConf);
}
@Override
public void render(Context context, SnapshotModel snapshotModel, SafeHtmlBuilder sb) {
if (!snapshotModel.getEntity().isVmConfigurationBroken()) {
super.render(context, snapshotModel, sb);
}
else {
sb.appendEscaped(constants.notAvailableLabel());
}
}
};
vmConfColumn.setFieldUpdater((index, snapshotModel, value) -> {
previewSnapshotModel.setSnapshotModel(snapshotModel);
previewSnapshotModel.clearMemorySelection();
updateWarnings();
refreshTable(previewTable);
if (snapshotModel.getVm() == null) {
snapshotModel.updateVmConfiguration(returnValue -> updateInfoPanel());
}
else {
updateInfoPanel();
}
});
previewTable.addColumn(vmConfColumn,
new ImageResourceHeader(resources.vmConfIcon(), SafeHtmlUtils.fromTrustedString(constants.vmConfiguration())),
"30px"); //$NON-NLS-1$
AbstractCheckboxColumn<SnapshotModel> memoryColumn = new AbstractCheckboxColumn<SnapshotModel>(
(index, snapshotModel, value) -> {
previewSnapshotModel.getSnapshotModel().getMemory().setEntity(value);
refreshTable(previewTable);
updateWarnings();
}) {
@Override
public Boolean getValue(SnapshotModel snapshotModel) {
return snapshotModel.getMemory().getEntity();
}
@Override
protected boolean canEdit(SnapshotModel snapshotModel) {
boolean containsMemory = !snapshotModel.getEntity().getMemoryVolume().isEmpty();
SnapshotModel selectedSnapshotModel = previewSnapshotModel.getSnapshotModel();
return containsMemory && snapshotModel == selectedSnapshotModel;
}
@Override
public void render(Context context, SnapshotModel snapshotModel, SafeHtmlBuilder sb) {
if (!snapshotModel.getEntity().getMemoryVolume().isEmpty()) {
super.render(context, snapshotModel, sb);
}
else {
sb.appendEscaped(constants.notAvailableLabel());
}
}
};
previewTable.addColumn(
memoryColumn,
templates.iconWithText(imageResourceToSafeHtml(resources.memorySmallIcon()), constants.memorySnapshot()),
"100px"); //$NON-NLS-1$
List<DiskImage> disks = previewSnapshotModel.getAllDisks();
Collections.sort(disks, new DiskByDiskAliasComparator());
for (final DiskImage disk : disks) {
previewTable.addColumn(new AbstractCheckboxColumn<SnapshotModel>((index, snapshotModel, value) -> {
ListModel diskListModel = previewSnapshotModel.getDiskSnapshotsMap().get(disk.getId());
DiskImage image = snapshotModel.getImageByDiskId(disk.getId());
diskListModel.setSelectedItem(Boolean.TRUE.equals(value) ? image : null);
refreshTable(previewTable);
updateWarnings();
updateInfoPanel();
}) {
@Override
public Boolean getValue(SnapshotModel snapshotModel) {
ListModel diskListModel = previewSnapshotModel.getDiskSnapshotsMap().get(disk.getId());
DiskImage image = snapshotModel.getImageByDiskId(disk.getId());
return image != null ? image.equals(diskListModel.getSelectedItem()) : false;
}
@Override
protected boolean canEdit(SnapshotModel model) {
return true;
}
@Override
public void render(Context context, SnapshotModel snapshotModel, SafeHtmlBuilder sb) {
DiskImage image = snapshotModel.getImageByDiskId(disk.getId());
if (image == null) {
sb.appendEscaped(constants.notAvailableLabel());
}
else if (image.getImageStatus() == ImageStatus.ILLEGAL) {
sb.append(templates.text(constants.notAvailableLabel()));
}
else {
super.render(context, snapshotModel, sb);
}
}
@Override
public SafeHtml getTooltip(SnapshotModel model) {
if (disk != null && disk.getId() != null) {
DiskImage image = model.getImageByDiskId(disk.getId());
if (image != null && image.getImageStatus() == ImageStatus.ILLEGAL) {
return SafeHtmlUtils.fromSafeConstant(constants.illegalStatus());
}
}
return null;
}
},
new SafeHtmlHeader(templates.iconWithText(imageResourceToSafeHtml(resources.diskIcon()), disk.getDiskAlias()),
SafeHtmlUtils.fromString(disk.getId().toString())),
"120px"); //$NON-NLS-1$
// Edit preview table
previewTable.asEditor().edit(previewSnapshotModel.getSnapshots());
}
previewTable.addCellPreviewHandler(new CellPreviewEvent.Handler<EntityModel>() {
long lastClick = -1000;
@Override
public void onCellPreview(CellPreviewEvent<EntityModel> event) {
NativeEvent nativeEvent = event.getNativeEvent();
long clickAt = System.currentTimeMillis();
if (BrowserEvents.CLICK.equals(nativeEvent.getType())) {
if (clickAt - lastClick < 300) { // double click: 2 clicks detected within 300 ms
SnapshotModel selectedSnapshotModel = (SnapshotModel) event.getValue();
if (!selectedSnapshotModel.getEntity().isVmConfigurationBroken()) {
previewSnapshotModel.clearSelection();
previewSnapshotModel.selectSnapshot(selectedSnapshotModel.getEntity().getId());
updateWarnings();
refreshTable(previewTable);
}
}
lastClick = System.currentTimeMillis();
}
}
});
}
private void refreshTable(EntityModelCellTable table) {
table.asEditor().edit(table.asEditor().flush());
table.redraw();
}
private void updateWarnings() {
List<DiskImage> selectedDisks = previewSnapshotModel.getSelectedDisks();
List<DiskImage> disksOfSelectedSnapshot = previewSnapshotModel.getSnapshotModel().getEntity().getDiskImages();
List<DiskImage> disksOfActiveSnapshot;
if (previewSnapshotModel.getActiveSnapshotModel() != null) {
disksOfActiveSnapshot = previewSnapshotModel.getActiveSnapshotModel().getEntity().getDiskImages();
} else {
disksOfActiveSnapshot = Collections.emptyList();
}
SnapshotModel selectedModel = previewSnapshotModel.getSnapshotModel();
boolean includeAllDisksOfSnapshot = selectedDisks.containsAll(disksOfSelectedSnapshot);
boolean includeMemory = selectedModel.getMemory().getEntity();
if (includeMemory) {
selectedModel.setOldClusterVersionOfSnapshotWithMemory();
}
SafeHtml warningImage = SafeHtmlUtils.fromTrustedString(AbstractImagePrototype.create(
resources.logWarningImage()).getHTML());
String warningText = ""; //$NON-NLS-1$
if (selectedModel.getOldClusterVersionOfSnapshotWithMemory() != null) {
// Show warning when snapshot with memory originates in different cluster version
warningText += constants.snapshotPreviewWithMemoryFromDifferentClusterVersion();
}
if (!includeAllDisksOfSnapshot && includeMemory) {
// Show warning in case of previewing a memory snapshot and excluding disks of the selected snapshot.
warningText += constants.snapshotPreviewWithMemoryAndPartialDisksWarning();
} else if (isDisksExcluded(disksOfActiveSnapshot, selectedDisks)) {
// Show warning when excluding disks.
warningText += constants.snapshotPreviewWithExcludedDisksWarning();
}
warningPanel.clear();
if (!warningText.isEmpty()) {
warningPanel.add(new HTML(templates.iconWithText(warningImage, warningText)));
warningPanel.setVisible(true);
} else {
warningPanel.setVisible(false);
}
}
// Search disks by ID (i.e. for each image, determines whether any image from the image-group is selected)
private boolean isDisksExcluded(List<DiskImage> disks, List<DiskImage> selectedDisks) {
for (DiskImage disk : disks) {
if (!containsDisk(disk, selectedDisks)) {
return true;
}
}
return false;
}
// Check whether the specified disk list contains a disk by its ID (image-group)
private boolean containsDisk(DiskImage snapshotDisk, List<DiskImage> disks) {
for (DiskImage disk : disks) {
if (disk.getId().equals(snapshotDisk.getId())) {
return true;
}
}
return false;
}
private void updateInfoPanel() {
ArrayList<DiskImage> selectedImages = (ArrayList<DiskImage>) previewSnapshotModel.getSelectedDisks();
Collections.sort(selectedImages, new DiskByDiskAliasComparator());
SnapshotModel snapshotModel = previewSnapshotModel.getSnapshotModel();
snapshotModel.setDisks(selectedImages);
vmSnapshotInfoPanel.updateTabsData(snapshotModel);
}
void localize() {
previewTableLabel.setText(constants.customPreviewSnapshotTableTitle());
}
@Override
public void edit(PreviewSnapshotModel model) {
driver.edit(model);
previewSnapshotModel = model;
snapshotInfoContainer.add(vmSnapshotInfoPanel);
previewTable.asEditor().edit(previewSnapshotModel.getSnapshots());
// Add selection listener
model.getSnapshots().getSelectedItemChangedEvent().addListener((ev, sender, args) -> {
ListModel snapshots = (ListModel) sender;
SnapshotModel snapshotModel = (SnapshotModel) snapshots.getSelectedItem();
if (snapshotModel != null) {
vmSnapshotInfoPanel.updatePanel(snapshotModel);
}
});
model.getSnapshots().getItemsChangedEvent().addListener((ev, sender, args) -> createPreviewTable());
}
@Override
public PreviewSnapshotModel flush() {
previewTable.flush();
return driver.flush();
}
@Override
public void cleanup() {
driver.cleanup();
}
private SafeHtml imageResourceToSafeHtml(ImageResource resource) {
return SafeHtmlUtils.fromTrustedString(AbstractImagePrototype.create(resource).getHTML());
}
}