summaryrefslogtreecommitdiff
path: root/plugins/VoiceService/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2020-01-16 22:03:01 +0300
committerGeorge Hazan <ghazan@miranda.im>2020-01-16 22:03:01 +0300
commitbd00314ec32d42fa09918a8ca274b0707c73ec75 (patch)
tree586b657b523ade102b92dee5323937e0a1c42d2d /plugins/VoiceService/src
parent30b859e8e95b1bb4f5e0d0f9d4d6fa40507fd788 (diff)
VoiceService: adaptaion of the old plugin written by Pescuma for Miranda NG
Diffstat (limited to 'plugins/VoiceService/src')
-rw-r--r--plugins/VoiceService/src/VoiceCall.cpp257
-rw-r--r--plugins/VoiceService/src/VoiceProvider.cpp178
-rw-r--r--plugins/VoiceService/src/frame.cpp741
-rw-r--r--plugins/VoiceService/src/frame.h36
-rw-r--r--plugins/VoiceService/src/hooks.cpp747
-rw-r--r--plugins/VoiceService/src/main.cpp69
-rw-r--r--plugins/VoiceService/src/options.cpp456
-rw-r--r--plugins/VoiceService/src/options.h67
-rw-r--r--plugins/VoiceService/src/popup.cpp212
-rw-r--r--plugins/VoiceService/src/popup.h49
-rw-r--r--plugins/VoiceService/src/resource.h87
-rw-r--r--plugins/VoiceService/src/services.cpp171
-rw-r--r--plugins/VoiceService/src/stdafx.cxx18
-rw-r--r--plugins/VoiceService/src/stdafx.h218
-rw-r--r--plugins/VoiceService/src/version.h13
15 files changed, 3319 insertions, 0 deletions
diff --git a/plugins/VoiceService/src/VoiceCall.cpp b/plugins/VoiceService/src/VoiceCall.cpp
new file mode 100644
index 0000000000..fbfd0760a1
--- /dev/null
+++ b/plugins/VoiceService/src/VoiceCall.cpp
@@ -0,0 +1,257 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static wchar_t *popupTitles[] = {
+ LPGENW("Voice call started"),
+ LPGENW("Voice call ringing"),
+ LPGENW("Voice call"),
+ LPGENW("Voice call on hold"),
+ LPGENW("Voice call ended"),
+ LPGENW("Voice call busy"),
+};
+
+static wchar_t *stateTexts[] = {
+ LPGENW("Call from %s has started"),
+ LPGENW("Call from %s is ringing"),
+ LPGENW("Calling %s"),
+ LPGENW("Call from %s is on hold"),
+ LPGENW("Call from %s has ended"),
+ LPGENW("%s is busy"),
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// VoiceCall class members
+
+VoiceCall::VoiceCall(VoiceProvider *module, const char *id)
+ : module(module), id(mir_strdup(id))
+{
+ hContact = NULL;
+ name[0] = 0;
+ number[0] = 0;
+ displayName[0] = 0;
+ state = -1;
+ end_time = 0;
+ clistBlinking = false;
+ incoming = false;
+ secure = false;
+ hwnd = NULL;
+ CreateDisplayName();
+}
+
+VoiceCall::~VoiceCall()
+{
+ RemoveNotifications();
+ mir_free(id);
+ id = NULL;
+}
+
+void VoiceCall::AppendCallerID(MCONTACT aHContact, const wchar_t *aName, const wchar_t *aNumber)
+{
+ bool changed = false;
+
+ if (aHContact != NULL) {
+ hContact = aHContact;
+ changed = true;
+ }
+
+ if (!IsEmptyW(aName)) {
+ lstrcpyn(name, aName, _countof(name));
+ changed = true;
+ }
+
+ if (!IsEmptyW(aNumber)) {
+ lstrcpyn(number, aNumber, _countof(number));
+ changed = true;
+ }
+
+ if (changed)
+ CreateDisplayName();
+}
+
+void VoiceCall::CreateDisplayName()
+{
+ wchar_t *contact = NULL;
+ if (hContact != NULL)
+ contact = Clist_GetContactDisplayName(hContact, 0);
+
+ wchar_t *nameTmp = NULL;
+ if (lstrcmp(name, number) != 0)
+ nameTmp = name;
+
+ if (contact != NULL) {
+ if (!IsEmptyW(number))
+ mir_snwprintf(displayName, _countof(displayName), _T("%s <%s>"), contact, number);
+ else
+ lstrcpyn(displayName, contact, _countof(displayName));
+ }
+ else if (!IsEmptyW(nameTmp) && !IsEmptyW(number)) {
+ mir_snwprintf(displayName, _countof(displayName), _T("%s <%s>"), name, number);
+ }
+ else if (!IsEmptyW(nameTmp)) {
+ lstrcpyn(displayName, name, _countof(displayName));
+ }
+ else if (!IsEmptyW(number)) {
+ lstrcpyn(displayName, number, _countof(displayName));
+ }
+ else {
+ lstrcpyn(displayName, TranslateT("Unknown number"), _countof(displayName));
+ }
+}
+
+void VoiceCall::RemoveNotifications()
+{
+ if (hwnd != NULL) {
+ DestroyWindow(hwnd);
+ hwnd = NULL;
+ }
+
+ if (clistBlinking) {
+ g_clistApi.pfnRemoveEvent(hContact, (LPARAM)this);
+ clistBlinking = false;
+ }
+}
+
+void VoiceCall::SetState(int aState)
+{
+ if (state == aState)
+ return;
+
+ if (aState == VOICE_STATE_RINGING)
+ incoming = true;
+ else if (aState == VOICE_STATE_CALLING)
+ incoming = false;
+
+ RemoveNotifications();
+
+ state = aState;
+
+ if (IsFinished()) {
+ if (end_time == 0)
+ end_time = GetTickCount();
+
+ // Remove id because providers can re-use them
+ mir_free(id);
+ id = NULL;
+ }
+
+ Notify();
+}
+
+void VoiceCall::Notify(bool popup, bool sound, bool clist)
+{
+ if (popup) {
+ wchar_t text[512];
+ mir_snwprintf(text, _countof(text), TranslateW(stateTexts[state]), displayName);
+
+ ShowPopup(NULL, TranslateW(popupTitles[state]), text);
+ }
+
+ if (sound)
+ Skin_PlaySound(g_sounds[state].szName);
+
+ if (clist && state == VOICE_STATE_RINGING) {
+ CLISTEVENT ce = {};
+ ce.hContact = hContact;
+ ce.hIcon = g_plugin.getIcon(IDI_RINGING);
+ ce.hDbEvent = 1001;
+ ce.pszService = MS_VOICESERVICE_CLIST_DBLCLK;
+ ce.lParam = (LPARAM)this;
+ g_clistApi.pfnAddEvent(&ce);
+
+ IcoLib_ReleaseIcon(ce.hIcon);
+
+ clistBlinking = true;
+ }
+
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+}
+
+bool VoiceCall::IsFinished()
+{
+ return IsFinalState(state);
+}
+
+bool VoiceCall::CanDrop()
+{
+ return !IsFinished();
+}
+
+void VoiceCall::Drop()
+{
+ if (!CanDrop())
+ return;
+
+ RemoveNotifications();
+
+ CallProtoService(module->name, PS_VOICE_DROPCALL, (WPARAM)id, 0);
+}
+
+bool VoiceCall::CanAnswer()
+{
+ return state == -1 || state == VOICE_STATE_RINGING || state == VOICE_STATE_ON_HOLD;
+}
+
+void VoiceCall::Answer()
+{
+ if (!CanAnswer())
+ return;
+
+ RemoveNotifications();
+
+ CallProtoService(module->name, PS_VOICE_ANSWERCALL, (WPARAM)id, 0);
+}
+
+bool VoiceCall::CanHold()
+{
+ return module->CanHold() && (state == -1 || state == VOICE_STATE_TALKING);
+}
+
+void VoiceCall::Hold()
+{
+ if (!CanHold())
+ return;
+
+ RemoveNotifications();
+
+ CallProtoService(module->name, PS_VOICE_HOLDCALL, (WPARAM)id, 0);
+}
+
+bool VoiceCall::CanSendDTMF()
+{
+ return module->CanSendDTMF() && state == VOICE_STATE_TALKING;
+}
+
+void VoiceCall::SendDTMF(wchar_t c)
+{
+ if (!CanSendDTMF())
+ return;
+
+ CallProtoService(module->name, PS_VOICE_SEND_DTMF, (WPARAM)id, (LPARAM)c);
+}
+
+void VoiceCall::SetNewCallHWND(HWND _h)
+{
+ if (_h != NULL)
+ RemoveNotifications();
+
+ hwnd = _h;
+}
diff --git a/plugins/VoiceService/src/VoiceProvider.cpp b/plugins/VoiceService/src/VoiceProvider.cpp
new file mode 100644
index 0000000000..b7a352036c
--- /dev/null
+++ b/plugins/VoiceService/src/VoiceProvider.cpp
@@ -0,0 +1,178 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static bool IsProtocol(const char *module)
+{
+ for (auto *pa : Accounts()) {
+ if (pa->szModuleName == NULL || pa->szModuleName[0] == '\0')
+ continue;
+
+ if (strcmp(module, pa->szModuleName) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// VoiceProvider class members
+
+static int VoiceState(WPARAM wParam, LPARAM)
+{
+ VOICE_CALL *in = (VOICE_CALL *)wParam;
+ if (in == NULL || in->cbSize < sizeof(VOICE_CALL) || in->moduleName == NULL || in->id == NULL)
+ return 0;
+
+ // Check if the call is aready in list
+ VoiceCall *call = FindVoiceCall(in->moduleName, in->id, !IsFinalState(in->state));
+ if (call == NULL)
+ return 0;
+
+ call->AppendCallerID(in->hContact,
+ (in->flags & VOICE_UNICODE) ? in->pwszName : _A2T(in->pszName),
+ (in->flags & VOICE_UNICODE) ? in->pwszNumber : _A2T(in->pszNumber));
+ call->secure = (in->flags & VOICE_SECURE) != 0;
+
+ if (in->state == VOICE_STATE_RINGING && call->hContact != NULL) {
+ int aut = g_plugin.getWord(call->hContact, "AutoAccept", AUTO_NOTHING);
+ if (aut == AUTO_ACCEPT || aut == AUTO_DROP) {
+ call->state = VOICE_STATE_RINGING;
+ call->Notify(false, false, false);
+
+ if (aut == AUTO_ACCEPT)
+ Answer(call);
+ else
+ call->Drop();
+
+ return 0;
+ }
+ }
+
+ if (in->state == VOICE_STATE_TALKING)
+ HoldOtherCalls(call);
+
+ call->SetState(in->state);
+
+ return 0;
+}
+
+VoiceProvider::VoiceProvider(const char *name, const wchar_t *description, int flags, const char *icon)
+{
+ strncpy(this->name, name, _countof(this->name));
+ this->name[_countof(this->name) - 1] = 0;
+
+ lstrcpyn(this->description, description, _countof(this->description));
+
+ if (icon == NULL)
+ this->icon[0] = 0;
+ else
+ lstrcpynA(this->icon, icon, _countof(this->icon));
+
+ this->flags = flags;
+ is_protocol = IsProtocol(name);
+ canHold = (ProtoServiceExists(name, PS_VOICE_HOLDCALL) != 0);
+
+ char str[MAXMODULELABELLENGTH];
+ mir_snprintf(str, _countof(str), "%s%s", name, PE_VOICE_CALL_STATE);
+ state_hook = HookEvent(str, VoiceState);
+}
+
+VoiceProvider::~VoiceProvider()
+{
+ UnhookEvent(state_hook);
+ state_hook = NULL;
+}
+
+bool VoiceProvider::CanCall(MCONTACT hContact, BOOL now)
+{
+ if (hContact == NULL)
+ return false;
+
+ if ((flags & VOICE_CAPS_CALL_CONTACT) == 0)
+ return false;
+
+ if (ProtoServiceExists(name, PS_VOICE_CALL_CONTACT_VALID))
+ return CallProtoService(name, PS_VOICE_CALL_CONTACT_VALID, (WPARAM)hContact, now) != FALSE;
+
+ if (is_protocol) {
+ if (now && CallProtoService(name, PS_GETSTATUS, 0, 0) <= ID_STATUS_OFFLINE)
+ return false;
+
+ if (!Proto_IsProtoOnContact(hContact, name))
+ return false;
+
+ return db_get_w(hContact, name, "Status", ID_STATUS_OFFLINE) > ID_STATUS_OFFLINE;
+ }
+
+ return true;
+}
+
+bool VoiceProvider::CanCall(const wchar_t *number)
+{
+ if (number == NULL || number[0] == 0)
+ return false;
+
+ if ((flags & VOICE_CAPS_CALL_STRING) == 0)
+ return false;
+
+ if (ProtoServiceExists(name, PS_VOICE_CALL_STRING_VALID))
+ return CallProtoService(name, PS_VOICE_CALL_STRING_VALID, (WPARAM)number, 0) != FALSE;
+
+ if (is_protocol)
+ return CallProtoService(name, PS_GETSTATUS, 0, 0) > ID_STATUS_OFFLINE;
+
+ return true;
+}
+
+bool VoiceProvider::CanHold()
+{
+ return canHold;
+}
+
+bool VoiceProvider::CanSendDTMF()
+{
+ return ProtoServiceExists(name, PS_VOICE_SEND_DTMF) != FALSE;
+}
+
+void VoiceProvider::Call(MCONTACT hContact, const wchar_t *number)
+{
+ CallProtoService(name, PS_VOICE_CALL, (WPARAM)hContact, (LPARAM)number);
+}
+
+HICON VoiceProvider::GetIcon()
+{
+ if (!IsEmptyA(icon))
+ return IcoLib_GetIcon(icon);
+
+ if (is_protocol)
+ return Skin_LoadProtoIcon(name, ID_STATUS_ONLINE);
+
+ return NULL;
+}
+
+void VoiceProvider::ReleaseIcon(HICON hIcon)
+{
+ if (hIcon == NULL)
+ return;
+
+ if (!IsEmptyA(icon))
+ IcoLib_ReleaseIcon(hIcon);
+}
diff --git a/plugins/VoiceService/src/frame.cpp b/plugins/VoiceService/src/frame.cpp
new file mode 100644
index 0000000000..a95f04f886
--- /dev/null
+++ b/plugins/VoiceService/src/frame.cpp
@@ -0,0 +1,741 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HWND hwnd_frame = NULL;
+HWND hwnd_container = NULL;
+
+int frame_id = -1;
+
+static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+#define H_SPACE 2
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+void InitFrames()
+{
+ if (ServiceExists(MS_CLIST_FRAMES_ADDFRAME)) {
+ hwnd_frame = CreateDialogW(g_plugin.getInst(), MAKEINTRESOURCE(IDD_CALLS), g_clistApi.hwndContactList, FrameWndProc);
+
+ CLISTFrame Frame = {};
+ Frame.cbSize = sizeof(CLISTFrame);
+ Frame.szName.w = TranslateT("Voice Calls");
+ Frame.hWnd = hwnd_frame;
+ Frame.align = alBottom;
+ Frame.Flags = F_VISIBLE | F_NOBORDER | F_LOCKED | F_UNICODE;
+ Frame.height = 0;
+ Frame.hIcon = g_plugin.getIcon(IDI_MAIN, true);
+
+ frame_id = CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)&Frame, 0);
+ }
+}
+
+void DeInitFrames()
+{
+ if (ServiceExists(MS_CLIST_FRAMES_REMOVEFRAME) && frame_id != -1)
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)frame_id, 0);
+
+ if (hwnd_frame != NULL)
+ DestroyWindow(hwnd_frame);
+}
+
+static int GetMaxLineHeight()
+{
+ return max(ICON_SIZE, font_max_height) + 1;
+}
+
+BOOL FrameIsFloating(int id)
+{
+ if (id == -1)
+ return TRUE; // no frames, always floating
+
+ return CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLOATING, id), 0);
+}
+
+void ResizeFrame(int id, HWND hwnd)
+{
+ int height = calls.getCount() * GetMaxLineHeight();
+ if (height > 0)
+ height += 2;
+
+ if (CanCallNumber()) {
+ height += 23 + 2;
+
+ if (SendMessage(GetDlgItem(hwnd, IDC_DIALPAD), BM_GETCHECK, 0, 0) == BST_CHECKED) {
+ RECT first, last;
+ GetWindowRect(GetDlgItem(hwnd, IDC_1), &first);
+ GetWindowRect(GetDlgItem(hwnd, IDC_SHARP), &last);
+
+ height += last.bottom - first.top + 1;
+ }
+ }
+
+ if (FrameIsFloating(id)) {
+ HWND parent = GetParent(hwnd);
+ if (parent == NULL)
+ return;
+
+ RECT r_client;
+ GetClientRect(hwnd, &r_client);
+
+ if (r_client.bottom - r_client.top == height)
+ return;
+
+ RECT parent_client, parent_window, r_window;
+ GetClientRect(parent, &parent_client);
+ GetWindowRect(parent, &parent_window);
+ GetWindowRect(hwnd, &r_window);
+
+ int diff = (parent_window.bottom - parent_window.top) - (parent_client.bottom - parent_client.top);
+ if (ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ diff += (r_window.top - parent_window.top);
+
+ SetWindowPos(parent, 0, 0, 0, parent_window.right - parent_window.left, height + diff, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+ }
+ else {
+ int old_height = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_HEIGHT, id), 0);
+ if (old_height == height)
+ return;
+
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_HEIGHT, id), (LPARAM)height);
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME, (WPARAM)id, (LPARAM)(FU_TBREDRAW | FU_FMREDRAW | FU_FMPOS));
+ }
+}
+
+void ShowFrame(int id, HWND hwnd, int show)
+{
+ BOOL is_visible = IsWindowVisible(hwnd);
+ if ((is_visible && show == SW_SHOW) || (!is_visible && show == SW_HIDE))
+ return;
+
+ if (ServiceExists(MS_CLIST_FRAMES_SHFRAME) && id != -1)
+ CallService(MS_CLIST_FRAMES_SHFRAME, (WPARAM)id, 0);
+ else
+ ShowWindow(GetParent(hwnd), show);
+}
+
+static int dialCtrls[] = {
+ IDC_DIALPAD, IDC_NUMBER, IDC_CALL,
+ IDC_1, IDC_2, IDC_3,
+ IDC_4, IDC_5, IDC_6,
+ IDC_7, IDC_8, IDC_9,
+ IDC_AST, IDC_0, IDC_SHARP
+};
+
+static wchar_t *lstrtrim(wchar_t *str)
+{
+ int len = lstrlen(str);
+
+ int i;
+ for (i = len - 1; i >= 0 && (str[i] == ' ' || str[i] == '\t'); --i);
+ if (i < len - 1) {
+ ++i;
+ str[i] = _T('\0');
+ len = i;
+ }
+
+ for (i = 0; i < len && (str[i] == ' ' || str[i] == '\t'); ++i);
+ if (i > 0)
+ memmove(str, &str[i], (len - i + 1) * sizeof(wchar_t));
+
+ return str;
+}
+
+static void InvalidateAll(HWND hwnd)
+{
+ InvalidateRect(GetDlgItem(hwnd, IDC_CALLS), NULL, FALSE);
+ for (int i = 0; i < _countof(dialCtrls); ++i)
+ InvalidateRect(GetDlgItem(hwnd, dialCtrls[i]), NULL, FALSE);
+ InvalidateRect(hwnd, NULL, FALSE);
+
+ if (frame_id != -1)
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME, (WPARAM)frame_id, (LPARAM)(FU_FMREDRAW));
+}
+
+static void ShowHideDialpad(HWND hwnd)
+{
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+
+ if (!CanCallNumber()) {
+ for (int i = 0; i < _countof(dialCtrls); ++i)
+ ShowWindow(GetDlgItem(hwnd, dialCtrls[i]), SW_HIDE);
+ }
+ else {
+ int i;
+ for (i = 0; i < 3; ++i)
+ ShowWindow(GetDlgItem(hwnd, dialCtrls[i]), SW_SHOW);
+
+ bool showDialpad = (SendMessage(GetDlgItem(hwnd, IDC_DIALPAD), BM_GETCHECK, 0, 0) == BST_CHECKED);
+
+ for (i = 3; i < _countof(dialCtrls); ++i)
+ ShowWindow(GetDlgItem(hwnd, dialCtrls[i]), showDialpad ? SW_SHOW : SW_HIDE);
+
+ VoiceCall *talking = NULL;
+ bool ringing = false;
+ bool calling = false;
+ for (i = 0; i < calls.getCount(); i++) {
+ VoiceCall *call = &calls[i];
+ if (call->state == VOICE_STATE_TALKING)
+ talking = call;
+ else if (call->state == VOICE_STATE_CALLING)
+ calling = true;
+ else if (call->state == VOICE_STATE_RINGING)
+ ringing = true;
+ }
+
+ wchar_t number[1024];
+ GetDlgItemText(hwnd, IDC_NUMBER, number, _countof(number));
+ lstrtrim(number);
+
+ if (ringing && number[0] != 0) {
+ SetWindowText(GetDlgItem(hwnd, IDC_NUMBER), _T(""));
+ number[0] = 0;
+ }
+
+ if (ringing || calling) {
+ for (i = 0; i < _countof(dialCtrls); ++i)
+ EnableWindow(GetDlgItem(hwnd, dialCtrls[i]), FALSE);
+ }
+ else if (talking) {
+ if (!showDialpad || !talking->CanSendDTMF()) {
+ for (i = 0; i < _countof(dialCtrls); ++i)
+ EnableWindow(GetDlgItem(hwnd, dialCtrls[i]), FALSE);
+
+ EnableWindow(GetDlgItem(hwnd, IDC_DIALPAD), TRUE);
+ }
+ else {
+ for (i = 0; i < _countof(dialCtrls); ++i)
+ EnableWindow(GetDlgItem(hwnd, dialCtrls[i]), TRUE);
+
+ EnableWindow(GetDlgItem(hwnd, IDC_NUMBER), FALSE);
+ EnableWindow(GetDlgItem(hwnd, IDC_CALL), FALSE);
+ }
+ }
+ else {
+ for (i = 0; i < _countof(dialCtrls); ++i)
+ EnableWindow(GetDlgItem(hwnd, dialCtrls[i]), TRUE);
+
+ EnableWindow(GetDlgItem(hwnd, IDC_CALL), CanCall(number));
+ }
+ }
+
+ SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
+
+ InvalidateAll(hwnd);
+}
+
+static int sttCompareProvidesByDescription(const VoiceProvider *p1, const VoiceProvider *p2)
+{
+ return lstrcmp(p2->description, p1->description);
+}
+
+static void DrawIconLib(HDC hDC, const RECT &rc, int iconId)
+{
+ HICON hIcon = g_plugin.getIcon(iconId);
+ if (hIcon == NULL)
+ return;
+
+ DrawIconEx(hDC, rc.left, (rc.top + rc.bottom - ICON_SIZE) / 2, hIcon, ICON_SIZE, ICON_SIZE, 0, NULL, DI_NORMAL);
+ IcoLib_ReleaseIcon(hIcon);
+}
+
+static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+
+ switch (msg) {
+ case WM_CREATE:
+ case WM_INITDIALOG:
+ SendDlgItemMessage(hwnd, IDC_DIALPAD, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwnd, IDC_DIALPAD, BUTTONSETASPUSHBTN, TRUE, 0);
+ SendDlgItemMessageA(hwnd, IDC_DIALPAD, BUTTONADDTOOLTIP, (LPARAM)Translate("Show dialpad"), 0);
+ SendDlgItemMessage(hwnd, IDC_DIALPAD, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon("vc_dialpad", TRUE));
+
+ SendDlgItemMessage(hwnd, IDC_CALL, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessageA(hwnd, IDC_CALL, BUTTONADDTOOLTIP, (LPARAM)Translate("Make call"), 0);
+ SendDlgItemMessage(hwnd, IDC_CALL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIcon("vca_call", TRUE));
+
+ PostMessage(hwnd, WMU_RESIZE_FRAME, 0, 1);
+ break;
+
+ case WM_SIZE:
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+ GetClientRect(hwnd, &rc);
+ {
+ int width = rc.right - rc.left;
+ int height = rc.bottom - rc.top;
+
+ if (CanCallNumber()) {
+ bool showDialpad = (SendMessage(GetDlgItem(hwnd, IDC_DIALPAD), BM_GETCHECK, 0, 0) == BST_CHECKED);
+
+ GetWindowRect(hwnd, &rc);
+
+ RECT first = { 0 }, last = { 0 };
+ GetWindowRect(GetDlgItem(hwnd, IDC_1), &first);
+ GetWindowRect(GetDlgItem(hwnd, IDC_SHARP), &last);
+
+ int dialpad_height = last.bottom - first.top;
+ int dialpad_width = last.right - first.left;
+
+
+ int call_height = 23;
+ int call_width = 25;
+ int top = height - call_height - 1;
+
+ if (showDialpad)
+ top -= dialpad_height + 1;
+
+ MoveWindow(GetDlgItem(hwnd, IDC_DIALPAD), 1, top, call_width - 2, call_height, FALSE);
+ MoveWindow(GetDlgItem(hwnd, IDC_NUMBER), call_width, top, width - 2 * call_width, call_height, FALSE);
+ MoveWindow(GetDlgItem(hwnd, IDC_CALL), width - call_width, top, call_width, call_height + 1, FALSE);
+
+ int dialpad_top = top + call_height + 1;
+ int dialpad_left = ((rc.right - rc.left) - dialpad_width) / 2;
+ int deltaX = dialpad_left - first.left;
+ int deltaY = dialpad_top - first.top;
+ for (int i = 3; i < _countof(dialCtrls); ++i) {
+ GetWindowRect(GetDlgItem(hwnd, dialCtrls[i]), &rc);
+ MoveWindow(GetDlgItem(hwnd, dialCtrls[i]), rc.left + deltaX, rc.top + deltaY, rc.right - rc.left, rc.bottom - rc.top, FALSE);
+ }
+
+ height -= call_height + 2;
+ if (showDialpad)
+ height -= dialpad_height + 1;
+ }
+
+ if (height <= 2) {
+ ShowWindow(GetDlgItem(hwnd, IDC_CALLS), SW_HIDE);
+ }
+ else {
+ MoveWindow(GetDlgItem(hwnd, IDC_CALLS), 1, 1, width - 2, height - 2, FALSE);
+ ShowWindow(GetDlgItem(hwnd, IDC_CALLS), SW_SHOW);
+ }
+ }
+ SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
+ InvalidateAll(hwnd);
+ break;
+
+ case WMU_REFRESH:
+ {
+ HWND list = GetDlgItem(hwnd, IDC_CALLS);
+
+ SendMessage(list, WM_SETREDRAW, FALSE, 0);
+ SendMessage(list, LB_RESETCONTENT, 0, 0);
+ for (int i = 0; i < calls.getCount(); i++) {
+ VoiceCall *call = &calls[i];
+
+ wchar_t text[512];
+ mir_snwprintf(text, _countof(text), _T("%d %s"), call->state, call->displayName);
+
+ int pos = SendMessage(list, LB_ADDSTRING, 0, (LPARAM)text);
+ if (pos == LB_ERR)
+ // TODO Show error
+ continue;
+
+ SendMessage(list, LB_SETITEMDATA, pos, (LPARAM)call);
+ }
+ SendMessage(list, WM_SETREDRAW, TRUE, 0);
+ }
+ __fallthrough;
+
+ case WMU_RESIZE_FRAME:
+ ShowHideDialpad(hwnd);
+
+ if (lParam)
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+
+ if (opts.resize_frame) {
+ if (calls.getCount() == 0 && !CanCallNumber()) {
+ ShowFrame(frame_id, hwnd, SW_HIDE);
+ }
+ else {
+ ResizeFrame(frame_id, hwnd);
+ ShowFrame(frame_id, hwnd, SW_SHOW);
+ }
+ }
+
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_CALL:
+ {
+ wchar_t number[1024];
+ GetDlgItemText(hwnd, IDC_NUMBER, number, _countof(number));
+ lstrtrim(number);
+
+ LIST<VoiceProvider> candidates(10, &sttCompareProvidesByDescription);
+
+ for (int i = 0; i < modules.getCount(); i++) {
+ if (!modules[i].CanCall(number))
+ continue;
+
+ candidates.insert(&modules[i]);
+ }
+
+ if (candidates.getCount() < 1)
+ break;
+
+ int selected;
+ if (candidates.getCount() == 1) {
+ selected = 0;
+ }
+ else {
+ HMENU menu = CreatePopupMenu();
+
+ for (int i = 0; i < candidates.getCount(); ++i) {
+ wchar_t text[1024];
+ mir_snwprintf(text, _countof(text), TranslateT("Call with %s"), candidates[i]->description);
+
+ MENUITEMINFO mii = { 0 };
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = text;
+ mii.cch = lstrlen(text);
+ mii.wID = i + 1;
+
+ // TODO: Add icon to menu
+
+ InsertMenuItem(menu, 0, TRUE, &mii);
+ }
+
+ GetWindowRect(GetDlgItem(hwnd, IDC_CALL), &rc);
+
+ POINT p;
+ p.x = rc.right;
+ p.y = rc.bottom + 1;
+
+ selected = TrackPopupMenu(menu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_RIGHTALIGN,
+ p.x, p.y, 0, hwnd, NULL);
+
+ DestroyMenu(menu);
+
+ if (selected == 0)
+ break;
+
+ selected--;
+ }
+
+ candidates[selected]->Call(NULL, number);
+ break;
+ }
+
+ case IDC_NUMBER:
+ if (HIWORD(wParam) == EN_CHANGE)
+ ShowHideDialpad(hwnd);
+ break;
+
+ case IDC_DIALPAD:
+ ShowHideDialpad(hwnd);
+ SendMessage(hwnd, WMU_RESIZE_FRAME, 0, 0);
+ break;
+
+ case IDC_1:
+ case IDC_2:
+ case IDC_3:
+ case IDC_4:
+ case IDC_5:
+ case IDC_6:
+ case IDC_7:
+ case IDC_8:
+ case IDC_9:
+ case IDC_AST:
+ case IDC_0:
+ case IDC_SHARP:
+ wchar_t text[2];
+ switch (LOWORD(wParam)) {
+ case IDC_1: text[0] = _T('1'); break;
+ case IDC_2: text[0] = _T('2'); break;
+ case IDC_3: text[0] = _T('3'); break;
+ case IDC_4: text[0] = _T('4'); break;
+ case IDC_5: text[0] = _T('5'); break;
+ case IDC_6: text[0] = _T('6'); break;
+ case IDC_7: text[0] = _T('7'); break;
+ case IDC_8: text[0] = _T('8'); break;
+ case IDC_9: text[0] = _T('9'); break;
+ case IDC_AST: text[0] = _T('*'); break;
+ case IDC_0: text[0] = _T('0'); break;
+ case IDC_SHARP: text[0] = _T('#'); break;
+ }
+ text[1] = 0;
+
+ Skin_PlaySound("voice_dialpad");
+ {
+ VoiceCall *call = GetTalkingCall();
+ if (call == NULL) {
+ SendMessage(GetDlgItem(hwnd, IDC_NUMBER), EM_REPLACESEL, TRUE, (LPARAM)text);
+ }
+ else {
+ wchar_t tmp[1024];
+
+ GetWindowText(GetDlgItem(hwnd, IDC_NUMBER), tmp, _countof(tmp));
+
+ tmp[_countof(tmp) - 2] = 0;
+ lstrcat(tmp, text);
+
+ SetWindowText(GetDlgItem(hwnd, IDC_NUMBER), tmp);
+
+ call->SendDTMF(text[0]);
+ }
+ }
+ break;
+
+ case IDC_CALLS:
+ if (HIWORD(wParam) == LBN_SELCHANGE) {
+ HWND list = GetDlgItem(hwnd, IDC_CALLS);
+
+ int pos = SendMessage(list, LB_GETCURSEL, 0, 0);
+ if (pos == LB_ERR)
+ break;
+
+ POINT p;
+ GetCursorPos(&p);
+ ScreenToClient(list, &p);
+
+ int ret = SendMessage(list, LB_ITEMFROMPOINT, 0, MAKELONG(p.x, p.y));
+ if (HIWORD(ret))
+ break;
+ if (pos != LOWORD(ret))
+ break;
+
+ SendMessage(list, LB_GETITEMRECT, pos, (LPARAM)&rc);
+ int x = rc.right - p.x;
+
+ int action;
+ if (x >= H_SPACE && x <= ICON_SIZE + H_SPACE)
+ action = 2;
+ else if (x >= ICON_SIZE + 2 * H_SPACE && x <= 2 * (ICON_SIZE + H_SPACE))
+ action = 1;
+ else
+ break;
+
+ VoiceCall *call = (VoiceCall *)SendMessage(list, LB_GETITEMDATA, pos, 0);
+ switch (call->state) {
+ case VOICE_STATE_TALKING:
+ if (action == 1)
+ call->Hold();
+ else
+ call->Drop();
+ break;
+
+ case VOICE_STATE_RINGING:
+ case VOICE_STATE_ON_HOLD:
+ if (action == 1)
+ Answer(call);
+ else
+ call->Drop();
+ break;
+
+ case VOICE_STATE_CALLING:
+ if (action == 2)
+ call->Drop();
+ break;
+ }
+ }
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ HWND list = GetDlgItem(hwnd, IDC_CALLS);
+ if ((HANDLE)wParam != list)
+ break;
+
+ POINT p;
+ p.x = LOWORD(lParam);
+ p.y = HIWORD(lParam);
+ ScreenToClient(list, &p);
+
+ int pos = SendMessage(list, LB_ITEMFROMPOINT, 0, MAKELONG(p.x, p.y));
+ if (HIWORD(pos))
+ break;
+ pos = LOWORD(pos);
+
+ if (pos >= calls.getCount())
+ break;
+
+ if (IsFinalState(calls[pos].state))
+ break;
+
+ // Just to get things strait
+ SendMessage(list, LB_SETCURSEL, pos, 0);
+
+ HMENU menu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_MENUS));
+ HMENU submenu = GetSubMenu(menu, 0);
+ TranslateMenu(submenu);
+
+ switch (calls[pos].state) {
+ case VOICE_STATE_CALLING:
+ DeleteMenu(menu, ID_FRAMEPOPUP_ANSWERCALL, MF_BYCOMMAND);
+ DeleteMenu(menu, ID_FRAMEPOPUP_HOLDCALL, MF_BYCOMMAND);
+ break;
+
+ case VOICE_STATE_TALKING:
+ DeleteMenu(menu, ID_FRAMEPOPUP_ANSWERCALL, MF_BYCOMMAND);
+ if (!calls[pos].module->CanHold())
+ DeleteMenu(menu, ID_FRAMEPOPUP_HOLDCALL, MF_BYCOMMAND);
+ break;
+ }
+
+ p.x = LOWORD(lParam);
+ p.y = HIWORD(lParam);
+ int ret = TrackPopupMenu(submenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, p.x, p.y, 0, hwnd, NULL);
+ DestroyMenu(menu);
+
+ switch (ret) {
+ case ID_FRAMEPOPUP_DROPCALL:
+ calls[pos].Drop();
+ break;
+
+ case ID_FRAMEPOPUP_ANSWERCALL:
+ Answer(&calls[pos]);
+ break;
+
+ case ID_FRAMEPOPUP_HOLDCALL:
+ calls[pos].Hold();
+ break;
+ }
+ break;
+ }
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT mis = (LPMEASUREITEMSTRUCT)lParam;
+ if (mis->CtlID != IDC_CALLS)
+ break;
+
+ mis->itemHeight = GetMaxLineHeight();
+ }
+ return TRUE;
+
+ case WM_CTLCOLORLISTBOX:
+ return (LRESULT)bk_brush;
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
+ if (dis->CtlID != IDC_CALLS || dis->itemID == -1)
+ break;
+
+ VoiceCall *call = (VoiceCall *)dis->itemData;
+ if (call == NULL)
+ break;
+
+ rc = dis->rcItem;
+
+ FillRect(dis->hDC, &rc, bk_brush);
+
+ rc.left += H_SPACE;
+ rc.right -= H_SPACE;
+ rc.bottom--;
+
+ int old_bk_mode = SetBkMode(dis->hDC, TRANSPARENT);
+
+ // Draw status
+ DrawIconLib(dis->hDC, rc, IDI_TALKING + call->state);
+
+ if (call->secure)
+ DrawIconLib(dis->hDC, rc, IDI_SECURE);
+
+ // Draw voice provider icon
+ rc.left += ICON_SIZE + H_SPACE;
+
+ HICON hIcon = call->module->GetIcon();
+ if (hIcon != NULL) {
+ DrawIconEx(dis->hDC, rc.left, (rc.top + rc.bottom - ICON_SIZE) / 2, hIcon, ICON_SIZE, ICON_SIZE, 0, NULL, DI_NORMAL);
+ call->module->ReleaseIcon(hIcon);
+ }
+
+ // Draw contact
+ rc.left += ICON_SIZE + H_SPACE;
+
+ int numIcons = 0;
+ switch (call->state) {
+ case VOICE_STATE_CALLING:
+ numIcons = 1;
+ break;
+
+ case VOICE_STATE_TALKING:
+ if (call->module->CanHold())
+ numIcons = 2;
+ else
+ numIcons = 1;
+ break;
+
+ case VOICE_STATE_RINGING:
+ case VOICE_STATE_ON_HOLD:
+ numIcons = 2;
+ break;
+ }
+
+ rc.right -= numIcons * (ICON_SIZE + H_SPACE);
+
+ HFONT old_font = (HFONT)SelectObject(dis->hDC, fonts[call->state]);
+ COLORREF old_color = SetTextColor(dis->hDC, font_colors[call->state]);
+
+ DrawText(dis->hDC, call->displayName, -1, &rc, DT_SINGLELINE | DT_NOPREFIX | DT_END_ELLIPSIS | DT_VCENTER);
+
+ SelectObject(dis->hDC, old_font);
+ SetTextColor(dis->hDC, old_color);
+
+ // Draw action icons
+ rc = dis->rcItem;
+ rc.right -= H_SPACE;
+ rc.bottom--;
+
+ switch (call->state) {
+ case VOICE_STATE_CALLING:
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, IDI_ACTION_DROP);
+ break;
+
+ case VOICE_STATE_TALKING:
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, IDI_ACTION_DROP);
+
+ if (call->module->CanHold()) {
+ rc.right -= ICON_SIZE + H_SPACE;
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, IDI_ACTION_HOLD);
+ }
+ break;
+
+ case VOICE_STATE_RINGING:
+ case VOICE_STATE_ON_HOLD:
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, IDI_ACTION_DROP);
+
+ rc.right -= ICON_SIZE + H_SPACE;
+ rc.left = rc.right - ICON_SIZE;
+ DrawIconLib(dis->hDC, rc, IDI_ACTION_ANSWER);
+ break;
+ }
+
+ SetBkMode(dis->hDC, old_bk_mode);
+ return TRUE;
+ }
+
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
diff --git a/plugins/VoiceService/src/frame.h b/plugins/VoiceService/src/frame.h
new file mode 100644
index 0000000000..992ab45612
--- /dev/null
+++ b/plugins/VoiceService/src/frame.h
@@ -0,0 +1,36 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __FRAME_H__
+# define __FRAME_H__
+
+
+void InitFrames();
+void DeInitFrames();
+
+
+extern HWND hwnd_frame;
+
+
+#define WMU_REFRESH (WM_USER + 25)
+#define WMU_RESIZE_FRAME (WM_USER + 26)
+
+
+#endif // __FRAME_H__ \ No newline at end of file
diff --git a/plugins/VoiceService/src/hooks.cpp b/plugins/VoiceService/src/hooks.cpp
new file mode 100644
index 0000000000..a0e9e85999
--- /dev/null
+++ b/plugins/VoiceService/src/hooks.cpp
@@ -0,0 +1,747 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+static IconItem mainIcons[] = {
+ { LPGEN("Main"), "main", IDI_MAIN },
+ { LPGEN("Dialpad"), "dialpad", IDI_DIALPAD },
+ { LPGEN("Secure"), "secure", IDI_SECURE },
+};
+
+static IconItem stateIcons[] = {
+ { LPGEN("Talking"), "talking", IDI_TALKING },
+ { LPGEN("Ringing"), "ringing", IDI_RINGING },
+ { LPGEN("Calling"), "calling", IDI_CALLING },
+ { LPGEN("On Hold"), "onhold", IDI_ON_HOLD },
+ { LPGEN("Ended"), "ended", IDI_ENDED },
+ { LPGEN("Busy"), "busy", IDI_BUSY },
+};
+
+static IconItem actionIcons[] = {
+ { LPGEN("Make Voice Call"), "call", IDI_ACTION_CALL },
+ { LPGEN("Answer Voice Call"), "answer", IDI_ACTION_ANSWER },
+ { LPGEN("Hold Voice Call"), "hold", IDI_ACTION_HOLD },
+ { LPGEN("Drop Voice Call"), "drop", IDI_ACTION_DROP },
+};
+
+SoundDescr g_sounds[NUM_STATES] = {
+ { "voice_started", LPGENW("Started talking") },
+ { "voice_ringing", LPGENW("Ringing") },
+ { "voice_calling", LPGENW("Calling a contact") },
+ { "voice_holded", LPGENW("Put a call on Hold") },
+ { "voice_ended", LPGENW("End of call") },
+ { "voice_busy", LPGENW("Busy signal") },
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static vector<HGENMENU> hCMCalls;
+static HGENMENU hCMCall = NULL;
+static HGENMENU hCMAnswer = NULL;
+static HGENMENU hCMDrop = NULL;
+static HGENMENU hCMHold = NULL;
+
+OBJLIST<VoiceProvider> modules(1, PtrKeySortT);
+OBJLIST<VoiceCall> calls(1, PtrKeySortT);
+
+HFONT fonts[NUM_STATES] = { 0 };
+COLORREF font_colors[NUM_STATES] = { 0 };
+int font_max_height;
+
+COLORREF bkg_color = { 0 };
+HBRUSH bk_brush = NULL;
+
+static INT_PTR CListDblClick(WPARAM wParam, LPARAM lParam);
+
+static INT_PTR Service_CanCall(WPARAM wParam, LPARAM lParam);
+static INT_PTR Service_Call(WPARAM wParam, LPARAM lParam);
+static INT_PTR CMAnswer(WPARAM wParam, LPARAM lParam);
+static INT_PTR CMHold(WPARAM wParam, LPARAM lParam);
+static INT_PTR CMDrop(WPARAM wParam, LPARAM lParam);
+
+static VOID CALLBACK ClearOldVoiceCalls(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+
+class CallingMethod
+{
+public:
+ VoiceProvider *provider;
+ MCONTACT hContact;
+ wchar_t number[128];
+
+ CallingMethod(VoiceProvider *provider, MCONTACT hContact, const wchar_t *number = NULL)
+ : provider(provider), hContact(hContact)
+ {
+ if (number == NULL)
+ this->number[0] = 0;
+ else
+ lstrcpyn(this->number, number, _countof(this->number));
+ }
+
+ void Call()
+ {
+ provider->Call(hContact, number);
+ }
+};
+
+static int sttCompareCallingMethods(const CallingMethod *p1, const CallingMethod *p2)
+{
+ if (p1->hContact != p2->hContact)
+ return (int)p2->hContact - (int)p1->hContact;
+
+ BOOL noNum1 = (IsEmptyW(p1->number) ? 1 : 0);
+ BOOL noNum2 = (IsEmptyW(p2->number) ? 1 : 0);
+ if (noNum1 != noNum2)
+ return noNum2 - noNum1;
+
+ if (!noNum1) {
+ int numDif = lstrcmp(p1->number, p2->number);
+ if (numDif != 0)
+ return numDif;
+ }
+
+ BOOL isProto1 = Proto_IsProtoOnContact(p1->hContact, p1->provider->name);
+ BOOL isProto2 = Proto_IsProtoOnContact(p2->hContact, p2->provider->name);
+ if (isProto1 != isProto2)
+ return isProto2 - isProto1;
+
+ return lstrcmp(p1->provider->description, p2->provider->description);
+}
+
+static void AddMethodsFrom(OBJLIST<CallingMethod> *list, MCONTACT hContact)
+{
+ for (int i = 0; i < modules.getCount(); i++) {
+ VoiceProvider *provider = &modules[i];
+ if (provider->CanCall(hContact))
+ list->insert(new CallingMethod(provider, hContact));
+ }
+}
+
+static void AddMethodsFrom(OBJLIST<CallingMethod> *list, MCONTACT hContact, const wchar_t *number)
+{
+ for (int i = 0; i < modules.getCount(); i++) {
+ VoiceProvider *provider = &modules[i];
+ if (provider->CanCall(number))
+ list->insert(new CallingMethod(provider, hContact, number));
+ }
+}
+
+static void BuildCallingMethodsList(OBJLIST<CallingMethod> *list, MCONTACT hContact)
+{
+ AddMethodsFrom(list, hContact);
+
+ // Fetch contact number
+ char *proto = Proto_GetBaseAccountName(hContact);
+
+ CMStringW protoNumber(db_get_wsm(hContact, proto, "Number"));
+ if (!protoNumber.IsEmpty())
+ AddMethodsFrom(list, hContact, protoNumber);
+
+ for (int i = 0; ; i++) {
+ char tmp[128];
+ mir_snprintf(tmp, _countof(tmp), "MyPhone%d", i);
+
+ CMStringW number(db_get_wsm(hContact, "UserInfo", tmp));
+ if (!number.IsEmpty())
+ AddMethodsFrom(list, hContact, number);
+
+ if (number.IsEmpty() && i >= 4)
+ break;
+ }
+}
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+static MCONTACT ConvertMetacontact(MCONTACT hContact)
+{
+ MCONTACT hTmp = db_mc_getMostOnline(hContact);
+ if (hTmp != NULL)
+ return hTmp;
+
+ return hContact;
+}
+
+static void AddAccount(PROTOACCOUNT *acc)
+{
+ if (!acc->IsEnabled())
+ return;
+ if (IsEmptyA(acc->szModuleName))
+ return;
+ if (!ProtoServiceExists(acc->szModuleName, PS_VOICE_CAPS))
+ return;
+
+ int flags = CallProtoService(acc->szModuleName, PS_VOICE_CAPS, 0, 0);
+
+ if ((flags & VOICE_CAPS_VOICE) == 0)
+ return;
+
+ VOICE_MODULE vm = { 0 };
+ vm.cbSize = sizeof(vm);
+ vm.name = acc->szModuleName;
+ vm.description = acc->tszAccountName;
+ vm.flags = flags;
+ VoiceRegister((WPARAM)&vm, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int AccListChanged(WPARAM wParam, LPARAM lParam)
+{
+ PROTOACCOUNT *acc = (PROTOACCOUNT *)lParam;
+ if (acc == NULL)
+ return 0;
+
+ VoiceProvider *provider = FindModule(acc->szModuleName);
+
+ switch (wParam) {
+ case PRAC_ADDED:
+ AddAccount(acc);
+ break;
+
+ case PRAC_CHANGED:
+ if (provider != NULL)
+ lstrcpyn(provider->description, acc->tszAccountName, _countof(provider->description));
+ break;
+
+ case PRAC_CHECKED:
+ {
+ BOOL enabled = acc->IsEnabled();
+
+ if (!enabled) {
+ if (provider != NULL)
+ VoiceUnregister((WPARAM)acc->szModuleName, 0);
+ }
+ else {
+ if (provider == NULL)
+ AddAccount(acc);
+ }
+ break;
+ }
+ case PRAC_REMOVED:
+ if (provider != NULL)
+ VoiceUnregister((WPARAM)acc->szModuleName, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR Service_CallItem(WPARAM wParam, LPARAM, LPARAM param)
+{
+ MCONTACT hContact = (MCONTACT)wParam;
+ int index = (int)param;
+
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ OBJLIST<CallingMethod> methods(10, sttCompareCallingMethods);
+ BuildCallingMethodsList(&methods, hContact);
+
+ if (index < 0 || index >= methods.getCount())
+ return -2;
+
+ methods[index].Call();
+ return 0;
+}
+
+static int PreBuildContactMenu(WPARAM wParam, LPARAM)
+{
+ Menu_ShowItem(hCMCall, false);
+ Menu_ShowItem(hCMAnswer, false);
+ Menu_ShowItem(hCMHold, false);
+ Menu_ShowItem(hCMDrop, false);
+ for (unsigned int i = 0; i < hCMCalls.size(); ++i)
+ Menu_ShowItem(hCMCalls[i], false);
+
+ MCONTACT hContact = (MCONTACT)wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ // There is a current call already?
+ VoiceCall *call = FindVoiceCall(hContact);
+ if (call == nullptr) {
+ OBJLIST<CallingMethod> methods(10, sttCompareCallingMethods);
+ BuildCallingMethodsList(&methods, hContact);
+
+ if (methods.getCount() == 1) {
+ CallingMethod *method = &methods[0];
+
+ wchar_t name[128];
+ if (!IsEmptyW(method->number))
+ mir_snwprintf(name, _countof(name), TranslateT("Call %s with %s"),
+ method->number, method->provider->description);
+ else
+ mir_snwprintf(name, _countof(name), TranslateT("Call with %s"),
+ method->provider->description);
+
+ Menu_ModifyItem(hCMCall, name);
+ Menu_ShowItem(hCMCall, true);
+ }
+ else if (methods.getCount() > 1) {
+ Menu_ModifyItem(hCMCall, TranslateT("Call"));
+ Menu_ShowItem(hCMCall, true);
+
+ for (int i = 0; i < methods.getCount(); ++i) {
+ CallingMethod *method = &methods[i];
+
+ HICON hIcon = method->provider->GetIcon();
+
+ wchar_t name[128];
+ if (!IsEmptyW(method->number))
+ mir_snwprintf(name, _countof(name), TranslateT("%s with %s"),
+ method->number, method->provider->description);
+ else
+ mir_snwprintf(name, _countof(name), TranslateT("with %s"),
+ method->provider->description);
+
+ char service[128];
+ mir_snprintf(service, _countof(service), "VoiceService/ContactMenu/Call_%d", i);
+
+ if (i == hCMCalls.size()) {
+ CreateServiceFunctionParam(service, Service_CallItem, i);
+
+ CMenuItem mi(&g_plugin);
+ mi.position = i;
+ mi.flags = CMIF_UNICODE;
+ mi.name.w = name;
+ mi.hIcon = hIcon;
+ mi.pszService = service;
+ mi.root = hCMCall;
+ hCMCalls.push_back(Menu_AddContactMenuItem(&mi));
+ }
+ else Menu_ModifyItem(hCMCalls[i], name, hIcon);
+
+ method->provider->ReleaseIcon(hIcon);
+ }
+ }
+ }
+ else {
+ switch (call->state) {
+ case VOICE_STATE_CALLING:
+ Menu_ShowItem(hCMDrop, true);
+ break;
+
+ case VOICE_STATE_TALKING:
+ if (call->module->CanHold())
+ Menu_ShowItem(hCMHold, true);
+ Menu_ShowItem(hCMDrop, true);
+ break;
+
+ case VOICE_STATE_RINGING:
+ case VOICE_STATE_ON_HOLD:
+ Menu_ShowItem(hCMAnswer, true);
+ Menu_ShowItem(hCMDrop, true);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int IconsChanged(WPARAM, LPARAM)
+{
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+
+ return 0;
+}
+
+static int ReloadColor(WPARAM, LPARAM)
+{
+ ColourIDW ci = { 0 };
+ lstrcpyn(ci.group, TranslateT("Voice Calls"), _countof(ci.group));
+ lstrcpyn(ci.name, TranslateT("Background"), _countof(ci.name));
+
+ bkg_color = Colour_GetW(ci);
+
+ if (bk_brush != NULL)
+ DeleteObject(bk_brush);
+ bk_brush = CreateSolidBrush(bkg_color);
+
+ if (hwnd_frame != NULL)
+ InvalidateRect(hwnd_frame, NULL, TRUE);
+
+ return 0;
+}
+
+VoiceProvider *FindModule(const char *szModule)
+{
+ for (int i = 0; i < modules.getCount(); i++)
+ if (strcmp(modules[i].name, szModule) == 0)
+ return &modules[i];
+
+ return NULL;
+}
+
+static bool IsCall(VoiceCall *call, const char *szModule, const char *id)
+{
+ return strcmp(call->module->name, szModule) == 0
+ && call->id != NULL && strcmp(call->id, id) == 0;
+}
+
+VoiceCall *FindVoiceCall(const char *szModule, const char *id, bool add)
+{
+ for (int i = 0; i < calls.getCount(); i++) {
+ if (IsCall(&calls[i], szModule, id)) {
+ return &calls[i];
+ }
+ }
+
+ if (add) {
+ VoiceProvider *module = FindModule(szModule);
+ if (module == NULL)
+ return NULL;
+
+ VoiceCall *tmp = new VoiceCall(module, id);
+ calls.insert(tmp);
+ return tmp;
+ }
+
+ return NULL;
+}
+
+VoiceCall* FindVoiceCall(MCONTACT hContact)
+{
+ for (int i = 0; i < calls.getCount(); i++) {
+ if (calls[i].state != VOICE_STATE_ENDED && calls[i].hContact == hContact) {
+ return &calls[i];
+ }
+ }
+
+ return NULL;
+}
+
+static VOID CALLBACK ClearOldVoiceCalls(HWND, UINT, UINT_PTR, DWORD)
+{
+ DWORD now = GetTickCount();
+ BOOL refresh = FALSE;
+ for (int i = calls.getCount() - 1; i >= 0; --i) {
+ VoiceCall *call = &calls[i];
+
+ if (call->state == VOICE_STATE_ENDED && call->end_time + TIME_TO_SHOW_ENDED_CALL < now) {
+ calls.remove(i);
+ refresh = TRUE;
+ }
+ }
+
+ if (refresh && hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+}
+
+bool CanCall(MCONTACT hContact, BOOL now)
+{
+ for (int i = 0; i < modules.getCount(); i++) {
+ if (modules[i].CanCall(hContact, now))
+ return true;
+ }
+
+ return false;
+}
+
+bool CanCall(const wchar_t *number)
+{
+ for (int i = 0; i < modules.getCount(); i++) {
+ if (modules[i].CanCall(number))
+ return true;
+ }
+
+ return false;
+}
+
+bool CanCallNumber()
+{
+ for (int i = 0; i < modules.getCount(); i++) {
+ if (modules[i].flags & VOICE_CAPS_CALL_STRING)
+ return true;
+ }
+
+ return false;
+}
+
+bool IsFinalState(int state)
+{
+ return state == VOICE_STATE_ENDED || state == VOICE_STATE_BUSY;
+}
+
+VoiceCall *GetTalkingCall()
+{
+ for (int i = 0; i < calls.getCount(); ++i) {
+ VoiceCall *call = &calls[i];
+
+ if (call->state == VOICE_STATE_TALKING)
+ return call;
+ }
+
+ return NULL;
+}
+
+void HoldOtherCalls(VoiceCall *call)
+{
+ for (int i = 0; i < calls.getCount(); ++i) {
+ VoiceCall *other = &calls[i];
+
+ if (other == call || other->state != VOICE_STATE_TALKING)
+ continue;
+
+ if (other->CanHold())
+ other->Hold();
+ else
+ other->Drop();
+ }
+}
+
+void Answer(VoiceCall *call)
+{
+ if (!call->CanAnswer())
+ return;
+
+ HoldOtherCalls(call);
+
+ // Now annswer it
+ call->Answer();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int ReloadFont(WPARAM, LPARAM)
+{
+ FontID fi = { 0 };
+ strncpy_s(fi.group, "Voice Calls", _TRUNCATE);
+
+ font_max_height = 0;
+ for (int i = 0; i < NUM_STATES; i++) {
+ if (fonts[i] != 0) DeleteObject(fonts[i]);
+
+ strncpy_s(fi.name, stateIcons[i].szName, _TRUNCATE);
+
+ LOGFONTA log_font = { 0 };
+ font_colors[i] = Font_Get(fi, &log_font);
+ fonts[i] = CreateFontIndirectA(&log_font);
+
+ font_max_height = max(font_max_height, log_font.lfHeight);
+ }
+
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+
+ return 0;
+}
+
+static INT_PTR Service_CanCall(WPARAM wParam, LPARAM)
+{
+ MCONTACT hContact = (MCONTACT)wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+ return CanCall(hContact) ? 1 : 0;
+}
+
+static INT_PTR Service_Call(WPARAM wParam, LPARAM)
+{
+ MCONTACT hContact = (MCONTACT)wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ OBJLIST<CallingMethod> methods(10, sttCompareCallingMethods);
+ BuildCallingMethodsList(&methods, hContact);
+
+ if (methods.getCount() < 1)
+ return -2;
+
+ CallingMethod *method = &methods[0];
+ if (!IsEmptyW(method->number))
+ return -2;
+
+ method->Call();
+ return 0;
+}
+
+static INT_PTR CMAnswer(WPARAM wParam, LPARAM)
+{
+ MCONTACT hContact = (MCONTACT)wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ VoiceCall *call = FindVoiceCall(hContact);
+ if (call != NULL)
+ Answer(call);
+
+ return 0;
+}
+
+static INT_PTR CMHold(WPARAM wParam, LPARAM)
+{
+ MCONTACT hContact = (MCONTACT)wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ VoiceCall *call = FindVoiceCall(hContact);
+ if (call != NULL)
+ call->Hold();
+
+ return 0;
+}
+
+static INT_PTR CMDrop(WPARAM wParam, LPARAM)
+{
+ MCONTACT hContact = (MCONTACT)wParam;
+ if (hContact == NULL)
+ return -1;
+
+ hContact = ConvertMetacontact(hContact);
+
+ VoiceCall *call = FindVoiceCall(hContact);
+ if (call != NULL)
+ call->Drop();
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Called when all the modules are loaded
+
+int ModulesLoaded(WPARAM, LPARAM)
+{
+ // add our modules to the KnownModules list
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM)MODULE_NAME, 0);
+
+ // Init icons
+ g_plugin.registerIcon(LPGEN("Voice Calls"), mainIcons, "vc");
+ g_plugin.registerIcon(LPGEN("Voice Calls"), stateIcons, "vc");
+ g_plugin.registerIcon(LPGEN("Voice Calls"), actionIcons, "vca");
+
+ HookEvent(ME_SKIN_ICONSCHANGED, IconsChanged);
+
+ // Init fonts
+ {
+ FontID fi = {};
+ strncpy_s(fi.group, LPGEN("Voice Calls"), _TRUNCATE);
+ strncpy_s(fi.dbSettingsGroup, MODULE_NAME, _TRUNCATE);
+
+ for (int i = 0; i < _countof(stateIcons); i++) {
+ fi.order = i;
+ strncpy_s(fi.name, stateIcons[i].szName, _TRUNCATE);
+ g_plugin.addFont(&fi);
+ }
+
+ ReloadFont(0, 0);
+ HookEvent(ME_FONT_RELOAD, ReloadFont);
+ }
+
+ // Init bkg color
+ {
+ ColourID ci = { 0 };
+ strncpy_s(ci.group, LPGEN("Voice Calls"), _TRUNCATE);
+ strncpy_s(ci.name, LPGEN("Background"), _TRUNCATE);
+ strncpy_s(ci.dbSettingsGroup, MODULE_NAME, _TRUNCATE);
+ strncpy_s(ci.setting, "BkgColor", _TRUNCATE);
+ ci.defcolour = GetSysColor(COLOR_BTNFACE);
+ g_plugin.addColor(&ci);
+
+ ReloadColor(0, 0);
+ HookEvent(ME_COLOUR_RELOAD, ReloadColor);
+ }
+
+ InitOptions();
+ InitFrames();
+
+ // Add menu items
+ CMenuItem mi(&g_plugin);
+ mi.position = -2000020000;
+
+ mi.name.a = actionIcons[0].szDescr;
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_ACTION_CALL);
+ mi.pszService = MS_VOICESERVICE_CM_CALL;
+ hCMCall = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, Service_Call);
+
+ mi.position++;
+ mi.name.a = actionIcons[1].szDescr;
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_ACTION_ANSWER);
+ mi.pszService = MS_VOICESERVICE_CM_ANSWER;
+ hCMAnswer = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, CMAnswer);
+
+ mi.position++;
+ mi.position++;
+ mi.name.a = actionIcons[2].szDescr;
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_ACTION_HOLD);
+ mi.pszService = MS_VOICESERVICE_CM_HOLD;
+ hCMHold = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, CMHold);
+
+ mi.position++;
+ mi.name.a = actionIcons[3].szDescr;
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_ACTION_DROP);
+ mi.pszService = MS_VOICESERVICE_CM_DROP;
+ hCMDrop = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, CMDrop);
+
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu);
+
+ // Util services
+ CreateServiceFunction(MS_VOICESERVICE_CALL, Service_Call);
+ CreateServiceFunction(MS_VOICESERVICE_CAN_CALL, Service_CanCall);
+
+ // Sounds
+ for (auto &it : g_sounds)
+ g_plugin.addSound(it.szName, LPGENW("Voice Calls"), it.wszDescr);
+ g_plugin.addSound("voice_dialpad", LPGENW("Voice Calls"), LPGENW("Dialpad press"));
+
+ SetTimer(NULL, 0, 1000, ClearOldVoiceCalls);
+
+ // Accounts
+ for (auto *pa : Accounts())
+ AddAccount(pa);
+
+ HookEvent(ME_PROTO_ACCLISTCHANGED, AccListChanged);
+ return 0;
+}
+
+int PreShutdown(WPARAM, LPARAM)
+{
+ DeInitFrames();
+ DeInitOptions();
+ return 0;
+}
+
+int ProtoAck(WPARAM, LPARAM lParam)
+{
+ ACKDATA *ack = (ACKDATA *)lParam;
+ if (ack->type == ACKTYPE_STATUS)
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+
+ return 0;
+}
diff --git a/plugins/VoiceService/src/main.cpp b/plugins/VoiceService/src/main.cpp
new file mode 100644
index 0000000000..ce67916c9f
--- /dev/null
+++ b/plugins/VoiceService/src/main.cpp
@@ -0,0 +1,69 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx =
+{
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ { 0x1bfc449d, 0x8f6f, 0x4080, { 0x8f, 0x35, 0xf9, 0x40, 0xb3, 0xde, 0x12, 0x84 } } // {1BFC449D-8F6F-4080-8F35-F940B3DE1284}
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULE_NAME, pluginInfoEx)
+{
+};
+
+CMPlugin g_plugin;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CreateServices(void);
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam);
+int PreShutdown(WPARAM wParam, LPARAM lParam);
+int ProtoAck(WPARAM wParam, LPARAM lParam);
+
+int CMPlugin::Load()
+{
+ CreateServices();
+
+ // Hooks
+ HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown);
+ HookEvent(ME_PROTO_ACK, ProtoAck);
+ return 0;
+}
+
+int CMPlugin::Unload()
+{
+ if (bk_brush != NULL)
+ DeleteObject(bk_brush);
+
+ return 0;
+}
diff --git a/plugins/VoiceService/src/options.cpp b/plugins/VoiceService/src/options.cpp
new file mode 100644
index 0000000000..ac5560e863
--- /dev/null
+++ b/plugins/VoiceService/src/options.cpp
@@ -0,0 +1,456 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "stdafx.h"
+#include "options.h"
+
+#include "../utils/mir_options.h"
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+HANDLE hOptHook = NULL;
+
+Options opts;
+
+static OptPageControl optionsControls[] =
+{
+ { &opts.resize_frame, CONTROL_CHECKBOX, IDC_FRAME_AUTOSIZE, "FrameAutoSize", TRUE }
+};
+
+static OptPageControl devicesControls[] = {
+ { NULL, CONTROL_CHECKBOX, IDC_ECHO, "EchoCancelation", TRUE },
+ { NULL, CONTROL_CHECKBOX, IDC_MIC_BOOST, "MicBoost", TRUE },
+};
+
+static OptPageControl popupsControls[] = {
+ { &opts.popup_enable, CONTROL_CHECKBOX, IDC_POPUPS, "PopupsEnable", FALSE },
+ { &opts.popup_bkg_color, CONTROL_COLOR, IDC_BGCOLOR, "PopupsBgColor", RGB(255,255,255) },
+ { &opts.popup_text_color, CONTROL_COLOR, IDC_TEXTCOLOR, "PopupsTextColor", RGB(0,0,0) },
+ { &opts.popup_use_win_colors, CONTROL_CHECKBOX, IDC_WINCOLORS, "PopupsWinColors", FALSE },
+ { &opts.popup_use_default_colors, CONTROL_CHECKBOX, IDC_DEFAULTCOLORS, "PopupsDefaultColors", FALSE },
+ { &opts.popup_delay_type, CONTROL_RADIO, IDC_DELAYFROMPU, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_DEFAULT },
+ { NULL, CONTROL_RADIO, IDC_DELAYCUSTOM, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_CUSTOM },
+ { NULL, CONTROL_RADIO, IDC_DELAYPERMANENT, "PopupsDelayType", POPUP_DELAY_DEFAULT, POPUP_DELAY_PERMANENT },
+ { &opts.popup_timeout, CONTROL_SPIN, IDC_DELAY, "PopupsTimeout", 10, IDC_DELAY_SPIN, (WORD)1, (WORD)255 },
+ { &opts.popup_right_click_action, CONTROL_COMBO, IDC_RIGHT_ACTION, "PopupsRightClick", POPUP_ACTION_CLOSEPOPUP },
+ { &opts.popup_left_click_action, CONTROL_COMBO, IDC_LEFT_ACTION, "PopupsLeftClick", POPUP_ACTION_CLOSEPOPUP }
+};
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK OptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return SaveOptsDlgProc(optionsControls, _countof(optionsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+static void PopupsEnableDisableCtrls(HWND hwndDlg)
+{
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_POPUPS);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOURS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYFROMPU), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYCUSTOM), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAYPERMANENT), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ACTIONS_G), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RIGHT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEFT_ACTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PREV), enabled);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BGCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOR), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DEFAULTCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_WINCOLORS), enabled &&
+ !IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY), enabled &&
+ IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM));
+}
+
+static LRESULT CALLBACK PopupsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LPARAM)TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_RIGHT_ACTION, CB_ADDSTRING, 0, (LPARAM)TranslateT("Close popup"));
+
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LPARAM)TranslateT("Do nothing"));
+ SendDlgItemMessage(hwndDlg, IDC_LEFT_ACTION, CB_ADDSTRING, 0, (LPARAM)TranslateT("Close popup"));
+
+ // Needs to be called here in this case
+ BOOL ret = SaveOptsDlgProc(popupsControls, _countof(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ return ret;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam)) {
+ case IDC_POPUPS:
+ case IDC_WINCOLORS:
+ case IDC_DEFAULTCOLORS:
+ case IDC_DELAYFROMPU:
+ case IDC_DELAYPERMANENT:
+ case IDC_DELAYCUSTOM:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ PopupsEnableDisableCtrls(hwndDlg);
+
+ break;
+ }
+ case IDC_PREV:
+ {
+ Options op = opts;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_DELAYFROMPU))
+ op.popup_delay_type = POPUP_DELAY_DEFAULT;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYCUSTOM))
+ op.popup_delay_type = POPUP_DELAY_CUSTOM;
+ else if (IsDlgButtonChecked(hwndDlg, IDC_DELAYPERMANENT))
+ op.popup_delay_type = POPUP_DELAY_PERMANENT;
+
+ op.popup_timeout = GetDlgItemInt(hwndDlg, IDC_DELAY, NULL, FALSE);
+ op.popup_bkg_color = SendDlgItemMessage(hwndDlg, IDC_BGCOLOR, CPM_GETCOLOUR, 0, 0);
+ op.popup_text_color = SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOR, CPM_GETCOLOUR, 0, 0);
+ op.popup_use_win_colors = IsDlgButtonChecked(hwndDlg, IDC_WINCOLORS) != 0;
+ op.popup_use_default_colors = IsDlgButtonChecked(hwndDlg, IDC_DEFAULTCOLORS) != 0;
+
+ ShowTestPopup(TranslateT("Test Contact"), TranslateT("Test description"), &op);
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return SaveOptsDlgProc(popupsControls, _countof(popupsControls), MODULE_NAME, hwndDlg, msg, wParam, lParam);
+}
+
+static void SetAllContactIcons(HWND hwndList)
+{
+ for (auto &cc : Contacts()) {
+ HANDLE hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)cc, 0);
+ if (hItem) {
+ // Some Module can handle it?
+ if (!CanCall(cc, FALSE)) {
+ SendMessage(hwndList, CLM_DELETEITEM, (WPARAM)hItem, 0);
+ }
+ else {
+ SendMessage(hwndList, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(0, g_plugin.getWord(cc, "AutoAccept", AUTO_NOTHING) == AUTO_ACCEPT ? 1 : 0));
+ SendMessage(hwndList, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(1, g_plugin.getWord(cc, "AutoAccept", AUTO_NOTHING) == AUTO_DROP ? 2 : 0));
+ }
+ }
+ }
+}
+
+
+static void SetListGroupIcons(HWND hwndList, HANDLE hFirstItem, HANDLE hParentItem, int *groupChildCount)
+{
+ int typeOfFirst;
+ int iconOn[2] = { 1,1 };
+ int childCount[2] = { 0,0 }, i;
+ int iImage;
+ HANDLE hItem, hChildItem;
+
+ typeOfFirst = SendMessage(hwndList, CLM_GETITEMTYPE, (WPARAM)hFirstItem, 0);
+ //check groups
+ if (typeOfFirst == CLCIT_GROUP) hItem = hFirstItem;
+ else hItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_NEXTGROUP, (LPARAM)hFirstItem);
+ while (hItem) {
+ hChildItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_CHILD, (LPARAM)hItem);
+ if (hChildItem) SetListGroupIcons(hwndList, hChildItem, hItem, childCount);
+ for (i = 0; i < _countof(iconOn); i++)
+ if (iconOn[i] && SendMessage(hwndList, CLM_GETEXTRAIMAGE, (WPARAM)hItem, i) == 0) iconOn[i] = 0;
+ hItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_NEXTGROUP, (LPARAM)hItem);
+ }
+ //check contacts
+ if (typeOfFirst == CLCIT_CONTACT) hItem = hFirstItem;
+ else hItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_NEXTCONTACT, (LPARAM)hFirstItem);
+ while (hItem) {
+ for (i = 0; i < _countof(iconOn); i++) {
+ iImage = SendMessage(hwndList, CLM_GETEXTRAIMAGE, (WPARAM)hItem, i);
+ if (iconOn[i] && iImage == 0) iconOn[i] = 0;
+ if (iImage != 0xFF) childCount[i]++;
+ }
+ hItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_NEXTCONTACT, (LPARAM)hItem);
+ }
+ //set icons
+ if (hParentItem != NULL) {
+ for (i = 0; i < _countof(iconOn); i++) {
+ SendMessage(hwndList, CLM_SETEXTRAIMAGE, (WPARAM)hParentItem, MAKELPARAM(i, childCount[i] ? (iconOn[i] ? i + 1 : 0) : 0xFF));
+ if (groupChildCount) groupChildCount[i] += childCount[i];
+ }
+ }
+}
+
+
+static void SetAllChildIcons(HWND hwndList, HANDLE hFirstItem, int iColumn, int iImage)
+{
+ int typeOfFirst, iOldIcon;
+ HANDLE hItem, hChildItem;
+
+ typeOfFirst = SendMessage(hwndList, CLM_GETITEMTYPE, (WPARAM)hFirstItem, 0);
+ //check groups
+ if (typeOfFirst == CLCIT_GROUP) hItem = hFirstItem;
+ else hItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_NEXTGROUP, (LPARAM)hFirstItem);
+ while (hItem) {
+ hChildItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_CHILD, (LPARAM)hItem);
+ if (hChildItem) SetAllChildIcons(hwndList, hChildItem, iColumn, iImage);
+ hItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_NEXTGROUP, (LPARAM)hItem);
+ }
+ //check contacts
+ if (typeOfFirst == CLCIT_CONTACT) hItem = hFirstItem;
+ else hItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_NEXTCONTACT, (LPARAM)hFirstItem);
+ while (hItem) {
+ iOldIcon = SendMessage(hwndList, CLM_GETEXTRAIMAGE, (WPARAM)hItem, iColumn);
+ if (iOldIcon != 0xFF && iOldIcon != iImage) SendMessage(hwndList, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage));
+ hItem = (HANDLE)SendMessage(hwndList, CLM_GETNEXTITEM, CLGN_NEXTCONTACT, (LPARAM)hItem);
+ }
+}
+
+
+static void ResetListOptions(HWND hwndList)
+{
+ SendMessage(hwndList, CLM_SETBKBITMAP, 0, 0);
+ SendMessage(hwndList, CLM_SETBKCOLOR, GetSysColor(COLOR_WINDOW), 0);
+ SendMessage(hwndList, CLM_SETGREYOUTFLAGS, 0, 0);
+ SendMessage(hwndList, CLM_SETLEFTMARGIN, 2, 0);
+ SendMessage(hwndList, CLM_SETINDENT, 10, 0);
+
+ for (int i = 0; i <= FONTID_MAX; i++)
+ SendMessage(hwndList, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT));
+
+ // SetWindowLong(hwndList,GWL_STYLE,GetWindowLong(hwndList,GWL_STYLE)|CLS_SHOWHIDDEN|CLS_NOHIDEOFFLINE|CLS_GREYALTERNATE);
+}
+
+
+int ImageList_AddIcon_NotShared(HIMAGELIST hIml, HINSTANCE hInstance, LPCTSTR szResource)
+{
+ HICON hIcon = LoadIcon(hInstance, szResource);
+ int res = ImageList_AddIcon(hIml, hIcon);
+ DestroyIcon(hIcon);
+ return res;
+}
+
+
+int ImageList_AddIcon_NotShared(HIMAGELIST hIml, int iconId)
+{
+ HICON hIcon = g_plugin.getIcon(iconId);
+ int res = ImageList_AddIcon(hIml, hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+ return res;
+}
+
+
+static LRESULT CALLBACK AutoDlgProc(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
+{
+ static HICON hAnswerIcon, hDropIcon;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+
+ HIMAGELIST hIml = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 3, 3);
+
+ ImageList_AddIcon_NotShared(hIml, GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_SMALLDOT));
+ ImageList_AddIcon_NotShared(hIml, IDI_ACTION_ANSWER);
+ ImageList_AddIcon_NotShared(hIml, IDI_ACTION_DROP);
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hIml);
+
+ hAnswerIcon = ImageList_GetIcon(hIml, 1, ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg, IDC_ANSWER, STM_SETICON, (WPARAM)hAnswerIcon, 0);
+
+ hDropIcon = ImageList_GetIcon(hIml, 2, ILD_NORMAL);
+ SendDlgItemMessage(hwndDlg, IDC_DROP, STM_SETICON, (WPARAM)hDropIcon, 0);
+
+ ResetListOptions(GetDlgItem(hwndDlg, IDC_LIST));
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRACOLUMNS, 2, 0);
+
+ SetAllContactIcons(GetDlgItem(hwndDlg, IDC_LIST));
+ SetListGroupIcons(GetDlgItem(hwndDlg, IDC_LIST), (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_ROOT, 0), NULL, NULL);
+ return TRUE;
+ }
+ case WM_SETFOCUS:
+ SetFocus(GetDlgItem(hwndDlg, IDC_LIST));
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_LIST:
+ switch (((LPNMHDR)lParam)->code) {
+ case CLN_NEWCONTACT:
+ case CLN_LISTREBUILT:
+ SetAllContactIcons(GetDlgItem(hwndDlg, IDC_LIST));
+ //fall through
+
+ case CLN_CONTACTMOVED:
+ SetListGroupIcons(GetDlgItem(hwndDlg, IDC_LIST), (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_ROOT, 0), NULL, NULL);
+ break;
+
+ case CLN_OPTIONSCHANGED:
+ ResetListOptions(GetDlgItem(hwndDlg, IDC_LIST));
+ break;
+
+ case NM_CLICK:
+ {
+ HANDLE hItem;
+ NMCLISTCONTROL *nm = (NMCLISTCONTROL *)lParam;
+ DWORD hitFlags;
+ int iImage;
+ int itemType;
+
+ // Make sure we have an extra column
+ if (nm->iColumn == -1)
+ break;
+
+ // Find clicked item
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x, nm->pt.y));
+ // Nothing was clicked
+ if (hItem == NULL) break;
+ // It was not a visbility icon
+ if (!(hitFlags & CLCHT_ONITEMEXTRA)) break;
+
+ // Get image in clicked column (0=none, 1=visible, 2=invisible)
+ iImage = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, 0));
+ if (iImage == 0)
+ iImage = nm->iColumn + 1;
+ else
+ if (iImage == 1 || iImage == 2)
+ iImage = 0;
+
+ // Get item type (contact, group, etc...)
+ itemType = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETITEMTYPE, (WPARAM)hItem, 0);
+
+ // Update list, making sure that the options are mutually exclusive
+ if (itemType == CLCIT_CONTACT) { // A contact
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn, iImage));
+ if (iImage && SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn ? 0 : 1, 0)) != 0xFF)
+ SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(nm->iColumn ? 0 : 1, 0));
+ }
+ else if (itemType == CLCIT_GROUP) { // A group
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_CHILD, (LPARAM)hItem);
+ if (hItem) {
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn, iImage);
+ if (iImage)
+ SetAllChildIcons(GetDlgItem(hwndDlg, IDC_LIST), hItem, nm->iColumn ? 0 : 1, 0);
+ }
+ }
+ // Update the all/none icons
+ SetListGroupIcons(GetDlgItem(hwndDlg, IDC_LIST), (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETNEXTITEM, CLGN_ROOT, 0), NULL, NULL);
+
+ // Activate Apply button
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ for (auto &cc : Contacts()) {
+ HANDLE hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_FINDCONTACT, cc, 0);
+ if (hItem) {
+ int set = 0;
+ for (int i = 0; i < 2; i++) {
+ int iImage = SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(i, 0));
+ if (iImage == i + 1) {
+ g_plugin.setWord(cc, "AutoAccept", iImage == 1 ? AUTO_ACCEPT : AUTO_DROP);
+ set = 1;
+ break;
+ }
+ }
+ if (!set)
+ g_plugin.setWord(cc, "AutoAccept", AUTO_NOTHING);
+ }
+ }
+ return TRUE;
+ }
+ break;
+ }
+ break;
+
+case WM_DESTROY:
+ DestroyIcon(hAnswerIcon);
+ DestroyIcon(hDropIcon);
+ ImageList_Destroy((HIMAGELIST)SendDlgItemMessage(hwndDlg, IDC_LIST, CLM_GETEXTRAIMAGELIST, 0, 0));
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int InitOptionsCallback(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp;
+ ZeroMemory(&odp, sizeof(odp));
+ odp.pPlugin = &g_plugin;
+ odp.flags = ODPF_BOLDGROUPS;
+
+ odp.szGroup.a = LPGEN("Popups");
+ odp.szTitle.a = LPGEN("Voice Calls");
+ odp.pfnDlgProc = PopupsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUPS);
+ g_plugin.addOptions(wParam, &odp);
+
+ ZeroMemory(&odp, sizeof(odp));
+ odp.szGroup.a = LPGEN("Voice Calls");
+ odp.szTitle.a = LPGEN("General");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTS);
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szGroup.a = LPGEN("Voice Calls");
+ odp.szTitle.a = LPGEN("Auto actions");
+ odp.pfnDlgProc = AutoDlgProc;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_AUTO);
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
+
+void InitOptions()
+{
+ LoadOptions();
+
+ hOptHook = HookEvent(ME_OPT_INITIALISE, InitOptionsCallback);
+}
+
+void DeInitOptions()
+{
+ UnhookEvent(hOptHook);
+}
+
+void LoadOptions()
+{
+ LoadOpts(optionsControls, _countof(optionsControls), MODULE_NAME);
+ LoadOpts(popupsControls, _countof(popupsControls), MODULE_NAME);
+}
diff --git a/plugins/VoiceService/src/options.h b/plugins/VoiceService/src/options.h
new file mode 100644
index 0000000000..4ba1131ed9
--- /dev/null
+++ b/plugins/VoiceService/src/options.h
@@ -0,0 +1,67 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __OPTIONS_H__
+# define __OPTIONS_H__
+
+
+#include <windows.h>
+
+#define POPUP_ACTION_DONOTHING 0
+#define POPUP_ACTION_CLOSEPOPUP 1
+
+#define POPUP_DELAY_DEFAULT 0
+#define POPUP_DELAY_CUSTOM 1
+#define POPUP_DELAY_PERMANENT 2
+
+
+struct Options {
+ // Popup
+ BYTE popup_enable;
+ WORD popup_delay_type;
+ WORD popup_timeout;
+ BYTE popup_use_win_colors;
+ BYTE popup_use_default_colors;
+ COLORREF popup_bkg_color;
+ COLORREF popup_text_color;
+ WORD popup_left_click_action;
+ WORD popup_right_click_action;
+
+ // Frame
+ BOOL resize_frame;
+};
+
+extern Options opts;
+
+
+// Initializations needed by options
+void InitOptions();
+
+// Deinitializations needed by options
+void DeInitOptions();
+
+
+// Loads the options from DB
+// It don't need to be called, except in some rare cases
+void LoadOptions();
+
+
+
+#endif // __OPTIONS_H__
diff --git a/plugins/VoiceService/src/popup.cpp b/plugins/VoiceService/src/popup.cpp
new file mode 100644
index 0000000000..a643850295
--- /dev/null
+++ b/plugins/VoiceService/src/popup.cpp
@@ -0,0 +1,212 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+// Prototypes /////////////////////////////////////////////////////////////////////////////////////
+
+#define WMU_ACTION (WM_USER + 1)
+
+
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+HWND hPopupWindow = NULL;
+
+
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+
+
+// Functions //////////////////////////////////////////////////////////////////////////////////////
+
+
+// Initializations needed by popups
+void InitPopups()
+{
+ // window needed for popup commands
+ hPopupWindow = CreateWindowEx(WS_EX_TOOLWINDOW, _T("static"), _T(MODULE_NAME) _T("_PopupWindow"),
+ 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP,
+ NULL, g_plugin.getInst(), NULL);
+ SetWindowLongPtr(hPopupWindow, GWLP_WNDPROC, (LPARAM)PopupWndProc);
+}
+
+
+// Deinitializations needed by popups
+void DeInitPopups()
+{
+}
+
+void ShowPopup(MCONTACT hContact, const wchar_t *title, const wchar_t *description, ...)
+{
+ if (!opts.popup_enable)
+ return;
+
+ wchar_t text[1024];
+
+ va_list va;
+ va_start(va, description);
+ _vsntprintf(text, _countof(text), description, va);
+ va_end(va);
+
+ ShowPopupEx(hContact, title, text, (void*)hContact, POPUP_TYPE_NORMAL, &opts);
+}
+
+// Show an popup
+void ShowPopupEx(MCONTACT hContact, const wchar_t *title, const wchar_t *description,
+ void *plugin_data, int type, const Options *op)
+{
+ // Make popup
+ POPUPDATAW ppd;
+
+ ZeroMemory(&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.lchIcon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(174), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
+
+ if (title != NULL)
+ lstrcpyn(ppd.lpwzContactName, title, _countof(ppd.lpwzContactName));
+ else if (hContact != NULL)
+ lstrcpyn(ppd.lpwzContactName, Clist_GetContactDisplayName(hContact, 0), _countof(ppd.lpwzContactName));
+
+ if (description != NULL)
+ lstrcpyn(ppd.lpwzText, description, _countof(ppd.lpwzText));
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST) {
+ if (op->popup_use_default_colors) {
+ ppd.colorBack = 0;
+ ppd.colorText = 0;
+ }
+ else if (op->popup_use_win_colors) {
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ else {
+ ppd.colorBack = op->popup_bkg_color;
+ ppd.colorText = op->popup_text_color;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.colorBack = RGB(200, 0, 0);
+ ppd.colorText = RGB(255, 255, 255);
+ }
+
+ if (type == POPUP_TYPE_NORMAL) {
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.PluginData = plugin_data;
+ }
+ else // if (type == POPUP_TYPE_TEST || type == POPUP_TYPE_ERROR)
+ {
+ ppd.PluginWindowProc = DumbPopupDlgProc;
+ }
+
+ if (type == POPUP_TYPE_NORMAL || type == POPUP_TYPE_TEST) {
+ switch (op->popup_delay_type) {
+ case POPUP_DELAY_CUSTOM:
+ ppd.iSeconds = opts.popup_timeout;
+ break;
+
+ case POPUP_DELAY_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+
+ //case POPUP_DELAY_DEFAULT:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ }
+ else // if (type == POPUP_TYPE_ERROR)
+ {
+ ppd.iSeconds = 0;
+ }
+
+ // Now that every field has been filled, we want to see the popup.
+ PUAddPopupW(&ppd);
+}
+
+// Show an error popup
+void ShowErrPopup(const wchar_t *description, const wchar_t *title)
+{
+ ShowPopupEx(NULL, title == NULL ? _T(MODULE_NAME) _T(" Error") : title, description,
+ NULL, POPUP_TYPE_ERROR, NULL);
+}
+
+void ShowTestPopup(const wchar_t *title, const wchar_t *description, const Options *op)
+{
+ ShowPopupEx(NULL, title, description, NULL, POPUP_TYPE_TEST, op);
+}
+
+// Handle to the hidden windows to handle actions for popup clicks
+// wParam has the number of MOTD in case of WMU_SHOW_MOTD_DETAILS
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WMU_ACTION) {
+ // if (lParam == POPUP_ACTION_OPENHISTORY)
+ // {
+ // CallService(MS_HISTORY_SHOWCONTACTHISTORY, wParam, 0);
+ // }
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_COMMAND:
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM)PUGetPluginData(hWnd), opts.popup_left_click_action);
+
+ if (opts.popup_left_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopup(hWnd);
+
+ return TRUE;
+
+ case WM_CONTEXTMENU:
+ SendMessage(hPopupWindow, WMU_ACTION, (WPARAM)PUGetPluginData(hWnd), opts.popup_right_click_action);
+
+ if (opts.popup_right_click_action != POPUP_ACTION_DONOTHING)
+ PUDeletePopup(hWnd);
+
+ return TRUE;
+
+ case UM_FREEPLUGINDATA:
+ return TRUE;
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+
+// Handle to popup events
+static LRESULT CALLBACK DumbPopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_COMMAND:
+ case WM_CONTEXTMENU:
+ PUDeletePopup(hWnd);
+ return TRUE;
+
+ case UM_FREEPLUGINDATA:
+ return TRUE;
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
diff --git a/plugins/VoiceService/src/popup.h b/plugins/VoiceService/src/popup.h
new file mode 100644
index 0000000000..449277c5e4
--- /dev/null
+++ b/plugins/VoiceService/src/popup.h
@@ -0,0 +1,49 @@
+/*
+Copyright (C) 2005 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __POPUP_H__
+# define __POPUP_H__
+
+// Initializations needed by popups
+void InitPopups();
+
+// Deinitializations needed by popups
+void DeInitPopups();
+
+
+#define POPUP_TYPE_NORMAL 0
+#define POPUP_TYPE_TEST 1
+#define POPUP_TYPE_ERROR 2
+
+// Show an popup
+void ShowPopup(MCONTACT hContact, const wchar_t *title, const wchar_t *description, ...);
+
+// Show an test
+void ShowTestPopup(const wchar_t *title, const wchar_t *description, const Options *op);
+
+// Show an error popup
+void ShowErrPopup(const char *description, const char *title = NULL);
+
+void ShowPopupEx(MCONTACT hContact, const wchar_t *title, const wchar_t *description,
+ void *plugin_data, int type, const Options *op);
+
+
+
+#endif // __POPUP_H__
diff --git a/plugins/VoiceService/src/resource.h b/plugins/VoiceService/src/resource.h
new file mode 100644
index 0000000000..4f4307b746
--- /dev/null
+++ b/plugins/VoiceService/src/resource.h
@@ -0,0 +1,87 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDR_MENUS 101
+#define IDD_CALLS 102
+#define IDI_TALKING 102
+#define IDI_RINGING 103
+#define IDI_CALLING 104
+#define IDI_ON_HOLD 105
+#define IDI_ENDED 106
+#define IDI_BUSY 107
+#define IDI_ACTION_CALL 108
+#define IDI_ACTION_ANSWER 109
+#define IDI_ACTION_HOLD 110
+#define IDI_ACTION_DROP 111
+#define IDI_MAIN 112
+#define IDI_DIALPAD 113
+#define IDD_NEW_CALL 113
+#define IDI_SECURE 114
+#define IDD_POPUPS 120
+#define IDI_SMALLDOT 211
+#define IDD_OPT_AUTO 215
+#define IDD_OPTS 216
+#define IDD_OPT_DEVICES 217
+#define IDC_CALLS 1000
+#define IDC_DELAY 1001
+#define IDC_TEXT 1001
+#define IDC_WINCOLORS 1002
+#define ID_ANSWER 1002
+#define IDC_DEFAULTCOLORS 1003
+#define ID_DROP 1003
+#define IDC_BGCOLOR 1004
+#define IDC_AUTO 1004
+#define IDC_TEXTCOLOR 1005
+#define IDC_FRAME_AUTOSIZE 1005
+#define IDC_PREV 1006
+#define IDC_NUMBER 1006
+#define IDC_DELAYFROMPU 1007
+#define IDC_CALL 1007
+#define IDC_DELAYCUSTOM 1008
+#define IDC_DIALPAD 1008
+#define IDC_DELAYPERMANENT 1009
+#define IDC_INPUT 1009
+#define IDC_OUTPUT 1010
+#define IDC_ECHO 1011
+#define IDC_MIC_BOOST 1012
+#define IDC_1 1012
+#define IDC_2 1013
+#define IDC_3 1014
+#define IDC_4 1015
+#define IDC_5 1016
+#define IDC_6 1017
+#define IDC_AST 1018
+#define IDC_0 1019
+#define IDC_SHARP 1020
+#define IDC_7 1021
+#define IDC_RIGHT_ACTION 1022
+#define IDC_8 1022
+#define IDC_LEFT_ACTION 1023
+#define IDC_9 1023
+#define IDC_POPUPS 1060
+#define IDC_DELAY_SPIN 1061
+#define IDC_COLOURS_G 1068
+#define IDC_BGCOLOR_L 1069
+#define IDC_TEXTCOLOR_L 1070
+#define IDC_DELAY_G 1071
+#define IDC_ACTIONS_G 1072
+#define IDC_RIGHT_ACTION_L 1073
+#define IDC_LEFT_ACTION_L 1074
+#define IDC_LIST 1079
+#define IDC_ANSWER 1204
+#define IDC_DROP 1205
+#define ID_FRAMEPOPUP_ANSWERCALL 40001
+#define ID_FRAMEPOPUP_DROPCALL 40002
+#define ID_FRAMEPOPUP_HOLDCALL 40003
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 115
+#define _APS_NEXT_COMMAND_VALUE 40004
+#define _APS_NEXT_CONTROL_VALUE 1013
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plugins/VoiceService/src/services.cpp b/plugins/VoiceService/src/services.cpp
new file mode 100644
index 0000000000..cb1cb7fa08
--- /dev/null
+++ b/plugins/VoiceService/src/services.cpp
@@ -0,0 +1,171 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK DlgProcNewCall(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ VoiceCall *call = (VoiceCall *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ call = (VoiceCall *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+
+ wchar_t text[1024];
+
+ VoiceCall *currentCall = GetTalkingCall();
+ if (currentCall == NULL) {
+ mir_snwprintf(text, _countof(text), TranslateT("%s wants to start a voice call with you. What you want to do?"),
+ call->displayName);
+ }
+ else if (currentCall->CanHold()) {
+ mir_snwprintf(text, _countof(text), TranslateT("%s wants to start a voice call with you. What you want to do?\n\nIf you answer the call, the current call will be put on hold."),
+ call->displayName);
+ }
+ else {
+ mir_snwprintf(text, _countof(text), TranslateT("%s wants to start a voice call with you. What you want to do?\n\nIf you answer the call, the current call will be dropped."),
+ call->displayName);
+ }
+
+ SendMessage(GetDlgItem(hwndDlg, IDC_TEXT), WM_SETTEXT, 0, (LPARAM)text);
+
+ HICON hIcon = g_plugin.getIcon(IDI_RINGING);
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
+ IcoLib_ReleaseIcon(hIcon);
+
+ if (call->hContact == NULL)
+ ShowWindow(GetDlgItem(hwndDlg, IDC_AUTO), SW_HIDE);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (wParam) {
+ case ID_ANSWER:
+ if (call->hContact != NULL && IsDlgButtonChecked(hwndDlg, IDC_AUTO))
+ g_plugin.setWord(call->hContact, "AutoAccept", AUTO_ACCEPT);
+
+ Answer(call);
+
+ DestroyWindow(hwndDlg);
+ break;
+
+ case ID_DROP:
+ if (call->hContact != NULL && IsDlgButtonChecked(hwndDlg, IDC_AUTO))
+ g_plugin.setWord(call->hContact, "AutoAccept", AUTO_DROP);
+
+ call->Drop();
+
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ call->Notify(false, false, true);
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ call->SetNewCallHWND(NULL);
+ break;
+ }
+
+ return FALSE;
+}
+
+static INT_PTR CListDblClick(WPARAM, LPARAM lParam)
+{
+ CLISTEVENT *ce = (CLISTEVENT *)lParam;
+
+ VoiceCall *call = (VoiceCall *)ce->lParam;
+
+ HWND hwnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_NEW_CALL), NULL, DlgProcNewCall, (LPARAM)call);
+
+ ShowWindow(hwnd, SW_SHOWNORMAL);
+
+ call->SetNewCallHWND(hwnd);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR VoiceRegister(WPARAM wParam, LPARAM)
+{
+ VOICE_MODULE *in = (VOICE_MODULE *)wParam;
+ if (in == NULL || in->cbSize < sizeof(VOICE_MODULE) || in->name == NULL || in->description == NULL)
+ return -1;
+
+ if (FindModule(in->name) != NULL)
+ return -2;
+
+ if (!ProtoServiceExists(in->name, PS_VOICE_CALL)
+ || !ProtoServiceExists(in->name, PS_VOICE_ANSWERCALL)
+ || !ProtoServiceExists(in->name, PS_VOICE_DROPCALL))
+ return -3;
+
+ modules.insert(new VoiceProvider(in->name, in->description, in->flags, in->icon));
+
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+
+ return 0;
+}
+
+INT_PTR VoiceUnregister(WPARAM wParam, LPARAM)
+{
+ char *moduleName = (char *)wParam;
+ if (moduleName == NULL || moduleName[0] == 0)
+ return -1;
+
+ VoiceProvider *module = FindModule(moduleName);
+ if (module == NULL)
+ return -2;
+
+ for (int i = calls.getCount() - 1; i >= 0; --i) {
+ VoiceCall *call = &calls[i];
+
+ if (call->module == module) {
+ call->Drop();
+ call->SetState(VOICE_STATE_ENDED);
+
+ calls.remove(i);
+ }
+ }
+
+ modules.remove(module);
+
+ if (hwnd_frame != NULL)
+ PostMessage(hwnd_frame, WMU_REFRESH, 0, 0);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CreateServices()
+{
+ CreateServiceFunction(MS_VOICESERVICE_CLIST_DBLCLK, CListDblClick);
+ CreateServiceFunction(MS_VOICESERVICE_REGISTER, VoiceRegister);
+ CreateServiceFunction(MS_VOICESERVICE_UNREGISTER, VoiceUnregister);
+}
diff --git a/plugins/VoiceService/src/stdafx.cxx b/plugins/VoiceService/src/stdafx.cxx
new file mode 100644
index 0000000000..0a3b3b3920
--- /dev/null
+++ b/plugins/VoiceService/src/stdafx.cxx
@@ -0,0 +1,18 @@
+/*
+Copyright (C) 2012-20 Miranda NG team (https://miranda-ng.org)
+
+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 version 2
+of the License.
+
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h" \ No newline at end of file
diff --git a/plugins/VoiceService/src/stdafx.h b/plugins/VoiceService/src/stdafx.h
new file mode 100644
index 0000000000..cb28b01834
--- /dev/null
+++ b/plugins/VoiceService/src/stdafx.h
@@ -0,0 +1,218 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef __COMMONS_H__
+# define __COMMONS_H__
+
+
+#include <windows.h>
+#include <tchar.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <time.h>
+#include <commctrl.h>
+
+
+// Disable "...truncated to '255' characters in the debug information" warnings
+#pragma warning(disable: 4786)
+
+#include <vector>
+using namespace std;
+
+// Miranda headers
+#include <win2k.h>
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_clistint.h>
+#include <m_clc.h>
+#include <m_contacts.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_utils.h>
+#include <m_button.h>
+#include <m_popup.h>
+#include <m_cluiframes.h>
+#include <m_icolib.h>
+#include <m_metacontacts.h>
+#include <m_fontservice.h>
+#include <m_skin.h>
+
+#include <m_voice.h>
+#include <m_voiceservice.h>
+
+#include "resource.h"
+#include "version.h"
+#include "options.h"
+#include "frame.h"
+#include "popup.h"
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMPlugin();
+
+ int Load() override;
+ int Unload() override;
+};
+
+#define MODULE_NAME "VoiceService"
+
+#define ACTION_CALL 0
+#define ACTION_ANSWER 1
+#define ACTION_HOLD 2
+#define ACTION_DROP 3
+
+#define NUM_STATES 6
+
+#define AUTO_NOTHING 0
+#define AUTO_ACCEPT 1
+#define AUTO_DROP 2
+
+extern HFONT fonts[NUM_STATES];
+extern COLORREF font_colors[NUM_STATES];
+extern int font_max_height;
+extern COLORREF bkg_color;
+extern HBRUSH bk_brush;
+
+class VoiceProvider
+{
+public:
+ wchar_t description[256];
+ char name[256];
+ char icon[256];
+ int flags;
+ bool is_protocol;
+
+ VoiceProvider(const char *name, const wchar_t *description, int flags, const char *icon);
+ ~VoiceProvider();
+
+ bool CanCall(const wchar_t *number);
+ bool CanCall(MCONTACT hContact, BOOL now = TRUE);
+ void Call(MCONTACT hContact, const wchar_t *number);
+
+ bool CanHold();
+
+ bool CanSendDTMF();
+
+ HICON GetIcon();
+ void ReleaseIcon(HICON hIcon);
+
+private:
+ bool canHold;
+ HANDLE state_hook;
+};
+
+class VoiceCall
+{
+public:
+ VoiceProvider *module;
+ char *id; // Protocol especific ID for this call
+ MCONTACT hContact;
+ wchar_t name[256];
+ wchar_t number[256];
+ wchar_t displayName[256];
+ int state;
+ DWORD end_time;
+ bool incoming;
+ bool secure;
+
+ VoiceCall(VoiceProvider *module, const char *id);
+ ~VoiceCall();
+
+ void AppendCallerID(MCONTACT hContact, const wchar_t *name, const wchar_t *number);
+
+ void SetState(int state);
+
+ void Drop();
+ void Answer();
+ void Hold();
+
+ bool CanDrop();
+ bool CanAnswer();
+ bool CanHold();
+
+ bool CanSendDTMF();
+ void SendDTMF(wchar_t c);
+
+ bool IsFinished();
+
+ void Notify(bool popup = true, bool sound = true, bool clist = true);
+ void SetNewCallHWND(HWND hwnd);
+
+private:
+ HWND hwnd;
+ bool clistBlinking;
+
+ void RemoveNotifications();
+ void CreateDisplayName();
+
+};
+
+extern OBJLIST<VoiceProvider> modules;
+extern OBJLIST<VoiceCall> calls;
+
+void Answer(VoiceCall *call);
+bool CanCall(MCONTACT hContact, BOOL now = TRUE);
+bool CanCall(const wchar_t *number);
+bool CanCallNumber();
+void HoldOtherCalls(VoiceCall *call);
+VoiceCall * GetTalkingCall();
+bool IsFinalState(int state);
+
+INT_PTR VoiceRegister(WPARAM wParam, LPARAM lParam);
+INT_PTR VoiceUnregister(WPARAM wParam, LPARAM lParam);
+
+VoiceProvider *FindModule(const char *szModule);
+VoiceCall *FindVoiceCall(const char *szModule, const char *id, bool add);
+VoiceCall *FindVoiceCall(MCONTACT hContact);
+
+__inline BOOL IsEmptyA(const char *str)
+{
+ return str == NULL || str[0] == 0;
+}
+
+__inline BOOL IsEmptyW(const WCHAR *str)
+{
+ return str == NULL || str[0] == 0;
+}
+
+#define ICON_SIZE 16
+
+#define TIME_TO_SHOW_ENDED_CALL 5000 // ms
+
+#define MS_VOICESERVICE_CLIST_DBLCLK "VoiceService/CList/RingingDblClk"
+
+#define MS_VOICESERVICE_CM_CALL "VoiceService/ContactMenu/Call"
+#define MS_VOICESERVICE_CM_ANSWER "VoiceService/ContactMenu/Answer"
+#define MS_VOICESERVICE_CM_HOLD "VoiceService/ContactMenu/Hold"
+#define MS_VOICESERVICE_CM_DROP "VoiceService/ContactMenu/Drop"
+
+struct SoundDescr
+{
+ const char *szName;
+ const wchar_t *wszDescr;
+};
+
+extern SoundDescr g_sounds[NUM_STATES];
+
+#endif // __COMMONS_H__
diff --git a/plugins/VoiceService/src/version.h b/plugins/VoiceService/src/version.h
new file mode 100644
index 0000000000..4273bdff8f
--- /dev/null
+++ b/plugins/VoiceService/src/version.h
@@ -0,0 +1,13 @@
+#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 1
+#define __RELEASE_NUM 2
+#define __BUILD_NUM 0
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "Voice Service"
+#define __FILENAME "VoiceService.dll"
+#define __DESCRIPTION "Provide services for protocols that support voice calls"
+#define __AUTHOR "Ricardo Pescuma Domenecci"
+#define __AUTHORWEB "https://miranda-ng.org/p/VoiceService/"
+#define __COPYRIGHT "© 2007-2009 Ricardo Pescuma Domenecci"