diff options
Diffstat (limited to 'tagit')
48 files changed, 1437 insertions, 1491 deletions
diff --git a/tagit/__init__.py b/tagit/__init__.py index ec5f1c2..8c4285a 100644 --- a/tagit/__init__.py +++ b/tagit/__init__.py @@ -20,6 +20,9 @@ import collections import os import typing +import kivy.config +kivy.config.Config.set('input', 'mouse', 'mouse,disable_multitouch') + # kivy imports from kivy.resources import resource_add_path import kivy @@ -40,6 +43,8 @@ kivy.require('1.9.1') # add resources resource_add_path(os.path.join(os.path.dirname(__file__), 'assets', 'icons', 'kivy')) resource_add_path(os.path.join(os.path.dirname(__file__), 'assets', 'fonts', 'kivy')) +resource_add_path(os.path.join(os.path.dirname(__file__), 'assets', 'themes')) +resource_add_path(os.path.join(os.path.dirname(__file__), 'assets')) # load font from kivy.core.text import LabelBase diff --git a/tagit/actions/__init__.py b/tagit/actions/__init__.py index b2ab6bd..b8b65b8 100644 --- a/tagit/actions/__init__.py +++ b/tagit/actions/__init__.py @@ -16,7 +16,6 @@ from . import filter from . import grouping from . import misc #from . import objects -from . import planes from . import search from . import session from . import tagging @@ -80,8 +79,8 @@ class ActionBuilder(BuilderBase): 'ShowConsole': misc.ShowConsole, 'ShowHelp': misc.ShowHelp, 'ShowSettings': misc.ShowSettings, - 'ClipboardCopy': misc.ClipboardCopy, - 'ClipboardPaste': misc.ClipboardPaste, + #'ClipboardCopy': misc.ClipboardCopy, + #'ClipboardPaste': misc.ClipboardPaste, ## objects #'RotateLeft': objects.RotateLeft, #'RotateRight': objects.RotateRight, @@ -93,10 +92,6 @@ class ActionBuilder(BuilderBase): #'SetRank3': objects.SetRank3, #'SetRank4': objects.SetRank4, #'SetRank5': objects.SetRank5, - ## planes - 'ShowDashboard': planes.ShowDashboard, - 'ShowBrowsing': planes.ShowBrowsing, - 'ShowCodash': planes.ShowCodash, ## search 'Search': search.Search, 'ShowSelected': search.ShowSelected, diff --git a/tagit/actions/action.kv b/tagit/actions/action.kv index 5352964..807dd18 100644 --- a/tagit/actions/action.kv +++ b/tagit/actions/action.kv @@ -27,19 +27,4 @@ # to be disabled **after** show. show: [] - # decoration - canvas.before: - Color: - rgba: 17 / 255, 32 / 255, 148 / 255, self.selected_alpha - Rectangle: - pos: self.x, self.y + 1 - size: self.size - - canvas.after: - Color: - rgba: 17 / 255, 32 / 255, 148 / 255, self.selected_alpha - Line: - width: 2 - rectangle: self.x, self.y, self.width, self.height - ## EOF ## diff --git a/tagit/actions/filter.py b/tagit/actions/filter.py index c5cc912..52f5817 100644 --- a/tagit/actions/filter.py +++ b/tagit/actions/filter.py @@ -90,7 +90,7 @@ class AddToken(Action): def apply(self, token=None): if token is None: - sugg = self.root.session.storage.all(ns.bsfs.Tag).label(node=False) + sugg = self.root.session.storage.all(ns.bsn.Tag).label(node=False) dlg = dialogues.TokenEdit(suggestions=sugg) dlg.bind(on_ok=lambda wx: self.add_from_string(wx.text)) dlg.open() @@ -125,7 +125,7 @@ class EditToken(Action): text = kp.StringProperty('Edit token') def apply(self, token): - sugg = self.root.session.storage.all(ns.bsfs.Tag).label(node=False) + sugg = self.root.session.storage.all(ns.bsn.Tag).label(node=False) text = self.root.session.filter_to_string(token) dlg = dialogues.TokenEdit(text=text, suggestions=sugg) dlg.bind(on_ok=lambda obj: self.on_ok(token, obj)) diff --git a/tagit/actions/grouping.py b/tagit/actions/grouping.py index 05c651e..301af20 100644 --- a/tagit/actions/grouping.py +++ b/tagit/actions/grouping.py @@ -23,7 +23,7 @@ from tagit.widgets import Binding from .action import Action # constants -GROUP_PREFIX = Namespace('http://example.com/me/group') +GROUP_PREFIX = Namespace('http://example.com/me/group')() # exports __all__ = [] @@ -57,7 +57,8 @@ class CreateGroup(Action): with self.root.browser as browser, \ self.root.session as session: # create group - grp = session.storage.node(ns.bsfs.Group, GROUP_PREFIX[uuid.UUID()()]) + grp = session.storage.node(ns.bsn.Group, + getattr(GROUP_PREFIX, uuid.UUID()())) if label is not None: grp.set(ns.bsg.label, label) @@ -99,7 +100,7 @@ class DissolveGroup(Action): cursor = browser.cursor if cursor is not None and cursor in browser.folds: grp = browser.folds[cursor].group - ents = session.storage.get(ns.bsfs.Entity, + ents = session.storage.get(ns.bsn.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 diff --git a/tagit/actions/misc.py b/tagit/actions/misc.py index b7d0a87..f74e0f9 100644 --- a/tagit/actions/misc.py +++ b/tagit/actions/misc.py @@ -27,7 +27,7 @@ from tagit.widgets import Binding from .action import Action # constants -HELP_URL = 'https://www.igsor.net/projects/tagit/' +HELP_URL = 'https://www.bsfs.io/' # exports __all__ = [] diff --git a/tagit/actions/planes.kv b/tagit/actions/planes.kv deleted file mode 100644 index 184f949..0000000 --- a/tagit/actions/planes.kv +++ /dev/null @@ -1,15 +0,0 @@ -#:import resource_find kivy.resources.resource_find - -<ShowDashboard>: - source: resource_find('atlas://planes/dashboard') - tooltip: 'Switch to the Dashboard' - -<ShowBrowsing>: - source: resource_find('atlas://planes/browsing') - tooltip: 'Switch to the browsing plane' - -<ShowCodash>: - source: resource_find('atlas://planes/codash') - tooltip: 'Switch to the contextual dashboard' - -## EOF ## diff --git a/tagit/actions/planes.py b/tagit/actions/planes.py deleted file mode 100644 index 89f93bb..0000000 --- a/tagit/actions/planes.py +++ /dev/null @@ -1,57 +0,0 @@ -""" - -Part of the tagit module. -A copy of the license is provided with the project. -Author: Matthias Baumgartner, 2022 -""" -# standard imports -import os - -# kivy imports -from kivy.lang import Builder -import kivy.properties as kp - -# inner-module imports -from .action import Action - -# exports -__all__ = [] - - -## code ## - -# load kv -Builder.load_file(os.path.join(os.path.dirname(__file__), 'planes.kv')) - -# classes - -class ShowDashboard(Action): - """Switch to the dashboard.""" - text = kp.StringProperty('Dashboard') - - def apply(self): - planes = self.root.planes - if planes.current_slide != planes.dashboard: - planes.load_slide(planes.dashboard) - - -class ShowBrowsing(Action): - """Switch to the browsing plane.""" - text = kp.StringProperty('Browsing') - - def apply(self): - planes = self.root.planes - if planes.current_slide != planes.browsing: - planes.load_slide(planes.browsing) - - -class ShowCodash(Action): - """Switch to the contextual dashboard.""" - text = kp.StringProperty('Context') - - def apply(self): - planes = self.root.planes - if planes.current_slide != planes.codash: - planes.load_slide(planes.codash) - -## EOF ## diff --git a/tagit/actions/search.py b/tagit/actions/search.py index ca36a51..dd6c50c 100644 --- a/tagit/actions/search.py +++ b/tagit/actions/search.py @@ -128,7 +128,7 @@ class Search(Action, StorageAwareMixin, ConfigAwareMixin): items = Cache.get(self._CACHE_CATEGORY, (query, sort), None) if items is None: # FIXME: mb/port: consider sort - items = list(session.storage.get(ns.bsfs.File, query)) + items = list(session.storage.sorted(ns.bsn.Entity, query)) Cache.append(self._CACHE_CATEGORY, (query, sort), items) # apply search order because it's cheaper to do it here rather diff --git a/tagit/actions/tagging.py b/tagit/actions/tagging.py index 8a20702..ab25cb8 100644 --- a/tagit/actions/tagging.py +++ b/tagit/actions/tagging.py @@ -9,6 +9,9 @@ from functools import reduce, partial import operator import os +# external imports +import urllib.parse + # kivy imports from kivy.lang import Builder import kivy.properties as kp @@ -24,7 +27,7 @@ from .action import Action # constants TAGS_SEPERATOR = ',' -TAG_PREFIX = Namespace('http://example.com/me/tag') +TAG_PREFIX = Namespace('http://example.com/me/tag')() # exports __all__ = [] @@ -45,7 +48,7 @@ class AddTag(Action): def apply(self): if len(self.root.browser.selection) > 0: - tags = self.root.session.storage.all(ns.bsfs.Tag).label(node=True) + tags = self.root.session.storage.all(ns.bsn.Tag).label(node=True) dlg = dialogues.SimpleInput( suggestions=set(tags.values()), suggestion_sep=TAGS_SEPERATOR) @@ -67,7 +70,8 @@ class AddTag(Action): # FIXME: replace with proper tag factory for lbl in labels: if lbl not in lut: - tag = self.root.session.storage.node(ns.bsfs.Tag, TAG_PREFIX[lbl]) + tag = self.root.session.storage.node(ns.bsn.Tag, + getattr(TAG_PREFIX, urllib.parse.quote(lbl))) tag.set(ns.bst.label, lbl) tags.add(tag) with self.root.browser as browser, \ @@ -75,7 +79,7 @@ class AddTag(Action): # get objects ents = browser.unfold(browser.selection) # collect tags - tags = reduce(operator.add, tags) # FIXME: mb/port: pass set once supported by Nodes.set + tags = reduce(operator.add, tags, self.root.session.storage.empty(ns.bsn.Tag)) # FIXME: mb/port: pass set once supported by Nodes.set # set tags ents.set(ns.bse.tag, tags) session.dispatch('on_predicate_modified', ns.bse.tag, ents, tags) @@ -93,7 +97,7 @@ class EditTag(Action): with self.root.browser as browser: if len(browser.selection) > 0: # get all known tags - all_tags = self.root.session.storage.all(ns.bsfs.Tag).label(node=True) + all_tags = self.root.session.storage.all(ns.bsn.Tag).label(node=True) # get selection tags ent_tags = browser.unfold(browser.selection).tag.label(node=True) if len(ent_tags) == 0: @@ -126,7 +130,7 @@ class EditTag(Action): added_labels = labels - original_labels # get tags of removed labels removed = {tag for tag, lbl in tags.items() if lbl in removed_labels} - removed = reduce(operator.add, removed) + removed = reduce(operator.add, removed, self.root.session.storage.empty(ns.bsn.Tag)) # get tags of added labels # FIXME: deny adding tags if tag vocabulary is fixed (ontology case) lut = {label: tag for tag, label in tags.items()} @@ -134,10 +138,11 @@ class EditTag(Action): # FIXME: replace with proper tag factory for lbl in added_labels: if lbl not in lut: - tag = self.root.session.storage.node(ns.bsfs.Tag, TAG_PREFIX[lbl]) + tag = self.root.session.storage.node(ns.bsn.Tag, + getattr(TAG_PREFIX, urllib.parse.quote(lbl))) tag.set(ns.bst.label, lbl) added.add(tag) - added = reduce(operator.add, added) + added = reduce(operator.add, added, self.root.session.storage.empty(ns.bsn.Tag)) # apply differences with self.root.browser as browser, \ self.root.session as session: diff --git a/tagit/apps/__init__.py b/tagit/apps/__init__.py index 4c64128..84d0bf1 100644 --- a/tagit/apps/__init__.py +++ b/tagit/apps/__init__.py @@ -1,10 +1,52 @@ -""" +#!/usr/bin/env python3 +"""The tagit applications. Part of the tagit module. A copy of the license is provided with the project. Author: Matthias Baumgartner, 2022 """ +# standard imports +import argparse +import typing + +# tagit imports +import tagit + # inner-module imports from .desktop import main as desktop +# exports +__all__: typing.Sequence[str] = ( + 'desktop', + 'main', + ) + +# config +apps = { + 'desktop' : desktop, + } + + +## code ## + +def main(argv=None): + """The BSFS browser, focused on image tagging.""" + parser = argparse.ArgumentParser(description=main.__doc__, prog='tagit') + # version + parser.add_argument('--version', action='version', + version='%(prog)s {}.{}.{}'.format(*tuple(tagit.version_info))) # pylint: disable=C0209 + # application selection + parser.add_argument('app', choices=apps.keys(), nargs='?', default='desktop', + help='Select the application to run.') + # dangling args + parser.add_argument('rest', nargs=argparse.REMAINDER) + # parse + args = parser.parse_args() + # run application + apps[args.app](args.rest) + +if __name__ == '__main__': + import sys + main(sys.argv[1:]) + ## EOF ## diff --git a/tagit/apps/desktop.py b/tagit/apps/desktop.py index 7b21336..149bf30 100644 --- a/tagit/apps/desktop.py +++ b/tagit/apps/desktop.py @@ -10,6 +10,7 @@ import typing # kivy imports from kivy.app import App +from kivy.resources import resource_find from kivy.uix.settings import SettingsWithSidebar # tagit imports @@ -26,6 +27,26 @@ __all__: typing.Sequence[str] = ( ## code ## +def load_data_hook(cfg, store): + """Data loading hook to circumvent non-persistent storage.""" + import pickle + import os + # fetch data_hook config flags + schema_path = os.path.expanduser(cfg('session', 'data_hook', 'schema')) + triples_path = os.path.expanduser(cfg('session', 'data_hook', 'triples')) + # load data if present + if os.path.exists(schema_path) and os.path.exists(triples_path): + with open(schema_path, 'rb') as ifile: + store._backend._schema = pickle.load(ifile) + with open(triples_path, 'rb') as ifile: + for triple in pickle.load(ifile): + store._backend._graph.add(triple) + return store + +config.declare(('session', 'data_hook', 'schema'), config.String(), '') +config.declare(('session', 'data_hook', 'triples'), config.String(), '') + + class TagitApp(App): """The tagit main application.""" @@ -34,41 +55,29 @@ class TagitApp(App): self.settings_cls = SettingsWithSidebar # set title - self.title = 'tagit v2.0' + self.title = 'tagit v0.23.03' - # FIXME: mb/port - # load essentials + # load config + from tagit.config.loader import load_settings, TAGITRC + cfg = load_settings(TAGITRC, 0) - #from tagit.config.loader import load_settings, TAGITRC - #cfg = load_settings(TAGITRC, 0) - cfg = config.Settings.Open(os.path.join(os.path.dirname(__file__), 'port-config.yaml')) - - # FIXME: mb/port/bsfs # open BSFS storage - store = bsfs.Open(cfg('session', 'bsfs')) + store = load_data_hook(cfg, bsfs.Open(cfg('session', 'bsfs'))) # FIXME: mb/port: data hook # check storage schema - # FIXME: Move somewhere else?! - with open(os.path.join(os.path.dirname(__file__), 'port-schema.nt'), 'rt') as ifile: + with open(resource_find('required_schema.nt'), 'rt') as ifile: required_schema = bsfs.schema.from_string(ifile.read()) - # FIXME: Since the store isn't persistent, we migrate to the required one here. - #if not required_schema <= store.schema: - # raise Exception('') - store.migrate(required_schema) - - # FIXME: debug: add some data to the storage - from . import port_data - port_data.add_port_data(store) + if not required_schema.consistent_with(store.schema): + raise Exception("The storage's schema is incompatible with tagit's requirements") + if not required_schema <= store.schema: + store.migrate(required_schema | store.schema) # create widget - return desktop.MainWindow(cfg, store, None) # FIXME: expects cfg, stor, log arguments + return desktop.MainWindow(cfg, store, None) # FIXME: mb/port: expects log arguments def on_start(self): # trigger startup operations self.root.on_startup() - # FIXME: mb/port - #def on_stop(self): - # self.root.session.storage.close() def main(argv): """Start the tagit GUI. Opens a window to browse images.""" diff --git a/tagit/apps/port-config.yaml b/tagit/apps/port-config.yaml deleted file mode 100644 index a9907b7..0000000 --- a/tagit/apps/port-config.yaml +++ /dev/null @@ -1,154 +0,0 @@ -session: - first_start: false - paths: - searchlog: ~/.tagit.log - bsfs: # FIXME: mb/port: rename to storage, but that space is currently polluted - Graph: - backend: - SparqlStore: {} - user: 'http://example.com/me' - script: - #- ShowBrowsing - - [AddToken, 'hello'] -storage: - index: - preview_size: - - 50 - - 200 - - 400 -ui: - standalone: - plane: browsing - keytriggers: - - CreateGroup - - DissolveGroup - - AddToGroup - - MoveCursorUp - - MoveCursorDown - - MoveCursorLeft - - MoveCursorRight - - MoveCursorLast - - MoveCursorFirst - - NextPage - - PreviousPage - - ScrollDown - - ScrollUp - - ZoomIn - - ZoomOut - - Select - - SelectAll - - SelectNone - - SelectMulti - - SelectRange - - AddToken - - GoBack - - GoForth - - SearchByAddressOnce - - SearchmodeSwitch - - AddTag - - EditTag - - OpenGroup - #- RepresentGroup - - Search - - ShowSelected - - RemoveSelected - - OpenExternal - - ShowHelp - browser: - maxcols: 8 - maxrows: 8 - buttondocks: - sidebar_left: - - Menu - - ShowDashboard - - AddToken - - AddTag - - EditTag - - CreateGroup - - DissolveGroup - - SelectAll - - SelectNone - - SelectInvert - - SelectAdditive - - SelectSubtractive - - SelectSingle - - SelectMulti - - SelectRange - - ShellDrop - filter: - - AddToken - - EditToken - context: - app: - - ShowSettings - - ShowHelp - - ShowConsole - - ShowBrowsing - browser: - - ZoomIn - - ZoomOut - # clipboard: - # - ClipboardCopy - # - ClipboardPaste - grouping: - - CreateGroup - - DissolveGroup - - AddToGroup - # - RepresentGroup - # - RemoveFromGroup - root: - - LoadSession - # search: - # - ShowSelected - # - RemoveSelected - select: - - SelectAll - - SelectNone - - SelectInvert - - SelectSingle - - SelectMulti - - SelectRange - - SelectAdditive - - SelectSubtractive - tagging: - - AddTag - - EditTag - # - SetRank1 - # - SetRank3 - # - SetRank5 - search: - sort_blacklist: - - entity - - flash - - latitude - - longitude - - mime - - author - - camera - - attributes - tiledocks: - dashboard: {} - #Buttons: - # buttons: - # - ShowBrowsing - # - CreateSession - # - CreateTempSession - # - LoadSession - # - ReloadSession - # - ImportObjects - # - ItemExport - # - UpdateSelectedObjects - # - ShowHelp - # - ShowSettings - #Hints: {} - #LibSummary: {} - #Searchtree: {} - #TagHistogram: {} - #Tagcloud: {} - sidebar_right: - Info: {} - CursorTags: {} - BrowserTags: {} - #SelectionTags: {} - #Geo: {} - window_size: 1024x768 diff --git a/tagit/apps/port_data.py b/tagit/apps/port_data.py deleted file mode 100644 index cda2d63..0000000 --- a/tagit/apps/port_data.py +++ /dev/null @@ -1,127 +0,0 @@ -import os -from tagit.utils import ns -from tagit.utils.bsfs import URI - -def add_port_data(store): - # tags - t_hello = store.node(ns.bsfs.Tag, URI('http://example.com/me/tag#hello')) \ - .set(ns.bst.label, 'hello') - t_world = store.node(ns.bsfs.Tag, URI('http://example.com/me/tag#world')) \ - .set(ns.bst.label, 'world') - t_foobar = store.node(ns.bsfs.Tag, URI('http://example.com/me/tag#foobar')) \ - .set(ns.bst.label, 'foobar') - - # entities - n0 = store.node(ns.bsfs.File, URI('http://example.com/me/entity#01')) \ - .set(ns.bse.filename, 'textfile.t') \ - .set(ns.bse.filesize, 100) \ - .set(ns.bse.tag, t_hello) \ - .set(ns.bse.tag, t_foobar) \ - .set(ns.bse.latitude, 47.374444) \ - .set(ns.bse.longitude, 8.541111) - n1 = store.node(ns.bsfs.File, URI('http://example.com/me/entity#02')) \ - .set(ns.bse.filename, 'document.pdf') \ - .set(ns.bse.filesize, 200) \ - .set(ns.bse.tag, t_world) \ - .set(ns.bse.tag, t_foobar) - n2 = store.node(ns.bsfs.File, URI('http://example.com/me/entity#03')) \ - .set(ns.bse.filename, 'document.odt') \ - .set(ns.bse.filesize, 300) \ - .set(ns.bse.tag, t_world) - n3 = store.node(ns.bsfs.File, URI('http://example.com/me/entity#04')) \ - .set(ns.bse.filename, 'image.jpg') \ - .set(ns.bse.comment, 'some image') \ - .set(ns.bse.tag, t_hello) \ - .set(ns.bse.tag, t_foobar) - n4 = store.node(ns.bsfs.File, URI('http://example.com/me/entity#05')) \ - .set(ns.bse.filename, 'image.png') \ - .set(ns.bse.comment, 'another image') \ - .set(ns.bse.tag, t_hello) - - # groups - grp = store.node(ns.bsfs.Group, URI('http://example.com/me/group#1234')) - grp.set(ns.bsg.represented_by, n0) - n0.set(ns.bse.group, grp) - n1.set(ns.bse.group, grp) - n3.set(ns.bse.group, grp) - - # previews - base = os.path.join(os.path.dirname(__file__), 'port-data') - n0.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent01_w100_h100')) \ - .set(ns.bsp.width, 100) \ - .set(ns.bsp.height, 100) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent01_w100_h100.jpg'), 'rb').read()) - ) - n0.set(ns.bse.preview, - store.node(ns.bsfs.Preview, str('http://example.com/me/preview#ent01_w400_h200')) \ - .set(ns.bsp.width, 200) \ - .set(ns.bsp.height, 400) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent01_w400_h200.jpg'), 'rb').read()) - ) - n0.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent01_w400_h400')) \ - .set(ns.bsp.width, 400) \ - .set(ns.bsp.height, 400) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent01_w400_h400.jpg'), 'rb').read()) - ) - n1.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent02_w100_h100')) \ - .set(ns.bsp.width, 100) \ - .set(ns.bsp.height, 100) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent02_w100_h100.jpg'), 'rb').read()) - ) - n1.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent02_w400_h200')) \ - .set(ns.bsp.width, 200) \ - .set(ns.bsp.height, 400) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent02_w400_h200.jpg'), 'rb').read()) - ) - n2.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent03_w100_h100')) \ - .set(ns.bsp.width, 100) \ - .set(ns.bsp.height, 100) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent03_w100_h100.jpg'), 'rb').read()) - ) - n2.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent03_w400_h200')) \ - .set(ns.bsp.width, 200) \ - .set(ns.bsp.height, 400) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent03_w400_h200.jpg'), 'rb').read()) - ) - n3.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent04_w100_h100')) \ - .set(ns.bsp.width, 100) \ - .set(ns.bsp.height, 100) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent04_w100_h100.png'), 'rb').read()) - ) - n3.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent04_w400_h200')) \ - .set(ns.bsp.width, 200) \ - .set(ns.bsp.height, 400) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent04_w400_h200.png'), 'rb').read()) - ) - n4.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent05_w100_h100')) \ - .set(ns.bsp.width, 100) \ - .set(ns.bsp.height, 100) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent05_w100_h100.jpg'), 'rb').read()) - ) - n4.set(ns.bse.preview, - store.node(ns.bsfs.Preview, URI('http://example.com/me/preview#ent05_w400_h200')) \ - .set(ns.bsp.width, 200) \ - .set(ns.bsp.height, 400) \ - .set(ns.bsp.orientation, 1) \ - .set(ns.bsp.asset, open(os.path.join(base, 'ent05_w400_h200.jpg'), 'rb').read()) - ) - diff --git a/tagit/assets/icons/scalable/objects/add_tag.svg b/tagit/assets/icons/scalable/objects/add_tag.svg new file mode 100644 index 0000000..6e73d56 --- /dev/null +++ b/tagit/assets/icons/scalable/objects/add_tag.svg @@ -0,0 +1,296 @@ +<?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_tag.svg" + inkscape:export-filename="../../kivy/objects/add_tag.png" + inkscape:export-xdpi="7.6199999" + inkscape:export-ydpi="7.6199999"> + <defs + id="defs4"> + <inkscape:path-effect + effect="bspline" + id="path-effect906" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850-3" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850-7" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850-7-3" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850-7-6" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-9" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-2" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-9-0" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-93" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-9-6" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-26" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-9-9" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-20" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.4" + inkscape:cx="-207.59181" + inkscape:cy="167.43915" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + showguides="true" + inkscape:guide-bbox="true" + inkscape:snap-global="false" + 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 /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-167.28122,-322.85977)"> + <g + transform="matrix(0.846821,-1.0151333,1.0151333,0.846821,-484.44796,260.70334)" + id="g870" + style="stroke:#c8c8c8;stroke-width:14.89931583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"> + <circle + r="23.687183" + cy="510.56808" + cx="261.76941" + id="path839" + style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:14.89931583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" /> + <g + style="stroke:#c8c8c8;stroke-width:14.89931583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + transform="translate(-9.9999667e-7,6.2227844)" + id="g864"> + <path + style="fill:none;stroke:#c8c8c8;stroke-width:14.89931583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 274.44346,437.98197 79.87503,66.36332 V 698.87342 H 169.22033 V 504.34529 l 80.13837,-66.36306" + id="path837" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccc" /> + <path + style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:14.89931583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" + id="path839-3" + sodipodi:type="arc" + sodipodi:cx="-420.58212" + sodipodi:cy="-313.05319" + sodipodi:rx="20.524721" + sodipodi:ry="20.524721" + sodipodi:start="0.40543239" + sodipodi:end="1.917446" + sodipodi:open="true" + d="m -401.7213,-304.95791 a 20.524721,20.524721 0 0 1 -25.83407,11.20855" + transform="rotate(-156.62757)" /> + </g> + </g> + <g + id="g848" + transform="translate(342.14286,-91.176621)"> + <path + inkscape:original-d="m -17.767337,666.45535 c 30.71351,-37.05555 61.4269,-74.11251 92.14015,-111.17087" + inkscape:path-effect="#path-effect906-26" + inkscape:connector-curvature="0" + id="path904-18" + d="M -17.767337,666.45535 C 12.945407,629.39916 43.658794,592.3422 74.372813,555.28448" + style="fill:none;stroke:#c8c8c8;stroke-width:15.01094818;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:original-d="M 83.888173,656.93999 C 46.832623,626.22647 9.7756729,595.51308 -27.282697,564.79983" + inkscape:path-effect="#path-effect906-20" + inkscape:connector-curvature="0" + id="path904-2" + d="M 83.888173,656.93999 C 46.831985,626.22724 9.7750303,595.51386 -27.282697,564.79983" + style="fill:none;stroke:#c8c8c8;stroke-width:15.01094818;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + </g> +</svg> diff --git a/tagit/assets/icons/scalable/objects/edit_tag.svg b/tagit/assets/icons/scalable/objects/edit_tag.svg new file mode 100644 index 0000000..c7d64e1 --- /dev/null +++ b/tagit/assets/icons/scalable/objects/edit_tag.svg @@ -0,0 +1,303 @@ +<?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="edit_tag.svg" + inkscape:export-filename="../../kivy/objects/edit_tag.png" + inkscape:export-xdpi="7.6199999" + inkscape:export-ydpi="7.6199999"> + <defs + id="defs4"> + <inkscape:path-effect + effect="bspline" + id="path-effect906" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850-3" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850-7" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850-7-3" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect850-7-6" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-9" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-2" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-9-0" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-93" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-9-6" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-26" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-9-9" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + <inkscape:path-effect + effect="bspline" + id="path-effect906-20" + is_visible="true" + weight="33.333333" + steps="2" + helper_size="0" + apply_no_weight="true" + apply_with_weight="true" + only_selected="false" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.4" + inkscape:cx="-97.948957" + inkscape:cy="170.29629" + 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 /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-167.28122,-322.85977)"> + <g + id="g1018" + transform="matrix(0.77049115,0.63859513,-0.63859513,0.77049115,403.95705,-187.73351)"> + <g + style="stroke:#c8c8c8;stroke-width:14.89931583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g870" + transform="matrix(0.00420274,-1.3209979,1.3209979,0.00420274,-397.55803,911.5166)"> + <circle + style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:14.89931583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" + id="path839" + cx="261.76941" + cy="510.56808" + r="23.687183" /> + <g + id="g864" + transform="translate(-9.9999667e-7,6.2227844)" + style="stroke:#c8c8c8;stroke-width:14.89931583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"> + <path + sodipodi:nodetypes="cccccc" + inkscape:connector-curvature="0" + id="path837" + d="m 274.44346,437.98197 79.87503,66.36332 V 698.87342 H 169.22033 V 504.34529 l 80.13837,-66.36306" + style="fill:none;stroke:#c8c8c8;stroke-width:14.89931583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + transform="rotate(-156.62757)" + d="m -401.7213,-304.95791 a 20.524721,20.524721 0 0 1 -25.83407,11.20855" + sodipodi:open="true" + sodipodi:end="1.917446" + sodipodi:start="0.40543239" + sodipodi:ry="20.524721" + sodipodi:rx="20.524721" + sodipodi:cy="-313.05319" + sodipodi:cx="-420.58212" + sodipodi:type="arc" + id="path839-3" + style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:14.89931583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" /> + </g> + </g> + <path + inkscape:original-d="m 500.54332,620.98029 c -48.09424,-10e-4 -96.18948,-10e-4 -144.28572,0" + inkscape:path-effect="#path-effect906" + inkscape:connector-curvature="0" + id="path904" + d="m 500.54332,620.98029 c -48.09424,0 -96.18948,0 -144.28572,0" + style="fill:none;stroke:#c8c8c8;stroke-width:15;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:original-d="m 500.54332,567.766 c -48.09424,-10e-4 -96.18948,-10e-4 -144.28572,0" + inkscape:path-effect="#path-effect906-26" + inkscape:connector-curvature="0" + id="path904-18" + d="m 500.54332,567.766 c -48.09424,0 -96.18948,0 -144.28572,0" + style="fill:none;stroke:#c8c8c8;stroke-width:15;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:original-d="m 500.54332,514.55171 c -48.09424,-10e-4 -96.18948,-10e-4 -144.28572,0" + inkscape:path-effect="#path-effect906-20" + inkscape:connector-curvature="0" + id="path904-2" + d="m 500.54332,514.55171 c -48.09424,0 -96.18948,0 -144.28572,0" + style="fill:none;stroke:#c8c8c8;stroke-width:15;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + </g> +</svg> diff --git a/tagit/assets/icons/scalable/planes/browsing.svg b/tagit/assets/icons/scalable/planes/browsing.svg deleted file mode 100644 index f502c36..0000000 --- a/tagit/assets/icons/scalable/planes/browsing.svg +++ /dev/null @@ -1,157 +0,0 @@ -<?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="browsing.svg" - inkscape:export-filename="../../kivy/misc/browsing.png" - inkscape:export-xdpi="7.6199999" - inkscape:export-ydpi="7.6199999"> - <defs - id="defs4"> - <marker - inkscape:stockid="Arrow2Mend" - orient="auto" - refY="0" - refX="0" - id="Arrow2Mend" - style="overflow:visible" - inkscape:isstock="true"> - <path - id="path847" - style="fill:#c8c8c8;fill-opacity:1;fill-rule:evenodd;stroke:#c8c8c8;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1" - d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" - transform="scale(-0.6)" - inkscape:connector-curvature="0" /> - </marker> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="1.4" - inkscape:cx="79.928267" - inkscape:cy="249.27915" - 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:guide - position="233.588,370" - orientation="1,0" - id="guide1107" - inkscape:locked="false" - inkscape:label="" - inkscape:color="rgb(0,0,255)" /> - <sodipodi:guide - position="144.36496,311.42857" - orientation="1,0" - id="guide1109" - inkscape:locked="false" - inkscape:label="" - inkscape:color="rgb(0,0,255)" /> - <sodipodi:guide - position="-77.142857,144.36496" - orientation="0,1" - id="guide1111" - inkscape:locked="false" - inkscape:label="" - inkscape:color="rgb(0,0,255)" /> - <sodipodi:guide - position="5.000315,233.58779" - orientation="0,1" - id="guide1113" - 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="fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:21.39488792;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="path4522" - cx="356.2576" - cy="511.83612" - r="178.27893" /> - <path - style="fill:#c8c8c8;stroke:#c8c8c8;stroke-width:25.67386436;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend);fill-opacity:1" - d="M 294.33569,574.10683 413.77539,454.66718" - id="path818" - inkscape:connector-curvature="0" /> - </g> -</svg> diff --git a/tagit/assets/icons/scalable/planes/codash.svg b/tagit/assets/icons/scalable/planes/codash.svg deleted file mode 100644 index b25c2b0..0000000 --- a/tagit/assets/icons/scalable/planes/codash.svg +++ /dev/null @@ -1,147 +0,0 @@ -<?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="codash.svg" - inkscape:export-filename="../../kivy/misc/dashboard.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.98994949" - inkscape:cx="63.505754" - inkscape:cy="116.58788" - 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="1056" - inkscape:window-x="0" - inkscape:window-y="1200" - 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:guide - position="233.588,370" - orientation="1,0" - id="guide1107" - inkscape:locked="false" - inkscape:label="" - inkscape:color="rgb(0,0,255)" /> - <sodipodi:guide - position="144.36496,311.42857" - orientation="1,0" - id="guide1109" - inkscape:locked="false" - inkscape:label="" - inkscape:color="rgb(0,0,255)" /> - <sodipodi:guide - position="-77.142857,144.36496" - orientation="0,1" - id="guide1111" - inkscape:locked="false" - inkscape:label="" - inkscape:color="rgb(0,0,255)" /> - <sodipodi:guide - position="5.000315,233.58779" - orientation="0,1" - id="guide1113" - 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)"> - <circle - style="fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:21.39488792;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="path4522" - cx="356.2576" - cy="511.83615" - r="178.27893" /> - <text - xml:space="preserve" - style="font-style:normal;font-weight:normal;font-size:103.40699768px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:2.5851748" - x="209.33694" - y="611.10687" - id="text823"><tspan - sodipodi:role="line" - id="tspan821" - x="209.33694" - y="611.10687" - style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:496.35357666px;font-family:'Berenis ADF Pro';-inkscape-font-specification:'Berenis ADF Pro Bold Italic';fill:#c8c8c8;fill-opacity:1;stroke-width:2.5851748">c</tspan></text> - </g> -</svg> diff --git a/tagit/assets/icons/scalable/planes/dashboard.svg b/tagit/assets/icons/scalable/planes/dashboard.svg deleted file mode 100644 index 6f7e4a3..0000000 --- a/tagit/assets/icons/scalable/planes/dashboard.svg +++ /dev/null @@ -1,142 +0,0 @@ -<?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="dashboard.svg" - inkscape:export-filename="../../kivy/misc/dashboard.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.98994949" - inkscape:cx="218.56417" - inkscape:cy="120.62849" - 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:guide - position="233.588,370" - orientation="1,0" - id="guide1107" - inkscape:locked="false" - inkscape:label="" - inkscape:color="rgb(0,0,255)" /> - <sodipodi:guide - position="144.36496,311.42857" - orientation="1,0" - id="guide1109" - inkscape:locked="false" - inkscape:label="" - inkscape:color="rgb(0,0,255)" /> - <sodipodi:guide - position="-77.142857,144.36496" - orientation="0,1" - id="guide1111" - inkscape:locked="false" - inkscape:label="" - inkscape:color="rgb(0,0,255)" /> - <sodipodi:guide - position="5.000315,233.58779" - orientation="0,1" - id="guide1113" - 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)"> - <path - d="m 356.64939,617.51072 c 0,0 -8.76851,5.82936 -14.76239,5.80268 -0.53358,0.24456 -0.84928,0.38685 -0.84928,0.38685 l 0.0133,-0.38685 c -1.25835,-0.0578 -2.52562,-0.23566 -3.80175,-0.55581 l -1.89422,-0.46688 c -10.46706,-2.59676 -16.0074,-12.9749 -13.40175,-23.43752 l 14.57119,-58.74275 6.57637,-26.53229 c 6.11838,-24.63362 -19.40009,5.23798 -24.63363,-6.10949 -3.45493,-7.50125 19.84919,-23.24187 36.87485,-35.10959 0,0 8.75961,-5.82047 14.75794,-5.79824 0.53803,-0.249 0.85373,-0.38684 0.85373,-0.38684 l -0.0222,0.38684 c 1.26725,0.0578 2.5345,0.23567 3.81065,0.55137 l 1.89421,0.47133 c 10.46707,2.59675 16.84335,13.18388 14.2466,23.6465 l -14.56675,58.7472 -6.58972,26.53228 c -6.10949,24.63363 18.98657,-5.35359 24.21566,6.00723 3.45493,7.49235 -20.27161,23.12626 -37.29282,34.99398 z m 36.93264,-183.6139 c -3.63724,14.65123 -18.46187,23.58425 -33.10866,19.94701 -14.65566,-3.63724 -23.5887,-18.45743 -19.95146,-33.11311 3.63725,-14.65567 18.46189,-23.58425 33.10866,-19.94701 14.65569,3.63279 23.58426,18.45744 19.95146,33.11311 z" - id="path2" - inkscape:connector-curvature="0" - style="fill:#c8c8c8;fill-opacity:1;stroke-width:4.44650316" - sodipodi:nodetypes="ccccccccccccccccccccccccscc" /> - <circle - style="fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:21.39488792;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="path4522" - cx="356.2576" - cy="511.83615" - r="178.27893" /> - </g> -</svg> diff --git a/tagit/apps/port-schema.nt b/tagit/assets/required_schema.nt index 7569052..d48f0bd 100644 --- a/tagit/apps/port-schema.nt +++ b/tagit/assets/required_schema.nt @@ -5,57 +5,55 @@ prefix xsd: <http://www.w3.org/2001/XMLSchema#> prefix schema: <http://schema.org/> # common bsfs prefixes -prefix bsfs: <http://bsfs.ai/schema/> -prefix bse: <http://bsfs.ai/schema/Entity#> -prefix bst: <http://bsfs.ai/schema/Tag#> -prefix bsg: <http://bsfs.ai/schema/Group#> -prefix bsp: <http://bsfs.ai/schema/Preview#> +prefix bsfs: <https://schema.bsfs.io/core/> +prefix bsl: <https://schema.bsfs.io/core/Literal/> +prefix bsn: <https://schema.bsfs.io/ie/Node/> +prefix bse: <https://schema.bsfs.io/ie/Node/Entity#> +prefix bst: <https://schema.bsfs.io/ie/Node/Tag#> +prefix bsg: <https://schema.bsfs.io/ie/Node/Group#> +prefix bsp: <https://schema.bsfs.io/ie/Node/Preview#> # essential nodes -bsfs:Entity rdfs:subClassOf bsfs:Node . -bsfs:Preview rdfs:subClassOf bsfs:Node . -bsfs:File rdfs:subClassOf bsfs:Entity . -bsfs:Tag rdfs:subClassOf bsfs:Node . -bsfs:Group rdfs:subClassOf bsfs:Node . +bsn:Entity rdfs:subClassOf bsfs:Node . +bsn:Preview rdfs:subClassOf bsfs:Node . +bsn:Tag rdfs:subClassOf bsfs:Node . +bsn:Group rdfs:subClassOf bsfs:Node . # common definitions -bsfs:BinaryBlob rdfs:subClassOf bsfs:Literal . -bsfs:URI rdfs:subClassOf bsfs:Literal . -bsfs:Number rdfs:subClassOf bsfs:Literal . -bsfs:Time rdfs:subClassOf bsfs:Literal . -bsfs:JPEG rdfs:subClassOf bsfs:BinaryBlob . +bsl:BinaryBlob rdfs:subClassOf bsfs:Literal . +bsl:URI rdfs:subClassOf bsfs:Literal . +bsl:Number rdfs:subClassOf bsfs:Literal . +bsl:Time rdfs:subClassOf bsfs:Literal . +<https://schema.bsfs.io/ie/Literal/BinaryBlob/JPEG> rdfs:subClassOf bsl:BinaryBlob . xsd:string rdfs:subClassOf bsfs:Literal . -xsd:integer rdfs:subClassOf bsfs:Number . +xsd:integer rdfs:subClassOf bsl:Number . +xsd:float rdfs:subClassOf bsl:Number . bse:filename rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:File ; + rdfs:domain bsn:Entity ; rdfs:range xsd:string ; - rdfs:label "File name"^^xsd:string ; - schema:description "Filename of entity in some filesystem."^^xsd:string ; bsfs:unique "true"^^xsd:boolean . bse:filesize rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:File ; + rdfs:domain bsn:Entity ; rdfs:range xsd:integer ; - rdfs:label "File size"^^xsd:string ; - schema:description "File size of entity in some filesystem."^^xsd:string ; bsfs:unique "true"^^xsd:boolean . bse:mime rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:File ; + rdfs:domain bsn:Entity ; rdfs:range xsd:string ; bsfs:unique "true"^^xsd:boolean . bse:preview rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:Entity ; - rdfs:range bsfs:Preview . + rdfs:domain bsn:Entity ; + rdfs:range bsn:Preview . bse:tag rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:File ; - rdfs:range bsfs:Tag . + rdfs:domain bsn:Entity ; + rdfs:range bsn:Tag . bst:label rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:Tag ; + rdfs:domain bsn:Tag ; rdfs:range xsd:string ; bsfs:unique "true"^^xsd:boolean . @@ -64,45 +62,49 @@ bse:comment rdfs:subClassOf bsfs:Predicate ; rdfs:range xsd:string . bse:group rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:Entity ; - rdfs:range bsfs:Group . + rdfs:domain bsn:Entity ; + rdfs:range bsn:Group . + +bsg:label rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsn:Group ; + rdfs:range xsd:string ; + bsfs:unique "true"^^xsd:boolean . bsg:represented_by rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:Group ; - rdfs:range bsfs:File ; + rdfs:domain bsn:Group ; + rdfs:range bsn:Entity ; bsfs:unique "true"^^xsd:boolean . bse:longitude rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:File ; - rdfs:range xsd:integer ; + rdfs:domain bsn:Entity ; + rdfs:range xsd:float ; bsfs:unique "true"^^xsd:boolean . bse:latitude rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:File ; - rdfs:range xsd:integer ; + rdfs:domain bsn:Entity ; + rdfs:range xsd:float ; bsfs:unique "true"^^xsd:boolean . ## preview nodes bsp:width rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:Preview ; + rdfs:domain bsn:Preview ; rdfs:range xsd:integer ; bsfs:unique "true"^^xsd:boolean . bsp:height rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:Preview ; + rdfs:domain bsn:Preview ; rdfs:range xsd:integer ; bsfs:unique "true"^^xsd:boolean . bsp:orientation rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:Preview ; + rdfs:domain bsn:Preview ; rdfs:range xsd:integer ; bsfs:unique "true"^^xsd:boolean . bsp:asset rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:Preview ; - rdfs:range bsfs:JPEG ; + rdfs:domain bsn:Preview ; + rdfs:range <https://schema.bsfs.io/ie/Literal/BinaryBlob/JPEG> ; bsfs:unique "true"^^xsd:boolean . - diff --git a/tagit/assets/themes/default/style.kv b/tagit/assets/themes/default/style.kv new file mode 100644 index 0000000..71b4cb7 --- /dev/null +++ b/tagit/assets/themes/default/style.kv @@ -0,0 +1,212 @@ + +# DEBUG: Draw borders around all widgets +#<Widget>: +# canvas.after: +# Line: +# rectangle: self.x+1,self.y+1,self.width-1,self.height-1 +# dash_offset: 5 +# dash_length: 3 + +# color definitions +#:set colors_background [0x1c/256, 0x1b/256, 0x22/256] # dark grey +#:set colors_text [0xc5/256, 0xc9/256, 0xc7/256] # silver +#:set colors_highlight [0xb5/256, 0x94/256, 0x10/256] # darkgold + +# generic styles + +<Label>: + # default text color + color: colors_text + + +# main window elements + +<MainWindow>: + # background color + canvas.before: + Color: + rgb: colors_background + Rectangle: + pos: self.pos + size: self.size + +<HGuide>: # Horizontal guide + canvas: + Color: + rgb: colors_text + Line: + points: self.x, self.center_y, self.x + self.width, self.center_y + width: 2 + + +# browser elements + +<BrowserItem>: + canvas.after: + # selection highlighting + Color: + rgba: colors_highlight + [1 if self.is_selected else 0] + + # checkmarks + #Ellipse: + # pos: self.x + 5, self.y + self.height - 30 + # size: 25, 25 + #Color: + # rgba: [1,1,1] + [1 if self.is_selected else 0] + #Line: + # width: 3 + # points: + # self.x + 12, self.y + self.height - 20, \ + # self.x + 17, self.y + self.height - 23, \ + # self.x + 22, self.y + self.height - 12 + + # border highlight + Line: + width: 2 + points: + self.x, self.y + self.height - 60, \ + self.x, self.y + self.height, \ + self.x + 60, self.y + self.height + Triangle: + points: + self.x, self.y + self.height - 40, \ + self.x, self.y + self.height, \ + self.x + 40, self.y + self.height + #Line: + # width: 2 + # points: + # self.x + self.width - 40, \ + # self.y, self.x + self.width, \ + # self.y, self.x + self.width, self.y + 40 + + # cursor highlighting + Color: + rgba: colors_text + [1 if self.is_cursor else 0] + Line: + width: 2 + rectangle: self.x, self.y, self.width, self.height + + +# filter elements + +<Addressbar>: + background_color: (0.2,0.2,0.2,1) if self.focus else (0.15,0.15,0.15,1) + foreground_color: (1,1,1,1) + +<Shingle>: + canvas.before: + Color: + rgba: colors_highlight + [0.25 if root.active else 0] + RoundedRectangle: + pos: root.pos + size: root.size + + canvas.after: + Color: + rgb: colors_text + Line: + rounded_rectangle: self.x+1, self.y+1, self.width-1, self.height-1, self.height/2 + +<Avatar>: + canvas.before: + Color: + rgb: colors_background + Ellipse: + pos: self.pos + size: self.size + + canvas.after: + Color: + rgb: colors_text + Line: + width: 2 + circle: self.center_x, self.center_y, self.height/2.0 + + + +<ShingleText>: + canvas.after: + Color: + rgba: colors_background + [0.5 if not self.active else 0] + Rectangle: + pos: self.pos + size: self.size + +<ShingleRemove>: + background_color: colors_background + background_normal: '' + opacity: 0.5 + + +# other elements + +<TileTabularLine>: + spacing: 5 + +<TileTabularRow>: + spacing: 5 + +<Action>: + # decoration + canvas.before: + Color: + rgba: 17 / 255, 32 / 255, 148 / 255, self.selected_alpha + Rectangle: + pos: self.x, self.y + 1 + size: self.size + + canvas.after: + Color: + rgba: 17 / 255, 32 / 255, 148 / 255, self.selected_alpha + Line: + width: 2 + rectangle: self.x, self.y, self.width, self.height + + +<DialogueContentBase>: + canvas: + # mask main window + Color: + rgba: 0,0,0, 0.7 * self.parent._anim_alpha + Rectangle: + size: self.parent._window.size if self.parent._window else (0, 0) + + # solid background color + Color: + rgb: 1, 1, 1 + BorderImage: + source: self.parent.background + border: self.parent.border + pos: self.pos + size: self.size + +<DialogueTitle>: + font_size: '16sp' + bold: True + halign: 'center' + valing: 'middle' + canvas.before: + # Background + Color: + rgb: 0.2, 0.2, 0.2 + Rectangle: + size: self.size + pos: self.pos + + # top border + #Color: + # rgb: 0.5, 0.5, 0.5 + #Line: + # points: self.x, self.y + self.height, self.x + self.width, self.y + self.height + # width: 2 + + # bottom border + #Color: + # rgb: 0.5, 0.5, 0.5 + #Line: + # points: self.x, self.y, self.x + self.width, self.y + # width: 2 + + + +## EOF ## diff --git a/tagit/config/loader.py b/tagit/config/loader.py index 87ac328..47a51fa 100644 --- a/tagit/config/loader.py +++ b/tagit/config/loader.py @@ -54,10 +54,8 @@ def load_settings(path=None, verbose=0): searchpaths += SETTINGS_PATH # create default user config on first start - first_start = False user_config = os.path.expanduser(os.path.join('~', TAGITRC)) if os.path.exists(DEFAULT_USER_CONFIG) and not os.path.exists(user_config): - first_start = True shutil.copy(DEFAULT_USER_CONFIG, user_config) # scan searchpaths @@ -76,8 +74,6 @@ def load_settings(path=None, verbose=0): # update verbosity from argument cfg.set(('session', 'verbose'), max(cfg('session', 'verbose'), verbose)) - # set first start according to previous user config existence - cfg.set(('session', 'first_start'), first_start) return cfg ## EOF ## diff --git a/tagit/config/settings.json b/tagit/config/settings.json deleted file mode 100644 index 8fd4754..0000000 --- a/tagit/config/settings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "ui": { - "standalone": { - "keytriggers": [ - ], - "buttondocks": { - "filter": [ - ], - "navigation_left": [ - ], - "navigation_right": [ - ], - "status": [ - ] - }, - "context": { - "root": [ - ] - } - } - } -} diff --git a/tagit/config/settings.yaml b/tagit/config/settings.yaml new file mode 100644 index 0000000..e9264d4 --- /dev/null +++ b/tagit/config/settings.yaml @@ -0,0 +1,10 @@ +ui: + standalone: + keytriggers: [] + buttondocks: + navigation_left: [] + navigation_right: [] + filter: [] + status: [] + context: + root: [] diff --git a/tagit/config/user-defaults.json b/tagit/config/user-defaults.json deleted file mode 100644 index 1ff7ab4..0000000 --- a/tagit/config/user-defaults.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "session": { - "first_start": false, - "paths": { - "searchlog": "~/.tagit.log" - } - }, - "storage": { - "index": { - "preview_size": [ - 50, - 200, - 400 - ] - } - }, - "ui": { - "standalone": { - "window_size": "1024x768", - "browser": { - "maxrows": 8, - "maxcols": 8 - }, - "buttondocks": { - "sidebar_left": [ - "Menu", - "ShowDashboard", - "AddTag", - "EditTag", - "CreateGroup", - "DissolveGroup", - "SelectAll", - "SelectNone", - "SelectInvert", - "SelectAdditive", - "SelectSubtractive", - "SelectSingle", - "SelectMulti", - "SelectRange" - ] - }, - "tiledocks": { - "dashboard": { - "Buttons": { - "buttons": [ - "ShowBrowsing", - "CreateSession", - "CreateTempSession", - "LoadSession", - "ReloadSession", - "ImportObjects", - "ItemExport", - "UpdateSelectedObjects", - "ShowHelp", - "ShowSettings" - ] - }, - "LibSummary": {}, - "Hints": {}, - "TagHistogram": {}, - "Tagcloud": {}, - "Searchtree": {} - }, - "sidebar_right": { - "Info": {}, - "CursorTags": {}, - "Venn": {} - } - }, - "search": { - "sort_blacklist": [ - "entity", - "flash", - "latitude", - "longitude", - "mime", - "author", - "camera", - "attributes" - ] - }, - "context": { - "app": [ - "ShowSettings", - "ShowHelp", - "ShowConsole" - ], - "session": [ - "ItemExport", - "ImportObjects" - ], - "search": [ - "ShowSelected", - "RemoveSelected" - ], - "browser": [ - "ZoomIn", - "ZoomOut" - ], - "select": [ - "SelectAll", - "SelectNone", - "SelectInvert", - "SelectSingle", - "SelectMulti", - "SelectRange", - "SelectAdditive", - "SelectSubtractive" - ], - "clipboard": [ - "ClipboardCopy", - "ClipboardPaste" - ], - "tagging": [ - "AddTag", - "EditTag", - "SetRank1", - "SetRank3", - "SetRank5" - ], - "grouping": [ - "CreateGroup", - "DissolveGroup", - "AddToGroup", - "RepresentGroup", - "RemoveFromGroup" - ], - "root": [ - "CloseSessionAndExit" - ] - } - } - } -} diff --git a/tagit/config/user-defaults.yaml b/tagit/config/user-defaults.yaml index 3ec23d0..447e10f 100644 --- a/tagit/config/user-defaults.yaml +++ b/tagit/config/user-defaults.yaml @@ -1,56 +1,95 @@ session: - first_start: false - paths: - searchlog: ~/.tagit.log -storage: - index: - preview_size: - - 50 - - 200 - - 400 + bsfs: + Graph: + backend: + SparqlStore: {} + user: 'http://example.com/me' + script: + - Search ui: standalone: + window_size: [1440, 810] + #maximize: True + keytriggers: + - MoveCursorUp + - MoveCursorDown + - MoveCursorLeft + - MoveCursorRight + - MoveCursorLast + - MoveCursorFirst + - NextPage + - PreviousPage + - ScrollDown + - ScrollUp + - ZoomIn + - ZoomOut + - Select + - SelectAll + - SelectNone + - SelectMulti + - SelectRange + - AddToken + - GoBack + - GoForth + - AddTag + - EditTag + - Search + - ShowSelected + - RemoveSelected browser: + cols: 4 + rows: 3 maxcols: 8 maxrows: 8 buttondocks: + navigation_left: + - MoveCursorFirst + - PreviousPage + - ScrollUp + navigation_right: + - ScrollDown + - NextPage + - MoveCursorLast + filter: + - GoBack + - GoForth + status: + - ZoomIn + - ZoomOut sidebar_left: - - Menu - - ShowDashboard - AddTag - EditTag - - CreateGroup - - DissolveGroup + - ShowSelected + - RemoveSelected - SelectAll - SelectNone - SelectInvert - - SelectAdditive - - SelectSubtractive - SelectSingle - - SelectMulti - SelectRange + - SelectMulti + - SelectAdditive + - SelectSubtractive context: app: - - ShowSettings - ShowHelp - ShowConsole + - ShowSettings browser: - ZoomIn - ZoomOut - clipboard: - - ClipboardCopy - - ClipboardPaste - grouping: - - CreateGroup - - DissolveGroup - - AddToGroup - - RepresentGroup - - RemoveFromGroup - root: - - CloseSessionAndExit + - MoveCursorFirst + - PreviousPage + - ScrollUp + - ScrollDown + - NextPage + - MoveCursorLast search: + - AddToken + - SortOrder - ShowSelected - RemoveSelected + - GoForth + - GoBack select: - SelectAll - SelectNone @@ -60,46 +99,9 @@ ui: - SelectRange - SelectAdditive - SelectSubtractive - session: - - ItemExport - - ImportObjects tagging: - AddTag - EditTag - - SetRank1 - - SetRank3 - - SetRank5 - search: - sort_blacklist: - - entity - - flash - - latitude - - longitude - - mime - - author - - camera - - attributes tiledocks: - dashboard: - Buttons: - buttons: - - ShowBrowsing - - CreateSession - - CreateTempSession - - LoadSession - - ReloadSession - - ImportObjects - - ItemExport - - UpdateSelectedObjects - - ShowHelp - - ShowSettings - Hints: {} - LibSummary: {} - Searchtree: {} - TagHistogram: {} - Tagcloud: {} sidebar_right: - CursorTags: {} Info: {} - Venn: {} - window_size: 1024x768 diff --git a/tagit/dialogues/dialogue.kv b/tagit/dialogues/dialogue.kv index e23f0db..e2cab66 100644 --- a/tagit/dialogues/dialogue.kv +++ b/tagit/dialogues/dialogue.kv @@ -6,68 +6,28 @@ ok_on_enter: True <DialogueContentBase>: - orientation: 'vertical' padding: '12dp' size_hint: 0.66, None height: self.minimum_height - canvas: - # mask main window - Color: - rgba: 0,0,0, 0.7 * self.parent._anim_alpha - Rectangle: - size: self.parent._window.size if self.parent._window else (0, 0) - - # solid background color - Color: - rgb: 1, 1, 1 - BorderImage: - source: self.parent.background - border: self.parent.border - pos: self.pos - size: self.size - <DialogueContentNoTitle@DialogueContentBase>: # nothing to do + +<DialogueTitle@Label>: + <DialogueContentTitle@DialogueContentBase>: title: '' title_color: 1,1,1,1 - Label: + DialogueTitle: text: root.title size_hint_y: None height: self.texture_size[1] + dp(16) text_size: self.width - dp(16), None - font_size: '16sp' color: root.title_color - bold: True - halign: 'center' - valing: 'middle' - - canvas.before: - # Background - Color: - rgb: 0.2, 0.2, 0.2 - Rectangle: - size: self.size - pos: self.pos - - # top border - #Color: - # rgb: 0.5, 0.5, 0.5 - #Line: - # points: self.x, self.y + self.height, self.x + self.width, self.y + self.height - # width: 2 - - # bottom border - #Color: - # rgb: 0.5, 0.5, 0.5 - #Line: - # points: self.x, self.y, self.x + self.width, self.y - # width: 2 # small space Label: @@ -75,7 +35,10 @@ height: 12 -<DialogueButtons>: + +<DialogueButton@Button>: + +<DialogueButtonRow>: orientation: 'vertical' size_hint_y: None height: dp(48+8) @@ -88,25 +51,25 @@ # here come the buttons -<DialogueButtons_One@DialogueButtons>: +<DialogueButtons_One@DialogueButtonRow>: ok_text: 'OK' - Button: + DialogueButton: text: root.ok_text on_press: get_root(self).ok() -<DialogueButtons_Two@DialogueButtons>: +<DialogueButtons_Two@DialogueButtonRow>: cancel_text: 'Cancel' ok_text: 'OK' ok_enabled: True BoxLayout: orientation: 'horizontal' - Button: + DialogueButton: text: root.cancel_text on_press: get_root(self).cancel() - Button: + DialogueButton: text: root.ok_text on_press: get_root(self).ok() disabled: not root.ok_enabled diff --git a/tagit/dialogues/dialogue.py b/tagit/dialogues/dialogue.py index 1aa0e9a..bf72a28 100644 --- a/tagit/dialogues/dialogue.py +++ b/tagit/dialogues/dialogue.py @@ -101,8 +101,8 @@ class DialogueContentTitle(DialogueContentBase): pass class DialogueContentNoTitle(DialogueContentBase): pass # buttons -class DialogueButtons(BoxLayout): pass -class DialogueButtons_One(DialogueButtons): pass -class DialogueButtons_Two(DialogueButtons): pass +class DialogueButtonRow(BoxLayout): pass +class DialogueButtons_One(DialogueButtonRow): pass +class DialogueButtons_Two(DialogueButtonRow): pass ## EOF ## diff --git a/tagit/external/setproperty/README.md b/tagit/external/setproperty/README.md index e579132..4849b6c 100644 --- a/tagit/external/setproperty/README.md +++ b/tagit/external/setproperty/README.md @@ -1,5 +1,27 @@ -build with +# SetProperty + +Analigous to kivy.properties.ListProperty, SetProperty provides a wrapper +that extends python's set() with [Kivy](https://kivy.org) events. + +## Installation + +Note that you'll need Cython to run the following commands. +Install Cython via: + +$ pip install Cython + +or via your system's package manager (e.g., ``sudo apt install cython``). + +Then, cythonize the SetProperty with: + +$ python preprocess.py + +build the shared library from cythonized with: + +$ python build.py build_ext --inplace + +or perform both steps with one command: $ python setup.py build_ext --inplace diff --git a/tagit/external/setproperty/build.py b/tagit/external/setproperty/build.py new file mode 100644 index 0000000..9b3d6ab --- /dev/null +++ b/tagit/external/setproperty/build.py @@ -0,0 +1,5 @@ + +from distutils.core import Extension, setup +ext = Extension(name="setproperty", sources=["setproperty.c"]) +setup(ext_modules=[ext]) + diff --git a/tagit/external/setproperty/preprocess.py b/tagit/external/setproperty/preprocess.py new file mode 100644 index 0000000..7c85b86 --- /dev/null +++ b/tagit/external/setproperty/preprocess.py @@ -0,0 +1,8 @@ + +from distutils.core import Extension +from Cython.Build import cythonize + +ext = Extension(name="setproperty", sources=["setproperty.pyx"]) +cythonize(ext) + + diff --git a/tagit/external/setproperty/setup.py b/tagit/external/setproperty/setup.py index 8500340..bd95d70 100644 --- a/tagit/external/setproperty/setup.py +++ b/tagit/external/setproperty/setup.py @@ -4,3 +4,4 @@ from Cython.Build import cythonize # define an extension that will be cythonized and compiled ext = Extension(name="setproperty", sources=["setproperty.pyx"]) setup(ext_modules=cythonize(ext)) + diff --git a/tagit/parsing/filter/from_string.py b/tagit/parsing/filter/from_string.py index ed24f63..60e5c47 100644 --- a/tagit/parsing/filter/from_string.py +++ b/tagit/parsing/filter/from_string.py @@ -69,7 +69,7 @@ class FromString(): > Require: searchable as specified in backend AND user-searchable as specified in frontend """ # all relevant predicates - predicates = {pred for pred in self.schema.predicates() if pred.domain <= self.schema.node(ns.bsfs.Entity)} + predicates = {pred for pred in self.schema.predicates() if pred.domain <= self.schema.node(ns.bsn.Entity)} # filter through accept/reject lists ... # FIXME # shortcuts @@ -78,9 +78,9 @@ class FromString(): # all predicates _PREDICATES = {self._uri2abb[pred.uri] for pred in predicates} | {'id', 'group'} # FIXME: properly document additions # numeric predicates - _PREDICATES_NUMERIC = {self._uri2abb[pred.uri] for pred in predicates if pred.range <= self.schema.literal(ns.bsfs.Number)} + _PREDICATES_NUMERIC = {self._uri2abb[pred.uri] for pred in predicates if pred.range <= self.schema.literal(ns.bsl.Number)} # datetime predicates - self._DATETIME_PREDICATES = {pred.uri for pred in predicates if pred.range <= self.schema.literal(ns.bsfs.Time)} + self._DATETIME_PREDICATES = {pred.uri for pred in predicates if pred.range <= self.schema.literal(ns.bsl.Time)} _PREDICATES_DATETIME = {self._uri2abb[pred] for pred in self._DATETIME_PREDICATES} diff --git a/tagit/parsing/filter/to_string.py b/tagit/parsing/filter/to_string.py index 0b1a3e1..6a1b035 100644 --- a/tagit/parsing/filter/to_string.py +++ b/tagit/parsing/filter/to_string.py @@ -20,7 +20,7 @@ class ToString(): self.matches = matcher.Filter() self.schema = schema - predicates = {pred for pred in self.schema.predicates() if pred.domain <= self.schema.node(ns.bsfs.Entity)} + predicates = {pred for pred in self.schema.predicates() if pred.domain <= self.schema.node(ns.bsn.Entity)} # shortcuts self._abb2uri = {pred.uri.fragment: pred.uri for pred in predicates} # FIXME: tie-breaking for duplicates self._uri2abb = {uri: fragment for fragment, uri in self._abb2uri.items()} @@ -206,10 +206,10 @@ class ToString(): guids = {guid for sub in query for guid in get_guids(sub.value) } elif self.matches(query, ast.filter.Not(matcher.Partial(ast.filter.Is))): negated = True - guids = get_guids(query.value) + guids = get_guids(query.expr.value) elif self.matches(query, ast.filter.Not(ast.filter.Or(matcher.Rest(matcher.Partial(ast.filter.Is))))): negated = True - guids = {guid for sub in query for guid in get_guids(sub.value) } + guids = {guid for sub in query.expr for guid in get_guids(sub.value) } if len(guids) == 0: # no matches diff --git a/tagit/tiles/decoration.kv b/tagit/tiles/decoration.kv index a53d013..ae7e49d 100644 --- a/tagit/tiles/decoration.kv +++ b/tagit/tiles/decoration.kv @@ -55,4 +55,33 @@ size: self.size +<TileDecorationRoundedBorder>: + cbox: cbox + + Label: + text: root.client.title + size: root.width, 20 + size_hint: None, None + pos: 0, root.height - self.height - 3 + + RelativeLayout: + id: cbox + pos: 5, 3 + size: root.width-10, root.height-30 + size_hint: None, None + + canvas.before: + Color: + rgb: 0xc5/256, 0xc9/256, 0xc7/256 + RoundedRectangle: + pos: self.pos + #radius: [10, 10, 10, 10] + size: self.size + Color: + rgb: 0x1c/256, 0x1b/256, 0x22/256 # FIXME: re-use from MainWindow? + RoundedRectangle: + pos: self.pos[0] + 2, self.pos[1] + 2 + radius: [5, 5, 5, 5] + size: self.size[0] - 4, self.size[1] - 4 + ## EOF ## diff --git a/tagit/tiles/decoration.py b/tagit/tiles/decoration.py index 471058d..c772f64 100644 --- a/tagit/tiles/decoration.py +++ b/tagit/tiles/decoration.py @@ -16,6 +16,7 @@ import kivy.properties as kp # exports __all__: typing.Sequence[str] = ( 'TileDecorationBorder', + 'TileDecorationRoundedBorder', 'TileDecorationFilledRectangle', 'TileDecorationVanilla', ) @@ -65,4 +66,12 @@ class TileDecorationBorder(TileDecoration): height = None if height is None else height + 30 return width, height +class TileDecorationRoundedBorder(TileDecoration): + @property + def default_size(self): + width, height = self.client.default_size + width = None if width is None else width + 30 + height = None if height is None else height + 30 + return width, height + ## EOF ## diff --git a/tagit/tiles/info.py b/tagit/tiles/info.py index 725e098..9555b35 100644 --- a/tagit/tiles/info.py +++ b/tagit/tiles/info.py @@ -11,8 +11,9 @@ from collections import OrderedDict from kivy.lang import Builder # tagit imports -from tagit.utils import ttime, ns +from tagit.utils import ttime, ns, magnitude_fmt from tagit.widgets.browser import BrowserAwareMixin +from tagit.widgets.session import StorageAwareMixin # inner-module imports from .tile import TileTabular @@ -29,15 +30,19 @@ Builder.load_string(''' title: 'Item info' tooltip: 'Key properties of the cursor item' # assuming 7 info items - default_size: None, 7*self.font_size + 6*5 - keywidth: min(65, self.width * 0.4) + #default_size: None, 7*self.font_size + 6*5 + keywidth: min(75, self.width * 0.4) ''') # classes -class Info(TileTabular, BrowserAwareMixin): +class Info(TileTabular, BrowserAwareMixin, StorageAwareMixin): """Show essential attributes about the cursor.""" + def on_root(self, wx, root): + BrowserAwareMixin.on_root(self, wx, root) + StorageAwareMixin.on_root(self, wx, root) + def on_browser(self, sender, browser): # remove old binding if self.browser is not None: @@ -53,6 +58,9 @@ class Info(TileTabular, BrowserAwareMixin): self.browser.unbind(cursor=self.update) self.browser = None + def on_predicate_modified(self, *args): + self.update() + def update(self, *args): cursor = self.root.browser.cursor if not self.visible or cursor is None: @@ -61,23 +69,17 @@ class Info(TileTabular, BrowserAwareMixin): else: preds = cursor.get( - ns.bsm.t_created, + ns.bsfs.Node().t_created, ns.bse.filesize, ns.bse.filename, - ns.bse.comment, + (ns.bse.tag, ns.bst.label), ) self.tabledata = OrderedDict({ 'Date' : ttime.from_timestamp_utc( - preds.get(ns.bsm.t_created, ttime.timestamp_min)).strftime('%d.%m.%y %H:%M'), - #'Size' : f'{preds.get(ns.bse.width, "?")} x {preds.get(ns.bse.height, "?")}', - #'ISO' : preds.get(ns.bse.iso, '?'), - 'Filesize' : preds.get(ns.bse.filesize, 'n/a'), + preds.get(ns.bsfs.Node().t_created, ttime.timestamp_min)).strftime('%d.%m.%y %H:%M'), + 'Filesize' : magnitude_fmt(preds.get(ns.bse.filesize, 0)), 'Filename' : preds.get(ns.bse.filename, 'n/a'), - 'Comment' : '; '.join(preds.get(ns.bse.comment, [])), - #'Exposure' : f'1 / {preds.get(ns.bse.exposure, 0):0.2f}', - #'Aperture' : preds.get(ns.bse.aperture, '?'), - #'Fx' : preds.get(ns.bse.focal_length_35, '?'), - #'Flash' : 'Yes' if preds.get(ns.bse.flash, False) else 'No', + 'Tags' : ', '.join(sorted(preds.get((ns.bse.tag, ns.bst.label), [' ']))), }) ## EOF ## diff --git a/tagit/tiles/tile.kv b/tagit/tiles/tile.kv index 277b23d..fcd9821 100644 --- a/tagit/tiles/tile.kv +++ b/tagit/tiles/tile.kv @@ -7,16 +7,28 @@ <TileTabular>: # config font_size: sp(15) - spacer: 10 keywidth: 0.5 # table - grid: grid - GridLayout: - id: grid - cols: 2 - size: root.size - size_hint: None, None + rows: rows + TileTabularLine: + id: rows + orientation: 'tb-lr' + size_hint: 1, 1 + +<TileTabularLine@StackLayout>: + spacing: 10 + +<TileTabularRow>: + orientation: 'horizontal' + size_hint: 1, None + height: self.minimum_height + spacing: 10 + +<TileTabularCell>: + valign: 'top' + height: self.texture_size[1] + text_size: self.width, None <TileWithLabel>: text: '' diff --git a/tagit/tiles/tile.py b/tagit/tiles/tile.py index 7ea690a..981d45b 100644 --- a/tagit/tiles/tile.py +++ b/tagit/tiles/tile.py @@ -9,6 +9,7 @@ import os # kivy imports from kivy.lang import Builder +from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label from kivy.uix.relativelayout import RelativeLayout import kivy.properties as kp @@ -38,51 +39,47 @@ class Tile(RelativeLayout): class TileWithLabel(Tile): pass +class TileTabularRow(BoxLayout): + pass + +class TileTabularCell(Label): + pass class TileTabular(Tile): tabledata = kp.ObjectProperty() - spacer = kp.NumericProperty(10) keywidth = kp.NumericProperty(0.5) - def on_size(self, wx, size): - if not self.visible or len(self.grid.children) == 0: - return - - height = self.height / (len(self.grid.children) / 2) - kwidth = self.width * self.keywidth if self.keywidth < 1 else self.keywidth - vwidth = self.width - kwidth - self.spacer - - # adjust size - for idx, child in enumerate(self.grid.children): - if idx % 2 == 1: # key - child.text_size = kwidth, height - child.width = kwidth - else: # label - child.text_size = vwidth, height - def on_tabledata(self, wx, data): # set items - self.grid.clear_widgets() - for key, value in data.items(): + self.rows.clear_widgets() + for t_key, t_value in data.items(): + # row + row = TileTabularRow() # left column (keys) - self.grid.add_widget(Label( - text=key, + key = TileTabularCell( + text=t_key, halign='right', - valign='top', font_size = self.font_size, - size_hint = (None, 1), - )) - + size_hint=(None, 1), + width=self.width * self.keywidth if self.keywidth < 1 else self.keywidth, + ) # right column (values) - self.grid.add_widget(Label( - text=str(value), + value = TileTabularCell( + text=str(t_value), halign='left', - valign='top', font_size = self.font_size, - )) - - # set sizes - self.on_size(self, self.size) + size_hint=(1, None), + ) + # adjust key's width and height dynamically. + # value's width and height are adjusted automatically + self.bind(width=lambda wx, width, key=key: setattr(key, 'width', + width * self.keywidth if self.keywidth < 1 else self.keywidth)) + key.bind(height=lambda wx, height, key=key: setattr(key, 'text_size', + (key.width, height))) + # add widgets + row.add_widget(key) + row.add_widget(value) + self.rows.add_widget(row) ## EOF ## diff --git a/tagit/utils/namespaces.py b/tagit/utils/namespaces.py index dbdb853..a17a927 100644 --- a/tagit/utils/namespaces.py +++ b/tagit/utils/namespaces.py @@ -11,29 +11,31 @@ import typing from . import bsfs as _bsfs # generic namespaces -xsd = _bsfs.Namespace('http://www.w3.org/2001/XMLSchema') +xsd = _bsfs.Namespace('http://www.w3.org/2001/XMLSchema')() # core bsfs namespaces -bsfs = _bsfs.Namespace('http://bsfs.ai/schema', fsep='/') -bsm = _bsfs.Namespace('http://bsfs.ai/schema/Meta') +bsfs = _bsfs.Namespace('https://schema.bsfs.io/core') +bsie = _bsfs.Namespace('https://schema.bsfs.io/ie') # auxiliary bsfs namespaces -bse = _bsfs.Namespace('http://bsfs.ai/schema/Entity') -bsg = _bsfs.Namespace('http://bsfs.ai/schema/Group') -bsp = _bsfs.Namespace('http://bsfs.ai/schema/Preview') -bst = _bsfs.Namespace('http://bsfs.ai/schema/Tag') +bsn = bsie.Node +bse = bsn.Entity() +bsg = bsn.Group() +bsl = bsfs.Literal +bsp = bsn.Preview() +bst = bsn.Tag() # export __all__: typing.Sequence[str] = ( 'bse', 'bsfs', 'bsg', - 'bsm', + 'bsie', + 'bsl', + 'bsn', 'bsp', 'bst', 'xsd', ) ## EOF ## - - diff --git a/tagit/widgets/browser.kv b/tagit/widgets/browser.kv index 8b4c8c3..63495be 100644 --- a/tagit/widgets/browser.kv +++ b/tagit/widgets/browser.kv @@ -11,19 +11,6 @@ is_cursor: False is_selected: False - canvas.after: - Color: - rgba: 1,1,1, 1 if self.is_cursor else 0 - Line: - width: 2 - rectangle: self.x, self.y, self.width, self.height - - Color: - rgba: self.scolor + [0.5 if self.is_selected else 0] - Rectangle: - pos: self.x, self.center_y - int(self.height) / 2 - size: self.width, self.height - <BrowserImage>: # This be an image preview: image @@ -58,6 +45,11 @@ # opacity: root.is_group and 1.0 or 0.0 # show: 'image', +<BrowserDescriptionLabel@Label>: + halign: 'left' + valign: 'center' + text_size: self.size + <BrowserDescription>: # This be a list item spacer: 20 preview: image @@ -68,12 +60,9 @@ # actual size is set in code pos: 0, 0 - Label: + BrowserDescriptionLabel: text: root.text markup: True - halign: 'left' - valign: 'center' - text_size: self.size size_hint: None, 1 width: root.width - image.width - root.spacer - 35 pos: root.height + root.spacer, 0 diff --git a/tagit/widgets/browser.py b/tagit/widgets/browser.py index 1e42c9c..17d99ed 100644 --- a/tagit/widgets/browser.py +++ b/tagit/widgets/browser.py @@ -171,7 +171,8 @@ class Browser(GridLayout, StorageAwareMixin, ConfigAwareMixin): """ # get groups and their shadow (group's members in items) groups = defaultdict(set) - for obj, grp in reduce(operator.add, items).group(node=True, view=list): + all_items = reduce(operator.add, items, self.root.session.storage.empty(ns.bsn.Entity)) + for obj, grp in all_items.group(node=True, view=list): groups[grp].add(obj) # don't fold groups if few members @@ -218,7 +219,7 @@ class Browser(GridLayout, StorageAwareMixin, ConfigAwareMixin): unfolded |= self.folds[itm].shadow else: unfolded |= {itm} - return reduce(operator.add, unfolded) # FIXME: What if items is empty? + return reduce(operator.add, unfolded, self.root.session.storage.empty(ns.bsn.Entity)) def neighboring_unselected(self): """Return the item closest to the cursor and not being selected. May return None.""" @@ -281,7 +282,6 @@ class Browser(GridLayout, StorageAwareMixin, ConfigAwareMixin): def on_cursor(self, sender, cursor): if cursor is not None: - #self.root.status.dispatch('on_status', os.path.basename(next(iter(cursor.guids)))) self.root.status.dispatch('on_status', cursor.filename(default='')) def on_items(self, sender, items): @@ -347,10 +347,7 @@ class Browser(GridLayout, StorageAwareMixin, ConfigAwareMixin): self.clear_widgets() for itm in range(self.page_size): - wx = factory( - browser=self, - scolor=self.root.session.cfg('ui', 'standalone', 'browser', 'select_color'), - ) + wx = factory(browser=self) self.bind(selection=wx.on_selection) self.bind(cursor=wx.on_cursor) self.add_widget(wx) @@ -398,18 +395,30 @@ class Browser(GridLayout, StorageAwareMixin, ConfigAwareMixin): if len(items) == 0: # FIXME: mb/port return + resolution = self._cell_resolution() + previews = self._fetch_previews(items, resolution) + default = resource_find('no_preview.png') + for ent, child in zip(reversed(items), childs): + if ent in previews: + buf = previews[ent] + else: + buf = open(default, 'rb') + child.update(ent, buf, f'{ent}x{resolution}') + + def _fetch_previews(self, items, resolution): + """Fetch previews matching *resolution* for *items*. + Return a dict with items as key and a BytesIO as value. + Items without valid asset are omitted from the dict. + """ # fetch previews node_preview = reduce(operator.add, items).get(ns.bse.preview, node=True) previews = {p for previews in node_preview.values() for p in previews} - previews = reduce(operator.add, previews) + previews = reduce(operator.add, previews) # FIXME: empty previews # fetch preview resolutions res_preview = previews.get(ns.bsp.width, ns.bsp.height, node=True) - # get target resolution - resolution = self._cell_resolution() - # get default preview - default = resource_find('no_preview.png') # select a preview for each item - for ent, child in zip(reversed(items), childs): + chosen = {} + for ent in items: try: # get previews and their resolution for this ent options = [] @@ -420,19 +429,21 @@ class Browser(GridLayout, StorageAwareMixin, ConfigAwareMixin): height = res.get(ns.bsp.height, 0) options.append((preview, Resolution(width, height))) # select the best fitting preview - chosen = rmatcher.by_area_min(resolution, options) - # open the preview file, default if no asset is available - thumb_data = chosen.asset(default=None) # FIXME: get all assets in one call - if thumb_data is None: - raise KeyError() - thumb = io.BytesIO(thumb_data) - + chosen[ent] = rmatcher.by_area_min(resolution, options) except (KeyError, IndexError): - # KeyError: - # no viable resolution found - thumb = open(default, 'rb') - # update the image in the child widget - child.update(ent, thumb, f'{ent}x{resolution}') + # skip objects w/o preview (KeyError in node_preview) + # skip objects w/o valid preview (IndexError in rmatcher) + pass + + # fetch assets + assets = reduce(operator.add, chosen.values()).asset(node=True) # FIXME: empty chosen + # build ent -> asset mapping and convert raw data to io buffer + return { + ent: io.BytesIO(assets[thumb]) + for ent, thumb + in chosen.items() + if thumb in assets + } #def _preload_all(self): # # prefer loading from start to end @@ -440,26 +451,24 @@ class Browser(GridLayout, StorageAwareMixin, ConfigAwareMixin): def _preload_items(self, items, resolution=None): """Load an item into the kivy *Cache* without displaying the image anywhere.""" - resolution = resolution if resolution is not None else self._cell_resolution() - def _buf_loader(buffer, fname): # helper method to load the image from a raw buffer with buffer as buf: return ImageLoaderTagit(filename=fname, inline=True, rawdata=buf) - for obj in items: - try: - buffer = obj.get('preview').best_match(resolution) - source = f'{obj.guid}x{resolution}' - - Loader.image(source, - nocache=False, mipmap=False, - anim_delay=0, - load_callback=partial(_buf_loader, buffer) # mb: pass load_callback - ) - - except PredicateNotSet: - pass + resolution = resolution if resolution is not None else self._cell_resolution() + try: + foo = self._fetch_previews(items, resolution) # FIXME: _fetch_previews fails on empty previews/chosen + except TypeError: + return + for obj, buffer in foo.items(): + guid = ','.join(obj.guids) + source = f'{guid}x{resolution}' + Loader.image(source, + nocache=False, mipmap=False, + anim_delay=0, + load_callback=partial(_buf_loader, buffer) # mb: pass load_callback + ) class BrowserAwareMixin(object): @@ -484,7 +493,6 @@ class BrowserItem(RelativeLayout): is_cursor = kp.BooleanProperty(False) is_selected = kp.BooleanProperty(False) is_group = kp.BooleanProperty(False) - scolor = kp.ListProperty([1, 0, 0]) # FIXME: set from config def update(self, obj): self.obj = obj @@ -572,7 +580,7 @@ class BrowserDescription(BrowserItem): grp = self.browser.folds[self.obj].group # FIXME: Here we could actually use a predicate reversal for Nodes.get # members = grp.get(ast.fetch.Node(ast.fetch.Predicate(ns.bse.group, reverse=True))) - members = self.browser.root.session.storage.get(ns.bsfs.File, + members = self.browser.root.session.storage.get(ns.bsn.Entity, ast.filter.Any(ns.bse.group, ast.filter.Is(grp))) # get group member's tags member_tags = members.tag.label(node=True) diff --git a/tagit/widgets/filter.kv b/tagit/widgets/filter.kv index b638570..5407610 100644 --- a/tagit/widgets/filter.kv +++ b/tagit/widgets/filter.kv @@ -1,4 +1,5 @@ #:import SearchmodeSwitch tagit.actions.filter +#:import AddToken tagit.actions.filter #-- #:import SortKey tagit.actions.search <Filter>: @@ -7,20 +8,36 @@ spacing: 5 tokens: tokens - BoxLayout: - orientation: 'horizontal' - spacing: 10 - id: tokens - - # Tokens will be inserted here - - SearchmodeSwitch: - show: 'image', + Widget: + size_hint_x: None + width: 5 + + ScrollView: + do_scroll_x: True + do_scroll_y: False + size_hint: 1, 1 + + BoxLayout: + orientation: 'horizontal' + spacing: 10 + id: tokens + size_hint: None, None + height: 35 + width: self.minimum_width + # Tokens will be inserted here + + AddToken: + show: 'image', root: root.root - SortKey: - show: 'image', - root: root.root + # FIXME: Temporarily disabled + #SearchmodeSwitch: + # show: 'image', + # root: root.root + + #SortKey: + # show: 'image', + # root: root.root SortOrder: show: 'image', @@ -30,55 +47,43 @@ root: root.root name: 'filter' orientation: 'lr-tb' - # space for 2 buttons - width: 3*30 + 2*5 - size_hint: None, 1.0 + # space for two buttons + width: 2*30 + 5 spacing: 5 + size_hint: None, None + height: 35 button_height: 30 button_show: 'image', +<Avatar@Label>: + active: False + +<ShingleText@Label>: + active: False + <Shingle>: orientation: 'horizontal' label: tlabel - - canvas.before: - Color: - rgba: 0,0,1, 0.25 if root.active else 0 - Rectangle: - pos: root.pos - size: root.size - - canvas.after: - Color: - rgba: 1,1,1,1 - Line: - rectangle: self.x+1, self.y+1, self.width-1, self.height-1 - - Label: + size_hint: None, None + width: self.minimum_width + height: 30 + + Avatar: + id: avatar + size_hint: None, None + text: root.avatar + width: self.parent.height + height: self.parent.height + active: root.active + + ShingleText: id: tlabel text: root.text - - canvas.after: - Color: - rgba: 0,0,0,0.5 if not root.active else 0 - Rectangle: - pos: self.pos - size: self.size - - - Button: - text: 'x' - bold: True - opacity: 0.5 - width: 20 - size_hint: None, 1.0 - background_color: [0,0,0,0] - background_normal: '' - on_press: root.remove() + active: root.active + width: (self.texture_size[0] + dp(20)) if self.text != '' else 0 + size_hint_x: None <Addressbar>: multiline: False - background_color: (0.2,0.2,0.2,1) if self.focus else (0.15,0.15,0.15,1) - foreground_color: (1,1,1,1) ## EOF ## diff --git a/tagit/widgets/filter.py b/tagit/widgets/filter.py index 15aefd6..1382c43 100644 --- a/tagit/widgets/filter.py +++ b/tagit/widgets/filter.py @@ -120,26 +120,46 @@ class Filter(BoxLayout, ConfigAwareMixin): return query, sort def abbreviate(self, token): + # FIXME: Return image matches = matcher.Filter() if matches(token, ast.filter.Any(ns.bse.tag, ast.filter.Any(ns.bst.label, matcher.Any()))): # tag token - return self.root.session.filter_to_string(token) + return 'T' if matches(token, matcher.Partial(ast.filter.Is)) or \ matches(token, ast.filter.Or(matcher.Rest(matcher.Partial(ast.filter.Is)))): # exclusive token - return 'E' + return '=' if matches(token, ast.filter.Not(matcher.Partial(ast.filter.Is))) or \ matches(token, ast.filter.Not(ast.filter.Or(matcher.Rest(matcher.Partial(ast.filter.Is))))): # reduce token - return 'R' + return '—' if matches(token, ast.filter.Any(ns.bse.group, matcher.Any())): # group token return 'G' if matches(token, ast.filter.Any(matcher.Partial(ast.filter.Predicate), matcher.Any())): # generic token - return token.predicate.predicate.get('fragment', '?').title() + #return token.predicate.predicate.get('fragment', '?').title()[0] + return 'P' return '?' + def tok_label(self, token): + matches = matcher.Filter() + if matches(token, ast.filter.Any(ns.bse.tag, ast.filter.Any(ns.bst.label, matcher.Any()))): + # tag token + return self.root.session.filter_to_string(token) + if matches(token, matcher.Partial(ast.filter.Is)) or \ + matches(token, ast.filter.Not(matcher.Partial(ast.filter.Is))): + return '1' + if matches(token, ast.filter.Or(matcher.Rest(matcher.Partial(ast.filter.Is)))): + return str(len(token)) + if matches(token, ast.filter.Not(ast.filter.Or(matcher.Rest(matcher.Partial(ast.filter.Is))))): + return str(len(token.expr)) + if matches(token, ast.filter.Any(matcher.Partial(ast.filter.Predicate), matcher.Any())): + # generic token + #return self.root.session.filter_to_string(token) + return token.predicate.predicate.get('fragment', '') + return '' + def show_address_once(self): """Single-shot address mode without changing the search mode.""" self.tokens.clear_widgets() @@ -189,7 +209,8 @@ class Filter(BoxLayout, ConfigAwareMixin): Shingle( tok, active=(tok in self.t_head), - text=self.abbreviate(tok), + avatar=self.abbreviate(tok), + text=self.tok_label(tok), root=self.root )) @@ -235,6 +256,7 @@ class Shingle(BoxLayout): # content active = kp.BooleanProperty(False) text = kp.StringProperty('') + avatar = kp.StringProperty('') # touch behaviour _single_tap_action = None @@ -249,7 +271,7 @@ class Shingle(BoxLayout): def on_touch_down(self, touch): """Edit shingle when touched.""" - if self.label.collide_point(*touch.pos): + if self.collide_point(*touch.pos): if touch.is_double_tap: # edit filter # ignore touch, such that the dialogue # doesn't loose the focus immediately after open diff --git a/tagit/widgets/session.py b/tagit/widgets/session.py index c233a15..30dfe51 100644 --- a/tagit/widgets/session.py +++ b/tagit/widgets/session.py @@ -68,13 +68,13 @@ class Session(Widget): # open BSFS storage store = bsfs.Open(cfg('session', 'bsfs')) # check storage schema - # FIXME: how to properly load the required schema? - with open(os.path.join(os.path.dirname(__file__), '..', 'apps', 'port-schema.nt'), 'rt') as ifile: + with open(resource_find('required_schema.nt'), 'rt') as ifile: required_schema = bsfs.schema.from_string(ifile.read()) - # FIXME: Since the store isn't persistent, we migrate to the required one here. - #if not required_schema <= store.schema: - # raise Exception('') - store.migrate(required_schema) + if not required_schema.consistent_with(store.schema): + raise Exception("The storage's schema is incompatible with tagit's requirements") + if not required_schema <= store.schema: + store.migrate(required_schema | store.schema) + # replace current with new storage self.storage = store diff --git a/tagit/widgets/status.kv b/tagit/widgets/status.kv index 2d49b15..0a680ab 100644 --- a/tagit/widgets/status.kv +++ b/tagit/widgets/status.kv @@ -1,5 +1,13 @@ #-- #:import ButtonDock tagit.widgets.dock.ButtonDock # FIXME: mb/port +<NavigationLabel@Label>: + markup: True + +<StatusLabel@Label>: + markup: True + valign: 'middle' + halign: 'center' + <Status>: orientation: 'horizontal' status: '' @@ -18,11 +26,10 @@ button_height: 30 button_show: 'image', - Label: + NavigationLabel: id: navigation_label size_hint: None, 1 width: 180 - markup: True text: root.navigation ButtonDock: @@ -36,13 +43,10 @@ button_height: 30 button_show: 'image', - Label: + StatusLabel: # gets remaining size id: status_label text_size: self.size - markup: True - valign: 'middle' - halign: 'left' text: root.status ButtonDock: diff --git a/tagit/windows/desktop.kv b/tagit/windows/desktop.kv index cbc5c48..d2ca0e7 100644 --- a/tagit/windows/desktop.kv +++ b/tagit/windows/desktop.kv @@ -1,13 +1,9 @@ +#:import TileDecorationRoundedBorder tagit.tiles.decoration.TileDecorationRoundedBorder #:import TileDecorationBorder tagit.tiles.decoration.TileDecorationBorder #:import TileDecorationFilledRectangle tagit.tiles.decoration.TileDecorationFilledRectangle -# DEBUG: Draw borders around all widgets -#<Widget>: -# canvas.after: -# Line: -# rectangle: self.x+1,self.y+1,self.width-1,self.height-1 -# dash_offset: 5 -# dash_length: 3 +<HGuide@Widget>: + <MainWindow>: # main content @@ -15,110 +11,85 @@ browser: browser filter: filter status: status - # required by actions.planes - planes: planes # required by Menu context: context - Carousel: - id: planes - loop: True - scroll_timeout: 0 # disables switching by touch event - # plane references - dashboard: dashboard - browsing: browsing - codash: codash + BoxLayout: + orientation: 'vertical' - # planes + Widget: + height: 5 + size_hint: 1, None - TileDock: # static dashboard plane - id: dashboard + Filter: + id: filter root: root - # plane config - size_hint: 1, 1 - visible: planes.current_slide == self - # dock config - name: 'dashboard' - decoration: TileDecorationBorder - cols: 3 - rows: 2 - # self.size won't be updated correctly - tile_width: self.width / self.cols - tile_height: self.height / self.rows - - BoxLayout: # browsing plane - id: browsing + size_hint: 1, None + height: 40 + + HGuide: + height: 20 + size_hint: 1, None + + Widget: # spacer + height: 20 + size_hint: 1, None + + BoxLayout: orientation: 'horizontal' - visible: planes.current_slide == self ButtonDock: # one column of buttons on the left root: root - orientation: 'tb-lr' + orientation: 'lr-tb' # one column of buttons - width: 30 + 2*10 + width: 1*30 + 2*10 name: 'sidebar_left' spacing: 10 padding: 10 - size_hint: None, 1 + size_hint: None, None button_height: 30 button_show: 'image', + # adjust height automatically to content + height: self.minimum_height + pos_hint: {'center_y': 0.5} - BoxLayout: # main content - orientation: 'vertical' + Widget: # spacer + width: 20 # ButtonDock already has a space of 10px + size_hint: None, 1 + + Browser: # browsing space + id: browser + root: root size_hint: 1, 1 - BoxLayout: - orientation: 'horizontal' - size_hint: 1, 1 - current: 0 - - BoxLayout: - orientation: 'vertical' - size_hint: 1, 1 - - Filter: - id: filter - root: root - size_hint: 1, None - height: 30 - - Browser: - id: browser - root: root - size_hint: 1, 0.96 - - Status: - id: status - root: root - size_hint: 1, None - height: 30 - + Widget: # spacer + width: 30 + size_hint: None, 1 + TileDock: # context info to the right root: root - visible: planes.current_slide == self.parent name: 'sidebar_right' - decoration: TileDecorationFilledRectangle + decoration: TileDecorationRoundedBorder + visible: True cols: 1 - rows: 3 - # self.height won't be updated correctly - #tile_height: self.size[1] / 4 - width: 180 - size_hint: None, 1 - - TileDock: # contextual dashboard - id: codash + rows: 1 + width: 220 + size_hint: None, 0.5 + pos_hint: {'center_y': 0.5} + + Widget: # spacer + height: 20 + size_hint: 1, None + + HGuide: + height: 20 + size_hint: 1, None + + Status: + id: status root: root - # plane config - size_hint: 1, 1 - visible: planes.current_slide == self - # dock config - name: 'codash' - decoration: TileDecorationBorder - cols: 4 - rows: 2 - # self.size won't be update correctly - tile_width: self.width / 4 - tile_height: self.height / 2 + size_hint: 1, None + height: 30 Context: # context menu id: context diff --git a/tagit/windows/desktop.py b/tagit/windows/desktop.py index 42b279e..2c087b2 100644 --- a/tagit/windows/desktop.py +++ b/tagit/windows/desktop.py @@ -17,8 +17,9 @@ from kivy.uix.floatlayout import FloatLayout import kivy.properties as kp # import Image and Loader to overwrite their caches later on -from kivy.loader import Loader from kivy.cache import Cache +from kivy.loader import Loader +from kivy.resources import resource_find # tagit imports from tagit import actions, config, dialogues @@ -44,6 +45,8 @@ logger = logging.getLogger(__name__) # load kv Builder.load_file(os.path.join(os.path.dirname(__file__), 'desktop.kv')) +# load styles +Builder.load_file(resource_find('default/style.kv')) # classes class MainWindow(FloatLayout): @@ -80,6 +83,9 @@ class MainWindow(FloatLayout): # bind pre-close checks from kivy.core.window import Window Window.bind(on_request_close=self.on_request_close) + Window.size = tuple(cfg('ui', 'standalone', 'window_size')) + if cfg('ui', 'standalone', 'maximize'): + Window.maximize() ## properties @@ -96,14 +102,6 @@ class MainWindow(FloatLayout): ## startup and shutdown def on_startup(self): - # switch to starting plane - if it's the dashboard no action is needed - if self.session.cfg('ui', 'standalone', 'plane') == 'browsing': - self.trigger('ShowBrowsing') - - # show welcome message - if self.session.cfg('session', 'first_start'): # FIXME: mb/port: move to starting app - self.display_welcome() - # run script for args in self.session.cfg('session', 'script'): if isinstance(args, str): @@ -123,35 +121,26 @@ class MainWindow(FloatLayout): #App.get_running_app().stop() def on_request_close(self, *args): - with open('.action_history', 'a') as ofile: - for itm in self.action_log: - ofile.write(f'{itm}\n') + #with open('.action_history', 'a') as ofile: + # for itm in self.action_log: + # ofile.write(f'{itm}\n') #App.get_running_app().stop() # FIXME: mb/port: from CloseSessionAndExit return False - def display_welcome(self): - """Display a welcome dialogue on the first start.""" - message = """ -[size=20sp]Welcome to [b]tagit[/b]![/size] - -Since you see this message, it's time to configure tagit. It's a good idea to get familiar with the configuration. Hit F1 or the config button to see all relevant settings. There, you can also get rid of this message. If you desire more flexibility, you can edit the config file directly. Check out the project homepage for more details. -""" # FIXME! - dialogues.Message(text=message, align='left').open() - ## config ## -config.declare(('session', 'first_start'), config.Bool(), True, - __name__, 'First start', 'Show the welcome message typically shown when tagit is started the first time.') - config.declare(('session', 'script'), config.List(config.Any()), [], __name__, 'start script', 'Actions to run after startup. Intended for testing.') config.declare(('session', 'script_delay'), config.Unsigned(), 0, __name__, 'script delay', 'Start script execution delay in seconds.') -config.declare(('ui', 'standalone', 'plane'), config.Enum('browsing', 'dashboard'), 'dashboard', - __name__, 'Initial plane', 'Start with the dashboard or browsing plane.') +config.declare(('ui', 'standalone', 'maximize'), config.Bool(), False, + __name__, 'Window maximization', 'Maximize the window upon startup.') + +config.declare(('ui', 'standalone', 'window_size'), config.List(config.Unsigned()), (1024, 768), + __name__, 'Wndow size', 'Set the window size upon startup.') config.declare(('ui', 'standalone', 'browser', 'cache_size'), config.Unsigned(), 1000, __name__, 'Cache size', 'Number of preview images that are held in the cache. Should be high or zero if memory is not an issue. Set to a small value to preserve memory, but should be at least the most common page size. It is advised to set a value in accordance with `ui.standalone.browser.cache_items`. If zero, no limit applies.') |