I'd like to implement something like the following HTML helpers in MvcSiteMapProvider:
Html.MvcSiteMap().Previous()
Html.MvcSiteMap().Next()
However, I am quite new to their API, is it possible to do that, and if so, how?
You can accomplish this by building custom HTML helpers. I have answered this question already at GitHub and provided a working demo project, but I am copying here for reference.
The logic to walk up and down the document would look something like this, the rest of the code is for the most part boilerplate templated HTML helper code.
private static ISiteMapNode GetNextNode(ISiteMapNode startingNode, IDictionary<string, object> sourceMetadata)
{
ISiteMapNode nextNode = null;
if (startingNode.HasChildNodes)
{
// Get the first child node
nextNode = startingNode.ChildNodes[0];
}
else if (startingNode.ParentNode != null)
{
// Get the next sibling node
nextNode = startingNode.NextSibling;
if (nextNode == null)
{
// If there are no more siblings, the next position
// should be the parent's next sibling
var parent = startingNode.ParentNode;
if (parent != null)
{
nextNode = parent.NextSibling;
}
}
}
// If the node is not visible or accessible, run the operation recursively until a visible node is found
if (nextNode != null && !(nextNode.IsVisible(sourceMetadata) || nextNode.IsAccessibleToUser()))
{
nextNode = GetNextNode(nextNode, sourceMetadata);
}
return nextNode;
}
private static ISiteMapNode GetPreviousNode(ISiteMapNode startingNode, IDictionary<string, object> sourceMetadata)
{
ISiteMapNode previousNode = null;
// Get the previous sibling
var previousSibling = startingNode.PreviousSibling;
if (previousSibling != null)
{
// If there are any children, go to the last descendant
if (previousSibling.HasChildNodes)
{
previousNode = previousSibling.Descendants.Last();
}
else
{
// If there are no children, return the sibling.
previousNode = previousSibling;
}
}
else
{
// If there are no more siblings before this one, go to the parent node
previousNode = startingNode.ParentNode;
}
// If the node is not visible or accessible, run the operation recursively until a visible node is found
if (previousNode != null && !(previousNode.IsVisible(sourceMetadata) || previousNode.IsAccessibleToUser()))
{
previousNode = GetPreviousNode(previousNode, sourceMetadata);
}
return previousNode;
}
Related
I just want to ask how can I properly use the document changes in my app? Btw there are 3 types of that which is ADDED, MODIFIED and lastly REMOVED. TYPE.ADDED works perfectly fine, but in modified and removed it doesn't work well in modified it. I am using a recyclerview for that and here's my code. Am I wrong utilizing it? Also, I am using a instance oldindex and newindex to know the index which is affected by the action performed.
for (DocumentChange doc : documentSnapshots.getDocumentChanges()) {
if(doc.getType() == DocumentChange.Type.ADDED) {
PostsClass post = doc.getDocument().toObject(PostsClass.class).withId(doc.getDocument().getId());
postList.add(post);
Log.d(TAG, post.toString());
postsAdapter.notifyDataSetChanged();
}
else if (doc.getType() == DocumentChange.Type.MODIFIED) {
adoptList.clear();
AdoptClass adopt = doc.getDocument().toObject(AdoptClass.class).withId(doc.getDocument().getId());
adoptList.add(adopt);
adoptListAdapter.notifyItemChanged(oldIndex);
}
else if (doc.getType() == DocumentChange.Type.REMOVED) {
adoptList.remove(oldIndex);
adoptListAdapter.notifyItemRemoved(oldIndex);
}
}
below code worked for me in 3 conditions ADDED,MODIFIED,REMOVED (Android Firestore)
for (DocumentChange documentChange : queryDocumentSnapshots.getDocumentChanges()) {
if (documentChange.getType() == DocumentChange.Type.ADDED) {
String doc_id = documentChange.getDocument().getId();
PostModel postModel = documentChange.getDocument().toObject(PostModel.class).withDocId(doc_id);
postModelList.add(postModel);
} else if (documentChange.getType() == DocumentChange.Type.MODIFIED) {
// modifying
String docID = documentChange.getDocument().getId();
PostModel changedModel = documentChange.getDocument().toObject(PostModel.class).withDocId(docID);
if (documentChange.getOldIndex() == documentChange.getNewIndex()) {
// Item changed but remained in same position
postModelList.set(documentChange.getOldIndex(),changedModel);
postListAdapter.notifyItemChanged(documentChange.getOldIndex());
}else {
// Item changed and changed position
postModelList.remove(documentChange.getOldIndex());
postModelList.add(documentChange.getNewIndex(),changedModel);
postListAdapter.notifyItemMoved(documentChange.getOldIndex(),documentChange.getNewIndex());
}
postListAdapter.notifyDataSetChanged();
} else if (documentChange.getType() == DocumentChange.Type.REMOVED) {
// remove
postModelList.remove(documentChange.getOldIndex());
postListAdapter.notifyItemRemoved(documentChange.getOldIndex());
}
}
I have a treeview of hierarchical data that represents elements retrieved from a database. I would like to be able to move child (leaf) nodes from one parent to another and rearrange the sequence of leaf nodes within a parent via drag-and-drop, finally updating the database with the results of the move operations.
Right now I can drop one of the leaf nodes onto a parent node which allows me to add the child to the parent. However, I don't really see how to drop a leaf node between two other leaf nodes, either in the same parent or in a different parent node.
I would like to be able to do something like the following. Starting with a tree that looks like this:
Root
|
+-+-P1
| |
| +--L1a
| +--L1b
|
+-+-P2
|
+--L2a
+--L2b
For example, I would like to be able to select L2a and drag it up and drop it between L1a and L1b. Or before L1a, or after L1b. Having done that I would like to drag the child nodes of P1 around and rearrange them via DND.
Part of this would be to provide an indication of the drop location. For example, a line between L1a and L1b if you have the cursor positioned 'between' these nodes.
Is this possible? I don't see any examples of this anywhere.
One other thing I am seeing is that the effect of DND doesn't remove the leaf node from its original location even though in the setOnDragDetected method call for my cells I call cell.startDragAndDrop(TransferMode.MOVE). How can I get that to work?
Edit 9/1/17
I figured out the last part of not removing the leaf node.
Here is the code I am using to test this process out. I have an interface IStoryItem that is the (empty) interface implemented by two subclasses, Story and Part (just to create a hierarchy to test with). The Story class has title (String) and parts (Part[]) fields. The Part class has title (String) and partNumber (int) fields. I have a Utils class that creates an array of Story objects to populate my TreeView instance.
Here is my controller class. It's a bit long and in need of cleanup but shows what I have tried so far.
public class Controller {
public TreeView<IStoryItem> tv;
private final DataFormat objectDataFormat = new DataFormat("application/x-java-serialized-object");
class StoryRoot implements IStoryItem { }
TreeItem<IStoryItem> rootItem;
#FXML
public void initialize() {
StoryRoot storyRoot = new StoryRoot();
rootItem = new TreeItem<>(storyRoot);
tv.setRoot(rootItem);
// For Drag and Drop:
// - rootItem can only accept Story nodes.
// - Story nodes can only accept Part nodes.
// - Part nodes can't accept any other nodes.
tv.setCellFactory(new Callback<TreeView<IStoryItem>, TreeCell<IStoryItem>>() {
#Override
public TreeCell<IStoryItem> call(TreeView<IStoryItem> siTreeView) {
TreeCell<IStoryItem> cell = new TreeCell<IStoryItem>() {
#Override
protected void updateItem(IStoryItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null) { setText(item.toString()); }
}
};
// The following calls are as outlined in:
// https://docs.oracle.com/javase/8/javafx/events-tutorial/drag-drop.htm#CHDJFJDH
cell.setOnDragDetected((MouseEvent event) -> {
// Don't drag Story nodes.
if (cell.getItem() instanceof Story) return;
// drag was detected, start a drag-and-drop gesture
// allow Move transfer mode only
Dragboard db = cell.startDragAndDrop(TransferMode.MOVE);
// Put the Part on the dragboard
// From: https://stackoverflow.com/a/30916660/780350
ClipboardContent content = new ClipboardContent();
content.put(objectDataFormat, cell.getItem());
db.setContent(content);
event.consume();
});
cell.setOnDragDropped((DragEvent event) -> {
try {
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasContent(objectDataFormat)) {
Part droppedPart = (Part)db.getContent(objectDataFormat);
IStoryItem targetStoryItem = cell.getItem();
// Question: How to handle drops between leaf items or
// before the initial leaf or after the final leaf.
if (targetStoryItem instanceof Story) {
Story story = (Story) targetStoryItem;
updateStoryWith(droppedPart, story);
addPartTo(cell.getTreeItem(), droppedPart);
success = true;
}
}
event.setDropCompleted(success);
event.consume();
} catch (Exception e) {
System.out.println(e.getMessage());
}
});
cell.setOnDragDone((DragEvent event) -> {
/*
* the drag and drop gesture ended
* if the data was successfully moved, clear it
*/
if (event.getTransferMode() == TransferMode.MOVE) {
// TODO: remove the part that got moved.
IStoryItem item = cell.getItem();
TreeItem<IStoryItem> ti = cell.getTreeItem();
TreeItem<IStoryItem> pti = ti.getParent();
pti.getChildren().remove(ti);
IStoryItem psi = pti.getValue();
boolean removed = removePartFrom(psi, item);
}
event.consume();
});
return cell;
};
});
tv.getSelectionModel()
.selectedItemProperty()
.addListener((observable, oldValue, newValue) -> inspectObject(newValue.getValue()));;
Story[] stories = Utils.createStories();
for (Story s: stories) {
addStoryToTree(s);
}
}
private void updateStoryWith(Part droppedPart, Story story) {
List<Part> partsList = new ArrayList<>(Arrays.asList(story.parts));
partsList.add(droppedPart);
Part [] newParts = (Part[])partsList.toArray(new Part[partsList.size()]);
int idx = 1;
for (Part part : newParts) {
part.partnumber = idx++;
}
story.parts = newParts;
}
private void inspectObject(Object o) {
if (!(o instanceof IStoryItem)) {
System.out.println(o.getClass().toString());
} else if (o instanceof Story) {
Story s = (Story)o;
System.out.println("Story: " + s.toString());
} else if (o instanceof Part) {
Part s = (Part)o;
System.out.println("Part: " + s.toString());
}
}
void addStoryToTree(Story story) {
if (story.parts.length == 0) return;
TreeItem<IStoryItem> item = new TreeItem<>(story);
rootItem.getChildren().add(item);
for (Part part : story.parts) {
addPartTo(item, part);
}
}
void addPartTo(TreeItem<IStoryItem> storyItem, Part part) {
TreeItem<IStoryItem> partItem = new TreeItem<>(part);
storyItem.getChildren().add(partItem);
}
boolean removePartFrom(IStoryItem si, IStoryItem pi) {
if (!(si instanceof Story)) return false;
if (!(pi instanceof Part)) return false;
Story story = (Story) si;
Part part = (Part) pi;
List<Part> plist = new LinkedList<>(Arrays.asList(story.parts));
if (!plist.contains(part)) return false;
boolean removed = plist.remove(part);
story.parts = plist.toArray(new Part[plist.size()]);
return removed;
}
}
The problem is why I can't find any attributes from the rootElement?
my xml is
<?xml version="1.0" encoding="GBK"?>
<AccountInfos>
<!--this is a test for dom4j-->
<AccountInfo1 WebsiteName="ÐÂÀË" Account="123">Account1</AccountInfo1>
<AccountInfo2 WebsiteName="ÍøÒ×" Account="123">Account2</AccountInfo2>
</AccountInfos>
and my code is like this
private void treeWalker(Element element)
{
int size = element.nodeCount();
for (int i = 0; i < size; i++)
{
Node node = element.node(i);
if (node instanceof Element)
{
treeWalker((Element) node);
}
else if(node instanceof Attribute)
{
Attribute attribute=(Attribute)node;
System.out.println(attribute.getName()+":"+attribute.getValue());
}
else
{
continue;
}
}
}
when I debug in this method I can‘t go into the second if block
Attributes are not considered part of the Element (or Branch, actually) content (such as Elements, Comments or Text nodes). You must retrieve then specially, e.g. with an attributeIterator().
I would like to find a list paragraph (starting with a. ), and append another list item to this list (it depend on the text of first list element).
I have tried many ways of creating new paragraph, but all what I achieved is that new list elements are created, but org.docx4j.wml.Text objects are appended to paragraph the new paragraph was appended. The new paragraph text is empty. How can be new list element created and appended to the right element?
a. list element 1 |test| //|test| should be appended to b.
b. //new items are created, but there is no text
c.
//traverse through a document
public List<Object> apply(Object obj) {
if (obj instanceof org.docx4j.wml.P) {
if (p.getPPr() != null) {
if (p.getPPr().getPStyle() != null) {
if ((p.getPPr().getPStyle().getVal().equals("Akapitzlist"))) {
//there is a list paragraph
ObjectFactory factory = Context.getWmlObjectFactory();
Object deepCopy = XmlUtils.deepCopy(obj);
//Create the paragraph
org.docx4j.wml.P para = factory.createP();
// Create the text element
org.docx4j.wml.Text t = factory.createText();
t.setValue("|test|");
// Create the run
org.docx4j.wml.R run = factory.createR();
run.getContent().add(t);
para.getContent().add(run);
//add new paragraph to the document
((org.docx4j.wml.P) obj).getContent().add(para);
}...}
My solution, just append to the body with incremented index. I' am creating deep copy to preserwe style.
public List<Object> apply(Object obj) {
Object deepCopy = null;
if (obj instanceof org.docx4j.wml.P) {
org.docx4j.wml.P p = (org.docx4j.wml.P) obj;
if (p.getPPr() != null) {
if (p.getPPr().getPStyle() != null) {
if ((p.getPPr().getPStyle().getVal().equals("Akapitzlist")) && (akapListCounter < 10)) {
if (((org.docx4j.wml.P) obj).getPPr().getPStyle() != null) {
if ((((org.docx4j.wml.P) obj).getPPr().getPStyle().getVal().equals("Akapitzlist"))) {
deepCopy = XmlUtils.deepCopy(obj);
akapListCounter++;
int indexOf = wmlDocumentEl.getBody().getContent().indexOf(obj);
List<Object> content = ((org.docx4j.wml.P) deepCopy).getContent();
for (Object el : content) {
System.out.println("class1:" + el.getClass().toString());
if (el instanceof org.docx4j.wml.R) {
List<Object> subc = ((org.docx4j.wml.R) el).getContent();
for (Object r : subc) {
((javax.xml.bind.JAXBElement) r).setValue("tetetete");
}
}
}// end for
wmlDocumentEl.getBody().getContent().add(indexOf + 1, deepCopy);
}
}//end get style
}
}
} else {}
}
return null;
}
I have a flex tree with dragMoveEnabled = true. I want to find out, when an item is dragged into itself or it's children. I'd like to use DragManager.showFeedback(DragManager.NONE) in the tree's onDragOver handler, but can't find out how get this to work. I'm using an ArrayCollection of nested objects as dataSource for the tree.
private function onDragOver(event:DragEvent):void {
event.preventDefault();
event.currentTarget.hideDropFeedback(event);
var index:int = tree.calculateDropIndex(event);
tree.selectedIndex = index;
var subCategory:CategoryVO = CategoryVO(tree.selectedItem);
var currentCategory:CategoryVO = subCategory;
while(currentCategory.parent != 0) {
if (currentCategory.parent == _draggedItem.id) {
DragManager.showFeedback(DragManager.NONE);
return;
}
currentCategory = tree.getParentItem(currentCategory);
if (currentCategory == null) {
break;
}
DragManager.showFeedback(DragManager.MOVE);
tree.showDropFeedback(event);
}
That's how I solved it.
_draggedItem holds the currently dragged item, set in the tree's onDragEnter handler. CategoryVO is the value object I use.
can you do something like:
if(event.currentTarget == event.target){
//item is being dropped on itself
}