// auto-generated
// **** THIS FILE IS AUTO-GENERATED, PLEASE DO NOT EDIT IT **** //

package binding

import (
	"bytes"

	"fyne.io/fyne/v2"
)

// BoolTree supports binding a tree of bool values.
//
// Since: 2.4
type BoolTree interface {
	DataTree

	Append(parent, id string, value bool) error
	Get() (map[string][]string, map[string]bool, error)
	GetValue(id string) (bool, error)
	Prepend(parent, id string, value bool) error
	Remove(id string) error
	Set(ids map[string][]string, values map[string]bool) error
	SetValue(id string, value bool) error
}

// ExternalBoolTree supports binding a tree of bool values from an external variable.
//
// Since: 2.4
type ExternalBoolTree interface {
	BoolTree

	Reload() error
}

// NewBoolTree returns a bindable tree of bool values.
//
// Since: 2.4
func NewBoolTree() BoolTree {
	t := &boundBoolTree{val: &map[string]bool{}}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)
	return t
}

// BindBoolTree returns a bound tree of bool values, based on the contents of the passed values.
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
//
// Since: 2.4
func BindBoolTree(ids *map[string][]string, v *map[string]bool) ExternalBoolTree {
	if v == nil {
		return NewBoolTree().(ExternalBoolTree)
	}

	t := &boundBoolTree{val: v, updateExternal: true}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)

	for parent, children := range *ids {
		for _, leaf := range children {
			t.appendItem(bindBoolTreeItem(v, leaf, t.updateExternal), leaf, parent)
		}
	}

	return t
}

type boundBoolTree struct {
	treeBase

	updateExternal bool
	val            *map[string]bool
}

func (t *boundBoolTree) Append(parent, id string, val bool) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append(ids, id)
	v := *t.val
	v[id] = val

	return t.doReload()
}

func (t *boundBoolTree) Get() (map[string][]string, map[string]bool, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	return t.ids, *t.val, nil
}

func (t *boundBoolTree) GetValue(id string) (bool, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	if item, ok := (*t.val)[id]; ok {
		return item, nil
	}

	return false, errOutOfBounds
}

func (t *boundBoolTree) Prepend(parent, id string, val bool) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append([]string{id}, ids...)
	v := *t.val
	v[id] = val

	return t.doReload()
}

// Remove takes the specified id out of the tree.
// It will also remove any child items from the data structure.
//
// Since: 2.5
func (t *boundBoolTree) Remove(id string) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	t.removeChildren(id)
	delete(t.ids, id)
	v := *t.val
	delete(v, id)

	return t.doReload()
}

func (t *boundBoolTree) removeChildren(id string) {
	for _, cid := range t.ids[id] {
		t.removeChildren(cid)

		delete(t.ids, cid)
		v := *t.val
		delete(v, cid)
	}
}

func (t *boundBoolTree) Reload() error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doReload()
}

func (t *boundBoolTree) Set(ids map[string][]string, v map[string]bool) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.ids = ids
	*t.val = v

	return t.doReload()
}

func (t *boundBoolTree) doReload() (retErr error) {
	updated := []string{}
	fire := false
	for id := range *t.val {
		found := false
		for child := range t.items {
			if child == id { // update existing
				updated = append(updated, id)
				found = true
				break
			}
		}
		if found {
			continue
		}

		// append new
		t.appendItem(bindBoolTreeItem(t.val, id, t.updateExternal), id, parentIDFor(id, t.ids))
		updated = append(updated, id)
		fire = true
	}

	for id := range t.items {
		remove := true
		for _, done := range updated {
			if done == id {
				remove = false
				break
			}
		}

		if remove { // remove item no longer present
			fire = true
			t.deleteItem(id, parentIDFor(id, t.ids))
		}
	}
	if fire {
		t.trigger()
	}

	for id, item := range t.items {
		var err error
		if t.updateExternal {
			item.(*boundExternalBoolTreeItem).lock.Lock()
			err = item.(*boundExternalBoolTreeItem).setIfChanged((*t.val)[id])
			item.(*boundExternalBoolTreeItem).lock.Unlock()
		} else {
			item.(*boundBoolTreeItem).lock.Lock()
			err = item.(*boundBoolTreeItem).doSet((*t.val)[id])
			item.(*boundBoolTreeItem).lock.Unlock()
		}
		if err != nil {
			retErr = err
		}
	}
	return
}

func (t *boundBoolTree) SetValue(id string, v bool) error {
	t.lock.Lock()
	(*t.val)[id] = v
	t.lock.Unlock()

	item, err := t.GetItem(id)
	if err != nil {
		return err
	}
	return item.(Bool).Set(v)
}

func bindBoolTreeItem(v *map[string]bool, id string, external bool) Bool {
	if external {
		ret := &boundExternalBoolTreeItem{old: (*v)[id]}
		ret.val = v
		ret.id = id
		return ret
	}

	return &boundBoolTreeItem{id: id, val: v}
}

type boundBoolTreeItem struct {
	base

	val *map[string]bool
	id  string
}

func (t *boundBoolTreeItem) Get() (bool, error) {
	t.lock.Lock()
	defer t.lock.Unlock()

	v := *t.val
	if item, ok := v[t.id]; ok {
		return item, nil
	}

	return false, errOutOfBounds
}

func (t *boundBoolTreeItem) Set(val bool) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doSet(val)
}

func (t *boundBoolTreeItem) doSet(val bool) error {
	(*t.val)[t.id] = val

	t.trigger()
	return nil
}

type boundExternalBoolTreeItem struct {
	boundBoolTreeItem

	old bool
}

