wobin@0
|
1 -- A Twitter client of sorts for World of Warcraft
|
wobin@0
|
2 -- Author: Wobin
|
wobin@0
|
3 -- Email: wobster@gmail.com
|
wobin@0
|
4 --
|
wobin@1
|
5 Squawk = LibStub("AceAddon-3.0"):NewAddon("Squawk")
|
wobin@0
|
6
|
wobin@0
|
7 Squawk.Model = {}
|
wobin@0
|
8 Squawk.View = {}
|
wobin@0
|
9 Squawk.Controller = {}
|
wobin@0
|
10
|
wobin@0
|
11 local Model = Squawk.Model
|
wobin@0
|
12 local View = Squawk.View
|
wobin@0
|
13 local Controller = Squawk.Controller
|
wobin@0
|
14
|
wobin@0
|
15 Model.UserSettings = {}
|
wobin@0
|
16 local Settings = Model.UserSettings
|
wobin@0
|
17
|
wobin@0
|
18 local defaults = {
|
wobin@0
|
19 profile = {
|
wobin@0
|
20 Squawks = {},
|
wobin@0
|
21 Follower = {},
|
wobin@0
|
22 Following = {},
|
wobin@7
|
23 Pending = {},
|
wobin@7
|
24 Requested = {},
|
wobin@0
|
25 Blocked = {},
|
wobin@0
|
26 }
|
wobin@0
|
27 }
|
wobin@0
|
28
|
wobin@0
|
29 function Squawk:OnInitialize()
|
wobin@0
|
30 Model.db = LibStub("AceDB-3.0"):New("SquawkDB", defaults)
|
wobin@4
|
31 Model.Squawks = Model.db.profile.Squawks
|
wobin@0
|
32 Settings.Follower = Model.db.profile.Follower
|
wobin@0
|
33 Settings.Following = Model.db.profile.Following
|
wobin@7
|
34 Settings.Pending = Model.db.profile.Pending
|
wobin@7
|
35 Settings.Requested = Model.db.profile.Requested
|
wobin@0
|
36 Settings.Blocked = Model.db.profile.Blocked
|
wobin@0
|
37 Settings.Private = Model.db.profile.Private
|
wobin@0
|
38
|
wobin@0
|
39 LibStub("AceComm-3.0"):Embed(Controller)
|
wobin@14
|
40 LibStub("AceTimer-3.0"):Embed(Controller)
|
wobin@3
|
41 Controller:RegisterComm("Squawk", Controller.ReceiveMessage)
|
wobin@14
|
42 LibStub("AceConsole-3.0"):Embed(View)
|
wobin@3
|
43
|
wobin@0
|
44 end
|
wobin@0
|
45
|
wobin@0
|
46 -- Model --
|
wobin@0
|
47 --[[
|
wobin@0
|
48 --Each Squawk will have the following information:
|
wobin@0
|
49 -- * Owner (Name)
|
wobin@0
|
50 -- * Time (Epoch)
|
wobin@0
|
51 -- * Message (140 characters)
|
wobin@0
|
52 -- * ReplyTo (Name)
|
wobin@0
|
53 -- * Related (Names)
|
wobin@0
|
54 --
|
wobin@0
|
55 -- Each User will have the following lists:
|
wobin@0
|
56 -- * Follower
|
wobin@0
|
57 -- * Following
|
wobin@0
|
58 -- * Blocked
|
wobin@7
|
59 -- * Pending (Requests to follow that you haven't acted on)
|
wobin@7
|
60 -- * Requested (Requests to follow that you have made)
|
wobin@0
|
61 -- * Privacy State
|
wobin@7
|
62 --
|
wobin@7
|
63 -- A user can only request to follow an online person. Requests can be approved
|
wobin@7
|
64 -- on or offline, but the initial request must be made online.
|
wobin@7
|
65 --
|
wobin@7
|
66 -- When a user makes a request to follow a private user, the subsequent paths occur:
|
wobin@7
|
67 -- - Followee is added to Settings.Requested
|
wobin@7
|
68 -- - Followee receives 'follow request' -> (their) Settings.Pending
|
wobin@7
|
69 -- - Followee acts on request -> (their) Settings.Pending cleared
|
wobin@7
|
70 -- 1) Follwer is online
|
wobin@7
|
71 -- - Follower receives 'request accepted' -> Added to Settings.Following and
|
wobin@7
|
72 -- cleared from Settings.Requested
|
wobin@7
|
73 -- 2) Follower is offline
|
wobin@7
|
74 -- - The next time Follower is online and recieves a Squawk we check if there
|
wobin@7
|
75 -- is a Settings.Requested for that name, and if so assume they have approved
|
wobin@7
|
76 -- and clear/add records appropriately.
|
wobin@13
|
77 --
|
wobin@13
|
78 -- For updating, there can be a few methods.
|
wobin@13
|
79 --
|
wobin@13
|
80 -- Guild open: you're not private, you broadcast to the guild your last X
|
wobin@13
|
81 -- squawks on login
|
wobin@13
|
82 --
|
wobin@13
|
83 -- followers:
|
wobin@0
|
84 --]]
|
wobin@0
|
85 Model.Squawk = {}
|
wobin@0
|
86 local Squawk = Model.Squawk
|
wobin@13
|
87 local function wrap(str, limit)
|
wobin@13
|
88 limit = limit or 72
|
wobin@13
|
89 local here = 1
|
wobin@13
|
90 return str:gsub("(%s+)()(%S+)()",
|
wobin@13
|
91 function(sp, st, word, fi)
|
wobin@13
|
92 if fi-here > limit then
|
wobin@13
|
93 here = st
|
wobin@13
|
94 return "\n"..word
|
wobin@13
|
95 end
|
wobin@13
|
96 end)
|
wobin@13
|
97 end
|
wobin@0
|
98 function Squawk:new(Message, Owner)
|
wobin@0
|
99 local o = {}
|
wobin@0
|
100 o.Owner = Owner or UnitName("player")
|
wobin@13
|
101 o.Message = wrap(Message)
|
wobin@10
|
102 o.Time = time()
|
wobin@0
|
103 local reply, to = strsplit("@", ((strsplit(" ", Message))))
|
wobin@0
|
104 if reply == "" then
|
wobin@0
|
105 o.ReplyTo = to
|
wobin@0
|
106 end
|
wobin@0
|
107
|
wobin@0
|
108 o.Related = {}
|
wobin@0
|
109
|
wobin@0
|
110 for word in string.gmatch(Message, "@(%a+)") do
|
wobin@0
|
111 if word ~= o.ReplyTo or "" then
|
wobin@0
|
112 table.insert(o.Related, word)
|
wobin@0
|
113 end
|
wobin@0
|
114 end
|
wobin@0
|
115
|
wobin@0
|
116 return o
|
wobin@0
|
117 end
|
wobin@0
|
118
|
wobin@0
|
119 function Squawk:Sort(Squawks)
|
wobin@14
|
120 table.sort(Squawks, function(a,b) return a.Time > b.Time end)
|
wobin@14
|
121 return Squawks
|
wobin@0
|
122 end
|
wobin@0
|
123
|
wobin@2
|
124 function Squawk:GetOwn(Squawks)
|
wobin@2
|
125 local mine = {}
|
wobin@2
|
126 for _, squawk in ipairs(Squawks) do
|
wobin@2
|
127 if squawk.Owner == UnitName("player") then
|
wobin@2
|
128 table.insert(mine, squawk)
|
wobin@2
|
129 end
|
wobin@2
|
130 end
|
wobin@2
|
131 return self:Sort(mine)
|
wobin@2
|
132 end
|
wobin@2
|
133
|
wobin@14
|
134 function Squawk:GetLast10(Squawks)
|
wobin@14
|
135 local mine = {}
|
wobin@14
|
136 local limit = #Squawks < 10 and #Squawks or 10
|
wobin@14
|
137
|
wobin@14
|
138 Squawks = Squawk:Sort(Squawks)
|
wobin@14
|
139
|
wobin@14
|
140 for i=1,limit do
|
wobin@14
|
141 table.insert(mine, Squawks[i])
|
wobin@14
|
142 end
|
wobin@14
|
143 return mine
|
wobin@14
|
144 end
|
wobin@14
|
145
|
wobin@0
|
146 function Settings:IsPrivate()
|
wobin@0
|
147 return Settings.Private
|
wobin@0
|
148 end
|
wobin@0
|
149
|
wobin@10
|
150 function Settings:TogglePrivate()
|
wobin@10
|
151 Settings.Private = not Settings.Private
|
wobin@10
|
152 end
|
wobin@10
|
153
|
wobin@0
|
154 function Settings:AddFollower(Name)
|
wobin@2
|
155 Settings.Follower[Name] = 1
|
wobin@7
|
156 self:RemovePending(Name)
|
wobin@0
|
157 end
|
wobin@0
|
158
|
wobin@0
|
159 function Settings:AddFollowing(Name)
|
wobin@2
|
160 Settings.Following[Name] = 1
|
wobin@7
|
161 self:RemoveRequested(Name)
|
wobin@0
|
162 end
|
wobin@0
|
163
|
wobin@0
|
164 function Settings:AddBlock(Name)
|
wobin@2
|
165 Settings.Blocked[Name] = 1
|
wobin@7
|
166 self:RemoveFollower(Name)
|
wobin@7
|
167 self:RemoveFollowing(Name)
|
wobin@7
|
168 end
|
wobin@7
|
169
|
wobin@7
|
170 function Settings:AddPending(Name)
|
wobin@7
|
171 Settings.Pending[Name] = 1
|
wobin@7
|
172 end
|
wobin@7
|
173
|
wobin@7
|
174 function Settings:AddRequested(Name)
|
wobin@7
|
175 Settings.Requested[Name] = 1
|
wobin@0
|
176 end
|
wobin@0
|
177
|
wobin@0
|
178 function Settings:RemoveFollower(Name)
|
wobin@2
|
179 if Settings.Follower[Name] then
|
wobin@2
|
180 Settings.Follower[Name] = nil
|
wobin@0
|
181 end
|
wobin@0
|
182 end
|
wobin@0
|
183
|
wobin@0
|
184 function Settings:RemoveFollowing(Name)
|
wobin@2
|
185 if Settings.Following[Name] then
|
wobin@2
|
186 Settings.Following[Name] = nil
|
wobin@0
|
187 end
|
wobin@0
|
188 end
|
wobin@0
|
189
|
wobin@0
|
190 function Settings:RemoveBlock(Name)
|
wobin@2
|
191 if Settings.Blocked[Name] then
|
wobin@2
|
192 Settings.Blocked[Name] = nil
|
wobin@0
|
193 end
|
wobin@0
|
194 end
|
wobin@0
|
195
|
wobin@7
|
196 function Settings:RemovePending(Name)
|
wobin@7
|
197 if Settings.Pending[Name] then
|
wobin@7
|
198 Settings.Pending[Name] = nil
|
wobin@7
|
199 end
|
wobin@7
|
200 end
|
wobin@7
|
201
|
wobin@7
|
202 function Settings:RemoveRequested(Name)
|
wobin@7
|
203 if Settings.Requested[Name] then
|
wobin@7
|
204 Settings.Requested[Name] = nil
|
wobin@7
|
205 end
|
wobin@7
|
206 end
|
wobin@7
|
207
|
wobin@0
|
208 --Controller--
|
wobin@0
|
209
|
wobin@0
|
210 function Controller:TheyWantToFollowMe(Name)
|
wobin@0
|
211 if Settings:IsPrivate() then
|
wobin@7
|
212 Settings:AddPending(Name)
|
wobin@0
|
213 self:PutForwardFollowRequest(Name)
|
wobin@0
|
214 self:SendMessageToTarget(Name, "#Pending|"..UnitName("player"))
|
wobin@0
|
215 else
|
wobin@0
|
216 Settings:AddFollower(Name)
|
wobin@0
|
217 View:NotifyOfNewFollower(Name)
|
wobin@0
|
218 self:SendMessageToTarget(Name, "#Follow|"..UnitName("player"))
|
wobin@0
|
219 end
|
wobin@0
|
220 end
|
wobin@0
|
221
|
wobin@7
|
222 function Controller:TheyWantToUnfollowMe(Name)
|
wobin@7
|
223 Settings:RemoveFollower(Name)
|
wobin@7
|
224 end
|
wobin@7
|
225
|
wobin@0
|
226 function Controller:IWantToFollowThem(Name)
|
wobin@2
|
227 self:SendMessageToTarget(Name, "#Request|"..UnitName("player"))
|
wobin@7
|
228 Settings:AddRequested(Name)
|
wobin@7
|
229 end
|
wobin@7
|
230
|
wobin@7
|
231 function Controller:IWantToUnfollowThem(Name)
|
wobin@7
|
232 Settings:RemoveFollowing(Name)
|
wobin@7
|
233 self:SendMessageToTarget(Name, "#Unfollow|"..UnitName("player"))
|
wobin@7
|
234 View:NotifyOfUnfollowing(Name)
|
wobin@0
|
235 end
|
wobin@0
|
236
|
wobin@0
|
237 function Controller:IAmNowFollowingThem(Name)
|
wobin@0
|
238 Settings:AddFollowing(Name)
|
wobin@0
|
239 View:NotifyOfNewFollowing(Name)
|
wobin@0
|
240 end
|
wobin@0
|
241
|
wobin@7
|
242 function Controller:AddANewSquawk(Name, Message, Source)
|
wobin@10
|
243 if not Settings.Blocked[Name] then
|
wobin@7
|
244
|
wobin@7
|
245 if Source == "WHISPER" then
|
wobin@10
|
246 if Settings.Requested[Name] then -- We've been approved offline!
|
wobin@10
|
247 Settings:AddFollowing(Name)
|
wobin@7
|
248 end
|
wobin@7
|
249
|
wobin@10
|
250 if not Settings.Following[Name] then -- If we're no longer following this person
|
wobin@7
|
251 self:SendMessageToTarget(Name, "#Unfollow|"..UnitName("player"))
|
wobin@9
|
252 return
|
wobin@9
|
253 end
|
wobin@10
|
254 end
|
wobin@10
|
255
|
wobin@10
|
256 if Source == "GUILD" and Name == UnitName("player") then
|
wobin@10
|
257 return
|
wobin@7
|
258 end
|
wobin@10
|
259
|
wobin@10
|
260 table.insert(Model.Squawks, Squawk:new(Message, Name))
|
wobin@10
|
261 View:UpdateSquawkList()
|
wobin@7
|
262 end
|
wobin@0
|
263 end
|
wobin@0
|
264
|
wobin@14
|
265 local trigger
|
wobin@14
|
266 local function RepressFailure(frame, event, ...)
|
wobin@14
|
267 if arg1:match(string.gsub(ERR_CHAT_PLAYER_NOT_FOUND_S, "%%s", "(.*)")) then
|
wobin@14
|
268 if trigger then Controller:CancelTimer(trigger, true) end
|
wobin@14
|
269 trigger = Controller:ScheduleTimer(
|
wobin@14
|
270 function()
|
wobin@14
|
271 ChatFrame_RemoveMessageEventFilter("CHAT_MSG_SYSTEM", RepressFailure)
|
wobin@14
|
272 end, 3) -- Give it three seconds and then remove the filter.
|
wobin@14
|
273 return true
|
wobin@14
|
274 else
|
wobin@14
|
275 return false, unpack(...)
|
wobin@14
|
276 end
|
wobin@14
|
277 end
|
wobin@14
|
278
|
wobin@2
|
279 function Controller:SendNewSquawk(Message)
|
wobin@10
|
280 if not Settings:IsPrivate() then
|
wobin@2
|
281 self:SendMessageToGuild("#Squawk|"..UnitName("player").."|"..Message)
|
wobin@2
|
282 end
|
wobin@2
|
283
|
wobin@2
|
284 self:AddANewSquawk(UnitName("player"), Message)
|
wobin@10
|
285 for name, _ in pairs(Settings.Following) do
|
wobin@2
|
286 self:SendMessageToTarget(name, "#Squawk|"..UnitName("player").."|"..Message)
|
wobin@2
|
287 end
|
wobin@2
|
288 end
|
wobin@2
|
289
|
wobin@1
|
290 function Controller:ImPending(Name)
|
wobin@1
|
291 View:NotifyOfPending(Name)
|
wobin@1
|
292 end
|
wobin@1
|
293
|
wobin@7
|
294 function Controller:PutForwardFollowRequest(Name)
|
wobin@7
|
295 View:NotifyOfPendingRequest(Name)
|
wobin@7
|
296 end
|
wobin@7
|
297
|
wobin@10
|
298 function Controller:ApprovePendingRequest(Name)
|
wobin@10
|
299 Settings:AddFollower(Name)
|
wobin@10
|
300 View:NotifyOfNewFollower(Name)
|
wobin@10
|
301 self:SendMessageToTarget(Name, "#Follow|"..UnitName("player"))
|
wobin@10
|
302 end
|
wobin@10
|
303
|
wobin@14
|
304
|
wobin@14
|
305
|
wobin@0
|
306 function Controller:SendMessageToTarget(Name, Message)
|
wobin@14
|
307 ChatFrame_AddMessageEventFilter("CHAT_MSG_SYSTEM", RepressFailure)
|
wobin@10
|
308 self:SendCommMessage("Squawk", Message, "WHISPER", Name)
|
wobin@0
|
309 end
|
wobin@0
|
310
|
wobin@0
|
311 function Controller:SendMessageToGuild(Message)
|
wobin@0
|
312 self:SendCommMessage("Squawk", Message, "GUILD")
|
wobin@0
|
313 end
|
wobin@0
|
314
|
wobin@0
|
315 local Parse = {
|
wobin@1
|
316 ["#Pending"] = Controller.ImPending,
|
wobin@1
|
317 ["#Follow"] = Controller.IAmNowFollowingThem,
|
wobin@7
|
318 ["#Unfollow"] = Controller.TheyWantToUnfollowMe,
|
wobin@2
|
319 ["#Squawk"] = Controller.AddANewSquawk,
|
wobin@2
|
320 ["#Request"] = Controller.TheyWantToFollowMe,
|
wobin@0
|
321 }
|
wobin@0
|
322
|
wobin@10
|
323 function Controller:ReceiveMessage(Message, Distribution, Sender)
|
wobin@0
|
324 local command, name, info = strsplit("|",Message)
|
wobin@10
|
325 View:Print(Distribution..":"..Message)
|
wobin@10
|
326 Parse[command](Controller, name, info, Distribution)
|
wobin@0
|
327 end
|
wobin@14
|
328
|
wobin@0
|
329 -- View --
|
wobin@0
|
330
|
wobin@0
|
331 function View:UpdateSquawkList()
|
wobin@3
|
332 self:Print("Updated Squawk List")
|
wobin@4
|
333 self:ShowMeMySquawks()
|
wobin@0
|
334 end
|
wobin@0
|
335
|
wobin@3
|
336 function View:NotifyOfPending(Name)
|
wobin@3
|
337 self:Print(Name.." will have to approve your request")
|
wobin@0
|
338 end
|
wobin@0
|
339
|
wobin@7
|
340 function View:NotifyOfPendingRequest(Name)
|
wobin@7
|
341 self:Print(Name.." wants to follow you.")
|
wobin@7
|
342 end
|
wobin@7
|
343
|
wobin@0
|
344 function View:NotifyOfNewFollowing(Name)
|
wobin@3
|
345 self:Print("You are now following "..Name)
|
wobin@0
|
346 end
|
wobin@0
|
347
|
wobin@7
|
348 function View:NotifyOfUnfollowing(Name)
|
wobin@7
|
349 self:Print("You are no longer following "..Name)
|
wobin@7
|
350 end
|
wobin@7
|
351
|
wobin@0
|
352 function View:NotifyOfNewFollower(Name)
|
wobin@3
|
353 self:Print(Name.." is now following you")
|
wobin@0
|
354 end
|
wobin@4
|
355
|
wobin@4
|
356 function View:ShowMeMySquawks()
|
wobin@5
|
357 for _,squawk in ipairs(Model.Squawks) do
|
wobin@4
|
358 self:Print(squawk.Message)
|
wobin@4
|
359 end
|
wobin@4
|
360 end
|
wobin@4
|
361
|
wobin@4
|
362 function View:ShowMeMyFollowers()
|
wobin@4
|
363 self:Print("My followers are:")
|
wobin@4
|
364 for name,_ in pairs(Settings.Follower) do
|
wobin@4
|
365 self:Print(name)
|
wobin@4
|
366 end
|
wobin@4
|
367 end
|
wobin@4
|
368
|
wobin@4
|
369 function View:ShowMeWhoImFollowing()
|
wobin@4
|
370 self:Print("I am following:")
|
wobin@4
|
371 for name,_ in pairs(Settings.Following) do
|
wobin@4
|
372 self:Print(name)
|
wobin@4
|
373 end
|
wobin@4
|
374 end
|
wobin@4
|
375
|
wobin@4
|
376 function View:ShowMeWhoIveBlocked()
|
wobin@4
|
377 self:Print("I've blocked:")
|
wobin@6
|
378 for name,_ in pairs(Settings.Blocked) do
|
wobin@4
|
379 self:Print(name)
|
wobin@4
|
380 end
|
wobin@4
|
381 end
|
wobin@10
|
382
|
wobin@13
|
383 local TimeSpan = { [1] = {"second", 60, 1},
|
wobin@13
|
384 [2] = {"minute", 3600, 60},
|
wobin@13
|
385 [3] = {"hour", 86400, 3600} }
|
wobin@13
|
386
|
wobin@13
|
387 function View:GetTime(stime)
|
wobin@13
|
388 local lapsed = difftime(time(), stime)
|
wobin@13
|
389 if lapsed < 86400 then -- if we're still in the same day...
|
wobin@13
|
390 for _,span in ipairs(TimeSpan) do
|
wobin@13
|
391 if lapsed < span[2] then
|
wobin@13
|
392 local timespan = math.floor(lapsed/span[3])
|
wobin@13
|
393 if timespan == 1 then
|
wobin@13
|
394 timespan = timespan .." ".. span[1]
|
wobin@13
|
395 else
|
wobin@13
|
396 timespan = timespan .. " ".. span[1].."s"
|
wobin@13
|
397 end
|
wobin@13
|
398 return timespan.. " ago"
|
wobin@13
|
399 end
|
wobin@13
|
400 end
|
wobin@13
|
401 end
|
wobin@13
|
402 return date("%I:%M %p %b %d", stime)
|
wobin@13
|
403 end
|
wobin@13
|
404
|
wobin@10
|
405 local LDBFeed = LibStub("LibDataBroker-1.1"):NewDataObject("Squawk", {type = "data source", text = "Awk!"})
|
wobin@10
|
406 local QTip = LibStub("LibQTip-1.0")
|
wobin@10
|
407
|
wobin@13
|
408 local function AddLine(tooltip, Line, Number, Owner, TimeStamp)
|
wobin@13
|
409 if #Line < 79 then
|
wobin@13
|
410 tooltip:AddLine(Number, Owner, Line, TimeStamp)
|
wobin@13
|
411 else
|
wobin@13
|
412 tooltip:AddLine(Number, Owner, Line:sub(1, 80).."-", TimeStamp)
|
wobin@13
|
413 AddLine(tooltip, Line:sub(81))
|
wobin@13
|
414 end
|
wobin@13
|
415 end
|
wobin@13
|
416
|
wobin@10
|
417 function LDBFeed:OnEnter()
|
wobin@13
|
418 local tooltip = QTip:Acquire("Squawk",4, "LEFT", "LEFT", "LEFT", "RIGHT")
|
wobin@10
|
419 self.tooltip = tooltip
|
wobin@13
|
420 tooltip:AddHeader('Squawk')
|
wobin@14
|
421 for i,squawk in ipairs(Squawk:GetLast10(Model.Squawks)) do
|
wobin@13
|
422 if #squawk.Message > 79 then
|
wobin@13
|
423 local head = true
|
wobin@13
|
424 local message = {strsplit("\n",squawk.Message)}
|
wobin@13
|
425 for _,line in ipairs(message) do
|
wobin@13
|
426 if head then
|
wobin@13
|
427 AddLine(tooltip, line, i..".", squawk.Owner, View:GetTime(squawk.Time))
|
wobin@13
|
428 head = false
|
wobin@13
|
429 else
|
wobin@13
|
430 AddLine(tooltip, line)
|
wobin@13
|
431 end
|
wobin@13
|
432 end
|
wobin@13
|
433 else
|
wobin@13
|
434 tooltip:AddLine(i..".", squawk.Owner, squawk.Message, View:GetTime(squawk.Time))
|
wobin@13
|
435 end
|
wobin@13
|
436 end
|
wobin@10
|
437 tooltip:SmartAnchorTo(self)
|
wobin@10
|
438 tooltip:Show()
|
wobin@10
|
439 end
|
wobin@10
|
440
|
wobin@10
|
441 function LDBFeed:OnTooltipShow()
|
wobin@10
|
442 end
|
wobin@10
|
443
|
wobin@10
|
444 function LDBFeed:OnLeave()
|
wobin@10
|
445 QTip:Release(self.tooltip)
|
wobin@10
|
446 end
|