~ubuntu-branches/ubuntu/lucid/anki/lucid-updates

« back to all changes in this revision

Viewing changes to ankiqt/ui/cardlist.py

  • Committer: Bazaar Package Importer
  • Author(s): Mackenzie Morgan
  • Date: 2010-05-31 15:55:50 UTC
  • mfrom: (7.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20100531155550-wj3tag8bvp6fwhpo
Tags: 0.9.9.8.6-2~lucid1
Backport from maverick to fix FTBFS (LP: #550145)

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
from anki.utils import fmtTimeSpan, parseTags, findTag, addTags, deleteTags, \
15
15
     stripHTML, ids2str
16
16
from ankiqt.ui.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter
17
 
from ankiqt.ui.utils import saveHeader, restoreHeader
 
17
from ankiqt.ui.utils import saveHeader, restoreHeader, saveState, \
 
18
     restoreState, applyStyles
18
19
from anki.errors import *
19
20
from anki.db import *
20
21
from anki.stats import CardStats
30
31
CARD_MODIFIED = 7
31
32
CARD_INTERVAL = 8
32
33
CARD_EASE = 9
33
 
CARD_PRIORITY = 10
 
34
CARD_NO = 10
 
35
CARD_PRIORITY = 11
 
36
CARD_TAGS = 12
 
37
CARD_FACTCREATED = 13
 
38
 
 
39
COLOUR_SUSPENDED1 = "#ffffcc"
 
40
COLOUR_SUSPENDED2 = "#ffffaa"
 
41
COLOUR_INACTIVE1 = "#ffcccc"
 
42
COLOUR_INACTIVE2 = "#ffaaaa"
 
43
COLOUR_MARKED1 = "#ccccff"
 
44
COLOUR_MARKED2 = "#aaaaff"
34
45
 
35
46
# Deck editor
36
47
##########################################################################
69
80
            f = QFont()
70
81
            f.setPixelSize(self.parent.config['editFontSize'])
71
82
            return QVariant(f)
 
83
        if role == Qt.TextAlignmentRole and index.column() == 2:
 
84
            return QVariant(Qt.AlignHCenter)
72
85
        elif role == Qt.DisplayRole or role == Qt.EditRole:
73
86
            if len(self.cards[index.row()]) == 1:
74
87
                # not cached yet
79
92
            s = s.replace("\n", u"  ")
80
93
            s = stripHTML(s)
81
94
            s = re.sub("\[sound:[^]]+\]", "", s)
 
95
            s = s.replace("&", "&")
82
96
            s = s.strip()
83
97
            return QVariant(s)
84
98
        else:
167
181
    def updateCard(self, index):
168
182
        try:
169
183
            self.cards[index.row()] = self.deck.s.first("""
170
 
    select id, question, answer, due, reps, factId, created, modified,
171
 
    interval, factor, priority from cards where id = :id""",
 
184
select id, question, answer, combinedDue, reps, factId, created, modified,
 
185
interval, factor, noCount, priority, (select tags from facts where
 
186
facts.id = cards.factId), (select created from facts where
 
187
facts.id = cards.factId) from cards where id = :id""",
172
188
                                                        id=self.cards[index.row()][0])
173
 
            self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
174
 
                      index, self.index(index.row(), 1))
 
189
            self.emit(SIGNAL("layoutChanged()"))
175
190
        except:
176
191
            # called after search changed
177
192
            pass
178
193
 
 
194
    def refresh(self):
 
195
        self.cards = [[x[0]] for x in self.cards]
 
196
        self.emit(SIGNAL("layoutChanged()"))
 
197
 
179
198
    # Tools
180
199
    ######################################################################
181
200
 
221
240
            return self.repsColumn(index)
222
241
        elif self.sortKey == "factor":
223
242
            return self.easeColumn(index)
 
243
        elif self.sortKey == "noCount":
 
244
            return self.noColumn(index)
 
245
        elif self.sortKey == "fact":
 
246
            return self.factCreatedColumn(index)
224
247
        else:
225
248
            return self.nextDue(index)
226
249
 
235
258
            k = _("Reps")
236
259
        elif self.sortKey == "factor":
237
260
            k = _("Ease")
 
261
        elif self.sortKey == "noCount":
 
262
            k = _("Lapses")
 
263
        elif self.sortKey == "fact":
 
264
            k = _("Fact Created")
238
265
        else:
239
266
            k = _("Due")
240
267
        self.columns[-1][0] = k
243
270
        return time.strftime("%Y-%m-%d", time.localtime(
244
271
            self.cards[index.row()][CARD_CREATED]))
245
272
 
 
273
    def factCreatedColumn(self, index):
 
274
        return time.strftime("%Y-%m-%d", time.localtime(
 
275
            self.cards[index.row()][CARD_FACTCREATED]))
 
276
 
246
277
    def modifiedColumn(self, index):
247
278
        return time.strftime("%Y-%m-%d", time.localtime(
248
279
            self.cards[index.row()][CARD_MODIFIED]))
257
288
    def easeColumn(self, index):
258
289
        return "%0.2f" % self.cards[index.row()][CARD_EASE]
259
290
 
 
291
    def noColumn(self, index):
 
292
        return "%d" % self.cards[index.row()][CARD_NO]
 
293
 
260
294
class StatusDelegate(QItemDelegate):
261
295
 
262
296
    def __init__(self, parent, model):
267
301
        if len(self.model.cards[index.row()]) == 1:
268
302
            self.model.updateCard(index)
269
303
        row = self.model.cards[index.row()]
 
304
        if row[CARD_PRIORITY] == -3:
 
305
            # custom render
 
306
            if index.row() % 2 == 0:
 
307
                brush = QBrush(QColor(COLOUR_SUSPENDED1))
 
308
            else:
 
309
                brush = QBrush(QColor(COLOUR_SUSPENDED2))
 
310
            painter.save()
 
311
            painter.fillRect(option.rect, brush)
 
312
            painter.restore()
270
313
        if row[CARD_PRIORITY] == 0:
271
314
            # custom render
272
 
            brush = QBrush(QColor("#ffffaa"))
 
315
            if index.row() % 2 == 0:
 
316
                brush = QBrush(QColor(COLOUR_INACTIVE1))
 
317
            else:
 
318
                brush = QBrush(QColor(COLOUR_INACTIVE2))
 
319
            painter.save()
 
320
            painter.fillRect(option.rect, brush)
 
321
            painter.restore()
 
322
        elif "Marked" in row[CARD_TAGS]:
 
323
            if index.row() % 2 == 0:
 
324
                brush = QBrush(QColor(COLOUR_MARKED1))
 
325
            else:
 
326
                brush = QBrush(QColor(COLOUR_MARKED2))
273
327
            painter.save()
274
328
            painter.fillRect(option.rect, brush)
275
329
            painter.restore()
283
337
        else:
284
338
            windParent = parent
285
339
        QMainWindow.__init__(self, windParent)
 
340
        applyStyles(self)
286
341
        self.parent = parent
287
342
        self.deck = self.parent.deck
288
343
        self.config = parent.config
292
347
        self.lastFilter = ""
293
348
        self.dialog = ankiqt.forms.cardlist.Ui_MainWindow()
294
349
        self.dialog.setupUi(self)
 
350
        self.setUnifiedTitleAndToolBarOnMac(True)
295
351
        restoreGeom(self, "editor")
 
352
        restoreState(self, "editor")
296
353
        restoreSplitter(self.dialog.splitter, "editor")
 
354
        # toolbar
 
355
        self.dialog.toolBar.setIconSize(QSize(self.config['iconSize'],
 
356
                                              self.config['iconSize']))
 
357
        self.dialog.toolBar.toggleViewAction().setText(_("Toggle Toolbar"))
297
358
        # flush all changes before we load
298
359
        self.deck.s.flush()
299
360
        self.model = DeckModel(self.parent, self.parent.deck)
300
361
        self.dialog.tableView.setSortingEnabled(False)
 
362
        self.dialog.tableView.setShowGrid(False)
301
363
        self.dialog.tableView.setModel(self.model)
302
364
        self.dialog.tableView.selectionModel()
303
365
        self.connect(self.dialog.tableView.selectionModel(),
304
366
                     SIGNAL("selectionChanged(QItemSelection,QItemSelection)"),
305
367
                     self.updateFilterLabel)
306
368
        self.dialog.tableView.setItemDelegate(StatusDelegate(self, self.model))
307
 
        if self.deck.getInt("reverseOrder"):
308
 
            self.dialog.actionReverseOrder.setChecked(True)
 
369
        self.updateSortOrder()
309
370
        self.updateFont()
310
371
        self.setupMenus()
311
372
        self.setupFilter()
319
380
        self.drawTags()
320
381
        self.updateFilterLabel()
321
382
        self.show()
322
 
        self.updateSearch()
323
383
        if self.parent.currentCard:
324
384
            self.currentCard = self.parent.currentCard
325
 
        self.focusCurrentCard()
 
385
        self.updateSearch()
326
386
        if sys.platform.startswith("darwin"):
327
387
            self.macCloseShortcut = QShortcut(QKeySequence("Ctrl+w"), self)
328
388
            self.connect(self.macCloseShortcut, SIGNAL("activated()"),
329
389
                         self.close)
330
390
 
331
 
    def findCardInDeckModel(self, model, card):
332
 
        for i, thisCard in enumerate(model.cards):
333
 
            if thisCard[0] == card.id:
 
391
    def findCardInDeckModel(self):
 
392
        for i, thisCard in enumerate(self.model.cards):
 
393
            if thisCard[0] == self.currentCard.id:
334
394
                return i
335
395
        return -1
336
396
 
361
421
        self.connect(self.dialog.sortBox, SIGNAL("activated(int)"),
362
422
                     self.sortChanged)
363
423
        self.sortChanged(self.sortIndex, refresh=False)
 
424
        self.connect(self.dialog.sortOrder, SIGNAL("clicked()"),
 
425
                     self.reverseOrder)
364
426
 
365
427
    def drawTags(self):
366
428
        self.dialog.tagList.view().setFixedWidth(200)
367
429
        self.dialog.tagList.setMaxVisibleItems(30)
368
430
        self.dialog.tagList.setFixedWidth(130)
369
431
        self.dialog.tagList.clear()
370
 
        alltags = [None, "Marked", "Suspended", None, None, None]
 
432
        alltags = [None, "Marked", None, None, "Leech", None, None]
371
433
        # system tags
372
 
        self.dialog.tagList.addItem(_("<Filter>"))
 
434
        self.dialog.tagList.addItem(_("Show All Cards"))
373
435
        self.dialog.tagList.addItem(QIcon(":/icons/rating.png"),
374
436
                                    _('Marked'))
375
437
        self.dialog.tagList.addItem(QIcon(":/icons/media-playback-pause.png"),
376
438
                                    _('Suspended'))
377
439
        self.dialog.tagList.addItem(QIcon(":/icons/chronometer.png"),
378
440
                                    _('Due'))
 
441
        self.dialog.tagList.addItem(QIcon(":/icons/emblem-important.png"),
 
442
                                    _('Leech'))
379
443
        self.dialog.tagList.addItem(QIcon(":/icons/editclear.png"),
380
444
                                    _('No fact tags'))
381
445
        self.dialog.tagList.insertSeparator(
394
458
            icon = QIcon(":/icons/" + icon)
395
459
            for t in sortedtags:
396
460
                self.dialog.tagList.addItem(icon, t.replace("_", " "))
397
 
            alltags.append(None)
398
 
            self.dialog.tagList.insertSeparator(
399
 
                self.dialog.tagList.count())
 
461
            if sortedtags:
 
462
                self.dialog.tagList.insertSeparator(
 
463
                    self.dialog.tagList.count())
 
464
                alltags.append(None)
400
465
        # fact tags
401
466
        alluser = sorted(self.deck.allTags())
402
467
        for tag in alltags:
422
487
            _("Reps"),
423
488
            _("Ease"),
424
489
            _("Fact Created"),
 
490
            _("Lapses"),
425
491
            ]
426
492
        self.sortFields = sorted(self.deck.allFields())
427
493
        self.sortList.extend([_("'%s'") % f for f in self.sortFields])
431
497
            self.sortIndex = 0
432
498
        self.dialog.sortBox.setCurrentIndex(self.sortIndex)
433
499
 
 
500
    def updateSortOrder(self):
 
501
        if self.deck.getInt("reverseOrder"):
 
502
            self.dialog.sortOrder.setIcon(QIcon(":/icons/view-sort-descending.png"))
 
503
        else:
 
504
            self.dialog.sortOrder.setIcon(QIcon(":/icons/view-sort-ascending.png"))
 
505
 
434
506
    def sortChanged(self, idx, refresh=True):
435
507
        if idx == 0:
436
508
            self.sortKey = "question"
450
522
            self.sortKey = "factor"
451
523
        elif idx == 8:
452
524
            self.sortKey = "fact"
 
525
        elif idx == 9:
 
526
            self.sortKey = "noCount"
453
527
        else:
454
 
            self.sortKey = ("field", self.sortFields[idx-9])
 
528
            self.sortKey = ("field", self.sortFields[idx-10])
455
529
        self.rebuildSortIndex(self.sortKey)
456
530
        self.sortIndex = idx
457
531
        self.deck.setVar('sortIndex', idx)
466
540
    def rebuildSortIndex(self, key):
467
541
        if key not in (
468
542
            "question", "answer", "created", "modified", "due", "interval",
469
 
            "reps", "factor"):
 
543
            "reps", "factor", "noCount"):
470
544
            return
471
545
        old = self.deck.s.scalar("select sql from sqlite_master where name = :k",
472
546
                                 k="ix_cards_sort")
491
565
        elif idx == 1:
492
566
            filter = "tag:marked"
493
567
        elif idx == 2:
494
 
            filter = "tag:suspended"
 
568
            filter = "is:suspended"
495
569
        elif idx == 3:
496
570
            filter = "is:due"
497
571
        elif idx == 4:
 
572
            filter = "tag:leech"
 
573
        elif idx == 5:
498
574
            filter = "tag:none"
499
575
        else:
500
576
            filter = "tag:" + self.alltags[idx]
511
587
            "cur": len(self.model.cards),
512
588
            "tot": self.deck.cardCount,
513
589
            "sel": ngettext("%d selected", "%d selected", selected) % selected
514
 
            })
 
590
            } + " - " + self.parent.deck.name())
515
591
 
516
 
    def onEvent(self):
 
592
    def onEvent(self, type='field'):
517
593
        if self.deck.undoAvailable():
518
594
            self.dialog.actionUndo.setText(_("Undo %s") %
519
595
                                           self.deck.undoName())
529
605
        # update list
530
606
        if self.currentRow and self.model.cards:
531
607
            self.model.updateCard(self.currentRow)
 
608
        if type == "tag":
 
609
            self.drawTags()
532
610
 
533
611
    def filterTextChanged(self):
534
612
        interval = 300
553
631
    def updateSearch(self, force=True):
554
632
        if self.parent.inDbHandler:
555
633
            return
556
 
        idx = self.dialog.tableView.currentIndex()
557
 
        row = idx.row()
558
634
        self.model.searchStr = unicode(self.dialog.filterEdit.text())
559
635
        self.model.showMatching(force)
560
636
        self.updateFilterLabel()
561
637
        self.onEvent()
562
638
        self.filterTimer = None
563
639
        if self.model.cards:
564
 
            if row == -1:
565
 
                row = 0
566
640
            self.dialog.cardInfoGroup.show()
567
641
            self.dialog.fieldsArea.show()
568
642
        else:
569
643
            self.dialog.cardInfoGroup.hide()
570
644
            self.dialog.fieldsArea.hide()
571
 
        self.dialog.tableView.selectRow(row)
572
 
        self.dialog.tableView.scrollTo(idx, QAbstractItemView.PositionAtCenter)
 
645
        if not self.focusCurrentCard():
 
646
            if self.model.cards:
 
647
                self.dialog.tableView.selectRow(0)
573
648
        if not self.model.cards:
574
649
            self.editor.setFact(None)
575
650
 
576
651
    def focusCurrentCard(self):
577
652
        if self.currentCard:
578
 
            currentCardIndex = self.findCardInDeckModel(
579
 
                                 self.model, self.currentCard)
 
653
            try:
 
654
                self.currentCard.id
 
655
            except:
 
656
                return False
 
657
            currentCardIndex = self.findCardInDeckModel()
580
658
            if currentCardIndex >= 0:
581
659
                sm = self.dialog.tableView.selectionModel()
582
660
                sm.clear()
584
662
                self.dialog.tableView.scrollTo(
585
663
                              self.model.index(currentCardIndex,0),
586
664
                              self.dialog.tableView.PositionAtCenter)
 
665
                return True
 
666
        return False
587
667
 
588
668
    def setupHeaders(self):
589
669
        if not sys.platform.startswith("win32"):
596
676
 
597
677
    def setupMenus(self):
598
678
        # actions
 
679
        self.connect(self.dialog.actionAddItems, SIGNAL("triggered()"), self.parent.onAddCard)
599
680
        self.connect(self.dialog.actionDelete, SIGNAL("triggered()"), self.deleteCards)
600
681
        self.connect(self.dialog.actionAddTag, SIGNAL("triggered()"), self.addTags)
601
682
        self.connect(self.dialog.actionDeleteTag, SIGNAL("triggered()"), self.deleteTags)
603
684
        self.connect(self.dialog.actionCram, SIGNAL("triggered()"), self.cram)
604
685
        self.connect(self.dialog.actionAddCards, SIGNAL("triggered()"), self.addCards)
605
686
        self.connect(self.dialog.actionChangeModel, SIGNAL("triggered()"), self.onChangeModel)
 
687
        self.connect(self.dialog.actionToggleSuspend, SIGNAL("triggered(bool)"), self.onSuspend)
 
688
        self.connect(self.dialog.actionToggleMark, SIGNAL("triggered(bool)"), self.onMark)
606
689
        # edit
607
690
        self.connect(self.dialog.actionFont, SIGNAL("triggered()"), self.onFont)
608
691
        self.connect(self.dialog.actionUndo, SIGNAL("triggered()"), self.onUndo)
609
692
        self.connect(self.dialog.actionRedo, SIGNAL("triggered()"), self.onRedo)
610
693
        self.connect(self.dialog.actionInvertSelection, SIGNAL("triggered()"), self.invertSelection)
611
 
        self.connect(self.dialog.actionReverseOrder, SIGNAL("triggered()"), self.reverseOrder)
612
694
        self.connect(self.dialog.actionSelectFacts, SIGNAL("triggered()"), self.selectFacts)
613
695
        self.connect(self.dialog.actionFindReplace, SIGNAL("triggered()"), self.onFindReplace)
614
696
        # jumps
618
700
        self.connect(self.dialog.actionNextCard, SIGNAL("triggered()"), self.onNextCard)
619
701
        self.connect(self.dialog.actionFind, SIGNAL("triggered()"), self.onFind)
620
702
        self.connect(self.dialog.actionFact, SIGNAL("triggered()"), self.onFact)
 
703
        self.connect(self.dialog.actionTags, SIGNAL("triggered()"), self.onTags)
 
704
        self.connect(self.dialog.actionSort, SIGNAL("triggered()"), self.onSort)
 
705
        self.connect(self.dialog.actionCardList, SIGNAL("triggered()"), self.onCardList)
621
706
        # help
622
707
        self.connect(self.dialog.actionGuide, SIGNAL("triggered()"), self.onHelp)
623
708
        runHook('editor.setupMenus', self)
634
719
        self.editor.setFact(None)
635
720
        self.editor.close()
636
721
        saveGeom(self, "editor")
 
722
        saveState(self, "editor")
637
723
        saveHeader(self.dialog.tableView.horizontalHeader(), "editor")
638
724
        self.hide()
639
725
        ui.dialogs.close("CardList")
640
 
        self.parent.moveToState("auto")
 
726
        if self.parent.currentCard:
 
727
            self.parent.moveToState("showQuestion")
 
728
        else:
 
729
            self.parent.moveToState("auto")
641
730
        self.teardownHooks()
642
731
        return True
643
732
 
675
764
        self.dialog.tagList.setEnabled(True)
676
765
        self.dialog.menubar.setEnabled(True)
677
766
        self.dialog.cardInfoGroup.setEnabled(True)
 
767
        self.dialog.toolBar.setEnabled(True)
678
768
 
679
769
    def onFactInvalid(self, fact):
680
770
        self.factValid = False
684
774
        self.dialog.tagList.setEnabled(False)
685
775
        self.dialog.menubar.setEnabled(False)
686
776
        self.dialog.cardInfoGroup.setEnabled(False)
 
777
        self.dialog.toolBar.setEnabled(False)
687
778
 
688
779
    def rowChanged(self, current, previous):
689
780
        self.currentRow = current
695
786
        self.editor.setFact(fact, True)
696
787
        self.showCardInfo(self.currentCard)
697
788
        self.onEvent()
 
789
        self.updateToggles()
698
790
 
699
791
    def setupCardInfo(self):
700
792
        self.currentCard = None
724
816
            "select id from cards where factId in (%s)" %
725
817
            ",".join([str(s) for s in self.selectedFacts()]))
726
818
 
727
 
    def updateAfterCardChange(self, reset=False):
 
819
    def updateAfterCardChange(self):
728
820
        "Refresh info like stats on current card"
729
821
        self.currentRow = self.dialog.tableView.currentIndex()
730
822
        self.rowChanged(self.currentRow, None)
731
 
        if reset:
732
 
            self.updateSearch()
 
823
        self.model.refresh()
 
824
        self.drawTags()
733
825
        self.parent.moveToState("auto")
734
826
 
735
827
    # Menu options
738
830
    def deleteCards(self):
739
831
        cards = self.selectedCards()
740
832
        n = _("Delete Cards")
 
833
        try:
 
834
            new = self.findCardInDeckModel() + 1
 
835
        except:
 
836
            # card has been deleted
 
837
            return
 
838
        self.dialog.tableView.setFocus()
741
839
        self.deck.setUndoStart(n)
742
840
        self.deck.deleteCards(cards)
743
841
        self.deck.setUndoEnd(n)
 
842
        new = min(max(0, new), len(self.model.cards) - 1)
 
843
        self.dialog.tableView.selectRow(new)
744
844
        self.updateSearch()
745
845
        self.updateAfterCardChange()
746
846
 
747
 
    def addTags(self):
748
 
        (tags, r) = ui.utils.getTag(self, self.deck, _("Enter tags to add:"))
749
 
        if tags:
750
 
            n = _("Add Tags")
 
847
    def addTags(self, tags=None, label=None):
 
848
        if tags is None:
 
849
            (tags, r) = ui.utils.getTag(self, self.deck, _("Enter tags to add:"))
 
850
        else:
 
851
            r = True
 
852
        if label is None:
 
853
            label = _("Add Tags")
 
854
        if r:
751
855
            self.parent.setProgressParent(self)
752
 
            self.deck.setUndoStart(n)
 
856
            self.deck.setUndoStart(label)
753
857
            self.deck.addTags(self.selectedFacts(), tags)
754
 
            self.deck.setUndoEnd(n)
 
858
            self.deck.setUndoEnd(label)
755
859
            self.parent.setProgressParent(None)
756
860
        self.updateAfterCardChange()
757
861
 
758
 
    def deleteTags(self):
759
 
        (tags, r) = ui.utils.getTag(self, self.deck, _("Enter tags to delete:"))
760
 
        if tags:
761
 
            n = _("Delete Tags")
 
862
    def deleteTags(self, tags=None, label=None):
 
863
        if tags is None:
 
864
            (tags, r) = ui.utils.getTag(self, self.deck, _("Enter tags to delete:"))
 
865
        else:
 
866
            r = True
 
867
        if label is None:
 
868
            label = _("Delete Tags")
 
869
        if r:
762
870
            self.parent.setProgressParent(self)
763
 
            self.deck.setUndoStart(n)
 
871
            self.deck.setUndoStart(label)
764
872
            self.deck.deleteTags(self.selectedFacts(), tags)
765
 
            self.deck.setUndoEnd(n)
 
873
            self.deck.setUndoEnd(label)
766
874
            self.parent.setProgressParent(None)
767
875
        self.updateAfterCardChange()
768
876
 
 
877
    def updateToggles(self):
 
878
        self.dialog.actionToggleSuspend.setChecked(self.isSuspended())
 
879
        self.dialog.actionToggleMark.setChecked(self.isMarked())
 
880
 
 
881
    def isSuspended(self):
 
882
        return self.currentCard and self.currentCard.priority == -3
 
883
 
 
884
    def onSuspend(self, sus):
 
885
        if sus:
 
886
            self._onSuspend()
 
887
        else:
 
888
            self._onUnsuspend()
 
889
 
 
890
    def _onSuspend(self):
 
891
        n = _("Suspend")
 
892
        self.parent.setProgressParent(self)
 
893
        self.deck.setUndoStart(n)
 
894
        self.deck.suspendCards(self.selectedCards())
 
895
        self.deck.setUndoEnd(n)
 
896
        self.parent.setProgressParent(None)
 
897
        self.model.refresh()
 
898
 
 
899
    def _onUnsuspend(self):
 
900
        n = _("Unsuspend")
 
901
        self.parent.setProgressParent(self)
 
902
        self.deck.setUndoStart(n)
 
903
        self.deck.unsuspendCards(self.selectedCards())
 
904
        self.deck.setUndoEnd(n)
 
905
        self.parent.setProgressParent(None)
 
906
        self.model.refresh()
 
907
 
 
908
    def isMarked(self):
 
909
        return self.currentCard and "Marked" in self.currentCard.fact.tags
 
910
 
 
911
    def onMark(self, mark):
 
912
        if mark:
 
913
            self._onMark()
 
914
        else:
 
915
            self._onUnmark()
 
916
 
 
917
    def _onMark(self):
 
918
        self.addTags(tags="Marked", label=_("Toggle Mark"))
 
919
 
 
920
    def _onUnmark(self):
 
921
        self.deleteTags(tags="Marked", label=_("Toggle Mark"))
 
922
 
769
923
    def reschedule(self):
770
924
        n = _("Reschedule")
771
925
        d = QDialog(self)
779
933
                self.deck.resetCards(self.selectedCards())
780
934
            else:
781
935
                try:
782
 
                    min = float(str(frm.rangeMin.text()))
783
 
                    max = float(str(frm.rangeMax.text()))
 
936
                    min = float(frm.rangeMin.value())
 
937
                    max = float(frm.rangeMax.value())
784
938
                except ValueError:
785
939
                    ui.utils.showInfo(
786
 
                        _("Please enter a valid start and end range."),
 
940
                        _("Please enter a valid range."),
787
941
                        parent=self)
788
942
                    return
789
943
                self.deck.rescheduleCards(self.selectedCards(), min, max)
791
945
            self.deck.rebuildCounts(full=False)
792
946
            self.deck.rebuildQueue()
793
947
            self.deck.setUndoEnd(n)
794
 
        self.updateAfterCardChange(reset=True)
 
948
        self.updateAfterCardChange()
795
949
 
796
950
    def addCards(self):
797
951
        sf = self.selectedFacts()
819
973
        facts = self.deck.s.query(Fact).filter(
820
974
            text("id in %s" % ids2str(sf))).order_by(Fact.created).all()
821
975
        self.deck.updateProgress(_("Generating Cards..."))
 
976
        ids = []
822
977
        for c, fact in enumerate(facts):
823
 
            self.deck.addCards(fact, d.selectedCms)
 
978
            ids.extend(self.deck.addCards(fact, d.selectedCms))
824
979
            if c % 50 == 0:
825
980
                self.deck.updateProgress()
826
981
        self.deck.flushMod()
827
 
        self.deck.updateAllPriorities()
 
982
        self.deck.updatePriorities(ids)
828
983
        self.deck.finishProgress()
829
984
        self.parent.setProgressParent(None)
830
985
        self.deck.setUndoEnd(n)
866
1021
    ######################################################################
867
1022
 
868
1023
    def selectFacts(self):
 
1024
        self.deck.startProgress()
869
1025
        sm = self.dialog.tableView.selectionModel()
 
1026
        sm.blockSignals(True)
870
1027
        cardIds = dict([(x, 1) for x in self.selectedFactsAsCards()])
871
1028
        for i, card in enumerate(self.model.cards):
872
1029
            if card.id in cardIds:
873
1030
                sm.select(self.model.index(i, 0),
874
1031
                          QItemSelectionModel.Select | QItemSelectionModel.Rows)
875
 
 
 
1032
            if i % 100 == 0:
 
1033
                self.deck.updateProgress()
 
1034
        sm.blockSignals(False)
 
1035
        self.deck.finishProgress()
 
1036
        self.updateFilterLabel()
 
1037
        self.updateAfterCardChange()
876
1038
 
877
1039
    def invertSelection(self):
878
1040
        sm = self.dialog.tableView.selectionModel()
885
1047
        self.model.cards.reverse()
886
1048
        self.model.reset()
887
1049
        self.focusCurrentCard()
 
1050
        self.updateSortOrder()
888
1051
 
889
1052
    # Edit: undo/redo
890
1053
    ######################################################################
1026
1189
 
1027
1190
    def onFind(self):
1028
1191
        self.dialog.filterEdit.setFocus()
 
1192
        self.dialog.filterEdit.selectAll()
1029
1193
 
1030
1194
    def onFact(self):
1031
1195
        self.editor.focusFirst()
1032
1196
 
 
1197
    def onTags(self):
 
1198
        self.dialog.tagList.setFocus()
 
1199
 
 
1200
    def onSort(self):
 
1201
        self.dialog.sortBox.setFocus()
 
1202
 
 
1203
    def onCardList(self):
 
1204
        self.dialog.tableView.setFocus()
 
1205
 
1033
1206
    # Help
1034
1207
    ######################################################################
1035
1208
 
1090
1263
    def __init__(self, parent, oldModel, oldTemplate):
1091
1264
        QDialog.__init__(self, parent, Qt.Window)
1092
1265
        self.parent = parent
 
1266
        self.origModel = self.parent.deck.currentModel
1093
1267
        self.oldModel = oldModel
1094
1268
        self.oldTemplate = oldTemplate
1095
1269
        self.form = ankiqt.forms.changemodel.Ui_Dialog()
1209
1383
            combos=self.fieldCombos,
1210
1384
            new=self.targetModel.fieldModels)
1211
1385
 
 
1386
    def reject(self):
 
1387
        self.parent.deck.currentModel = self.origModel
 
1388
        self.modelChooser.deinit()
 
1389
        return QDialog.reject(self)
 
1390
 
1212
1391
    def accept(self):
1213
1392
        saveGeom(self, "changeModel")
 
1393
        self.parent.deck.currentModel = self.origModel
1214
1394
        # check maps
1215
1395
        fmap = self.getFieldMap()
1216
1396
        cmap = self.getTemplateMap()
1217
1397
        if not cmap or (self.targetModel != self.oldModel and
1218
1398
                        not fmap):
1219
 
            return ui.utils.showInfo(
 
1399
            ui.utils.showInfo(
1220
1400
                _("Targets must be unique."), parent=self)
 
1401
            return
 
1402
        if [c for c in cmap.values() if not c]:
 
1403
            if not ui.utils.askUser(_("""\
 
1404
Any cards with templates mapped to nothing will be deleted.
 
1405
If a fact has no remaining cards, it will be lost.
 
1406
Are you sure you want to continue?"""), parent=self):
 
1407
                return
 
1408
        self.modelChooser.deinit()
1221
1409
        if self.targetModel == self.oldModel:
1222
1410
            self.ret = (self.targetModel, None, cmap)
1223
1411
            return QDialog.accept(self)