func (t *boundExternalBoolTreeItem) setIfChanged(val bool) error {
	if val == t.old {
		return nil
	}
	(*t.val)[t.id] = val
	t.old = val

	t.trigger()
	return nil
}

// BytesTree supports binding a tree of []byte values.
//
// Since: 2.4
type BytesTree interface {
	DataTree

	Append(parent, id string, value []byte) error
	Get() (map[string][]string, map[string][]byte, error)
	GetValue(id string) ([]byte, error)
	Prepend(parent, id string, value []byte) error
	Remove(id string) error
	Set(ids map[string][]string, values map[string][]byte) error
	SetValue(id string, value []byte) error
}

// ExternalBytesTree supports binding a tree of []byte values from an external variable.
//
// Since: 2.4
type ExternalBytesTree interface {
	BytesTree

	Reload() error
}

// NewBytesTree returns a bindable tree of []byte values.
//
// Since: 2.4
func NewBytesTree() BytesTree {
	t := &boundBytesTree{val: &map[string][]byte{}}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)
	return t
}

// BindBytesTree returns a bound tree of []byte values, based on the contents of the passed values.
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
//
// Since: 2.4
func BindBytesTree(ids *map[string][]string, v *map[string][]byte) ExternalBytesTree {
	if v == nil {
		return NewBytesTree().(ExternalBytesTree)
	}

	t := &boundBytesTree{val: v, updateExternal: true}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)

	for parent, children := range *ids {
		for _, leaf := range children {
			t.appendItem(bindBytesTreeItem(v, leaf, t.updateExternal), leaf, parent)
		}
	}

	return t
}

type boundBytesTree struct {
	treeBase

	updateExternal bool
	val            *map[string][]byte
}

func (t *boundBytesTree) Append(parent, id string, val []byte) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append(ids, id)
	v := *t.val
	v[id] = val

	return t.doReload()
}

func (t *boundBytesTree) Get() (map[string][]string, map[string][]byte, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	return t.ids, *t.val, nil
}

func (t *boundBytesTree) GetValue(id string) ([]byte, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	if item, ok := (*t.val)[id]; ok {
		return item, nil
	}

	return nil, errOutOfBounds
}

func (t *boundBytesTree) Prepend(parent, id string, val []byte) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append([]string{id}, ids...)
	v := *t.val
	v[id] = val

	return t.doReload()
}

// Remove takes the specified id out of the tree.
// It will also remove any child items from the data structure.
//
// Since: 2.5
func (t *boundBytesTree) Remove(id string) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	t.removeChildren(id)
	delete(t.ids, id)
	v := *t.val
	delete(v, id)

	return t.doReload()
}

func (t *boundBytesTree) removeChildren(id string) {
	for _, cid := range t.ids[id] {
		t.removeChildren(cid)

		delete(t.ids, cid)
		v := *t.val
		delete(v, cid)
	}
}

func (t *boundBytesTree) Reload() error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doReload()
}

func (t *boundBytesTree) Set(ids map[string][]string, v map[string][]byte) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.ids = ids
	*t.val = v

	return t.doReload()
}

func (t *boundBytesTree) doReload() (retErr error) {
	updated := []string{}
	fire := false
	for id := range *t.val {
		found := false
		for child := range t.items {
			if child == id { // update existing
				updated = append(updated, id)
				found = true
				break
			}
		}
		if found {
			continue
		}

		// append new
		t.appendItem(bindBytesTreeItem(t.val, id, t.updateExternal), id, parentIDFor(id, t.ids))
		updated = append(updated, id)
		fire = true
	}

	for id := range t.items {
		remove := true
		for _, done := range updated {
			if done == id {
				remove = false
				break
			}
		}

		if remove { // remove item no longer present
			fire = true
			t.deleteItem(id, parentIDFor(id, t.ids))
		}
	}
	if fire {
		t.trigger()
	}

	for id, item := range t.items {
		var err error
		if t.updateExternal {
			item.(*boundExternalBytesTreeItem).lock.Lock()
			err = item.(*boundExternalBytesTreeItem).setIfChanged((*t.val)[id])
			item.(*boundExternalBytesTreeItem).lock.Unlock()
		} else {
			item.(*boundBytesTreeItem).lock.Lock()
			err = item.(*boundBytesTreeItem).doSet((*t.val)[id])
			item.(*boundBytesTreeItem).lock.Unlock()
		}
		if err != nil {
			retErr = err
		}
	}
	return
}

func (t *boundBytesTree) SetValue(id string, v []byte) error {
	t.lock.Lock()
	(*t.val)[id] = v
	t.lock.Unlock()

	item, err := t.GetItem(id)
	if err != nil {
		return err
	}
	return item.(Bytes).Set(v)
}

func bindBytesTreeItem(v *map[string][]byte, id string, external bool) Bytes {
	if external {
		ret := &boundExternalBytesTreeItem{old: (*v)[id]}
		ret.val = v
		ret.id = id
		return ret
	}

	return &boundBytesTreeItem{id: id, val: v}
}

type boundBytesTreeItem struct {
	base

	val *map[string][]byte
	id  string
}

func (t *boundBytesTreeItem) Get() ([]byte, error) {
	t.lock.Lock()
	defer t.lock.Unlock()

	v := *t.val
	if item, ok := v[t.id]; ok {
		return item, nil
	}

	return nil, errOutOfBounds
}

func (t *boundBytesTreeItem) Set(val []byte) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doSet(val)
}

func (t *boundBytesTreeItem) doSet(val []byte) error {
	(*t.val)[t.id] = val

	t.trigger()
	return nil
}

type boundExternalBytesTreeItem struct {
	boundBytesTreeItem

	old []byte
}

