AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Class library (OOP) - Wish list / Bug reports thread
Goto page Previous  1, 2, 3, ... 16, 17, 18  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
bmcclure



Joined: 24 Nov 2007
Posts: 766

PostPosted: Wed Jan 28, 2009 4:22 pm    Post subject: Reply with quote

A container class sounds ideal from what you're saying. Will it still work, however, since I don't know which windows will be created or which controls will be added to which windows until the user actually does so?

Is it possible to reference a container in a container, so that all controls in the subcontainer are destroyed when the parent container is?

Is there any inheritance or does each class need every function defined that it will use? I'm assuming the latter at this point, but just wanted to make sure.

Trying to figure out the best way to implement the situation you were talking about. Would you recommend something like a Window class for the container, which then contains an object per window which stores references to each control on the window? I'm assuming I could then just keep track of which window object belongs to which physical window, and destroy that object upon window close.

Thanks for helping me work through the logistics of this; I'm starting to see why this class is pretty exciting and types of things are possible with it. I definitely think many of my libraries need to be turned into classes so that I can manage them as objects.
_________________
Ben

My Trac projects
My Wiki
[Broken] - My music
Back to top
View user's profile Send private message
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Thu Jan 29, 2009 1:52 am    Post subject: Reply with quote

For those that don't want to read this... I'm working on a website to "organize" all this info and make it more presentable.

bmcclure wrote:
Is it possible to reference a container in a container, so that all controls in the subcontainer are destroyed when the parent container is?

Provided I'm understanding the question, this is the default behavior. Whenever a container is destroyed, all objects inside are destroyed (via a dynamic call to %ClassName%_destroy(ClassObject), where ClassName is the class name for the object - set in the %ClassName%_new function (via %ClassName%_initClass) - see the rectangle class for more details. So, when you destroy the subcontainer, all objects in that subcontainer are destroyed. And when you destroy the "top-level" container, all sub-containers, and their sub-containers, ..., and their objects are destroyed automatically - all via dynamic function calls. So, make sure any classes you create have that a %ClassName%_destroy(ClassObject) function to destroy all built-in values, and calls %ClassName%_destroy_private(ClassObject), which the user creates to destroy any user-defined values (see the Rectangle class for detail).

bmcclure wrote:
Is there any inheritance or does each class need every function defined that it will use? I'm assuming the latter at this point, but just wanted to make sure.

Yes, right now, at least, there is no inheritance. However, such an idea is not outside the scope of possibilities. Such method calls will use dynamic function calls, and like normal inheritence, the class would, at creation, specify which class it inherits. However, I'm not sure if it's needed - please elaborate what you need though so I can get a better idea. When I think inheritance, I think of this kind of example. You have a shape object, that has a draw function. You then have other classes, circle, square, etc., that each have their own draw function. In shape, you could store either a shape, circle, square, or any other class that has inherits shape. Then, on this shape object, you would say, Shape_draw(myShape) and it would call the draw function for the object. This latter part CAN be added, but the former is unneeded. Since each object holds it's type, there will never be the need to store a circle in a shape object - since the circle would know it's a circle. If you would like to be able to call Shape_draw(myCircle) and it would call Circle_draw (provided myCircle is a Cirle object), I can add this. The only downside would be it's a dynamic call, but if you knew the object was a cirle, beforehand, you can always call Circle_draw instead. Of course, you would have to create the Circle_draw function. Now, if such a function didn't exist THERE IS a way to detect this (of sorts).

Code:
;myShape/Circle/Square would have a Shape/Circle/Square object
myShape := "Shape"
myCircle := "Circle"
mySquare := "Square"

x := 0
y := 0

;Returns "Shape"
MsgBox, % "Shape_draw returns: " . Shape_draw(myShape, X, Y)

;Returns "Circle"
MsgBox, % "Shape_draw returns: " . Shape_draw(myCircle, X, Y)

;Returns ""
MsgBox, % "Shape_draw returns: " . Shape_draw(mySquare, X, Y)

Shape_draw(Shape, arg1, arg2)
{
    ;here you would use ClassName := Class_getClass(Shape)
    ClassName := Shape
   

    ;if ClassName != ThisClass ("Shape in this case")
    ;prevents calling this function recursively (Forever!!!)
    ;crashed the code... rightfully so

    ;then, here would be a check:
    callFunction := ClassName != "Shape" && ClassInherits(Shape, "Shape")
   
    if (callFunction && ReturnValue := "?" . %ClassName%_draw(Shape, arg1, arg2))
        return subStr(ReturnValue, 2)

    MsgBox, % "Called Shape_draw"
    return "Shape"
}

