vanilla-wow-addons – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | --[[ |
2 | Name: AceTab-2.0 |
||
3 | Revision: $Rev: 16217 $ |
||
4 | Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) |
||
5 | Website: http://www.wowace.com/ |
||
6 | Documentation: http://www..wowace.com/index.php/AceTab-2.0 |
||
7 | SVN: http://svn.wowace.com/root/trunk/Ace2/AceTab-2.0 |
||
8 | Description: A tab-completion library |
||
9 | Dependencies: AceLibrary, AceEvent-2.0 |
||
10 | ]] |
||
11 | |||
12 | local MAJOR_VERSION = "AceTab-2.0" |
||
13 | local MINOR_VERSION = "$Revision: 16217 $" |
||
14 | |||
15 | if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end |
||
16 | if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end |
||
17 | |||
18 | local AceEvent |
||
19 | local AceTab = {} |
||
20 | local _G = getfenv() |
||
21 | |||
22 | local hookedFrames = {} |
||
23 | local framesHooked = {} |
||
24 | |||
25 | function AceTab:RegisterTabCompletion(descriptor, regex, wlfunc, usage, editframes) |
||
26 | self:argCheck(descriptor, 2, "string") |
||
27 | self:argCheck(regex, 3, "string", "table") |
||
28 | self:argCheck(wlfunc, 4, "string", "function", "nil") |
||
29 | self:argCheck(usage, 5, "string", "function", "boolean", "nil") |
||
30 | self:argCheck(editframe, 6, "string", "table", "nil") |
||
31 | |||
32 | if type(regex) == "string" then regex = {regex} end |
||
33 | |||
34 | if type(wlfunc) == "string" and type(self[wlfunc]) ~= "function" then |
||
35 | self:error("Cannot register function %q; it does not exist", wlfunc) |
||
36 | end |
||
37 | |||
38 | if type(usage) == "string" and type(self[usage]) ~= "function" then |
||
39 | self:error("Cannot register usage function %q; it does not exist", usage) |
||
40 | end |
||
41 | |||
42 | if not editframes then editframes = {"ChatFrameEditBox"} end |
||
43 | |||
44 | if type(editframes) == "table" and editframes.Show then editframes = {editframes:GetName()} end |
||
45 | |||
46 | for _, frame in pairs(editframes) do |
||
47 | local Gframe |
||
48 | if type(frame) == "table" then |
||
49 | Gframe = frame |
||
50 | frame = frame:GetName() |
||
51 | else |
||
52 | Gframe = _G[frame] |
||
53 | end |
||
54 | |||
55 | if type(Gframe) ~= "table" or not Gframe.Show then |
||
56 | self:error("Cannot register frame %q; it does not exist", frame) |
||
57 | frame = nil |
||
58 | end |
||
59 | |||
60 | if frame then |
||
61 | if Gframe:GetFrameType() ~= "EditBox" then |
||
62 | self:error("Cannot register frame %q; it is not an EditBox", frame) |
||
63 | frame = nil |
||
64 | else |
||
65 | if AceEvent and AceEvent:IsFullyInitialized() then |
||
66 | if not framesHooked[Gframe] then |
||
67 | framesHooked[Gframe] = true |
||
68 | local orig = Gframe:GetScript("OnTabPressed") |
||
69 | if type(orig) ~= "function" then |
||
70 | orig = function() end |
||
71 | end |
||
72 | Gframe:SetScript("OnTabPressed", function() |
||
73 | if self:OnTabPressed(Gframe) then |
||
74 | return orig() |
||
75 | end |
||
76 | end) |
||
77 | Gframe.curMatch = 0 |
||
78 | Gframe.matches = {} |
||
79 | Gframe.pMatchLen = 0 |
||
80 | end |
||
81 | else |
||
82 | hookedFrames[frame] = true |
||
83 | end |
||
84 | end |
||
85 | end |
||
86 | end |
||
87 | |||
88 | if not self.registry[descriptor] then |
||
89 | self.registry[descriptor] = {} |
||
90 | end |
||
91 | |||
92 | if not self.registry[descriptor][self] then |
||
93 | self.registry[descriptor][self] = {} |
||
94 | end |
||
95 | self.registry[descriptor][self] = {patterns = regex, wlfunc = wlfunc, usage = usage, frames = editframes} |
||
96 | |||
97 | |||
98 | if not AceEvent and AceLibrary:HasInstance("AceEvent-2.0") then |
||
99 | external(AceTab, "AceEvent-2.0", AceLibrary("AceEvent-2.0")) |
||
100 | end |
||
101 | if AceEvent then |
||
102 | if not self:IsEventRegistered("AceEvent_FullyInitialized") then |
||
103 | self:RegisterEvent("AceEvent_FullyInitialized", "AceEvent_FullyInitialized", true) |
||
104 | end |
||
105 | end |
||
106 | end |
||
107 | |||
108 | function AceTab:IsTabCompletionRegistered(descriptor) |
||
109 | self:argCheck(descriptor, 2, "string") |
||
110 | return self.registry[descriptor] and self.registry[descriptor][self] |
||
111 | end |
||
112 | |||
113 | function AceTab:UnregisterTabCompletion(descriptor) |
||
114 | self:argCheck(descriptor, 2, "string") |
||
115 | if self.registry[descriptor] and self.registry[descriptor][self] then |
||
116 | self.registry[descriptor][self] = nil |
||
117 | else |
||
118 | self:error("Cannot unregister a tab completion (%s) that you have not registered.", descriptor) |
||
119 | end |
||
120 | end |
||
121 | |||
122 | local GCS |
||
123 | GCS = function(s1, s2) |
||
124 | if not s1 and not s2 then return end |
||
125 | if not s1 then s1 = s2 end |
||
126 | if not s2 then s2 = s1 end |
||
127 | local s1len, s2len = string.len(s1), string.len(s2) |
||
128 | if s2len < s1len then |
||
129 | s1, s2 = s2, s1 |
||
130 | end |
||
131 | if string.find(string.lower(s2), string.lower(s1)) then |
||
132 | return s1 |
||
133 | else |
||
134 | return GCS(string.sub(s1, 1, -2), s2) |
||
135 | end |
||
136 | end |
||
137 | local pos |
||
138 | local function CycleTab() |
||
139 | this.pMatchLen = string.len(this.lMatch) |
||
140 | local cMatch = 0 |
||
141 | local matched = false |
||
142 | for desc, mList in pairs(this.matches) do |
||
143 | if not matched then |
||
144 | for _, m in ipairs(mList) do |
||
145 | cMatch = cMatch + 1 |
||
146 | if cMatch == this.curMatch then |
||
147 | this.lMatch = m |
||
148 | this.curMatch = this.curMatch + 1 |
||
149 | matched = true |
||
150 | break |
||
151 | end |
||
152 | end |
||
153 | end |
||
154 | end |
||
155 | if not matched then |
||
156 | this.curMatch = 1 |
||
157 | this.lMatch = this.origWord |
||
158 | end |
||
159 | this:HighlightText(pos - this.pMatchLen, pos) |
||
160 | this:Insert(this.lMatch) |
||
161 | end |
||
162 | |||
163 | function AceTab:OnTabPressed() |
||
164 | local ost = this:GetScript("OnTextSet") |
||
165 | if type(ost) ~= "function" then |
||
166 | ost = nil |
||
167 | end |
||
168 | if ost then this:SetScript("OnTextSet", nil) end |
||
169 | if this:GetText() == "" then return true end |
||
170 | this:Insert("\255") |
||
171 | pos = string.find(this:GetText(), "\255", 1) - 1 |
||
172 | this:HighlightText(pos, pos+1) |
||
173 | this:Insert("\0") |
||
174 | if ost then this:SetScript("OnTextSet", ost) end |
||
175 | local fulltext = this:GetText() |
||
176 | local text = string.sub(fulltext, 0, pos) or "" |
||
177 | |||
178 | local left = string.find(string.sub(text, 1, pos), "%w+$") |
||
179 | left = left and left-1 or pos |
||
180 | if not left or left == 1 and string.sub(text, 1, 1) == "/" then return true end |
||
181 | |||
182 | local _, _, word = string.find(string.sub(text, left, pos), "(%w+)") |
||
183 | word = word or "" |
||
184 | this.lMatch = this.curMatch > 0 and (this.lMatch or this.origWord) |
||
185 | |||
186 | if this.lMatch and this.lMatch ~= "" and string.find(string.sub(text, 1, pos), this.lMatch.."$") then |
||
187 | return CycleTab() |
||
188 | else |
||
189 | this.matches = {} |
||
190 | this.curMatch = 0 |
||
191 | this.lMatch = nil |
||
192 | end |
||
193 | |||
194 | local completions = {} |
||
195 | local numMatches = 0 |
||
196 | local firstMatch, hasNonFallback |
||
197 | |||
198 | for desc, entry in pairs(AceTab.registry) do |
||
199 | for _, s in pairs(entry) do |
||
200 | for _, f in pairs(s.frames) do |
||
201 | if _G[f] == this then |
||
202 | for _, regex in ipairs(s.patterns) do |
||
203 | local cands = {} |
||
204 | if string.find(string.sub(text, 1, left), regex.."$") then |
||
205 | local c = s.wlfunc(cands, fulltext, left) |
||
206 | if c ~= false then |
||
207 | local mtemp = {} |
||
208 | this.matches[desc] = this.matches[desc] or {} |
||
209 | for _, cand in ipairs(cands) do |
||
210 | if string.find(string.lower(cand), string.lower(word), 1, 1) == 1 then |
||
211 | mtemp[cand] = true |
||
212 | numMatches = numMatches + 1 |
||
213 | if numMatches == 1 then firstMatch = cand end |
||
214 | end |
||
215 | end |
||
216 | for i in pairs(mtemp) do |
||
217 | table.insert(this.matches[desc], i) |
||
218 | end |
||
219 | this.matches[desc].usage = s.usage |
||
220 | if regex ~= "" and this.matches[desc][1] then |
||
221 | hasNonFallback = true |
||
222 | this.matches[desc].notFallback = true |
||
223 | end |
||
224 | end |
||
225 | end |
||
226 | end |
||
227 | end |
||
228 | end |
||
229 | end |
||
230 | end |
||
231 | |||
232 | local _, set = next(this.matches) |
||
233 | if not set or numMatches == 0 and not hasNonFallback then return true end |
||
234 | |||
235 | this:HighlightText(left, left + string.len(word)) |
||
236 | if numMatches == 1 then |
||
237 | this:Insert(firstMatch) |
||
238 | this:Insert(" ") |
||
239 | else |
||
240 | if this.curMatch == 0 then |
||
241 | this.curMatch = 1 |
||
242 | this.origWord = word |
||
243 | this.lMatch = word |
||
244 | CycleTab() |
||
245 | end |
||
246 | local gcs |
||
247 | for h, c in pairs(this.matches) do |
||
248 | if hasNonFallback and not c.notFallback then break end |
||
249 | local u = c.usage |
||
250 | c.usage = nil |
||
251 | local candUsage = u and {} |
||
252 | local gcs2 |
||
253 | if next(c) then |
||
254 | if not u then DEFAULT_CHAT_FRAME:AddMessage(h..":") end |
||
255 | for _, m in ipairs(c) do |
||
256 | if not u then DEFAULT_CHAT_FRAME:AddMessage(m) end |
||
257 | gcs2 = GCS(gcs2, m) |
||
258 | end |
||
259 | end |
||
260 | gcs = GCS(gcs, gcs2) |
||
261 | if u then |
||
262 | if type(u) == "function" then |
||
263 | local us = u(candUsage, c, gcs2, string.sub(text, 1, left)) |
||
264 | if candUsage and next(candUsage) then us = candUsage end |
||
265 | if type(us) == "string" then |
||
266 | DEFAULT_CHAT_FRAME:AddMessage(us) |
||
267 | elseif type(us) == "table" and numMatches > 0 then |
||
268 | for _, v in ipairs(c) do |
||
269 | if us[v] then DEFAULT_CHAT_FRAME:AddMessage(string.format("%s - %s", v, us[v])) end |
||
270 | end |
||
271 | end |
||
272 | end |
||
273 | end |
||
274 | end |
||
275 | if curMatch == 0 then |
||
276 | this:Insert(gcs or word) |
||
277 | end |
||
278 | end |
||
279 | end |
||
280 | |||
281 | function AceTab:AceEvent_FullyInitialized() |
||
282 | for frame in pairs(hookedFrames) do |
||
283 | local Gframe = _G[frame] |
||
284 | if not framesHooked[Gframe] then |
||
285 | framesHooked[Gframe] = true |
||
286 | local orig = Gframe:GetScript("OnTabPressed") |
||
287 | if type(orig) ~= "function" then |
||
288 | orig = function() end |
||
289 | end |
||
290 | Gframe:SetScript("OnTabPressed", function() |
||
291 | if self:OnTabPressed(Gframe) then |
||
292 | return orig() |
||
293 | end |
||
294 | end) |
||
295 | Gframe.curMatch = 0 |
||
296 | Gframe.matches = {} |
||
297 | Gframe.pMatchLen = 0 |
||
298 | end |
||
299 | end |
||
300 | end |
||
301 | |||
302 | local function external(self, major, instance) |
||
303 | if major == "AceEvent-2.0" then |
||
304 | if not AceEvent then |
||
305 | AceEvent = instance |
||
306 | |||
307 | AceEvent:embed(self) |
||
308 | end |
||
309 | end |
||
310 | end |
||
311 | |||
312 | local function activate(self, oldLib, oldDeactivate) |
||
313 | if oldLib then |
||
314 | self.registry = oldLib.registry |
||
315 | end |
||
316 | |||
317 | if not self.registry then |
||
318 | self.registry = {} |
||
319 | end |
||
320 | |||
321 | if oldDeactivate then |
||
322 | oldDeactivate(oldLib) |
||
323 | end |
||
324 | end |
||
325 | |||
326 | AceLibrary:Register(AceTab, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) |
||
327 | AceTab = AceLibrary(MAJOR_VERSION) |