You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2018/08/08 04:35:39 UTC

[openmeetings] branch 4.0.x updated: [OPENMEETINGS-1919] drag and drop should work better

This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch 4.0.x
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/4.0.x by this push:
     new 4539a69  [OPENMEETINGS-1919] drag and drop should work better
4539a69 is described below

commit 4539a6949ed4b1f18ac4d75ebf5fb42f82393f86
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Wed Aug 8 11:35:29 2018 +0700

    [OPENMEETINGS-1919] drag and drop should work better
---
 .../web/common/tree/FileItemPanel.java             |   4 +-
 .../openmeetings/web/common/tree/FileItemTree.java | 162 +------------
 .../openmeetings/web/common/tree/FolderPanel.html  |   2 +-
 .../openmeetings/web/common/tree/FolderPanel.java  | 260 ++++++++++++++++-----
 openmeetings-web/src/main/webapp/css/raw-tree.css  |   5 +-
 5 files changed, 209 insertions(+), 224 deletions(-)

diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileItemPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileItemPanel.java
index 17ded5e..d930475 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileItemPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileItemPanel.java
@@ -34,7 +34,7 @@ public class FileItemPanel extends FolderPanel {
 	private static final long serialVersionUID = 1L;
 	private final WebMarkupContainer errors = new WebMarkupContainer("errors");
 
-	public FileItemPanel(String id, final IModel<? extends BaseFileItem> model, final FileTreePanel fileTreePanel) {
+	public FileItemPanel(String id, final IModel<BaseFileItem> model, final FileTreePanel fileTreePanel) {
 		super(id, model, fileTreePanel);
 		BaseFileItem f = model.getObject();
 		long errorCount = getBean(FileItemLogDao.class).countErrors(f);
@@ -54,6 +54,6 @@ public class FileItemPanel extends FolderPanel {
 				fileTreePanel.errorsDialog.open(target);
 			}
 		}).setVisible(visible);
-		drag.add(errors);
+		add(errors);
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileItemTree.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileItemTree.java
index 9cc8c73..52614d4 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileItemTree.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FileItemTree.java
@@ -18,33 +18,17 @@
  */
 package org.apache.openmeetings.web.common.tree;
 
-import java.util.Optional;
-
 import org.apache.openmeetings.db.entity.file.BaseFileItem;
 import org.apache.openmeetings.db.entity.file.BaseFileItem.Type;
-import org.apache.openmeetings.db.entity.record.Recording;
-import org.apache.openmeetings.db.entity.record.Recording.Status;
 import org.apache.wicket.Component;
-import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
-import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.extensions.markup.html.repeater.tree.DefaultNestedTree;
 import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
-import org.apache.wicket.extensions.markup.html.repeater.tree.content.Folder;
 import org.apache.wicket.markup.repeater.ReuseIfModelsEqualStrategy;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
-import org.apache.wicket.util.string.Strings;
-
-import com.github.openjson.JSONObject;
 
 public class FileItemTree extends DefaultNestedTree<BaseFileItem> {
 	private static final long serialVersionUID = 1L;
-	private static final String CSS_CLASS_FILE = "file ";
-	private static final String PARAM_MOD = "mod";
-	private static final String PARAM_SHIFT = "s";
-	private static final String PARAM_CTRL = "c";
 	final FileTreePanel treePanel;
 
 	public FileItemTree(String id, FileTreePanel treePanel, ITreeProvider<BaseFileItem> tp) {
@@ -53,26 +37,6 @@ public class FileItemTree extends DefaultNestedTree<BaseFileItem> {
 		setItemReuseStrategy(new ReuseIfModelsEqualStrategy());
 	}
 
-	private void onClick(AjaxRequestTarget target, BaseFileItem f) {
-		String mod = getRequest().getRequestParameters().getParameterValue(PARAM_MOD).toOptionalString();
-		boolean shift = false, ctrl = false;
-		if (!Strings.isEmpty(mod)) {
-			JSONObject o = new JSONObject(mod);
-			shift = o.optBoolean(PARAM_SHIFT);
-			ctrl = o.optBoolean(PARAM_CTRL);
-		}
-		treePanel.select(f, target, shift, ctrl);
-		if (Type.Folder == f.getType() && getState(f) == State.COLLAPSED) {
-			this.expand(f);
-		} else {
-			treePanel.update(target, f);
-		}
-	}
-
-	private static boolean isClickable() {
-		return true;
-	}
-
 	@Override
 	public OmTreeProvider getProvider() {
 		return (OmTreeProvider)super.getProvider();
@@ -87,126 +51,10 @@ public class FileItemTree extends DefaultNestedTree<BaseFileItem> {
 	}
 
 	@Override
-	protected Component newContentComponent(String id, IModel<BaseFileItem> node) {
-		return new Folder<BaseFileItem>(id, this, node) {
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			protected Component newLabelComponent(String id, final IModel<BaseFileItem> lm) {
-				BaseFileItem r = lm.getObject();
-				return Type.Folder == r.getType() || r.getId() == null
-						? new FolderPanel(id, lm, treePanel)
-						: new FileItemPanel(id, lm, treePanel);
-			}
-
-			@Override
-			protected boolean isSelected() {
-				return treePanel.isSelected(getModelObject());
-			}
-
-			@Override
-			protected boolean isClickable() {
-				return FileItemTree.isClickable();
-			}
-
-			@Override
-			protected void onClick(Optional<AjaxRequestTarget> targetOptional) {
-				FileItemTree.this.onClick(targetOptional.get(), getModelObject());
-			}
-
-			@Override
-			protected MarkupContainer newLinkComponent(String id, IModel<BaseFileItem> model) {
-				final BaseFileItem f = getModelObject();
-				return new AjaxLink<Void>(id) {
-					private static final long serialVersionUID = 1L;
-
-					@Override
-					public boolean isEnabled() {
-						return FileItemTree.isClickable();
-					}
-
-					@Override
-					public void onClick(AjaxRequestTarget target) {
-						FileItemTree.this.onClick(target, f);
-					}
-
-					@Override
-					protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
-						super.updateAjaxAttributes(attributes);
-						attributes.getDynamicExtraParameters().add(
-								String.format("return {%s: JSON.stringify({%s: attrs.event.shiftKey, %s: attrs.event.ctrlKey})};"
-										, PARAM_MOD, PARAM_SHIFT, PARAM_CTRL));
-					}
-				};
-			}
-
-			@Override
-			protected String getOtherStyleClass(BaseFileItem r) {
-				return getItemStyle(r, false);
-			}
-
-			@Override
-			protected String getOpenStyleClass() {
-				return getItemStyle(getModelObject(), true);
-			}
-
-			@Override
-			protected String getClosedStyleClass() {
-				return getItemStyle(getModelObject(), false);
-			}
-
-			@Override
-			protected String getSelectedStyleClass() {
-				return "ui-state-active";
-			}
-
-			@Override
-			protected IModel<String> newLabelModel(IModel<BaseFileItem> model) {
-				return Model.of(model.getObject().getName());
-			}
-		};
-	}
-
-	private static String getItemStyle(BaseFileItem f, boolean open) {
-		StringBuilder style = new StringBuilder("big om-icon ");
-		if (f.getId() == null) {
-			style.append(CSS_CLASS_FILE).append(f.getHash().indexOf("my") > -1 ? "my" : "public");
-		} else {
-			if (!f.exists()) {
-				style.append("broken ");
-			}
-			switch(f.getType()) {
-				case Folder:
-					style.append(CSS_CLASS_FILE).append(open ? "folder-open " : "folder ");
-					break;
-				case Image:
-					style.append(CSS_CLASS_FILE).append("image ");
-					break;
-				case PollChart:
-					style.append(CSS_CLASS_FILE).append("chart ");
-					break;
-				case WmlFile:
-					style.append(CSS_CLASS_FILE).append("wml ");
-					break;
-				case Video:
-				case Recording:
-				{
-					style.append("recording ");
-					if (f instanceof Recording) {
-						Status st = ((Recording)f).getStatus();
-						if (Status.RECORDING == st || Status.CONVERTING == st) {
-							style.append("processing");
-						}
-					}
-				}
-					break;
-				case Presentation:
-					style.append(CSS_CLASS_FILE).append("doc ");
-					break;
-				default:
-					break;
-			}
-		}
-		return style.toString();
+	protected Component newContentComponent(String id, IModel<BaseFileItem> lm) {
+		BaseFileItem r = lm.getObject();
+		return Type.Folder == r.getType() || r.getId() == null
+				? new FolderPanel(id, lm, treePanel)
+				: new FileItemPanel(id, lm, treePanel);
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FolderPanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FolderPanel.html
index 8dba394..1b8b067 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FolderPanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FolderPanel.html
@@ -20,6 +20,6 @@
 -->
 <html xmlns:wicket="http://wicket.apache.org">
 <wicket:panel>
-	<div wicket:id="drop" class="file item"><div wicket:id="drag"><wicket:child /><span wicket:id="name" class="name"></span></div></div>
+	<div class="file item"><wicket:child /><span wicket:id="name" class="name"></span></div>
 </wicket:panel>
 </html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FolderPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FolderPanel.java
index 0bda5a4..a5ace9e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FolderPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/tree/FolderPanel.java
@@ -30,76 +30,71 @@ import org.apache.openmeetings.db.entity.file.BaseFileItem;
 import org.apache.openmeetings.db.entity.file.BaseFileItem.Type;
 import org.apache.openmeetings.db.entity.file.FileItem;
 import org.apache.openmeetings.db.entity.record.Recording;
+import org.apache.openmeetings.db.entity.record.Recording.Status;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
-import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.ajax.AjaxEventBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
+import org.apache.wicket.behavior.Behavior;
 import org.apache.wicket.extensions.ajax.markup.html.AjaxEditableLabel;
-import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree.State;
+import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
+import org.apache.wicket.util.string.Strings;
 
-import com.googlecode.wicket.jquery.core.JQueryBehavior;
+import com.github.openjson.JSONObject;
+import com.googlecode.wicket.jquery.core.IJQueryWidget.JQueryWidget;
 import com.googlecode.wicket.jquery.core.Options;
-import com.googlecode.wicket.jquery.ui.interaction.draggable.Draggable;
-import com.googlecode.wicket.jquery.ui.interaction.droppable.Droppable;
+import com.googlecode.wicket.jquery.ui.interaction.draggable.DraggableBehavior;
+import com.googlecode.wicket.jquery.ui.interaction.draggable.IDraggableListener;
+import com.googlecode.wicket.jquery.ui.interaction.droppable.DroppableBehavior;
+import com.googlecode.wicket.jquery.ui.interaction.droppable.IDroppableListener;
 
-public class FolderPanel extends Panel {
+public class FolderPanel extends Panel implements IDraggableListener, IDroppableListener {
 	private static final long serialVersionUID = 1L;
-	protected final MarkupContainer drop;
-	protected final MarkupContainer drag;
+	private static final String CSS_CLASS_FILE = "file ";
+	private static final String PARAM_MOD = "mod";
+	private static final String PARAM_SHIFT = "s";
+	private static final String PARAM_CTRL = "c";
+	private final StyleBehavior styleClass;
+	private final FileTreePanel treePanel;
 
-	public FolderPanel(String id, final IModel<? extends BaseFileItem> model, final FileTreePanel treePanel) {
+	public FolderPanel(String id, final IModel<BaseFileItem> model, final FileTreePanel treePanel) {
 		super(id, model);
-		BaseFileItem r = model.getObject();
-		boolean editable = !treePanel.isReadOnly() && !r.isReadOnly();
-		drop = r.getType() == Type.Folder && editable ? new Droppable<BaseFileItem>("drop", Model.of(r)) {
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			public void onConfigure(JQueryBehavior behavior) {
-				super.onConfigure(behavior);
-				behavior.setOption("hoverClass", Options.asString("ui-state-hover"));
-				behavior.setOption("accept", Options.asString(getDefaultModelObject() instanceof Recording ? ".recorditem" : ".fileitem"));
-			}
+		this.treePanel = treePanel;
+		styleClass = new StyleBehavior();
+	}
 
-			@Override
-			public void onDrop(AjaxRequestTarget target, Component component) {
-				Object o = component.getDefaultModelObject();
-				if (o instanceof BaseFileItem) {
-					BaseFileItem p = (BaseFileItem)drop.getDefaultModelObject();
-					BaseFileItem f = (BaseFileItem)o;
-					if (treePanel.isSelected(f)) {
-						moveAll(treePanel, target, p);
-					} else {
-						move(treePanel, target, p, f);
-					}
-					treePanel.updateNode(target, p);
-				}
-				target.add(treePanel.trees);
-			}
-		} : new WebMarkupContainer("drop");
-		if (r.getId() == null || treePanel.isReadOnly()) {
-			drag = new WebMarkupContainer("drag");
-		} else {
-			drag = new Draggable<BaseFileItem>("drag", Model.of(r)) {
-				private static final long serialVersionUID = 1L;
-
-				@Override
-				public void onConfigure(JQueryBehavior behavior) {
-					super.onConfigure(behavior);
-					behavior.setOption("revert", "treeRevert");
-					behavior.setOption("cursor", Options.asString("move"));
-					behavior.setOption("helper", "dragHelper");
-					behavior.setOption("cursorAt", "{left: 40, top: 18}");
-				}
-			}.setContainment(treePanel.getContainment());
-			String cls = r instanceof Recording ? "recorditem" : "fileitem";
-			drag.add(AttributeModifier.append(ATTR_CLASS, r.isReadOnly() ? "readonlyitem" : cls));
+	@Override
+	protected void onInitialize() {
+		super.onInitialize();
+		final BaseFileItem f = (BaseFileItem)getDefaultModelObject();
+		boolean editable = !treePanel.isReadOnly() && !f.isReadOnly();
+		final String selector = JQueryWidget.getSelector(this);
+		if (f.getType() == Type.Folder && editable) {
+			add(new DroppableBehavior(
+					selector
+					, new Options()
+						.set("hoverClass", Options.asString("ui-state-hover"))
+						.set("accept", Options.asString(getDefaultModelObject() instanceof Recording ? ".recorditem" : ".fileitem"))
+					, this));
 		}
-		Component name = r.getId() == null || !editable ? new Label("name", r.getName()) : new AjaxEditableLabel<String>("name", Model.of(model.getObject().getName())) {
+		if (f.getId() != null && !treePanel.isReadOnly()) {
+			add(new DraggableBehavior(
+					selector
+					, new Options()
+						.set("revert", "treeRevert")
+						.set("cursor", Options.asString("move"))
+						.set("helper", "dragHelper")
+						.set("cursorAt", "{left: 40, top: 18}")
+						.set("containment", Options.asString(treePanel.getContainment()))
+					, this));
+		}
+		Component name = f.getId() == null || !editable ? new Label("name", f.getName()) : new AjaxEditableLabel<String>("name", Model.of(f.getName())) {
 			private static final long serialVersionUID = 1L;
 
 			@Override
@@ -110,18 +105,33 @@ public class FolderPanel extends Panel {
 			@Override
 			protected void onSubmit(AjaxRequestTarget target) {
 				super.onSubmit(target);
-				BaseFileItem fi = model.getObject();
-				fi.setName(getEditor().getModelObject());
-				if (fi instanceof Recording) {
-					getBean(RecordingDao.class).update((Recording)fi);
+				f.setName(getEditor().getModelObject());
+				if (f instanceof Recording) {
+					getBean(RecordingDao.class).update((Recording)f);
 				} else {
-					getBean(FileItemDao.class).update((FileItem)fi);
+					getBean(FileItemDao.class).update((FileItem)f);
 				}
 			}
 		};
-		drag.add(name);
-		add(drop.add(drag).setOutputMarkupId(true));
-		add(AttributeModifier.append(ATTR_TITLE, r.getName()));
+		add(name);
+		add(AttributeModifier.append(ATTR_TITLE, f.getName()));
+		add(styleClass);
+		add(new AjaxEventBehavior("click") {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void onEvent(AjaxRequestTarget target) {
+				onClick(target, f);
+			}
+
+			@Override
+			protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
+				super.updateAjaxAttributes(attributes);
+				attributes.getDynamicExtraParameters().add(
+						String.format("return {%s: JSON.stringify({%s: attrs.event.shiftKey, %s: attrs.event.ctrlKey})};"
+								, PARAM_MOD, PARAM_SHIFT, PARAM_CTRL));
+			}
+		});
 	}
 
 	private static void moveAll(final FileTreePanel treePanel, AjaxRequestTarget target, BaseFileItem p) {
@@ -146,4 +156,130 @@ public class FolderPanel extends Panel {
 		}
 		treePanel.updateNode(target, f);
 	}
+
+	private void onClick(AjaxRequestTarget target, BaseFileItem f) {
+		String mod = getRequest().getRequestParameters().getParameterValue(PARAM_MOD).toOptionalString();
+		boolean shift = false, ctrl = false;
+		if (!Strings.isEmpty(mod)) {
+			JSONObject o = new JSONObject(mod);
+			shift = o.optBoolean(PARAM_SHIFT);
+			ctrl = o.optBoolean(PARAM_CTRL);
+		}
+		treePanel.select(f, target, shift, ctrl);
+		if (Type.Folder == f.getType() && treePanel.tree.getState(f) == State.COLLAPSED) {
+			treePanel.tree.expand(f);
+		} else {
+			treePanel.update(target, f);
+		}
+	}
+
+	private CharSequence getItemStyle() {
+		final BaseFileItem f = (BaseFileItem)getDefaultModelObject();
+		boolean open = State.EXPANDED == treePanel.tree.getState(f);
+		StringBuilder style = new StringBuilder("big om-icon ");
+		if (f.getId() == null) {
+			style.append(CSS_CLASS_FILE).append(f.getHash().indexOf("my") > -1 ? "my " : "public ");
+		} else {
+			if (!f.exists()) {
+				style.append("broken ");
+			}
+			switch(f.getType()) {
+				case Folder:
+					style.append(CSS_CLASS_FILE).append(open ? "folder-open " : "folder ");
+					break;
+				case Image:
+					style.append(CSS_CLASS_FILE).append("image ");
+					break;
+				case PollChart:
+					style.append(CSS_CLASS_FILE).append("chart ");
+					break;
+				case WmlFile:
+					style.append(CSS_CLASS_FILE).append("wml ");
+					break;
+				case Video:
+				case Recording:
+				{
+					style.append("recording ");
+					if (f instanceof Recording) {
+						Status st = ((Recording)f).getStatus();
+						if (Status.RECORDING == st || Status.CONVERTING == st) {
+							style.append("processing");
+						}
+					}
+				}
+					break;
+				case Presentation:
+					style.append(CSS_CLASS_FILE).append("doc ");
+					break;
+				default:
+					break;
+			}
+		}
+		if (treePanel.isSelected(f)) {
+			style.append("ui-state-active ");
+		}
+		String cls = f instanceof Recording ? "recorditem " : "fileitem ";
+		style.append(f.isReadOnly() ? "readonlyitem " : cls);
+		return style;
+	}
+
+	@Override
+	public boolean isStopEventEnabled() {
+		return false;
+	}
+
+	@Override
+	public void onDragStart(AjaxRequestTarget target, int top, int left) {
+		// noop
+	}
+
+	@Override
+	public void onDragStop(AjaxRequestTarget target, int top, int left) {
+		// noop
+	}
+
+	@Override
+	public boolean isOverEventEnabled() {
+		return false;
+	}
+
+	@Override
+	public boolean isExitEventEnabled() {
+		return false;
+	}
+
+	@Override
+	public void onOver(AjaxRequestTarget target, Component component) {
+		// noop
+	}
+
+	@Override
+	public void onExit(AjaxRequestTarget target, Component component) {
+		// noop
+	}
+
+	@Override
+	public void onDrop(AjaxRequestTarget target, Component component) {
+		Object o = component.getDefaultModelObject();
+		if (o instanceof BaseFileItem) {
+			BaseFileItem p = (BaseFileItem)getDefaultModelObject();
+			BaseFileItem f = (BaseFileItem)o;
+			if (treePanel.isSelected(f)) {
+				moveAll(treePanel, target, p);
+			} else {
+				move(treePanel, target, p, f);
+			}
+			treePanel.updateNode(target, p);
+		}
+		target.add(treePanel.trees);
+	}
+
+	private class StyleBehavior extends Behavior {
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public void onComponentTag(Component component, ComponentTag tag) {
+			tag.put(ATTR_CLASS, getItemStyle());
+		}
+	}
 }
diff --git a/openmeetings-web/src/main/webapp/css/raw-tree.css b/openmeetings-web/src/main/webapp/css/raw-tree.css
index beb2b1b..220c174 100644
--- a/openmeetings-web/src/main/webapp/css/raw-tree.css
+++ b/openmeetings-web/src/main/webapp/css/raw-tree.css
@@ -47,10 +47,11 @@
 	padding-right: 5px;
 	padding-left: 5px;
 }
-.trees a.om-icon {
+.trees .om-icon {
 	width: auto;
+	margin-top: 0;
 }
-.trees a.om-icon span {
+.trees .om-icon span {
 	white-space: nowrap;
 	padding-right: 20px;
 	vertical-align: top;