func (t *boundExternalBytesTreeItem) setIfChanged(val []byte) error {
	if bytes.Equal(val, t.old) {
		return nil
	}
	(*t.val)[t.id] = val
	t.old = val

	t.trigger()
	return nil
}

// FloatTree supports binding a tree of float64 values.
//
// Since: 2.4
type FloatTree interface {
	DataTree

	Append(parent, id string, value float64) error
	Get() (map[string][]string, map[string]float64, error)
	GetValue(id string) (float64, error)
	Prepend(parent, id string, value float64) error
	Remove(id string) error
	Set(ids map[string][]string, values map[string]float64) error
	SetValue(id string, value float64) error
}

// ExternalFloatTree supports binding a tree of float64 values from an external variable.
//
// Since: 2.4
type ExternalFloatTree interface {
	FloatTree

	Reload() error
}

// NewFloatTree returns a bindable tree of float64 values.
//
// Since: 2.4
func NewFloatTree() FloatTree {
	t := &boundFloatTree{val: &map[string]float64{}}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)
	return t
}

// BindFloatTree returns a bound tree of float64 values, based on the contents of the passed values.
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
//
// Since: 2.4
func BindFloatTree(ids *map[string][]string, v *map[string]float64) ExternalFloatTree {
	if v == nil {
		return NewFloatTree().(ExternalFloatTree)
	}

	t := &boundFloatTree{val: v, updateExternal: true}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)

	for parent, children := range *ids {
		for _, leaf := range children {
			t.appendItem(bindFloatTreeItem(v, leaf, t.updateExternal), leaf, parent)
		}
	}

	return t
}

type boundFloatTree struct {
	treeBase

	updateExternal bool
	val            *map[string]float64
}

func (t *boundFloatTree) Append(parent, id string, val float64) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append(ids, id)
	v := *t.val
	v[id] = val

	return t.doReload()
}

func (t *boundFloatTree) Get() (map[string][]string, map[string]float64, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	return t.ids, *t.val, nil
}

func (t *boundFloatTree) GetValue(id string) (float64, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	if item, ok := (*t.val)[id]; ok {
		return item, nil
	}

	return 0.0, errOutOfBounds
}

func (t *boundFloatTree) Prepend(parent, id string, val float64) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append([]string{id}, ids...)
	v := *t.val
	v[id] = val

	return t.doReload()
}

// Remove takes the specified id out of the tree.
// It will also remove any child items from the data structure.
//
// Since: 2.5
func (t *boundFloatTree) Remove(id string) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	t.removeChildren(id)
	delete(t.ids, id)
	v := *t.val
	delete(v, id)

	return t.doReload()
}

func (t *boundFloatTree) removeChildren(id string) {
	for _, cid := range t.ids[id] {
		t.removeChildren(cid)

		delete(t.ids, cid)
		v := *t.val
		delete(v, cid)
	}
}

func (t *boundFloatTree) Reload() error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doReload()
}

func (t *boundFloatTree) Set(ids map[string][]string, v map[string]float64) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.ids = ids
	*t.val = v

	return t.doReload()
}

func (t *boundFloatTree) doReload() (retErr error) {
	updated := []string{}
	fire := false
	for id := range *t.val {
		found := false
		for child := range t.items {
			if child == id { // update existing
				updated = append(updated, id)
				found = true
				break
			}
		}
		if found {
			continue
		}

		// append new
		t.appendItem(bindFloatTreeItem(t.val, id, t.updateExternal), id, parentIDFor(id, t.ids))
		updated = append(updated, id)
		fire = true
	}

	for id := range t.items {
		remove := true
		for _, done := range updated {
			if done == id {
				remove = false
				break
			}
		}

		if remove { // remove item no longer present
			fire = true
			t.deleteItem(id, parentIDFor(id, t.ids))
		}
	}
	if fire {
		t.trigger()
	}

	for id, item := range t.items {
		var err error
		if t.updateExternal {
			item.(*boundExternalFloatTreeItem).lock.Lock()
			err = item.(*boundExternalFloatTreeItem).setIfChanged((*t.val)[id])
			item.(*boundExternalFloatTreeItem).lock.Unlock()
		} else {
			item.(*boundFloatTreeItem).lock.Lock()
			err = item.(*boundFloatTreeItem).doSet((*t.val)[id])
			item.(*boundFloatTreeItem).lock.Unlock()
		}
		if err != nil {
			retErr = err
		}
	}
	return
}

func (t *boundFloatTree) SetValue(id string, v float64) error {
	t.lock.Lock()
	(*t.val)[id] = v
	t.lock.Unlock()

	item, err := t.GetItem(id)
	if err != nil {
		return err
	}
	return item.(Float).Set(v)
}

func bindFloatTreeItem(v *map[string]float64, id string, external bool) Float {
	if external {
		ret := &boundExternalFloatTreeItem{old: (*v)[id]}
		ret.val = v
		ret.id = id
		return ret
	}

	return &boundFloatTreeItem{id: id, val: v}
}

type boundFloatTreeItem struct {
	base

	val *map[string]float64
	id  string
}

func (t *boundFloatTreeItem) Get() (float64, error) {
	t.lock.Lock()
	defer t.lock.Unlock()

	v := *t.val
	if item, ok := v[t.id]; ok {
		return item, nil
	}

	return 0.0, errOutOfBounds
}

func (t *boundFloatTreeItem) Set(val float64) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doSet(val)
}

func (t *boundFloatTreeItem) doSet(val float64) error {
	(*t.val)[t.id] = val

	t.trigger()
	return nil
}

type boundExternalFloatTreeItem struct {
	boundFloatTreeItem

	old float64
}

