/*
Jabber Protocol Plugin for Miranda IM
Copyright (C) 2002-04 Santithorn Bunchua
Copyright (C) 2005-12 George Hazan
Copyright (C) 2007-09 Maxim Mluhov
Copyright (C) 2007-09 Victor Pavlychko
Copyright (C) 2012-13 Miranda NG Project
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 option ) 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "jabber.h"
#include "jabber_caps.h"
#include "jabber_form2.h"
/////////////////////////////////////////////////////////////////////////////////////////
// FORM_TYPE Registry
namespace NSJabberRegistry
{
// http://jabber.org/network/serverinfo
static TJabberDataFormRegisry_Field form_type_serverinfo[] =
{
{ _T("abuse-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for communication related to abusive traffic") },
{ _T("feedback-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for customer feedback") },
{ _T("sales-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for communication related to sales and marketing") },
{ _T("security-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for communication related to security concerns") },
{ _T("support-addresses"), JDFT_LIST_MULTI, _T("One or more addresses for customer support") },
};
// http://jabber.org/protocol/admin
static TJabberDataFormRegisry_Field form_type_admin[] =
{
{ _T("accountjid"), JDFT_JID_SINGLE, _T("The Jabber ID of a single entity to which an operation applies") },
{ _T("accountjids"), JDFT_JID_MULTI, _T("The Jabber ID of one or more entities to which an operation applies") },
{ _T("activeuserjids"), JDFT_JID_MULTI, _T("The Jabber IDs associated with active sessions") },
{ _T("activeusersnum"), JDFT_TEXT_SINGLE, _T("The number of online entities that are active") },
{ _T("adminjids"), JDFT_JID_MULTI, _T("A list of entities with administrative privileges") },
{ _T("announcement"), JDFT_TEXT_MULTI, _T("The text of an announcement to be sent to active users or all users") },
{ _T("blacklistjids"), JDFT_JID_MULTI, _T("A list of entities with whom communication is blocked") },
{ _T("delay"), JDFT_LIST_MULTI, _T("The number of seconds to delay before applying a change") },
{ _T("disableduserjids"), JDFT_JID_MULTI, _T("The Jabber IDs that have been disabled") },
{ _T("disabledusersnum"), JDFT_TEXT_SINGLE, _T("The number of disabled entities") },
{ _T("email"), JDFT_TEXT_SINGLE, _T("The email address for a user") },
{ _T("given_name"), JDFT_TEXT_SINGLE, _T("The given (first) name of a user") },
{ _T("idleusersnum"), JDFT_TEXT_SINGLE, _T("The number of online entities that are idle") },
{ _T("ipaddresses"), JDFT_LIST_MULTI, _T("The IP addresses of an account's online sessions") },
{ _T("lastlogin"), JDFT_TEXT_SINGLE, _T("The last login time (per XEP-0082) of a user") },
{ _T("loginsperminute"), JDFT_TEXT_SINGLE, _T("The number of logins per minute for an account") },
{ _T("max_items"), JDFT_LIST_SINGLE, _T("The maximum number of items associated with a search or list") },
{ _T("motd"), JDFT_TEXT_MULTI, _T("The text of a message of the day") },
{ _T("onlineresources"), JDFT_TEXT_SINGLE, _T("The names of an account's online sessions") },
{ _T("onlineuserjids"), JDFT_JID_MULTI, _T("The Jabber IDs associated with online users") },
{ _T("onlineusersnum"), JDFT_TEXT_SINGLE, _T("The number of online entities") },
{ _T("password"), JDFT_TEXT_PRIVATE, _T("The password for an account") },
{ _T("password-verify"), JDFT_TEXT_PRIVATE, _T("Password verification") },
{ _T("registereduserjids"), JDFT_JID_MULTI, _T("A list of registered entities") },
{ _T("registeredusersnum"), JDFT_TEXT_SINGLE, _T("The number of registered entities") },
{ _T("rostersize"), JDFT_TEXT_SINGLE, _T("Number of roster items for an account") },
{ _T("stanzaspersecond"), JDFT_TEXT_SINGLE, _T("The number of stanzas being sent per second by an account") },
{ _T("surname"), JDFT_TEXT_SINGLE, _T("The family (last) name of a user") },
{ _T("welcome"), JDFT_TEXT_MULTI, _T("The text of a welcome message") },
{ _T("whitelistjids"), JDFT_JID_MULTI, _T("A list of entities with whom communication is allowed") },
};
// http://jabber.org/protocol/muc#register
static TJabberDataFormRegisry_Field form_type_muc_register[] =
{
{ _T("muc#register_first"), JDFT_TEXT_SINGLE, _T("First Name") },
{ _T("muc#register_last"), JDFT_TEXT_SINGLE, _T("Last Name") },
{ _T("muc#register_roomnick"), JDFT_TEXT_SINGLE, _T("Desired Nickname") },
{ _T("muc#register_url"), JDFT_TEXT_SINGLE, _T("Your URL") },
{ _T("muc#register_email"), JDFT_TEXT_SINGLE, _T("Email Address") },
{ _T("muc#register_faqentry"), JDFT_TEXT_MULTI, _T("FAQ Entry") },
};
// http://jabber.org/protocol/muc#roomconfig
static TJabberDataFormRegisry_Field form_type_muc_roomconfig[] =
{
{ _T("muc#roomconfig_allowinvites"), JDFT_BOOLEAN, _T("Whether to Allow Occupants to Invite Others") },
{ _T("muc#roomconfig_changesubject"), JDFT_BOOLEAN, _T("Whether to Allow Occupants to Change Subject") },
{ _T("muc#roomconfig_enablelogging"), JDFT_BOOLEAN, _T("Whether to Enable Logging of Room Conversations") },
{ _T("muc#roomconfig_lang"), JDFT_TEXT_SINGLE, _T("Natural Language for Room Discussions") },
{ _T("muc#roomconfig_maxusers"), JDFT_LIST_SINGLE, _T("Maximum Number of Room Occupants") },
{ _T("muc#roomconfig_membersonly"), JDFT_BOOLEAN, _T("Whether an Make Room Members-Only") },
{ _T("muc#roomconfig_moderatedroom"), JDFT_BOOLEAN, _T("Whether to Make Room Moderated") },
{ _T("muc#roomconfig_passwordprotectedroom"), JDFT_BOOLEAN, _T("Whether a Password is Required to Enter") },
{ _T("muc#roomconfig_persistentroom"), JDFT_BOOLEAN, _T("Whether to Make Room Persistent") },
{ _T("muc#roomconfig_presencebroadcast"), JDFT_LIST_MULTI, _T("Roles for which Presence is Broadcast") },
{ _T("muc#roomconfig_publicroom"), JDFT_BOOLEAN, _T("Whether to Allow Public Searching for Room") },
{ _T("muc#roomconfig_roomadmins"), JDFT_JID_MULTI, _T("Full List of Room Admins") },
{ _T("muc#roomconfig_roomdesc"), JDFT_TEXT_SINGLE, _T("Short Description of Room") },
{ _T("muc#roomconfig_roomname"), JDFT_TEXT_SINGLE, _T("Natural-Language Room Name") },
{ _T("muc#roomconfig_roomowners"), JDFT_JID_MULTI, _T("Full List of Room Owners") },
{ _T("muc#roomconfig_roomsecret"), JDFT_TEXT_PRIVATE, _T("The Room Password") },
{ _T("muc#roomconfig_whois"), JDFT_LIST_SINGLE, _T("Affiliations that May Discover Real JIDs of Occupants") },
};
// http://jabber.org/protocol/pubsub#publish-options
static TJabberDataFormRegisry_Field form_type_publish_options[] =
{
{ _T("pubsub#access_model"), JDFT_LIST_SINGLE, _T("Precondition: node configuration with the specified access model") },
};
// http://jabber.org/protocol/pubsub#subscribe_authorization
static TJabberDataFormRegisry_Field form_type_subscribe_auth[] =
{
{ _T("pubsub#allow"), JDFT_BOOLEAN, _T("Whether to allow the subscription") },
{ _T("pubsub#subid"), JDFT_TEXT_SINGLE, _T("The SubID of the subscription") },
{ _T("pubsub#node"), JDFT_TEXT_SINGLE, _T("The NodeID of the relevant node") },
{ _T("pubsub#subscriber_jid"), JDFT_JID_SINGLE, _T("The address (JID) of the subscriber") },
};
// http://jabber.org/protocol/pubsub#subscribe_options
static TJabberDataFormRegisry_Field form_type_subscribe_options[] =
{
{ _T("pubsub#deliver"), JDFT_BOOLEAN, _T("Whether an entity wants to receive or disable notifications") },
{ _T("pubsub#digest"), JDFT_BOOLEAN, _T("Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually") },
{ _T("pubsub#digest_frequency"), JDFT_TEXT_SINGLE, _T("The minimum number of milliseconds between sending any two notification digests") },
{ _T("pubsub#expire"), JDFT_TEXT_SINGLE, _T("The DateTime at which a leased subscription will end or has ended") },
{ _T("pubsub#include_body"), JDFT_BOOLEAN, _T("Whether an entity wants to receive an XMPP message body in addition to the payload format") },
{ _T("pubsub#show-values"), JDFT_LIST_MULTI, _T("The presence states for which an entity wants to receive notifications") },
{ _T("pubsub#subscription_type"), JDFT_LIST_SINGLE, _T("") },
{ _T("pubsub#subscription_depth"), JDFT_LIST_SINGLE, _T("") },
};
// http://jabber.org/protocol/pubsub#node_config
static TJabberDataFormRegisry_Field form_type_node_config[] =
{
{ _T("pubsub#access_model"), JDFT_LIST_SINGLE, _T("Who may subscribe and retrieve items") },
{ _T("pubsub#body_xslt"), JDFT_TEXT_SINGLE, _T("The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.") },
{ _T("pubsub#collection"), JDFT_TEXT_SINGLE, _T("The collection with which a node is affiliated") },
{ _T("pubsub#dataform_xslt"), JDFT_TEXT_SINGLE, _T("The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine") },
{ _T("pubsub#deliver_payloads"), JDFT_BOOLEAN, _T("Whether to deliver payloads with event notifications") },
{ _T("pubsub#itemreply"), JDFT_LIST_SINGLE, _T("Whether owners or publisher should receive replies to items") },
{ _T("pubsub#children_association_policy"), JDFT_LIST_SINGLE, _T("Who may associate leaf nodes with a collection") },
{ _T("pubsub#children_association_whitelist"), JDFT_JID_MULTI, _T("The list of JIDs that may associated leaf nodes with a collection") },
{ _T("pubsub#children"), JDFT_TEXT_MULTI, _T("The child nodes (leaf or collection) associated with a collection") },
{ _T("pubsub#children_max"), JDFT_TEXT_SINGLE, _T("The maximum number of child nodes that can be associated with a collection") },
{ _T("pubsub#max_items"), JDFT_TEXT_SINGLE, _T("The maximum number of items to persist") },
{ _T("pubsub#max_payload_size"), JDFT_TEXT_SINGLE, _T("The maximum payload size in bytes") },
{ _T("pubsub#node_type"), JDFT_LIST_SINGLE, _T("Whether the node is a leaf (default) or a collection") },
{ _T("pubsub#notify_config"), JDFT_BOOLEAN, _T("Whether to notify subscribers when the node configuration changes") },
{ _T("pubsub#notify_delete"), JDFT_BOOLEAN, _T("Whether to notify subscribers when the node is deleted") },
{ _T("pubsub#notify_retract"), JDFT_BOOLEAN, _T("Whether to notify subscribers when items are removed from the node") },
{ _T("pubsub#persist_items"), JDFT_BOOLEAN, _T("Whether to persist items to storage") },
{ _T("pubsub#presence_based_delivery"), JDFT_BOOLEAN, _T("Whether to deliver notifications to available users only") },
{ _T("pubsub#publish_model"), JDFT_LIST_SINGLE, _T("The publisher model") },
{ _T("pubsub#replyroom"), JDFT_JID_MULTI, _T("The specific multi-user chat rooms to specify for replyroom") },
{ _T("pubsub#replyto"), JDFT_JID_MULTI, _T("The specific JID(s) to specify for replyto") },
{ _T("pubsub#roster_groups_allowed"), JDFT_LIST_MULTI, _T("The roster group(s) allowed to subscribe and retrieve items") },
{ _T("pubsub#send_item_subscribe"), JDFT_BOOLEAN, _T("Whether to send items to new subscribers") },
{ _T("pubsub#subscribe"), JDFT_BOOLEAN, _T("Whether to allow subscriptions") },
{ _T("pubsub#title"), JDFT_TEXT_SINGLE, _T("A friendly name for the node") },
{ _T("pubsub#type"), JDFT_TEXT_SINGLE, _T("The type of node data, usually specified by the namespace of the payload (if any); MAY be list-single rather than text-single") },
};
// http://jabber.org/protocol/pubsub#meta-data
static TJabberDataFormRegisry_Field form_type_metadata[] =
{
{ _T("pubsub#contact"), JDFT_JID_MULTI, _T("The JIDs of those to contact with questions") },
{ _T("pubsub#creation_date"), JDFT_TEXT_SINGLE, _T("The datetime when the node was created") },
{ _T("pubsub#creator"), JDFT_JID_SINGLE, _T("The JID of the node creator") },
{ _T("pubsub#description"), JDFT_TEXT_SINGLE, _T("A description of the node") },
{ _T("pubsub#language"), JDFT_TEXT_SINGLE, _T("The default language of the node") },
{ _T("pubsub#num_subscribers"), JDFT_TEXT_SINGLE, _T("The number of subscribers to the node") },
{ _T("pubsub#owner"), JDFT_JID_MULTI, _T("The JIDs of those with an affiliation of owner") },
{ _T("pubsub#publisher"), JDFT_JID_MULTI, _T("The JIDs of those with an affiliation of publisher") },
{ _T("pubsub#title"), JDFT_TEXT_SINGLE, _T("The name of the node") },
{ _T("pubsub#type"), JDFT_TEXT_SINGLE, _T("Payload type") },
};
// http://jabber.org/protocol/rc
static TJabberDataFormRegisry_Field form_type_rc[] =
{
{ _T("auto-auth"), JDFT_BOOLEAN, _T("Whether to automatically authorize subscription requests") },
{ _T("auto-files"), JDFT_BOOLEAN, _T("Whether to automatically accept file transfers") },
{ _T("auto-msg"), JDFT_BOOLEAN, _T("Whether to automatically open new messages") },
{ _T("auto-offline"), JDFT_BOOLEAN, _T("Whether to automatically go offline when idle") },
{ _T("sounds"), JDFT_BOOLEAN, _T("Whether to play sounds") },
{ _T("files"), JDFT_LIST_MULTI, _T("A list of pending file transfers") },
{ _T("groupchats"), JDFT_LIST_MULTI, _T("A list of joined groupchat rooms") },
{ _T("status"), JDFT_LIST_SINGLE, _T("A presence or availability status") },
{ _T("status-message"), JDFT_TEXT_MULTI, _T("The status message text") },
{ _T("status-priority"), JDFT_TEXT_SINGLE, _T("The new priority for the client") },
};
// jabber:iq:register
static TJabberDataFormRegisry_Field form_type_register[] =
{
{ _T("username"), JDFT_TEXT_SINGLE, _T("Account name associated with the user") },
{ _T("nick"), JDFT_TEXT_SINGLE, _T("Familiar name of the user") },
{ _T("password"), JDFT_TEXT_PRIVATE, _T("Password or secret for the user") },
{ _T("name"), JDFT_TEXT_SINGLE, _T("Full name of the user") },
{ _T("first"), JDFT_TEXT_SINGLE, _T("First name or given name of the user") },
{ _T("last"), JDFT_TEXT_SINGLE, _T("Last name, surname, or family name of the user") },
{ _T("email"), JDFT_TEXT_SINGLE, _T("Email address of the user") },
{ _T("address"), JDFT_TEXT_SINGLE, _T("Street portion of a physical or mailing address") },
{ _T("city"), JDFT_TEXT_SINGLE, _T("Locality portion of a physical or mailing address") },
{ _T("state"), JDFT_TEXT_SINGLE, _T("Region portion of a physical or mailing address") },
{ _T("zip"), JDFT_TEXT_SINGLE, _T("Postal code portion of a physical or mailing address") },
};
// jabber:iq:search
static TJabberDataFormRegisry_Field form_type_search[] =
{
{ _T("first"), JDFT_TEXT_SINGLE, _T("First Name") },
{ _T("last"), JDFT_TEXT_SINGLE, _T("Family Name") },
{ _T("nick"), JDFT_TEXT_SINGLE, _T("Nickname") },
{ _T("email"), JDFT_TEXT_SINGLE, _T("Email Address") },
};
// urn:xmpp:ssn
static TJabberDataFormRegisry_Field form_type_ssn[] =
{
{ _T("accept"), JDFT_BOOLEAN, _T("Whether to accept the invitation") },
{ _T("continue"), JDFT_TEXT_SINGLE, _T("Another resource with which to continue the session") },
{ _T("disclosure"), JDFT_LIST_SINGLE, _T("Disclosure of content, decryption keys or identities") },
{ _T("http://jabber.org/protocol/chatstates"), JDFT_LIST_SINGLE, _T("Whether may send Chat State Notifications per XEP-0085") },
{ _T("http://jabber.org/protocol/xhtml-im"), JDFT_LIST_SINGLE, _T("Whether allowed to use XHTML-IM formatting per XEP-0071") },
{ _T("language"), JDFT_LIST_SINGLE, _T("Primary written language of the chat (each value appears in order of preference and conforms to RFC 4646 and the IANA registry)") },
{ _T("logging"), JDFT_LIST_SINGLE, _T("Whether allowed to log messages (i.e., whether Off-The-Record mode is required)") },
{ _T("renegotiate"), JDFT_BOOLEAN, _T("Whether to renegotiate the session") },
{ _T("security"), JDFT_LIST_SINGLE, _T("Minimum security level") },
{ _T("terminate"), JDFT_BOOLEAN, _T("Whether to terminate the session") },
{ _T("urn:xmpp:receipts"), JDFT_BOOLEAN, _T("Whether to enable Message Receipts per XEP-0184") },
};
TJabberDataFormRegisry_Form form_types[] =
{
/*0157*/ { _T("http://jabber.org/network/serverinfo"), form_type_serverinfo, SIZEOF(form_type_serverinfo) },
/*0133*/ { _T("http://jabber.org/protocol/admin"), form_type_admin, SIZEOF(form_type_admin) },
/*0045*/ { _T("http://jabber.org/protocol/muc#register"), form_type_muc_register, SIZEOF(form_type_muc_register) },
/*0045*/ { _T("http://jabber.org/protocol/muc#roomconfig"), form_type_muc_roomconfig, SIZEOF(form_type_muc_roomconfig) },
/*0060*/ { _T("http://jabber.org/protocol/pubsub#publish-options"), form_type_publish_options, SIZEOF(form_type_publish_options) },
/*0060*/ { _T("http://jabber.org/protocol/pubsub#subscribe_authorization"), form_type_subscribe_auth, SIZEOF(form_type_subscribe_auth) },
/*0060*/ { _T("http://jabber.org/protocol/pubsub#subscribe_options"), form_type_subscribe_options, SIZEOF(form_type_subscribe_options) },
/*0060*/ { _T("http://jabber.org/protocol/pubsub#node_config"), form_type_node_config, SIZEOF(form_type_node_config) },
/*0060*/ { _T("http://jabber.org/protocol/pubsub#meta-data"), form_type_metadata, SIZEOF(form_type_metadata) },
/*0146*/ { _T("http://jabber.org/protocol/rc"), form_type_rc, SIZEOF(form_type_rc) },
/*0077*/ { _T("jabber:iq:register"), form_type_register, SIZEOF(form_type_register) },
/*0055*/ { _T("jabber:iq:search"), form_type_search, SIZEOF(form_type_search) },
/*0155*/ { _T("urn:xmpp:ssn"), form_type_ssn, SIZEOF(form_type_ssn) },
};
};
CJabberDataField::CJabberDataField(CJabberDataForm *form, XmlNode *node):
m_node(node), m_options(5), m_values(1), m_descriptions(1)
{
m_typeName = JabberXmlGetAttrValue(m_node, "type");
m_var = JabberXmlGetAttrValue(m_node, "var");
m_label = JabberXmlGetAttrValue(m_node, "label");
m_required = JabberXmlGetChild(m_node, "required") ? true : false;
if (m_typeName)
{
if (!lstrcmp(m_typeName, _T("text-private")))
m_type = JDFT_TEXT_PRIVATE;
else if (!lstrcmp(m_typeName, _T("text-multi")) || !lstrcmp(m_typeName, _T("jid-multi")))
m_type = JDFT_TEXT_MULTI;
else if (!lstrcmp(m_typeName, _T("boolean")))
m_type = JDFT_BOOLEAN;
else if (!lstrcmp(m_typeName, _T("list-single")))
m_type = JDFT_LIST_SINGLE;
else if (!lstrcmp(m_typeName, _T("list-multi")))
m_type = JDFT_LIST_MULTI;
else if (!lstrcmp(m_typeName, _T("fixed")))
m_type = JDFT_FIXED;
else if (!lstrcmp(m_typeName, _T("hidden")))
m_type = JDFT_HIDDEN;
else
m_type = JDFT_TEXT_SINGLE;
} else
{
m_typeName = _T("text-single");
m_type = JDFT_TEXT_SINGLE;
}
for (int i = 0; i < m_node->numChild; ++i)
{
if (!lstrcmpA(m_node->child[i]->name, "value"))
{
m_values.insert(m_node->child[i]->text, m_values.getCount());
} else
if (!lstrcmpA(m_node->child[i]->name, "option"))
{
TOption *opt = new TOption;
opt->label = JabberXmlGetAttrValue(m_node->child[i], "label");
opt->value = NULL;
if (XmlNode *p = JabberXmlGetChild(m_node->child[i], "value"))
opt->value = p->text;
m_options.insert(opt, m_options.getCount());
} else
if (!lstrcmpA(m_node->child[i]->name, "desc"))
{
m_descriptions.insert(m_node->child[i]->text, m_descriptions.getCount());
}
}
}
CJabberDataField::~CJabberDataField()
{
m_values.destroy();
m_descriptions.destroy();
}
CJabberDataFieldSet::CJabberDataFieldSet():
m_fields(5)
{
}
CJabberDataField *CJabberDataFieldSet::GetField(TCHAR *var)
{
for (int i = 0; i < m_fields.getCount(); ++i)
if (!lstrcmp(m_fields[i].GetVar(), var))
return &(m_fields[i]);
return NULL;
}
CJabberDataForm::CJabberDataForm(XmlNode *node):
m_node(node),
m_form_type(0),
m_form_type_info(0),
m_title(0),
m_instructions(1),
m_items(1)
{
m_typename = JabberXmlGetAttrValue(m_node, "type");
for (int i = 0; i < m_node->numChild; ++i)
{
XmlNode *child = m_node->child[i];
if (!lstrcmpA(child->name, "field"))
{
CJabberDataField *field = new CJabberDataField(this, child);
m_fields.AddField(field);
if ((field->GetType() == JDFT_HIDDEN) && !lstrcmp(field->GetVar(), _T("FORM_TYPE")))
{
using NSJabberRegistry::form_types;
m_form_type = field->GetValue();
for (int j = 0; j < SIZEOF(form_types); ++j)
if (!lstrcmp(m_form_type, form_types[j].form_type))
{
m_form_type_info = form_types + j;
break;
}
}
} else
if (!lstrcmpA(child->name, "title"))
{
m_title = child->text;
} else
if (!lstrcmpA(child->name, "instructions"))
{
m_instructions.insert(child->text, m_instructions.getCount());
} else
if (!lstrcmpA(child->name, "reported"))
{
if (m_reported.GetCount())
continue; // ignore second -> error!!!!!!!!!!!
for (int j = 0; j < child->numChild; ++j)
{
XmlNode *child2 = child->child[i];
if (!lstrcmpA(child2->name, "field"))
{
CJabberDataField *field = new CJabberDataField(this, child2);
m_reported.AddField(field);
}
}
} else
if (!lstrcmpA(child->name, "item"))
{
CJabberDataFieldSet *item = new CJabberDataFieldSet;
m_items.insert(item);
for (int j = 0; j < child->numChild; ++j)
{
XmlNode *child2 = child->child[i];
if (!lstrcmpA(child2->name, "field"))
{
CJabberDataField *field = new CJabberDataField(this, child2);
item->AddField(field);
}
}
}
}
}
CJabberDataForm::~CJabberDataForm()
{
}
/////////////////////////////////////////////////////////////////////////////////////////
// UI classes
#define FORM_CONTROL_MINWIDTH 100
#define FORM_CONTROL_HEIGHT 20
class CFormCtrlBase;
class CJabberDlgDataPage
{
public:
CJabberDlgDataPage(HWND hwndParent);
~CJabberDlgDataPage();
void AddField(CJabberDataField *field);
XmlNode *FetchData();
HWND GetHwnd() { return m_hwnd; }
void Layout();
// internal usage
int AddControl(CFormCtrlBase *ctrl)
{
m_controls.insert(ctrl, m_controls.getCount());
return m_controls.getCount();
}
private:
HWND m_hwnd;
OBJLIST m_controls;
int m_scrollPos, m_height, m_dataHeight;
BOOL DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
};
class CFormCtrlBase
{
public:
CFormCtrlBase(CJabberDlgDataPage *parent, CJabberDataField *field):
m_field(field), m_parent(parent),
m_hwnd(NULL), m_hwndLabel(NULL)
{
}
HWND GetHwnd() { return m_hwnd; }
void Init();
int Layout(HDWP hdwp, int x, int y, int w);
virtual XmlNode *FetchData() = 0;
protected:
int m_id;
HWND m_hwnd, m_hwndLabel;
CJabberDataField *m_field;
CJabberDlgDataPage *m_parent;
virtual void CreateControl() = 0;
virtual int GetHeight(int width) = 0;
SIZE GetControlTextSize(HWND hwnd, int w);
void CreateLabel();
void SetupFont();
XmlNode *CreateNode();
};
void CFormCtrlBase::Init()
{
m_id = m_parent->AddControl(this);
CreateControl();
SetupFont();
}
SIZE CFormCtrlBase::GetControlTextSize(HWND hwnd, int w)
{
int length = GetWindowTextLength(hwnd) + 1;
TCHAR *text = (TCHAR *)mir_alloc(sizeof(TCHAR) * length);
GetWindowText(hwnd, text, length);
RECT rc;
SetRect(&rc, 0, 0, w, 0);
HDC hdc = GetDC(hwnd);
HFONT hfntSave = (HFONT)SelectObject(hdc, (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0));
DrawText(hdc, text, -1, &rc, DT_CALCRECT|DT_WORDBREAK);
SelectObject(hdc, hfntSave);
ReleaseDC(hwnd, hdc);
mir_free(text);
SIZE res = { rc.right, rc.bottom };
return res;
}
int CFormCtrlBase::Layout(HDWP hdwp, int x, int y, int w)
{
SIZE szLabel = {0}, szCtrl = {0};
int h = 0;
if (m_hwndLabel)
{
SIZE szLabel = GetControlTextSize(m_hwndLabel, w);
szCtrl.cx = w - szLabel.cx - 5;
szCtrl.cy = GetHeight(szCtrl.cx);
if ((szCtrl.cx >= FORM_CONTROL_MINWIDTH) && (szCtrl.cy <= FORM_CONTROL_HEIGHT))
{
DeferWindowPos(hdwp, m_hwndLabel, NULL, x, y + (szCtrl.cy - szLabel.cy) / 2, szLabel.cx, szLabel.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
DeferWindowPos(hdwp, m_hwnd, NULL, x + szLabel.cx + 5, y, szCtrl.cx, szCtrl.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
h = szCtrl.cy;
} else
{
szCtrl.cx = w - 10;
szCtrl.cy = GetHeight(szCtrl.cx);
DeferWindowPos(hdwp, m_hwndLabel, NULL, x, y, szLabel.cx, szLabel.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
DeferWindowPos(hdwp, m_hwnd, NULL, x + 10, y + szLabel.cy + 2, szCtrl.cx, szCtrl.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
h = szLabel.cy + 2 + szCtrl.cy;
}
} else
{
h = GetHeight(w);
DeferWindowPos(hdwp, m_hwnd, NULL, x, y, w, h, SWP_NOZORDER|SWP_SHOWWINDOW);
}
return h;
}
void CFormCtrlBase::CreateLabel()
{
if (m_field->GetLabel())
{
m_hwndLabel = CreateWindow(_T("static"), m_field->GetLabel(),
WS_CHILD|WS_VISIBLE/*|SS_CENTERIMAGE*/,
0, 0, 0, 0,
m_parent->GetHwnd(), (HMENU)-1, hInst, NULL);
}
}
void CFormCtrlBase::SetupFont()
{
if (m_hwnd)
{
HFONT hFont = (HFONT)SendMessage(GetParent(m_hwnd), WM_GETFONT, 0, 0);
if (m_hwnd) SendMessage(m_hwnd, WM_SETFONT, (WPARAM)hFont, 0);
if (m_hwndLabel) SendMessage(m_hwndLabel, WM_SETFONT, (WPARAM) hFont, 0);
}
}
XmlNode *CFormCtrlBase::CreateNode()
{
XmlNode* field = new XmlNode("field");
field->addAttr("var", m_field->GetVar());
field->addAttr("type", m_field->GetTypeName());
return field;
}
class CFormCtrlBoolean: public CFormCtrlBase
{
public:
CFormCtrlBoolean(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
m_hwnd = CreateWindowEx(0, _T("button"), m_field->GetLabel(),
WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX|BS_MULTILINE,
0, 0, 0, 0,
m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
if (m_field->GetValue() && !_tcscmp(m_field->GetValue(), _T("1")))
SendMessage(m_hwnd, BM_SETCHECK, 1, 0);
}
int GetHeight(int width)
{
return GetControlTextSize(m_hwnd, width - 20).cy;
}
XmlNode *FetchData()
{
XmlNode *field = CreateNode();
field->addChild("value", (SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED) ? _T("1") : _T("0"));
return field;
}
};
class CFormCtrlFixed: public CFormCtrlBase
{
public:
CFormCtrlFixed(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
CreateLabel();
m_hwnd = CreateWindow(_T("edit"), m_field->GetValue(),
WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_READONLY,
0, 0, 0, 0,
m_parent->GetHwnd(), (HMENU)-1, hInst, NULL);
}
int GetHeight(int width)
{
return GetControlTextSize(m_hwnd, width - 2).cy;
}
XmlNode *FetchData()
{
XmlNode *field = CreateNode();
for (int i = 0; i < m_field->GetValueCount(); ++i)
field->addChild("value", m_field->GetValue(i));
return field;
}
};
class CFormCtrlHidden: public CFormCtrlBase
{
public:
CFormCtrlHidden(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
}
int GetHeight(int width)
{
return 0;
}
XmlNode *FetchData()
{
XmlNode *field = CreateNode();
for (int i = 0; i < m_field->GetValueCount(); ++i)
field->addChild("value", m_field->GetValue(i));
return field;
}
};
/*
class CFormCtrlJidMulti: public CFormCtrlBase
{
public:
CFormCtrlJidMulti(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
}
int GetHeight(int width)
{
return 20;
}
XmlNode *FetchData()
{
return NULL;
}
};
class CFormCtrlJidSingle: public CFormCtrlBase
{
public:
CFormCtrlJidSingle(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
}
int GetHeight(int width)
{
return 20;
}
XmlNode *FetchData()
{
return NULL;
}
};
*/
class CFormCtrlListMulti: public CFormCtrlBase
{
public:
CFormCtrlListMulti(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
CreateLabel();
m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("listbox"),
NULL, WS_CHILD|WS_VISIBLE|WS_TABSTOP|LBS_MULTIPLESEL,
0, 0, 0, 0,
m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
for (int i = 0; i < m_field->GetOptionCount(); ++i)
{
DWORD dwIndex = SendMessage(m_hwnd, LB_ADDSTRING, 0, (LPARAM)m_field->GetOption(i)->label);
SendMessage(m_hwnd, LB_SETITEMDATA, dwIndex, (LPARAM)m_field->GetOption(i)->value);
for (int j = 0; j < m_field->GetValueCount(); ++j)
if (!lstrcmp_null(m_field->GetValue(j), m_field->GetOption(i)->value))
{
SendMessage(m_hwnd, LB_SETSEL, TRUE, dwIndex);
break;
}
}
}
int GetHeight(int width)
{
return 20 * 3;
}
XmlNode *FetchData()
{
XmlNode *field = CreateNode();
int count = SendMessage(m_hwnd, LB_GETCOUNT, 0, 0);
for (int i = 0; i < count; ++i)
if (SendMessage(m_hwnd, LB_GETSEL, i, 0) > 0)
field->addChild("value", (TCHAR *)SendMessage(m_hwnd, LB_GETITEMDATA, i, 0));
return field;
}
};
class CFormCtrlListSingle: public CFormCtrlBase
{
public:
CFormCtrlListSingle(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
CreateLabel();
m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("combobox"), NULL,
WS_CHILD|WS_VISIBLE|WS_TABSTOP|CBS_DROPDOWNLIST,
0, 0, 0, 0,
m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
for (int i = 0; i < m_field->GetOptionCount(); ++i)
{
DWORD dwIndex = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)m_field->GetOption(i)->label);
SendMessage(m_hwnd, CB_SETITEMDATA, dwIndex, (LPARAM)m_field->GetOption(i)->value);
if (!lstrcmp_null(m_field->GetValue(), m_field->GetOption(i)->value))
SendMessage(m_hwnd, CB_SETCURSEL, dwIndex, 0);
}
}
int GetHeight(int width)
{
return 20;
}
XmlNode *FetchData()
{
XmlNode *field = CreateNode();
int sel = SendMessage(m_hwnd, CB_GETCURSEL, 0, 0);
if (sel != CB_ERR)
field->addChild("value", (TCHAR *)SendMessage(m_hwnd, CB_GETITEMDATA, sel, 0));
return field;
}
};
class CFormCtrlTextMulti: public CFormCtrlBase
{
public:
CFormCtrlTextMulti(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
CreateLabel();
int i, length = 1;
for (i = 0; i < m_field->GetValueCount(); ++i)
length += lstrlen(m_field->GetValue(i)) + 2;
TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * length);
*str = 0;
for (i = 0; i < m_field->GetValueCount(); ++i)
{
if (i) lstrcat(str, _T("\r\n"));
lstrcat(str, m_field->GetValue(i));
}
m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("edit"), str,
WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_VSCROLL|ES_LEFT|ES_MULTILINE|ES_AUTOVSCROLL|ES_WANTRETURN,
0, 0, 0, 0,
m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
// WNDPROC oldWndProc = (WNDPROC)SetWindowLongPtr(item->hCtrl, GWL_WNDPROC, (LPARAM)JabberFormMultiLineWndProc);
// SetWindowLongPtr(item->hCtrl, GWL_USERDATA, (LONG) oldWndProc);
mir_free(str);
}
int GetHeight(int width)
{
return 20 * 3;
}
XmlNode *FetchData()
{
XmlNode *field = CreateNode();
int len = GetWindowTextLength(m_hwnd);
TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len+1));
GetWindowText(m_hwnd, str, len+1);
TCHAR *p = str;
while (p != NULL)
{
TCHAR *q = _tcsstr( p, _T("\r\n"));
if (q) *q = '\0';
field->addChild("value", p);
p = q ? q+2 : NULL;
}
mir_free(str);
return field;
}
};
class CFormCtrlTextSingle: public CFormCtrlBase
{
public:
CFormCtrlTextSingle(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
CreateLabel();
m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("edit"), m_field->GetValue(),
WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL,
0, 0, 0, 0,
m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
}
int GetHeight(int width)
{
return 20;
}
XmlNode *FetchData()
{
XmlNode *field = CreateNode();
int len = GetWindowTextLength(m_hwnd);
TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len+1));
GetWindowText(m_hwnd, str, len+1);
field->addChild("value", str);
mir_free(str);
return field;
}
};
class CFormCtrlTextPrivate: public CFormCtrlBase
{
public:
CFormCtrlTextPrivate(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}
void CreateControl()
{
CreateLabel();
m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("edit"), m_field->GetValue(),
WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL|ES_PASSWORD,
0, 0, 0, 0,
m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
}
int GetHeight(int width)
{
return 20;
}
XmlNode *FetchData()
{
XmlNode *field = CreateNode();
int len = GetWindowTextLength(m_hwnd);
TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len+1));
GetWindowText(m_hwnd, str, len+1);
field->addChild("value", str);
mir_free(str);
return field;
}
};
CJabberDlgDataPage::CJabberDlgDataPage(HWND hwndParent):
m_controls(5)
{
m_hwnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_DATAFORM_PAGE), hwndParent, DlgProc, (LPARAM)this);
ShowWindow(m_hwnd, SW_SHOW);
}
CJabberDlgDataPage::~CJabberDlgDataPage()
{
DestroyWindow(m_hwnd);
}
void CJabberDlgDataPage::AddField(CJabberDataField *field)
{
CFormCtrlBase *ctrl = NULL;
switch (field->GetType())
{
case JDFT_BOOLEAN: ctrl = new CFormCtrlBoolean(this, field); break;
case JDFT_FIXED: ctrl = new CFormCtrlFixed(this, field); break;
case JDFT_HIDDEN: ctrl = new CFormCtrlHidden(this, field); break;
case JDFT_JID_MULTI: ctrl = new CFormCtrlTextMulti(this, field); break;
case JDFT_JID_SINGLE: ctrl = new CFormCtrlTextSingle(this, field); break;
case JDFT_LIST_MULTI: ctrl = new CFormCtrlListMulti(this, field); break;
case JDFT_LIST_SINGLE: ctrl = new CFormCtrlListSingle(this, field); break;
case JDFT_TEXT_MULTI: ctrl = new CFormCtrlTextMulti(this, field); break;
case JDFT_TEXT_PRIVATE: ctrl = new CFormCtrlTextPrivate(this, field); break;
case JDFT_TEXT_SINGLE: ctrl = new CFormCtrlTextSingle(this, field); break;
}
if (ctrl) ctrl->Init();
}
XmlNode *CJabberDlgDataPage::FetchData()
{
XmlNode *result = new XmlNode("x");
result->addAttr("xmlns", JABBER_FEAT_DATA_FORMS);
result->addAttr("type", "submit");
for (int i = 0; i < m_controls.getCount(); ++i)
if (XmlNode *field = m_controls[i].FetchData())
result->addChild(field);
return result;
}
void CJabberDlgDataPage::Layout()
{
RECT rc; GetClientRect(m_hwnd, &rc);
int w = rc.right - 20;
int x = 10;
int y = 10;
m_height = rc.bottom;
m_scrollPos = GetScrollPos(m_hwnd, SB_VERT);
HDWP hdwp = BeginDeferWindowPos(m_controls.getCount());
for (int i = 0; i < m_controls.getCount(); ++i)
if (int h = m_controls[i].Layout(hdwp, x, y - m_scrollPos, w))
y += h + 5;
EndDeferWindowPos(hdwp);
m_dataHeight = y + 5;
SCROLLINFO si = {0};
si.cbSize = sizeof(si);
si.fMask = SIF_DISABLENOSCROLL|SIF_PAGE|SIF_RANGE;
si.nPage = m_height;
si.nMin = 0;
si.nMax = m_dataHeight;
SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);
}
BOOL CJabberDlgDataPage::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
{
SCROLLINFO si = {0};
si.cbSize = sizeof(si);
si.fMask = SIF_DISABLENOSCROLL;
SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);
m_scrollPos = 0;
break;
}
case WM_MOUSEWHEEL:
{
int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
if (zDelta)
{
int i, nScrollLines = 0;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (void*)&nScrollLines, 0);
for (i = 0; i < (nScrollLines + 1) / 2; i++ )
SendMessage(m_hwnd, WM_VSCROLL, (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP, 0);
}
SetWindowLongPtr(m_hwnd, DWL_MSGRESULT, 0);
return TRUE;
}
case WM_VSCROLL:
{
int pos = m_scrollPos;
switch (LOWORD(wParam))
{
case SB_LINEDOWN:
pos += 15;
break;
case SB_LINEUP:
pos -= 15;
break;
case SB_PAGEDOWN:
pos += m_height - 10;
break;
case SB_PAGEUP:
pos -= m_height - 10;
break;
case SB_THUMBTRACK:
pos = HIWORD(wParam);
break;
}
if (pos > m_dataHeight - m_height) pos = m_dataHeight - m_height;
if (pos < 0) pos = 0;
if (m_scrollPos != pos)
{
ScrollWindow(m_hwnd, 0, m_scrollPos - pos, NULL, NULL);
SetScrollPos(m_hwnd, SB_VERT, pos, TRUE);
m_scrollPos = pos;
}
break;
}
case WM_SIZE:
Layout();
break;
}
return FALSE;
}
BOOL CJabberDlgDataPage::DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CJabberDlgDataPage *pThis = NULL;
if (msg == WM_INITDIALOG)
{
if (pThis = (CJabberDlgDataPage *)lParam)
pThis->m_hwnd = hwnd;
SetWindowLongPtr(hwnd, GWL_USERDATA, lParam);
} else
{
pThis = (CJabberDlgDataPage *)GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (pThis)
{
BOOL result = pThis->DlgProc(msg, wParam, lParam);
if (msg == WM_DESTROY)
{
pThis->m_hwnd = NULL;
SetWindowLongPtr(hwnd, GWL_USERDATA, 0);
}
return result;
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////////
// Data form control -- Window class support
const TCHAR *CCtrlJabberForm::ClassName = _T("JabberDataFormControl");
bool CCtrlJabberForm::ClassRegistered = false;
bool CCtrlJabberForm::RegisterClass()
{
if (ClassRegistered) return true;
WNDCLASSEX wcx = {0};
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = ClassName;
wcx.lpfnWndProc = DefWindowProc;
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = 0;
wcx.style = CS_GLOBALCLASS;
if (::RegisterClassEx(&wcx))
ClassRegistered = true;
return ClassRegistered;
}
bool CCtrlJabberForm::UnregisterClass()
{
if (!ClassRegistered) return true;
if (::UnregisterClass(ClassName, hInst) == 0)
ClassRegistered = false;
return !ClassRegistered;
}
/////////////////////////////////////////////////////////////////////////////////
// Data form control
CCtrlJabberForm::CCtrlJabberForm(CDlgBase* dlg, int ctrlId):
CCtrlBase(dlg, ctrlId), m_pForm(NULL), m_pDlgPage(NULL)
{
}
CCtrlJabberForm::~CCtrlJabberForm()
{
if (m_pDlgPage) delete m_pDlgPage;
}
void CCtrlJabberForm::OnInit()
{
CSuper::OnInit();
Subclass();
SetupForm();
}
void CCtrlJabberForm::SetDataForm(CJabberDataForm *pForm)
{
if (m_pDlgPage)
{
delete m_pDlgPage;
m_pDlgPage = NULL;
}
m_pForm = pForm;
SetupForm();
}
XmlNode *CCtrlJabberForm::FetchData()
{
return m_pDlgPage ? m_pDlgPage->FetchData() : NULL;
}
void CCtrlJabberForm::SetupForm()
{
if (!m_pForm || !m_hwnd) return;
m_pDlgPage = new CJabberDlgDataPage(m_hwnd);
for (int i = 0; i < m_pForm->GetFields()->GetCount(); ++i)
m_pDlgPage->AddField(m_pForm->GetFields()->GetField(i));
Layout();
}
void CCtrlJabberForm::Layout()
{
if (!m_pDlgPage) return;
RECT rc;
GetClientRect(m_hwnd, &rc);
SetWindowPos(m_pDlgPage->GetHwnd(), NULL, 0, 0, rc.right, rc.bottom, SWP_NOZORDER);
}
LRESULT CCtrlJabberForm::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_SIZE:
{
Layout();
break;
}
}
return CSuper::CustomWndProc(msg, wParam, lParam);
}
/////////////////////////////////////////////////////////////////////////////////
// testing
class CJabberDlgFormTest: public CDlgBase
{
public:
CJabberDlgFormTest(CJabberDataForm *pForm):
CDlgBase(IDD_DATAFORM_TEST, NULL),
m_frm(this, IDC_DATAFORM)
{
m_frm.SetDataForm(pForm);
}
int Resizer(UTILRESIZECONTROL *urc)
{
return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
}
private:
CCtrlJabberForm m_frm;
};
static VOID CALLBACK CreateDialogApcProc(void* param)
{
XmlNode *node = (XmlNode *)param;
CJabberDataForm form(node);
CCtrlJabberForm::RegisterClass();
CJabberDlgFormTest dlg(&form);
dlg.DoModal();
delete node;
}
void LaunchForm(XmlNode *node)
{
node = JabberXmlCopyNode(node);
CallFunctionAsync(CreateDialogApcProc, node);
}