From 9814933f4bc5a7a4320819de54e313d8fc0ceffe Mon Sep 17 00:00:00 2001 From: sje Date: Wed, 3 Oct 2007 05:26:48 +0000 Subject: initial revision of new metacontacts git-svn-id: https://server.scottellis.com.au/svn/mim_plugs@338 4f64403b-2f21-0410-a795-97e2b3489a10 --- meta2/api.cpp | 317 +++++++++++++++++++ meta2/api.h | 28 ++ meta2/collection.h | 588 ++++++++++++++++++++++++++++++++++++ meta2/common.cpp | 11 + meta2/common.h | 134 ++++++++ meta2/core_functions.cpp | 138 +++++++++ meta2/core_functions.h | 25 ++ meta2/edit_meta.cpp | 243 +++++++++++++++ meta2/edit_meta.h | 2 + meta2/icons.cpp | 102 +++++++ meta2/icons.h | 12 + meta2/icons/mcmenu.ico | Bin 0 -> 1406 bytes meta2/icons/mcmenuof.ico | Bin 0 -> 1406 bytes meta2/icons/mcmenuoff2.ico | Bin 0 -> 1406 bytes meta2/icons/meta_add.ico | Bin 0 -> 1406 bytes meta2/icons/meta_convert.ico | Bin 0 -> 1406 bytes meta2/icons/meta_edit.ico | Bin 0 -> 1406 bytes meta2/icons/meta_remove2.ico | Bin 0 -> 1406 bytes meta2/icons/meta_set_as_default.ico | Bin 0 -> 1406 bytes meta2/import.cpp | 46 +++ meta2/import.h | 2 + meta2/m_metacontacts.h | 166 ++++++++++ meta2/menu.cpp | 265 ++++++++++++++++ meta2/menu.h | 9 + meta2/menu.ico | Bin 0 -> 2550 bytes meta2/meta2.cpp | 107 +++++++ meta2/meta2.rc | 170 +++++++++++ meta2/meta2.sln | 26 ++ meta2/meta2.vcproj | 584 +++++++++++++++++++++++++++++++++++ meta2/meta_api.c | 245 +++++++++++++++ meta2/options.cpp | 71 +++++ meta2/options.h | 13 + meta2/priorities.cpp | 354 ++++++++++++++++++++++ meta2/priorities.h | 6 + meta2/proto.cpp | 547 +++++++++++++++++++++++++++++++++ meta2/proto.h | 11 + meta2/resource.h | 45 +++ meta2/resource.rc | 2 + meta2/select_meta.cpp | 95 ++++++ meta2/select_meta.h | 2 + meta2/settings.cpp | 110 +++++++ meta2/settings.h | 4 + meta2/version.h | 26 ++ meta2/version.rc | 33 ++ 44 files changed, 4539 insertions(+) create mode 100644 meta2/api.cpp create mode 100644 meta2/api.h create mode 100644 meta2/collection.h create mode 100644 meta2/common.cpp create mode 100644 meta2/common.h create mode 100644 meta2/core_functions.cpp create mode 100644 meta2/core_functions.h create mode 100644 meta2/edit_meta.cpp create mode 100644 meta2/edit_meta.h create mode 100644 meta2/icons.cpp create mode 100644 meta2/icons.h create mode 100644 meta2/icons/mcmenu.ico create mode 100644 meta2/icons/mcmenuof.ico create mode 100644 meta2/icons/mcmenuoff2.ico create mode 100644 meta2/icons/meta_add.ico create mode 100644 meta2/icons/meta_convert.ico create mode 100644 meta2/icons/meta_edit.ico create mode 100644 meta2/icons/meta_remove2.ico create mode 100644 meta2/icons/meta_set_as_default.ico create mode 100644 meta2/import.cpp create mode 100644 meta2/import.h create mode 100644 meta2/m_metacontacts.h create mode 100644 meta2/menu.cpp create mode 100644 meta2/menu.h create mode 100644 meta2/menu.ico create mode 100644 meta2/meta2.cpp create mode 100644 meta2/meta2.rc create mode 100644 meta2/meta2.sln create mode 100644 meta2/meta2.vcproj create mode 100644 meta2/meta_api.c create mode 100644 meta2/options.cpp create mode 100644 meta2/options.h create mode 100644 meta2/priorities.cpp create mode 100644 meta2/priorities.h create mode 100644 meta2/proto.cpp create mode 100644 meta2/proto.h create mode 100644 meta2/resource.h create mode 100644 meta2/resource.rc create mode 100644 meta2/select_meta.cpp create mode 100644 meta2/select_meta.h create mode 100644 meta2/settings.cpp create mode 100644 meta2/settings.h create mode 100644 meta2/version.h create mode 100644 meta2/version.rc diff --git a/meta2/api.cpp b/meta2/api.cpp new file mode 100644 index 0000000..03e3c71 --- /dev/null +++ b/meta2/api.cpp @@ -0,0 +1,317 @@ +#include "common.h" +#include "api.h" +#include "proto.h" +#include "core_functions.h" + +HANDLE hEventDefaultChanged = 0, hEventForceSend = 0, hEventUnforceSend = 0, hSubcontactsChanged = 0; + +void FireSubcontactsChanged(HANDLE hMeta) { + NotifyEventHooks(hSubcontactsChanged, (WPARAM)hMeta, 0); +} + +//get the handle for a contact's parent metacontact +//wParam=(HANDLE)hSubContact +//lParam=0 +//returns a handle to the parent metacontact, or null if this contact is not a subcontact +int MetaAPI_GetMeta(WPARAM wParam, LPARAM lParam) { + return (int)(HANDLE)DBGetContactSettingDword((HANDLE)wParam, MODULE, "Handle", 0); +} + +//gets the handle for the default contact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a handle to the default contact, or null on failure +int MetaAPI_GetDefault(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + if(metaMap.exists(hMeta) == false || metaMap[hMeta].size() == 0) return 0; + + int def = DBGetContactSettingByte(hMeta, MODULE, "Default", -1); + if(def < 0 || def >= metaMap[hMeta].size()) return 0; + + return (int)metaMap[hMeta][def].handle(); +} + +//gets the contact number for the default contact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns an int contact number, or -1 on failure +int MetaAPI_GetDefaultNum(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + if(metaMap.exists(hMeta) == false || metaMap[hMeta].size() == 0) return -1; + + int def = DBGetContactSettingByte(hMeta, MODULE, "Default", -1); + if(def < 0 || def >= metaMap[hMeta].size()) return -1; + return def; +} + +//gets the handle for the 'most online' contact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a handle to the 'most online' contact +int MetaAPI_GetMostOnline(WPARAM wParam, LPARAM lParam) { + return (int)Meta_GetMostOnline((HANDLE)wParam); +} + +//gets the number of subcontacts for a metacontact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns an int representing the number of subcontacts for the given metacontact, or -1 on failure +int MetaAPI_GetNumContacts(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + if(metaMap.exists(hMeta)) return metaMap[hMeta].size(); + return -1; +} + +//gets the handle of a subcontact, using the subcontact's number +//wParam=(HANDLE)hMetaContact +//lParam=(int)contact number +//returns a handle to the specified subcontact +int MetaAPI_GetContact(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + if(metaMap.exists(hMeta) && lParam >= 0 && metaMap[hMeta].size() > lParam) return (int)metaMap[hMeta][lParam].handle(); + return 0; +} + +//sets the default contact, using the subcontact's contact number +//wParam=(HANDLE)hMetaContact +//lParam=(int)contact number +//returns 0 on success +int MetaAPI_SetDefaultContactNum(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + DWORD num_contacts = metaMap.exists(hMeta) ? metaMap[hMeta].size() : -1; + if(num_contacts < 0) + return 1; + if((DWORD)lParam >= num_contacts || (DWORD)lParam < 0) + return 1; + if(DBWriteContactSettingByte((HANDLE)wParam, MODULE, "Default", (DWORD)lParam)) + return 1; + + NotifyEventHooks(hEventDefaultChanged, wParam, (LPARAM)metaMap[hMeta][(int)lParam].handle()); + return 0; +} + +//sets the default contact, using the subcontact's handle +//wParam=(HANDLE)hMetaContact +//lParam=(HANDLE)hSubcontact +//returns 0 on success +int MetaAPI_SetDefaultContact(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam, hSub = (HANDLE)lParam; + HANDLE hMetaFromSub = (HANDLE)DBGetContactSettingDword(hSub, MODULE, "Handle", 0); + if(hMetaFromSub != hMeta || metaMap.exists(hMeta) == false) + return 1; + + int contact_num = metaMap[hMeta].index_of(hSub); + if(contact_num == -1) return 1; + + if(DBWriteContactSettingByte(hMeta, MODULE, "Default", contact_num)) + return 1; + + NotifyEventHooks(hEventDefaultChanged, wParam, lParam); + return 0; +} + +//temporarily forces the metacontact to send using a specific subcontact, using the subcontact's contact number +//wParam=(HANDLE)hMetaContact +//lParam=(int)contact number +//returns 0 on success +int MetaAPI_ForceSendContactNum(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + int contact_num = (int)lParam; + if(!hMeta || metaMap.exists(hMeta) == false || contact_num < 0 || contact_num >= metaMap[hMeta].size() || DBGetContactSettingByte(hMeta, MODULE, "ForceDefault", 0)) + return 1; + + HANDLE hSub = metaMap[hMeta][contact_num].handle(); + HANDLE hMetaFromSub = (HANDLE)DBGetContactSettingDword(hSub, MODULE, "Handle", 0); + if(hMetaFromSub != hMeta) + return 1; + + DBWriteContactSettingDword(hMeta, MODULE, "ForceSend", (DWORD)hSub); + + NotifyEventHooks(hEventForceSend, wParam, (LPARAM)hSub); + return 0; +} + +//temporarily forces the metacontact to send using a specific subcontact, using the subcontact's handle +//wParam=(HANDLE)hMetaContact +//lParam=(HANDLE)hSubcontact +//returns 0 on success (will fail if 'force default' is in effect) +int MetaAPI_ForceSendContact(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam, hSub = (HANDLE)lParam; + HANDLE hMetaFromSub = (HANDLE)DBGetContactSettingDword(hSub, MODULE, "Handle", 0); + if(!hSub || !hMeta || hMetaFromSub != hMeta || !metaMap.exists(hMeta) || metaMap[hMeta].index_of(hSub) == -1 || DBGetContactSettingByte(hMeta, MODULE, "ForceDefault", 0)) + return 1; + + DBWriteContactSettingDword(hMeta, MODULE, "ForceSend", (DWORD)hSub); + + NotifyEventHooks(hEventForceSend, wParam, lParam); + return 0; +} + +//'unforces' the metacontact to send using a specific subcontact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns 0 on success (will fail if 'force default' is in effect) +int MetaAPI_UnforceSendContact(WPARAM wParam, LPARAM lParam) { + if(DBGetContactSettingByte((HANDLE)wParam, MODULE, "ForceDefault", 0) == 0) + return 1; + + DBWriteContactSettingDword((HANDLE)wParam, MODULE, "ForceSend", 0); + + NotifyEventHooks(hEventUnforceSend, wParam, lParam); + return 0; +} + + +//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact +// overrides 'force send' above, and will even force use of offline contacts +// will send ME_MC_FORCESEND event +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns 1(true) or 0(false) representing new state of 'force default' +int MetaAPI_ForceDefault(WPARAM wParam, LPARAM lParam) { + int old_state = DBGetContactSettingByte((HANDLE)wParam, MODULE, "ForceDefault", 0); + DBWriteContactSettingByte((HANDLE)wParam, MODULE, "ForceDefault", old_state ? 0 : 1); + return old_state ? 0 : 1; +} + +// method to get state of 'force' for a metacontact +// wParam=(HANDLE)hMetaContact +// lParam= (int)&contact_number or NULL +// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to, +// or if none is in force, the value (DWORD)-1 will be copied +// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above) +int MetaAPI_GetForceState(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + HANDLE hContact; + + if(!hMeta || !metaMap.exists(hMeta)) return 0; + + if(DBGetContactSettingByte(hMeta, MODULE, "ForceDefault", 0)) { + if(lParam) *(DWORD *)lParam = DBGetContactSettingByte((HANDLE)wParam, MODULE, "Default", -1); + return 1; + } + + hContact = (HANDLE)DBGetContactSettingDword(hMeta, MODULE, "ForceSend", 0); + + if(!hContact) { + if(lParam) *(DWORD *)lParam = -1; + } else { + if(lParam) *(DWORD *)lParam = (DWORD)metaMap[hMeta].index_of(hContact); + } + + return 0; +} + +// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :) +// wParam=lParam=0 +int MetaAPI_GetProtoName(WPARAM wParam, LPARAM lParam) { + return (int)MODULE; +} + +// added 0.9.5.0 (22/3/05) +// wParam=(HANDLE)hContact +// lParam=0 +// convert a given contact into a metacontact +int MetaAPI_ConvertToMeta(WPARAM wParam, LPARAM lParam) { + return (int)Meta_Convert((HANDLE)wParam); +} + +// added 0.9.5.0 (22/3/05) +// wParam=(HANDLE)hContact +// lParam=(HANDLE)hMeta +// add an existing contact to a metacontact +int MetaAPI_AddToMeta(WPARAM wParam, LPARAM lParam) { + Meta_Assign((HANDLE)wParam, (HANDLE)lParam); + return 0; +} + +// added 0.9.5.0 (22/3/05) +// wParam=0 +// lParam=(HANDLE)hContact +// remove a contact from a metacontact +int MetaAPI_RemoveFromMeta(WPARAM wParam, LPARAM lParam) { + Meta_Remove((HANDLE)lParam); + return 0; +} + +// added 0.9.13.2 (6/10/05) +// wParam=(BOOL)disable +// lParam=0 +// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting +// should be called once in your 'onmodulesloaded' event handler +bool meta_group_hack_disabled = false; +int MetaAPI_DisableHiddenGroup(WPARAM wParam, LPARAM lParam) { + meta_group_hack_disabled = (BOOL)wParam; + return 0; +} + +int ModulesLoadedAPI(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0); + char *proto; + HANDLE hMeta; + while(hContact != NULL) { + DWORD id = DBGetContactSettingDword(hContact, MODULE, "ParentMetaID", (DWORD)-1); + if(id != (DWORD)-1) { + hMeta = GetMetaHandle(id); + if(hMeta) { + if(id >= next_meta_id) next_meta_id = id + 1; + metaMap[hMeta].add(hContact); + DBWriteContactSettingDword(hContact, MODULE, "Handle", (DWORD)hMeta); + DBWriteContactSettingByte(hContact, MODULE, "IsSubcontact", 1); + } else + DBDeleteContactSetting(hContact, MODULE, "ParentMetaID"); + } else if(IsMetacontact(hContact)) { + meta_count++; + DBWriteContactSettingWord(hContact, MODULE, "Status", ID_STATUS_OFFLINE); + } + + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + + return 0; +} + +#define NUM_API_SERVICES 17 +HANDLE hServicesAPI[NUM_API_SERVICES] = {0}; +HANDLE hEventModulesLoadedAPI = 0; +void InitAPI() { + int i = 0; + hServicesAPI[i++] = CreateServiceFunction(MS_MC_GETMETACONTACT, MetaAPI_GetMeta); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_GETDEFAULTCONTACT, MetaAPI_GetDefault); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_GETDEFAULTCONTACTNUM, MetaAPI_GetDefaultNum); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_GETMOSTONLINECONTACT, MetaAPI_GetMostOnline); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_GETNUMCONTACTS, MetaAPI_GetNumContacts); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_GETSUBCONTACT, MetaAPI_GetContact); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_SETDEFAULTCONTACTNUM, MetaAPI_SetDefaultContactNum); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_SETDEFAULTCONTACT, MetaAPI_SetDefaultContact); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_FORCESENDCONTACTNUM, MetaAPI_ForceSendContactNum); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_FORCESENDCONTACT, MetaAPI_ForceSendContact); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_UNFORCESENDCONTACT, MetaAPI_UnforceSendContact); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_GETPROTOCOLNAME, MetaAPI_GetProtoName); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_GETFORCESTATE, MetaAPI_GetForceState); + + hServicesAPI[i++] = CreateServiceFunction(MS_MC_CONVERTTOMETA, MetaAPI_ConvertToMeta); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_ADDTOMETA, MetaAPI_AddToMeta); + hServicesAPI[i++] = CreateServiceFunction(MS_MC_REMOVEFROMMETA, MetaAPI_RemoveFromMeta); + + hServicesAPI[i++] = CreateServiceFunction(MS_MC_DISABLEHIDDENGROUP, MetaAPI_DisableHiddenGroup); + + hEventDefaultChanged = CreateHookableEvent(ME_MC_DEFAULTTCHANGED); + hEventForceSend = CreateHookableEvent(ME_MC_FORCESEND); + hEventUnforceSend = CreateHookableEvent(ME_MC_UNFORCESEND); + hSubcontactsChanged = CreateHookableEvent(ME_MC_SUBCONTACTSCHANGED); + + //hEventModulesLoadedAPI = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoadedAPI); + ModulesLoadedAPI(0, 0); +} + +void DeinitAPI() { + if(hEventModulesLoadedAPI) UnhookEvent(hEventModulesLoadedAPI); + DestroyHookableEvent(hSubcontactsChanged); + DestroyHookableEvent(hEventUnforceSend); + DestroyHookableEvent(hEventForceSend); + DestroyHookableEvent(hEventDefaultChanged); + + for(int i = 0; i < NUM_API_SERVICES; i++) + DestroyServiceFunction(hServicesAPI[i]); +} \ No newline at end of file diff --git a/meta2/api.h b/meta2/api.h new file mode 100644 index 0000000..e1aa8f5 --- /dev/null +++ b/meta2/api.h @@ -0,0 +1,28 @@ +#include + +int MetaAPI_GetMeta(WPARAM wParam, LPARAM lParam); +int MetaAPI_GetDefault(WPARAM wParam, LPARAM lParam); +int MetaAPI_GetDefaultNum(WPARAM wParam, LPARAM lParam); +int MetaAPI_GetMostOnline(WPARAM wParam, LPARAM lParam); +int MetaAPI_GetNumContacts(WPARAM wParam, LPARAM lParam); +int MetaAPI_GetContact(WPARAM wParam, LPARAM lParam); +int MetaAPI_SetDefaultContactNum(WPARAM wParam, LPARAM lParam); +int MetaAPI_SetDefaultContact(WPARAM wParam, LPARAM lParam); +int MetaAPI_ForceSendContactNum(WPARAM wParam, LPARAM lParam); +int MetaAPI_ForceSendContact(WPARAM wParam, LPARAM lParam); +int MetaAPI_UnforceSendContact(WPARAM wParam, LPARAM lParam); +int MetaAPI_ForceDefault(WPARAM wParam, LPARAM lParam); +int MetaAPI_GetForceState(WPARAM wParam, LPARAM lParam); +int MetaAPI_GetProtoName(WPARAM wParam, LPARAM lParam); +int MetaAPI_ConvertToMeta(WPARAM wParam, LPARAM lParam); +int MetaAPI_AddToMeta(WPARAM wParam, LPARAM lParam); +int MetaAPI_RemoveFromMeta(WPARAM wParam, LPARAM lParam); +int MetaAPI_DisableHiddenGroup(WPARAM wParam, LPARAM lParam); + + +void FireSubcontactsChanged(HANDLE hMeta); + +extern bool meta_group_hack_disabled; +void InitAPI(); +void DeinitAPI(); + diff --git a/meta2/collection.h b/meta2/collection.h new file mode 100644 index 0000000..725bb1d --- /dev/null +++ b/meta2/collection.h @@ -0,0 +1,588 @@ +#include + +template class Collection { +protected: + unsigned long count; +public: + Collection(): count(0) {} + + virtual void clear() = 0; + virtual void add(T &val) = 0; + virtual const bool remove(T &val) = 0; + + const unsigned long size() const {return count;} +}; + +template class Node { +public: + T val; + + Node(T &v): val(v) {} + virtual ~Node() {} +}; + +template class ListNode: public Node { +public: + ListNode *next, *prev; + + ListNode(T &v): Node(v), next(0), prev(0) {} + virtual ~ListNode() { + if(next) next->prev = prev; + if(prev) prev->next = next; + } +}; + +template class LinkedList: public Collection { +protected: + ListNode *head, *tail; + +public: + class Iterator { + friend class LinkedList; + protected: + ListNode *n; + Iterator(ListNode *start): n(start) {} + public: + Iterator(const Iterator &other): n(other.n) {} + + virtual T &val() {return n->val;} + virtual void next() {if(n) n = n->next;} + virtual void prev() {if(n) n = n->prev;} + virtual const bool has_val() {return (n ? true : false); } + }; + + LinkedList(): Collection(), head(0), tail(0) {}; + LinkedList(const LinkedList &other): Collection(), head(0), tail(0) { + for(Iterator i = other.start(); i.has_val(); i.next()) + add(i.val()); + } + virtual ~LinkedList() {clear();} + + LinkedList &operator=(const LinkedList &other) { + clear(); + for(Iterator i = other.start(); i.has_val(); i.next()) + add(i.val()); + return *this; + } + + virtual void clear() { + ListNode *n; + while(head) { + n = head; + head = head->next; + delete n; + } + tail = 0; + Collection::count = 0; + } + + virtual Iterator start() const {return Iterator(head);} + + virtual void add_front(T &val) { + ListNode *n = new ListNode(val); + n->next = head; + if(head) head->prev = n; + head = n; + if(!tail) tail = n; + Collection::count++; + } + + virtual void add(T &val) { + ListNode *n = new ListNode(val); + n->prev = tail; + if(tail) tail->next = n; + tail = n; + if(!head) head = n; + Collection::count++; + } + + virtual const bool remove(T &val) { + ListNode *n = head; + while(n) { + if(n->val == val) { + if(n == head) head = head->next; + if(n == tail) tail = tail->prev; + + delete n; + Collection::count--; + return true; + } else + n = n->next; + } + + return false; + } + + virtual const bool contains(T &val) const { + ListNode *n = head; + while(n) { + if(n->val == val) { + return true; + } else + n = n->next; + } + + return false; + } + + // queue/stack functions + // stack - use push/pop + // queue - use push_back/pop + virtual void push(T val) { + add_front(val); + } + + virtual void push_back(T &val) { + add(val); + } + + virtual const bool pop(T &val) { + if(!head) return false; + + ListNode *n = head; + if(head) { + head = head->next; + if(n == tail) tail = 0; + val = n->val; + delete n; + Collection::count--; + return true; + } else + return false; + } +}; + +template class DynamicArray: public Collection { +protected: + T *ar; + + unsigned long initial, limit, increment; + +public: + class Iterator { + friend class DynamicArray; + protected: + T *ar; + unsigned long count; + unsigned long pos; + Iterator(T *a, const int c, unsigned long p): ar(a), count(c), pos(p) {} + public: + Iterator(const Iterator &other): ar(other.ar), count(other.count), pos(other.pos) {} + + virtual T &val() {return ar[pos];} + virtual void next() {pos++;} + virtual void prev() {pos--;} + virtual const bool has_val() {return pos < count; } + }; + + DynamicArray(unsigned long init = 0, unsigned long inc = 1): Collection(), ar(0), initial(init), limit(init), increment(inc) { + if(limit) ar = (T *)malloc(limit * sizeof(T)); + } + virtual ~DynamicArray() {if(ar) free(ar);} + + virtual void clear() { + Collection::count = 0; + limit = initial; + if(limit) ar = (T *)realloc(ar, limit * sizeof(T)); + else { + free(ar); + ar = 0; + } + } + + virtual Iterator start() const {return Iterator(ar, Collection::count, 0);} + + virtual void add(T &val) { + if(Collection::count == limit) { + limit += increment; + ar = (T *)realloc(ar, limit * sizeof(T)); + ar[Collection::count++] = val; + } else + ar[Collection::count++] = val; + } + + virtual void add_all(DynamicArray &other) { + for(DynamicArray::Iterator i = other.start(); i.has_val(); i.next()) { + add(i.val()); + } + } + + virtual const bool remove(T &val) { + for(unsigned long i = 0; i < Collection::count; i++) { + if(ar[i] == val) { + memmove(ar + i, ar + i + 1, (Collection::count - i) * sizeof(T)); + Collection::count--; + return true; + } + } + return false; + } + + virtual const bool remove(const unsigned long index) { + if(index >= Collection::count) return false; + + memmove(ar + index, ar + index + 1, (Collection::count - index) * sizeof(T)); + Collection::count--; + return true; + } + + virtual const bool insert(T &val, const unsigned long index) { + if(index > Collection::count) return false; + + if(Collection::count == limit) { + limit += increment; + ar = (T *)realloc(ar, limit * sizeof(T)); + } + + if(index < Collection::count) + memmove(ar + index + 1, ar + index, (Collection::count - index) * sizeof(T)); + + ar[index] = val; + Collection::count++; + return true; + } + + virtual T &operator[](const int index) { + return ar[index]; + } + + const bool index_of(const T &val, unsigned long &index) const { + for(int i = 0; i < Collection::count; i++) { + if(ar[index] == val) { + index = i; + return true; + } + } + return false; + } + + const int index_of(const T &val) const { + for(int i = 0; i < Collection::count; i++) { + if(ar[i] == val) { + return i; + } + } + return -1; + } + + // stack functions + virtual const bool pop(T &val) { + if(Collection::count) { + val = ar[Collection::count -1]; + remove(Collection::count -1); + return true; + } + return false; + } + + virtual void push(T &val) { + add(val); + } +}; + +template class SortedDynamicArray: public DynamicArray { +public: + SortedDynamicArray(unsigned long init = 0, unsigned long inc = 1): DynamicArray(init, inc) {} + virtual ~SortedDynamicArray() {} + + const bool get_index(T &val, unsigned long &index) { + unsigned long low = 0; + unsigned long high = Collection::count-1; + + while( high < Collection::count && low <= high ) + { + unsigned long i = ( low+high )/2; + if ( DynamicArray::ar[i] == val ) + { index = i; + return true; + } else + if (DynamicArray::ar[i] < val) + low = i+1; + else + high = i-1; + } + + index = low; + return false; + } + + virtual void add(T &val) { + unsigned long index; + get_index(val, index); + insert(val, index); + } +}; + +template class TreeNode: public Node { +public: + TreeNode *parent, *left, *right; + + TreeNode(T &v, TreeNode *par): Node(v), parent(par), left(0), right(0) {} + virtual ~TreeNode() { + if(parent) { + if(parent->left == this) parent->left = 0; + else parent->right = 0; + } + } +}; + +template class BinaryTree: public Collection { +protected: + + TreeNode *root; + + TreeNode *delete_node(TreeNode *n) { + if(n->left && n->right) { + //if(rand() & 1) { // ? + TreeNode *minmax = n->left; + while(minmax->right) minmax = minmax->right; + //} else { + // Node *minmax = current->right; + // while(minmax->left) minmax = minmax->left; + //} + n->val = minmax->val; + delete_node(minmax); + Collection::count--; + return n; + } else if(n->right) { + if(n->parent) { + if(n->parent->left = n) n->parent->left = n->right; + else n->parent->right = n->right; + } else + root = n->right; + n->right->parent = n->parent; + } else if(n->left) { + if(n->parent) { + if(n->parent->left = n) n->parent->left = n->left; + else n->parent->right = n->left; + } else + root = n->left; + n->left->parent = n->parent; + } else { + if(n == root) root = 0; + } + delete n; + Collection::count--; + return 0; + } + + void insert_node(TreeNode *n) { + TreeNode *current = root, *parent = 0; + while(current) { + parent = current; + if(n->val < current->val) + current = current->left; + else + current = current->right; + } + + if(parent) { + if(n->val < parent->val) { + parent->left = n; + n->parent = parent; + } else { + parent->right = n; + n->parent = parent; + } + } else + root = n; + + } + +public: + class Iterator { + friend class BinaryTree; + protected: + + class EvalNode { + public: + bool evaluate; + TreeNode *node; + + EvalNode(): evaluate(false), node(0) {} + EvalNode(const bool eval, TreeNode *n): evaluate(eval), node(n) {} + const bool operator==(const EvalNode &other) const {return node == other.node;} + EvalNode &operator=(const EvalNode &other) {evaluate = other.evaluate; node = other.node; return *this;} + + }; + + TreeNode *n; + LinkedList stack; + + + Iterator(TreeNode *start): n(0) { + if(start) { + stack.push(EvalNode(true, start)); + next(); + } + } + + public: + Iterator(const Iterator &other):n(other.n), stack(other.stack) {} + virtual ~Iterator() {} + + virtual T &val() {return n->val;} + virtual void next() { + EvalNode en; + bool popped = false; + while((popped = stack.pop(en)) && en.evaluate) { + if(en.node->right) stack.push(EvalNode(true, en.node->right)); + stack.push(EvalNode(false, en.node)); + if(en.node->left) stack.push(EvalNode(true, en.node->left)); + } + + n = (popped ? en.node : 0); + } + virtual const bool has_val() {return (n ? true : false);} + }; + + BinaryTree(): Collection(), root(0) {}; + BinaryTree(BinaryTree &other): Collection(), root(0) { + for(Iterator i = other.start(); i.has_val(); i.next()) + add(i.val()); + } + virtual ~BinaryTree() {clear();} + + BinaryTree &operator=(BinaryTree &other) { + clear(); + for(Iterator i = other.start(); i.has_val(); i.next()) + add(i.val()); + return *this; + } + + virtual void clear() { + TreeNode *current = root, *parent = 0; + while(current) { + if(current->left) current = current->left; + else if(current->right) current = current->right; + else { + parent = current->parent; + delete current; + current = parent; + } + } + + root = 0; + Collection::count = 0; + } + + void add(T &val) { + TreeNode *n = new TreeNode(val, 0); + insert_node(n); + Collection::count++; + } + + const bool remove(T &val) { + TreeNode *current = root; + while(current) { + if(current->val == val) + break; + else if(val < current->val) + current = current->left; + else + current = current->right; + } + + if(current) { + delete_node(current); + return true; + } + + return false; + } + + const bool contains(T &val) const { + TreeNode *current = root; + while(current) { + if(current->val == val) + break; + else if(val < current->val) + current = current->left; + else + current = current->right; + } + + return current != 0; + } + + Iterator start() const {return Iterator(root);} +}; + +template class Pair { +public: + A first; + B second; + + Pair(const A &f): first(f) {} + Pair(const A &f, const B &s): first(f), second(s) {} + Pair(Pair &other): first(other.first), second(other.second) {} + virtual ~Pair() {} + + const bool operator<(const Pair &other) const {return first < other.first;} + const bool operator==(const Pair &other) const {return first == other.first;} + Pair &operator=(const Pair &other) {first = other.first; second = other.second; return *this;} +}; + +template class Map: public BinaryTree< Pair< A, B > > { +protected: + + TreeNode > *find(A &val) const { + TreeNode > *n = BinaryTree< Pair< A, B > >::root; + while(n) { + if(n->val.first == val) + return n; + else if(val < n->val.first) + n = n->left; + else + n = n->right; + + } + return 0; + } +public: + Map(): BinaryTree< Pair >() {} + virtual ~Map() {} + + void put(A &key, B &value) { + add(Pair(key, value)); + } + + const bool get(A &key, B &val) const { + const TreeNode > *n = find(key); + if(n) { + val = n->val.second; + return true; + } else + return false; + } + + B &operator[](A &key) { + TreeNode > *n = find(key); + if(n) + return n->val.second; + else { + Pair< A, B > p(key); + TreeNode > *n = new TreeNode >(p, 0); + insert_node(n); + Collection< Pair< A, B > >::count++; + return n->val.second; + } + } + + virtual const bool exists(A &key) const { + const TreeNode > *n = find(key); + if(n) { + return true; + } else + return false; + } + + virtual const bool remove(A &key) { + TreeNode > *n = find(key); + if(n) { + delete_node(n); + return true; + } else + return false; + } +}; diff --git a/meta2/common.cpp b/meta2/common.cpp new file mode 100644 index 0000000..fe22092 --- /dev/null +++ b/meta2/common.cpp @@ -0,0 +1,11 @@ +#include "common.h" + +bool ContactHandle::operator==(const ContactHandle &other) const { + return hContact == other.handle(); +} + +bool ContactHandle::operator<(const ContactHandle &other) const { + //return CallService(MS_CLIST_CONTACTSCOMPARE, (WPARAM)hContact, (LPARAM)other.handle()) < 0; + return hContact < other.handle(); +} + diff --git a/meta2/common.h b/meta2/common.h new file mode 100644 index 0000000..c613ef5 --- /dev/null +++ b/meta2/common.h @@ -0,0 +1,134 @@ +#ifndef _COMMON_INC +#define _COMMON_INC + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. +#endif + +#if defined( UNICODE ) && !defined( _UNICODE ) +#define _UNICODE +#endif + +#include + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include +#include + +#define MIRANDA_VER 0x0700 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "collection.h" + +//////////// +// included for backward compatibility +#ifndef CMIF_UNICODE +#define CMIF_UNICODE 512 //will return TCHAR* instead of char* +#if defined( _UNICODE ) + #define CMIF_TCHAR CMIF_UNICODE //will return TCHAR* instead of char* +#else + #define CMIF_TCHAR 0 //will return char*, as usual +#endif +#endif +//////////// + +#define MODULE "meta2" + +extern HINSTANCE hInst; +extern PLUGINLINK *pluginLink; +extern MM_INTERFACE mmi; +extern UTF8_INTERFACE utfi; +extern HANDLE metaMainThread; +extern DWORD next_meta_id; + +extern int meta_count; + +#ifndef MIID_META2 +#define MIID_META2 { 0x4415A85D, 0xD6DA, 0x4551, { 0xB2, 0xB8, 0x9B, 0xDD, 0x82, 0xE2, 0x4B, 0x50 } } +#endif + +#define MAX_SUBCONTACTS 20 + +inline bool MetaEnabled() { + return DBGetContactSettingByte(0, MODULE, "Enabled", 1) == 1; +} + +inline bool IsMetacontact(HANDLE hContact) { + return DBGetContactSettingDword(hContact, MODULE, "MetaID", (DWORD)-1) != (DWORD)-1; +} + +inline bool IsSubcontact(HANDLE hContact) { + return DBGetContactSettingByte(hContact, MODULE, "IsSubcontact", 0) == 1; +} + +inline char *ContactProto(HANDLE hContact) { + return (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); +} + +inline WORD ContactStatus(HANDLE hContact, char *proto) { + if(!proto) return ID_STATUS_OFFLINE; + return DBGetContactSettingWord(hContact, proto, "Status", ID_STATUS_OFFLINE); +} + +class ContactHandle { +public: + ContactHandle(const HANDLE &hCon): hContact(hCon) {} + virtual ~ContactHandle() {} + + HANDLE handle() const {return hContact;} + //operator HANDLE () const {return hContact;} + + bool operator==(const ContactHandle &other) const; + bool operator<(const ContactHandle &other) const; + const ContactHandle &operator=(HANDLE h) {hContact = h;} +protected: + HANDLE hContact; +}; + +class SubcontactList: public SortedDynamicArray { +public: + + void add(HANDLE h) { + SortedDynamicArray::add(ContactHandle(h)); + } + + void remove(HANDLE h) { + SortedDynamicArray::remove(ContactHandle(h)); + } + +}; + + +#endif diff --git a/meta2/core_functions.cpp b/meta2/core_functions.cpp new file mode 100644 index 0000000..aba22c0 --- /dev/null +++ b/meta2/core_functions.cpp @@ -0,0 +1,138 @@ +#include "common.h" +#include "core_functions.h" +#include "proto.h" +#include "api.h" +#include "priorities.h" + +MetaMap metaMap; + +HANDLE GetMetaHandle(DWORD id) { + HANDLE hContact = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0); + char *proto; + while(hContact) { + proto = ContactProto(hContact); + if(proto && !strcmp(proto, MODULE)) { + DWORD mid = DBGetContactSettingDword(hContact, MODULE, META_ID, (DWORD)-1); + if(mid == id) return hContact; + } + + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + return 0; +} + +void Meta_Hide(bool hide) { + HANDLE hContact = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0); + char *proto; + HANDLE hMeta; + while(hContact != NULL) { + if(IsMetacontact(hContact)) { + DBWriteContactSettingByte(hContact, "CList", "Hidden", hide ? 1 : 0); + } else if(IsSubcontact(hContact) && !meta_group_hack_disabled) { + DBWriteContactSettingByte(hContact, "CList", "Hidden", hide ? 0 : 1); + } + + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } +} + +HANDLE Meta_GetMostOnline(HANDLE hMeta) { + return Meta_GetMostOnlineSupporting(hMeta, PFLAGNUM_1, PF1_IM); +} + +HANDLE Meta_GetMostOnlineSupporting(HANDLE hMeta, int flag, int cap) { + if(!metaMap.exists(hMeta)) return 0; + + HANDLE most_online = (HANDLE)MetaAPI_GetDefault((WPARAM)hMeta, 0); + char *most_online_proto = ContactProto(most_online); + int most_online_status = ContactStatus(most_online, most_online_proto); + int most_online_prio = GetRealPriority(most_online_proto, most_online_status); + + char *proto; + int status, prio; + SubcontactList::Iterator i = metaMap[hMeta].start(); + while(i.has_val()) { + proto = ContactProto(i.val().handle()); + if(proto && (CallContactService(i.val().handle(), PS_GETCAPS, flag, 0) & cap)) { + status = ContactStatus(i.val().handle(), proto); + if((prio = GetRealPriority(proto, status)) < most_online_prio) { + most_online_status = status; + most_online = i.val().handle(); + most_online_proto = proto; + most_online_prio = prio; + } + } + i.next(); + } + + return most_online; +} + +void Meta_CalcStatus(HANDLE hMeta) { + HANDLE hSub = Meta_GetMostOnline(hMeta); + char *proto = ContactProto(hSub); + DBWriteContactSettingWord(hMeta, MODULE, "Status", ContactStatus(hSub, proto)); +} + +HANDLE Meta_Convert(HANDLE hSub) { + HANDLE hMeta = NewMetaContact(); + + DBWriteContactSettingByte(hMeta, MODULE, "Default", 0); + Meta_Assign(hSub, hMeta); + + DBVARIANT dbv; + if(!DBGetContactSettingUTF8String(hSub, "CList", "Group", &dbv)) { + DBWriteContactSettingUTF8String(hMeta, "CList", "Group", dbv.pszVal); + DBFreeVariant(&dbv); + } + if(!DBGetContactSettingUTF8String(hSub, "CList", "MyHandle", &dbv)) { + DBWriteContactSettingUTF8String(hMeta, "CList", "MyHandle", dbv.pszVal); + DBFreeVariant(&dbv); + } + + char *subProto = ContactProto(hSub); + if(subProto) { + if(!DBGetContactSettingUTF8String(hSub, subProto, "Nick", &dbv)) { + DBWriteContactSettingUTF8String(hMeta, MODULE, "Nick", dbv.pszVal); + DBFreeVariant(&dbv); + } + } + + Meta_CalcStatus(hMeta); + return hMeta; +} + +void Meta_Assign(HANDLE hSub, HANDLE hMeta) { + metaMap[hMeta].add(hSub); + DBWriteContactSettingDword(hSub, MODULE, "ParentMetaID", DBGetContactSettingDword(hMeta, MODULE, META_ID, -1)); + DBWriteContactSettingDword(hSub, MODULE, "Handle", (DWORD)hMeta); + DBWriteContactSettingByte(hSub, MODULE, "IsSubcontact", 1); + if(MetaEnabled()) { + if(!meta_group_hack_disabled) + DBWriteContactSettingByte(hSub, "CList", "Hidden", 1); + } else // shouldn't happen, as the menu option is hidden when metas are disabled... + DBWriteContactSettingByte(hMeta, "CList", "Hidden", 1); + + FireSubcontactsChanged(hMeta); +} + +void Meta_Remove(HANDLE hSub) { + HANDLE hMeta = (HANDLE)DBGetContactSettingDword(hSub, MODULE, "Handle", 0); + if(hMeta) { + DBDeleteContactSetting(hSub, MODULE, "ParentMetaID"); + // deleting these (resident) settings doesn't work :( [25/9/07] + DBWriteContactSettingDword(hSub, MODULE, "Handle", 0); + DBWriteContactSettingByte(hSub, MODULE, "IsSubcontact", 0); + if(!meta_group_hack_disabled) DBWriteContactSettingByte(hSub, "CList", "Hidden", 0); + + metaMap[hMeta].remove(hSub); + if(metaMap[hMeta].size() == 0) { + CallService(MS_DB_CONTACT_DELETE, (WPARAM)hMeta, 0); + } else { + int def = DBGetContactSettingByte(hMeta, MODULE, "Default", -1); + if(def < 0 || def >= metaMap[hMeta].size()) + DBWriteContactSettingByte(hMeta, MODULE, "Default", 0); + } + FireSubcontactsChanged(hMeta); + } +} \ No newline at end of file diff --git a/meta2/core_functions.h b/meta2/core_functions.h new file mode 100644 index 0000000..8f6a6ee --- /dev/null +++ b/meta2/core_functions.h @@ -0,0 +1,25 @@ +class MetaMap: public Map { +public: + SubcontactList &operator[](HANDLE h) { + return Map::operator[](ContactHandle(h)); + } + + const bool exists(HANDLE h) const { + return Map::exists(ContactHandle(h)); + } + + const bool remove(HANDLE h) { + return Map::remove(ContactHandle(h)); + } +}; + +extern MetaMap metaMap; + +HANDLE GetMetaHandle(DWORD id); +void Meta_Hide(bool hide); +HANDLE Meta_GetMostOnline(HANDLE hMeta); +HANDLE Meta_GetMostOnlineSupporting(HANDLE hMeta, int flag, int cap); +HANDLE Meta_Convert(HANDLE hSub); +void Meta_CalcStatus(HANDLE hMeta); +void Meta_Assign(HANDLE hSub, HANDLE hMeta); +void Meta_Remove(HANDLE hSub); \ No newline at end of file diff --git a/meta2/edit_meta.cpp b/meta2/edit_meta.cpp new file mode 100644 index 0000000..a43bec1 --- /dev/null +++ b/meta2/edit_meta.cpp @@ -0,0 +1,243 @@ +#include "common.h" +#include "edit_meta.h" +#include "resource.h" +#include "core_functions.h" +#include "icons.h" +#include "api.h" + +TCHAR *GetIdString(HANDLE hContact, char *proto) { + if(proto) { + char *field = (char *)CallProtoService(proto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0); + DBVARIANT dbv; + + if(!DBGetContactSetting(hContact,proto,field,&dbv)) { + TCHAR *buff = 0; + switch(dbv.type) + { + case DBVT_ASCIIZ: + return mir_a2t(dbv.pszVal); + case DBVT_UTF8: +#ifdef _UNICODE + return mir_utf8decodeW(dbv.pszVal); +#else + return mir_utf8decode(dbv.pszVal, 0); +#endif + case DBVT_WCHAR: + return mir_u2t(dbv.pwszVal); + case DBVT_BYTE: + buff = (TCHAR *)mir_alloc(4 * sizeof(TCHAR)); + mir_sntprintf(buff, 4, _T("%d"),dbv.bVal); + return buff; + case DBVT_WORD: + buff = (TCHAR *)mir_alloc(16 * sizeof(TCHAR)); + mir_sntprintf(buff, 16, _T("%d"),dbv.wVal); + return buff; + case DBVT_DWORD: + buff = (TCHAR *)mir_alloc(32 * sizeof(TCHAR)); + mir_sntprintf(buff, 32, _T("%d"),dbv.dVal); + return buff; + default: + return 0; + } + DBFreeVariant(&dbv); + } + } + return 0; +} + +HANDLE hMeta; +int def; +SubcontactList subs; +bool changed; + +void FillList(HWND hwndLst) { + LVITEM LvItem = {0}; + LvItem.mask=LVIF_TEXT; // Text Style + LvItem.cchTextMax = 256; // Max size of test + + int row = 0; + char *proto; + TCHAR *proto_t; + + SendMessage(hwndLst, LVM_DELETEALLITEMS, 0, 0); + + for(SubcontactList::Iterator i = subs.start(); i.has_val(); i.next()) { + LvItem.iItem = row; + + LvItem.iSubItem = 0; // clist display name + LvItem.pszText = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)i.val().handle(), GCDNF_TCHAR); + SendMessage(hwndLst, LVM_INSERTITEM, (WPARAM)0, (LPARAM)&LvItem); + + proto = ContactProto(i.val().handle()); + + LvItem.iSubItem = 1; // id + LvItem.pszText = GetIdString(i.val().handle(), proto); + SendMessage(hwndLst, LVM_SETITEM, (WPARAM)0, (LPARAM)&LvItem); + mir_free(LvItem.pszText); + +#ifdef _UNICODE + proto_t = mir_a2u(proto); +#else + proto_t = proto; +#endif + LvItem.iSubItem = 2; // protocol + LvItem.pszText = proto_t; + SendMessage(hwndLst, LVM_SETITEM, (WPARAM)0, (LPARAM)&LvItem); +#ifdef _UNICODE + mir_free(proto_t); +#endif; + + LvItem.iSubItem = 3; // default + LvItem.pszText = (row == def ? TranslateT("TRUE") : TranslateT("FALSE")); + SendMessage(hwndLst, LVM_SETITEM, (WPARAM)0, (LPARAM)&LvItem); + + row++; + } +} + +void SetListSelection(HWND hList, int sel) { + LVITEM LvItem; + + ZeroMemory(&LvItem, sizeof(LvItem)); + LvItem.iItem = sel; + LvItem.mask = LVIF_STATE; + LvItem.stateMask = LVIS_SELECTED|LVIS_FOCUSED; + LvItem.state = LVIS_SELECTED|LVIS_FOCUSED; + + SendMessage(hList, LVM_SETITEMSTATE, (WPARAM)sel, (LPARAM)&LvItem); + +} + +void ApplyChanges() { + if(def >= 0 && def < metaMap[hMeta].size()) + MetaAPI_SetDefaultContactNum((WPARAM)hMeta, (LPARAM)def); + else + MetaAPI_SetDefaultContactNum((WPARAM)hMeta, (LPARAM)0); + + unsigned long index; + for(SubcontactList::Iterator i = metaMap[hMeta].start(); i.has_val(); i.next()) { + if(!subs.get_index(i.val(), index)) { + Meta_Remove(i.val().handle()); + } + } +} + +#define WMU_SET_BUTTONS (WM_USER + 0x100) +BOOL CALLBACK Meta_EditDialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + switch(msg) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIconEx(I_EDIT)); + + SetDlgItemText(hwndDlg, IDC_ED_NAME, (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hMeta, GCDNF_TCHAR)); + + { + HWND hwndLst = GetDlgItem(hwndDlg, IDC_LST_CONTACTS); + SendMessage(hwndLst,LVM_SETEXTENDEDLISTVIEWSTYLE, 0,LVS_EX_FULLROWSELECT); // Set style + + LVCOLUMN LvCol = {0}; + LvCol.mask=LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM; + + LvCol.pszText=TranslateT("Contact"); + LvCol.cx=100; + SendMessage(hwndLst,LVM_INSERTCOLUMN,0,(LPARAM)&LvCol); + + LvCol.pszText=TranslateT("Id"); + LvCol.cx=130; + SendMessage(hwndLst,LVM_INSERTCOLUMN,1,(LPARAM)&LvCol); + LvCol.pszText=TranslateT("Protocol"); + LvCol.cx=100; + SendMessage(hwndLst,LVM_INSERTCOLUMN,2,(LPARAM)&LvCol); + LvCol.pszText=TranslateT("Default"); + LvCol.cx=60; + SendMessage(hwndLst,LVM_INSERTCOLUMN,3,(LPARAM)&LvCol); + + FillList(hwndLst); + SendMessage(hwndDlg, WMU_SET_BUTTONS, 0, 0); + } + + break; + case WMU_SET_BUTTONS: + { + HWND hwnd = GetDlgItem(hwndDlg, IDOK); + EnableWindow(hwnd, changed); + hwnd = GetDlgItem(hwndDlg, IDC_VALIDATE); + EnableWindow(hwnd, changed); + + int index = SendDlgItemMessage(hwndDlg, IDC_LST_CONTACTS, LVM_GETNEXTITEM,-1,LVNI_FOCUSED|LVNI_SELECTED); // return item selected + hwnd = GetDlgItem(hwndDlg, IDC_BTN_SETDEFAULT); + EnableWindow(hwnd, index != -1 && index != def); + hwnd = GetDlgItem(hwndDlg, IDC_BTN_REM); + EnableWindow(hwnd, index != -1); + + } + return TRUE; + case WM_COMMAND: + if(HIWORD(wParam) == BN_CLICKED) { + switch(LOWORD(wParam)) + { + case IDC_BTN_SETDEFAULT: + { + int index = SendDlgItemMessage(hwndDlg, IDC_LST_CONTACTS, LVM_GETNEXTITEM,-1,LVNI_FOCUSED|LVNI_SELECTED); // return item selected + def = index; + changed = true; + FillList(GetDlgItem(hwndDlg, IDC_LST_CONTACTS)); + SetListSelection(GetDlgItem(hwndDlg, IDC_LST_CONTACTS), index); + SendMessage(hwndDlg, WMU_SET_BUTTONS, 0, 0); + } + break; + case IDC_BTN_REM: + { + int index = SendDlgItemMessage(hwndDlg, IDC_LST_CONTACTS, LVM_GETNEXTITEM,-1,LVNI_FOCUSED|LVNI_SELECTED); // return item selected + if(index >= 0 && index < subs.size()) { + subs.remove(subs[index].handle()); + if(def >= index && def > 0) def--; + changed = true; + FillList(GetDlgItem(hwndDlg, IDC_LST_CONTACTS)); + SendMessage(hwndDlg, WMU_SET_BUTTONS, 0, 0); + } + } + break; + case IDOK: + EndDialog(hwndDlg, IDOK); + break; + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + case IDC_VALIDATE: + ApplyChanges(); + break; + } + return TRUE; + } + break; + case WM_NOTIFY: // the message that is being sent always + switch(LOWORD(wParam)) // hit control + { + case IDC_LST_CONTACTS: // did we hit our ListView contorl? + if(((LPNMHDR)lParam)->code == NM_CLICK) { + SendMessage(hwndDlg, WMU_SET_BUTTONS, 0, 0); + } + break; + } + break; + + } + return FALSE; +} + +void EditMeta(HANDLE hM) { + hMeta = hM; + subs.add_all(metaMap[hMeta]); + def = (int)DBGetContactSettingByte(hMeta, MODULE, "Default", -1); + changed = false; + + HWND clui = (HWND)CallService(MS_CLUI_GETHWND,0,0); + if(DialogBox(hInst, MAKEINTRESOURCE(IDD_METAEDIT), clui, Meta_EditDialogProc) == IDOK) { + + // apply changes + + ApplyChanges(); + } + subs.clear(); +} \ No newline at end of file diff --git a/meta2/edit_meta.h b/meta2/edit_meta.h new file mode 100644 index 0000000..f102bd1 --- /dev/null +++ b/meta2/edit_meta.h @@ -0,0 +1,2 @@ + +void EditMeta(HANDLE hMeta); \ No newline at end of file diff --git a/meta2/icons.cpp b/meta2/icons.cpp new file mode 100644 index 0000000..bf28b8c --- /dev/null +++ b/meta2/icons.cpp @@ -0,0 +1,102 @@ +#include "common.h" +#include "icons.h" +#include "resource.h" +#include "menu.h" + +HANDLE hIcoLibIconsChanged = NULL; + + +typedef struct { + char* szDescr; + char* szName; + int defIconID; + HANDLE hIcolib; +} IconStruct; + +static IconStruct iconList[] = { + { "Toggle Off", "mc_off", IDI_MCMENUOFF, 0}, + { "Toggle On", "mc_on", IDI_MCMENU, 0}, + { "Convert to MetaContact", "mc_convert", IDI_MCCONVERT, 0}, + { "Add to Existing", "mc_add", IDI_MCADD, 0}, + { "Edit", "mc_edit", IDI_MCEDIT, 0}, + { "Set to Default", "mc_default", IDI_MCSETDEFAULT, 0}, + { "Remove", "mc_remove", IDI_MCREMOVE, 0}, +}; + + +HICON LoadIconEx(IconIndex i) { + HICON hIcon; + + hIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)iconList[(int)i].szName); + + return hIcon; +} + +HANDLE GetIcolibHandle(IconIndex i) { + return iconList[i].hIcolib; +} + +void ReleaseIconEx(HICON hIcon) { + CallService(MS_SKIN2_RELEASEICON, (WPARAM)hIcon, 0); +} + +int ReloadIcons(WPARAM wParam, LPARAM lParam) { + // fix menu icons + CLISTMENUITEM menu = {0}; + + menu.cbSize = sizeof(menu); + menu.flags = CMIM_ICON; + + menu.hIcon = LoadIconEx(MetaEnabled() ? I_MENUOFF : I_MENU); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuOnOff, (LPARAM)&menu); + ReleaseIconEx(menu.hIcon); + + menu.hIcon = LoadIconEx(I_CONVERT); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuConvert, (LPARAM)&menu); + ReleaseIconEx(menu.hIcon); + + menu.hIcon = LoadIconEx(I_ADD); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuAdd, (LPARAM)&menu); + ReleaseIconEx(menu.hIcon); + + menu.hIcon = LoadIconEx(I_EDIT); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuEdit, (LPARAM)&menu); + ReleaseIconEx(menu.hIcon); + + menu.hIcon = LoadIconEx(I_SETDEFAULT); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuDefault, (LPARAM)&menu); + ReleaseIconEx(menu.hIcon); + + menu.hIcon = LoadIconEx(I_REMOVE); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuRemove, (LPARAM)&menu); + ReleaseIconEx(menu.hIcon); + + return 0; +} + +void InitIcons() { + SKINICONDESC sid = {0}; + char path[MAX_PATH]; + int i; + + sid.cbSize = sizeof(SKINICONDESC); + sid.pszSection = MODULE; + sid.pszDefaultFile = path; + GetModuleFileNameA(hInst, path, sizeof(path)); + + for (i = 0; i < sizeof(iconList) / sizeof(IconStruct); ++i) + { + sid.pszDescription = Translate(iconList[i].szDescr); + sid.pszName = iconList[i].szName; + sid.iDefaultIndex = -iconList[i].defIconID; + iconList[i].hIcolib = (HANDLE)CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid); + } + + //hIcoLibIconsChanged = HookEvent(ME_SKIN2_ICONSCHANGED, ReloadIcons); + + //ReloadIcons(0, 0); +} + +void DeinitIcons() { + UnhookEvent(hIcoLibIconsChanged); +} diff --git a/meta2/icons.h b/meta2/icons.h new file mode 100644 index 0000000..630e6e9 --- /dev/null +++ b/meta2/icons.h @@ -0,0 +1,12 @@ +#ifndef _ICONS_INC +#define _ICONS_INC + +typedef enum {I_MENUOFF, I_MENU, I_CONVERT, I_ADD, I_EDIT, I_SETDEFAULT, I_REMOVE} IconIndex; +HICON LoadIconEx(IconIndex i); +HANDLE GetIcolibHandle(IconIndex i); +void ReleaseIconEx(HICON hIcon); + +void InitIcons(void); +void DeinitIcons(void); + +#endif diff --git a/meta2/icons/mcmenu.ico b/meta2/icons/mcmenu.ico new file mode 100644 index 0000000..66a521a Binary files /dev/null and b/meta2/icons/mcmenu.ico differ diff --git a/meta2/icons/mcmenuof.ico b/meta2/icons/mcmenuof.ico new file mode 100644 index 0000000..37443ec Binary files /dev/null and b/meta2/icons/mcmenuof.ico differ diff --git a/meta2/icons/mcmenuoff2.ico b/meta2/icons/mcmenuoff2.ico new file mode 100644 index 0000000..c9cda8b Binary files /dev/null and b/meta2/icons/mcmenuoff2.ico differ diff --git a/meta2/icons/meta_add.ico b/meta2/icons/meta_add.ico new file mode 100644 index 0000000..32bc5a9 Binary files /dev/null and b/meta2/icons/meta_add.ico differ diff --git a/meta2/icons/meta_convert.ico b/meta2/icons/meta_convert.ico new file mode 100644 index 0000000..da56997 Binary files /dev/null and b/meta2/icons/meta_convert.ico differ diff --git a/meta2/icons/meta_edit.ico b/meta2/icons/meta_edit.ico new file mode 100644 index 0000000..87e8b19 Binary files /dev/null and b/meta2/icons/meta_edit.ico differ diff --git a/meta2/icons/meta_remove2.ico b/meta2/icons/meta_remove2.ico new file mode 100644 index 0000000..9bf7265 Binary files /dev/null and b/meta2/icons/meta_remove2.ico differ diff --git a/meta2/icons/meta_set_as_default.ico b/meta2/icons/meta_set_as_default.ico new file mode 100644 index 0000000..c608232 Binary files /dev/null and b/meta2/icons/meta_set_as_default.ico differ diff --git a/meta2/import.cpp b/meta2/import.cpp new file mode 100644 index 0000000..abdab20 --- /dev/null +++ b/meta2/import.cpp @@ -0,0 +1,46 @@ +#include "common.h" +#include "import.h" +#include "core_functions.h" +#include "proto.h" + +void ImportOldMetas() { + HANDLE hContact = (HANDLE)CallService( MS_DB_CONTACT_FINDFIRST, 0, 0); + char *proto; + HANDLE hMeta; + while(hContact != NULL) { + DWORD id = DBGetContactSettingDword(hContact, "MetaContacts", "MetaLink", (DWORD)-1); + if(id != (DWORD)-1) { + if(id > next_meta_id) next_meta_id = id + 1; + hMeta = GetMetaHandle(id); + if(hMeta) { + Meta_Assign(hContact, hMeta); + } else { + hMeta = NewMetaContact(); + DBWriteContactSettingDword(hMeta, MODULE, META_ID, id); + + DBWriteContactSettingByte(hMeta, MODULE, "Default", 0); + Meta_Assign(hContact, hMeta); + + DBVARIANT dbv; + if(!DBGetContactSettingUTF8String(hContact, "CList", "Group", &dbv)) { + DBWriteContactSettingUTF8String(hMeta, "CList", "Group", dbv.pszVal); + DBFreeVariant(&dbv); + } + if(!DBGetContactSettingUTF8String(hContact, "CList", "MyHandle", &dbv)) { + DBWriteContactSettingUTF8String(hMeta, "CList", "MyHandle", dbv.pszVal); + DBFreeVariant(&dbv); + } + + char *subProto = ContactProto(hContact); + if(subProto) { + if(!DBGetContactSettingUTF8String(hContact, subProto, "Nick", &dbv)) { + DBWriteContactSettingUTF8String(hMeta, MODULE, "Nick", dbv.pszVal); + DBFreeVariant(&dbv); + } + } + } + } + + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } +} \ No newline at end of file diff --git a/meta2/import.h b/meta2/import.h new file mode 100644 index 0000000..c18996c --- /dev/null +++ b/meta2/import.h @@ -0,0 +1,2 @@ + +void ImportOldMetas(); \ No newline at end of file diff --git a/meta2/m_metacontacts.h b/meta2/m_metacontacts.h new file mode 100644 index 0000000..9f348bd --- /dev/null +++ b/meta2/m_metacontacts.h @@ -0,0 +1,166 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. +Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au) + +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. +*/ + +#ifndef M_METACONTACTS_H__ +#define M_METACONTACTS_H__ 1 + +#ifndef MIID_METACONTACTS +#define MIID_METACONTACTS {0xc0325019, 0xc1a7, 0x40f5, { 0x83, 0x65, 0x4f, 0x46, 0xbe, 0x21, 0x86, 0x3e}} +#endif + +//get the handle for a contact's parent metacontact +//wParam=(HANDLE)hSubContact +//lParam=0 +//returns a handle to the parent metacontact, or null if this contact is not a subcontact +#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta" + +//gets the handle for the default contact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a handle to the default contact, or null on failure +#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault" + +//gets the contact number for the default contact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a DWORD contact number, or -1 on failure +#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum" + +//gets the handle for the 'most online' contact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a handle to the 'most online' contact +#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline" + +//gets the number of subcontacts for a metacontact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a DWORD representing the number of subcontacts for the given metacontact +#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts" + +//gets the handle of a subcontact, using the subcontact's number +//wParam=(HANDLE)hMetaContact +//lParam=(DWORD)contact number +//returns a handle to the specified subcontact +#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact" + +//sets the default contact, using the subcontact's contact number +//wParam=(HANDLE)hMetaContact +//lParam=(DWORD)contact number +//returns 0 on success +#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault" + +//sets the default contact, using the subcontact's handle +//wParam=(HANDLE)hMetaContact +//lParam=(HANDLE)hSubcontact +//returns 0 on success +#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle" + +//forces the metacontact to send using a specific subcontact, using the subcontact's contact number +//wParam=(HANDLE)hMetaContact +//lParam=(DWORD)contact number +//returns 0 on success +#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact" + +//forces the metacontact to send using a specific subcontact, using the subcontact's handle +//wParam=(HANDLE)hMetaContact +//lParam=(HANDLE)hSubcontact +//returns 0 on success (will fail if 'force default' is in effect) +#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle" + +//'unforces' the metacontact to send using a specific subcontact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns 0 on success (will fail if 'force default' is in effect) +#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact" + +//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact +// overrides (and clears) 'force send' above, and will even force use of offline contacts +// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns 1(true) or 0(false) representing new state of 'force default' +#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault" + +// method to get state of 'force' for a metacontact +// wParam=(HANDLE)hMetaContact +// lParam= (DWORD)&contact_number or NULL +// +// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to, +// or if none is in force, the value (DWORD)-1 will be copied +// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above) +#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState" + +// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set) +// wParam=(HANDLE)hMetaContact +// lParam=(HANDLE)hDefaultContact +#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged" + +// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when +// contacts are reordered) - a signal to re-read metacontact data +// wParam=(HANDLE)hMetaContact +// lParam=0 +#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged" + +// fired when a metacontact is forced to send using a specific subcontact +// wParam=(HANDLE)hMetaContact +// lParam=(HANDLE)hForceContact +#define ME_MC_FORCESEND "MetaContacts/ForceSend" + +// fired when a metacontact is 'unforced' to send using a specific subcontact +// wParam=(HANDLE)hMetaContact +// lParam=0 +#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend" + +// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :) +// wParam=lParam=0 +#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName" + + +// added 0.9.5.0 (22/3/05) +// wParam=(HANDLE)hContact +// lParam=0 +// convert a given contact into a metacontact +#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact" + +// added 0.9.5.0 (22/3/05) +// wParam=(HANDLE)hContact +// lParam=(HANDLE)hMeta +// add an existing contact to a metacontact +#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact" + +// added 0.9.5.0 (22/3/05) +// wParam=0 +// lParam=(HANDLE)hContact +// remove a contact from a metacontact +#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact" + + +// added 0.9.13.2 (6/10/05) +// wParam=(BOOL)disable +// lParam=0 +// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting +// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called +// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done) +#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup" + +#endif diff --git a/meta2/menu.cpp b/meta2/menu.cpp new file mode 100644 index 0000000..68c3674 --- /dev/null +++ b/meta2/menu.cpp @@ -0,0 +1,265 @@ +#include "common.h" +#include "menu.h" +#include "resource.h" +#include "icons.h" +#include "core_functions.h" +#include "api.h" +#include "select_meta.h" +#include "edit_meta.h" + +HANDLE hServiceMenuOnOff = 0, hServiceMenuConvert = 0, hServiceMenuAdd = 0, hServiceMenuEdit = 0, + hServiceMenuDefault = 0, hServiceMenuRemove = 0, hServiceToggle = 0; +HANDLE hMenuOnOff = 0, hMenuConvert = 0, hMenuAdd = 0, hMenuEdit = 0, hMenuDefault = 0, hMenuRemove = 0; +POINT menuMousePoint; + +int ServiceMenuOnOff(WPARAM wParam, LPARAM lParam) { + DBWriteContactSettingByte(0, MODULE, "Enabled", MetaEnabled() ? 0 : 1); + Meta_Hide(!MetaEnabled()); + + CLISTMENUITEM menu = {0}; + menu.cbSize=sizeof(menu); + menu.flags = CMIM_NAME | CMIM_ICON | CMIF_TCHAR | CMIF_ICONFROMICOLIB; + menu.ptszName = MetaEnabled() ? _T("Disable MetaContacts") : _T("Enable MetaContacts"); + menu.icolibItem = GetIcolibHandle(MetaEnabled() ? I_MENUOFF : I_MENU); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuOnOff, (LPARAM)&menu); + + return 0; +} + +HANDLE hEventMenuBuild = 0; + +int ContactMenuConvert(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + Meta_Convert(hContact); + return 0; +} + +int ContactMenuAdd(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + TCHAR buff[256]; + mir_sntprintf(buff, 256, TranslateT("Adding %s..."), (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR)); + HANDLE hMeta = SelectMeta(buff); + if(hMeta) + Meta_Assign(hContact, hMeta); + return 0; +} + +int ContactMenuRemove(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + + Meta_Remove(hContact); + return 0; +} + +int ContactMenuEdit(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + + EditMeta(hContact); + return 0; +} + +int ContactMenuDefault(WPARAM wParam, LPARAM lParam) { + HANDLE hSub = (HANDLE)wParam, + hMeta = (HANDLE)DBGetContactSettingDword(hSub, MODULE, "Handle", 0); + + MetaAPI_SetDefaultContact((WPARAM)hMeta, (LPARAM)hSub); + return 0; +} + +// show contact's context menu +DWORD CALLBACK sttMenuFunc( LPVOID param ) +{ + HMENU hMenu; + TPMPARAMS tpmp = {0}; + BOOL menuRet; + HANDLE hSub = (HANDLE)param; + + hMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)hSub, 0); + + tpmp.cbSize = sizeof(tpmp); + + menuRet = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, menuMousePoint.x, menuMousePoint.y, (HWND)CallService(MS_CLUI_GETHWND, 0, 0), &tpmp); + + CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(menuRet), MPCF_CONTACTMENU), (LPARAM)hSub); + + DestroyMenu(hMenu); + + return 0; +} + +int Meta_ContactMenuFunc(WPARAM wParam, LPARAM lParam) { + char buff[1024]; + HANDLE hMeta = (HANDLE)wParam; + int contact_num = (int)lParam; + if(metaMap.exists(hMeta) && contact_num >= 0 && contact_num < metaMap[hMeta].size()) { + QueueUserAPC((PAPCFUNC)sttMenuFunc, metaMainThread, (ULONG)metaMap[hMeta][contact_num].handle()); + } + + return 0; +} + +HANDLE hMenuContact[MAX_SUBCONTACTS], hServiceContactMenu = 0; + +int PrebuildContactMenu(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(CLISTMENUITEM); + + if(MetaEnabled()) { + if(IsSubcontact(hContact)) { + mi.flags = CMIM_FLAGS | CMIF_HIDDEN; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuAdd, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuConvert, (LPARAM)&mi); + mi.flags = CMIM_FLAGS; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuDefault, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuRemove, (LPARAM)&mi); + } else if(IsMetacontact(hContact)) { + GetCursorPos(&menuMousePoint); + + mi.flags = CMIM_FLAGS | CMIF_HIDDEN; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuAdd, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuConvert, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuDefault, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuRemove, (LPARAM)&mi); + + if(!metaMap.exists(hContact)) { + PUShowMessage("No such meta!", SM_WARNING); + return 0; + } + + // show subcontact menu items + mi.flags = CMIM_FLAGS | CMIM_NAME | CMIM_ICON | CMIF_TCHAR; + HIMAGELIST il = (HIMAGELIST)CallService(MS_CLIST_GETICONSIMAGELIST, 0, 0); + SubcontactList::Iterator i = metaMap[hContact].start(); + int count = 0; + while(i.has_val()) { + mi.ptszName = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)i.val().handle(), GCDNF_TCHAR); + mi.hIcon = ImageList_GetIcon(il, (int)CallService(MS_CLIST_GETCONTACTICON, (WPARAM)i.val().handle(), 0), 0); + + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuContact[count], (LPARAM)&mi); + + i.next(); + count++; + } + mi.flags = CMIM_FLAGS | CMIF_HIDDEN; + for(;count < MAX_SUBCONTACTS; count++) + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuContact[count], (LPARAM)&mi); + + // show hide nudge menu item +// wParam = char *szProto +// lParam = BOOL show +#define MS_NUDGE_SHOWMENU "NudgeShowMenu" + { + char serviceFunc[256]; + hContact = Meta_GetMostOnline((HANDLE)wParam); + mir_snprintf(serviceFunc, 256, "%s/SendNudge", ContactProto(hContact)); + CallService(MS_NUDGE_SHOWMENU, (WPARAM)MODULE, (LPARAM)ServiceExists(serviceFunc)); + } + + } else { + mi.flags = CMIM_FLAGS; + if(meta_count) CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuAdd, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuConvert, (LPARAM)&mi); + mi.flags = CMIM_FLAGS | CMIF_HIDDEN; + if(!meta_count) CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuAdd, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuRemove, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuDefault, (LPARAM)&mi); + } + } else { + mi.flags = CMIM_FLAGS | CMIF_HIDDEN; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuAdd, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuConvert, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuDefault, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuRemove, (LPARAM)&mi); + } + + return 0; +} + +void InitMenu() { + CLISTMENUITEM menu = {0}; + menu.cbSize=sizeof(menu); + + menu.flags = CMIM_ALL | CMIF_TCHAR | CMIF_ICONFROMICOLIB; + + // main menu item + hServiceMenuOnOff = CreateServiceFunction(MODULE "/MenuOnOff", ServiceMenuOnOff); + menu.ptszName = MetaEnabled() ? _T("Disable MetaContacts") : _T("Enable MetaContacts"); + menu.pszService = MODULE "/MenuOnOff"; + menu.icolibItem = GetIcolibHandle(MetaEnabled() ? I_MENUOFF : I_MENU); + hMenuOnOff = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&menu); + + // normal and subcontact menu items + hServiceMenuConvert = CreateServiceFunction(MODULE "/ContactMenuConvert", ContactMenuConvert); + menu.ptszName = _T("Convert to MetaContact"); + menu.pszService = MODULE "/ContactMenuConvert"; + menu.icolibItem = GetIcolibHandle(I_CONVERT); + hMenuConvert = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + + hServiceMenuAdd = CreateServiceFunction(MODULE "/ContactMenuAdd", ContactMenuAdd); + menu.ptszName = _T("Add to existing MetaContact..."); + menu.pszService = MODULE "/ContactMenuAdd"; + menu.icolibItem = GetIcolibHandle(I_ADD); + hMenuAdd = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + + hServiceMenuRemove = CreateServiceFunction(MODULE "/ContactMenuRemove", ContactMenuRemove); + menu.ptszName = _T("Remove from MetaContact"); + menu.pszService = MODULE "/ContactMenuRemove"; + menu.icolibItem = GetIcolibHandle(I_REMOVE); + hMenuRemove = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + + hServiceMenuDefault = CreateServiceFunction(MODULE "/ContactMenuDefault", ContactMenuDefault); + menu.ptszName = _T("Set as MetaContact default"); + menu.pszService = MODULE "/ContactMenuDefault"; + menu.icolibItem = GetIcolibHandle(I_SETDEFAULT); + hMenuDefault = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + + // hidden contact menu items...ho hum + hServiceContactMenu = CreateServiceFunction("MetaContacts/MenuFunc",Meta_ContactMenuFunc); + + menu.flags = CMIM_ALL | CMIF_TCHAR | CMIF_HIDDEN; + menu.pszContactOwner = MODULE; + menu.hIcon = 0; + menu.position = -99000; + menu.hIcon = 0; + + TCHAR buff[256]; + menu.ptszName = buff; + for(int i = 0; i < MAX_SUBCONTACTS; i++) { + menu.position--; + menu.popupPosition = i; + mir_sntprintf(buff, 256, _T("Context%d"), i); + + menu.pszService= "MetaContacts/MenuFunc"; + + hMenuContact[i] = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + } + + + // metacontact items + menu.flags = CMIM_ALL | CMIF_TCHAR | CMIF_ICONFROMICOLIB; + hServiceMenuEdit = CreateServiceFunction(MODULE "/ContactMenuEdit", ContactMenuEdit); + menu.ptszName = _T("Edit MetaContact"); + menu.pszService = MODULE "/ContactMenuEdit"; + menu.icolibItem = GetIcolibHandle(I_EDIT); + menu.pszContactOwner = MODULE; + hMenuEdit = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + + hEventMenuBuild = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PrebuildContactMenu); + + // for toptoolbar buttons (backward compatible) + hServiceToggle = CreateServiceFunction("MetaContacts/OnOff", ServiceMenuOnOff); + +} + +void DeinitMenu() { + UnhookEvent(hEventMenuBuild); + DestroyServiceFunction(hServiceMenuRemove); + DestroyServiceFunction(hServiceMenuDefault); + DestroyServiceFunction(hServiceMenuEdit); + DestroyServiceFunction(hServiceMenuAdd); + DestroyServiceFunction(hServiceMenuConvert); + DestroyServiceFunction(hServiceMenuOnOff); + DestroyServiceFunction(hServiceContactMenu); + DestroyServiceFunction(hServiceToggle); +} \ No newline at end of file diff --git a/meta2/menu.h b/meta2/menu.h new file mode 100644 index 0000000..2e77e6f --- /dev/null +++ b/meta2/menu.h @@ -0,0 +1,9 @@ +#ifndef _MENU_INC +#define _MENU_INC + +extern HANDLE hMenuOnOff, hMenuConvert, hMenuAdd, hMenuEdit, hMenuDefault, hMenuRemove; + +void InitMenu(); +void DeinitMenu(); + +#endif diff --git a/meta2/menu.ico b/meta2/menu.ico new file mode 100644 index 0000000..94f62a2 Binary files /dev/null and b/meta2/menu.ico differ diff --git a/meta2/meta2.cpp b/meta2/meta2.cpp new file mode 100644 index 0000000..eba4262 --- /dev/null +++ b/meta2/meta2.cpp @@ -0,0 +1,107 @@ +/* Replace "dll.h" with the name of your header */ +#include "common.h" +#include "version.h" +#include "resource.h" +#include "options.h" +#include "proto.h" +#include "menu.h" +#include "icons.h" +#include "api.h" +#include "priorities.h" +#include "m_metacontacts.h" +#include "settings.h" +#include "import.h" + +/////////////////////////////////////////////// +// Common Plugin Stuff +/////////////////////////////////////////////// +HINSTANCE hInst; +PLUGINLINK *pluginLink; +MM_INTERFACE mmi; +UTF8_INTERFACE utfi; +HANDLE metaMainThread; + +PLUGININFOEX pluginInfo={ + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESC, + __AUTHOR, + __AUTHOREMAIL, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + 0, + { 0x5F9B2B98, 0x3412, 0x4671, { 0x89, 0xAD, 0x5B, 0xA9, 0x53, 0x74, 0xC6, 0xA8 } } +}; + + +extern "C" BOOL APIENTRY DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) { + hInst=hinstDLL; + return TRUE; +} + +extern "C" __declspec (dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion) { + return &pluginInfo; +} + + +// TODO: add any interfaces you implement to this list +static const MUUID interfaces[] = {MIID_PROTOCOL, MIID_METACONTACTS, MIID_LAST}; +extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void) +{ + return interfaces; +} + +int ModulesLoaded(WPARAM wParam, LPARAM lParam) { + InitIcons(); + InitMenu(); + + if(DBGetContactSettingByte(0, MODULE, "FirstRun", 1) == 1) { + ImportOldMetas(); + DBWriteContactSettingByte(0, MODULE, "FirstRun", 0); + } + return 0; +} + +HANDLE hModulesLoaded; +extern "C" __declspec (dllexport) int Load(PLUGINLINK *link) { + + pluginLink=link; + DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &metaMainThread, THREAD_SET_CONTEXT, FALSE, 0 ); + + mir_getMMI(&mmi); + mir_getUTFI(&utfi); + + InitOptions(); + InitPriorities(); + + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(MODULE "/Status")); + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(MODULE "/IdleTS")); + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(MODULE "/Handle")); + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(MODULE "/WindowOpen")); + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(MODULE "/IsSubcontact")); + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(MODULE "/ForceSend")); + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(MODULE "/TempDefault")); + + InitProto(); + InitAPI(); + InitSettings(link); + + // hook modules loaded + hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); + return 0; +} + +extern "C" __declspec (dllexport) int Unload(void) { + DeinitSettings(); + UnhookEvent(hModulesLoaded); + DeinitMenu(); + DeinitIcons(); + DeinitAPI(); + DeinitProto(); + DeinitPriorities(); + DeinitOptions(); + + return 0; +} diff --git a/meta2/meta2.rc b/meta2/meta2.rc new file mode 100644 index 0000000..289c49f --- /dev/null +++ b/meta2/meta2.rc @@ -0,0 +1,170 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral (Default) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUD) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_DEFAULT +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPT1 DIALOGEX 0, 0, 246, 179 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN +END + +IDD_PRIORITIES DIALOGEX 0, 0, 266, 157 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Subcontact Priorities",IDC_STATIC,7,7,252,143 + EDITTEXT IDC_ED_PRIORITY,157,78,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SP_PRIORITY,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,195,78,11,14 + RTEXT "Rank:",IDC_STATIC,42,81,49,8 + RTEXT "Status:",IDC_STATIC,30,57,61,8 + COMBOBOX IDC_CMB_STATUS,98,53,112,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + RTEXT "Protocol:",IDC_STATIC,30,31,61,8 + COMBOBOX IDC_CMB_PROTOCOL,98,28,112,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Reset All",IDC_BTN_RESET,87,119,91,14 + CONTROL "Default",IDC_CHK_DEFAULT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,97,81,58,10 + CTEXT "(Lower ranks are preferred)",IDC_STATIC,51,99,162,8 +END + +IDD_METASELECT DIALOGEX 0, 0, 256, 259 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Select MetaContact" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CTEXT "Please select a MetaContact:",IDC_STATIC,27,11,201,14 + DEFPUSHBUTTON "&Ok",IDOK,73,238,48,14,WS_DISABLED + PUSHBUTTON "&Cancel",IDCANCEL,133,238,48,14 + LISTBOX IDC_METALIST,44,28,168,168,LBS_NOINTEGRALHEIGHT | WS_VSCROLL + CONTROL "Sort Alphabetically",IDC_CHK_SRT,"Button",BS_AUTOCHECKBOX | BS_VCENTER | WS_TABSTOP,78,210,124,13 +END + +IDD_METAEDIT DIALOGEX 0, 0, 311, 260 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION +EXSTYLE WS_EX_STATICEDGE +CAPTION "Edit MetaContact" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "&OK",IDOK,70,241,50,14,WS_DISABLED + PUSHBUTTON "&Cancel",IDCANCEL,130,241,50,14 + LTEXT "Name:",IDC_STATIC,55,26,42,8 + GROUPBOX "Information",IDC_STATIC,5,4,299,45 + GROUPBOX "Contacts",IDC_STATIC,5,54,299,180 + DEFPUSHBUTTON "&Apply",IDC_VALIDATE,190,241,50,14,WS_DISABLED + PUSHBUTTON "&Remove",IDC_BTN_REM,103,214,50,14,WS_DISABLED + PUSHBUTTON "&Set as Default",IDC_BTN_SETDEFAULT,157,214,50,14,WS_DISABLED + EDITTEXT IDC_ED_NAME,96,24,160,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP + CONTROL "List1",IDC_LST_CONTACTS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,13,66,283,142,WS_EX_CLIENTEDGE +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include \r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_METASELECT, DIALOG + BEGIN + LEFTMARGIN, 8 + RIGHTMARGIN, 247 + TOPMARGIN, 5 + BOTTOMMARGIN, 252 + END +END +#endif // APSTUDIO_INVOKED + +#endif // Neutral (Default) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MCSETDEFAULT ICON "icons\\meta_set_as_default.ico" +IDI_MCMENU ICON "icons\\mcmenu.ico" +IDI_MCMENUOFF ICON "icons\\mcmenuof.ico" +IDI_MCMENUOFF2 ICON "icons\\mcmenuoff2.ico" +IDI_MCADD ICON "icons\\meta_add.ico" +IDI_MCCONVERT ICON "icons\\meta_convert.ico" +IDI_MCEDIT ICON "icons\\meta_edit.ico" +IDI_MCREMOVE ICON "icons\\meta_remove2.ico" +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/meta2/meta2.sln b/meta2/meta2.sln new file mode 100644 index 0000000..70e7972 --- /dev/null +++ b/meta2/meta2.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "meta2", "meta2.vcproj", "{C7542E4E-BCC9-4620-8C72-77DBD8119186}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug (Unicode)|Win32 = Debug (Unicode)|Win32 + Debug|Win32 = Debug|Win32 + Release (Unicode)|Win32 = Release (Unicode)|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C7542E4E-BCC9-4620-8C72-77DBD8119186}.Debug (Unicode)|Win32.ActiveCfg = Debug (Unicode)|Win32 + {C7542E4E-BCC9-4620-8C72-77DBD8119186}.Debug (Unicode)|Win32.Build.0 = Debug (Unicode)|Win32 + {C7542E4E-BCC9-4620-8C72-77DBD8119186}.Debug|Win32.ActiveCfg = Debug|Win32 + {C7542E4E-BCC9-4620-8C72-77DBD8119186}.Debug|Win32.Build.0 = Debug|Win32 + {C7542E4E-BCC9-4620-8C72-77DBD8119186}.Release (Unicode)|Win32.ActiveCfg = Release (Unicode)|Win32 + {C7542E4E-BCC9-4620-8C72-77DBD8119186}.Release (Unicode)|Win32.Build.0 = Release (Unicode)|Win32 + {C7542E4E-BCC9-4620-8C72-77DBD8119186}.Release|Win32.ActiveCfg = Release|Win32 + {C7542E4E-BCC9-4620-8C72-77DBD8119186}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/meta2/meta2.vcproj b/meta2/meta2.vcproj new file mode 100644 index 0000000..c99ae3a --- /dev/null +++ b/meta2/meta2.vcproj @@ -0,0 +1,584 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/meta2/meta_api.c b/meta2/meta_api.c new file mode 100644 index 0000000..d84e469 --- /dev/null +++ b/meta2/meta_api.c @@ -0,0 +1,245 @@ +/* +MetaContacts Plugin for Miranda IM. + +Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. +Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au) + +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. +*/ + +/** @file meta_api.c +* +* API functions needed to handle MetaContacts. +* Centralizes functions called by other plugins. +*/ + +#include "metacontacts.h" + +//get the handle for a contact's parent metacontact +//wParam=(HANDLE)hSubContact +//lParam=0 +//returns a handle to the parent metacontact, or null if this contact is not a subcontact +int MetaAPI_GetMeta(WPARAM wParam, LPARAM lParam) { + return (int)(HANDLE)DBGetContactSettingDword((HANDLE)wParam, META_PROTO, "Handle", 0); +} + +//gets the handle for the default contact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a handle to the default contact, or null on failure +int MetaAPI_GetDefault(WPARAM wParam, LPARAM lParam) { + DWORD default_contact_number = DBGetContactSettingDword((HANDLE)wParam, META_PROTO, "Default", -1); + if(default_contact_number != -1) { + return (int)Meta_GetContactHandle((HANDLE)wParam, default_contact_number); + } + return 0; +} + +//gets the contact number for the default contact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a DWORD contact number, or -1 on failure +int MetaAPI_GetDefaultNum(WPARAM wParam, LPARAM lParam) { + return DBGetContactSettingDword((HANDLE)wParam, META_PROTO, "Default", -1); +} + +//gets the handle for the 'most online' contact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a handle to the 'most online' contact +int MetaAPI_GetMostOnline(WPARAM wParam, LPARAM lParam) { + return (int)Meta_GetMostOnline((HANDLE)wParam); +} + +//gets the number of subcontacts for a metacontact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns a DWORD representing the number of subcontacts for the given metacontact +int MetaAPI_GetNumContacts(WPARAM wParam, LPARAM lParam) { + DWORD num_contacts = DBGetContactSettingDword((HANDLE)wParam, META_PROTO, "NumContacts", -1); + return num_contacts; +} + +//gets the handle of a subcontact, using the subcontact's number +//wParam=(HANDLE)hMetaContact +//lParam=(DWORD)contact number +//returns a handle to the specified subcontact +int MetaAPI_GetContact(WPARAM wParam, LPARAM lParam) { + return (int)Meta_GetContactHandle((HANDLE)wParam, (DWORD)lParam); +} + +//sets the default contact, using the subcontact's contact number +//wParam=(HANDLE)hMetaContact +//lParam=(DWORD)contact number +//returns 0 on success +int MetaAPI_SetDefaultContactNum(WPARAM wParam, LPARAM lParam) { + DWORD num_contacts = DBGetContactSettingDword((HANDLE)wParam, META_PROTO, "NumContacts", -1); + if(num_contacts == -1) + return 1; + if((DWORD)lParam >= num_contacts || (DWORD)lParam < 0) + return 1; + if(DBWriteContactSettingDword((HANDLE)wParam, META_PROTO, "Default", (DWORD)lParam)) + return 1; + + NotifyEventHooks(hEventDefaultChanged, wParam, (LPARAM)Meta_GetContactHandle((HANDLE)wParam, (int)lParam)); + return 0; +} + +//sets the default contact, using the subcontact's handle +//wParam=(HANDLE)hMetaContact +//lParam=(HANDLE)hSubcontact +//returns 0 on success +int MetaAPI_SetDefaultContact(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)DBGetContactSettingDword((HANDLE)lParam, META_PROTO, "Handle", 0); + DWORD contact_number = Meta_GetContactNumber((HANDLE)lParam); + if(contact_number == -1 || !hMeta || hMeta != (HANDLE)wParam) + return 1; + if(DBWriteContactSettingDword(hMeta, META_PROTO, "Default", contact_number)) + return 1; + + NotifyEventHooks(hEventDefaultChanged, wParam, lParam); + return 0; +} + +//forces the metacontact to send using a specific subcontact, using the subcontact's contact number +//wParam=(HANDLE)hMetaContact +//lParam=(DWORD)contact number +//returns 0 on success +int MetaAPI_ForceSendContactNum(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = Meta_GetContactHandle((HANDLE)wParam, (int)lParam); + HANDLE hMeta = (HANDLE)DBGetContactSettingDword(hContact, META_PROTO, "Handle", 0); + if(!hContact || !hMeta || hMeta != (HANDLE)wParam || DBGetContactSettingByte(hMeta, META_PROTO, "ForceDefault", 0)) + return 1; + + DBWriteContactSettingDword(hMeta, META_PROTO, "ForceSend", (DWORD)hContact); + + NotifyEventHooks(hEventForceSend, wParam, (LPARAM)hContact); + return 0; +} + +//forces the metacontact to send using a specific subcontact, using the subcontact's handle +//wParam=(HANDLE)hMetaContact +//lParam=(HANDLE)hSubcontact +//returns 0 on success (will fail if 'force default' is in effect) +int MetaAPI_ForceSendContact(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)lParam; + HANDLE hMeta = (HANDLE)DBGetContactSettingDword(hContact, META_PROTO, "Handle", 0); + if(!hContact || !hMeta || hMeta != (HANDLE)wParam || DBGetContactSettingByte(hMeta, META_PROTO, "ForceDefault", 0)) + return 1; + + DBWriteContactSettingDword(hMeta, META_PROTO, "ForceSend", (DWORD)hContact); + + NotifyEventHooks(hEventForceSend, wParam, lParam); + return 0; +} + +//'unforces' the metacontact to send using a specific subcontact +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns 0 on success (will fail if 'force default' is in effect) +int MetaAPI_UnforceSendContact(WPARAM wParam, LPARAM lParam) { + if(DBGetContactSettingByte((HANDLE)wParam, META_PROTO, "ForceDefault", 0)) + return 1; + + DBWriteContactSettingDword((HANDLE)wParam, META_PROTO, "ForceSend", 0); + + NotifyEventHooks(hEventUnforceSend, wParam, lParam); + return 0; +} + + +//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact +// overrides 'force send' above, and will even force use of offline contacts +// will send ME_MC_FORCESEND event +//wParam=(HANDLE)hMetaContact +//lParam=0 +//returns 1(true) or 0(false) representing new state of 'force default' +int MetaAPI_ForceDefault(WPARAM wParam, LPARAM lParam) { + // forward to menu function + Meta_ForceDefault(wParam, lParam); + return DBGetContactSettingByte((HANDLE)wParam, META_PROTO, "ForceDefault", 0); +} + +// method to get state of 'force' for a metacontact +// wParam=(HANDLE)hMetaContact +// lParam= (DWORD)&contact_number or NULL +// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to, +// or if none is in force, the value (DWORD)-1 will be copied +// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above) +int MetaAPI_GetForceState(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + HANDLE hContact; + + if(!hMeta) return 0; + + if(DBGetContactSettingByte(hMeta, META_PROTO, "ForceDefault", 0)) { + if(lParam) *(DWORD *)lParam = DBGetContactSettingDword((HANDLE)wParam, META_PROTO, "Default", -1); + return 1; + } + + hContact = (HANDLE)DBGetContactSettingDword(hMeta, META_PROTO, "ForceSend", 0); + + if(!hContact) { + if(lParam) *(DWORD *)lParam = -1; + } else { + if(lParam) *(DWORD *)lParam = (DWORD)Meta_GetContactNumber(hContact); + } + + return 0; +} + +// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :) +// wParam=lParam=0 +int MetaAPI_GetProtoName(WPARAM wParam, LPARAM lParam) { + return (int)META_PROTO; +} + +// added 0.9.5.0 (22/3/05) +// wParam=(HANDLE)hContact +// lParam=0 +// convert a given contact into a metacontact +int MetaAPI_ConvertToMeta(WPARAM wParam, LPARAM lParam) { + return Meta_Convert(wParam, lParam); +} + +// added 0.9.5.0 (22/3/05) +// wParam=(HANDLE)hContact +// lParam=(HANDLE)hMeta +// add an existing contact to a metacontact +int MetaAPI_AddToMeta(WPARAM wParam, LPARAM lParam) { + return Meta_Assign((HANDLE)wParam, (HANDLE)lParam, FALSE); +} + +// added 0.9.5.0 (22/3/05) +// wParam=0 +// lParam=(HANDLE)hContact +// remove a contact from a metacontact +int MetaAPI_RemoveFromMeta(WPARAM wParam, LPARAM lParam) { + // notice we switch args - to keep the API function consistent with the others + return Meta_Delete((WPARAM)lParam, (LPARAM)wParam); +} + +// added 0.9.13.2 (6/10/05) +// wParam=(BOOL)disable +// lParam=0 +// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting +// should be called once in your 'onmodulesloaded' event handler + +BOOL meta_group_hack_disabled = FALSE; // this global flag is used in utils 'SetGroup' function + +int MetaAPI_DisableHiddenGroup(WPARAM wParam, LPARAM lParam) { + meta_group_hack_disabled = (BOOL)wParam; + return 0; +} diff --git a/meta2/options.cpp b/meta2/options.cpp new file mode 100644 index 0000000..7c96cba --- /dev/null +++ b/meta2/options.cpp @@ -0,0 +1,71 @@ +#include "common.h" +#include "options.h" +#include "resource.h" + +Options options; + +void LoadOptions() { + //options.dummy = DBGetContactSettingDword(0, MODULE, "Dummy", 0); +} + +void SaveOptions() { + //DBWriteContactSettingDword(0, MODULE, "Dummy", options.dummy); +} + +BOOL CALLBACK DlgProcOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + static HANDLE hItemAll; + + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + // TODO: read options variable into control states + return FALSE; + case WM_COMMAND: + // enable the 'apply' button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + case WM_NOTIFY: + switch(((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) + { + case PSN_APPLY: + // TODO: read control states into options variable + SaveOptions(); + } + break; + } + break; + } + + return 0; +} + +int OptInit(WPARAM wParam, LPARAM lParam) { + OPTIONSDIALOGPAGE odp = { 0 }; + odp.cbSize = sizeof(odp); + odp.flags = ODPF_BOLDGROUPS; + odp.flags |= ODPF_TCHAR; + odp.position = -790000000; + odp.hInstance = hInst; + + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT1); + odp.ptszTitle = TranslateT("MetaContacts"); + odp.ptszGroup = TranslateT("Contact List"); + odp.ptszTab = TranslateT("General"); + odp.nIDBottomSimpleControl = 0; + odp.pfnDlgProc = DlgProcOpts; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + return 0; +} + +HANDLE hEventOptInit; +void InitOptions() { + hEventOptInit = HookEvent(ME_OPT_INITIALISE, OptInit); + LoadOptions(); +} + +void DeinitOptions() { + UnhookEvent(hEventOptInit); +} diff --git a/meta2/options.h b/meta2/options.h new file mode 100644 index 0000000..82c90c7 --- /dev/null +++ b/meta2/options.h @@ -0,0 +1,13 @@ +#ifndef _OPTIONS_INC +#define _OPTIONS_INC + +typedef struct { + int dummy; +} Options; + +extern Options options; + +void InitOptions(); +void DeinitOptions(); + +#endif diff --git a/meta2/priorities.cpp b/meta2/priorities.cpp new file mode 100644 index 0000000..7b35223 --- /dev/null +++ b/meta2/priorities.cpp @@ -0,0 +1,354 @@ +#include "common.h" +#include "priorities.h" +#include "resource.h" +#include + +/* +#define ID_STATUS_OFFLINE 40071 ->8 +#define ID_STATUS_ONLINE 40072 ->0 +#define ID_STATUS_AWAY 40073 ->4 +#define ID_STATUS_DND 40074 ->7 +#define ID_STATUS_NA 40075 ->6 +#define ID_STATUS_OCCUPIED 40076 ->5 +#define ID_STATUS_FREECHAT 40077 ->1 +#define ID_STATUS_INVISIBLE 40078 ->0 +#define ID_STATUS_ONTHEPHONE 40079 ->2 +#define ID_STATUS_OUTTOLUNCH 40080 ->3 +*/ + +int status_order[10] = {8, 0, 4, 7, 6, 5, 1, 0, 2, 3}; + +int GetDefaultPrio(int status) { + return status_order[status - ID_STATUS_OFFLINE]; +} + +typedef struct { + int prio[10]; // priority for each status + BOOL def[10]; // use default for this one? +} ProtoStatusPrio; + +ProtoStatusPrio *priorities = 0; + +int GetRealPriority(char *proto, int status) { + char szSetting[256]; + if(!proto) { + mir_snprintf(szSetting, 256, "DefaultPrio_%d", status); + return DBGetContactSettingWord(0, MODULE, szSetting, GetDefaultPrio(status)); + } else { + int prio; + mir_snprintf(szSetting, 256, "ProtoPrio_%s%d", proto, status); + prio = DBGetContactSettingWord(0, MODULE, szSetting, 0xFFFF); + if(prio == 0xFFFF) { + mir_snprintf(szSetting, 256, "DefaultPrio_%d", status); + return DBGetContactSettingWord(0, MODULE, szSetting, GetDefaultPrio(status)); + } else + return prio; + } + return 0xFFFF; +} + +void ReadPriorities() { + int num_protocols; + PROTOCOLDESCRIPTOR **pppDesc; + char szSetting[256]; + ProtoStatusPrio * current; + int i, j; + + CallService(MS_PROTO_ENUMPROTOCOLS, (LPARAM)&num_protocols, (WPARAM)&pppDesc); + + current = priorities = (ProtoStatusPrio *)malloc((num_protocols + 1) * sizeof(ProtoStatusPrio)); + for(i = ID_STATUS_OFFLINE; i <= ID_STATUS_OUTTOLUNCH; i++) { + mir_snprintf(szSetting, 256, "DefaultPrio_%d", i); + current->def[i - ID_STATUS_OFFLINE] = TRUE; + current->prio[i - ID_STATUS_OFFLINE] = DBGetContactSettingWord(0, MODULE, szSetting, GetDefaultPrio(i)); + } + for(i = 0; i < num_protocols; i++) { + current = priorities + (i + 1); + for(j = ID_STATUS_OFFLINE; j <= ID_STATUS_OUTTOLUNCH; j++) { + mir_snprintf(szSetting, 256, "ProtoPrio_%s%d", pppDesc[i]->szName, j); + current->prio[j - ID_STATUS_OFFLINE] = DBGetContactSettingWord(0, MODULE, szSetting, 0xFFFF); + current->def[j - ID_STATUS_OFFLINE] = (current->prio[j - ID_STATUS_OFFLINE] == 0xFFFF); + } + } +} + +void WritePriorities() { + int num_protocols; + PROTOCOLDESCRIPTOR **pppDesc; + char szSetting[256]; + ProtoStatusPrio * current = priorities; + int i, j; + + CallService(MS_PROTO_ENUMPROTOCOLS, (LPARAM)&num_protocols, (WPARAM)&pppDesc); + + for(i = ID_STATUS_OFFLINE; i <= ID_STATUS_OUTTOLUNCH; i++) { + mir_snprintf(szSetting, 256, "DefaultPrio_%d", i); + if(current->prio[i - ID_STATUS_OFFLINE] != GetDefaultPrio(i)) + DBWriteContactSettingWord(0, MODULE, szSetting, current->prio[i - ID_STATUS_OFFLINE]); + else + DBDeleteContactSetting(0, MODULE, szSetting); + } + for(i = 0; i < num_protocols; i++) { + current = priorities + (i + 1); + for(j = ID_STATUS_OFFLINE; j <= ID_STATUS_OUTTOLUNCH; j++) { + mir_snprintf(szSetting, 256, "ProtoPrio_%s%d", pppDesc[i]->szName, j); + if(!current->def[j - ID_STATUS_OFFLINE]) + DBWriteContactSettingWord(0, MODULE, szSetting, current->prio[j - ID_STATUS_OFFLINE]); + else + DBDeleteContactSetting(0, MODULE, szSetting); + } + } +} + +int GetIsDefault(int proto_index, int status) { + return (priorities + (proto_index + 1))->def[status - ID_STATUS_OFFLINE]; +} + +BOOL GetPriority(int proto_index, int status) { + ProtoStatusPrio * current; + if(proto_index == -1) { + current = priorities; + return current->prio[status - ID_STATUS_OFFLINE]; + } else { + current = priorities + (proto_index + 1); + if(current->def[status - ID_STATUS_OFFLINE]) { + current = priorities; + } + return current->prio[status - ID_STATUS_OFFLINE]; + } + return 0xFFFF; +} + +void SetPriority(int proto_index, int status, BOOL def, int prio) { + ProtoStatusPrio * current; + if(prio < 0) prio = 0; + if(prio > 500) prio = 500; + if(proto_index == -1) { + current = priorities; + current->prio[status - ID_STATUS_OFFLINE] = prio; + } else { + current = priorities + (proto_index + 1); + current->def[status - ID_STATUS_OFFLINE] = def; + if(!def) { + current->prio[status - ID_STATUS_OFFLINE] = prio; + } + } +} + +void ResetPriorities() { + int num_protocols; + PROTOCOLDESCRIPTOR **pppDesc; + ProtoStatusPrio * current; + int i, j; + + CallService(MS_PROTO_ENUMPROTOCOLS, (LPARAM)&num_protocols, (WPARAM)&pppDesc); + + current = priorities; + for(i = ID_STATUS_OFFLINE; i <= ID_STATUS_OUTTOLUNCH; i++) { + current->def[i - ID_STATUS_OFFLINE] = TRUE; + current->prio[i - ID_STATUS_OFFLINE] = GetDefaultPrio(i); + } + for(i = 0; i < num_protocols; i++) { + current = priorities + (i + 1); + for(j = ID_STATUS_OFFLINE; j <= ID_STATUS_OUTTOLUNCH; j++) { + current->def[j - ID_STATUS_OFFLINE] = TRUE; + } + } +} + +#define WMU_FILLSTATUSCMB (WM_USER + 0x100) +#define WMU_FILLPRIODATA (WM_USER + 0x101) + +BOOL CALLBACK DlgProcOptsPriorities(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND hw; + + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + SendMessage(GetDlgItem(hwndDlg, IDC_SP_PRIORITY), UDM_SETRANGE, 0, (LPARAM)MAKELONG(500, 0)); + ReadPriorities(); + { + int num_protocols; + PROTOCOLDESCRIPTOR **pppDesc; + int i, index; + + CallService(MS_PROTO_ENUMPROTOCOLS, (LPARAM)&num_protocols, (WPARAM)&pppDesc); + hw = GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL); + index = SendMessage(hw, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)TranslateT("")); + SendMessage(hw, CB_SETITEMDATA, (WPARAM)index, -1); + for(i = 0; i < num_protocols; i++) { + if(pppDesc[i]->type == PROTOTYPE_PROTOCOL) { + if(strcmp(pppDesc[i]->szName, MODULE) != 0) { + index = SendMessageA(hw, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)pppDesc[i]->szName); + SendMessage(hw, CB_SETITEMDATA, (WPARAM)index, i); + } + } + } + + SendMessage(hw, CB_SETCURSEL, 0, 0); + SendMessage(hwndDlg, WMU_FILLSTATUSCMB, 0, 0); + SendMessage(hwndDlg, WMU_FILLPRIODATA, 0, 0); + } + return FALSE; + case WMU_FILLPRIODATA: + { + int sel = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL), CB_GETCURSEL, 0, 0); + if(sel != -1) { + int index = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL), CB_GETITEMDATA, (WPARAM)sel, 0); + sel = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_STATUS), CB_GETCURSEL, 0, 0); + if(sel != -1) { + int status = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_STATUS), CB_GETITEMDATA, (WPARAM)sel, 0); + SetDlgItemInt(hwndDlg, IDC_ED_PRIORITY, GetPriority(index, status), FALSE); + if(index == -1) { + EnableWindow(GetDlgItem(hwndDlg, IDC_ED_PRIORITY), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_SP_PRIORITY), TRUE); + CheckDlgButton(hwndDlg, IDC_CHK_DEFAULT, TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_DEFAULT), FALSE); + } else { + if(GetIsDefault(index, status)) { + CheckDlgButton(hwndDlg, IDC_CHK_DEFAULT, TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ED_PRIORITY), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_SP_PRIORITY), FALSE); + } else { + CheckDlgButton(hwndDlg, IDC_CHK_DEFAULT, FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ED_PRIORITY), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_SP_PRIORITY), TRUE); + } + + EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_DEFAULT), TRUE); + } + } + } + } + return TRUE; + case WMU_FILLSTATUSCMB: + { + int sel = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL), CB_GETCURSEL, 0, 0); + if(sel != -1) { + int index = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL), CB_GETITEMDATA, (WPARAM)sel, 0); + HWND hw = GetDlgItem(hwndDlg, IDC_CMB_STATUS); + SendMessage(hw, CB_RESETCONTENT, 0, 0); + if(index == -1) { + int i; + for(i = ID_STATUS_OFFLINE; i <= ID_STATUS_OUTTOLUNCH; i++) { + index = SendMessage(hw, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)(TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, i, GCMDF_TCHAR)); + SendMessage(hw, CB_SETITEMDATA, (WPARAM)index, i); + } + } else { + int num_protocols, caps, i; + PROTOCOLDESCRIPTOR **pppDesc; + CallService(MS_PROTO_ENUMPROTOCOLS, (LPARAM)&num_protocols, (WPARAM)&pppDesc); + + caps = CallProtoService(pppDesc[index]->szName, PS_GETCAPS, PFLAGNUM_2, 0); + + for(i = ID_STATUS_OFFLINE; i <= ID_STATUS_OUTTOLUNCH; i++) { + if(caps & Proto_Status2Flag(i)) { + index = SendMessage(hw, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)(TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, i, GCMDF_TCHAR)); + SendMessage(hw, CB_SETITEMDATA, (WPARAM)index, i); + } + } + } + SendMessage(hw, CB_SETCURSEL, 0, 0); + SendMessage(hwndDlg, WMU_FILLPRIODATA, 0, 0); + } + } + return TRUE; + case WM_COMMAND: + if ( HIWORD( wParam ) == BN_CLICKED ) { + switch( LOWORD( wParam )) { + case IDC_CHK_DEFAULT: + { + int sel = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL), CB_GETCURSEL, 0, 0); + if(sel != -1) { + int index = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL), CB_GETITEMDATA, (WPARAM)sel, 0); + sel = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_STATUS), CB_GETCURSEL, 0, 0); + if(sel != -1) { + BOOL checked = IsDlgButtonChecked(hwndDlg, IDC_CHK_DEFAULT); + int status = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_STATUS), CB_GETITEMDATA, (WPARAM)sel, 0); + if(checked) { + SetPriority(index, status, TRUE, 0); + SetDlgItemInt(hwndDlg, IDC_ED_PRIORITY, GetPriority(index, status), FALSE); + } else { + SetPriority(index, status, FALSE, GetDlgItemInt(hwndDlg, IDC_ED_PRIORITY, 0, FALSE)); + } + EnableWindow(GetDlgItem(hwndDlg, IDC_ED_PRIORITY), !checked); + EnableWindow(GetDlgItem(hwndDlg, IDC_SP_PRIORITY), !checked); + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + } + } + } + break; + case IDC_BTN_RESET: + ResetPriorities(); + SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL), CB_SETCURSEL, 0, 0); + SendMessage(hwndDlg, WMU_FILLSTATUSCMB, 0, 0); + SendMessage(hwndDlg, WMU_FILLPRIODATA, 0, 0); + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + break; + } + } + if ( HIWORD( wParam ) == EN_CHANGE && LOWORD(wParam) == IDC_ED_PRIORITY && ( HWND )lParam == GetFocus()) { + int sel = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL), CB_GETCURSEL, 0, 0); + if(sel != -1) { + int index = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTOCOL), CB_GETITEMDATA, (WPARAM)sel, 0); + sel = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_STATUS), CB_GETCURSEL, 0, 0); + if(sel != -1) { + int status = SendMessage(GetDlgItem(hwndDlg, IDC_CMB_STATUS), CB_GETITEMDATA, (WPARAM)sel, 0); + int prio = GetDlgItemInt(hwndDlg, IDC_ED_PRIORITY, 0, FALSE); + SetPriority(index, status, FALSE, prio); + if(prio != GetPriority(index, status)) + SetDlgItemInt(hwndDlg, IDC_ED_PRIORITY, GetPriority(index, status), FALSE); + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + } + } + } + if ( HIWORD( wParam ) == CBN_SELCHANGE) { + switch( LOWORD( wParam )) { + case IDC_CMB_STATUS: + SendMessage(hwndDlg, WMU_FILLPRIODATA, 0, 0); + break; + case IDC_CMB_PROTOCOL: + SendMessage(hwndDlg, WMU_FILLSTATUSCMB, 0, 0); + break; + } + } + break; + + case WM_NOTIFY: + if (((LPNMHDR)lParam)->code == PSN_APPLY ) { + WritePriorities(); + return TRUE; + } + break; + case WM_DESTROY: + free(priorities); + priorities = 0; + break; + } + + return FALSE; +} + +int PrioOptInit(WPARAM wParam, LPARAM lParam) { + OPTIONSDIALOGPAGE odp = {0}; + odp.cbSize = sizeof(odp); + odp.hInstance = hInst; + odp.flags = ODPF_BOLDGROUPS; + + odp.pszTemplate = MAKEINTRESOURCEA(IDD_PRIORITIES); + odp.pszTitle = Translate("MetaContacts"); + odp.pszGroup = Translate("Contact List"); + odp.pszTab = Translate("Priorities"); + odp.pfnDlgProc = DlgProcOptsPriorities; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + return 0; +} + +void InitPriorities() { + HookEvent(ME_OPT_INITIALISE, PrioOptInit); +} + +void DeinitPriorities() { +} diff --git a/meta2/priorities.h b/meta2/priorities.h new file mode 100644 index 0000000..3036360 --- /dev/null +++ b/meta2/priorities.h @@ -0,0 +1,6 @@ + + +int GetRealPriority(char *proto, int status); + +void InitPriorities(); +void DeinitPriorities(); diff --git a/meta2/proto.cpp b/meta2/proto.cpp new file mode 100644 index 0000000..61ff69d --- /dev/null +++ b/meta2/proto.cpp @@ -0,0 +1,547 @@ +#include "common.h" +#include "proto.h" +#include "resource.h" +#include "core_functions.h" +#include "api.h" +#include "priorities.h" + +bool firstSetOnline = true; +DWORD status = ID_STATUS_OFFLINE; +DWORD setStatusTimerId = 0; + +DWORD next_meta_id = 1; +int meta_count = 0; + +// a 'fake' module name for copied subcontact events - used to prevent infinite recursion when creating event copies +#define META_COPY_MODULE "MetaCopy" + +HANDLE NewMetaContact() { + HANDLE hMeta = (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0); + DBWriteContactSettingDword(hMeta, MODULE, META_ID, next_meta_id++); + CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hMeta, ( LPARAM )MODULE); + meta_count++; + return hMeta; +} + + +int GetCaps(WPARAM wParam,LPARAM lParam) { + int ret = 0; + switch (wParam) { + case PFLAGNUM_1: + ret = PF1_NUMERICUSERID | PF1_IM | PF1_MODEMSGRECV | PF1_FILESEND; + break; + case PFLAGNUM_2: + ret = PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND + | PF2_HEAVYDND | PF2_FREECHAT | PF2_OUTTOLUNCH | PF2_ONTHEPHONE; + break; + case PFLAGNUM_3: + ret = PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND + | PF2_HEAVYDND | PF2_FREECHAT | PF2_OUTTOLUNCH | PF2_ONTHEPHONE; + break; + case PFLAGNUM_4: + ret = PF4_SUPPORTTYPING | PF4_AVATARS | PF4_SUPPORTIDLE | PF4_IMSENDUTF | PF4_IMSENDOFFLINE; + break; + case PFLAGNUM_5: + ret = PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND + | PF2_HEAVYDND | PF2_FREECHAT | PF2_OUTTOLUNCH | PF2_ONTHEPHONE; + break; + case PFLAG_UNIQUEIDTEXT: + ret = (int) Translate("Meta ID"); + break; + case PFLAG_MAXLENOFMESSAGE: + ret = 2048; + break; + case PFLAG_UNIQUEIDSETTING: + ret = (int) META_ID; + break; + } + return ret; +} + +int GetName(WPARAM wParam,LPARAM lParam) { + char *name = (char *)Translate(MODULE); + size_t size = min(strlen(name),wParam-1); // copy only the first size bytes. + if(strncpy((char *)lParam,name,size)==NULL) + return 1; + ((char *)lParam)[size]='\0'; + return 0; +} + +int LoadIcon(WPARAM wParam,LPARAM lParam) { + + UINT id; + switch (wParam & 0xFFFF) + { + case PLI_PROTOCOL: + id = IDI_MCMENU; + break; + case PLI_ONLINE: + id = IDI_MCMENU; + break; + case PLI_OFFLINE: + id = IDI_MCMENUOFF; + break; + default: + return (int) (HICON) NULL; + } + + return (int) LoadImage(hInst, MAKEINTRESOURCE(id), IMAGE_ICON, + GetSystemMetrics(wParam & PLIF_SMALL ? SM_CXSMICON : SM_CXICON), + GetSystemMetrics(wParam & PLIF_SMALL ? SM_CYSMICON : SM_CYICON), 0); + return 0; +} + +int ProtoGetInfo(WPARAM wParam,LPARAM lParam) { + CCSDATA *ccs = ( CCSDATA* )lParam; + + ccs->hContact = Meta_GetMostOnline(ccs->hContact); + char *proto = ContactProto(ccs->hContact); + if(!proto) + return 1; + + char szServiceName[256]; + mir_snprintf(szServiceName, 256, "%s%s", proto, PSS_GETINFO); + if (ServiceExists(szServiceName)) { + return CallContactService(ccs->hContact, PSS_GETINFO, ccs->wParam, ccs->lParam); + } + return 1; +} + +int ProtoGetAwayMsg(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = ( CCSDATA* )lParam; + + ccs->hContact = Meta_GetMostOnline(ccs->hContact); + char *proto = ContactProto(ccs->hContact); + if(!proto) + return 0; + + char szServiceName[256]; + mir_snprintf(szServiceName, 256, "%s%s", proto, PSS_GETAWAYMSG); + if (ServiceExists(szServiceName)) { + return CallContactService(ccs->hContact, PSS_GETAWAYMSG, ccs->wParam, ccs->lParam); + } + return 0; +} + +void CALLBACK SetStatusProc(HWND hWnd, UINT msg, UINT_PTR id, DWORD dw) +{ + int previousMode = status; + status = (int)ID_STATUS_ONLINE; + ProtoBroadcastAck(MODULE, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)previousMode, status); + + KillTimer(0, setStatusTimerId); +} + +int SetStatus(WPARAM wParam,LPARAM lParam) +{ + // firstSetOnline starts out true - used to delay metacontact's 'onlineness' to prevent double status notifications on startup + if(status == ID_STATUS_OFFLINE && firstSetOnline) { + // causes crash on exit if miranda is closed in under options.set_status_from_offline milliseconds! + //CloseHandle( CreateThread( NULL, 0, SetStatusThread, (void *)wParam, 0, 0 )); + setStatusTimerId = SetTimer(0, 0, 10000, SetStatusProc); + firstSetOnline = FALSE; + } else { + int previousMode = status; + status = (int)wParam; + ProtoBroadcastAck(MODULE, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)previousMode, status); + } + return 0; +} + +int GetStatus(WPARAM wParam,LPARAM lParam) { + return status; +} + +int ProtoSendMessage(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + char *message = (char *)ccs->lParam; + int flags = ccs->wParam; + char *buff = 0; + + HANDLE most_online = Meta_GetMostOnline(ccs->hContact); + char *proto = ContactProto(most_online); + if(ContactStatus(most_online, proto) == ID_STATUS_OFFLINE) { + most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_4, PF4_IMSENDOFFLINE); + proto = ContactProto(most_online); + } + + char szServiceName[256]; + mir_snprintf(szServiceName, 256, "%s", PSS_MESSAGE); + if((flags & PREF_UNICODE) && proto != 0) { + char szTemp[256]; + mir_snprintf(szTemp, 256, "%s%sW", proto, PSS_MESSAGE); + if (ServiceExists(szTemp)) + strncpy(szServiceName, PSS_MESSAGE "W", sizeof(szServiceName)); + } + if((flags & PREF_UTF) && !(CallContactService(most_online, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_IMSENDUTF)) { + ccs->wParam &= ~PREF_UTF; + ccs->wParam |= PREF_UNICODE; + wchar_t *unicode = mir_utf8decodeW(message); + char *ascii = mir_u2a(unicode); + + char *buff = new char[strlen(ascii) + 1 + (wcslen(unicode) + 1) * sizeof(wchar_t)]; + strcpy(buff, ascii); + wcscpy((wchar_t *)(buff + strlen(ascii) + 1), unicode); + mir_free(unicode); + mir_free(ascii); + ccs->lParam = (LPARAM)buff; + } + + int ret = (int)CallContactService(most_online, szServiceName, ccs->wParam, ccs->lParam); + if(buff) { + ccs->lParam = (LPARAM)message; + delete[] buff; + } + return ret; +} + +int ProtoSendMessageW(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + if(!(ccs->wParam & PREF_UTF)) + ccs->wParam |= PREF_UNICODE; + + return ProtoSendMessage(wParam, lParam); +} + +int ProtoRecvMessage(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + PROTORECVEVENT *pre = (PROTORECVEVENT *) ccs->lParam; + + // use the subcontact's protocol to add to the db (AIMOSCAR removes HTML here!) + HANDLE most_online = Meta_GetMostOnline(ccs->hContact); + char *proto = ContactProto(most_online); + if(proto) { + char service[256]; + mir_snprintf(service, 256, "%s%s", proto, PSR_MESSAGE); + if(ServiceExists(service)) + return CallService(service, wParam, lParam); + } + + return 0; +} + +int RedirectACKs(WPARAM wParam, LPARAM lParam) +{ + ACKDATA *ack = (ACKDATA*) lParam; + HANDLE hMeta; + + if(ack->hContact == 0 || (hMeta = (HANDLE)DBGetContactSettingDword(ack->hContact, MODULE, "Handle", 0)) == 0) + return 0; // Can't find the MetaID, let through the protocol chain + + if(!strcmp(ack->szModule, MODULE)) { + return 0; // don't rebroadcast our own acks + } + + // if it's for something we don't support, ignore + if(ack->type != ACKTYPE_MESSAGE && ack->type != ACKTYPE_CHAT && ack->type != ACKTYPE_FILE && ack->type != ACKTYPE_AWAYMSG + && ack->type != ACKTYPE_AVATAR && ack->type != ACKTYPE_GETINFO) + { + return 0; + } + + // change the hContact in the avatar info struct, if it's the avatar we're using - else drop it + if(ack->type == ACKTYPE_AVATAR) { + if(ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED || ack->result == ACKRESULT_STATUS) { + if(ack->hContact == 0) { + return 0; + } + + if(ack->hProcess) { + PROTO_AVATAR_INFORMATION AI; + memcpy(&AI, (PROTO_AVATAR_INFORMATION *)ack->hProcess, sizeof(PROTO_AVATAR_INFORMATION)); + if(AI.hContact) + AI.hContact = hMeta; + + return ProtoBroadcastAck(MODULE,hMeta,ack->type,ack->result, (HANDLE)&AI, ack->lParam); + } else + return ProtoBroadcastAck(MODULE,hMeta,ack->type,ack->result, 0, ack->lParam); + } + } + + return ProtoBroadcastAck(MODULE, hMeta, ack->type, ack->result, ack->hProcess, ack->lParam); +} + +int ProtoGetAvatarInfo(WPARAM wParam, LPARAM lParam) { + PROTO_AVATAR_INFORMATION *AI = (PROTO_AVATAR_INFORMATION *) lParam; + + HANDLE hMeta = AI->hContact; + + // find the most online contact supporting avatars, according to priorities + HANDLE most_online = (HANDLE)Meta_GetMostOnlineSupporting(hMeta, PFLAGNUM_4, PF4_AVATARS); + char *most_online_proto = ContactProto(most_online); + + AI->hContact = most_online; + char szServiceName[100]; + mir_snprintf(szServiceName, sizeof(szServiceName), "%s%s", most_online_proto, PS_GETAVATARINFO); + int result = CallService(szServiceName, wParam, lParam); + AI->hContact = hMeta; + if (result != CALLSERVICE_NOTFOUND) return result; + + return GAIR_NOAVATAR; // fail +} + +int ProtoFileSend(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + HANDLE hMeta = ccs->hContact; + + // find the most online contact supporting file send, according to priorities + HANDLE most_online = (HANDLE)Meta_GetMostOnlineSupporting(hMeta, PFLAGNUM_1, PF1_FILESEND); + char *most_online_proto = ContactProto(most_online); + + ccs->hContact = most_online; + char szServiceName[100]; + mir_snprintf(szServiceName, sizeof(szServiceName), "%s%s", most_online_proto, PSS_FILE); + int result = CallService(szServiceName, wParam, lParam); + ccs->hContact = hMeta; + if(result != CALLSERVICE_NOTFOUND) return result; + + return 0; // fail +} + +int ProtoUserIsTyping(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + // find the most online contact supporting file send, according to priorities + HANDLE most_online = (HANDLE)Meta_GetMostOnlineSupporting(hMeta, PFLAGNUM_4, PF4_SUPPORTTYPING); + char *most_online_proto = ContactProto(most_online); + + CallContactService(most_online, PSS_USERISTYPING, (WPARAM)most_online, lParam); + return 0; +} + +int EventContactIsTyping(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta; + + if((hMeta = (HANDLE)DBGetContactSettingDword((HANDLE)wParam, MODULE, "Handle", 0)) != 0 && MetaEnabled()) { + // try to remove any clist events added for subcontact + CallServiceSync(MS_CLIST_REMOVEEVENT, wParam, (LPARAM) 1); + + CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hMeta, lParam); + + // stop processing of event + return 1; + } + + return 0; +} + +int SendNudge(WPARAM wParam,LPARAM lParam) +{ + HANDLE hMeta = (HANDLE)wParam, + hSubContact = Meta_GetMostOnline(hMeta); + + char servicefunction[256]; + char *protoName = ContactProto(hSubContact); + mir_snprintf(servicefunction, 256, "%s/SendNudge", protoName); + + return CallService(servicefunction, (WPARAM)hSubContact, lParam); +} + +int ContactDeleted(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + if(IsSubcontact(hContact)) + Meta_Remove(hContact); + else if(IsMetacontact(hContact)) { + SubcontactList::Iterator i = metaMap[hContact].start(); + HANDLE hSub; + while(i.has_val()) { + // same functionality as Meta_Remove - except the meta is not deleted when all subcontacts are removed here since it's already happening, + // and the 'subcontacts changed' event isn't fired + hSub = i.val().handle(); + DBDeleteContactSetting(hSub, MODULE, "ParentMetaID"); + // deleting these (resident) settings doesn't work :( [25/9/07] + DBWriteContactSettingDword(hSub, MODULE, "Handle", 0); + DBWriteContactSettingByte(hSub, MODULE, "IsSubcontact", 0); + if(!meta_group_hack_disabled) DBWriteContactSettingByte(hSub, "CList", "Hidden", 0); + + i.next(); + } + metaMap.remove(hContact); + meta_count--; + } + return 0; +} + +int SettingChanged(WPARAM wParam, LPARAM lParam) { + if(wParam == 0 || !IsSubcontact((HANDLE)wParam)) return 0; + + DBCONTACTWRITESETTING *dcws = (DBCONTACTWRITESETTING *)lParam; + if(strcmp(dcws->szSetting, "Status") == 0) { + // subcontact status has changed + Meta_CalcStatus((HANDLE)DBGetContactSettingDword((HANDLE)wParam, MODULE, "Handle", 0)); + } + + // keep subcontacts hidden if the clist doesn't do it for us + if(strcmp(dcws->szSetting, "Hidden") == 0 && strcmp(dcws->szModule, "CList") == 0 && MetaEnabled() && !meta_group_hack_disabled) { + if(dcws->value.type == DBVT_DELETED || dcws->value.bVal == 0) { + DBWriteContactSettingByte((HANDLE)wParam, "CList", "Hidden", 1); + } + } + + return 0; +} + +int MetaChanged(WPARAM wParam, LPARAM lParam) { + Meta_CalcStatus((HANDLE)wParam); + return 0; +} + +int WindowEvent(WPARAM wParam, LPARAM lParam) { + MessageWindowEventData *mwed = (MessageWindowEventData *)lParam; + if(IsMetacontact(mwed->hContact) || IsSubcontact(mwed->hContact)) { + if(mwed->uType == MSG_WINDOW_EVT_OPEN || mwed->uType == MSG_WINDOW_EVT_OPENING) { + DBWriteContactSettingByte(mwed->hContact, MODULE, "WindowOpen", 1); + if(IsMetacontact(mwed->hContact)) + CallService(MS_CLIST_REMOVEEVENT, (WPARAM)mwed->hContact, (LPARAM)EVENTTYPE_MESSAGE); + } else if(mwed->uType == MSG_WINDOW_EVT_CLOSE || mwed->uType == MSG_WINDOW_EVT_CLOSING) + DBWriteContactSettingByte(mwed->hContact, MODULE, "WindowOpen", 0); + + } + return 0; +} + +void RegisterProto() { + PROTOCOLDESCRIPTOR pd = {0}; + pd.cbSize = sizeof(pd); + pd.szName = MODULE; + pd.type = PROTOTYPE_PROTOCOL; + CallService(MS_PROTO_REGISTERMODULE,0,(LPARAM)&pd); +} + +// redirect events to subcontact (except the ones we add in the EventAdded handler below - i.e. dbei->szModule == META_COPY_MODULE) +int EventFilterAdd(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + DBEVENTINFO *dbei = (DBEVENTINFO *)lParam; + + if(IsMetacontact(hContact) && strcmp(dbei->szModule, MODULE) == 0) { + // add the event to the subcontact instead + HANDLE most_online = Meta_GetMostOnline(hContact); + dbei->szModule = ContactProto(most_online); + CallService(MS_DB_EVENT_ADD, (WPARAM)most_online, (LPARAM)dbei); + return 1; // don't add original event + } + if(IsSubcontact(hContact)) { + if(dbei->eventType == EVENTTYPE_MESSAGE && !(dbei->flags & DBEF_SENT) && DBGetContactSettingByte(hContact, MODULE, "WindowOpen", 0) == 0) { + dbei->flags |= DBEF_READ; + } + } + + return 0; +} + +// add subcontact events to metacontacts +int EventAdded(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam, + hDbEvent = (HANDLE)lParam, + hMeta; + + if(MetaEnabled() && (hMeta = (HANDLE)DBGetContactSettingDword(hContact, MODULE, "Handle", 0)) != 0) { + DBEVENTINFO dbei = {0}; + dbei.cbSize = sizeof(dbei); + dbei.cbBlob = (int)CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0); + dbei.pBlob = (PBYTE)mir_alloc(dbei.cbBlob); + if(CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei) == 0) { + if(dbei.eventType < EVENTTYPE_ADDED) { + DBEVENTINFO dbeiMeta = {0}; + dbeiMeta.cbSize = sizeof(dbeiMeta); + dbeiMeta.eventType = dbei.eventType; + dbeiMeta.flags = dbei.flags & ~DBEF_READ; + dbeiMeta.cbBlob = dbei.cbBlob; + dbeiMeta.pBlob = dbei.pBlob; + dbeiMeta.szModule = dbei.szModule; + dbeiMeta.timestamp = dbei.timestamp; + + if(dbei.eventType == EVENTTYPE_MESSAGE) { + if(!(dbeiMeta.flags & DBEF_SENT) + && DBGetContactSettingByte(hContact, MODULE, "WindowOpen", 0) == 1 + && DBGetContactSettingByte(hMeta, MODULE, "WindowOpen", 0) == 0) + { + dbeiMeta.flags |= DBEF_READ; + } + + // set default + int num = metaMap[hMeta].index_of(hContact); + if(num != -1 && num != DBGetContactSettingByte(hMeta, MODULE, "Default", -1)) + MetaAPI_SetDefaultContactNum((WPARAM)hMeta, (LPARAM)num); + + // set meta nick + char *proto = ContactProto(hContact); + DBVARIANT dbv; + if(proto && !DBGetContactSettingUTF8String(0, proto, "Nick", &dbv)) { + DBWriteContactSettingUTF8String(0, MODULE, "Nick", dbv.pszVal); + DBFreeVariant(&dbv); + } + } + CallService(MS_DB_EVENT_ADD, (WPARAM)hMeta, (LPARAM)&dbeiMeta); + } + } + mir_free(dbei.pBlob); + } + + return 0; +} + +HANDLE hEventMessageWindow = 0; +int ModulesLoadedProto(WPARAM wParam, LPARAM lParam) { + hEventMessageWindow = (HANDLE)HookEvent(ME_MSG_WINDOWEVENT, WindowEvent); + + Meta_Hide(DBGetContactSettingByte(0, MODULE, "Enabled", 1) == 0); + return 0; +} + +#define NUM_SERVICES 14 +HANDLE hServices[NUM_SERVICES] = {0}, hEventContactDeleted = 0, hEventSettingChanged = 0; +#define NUM_HOOKS_INTERNAL 8 +HANDLE hHooksInternal[NUM_HOOKS_INTERNAL] = {0}; + +void InitProto() { + RegisterProto(); + + // create our services + int i = 0; + + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_GETCAPS, GetCaps); + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_GETNAME, GetName); + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_LOADICON, LoadIcon); + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_SETSTATUS, SetStatus); + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_GETSTATUS, GetStatus); + + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_GETINFO, ProtoGetInfo); + + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_MESSAGE, ProtoSendMessage); + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_MESSAGE"W", ProtoSendMessageW); + hServices[i++] = CreateProtoServiceFunction(MODULE, PSR_MESSAGE, ProtoRecvMessage); + + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_GETAWAYMSG, ProtoGetAwayMsg); + + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_GETAVATARINFO, ProtoGetAvatarInfo); + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_FILE, ProtoFileSend); + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_USERISTYPING, ProtoUserIsTyping); + + // REMEMBER to modify the NUM_SERVICES #define above if you add more services! + + hServices[i++] = CreateProtoServiceFunction(MODULE, "/SendNudge", SendNudge); + + hEventContactDeleted = HookEvent(ME_DB_CONTACT_DELETED, ContactDeleted); + hEventSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged); + + i = 0; + hHooksInternal[i++] = (HANDLE)HookEvent(ME_MC_DEFAULTTCHANGED, MetaChanged ); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_MC_FORCESEND, MetaChanged ); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_MC_UNFORCESEND, MetaChanged ); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_PROTO_ACK, RedirectACKs); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoadedProto); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_DB_EVENT_ADDED, EventAdded); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_DB_EVENT_FILTER_ADD, EventFilterAdd); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_PROTO_CONTACTISTYPING, EventContactIsTyping); +} + +void DeinitProto() { + UnhookEvent(hEventMessageWindow); + UnhookEvent(hEventSettingChanged); + UnhookEvent(hEventContactDeleted); + for(int i = 0; i < NUM_SERVICES; i++) + DestroyServiceFunction(hServices[i]); + for(int i = 0; i < NUM_HOOKS_INTERNAL; i++) + UnhookEvent(hHooksInternal[i]); + +} \ No newline at end of file diff --git a/meta2/proto.h b/meta2/proto.h new file mode 100644 index 0000000..176791e --- /dev/null +++ b/meta2/proto.h @@ -0,0 +1,11 @@ +#ifndef _FILTER_INC +#define _FILTER_INC + +void InitProto(); +void DeinitProto(); + +HANDLE NewMetaContact(); + +#define META_ID "MetaID" + +#endif diff --git a/meta2/resource.h b/meta2/resource.h new file mode 100644 index 0000000..7462749 --- /dev/null +++ b/meta2/resource.h @@ -0,0 +1,45 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by meta2.rc +// +#define IDD_OPT1 109 +#define IDI_MCSETDEFAULT 120 +#define IDI_MCMENU 121 +#define IDI_MCMENUOFF 122 +#define IDI_MCMENUOFF2 123 +#define IDI_MCADD 124 +#define IDI_MCCONVERT 125 +#define IDI_MCEDIT 126 +#define IDI_MCREMOVE 127 + +#define IDD_PRIORITIES 128 +#define IDC_ED_PRIORITY 1001 +#define IDC_SP_PRIORITY 1002 +#define IDC_CMB_STATUS 1003 +#define IDC_CMB_PROTOCOL 1004 +#define IDC_BTN_RESET 1005 +#define IDC_CHK_DEFAULT 1006 + +#define IDD_METASELECT 129 +#define IDC_METALIST 1007 +#define IDC_CHK_SRT 1008 + +#define IDD_METAEDIT 130 +#define IDC_VALIDATE 1009 +#define IDC_BTN_REM 1010 +#define IDC_BTN_SETDEFAULT 1011 +#define IDC_ED_NAME 1012 +#define IDC_LST_CONTACTS 1013 + +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1014 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/meta2/resource.rc b/meta2/resource.rc new file mode 100644 index 0000000..f8cad6f --- /dev/null +++ b/meta2/resource.rc @@ -0,0 +1,2 @@ +#include "version.rc" +#include "meta2.rc" diff --git a/meta2/select_meta.cpp b/meta2/select_meta.cpp new file mode 100644 index 0000000..74df9c5 --- /dev/null +++ b/meta2/select_meta.cpp @@ -0,0 +1,95 @@ +#include "common.h" +#include "select_meta.h" +#include "resource.h" +#include "icons.h" + + +void FillList(HWND list, bool sort) +{ + SendMessage(list, LB_RESETCONTENT, 0, 0); + HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); + int count = 0; + while(hContact) { + if(!IsMetacontact(hContact)) { + // This isn't a MetaContact, go to the next + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0); + continue; + } + + // get contact display name from clist + TCHAR *szCDN = (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR); + int index; + if(sort) { + int j; + TCHAR buff[1024]; + for(j = 0; j < count; j++) { + SendMessageW(list, LB_GETTEXT, j, (LPARAM)buff); + if(_tcscmp(buff, szCDN) > 0) break; + } + index = SendMessageW(list, LB_INSERTSTRING, (WPARAM)j, (LPARAM)szCDN); + } else + index = SendMessage(list, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)szCDN); + SendMessage(list, LB_SETITEMDATA, (WPARAM)index, (LPARAM)hContact); + count++; + + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0); + } +} + +BOOL CALLBACK Meta_SelectDialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { + case WM_INITDIALOG: + { + TranslateDialogDefault( hwndDlg ); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIconEx(I_ADD)); + SetWindowText(hwndDlg, (TCHAR *)lParam); + FillList(GetDlgItem(hwndDlg, IDC_METALIST), false); + } + case WM_COMMAND: + if(HIWORD(wParam) == BN_CLICKED) { + switch(LOWORD(wParam)) + { + case IDOK: + { + HANDLE hContact = (HANDLE)GetWindowLong(hwndDlg, GWL_USERDATA); + int item = SendMessage(GetDlgItem(hwndDlg, IDC_METALIST),LB_GETCURSEL, 0, 0); + HANDLE hMeta = (HANDLE)SendMessage(GetDlgItem(hwndDlg, IDC_METALIST), LB_GETITEMDATA, (WPARAM)item, 0); + + EndDialog(hwndDlg, (INT_PTR)hMeta); + break; + } + case IDCANCEL: + EndDialog(hwndDlg, 0); + break; + + case IDC_CHK_SRT: + FillList(GetDlgItem(hwndDlg,IDC_METALIST), IsDlgButtonChecked(hwndDlg, IDC_CHK_SRT)); + EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); + break; + } + return TRUE; + } + if(LOWORD(wParam) == IDC_METALIST && HIWORD(wParam) == LBN_SELCHANGE) { + int item = SendMessage(GetDlgItem(hwndDlg, IDC_METALIST),LB_GETCURSEL, 0, 0); + EnableWindow(GetDlgItem(hwndDlg, IDOK), item != -1); + } + break; + case WM_DESTROY: + { // Free all allocated memory and return the focus to the CList + HWND clist = GetParent(hwndDlg); + ReleaseIconEx((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_BIG, 0)); + EndDialog(hwndDlg,TRUE); + SetFocus(clist); + return TRUE; + } + } + return FALSE; // All other Message are not handled +} + + +HANDLE SelectMeta(TCHAR *msg) { + HWND clui = (HWND)CallService(MS_CLUI_GETHWND,0,0); + return (HANDLE)DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_METASELECT), clui, Meta_SelectDialogProc, (WPARAM)msg); +} \ No newline at end of file diff --git a/meta2/select_meta.h b/meta2/select_meta.h new file mode 100644 index 0000000..0547cad --- /dev/null +++ b/meta2/select_meta.h @@ -0,0 +1,2 @@ + +HANDLE SelectMeta(TCHAR *msg); \ No newline at end of file diff --git a/meta2/settings.cpp b/meta2/settings.cpp new file mode 100644 index 0000000..06c9b9f --- /dev/null +++ b/meta2/settings.cpp @@ -0,0 +1,110 @@ +#include "common.h" +#include "settings.h" +#include "core_functions.h" + +PLUGINLINK *core_link = 0; +typedef int (*ServiceFunc)(const char *,WPARAM,LPARAM); +ServiceFunc coreCallService, coreCallServiceSync; + +int ServiceFuncRedirect(const char *service,WPARAM wParam, LPARAM lParam, ServiceFunc coreServiceFunc) { + if(wParam ==0 + || strncmp(service, "DB/Contact/", 11) != 0 + || (strcmp(MS_DB_CONTACT_GETSETTING, service) != 0 + && strcmp(MS_DB_CONTACT_GETSETTING_STR, service) != 0 + && strcmp(MS_DB_CONTACT_GETSETTINGSTATIC, service) != 0 + && strcmp(MS_DB_CONTACT_WRITESETTING, service) != 0 + && strcmp(MS_DB_CONTACT_DELETESETTING, service) != 0 + && strcmp(MS_DB_CONTACT_ENUMSETTINGS, service) != 0)) + { + return coreServiceFunc(service, wParam, lParam); + } + + HANDLE hContact = (HANDLE)wParam; + if(strcmp(MS_DB_CONTACT_ENUMSETTINGS, service) == 0) { + DBCONTACTENUMSETTINGS *ces = (DBCONTACTENUMSETTINGS *)lParam; + if(IsMetacontact(hContact) && strcmp(ces->szModule, MODULE) != 0) { + HANDLE hSub = Meta_GetMostOnline(hContact); + return coreServiceFunc(service, (WPARAM)hSub, lParam); + } else + return coreServiceFunc(service, wParam, lParam); + } + + const char *szSetting = 0, *szModule = 0; + bool read; // true for get setting, false for write setting + if(strcmp(MS_DB_CONTACT_WRITESETTING, service) == 0 || strcmp(MS_DB_CONTACT_DELETESETTING, service)) { + read = false; + DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *)lParam; + szSetting = cws->szSetting; + szModule = cws->szModule; + } else { // get setting + read = true; + DBCONTACTGETSETTING *cgs = (DBCONTACTGETSETTING *)lParam; + szSetting = cgs->szSetting; + szModule = cgs->szModule; + } + + if(szModule == 0 || szSetting == 0 + || strcmp(szModule, "Protocol") == 0 + || strcmp(szModule, "_Filter") == 0 + || (strcmp(szModule, MODULE) == 0 + && (strcmp(szSetting, "MetaID") == 0 + || strcmp(szSetting, "IsSubcontact") == 0 + || strcmp(szSetting, "Default") == 0 + || strcmp(szSetting, "Handle") == 0 + || strcmp(szSetting, "WindowOpen") == 0 + || strcmp(szSetting, "ParentMetaID") == 0 + || strcmp(szSetting, "Status") == 0)) + || !IsMetacontact(hContact)) + { + return coreServiceFunc(service, wParam, lParam); + } + + HANDLE hMeta = hContact; + int ret; + if((ret = coreServiceFunc(service, (WPARAM)hMeta, lParam)) != 0) { + // if the setting does not exist in the metacontact, get it from the most online subcontact + HANDLE hSub = Meta_GetMostOnline(hMeta); + if(!hSub) return ret; // no most online - fail + if((ret = coreServiceFunc(service, (WPARAM)hSub, lParam)) != 0) { + // if it does not exist in the subcontact and we're using the meta proto module, try changing the module to the subcontact proto module + if(strcmp(szModule, MODULE) == 0) { + char *subProto = ContactProto(hSub); + if(subProto) { + if(read) { + DBCONTACTGETSETTING *cgs = (DBCONTACTGETSETTING *)lParam; + cgs->szModule = MODULE; + } else { + DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *)lParam; + cws->szModule = MODULE; + } + return coreServiceFunc(service, (WPARAM)hSub, lParam); + } else + return ret; // no subcontact proto - fail + } else + return ret; // not reading from meta proto module - fail + } else + return 0; // got from subcontact + } else + return 0; // got from metacontact +} + +int CallServiceRedirect(const char *service,WPARAM wParam, LPARAM lParam) { + return ServiceFuncRedirect(service, wParam, lParam, coreCallService); +} + +int CallServiceSyncRedirect(const char *service,WPARAM wParam, LPARAM lParam) { + return ServiceFuncRedirect(service, wParam, lParam, coreCallServiceSync); +} + +void InitSettings(PLUGINLINK *link) { + core_link = link; + coreCallService = core_link->CallService; + coreCallServiceSync = core_link->CallServiceSync; + core_link->CallService = CallServiceRedirect; + core_link->CallServiceSync = CallServiceSyncRedirect; +} + +void DeinitSettings() { + core_link->CallService = coreCallService; + core_link->CallServiceSync = coreCallServiceSync; +} \ No newline at end of file diff --git a/meta2/settings.h b/meta2/settings.h new file mode 100644 index 0000000..f8b0f31 --- /dev/null +++ b/meta2/settings.h @@ -0,0 +1,4 @@ +#include + +void InitSettings(PLUGINLINK *link); +void DeinitSettings(); \ No newline at end of file diff --git a/meta2/version.h b/meta2/version.h new file mode 100644 index 0000000..81ba1a8 --- /dev/null +++ b/meta2/version.h @@ -0,0 +1,26 @@ +// Set the version number here - it will affect the version resource and the version field of the pluginInfo structure +// (Be careful that you don't have the resource file open when you change this and rebuild, otherwise the changes may not +// take effect within the version resource) + +#define __MAJOR_VERSION 0 +#define __MINOR_VERSION 0 +#define __RELEASE_NUM 0 +#define __BUILD_NUM 1 + +#define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM +#define __FILEVERSION_STRING_DOTS __MAJOR_VERSION.__MINOR_VERSION.__RELEASE_NUM.__BUILD_NUM +#define __STRINGIFY(x) #x +#define __VERSION_STRING __STRINGIFY(__FILEVERSION_STRING_DOTS) + +#ifdef _UNICODE +#define __PLUGIN_NAME "meta2 (Unicode)" +#else +#define __PLUGIN_NAME "meta2" +#endif +#define __FILENAME "meta2.dll" + +#define __DESC "Merges multiple contacts into one" +#define __AUTHOR "Scott Ellis" +#define __AUTHOREMAIL "mail@scottellis.com.au" +#define __AUTHORWEB "http://www.scottellis.com.au" +#define __COPYRIGHT "© 2007 Scott Ellis" diff --git a/meta2/version.rc b/meta2/version.rc new file mode 100644 index 0000000..0bb7a49 --- /dev/null +++ b/meta2/version.rc @@ -0,0 +1,33 @@ + +#include +#include "version.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION __FILEVERSION_STRING + PRODUCTVERSION __FILEVERSION_STRING + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Author", __AUTHOR + VALUE "FileDescription", __DESC + VALUE "InternalName", __PLUGIN_NAME + VALUE "LegalCopyright", __COPYRIGHT + VALUE "OriginalFilename", __FILENAME + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END -- cgit v1.2.3