This script was written to ease the attribute editing.
It creates a new child node to this node named <attributes>, and populates this <attributes> node with children nodes for all the attributes of the node.
Then the user can edit the attributes as nodes, add attributes and delete some, then if the user activates the script again, the attribute nodes are inserted in the node as its attributes and the <attributes> node is finally deleted.
The attributes are sorted before to be displayed as nodes and they are sorted back when inserted in the node.
I mapped this script to alt+3. So pressing this shows the attributes as nodes, then I edit them, and pressing it again inserts the attributes back to the node.
// @ExecutionModes({ON_SELECTED_NODE})
// This script was written to ease the attribute editing.
// It creates a new child node to this node named <attributes>, and populates this <attributes> node with children nodes for all the attributes of the node.
// Then the user can edit the attributes as nodes, add attributes and delete some, then if the user activates the script again, the attribute nodes are inserted in the node as its attributes and the <attributes> node is finally deleted.
// The attributes are sorted before to be displayed as nodes and they are sorted back when inserted in the node.
// Sort the attributes
def sortAttributes() {
def caseInsensitive = true
def comparator = {
def string = it[0]
if (caseInsensitive)
string.toLowerCase()
else
string
}
def nodeAttributes = node.attributes
// save original attributes
def attribs = []
nodeAttributes.names.eachWithIndex { name, i ->
attribs.add([name, nodeAttributes.get(i)])
}
// replace attributes
nodeAttributes.clear()
attribs.sort(comparator).each { k, v ->
nodeAttributes.add(k, v)
}
}
// Check if the '<attributes>' node was created previously
attributesNodeExists = false
attributesNodeIndex = 0
if (!node.children.empty) {
attributesNodeIndex = node.children.size() - 1
if (node.children[attributesNodeIndex].text == '<attributes>')
attributesNodeExists = true;
}
// If the '<attributes>' node was previously created, put its attributes back as attributes in the node
if (attributesNodeExists) {
// Remove all attributes from the node
node.attributes.clear()
// Put the nodes as attributes (the children of the children[attributesNodeIndex] "<attributes>" node)
node.children[attributesNodeIndex].children.each {
attributeName = it.text
attributeValue = it.children[0].text
node.attributes.add([attributeName, attributeValue])
}
// Delete the "<attributes>" node
node.children[attributesNodeIndex].delete()
// Sort again the attributes once added to the node, in case there were new attributes
sortAttributes()
}
// If the '<attributes>' node is not yet created, create it
else {
// Sort the attributes
sortAttributes()
// Put the attributes as nodes
attributesNode = node.createChild()
attributesNode.text = '<attributes>'
node.attributes.names.eachWithIndex{ name, i ->
value = node.attributes.get(i)
// Create the node for the attribute name
nameNode = attributesNode.createChild()
nameNode.text = name
// Create the node for the attribute value
valueNode = nameNode.createChild()
valueNode.text = value
}
}
I would like to add to this script later child nodes to each of the attribute nodes containing all the possible values found in the map so one could select from them a value for the current attribute.
Hope it may be helpful.
Best regards,
Alexandre
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have improved the script a little bit. If an attribute contains a comma-separated list, it will split the list into nodes. Then the user could edit the nodes and they will be joined back into a comma separated list to its attribute. The only bug there is that the list of nodes split dosen't sort. The function sortChildren() is not working for some reason. If somebody could tell me why it is not sorting that would be great.
Thanks.
// @ExecutionModes({ON_SELECTED_NODE})
// This script was written to ease the attribute editing.
// It creates a new child node to this node named <attributes>, and populates this <attributes> node with children nodes for all the attributes of the node.
// Then the user can edit the attributes as nodes, add attributes and delete some, then if the user activates the script again, the attribute nodes are inserted in the node as its attributes and the <attributes> node is finally deleted.
// The attributes are sorted before to be displayed as nodes and they are sorted back when inserted in the node.
// If an attribute is a comma-separated list like item1, Item2, Item3, it will be splitted into nodes, then the user can edit the list of nodes, and it will be joined by together upon insertion of the attributes. The nodes will be sorted.
// Functions
// Sort the attributes
def sortAttributes(pNode) {
def caseInsensitive = true
def comparator = {
def string = it[0]
if (caseInsensitive)
string.toLowerCase()
else
string
}
def nodeAttributes = pNode.attributes
// save original attributes
def attribs = []
nodeAttributes.names.eachWithIndex { name, i ->
attribs.add([name, nodeAttributes.get(i)])
}
// replace attributes
nodeAttributes.clear()
attribs.sort(comparator).each { k, v ->
nodeAttributes.add(k, v)
}
return nodeAttributes
}
// Sort the children
// Tofix: The function is not sorting
def sortChildren(pNode) {
def sorted = new ArrayList(pNode.children).sort{ it.text }
sorted.eachWithIndex { it, i ->
it.moveTo(pNode, i)
}
return pNode
}
// Main
// Check if the '<attributes>' node was created previously
attributesNodeExists = false
attributesNodeIndex = 0
if (!node.children.empty) {
attributesNodeIndex = node.children.size() - 1
if (node.children[attributesNodeIndex].text == '<attributes>')
attributesNodeExists = true;
}
// If the '<attributes>' node was previously created, put its attributes back as attributes in the node
if (attributesNodeExists) {
// Remove all attributes from the node
node.attributes.clear()
// Set the '<attributes>' node
attributesNode = node.children[attributesNodeIndex]
// Add each node as an attribute
attributesNode.children.each {
attributeValueNode = it.children[0]
attributeName = it.text
attributeValue = attributeValueNode.text
// If the child node is a comma-separated list node, then join them together by a comma
if (attributeValue.toString() == '<comma-separated list>') {
// Get and sort the children nodes before to join them
attributeValueNode = sortChildren(attributeValueNode)
// Join the nodes of the comma-separated list node
commaSeparatedList = ''
attributeValueNode.children.each {
commaSeparatedList += it.text.trim() + ', '
}
// Set the attribute to the comma-separated list (remove the trailing comma)
attributeValue = commaSeparatedList.replaceAll(', $', '');
}
// Add the attribute back to the node
node.attributes.add([attributeName, attributeValue])
}
// Delete the "<attributes>" node
node.children[attributesNodeIndex].delete()
// Sort again the attributes once added to the node, in case there were new attributes
node = sortAttributes(node)
}
// If the '<attributes>' node is not yet created, create it and add the attributes as nodes
else {
// Sort the attributes
node = sortAttributes(node)
// Create the '<attributes>' node that will contain the attributes as its children
attributesNode = node.createChild()
attributesNode.text = '<attributes>'
// Loop the attributes' names and indexes to add them as nodes to the '<attributes>' node
node.attributes.names.eachWithIndex{ attributeName, attributeIndex ->
// Get the attribute value using the index
attributeValue = node.attributes.get(attributeIndex)
// Create the attribute name node
attributeNameNode = attributesNode.createChild()
attributeNameNode.text = attributeName
// Create the attribute value node
attributeValueNode = attributeNameNode.createChild()
// If the value of the attribute is a comma-separated list, then split it to child nodes
if (attributeValue.toString().contains(',')) {
attributeValueNode.text = '<comma-separated list>'
// Split to child nodes (as items of the list)
commaSeparatedList = attributeValue.split(',')
commaSeparatedList.each {
listItemNode = attributeValueNode.createChild()
listItemNode.text = it
}
// Sort the children (of commaSeparatedlistNode) created by the split
attributeValueNode = sortChildren(attributeValueNode)
}
// If not a comma-separated list, create the node for the attribute value
else
attributeValueNode.text = attributeValue
}
}
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Sounds very interesting as a new way to edit attributes
...would like to add to this script later child nodes to each of the
attribute nodes containing all the possible values found in the map so one
could select from them a value for the current attribute...
If you finally devote to this, you could experiment using a checkbox-icon
on each node. The checkbox icon can toggle between marked/notmarked whith a
mouse click. Also its very visual and intuitive.
Just suggesting
On 10 Mar 2016 03:58, "Alexandre" alexandreviau77@users.sf.net wrote:
I have improved the script a little bit. If an attribute contains a
comma-separated list, it will split the list into nodes. Then the user
could edit the nodes and they will be joined back into a comma separated
list to its attribute. The only bug there is that the list of nodes split
dosen't sort. The function sortChildren() is not working for some reason.
If somebody could tell me why it is not sorting that would be great.
Thanks.
// @ExecutionModes({ON_SELECTED_NODE})
// This script was written to ease the attribute editing.// It creates a new child node to this node named <attributes>, and populates this <attributes> node with children nodes for all the attributes of the node. // Then the user can edit the attributes as nodes, add attributes and delete some, then if the user activates the script again, the attribute nodes are inserted in the node as its attributes and the <attributes> node is finally deleted.// The attributes are sorted before to be displayed as nodes and they are sorted back when inserted in the node.// If an attribute is a comma-separated list like item1, Item2, Item3, it will be splitted into nodes, then the user can edit the list of nodes, and it will be joined by together upon insertion of the attributes. The nodes will be sorted.
// Functions
// Sort the attributes
def sortAttributes(pNode) {
def caseInsensitive = true
def comparator = {
def string = it[0]
if (caseInsensitive)
string.toLowerCase()
else
string
}
def nodeAttributes = pNode.attributes
// save original attributes
def attribs = []
nodeAttributes.names.eachWithIndex { name, i ->
attribs.add([name, nodeAttributes.get(i)])
}
// replace attributes
nodeAttributes.clear()
attribs.sort(comparator).each { k, v ->
nodeAttributes.add(k, v)
}
return nodeAttributes
}
// Sort the children
// Tofix: The function is not sorting
def sortChildren(pNode) {
def sorted = new ArrayList(pNode.children).sort{ it.text }
sorted.eachWithIndex { it, i ->
it.moveTo(pNode, i)
}
return pNode
}
// Main
// Check if the '<attributes>' node was created previously
attributesNodeExists = false
attributesNodeIndex = 0
if (!node.children.empty) {
attributesNodeIndex = node.children.size() - 1
if (node.children[attributesNodeIndex].text == '<attributes>')
attributesNodeExists = true;
}
// If the '<attributes>' node was previously created, put its attributes back as attributes in the node
if (attributesNodeExists) {
// Remove all attributes from the node
node.attributes.clear()
// Set the '<attributes>' node
attributesNode = node.children[attributesNodeIndex]
// Add each node as an attribute
attributesNode.children.each {
attributeValueNode = it.children[0]
attributeName = it.text
attributeValue = attributeValueNode.text
// If the child node is a comma-separated list node, then join them together by a comma
if (attributeValue.toString() == '<comma-separated list>') {
// Get and sort the children nodes before to join them
attributeValueNode = sortChildren(attributeValueNode)
// Join the nodes of the comma-separated list node
commaSeparatedList = ''
attributeValueNode.children.each {
commaSeparatedList += it.text.trim() + ', '
}
// Set the attribute to the comma-separated list (remove the trailing comma)
attributeValue = commaSeparatedList.replaceAll(', $', '');
}
// Add the attribute back to the node
node.attributes.add([attributeName, attributeValue])
}
// Delete the "<attributes>" node
node.children[attributesNodeIndex].delete()
// Sort again the attributes once added to the node, in case there were new attributes
node = sortAttributes(node)
}
// If the '<attributes>' node is not yet created, create it and add the attributes as nodes
else {
// Sort the attributes
node = sortAttributes(node)
// Create the '<attributes>' node that will contain the attributes as its children
attributesNode = node.createChild()
attributesNode.text = '<attributes>'
// Loop the attributes' names and indexes to add them as nodes to the '<attributes>' node
node.attributes.names.eachWithIndex{ attributeName, attributeIndex ->
// Get the attribute value using the index
attributeValue = node.attributes.get(attributeIndex)
// Create the attribute name node
attributeNameNode = attributesNode.createChild()
attributeNameNode.text = attributeName
// Create the attribute value node
attributeValueNode = attributeNameNode.createChild()
// If the value of the attribute is a comma-separated list, then split it to child nodes
if (attributeValue.toString().contains(',')) {
attributeValueNode.text = '<comma-separated list>'
// Split to child nodes (as items of the list)
commaSeparatedList = attributeValue.split(',')
commaSeparatedList.each {
listItemNode = attributeValueNode.createChild()
listItemNode.text = it
}
// Sort the children (of commaSeparatedlistNode) created by the split
attributeValueNode = sortChildren(attributeValueNode)
}
// If not a comma-separated list, create the node for the attribute value
else
attributeValueNode.text = attributeValue
}
}
Ok I know what you mean by the checkbox icon, so I tried I put one but of course clicking a checked icon wont toggle to the unchecked icon. I don't now how to toggle that with the mouse click.
Best regards,
Alexandre
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
clicking a checked icon wont toggle to the unchecked icon
I had this impression that I've seen it work in freeplane before... Nonetheless as you indicate and I just confirmed both with freeplane 1.5.x and 1.3.15, clicking the icon does not make it toggle between check/unchecked...
Lets try the tap the greater common knowlegde from the community: Does anyone know if its possible (with a script) to make freeplane execute an action/script when the user clicks on a specific icon? Ideally it would be a generalization of the context-option "Link/Add hyperlink to menu item...", allowing to specify the icon and the action/script to be executed
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
//@ExecutionModes({ON_SELECTED_NODE})//Editattributesasnodes///Documentation//Version2//Description//Thisscriptwaswrittentoeasetheattributeediting.//Itcreatesanewchildnodetothisnodenamed<attributes>,andpopulatesthis<attributes>nodewithchildrennodesforalltheattributesofthenode.//Thentheusercanedittheattributesasnodes,addattributesanddeletesome,theniftheuseractivatesthescriptagain,theattributenodesareinsertedinthenodeasitsattributesandthe<attributes>nodeisfinallydeleted.//Theattributesaresortedbeforetobedisplayedasnodesandtheyaresortedbackwheninsertedinthenode.//Ifanattributeisacomma-separatedlistlikeitem1,Item2,Item3,itwillbesplittedintonodes,thentheusercaneditthelistofnodes,anditwillbejoinedbytogetheruponinsertionoftheattributes.Thenodeswillbesorted.//Ifthehotkeyispressedagainonaattributevalue,apicklistisshowntoselectvaluesforthisattribute,thelististakenfromtheattributeswiththesamenameinthecurrentmap.Iftheattributevalueisacomma-separatedlist,thenthelistcanbemulti-selected.//USAGE://1-Pressthehotkey(mappedtothisscript)onanodethatcontainsattributes,theattributeswillbeshownasnodes//2-Pressthesamehotkeyagainonanattributevalue(oronthe'<comma-separated list> node) and a picklist will be show allowing to select values for the attribute (comma-separated list'spicklisthasmultiselection)//3-Pressagainthesamehotkeyonthevaluesfromthepicklisttocheck/uncheckthem,thevaluesoftheattributeswillchangeatthesametime.//4-Toclosethepicklist,pressthesamehotkeyonthevalue(oronthe'<comma-separated list> node) and the picklist should close. // 5- Press the hotkey on the '<attributes>' node to show a list of all the attribute names in the current map to select from. Press the hotkey on these attribute names in the picklist to check/uncheck them, then press the hotkey again on the <attributes> node to set the list of attribute to the selection in the picklist. // 6- To close the attributes as nodes and 'save' the attributes in the node, press the same hotkey on the root node where the attributes are stored. // 7- Add new nodes to picklists and then press the hotkey on them and they will become checked. This way it allow to add new picklists values. It works also for the attributes picklist also. // 8- So basically, simply press the hotkey on any node that has the list icon or the checkbox icon. // Bugs // When closing the comma-separated list, it should update the inner attributes because then re-opening it, should show then values that where added (or removed) before closing it. // If a comma-separated list attribute (in other attributes) is empty on opening, then the picklist doesn'tshow,thereisanerrormessage.Itshowsonlywhentheyiscommaseparateddataalreadyinside.//Ifanattributehasnovaluenodethereisanullerror.//Ifancomma-separatedattributeisaddedwiththeattributespicklist,ithasnovalueandifthenweopenthepicklistforthisaddedattribute,itshowsvaluesascomma-separatedvalue(textseparatedwithcommas)...Iamnotsurethiscanbefixedasthereisnoreallywayofknowingthisattributeiscommaseparatedbycheckingdatafromthisnodeonly,Ishouldcheckgloballyintheotherattributeswiththesamenameinthemapifthisvalueisacommaseparatedlist,thenIwouldneedtosetmaybeacommaasthevalueintheinnerattributestoindicateitisacomma-separatedlistbutitisempty.Soitisnotsomuchabugbutitwouldbebettertoprovideexpectedbehaviorwiththisattribute.//Todo//FR20160331111321Maybethiscodeforthecomma-separatedlistshouldbeinafunctionforthecommaseparatedlistfunctions,andcalledherebypassingthenodeandsettingthecontext//Ifavalueisdeletedina(single)valuepicklist,thenitshouldbedeletedeverywhereinthemap//Ifavalueisdeletedina(comma-separated)valuepicklist,thenitshouldbedeletedeverywhereinthemap//NowthatthesetcontextfunctionworksandIcanoperateonsurroundingnodes,removethecodethat'closes'thecommaseparated(thatisintheattributesdeactivateIthink)andsimplycallthecommaseparateddeactivatefromtheattributesdeactivate//Maybeinthemulti-selectpicklist,iftheuserholdsctrlandclickseveralnodestoselectmultiplenodes,thenpressingthehotkeywouldselect/unselectthesenodesdependingiftheyarecheckedorunchecked.//Maybedoadatepicklisttoselectdatesandtimes,atreebyyear>month>day>hour>minutes//Ifthereare2attributeswiththesamenameandoneofthemisaVALUEandtheotherisaCOMMA_SEPARATED_LIST_VALUEandtheVALUEisfirstinthelist,thenthedefineType()functionwilldetectitasaVALUE,whichisfinebutmaybetherecouldbeavalidationtopreventduplicatedattributenames.//Versionhistory//2016-03-31_08.12.55//Fixed:Whenshowingattributes,therewerenocheckboxes(introducedwhilefixingandimproving)//Done:Featurerequest:FR20160329105156Deletethepicklistvalueswouldremovetheattributesgloballyatthemaplevel//Fixed:thedeletionofalltheattributesinthemapfortheattributesdeletedinthepicklist(itisintheattributesdeactivatefunction)//Fixed:Afterdeletingoraddingitemstotheattributesnodelist,ifIactivatethepicklistitshouldaddtheitemstotheinnerattributes//Fixed:IftheattributesareclosedfromtherootNodewiththepicklistclosed,thentheattributesarenotaddedtotheinnerAttributes,butifitisopenedthentheyareadded.//Fixed:Ifaitemisaddedtothenodesattributesandaftertheattributespicklistisopened,thenewitem(s)wherenotaddedtothepicklist,,.itisfixednow.//Fixed:IfIcheckanewitemintheattributespicklistandthenpressthehotkeyfromtherootNode,thenewitemisnotaddedtotherootNodesinnerattributes,moreoverthe<picklist>itemisaddedtotherootnodesattributes.//Fixed:Closingtheattributes(fromthe<attributes>nodes)doesn't add to the rootNode the newly checked attributes from the picklist // Fixed: Closing the rootNode deletes the attributes... // 2016-03-30_08.05.53 // Fixed: Closing the root node doesn'tupdatetheinnerattributes(itwasnewbugintroducedwithchanges)//Fixed:Closingthenodesattributesdosen't update the inner attributes // 2016-03-29_11.01.44 // Fixed: (Bug20160329122350) There seems to be a new bug, if the picklist is opened for the attributes, and the rootnode is deactivated, then picklist is added as an inner attribute.... it doesn'tdothatforthecommaseparatedpicklistandthevaluepicklist.Iftheattributespicklistisclosedbeforetherootnodethenitisfine.Ishoulddeactivatetheattributesbefore.SeethisreferenceBug20160329122350inthecode//2016-03-24_13.41.01//Fixed:Ifanewvalueisaddedtothecomma-separatedpicklistandthenitischecked,itisaddedtothecomma-separatedlistanditsnodevaluesbutisnotsorted.Thisisfixed.//2016-03-24_01.22.32//Fixed:Ifanattributevaluehadaemptyvalue(notnonode)butanodewithouttext,therewasanindexerrorbecauseofthecomparatorfunction.Itisfixed.//2016-03-24_12.55.19//Added:Possibilitytoaddnewattributesvaluetotheattributespicklist.//2016-03-24_08.59.14//Added:Iconstoidentifythenodesthatcanbeexpandedwiththehotkey.Soitisclearnowtotheuserswheretheytheycanpressthehotkey:onthelisticonoronthecheckboxicon.//Modif:SmallchangeinconditionsinthedefineType()//2016-03-23_10.34.46(uploaded)//Fixed:Ifixedanimportantbug:ifthenodehadchildrentheattributesasnodeswherenotshowing.Ididn't see that because my task management nodes have all information as attributes. // 2016-03-23_01.48.40 (uploaded) // Fixed: When the node attributes are closed, and there were comma-separated picklist opened with modified list of attribute values (like values added) but without having the comma-separated list 'hotkeyed' yet so the comma-separated value is set to the list of attributes, the comma-separated list was not set, but now it is set before closure. // Fixed: The sorting of the comma-separated values when the list of comma-separated value nodes is closed. // 2016-03-23_01.02.46 (uploaded) // Added: Possibility to add picklist values: Add new nodes to picklists and then press the hotkey on them and they will become checked. This way it allow to add new picklists values. // 2016-03-23_12.49.43 (uploaded) // Fixed: Something that happened with the comma-separated list when opened, I fixed that // Modified: The comments header order // 2016-03-23_11.03.46 (uploaded) // Fixed: The picklist of the comma-separated values, the split on ',' messed-up the sorting, so the elements needed to be trimmed before to be sorted. // 2016-03-22_07.58.19 (uploaded) // Added: Picklist for attributes: if attribute names in the picklist are unchecked, they will be removed from the list of attributes. If some attribute names in the picklist are checked but they are not in the list of attributes, then will be added with an empty value. // Fixed: Sorting everywhere adding a global comparator with case insensitive // Fixed: Tokenize had to split on ',' rather than ',' or the sorting was changing because of the left-over space // Modified: I changed some variable names like name for attributeName and i for attributeIndex // 2016-03-22_01.11.32 (uploaded) 2016-03-22_11.11.42, added toString() before the contains() // Fixed: A bug introduced with the // 2016-03-22_12.27.38 (uploaded) // Fixed: It is not possible now to have all values of the value picklist unchecked, 1 has to be checked, so uncheck of the current item doesn'tuncheck.//2016-03-22_11.11.42(uploaded)//Modif:defineType()anddefineState()functionsmovedintheinitialize()function//Fixed:Ifaftereditingacomma-separatedlist,thereisonly1valueinthecomma-separatedlist,thenitisnotdetectedasacommaseparatedlist,thendoingthehotkeyonthenodeoronthe<comma-separatedlist>nodehasstrangebehaviors...Itisbecauseitisdetectedasavalueafteredition,butshouldbeVALUE_COMMA_SEPARATED.ThisisfixednowbycheckingthevalueoftheinnerAttributeinthenodeandnotthecurrentvalueofthenode.//2016-03-22_09.24.54(uploaded)//Fixed:Bugonthemaprootnode//Modif:Removedthemessageandthecodetomanagethenodethatdon't have attributes, and I added code to set the type as NODE for this and it manages itself as a deactivated NODE. // Addition: Added some initialized variables and put the initialization code in a function. // Modif: I used the lastChildText variable instead of the LastChildIndex at 2 places. // 2016-03-22_07.49.50 (uploaded) // Added: Message if the node doesn'thaveattributes,thena<attributes>nodeiscreatedtoallowmanualcreationofattributes.//2016-03-22_07.27.19(uploaded)//Fixed:Sortingthecomma-separatednodevalues//2016-03-22_07.20.21(uploaded)//Fixed:picklistsonnumericvalueswasnotworking//2016-03-22_00.57.46(uploaded)///Declaration/* DEBUG = true */DEBUG=false//TypesUNDEFINED=0NODE=1ATTRIBUTES=2ATTRIBUTES_PICKLIST=3ATTRIBUTES_PICKLIST_VALUE=4ATTRIBUTE=5VALUE=6VALUE_COMMA_SEPARATED=7PICKLIST=8PICKLIST_VALUE=9COMMA_SEPARATED_LIST=10COMMA_SEPARATED_LIST_VALUE=11COMMA_SEPARATED_LIST_PICKLIST=12COMMA_SEPARATED_LIST_PICKLIST_VALUE=13//StatesNONE=0ACTIVATED=1DEACTIVATED=2//ThesignaturetexttoidentifynodesATTRIBUTES_TEXT='<attributes>'PICKLIST_TEXT='<picklist>'COMMA_SEPARATED_LIST_TEXT='<comma-separated list>'//IconsLIST_ICON='list'//Functions//Initializationdefinitialization(){//Usedtocomparevalues(caseinsensitive)duringsorting.//NOTE:ThesortingoftheCOMMA_SEPARATED_LIST_TEXTisnotusingthecomparatorbecausethechildrenobjectsseemsnotComparablesoitishard-codedas.toLower()(insensitive).defcaseInsensitive=truecomparator={if(it.size()>0){defstring=it[0]if(caseInsensitive)string.toLowerCase()elsestring}else''}setContext(node)}defsetContext(pNode){//Functionthatdefineswhereweareinthetryandwhatactiontotakeandthepropertiesusefulforthecurrentcontext//NOTE:WhenthesetcontextisusedandthenthepNodeinafunction,Ihavetoexplicitlyuseforexample'pNode.children[...' because only 'children' will not use the pNode passed but the current node, not the node of the context that was set. // pNode has children hasChildren = false if (!pNode.children.empty) hasChildren = true // Index of the last child lastChildIndex = -1 if (hasChildren) lastChildIndex = pNode.children.size() - 1 // Text of the last child pNode lastChildText = '' if (hasChildren) lastChildText = pNode.children[lastChildIndex].text // Text of the parent pNode parentText = '' if (pNode.parent != null) parentText = pNode.parent.text // Text of the grandParent pNode grandParentText = '' if (pNode.parent != null) if (pNode.parent.parent != null) grandParentText = pNode.parent.parent.text // pNode has "inner" attributes hasInnerAttributes = false if (!pNode.attributes.empty) hasInnerAttributes = true // pNode has icons hasIcons = false if (pNode.icons.size() > 0) hasIcons = true type = defineType(pNode) state = defineState(pNode, type) } // Utility // Sort the attributes def sortAttributes(pNode) { def nodeAttributes = pNode.attributes // save original attributes def attribs = [] nodeAttributes.names.eachWithIndex { attributeName, attributeIndex -> attribs.add([attributeName, nodeAttributes.get(attributeIndex)]) } // replace attributes nodeAttributes.clear() attribs.sort(comparator).each { k, v -> nodeAttributes.add(k, v) } return nodeAttributes } // Get the attribute inner value from a attributeName def getInnerAttributeValue(rootNode, searchedAttributeName) { attributeValue = '' rootNode.attributes.names.eachWithIndex { attributeName, attributeIndex -> if (attributeName == searchedAttributeName) attributeValue = rootNode.attributes.get(attributeIndex) } return attributeValue } // Message box (mainly to debug) def m(message) { javax.swing.JOptionPane.showMessageDialog(null, message); } // Append debug messages to the debug log def d(message) { if (DEBUG) { BufferedWriter bw = new BufferedWriter(new FileWriter("c:/temp/debug.txt", true)); bw.write(message); bw.newLine(); bw.flush(); bw.close(); } } // Object definitions // Define type def defineType(pNode) { def type = UNDEFINED // NODE if (hasInnerAttributes) type = NODE // ATTRIBUTES else if (pNode.text == ATTRIBUTES_TEXT) type = ATTRIBUTES // ATTRIBUTES_PICKLIST else if (parentText == ATTRIBUTES_TEXT && pNode.text == PICKLIST_TEXT) type = ATTRIBUTES_PICKLIST // ATTRIBUTES_PICKLIST_VALUE else if (grandParentText == ATTRIBUTES_TEXT && parentText == PICKLIST_TEXT) type = ATTRIBUTES_PICKLIST_VALUE // ATTRIBUTE else if (parentText == ATTRIBUTES_TEXT) type = ATTRIBUTE // VALUE AND VALUE_COMMA_SEPARATED else if (grandParentText == ATTRIBUTES_TEXT) { // Get the inner attribute value from the rootNode because if after the comma separated list modification, if there is only 1 item in the list, it will be considered a VALUE and not at VALUE_COMMA_SEPARATED_LIST, so check the inner attribute to be sure what kind of node it is def innerAttributeValue = getInnerAttributeValue(parent.parent.parent, parentText) if (innerAttributeValue.toString().contains(',')) type = VALUE_COMMA_SEPARATED else type = VALUE } // PICKLIST else if (pNode.text == PICKLIST_TEXT && parentText != COMMA_SEPARATED_LIST_TEXT) type = PICKLIST // PICKLIST_VALUE else if (parentText == PICKLIST_TEXT && grandParentText != COMMA_SEPARATED_LIST_TEXT) type = PICKLIST_VALUE // COMMA_SEPARATED_LIST else if (pNode.text == COMMA_SEPARATED_LIST_TEXT) type = COMMA_SEPARATED_LIST // COMMA_SEPARATED_LIST_VALUE else if (parentText == COMMA_SEPARATED_LIST_TEXT && pNode.text != PICKLIST_TEXT) type = COMMA_SEPARATED_LIST_VALUE // COMMA_SEPARATED_LIST_PICKLIST else if (parentText == COMMA_SEPARATED_LIST_TEXT && pNode.text == PICKLIST_TEXT) type = COMMA_SEPARATED_LIST_PICKLIST // COMMA_SEPARATED_LIST_PICKLIST_VALUE else if (grandParentText == COMMA_SEPARATED_LIST_TEXT && parentText == PICKLIST_TEXT) type = COMMA_SEPARATED_LIST_PICKLIST_VALUE // NODE (without attributes) else if (!hasInnerAttributes) type = NODE // Debug type if (DEBUG) { def sType = '' if (type == UNDEFINED) sType = 'UNDEFINED' else if (type == NODE) sType = 'NODE' else if (type == ATTRIBUTES) sType = 'ATTRIBUTES' else if (type == ATTRIBUTE) sType = 'ATTRIBUTE' else if (type == VALUE) sType = 'VALUE' else if (type == VALUE_COMMA_SEPARATED) sType = 'VALUE_COMMA_SEPARATED' else if (type == PICKLIST) sType = 'PICKLIST' else if (type == PICKLIST_VALUE) sType = 'PICKLIST_VALUE' else if (type == COMMA_SEPARATED_LIST) sType = 'COMMA_SEPARATED_LIST' else if (type == COMMA_SEPARATED_LIST_VALUE) sType = 'COMMA_SEPARATED_LIST_VALUE' else if (type == COMMA_SEPARATED_LIST_PICKLIST) sType = 'COMMA_SEPARATED_LIST_PICKLIST' else if (type == COMMA_SEPARATED_LIST_PICKLIST_VALUE) sType = 'COMMA_SEPARATED_LIST_PICKLIST_VALUE' m(sType) } return type } // Define state def defineState(pNode, type) { def state = NONE // NODE if (type == NODE) { if (lastChildText == ATTRIBUTES_TEXT) state = ACTIVATED else state = DEACTIVATED } // ATTRIBUTES else if (type == ATTRIBUTES) { if (lastChildText == PICKLIST_TEXT) state = ACTIVATED else state = DEACTIVATED } // ATTRIBUTES_PICKLIST (NO ACTION) // ATTRIBUTES_PICKLIST_VALUE else if (type == ATTRIBUTES_PICKLIST_VALUE) { if (hasIcons) { if (pNode.icons[0] == 'checked') state = ACTIVATED else if (pNode.icons[0] == 'unchecked') state = DEACTIVATED } else // If there is no icons set as DEACTIVATED so it will be checked when pressing the hotkey. state = DEACTIVATED } // ATTRIBUTE else if (type == ATTRIBUTE) { if (lastChildText == COMMA_SEPARATED_LIST_TEXT) state = ACTIVATED } // VALUE else if (type == VALUE) { if (!hasChildren) state = DEACTIVATED else if (lastChildText == PICKLIST_TEXT) state = ACTIVATED } // VALUE_COMMA_SEPARATED else if (type == VALUE_COMMA_SEPARATED) { if (lastChildText == COMMA_SEPARATED_LIST_TEXT) state = ACTIVATED else state = DEACTIVATED } // PICKLIST (NO ACTION) // PICKLIST_VALUE else if (type == PICKLIST_VALUE) { if (hasIcons) { if (pNode.icons[0] == 'checked') state = ACTIVATED else if (pNode.icons[0] == 'unchecked') state = DEACTIVATED } else // If there is no icons set as DEACTIVATED so it will be checked when pressing the hotkey. state = DEACTIVATED } // COMMA_SEPARATED_LIST else if (type == COMMA_SEPARATED_LIST) { if (lastChildText == PICKLIST_TEXT) state = ACTIVATED else state = DEACTIVATED } // COMMA_SEPARATED_LIST_VALUE else if (type == COMMA_SEPARATED_LIST_VALUE) { if (pNode.icons[0] == 'checked') state = ACTIVATED else if (pNode.icons[0] == 'unchecked') state = DEACTIVATED } // COMMA_SEPARATED_LIST_PICKLIST else if (type == COMMA_SEPARATED_LIST_PICKLIST) state = ACTIVATED // COMMA_SEPARATED_LIST_PICKLIST_VALUE else if (type == COMMA_SEPARATED_LIST_PICKLIST_VALUE) { if (hasIcons) { if (pNode.icons[0] == 'checked') state = ACTIVATED else if (pNode.icons[0] == 'unchecked') state = DEACTIVATED } else // If there is no icons set as DEACTIVATED so it will be checked when pressing the hotkey. state = DEACTIVATED } // Debug state if (DEBUG) { def sState = '' if (state == NONE) sState = 'NONE' else if (state == DEACTIVATED) sState = 'DEACTIVATED' else if (state == ACTIVATED) sState = 'ACTIVATED' m(sState) } return state } // Actions // NODE def nodeActivate() { // Show the attributes and their values as nodes, if attributes values are comma-separated list they are expanded as a COMMA_SEPARATED_LIST_TEXT branch // Sort the attributes node = sortAttributes(node) // Create the ATTRIBUTES node that will contain the attributes as its children attributesNode = node.createChild() attributesNode.text = ATTRIBUTES_TEXT attributesNode.icons.addIcon(LIST_ICON) // Loop the attributes'namesandindexestoaddthemasnodestotheATTRIBUTESnodenode.attributes.names.eachWithIndex{attributeName,attributeIndex->// Get the attribute value using the indexattributeValue =node.attributes.get(attributeIndex)// Create the attribute name nodeattributeNameNode =attributesNode.createChild()attributeNameNode.text =attributeName// Create the attribute value node (if it is a comma-separated list, it will be added as it is, a comma-separated list)attributeValueNode =attributeNameNode.createChild()attributeValueNode.text =attributeValueattributeValueNode.icons.addIcon(LIST_ICON)// If the value of the attribute is a comma-separated list, then split it to child nodesif(attributeValue.toString().contains(',')){commaSeparatedListNode =attributeValueNode.createChild()commaSeparatedListNode.text =COMMA_SEPARATED_LIST_TEXTcommaSeparatedListNode.icons.addIcon(LIST_ICON)// Split to child nodes (as items of the list)commaSeparatedListValues =attributeValue.tokenize(',')*.trim().sort(comparator).unique()// Items must be trimmed before to be sorted or the sort will be brokencommaSeparatedListValues.each{listItemNode =commaSeparatedListNode.createChild()listItemNode.text =it}}}}defnodeDeactivate(){// Hide the attributes as nodes and set the inner attributes to the values set in the nodes// Deactivate the attributessetContext(node.children[node.children.size()-1]) // Impersonate as like to be the ATTRIBUTES_TEXT node attributesDeactivate(node.children[node.children.size()-1]) // Delete the ATTRIBUTES_TEXT node setContext(node) // Switch back "to be" the rootNode node.children[lastChildIndex].delete() } // ATTRIBUTES def attributesActivate() { // First add the attributes as nodes to the inner attributes attributesAddToInnerAttributes(node) setContext(node) // The setContext() is used in attributesAddToInnerAttributes() so we need to set it here // Create the ATTRIBUTE_PICKLIST node pickListNode = node.createChild() lastChildIndex++ // It has to be incremented because we add a node pickListNode.text = PICKLIST_TEXT def attributeNames = [] // Loop all the nodes in the map c.findAllDepthFirst().each { n -> // If the node has attributes if (!n.attributes.empty) { // Loop each attributes in the node n.attributes.names.eachWithIndex { attributeName, attributeIndex -> // Put the attributes (uniquely) in a temporary list attributeNames.add(attributeName.trim()) } attributeNames = attributeNames.unique() // Reduce the list to take less memory } } // Add the attributes as nodes to the picklist attributeNames.sort(comparator).unique().each { newValueNode = pickListNode.createChild() newValueNode.text = it } // Check the values in the picklist that correspond to the attributes values list node.children[lastChildIndex].children.each { // Loop the ATTRIBUTE_PICKLIST values pickListNode = it pickListNode.icons.clear() pickListNode.icons.addIcon('unchecked') // Loop the attribute values nodes and see if the current picklist value is there, if yes then check it it in the picklist value node.children.each { if (it.text != PICKLIST_TEXT) { if (pickListNode.text.toString().trim() == it.text.toString().trim()) { // If the picklist value is equal to the comma-separated value pickListNode.icons.clear() pickListNode.icons.addIcon('checked') } } } } } def attributesDeactivate(pNode) { d('ATTRIBUTESDEACTIVATE' + pNode.text) if (pNode.children[lastChildIndex].text == PICKLIST_TEXT) { // This condition is because this function is used in another context, so when it is used there may be no picklist opened, so we don'tprocessthepicklistcodethen///Removeattributesinthemap//FR20160329105156//Ifavalueinthepicklistisremovedfromthepicklistthenitwillberemovedeverywhereitisusedinthecurrentmap//I'm not sure this code goes exactly here c.findAllDepthFirst().each { n -> // If the node has attributes if (!n.attributes.empty) { // Loop each attributes in the current node of the map n.attributes.names.eachWithIndex { attributeName, attributeIndex -> // For each of the attribute check if it is in the PICKLIST, if not then delete it from the current node found = false pNode.children[lastChildIndex].children.each { // Loop the PICKLIST d(attributeName + '=' + it.text) if (attributeName == it.text) found = true } // If the attribute was not found in the PICKLIST, delete it if (!found) { //d('delete' + attributeName) // Tofix n.attributes.removeAll(attributeName) } } } } /// Picklist checked to temp list def attributes = [] // Totest: It seems that the 2 blocks of code below that get the attributes could be one 1 that gets the checked attributes, it seems only the 2nd one could be enough // Keep selected attributes: Put the attributes and their names to a temp 'attributes' list if they are checked in the picklist (not to have the attributes that are unchecked in the picklist) d('KEEPSELECTED') // Loop the attributes names pNode.children.each { if (it.text != PICKLIST_TEXT) { attributeName = it.text attributeValue = it.children[0].text // Loop the PICKLIST attribute names pNode.children[lastChildIndex].children.each { // If the attribute name is found in the picklist and it is checked, then add it to the temporary array if (it.text == attributeName && it.icons[0] == 'checked') { d(attributeName + '' + attributeValue) attributes.add([attributeName,attributeValue]) } } } } // Add newly selected items: Add also the attributes that are checked in the picklist to the 'attributes' list and that are not in the attribute node list d('ADDNEWLY...') def found = false // Loop the PICKLIST pNode.children[lastChildIndex].children.each { // If checked in the picklist, see if it is in the temporary list, if not add it if (it.icons[0] == 'checked') { pickListAttributeName = it.text found = false attributes.each { attributeName, attributeValue -> if (attributeName == pickListAttributeName) found = true } if (!found) { // Add to the attributes list with an empty value d(pickListAttributeName) attributes.add([pickListAttributeName,'']) } } } /// Create all nodes from the temporary list // Remove all child nodes pNode.children.each { it.delete() } // Create the nodes d('CREATETHENODES') attributes.sort(comparator).each { attributeName, attributeValue -> // Create the attribute name node and set it attributeNameNode = pNode.createChild() attributeNameNode.text = attributeName d(attributeNameNode.text) // Create the attribute value node and set it attributeValueNode = attributeNameNode.createChild() attributeValueNode.text = attributeValue attributeValueNode.icons.addIcon(LIST_ICON) } } /// Add each node as an attribute "inside" the root node d('ADDEACHNODE...') d(pNode.text) attributesAddToInnerAttributes(pNode) } def attributesAddToInnerAttributes(pNode) { // Add each node as an attribute "inside" the root node d('ADDEACHNODE') // Remove all attributes from the root node pNode.parent.attributes.clear() // Totest Bug20160329122350 pNode.children.each { if (it.text != PICKLIST_TEXT) { // If the child of ATTRIBUTES_TEXT is not the picklist def attributeValueNode = it.children[0] // FR20160331111321 Maybe this code for the comma-separated list should be in a function for the commaseparated list functions, and called here by passing the node and setting the context // Set the comma-separated attribute values if not set yet // Check if the child of the attribute value is COMMA_SEPARATED_LIST_TEXT if (!attributeValueNode.children.empty) { if (attributeValueNode.children[0].text == COMMA_SEPARATED_LIST_TEXT) { setContext(attributeValueNode.children[0]) def commaSeparatedListValue = '' // Loop the comma-separated list attribute values attributeValueNode.children[0].children.sort{it.text.toLowerCase()}.each { // We cannot use .sort(comparable) because the children node is not a Comparable it seems if (it.text != PICKLIST_TEXT) commaSeparatedListValue += it.text + ',' } // Remove trailing comma commaSeparatedListValue = commaSeparatedListValue.replaceAll(',$', ''); // Set the comma-separated list attributeValueNode.text = commaSeparatedListValue } } // Add the attribute back to the node def attributeName = it.text def attributeValue = attributeValueNode.text pNode.parent.attributes.add([attributeName,attributeValue]) d(attributeName + '' + attributeValue) } } // Sort again the attributes once added to the node, in case there were new attributes node = sortAttributes(node) } // ATTRIBUTES_PICKLIST_VALUE def attributesPickListValueActivate() { // Check the current node node.icons.clear() node.icons.addIcon('checked') } def attributesPickListValueDeactivate() { // Uncheck the current node node.icons.clear() node.icons.addIcon('unchecked') } // VALUE def valueActivate() { // Gets all the possible values for that attribute in the map and shows them uniquely and sorted in the PICKLIST_TEXT pickListNode = node.createChild() pickListNode.text = PICKLIST_TEXT // Add values to the PICKLIST def attributeNameToSearch = parent.text def attributeValues = [] // Loop all nodes in the map c.findAllDepthFirst().each { n -> // Loop the children of the current node n.children.each { // If the node has attributes if (!it.attributes.empty) { // Loop the attributes and check if it contains the attribute searched for, if yes then get its value it.attributes.names.eachWithIndex { attributeName, attributeIndex -> if (attributeName == attributeNameToSearch) attributeValues.add(it.attributes.get(attributeIndex).toString().trim()) } attributeValues = attributeValues.unique() // Reduce the list to take less memory } } } // Show the attributes collected as new nodes attributeValues.sort(comparator).unique().each { newValueNode = pickListNode.createChild() newValueNode.text = it } // Check the value in the picklist that correspond to the current value and set the other as unchecked node.children[lastChildIndex].children.each { // Loop the PICKLIST values it.icons.clear() if (it.text.toString().trim() == text.toString().trim()) it.icons.addIcon('checked') else it.icons.addIcon('unchecked') } } def valueDeactivate() { // Get the first checked value in the picklist opened and set it as the value and close the picklist def checkedText = '' // This code is not necessary because now the attribute value is set at the same time the picklist value is checked, if I want to disable the setting at check then disable it and uncomment this code to set the value from the attribute value // Loop the children of the PICKLIST // node.children[0].children.each { // if (it.icons[0] == 'checked') // checkedText = it.text // return true; // Break from the each closure // } //node.text = checkedText // Remove the PICKLIST node.children[0].delete() } // PICKLIST_VALUE def pickListValueActivate() { // Put unchecked icons to all values (this way of clearing all here avoids multiple selection when not COMMA_SEPARATED_LIST parent.children.each { it.icons.clear() it.icons.addIcon('unchecked') } // Check the current node node.icons.clear() node.icons.addIcon('checked') // Set the value of the attribute parent.parent.text = node.text.trim() } // VALUE_COMMA_SEPARATED def valueCommaSeparatedActivate() { // Split the comma-separated list into child nodes of the COMMA_SEPARATED_LIST_TEXT node commaSeparatedListNode = node.createChild() commaSeparatedListNode.text = COMMA_SEPARATED_LIST_TEXT // Split to child nodes (as items of the list) commaSeparatedListValues = node.text.tokenize(',')*.trim().sort(comparator).unique() // Items must be trimmed before to be sorted or the sort will be broken commaSeparatedListValues.each { listItemNode = commaSeparatedListNode.createChild() listItemNode.text = it } } def valueCommaSeparatedDeactivate() { // Build the comma-separated list from the nodes commaSeparatedListValue = '' children[0].children.sort{it.text.toLowerCase()}.each { // We cannot use .sort(comparable) because the children node is not a Comparable it seems if (it.text != PICKLIST_TEXT) commaSeparatedListValue += it.text + ',' } // Remove trailing comma commaSeparatedListValue = commaSeparatedListValue.replaceAll(',$', ''); // Set the comma-separated list node.text = commaSeparatedListValue // Delete the COMMA_SEPARATED_LIST_TEXT node children[0].delete() } // COMMA_SEPARATED_LIST def commaSeparatedListActivate() { pickListNode = node.createChild() lastChildIndex++ // It has to be incremented because we add a node pickListNode.text = PICKLIST_TEXT // Add values to the PICKLIST def attributeNameToSearch = parent.parent.text def attributeValues = [] // Loop all nodes in the map c.findAllDepthFirst().each { n -> // Loop the children of the current node n.children.each { // If the node has attributes if (!it.attributes.empty) { // Loop the attributes and check if it contains the attribute searched for, if yes then get its value it.attributes.names.eachWithIndex { attributeName, attributeIndex -> if (attributeName == attributeNameToSearch) { // Here split the attributes and add them commaSeparatedSplittedValues = it.attributes.get(attributeIndex).tokenize(',')*.trim().sort(comparator).unique() // Items must be trimmed before to be sorted or the sort will be broken // Add each of the splitted values individually commaSeparatedSplittedValues.each { attributeValues.add(it.toString()) } } attributeValues = attributeValues.unique() // Reduce the list to take less memory } } } } // Show the attributes collected as new nodes attributeValues.sort(comparator).unique().each { newValueNode = pickListNode.createChild() newValueNode.text = it } // Check the values in the picklist that correspond to the values in the comma separated list and set the other as unchecked node.children[lastChildIndex].children.each { // Loop the PICKLIST values pickListNode = it pickListNode.icons.clear() pickListNode.icons.addIcon('unchecked') // Loop the comma-separated nodes and see if the current picklist value is there, if yes then check it it in the picklist value node.children.each { if (it.text != PICKLIST_TEXT) { if (pickListNode.text.toString().trim() == it.text.toString().trim()) { // If the picklist value is equal to the comma-separated value pickListNode.icons.clear() pickListNode.icons.addIcon('checked') } } } } } def commaSeparatedListDeactivate() { // Delete the PICKLIST if (lastChildText == PICKLIST_TEXT) { node.children[lastChildIndex].delete() lastChildIndex-- // It has to be decremented because we remove a node (but in practice it has no effect) } } // COMMA_SEPARATED_LIST_PICKLIST_VALUE def commaSeparatedListPickListValueActivate() { // Check the current node node.icons.clear() node.icons.addIcon('checked') // Build the COMMA_SEPARATED_LIST value from the PICKLIST checked values commaSeparatedListPickListValueReloadList() } def commaSeparatedListPickListValueDeactivate() { // Uncheck the current node node.icons.clear() node.icons.addIcon('unchecked') // Build the COMMA_SEPARATED_LIST value from the PICKLIST checked values commaSeparatedListPickListValueReloadList() } def commaSeparatedListPickListValueReloadList() { // Build the COMMA_SEPARATED_LIST value from the PICKLIST checked values commaSeparatedListValue = '' // Remove all comma-separated values as node parent.parent.children.each { if (it.text != PICKLIST_TEXT) it.delete() } lastChildIndex = 0 parent.children.sort{it.text.toLowerCase()}.each { if (it.icons[0] == 'checked') { // Append the value as a node newValueNode = parent.parent.createChild() newValueNode.text = it.text.trim() lastChildIndex++ // Move the node created appendIndex = lastChildIndex - 1 // -1 because it is added before the picklist newValueNode.moveTo(node.parent.parent, appendIndex) // Append the value // Build the comma-separated list commaSeparatedListValue += it.text.trim() + ',' } } // Remove trailing comma commaSeparatedListValue = commaSeparatedListValue.replaceAll(',$', '');//Setthecomma-separatednodevaluetothevaluebuiltaboveparent.parent.parent.text=commaSeparatedListValue}///Maininitialization()//Doactionsaccordingtothetypeandstate//NODEif(type==NODE&&state==DEACTIVATED)nodeActivate()//Showtheattributesandtheirvaluesasnodes,ifattributesvaluesarecomma-separatedlisttheyareexpandedasaCOMMA_SEPARATED_LIST_TEXTbranchelseif(type==NODE&&state==ACTIVATED)nodeDeactivate()//Hidetheattributesasnodesandsettheinnerattributestothevaluessetinthenodes//ATTRIBUTESelseif(type==ATTRIBUTES&&state==ACTIVATED)attributesDeactivate(node)elseif(type==ATTRIBUTES&&state==DEACTIVATED)attributesActivate()//ATTRIBUTES_PICKLIST(NOACTION)//ATTRIBUTES_PICKLIST_VALUEelseif(type==ATTRIBUTES_PICKLIST_VALUE&&state==ACTIVATED)attributesPickListValueDeactivate()elseif(type==ATTRIBUTES_PICKLIST_VALUE&&state==DEACTIVATED)attributesPickListValueActivate()//ATTRIBUTE(NOACTION)//VALUEelseif(type==VALUE&&state==DEACTIVATED)valueActivate()//Getallthepossiblevaluesofthatattributeinthemap,checkalltheothernodesandtheirvaluesandgetauniquelistelseif(type==VALUE&&state==ACTIVATED)valueDeactivate()//Getthefirstcheckedvalueinthepicklistopenedandsetitasthevalueandclosethepicklist//PICKLIST(NOACTION)//PICKLIST_VALUE(NODEACTIVATEbecauseonevaluehastobechecked)elseif(type==PICKLIST_VALUE&&state==DEACTIVATED)pickListValueActivate()//VALUE_COMMA_SEPARATEDelseif(type==VALUE_COMMA_SEPARATED&&state==ACTIVATED)valueCommaSeparatedDeactivate()elseif(type==VALUE_COMMA_SEPARATED&&state==DEACTIVATED)valueCommaSeparatedActivate()//COMMA_SEPARATED_LISTelseif(type==COMMA_SEPARATED_LIST&&state==ACTIVATED)commaSeparatedListDeactivate()elseif(type==COMMA_SEPARATED_LIST&&state==DEACTIVATED)commaSeparatedListActivate()//COMMA_SEPARATED_LIST_VALUE(NOACTION)//COMMA_SEPARATED_LIST_PICKLIST(NOACTION)//COMMA_SEPARATED_LIST_PICKLIST_VALUEelseif(type==COMMA_SEPARATED_LIST_PICKLIST_VALUE&&state==ACTIVATED)commaSeparatedListPickListValueDeactivate()elseif(type==COMMA_SEPARATED_LIST_PICKLIST_VALUE&&state==DEACTIVATED)commaSeparatedListPickListValueActivate()
Last edit: Alexandre 2016-04-01
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I will add another picklist for the attributes, to be able to add/remove attributes from a multi-selection list. I think that would be useful, for example if we have a map where the nodes should have all the same attributes then the picklist will show the attributes missing in the node by showing attributes as unchecked in the picklist. Then the user could check them to add them.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I think it's time for you to make an add-on from your script. I could help you with it. What you had to do is: get an account at Github and install git and then upload the add-on to your repository.
Volker
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I just uploaded a new version with a picklist for the <attributes> node, to help add/remove attributes from a selection of all attribute names available in the map. If attribute names in the picklist are unchecked, they will be removed from the list of attributes. If some attribute names in the picklist are checked but they are not in the list of attributes, they will be added with an empty value.
I also fixed some bugs and the sorting. Please see comments history.
Best regards,
Alexandre
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
2016-03-23_11.03.46
Fixed: The picklist of the comma-separated values, the split on ', ' messed-up the sorting, so the elements needed to be trimmed before to be sorted.
Last edit: Alexandre 2016-03-23
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
2016-03-23_01.02.46
Added: Possibility to add picklist values: Add new nodes to picklists and then press the hotkey on them and they will become checked. This way it allow to add new picklists values.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Fixed: When the node attributes are closed, and there were comma-separated picklist opened with modified list of attribute values (like values added) but without having the comma-separated list 'hotkeyed' yet so the comma-separated value is set to the list of attributes, the comma-separated list was not set, but now it is set before closure.
Fixed: The sorting of the comma-separated values when the list of comma-separated value nodes is closed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Added: Icons to identify the nodes that can be expanded with the hotkey. So it is clear now to the users where they they can press the hotkey: on the list icon or on the checkbox icon.
If you don't like the icon you may change it by modifying the constant LIST_ICON and set it to another icon name.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
2016-03-24_01.22.32
Fixed: If an attribute value had a empty value (not no node) but a node without text, there was an index error because of the comparator function. It is fixed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi,
This script was written to ease the attribute editing.
It creates a new child node to this node named <attributes>, and populates this <attributes> node with children nodes for all the attributes of the node.
Then the user can edit the attributes as nodes, add attributes and delete some, then if the user activates the script again, the attribute nodes are inserted in the node as its attributes and the <attributes> node is finally deleted.
The attributes are sorted before to be displayed as nodes and they are sorted back when inserted in the node.
I mapped this script to alt+3. So pressing this shows the attributes as nodes, then I edit them, and pressing it again inserts the attributes back to the node.
I would like to add to this script later child nodes to each of the attribute nodes containing all the possible values found in the map so one could select from them a value for the current attribute.
Hope it may be helpful.
Best regards,
Alexandre
Uauuu. That sounds amazing! What a great idea. Congrats.
+1 very cool
I have improved the script a little bit. If an attribute contains a comma-separated list, it will split the list into nodes. Then the user could edit the nodes and they will be joined back into a comma separated list to its attribute. The only bug there is that the list of nodes split dosen't sort. The function sortChildren() is not working for some reason. If somebody could tell me why it is not sorting that would be great.
Thanks.
Sounds very interesting as a new way to edit attributes
...would like to add to this script later child nodes to each of the
attribute nodes containing all the possible values found in the map so one
could select from them a value for the current attribute...
If you finally devote to this, you could experiment using a checkbox-icon
on each node. The checkbox icon can toggle between marked/notmarked whith a
mouse click. Also its very visual and intuitive.
Just suggesting
On 10 Mar 2016 03:58, "Alexandre" alexandreviau77@users.sf.net wrote:
Hi Zipzap,
I was thinking to have a form with a list with checkboxes inside. But how to use the checkbox icons?
Thanks,
Alexandre
Hi Zipzap,
Ok I know what you mean by the checkbox icon, so I tried I put one but of course clicking a checked icon wont toggle to the unchecked icon. I don't now how to toggle that with the mouse click.
Best regards,
Alexandre
I had this impression that I've seen it work in freeplane before... Nonetheless as you indicate and I just confirmed both with freeplane 1.5.x and 1.3.15, clicking the icon does not make it toggle between check/unchecked...
Lets try the tap the greater common knowlegde from the community: Does anyone know if its possible (with a script) to make freeplane execute an action/script when the user clicks on a specific icon? Ideally it would be a generalization of the context-option "Link/Add hyperlink to menu item...", allowing to specify the icon and the action/script to be executed
Last edit: Alexandre 2016-04-01
Well done. Hats off ☺
Alexander would you mind if the script got published in the wiki in section
scripts incubator or another one? It would be a great addition.
+1 on the script - very nice
+1 on the suggestion to post in scripts incubator (much easier to copy script).
Hi Zipzap,
Thanks. Please try the latest version I just put in the same post, I fixed some bugs and did some modifications.
I would be glad if the script got published and be useful to somebody else. Please tell me if it gets published and how I could do updates to it.
Best regards,
Alexandre
I will add another picklist for the attributes, to be able to add/remove attributes from a multi-selection list. I think that would be useful, for example if we have a map where the nodes should have all the same attributes then the picklist will show the attributes missing in the node by showing attributes as unchecked in the picklist. Then the user could check them to add them.
Well done, Alexandre.
I think it's time for you to make an add-on from your script. I could help you with it. What you had to do is: get an account at Github and install git and then upload the add-on to your repository.
Volker
Making it an addon will ease future updates
[edit: remove citation]
Last edit: Volker Börchers 2016-03-22
Hi,
Thanks I will try to do the addon this week.
I just uploaded a new version with a picklist for the <attributes> node, to help add/remove attributes from a selection of all attribute names available in the map. If attribute names in the picklist are unchecked, they will be removed from the list of attributes. If some attribute names in the picklist are checked but they are not in the list of attributes, they will be added with an empty value.
I also fixed some bugs and the sorting. Please see comments history.
Best regards,
Alexandre
2016-03-23_11.03.46
Fixed: The picklist of the comma-separated values, the split on ', ' messed-up the sorting, so the elements needed to be trimmed before to be sorted.
Last edit: Alexandre 2016-03-23
2016-03-23_01.02.46
Added: Possibility to add picklist values: Add new nodes to picklists and then press the hotkey on them and they will become checked. This way it allow to add new picklists values.
2016-03-23_01.48.40
Fixed: When the node attributes are closed, and there were comma-separated picklist opened with modified list of attribute values (like values added) but without having the comma-separated list 'hotkeyed' yet so the comma-separated value is set to the list of attributes, the comma-separated list was not set, but now it is set before closure.
Fixed: The sorting of the comma-separated values when the list of comma-separated value nodes is closed.
wow THANK YOU A LOT! great job!!!!! i will study your code to get a deeper understandig!
wow THANK YOU A LOT! great job!!!!! i will study your code to get a deeper understandig!
Thanks Blood Wolf, I hope it is useful for you too. Please check out the updated version I just put now above, it fixes this bug:
2016-03-23_10.34.46
Fixed: Important bug: if the node had children the attributes as nodes where not showing. This is fixed now.
Last edit: Alexandre 2016-03-23
2016-03-24_08.59.14
Added: Icons to identify the nodes that can be expanded with the hotkey. So it is clear now to the users where they they can press the hotkey: on the list icon or on the checkbox icon.
If you don't like the icon you may change it by modifying the constant LIST_ICON and set it to another icon name.
2016-03-24_12.55.19
Added: Possibility to add new attributes value to the attributes picklist.
2016-03-24_01.22.32
Fixed: If an attribute value had a empty value (not no node) but a node without text, there was an index error because of the comparator function. It is fixed.