1# Copyright: Ankitects Pty Ltd and contributors
2# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
3
4from aqt.qt import *
5import re
6
7class TagEdit(QLineEdit):
8
9    lostFocus = pyqtSignal()
10
11    # 0 = tags, 1 = decks
12    def __init__(self, parent, type=0):
13        QLineEdit.__init__(self, parent)
14        self.col = None
15        self.model = QStringListModel()
16        self.type = type
17        if type == 0:
18            self.completer = TagCompleter(self.model, parent, self)
19        else:
20            self.completer = QCompleter(self.model, parent)
21        self.completer.setCompletionMode(QCompleter.PopupCompletion)
22        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
23        self.setCompleter(self.completer)
24
25    def setCol(self, col):
26        "Set the current col, updating list of available tags."
27        self.col = col
28        if self.type == 0:
29            l = sorted(self.col.tags.all())
30        else:
31            l = sorted(self.col.decks.allNames())
32        self.model.setStringList(l)
33
34    def focusInEvent(self, evt):
35        QLineEdit.focusInEvent(self, evt)
36
37    def keyPressEvent(self, evt):
38        if evt.key() in (Qt.Key_Up, Qt.Key_Down):
39            # show completer on arrow key up/down
40            if not self.completer.popup().isVisible():
41                self.showCompleter()
42            return
43        if (evt.key() == Qt.Key_Tab and evt.modifiers() & Qt.ControlModifier):
44            # select next completion
45            if not self.completer.popup().isVisible():
46                self.showCompleter()
47            index = self.completer.currentIndex()
48            self.completer.popup().setCurrentIndex(index)
49            cur_row = index.row()
50            if not self.completer.setCurrentRow(cur_row + 1):
51                self.completer.setCurrentRow(0)
52            return
53        if evt.key() in (Qt.Key_Enter, Qt.Key_Return):
54            # apply first completion if no suggestion selected
55            selected_row = self.completer.popup().currentIndex().row()
56            if selected_row == -1:
57                self.completer.setCurrentRow(0)
58                index = self.completer.currentIndex()
59                self.completer.popup().setCurrentIndex(index)
60            self.hideCompleter()
61            QWidget.keyPressEvent(self, evt)
62            return
63        QLineEdit.keyPressEvent(self, evt)
64        if not evt.text():
65            # if it's a modifier, don't show
66            return
67        if evt.key() not in (
68            Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Space,
69            Qt.Key_Tab, Qt.Key_Backspace, Qt.Key_Delete):
70            self.showCompleter()
71
72    def showCompleter(self):
73        self.completer.setCompletionPrefix(self.text())
74        self.completer.complete()
75
76    def focusOutEvent(self, evt):
77        QLineEdit.focusOutEvent(self, evt)
78        self.lostFocus.emit()
79        self.completer.popup().hide()
80
81    def hideCompleter(self):
82        if sip.isdeleted(self.completer):
83            return
84        self.completer.popup().hide()
85
86class TagCompleter(QCompleter):
87
88    def __init__(self, model, parent, edit, *args):
89        QCompleter.__init__(self, model, parent)
90        self.tags = []
91        self.edit = edit
92        self.cursor = None
93
94    def splitPath(self, tags):
95        stripped_tags = tags.strip()
96        stripped_tags = re.sub("  +", " ", stripped_tags)
97        self.tags = self.edit.col.tags.split(stripped_tags)
98        self.tags.append("")
99        p = self.edit.cursorPosition()
100        if tags.endswith("  "):
101            self.cursor = len(self.tags) - 1
102        else:
103            self.cursor = stripped_tags.count(" ", 0, p)
104        return [self.tags[self.cursor]]
105
106    def pathFromIndex(self, idx):
107        if self.cursor is None:
108            return self.edit.text()
109        ret = QCompleter.pathFromIndex(self, idx)
110        self.tags[self.cursor] = ret
111        try:
112            self.tags.remove("")
113        except ValueError:
114            pass
115        return " ".join(self.tags) + " "