Thread Tools Display Modes
05-26-06, 03:16 AM   #1
Selice
A Defias Bandit
Join Date: May 2006
Posts: 3
How to read text lines out of a ScrollingMessageFrame?

Hi,

is there any way to read the stored text lines our of a ScrollingMessageFrame?
WoWWiki lists multiple methods of this frame, but none of them seems to be able to fetch text lines out of such a frame.
I intend to write some kind of search tool for searching text in ChatFrame1 to ChatFrame7.

I already asked this question in the official German UI forum, but the German UI forum has only limited value for programming questions
I regularily read the US UI-Forum for some more adequate information, but unfortunately, I can't post there as owner of an European WoW account.

So, does anywone here know a method to extract lines our of this frame or can give me a definite "It's not possible?". In the latter case, I think I'll hook AddMessage or use the CHAT_MSG_* events and buffer the chat data myself.

Thanks,
Selice
  Reply With Quote
05-26-06, 03:43 PM   #2
Esamynn
Featured Artist
Premium Member
Featured
Join Date: Jan 2005
Posts: 395
There is no way to extract lines that have already been added to a MessageFrame. The API simply doesn't support it. Your options are as you mentioned, either hook :AddMessage or monitor the chat events.

If you are looking to record only information that comes from events that are generated from the client, your best option is to monitor the relevent events.

If you want to store the exact contents of a MessageFrame then you have to hook :AddMessage

When hooking :AddMessage, you have two options:
1) If you only want to monitor select MessageFrames you can create an entry in the Lua table of each MessageFrame you want to monitor
2) If you want to cache the conents of all message frames, you probably want to hook :AddMessage at the metatable level.

If you are unsure as to how to proceed with the hooking process, post here and I'll help you through it.
  Reply With Quote
05-28-06, 04:38 AM   #3
Selice
A Defias Bandit
Join Date: May 2006
Posts: 3
Hi jbcc,

Thanks for your answer.

Since you state that reading lines out of the frame is impossible, the metatable-approach sounds interesting.
I have no experience with metattables. According to

http://www.lua.org/manual/5.0/manual.html#2.8

I sassume, that I have to hook or add the "__call" key of the metatable?

As far as I know, normal hooking of AddMessage in a chatframe would be:

Code:
OriginalMethod = ChatFrame1.Addmessage;
ChatFrame1.AddMessage = NewMethod;

function NewMethod(self, ...)
  DoSomethingToStoreData();
  OriginalMethod(self, unpack(arg));
end
I haven't tested this example, but to my understanding, it should work.

So how would I hook AddMessage by using metatables?

I already tested, that "getmetattable(ChatFrame1) == getmetatable(ChatFrame2)" is true, so it would indeed affect all chatframes at once.

Thanks,
Selice

Last edited by Selice : 05-28-06 at 05:05 AM.
  Reply With Quote
05-30-06, 12:16 PM   #4
Esamynn
Featured Artist
Premium Member
Featured
Join Date: Jan 2005
Posts: 395
First some background information:

Actually, the metatable key that we would be dealing with is the __index key. This metatable key allows you to provide additional values that, within certain conditions, appear to be in your table when really they keys for those values are nil in your table and the values are actually stored somewhere else.

Below is the sample code for what Lua does when you attempt to index a value. As the manual notes, this code is only a reflection of actual behaviour which is coded directly into the Lua interpreter and much more efficient than as it appears here.
Code:
function gettable_event (table, key)
   local h
   if type(table) == "table" then
      local v = rawget(table, key)
      if v ~= nil then return v end
      h = metatable(table).__index
      if h == nil then return nil end
   else
      h = metatable(table).__index
      if h == nil then
         error("...");
      end
   end
   if type(h) == "function" then
      return h(table, key)      -- call the handler
   else 
      return h[key]          -- or repeat operation on it
   end
end
I don't know how familiar you are with Lua, so if you are unfamiliar with the difference between the '.' operator and the ':' operator when referencing tables, please read this page before going further: http://www.lua.org/pil/16.html