func (t *boundExternalFloatTreeItem) setIfChanged(val float64) error {
	if val == t.old {
		return nil
	}
	(*t.val)[t.id] = val
	t.old = val

	t.trigger()
	return nil
}

// IntTree supports binding a tree of int values.
//
// Since: 2.4
type IntTree interface {
	DataTree

	Append(parent, id string, value int) error
	Get() (map[string][]string, map[string]int, error)
	GetValue(id string) (int, error)
	Prepend(parent, id string, value int) error
	Remove(id string) error
	Set(ids map[string][]string, values map[string]int) error
	SetValue(id string, value int) error
}

// ExternalIntTree supports binding a tree of int values from an external variable.
//
// Since: 2.4
type ExternalIntTree interface {
	IntTree

	Reload() error
}

// NewIntTree returns a bindable tree of int values.
//
// Since: 2.4
func NewIntTree() IntTree {
	t := &boundIntTree{val: &map[string]int{}}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)
	return t
}

// BindIntTree returns a bound tree of int values, based on the contents of the passed values.
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
//
// Since: 2.4
func BindIntTree(ids *map[string][]string, v *map[string]int) ExternalIntTree {
	if v == nil {
		return NewIntTree().(ExternalIntTree)
	}

	t := &boundIntTree{val: v, updateExternal: true}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)

	for parent, children := range *ids {
		for _, leaf := range children {
			t.appendItem(bindIntTreeItem(v, leaf, t.updateExternal), leaf, parent)
		}
	}

	return t
}

type boundIntTree struct {
	treeBase

	updateExternal bool
	val            *map[string]int
}

func (t *boundIntTree) Append(parent, id string, val int) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append(ids, id)
	v := *t.val
	v[id] = val

	return t.doReload()
}

func (t *boundIntTree) Get() (map[string][]string, map[string]int, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	return t.ids, *t.val, nil
}

func (t *boundIntTree) GetValue(id string) (int, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	if item, ok := (*t.val)[id]; ok {
		return item, nil
	}

	return 0, errOutOfBounds
}

func (t *boundIntTree) Prepend(parent, id string, val int) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append([]string{id}, ids...)
	v := *t.val
	v[id] = val

	return t.doReload()
}

// Remove takes the specified id out of the tree.
// It will also remove any child items from the data structure.
//
// Since: 2.5
func (t *boundIntTree) Remove(id string) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	t.removeChildren(id)
	delete(t.ids, id)
	v := *t.val
	delete(v, id)

	return t.doReload()
}

func (t *boundIntTree) removeChildren(id string) {
	for _, cid := range t.ids[id] {
		t.removeChildren(cid)

		delete(t.ids, cid)
		v := *t.val
		delete(v, cid)
	}
}

func (t *boundIntTree) Reload() error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doReload()
}

func (t *boundIntTree) Set(ids map[string][]string, v map[string]int) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.ids = ids
	*t.val = v

	return t.doReload()
}

func (t *boundIntTree) doReload() (retErr error) {
	updated := []string{}
	fire := false
	for id := range *t.val {
		found := false
		for child := range t.items {
			if child == id { // update existing
				updated = append(updated, id)
				found = true
				break
			}
		}
		if found {
			continue
		}

		// append new
		t.appendItem(bindIntTreeItem(t.val, id, t.updateExternal), id, parentIDFor(id, t.ids))
		updated = append(updated, id)
		fire = true
	}

	for id := range t.items {
		remove := true
		for _, done := range updated {
			if done == id {
				remove = false
				break
			}
		}

		if remove { // remove item no longer present
			fire = true
			t.deleteItem(id, parentIDFor(id, t.ids))
		}
	}
	if fire {
		t.trigger()
	}

	for id, item := range t.items {
		var err error
		if t.updateExternal {
			item.(*boundExternalIntTreeItem).lock.Lock()
			err = item.(*boundExternalIntTreeItem).setIfChanged((*t.val)[id])
			item.(*boundExternalIntTreeItem).lock.Unlock()
		} else {
			item.(*boundIntTreeItem).lock.Lock()
			err = item.(*boundIntTreeItem).doSet((*t.val)[id])
			item.(*boundIntTreeItem).lock.Unlock()
		}
		if err != nil {
			retErr = err
		}
	}
	return
}

func (t *boundIntTree) SetValue(id string, v int) error {
	t.lock.Lock()
	(*t.val)[id] = v
	t.lock.Unlock()

	item, err := t.GetItem(id)
	if err != nil {
		return err
	}
	return item.(Int).Set(v)
}

func bindIntTreeItem(v *map[string]int, id string, external bool) Int {
	if external {
		ret := &boundExternalIntTreeItem{old: (*v)[id]}
		ret.val = v
		ret.id = id
		return ret
	}

	return &boundIntTreeItem{id: id, val: v}
}

type boundIntTreeItem struct {
	base

	val *map[string]int
	id  string
}

func (t *boundIntTreeItem) Get() (int, error) {
	t.lock.Lock()
	defer t.lock.Unlock()

	v := *t.val
	if item, ok := v[t.id]; ok {
		return item, nil
	}

	return 0, errOutOfBounds
}

func (t *boundIntTreeItem) Set(val int) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doSet(val)
}

func (t *boundIntTreeItem) doSet(val int) error {
	(*t.val)[t.id] = val

	t.trigger()
	return nil
}

type boundExternalIntTreeItem struct {
	boundIntTreeItem

	old int
}

func (t *boundExternalIntTreeItem) setIfChanged(val int) error {
	if val == t.old {
		return nil
	}
	(*t.val)[t.id] = val
	t.old = val

	t.trigger()
	return nil
}

