diff options
Diffstat (limited to 'src/kvirc/kvs/kvi_kvs_object.cpp')
-rw-r--r-- | src/kvirc/kvs/kvi_kvs_object.cpp | 1743 |
1 files changed, 1743 insertions, 0 deletions
diff --git a/src/kvirc/kvs/kvi_kvs_object.cpp b/src/kvirc/kvs/kvi_kvs_object.cpp new file mode 100644 index 00000000..0447cfd9 --- /dev/null +++ b/src/kvirc/kvs/kvi_kvs_object.cpp @@ -0,0 +1,1743 @@ +//============================================================================= +// +// File : kvi_kvs_object.cpp +// Created on Wed 08 Oct 2003 02:31:57 by Szymon Stefanek +// +// This file is part of the KVIrc IRC client distribution +// Copyright (C) 2003 Szymon Stefanek <pragma at kvirc dot net> +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#define __KVIRC__ + +#include "kvi_kvs_object.h" +#include "kvi_kvs_hash.h" +#include "kvi_kvs_kernel.h" +#include "kvi_window.h" +#include "kvi_app.h" + +#include "kvi_modulemanager.h" +#include "kvi_console.h" +#include "kvi_locale.h" +#include "kvi_error.h" +#include "kvi_out.h" +#include "kvi_mirccntrl.h" +#include "kvi_iconmanager.h" +#include "kvi_malloc.h" + +#include "kvi_kvs_object_controller.h" +#include "kvi_kvs_object_functioncall.h" +#include "kvi_kvs_object_functionhandlerimpl.h" + +#include <qmetaobject.h> +#include <qtimer.h> +#include <time.h> +#include <qiconset.h> + + + +/* + @doc: objects + @title: + Object scripting + @short: + Object scripting overview + @keyterms: + objects , object scripting , complex data structures + @body: + [big]Introduction[/big] + + The KVIrc scripting language is not object oriented in nature. + Anyway , objects are a highlevel abstraction that allow + to write complex code in a "clean" way. + So I've added at least some pseudo-object support.[br][br] + + [big]Basic concepts[/big] + + Objects are arranged in tree structures. + Each object that you create is either toplevel object or a children + of another object. A toplevel object is a parentless one. + Obviously all objects can have children objects.[br][br] + + When an object is destroyed , all its children are also destroyed. + The toplevel objects are automatically destroyed when KVIrc quits. + The objects are global to the entire application (this is different + from previous KVIrc releases where the objects were local + to the current frame window and arranged in a single tree + with a builtin root object).[br][br] + + Each object is an instance of a class that defines its features. + Each object has also a name , that is not necessary unique and is assigned + by the programmer; the name is just a mnemonic expedient, and + you may also not need it.[br][br] + + Each object is identified by an [b]OPAQUE UNIQUE ID[/b]. + The ID is assigned by KVIrc and can be held in any variable. + You can think the object id as a "handle for the object" or the object's pointer. + Any action performed on the object will require its ID.[br][br] + + [big]Creation and destruction[/big] + + To create an object you must use the [fnc]$new[/fnc]() + function. [fnc]$new[/fnc]() requires three parameters:[br] + - The object class (more about object classes later in this document)[br] + - The ID of the parent object , (that can be 0 for toplevel objects).[br] + - The object name (eventually empty)[br] + [example] + %myobject = [fnc]$new[/fnc]([class]object[/class],0,theName) + [/example] + [fnc]$new[/fnc]() returns the ID of the newly created object, or + the STRING "0" if the object creation fails + (it is a string because the object id's are generally strings, and 0 is an "invalid object id"). + In well written scripts it is not common that the object creation fails, anyway + you can check if the creation has failed in the following way:[br] + [example] + [cmd]if[/cmd](%myobject)[cmd]echo[/cmd] "Object created!" + else [cmd]echo[/cmd] "Object creation failed!" + [/example] + You can also test the object ID's for equality:[br] + [example] + [cmd]if[/cmd](%myobject == %anotherobject)[cmd]echo[/cmd] "This is the same object!"; + [/example] + The parent object id is optional, if not specified it is assumed to be 0. + The object name is optional , but it may help you later in finding the object.[br][br] + + To destroy an object use the [cmd]delete[/cmd] command. (In previous versions + this command was named "destroy" and delete is currently aliased to that name too).[br] + [example] + [cmd]delete[/cmd] %myobject + [/example] + If the destroyed object has children objects , these are destroyed too.[br][br] + + [big]Fields : objects as pseudo-structures[/big] + + All the objects can contain variable fields. + You can set an object's field by using the object scope operator "->":[br] + [example] + %myobject->%fieldVariable = dataString + [/example] + To unset a field set it with empty data string (just like with a normal variable). + To retrieve the field data use the object scope operator in the same way:[br] + [example] + [cmd]echo[/cmd] %myobject->%fieldVariable + [/example] + The '->' operator has been stolen from the C language. + In the KVIrc scripting language it switches from the global namespace + to the object's one.[br] + So in the above example %fieldVariable is owned by the object.[br] + The first character of the variable name has no special meaning in the + object namespace (in the global namespace the variables starting + with an uppercase letter are global to the application , the other ones are local + to the command sequence). The variable names are completely case insensitive.[br][br] + + Any [doc:operators]operator[/doc] can be used with the object field variables:[br] + [example] + %myobject->%fieldVariable = 0 + %myobject->%fieldVarialbe ++ + [cmd]if[/cmd]0(%myobject->%fieldVariable != 1)[cmd]echo[/cmd] KVIrc is drunk , maybe a reboot will help ? + [/example] + You can simulate C structures "on the fly" by using objects and fields:[br] + [example] + # Create an user description on the fly + %myobj = [fnc]$new[/fnc]([class]object[/class],0,userDescription) + # Set the fields + %myobj->%nickname = Pragma + %myobj->%username = daemon + %myobj->%hostname = pippo.pragma.org + %myobj->%info = Pragma goes always sleep too late + %myobj->%info [doc:operators]<<[/doc] and wakes up too late too! + # Call an (user defined) alias that stores the data to a file + storetofile %myobj + # Destroy the object + [cmd]delete[/cmd] %myobj + [/example] + The field variables can be also dictionaries:[br] + [example] + %theobj->%field[key] = something + [/example] + Unlike in C , there is no need to declare object fields. + Any object can have any field variable ; an "unset" field is equivalent to an "empty" field.[br] + Note:[br] + The KVIrc scripting language is not typed. + Any object class (be patient...I'll explain classes in a while) identifier can be stored in any KVIrc variable: + it is not possible to find out the object features by "examining" its identifier. + This may make the usage of objects a bit "unclear"; + Howewer , with some experience you will be able to use the objects in a very powerful way. + The type-safety can be also simulated by a careful usage of object names; + in the above example , the %myobj object was created with the "userDescription" name. + The storetofile alias could check the passed object's name and refuse to work + if that does not match "userDescription".[br][br] + + A more complex use of fields will be described later in this document.[br][br] + + [big]Member functions[/big] + + Just like in C++ , the objects have member functions. + For example , the "object" class (again...read on) objects export the [classfnc:object]$name[/classfnc]() + and [classfnc:object]$className[/classfnc]() functions.[br] + [example] + %tmp = [fnc]$new[/fnc]([class]object[/class],0,myobject) + [cmd]echo[/cmd] The object's name is %tmp->[classfnc:object]$name[/classfnc]() , the class name is %tmp->[classfnc:object]$className[/classfnc]() + # Destroy the object + [cmd]delete[/cmd] %tmp + [/example] + Another cool function exported by the [class:object]object[/class] class is the + [classfnc:object]$children[/classfnc]() function. + It returns a comma separated list of children identifiers.[br] + [example] + %tmp = [fnc]$new[/fnc]([class]object[/class],0,myobject) + %tmpchild = [fnc]$new[/fnc]([class]object[/class],%tmp,child1) + %tmpchild = [fnc]$new[/fnc]([class]object[/class],%tmp,child2) + %tmpchild = [fnc]$new[/fnc]([class]object[/class],%tmp,child3) + [cmd]echo[/cmd] The object's children list is : %tmp->[classfnc:object]$children[/classfnc]() + # Destroy the object and the children + [cmd]delete[/cmd] %tmp + [/example] + There are two special functions for each objects: the "constructor" and the "destructor". + You will find more informations on constructors and destructors later in this document, + for now it's enough that you know that these functions are called automatically by KVirc: + the constructor is called when the object is created and the destructor is called when the + object is being destroyed with [cmd]delete[/cmd].[br][br] + + The object functions can be reimplemented on-the-fly + by using the [cmd]privateimpl[/cmd] command: you can simply modify the behaviour of the function + by writing your own function body. + (This is an uncommon feature: unlike many other languages , you can reimplement object + functions at run-time, when the object has been already created.)[br][br] + + A more complex example[br] + [example] + %tmp = [fnc]$new[/fnc]([class]object[/class],0,myobject) + [cmd]foreach[/cmd](%i,1,2,3) + { + %tmpchild = [fnc]$new[/fnc]([class]object[/class],%tmp,child%i) + [cmd]privateimpl[/cmd](%tmpchild,destructor){ [cmd]echo[/cmd] Object [fnc]$this[/fnc] ([fnc]$this[/fnc]->[classfnc:object]$name[/classfnc]()) destroyed; } + } + [cmd]privateimpl[/cmd](%tmp,destructor) + { + %count = 0; + [cmd]foreach[/cmd](%t,[fnc]$this[/fnc]->[classfnc:object]$children[/classfnc]()) + { + [cmd]echo[/cmd] Children : %t->[classfnc:object]$name[/classfnc]() with class %t->[classfnc:object]$class[/classfnc]() + %count++ + } + [cmd]echo[/cmd] Just before destroying my %count children. + } + # Destroy the object and the children + [cmd]delete[/cmd] %tmp + [/example] + + In the example above four objects have been created. + A "parent" object named "myobject" , and three children objects. + The destructor has been reimplemented for each child object, + to make it "say" its name (Please note the usage of [fnc]$this[/fnc]). + In the parent destructor the children have been counted and listed.[br] + Then the parent object is destroyed causing to:[br] + - trigger the parent destructor.[br] + - destroy all the children (and conseguently trigger all the "individual" destructors).[br][br] + + Not all the object functions must return a value: + If a function does not return a meaningful value , or you just want to ignore it , you can call it in the following way:[br] + [example] + %anyobject->$functionname() + [/example] + [br] + + [big]Classes[/big] + As said before , all objects are instances of a specific class. + This concept is common to almost all object oriented languages. + A class is a collection of methods that define an object's behaviour. + Hehe...it is not easy to explain it , so I'll try with an example:[br] + [example] + class HostAddress + { + field hostname + function ipnumber() + function isLocalhost() + } + [/example] + The above class is a rappresentation of a host address. + You create an [b]instance of this class[/b] and set the hostname field, for example, + to www.kernel.org. + The object is now able to give you informations about the hostname in a transparent way: + You can call the ipnumber() function, and the object will return you the + digits and dots rappresentation of www.kernel.org. + The isLocalhost() function will return true if the hostname refers to the local machine + The object internal job is hidden from the user , but probably it will be a huge job. + To obtain the IP number from the hostname , the object will probably have to perform a DNS call (usually a complex task). + To check if the hostname references the local machine , the object will have to obtain the local hostname + from the system (in some "unspecified" way) and then compare it with the given "hostname" field.[br][br] + + The internal job of the object is defined by the "implementation of the class". + Obviously , the programmer that creates the class has to write that implementation.[br] + + [example] + class HostAddress + { + field hostname + function ipnumber() + { + find the nearest DNS server + make the dns call + wait for the response + decode the response + } + function isLocalhost() + { + query the kernel for the local hostname + compare the obtained hostname with the hostname field + } + } + [/example] + In the above example I have "implemented" the two functions by using a "fantastic" language.[br][br] + + Let's go back to the real world.[br][br] + + KVirc contains a [doc:classes]set of built-in ready-to-use classes[/doc]. + The basic class is [class]object[/class]: all the other classes are derived from this (more about + object inheritance later in this doc).[br][br] + + Another available class is [class]socket[/class] that is an interface to the real system sockets. + An instance of the [class]socket[/class] class can connect and communicate with other hosts on the net.[br][br] + + The [b]class definitions are GLOBAL to the entire application[/b]: all server windows share them.[br][br] + + So now we can say that in KVIrc + [b]a CLASS is a collection of features that define the behaviour of an object. + The user interface to the class are the member functions and the events.[/b][br][br] + + [big]Inheritance[/big] + + Someone asked for derived classes ?[br] + Here we go:[br] + The [cmd]class[/cmd] command allows you to define new object classes. + In KVI++, A new class must be always derived from some other class: the lowest possible + level of inheritance is 1: deriving from class [class]object[/class].[br] + [example] + [cmd]class[/cmd](helloworld,object) + { + sayhello() + { + [cmd]echo[/cmd] Hello world! + } + } + [/example] + + The above class is named "helloworld". It inherits the [class]object[/class] class. + This means that it acquires all the [class]object[/class] fuunctions: [classfnc:object]$name[/classfnc]() , + [classfnc:object]$class[/class]() , [classfnc:object]$children[/classfnc]()... + Additionally , it has the $sayhello() function, that "echoes Hello world" to the console. + Now you can create an instance of this class: + [example] + %instance = [fnc]$new[/fnc](helloworld) + %instance->$sayhello() + [/example] + You should see "Hello world" printed in the console. + Easy job...let's make the things a bit more complex now: + derive another class from helloworld and make it say "hello" in two different languages:[br] + [example] + [cmd]class[/cmd](localizedhelloworld,helloworld) + { + [comment]# define the setlanguage function[/comment] + [comment]# note that <$0 = language> is just a programmer reminder[/comment] + setlanguage(<$0 = language>) + { + [cmd]if[/cmd](($0 == english) || ($0 == italian)) + { + [fnc:$this]$$[/fnc]->%lang = $0 + [cmd]return[/cmd] 1 + } else { + [cmd]echo[/cmd] I don't know that language ($0) + [cmd]echo[/cmd] defaulting to english + [fnc:$this]$$[/fnc]->%lang = english + [cmd]return[/cmd] 0 + } + } + + sayhello() + { + [cmd]if[/cmd]([fnc:$this]$$[/fnc]->%lang == italian)[cmd]echo[/cmd] Ciao mondo! + else [fnc:$this]$$[/fnc]->$helloworld:sayhello() + } + } + [/example] + Now you can call:[br] + [example] + %m = [fnc]$new[/fnc](localizedhelloworld) + %m->$setLanguage(italian) + %m->$sayhello() + %m->$setLanguage(english) + %m->$sayhello() + %m->$setLanguage(turkish) + %m->$sayhello() + [cmd]delete[/cmd] %myobj + [/example] + The class defined above is inherited from the previously defined helloworld class: + so it inherits the "object" class functions and events and the sayhello function from "helloworld". + In addition a setlanguage function is defined that stores in a variable the language name passed + as a parameter (after checking its validity). ($0 evaluates to the first parameter passed) + If the language is unknown the setlanguage function will return 0 (false). + Now we want to be able to say "hello world" in italian and english. + So we [b]override[/b] the inherited sayhello function. + "To override" means "to reimplement" : if you call %object->$sayhello() and %object + contains the ID of an instance of class "localizedhelloworld" , the new implementation of that function willl be called (executed). + The inherited sayhello was able to say "hello world" only in english , so we can still use it in the new implementation + without rewriting its contents. So if the language set is "not italian" we assume that it is english and + call the [b]base class implementation[/b].[br] + [example] + [fnc]$this/[fnc]->$helloworld:sayhello() + [comment]# equivalent to $$->$helloworld:sayhello(),[/comment] + [comment]# to $this->$helloworld::sayhello(),[/comment] + [comment]# and to $$->$helloworld::sayhello()[/comment] + [/example] + otherwise the language is italian and we say "hello" in italian :). + So , to call a base class implementation of a function we "prepend" the base class name before the function name in the call. + The base class name could be also [class]object[/class] in this case , but the [class]object[/class] class has no "sayhello" function defined + so it would result in an error.[br][br] + In the above example , all the values of [fnc]$this[/fnc]</a>->%language + that are not equal to "italian" are assumed to be "english". + This is not always true , for example , just after the object creation the %language variable field + is effectively empty. The above class works correctly in this case , but we might want to have always + a coherent state of the field variables , so we need another concept: the class [b]constructor[/b] + that will be discussed in the next paragraph.[br][br] + + Note: multiple inheritance (inheritance from more than one base class) is not implemented , KVIrc is not a compiler :)[br][br] + + Objects are much more powerful....[br][br] + + Do a [cmd]clearobjects[/cmd] to cleanup the old class definitions , and read on.[br][br] + + [big]Constructors and destructors[/big] + + The class constructor is a [b]function[/b] that is called automatically just after the object + has been created internally by KVIrc and just before the [fnc]$new[/fnc] + function returns. It should be used to setup the internal object state.[br] + Unlike in C++ , in KVIrc , the constructor CAN return a value:[br] + If it returns 0 it signals the object creation failure : the object + is immediately destroyed and [fnc]$new[/fnc]() returns 0 to the caller. + Any other return value is treated as success , so the object is effectively + created and [fnc]$new[/fnc]() returns its ID to the caller.[br] + All the builtin classes have a constructor defined that will almost never fail (only if we run out of memory), + so you can avoid to check the [fnc]$new[/fnc]() return value + when creating the instances of the built-in classes.[br][br] + + In derived classes you can override the constructor to setup your object's state.[br] + You should [b]always call the base class constructor[/b] in your overridden one , to setup + the base class state , and propagate its return value (eventually modified if the base class + constructor is succesfull but your derived class initialization fails).[br] + In practice , the builtin class constructors do nothing other than setting the return + value to 1 so you can even avoid to call them, but in any other case you must do it.[br][br] + + This is different from C (for example), where the constructors are called (more or less) + automatically.[br][br] + + [big]Signals and slots[/big] + + The signals and slots are a powerful mean of inter-object communication. + A signal is emitted by an object to notify a change in its state. + For example , the [class:button]button class[/class] emits the + [classsignal:button]clicked[/classsignal] signal when the user clicks the button.[br][br] + A signal is emitted by an object and can be received and handled by any other existing object + (including the object that emits the signal).[br] + The handler function for a signal is called "slot".[br] + It is just a convention : in fact , a slot is a normal object function (and any object function can be a slot). + More than one slot can be connected to a single signal , and more signals can be connected to a single slot.[br] + In this way , many objects can be notified of a change in a single object , as well as a single object + can easily handle state-changes for many objects.[br] + The signal / slot behaviour could be easily implemented by a careful usage of object functions. + [b]So why signals and slots ?[/b][br] + Because signals are much more powerful in many situations. + The signals have no equivalent in C/C++...but they have been implemented in many highlevel + C/C++ libraries and development kits (including the system-wide signal/handler mechanism implemented + by all the modern kernels and used in inter-process communication).[br] +*/ + + + + +/////////////////////////////////////////////////////////////////////////////////////// + +/* + @doc: object + @keyterms: + object class, object, class + @title: + object class + @type: + class + @short: + Base class for all the KVIrc objects + @inherits: + none + @description: + This is the base class for all the builtin KVirc object classes. + It exports the functions to retrieve the object name, to iterate + through children objects and to lookup a child object by name or class. + Additionally , this class provides the builtin timer functionality. + The [classfnc]$constructor[/classfnc] and [classfnc]$destructor[/classfnc] + functions are empty implementations that all the other classes inherit. + @functions: + !fn: $constructor() + Constructor for this object class. + The default implementation does nothing. + !fn: $destructor() + Destructor for this object class. + The default implementation emits the signal "[classsignal]destroyed[/classsignal]". + !fn: $name() + Returns the name of this object. + !fn: $parent() + Returns the parent object of this object or 0 if this object has no parent. + !fn: $timerEvent(<timerId>) + Handler for the timer events. + The default implementation does nothing. + See also [classfnc]$startTimer[/classfnc]() + and [classfnc]$killTimer[/classfnc](). + !fn: $startTimer(<timeout>) + Starts a builtin timer for this object and returns its timer id + as a string or '-1' if the <timeout> was invalid. + The [classfnc]$timerEvent[/classfnc]() handler function + will be called at each <timeout>. The <timeout> is in milliseconds. + !fn: $killTimer(<timer id>) + Stops the timer specified by <timer id>. + !fn: $killTimers() + Stops all the builtin timers running. + !fn: $className() + Returns the class name of this object instance + !fn: $findChild(<class>,<name>) + Returns the first child that matches <class> and <name>. + If <class> is an empty string, any class matches, + if <name> is an empty string, any name matches. + This function traverses the entire tree of children + but is NOT recursive. + !fn: $childCount() + Returns the number of children objects + !fn: $emit(<signal_name>[,parameters]) + Emits the signal <signal_name> passing the optional [parameters]. + See the [doc:objects]objects documentation[/doc] for an overview of signals and slots. + !fn: $children() + Returns an array of children object identifiers. + !fn: $signalSender() + Returns the current signal sender when in a slot connected to a signal. + In other contexts this function returns an empty string. + You can safely use it to test if the current function has been + triggered directly or from a signal emission. + !fn: $signalName() + Returns the name of the signal last signal that has triggered + one of this object's slots. + This means that in a slot handler it returns the name of the signal + that has triggered it. + !fn: $property(<Qt property name>[,bNowarning:boolean]) + This is for really advanced scripting.[br] + All KVIrc widgets are based on the Qt library ones.[br] + The Qt library allow to set and read special properties.[br] + You will have to take a look at the Qt documentation for each widget type + to see the available property names.[br] + The supported property types are: Rect, Size, Point, Color, String, CString, + Int, UInt, Bool and enumeration types.[br] + For example, the widget's x coordinate can be retrieved by using the [classfnc]$x[/classfnc]() + function or by calling $property(x).[br] + There are many properties that are available ony through the [classfnc]$property()[classfnc]" call:[br] + For example, you can find out if the widget accepts drops by calling [classfnc]$property(acceptDrops)[classfnc].[br] + This function will be mainly useful in the [class]wrapper[/class] class. + !fn: $setProperty(<Qt property>,<property value>) + Sets a qt property for this widget.[br] + This is for advanced scripting, and can control really many features of the Qt widgets.[br] + For example, the [class]multilineedit[/class] widgets can be set to + the "password" echo mode only by using this function call:[br] + [example] + %X=$new(lineedit, 0, a_name) + %X->$show() + %X->$setProperty(echoMode,Password) + [/example] + The available properties to be set are listed by [classfnc]$listProperties[/classfnc]()[br] + and must appear in the list as writeable.[br] + This function will be mainly useful in the [class]wrapper[/class] class. + !fn: $listProperties([bArray]) + Lists the properties of this object.[br] + If <bArray> is $true then the function returns the properties + as an array of descriptive strings, otherwise the properties are dumped to the + active window. If <bArray> is not passed then it is assumed to be $false. + This function will be mainly useful in the [class]wrapper[/class] class. + @signals: + !sg: destroyed() + Emitted by the default implementation of [classfnc]$destructor[/classfnc]. + If you reimplement [classfnc]$destructor[/classfnc] in one of the derived + classes (or as a private implementation), and still want this signal + to be emitted you must emit it by yourself, or (better) call the base class + destructor. +*/ + +// we use a char * pointer just to store a number +// we don't use void * just because incrementing a void pointer doesn't look that good +static char * g_hNextObjectHandle = (char *)0; + + +KviKvsObject::KviKvsObject(KviKvsObjectClass * pClass,KviKvsObject * pParent,const QString &szName) +: QObject(pParent) +{ + if(g_hNextObjectHandle == 0)g_hNextObjectHandle++; // make sure it's never 0 + m_hObject = (kvs_hobject_t)g_hNextObjectHandle; + g_hNextObjectHandle++; + + m_pObject = 0; + m_bObjectOwner = true; // true by default + + m_szName = szName; + + m_pClass = pClass; + + m_pChildList = new KviPointerList<KviKvsObject>; + m_pChildList->setAutoDelete(false); + + m_pDataContainer = new KviKvsHash(); + + m_pFunctionHandlers = 0; // no local function handlers yet! + + m_bInDelayedDeath = false; + + m_pSignalDict = 0; // no signals connected to remote slots + m_pConnectionList = 0; // no local slots connected to remote signals + + if(pParent)pParent->registerChild(this); + + KviKvsKernel::instance()->objectController()->registerObject(this); + +// debug("Hello world!"); +// [root@localhost cvs3]# kvirc +// Hello world! +// [root@localhost cvs3]# date +// Tue Sep 5 21:53:54 CEST 2000 +// [root@localhost cvs3]# + +// Ported to KVS on 29.04.2005 +} + +KviKvsObject::~KviKvsObject() +{ + m_bInDelayedDeath = true; + + callFunction(this,"destructor"); + + while(m_pChildList->first())delete m_pChildList->first(); + delete m_pChildList; + +#if 0 + // Disconnect all the signals + if(m_pSignalDict) + { + KviPointerHashTableIterator<QString,KviKvsObjectConnectionList> it(*m_pSignalDict); + + while(it.current()) + { + KviKvsObjectConnectionListIterator cit(*(it.current())); + while(cit.current()) + { + disconnectSignal(it.currentKey(),cit.current()); + // ++cit // NO!...we point to the next now! + } + // the iterator should automatically point to the next now + //if(m_pSignalDict)++it; + } + } + + // Disconnect all the slots + if(m_pConnectionList) + { + KviKvsObjectConnectionListIterator cit(*m_pConnectionList); + while(cit.current()) + { + QString szSig = cit.current()->szSignal; + cit.current()->pSourceObject->disconnectSignal(szSig,cit.current()); + //++cit;// NO!... we point to the next now! + } + } +#else + // Disconnect all the signals + for(;;) + { + if(!m_pSignalDict)break; + KviPointerHashTableEntry<QString,KviKvsObjectConnectionList> * pSignalList = m_pSignalDict->firstEntry(); + if(!pSignalList)break; + KviKvsObjectConnection * pConnection = pSignalList->data()->first(); + if(!pConnection)break; + disconnectSignal(pSignalList->key(),pConnection); + } + + // Disconnect all the slots + for(;;) + { + if(!m_pConnectionList)break; + KviKvsObjectConnection * pConnection = m_pConnectionList->first(); + if(!pConnection)break; + QString szSignalCopy = pConnection->szSignal; // we need this since pConnection is deleted inside disconnectSignal() and pConnection->szSignal dies too (but is referenced after the connection delete) + pConnection->pSourceObject->disconnectSignal(szSignalCopy,pConnection); + } +#endif + + KviKvsKernel::instance()->objectController()->unregisterObject(this); + + if(parentObject())parentObject()->unregisterChild(this); + + if(m_pObject) + { + disconnect(m_pObject,SIGNAL(destroyed()),this,SLOT(objectDestroyed())); + if(m_bObjectOwner)delete m_pObject; + } + + delete m_pDataContainer; + if(m_pFunctionHandlers)delete m_pFunctionHandlers; +} + +bool KviKvsObject::init(KviKvsRunTimeContext * pContext,KviKvsVariantList *pParams) +{ + return true; +} + +QWidget * KviKvsObject::parentScriptWidget() +{ + if(parentObject()) + { + if(parentObject()->object()) + { + if(parentObject()->object()->isWidgetType()) + return (QWidget *)(parentObject()->object()); + } + } + return 0; +} + +void KviKvsObject::unregisterChild(KviKvsObject *pChild) +{ + m_pChildList->removeRef(pChild); +} + +void KviKvsObject::registerChild(KviKvsObject *pChild) +{ + m_pChildList->append(pChild); +} + +// SIGNAL/SLOT stuff + +bool KviKvsObject::connectSignal(const QString &sigName,KviKvsObject * pTarget,const QString &slotName) +{ + if(!pTarget->lookupFunctionHandler(slotName))return false; // no such slot + + if(!m_pSignalDict) + { + m_pSignalDict = new KviPointerHashTable<QString,KviKvsObjectConnectionList>(7,false); + m_pSignalDict->setAutoDelete(true); + } + + KviKvsObjectConnectionList * l = m_pSignalDict->find(sigName); + if(!l) + { + l = new KviKvsObjectConnectionList; + l->setAutoDelete(true); + m_pSignalDict->insert(sigName,l); + } + + KviKvsObjectConnection * con = new KviKvsObjectConnection; + + con->pSourceObject = this; + con->pTargetObject = pTarget; + con->szSignal = sigName; + con->szSlot = slotName; + + l->append(con); + pTarget->registerConnection(con); + return true; +} + +void KviKvsObject::registerConnection(KviKvsObjectConnection *pConnection) +{ + if(!m_pConnectionList) + { + m_pConnectionList = new KviKvsObjectConnectionList; + m_pConnectionList->setAutoDelete(false); + } + m_pConnectionList->append(pConnection); +} + +bool KviKvsObject::disconnectSignal(const QString &sigName,KviKvsObject * pTarget,const QString &slotName) +{ + if(!m_pSignalDict)return false; //no such signal to disconnect + + KviKvsObjectConnectionList * l = m_pSignalDict->find(sigName); + if(!l)return false; + + KviKvsObjectConnectionListIterator it(*l); + + while(KviKvsObjectConnection * sl = it.current()) + { + if(sl->pTargetObject == pTarget) + { + if(KviQString::equalCI(sl->szSlot,slotName)) + { + pTarget->unregisterConnection(sl); + l->removeRef(sl); + if(l->isEmpty())m_pSignalDict->remove(sigName); + if(m_pSignalDict->isEmpty()) + { + delete m_pSignalDict; + m_pSignalDict = 0; + } + return true; + } + } + ++it; + } + return false; +} + +bool KviKvsObject::disconnectSignal(const QString &sigName,KviKvsObjectConnection * pConnection) +{ + if(!m_pSignalDict)return false; + KviKvsObjectConnectionList * l = m_pSignalDict->find(sigName); + //__range_valid(l); + if(!l)return false; + pConnection->pTargetObject->unregisterConnection(pConnection); + //__range_valid(l->findRef(pConnection) > -1); + l->removeRef(pConnection); + if(l->isEmpty())m_pSignalDict->remove(sigName); + if(m_pSignalDict->isEmpty()) + { + delete m_pSignalDict; + m_pSignalDict = 0; + } + return true; +} + +bool KviKvsObject::unregisterConnection(KviKvsObjectConnection * pConnection) +{ + if(!m_pConnectionList)return false; + bool bOk = m_pConnectionList->removeRef(pConnection); // no auto delete ! + if(!bOk)return false; + if(m_pConnectionList->isEmpty()) + { + delete m_pConnectionList; + m_pConnectionList = 0; + } + return true; +} + +int KviKvsObject::emitSignal(const QString &sigName,KviKvsObjectFunctionCall * pOuterCall,KviKvsVariantList * pParams) +{ + if(!m_pSignalDict)return 0; + + KviKvsObjectConnectionList * l = m_pSignalDict->find(sigName); + if(!l)return 0; // no slots registered + + KviKvsVariant retVal; + + // The objects we're going to disconnect + KviPointerList<KviKvsObjectConnection> * pDis = 0; + + kvs_int_t emitted = 0; + + KviKvsObjectConnectionListIterator it(*l); + + while(KviKvsObjectConnection * s = it.current()) + { + // save it , since s may be destroyed in the call! + KviKvsObject * pTarget = s->pTargetObject; + + emitted++; + + kvs_hobject_t hTarget = pTarget->handle(); + kvs_hobject_t hOld = pTarget->signalSender(); + + pTarget->setSignalSender(m_hObject); + pTarget->setSignalName(sigName); + + if(!pTarget->callFunction(this,s->szSlot,QString::null,pOuterCall->context(),&retVal,pParams)) + { + if(KviKvsKernel::instance()->objectController()->lookupObject(hTarget) && it.current()) + { + pOuterCall->warning( + __tr2qs("Broken slot '%Q' in target object '%Q::%Q' while emitting signal '%Q' from object '%Q::%Q': disconnecting"), + &(s->szSlot), + &(s->pTargetObject->getClass()->name()), + &(s->pTargetObject->getName()), + &(sigName), + &(getClass()->name()), + &m_szName); + + if(!pDis) + { + pDis = new KviPointerList<KviKvsObjectConnection>; + pDis->setAutoDelete(false); + } + pDis->append(s); + } else { + // else destroyed in the call! (already disconnected) + + pOuterCall->warning( + __tr2qs("Slot target object destroyed while emitting signal '%Q' from object '%Q::%Q'"), + &(sigName), + &(getClass()->name()), + &m_szName); + } + } + + if(KviKvsKernel::instance()->objectController()->lookupObject(hTarget)) + { + pTarget->setSignalSender(hOld); + } + + ++it; + } + + if(pDis) + { + // we have some signals to disconnect (because they're broken) + for(KviKvsObjectConnection * con = pDis->first();con;con = pDis->next()) + disconnectSignal(sigName,con); + delete pDis; + } + + return emitted; +} + +bool KviKvsObject::function_name(KviKvsObjectFunctionCall * c) +{ + c->returnValue()->setString(getName()); + return true; +} + +bool KviKvsObject::function_parent(KviKvsObjectFunctionCall * c) +{ + KviKvsObject * o = parentObject(); + c->returnValue()->setHObject(o ? o->handle() : (kvs_hobject_t)0); + return true; +} + +bool KviKvsObject::function_className(KviKvsObjectFunctionCall * c) +{ + c->returnValue()->setString(getClass()->name()); + return true; +} + +bool KviKvsObject::function_childCount(KviKvsObjectFunctionCall * c) +{ + c->returnValue()->setInteger((kvs_int_t)(m_pChildList->count())); + return true; +} + +bool KviKvsObject::function_signalSender(KviKvsObjectFunctionCall * c) +{ + c->returnValue()->setHObject(m_hSignalSender); + return true; +} + +bool KviKvsObject::function_signalName(KviKvsObjectFunctionCall * c) +{ + c->returnValue()->setString(m_szSignalName); + return true; +} + +bool KviKvsObject::function_destructor(KviKvsObjectFunctionCall * c) +{ + emitSignal("destroyed",c); + return true; +} + +bool KviKvsObject::function_children(KviKvsObjectFunctionCall * c) +{ + KviKvsArray * a = new KviKvsArray(); + int id=0; + for(KviKvsObject * o = m_pChildList->first();o;o = m_pChildList->next()) + { + a->set(id,new KviKvsVariant(o->handle())); + id++; + } + c->returnValue()->setArray(a); + return true; +} +bool KviKvsObject::function_findChild(KviKvsObjectFunctionCall * c) +{ + QString szClass,szName; + KVSO_PARAMETERS_BEGIN(c) + KVSO_PARAMETER("className",KVS_PT_STRING,KVS_PF_OPTIONAL,szClass) + KVSO_PARAMETER("objectName",KVS_PT_STRING,KVS_PF_OPTIONAL,szName) + KVSO_PARAMETERS_END(c) + + KviKvsObject * o = findChild(szClass,szName); + c->returnValue()->setHObject(o ? o->handle() : (kvs_hobject_t)0); + + return true; +} + +bool KviKvsObject::function_emit(KviKvsObjectFunctionCall * c) +{ + QString szSignal; + KviKvsVariantList vList; + KVSO_PARAMETERS_BEGIN(c) + KVSO_PARAMETER("signal",KVS_PT_NONEMPTYSTRING,0,szSignal) + KVSO_PARAMETER("params",KVS_PT_VARIANTLIST,KVS_PF_OPTIONAL,vList) + KVSO_PARAMETERS_END(c) + + emitSignal(szSignal,c,&vList); + return true; +} + +bool KviKvsObject::function_startTimer(KviKvsObjectFunctionCall * c) +{ + kvs_int_t timeout; + KVSO_PARAMETERS_BEGIN(c) + KVSO_PARAMETER("timeout",KVS_PT_UINT,0,timeout) + KVSO_PARAMETERS_END(c) + + c->returnValue()->setInteger((kvs_int_t)(startTimer(timeout))); + return true; +} + +bool KviKvsObject::function_killTimer(KviKvsObjectFunctionCall * c) +{ + kvs_int_t id; + KVSO_PARAMETERS_BEGIN(c) + KVSO_PARAMETER("timerId",KVS_PT_INT,0,id) + KVSO_PARAMETERS_END(c) + killTimer(id); + return true; +} + +bool KviKvsObject::function_killTimers(KviKvsObjectFunctionCall * c) +{ + // FIXME: QT4 does not seem to have QObject::killTimers() +#ifndef COMPILE_USE_QT4 + killTimers(); +#endif + return true; +} + +bool KviKvsObject::function_listProperties(KviKvsObjectFunctionCall * c) +{ + bool bArray; + KVSO_PARAMETERS_BEGIN(c) + KVSO_PARAMETER("bArray",KVS_PT_BOOL,KVS_PF_OPTIONAL,bArray) + KVSO_PARAMETERS_END(c) + + c->returnValue()->setNothing(); + + KviKvsArray * a = bArray ? new KviKvsArray() : 0; + + KviWindow * w = c->context()->window(); + + if(!bArray) + w->output(KVI_OUT_SYSTEMMESSAGE,__tr2qs("Listing Qt properties for object named \"%Q\" of KVS class %Q"),&m_szName,&(m_pClass->name())); + kvs_int_t cnt = 0; + if(m_pObject) + { + const QMetaObject *o = m_pObject->metaObject(); + if(!bArray) + w->output(KVI_OUT_SYSTEMMESSAGE,__tr2qs("Properties for Qt class %s"),o->className()); +#ifndef COMPILE_USE_QT4 + while(o) + { +#endif + kvs_int_t idx = 0; + #ifdef COMPILE_USE_QT4 + QMetaProperty prop = o->property(idx); + const QMetaProperty *p = ∝ + #else + const QMetaProperty *p = o->property(idx); + #endif + + while(p) + { + QString szOut; + QString szName = p->name(); + #ifdef COMPILE_USE_QT4 + QString szType = p->typeName(); + #else + QString szType = p->type(); + #endif + if(bArray) + KviQString::sprintf(szOut,"%Q, %Q",&szName,&szType); + else { + KviQString::sprintf(szOut,__tr2qs("Property: %c%Q%c, type %Q"),KVI_TEXT_BOLD,&szName,KVI_TEXT_BOLD,&szType); + szOut.prepend(" "); + } + + if(p->isEnumType()) + { + szOut += ", enum("; +#ifndef COMPILE_USE_QT4 + // FIXME: Qt 4.x needs QMetaEnum for this loop + QStrList le = p->enumKeys(); + int i = 0; + for(char *c2 = le.first(); c2; c2 = le.next()) + { + if(i == 0) + i++; + else + szOut.append(", "); + szOut.append(c2); + } +#endif + szOut += ")"; + } + + +#ifdef COMPILE_USE_QT4 + // FIXME: QT4 Need to read better the docs and check the changes: there seem to be too many + // for me to fix now. Actually I need to get the whole executable working... + if(p->isWritable())szOut += ", writable"; +#else + if(p->isSetType())szOut += ", set"; + if(p->writable())szOut += ", writable"; +#endif + if(bArray) + a->set(cnt,new KviKvsVariant(szOut)); + else + w->outputNoFmt(KVI_OUT_SYSTEMMESSAGE,szOut); +#ifdef COMPILE_USE_QT4 + idx++; + if (idx<o->propertyCount()){ + prop = o->property(idx); + p = ∝ + } + else p=0; +#else + p = o->property(idx); + idx++; +#endif + + cnt++; + } +#ifndef COMPILE_USE_QT4 + o = o->superClass(); + } +#endif + + } + + if(bArray) + c->returnValue()->setArray(a); + else + w->output(KVI_OUT_SYSTEMMESSAGE,__tr2qs("%d properties listed"),cnt); + return true; +} + + +// rewritten using the new KVS features :) +bool KviKvsObject::function_setProperty(KviKvsObjectFunctionCall * c) +{ + QString szName; + KviKvsVariant * v; + KVSO_PARAMETERS_BEGIN(c) + KVSO_PARAMETER("propertyName",KVS_PT_NONEMPTYSTRING,0,szName) + KVSO_PARAMETER("propertyValue",KVS_PT_VARIANT,0,v) + KVSO_PARAMETERS_END(c) + + c->returnValue()->setNothing(); + + if(!m_pObject) + { + // there are no Qt properties at all + c->warning(__tr2qs("The object named \"%Q\" of class %Q has no Qt properties"),&m_szName,&(m_pClass->name())); + return true; + } + +#ifdef COMPILE_USE_QT4 + int idx = m_pObject->metaObject()->indexOfProperty(szName); +#else + int idx = m_pObject->metaObject()->findProperty(szName,true); +#endif + if(idx < 0) + { + c->warning(__tr2qs("No Qt property named \"%Q\" for object named \"%Q\" of class %Q"),&szName,&m_szName,&(m_pClass->name())); + return true; + } +#ifdef COMPILE_USE_QT4 + QMetaProperty prop = m_pObject->metaObject()->property(idx); + const QMetaProperty * p = ∝ +#else + const QMetaProperty * p = m_pObject->metaObject()->property(idx,true); +#endif + if(!p) + { + c->warning(__tr2qs("Can't find property named \"%Q\" for object named \"%Q\" of class %Q: the property is indexed but it doesn't really exist"),&szName,&m_szName,&(m_pClass->name())); + return true; + } + + QVariant vv = m_pObject->property(szName); + if(!vv.isValid()) + { + c->warning(__tr2qs("Can't find property named \"%Q\" for object named \"%Q\" of class %Q: the property is indexed and defined but the returned variant is not valid"),&szName,&m_szName,&(m_pClass->name())); + return true; + } + + if(p->isEnumType()) + { + QString szKey; + v->asString(szKey); +#ifdef COMPILE_USE_QT4 + int val = p->enumerator().keyToValue(szKey); +#else + int val = p->keyToValue(szKey); +#endif + QVariant var(val); + m_pObject->setProperty(szName,var); + return true; + } + +#define WRONG_TYPE(__therighttype) \ + { \ + c->warning(__tr2qs("The property is of type %s but the supplied argument can't be converted to that type (expecting \"%s\")"),p->type(),__therighttype); \ + return true; \ + } + + switch(vv.type()) + { + case QVariant::Int: + { + kvs_int_t i; + if(!v->asInteger(i))WRONG_TYPE("integer") + m_pObject->setProperty(szName,QVariant((int)i)); + } + break; + case QVariant::UInt: + { + kvs_int_t i; + if(!v->asInteger(i))WRONG_TYPE("unsigned integer") + if(i < 0)WRONG_TYPE("unsigned integer") + m_pObject->setProperty(szName,QVariant((unsigned int)i)); + } + break; + case QVariant::Bool: + m_pObject->setProperty(szName,QVariant(v->asBoolean())); + break; + case QVariant::String: + { + QString s; + v->asString(s); + m_pObject->setProperty(szName,QVariant(s)); + } + break; + case QVariant::CString: + { + QString s; + v->asString(s); + m_pObject->setProperty(szName,QVariant(s.utf8())); + } + break; + case QVariant::Point: + { + if(!v->isArray())WRONG_TYPE("array(integer,integer)") + KviKvsArray * a = v->array(); + KviKvsVariant * x = a->at(0); + KviKvsVariant * y = a->at(1); + if(!x || !y)WRONG_TYPE("array(integer,integer)") + kvs_int_t iX,iY; + if(!x->asInteger(iX) || !y->asInteger(iY))WRONG_TYPE("array(integer,integer)") + m_pObject->setProperty(szName,QVariant(QPoint(iX,iY))); + } + break; + case QVariant::Size: + { + if(!v->isArray())WRONG_TYPE("array(integer,integer)") + KviKvsArray * a = v->array(); + KviKvsVariant * w = a->at(0); + KviKvsVariant * h = a->at(1); + if(!w || !h)WRONG_TYPE("array(integer,integer)") + kvs_int_t iW,iH; + if(!w->asInteger(iW) || !h->asInteger(iH))WRONG_TYPE("array(integer,integer)") + m_pObject->setProperty(szName,QVariant(QSize(iW,iH))); + } + break; + case QVariant::Rect: + { + if(!v->isArray())WRONG_TYPE("array(integer,integer,integer,integer)") + KviKvsArray * a = v->array(); + KviKvsVariant * x = a->at(0); + KviKvsVariant * y = a->at(1); + KviKvsVariant * w = a->at(2); + KviKvsVariant * h = a->at(3); + if(!x || !y || !w || !h)WRONG_TYPE("array(integer,integer,integer,integer)") + kvs_int_t iX,iY,iW,iH; + if(!x->asInteger(iX) || !y->asInteger(iY) || !w->asInteger(iW) || !h->asInteger(iH))WRONG_TYPE("array(integer,integer,integer,integer)") + m_pObject->setProperty(szName,QVariant(QRect(iX,iY,iW,iH))); + } + break; +#ifndef COMPILE_USE_QT4 + // FIXME: QT4 ???? + case QVariant::Color: + { + if(!v->isArray())WRONG_TYPE("array(integer,integer,integer)") + KviKvsArray * a = v->array(); + KviKvsVariant * r = a->at(0); + KviKvsVariant * g = a->at(1); + KviKvsVariant * b = a->at(3); + if(!r || !g || !b)WRONG_TYPE("array(integer,integer,integer)") + kvs_int_t iR,iG,iB; + if(!r->asInteger(iR) || !g->asInteger(iG) || !b->asInteger(iB))WRONG_TYPE("array(integer,integer,integer)") + m_pObject->setProperty(szName,QVariant(QColor(iR,iG,iB))); + } + break; + case QVariant::Font: + { + if(!v->isArray())WRONG_TYPE("array(string,integer,string)") + KviKvsArray * a = v->array(); + KviKvsVariant * ff = a->at(0); + KviKvsVariant * ps = a->at(1); + KviKvsVariant * fl = a->at(3); + if(!ff || !ps)WRONG_TYPE("array(string,integer,string)") + kvs_int_t iPs; + if(!ps->asInteger(iPs))WRONG_TYPE("array(string,integer,string)") + QString szFf,szFl; + ff->asString(szFf); + if(fl)fl->asString(szFl); + QFont fnt; + fnt.setFamily(szFf); + fnt.setPointSize(iPs); + if(szFl.find('b') != -1)fnt.setBold(true); + if(szFl.find('i') != -1)fnt.setItalic(true); + if(szFl.find('u') != -1)fnt.setUnderline(true); + if(szFl.find('o') != -1)fnt.setOverline(true); + if(szFl.find('f') != -1)fnt.setFixedPitch(true); + if(szFl.find('s') != -1)fnt.setStrikeOut(true); + m_pObject->setProperty(szName,QVariant(fnt)); + } + break; + case QVariant::Pixmap: + case QVariant::IconSet: + { + if(v->isHObject()) + { + if(v->hobject() == (kvs_hobject_t)0) + { + // null pixmap + if(vv.type() == QVariant::Pixmap) + m_pObject->setProperty(szName,QVariant(QPixmap())); + else + m_pObject->setProperty(szName,QVariant(QIconSet())); + } else { + KviKvsObject * pix = KviKvsKernel::instance()->objectController()->lookupObject(v->hobject()); + if(!pix->inherits("KviScriptPixmapObject")) + c->warning(__tr2qs("A pixmap object, an image_id or an image file path is required for this property")); + else { + QVariant pixv = pix->property("pixmap"); + if(vv.type() == QVariant::Pixmap) + m_pObject->setProperty(szName,pixv); + else + m_pObject->setProperty(szName,QVariant(QIconSet(pixv.toPixmap()))); + } + } + } else { + QString szStr; + v->asString(szStr); + QPixmap * pPix = g_pIconManager->getImage(szStr); + if(pPix) + { + if(vv.type() == QVariant::Pixmap) + m_pObject->setProperty(szName,QVariant(*pPix)); + else + m_pObject->setProperty(szName,QVariant(QIconSet(*pPix))); + } + else + c->warning(__tr2qs("Can't find the requested image")); + } + } + break; +#endif + default: + c->warning(__tr2qs("Property \"%Q\" for object named \"%Q\" of class %Q has an unsupported data type"),&szName,&m_szName,&(m_pClass->name())); + c->returnValue()->setNothing(); + break; + } + return true; +} + +bool KviKvsObject::function_property(KviKvsObjectFunctionCall * c) +{ + QString szName; + bool bNoerror; + KVSO_PARAMETERS_BEGIN(c) + KVSO_PARAMETER("propertyName",KVS_PT_NONEMPTYSTRING,0,szName) + KVSO_PARAMETER("bNowarning",KVS_PT_BOOL,KVS_PF_OPTIONAL,bNoerror) + KVSO_PARAMETERS_END(c) + + if(!m_pObject) + { + // there are no Qt properties at all + if (bNoerror) c->returnValue()->setString("No Qt properties"); + else + { + c->warning(__tr2qs("The object named \"%Q\" of class %Q has no Qt properties"),&m_szName,&(m_pClass->name())); + c->returnValue()->setNothing(); + } + return true; + } + +#ifdef COMPILE_USE_QT4 + int idx = m_pObject->metaObject()->indexOfProperty(szName); +#else + int idx = m_pObject->metaObject()->findProperty(szName,true); +#endif + if(idx < 0) + { + if (bNoerror) c->returnValue()->setString("No Qt properties"); + else + { + c->warning(__tr2qs("No Qt property named \"%Q\" for object named \"%Q\" of class %Q"),&szName,&m_szName,&(m_pClass->name())); + c->returnValue()->setNothing(); + } + return true; + } +#ifdef COMPILE_USE_QT4 + QMetaProperty prop = m_pObject->metaObject()->property(idx); + const QMetaProperty * p = ∝ +#else + const QMetaProperty * p = m_pObject->metaObject()->property(idx,true); +#endif + if(!p) + { + c->warning(__tr2qs("Can't find property named \"%Q\" for object named \"%Q\" of class %Q: the property is indexed but it doesn't really exist"),&szName,&m_szName,&(m_pClass->name())); + c->returnValue()->setNothing(); + return true; + } + + QVariant v = m_pObject->property(szName); + if(!v.isValid()) + { + c->warning(__tr2qs("Can't find property named \"%Q\" for object named \"%Q\" of class %Q: the property is indexed and defined but the returned variant is not valid"),&szName,&m_szName,&(m_pClass->name())); + c->returnValue()->setNothing(); + return true; + } + + if(p->isEnumType()) + { +#ifdef COMPILE_USE_QT4 + c->returnValue()->setString(p->enumerator().valueToKey(v.toInt())); +#else + c->returnValue()->setString(p->valueToKey(v.toInt())); +#endif + return true; + } + + switch(v.type()) + { + case QVariant::Int: + c->returnValue()->setInteger((kvs_int_t)v.toInt()); + break; + case QVariant::UInt: + c->returnValue()->setInteger((kvs_int_t)v.toUInt()); + break; + case QVariant::Bool: + c->returnValue()->setBoolean(v.toBool()); + break; + case QVariant::String: + c->returnValue()->setString(v.toString()); + break; + case QVariant::CString: + c->returnValue()->setString(QString::fromUtf8(v.toCString().data())); + break; + case QVariant::Point: + { + QPoint p = v.toPoint(); + KviKvsArray * a = new KviKvsArray(); + a->set(0,new KviKvsVariant((kvs_int_t)p.x())); + a->set(1,new KviKvsVariant((kvs_int_t)p.y())); + c->returnValue()->setArray(a); + } + break; + case QVariant::Size: + { + QSize p = v.toSize(); + KviKvsArray * a = new KviKvsArray(); + a->set(0,new KviKvsVariant((kvs_int_t)p.width())); + a->set(1,new KviKvsVariant((kvs_int_t)p.height())); + c->returnValue()->setArray(a); + } + break; + case QVariant::Rect: + { + QRect p = v.toRect(); + KviKvsArray * a = new KviKvsArray(); + a->set(0,new KviKvsVariant((kvs_int_t)p.x())); + a->set(1,new KviKvsVariant((kvs_int_t)p.y())); + a->set(2,new KviKvsVariant((kvs_int_t)p.width())); + a->set(3,new KviKvsVariant((kvs_int_t)p.height())); + c->returnValue()->setArray(a); + } + break; +#ifndef COMPILE_USE_QT4 + // FIXME: QT4 ? + case QVariant::Color: + { + QColor clr = v.toColor(); + KviKvsArray * a = new KviKvsArray(); + a->set(0,new KviKvsVariant((kvs_int_t)clr.red())); + a->set(1,new KviKvsVariant((kvs_int_t)clr.green())); + a->set(2,new KviKvsVariant((kvs_int_t)clr.blue())); + c->returnValue()->setArray(a); + } + break; + case QVariant::Font: + { + QFont f = v.toFont(); + KviKvsArray * a = new KviKvsArray(); + a->set(0,new KviKvsVariant(f.family())); + a->set(1,new KviKvsVariant((kvs_int_t)f.pointSize())); + QString szFlags; + if(f.bold())szFlags += "b"; + if(f.underline())szFlags += "u"; + if(f.overline())szFlags += "o"; + if(f.strikeOut())szFlags += "s"; + if(f.fixedPitch())szFlags += "f"; + if(f.italic())szFlags += "i"; + a->set(2,new KviKvsVariant(szFlags)); + c->returnValue()->setString(szFlags); + } + break; +#endif + default: + if (bNoerror) c->returnValue()->setString("Unsupported_data_type"); + else + { + c->warning(__tr2qs("Property \"%Q\" for object named \"%Q\" of class %Q has an unsupported data type"),&szName,&m_szName,&(m_pClass->name())); + c->returnValue()->setNothing(); + } + break; + } + return true; +} + +void KviKvsObject::killAllChildrenWithClass(KviKvsObjectClass *cl) +{ + KviPointerList<KviKvsObject> l; + l.setAutoDelete(true); + for(KviKvsObject * o=m_pChildList->first();o;o=m_pChildList->next()) + { + if(o->getClass() == cl) + { + l.append(o); + } else o->killAllChildrenWithClass(cl); + } +} + +bool KviKvsObject::inheritsClass(KviKvsObjectClass * pClass) +{ + if(pClass == m_pClass)return true; + KviKvsObjectClass * cl = m_pClass->m_pParentClass; + while(cl) + { + if(cl == pClass)return true; + else cl = cl->m_pParentClass; + } + return false; +} + +KviKvsObjectClass * KviKvsObject::getClass(const QString & classOverride) +{ + if(classOverride.isEmpty())return m_pClass; + KviKvsObjectClass * cl = m_pClass; // class override can be also THIS class + // if object->$function() is a local override, class::object->$function() + // is the class member function (not the local override) + while(cl) + { + if(KviQString::equalCI(cl->name(),classOverride))break; + else cl = cl->m_pParentClass; + } + return cl; +} + +KviKvsObjectFunctionHandler * KviKvsObject::lookupFunctionHandler(const QString & funcName,const QString & classOverride) +{ + KviKvsObjectFunctionHandler * h = 0; + + if(classOverride.isEmpty() && m_pFunctionHandlers) + { + // lookup the local overrides + h = m_pFunctionHandlers->find(funcName); + } + + if(!h) + { + // not a local override function... lookup in the class + KviKvsObjectClass * cl = getClass(classOverride); + if(cl)return cl->lookupFunctionHandler(funcName); + } + + return h; +} + + +bool KviKvsObject::die() +{ + if(m_bInDelayedDeath)return false; + m_bInDelayedDeath = true; + QTimer::singleShot(0,this,SLOT(delayedDie())); + return true; +} + +bool KviKvsObject::dieNow() +{ + if(m_bInDelayedDeath)return false; + m_bInDelayedDeath = true; + delete this; + return true; +} + +void KviKvsObject::delayedDie() +{ + delete this; // byez! +} + +void KviKvsObject::setObject(QObject * o,bool bIsOwned) +{ + //__range_invalid(m_pObject); + m_bObjectOwner = bIsOwned; + m_pObject = o; + o->installEventFilter(this); + connect(m_pObject,SIGNAL(destroyed()),this,SLOT(objectDestroyed())); +} + +void KviKvsObject::objectDestroyed() +{ + m_pObject = 0; + die(); +} + +bool KviKvsObject::eventFilter(QObject *o,QEvent *e) +{ + return false; // do not stop +} + +void KviKvsObject::timerEvent(QTimerEvent *e) +{ + KviKvsVariant * v = new KviKvsVariant(); + v->setInteger(e->timerId()); + KviKvsVariantList parms(v); + + callFunction(this,"timerEvent",&parms); +} + +bool KviKvsObject::callFunction(KviKvsObject * pCaller,const QString &fncName,KviKvsVariant * pRetVal,KviKvsVariantList * pParams) +{ + KviKvsVariant rv; + if(!pRetVal)pRetVal = &rv; + KviKvsRunTimeContext ctx(0,g_pApp->activeConsole(),KviKvsKernel::instance()->emptyParameterList(),pRetVal,0); + if(!pParams)pParams = KviKvsKernel::instance()->emptyParameterList(); + return callFunction(pCaller,fncName,QString::null,&ctx,pRetVal,pParams); +} + + +bool KviKvsObject::callFunction(KviKvsObject * pCaller,const QString &fncName,KviKvsVariantList * pParams) +{ + KviKvsVariant fakeRetVal; + return callFunction(pCaller,fncName,&fakeRetVal,pParams); +} + +bool KviKvsObject::callFunction( + KviKvsObject * pCaller, + const QString & fncName, + const QString & classOverride, + KviKvsRunTimeContext * pContext, + KviKvsVariant * pRetVal, + KviKvsVariantList * pParams) +{ + KviKvsObjectFunctionHandler * h = lookupFunctionHandler(fncName,classOverride); + + if(!h) + { + if(classOverride.isEmpty()) + pContext->error(__tr2qs("Cannot find object function $%Q for object named \"%Q\" of class %Q"),&fncName,&m_szName,&(getClass()->name())); + else + pContext->error(__tr2qs("Cannot find object function $%Q::%Q for object named \"%Q\" of class %Q"),&classOverride,&fncName,&m_szName,&(getClass()->name())); + return false; + } + + if(h->flags() & KviKvsObjectFunctionHandler::Internal) + { + if(pCaller != this) + { + pContext->error(__tr2qs("Cannot call internal object function $%Q (for object named \"%Q\" of class %Q) from this context"),&fncName,&m_szName,&(getClass()->name())); + return false; + } + } + + KviKvsObjectFunctionCall fc(pContext,pParams,pRetVal); + + return h->call(this,&fc); + + // Not only gcc spits out compiler errors: + // 25.09.2001 , at this point in file + + // c:\programmi\microsoft visual studio\myprojects\kvirc3\src\kvirc\uparser\kvi_scriptobject.cpp(1234) : fatal error C1001: INTERNAL COMPILER ERROR + // (compiler file 'E:\8168\vc98\p2\src\P2\main.c', line 494) + // Please choose the Technical Support command on the Visual C++ + // Help menu, or open the Technical Support help file for more information +} + + + +void KviKvsObject::registerPrivateImplementation(const QString &szFunctionName,const QString &szCode) +{ + if(szCode.isEmpty()) + { + if(m_pFunctionHandlers) + { + m_pFunctionHandlers->remove(szFunctionName); + if(m_pFunctionHandlers->isEmpty()) + { + delete m_pFunctionHandlers; + m_pFunctionHandlers = 0; + } + } + } else { + if(!m_pFunctionHandlers) + { + m_pFunctionHandlers = new KviPointerHashTable<QString,KviKvsObjectFunctionHandler>(7,false); + m_pFunctionHandlers->setAutoDelete(true); + } + + QString szContext = m_pClass->name(); + szContext += "[privateimpl]::"; + szContext += szFunctionName; + + m_pFunctionHandlers->replace(szFunctionName,new KviKvsObjectScriptFunctionHandler(szContext,szCode)); + } +} + + +KviKvsObject * KviKvsObject::findChild(const QString &szClass,const QString &szName) +{ + for(KviKvsObject * o = m_pChildList->first();o;o= m_pChildList->next()) + { + if(szClass.isEmpty()) + { + // any class matches + if(szName.isEmpty())return o; // any name matches + // name must match + if(KviQString::equalCI(szName,o->name()))return o; + } else { + if(KviQString::equalCI(szClass,o->getClass()->name())) + { + if(szName.isEmpty())return o; // any name matches + // name must match + if(KviQString::equalCI(szName,o->name()))return o; + } + } + KviKvsObject * c = o->findChild(szClass,szName); + if(c)return c; + } + return 0; +} + + |