Going back to Blizzard Lua and the interface. As I'm sure you know, all UI widget objects appear in the Lua environment in the form of a Lua table. Each of these tables appears to have a number of member functions the you can call, but if you loop through the values in a widget's table you will notice that the none widget's member functions are actally stored in the Lua table that represents it. The member funcions are retrieved through an __index metatable value that points to a function which returns the appropriate member function for the widget you are working with.

Note the following:

getmetatable(A) == getmetatable(B) for ALL widgets A, B

also,

getmetatable(A)["__index"] == getmetatable(B)["__index"] for ALL widgets A, B

All widgets use the same handler function for retrieving their member functions, regardless of their type. As it turns out, this handler function is extremely slow (relatively speaking) and Iriel has done some research in the past on speeding up the retrieval process, but that is another topic altogeather.

Now for the topic at hand:

If you were interested in storing the contents of all ScrollingMessageFrame objects (regardless of their purpose) you could in fact hook the handler function in the widget metatable to provide your own version of the :AddMessage function. However, you MUST be very careful when using this method as you are hooking a function that is used EVERYWHERE throughout the UI and if you do it badly you could cause a massive amount of extra overhead because each and every call to a widget function would incurr the additional cost of your hooked function.

If I am reading you right, your purpose is to store the history of each of the default ChatFrame objects in a format that is retrievable. If this is the case, and you don't have any real interest in any other MessageFrames that may exist in the UI, then I suggest that you hook the :AddMessage function by adding a "AddMessage" value to each of the default chat frames as you noted in your code above.

</Essay>


Originally Posted by Selice
Code:
OriginalMethod = ChatFrame1.Addmessage;
ChatFrame1.AddMessage = NewMethod;

function NewMethod(self, ...)
  DoSomethingToStoreData();
  OriginalMethod(self, unpack(arg));
end
About this code: Please be aware that the use of '...' creates a new table for each call of this function, and for a function that is called as often as :AddMessage you would be generating a MASSIVE amount of garbage that the GC would then need to clean up. The only way to avoid this is to specifically allow for each possible argument of the function you are hooking, which shouldn't be a problem in this case because the signature of :AddMessage isn't likely to change for the foreseeable future.
  Reply With Quote
06-10-06, 03:17 AM   #5
lje
A Kobold Labourer
Join Date: May 2006
Posts: 1
Guild raids prevented me from following this topic for the last two weeks, but now I'm back.

Originally Posted by jbcc
Note the following:

getmetatable(A) == getmetatable(B) for ALL widgets A, B

also,

getmetatable(A)["__index"] == getmetatable(B)["__index"] for ALL widgets A, B

All widgets use the same handler function for retrieving their member functions, regardless of their type. As it turns out, this handler function is extremely slow (relatively speaking) and Iriel has done some research in the past on speeding up the retrieval process, but that is another topic altogeather.
Ich just checked "getmetatable(ChatFrame1) == getmetatable(WorldFrame)" and this is indeed "true". This is kind of shocking to me.
I think because of this fact, I will hook AddMessage in ChatFrame1 to ChatFrame7 instead.

I found another reason, not to use the metatable approach:

Code:
function gettable_event (table, key)
   local h
   if type(table) == "table" then
      local v = rawget(table, key)
      if v ~= nil then return v end
     ...
This means, that metatable.__index will NOT be used, when a local AddMessage function exists in the frame.
A local AddMessage exists, as soon as any addon hooks it.

Addons usually use:

Old_AddMessage = Frame.AddMessage;
Frame.AddMessage = New_AddMessage_Of_Addon;

Ther first Addon that does this, will "fetch" the function deliverd by the metatabe to Old_Message, because at this point in time, there is no local AddMessage function in this frame.

Any subsequent AddOns, that hook AddMessage in this way, will fetch the AddMessage function of the previous AddOn in the hooking chain.

But now the problem: As soon as any AddOn hooked AddMessage, changes to the __index function of the metatable are irrelevant, since the first hooking AddOn now has a static reference to the original AddMessage function.

Is my conclusion correct, or am I missing something?

Kind regards,
Selice
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » How to read text lines out of a ScrollingMessageFrame?


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off