// RuneTree supports binding a tree of rune values.
//
// Since: 2.4
type RuneTree interface {
	DataTree

	Append(parent, id string, value rune) error
	Get() (map[string][]string, map[string]rune, error)
	GetValue(id string) (rune, error)
	Prepend(parent, id string, value rune) error
	Remove(id string) error
	Set(ids map[string][]string, values map[string]rune) error
	SetValue(id string, value rune) error
}

// ExternalRuneTree supports binding a tree of rune values from an external variable.
//
// Since: 2.4
type ExternalRuneTree interface {
	RuneTree

	Reload() error
}

// NewRuneTree returns a bindable tree of rune values.
//
// Since: 2.4
func NewRuneTree() RuneTree {
	t := &boundRuneTree{val: &map[string]rune{}}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)
	return t
}

// BindRuneTree returns a bound tree of rune values, based on the contents of the passed values.
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
//
// Since: 2.4
func BindRuneTree(ids *map[string][]string, v *map[string]rune) ExternalRuneTree {
	if v == nil {
		return NewRuneTree().(ExternalRuneTree)
	}

	t := &boundRuneTree{val: v, updateExternal: true}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)

	for parent, children := range *ids {
		for _, leaf := range children {
			t.appendItem(bindRuneTreeItem(v, leaf, t.updateExternal), leaf, parent)
		}
	}

	return t
}

type boundRuneTree struct {
	treeBase

	updateExternal bool
	val            *map[string]rune
}

func (t *boundRuneTree) Append(parent, id string, val rune) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append(ids, id)
	v := *t.val
	v[id] = val

	return t.doReload()
}

func (t *boundRuneTree) Get() (map[string][]string, map[string]rune, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	return t.ids, *t.val, nil
}

func (t *boundRuneTree) GetValue(id string) (rune, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	if item, ok := (*t.val)[id]; ok {
		return item, nil
	}

	return rune(0), errOutOfBounds
}

func (t *boundRuneTree) Prepend(parent, id string, val rune) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append([]string{id}, ids...)
	v := *t.val
	v[id] = val

	return t.doReload()
}

// Remove takes the specified id out of the tree.
// It will also remove any child items from the data structure.
//
// Since: 2.5
func (t *boundRuneTree) Remove(id string) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	t.removeChildren(id)
	delete(t.ids, id)
	v := *t.val
	delete(v, id)

	return t.doReload()
}

func (t *boundRuneTree) removeChildren(id string) {
	for _, cid := range t.ids[id] {
		t.removeChildren(cid)

		delete(t.ids, cid)
		v := *t.val
		delete(v, cid)
	}
}

func (t *boundRuneTree) Reload() error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doReload()
}

func (t *boundRuneTree) Set(ids map[string][]string, v map[string]rune) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.ids = ids
	*t.val = v

	return t.doReload()
}

func (t *boundRuneTree) doReload() (retErr error) {
	updated := []string{}
	fire := false
	for id := range *t.val {
		found := false
		for child := range t.items {
			if child == id { // update existing
				updated = append(updated, id)
				found = true
				break
			}
		}
		if found {
			continue
		}

		// append new
		t.appendItem(bindRuneTreeItem(t.val, id, t.updateExternal), id, parentIDFor(id, t.ids))
		updated = append(updated, id)
		fire = true
	}

	for id := range t.items {
		remove := true
		for _, done := range updated {
			if done == id {
				remove = false
				break
			}
		}

		if remove { // remove item no longer present
			fire = true
			t.deleteItem(id, parentIDFor(id, t.ids))
		}
	}
	if fire {
		t.trigger()
	}

	for id, item := range t.items {
		var err error
		if t.updateExternal {
			item.(*boundExternalRuneTreeItem).lock.Lock()
			err = item.(*boundExternalRuneTreeItem).setIfChanged((*t.val)[id])
			item.(*boundExternalRuneTreeItem).lock.Unlock()
		} else {
			item.(*boundRuneTreeItem).lock.Lock()
			err = item.(*boundRuneTreeItem).doSet((*t.val)[id])
			item.(*boundRuneTreeItem).lock.Unlock()
		}
		if err != nil {
			retErr = err
		}
	}
	return
}

func (t *boundRuneTree) SetValue(id string, v rune) error {
	t.lock.Lock()
	(*t.val)[id] = v
	t.lock.Unlock()

	item, err := t.GetItem(id)
	if err != nil {
		return err
	}
	return item.(Rune).Set(v)
}

func bindRuneTreeItem(v *map[string]rune, id string, external bool) Rune {
	if external {
		ret := &boundExternalRuneTreeItem{old: (*v)[id]}
		ret.val = v
		ret.id = id
		return ret
	}

	return &boundRuneTreeItem{id: id, val: v}
}

type boundRuneTreeItem struct {
	base

	val *map[string]rune
	id  string
}

func (t *boundRuneTreeItem) Get() (rune, error) {
	t.lock.Lock()
	defer t.lock.Unlock()

	v := *t.val
	if item, ok := v[t.id]; ok {
		return item, nil
	}

	return rune(0), errOutOfBounds
}

func (t *boundRuneTreeItem) Set(val rune) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doSet(val)
}

func (t *boundRuneTreeItem) doSet(val rune) error {
	(*t.val)[t.id] = val

	t.trigger()
	return nil
}

type boundExternalRuneTreeItem struct {
	boundRuneTreeItem

	old rune
}

func (t *boundExternalRuneTreeItem) setIfChanged(val rune) error {
	if val == t.old {
		return nil
	}
	(*t.val)[t.id] = val
	t.old = val

	t.trigger()
	return nil
}

