aboutsummaryrefslogtreecommitdiffstats
path: root/tagit
diff options
context:
space:
mode:
authorMatthias Baumgartner <dev@igsor.net>2023-01-25 17:31:10 +0100
committerMatthias Baumgartner <dev@igsor.net>2023-01-25 17:31:10 +0100
commitd531555fe3483fac7676aa634f3787e8eab9b67f (patch)
treecfd847d785feb3c7b0a72b686c0d341a2852de45 /tagit
parentcdd1dc960a3da5e73a86dd3ec5412417387c70d6 (diff)
downloadtagit-d531555fe3483fac7676aa634f3787e8eab9b67f.tar.gz
tagit-d531555fe3483fac7676aa634f3787e8eab9b67f.tar.bz2
tagit-d531555fe3483fac7676aa634f3787e8eab9b67f.zip
grouping
Diffstat (limited to 'tagit')
-rw-r--r--tagit/actions/__init__.py10
-rw-r--r--tagit/actions/grouping.py65
-rw-r--r--tagit/apps/port-config.yaml17
-rw-r--r--tagit/assets/icons/scalable/grouping/add.svg147
-rw-r--r--tagit/assets/icons/scalable/grouping/create.svg135
-rw-r--r--tagit/assets/icons/scalable/grouping/group.svg127
-rw-r--r--tagit/assets/icons/scalable/grouping/remove.svg133
-rw-r--r--tagit/assets/icons/scalable/grouping/represent.svg133
-rw-r--r--tagit/assets/icons/scalable/grouping/ungroup.svg134
9 files changed, 860 insertions, 41 deletions
diff --git a/tagit/actions/__init__.py b/tagit/actions/__init__.py
index 6416a4b..7144e44 100644
--- a/tagit/actions/__init__.py
+++ b/tagit/actions/__init__.py
@@ -13,7 +13,7 @@ from tagit.utils.builder import BuilderBase
# inner-module imports
from . import browser
from . import filter
-#from . import grouping
+from . import grouping
from . import misc
#from . import objects
from . import planes
@@ -67,10 +67,10 @@ class ActionBuilder(BuilderBase):
#'SearchByAddressOnce': filter.SearchByAddressOnce,
#'SearchmodeSwitch': filter.SearchmodeSwitch,
## grouping
- #'CreateGroup': grouping.CreateGroup,
- #'DissolveGroup': grouping.DissolveGroup,
- #'AddToGroup': grouping.AddToGroup,
- #'OpenGroup': grouping.OpenGroup,
+ 'CreateGroup': grouping.CreateGroup,
+ 'DissolveGroup': grouping.DissolveGroup,
+ 'AddToGroup': grouping.AddToGroup,
+ 'OpenGroup': grouping.OpenGroup,
#'RepresentGroup': grouping.RepresentGroup,
#'RemoveFromGroup': grouping.RemoveFromGroup,
## misc
diff --git a/tagit/actions/grouping.py b/tagit/actions/grouping.py
index eddaeb6..05c651e 100644
--- a/tagit/actions/grouping.py
+++ b/tagit/actions/grouping.py
@@ -15,14 +15,16 @@ import kivy.properties as kp
# tagit imports
from tagit import config, dialogues
-#from tagit.parsing.search import ast # FIXME: mb/port
-#from tagit.storage.broker import Representative # FIXME: mb/port
+from tagit.utils import Frame, ns
+from tagit.utils.bsfs import Namespace, ast, uuid
from tagit.widgets import Binding
-from tagit.utils import Frame
# inner-module imports
from .action import Action
+# constants
+GROUP_PREFIX = Namespace('http://example.com/me/group')
+
# exports
__all__ = []
@@ -54,30 +56,30 @@ class CreateGroup(Action):
if len(self.root.browser.selection) > 1:
with self.root.browser as browser, \
self.root.session as session:
-
# create group
- grp = Group.Create()
+ grp = session.storage.node(ns.bsfs.Group, GROUP_PREFIX[uuid.UUID()()])
if label is not None:
- grp.label = label
+ grp.set(ns.bsg.label, label)
# add items to group
- ents = self.root.session.storage.entities(browser.unfold(browser.selection))
- ents.group += grp
+ ents = browser.unfold(browser.selection)
+ ents.set(ns.bse.group, grp)
# select a random representative
- grp.represented_by = random.choice(ents)
+ rep = random.choice(list(ents))
+ grp.set(ns.bsg.represented_by, rep)
# set selection and cursor to representative
# the representative will become valid after the search was re-applied
browser.selection.clear()
- browser.selection.add(grp.represented_by)
+ browser.selection.add(rep)
browser.cursor = rep
# notification
- logger.info(f'Grouped {len(items)} items')
+ logger.info(f'Grouped {len(ents)} items')
# change event
- session.dispatch('on_predicate_modified', 'group', items, {grp})
+ session.dispatch('on_predicate_modified', ns.bse.group, ents, {grp})
# jump to cursor
# needs to be done *after* the browser was updated
@@ -96,13 +98,11 @@ class DissolveGroup(Action):
self.root.session as session:
cursor = browser.cursor
if cursor is not None and cursor in browser.folds:
- # remove tag from items
- items = list(cursor.members())
- #ents = ...
- #grp = ...
- #ents.group -= grp
- for obj in items:
- obj.group -= [cursor.represents()] # FIXME
+ grp = browser.folds[cursor].group
+ ents = session.storage.get(ns.bsfs.Entity,
+ ast.filter.Any(ns.bse.group, ast.filter.Is(grp)))
+ #ents.remove(ns.bse.group, grp) # FIXME: mb/port
+ #grp.delete() # FIXME: mb/port
# FIXME: fix cursor and selection
# cursor: leave at item that was the representative
@@ -110,10 +110,10 @@ class DissolveGroup(Action):
browser.frame = Frame()
# notification
- logger.info(f'Ungrouped {len(items)} items')
+ logger.info(f'Ungrouped {len(ents)} items')
# change event
- session.dispatch('on_predicate_modified', 'group', items, {cursor.represents()})
+ session.dispatch('on_predicate_modified', ns.bse.group, ents, {grp})
self.root.trigger('JumpToCursor')
@@ -130,12 +130,14 @@ class AddToGroup(Action):
self.root.session as session:
cursor = browser.cursor
if cursor is not None and cursor in browser.folds:
- items = browser.unfold(browser.selection)
- for obj in items:
+ grp = browser.folds[cursor].group
+ ents = browser.unfold(browser.selection)
+
+ for obj in ents:
if obj == cursor:
# don't add group to itself
continue
- obj.group += [cursor.represents()] # FIXME: Not quite sure how to handle this
+ obj.set(ns.bse.group, gr)
# all selected items will be folded, hence it becomes empty
if cursor in browser.selection:
@@ -144,7 +146,7 @@ class AddToGroup(Action):
browser.selection.clear()
# change event
- session.dispatch('on_predicate_modified', 'group', items, {cursor.represents()})
+ session.dispatch('on_predicate_modified', ns.bse.group, ents, {grp})
class OpenGroup(Action):
@@ -155,11 +157,12 @@ class OpenGroup(Action):
return Binding.check(evt, self.cfg('bindings', 'grouping', 'open'))
def apply(self, cursor=None):
- if cursor is None:
- cursor = self.root.browser.cursor
- if cursor is not None and cursor in self.root.browser.folds:
- token = ast.Token('group', ast.SetInclude(cursor.represents()))
- self.root.trigger('AddToken', token)
+ if cursor is None:
+ cursor = self.root.browser.cursor
+ elif cursor in self.root.browser.folds:
+ grp = self.root.browser.folds[cursor].group
+ self.root.trigger('AddToken', ast.filter.Any(
+ ns.bse.group, ast.filter.Is(grp)))
class RepresentGroup(Action):
@@ -170,6 +173,7 @@ class RepresentGroup(Action):
return Binding.check(evt, self.cfg('bindings', 'grouping', 'represent'))
def apply(self):
+ return # FIXME: mb/port
with self.root.browser as browser, \
self.root.filter as filter, \
self.root.session as session:
@@ -191,6 +195,7 @@ class RemoveFromGroup(Action):
return Binding.check(evt, self.cfg('bindings', 'grouping', 'remove'))
def apply(self):
+ return # FIXME: mb/port
with self.root.browser as browser, \
self.root.filter as filter, \
self.root.session as session:
diff --git a/tagit/apps/port-config.yaml b/tagit/apps/port-config.yaml
index 33afbbd..6b0b06d 100644
--- a/tagit/apps/port-config.yaml
+++ b/tagit/apps/port-config.yaml
@@ -20,6 +20,9 @@ ui:
standalone:
plane: browsing
keytriggers:
+ - CreateGroup
+ - DissolveGroup
+ - AddToGroup
- MoveCursorUp
- MoveCursorDown
- MoveCursorLeft
@@ -39,6 +42,8 @@ ui:
- SelectRange
- AddTag
- EditTag
+ - OpenGroup
+ #- RepresentGroup
- ShowHelp
browser:
maxcols: 8
@@ -50,8 +55,8 @@ ui:
- AddToken
- AddTag
- EditTag
- #- CreateGroup
- #- DissolveGroup
+ - CreateGroup
+ - DissolveGroup
- SelectAll
- SelectNone
- SelectInvert
@@ -76,10 +81,10 @@ ui:
# clipboard:
# - ClipboardCopy
# - ClipboardPaste
- # grouping:
- # - CreateGroup
- # - DissolveGroup
- # - AddToGroup
+ grouping:
+ - CreateGroup
+ - DissolveGroup
+ - AddToGroup
# - RepresentGroup
# - RemoveFromGroup
# root:
diff --git a/tagit/assets/icons/scalable/grouping/add.svg b/tagit/assets/icons/scalable/grouping/add.svg
new file mode 100644
index 0000000..3069273
--- /dev/null
+++ b/tagit/assets/icons/scalable/grouping/add.svg
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="add.svg"
+ inkscape:export-filename="../../kivy/grouping/create.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="41.873283"
+ inkscape:cy="194.67797"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1151"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="-70.205601,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,171.22086"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <ellipse
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:17.38639641;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path889"
+ cx="356.2576"
+ cy="511.83615"
+ rx="180.28317"
+ ry="180.28319" />
+ <ellipse
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:38.57448196;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:77.14896078, 38.57448039;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path891-5-0"
+ cx="355.72531"
+ cy="-422.3046"
+ transform="scale(1,-1)"
+ rx="54.830173"
+ ry="55.384014" />
+ <ellipse
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:9.16811562;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path891-5-0-6"
+ cx="435.48053"
+ cy="-561.84009"
+ transform="scale(1,-1)"
+ rx="50.245941"
+ ry="50.799942" />
+ <ellipse
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:38.57448196;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:77.14896078, 38.57448039;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path891-5-0-0"
+ cx="275.97003"
+ cy="-561.84003"
+ transform="scale(1,-1)"
+ rx="54.830173"
+ ry="55.384014" />
+ <g
+ id="g841"
+ transform="matrix(0.6024096,0,0,0.6024096,433.43894,320.09479)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path822"
+ d="M -58.99295,400.79722 H 66.77104"
+ style="fill:none;stroke:#c8c8c8;stroke-width:25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path822-3"
+ d="M 3.389045,338.41523 V 464.17921"
+ style="fill:none;stroke:#c8c8c8;stroke-width:25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/grouping/create.svg b/tagit/assets/icons/scalable/grouping/create.svg
new file mode 100644
index 0000000..549f27e
--- /dev/null
+++ b/tagit/assets/icons/scalable/grouping/create.svg
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="create_group.svg"
+ inkscape:export-filename="../../kivy/grouping/create.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="196.9317"
+ inkscape:cy="198.71859"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1031"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <ellipse
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:17.38639641;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path889"
+ cx="356.2576"
+ cy="511.83615"
+ rx="180.28317"
+ ry="180.28319" />
+ <g
+ id="g1097"
+ transform="matrix(0.37263226,0,0,0.37639623,804.3104,348.85233)"
+ style="fill:#c8c8c8;fill-opacity:1">
+ <circle
+ transform="scale(1,-1)"
+ r="147.14285"
+ cy="-195.14612"
+ cx="-1203.8279"
+ id="path891-5-0"
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:103;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:206.0000011, 103.00000055000000998;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
+ <circle
+ transform="scale(1,-1)"
+ r="147.14285"
+ cy="-565.86047"
+ cx="-989.7959"
+ id="path891-5-0-6"
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:103;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:206.0000011, 103.00000055000000998;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
+ <circle
+ transform="scale(1,-1)"
+ r="147.14285"
+ cy="-565.86041"
+ cx="-1417.86"
+ id="path891-5-0-0"
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:103;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:206.0000011, 103.00000055000000998;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
+ </g>
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/grouping/group.svg b/tagit/assets/icons/scalable/grouping/group.svg
new file mode 100644
index 0000000..975f413
--- /dev/null
+++ b/tagit/assets/icons/scalable/grouping/group.svg
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="group.svg"
+ inkscape:export-filename="../../kivy/grouping/group.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.9899495"
+ inkscape:cx="298.903"
+ inkscape:cy="113.59541"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1031"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <g
+ id="g895"
+ transform="translate(1.0798198,48.934962)">
+ <rect
+ y="326.91025"
+ x="249.19569"
+ height="191.95821"
+ width="291.98779"
+ id="rect4221-6-2-3"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c8c8c8;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:8.10094929;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ y="366.92203"
+ x="209.18385"
+ height="191.95819"
+ width="291.98776"
+ id="rect4221-6-2"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c8c8c8;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:8.10094929;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ y="406.93393"
+ x="169.17207"
+ height="191.95819"
+ width="291.98776"
+ id="rect4221"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c8c8c8;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:8.10094929;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ </g>
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/grouping/remove.svg b/tagit/assets/icons/scalable/grouping/remove.svg
new file mode 100644
index 0000000..ebf8ed9
--- /dev/null
+++ b/tagit/assets/icons/scalable/grouping/remove.svg
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="remove.svg"
+ inkscape:export-filename="../../kivy/grouping/create.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="41.873283"
+ inkscape:cy="194.67797"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1151"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="-70.205601,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,171.22086"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <ellipse
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:17.38639641;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path889"
+ cx="356.2576"
+ cy="511.83615"
+ rx="180.28317"
+ ry="180.28319" />
+ <ellipse
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:38.57448196;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:77.14896078, 38.57448039;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path891-5-0"
+ cx="355.72531"
+ cy="-422.3046"
+ transform="scale(1,-1)"
+ rx="54.830173"
+ ry="55.384014" />
+ <ellipse
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#ff9191;stroke-width:9.16811562;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:18.33623145, 18.33623144999999965;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path891-5-0-6"
+ cx="435.48053"
+ cy="-561.84009"
+ transform="scale(1,-1)"
+ rx="50.245941"
+ ry="50.799942" />
+ <ellipse
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:38.57448196;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:77.14896078, 38.57448039;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path891-5-0-0"
+ cx="275.97003"
+ cy="-561.84003"
+ transform="scale(1,-1)"
+ rx="54.830173"
+ ry="55.384014" />
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/grouping/represent.svg b/tagit/assets/icons/scalable/grouping/represent.svg
new file mode 100644
index 0000000..c7b5850
--- /dev/null
+++ b/tagit/assets/icons/scalable/grouping/represent.svg
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="represent.svg"
+ inkscape:export-filename="../../kivy/grouping/create.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="119.40249"
+ inkscape:cy="196.69828"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1151"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <ellipse
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:17.38639641;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path889"
+ cx="356.2576"
+ cy="511.83615"
+ rx="180.28317"
+ ry="180.28319" />
+ <ellipse
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:38.57448196;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:77.14896078, 38.57448039;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path891-5-0"
+ cx="355.72531"
+ cy="-422.3046"
+ transform="scale(1,-1)"
+ rx="54.830173"
+ ry="55.384014" />
+ <ellipse
+ style="opacity:1;fill:#ff9191;fill-opacity:1;stroke:none;stroke-width:38.57448196;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:77.14896078, 38.57448038999999795;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path891-5-0-6"
+ cx="435.48053"
+ cy="-561.84009"
+ transform="scale(1,-1)"
+ rx="54.830173"
+ ry="55.384014" />
+ <ellipse
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:38.57448196;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:77.14896078, 38.57448039;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path891-5-0-0"
+ cx="275.97003"
+ cy="-561.84003"
+ transform="scale(1,-1)"
+ rx="54.830173"
+ ry="55.384014" />
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/grouping/ungroup.svg b/tagit/assets/icons/scalable/grouping/ungroup.svg
new file mode 100644
index 0000000..0e656b7
--- /dev/null
+++ b/tagit/assets/icons/scalable/grouping/ungroup.svg
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="ungroup.svg"
+ inkscape:export-filename="../../kivy/grouping/ungroup.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="274.46091"
+ inkscape:cy="39.114495"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1031"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <circle
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:17.683;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:17.683,70.732;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path889"
+ cx="356.2576"
+ cy="511.83615"
+ r="180.13463" />
+ <g
+ id="g1097"
+ transform="matrix(0.37263226,0,0,0.37639623,804.3104,348.85233)"
+ style="fill:#c8c8c8;fill-opacity:1">
+ <circle
+ transform="scale(1,-1)"
+ r="147.14285"
+ cy="-195.14612"
+ cx="-1203.8279"
+ id="path891-5-0"
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:103;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:206.0000011, 103.00000055000000998;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
+ <circle
+ transform="scale(1,-1)"
+ r="147.14285"
+ cy="-565.86047"
+ cx="-989.7959"
+ id="path891-5-0-6"
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:103;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:206.0000011, 103.00000055000000998;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
+ <circle
+ transform="scale(1,-1)"
+ r="147.14285"
+ cy="-565.86041"
+ cx="-1417.86"
+ id="path891-5-0-0"
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:103;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:206.0000011, 103.00000055000000998;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
+ </g>
+ </g>
+</svg>