2021SC@SDUSC
软件工程应用与实践——OpenMeetings项目分析(八):
本周,继续分析common包下tree包的内容。
FileTreePanel:
public abstract class FileTreePanel extends Panel {
private static final long serialVersionUID = 1L;
private static final String ALIGN_LEFT_CLASS = " align-left";
private static final String ALIGN_RIGHT_CLASS = " align-right";
private static final String BASE_CLASS = " om-icon big clickable";
private static final String UPLOAD_CLASS = "add" + BASE_CLASS + ALIGN_LEFT_CLASS;
private static final String CREATE_DIR_CLASS = "folder-create" + BASE_CLASS + ALIGN_LEFT_CLASS;
private static final String TRASH_CLASS = "trash" + BASE_CLASS + ALIGN_RIGHT_CLASS;
private static final String DISABLED_CLASS = " disabled";
final WebMarkupContainer trees = new WebMarkupContainer("tree-container");
private final WebMarkupContainer sizes = new WebMarkupContainer("sizes");
private BaseFileItem lastSelected = null;
private Map<String, BaseFileItem> selected = new HashMap<>();
File dwnldFile;
final AjaxDownloadBehavior downloader = new AjaxDownloadBehavior(new IResource() {
private static final long serialVersionUID = 1L;
@Override
public void respond(Attributes attributes) {
new FileSystemResource(dwnldFile.toPath()) {
private static final long serialVersionUID = 1L;
@Override
protected ResourceResponse createResourceResponse(Attributes attr, Path path) {
ResourceResponse response = super.createResourceResponse(attr, path);
response.setCacheDuration(NONE);
return response;
}
}.respond(attributes);
}
});
protected final IModel<String> homeSize = Model.of((String)null);
protected final IModel<String> publicSize = Model.of((String)null);
final ConvertingErrorsDialog errorsDialog = new ConvertingErrorsDialog("errors", Model.of((Recording)null));
final FileItemTree tree;
final AjaxSplitButton download = new AjaxSplitButton("download", new ArrayList<IMenuItem>()) {
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget target, IMenuItem item) {
item.onClick(target);
}
};
private final Form<Void> form = new Form<>("form");
private final NameDialog addFolder;
private final ConfirmableBorderDialog trashConfirm;
private ConfirmableAjaxBorder trashBorder;
private final Long roomId;
private boolean readOnly = true;
private final Component createDir = new WebMarkupContainer("create").add(new AjaxEventBehavior(EVT_CLICK) {
private static final long serialVersionUID = 1L;
@Override
protected void onEvent(AjaxRequestTarget target) {
addFolder.open(target);
}
});
private final Component upload = new WebMarkupContainer("upload");
public FileTreePanel(String id, Long roomId, NameDialog addFolder, ConfirmableBorderDialog trashConfirm) {
super(id);
this.roomId = roomId;
this.addFolder = addFolder;
this.trashConfirm = trashConfirm;
final OmTreeProvider tp = new OmTreeProvider(roomId);
select(tp.getRoot(), null, false, false);
form.add(tree = new FileItemTree("tree", this, tp));
form.add(download.setVisible(false).setOutputMarkupPlaceholderTag(true));
add(form.add(downloader));
}
@Override
protected void onInitialize() {
super.onInitialize();
download.setDefaultModelObject(newDownloadMenuList());
Droppable<BaseFileItem> trashToolbar = new Droppable<BaseFileItem>("trash-toolbar") {
private static final long serialVersionUID = 1L;
@Override
public void onConfigure(JQueryBehavior behavior) {
super.onConfigure(behavior);
behavior.setOption("hoverClass", Options.asString("ui-state-hover trash-toolbar-hover"));
behavior.setOption("accept", Options.asString(".recorditem, .fileitem"));
}
@Override
public JQueryBehavior newWidgetBehavior(String selector) {
return new DroppableBehavior(selector, this) {
private static final long serialVersionUID = 1L;
@Override
protected JQueryAjaxBehavior newOnDropAjaxBehavior(IJQueryAjaxAware source) {
return new OnDropAjaxBehavior(source) {
private static final long serialVersionUID = 1L;
@Override
public CharSequence getCallbackFunctionBody(CallbackParameter... parameters) {
String dialogId = UUID.randomUUID().toString();
String statement = "var $drop = $(this);";
statement += "$('body').append('<div id=" + dialogId + ">" + getString("713") + "</div>');";
statement += "$( '#" + dialogId
+ "' ).dialog({ title: '" + escapeEcmaScript(getString("80")) + "', classes: {'ui-dialog-titlebar': 'ui-corner-all no-close'}, buttons: [";
statement += " { text: '" + escapeEcmaScript(getString("54")) + "', click: function() { $drop.append(ui.draggable); $(this).dialog('close'); " + super.getCallbackFunctionBody(parameters) + " } },";
statement += " { text: '" + escapeEcmaScript(getString("lbl.cancel")) + "', click: function() { $(this).dialog('close'); } } ";
statement += "],";
statement += "close: function(event, ui) { $(this).dialog('destroy').remove(); }";
statement += "});";
return statement;
}
};
}
};
}
@Override
public void onDrop(AjaxRequestTarget target, Component component) {
Object o = component.getDefaultModelObject();
if (o instanceof BaseFileItem) {
BaseFileItem f = (BaseFileItem)o;
if (isSelected(f)) {
deleteAll(target);
} else {
delete(f, target);
}
}
}
};
form.add(trashToolbar);
trashToolbar.add(getUpload());
trashToolbar.add(createDir);
trashToolbar.add(new WebMarkupContainer("refresh").add(new AjaxEventBehavior(EVT_CLICK) {
private static final long serialVersionUID = 1L;
@Override
protected void onEvent(AjaxRequestTarget target) {
update(target);
}
}));
trashToolbar.add(trashBorder = new ConfirmableAjaxBorder("trash", getString("80"), getString("713"), trashConfirm) {
private static final long serialVersionUID = 1L;
@Override
protected boolean isClickable() {
return !readOnly && !selected.isEmpty();
}
@Override
protected void onSubmit(AjaxRequestTarget target) {
deleteAll(target);
}
});
form.add(trees.add(tree).setOutputMarkupId(true));
updateSizes();
form.add(sizes.add(new Label("homeSize", homeSize), new Label("publicSize", publicSize)).setOutputMarkupId(true));
form.add(errorsDialog);
setReadOnly(false, null);
}
@Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(FileTreePanel.class, "filetree.js")));
}
protected String getContainment() {
return ".file.item.drop.area";
}
protected Component getUpload() {
return upload.setVisible(false);
}
private void deleteAll(AjaxRequestTarget target) {
for (Entry<String, BaseFileItem> e : selected.entrySet()) {
BaseFileItem f = e.getValue();
if (!f.isReadOnly()) {
delete(f, target);
}
}
updateSelected(target);
selected.clear();
}
void delete(BaseFileItem f, IPartialPageRequestHandler handler) {
Long id = f.getId();
if (id != null) {
if (f instanceof Recording) {
getBean(RecordingDao.class).delete(f);
} else {
getBean(FileItemDao.class).delete(f);
}
}
update(handler);
}
public void setReadOnly(boolean readOnly, IPartialPageRequestHandler handler) {
if (this.readOnly != readOnly) {
this.readOnly = readOnly;
tree.refreshRoots(!readOnly);
createDir.setEnabled(!readOnly);
createDir.add(AttributeModifier.replace(ATTR_CLASS, new StringBuilder(CREATE_DIR_CLASS).append(readOnly ? DISABLED_CLASS : "")));
upload.setEnabled(!readOnly);
upload.add(AttributeModifier.replace(ATTR_CLASS, new StringBuilder(UPLOAD_CLASS).append(readOnly ? DISABLED_CLASS : "")));
trashBorder.add(AttributeModifier.replace(ATTR_CLASS, new StringBuilder(TRASH_CLASS).append(readOnly ? DISABLED_CLASS : "")));
if (handler != null) {
handler.add(createDir, upload, trashBorder);
update(handler);
}
}
}
public boolean isReadOnly() {
return readOnly;
}
protected abstract void update(AjaxRequestTarget target, BaseFileItem f);
public void createFolder(AjaxRequestTarget target, String name) {
BaseFileItem p = lastSelected;
boolean isRecording = p instanceof Recording;
BaseFileItem f = isRecording ? new Recording() : new FileItem();
f.setName(name);
f.setHash(UUID.randomUUID().toString());
f.setInsertedBy(getUserId());
f.setInserted(new Date());
f.setType(Type.Folder);
f.setOwnerId(p.getOwnerId());
f.setGroupId(p.getGroupId());
f.setRoomId(p.getRoomId());
f.setParentId(Type.Folder == p.getType() ? p.getId() : null);
if (isRecording) {
getBean(RecordingDao.class).update((Recording)f);
} else {
getBean(FileItemDao.class).update((FileItem)f);
}
update(target);
}
public abstract void updateSizes();
public boolean isSelected(BaseFileItem f) {
return selected.containsKey(f.getHash());
}
public Map<String, BaseFileItem> getSelected() {
return selected;
}
public BaseFileItem getLastSelected() {
return lastSelected;
}
public void update(IPartialPageRequestHandler handler) {
updateSizes();
handler.add(sizes, trees);
}
private void updateSelected(AjaxRequestTarget target) {
for (Entry<String, BaseFileItem> e : selected.entrySet()) {
updateNode(target, e.getValue());
}
}
void updateNode(AjaxRequestTarget target, BaseFileItem fi) {
if (fi != null && !fi.isDeleted() && target != null) {
if (Type.Folder == fi.getType()) {
tree.updateBranch(fi, target);
} else {
tree.updateNode(fi, target);
}
}
}
private static boolean sameParent(Long roomId, BaseFileItem f1, BaseFileItem f2) {
if (f1 instanceof Recording && f2 instanceof FileItem) {
return false;
}
if (f1.getParentId() != null && f1.getParentId().equals(f2.getParentId())) {
return true;
}
if (f1.getParentId() == null && f2.getParentId() == null) {
if (f1.getOwnerId() != null && f1.getOwnerId().equals(f2.getOwnerId())) {
return true;
}
if (f1.getRoomId() != null && f1.getRoomId().equals(f2.getRoomId())) {
return true;
}
if (f2 instanceof FileItem && roomId != null && f1.getRoomId() == null && f2.getRoomId() == null && f1.getOwnerId() == null && f2.getOwnerId() == null) {
return true;
}
}
return false;
}
private static boolean isDownloadable(BaseFileItem f) {
return !f.isReadOnly() && (f.getType() == Type.Presentation || f.getType() == Type.Image);
}
public void select(BaseFileItem fi, AjaxRequestTarget target, boolean shift, boolean ctrl) {
updateSelected(target); //all previously selected are in update list
if (ctrl) {
if (isSelected(fi)) {
selected.remove(fi.getHash());
} else {
selected.put(fi.getHash(), fi);
}
lastSelected = fi;
} else if (shift && lastSelected != null && !lastSelected.getHash().equals(fi.getHash()) && sameParent(roomId, fi, lastSelected)) {
selected.clear();
String lastHash = null;
for (BaseFileItem f : tree.getProvider().getByParent(fi, fi.getParentId())) {
if (lastHash == null) {
if (f.getHash().equals(lastSelected.getHash())) {
lastHash = fi.getHash();
}
if (f.getHash().equals(fi.getHash())) {
lastHash = lastSelected.getHash();
}
}
if (lastHash != null) {
selected.put(f.getHash(), f);
if (f.getHash().equals(lastHash)) {
break;
}
}
}
} else {
selected.clear();
selected.put(fi.getHash(), fi);
lastSelected = fi;
}
updateSelected(target); //all finally selected are in the update list
if (target != null) {
target.add(trashBorder, download.setVisible(isDownloadable(lastSelected)));
}
}
@Override
protected void onDetach() {
homeSize.detach();
publicSize.detach();
super.onDetach();
}
private List<IMenuItem> newDownloadMenuList() {
List<IMenuItem> list = new ArrayList<>();
//original
list.add(new DownloadMenuItem(getString("files.download.original"), this, null));
//pdf
list.add(new DownloadMenuItem(getString("files.download.pdf"), this, EXTENSION_PDF));
//jpg
list.add(new DownloadMenuItem(getString("files.download.jpg"), this, EXTENSION_JPG));
return list;
}
}
首先,上类的结构图:
首先,此类继承自Panel类,本身就是一个图形化界面的组件面板;
构造方法:参数为String id, Long roomId, NameDialog addFolder, ConfirmableBorderDialog trashConfirm,没有返回值,作用是实例化FileTreePanle的对象;方法开头是利用super调用父组件Panel的参数为String的构造方法,然后将参数值赋值给类的成员变量,最后使用add方法为成员变量form添加根据传入参数创建的的FileItemTree等对象;
onInitialize方法:参数返回值都为空,作用是对组件进行初始化,首先利用super调用org.apache.wicket public abstract class Componen类的oninitiallize方法,然后创建了一个com.googlecode.wicket.jquery.ui.interaction.droppable public abstract class Droppable类型得对象,使用了内部类,重写了com.googlecode.wicket.jquery.core.JQueryContainer类的数个方法,其中重写newWidgetBehavior方法时,基返回对象部分又调用了内部类实例化OnDropAjaxBehavior对象,理解起来比较费劲,层层嵌套;方法最后同样是为成员变量form以及此方法中新创建的trashToolbar对象用add添加新属性或对象;
renderHead方法:形参为IHeaderResponse response返回值为空,作用是渲染组件的头部;首先用super调用了父类org.apache.wicket.Component的构造方法,然后创建一个JavaScriptResourceReference对象并作为forReference方法的参数,由reponse进行具体的渲染;
getContainment方法:非常简单,返回一个字符串常量;
getUpload方法:返回将WebMarkupContainer类型得成员变量upload进行setvisible方法后的component对象;
deleteAll方法:形参为AjaxRequestTarget target返回值为空,作用为删除全部文件面板,采用了iterator形式的for遍历了selected的entryset,最后对selected对象执行了clear方法,清除hashmap的键值对;
delete方法,形参为BaseFileItem f, IPartialPageRequestHandler handler,返回值为空,作用是删除特定文件项对应的面板;
setReadOnly方法:形参为boolean readOnly, IPartialPageRequestHandler handler,返回值为空,作用是使fileItemTree只读,判断给的参数和原有的状态参数相不相等,不相等就执行文件树的状态值的更新;
isReadOnly方法:返回是否为只读文件;
update方法:抽象方法留给子类去实现;createFolder方法:创建文件夹相关的面板操作,参数为AjaxRequestTarget target, String name,判断成员变量lastSelected的类型是否是Recording,是就新建f为Recording对象,否则新建为fileitem对象,然后对f进行一系列的属性复制赋值;
isSelected方法:形参为BaseFileItem f,返回值为空,作用为检查某文件是否被选中;
getSelected方法以及getLastSelected都是普通的get方法不做介绍;
updateSelected方法:形参为AjaxRequestTarget target,返回值为空,用以更新被选中的文件,代码时以iterator形式的for循环遍历selected的entrySet();
update作用类似,形参为IPartialPageRequestHandler handler,返回值为空,作用为更新面板状态,首先调用抽象函数(之后子类还会实现,目前为空)然后用handler的add方法将两个成员变量添加进去;
updateNode方法:形参为AjaxRequestTarget target, BaseFileItem fi,返回值为空,作用为更新成员变量文件树的结点,判断传入的basefileitem是否为空或者已经被删除,不为空且没被删除,则更新到文件树里;
sameParent方法:形参为:Long roomId, BaseFileItem f1, BaseFileItem f2,判断两个基础文件项是否为同一父文件项下的文件项;
select方法:形参为BaseFileItem fi, AjaxRequestTarget target, boolean shift, boolean ctrl,作用为选中某个文件,根据输入的两个布尔值,进行判断当前文件是否已经被选中,选中了就删除,没选中就选择上,这是在按着ctrl键的情况,如果没按ctrl,需要同时按着shift且lastSelected不为空且lastSelected和参数传入的基础文件项相同,采取更新文件项,将要选中的文件加入到选中队列里;
onDetach方法:作用为组件分离时执行的具体操作,用两个成员变量的detach方法具体执行;
newDownloadMenuList方法:创建动态数组,并向里面add新的DownloadMenuItem对象,三个种类的文件,分别是原始文件(文本),pdf和jpg图片;
OmTreeProvider:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.web.common.tree;
import static org.apache.openmeetings.db.util.AuthLevelUtil.hasAdminLevel;
import static org.apache.openmeetings.web.app.Application.getBean;
import static org.apache.openmeetings.web.app.WebSession.getRights;
import static org.apache.openmeetings.web.app.WebSession.getUserId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.apache.openmeetings.db.dao.file.FileItemDao;
import org.apache.openmeetings.db.dao.record.RecordingDao;
import org.apache.openmeetings.db.dao.user.UserDao;
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.user.Group;
import org.apache.openmeetings.db.entity.user.GroupUser;
import org.apache.openmeetings.web.app.Application;
import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
public class OmTreeProvider implements ITreeProvider<BaseFileItem> {
private static final long serialVersionUID = 1L;
private static final List<Type> VIDEO_TYPES = Arrays.asList(Type.Folder, Type.Video);
public static final String RECORDINGS_MY = "recordings-my";
public static final String RECORDINGS_PUBLIC = "recordings-public";
public static final String RECORDINGS_GROUP = "recordings-group-%s";
public static final String FILES_MY = "files-my";
public static final String FILES_ROOM = "files-room";
public static final String FILES_GROUP = "files-group-%s";
private final Long roomId;
private final List<BaseFileItem> roots = new ArrayList<>();
private final String PUBLIC, GROUP_FILE, GROUP_REC;
public OmTreeProvider(Long roomId) {
this.roomId = roomId;
PUBLIC = Application.getString("861");
GROUP_FILE = Application.getString("files.root.group");
GROUP_REC = Application.getString("recordings.root.group");
refreshRoots(true);
}
public void refreshRoots(boolean all) {
List<BaseFileItem> fRoot = new ArrayList<>(), rRoot = new ArrayList<>();
if (all && roomId != null) {
BaseFileItem r = createRoot(Application.getString("706"), FILES_MY, false);
r.setOwnerId(getUserId());
fRoot.add(r);
}
if (roomId != null) {
BaseFileItem r = createRoot(Application.getString("707"), FILES_ROOM, false);
r.setRoomId(roomId);
fRoot.add(r);
}
if (all) {
{
BaseFileItem r = createRoot(Application.getString("860"), RECORDINGS_MY, true);
r.setOwnerId(getUserId());
rRoot.add(r);
}
{
BaseFileItem r = createRoot(PUBLIC, RECORDINGS_PUBLIC, true);
rRoot.add(r);
}
}
for (GroupUser gu : getBean(UserDao.class).get(getUserId()).getGroupUsers()) {
Group g = gu.getGroup();
boolean readOnly = g.isRestricted() && !hasAdminLevel(getRights()) && !gu.isModerator();
if (all) {
BaseFileItem r = createRoot(String.format("%s (%s)", GROUP_REC, g.getName()), String.format(RECORDINGS_GROUP, g.getId()), true);
r.setReadOnly(readOnly);
r.setGroupId(g.getId());
rRoot.add(r);
}
BaseFileItem r = createRoot(String.format("%s (%s)", GROUP_FILE, g.getName()), String.format(FILES_GROUP, g.getId()), false);
r.setGroupId(g.getId());
//group videos are read-only in recordings tree
r.setReadOnly(roomId == null || readOnly);
fRoot.add(r);
}
roots.clear();
if (roomId == null) {
roots.addAll(rRoot);
roots.addAll(fRoot);
} else {
roots.addAll(fRoot);
roots.addAll(rRoot);
}
}
static BaseFileItem createRoot(String name, String hash, boolean rec) {
BaseFileItem f = rec ? new Recording() : new FileItem();
f.setType(Type.Folder);
f.setName(name);
f.setHash(hash);
return f;
}
public BaseFileItem getRoot() {
return roots.get(0);
}
@Override
public Iterator<BaseFileItem> getRoots() {
return roots.iterator();
}
public List<BaseFileItem> getByParent(BaseFileItem node, Long id) {
List<BaseFileItem> list = new ArrayList<>();
if (node instanceof Recording) {
Recording rec = (Recording)node;
RecordingDao dao = getBean(RecordingDao.class);
List<Recording> _list;
if (id == null) {
if (node.getOwnerId() == null) {
_list = dao.getRootByPublic(rec.getGroupId());
} else {
_list = dao.getRootByOwner(node.getOwnerId());
}
} else {
_list = dao.getByParent(id);
}
list.addAll(_list);
} else {
FileItemDao dao = getBean(FileItemDao.class);
List<FileItem> _list;
if (id == null) {
if (node.getRoomId() != null) {
_list = dao.getByRoom(node.getRoomId());
} else if (node.getGroupId() != null) {
_list = dao.getByGroup(node.getGroupId(), roomId == null ? VIDEO_TYPES : null);
} else {
_list = dao.getByOwner(node.getOwnerId());
}
} else {
_list = dao.getByParent(id, roomId == null ? VIDEO_TYPES : null);
}
list.addAll(_list);
}
if (node.isReadOnly()) {
for (BaseFileItem f : list) {
f.setReadOnly(true);
}
}
return list;
}
@Override
public Iterator<BaseFileItem> getChildren(BaseFileItem node) {
return getByParent(node, node.getId()).iterator();
}
@Override
public boolean hasChildren(BaseFileItem node) {
return node.getId() == null || Type.Folder == node.getType();
}
@Override
public IModel<BaseFileItem> model(BaseFileItem object) {
return Model.of(object);
}
@Override
public void detach() {
//no-op
}
}
此类实现了org.apache.wicket.extensions.markup.html.repeater.tree包下的ITreeProvider<T>的接口,提供一些关于文件树的操作的一些方法;
构造方法:形参为Long roomId,用来实例化OmTreeProvider对象,先对一系列成员变量进行赋值,然后刷新状态;
refreshRoots方法:形参为布尔值all,用来刷新状态,首先创建两个动态数组froot和rRoot,然后根据布尔值all以及roomid是否等于0来创建不同的新的基础文件项添加到数组内,并设置组号,房间号,是否只读等属性,最后再利用迭代器形式的for循环遍历GroupUser对象,更新root的状态,先clear再add方法前面创建的froot和rroot;createRoot方法:形参为String name, String hash, boolean rec,返回值为BaseFileItem,作用是根据指定的根文件的参数创建一个根文件;
getRoot方法:返回成员变量roots的key为0的value,参数为空,返回值为一个BaseFileItem的对象;
getRoots方法:返回roots对象的迭代器,返回值是一个basefileitem类型的迭代器对象;
getByParent方法:形参为BaseFileItem node, Long id,返回值为List<BaseFileItem>,作用为通过某文件树里的项的父文件项获取文件项本身,首先判断传入的参数node的类型是不是Recording类型,是就创建一个RecordingDao对象,否就创建一个FileItemDao对象,然后需要判断传入的id是否存在以及node.getOwnerId的id是否存在,然后根据判断结果采用不同的方法得到文件项并将它add到list后面,最后根据node是否为只读,选择性的将结果设置只读然后返回;
getChildren方法:形参aseFileItem node,得到某文件项的子文件项,返回值Iterator<BaseFileItem>;
hasChildren方法:形参为BaseFileItem node,返回值为boolean,判断某文件项是否含有子项;
model方法:返回值为IModel<BaseFileItem>,形参为BaseFileItem object,利用org.apache.wicket.model类的对象调用of方法获取BaseFileItem类型的IModel对象;
至此,common下的tree包下的java类分析完成,下面简单分析一下此目录下的html文件
html文件:
FolderPanel.html,FileTreePanel.html:
<html xmlns:wicket="http://wicket.apache.org">
<wicket:panel>
<div class="file item"><wicket:child /><span wicket:id="name" class="name"></span></div>
</wicket:panel>
</html>
<html xmlns:wicket="http://wicket.apache.org">
<wicket:extend><span wicket:id="errors" class="errors"></span></wicket:extend>
</html>
只是用来wicket包下的标签构建了一个简单的页面;
ConvertingErrorsDialog.html:
<html xmlns:wicket="http://wicket.apache.org">
<wicket:panel>
<div wicket:id="container" class="dialog errors">
<h3 wicket:id="message"></h3>
<table style="width: 100%">
<tr>
<th><wicket:message key="1593"/></th>
<th><wicket:message key="1594"/></th>
</tr>
<tr wicket:id="row">
<td wicket:id="exitCode"></td>
<td wicket:id="message" class="message"></td>
</tr>
</table>
</div>
</wicket:panel>
</html>
div标签里面套了一个三级标题和一个table表格。仅仅是对标签的属性进行了一些设置,没有实际的内容展示;FileItemPanel.html类似,不再赘述;
filetree.js:
/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
function dragHelper() {
let s = $(this);
if (s.hasClass('ui-state-active')) {
s = $('.file.ui-state-active.ui-draggable.ui-draggable-handle, .recording.ui-state-active.ui-draggable.ui-draggable-handle');
}
const c = $('<div/>').attr('id', 'draggingContainer').addClass('drag-container').width(80).height(36)
, h = $('<div class="ui-corner-all ui-widget-header"/>').append(s.clone()).width(s.width());
return c.append(h);
}
function treeRevert(dropped) {
$('.file.tree .trees')[0].scrollTop = $(this).parent()[0].offsetTop - 32;
return !dropped || (!!dropped && !!dropped.context && $(dropped.context).hasClass('wb', 'room'));
}
定义了两个函数drapHelper和treeRevert;
函数里主要是各类参数麻烦,结构并不困难;
至此,tree包下的类分析完毕。篇幅原因,本周不继续往下分析,下周应开始分析common包下剩余的类,包含数10个java类以及若干html文件,大部分都是图形界面有关的类,代码量仍然很大。