// StringTree supports binding a tree of string values.
//
// Since: 2.4
type StringTree interface {
	DataTree

	Append(parent, id string, value string) error
	Get() (map[string][]string, map[string]string, error)
	GetValue(id string) (string, error)
	Prepend(parent, id string, value string) error
	Remove(id string) error
	Set(ids map[string][]string, values map[string]string) error
	SetValue(id string, value string) error
}

// ExternalStringTree supports binding a tree of string values from an external variable.
//
// Since: 2.4
type ExternalStringTree interface {
	StringTree

	Reload() error
}

// NewStringTree returns a bindable tree of string values.
//
// Since: 2.4
func NewStringTree() StringTree {
	t := &boundStringTree{val: &map[string]string{}}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)
	return t
}

// BindStringTree returns a bound tree of string values, based on the contents of the passed values.
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
//
// Since: 2.4
func BindStringTree(ids *map[string][]string, v *map[string]string) ExternalStringTree {
	if v == nil {
		return NewStringTree().(ExternalStringTree)
	}

	t := &boundStringTree{val: v, updateExternal: true}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)

	for parent, children := range *ids {
		for _, leaf := range children {
			t.appendItem(bindStringTreeItem(v, leaf, t.updateExternal), leaf, parent)
		}
	}

	return t
}

type boundStringTree struct {
	treeBase

	updateExternal bool
	val            *map[string]string
}

func (t *boundStringTree) Append(parent, id string, val string) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append(ids, id)
	v := *t.val
	v[id] = val

	return t.doReload()
}

func (t *boundStringTree) Get() (map[string][]string, map[string]string, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	return t.ids, *t.val, nil
}

func (t *boundStringTree) GetValue(id string) (string, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	if item, ok := (*t.val)[id]; ok {
		return item, nil
	}

	return "", errOutOfBounds
}

func (t *boundStringTree) Prepend(parent, id string, val string) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append([]string{id}, ids...)
	v := *t.val
	v[id] = val

	return t.doReload()
}

// Remove takes the specified id out of the tree.
// It will also remove any child items from the data structure.
//
// Since: 2.5
func (t *boundStringTree) Remove(id string) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	t.removeChildren(id)
	delete(t.ids, id)
	v := *t.val
	delete(v, id)

	return t.doReload()
}

func (t *boundStringTree) removeChildren(id string) {
	for _, cid := range t.ids[id] {
		t.removeChildren(cid)

		delete(t.ids, cid)
		v := *t.val
		delete(v, cid)
	}
}

func (t *boundStringTree) Reload() error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doReload()
}

func (t *boundStringTree) Set(ids map[string][]string, v map[string]string) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.ids = ids
	*t.val = v

	return t.doReload()
}

func (t *boundStringTree) doReload() (retErr error) {
	updated := []string{}
	fire := false
	for id := range *t.val {
		found := false
		for child := range t.items {
			if child == id { // update existing
				updated = append(updated, id)
				found = true
				break
			}
		}
		if found {
			continue
		}

		// append new
		t.appendItem(bindStringTreeItem(t.val, id, t.updateExternal), id, parentIDFor(id, t.ids))
		updated = append(updated, id)
		fire = true
	}

	for id := range t.items {
		remove := true
		for _, done := range updated {
			if done == id {
				remove = false
				break
			}
		}

		if remove { // remove item no longer present
			fire = true
			t.deleteItem(id, parentIDFor(id, t.ids))
		}
	}
	if fire {
		t.trigger()
	}

	for id, item := range t.items {
		var err error
		if t.updateExternal {
			item.(*boundExternalStringTreeItem).lock.Lock()
			err = item.(*boundExternalStringTreeItem).setIfChanged((*t.val)[id])
			item.(*boundExternalStringTreeItem).lock.Unlock()
		} else {
			item.(*boundStringTreeItem).lock.Lock()
			err = item.(*boundStringTreeItem).doSet((*t.val)[id])
			item.(*boundStringTreeItem).lock.Unlock()
		}
		if err != nil {
			retErr = err
		}
	}
	return
}

func (t *boundStringTree) SetValue(id string, v string) error {
	t.lock.Lock()
	(*t.val)[id] = v
	t.lock.Unlock()

	item, err := t.GetItem(id)
	if err != nil {
		return err
	}
	return item.(String).Set(v)
}

func bindStringTreeItem(v *map[string]string, id string, external bool) String {
	if external {
		ret := &boundExternalStringTreeItem{old: (*v)[id]}
		ret.val = v
		ret.id = id
		return ret
	}

	return &boundStringTreeItem{id: id, val: v}
}

type boundStringTreeItem struct {
	base

	val *map[string]string
	id  string
}

func (t *boundStringTreeItem) Get() (string, error) {
	t.lock.Lock()
	defer t.lock.Unlock()

	v := *t.val
	if item, ok := v[t.id]; ok {
		return item, nil
	}

	return "", errOutOfBounds
}

func (t *boundStringTreeItem) Set(val string) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doSet(val)
}

func (t *boundStringTreeItem) doSet(val string) error {
	(*t.val)[t.id] = val

	t.trigger()
	return nil
}

type boundExternalStringTreeItem struct {
	boundStringTreeItem

	old string
}

func (t *boundExternalStringTreeItem) setIfChanged(val string) error {
	if val == t.old {
		return nil
	}
	(*t.val)[t.id] = val
	t.old = val

	t.trigger()
	return nil
}

// UntypedTree supports binding a tree of any values.
//
// Since: 2.5
type UntypedTree interface {
	DataTree

	Append(parent, id string, value any) error
	Get() (map[string][]string, map[string]any, error)
	GetValue(id string) (any, error)
	Prepend(parent, id string, value any) error
	Remove(id string) error
	Set(ids map[string][]string, values map[string]any) error
	SetValue(id string, value any) error
}

