diff --git a/bin/keyboardcomposer b/bin/keyboardcomposer --- a/bin/keyboardcomposer +++ b/bin/keyboardcomposer @@ -549,11 +549,10 @@ if __name__ == "__main__": root = Tk() initTkdnd(root.tk, 'tkdnd/trunk/') - # this has yet to be moved into the session graph + session = clientsession.getUri('keyboardcomposer', opts) + tl = toplevelat("Keyboard Composer - %s" % opts.session, - existingtoplevel=root) - - session = clientsession.getUri('keyboardcomposer', opts) + existingtoplevel=root, graph=graph, session=session) kc = KeyboardComposer(tl, graph, session, hw_sliders=not opts.no_sliders) diff --git a/bin/rdfdb b/bin/rdfdb --- a/bin/rdfdb +++ b/bin/rdfdb @@ -16,7 +16,12 @@ callers faster. A caller can submit a patch which we'll persist and broadcast to every other client. -Global data undo should probably happen within this service. +Global data undo should probably happen within this service. Some +operations should not support undo, such as updating the default +position of a window. How will we separate those? A blacklist of +subj+pred pairs that don't save undo? Or just save the updates like +everything else, but when you press undo, there's a way to tell which +updates *should* be part of your app's undo system? Maybe some subgraphs are for transient data (e.g. current timecode, mouse position in curvecalc) that only some listeners want to hear about. diff --git a/light9/rdfdb/syncedgraph.py b/light9/rdfdb/syncedgraph.py --- a/light9/rdfdb/syncedgraph.py +++ b/light9/rdfdb/syncedgraph.py @@ -1,6 +1,6 @@ from rdflib import ConjunctiveGraph, RDFS, RDF, Graph import logging, cyclone.httpclient, traceback, urllib -from twisted.internet import reactor +from twisted.internet import reactor, defer log = logging.getLogger('syncedgraph') from light9.rdfdb.patch import Patch, ALLSTMTS from light9.rdfdb.rdflibpatch import patchQuads @@ -109,8 +109,10 @@ class PatchSender(object): self._currentSendPatchRequest = None def sendPatch(self, p): - self._patchesToSend.append(p) + sendResult = defer.Deferred() + self._patchesToSend.append((p, sendResult)) self._continueSending() + return sendResult def _continueSending(self): if not self._patchesToSend or self._currentSendPatchRequest: @@ -129,14 +131,15 @@ class PatchSender(object): for q in p.addQuads: print q print "----" else: - p = self._patchesToSend.pop(0) + p, sendResult = self._patchesToSend.pop(0) else: - p = self._patchesToSend.pop(0) + p, sendResult = self._patchesToSend.pop(0) self._currentSendPatchRequest = sendPatch( self.target, p, senderUpdateUri=self.myUpdateResource) self._currentSendPatchRequest.addCallbacks(self._sendPatchDone, self._sendPatchErr) + self._currentSendPatchRequest.chainDeferred(sendResult) def _sendPatchDone(self, result): self._currentSendPatchRequest = None diff --git a/light9/rdfdb/web/index.xhtml b/light9/rdfdb/web/index.xhtml --- a/light9/rdfdb/web/index.xhtml +++ b/light9/rdfdb/web/index.xhtml @@ -28,6 +28,13 @@ // /g, function (match, short) { return "light9:"+short; }) + .replace(//g, function (match, short) { return "kcsession:"+short }); + } + function onMessage(d) { if (d.clients !== undefined) { $("#clients").empty().text(JSON.stringify(d.clients)); @@ -36,8 +43,8 @@ $("#patches").prepend( $("
").addClass("patch") .append($("").text("Patch")) - .append($("
").addClass("deletes").text(d.patch.deletes)) - .append($("
").addClass("adds").text(d.patch.adds)) + .append($("
").addClass("deletes").text(collapseCuries(d.patch.deletes))) + .append($("
").addClass("adds").text(collapseCuries(d.patch.adds))) ); } diff --git a/light9/uihelpers.py b/light9/uihelpers.py --- a/light9/uihelpers.py +++ b/light9/uihelpers.py @@ -1,9 +1,10 @@ """all the tiny tk helper functions""" from __future__ import nested_scopes -from Tkinter import * -from Tix import * -from types import StringType +#from Tkinter import Button +from rdflib import Literal +from Tix import Button, Toplevel, Tk, IntVar, Entry, DoubleVar +from light9.namespaces import L9 windowlocations = { 'sub' : '425x738+00+00', @@ -32,21 +33,45 @@ def toplevel_savegeometry(tl,name): # it's ok if there's no saved geometry pass -def toplevelat(name, existingtoplevel=None): +def toplevelat(name, existingtoplevel=None, graph=None, session=None): tl = existingtoplevel or Toplevel() tl.title(name) - try: - f=open(".light9-window-geometry-%s" % name.replace(' ','_')) - windowlocations[name]=f.read() # file has no newline - except: - # it's ok if there's no saved geometry - pass + lastSaved = [None] + setOnce = [False] + def setPosFromGraphOnce(): + """ + the graph is probably initially empty, but as soon as it gives + us one window position, we stop reading them + """ + if setOnce[0]: + return + geo = graph.value(session, L9.windowGeometry) + + if geo is not None and geo != lastSaved[0]: + setOnce[0] = True + tl.geometry(geo) + lastSaved[0] = geo + + def savePos(): + geo = tl.geometry() + # todo: need a way to filter out the startup window sizes that + # weren't set by the user + if geo.startswith("1x1") or geo.startswith(("378x85", "378x86")): + return + if geo == lastSaved[0]: + return + lastSaved[0] = geo + graph.patchObject(session, session, L9.windowGeometry, Literal(geo)) + + if graph is not None and session is not None: + graph.addHandler(setPosFromGraphOnce) if name in windowlocations: tl.geometry(positionOnCurrentDesktop(windowlocations[name])) - tl._toplevelat_funcid = tl.bind("",lambda ev,tl=tl,name=name: toplevel_savegeometry(tl,name)) + if graph is not None: + tl._toplevelat_funcid = tl.bind("",lambda ev,tl=tl,name=name: savePos()) return tl