diff --git a/ankiqt/ui/main.py b/ankiqt/ui/main.py
index 1e78ba68c..dcaa38552 100755
--- a/ankiqt/ui/main.py
+++ b/ankiqt/ui/main.py
@@ -2168,6 +2168,7 @@ it to your friends.
self.connect(self.syncThread, SIGNAL("fullSyncFinished"), self.fullSyncFinished)
self.connect(self.syncThread, SIGNAL("fullSyncProgress"), self.fullSyncProgress)
self.connect(self.syncThread, SIGNAL("badUserPass"), self.badUserPass)
+ self.connect(self.syncThread, SIGNAL("syncConflicts"), self.onConflict)
self.syncThread.start()
self.switchToWelcomeScreen()
self.setEnabled(False)
@@ -2185,6 +2186,22 @@ it to your friends.
ok.append(d)
return ok
+ def onConflict(self, deckName):
+ diag = ui.utils.askUserDialog(_("""\
+%s has been changed on both the local
+and remote side. What do you want to do?""" % deckName),
+ [_("Keep Local"),
+ _("Keep Remote"),
+ _("Cancel")])
+ diag.setDefault(2)
+ ret = diag.run()
+ if ret == _("Keep Local"):
+ self.syncThread.conflictResolution = "keepLocal"
+ elif ret == _("Keep Remote"):
+ self.syncThread.conflictResolution = "keepRemote"
+ else:
+ self.syncThread.conflictResolution = "cancel"
+
def onSyncFinished(self):
"Reopen after sync finished."
self.mainWin.buttonStack.show()
diff --git a/ankiqt/ui/sync.py b/ankiqt/ui/sync.py
index 1711d63e2..a7c88a63a 100755
--- a/ankiqt/ui/sync.py
+++ b/ankiqt/ui/sync.py
@@ -100,7 +100,8 @@ class Sync(QThread):
# multi-mode setup
if deck:
c = sqlite.connect(deck)
- syncName = c.execute("select syncName from decks").fetchone()[0]
+ (syncName, localChanged) = c.execute(
+ "select syncName, modified > lastSync from decks").fetchone()
c.close()
if not syncName:
return
@@ -108,6 +109,10 @@ class Sync(QThread):
else:
syncName = self.parent.syncName
path = self.parent.deckPath
+ c = sqlite.connect(path)
+ localChanged = c.execute(
+ "select modified > lastSync from decks").fetchone()[0]
+ c.close()
# ensure deck mods cached
try:
proxy = self.connect()
@@ -127,12 +132,25 @@ class Sync(QThread):
self.emit(SIGNAL("noMatchingDeck"), keys, not self.onlyMerge)
self.setStatus("")
return
- self.setStatus(_("Syncing %s...") % syncName, 0)
+ # check clock
timediff = abs(proxy.timestamp - time.time())
if timediff > 300:
self.emit(SIGNAL("syncClockOff"), timediff)
return
+ # check conflicts
+ remoteChanged = proxy.hasChanged(syncName)
+ self.conflictResolution = None
+ if localChanged and remoteChanged:
+ self.emit(SIGNAL("syncConflicts"), syncName)
+ while not self.conflictResolution:
+ time.sleep(0.2)
+ if self.conflictResolution == "cancel":
+ if not deck:
+ # alert we're finished early
+ self.emit(SIGNAL("syncFinished"))
+ return
# reopen
+ self.setStatus(_("Syncing %s...") % syncName, 0)
self.deck = None
try:
self.deck = DeckStorage.Deck(path)
@@ -146,8 +164,12 @@ class Sync(QThread):
# summary
self.setStatus(_("Fetching summary from server..."), 0)
sums = client.summaries()
- if client.needFullSync(sums):
+ if self.conflictResolution or client.needFullSync(sums):
self.setStatus(_("Preparing full sync..."), 0)
+ if self.conflictResolution == "keepLocal":
+ client.remoteTime = 0
+ elif self.conflictResolution == "keepRemote":
+ client.localTime = 0
ret = client.prepareFullSync()
if ret[0] == "fromLocal":
self.setStatus(_("Uploading..."), 0)
diff --git a/ankiqt/ui/utils.py b/ankiqt/ui/utils.py
index 1650e2522..094ad464e 100644
--- a/ankiqt/ui/utils.py
+++ b/ankiqt/ui/utils.py
@@ -75,6 +75,44 @@ def askUser(text, parent=None, help=""):
break
return r == QMessageBox.Yes
+class ButtonedDialog(QMessageBox):
+
+ def __init__(self, text, buttons, parent=None, help=""):
+ QDialog.__init__(self, parent)
+ self.buttons = []
+ self.setWindowTitle("Anki")
+ self.help = help
+ self.setIcon(QMessageBox.Warning)
+ self.setText(text)
+ # v = QVBoxLayout()
+ # v.addWidget(QLabel(text))
+ # box = QDialogButtonBox()
+ # v.addWidget(box)
+ for b in buttons:
+ self.buttons.append(
+ self.addButton(b, QMessageBox.AcceptRole))
+ if help:
+ self.addButton(_("Help"), QMessageBox.HelpRole)
+ buttons.append(_("Help"))
+ #self.setLayout(v)
+
+ def run(self):
+ self.exec_()
+ but = self.clickedButton().text()
+ if but == "Help":
+ # FIXME stop dialog closing?
+ openWikiLink(self.help)
+ return self.clickedButton().text()
+
+ def setDefault(self, idx):
+ self.setDefaultButton(self.buttons[idx])
+
+def askUserDialog(text, buttons, parent=None, help=""):
+ if not parent:
+ parent = ankiqt.mw
+ diag = ButtonedDialog(text, buttons, parent, help)
+ return diag
+
class GetTextDialog(QDialog):
def __init__(self, parent, question, help=None, edit=None):