// ExternalUntypedTree supports binding a tree of any values from an external variable.
//
// Since: 2.5
type ExternalUntypedTree interface {
	UntypedTree

	Reload() error
}

// NewUntypedTree returns a bindable tree of any values.
//
// Since: 2.5
func NewUntypedTree() UntypedTree {
	t := &boundUntypedTree{val: &map[string]any{}}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)
	return t
}

// BindUntypedTree returns a bound tree of any values, based on the contents of the passed values.
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
//
// Since: 2.4
func BindUntypedTree(ids *map[string][]string, v *map[string]any) ExternalUntypedTree {
	if v == nil {
		return NewUntypedTree().(ExternalUntypedTree)
	}

	t := &boundUntypedTree{val: v, updateExternal: true}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)

	for parent, children := range *ids {
		for _, leaf := range children {
			t.appendItem(bindUntypedTreeItem(v, leaf, t.updateExternal), leaf, parent)
		}
	}

	return t
}

type boundUntypedTree struct {
	treeBase

	updateExternal bool
	val            *map[string]any
}

func (t *boundUntypedTree) Append(parent, id string, val any) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append(ids, id)
	v := *t.val
	v[id] = val

	return t.doReload()
}

func (t *boundUntypedTree) Get() (map[string][]string, map[string]any, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	return t.ids, *t.val, nil
}

func (t *boundUntypedTree) GetValue(id string) (any, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	if item, ok := (*t.val)[id]; ok {
		return item, nil
	}

	return nil, errOutOfBounds
}

func (t *boundUntypedTree) Prepend(parent, id string, val any) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append([]string{id}, ids...)
	v := *t.val
	v[id] = val

	return t.doReload()
}

// Remove takes the specified id out of the tree.
// It will also remove any child items from the data structure.
//
// Since: 2.5
func (t *boundUntypedTree) Remove(id string) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	t.removeChildren(id)
	delete(t.ids, id)
	v := *t.val
	delete(v, id)

	return t.doReload()
}

func (t *boundUntypedTree) removeChildren(id string) {
	for _, cid := range t.ids[id] {
		t.removeChildren(cid)

		delete(t.ids, cid)
		v := *t.val
		delete(v, cid)
	}
}

func (t *boundUntypedTree) Reload() error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doReload()
}

func (t *boundUntypedTree) Set(ids map[string][]string, v map[string]any) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.ids = ids
	*t.val = v

	return t.doReload()
}

func (t *boundUntypedTree) doReload() (retErr error) {
	updated := []string{}
	fire := false
	for id := range *t.val {
		found := false
		for child := range t.items {
			if child == id { // update existing
				updated = append(updated, id)
				found = true
				break
			}
		}
		if found {
			continue
		}

		// append new
		t.appendItem(bindUntypedTreeItem(t.val, id, t.updateExternal), id, parentIDFor(id, t.ids))
		updated = append(updated, id)
		fire = true
	}

	for id := range t.items {
		remove := true
		for _, done := range updated {
			if done == id {
				remove = false
				break
			}
		}

		if remove { // remove item no longer present
			fire = true
			t.deleteItem(id, parentIDFor(id, t.ids))
		}
	}
	if fire {
		t.trigger()
	}

	for id, item := range t.items {
		var err error
		if t.updateExternal {
			item.(*boundExternalUntypedTreeItem).lock.Lock()
			err = item.(*boundExternalUntypedTreeItem).setIfChanged((*t.val)[id])
			item.(*boundExternalUntypedTreeItem).lock.Unlock()
		} else {
			item.(*boundUntypedTreeItem).lock.Lock()
			err = item.(*boundUntypedTreeItem).doSet((*t.val)[id])
			item.(*boundUntypedTreeItem).lock.Unlock()
		}
		if err != nil {
			retErr = err
		}
	}
	return
}

func (t *boundUntypedTree) SetValue(id string, v any) error {
	t.lock.Lock()
	(*t.val)[id] = v
	t.lock.Unlock()

	item, err := t.GetItem(id)
	if err != nil {
		return err
	}
	return item.(Untyped).Set(v)
}

func bindUntypedTreeItem(v *map[string]any, id string, external bool) Untyped {
	if external {
		ret := &boundExternalUntypedTreeItem{old: (*v)[id]}
		ret.val = v
		ret.id = id
		return ret
	}

	return &boundUntypedTreeItem{id: id, val: v}
}

type boundUntypedTreeItem struct {
	base

	val *map[string]any
	id  string
}

func (t *boundUntypedTreeItem) Get() (any, error) {
	t.lock.Lock()
	defer t.lock.Unlock()

	v := *t.val
	if item, ok := v[t.id]; ok {
		return item, nil
	}

	return nil, errOutOfBounds
}

func (t *boundUntypedTreeItem) Set(val any) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doSet(val)
}

func (t *boundUntypedTreeItem) doSet(val any) error {
	(*t.val)[t.id] = val

	t.trigger()
	return nil
}

type boundExternalUntypedTreeItem struct {
	boundUntypedTreeItem

	old any
}

func (t *boundExternalUntypedTreeItem) setIfChanged(val any) error {
	if val == t.old {
		return nil
	}
	(*t.val)[t.id] = val
	t.old = val

	t.trigger()
	return nil
}

// URITree supports binding a tree of fyne.URI values.
//
// Since: 2.4
type URITree interface {
	DataTree

	Append(parent, id string, value fyne.URI) error
	Get() (map[string][]string, map[string]fyne.URI, error)
	GetValue(id string) (fyne.URI, error)
	Prepend(parent, id string, value fyne.URI) error
	Remove(id string) error
	Set(ids map[string][]string, values map[string]fyne.URI) error
	SetValue(id string, value fyne.URI) error
}

