diff options
Diffstat (limited to 'plugins')
35 files changed, 3936 insertions, 0 deletions
diff --git a/plugins/ExternalAPI/m_voice.h b/plugins/ExternalAPI/m_voice.h new file mode 100644 index 0000000000..e88eaeb842 --- /dev/null +++ b/plugins/ExternalAPI/m_voice.h @@ -0,0 +1,180 @@ +/* +Copyright (C) 2006-2009 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 __M_VOICE_H__ +# define __M_VOICE_H__ + + +#define EVENTTYPE_VOICE_CALL 8739 + + +#define VOICE_CAPS_VOICE (1<<0) // Voice is supported for this protocol. You need to set this one. +#define VOICE_CAPS_CALL_CONTACT (1<<1) // Set if a call can be made to a hContact (PS_VOICE_CALL_CONTACT_VALID is used to validate the string) +#define VOICE_CAPS_CALL_STRING (1<<3) // Set if a call can be made to some string (PS_VOICE_CALL_STRING_VALID is used to validate the string) +/* +Request to the protocol capabilities relative to voice. + +wParam: 0 +lParam: 0 +return: VOICE_CAPS_* +*/ +#define PS_VOICE_CAPS "/Voice/Caps" + + + +#define VOICE_SECURE 0x00000001 +#define VOICE_UNICODE 0x80000000 + +#ifdef UNICODE +# define VOICE_TCHAR VOICE_UNICODE +#else +# define VOICE_TCHAR 0 +#endif + +#define VOICE_STATE_TALKING 0 +#define VOICE_STATE_RINGING 1 +#define VOICE_STATE_CALLING 2 +#define VOICE_STATE_ON_HOLD 3 +#define VOICE_STATE_ENDED 4 +#define VOICE_STATE_BUSY 5 + +typedef struct { + int cbSize; // Struct size + const char *moduleName; // The name of the module (the same as VOICE_MODULE.name or the protocol szModule) + char *id; // Protocol especific ID for this call + int flags; // VOICE_UNICODE to say the string is unicode or 0. VOICE_SECURE to say this is a + // encrypted call + + MCONTACT hContact; // Contact associated with the call (can be NULL) + + union { // Number to call (can be NULL) + const TCHAR *ptszNumber;// Or the contact or the number must be != NULL + const char *pszNumber; // If both are != NULL the call will be made to the number + const WCHAR *pwszNumber;// and will be associated with the contact + }; // This fields are only needed in first notification for a call id + + union { // Name of the caller. This makes sense only on incoming calls, + const TCHAR *ptszName; // where no contact is associated and the caller has a name and a number. + const char *pszName; + const WCHAR *pwszName; + }; + + + int state; // VOICE_STATE_* + +} VOICE_CALL; + + +/* +Notifies that a voice call changed state + +wParam: const VOICE_CALL * +lParam: ignored +return: 0 on success +*/ +#define PE_VOICE_CALL_STATE "/Voice/State" + + + +/* +Request the protocol to make a voice call + +wParam: (HANDLE) hContact +lParam: (const TCHAR *) number +return: 0 on success +Or the contact or the number must be != NULL. If both are != NULL the call will be +made to the number and will be associated with the contact. +*/ +#define PS_VOICE_CALL "/Voice/Call" + +/* +Service called to make the protocol answer a call or restore a hold call. +It is an async call. If the call was answered, the PE_VOICE_CALL_STATE +notification will be fired. + +wParam: (const char *) id +lParam: ignored +return: 0 on success +*/ +#define PS_VOICE_ANSWERCALL "/Voice/AnswerCall" + +/* +Service called to make the protocol answer a call. This can be called if the +call is ringing or has started. If called any other time it should be ignored. +It is an async call. If the call was droped, the PE_VOICE_CALL_STATE +notification will be fired. + +wParam: (const char *) id +lParam: ignored +return: 0 on success +*/ +#define PS_VOICE_DROPCALL "/Voice/DropCall" + +/* +Service called to make the protocol hold a call. This means that the call should not +be droped, but it should be muted and put in a hold, to allow other call to be answered. +If the protocol can't hold a cal, it should be droped. + +This can be called if the call has started. If called any other time it should be ignored. +It is an async call. If the call was droped, the PE_VOICE_CALL_STATE +notification will be fired. + +wParam: (const char *) id +lParam: ignored +return: 0 on success +*/ +#define PS_VOICE_HOLDCALL "/Voice/HoldCall" + +/* +Send a DTMF (one digit text) to a talking call. + +wParam: (const char *) id +lParam: (TCHAR) dtmf +return: 0 on success +*/ +#define PS_VOICE_SEND_DTMF "/Voice/SendDTMF" + +/* +Used if protocol support VOICE_CALL_STRING. The call string is passed as +wParam and the proto should validate it. If this service does not exist all numbers can be called. + +wParam: (const TCHAR *) call string +lParam: ignored +return: 0 if wrong, 1 if correct +*/ +#define PS_VOICE_CALL_STRING_VALID "/Voice/CallStringValid" + +/* +Used if protocol support VOICE_CALL_CONTACT. +The hContact is passed as wParam and the proto should tell if this contact can be +called. If this service does not exist all contacts can be called (or, if it is a protocol, +all contacts from the protocol can be called). + +wParam: (HANDLE) hContact +lParam: (BOOL) TRUE if it is a test for 'can call now?', FALSE if is a test for 'will be possible to call someday?' +return: 0 if can't be called, 1 if can +*/ +#define PS_VOICE_CALL_CONTACT_VALID "/Voice/CallContactValid" + + + + + +#endif // __M_VOICE_H__ diff --git a/plugins/ExternalAPI/m_voiceservice.h b/plugins/ExternalAPI/m_voiceservice.h new file mode 100644 index 0000000000..c70dc8ea3e --- /dev/null +++ b/plugins/ExternalAPI/m_voiceservice.h @@ -0,0 +1,87 @@ +/* +Copyright (C) 2007-2009 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 __M_VOICESERVICE_H__ +# define __M_VOICESERVICE_H__ + + +/* +This services are a mirror of the services/notifications in m_voice.h, +with the difference that that ones are to be used by protocols, and this ones +are to be used by plugins that can make calls to contacts in multiple protocols. + +To get the devices for input/output and some options, query the db directly: + VoiceService/EchoCancelation BYTE default: TRUE + VoiceService/MicBoost BYTE default: TRUE + VoiceService/Input TString default: windows default + VoiceService/Output TString default: windows default +*/ + + +struct VOICE_MODULE +{ + int cbSize; // sizeof(VOICE_MODULE) + TCHAR *description; // The description of the voice provider. This is the name that will be shown + // to the user + char *name; // The internal name of the voice provider. All PS_* serivces + // defined in m_voide.h need to be created based in this name. For example, + // PS_VOICE_CALL (/Voice/Call) need to be created as <name>/Voice/Call + char *icon; // Icon to identify provider (from icolib) + int flags; // or of VOICE_CAPS_*. You don't need to send VOICE_CAPS_VOICE. +}; +/* +Register a new plugin that can make/receive voice calls (a voice provider). + +wParam: const VOICE_MODULE * +lParam: ignored +return: 0 on success +*/ +#define MS_VOICESERVICE_REGISTER "VoiceService/Register" + +/* +Unregister a plugin that can make/receive voice calls (a voice provider). + +wParam: (const char *) Provider name +lParam: ignored +return: 0 on success +*/ +#define MS_VOICESERVICE_UNREGISTER "VoiceService/Unregister" + +/* +Request a voice call to hContact. + +wParam: (HANDLE) hContact +lParam: ignored +return: the number of option calls for a contact. If > 0, it can be called +*/ +#define MS_VOICESERVICE_CAN_CALL "VoiceService/CanCall" + +/* +Request a voice call to hContact. + +wParam: (HANDLE) hContact +lParam: (char *) Voice provider or NULL to use any provider avaiable +return: 0 on success +*/ +#define MS_VOICESERVICE_CALL "VoiceService/Call" + + + +#endif // __M_VOICESERVICE_H__ diff --git a/plugins/VoiceService/VoiceService.vcxproj b/plugins/VoiceService/VoiceService.vcxproj new file mode 100644 index 0000000000..8e1ec7eeff --- /dev/null +++ b/plugins/VoiceService/VoiceService.vcxproj @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectName>VoiceService</ProjectName> + <ProjectGuid>{48A76FF0-9506-4D70-81D7-E3A2B2B2A65A}</ProjectGuid> + </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(ProjectDir)..\..\build\vc.common\plugin.props" /> + </ImportGroup> + <ItemGroup> + <ClCompile Include="..\..\utils\mir_options.cpp"> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/plugins/VoiceService/VoiceService.vcxproj.filters b/plugins/VoiceService/VoiceService.vcxproj.filters new file mode 100644 index 0000000000..83cffdb711 --- /dev/null +++ b/plugins/VoiceService/VoiceService.vcxproj.filters @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(ProjectDir)..\..\build\vc.common\common.filters" /> + <ItemGroup> + <ClInclude Include="src\*.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClCompile Include="src\*.cpp;src\*.cxx"> + <Filter>Source Files</Filter> + </ClCompile> + <ResourceCompile Include="res\*.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + <None Include="res\*.ico;res\*.bmp;res\*.cur"> + <Filter>Resource Files</Filter> + </None> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/plugins/VoiceService/res/Answer.ico b/plugins/VoiceService/res/Answer.ico Binary files differnew file mode 100644 index 0000000000..1f1c4ee389 --- /dev/null +++ b/plugins/VoiceService/res/Answer.ico diff --git a/plugins/VoiceService/res/Busy.ico b/plugins/VoiceService/res/Busy.ico Binary files differnew file mode 100644 index 0000000000..397ecb2b12 --- /dev/null +++ b/plugins/VoiceService/res/Busy.ico diff --git a/plugins/VoiceService/res/Call.ico b/plugins/VoiceService/res/Call.ico Binary files differnew file mode 100644 index 0000000000..6559874da9 --- /dev/null +++ b/plugins/VoiceService/res/Call.ico diff --git a/plugins/VoiceService/res/Calling.ico b/plugins/VoiceService/res/Calling.ico Binary files differnew file mode 100644 index 0000000000..6559874da9 --- /dev/null +++ b/plugins/VoiceService/res/Calling.ico diff --git a/plugins/VoiceService/res/Drop.ico b/plugins/VoiceService/res/Drop.ico Binary files differnew file mode 100644 index 0000000000..48cfa1437c --- /dev/null +++ b/plugins/VoiceService/res/Drop.ico diff --git a/plugins/VoiceService/res/Hold.ico b/plugins/VoiceService/res/Hold.ico Binary files differnew file mode 100644 index 0000000000..b789a7bf84 --- /dev/null +++ b/plugins/VoiceService/res/Hold.ico diff --git a/plugins/VoiceService/res/Main.ico b/plugins/VoiceService/res/Main.ico Binary files differnew file mode 100644 index 0000000000..952d87e5aa --- /dev/null +++ b/plugins/VoiceService/res/Main.ico diff --git a/plugins/VoiceService/res/On hold.ico b/plugins/VoiceService/res/On hold.ico Binary files differnew file mode 100644 index 0000000000..357bf07463 --- /dev/null +++ b/plugins/VoiceService/res/On hold.ico diff --git a/plugins/VoiceService/res/Rinning.ico b/plugins/VoiceService/res/Rinning.ico Binary files differnew file mode 100644 index 0000000000..0f2a5b5410 --- /dev/null +++ b/plugins/VoiceService/res/Rinning.ico diff --git a/plugins/VoiceService/res/Talking.ico b/plugins/VoiceService/res/Talking.ico Binary files differnew file mode 100644 index 0000000000..b73bc5d7e4 --- /dev/null +++ b/plugins/VoiceService/res/Talking.ico diff --git a/plugins/VoiceService/res/dialpad.ico b/plugins/VoiceService/res/dialpad.ico Binary files differnew file mode 100644 index 0000000000..45258ed769 --- /dev/null +++ b/plugins/VoiceService/res/dialpad.ico diff --git a/plugins/VoiceService/res/ended.ico b/plugins/VoiceService/res/ended.ico Binary files differnew file mode 100644 index 0000000000..397ecb2b12 --- /dev/null +++ b/plugins/VoiceService/res/ended.ico diff --git a/plugins/VoiceService/res/resource.rc b/plugins/VoiceService/res/resource.rc new file mode 100644 index 0000000000..b9ea23df94 --- /dev/null +++ b/plugins/VoiceService/res/resource.rc @@ -0,0 +1,290 @@ +//Microsoft Developer Studio generated resource script. +// +#include "..\src\resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CALLS DIALOGEX 0, 0, 195, 225 +STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LISTBOX IDC_CALLS,0,0,195,126,LBS_SORT | LBS_OWNERDRAWFIXED | + LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | LBS_NOSEL | NOT + WS_BORDER | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_DIALPAD,"MButtonClass",0x0,0,126,20,14, + 0x18000000L + EDITTEXT IDC_NUMBER,20,126,155,14,ES_AUTOHSCROLL + CONTROL "",IDC_CALL,"MButtonClass",0x0,175,126,20,14,0x18000000L + PUSHBUTTON "1",IDC_1,67,143,18,18 + PUSHBUTTON "2",IDC_2,87,143,18,18 + PUSHBUTTON "3",IDC_3,107,143,18,18 + PUSHBUTTON "4",IDC_4,67,163,18,18 + PUSHBUTTON "5",IDC_5,87,163,18,18 + PUSHBUTTON "6",IDC_6,107,163,18,18 + PUSHBUTTON "7",IDC_7,67,183,18,18 + PUSHBUTTON "8",IDC_8,87,183,18,18 + PUSHBUTTON "9",IDC_9,107,183,18,18 + PUSHBUTTON "*",IDC_AST,67,203,18,18 + PUSHBUTTON "0",IDC_0,87,203,18,18 + PUSHBUTTON "#",IDC_SHARP,107,203,18,18 +END + +IDD_POPUPS DIALOGEX 0, 0, 314, 240 +STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "Enable popups",IDC_POPUPS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,3,308,12 + GROUPBOX " Colours ",IDC_COLOURS_G,3,25,158,75 + CONTROL "",IDC_BGCOLOR,"ColourPicker",WS_TABSTOP,11,37,35,14 + LTEXT "Background colour",IDC_BGCOLOR_L,55,41,66,8 + CONTROL "",IDC_TEXTCOLOR,"ColourPicker",WS_TABSTOP,11,55,35,14 + LTEXT "Text colour",IDC_TEXTCOLOR_L,55,59,66,8 + CONTROL "Use Windows colours",IDC_WINCOLORS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,74,108,10 + CONTROL "Use default colours",IDC_DEFAULTCOLORS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,84,107,10 + GROUPBOX " Delay ",IDC_DELAY_G,166,25,145,75 + CONTROL "From popup plugin",IDC_DELAYFROMPU,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,175,38,122,10 + CONTROL "Custom",IDC_DELAYCUSTOM,"Button",BS_AUTORADIOBUTTON,175, + 52,54,10 + CONTROL "Permanent",IDC_DELAYPERMANENT,"Button", + BS_AUTORADIOBUTTON,175,66,122,10 + EDITTEXT IDC_DELAY,233,50,31,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS | + UDS_HOTTRACK,268,51,11,11 + GROUPBOX " Actions ",IDC_ACTIONS_G,3,103,308,47 + RTEXT "On right click:",IDC_RIGHT_ACTION_L,13,118,62,9 + COMBOBOX IDC_RIGHT_ACTION,83,116,156,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + RTEXT "On left click:",IDC_LEFT_ACTION_L,13,132,62,9 + COMBOBOX IDC_LEFT_ACTION,83,132,156,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Preview",IDC_PREV,131,161,50,14 +END + +IDD_NEW_CALL DIALOG DISCARDABLE 0, 0, 229, 87 +STYLE DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "New Voice Call" +FONT 8, "MS Shell Dlg" +BEGIN + CTEXT "%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.", + IDC_TEXT,7,7,215,36 + DEFPUSHBUTTON "Answer",ID_ANSWER,58,47,50,14 + PUSHBUTTON "Drop",ID_DROP,121,47,50,14 + CONTROL "From now on, repeat this action for this contact", + IDC_AUTO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,67,215, + 12 +END + +IDD_OPTS DIALOGEX 0, 0, 233, 41 +STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Frame",IDC_STATIC,3,3,227,30 + CONTROL "Auto-size frame",IDC_FRAME_AUTOSIZE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,15,214,12 +END + +IDD_OPT_AUTO DIALOGEX 0, 0, 313, 240 +STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Automatic Actions",IDC_STATIC,0,0,313,240 + CONTROL "",IDC_LIST,"CListControl",WS_TABSTOP | 0x1f0,8,14,297, + 183,WS_EX_CLIENTEDGE + ICON IDI_ACTION_ANSWER,IDC_ANSWER,8,204,20,20 + LTEXT "Auto accept this contact calls",IDC_STATIC,26,205,279,8, + SS_NOPREFIX | SS_CENTERIMAGE + ICON IDI_ACTION_DROP,IDC_DROP,8,219,20,20 + LTEXT "Auto drop this contacts calls",IDC_STATIC,26,220,279,8, + SS_NOPREFIX | SS_CENTERIMAGE +END + +IDD_OPT_DEVICES DIALOGEX 0, 0, 295, 221 +STYLE DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Audio",IDC_STATIC,3,3,289,78 + LTEXT "Input:",IDC_STATIC,11,16,42,12 + COMBOBOX IDC_INPUT,58,15,228,13,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Output:",IDC_STATIC,11,33,42,12 + COMBOBOX IDC_OUTPUT,58,32,228,13,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Echo cancelation",IDC_ECHO,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,11,49,275,12 + CONTROL "Microphone boost",IDC_MIC_BOOST,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,64,275,12 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_POPUPS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 311 + TOPMARGIN, 3 + BOTTOMMARGIN, 237 + END + + IDD_NEW_CALL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 222 + TOPMARGIN, 7 + BOTTOMMARGIN, 80 + END + + IDD_OPTS, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 230 + TOPMARGIN, 3 + BOTTOMMARGIN, 38 + END + + IDD_OPT_AUTO, DIALOG + BEGIN + VERTGUIDE, 8 + VERTGUIDE, 305 + END + + IDD_OPT_DEVICES, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 292 + TOPMARGIN, 3 + BOTTOMMARGIN, 218 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENUS MENU DISCARDABLE +BEGIN + POPUP "Frame Popup" + BEGIN + MENUITEM "Answer call", ID_FRAMEPOPUP_ANSWERCALL + MENUITEM "Drop call", ID_FRAMEPOPUP_DROPCALL + MENUITEM "Hold call", ID_FRAMEPOPUP_HOLDCALL + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_TALKING ICON DISCARDABLE "res\\Talking.ico" +IDI_RINGING ICON DISCARDABLE "res\\Rinning.ico" +IDI_ENDED ICON DISCARDABLE "res\\ended.ico" +IDI_BUSY ICON DISCARDABLE "res\\Busy.ico" +IDI_ON_HOLD ICON DISCARDABLE "res\\On hold.ico" +IDI_ACTION_CALL ICON DISCARDABLE "res\\Call.ico" +IDI_ACTION_ANSWER ICON DISCARDABLE "res\\Answer.ico" +IDI_ACTION_HOLD ICON DISCARDABLE "res\\Hold.ico" +IDI_ACTION_DROP ICON DISCARDABLE "res\\Drop.ico" +IDI_MAIN ICON DISCARDABLE "res\\Main.ico" +IDI_CALLING ICON DISCARDABLE "res\\Calling.ico" +IDI_DIALPAD ICON DISCARDABLE "res\\dialpad.ico" +IDI_SMALLDOT ICON DISCARDABLE "res/smalldot.ico" +IDI_SECURE ICON DISCARDABLE "res\\secure.ico" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Portuguese (Brazil) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PTB) +#ifdef _WIN32 +LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Portuguese (Brazil) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/VoiceService/res/secure.ico b/plugins/VoiceService/res/secure.ico Binary files differnew file mode 100644 index 0000000000..738650129d --- /dev/null +++ b/plugins/VoiceService/res/secure.ico diff --git a/plugins/VoiceService/res/smalldot.ico b/plugins/VoiceService/res/smalldot.ico Binary files differnew file mode 100644 index 0000000000..47f0556654 --- /dev/null +++ b/plugins/VoiceService/res/smalldot.ico diff --git a/plugins/VoiceService/res/version.rc b/plugins/VoiceService/res/version.rc new file mode 100644 index 0000000000..5a5ddd63ed --- /dev/null +++ b/plugins/VoiceService/res/version.rc @@ -0,0 +1,9 @@ +// Microsoft Visual C++ generated resource script. +// +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + +#include "..\src\version.h" + +#include "..\..\build\Version.rc" 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" |