mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 15:02:21 -04:00
Merge branch 'drag-and-drop'
This commit is contained in:
commit
1aaa81ee6f
4 changed files with 87 additions and 3 deletions
|
@ -6,6 +6,7 @@ import simplejson, copy
|
||||||
from anki.utils import intTime, ids2str
|
from anki.utils import intTime, ids2str
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
|
from anki.errors import DeckRenameError
|
||||||
|
|
||||||
# fixmes:
|
# fixmes:
|
||||||
# - make sure users can't set grad interval < 1
|
# - make sure users can't set grad interval < 1
|
||||||
|
@ -161,7 +162,7 @@ class DeckManager(object):
|
||||||
"Rename deck prefix to NAME if not exists. Updates children."
|
"Rename deck prefix to NAME if not exists. Updates children."
|
||||||
# make sure target node doesn't already exist
|
# make sure target node doesn't already exist
|
||||||
if newName in self.allNames():
|
if newName in self.allNames():
|
||||||
raise Exception("Deck exists")
|
raise DeckRenameError(_("That deck already exists."))
|
||||||
# rename children
|
# rename children
|
||||||
for grp in self.all():
|
for grp in self.all():
|
||||||
if grp['name'].startswith(g['name'] + "::"):
|
if grp['name'].startswith(g['name'] + "::"):
|
||||||
|
@ -174,10 +175,40 @@ class DeckManager(object):
|
||||||
# finally, ensure we have parents
|
# finally, ensure we have parents
|
||||||
self._ensureParents(newName)
|
self._ensureParents(newName)
|
||||||
|
|
||||||
|
def renameForDragAndDrop(self, draggedDeckDid, ontoDeckDid):
|
||||||
|
draggedDeck = self.get(draggedDeckDid)
|
||||||
|
draggedDeckName = draggedDeck['name']
|
||||||
|
ontoDeckName = self.get(ontoDeckDid)['name']
|
||||||
|
|
||||||
|
if ontoDeckDid == None or ontoDeckDid == '':
|
||||||
|
if len(self._path(draggedDeckName)) > 1:
|
||||||
|
self.rename(draggedDeck, self._basename(draggedDeckName))
|
||||||
|
elif self._canDragAndDrop(draggedDeckName, ontoDeckName):
|
||||||
|
draggedDeck = self.get(draggedDeckDid)
|
||||||
|
draggedDeckName = draggedDeck['name']
|
||||||
|
ontoDeckName = self.get(ontoDeckDid)['name']
|
||||||
|
self.rename(draggedDeck, ontoDeckName + "::" + self._basename(draggedDeckName))
|
||||||
|
|
||||||
|
def _canDragAndDrop(self, draggedDeckName, ontoDeckName):
|
||||||
|
return draggedDeckName <> ontoDeckName \
|
||||||
|
and not self._isParent(ontoDeckName, draggedDeckName) \
|
||||||
|
and not self._isAncestor(draggedDeckName, ontoDeckName)
|
||||||
|
|
||||||
|
def _isParent(self, parentDeckName, childDeckName):
|
||||||
|
return self._path(childDeckName) == self._path(parentDeckName) + [ self._basename(childDeckName) ]
|
||||||
|
|
||||||
|
def _isAncestor(self, ancestorDeckName, descendantDeckName):
|
||||||
|
ancestorPath = self._path(ancestorDeckName)
|
||||||
|
return ancestorPath == self._path(descendantDeckName)[0:len(ancestorPath)]
|
||||||
|
|
||||||
|
def _path(self, name):
|
||||||
|
return name.split("::")
|
||||||
|
def _basename(self, name):
|
||||||
|
return self._path(name)[-1]
|
||||||
|
|
||||||
def _ensureParents(self, name):
|
def _ensureParents(self, name):
|
||||||
path = name.split("::")
|
|
||||||
s = ""
|
s = ""
|
||||||
for p in path[:-1]:
|
for p in self._path(name)[:-1]:
|
||||||
if not s:
|
if not s:
|
||||||
s += p
|
s += p
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -11,3 +11,9 @@ class AnkiError(Exception):
|
||||||
if self.data:
|
if self.data:
|
||||||
m += ": %s" % repr(self.data)
|
m += ": %s" % repr(self.data)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
class DeckRenameError(Exception):
|
||||||
|
def __init__(self, description):
|
||||||
|
self.description = description
|
||||||
|
def __str__(self):
|
||||||
|
return "Couldn't rename deck: " + description
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -79,3 +79,49 @@ def test_rename():
|
||||||
d.decks.rename(d.decks.get(id), "yo")
|
d.decks.rename(d.decks.get(id), "yo")
|
||||||
for n in "yo", "yo::two", "yo::two::three":
|
for n in "yo", "yo::two", "yo::two::three":
|
||||||
assert n in d.decks.allNames()
|
assert n in d.decks.allNames()
|
||||||
|
|
||||||
|
def test_renameForDragAndDrop():
|
||||||
|
d = getEmptyDeck()
|
||||||
|
|
||||||
|
def deckNames():
|
||||||
|
return [ name for name in sorted(d.decks.allNames()) if name <> u'Default' ]
|
||||||
|
|
||||||
|
languages_did = d.decks.id('Languages')
|
||||||
|
chinese_did = d.decks.id('Chinese')
|
||||||
|
hsk_did = d.decks.id('Chinese::HSK')
|
||||||
|
|
||||||
|
# Renaming also renames children
|
||||||
|
d.decks.renameForDragAndDrop(chinese_did, languages_did)
|
||||||
|
assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
|
||||||
|
|
||||||
|
# Dragging a deck onto itself is a no-op
|
||||||
|
d.decks.renameForDragAndDrop(languages_did, languages_did)
|
||||||
|
assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
|
||||||
|
|
||||||
|
# Dragging a deck onto its parent is a no-op
|
||||||
|
d.decks.renameForDragAndDrop(hsk_did, chinese_did)
|
||||||
|
assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
|
||||||
|
|
||||||
|
# Dragging a deck onto a descendant is a no-op
|
||||||
|
d.decks.renameForDragAndDrop(languages_did, hsk_did)
|
||||||
|
assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
|
||||||
|
|
||||||
|
# Can drag a grandchild onto its grandparent. It becomes a child
|
||||||
|
d.decks.renameForDragAndDrop(hsk_did, languages_did)
|
||||||
|
assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::HSK' ]
|
||||||
|
|
||||||
|
# Can drag a deck onto its sibling
|
||||||
|
d.decks.renameForDragAndDrop(hsk_did, chinese_did)
|
||||||
|
assert deckNames() == [ 'Languages', 'Languages::Chinese', 'Languages::Chinese::HSK' ]
|
||||||
|
|
||||||
|
# Can drag a deck back to the top level
|
||||||
|
d.decks.renameForDragAndDrop(chinese_did, None)
|
||||||
|
assert deckNames() == [ 'Chinese', 'Chinese::HSK', 'Languages' ]
|
||||||
|
|
||||||
|
# Dragging a top level deck to the top level is a no-op
|
||||||
|
d.decks.renameForDragAndDrop(chinese_did, None)
|
||||||
|
assert deckNames() == [ 'Chinese', 'Chinese::HSK', 'Languages' ]
|
||||||
|
|
||||||
|
# '' is a convenient alias for the top level DID
|
||||||
|
d.decks.renameForDragAndDrop(hsk_did, '')
|
||||||
|
assert deckNames() == [ 'Chinese', 'HSK', 'Languages' ]
|
||||||
|
|
Loading…
Reference in a new issue