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) + " "