// ExternalURITree supports binding a tree of fyne.URI values from an external variable.
//
// Since: 2.4
type ExternalURITree interface {
	URITree

	Reload() error
}

// NewURITree returns a bindable tree of fyne.URI values.
//
// Since: 2.4
func NewURITree() URITree {
	t := &boundURITree{val: &map[string]fyne.URI{}}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)
	return t
}

// BindURITree returns a bound tree of fyne.URI values, based on the contents of the passed values.
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
//
// Since: 2.4
func BindURITree(ids *map[string][]string, v *map[string]fyne.URI) ExternalURITree {
	if v == nil {
		return NewURITree().(ExternalURITree)
	}

	t := &boundURITree{val: v, updateExternal: true}
	t.ids = make(map[string][]string)
	t.items = make(map[string]DataItem)

	for parent, children := range *ids {
		for _, leaf := range children {
			t.appendItem(bindURITreeItem(v, leaf, t.updateExternal), leaf, parent)
		}
	}

	return t
}

type boundURITree struct {
	treeBase

	updateExternal bool
	val            *map[string]fyne.URI
}

func (t *boundURITree) Append(parent, id string, val fyne.URI) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append(ids, id)
	v := *t.val
	v[id] = val

	return t.doReload()
}

func (t *boundURITree) Get() (map[string][]string, map[string]fyne.URI, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	return t.ids, *t.val, nil
}

func (t *boundURITree) GetValue(id string) (fyne.URI, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()

	if item, ok := (*t.val)[id]; ok {
		return item, nil
	}

	return fyne.URI(nil), errOutOfBounds
}

func (t *boundURITree) Prepend(parent, id string, val fyne.URI) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	ids, ok := t.ids[parent]
	if !ok {
		ids = make([]string, 0)
	}

	t.ids[parent] = append([]string{id}, ids...)
	v := *t.val
	v[id] = val

	return t.doReload()
}

// Remove takes the specified id out of the tree.
// It will also remove any child items from the data structure.
//
// Since: 2.5
func (t *boundURITree) Remove(id string) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	t.removeChildren(id)
	delete(t.ids, id)
	v := *t.val
	delete(v, id)

	return t.doReload()
}

func (t *boundURITree) removeChildren(id string) {
	for _, cid := range t.ids[id] {
		t.removeChildren(cid)

		delete(t.ids, cid)
		v := *t.val
		delete(v, cid)
	}
}

func (t *boundURITree) Reload() error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doReload()
}

func (t *boundURITree) Set(ids map[string][]string, v map[string]fyne.URI) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	t.ids = ids
	*t.val = v

	return t.doReload()
}

func (t *boundURITree) doReload() (retErr error) {
	updated := []string{}
	fire := false
	for id := range *t.val {
		found := false
		for child := range t.items {
			if child == id { // update existing
				updated = append(updated, id)
				found = true
				break
			}
		}
		if found {
			continue
		}

		// append new
		t.appendItem(bindURITreeItem(t.val, id, t.updateExternal), id, parentIDFor(id, t.ids))
		updated = append(updated, id)
		fire = true
	}

	for id := range t.items {
		remove := true
		for _, done := range updated {
			if done == id {
				remove = false
				break
			}
		}

		if remove { // remove item no longer present
			fire = true
			t.deleteItem(id, parentIDFor(id, t.ids))
		}
	}
	if fire {
		t.trigger()
	}

	for id, item := range t.items {
		var err error
		if t.updateExternal {
			item.(*boundExternalURITreeItem).lock.Lock()
			err = item.(*boundExternalURITreeItem).setIfChanged((*t.val)[id])
			item.(*boundExternalURITreeItem).lock.Unlock()
		} else {
			item.(*boundURITreeItem).lock.Lock()
			err = item.(*boundURITreeItem).doSet((*t.val)[id])
			item.(*boundURITreeItem).lock.Unlock()
		}
		if err != nil {
			retErr = err
		}
	}
	return
}

func (t *boundURITree) SetValue(id string, v fyne.URI) error {
	t.lock.Lock()
	(*t.val)[id] = v
	t.lock.Unlock()

	item, err := t.GetItem(id)
	if err != nil {
		return err
	}
	return item.(URI).Set(v)
}

func bindURITreeItem(v *map[string]fyne.URI, id string, external bool) URI {
	if external {
		ret := &boundExternalURITreeItem{old: (*v)[id]}
		ret.val = v
		ret.id = id
		return ret
	}

	return &boundURITreeItem{id: id, val: v}
}

type boundURITreeItem struct {
	base

	val *map[string]fyne.URI
	id  string
}

func (t *boundURITreeItem) Get() (fyne.URI, error) {
	t.lock.Lock()
	defer t.lock.Unlock()

	v := *t.val
	if item, ok := v[t.id]; ok {
		return item, nil
	}

	return fyne.URI(nil), errOutOfBounds
}

func (t *boundURITreeItem) Set(val fyne.URI) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	return t.doSet(val)
}

func (t *boundURITreeItem) doSet(val fyne.URI) error {
	(*t.val)[t.id] = val

	t.trigger()
	return nil
}

type boundExternalURITreeItem struct {
	boundURITreeItem

	old fyne.URI
}

func (t *boundExternalURITreeItem) setIfChanged(val fyne.URI) error {
	if compareURI(val, t.old) {
		return nil
	}
	(*t.val)[t.id] = val
	t.old = val

	t.trigger()
	return nil
}