Circle_draw(Shape, arg1, arg2)
{
    ;this function does not have to return a value
    ;Shape_draw would then just return instead of returning the value
   
    MsgBox, % "Called Circle_draw"
   
    return "Circle"
}

Square_draw(Shape, arg1, arg2)
{
    MsgBox, % "Called Square_draw"
   
    return
}

ClassInherits(ClassObject, ClassName)
{
    ;would see if ClassObject was a "superclass" of ClassName
    ;e.g. A Circle is a Shape, so if the Class object was a Cicle, and ClassName
    ;was "Shape" this function would return true
   
    ;if ClassObject was a Shape and ClassName was "Circle", then this function
    ;would return false, since a shape is not a Circle

    ;when you call Class_setClass in the _new function of your class, you would
    ;say Circle inherits Shape - in some form.  Then, this function would then
    ;know (by looking at your Circle object) that Cicrle inherits the Shape class functions
   
    ;this is just a test, so such "advanced" methods are not shown :(
    if (ClassObject = "Circle" and ClassName = "Shape"
        || ClassObject = "Square" and ClassName = "Shape")
        return true
       
    return false
   
}


Now, I'm going to see if I can't find a better way, because this method would require EVERY function to have those first few lines. Because later on (whenever) someone may want to extend your class, these functions would have to allow calling that class. Of course, this is only if you wanted your function to be inherited. You could be a stingy old man/woman and horde all your money and not let future generations inherit from your "fortune", but then you could leave it off. Like I said, I'll see what I can do, but there is no structure, yet, that will allow parsing args in a function call, so I don't believe there is a way to wrap this mess of a call like such, call(ThisFunction, args). Well..., there might be, but what would happend is the call method would parse the args, then have a big if branch (one for each arg count), and pass the args. However, even if this worked fine, I'm not sure if ByRef would work correctly. Because the values would be moved to another variable (a temp value), the address would change, and yeah, no, ByRef won't work. Using the above method, though, it will. So, yeah, it might be hard to remove it. The good is that the structure is a simple cut and paste. There will be a function to check inheritance, so that's good. There's also a function to get the className (for the dynamic call). Only call the function is the object inherits from the base-class. Also, if it didn't inherit, then the function should just return because calling Shape_draw passing a Car object wouldn't make much sense...

Now, the other way. If you had a Car_hasEngine function, and you had a Truck class (which) inherited from Car, you COULD NOT call Truck_hasEngine and expect it to call Car_hasEngine (if no Truck_hasEngine function existed). You could, however, create a Truck_hasEngine function that calls Car_hasEngine and returns its value. This would allow you to reuse the function without recreating it. Also, in this direction, it's not a dynamic function call, just a normal one. Also, you could call Car_hasEngine passing a Truck object (presuming Truck inherits Car) and it would work. Now, all this would add some calling overhead, but that's the cost of functionality.

So, depending what you want from inheritance, I can add the ability to call Shape_draw(myCircle) and it would call call (presuming my circle is a Circle object), Circle_draw. You would, of course, have to write a Cirle_draw function that did what you wanted. You would then need to (I'm work on the details), specify that Circle extends Shape. This would be done as part of the className value. Since this value is stored internally (and only has a single copy for all objects), it increases the base cost a little, but it doesn't cost any more for each new object. Also, I'm sure many could benefit from this, so I shall add it. Of course, if you knew it was a circle before hand, you could call cirle_draw instead (to prevent a need to call the function dynamically).

wrote:
Would you recommend something like a Window class for the container, which then contains an object per window which stores references to each control on the window?

I would recommend an array for the 99 guis (since you know there are 99 and that never changes). Also, this way, when the user interacts with gui1, you would be able to get that Gui object with this call, Array_get(Guis, 1) - where guis is your Array to hold the Guis. Then, you would have a Gui class that would hold whatever gui values you need, as well as a Vector, Cddl, or some other container (if you have ideas I could whip it up), to store the different controls. An array wouldn't work, because you don't know the number of controls on creation - or if you did, then array would work. Also, this way, when Gui1 is destroyed, you only need to call, Array_set(Guis, 1, 0) which will destroy the first gui - the last zero is the null pointer. You would add a gui by calling, Array_set(Guis, 1, Gui1), where Gui1 is the address returned from Gui_new or you could just call Array_set(Guis, 1, Gui_new(args)) if you didn't want to store the Gui address in a variable. Of course, you could always call Array_get(Guis, 1) to get the address for the first gui object. The address would be returned because Gui isn't a wrapper like String and Int (which instead "unwrap" their value and return it - call Array_getAddress to return the address for a wrapper class, which also returns the address for non-wrappers).

Now, some "genaral info".

The container classes accept any classes in them (and can contain multiple classes). Because the class objects store their class name internally, they are able to be destroyed by dynamic call - it calls %ClassName%_Destroy(ClassObject), so make sure that you have such a method defined. This function should do all cleanup for built-in values. If there are user-define values, this function must call %ClassName%_destroy_private, which must be written by the user of the class to clean-up their user-defined values. To clean up those values, the user only needs to call the setter functions with either "" (for strings) or "" or 0 for numerical/object data. The class name can be any combinations of lower-case letters, (a-z), upper-case letters (A-Z), digits (0-9), and the underscore ("_"). The only thing is that the class should only have "_Node" as a suffix if its a "node" class. Node classes should only be used in a container where each "node" in the container is it's own class. For example, Cddl_Node is a node of a Cddl class. A Cddl is returned from Cddl_new and a Cdll_Node is returned from Cddl_addBefore/after. When/if you write a container class, there's stuff to learn for it. I'm working on putting this on a site - so it's more organized and doesn't look so "complicated", because it really isn't - there's just a lot that you can do, and thus there's a lot for me to say.

None of the classes have a "search" feature yet, because I'm not sure how it needs to be implemented. How would you want to be able to find the control from the list of controls? I'm thinking something like a "valueOf" function that would be called dynamically for each object. For example, this valueOf function for your controls would return something to uniquely identify them, so when looking for them in a list of values, it returns the unique value. For menus/menu items, this would be the menu/menu item handle (separators wouldn't be unique), for controls this would be the variable given to the control, or some other value. Since the classes are still in their beginning stages this is the ideal time to add functions to help gain access to the values within a container - any suggestions you have are much welcome.

Disclaimer: Because AHK isn't an OOP language, some OOP stuff isn't possible, and others, while possible, might not be as "pretty" as some would like because it's a "hack".
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Thu Jan 29, 2009 7:27 am    Post subject: Reply with quote

I just thought about something concerning retrieving the "value" of an object (to use for a search). Here are my thoughts - I would love yal`s feedback.

I'm planning on adding searching to the containers to allow looking for a value - indexOf is the function name in java for this (at least for vectors). This would allow searching the container for the inputted "value". I'm thinking that a getValue method would be necessary. This would be another dynammic call, which I'm trying to advoid, but I'm still racking my brain on how to avoid a dynammic call. Actually, just thought of it. Use the "class name" variable (which by the end of all this will contain everything else in the world along with the class name). This variable is best for this because it only adds a little overhead, since the variable is only stored once and all the objects of that class only have the address to it. By storing the address (e.g. "b0", 1 - like the getters/setters use) and length for the class value to use, there is no dynammic call needs to be done. The downsize is that the "value" used must be one of the data values stored, which may be too limiting. Just some thoughts, I'll do whatever, just need some feedback.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Thu Jan 29, 2009 7:03 pm    Post subject: Reply with quote

There's a new revision, check the main page for additions. Also, I'm working on a website for all this info. It's not complete, but it still has some good information so check it out. If you run into a blank page or an error, please just use your browser's back button. I'm finishing up the pages, it just go mind-numbing. Also, then I'm going to start working on the Vector class, then finish up my menu wrapper, so be on the look out. After that, I'm going to take a break from this to get back to working on my bookmark manager that I'm going to sell. I'll still be keeping on eye on my mail so I'll get a message if there's a post, and I'll respond promply.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Fri Jan 30, 2009 3:49 pm    Post subject: Reply with quote

I've been putting some thought into it and I think that it might be easier to have all built-in and user-defined index offsets use this new scheme.

Currently, built-in values use "b" followed by the zero-based byte offset to set built-in values. Ex. "b0" for the first byte, "b1" for the second, "b2" for the third, "b3" for the fourth, and "b4" starting the next byte.

For user-defined values, you would enter 1 for the first value, 2 for the second, etc. These are in four byte multiples. I'm adding the ability to specify 1 for the first byte, "1a" for the second byte, "1b" for the third, "1c" for the fourth and 2 starting the second word (4 byte multiple). I'm thinking it might be best to use this same format for the built-in values. You would still need to have the leading "b" to specify that it is for built-in values. This way there is the same syntax for built-in and user-defined values.

Likewise, I'm thinking about having the built-in size and user-defined size be on the same scale as well. Right now, built-in sizes are in bytes and user-defined are in words (4 byte multiples). I'm thinking that there shouldn't be any need to have, for example, 6 bytes for a built-in value, so to use words for both. However, up to yal. It would be a small edit and since any code that uses these classes will call the wrapper functions, changing the internal class has no impact on any code built using the class (i.e. you don't have to modify any code you built using the classes).
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
Guest






PostPosted: Fri Jan 30, 2009 6:16 pm    Post subject: Reply with quote

Back to top
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Fri Jan 30, 2009 6:40 pm    Post subject: Reply with quote

I'm dense when it comes to stuff like that. I don't think yor became profound, but I believe it could be seen as abnormal, so just to clear it up... were you joking or is it abnormal... like I said, I'm dense when it comes to this stuff.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Fri Jan 30, 2009 10:41 pm    Post subject: Reply with quote

I'm starting to wonder if I'm talking to myself... Thanks guest, at least someone is listening.

Ok, I'm writing the Vector class, which is coming along fine. Already have it "done" except for a big question - how to implement "remove" abilities. Right now, there are four scenarios that I have.

1) Set a new value and destroy the old (Vector size doesn't change)
2) Set a new value and don't destroy the old (only decrease its lock count). Again, vector size doesn't change.
3) Remove previous value (and destroy it). Vector size decreases by one.
4) Remove previous value (but don't destroy it; only decrease its lock count). Again, vector size decreases by one.

Now, the dilema - naming. I'm trying to keep the same naming for all the classes. So, with that in mind, any ideas?

I currently have "set" for option 1. I belive when you set a value, you remove the old and get rid of it. For option 2, I'm thinking replace. For this option you only want to replace the value with the new value (not destroy it). For option 3, remove, and for option 4 discard.

I'm not good with naming things, so PLEASE I need some feedback. I'm trying to make this easy to use and consistent between the classes. I'm going to add Cdll, Node, Rectangle, and the new Vector class to the website probably all later today, so be on the look out. After that, I'm going to start working on my menu wrapper. Maybe by working on it I'll start getting better ideas of what to use for "search" abilities.

I'm thinking, since a dynamic function call is a must in order to add good search abilities to the classes, allow passing a function name to use. This would allow the same search to search for different values. For example, if we had a Vector of books, we might want to search by ISBN number or by title. By allow passing a function name, the same search function could be used for either. However, I really need yal's help with this - I'm not good with this aspect of programming - naming stuff, user friendly functions, etc. Guis are fairly easy, but how to name a function, not my thing. So, in order to make this OOP library useful for people, I really need some feedback Shocked
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
bmcclure



Joined: 24 Nov 2007
Posts: 766

PostPosted: Fri Jan 30, 2009 11:47 pm    Post subject: Reply with quote

I'm listening--closely! Smile

Still working on turning many of my SteamWin libs into classes with your assistance so far, and I'm sure I'm not finished with my questions yet!

You're doing a great job on this so far, and I eagerly await all of your updates. I'll let you know as I think of some suggestions, or run into points of frustration when using this in my projects.

Thanks!
_________________
Ben

My Trac projects
My Wiki
[Broken] - My music
Back to top
View user's profile Send private message
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Fri Jan 30, 2009 11:55 pm    Post subject: Reply with quote

K, then I'll just presume that what I say sounds good, unless I hear otherwise, and I'll update the code accordingly. Thank you for that. I'm not used to the idea of premusing that everything is good unless someone complains - I guess that's the life of a programmer.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
bmcclure



Joined: 24 Nov 2007
Posts: 766

PostPosted: Sat Jan 31, 2009 12:16 am    Post subject: Reply with quote

I will also try to be more proactive, since this is something I support and I would hate to think that you think this isn't appreciated Smile

So far the changes you've proposed sound good. Until I get a full handle on the library and test my classes, I probably won't be able to provide too much valuable input. I'm still trying to re-think my project in terms of objects and what I can do with them.

As I'm using this more and more, however, I'm sure I will have ideas and additional input on your ideas, as it will directly impact my projects Wink
_________________
Ben

My Trac projects
My Wiki
[Broken] - My music
Back to top
View user's profile Send private message
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Sat Jan 31, 2009 12:30 am    Post subject: Reply with quote

Thanks. No, I'm sure this is appreciated, it's just I'm sure not many know what can be done with this. That's why I'm trying to get the website up so that yal can see the syntax for all the commands that yal are "meant" to use (some are only meant to be used internally). Also, I'm taking the design slow to make sure that I don't add features that will be hard to change. Since I'm wrapping everything up so well, it makes it very easy to modify things without it impacting the end-user (beside needing to download the latest version of class.ahk and the classes they use).

Just a heads up. I found some syntax problems that I'm fixing so that the code behaves as expected. Also, the next release will use the aforementioned "indexing".

Use 1, 2, 3 for user-defined values (as before) for the 4-byte multiples. Add an "a", "b", or "c" suffix to access the 2nd, 3rd, and 4th byte (to allow "packing" several values - like two shorts, into a single word). So, the first byte would be 1, then 1a, 1b, 1c, and then 2 to start the next word. Built-in values will use the same scheme (so this part changed) and have a leading "b" to specify it as a built-in value. Like before, the offsets are based on the value returned from Class_getBuiltInSize, so remember to call Class_setBuiltInSize in your Class' new function to set the built-in size accordingly. Also, all classes will be updated using this new indexing scheme, but that doesn't have any impact on yal's code, since you should be accessing Class fields using the getters/setters.

I'll probably post the updated classes within the next few hours - cleaning up the code, organizing it, and making sure it's all updated and checked out. The Vector class will also be released at that time. Also, I think I can get the website caught up tonight (ish) to include the Rectangle example class, Vector, Cddl, and Node class member functions.

Until that time, stay safe - or something like that. (Study hard Question Question )

PS. Inheritance is something I'll get to shortly. I really want to release my menu wrapper and now that the Vector class is working, I'm going to finish that up. It's mostly done except for some testing and making sure it has the updates I gave in the Node class. Plus, the Menu and MenuItem class are directly adapted from the Node class so I think they will serve as a prime example of how to use this library.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Sat Jan 31, 2009 7:00 am    Post subject: Reply with quote

The newest revision are out. The new indexing system is used as well as Vectors make their debut. In addition, some much needed error checking is added. Now, the code verifies that the address is valid (not freed and that it is actually one used). The website will be updated tomorrow with all the functionality and some remarks. I'll upload examples soon, but I really want to get the menu wrapper finished, so I'll be working on that first.

Edit:
Since Lexikos says that checking a pointer's validity hampers the code, I'm going to remove it. I'll replace it with this fair warning. DO NOT try to refer to memory that has already been freed. Such actions will crash your code. Check the documentation (when it finally gets all added...) for details on whether an object is destroyed or not after a "remove" function has been used. You should always have a mental note where your objects appear (and when you remove them). If you only store them inside a container and don't manipulate them outside of one, then you are safe because once the container is destroyed, so are its contents.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Sun Feb 01, 2009 3:42 pm    Post subject: Reply with quote

The newest version is out. Removed HeapValidate under advisement of Lexikos, so thanks for that. Also, changed all occurances of setSize to setUserDefinedSize (when such a call sets the user-defined size). Finally, fixed a bug that would crash the code when setting an object to its previous value (if the lock count was one).

This will probably be the last "big update" for a while. I'm about to release my menu wrapper which makes use of this library, and I'll be updating the website shortly, but I'm still thinking about how to implement "search" features. I have an idea but still processing it, so that will be updated shortly (when I figure out exactly how I want to do it).

The website is now my focus - to add in all the functionality that the code now has. Of course, I'll be keeping a watch on this forum and posting new code as added.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Tue Feb 03, 2009 2:24 pm    Post subject: Reply with quote

I know this doesn't have to do directly with the Class library, per se, but since some showed interest I'll announce it. My menu wrapper (which makes use of the Class library) is now uploaded and can be discussed here. Each menu / menu item stores a Menu / MenuItem object to associate values with the menu / menu item. These values can be project specific, and there is an included example that demonstrates its functionality. Both the Menu and MenuItem Class are derived from the Node Class, only setting the built-in size to reflect the Class' built-in size. You can check them out for a "real-life" example of how to use this Class library for your own needs

I'm going to start working on updating the website to have it include all currentt functionality. Also, I'm going to update the tutorial, Extending the Node Class, to include the newly added features.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3, ... 16, 17, 18  Next
Page 2 of 18

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group