From a13e82647294da4add976a24335fec50d7bfe905 Mon Sep 17 00:00:00 2001 From: sje Date: Wed, 1 Nov 2006 14:46:09 +0000 Subject: git-svn-id: https://server.scottellis.com.au/svn/mim_plugs@15 4f64403b-2f21-0410-a795-97e2b3489a10 --- tipper/common.h | 82 +++ tipper/docs/autoexec_Tipper(Omniwolf).ini | 339 +++++++++ tipper/docs/autoexec_Tipper(Tweety).ini | 377 ++++++++++ tipper/docs/licence_Tipper.txt | 6 + tipper/docs/m_tipper.h | 23 + tipper/docs/readme_tipper.txt | 143 ++++ tipper/m_tipper.h | 19 + tipper/message_pump.cpp | 201 +++++ tipper/message_pump.h | 41 ++ tipper/options.cpp | 1126 +++++++++++++++++++++++++++++ tipper/options.h | 74 ++ tipper/popwin.cpp | 837 +++++++++++++++++++++ tipper/popwin.h | 17 + tipper/resource.h | 85 +++ tipper/resource.rc | 6 + tipper/subst.cpp | 468 ++++++++++++ tipper/subst.h | 14 + tipper/tipper.cpp | 295 ++++++++ tipper/tipper.h | 12 + tipper/tipper.mdsp | 107 +++ tipper/tipper.rc | 239 ++++++ tipper/tipper.sln | 20 + tipper/tipper.vcproj | 376 ++++++++++ tipper/translations.cpp | 654 +++++++++++++++++ tipper/translations.h | 14 + tipper/version.h | 23 + tipper/version.rc | 33 + 27 files changed, 5631 insertions(+) create mode 100644 tipper/common.h create mode 100644 tipper/docs/autoexec_Tipper(Omniwolf).ini create mode 100644 tipper/docs/autoexec_Tipper(Tweety).ini create mode 100644 tipper/docs/licence_Tipper.txt create mode 100644 tipper/docs/m_tipper.h create mode 100644 tipper/docs/readme_tipper.txt create mode 100644 tipper/m_tipper.h create mode 100644 tipper/message_pump.cpp create mode 100644 tipper/message_pump.h create mode 100644 tipper/options.cpp create mode 100644 tipper/options.h create mode 100644 tipper/popwin.cpp create mode 100644 tipper/popwin.h create mode 100644 tipper/resource.h create mode 100644 tipper/resource.rc create mode 100644 tipper/subst.cpp create mode 100644 tipper/subst.h create mode 100644 tipper/tipper.cpp create mode 100644 tipper/tipper.h create mode 100644 tipper/tipper.mdsp create mode 100644 tipper/tipper.rc create mode 100644 tipper/tipper.sln create mode 100644 tipper/tipper.vcproj create mode 100644 tipper/translations.cpp create mode 100644 tipper/translations.h create mode 100644 tipper/version.h create mode 100644 tipper/version.rc diff --git a/tipper/common.h b/tipper/common.h new file mode 100644 index 0000000..9b9e0e4 --- /dev/null +++ b/tipper/common.h @@ -0,0 +1,82 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. +#endif + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include +#include +#include +#include +#include + +#define MIRANDA_VER 0x0600 // for tabbed options + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#define MODULE "Tipper" + +extern HMODULE hInst; +extern PLUGINLINK *pluginLink; +extern HANDLE mainThread; + +extern HFONT hFontTitle, hFontLabels, hFontValues; +extern COLORREF colTitle, colLabels, colBg, colValues; + +extern int code_page; + +extern struct MM_INTERFACE memoryManagerInterface; diff --git a/tipper/docs/autoexec_Tipper(Omniwolf).ini b/tipper/docs/autoexec_Tipper(Omniwolf).ini new file mode 100644 index 0000000..b68bdc8 --- /dev/null +++ b/tipper/docs/autoexec_Tipper(Omniwolf).ini @@ -0,0 +1,339 @@ +SETTINGS: + +[Tipper] +WinWidth=d400 +WinMaxHeight=d400 +Transparency=b19 +Border=b0 +RoundCorners=b1 +Animate=b0 +TransparentBg=b0 +RightIcon=b0 +AVLayout=b2 +AVSize=d86 +TextIndent=d22 +ShowNoFocus=b1 +DSNumValues=w19 +DINumValues=w22 +ShowStatusMsg=b1 +Type0=b1 +TransFunc0=w7 +DIValue0=u%Status% +DILineAbove0=b0 +DIValNewline0=b0 +Type1=b1 +TransFunc1=w7 +DILineAbove1=b0 +DIValNewline1=b0 +Type2=b1 +TransFunc2=w2 +DILineAbove2=b0 +DIValNewline2=b0 +Type3=b1 +TransFunc3=w3 +Type4=b1 +TransFunc4=w1 +DILineAbove3=b0 +DIValNewline3=b0 +Type5=b1 +TransFunc5=w1 +DILineAbove4=b1 +DIValNewline4=b0 +Type6=b1 +TransFunc6=w1 +DILineAbove5=b0 +DIValNewline5=b0 +DILabel0=uStatus: +Type7=b1 +TransFunc7=w1 +DILineAbove6=b0 +DIValNewline6=b0 +Name8=umetaS0 +Type8=b1 +TransFunc8=w1 +Name9=umetaS1 +Type9=b1 +TransFunc9=w1 +Name10=umetaS2 +Type10=b1 +TransFunc10=w1 +Name11=umetaS3 +Type11=b1 +TransFunc11=w1 +Name12=umetaS4 +Type12=b1 +TransFunc12=w1 +Name13=umetaS5 +Type13=b1 +TransFunc13=w1 +Type14=b1 +TransFunc14=w1 +Type15=b1 +TransFunc15=w1 +Type16=b1 +TransFunc16=w1 +DILineAbove7=b0 +DIValNewline7=b0 +DILineAbove8=b1 +DIValNewline8=b0 +DILineAbove9=b1 +DIValNewline9=b0 +DILineAbove10=b1 +DIValNewline10=b1 +Name14=umetaS6 +Name16=umetaS8 +Type17=b1 +TransFunc17=w1 +Name15=umetaS7 +Name17=umetaS9 +Type18=b1 +TransFunc18=w1 +Type19=b1 +TransFunc19=w0 +Type20=b1 +TransFunc20=w0 +FontFirstSize=b241 +FontFirstSty=b1 +FontFirstSet=b0 +FontFirstCol=d255 +FontLabelsSize=b243 +FontLabelsSty=b0 +FontLabelsSet=b0 +FontLabelsCol=d4210752 +FontValuesSize=b243 +FontValuesSty=b0 +FontValuesSet=b0 +FontValuesCol=d0 +ColourBg=d14476004 +Name0=uicqIP +Name18=uStatus +Type21=b1 +TransFunc21=w1 +DILineAbove11=b1 +DIValNewline11=b1 +Name1=uicqIPreal +Name19=uMirVer +Name20=usametimeID +Name21=uStatus +Name22=uUIN +Type22=b1 +TransFunc22=w0 +DILineAbove12=b1 +DIValNewline12=b0 +RightLabels=b0 +TimeIn=w500 +DILineAbove17=b0 +DIValNewline17=b0 +DILineAbove18=b0 +DIValNewline18=b0 +DILineAbove19=b0 +DIValNewline19=b0 +DILineAbove20=b0 +DIValNewline20=b0 +DILineAbove21=b0 +DIValNewline21=b0 +DILineAbove13=b0 +DIValNewline13=b0 +DILineAbove14=b0 +DIValNewline14=b0 +DILineAbove15=b0 +DIValNewline15=b0 +DILineAbove16=b0 +DIValNewline16=b0 +DILineAbove22=b1 +DIValNewline22=b0 +Module0=s +Setting0=sIP +Module1=s +Setting1=sRealIP +Module2=s +Setting2=sIdleTS +Module3=s +Setting3=sIdleTS +Module4=s +Module5=s +Module6=s +Module7=s +Module8=s +Module9=s +Module10=s +Module11=s +Module12=s +Module13=s +Module14=s +Module15=s +Module16=s +Setting16=sStatus8 +Module17=s +Setting17=sStatus9 +Module18=s +Module19=s +Setting19=sMirVer +Module20=s +Setting20=sstid +Module21=s +Setting21=sStatus +Module22=s +Setting22=sUIN +BorderCol=d0 +Name3=uIdleTS-diff +Setting7=sLogonTS +Setting8=sStatus0 +Setting9=sStatus1 +Setting10=sStatus2 +Setting11=sStatus3 +Setting12=sStatus4 +Setting13=sStatus5 +DILineAbove23=b0 +DIValNewline23=b0 +DILineAbove24=b0 +DIValNewline24=b0 +DILineAbove25=b0 +DIValNewline25=b0 +DILineAbove26=b0 +DIValNewline26=b0 +DILineAbove27=b0 +DIValNewline27=b0 +DILineAbove28=b0 +DIValNewline28=b0 +TitleLayout=b0 +Padding=w4 +Position=b1 +MinWidth=d0 +MinHeight=d0 +WORD to status description=d1 +NextFuncId=d17 +DWORD timestamp to time=d2 +DWORD timestamp to time difference=d3 +BYTE to Yes/No=d4 +BYTE to Male/Female (ICQ)=d5 +WORD to country name=d6 +DWORD to ip address=d7 +Day|Month|Year to date=d8 +Day|Month|Year to age=d9 +Hours|Minutes|Seconds to time=d10 +Day|Month|Year|Hours|Minutes|Seconds to time difference=d11 +DWORD timestamp to time (no seconds)=d12 +Hours|Minutes to time=d13 +DWORD timestamp to date (short)=d14 +DWORD timestamp to date (long)=d15 +MaxWidth=d350 +MaxHeight=d400 +Opacity=b85 +TransFuncId0=d7 +TransFuncId1=d7 +TransFuncId2=d14 +TransFuncId3=d3 +TransFuncId4=d12 +TransFuncId5=d14 +TransFuncId6=d3 +TransFuncId7=d12 +TransFuncId8=d1 +TransFuncId9=d1 +TransFuncId10=d1 +TransFuncId11=d1 +TransFuncId12=d1 +TransFuncId13=d1 +TransFuncId14=d1 +MouseTollerance=b16 +DropShadow=b1 +DILineAbove29=b0 +DIValNewline29=b0 +RightValues=b0 +SBarTips=b1 +xStatus: empty xStatus name to default name=d16 +DILineAbove30=b0 +DIValNewline30=b0 +DividerCol=d8421440 +SidebarCol=d9741995 +AvatarRoundCorners=b0 +AvatarPadding=w7 +TextPadding=w4 +SidebarWidth=d22 +Name2=uIdleTS-date +Name4=uIdleTS-time +Setting4=sIdleTS +Setting14=sStatus6 +TransFuncId15=d1 +Name5=uLogonTS-date +Name6=uLogonTS-diff +Name7=uLogonTS-time +Setting15=sStatus7 +TransFuncId16=d1 +TransFuncId17=d1 +Setting18=sStatus +TransFuncId18=d1 +Setting5=sLogonTS +Setting6=sLogonTS +FontValues=uTahoma +FontLabels=uTahoma +FontFirst=uTahoma +DILabel26=uHigh/Low: +DILabel27=uPressure: +DILabel1=uName: +DIValue1=u%raw:/FirstName|% %raw:/LastName% +DIValue3=u%raw:/MirVer% +DILabel6=uExternal IP: +DILabel23=uVisibility: +DILabel28=uHumidity: +DILabel7=uInternal IP: +DILabel8=uActive subcontact: +DIValue8=u%sys:meta_subproto% - %sys:meta_subname% +DILabel22=uObservation time: +DIValue22=u%raw:Current/Update% +DIValue23=u%raw:Current/Visibility% +DILabel24=uFeels like: +DIValue24=u%raw:Current/Feel% +DILabel25=uWind: +DIValue25=u%raw:Current/Wind Direction% (%raw:Current/Wind Direction DEG%)/%raw:Current/Wind Speed% +DIValue26=u%raw:Current/High%/%raw:Current/Low% +DIValue27=u%raw:Current/Pressure% (%raw:Current/Pressure Tendency%) +DIValue28=u%raw:Current/Humidity% +DILabel29=uUV Index: +DIValue29=u%raw:Current/UV% - %raw:Current/UVI% +DILabel30=uSunrise/Sunset: +DIValue30=u%raw:Current/Sunrise%/%raw:Current/Sunset% +DILabel31=uMoonphase +DIValue31=u%raw:Current/Moon% +DILineAbove31=b0 +DIValNewline31=b0 +DIValue6=u%icqIP% +DIValue7=u%icqIPreal% +DILabel2=u%sys:uidname^!MetaContacts%: +DIValue2=u%sys:uid% +DILabel3=uClient: +DILabel4=uLogged on: +DIValue4=u%LogonTS-diff% +DILabel5=uIdle: +DIValue5=u%IdleTS-diff% +FontFirstFlags=d288 +FontLabelsFlags=d288 +FontValuesFlags=d288 +DILabel9=uListening To: +DIValue9=u%raw:/ListeningTo% +DILabel10=uLast message: (%sys:last_msg_reltime% ago) +DIValue10=u%sys:last_msg% +DILabel11=uStatus Message: +DIValue11=u%sys:status_msg% +DILabel12=uObservation time: +DIValue12=u%raw:Current/Update% +DILabel13=uVisibility: +DIValue13=u%raw:Current/Visibility% +DILabel14=uFeels like: +DIValue14=u%raw:Current/Feel% +DILabel15=uWind: +DIValue15=u%raw:Current/Wind Direction% (%raw:Current/Wind Direction DEG%)/%raw:Current/Wind Speed% +DILabel16=uHigh/Low: +DIValue16=u%raw:Current/High%/%raw:Current/Low% +DILabel17=uPressure: +DIValue17=u%raw:Current/Pressure% (%raw:Current/Pressure Tendency%) +DILabel18=uHumidity: +DIValue18=u%raw:Current/Humidity% +DILabel19=uUV Index: +DIValue19=u%raw:Current/UV% - %raw:Current/UVI% +DILabel20=uSunrise/Sunset: +DIValue20=u%raw:Current/Sunrise%/%raw:Current/Sunset% +DILabel21=uMoonphase +DIValue21=u%raw:Current/Moon% + + diff --git a/tipper/docs/autoexec_Tipper(Tweety).ini b/tipper/docs/autoexec_Tipper(Tweety).ini new file mode 100644 index 0000000..9f555f5 --- /dev/null +++ b/tipper/docs/autoexec_Tipper(Tweety).ini @@ -0,0 +1,377 @@ +SETTINGS: + +[Tipper] +WORD to status description=d1 +NextFuncId=d17 +DWORD timestamp to time=d2 +DWORD timestamp to time difference=d3 +BYTE to Yes/No=d4 +BYTE to Male/Female (ICQ)=d5 +WORD to country name=d6 +DWORD to ip address=d7 +Day|Month|Year to date=d8 +Day|Month|Year to age=d9 +Hours|Minutes|Seconds to time=d10 +Day|Month|Year|Hours|Minutes|Seconds to time difference=d11 +DWORD timestamp to time (no seconds)=d12 +Hours|Minutes to time=d13 +DWORD timestamp to date (short)=d14 +DWORD timestamp to date (long)=d15 +WinWidth=d400 +WinMaxHeight=d400 +Transparency=b19 +Border=b0 +RoundCorners=b1 +Animate=b1 +TransparentBg=b0 +RightIcon=b0 +AVLayout=b2 +AVSize=d80 +TextIndent=d23 +ShowNoFocus=b1 +NumValues=w3 +FontFirstSize=b247 +FontFirstSty=b1 +FontFirstSet=b0 +FontFirstCol=d6572572 +FontLabelsSize=b247 +FontLabelsSty=b1 +FontLabelsSet=b0 +FontLabelsCol=d6572572 +FontValuesSize=b247 +FontValuesSty=b0 +FontValuesSet=b0 +FontValuesCol=d6049571 +ColourBg=d15987699 +Label0=uEtat +Type0=b0 +TransFunc0=w7 +Label1=uMessage d'état +Type1=b1 +TransFunc1=w7 +Label2=uDernier Message +Type2=b1 +TransFunc2=w2 +ShowStatusMsg=b1 +DSNumValues=w21 +DILineAbove0=b0 +DIValNewline0=b0 +DINumValues=w38 +DILineAbove1=b0 +DIValNewline1=b0 +DILineAbove2=b0 +DIValNewline2=b0 +DILineAbove3=b0 +DIValNewline3=b0 +DILineAbove4=b0 +DIValNewline4=b0 +Type3=b1 +TransFunc3=w3 +Type4=b1 +TransFunc4=w1 +Type5=b1 +TransFunc5=w1 +Type6=b0 +TransFunc6=w1 +DILineAbove5=b0 +DIValNewline5=b0 +Type7=b1 +TransFunc7=w1 +DILineAbove6=b0 +DIValNewline6=b0 +Type8=b1 +TransFunc8=w1 +Type9=b1 +TransFunc9=w1 +Name10=umetaS0 +Type10=b1 +TransFunc10=w1 +Name11=umetaS1 +Type11=b1 +TransFunc11=w1 +Name12=umetaS2 +Type12=b1 +TransFunc12=w1 +Name13=umetaS3 +Type13=b1 +TransFunc13=w1 +Type14=b1 +TransFunc14=w1 +Type15=b1 +TransFunc15=w1 +Type16=b1 +TransFunc16=w1 +DILineAbove7=b0 +DIValNewline7=b0 +DILineAbove8=b1 +DIValNewline8=b0 +DILineAbove9=b0 +DIValNewline9=b0 +DILineAbove10=b0 +DIValNewline10=b0 +Name14=umetaS4 +Name16=umetaS6 +Type17=b1 +TransFunc17=w1 +Name15=umetaS5 +Name17=umetaS7 +Type18=b1 +TransFunc18=w1 +Type19=b1 +TransFunc19=w0 +Type20=b1 +TransFunc20=w0 +Name18=umetaS8 +Type21=b1 +TransFunc21=w1 +DILineAbove11=b0 +DIValNewline11=b0 +Name19=umetaS9 +Name21=uStatus +Name22=uUIN +Type22=b1 +TransFunc22=w0 +DILineAbove12=b0 +DIValNewline12=b0 +RightLabels=b0 +TimeIn=w300 +DILineAbove17=b0 +DIValNewline17=b0 +DILineAbove18=b0 +DIValNewline18=b0 +DILineAbove19=b0 +DIValNewline19=b0 +DILineAbove20=b0 +DIValNewline20=b0 +DILineAbove21=b0 +DIValNewline21=b0 +DILineAbove13=b1 +DIValNewline13=b1 +DILineAbove14=b0 +DIValNewline14=b1 +DILineAbove15=b1 +DIValNewline15=b0 +DILineAbove16=b0 +DIValNewline16=b0 +DILineAbove22=b0 +DIValNewline22=b0 +Module1=s +Module2=s +Setting2=sRealIP +Module3=s +Setting3=sIdleTS +Module7=s +Module8=s +Module9=s +Module10=s +Module11=s +Module12=s +Module13=s +Module14=s +Module15=s +Module16=s +Module17=s +Module18=s +Module19=s +Module20=s +Module21=s +Setting21=sStatus +Module22=s +Setting22=sUIN +BorderCol=d6567708 +Setting7=sLogonTS +Setting8=sLogonTS +Setting9=sLogonTS +Setting10=sStatus0 +Setting11=sStatus1 +Setting12=sStatus2 +Setting13=sStatus3 +DILineAbove23=b0 +DIValNewline23=b0 +DILineAbove24=b0 +DIValNewline24=b0 +DILineAbove25=b1 +DIValNewline25=b1 +DILineAbove26=b1 +DIValNewline26=b1 +DILineAbove27=b1 +DIValNewline27=b1 +DILineAbove28=b1 +DIValNewline28=b0 +Setting14=sStatus4 +DILineAbove29=b0 +DIValNewline29=b0 +DILineAbove30=b0 +DIValNewline30=b0 +DILineAbove31=b0 +DIValNewline31=b0 +TitleLayout=b0 +Padding=w3 +Position=b1 +MinWidth=d300 +MinHeight=d0 +TransFuncId0=d5 +TransFuncId1=d7 +TransFuncId2=d7 +TransFuncId3=d3 +TransFuncId4=d14 +TransFuncId5=d12 +TransFuncId6=d1 +TransFuncId7=d3 +TransFuncId8=d14 +TransFuncId9=d12 +TransFuncId10=d1 +TransFuncId11=d1 +TransFuncId12=d1 +TransFuncId13=d1 +TransFuncId14=d1 +TransFuncId15=d1 +DILineAbove32=b0 +DIValNewline32=b0 +DILineAbove33=b0 +DIValNewline33=b0 +MaxWidth=d350 +MaxHeight=d400 +Opacity=b75 +DILabel21=uHumidity: +DILineAbove34=b0 +DIValNewline34=b0 +Module4=s +Setting15=sStatus5 +TransFuncId16=d1 +DILineAbove35=b0 +DIValNewline35=b0 +DILineAbove36=b0 +DIValNewline36=b0 +MouseTollerance=b16 +DILineAbove37=b0 +DIValNewline37=b0 +DILineAbove38=b0 +DIValNewline38=b0 +FontValues=uTahoma +FontLabels=uTahoma +FontFirst=uTahoma +xStatus: empty xStatus name to default name=d16 +DropShadow=b1 +RightValues=b0 +SBarTips=b1 +DividerCol=d10304821 +SidebarCol=d15391189 +AvatarRoundCorners=b0 +AvatarPadding=w6 +TextPadding=w4 +SidebarWidth=d22 +TransFuncId17=d1 +TransFuncId18=d1 +Name0=uGender +Module0=sUserInfo +Setting0=sGender +Name1=uicqIP +Setting1=sIP +Name2=uicqIPreal +DILabel20=uPressure: +DILabel39=uLast message: (%sys:last_msg_reltime% ago) +DIValue39=u%sys:last_msg% +DILineAbove39=b1 +DIValNewline39=b1 +DILabel0=uStatus: +DIValue0=u%Status% +DILabel4=uID: +DILabel1=uNick: +DIValue1=u%raw:/Nick% +DIValue29=u%raw:/CListName1% (%metaS1%) %raw:/Login1% +DILabel29=u%raw:/Protocol1%: +DILabel30=u%raw:/Protocol2%: +DILabel31=u%raw:/Protocol3%: +DILabel32=u%raw:/Protocol4%: +DIValue30=u%raw:/CListName2% (%metaS2%) %raw:/Login2% +DIValue31=u%raw:/CListName3% (%metaS3%) %raw:/Login3% +DIValue32=u%raw:/CListName4% (%metaS4%) %raw:/Login4% +DILabel38=u%raw:/Protocol9%: +DIValue38=u%raw:/CListName9% (%metaS9%) %raw:/Login9% +DILabel37=u%raw:/Protocol9%: +DIValue37=u%raw:/CListName9% (%metaS9%) %raw:/Login9% +DILabel35=u%raw:/Protocol7%: +DIValue35=u%raw:/CListName7% (%metaS7%) %raw:/Login7% +DILabel36=u%raw:/Protocol8%: +DIValue36=u%raw:/CListName8% (%metaS8%) %raw:/Login8% +DILabel33=u%raw:/Protocol5%: +DIValue33=u%raw:/CListName5% (%metaS5%) %raw:/Login5% +DILabel34=u%raw:/Protocol6%: +DIValue34=u%raw:/CListName6% (%metaS6%) %raw:/Login6% +FontFirstFlags=d288 +FontLabelsFlags=d288 +FontValuesFlags=d288 +DILabel16=uVisibility: +DILabel19=uHigh/Low: +DILabel28=u%raw:/Protocol0%: +DIValue28=u%raw:/CListName0% (%metaS0%) %raw:/Login0% +Name3=uidle_ago +Name4=uidle_date +Name5=uidle_time +Module5=s +Name6=ulastseen_status +Module6=sSeenModule +Setting6=sStatus +Setting16=sStatus6 +Setting4=sIdleTS +Setting5=sIdleTS +Name8=ulogon_date +Setting17=sStatus7 +Setting18=sStatus8 +TransFuncId19=d1 +DILabel13=uExtra status title: +DIValue13=u%raw:/XStatusName% +DILabel14=uExtra status message: +DIValue14=u%raw:/XStatusMsg% +DILabel15=uObservation time: +DIValue15=u%raw:Current/Update% +DIValue16=u%raw:Current/Visibility% +DILabel17=uFeels like: +DIValue17=u%raw:Current/Feel% +DILabel18=uWind: +DIValue18=u%raw:Current/Wind Direction% (%raw:Current/Wind Direction DEG%)/%raw:Current/Wind Speed% +DIValue19=u%raw:Current/High%/%raw:Current/Low% +DIValue20=u%raw:Current/Pressure% (%raw:Current/Pressure Tendency%) +DIValue21=u%raw:Current/Humidity% +DILabel22=uUV Index: +DIValue22=u%raw:Current/UV% - %raw:Current/UVI% +DILabel23=uSunrise/Sunset: +DIValue23=u%raw:Current/Sunrise%/%raw:Current/Sunset% +DILabel24=uMoonphase +DIValue24=u%raw:Current/Moon% +DILabel25=uTopic : +DIValue25=u%raw:/Topic% +DILabel26=uLast message: (%sys:last_msg_reltime% ago) +DIValue26=u%sys:last_msg% +DILabel27=uStatus message : +DIValue27=u%sys:status_msg% +Name9=ulogon_time +Setting19=sStatus9 +Name20=uStatus +Setting20=sStatus +TransFuncId20=d1 +DIValue8=u%raw:/MirVer% +DILabel11=uIdle: +DIValue11=u%idle_date% @ %idle_time% (%idle_ago%) +DILabel12=uLogon: +DIValue12=u%logon_date% @ %logon_time% (%logon_ago%) +Name7=ulogon_ago +DILabel8=uClient: +DILabel9=uIP: +DIValue9=u%icqIP% (%icqIPreal%) +DILabel10=uLast seen: +DIValue10=u%raw:SeenModule/Day%.%raw:SeenModule/Month%.%raw:SeenModule/Year% @ %raw:SeenModule/Hours%:%raw:SeenModule/Minutes%:%raw:SeenModule/Seconds% (%lastseen_status%) +DILabel2=uEmail: +DIValue2=u%raw:/e-mail% +DILabel3=uUIN: +DIValue3=u%raw:/UIN% +DIValue4=u%raw:/stid% +DILabel5=uID: +DIValue5=u%raw:/jid% +DILabel6=uGender: +DIValue6=u%Gender% +DILabel7=uBirthday: +DIValue7=u%raw:mBirthday/BirthDay%-%raw:mBirthday/BirthMonth%-%raw:mBirthday/BirthYear% ( %raw:UserInfo/Age% ) + + diff --git a/tipper/docs/licence_Tipper.txt b/tipper/docs/licence_Tipper.txt new file mode 100644 index 0000000..082744b --- /dev/null +++ b/tipper/docs/licence_Tipper.txt @@ -0,0 +1,6 @@ +The Tipper plugin for Miranda-IM is Copyright (c) 2006 Scott Ellis (mail@scottellis.com.au) + +http://www.scottellis.com.au + +It is released under the General Public Licence, available here: +http://www.gnu.org/copyleft/gpl.html \ No newline at end of file diff --git a/tipper/docs/m_tipper.h b/tipper/docs/m_tipper.h new file mode 100644 index 0000000..031d490 --- /dev/null +++ b/tipper/docs/m_tipper.h @@ -0,0 +1,23 @@ +// Tipper API +// note: Tipper is internally unicode and requires unicows.dll to function correctly on 95/98/ME +// so you'll find a lot of wchar_t stuff in here + +// use hContact, module and setting to read your db value(s) and put the resulting string into buff +// return buff if the translation was successful, or return 0 for failure +typedef wchar_t *(TranslateFunc)(HANDLE hContact, const char *module, const char *setting_or_prefix, wchar_t *buff, int bufflen); + +typedef struct { + TranslateFunc *tfunc; // address of your translation function (see typedef above) + const wchar_t *name; // make sure this is unique, and DO NOT translate it + DWORD id; // will be overwritten by Tipper - do not use +} DBVTranslation; + +// add a translation to tipper +// wParam not used +// lParam = (DBVTranslation *)translation +#define MS_TIPPER_ADDTRANSLATION "Tipper/AddTranslation" + +// unicode extension to the basic functionality +// wParam - optional (wchar_t *)text for text-only tips +// lParam - (CLCINFOTIP *)infoTip +#define MS_TIPPER_SHOWTIPW "mToolTip/ShowTipW" diff --git a/tipper/docs/readme_tipper.txt b/tipper/docs/readme_tipper.txt new file mode 100644 index 0000000..2ab3603 --- /dev/null +++ b/tipper/docs/readme_tipper.txt @@ -0,0 +1,143 @@ +Document updated: 28/9/06 + +****** +Tipper - shows a tooltip when you hover the mouse over a contact in your contact list +****** + +Most options are self explanitory...except for 'items' and 'substitutions'. + +If you want to set this up yourself, you need a moderate understanding of the miranda database (profile) and the Database Editor++ plugin. + +The easiest way is to copy the autoexec_tipper.ini file (in the same folder as this document) to the miranda program folder and restart - +it will (normally) ask you if you wish to import the settings. If you click yes, you will find several examples in your Tipper options that +will get you started. You can also ask your nerdier miranda-using buddies to create such a file for you, if they have a good setup. + +To get an idea of how tipper works, try playing with items. Items are simply a label and some text (the value). Try adding some items. Once +you've played around a bit you'll get the idea, and then you'll understand the need for substitutions. + +Substitutions allow you to put useful, contact related, information into the label or value of an item. To create a substitution you need +to have a relatively good understanding of the miranda database - a few hours browsing with dbeditor++ will give you a clue. You create a +substitution by specifying a name and the database module and setting names for the data it will contain. You can then put this data into +any item (label or value) by enclosing the substitution name in '%' symbols. Many database values are not terribly friendly to humans, so +when creating a substitution, you can also choose a translation which will convert the value into a more readable format. + +To get technical: + +A 'Substitution' is a name for a database value, represented according to a particular translation. When creating new substitutions, you +specify its name, the database module (or the contact's protocol module) and the setting name. Then you select a translation from the drop +down list. This transformation will convert the database value into a string. + +An 'Item' is just a label and a value. However, any substitution names (surrounded by % symbols) occuring in either a label or a value will +be substituted as described above. If you want to put a % symbol in a value or label, use '%%' instead. + +A good example is representing a contacts status (as 'Online' etc) in the tooltip. + +First, create a substitution called 'status' (without quotes) - the module will be the contact's protocol module, the setting name will be +'Status' (without quotes - note the capital 'S') and the translation will be 'WORD to status description' (since this value is a WORD value +in the database). Then, create an item and specify 'Status:' for the label and '%status%' for the value. Done. + +There is also a built in substitution, called a 'raw' substitution. It is not listed in the substitution list, but it is available in all +labels and values. It's format is: + +%raw:/% + +No translation is performed on 'raw' values. For example, to display a contact's contact list group in the tooltip, add an item with the +label 'Group:' and the value '%raw:CList/Group%'. If you do not specify a module name (you must still include the '/'), the contact's +protocol module will be used. This is ultimately just a shortcut for the 'null translation'. + +There are also 'system' substitutions (thanks to y_b), also not listed but available in all item labels and values, with the following +format: + +%sys:% + +Were name can be any of the following (as at 28/9/06): + +uid - contact's unique identifier +uidname - name of unique identifier +proto - contact's protocol +status_msg - contact's status message +last_msg - last message received from contact +last_msg_time - time of last received message +last_msg_date - date of last received message +last_msg_reltime - relative time of last message (i.e. time between then and now) +meta_subname - nickname of active subcontact (for metacontacts) +meta_subuid - unique id of active subcontact +meta_subproto - active subcontact protocol (also for metacontacts) + +If a substitution's value results in no text (or nothing but spaces, tabs, newlines, and carriage returns), the whole item containing that +substitution will not be displayed. If there is an error during the substitution process (e.g. a substitution name that does not exist, an odd +number of % symbols, etc) then the value of that substitution will be "*". Note that you can use double-percent ("%%") in plain text (not in +substitutions) if you want an actual percent symbol. + +ADVANCED +-------- + +Alternative Text: +In any substitution you can have 'alternate text' to use if the substitution fails (missing db value, etc). The format is: + +%x|y% + +where x is the original substitution and y is the alternative text. Note that you can use '|' in the alternative text, since it uses the +*first* occurence to determine where the alternative text begins. Normally if any substitution results in no value, the entire item will not +be displayed - but if you omit the 'y' value above (i.e. have nothing for the 'alternate' text) then the substitution process will continue. +As an example, consider the following item value: + +%raw:/FirstName|% %raw:/LastName% + +The above value will display the contact's first name if it's available, and then their last name - but if the last name is not available, the +entire item will not be displayed. + +Specific protocol: +If you end a substitution with '^' and a protocol name, then that substitution will only be displayed if the contact belongs to the given +protocol: + +%x^y% + +where y is the protocol name. If you want to display an item for every protocol *except* one, use + +%x^!y% + +If you use alternative text and specific protocol together, specify the alternative text first: + +%x|y^z% + +In such substitutions you can use a '^' symbol in the alternative text, as Tipper will take the *last* '^' symbol as the start of the protocol +specifier. If you want to use a '^' symbol in alternative text without a specific protocol, just append a '^' to the end of the descriptor, +e.g.: + +%x|y^% + + +'HIDDEN' DB SETTINGS +-------------------- + +Due to space constraints in the options page and my own laziness, the following settings are only accessible via the database (i.e. using dbeditor++): + +DWORD Tipper/MinHeight +DWORD Tipper/MinWidth +WORD Tipper/AvatarPadding +WORD Tipper/TextPadding (space between lines of text) +DWORD Tipper/SidebarWidth +BYTE Tipper/MouseTollerance (distance mouse can move while tip is shown, before it is automatically closed, in pixels) +BYTE Tipper/AvatarRoundCorners + + +******************** +'Variables' support: +******************** + +This plugin supports the variables plugin by UnregistereD (http://www.cs.vu.nl/~pboon/variables.zip) + +Be sure to use the unicode version! + +All text in 'Items' - that is, labels and values, will be processed by variables BEFORE Tipper substitutions are applied. + + +Good luck and have fun. + + +Scott +mail@scottellis.com.au +www.scottellis.com.au + +p.s. Thanks to Omniwolf and Tweety for compiling autoexec_Tipper(*).ini files diff --git a/tipper/m_tipper.h b/tipper/m_tipper.h new file mode 100644 index 0000000..81dcb59 --- /dev/null +++ b/tipper/m_tipper.h @@ -0,0 +1,19 @@ +// Tipper API +// note: Tipper is internally unicode and requires unicows.dll to function correctly on 95/98/ME +// so you'll find a lot of wchar_t stuff in here + +// translation function type +// use hContact, module and setting to read your db value(s) and put the resulting string into buff +// return buff if the translation was successful, or return 0 for failure +typedef wchar_t *(TranslateFunc)(HANDLE hContact, const char *module, const char *setting_or_prefix, wchar_t *buff, int bufflen); + +typedef struct { + TranslateFunc *tfunc; // address of your translation function (see typedef above) + const wchar_t *name; // make sure this is unique, and DO NOT translate it + DWORD id; // will be overwritten by Tipper - do not use +} DBVTranslation; + +// add a translation to tipper +// wParam not used +// lParam = (DBVTranslation *)translation +#define MS_TIPPER_ADDTRANSLATION "Tipper/AddTranslation" diff --git a/tipper/message_pump.cpp b/tipper/message_pump.cpp new file mode 100644 index 0000000..d891a33 --- /dev/null +++ b/tipper/message_pump.cpp @@ -0,0 +1,201 @@ +#include "common.h" +#include "message_pump.h" +#include "popwin.h" +#include "options.h" + +HMODULE hUserDll; +BOOL (WINAPI *MySetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD) = 0; +BOOL (WINAPI *MyAnimateWindow)(HWND hWnd,DWORD dwTime,DWORD dwFlags) = 0; + +//DWORD message_pump_thread_id = 0; +unsigned int message_pump_thread_id = 0; +HANDLE hMPEvent; + +//DWORD CALLBACK MessagePumpThread(LPVOID param) { +unsigned int CALLBACK MessagePumpThread(void *param) { + if(param) SetEvent((HANDLE)param); + + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + + HWND hwndTip = 0; + + MSG hwndMsg = {0}; + while(GetMessage(&hwndMsg, 0, 0, 0) > 0 && !Miranda_Terminated()) { + if(!IsDialogMessage(hwndMsg.hwnd, &hwndMsg)) { + switch(hwndMsg.message) { + case MUM_CREATEPOPUP: + { + if(hwndTip) DestroyWindow(hwndTip); + // if child of clist, zorder is right, but it steals the first click on a contact :( + + // copy topmost exstyle from clist, since we'll put clist under tip in WM_CREATE message + //LONG clist_exstyle = GetWindowLong((HWND)CallService(MS_CLUI_GETHWND, 0, 0), GWL_EXSTYLE); + //hwndTip = CreateWindowEx((clist_exstyle & WS_EX_TOPMOST), POP_WIN_CLASS, _T("TipperPopup"), WS_POPUP, 0, 0, 0, 0, 0/*(HWND)CallService(MS_CLUI_GETHWND, 0, 0)*/, 0, hInst, (LPVOID)hwndMsg.lParam); + + hwndTip = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, POP_WIN_CLASS, _T("TipperPopup"), WS_POPUP, 0, 0, 0, 0, 0/*(HWND)CallService(MS_CLUI_GETHWND, 0, 0)*/, 0, hInst, (LPVOID)hwndMsg.lParam); + if(hwndMsg.lParam) free((LPVOID)hwndMsg.lParam); + } + break; + + case MUM_DELETEPOPUP: + { + if(hwndTip) { + DestroyWindow(hwndTip); + hwndTip = 0; + } + } + break; + case MUM_GOTSTATUS: + { + if(hwndTip) SendMessage(hwndTip, PUM_SETSTATUSTEXT, hwndMsg.wParam, hwndMsg.lParam); + else if(hwndMsg.lParam) free((void *)hwndMsg.lParam); + } + break; + case MUM_GOTAVATAR: + { + if(hwndTip) SendMessage(hwndTip, PUM_SETAVATAR, hwndMsg.wParam, hwndMsg.lParam); + } + break; + default: + { + TranslateMessage(&hwndMsg); + DispatchMessage(&hwndMsg); + } + break; + } + } + } + + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + + return 0; +} + +void PostMPMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + PostThreadMessage(message_pump_thread_id, msg, wParam, lParam); +} + +// given a popup data pointer, and a handle to an event, this function +// will post a message to the message queue which will set the hwnd value +// and then set the event...so create an event, call this function and then wait on the event +// when the event is signalled, the hwnd will be valid +void FindWindow(POPUPDATAW *pd, HANDLE hEvent, HWND *hwnd); + +void InitMessagePump() { + WNDCLASS popup_win_class = {0}; + popup_win_class.style = 0; + popup_win_class.lpfnWndProc = PopupWindowProc; + popup_win_class.hInstance = hInst; + popup_win_class.lpszClassName = POP_WIN_CLASS; + popup_win_class.hCursor = LoadCursor(NULL, IDC_ARROW); + RegisterClass(&popup_win_class); + + hUserDll = LoadLibrary(_T("user32.dll")); + if (hUserDll) { + MySetLayeredWindowAttributes = (BOOL (WINAPI *)(HWND,COLORREF,BYTE,DWORD))GetProcAddress(hUserDll, "SetLayeredWindowAttributes"); + MyAnimateWindow=(BOOL (WINAPI*)(HWND,DWORD,DWORD))GetProcAddress(hUserDll,"AnimateWindow"); + } + + hMPEvent = CreateEvent(0, TRUE, 0, 0); + CloseHandle((HANDLE)_beginthreadex(0, 0, MessagePumpThread, hMPEvent, 0, &message_pump_thread_id)); + WaitForSingleObject(hMPEvent, INFINITE); + CloseHandle(hMPEvent); +} + +void DeinitMessagePump() { + PostMPMessage(WM_QUIT, 0, 0); + + FreeLibrary(hUserDll); + + UnregisterClass(POP_WIN_CLASS, hInst); +} + +int ShowTip(WPARAM wParam, LPARAM lParam) { + CLCINFOTIP *clcit = (CLCINFOTIP *)lParam; + if(clcit->isGroup) return 0; // no group tips (since they're pretty useless) + if(clcit->isTreeFocused == 0 && options.show_no_focus == false) return 0; + + CLCINFOTIPEX *clcit2 = (CLCINFOTIPEX *)malloc(sizeof(CLCINFOTIPEX)); + memcpy(clcit2, clcit, sizeof(CLCINFOTIP)); + clcit2->cbSize = sizeof(CLCINFOTIPEX); + clcit2->proto = 0; + clcit2->text = 0; + + if(wParam) { // wParam is char pointer containing text - e.g. status bar tooltip + int size = MultiByteToWideChar(code_page, 0, (char *)wParam, -1, 0, 0); + if(size) { + clcit2->text = (wchar_t *)malloc(size * sizeof(wchar_t)); + MultiByteToWideChar(code_page, 0, (char *)wParam, -1, clcit2->text, size); + GetCursorPos(&clcit2->ptCursor); // cursor pos broken? + } + } + + PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)clcit2); + return 1; +} + +int ShowTipW(WPARAM wParam, LPARAM lParam) { + CLCINFOTIP *clcit = (CLCINFOTIP *)lParam; + if(clcit->isGroup) return 0; // no group tips (since they're pretty useless) + if(clcit->isTreeFocused == 0 && options.show_no_focus == false) return 0; + + CLCINFOTIPEX *clcit2 = (CLCINFOTIPEX *)malloc(sizeof(CLCINFOTIPEX)); + memcpy(clcit2, clcit, sizeof(CLCINFOTIP)); + clcit2->cbSize = sizeof(CLCINFOTIPEX); + clcit2->proto = 0; + clcit2->text = 0; + + if(wParam) { // wParam is char pointer containing text - e.g. status bar tooltip + clcit2->text = _tcsdup((TCHAR *)wParam); + GetCursorPos(&clcit2->ptCursor); // cursor pos broken? + } + + PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)clcit2); + return 1; +} + +int HideTip(WPARAM wParam, LPARAM lParam) { + //CLCINFOTIP *clcit = (CLCINFOTIP *)lParam; + PostMPMessage(MUM_DELETEPOPUP, 0, 0); + return 1; +} + +int ProtoAck(WPARAM wParam, LPARAM lParam) { + ACKDATA *ack = (ACKDATA *)lParam; + char *szMsg = (char *)ack->lParam; + if(ack->type == ACKTYPE_AWAYMSG && ack->result == ACKRESULT_SUCCESS && szMsg) { + int size = MultiByteToWideChar(code_page, 0, szMsg, -1, 0, 0); + if(size > 1) { + wchar_t *msg = (wchar_t *)malloc(size * sizeof(wchar_t)); + MultiByteToWideChar(code_page, 0, (char *) szMsg, -1, msg, size); + PostMPMessage(MUM_GOTSTATUS, (WPARAM)ack->hContact, (LPARAM)msg); + } + } + return 0; +} + +int FramesShowSBTip(WPARAM wParam, LPARAM lParam) { + if(options.status_bar_tips) { + char *proto = (char *)wParam; + + CLCINFOTIPEX *clcit2 = (CLCINFOTIPEX *)malloc(sizeof(CLCINFOTIPEX)); + memset(clcit2, 0, sizeof(CLCINFOTIPEX)); + clcit2->cbSize = sizeof(CLCINFOTIPEX); + clcit2->proto = proto; // assume static string + GetCursorPos(&clcit2->ptCursor); + + PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)clcit2); + + return 1; + } + return 0; +} + +int FramesHideSBTip(WPARAM wParam, LPARAM lParam) { + if(options.status_bar_tips) { + PostMPMessage(MUM_DELETEPOPUP, 0, 0); + return 1; + } + return 0; +} + diff --git a/tipper/message_pump.h b/tipper/message_pump.h new file mode 100644 index 0000000..4ed5eed --- /dev/null +++ b/tipper/message_pump.h @@ -0,0 +1,41 @@ +#ifndef _MESSAGE_PUMP_INC +#define _MESSAGE_PUMP_INC + +//extern DWORD message_pump_thread_id; +extern unsigned int message_pump_thread_id; +void PostMPMessage(UINT msg, WPARAM, LPARAM); + +#define MUM_CREATEPOPUP (WM_USER + 0x011) +#define MUM_DELETEPOPUP (WM_USER + 0x012) +#define MUM_GOTSTATUS (WM_USER + 0x013) +#define MUM_GOTAVATAR (WM_USER + 0x014) + +extern HMODULE hUserDll; +extern BOOL (WINAPI *MySetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD); +extern BOOL (WINAPI *MyAnimateWindow)(HWND hWnd,DWORD dwTime,DWORD dwFlags); + +void InitMessagePump(); +void DeinitMessagePump(); + +int ShowTip(WPARAM wParam, LPARAM lParam); +int ShowTipW(WPARAM wParam, LPARAM lParam); +int HideTip(WPARAM wParam, LPARAM lParam); + +int FramesShowSBTip(WPARAM wParam, LPARAM lParam); +int FramesHideSBTip(WPARAM wParam, LPARAM lParam); + +int ProtoAck(WPARAM wParam, LPARAM lParam); + +typedef struct { + int cbSize; + int isTreeFocused; //so the plugin can provide an option + int isGroup; //0 if it's a contact, 1 if it's a group + HANDLE hItem; //handle to group or contact + POINT ptCursor; + RECT rcItem; + TCHAR *text; // for tips with specific text + char *proto; // for proto tips +} CLCINFOTIPEX; + + +#endif diff --git a/tipper/options.cpp b/tipper/options.cpp new file mode 100644 index 0000000..260ccbb --- /dev/null +++ b/tipper/options.cpp @@ -0,0 +1,1126 @@ +#include "common.h" +#include "options.h" +#include "resource.h" +#include "popwin.h" +#include + +Options options; + +#define WMU_ENABLE_LIST_BUTTONS (WM_USER + 0x030) +#define WMU_ENABLE_MODULE_ENTRY (WM_USER + 0x031) + +void CreateDefaultItems() { + DSListNode *ds_node; + DIListNode *di_node; + + // last message item + di_node = (DIListNode *)malloc(sizeof(DIListNode)); + _tcsncpy(di_node->di.label, _T("Last message: (%sys:last_msg_reltime% ago)"), LABEL_LEN); + _tcsncpy(di_node->di.value, _T("%sys:last_msg%"), VALUE_LEN); + di_node->di.line_above = di_node->di.value_newline = true; + di_node->next = options.di_list; + options.di_list = di_node; + options.di_count++; + + // status message item + di_node = (DIListNode *)malloc(sizeof(DIListNode)); + _tcsncpy(di_node->di.label, _T("Status message:"), LABEL_LEN); + _tcsncpy(di_node->di.value, _T("%sys:status_msg%"), VALUE_LEN); + di_node->di.line_above = di_node->di.value_newline = true; + di_node->next = options.di_list; + options.di_list = di_node; + options.di_count++; + + // status substitution + ds_node = (DSListNode *)malloc(sizeof(DSListNode)); + _tcsncpy(ds_node->ds.name, _T("status"), LABEL_LEN); + ds_node->ds.type = DVT_PROTODB; + strncpy(ds_node->ds.setting_name, "Status", SETTING_NAME_LEN); + ds_node->ds.translate_func_id = 1; + ds_node->next = options.ds_list; + options.ds_list = ds_node; + options.ds_count++; + + // status item + di_node = (DIListNode *)malloc(sizeof(DIListNode)); + _tcsncpy(di_node->di.label, _T("Status:"), LABEL_LEN); + _tcsncpy(di_node->di.value, _T("%status%"), VALUE_LEN); + di_node->di.line_above = di_node->di.value_newline = false; + di_node->next = options.di_list; + options.di_list = di_node; + options.di_count++; + + // client substitution + ds_node = (DSListNode *)malloc(sizeof(DSListNode)); + _tcsncpy(ds_node->ds.name, _T("client"), LABEL_LEN); + ds_node->ds.type = DVT_PROTODB; + strncpy(ds_node->ds.setting_name, "MirVer", SETTING_NAME_LEN); + ds_node->ds.translate_func_id = 0; + ds_node->next = options.ds_list; + options.ds_list = ds_node; + options.ds_count++; + + // client item + di_node = (DIListNode *)malloc(sizeof(DIListNode)); + _tcsncpy(di_node->di.label, _T("Client:"), LABEL_LEN); + _tcsncpy(di_node->di.value, _T("%client%"), VALUE_LEN); + di_node->di.line_above = di_node->di.value_newline = false; + di_node->next = options.di_list; + options.di_list = di_node; + options.di_count++; + + // idle time substitution (long date) + ds_node = (DSListNode *)malloc(sizeof(DSListNode)); + _tcsncpy(ds_node->ds.name, _T("idle"), LABEL_LEN); + ds_node->ds.type = DVT_PROTODB; + strncpy(ds_node->ds.setting_name, "IdleTS", SETTING_NAME_LEN); + ds_node->ds.translate_func_id = 15; + ds_node->next = options.ds_list; + options.ds_list = ds_node; + options.ds_count++; + + // idle time substitution (time difference) + ds_node = (DSListNode *)malloc(sizeof(DSListNode)); + _tcsncpy(ds_node->ds.name, _T("idle_diff"), LABEL_LEN); + ds_node->ds.type = DVT_PROTODB; + strncpy(ds_node->ds.setting_name, "IdleTS", SETTING_NAME_LEN); + ds_node->ds.translate_func_id = 3; + ds_node->next = options.ds_list; + options.ds_list = ds_node; + options.ds_count++; + + + // idle item + di_node = (DIListNode *)malloc(sizeof(DIListNode)); + _tcsncpy(di_node->di.label, _T("Idle:"), LABEL_LEN); + _tcsncpy(di_node->di.value, _T("%idle% (%idle_diff% ago)"), VALUE_LEN); + di_node->di.line_above = di_node->di.value_newline = false; + di_node->next = options.di_list; + options.di_list = di_node; + options.di_count++; + + // first name substitution + ds_node = (DSListNode *)malloc(sizeof(DSListNode)); + _tcsncpy(ds_node->ds.name, _T("first_name"), LABEL_LEN); + ds_node->ds.type = DVT_PROTODB; + strncpy(ds_node->ds.setting_name, "FirstName", SETTING_NAME_LEN); + ds_node->ds.translate_func_id = 0; + ds_node->next = options.ds_list; + options.ds_list = ds_node; + options.ds_count++; + + // last name substitution + ds_node = (DSListNode *)malloc(sizeof(DSListNode)); + _tcsncpy(ds_node->ds.name, _T("last_name"), LABEL_LEN); + ds_node->ds.type = DVT_PROTODB; + strncpy(ds_node->ds.setting_name, "LastName", SETTING_NAME_LEN); + ds_node->ds.translate_func_id = 0; + ds_node->next = options.ds_list; + options.ds_list = ds_node; + options.ds_count++; + + // name item + di_node = (DIListNode *)malloc(sizeof(DIListNode)); + _tcsncpy(di_node->di.label, _T("Name:"), LABEL_LEN); + _tcsncpy(di_node->di.value, _T("%first_name% %last_name%"), VALUE_LEN); + di_node->di.line_above = di_node->di.value_newline = false; + di_node->next = options.di_list; + options.di_list = di_node; + options.di_count++; + +} + +bool LoadDS(DisplaySubst *ds, int index) { + char setting[512]; + DBVARIANT dbv; + mir_snprintf(setting, 512, "Name%d", index); + ds->name[0] = 0; + if(!DBGetContactSettingWString(0, MODULE, setting, &dbv)) { + _tcsncpy(ds->name, dbv.pwszVal, LABEL_LEN); + DBFreeVariant(&dbv); + } else if(!DBGetContactSettingStringUtf(0, MODULE, setting, &dbv)) { + MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, ds->name, LABEL_LEN); + DBFreeVariant(&dbv); + } else if(!DBGetContactSetting(0, MODULE, setting, &dbv)) { + if(dbv.type == DBVT_ASCIIZ) MultiByteToWideChar(code_page, 0, dbv.pszVal, -1, ds->name, LABEL_LEN); + DBFreeVariant(&dbv); + } else + return false; + ds->name[LABEL_LEN - 1] = 0; + + mir_snprintf(setting, 512, "Type%d", index); + ds->type = (DisplaySubstType)DBGetContactSettingByte(0, MODULE, setting, DVT_PROTODB); + + mir_snprintf(setting, 512, "Module%d", index); + ds->module_name[0] = 0; + if(!DBGetContactSetting(0, MODULE, setting, &dbv)) { + strncpy(ds->module_name, dbv.pszVal, MODULE_NAME_LEN); + ds->module_name[MODULE_NAME_LEN - 1] = 0; + DBFreeVariant(&dbv); + } + + mir_snprintf(setting, 512, "Setting%d", index); + ds->setting_name[0] = 0; + if(!DBGetContactSetting(0, MODULE, setting, &dbv)) { + strncpy(ds->setting_name, dbv.pszVal, SETTING_NAME_LEN); + ds->setting_name[SETTING_NAME_LEN - 1] = 0; + DBFreeVariant(&dbv); + } + + mir_snprintf(setting, 512, "TransFuncId%d", index); + ds->translate_func_id = DBGetContactSettingDword(0, MODULE, setting, (DWORD)-1); + + // a little backward compatibility + if((DWORD)ds->translate_func_id == (DWORD)-1) { + mir_snprintf(setting, 512, "TransFunc%d", index); + ds->translate_func_id = (DWORD)DBGetContactSettingWord(0, MODULE, setting, 0); + } + return true; +} + +void SaveDS(DisplaySubst *ds, int index) { + char setting[512]; + mir_snprintf(setting, 512, "Name%d", index); + if(DBWriteContactSettingWString(0, MODULE, setting, ds->name)) { + char buff[LABEL_LEN]; + WideCharToMultiByte(code_page, 0, ds->name, -1, buff, LABEL_LEN, 0, 0); + buff[LABEL_LEN] = 0; + DBWriteContactSettingString(0, MODULE, setting, buff); + } + mir_snprintf(setting, 512, "Type%d", index); + DBWriteContactSettingByte(0, MODULE, setting, (BYTE)ds->type); + mir_snprintf(setting, 512, "Module%d", index); + DBWriteContactSettingString(0, MODULE, setting, ds->module_name); + mir_snprintf(setting, 512, "Setting%d", index); + DBWriteContactSettingString(0, MODULE, setting, ds->setting_name); + mir_snprintf(setting, 512, "TransFuncId%d", index); + DBWriteContactSettingDword(0, MODULE, setting, (WORD)ds->translate_func_id); +} + +bool LoadDI(DisplayItem *di, int index) { + char setting[512]; + DBVARIANT dbv; + mir_snprintf(setting, 512, "DILabel%d", index); + di->label[0] = 0; + if(!DBGetContactSettingWString(0, MODULE, setting, &dbv)) { + _tcsncpy(di->label, dbv.pwszVal, LABEL_LEN); + DBFreeVariant(&dbv); + } else if(!DBGetContactSettingStringUtf(0, MODULE, setting, &dbv)) { + MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, di->label, LABEL_LEN); + DBFreeVariant(&dbv); + } else if(!DBGetContactSetting(0, MODULE, setting, &dbv)) { + if(dbv.type == DBVT_ASCIIZ) MultiByteToWideChar(code_page, 0, dbv.pszVal, -1, di->label, LABEL_LEN); + DBFreeVariant(&dbv); + } else + return false; + di->label[LABEL_LEN - 1] = 0; + + mir_snprintf(setting, 512, "DIValue%d", index); + di->value[0] = 0; + if(!DBGetContactSettingWString(0, MODULE, setting, &dbv)) { + _tcsncpy(di->value, dbv.pwszVal, VALUE_LEN); + DBFreeVariant(&dbv); + } else if(!DBGetContactSettingStringUtf(0, MODULE, setting, &dbv)) { + MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, di->value, VALUE_LEN); + DBFreeVariant(&dbv); + } else if(!DBGetContactSetting(0, MODULE, setting, &dbv)) { + MultiByteToWideChar(code_page, 0, dbv.pszVal, -1, di->value, VALUE_LEN); + DBFreeVariant(&dbv); + } + + di->value[VALUE_LEN - 1] = 0; + + mir_snprintf(setting, 512, "DILineAbove%d", index); + di->line_above = (DBGetContactSettingByte(0, MODULE, setting, 0) == 1); + mir_snprintf(setting, 512, "DIValNewline%d", index); + di->value_newline = (DBGetContactSettingByte(0, MODULE, setting, 0) == 1); + + return true; +} + +void SaveDI(DisplayItem *di, int index) { + char setting[512]; + mir_snprintf(setting, 512, "DILabel%d", index); + if(DBWriteContactSettingWString(0, MODULE, setting, di->label)) { + char buff[LABEL_LEN]; + WideCharToMultiByte(code_page, 0, di->label, -1, buff, LABEL_LEN, 0, 0); + DBWriteContactSettingString(0, MODULE, setting, buff); + } + mir_snprintf(setting, 512, "DIValue%d", index); + if(DBWriteContactSettingWString(0, MODULE, setting, di->value)) { + char buff[VALUE_LEN]; + WideCharToMultiByte(code_page, 0, di->value, -1, buff, VALUE_LEN, 0, 0); + DBWriteContactSettingString(0, MODULE, setting, buff); + } + mir_snprintf(setting, 512, "DILineAbove%d", index); + DBWriteContactSettingByte(0, MODULE, setting, di->line_above ? 1 : 0); + mir_snprintf(setting, 512, "DIValNewline%d", index); + DBWriteContactSettingByte(0, MODULE, setting, di->value_newline ? 1 : 0); +} + +void SaveOptions() { + DBWriteContactSettingDword(0, MODULE, "MaxWidth", options.win_width); + DBWriteContactSettingDword(0, MODULE, "MaxHeight", options.win_max_height); + DBWriteContactSettingByte(0, MODULE, "Opacity", (BYTE)options.opacity); + DBWriteContactSettingByte(0, MODULE, "Border", (options.border ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "DropShadow", (options.drop_shadow ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "RoundCorners", (options.round ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "AvatarRoundCorners", (options.av_round ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "Animate", (options.animate ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "TransparentBg", (options.trans_bg ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "TitleLayout", (BYTE)options.title_layout); + if(ServiceExists(MS_AV_DRAWAVATAR)) + DBWriteContactSettingByte(0, MODULE, "AVLayout", (BYTE)options.av_layout); + DBWriteContactSettingDword(0, MODULE, "AVSize", options.av_size); + DBWriteContactSettingDword(0, MODULE, "TextIndent", options.text_indent); + DBWriteContactSettingByte(0, MODULE, "ShowNoFocus", (options.show_no_focus ? 1 : 0)); + + int index = 0; + DSListNode *ds_node = options.ds_list; + while(ds_node) { + SaveDS(&ds_node->ds, index); + ds_node = ds_node->next; + index++; + } + DBWriteContactSettingWord(0, MODULE, "DSNumValues", index); + + index = 0; + DIListNode *di_node = options.di_list; + while(di_node) { + SaveDI(&di_node->di, index); + di_node = di_node->next; + index++; + } + DBWriteContactSettingWord(0, MODULE, "DINumValues", index); + + DBWriteContactSettingWord(0, MODULE, "TimeIn", options.time_in); + CallService(MS_CLC_SETINFOTIPHOVERTIME, options.time_in, 0); + + DBWriteContactSettingWord(0, MODULE, "Padding", options.padding); + DBWriteContactSettingWord(0, MODULE, "AvatarPadding", options.av_padding); + DBWriteContactSettingWord(0, MODULE, "TextPadding", options.text_padding); + DBWriteContactSettingByte(0, MODULE, "Position", (BYTE)options.pos); + DBWriteContactSettingDword(0, MODULE, "MinWidth", (DWORD)options.min_width); + DBWriteContactSettingDword(0, MODULE, "MinHeight", (DWORD)options.min_height); + DBWriteContactSettingDword(0, MODULE, "SidebarWidth", (DWORD)options.sidebar_width); + DBWriteContactSettingByte(0, MODULE, "MouseTollerance", (BYTE)options.mouse_tollerance); + DBWriteContactSettingByte(0, MODULE, "SBarTips", (options.status_bar_tips ? 1 : 0)); + + DBWriteContactSettingWord(0, MODULE, "LabelVAlign", options.label_valign); + DBWriteContactSettingWord(0, MODULE, "LabelHAlign", options.label_halign); + DBWriteContactSettingWord(0, MODULE, "ValueVAlign", options.value_valign); + DBWriteContactSettingWord(0, MODULE, "ValueHAlign", options.value_halign); +} + +void LoadOptions() { + options.win_width = DBGetContactSettingDword(0, MODULE, "MaxWidth", 420); + options.win_max_height = DBGetContactSettingDword(0, MODULE, "MaxHeight", 400); + options.opacity = DBGetContactSettingByte(0, MODULE, "Opacity", 75); + options.border = (DBGetContactSettingByte(0, MODULE, "Border", 1) == 1); + options.drop_shadow = (DBGetContactSettingByte(0, MODULE, "DropShadow", 1) == 1); + options.round = (DBGetContactSettingByte(0, MODULE, "RoundCorners", 1) == 1); + options.av_round = (DBGetContactSettingByte(0, MODULE, "AvatarRoundCorners", options.round ? 1 : 0) == 1); + options.animate = (DBGetContactSettingByte(0, MODULE, "Animate", 0) == 1); + options.trans_bg = (DBGetContactSettingByte(0, MODULE, "TransparentBg", 0) == 1); + options.title_layout = (PopupTitleLayout)DBGetContactSettingByte(0, MODULE, "TitleLayout", (BYTE)PTL_LEFTICON); + if(ServiceExists(MS_AV_DRAWAVATAR)) + options.av_layout = (PopupAvLayout)DBGetContactSettingByte(0, MODULE, "AVLayout", PAV_RIGHT); + else + options.av_layout = PAV_NONE; + options.av_size = DBGetContactSettingDword(0, MODULE, "AVSize", 60); //tweety + options.text_indent = DBGetContactSettingDword(0, MODULE, "TextIndent", 22); + options.sidebar_width = DBGetContactSettingDword(0, MODULE, "SidebarWidth", 22); + options.show_no_focus = (DBGetContactSettingByte(0, MODULE, "ShowNoFocus", 1) == 1); + + int real_count = 0; + options.ds_list = 0; + DSListNode *ds_node; + options.ds_count = DBGetContactSettingWord(0, MODULE, "DSNumValues", 0); + for(int i = options.ds_count - 1; i >= 0; i--) { + ds_node = (DSListNode *)malloc(sizeof(DSListNode)); + if(LoadDS(&ds_node->ds, i)) { + ds_node->next = options.ds_list; + options.ds_list = ds_node; + real_count++; + } else free(ds_node); + } + options.ds_count = real_count; + + real_count = 0; + options.di_list = 0; + DIListNode *di_node; + options.di_count = DBGetContactSettingWord(0, MODULE, "DINumValues", 0); + for(int i = options.di_count - 1; i >= 0; i--) { + di_node = (DIListNode *)malloc(sizeof(DIListNode)); + if(LoadDI(&di_node->di, i)) { + di_node->next = options.di_list; + options.di_list = di_node; + real_count++; + } else free(di_node); + } + options.di_count = real_count; + + options.time_in = DBGetContactSettingWord(0, MODULE, "TimeIn", 750); + options.padding = DBGetContactSettingWord(0, MODULE, "Padding", 4); + options.av_padding = DBGetContactSettingWord(0, MODULE, "AvatarPadding", 6); + options.text_padding = DBGetContactSettingWord(0, MODULE, "TextPadding", 4); + options.pos = (PopupPosition)DBGetContactSettingByte(0, MODULE, "Position", (BYTE)PP_BOTTOMRIGHT); + options.min_width = DBGetContactSettingDword(0, MODULE, "MinWidth", 0); + options.min_height = DBGetContactSettingDword(0, MODULE, "MinHeight", 0); + + options.mouse_tollerance = DBGetContactSettingByte(0, MODULE, "MouseTollerance", (BYTE)GetSystemMetrics(SM_CXSMICON)); + options.status_bar_tips = (DBGetContactSettingByte(0, MODULE, "SBarTips", 1) == 1); + + // convert defunct last message and status message options to new 'sys' items, and remove the old settings + if(DBGetContactSettingByte(0, MODULE, "ShowLastMessage", 0)) { + DBDeleteContactSetting(0, MODULE, "ShowLastMessage"); + + // find end of list + di_node = options.di_list; + while(di_node && di_node->next) di_node = di_node->next; + + // last message item + if(di_node) { + di_node->next = (DIListNode *)malloc(sizeof(DIListNode)); + di_node = di_node->next; + } else { + options.di_list = (DIListNode *)malloc(sizeof(DIListNode)); + di_node = options.di_list; + } + + _tcsncpy(di_node->di.label, _T("Last message: (%sys:last_msg_reltime% ago)"), LABEL_LEN); + _tcsncpy(di_node->di.value, _T("%sys:last_msg%"), VALUE_LEN); + di_node->di.line_above = di_node->di.value_newline = true; + di_node->next = 0; + options.di_count++; + } + + if(DBGetContactSettingByte(0, MODULE, "ShowStatusMessage", 0)) { + DBDeleteContactSetting(0, MODULE, "ShowStatusMessage"); + + // find end of list + di_node = options.di_list; + while(di_node && di_node->next) di_node = di_node->next; + + // status message item + if(di_node) { + di_node->next = (DIListNode *)malloc(sizeof(DIListNode)); + di_node = di_node->next; + } else { + options.di_list = (DIListNode *)malloc(sizeof(DIListNode)); + di_node = options.di_list; + } + + _tcsncpy(di_node->di.label, _T("Status message:"), LABEL_LEN); + _tcsncpy(di_node->di.value, _T("%sys:status_msg%"), VALUE_LEN); + di_node->di.line_above = di_node->di.value_newline = true; + di_node->next = 0; + options.di_count++; + } + + options.label_valign = DBGetContactSettingWord(0, MODULE, "LabelVAlign", DT_TOP /*DT_VCENTER*/); + options.label_halign = DBGetContactSettingWord(0, MODULE, "LabelHAlign", DT_LEFT); + options.value_valign = DBGetContactSettingWord(0, MODULE, "ValueVAlign", DT_TOP /*DT_VCENTER*/); + options.value_halign = DBGetContactSettingWord(0, MODULE, "ValueHAlign", DT_LEFT); + + if(options.ds_count == 0 && options.di_count == 0 && DBGetContactSettingByte(0, MODULE, "DefaultsCreated", 0) == 0) { + // set up some reasonable defaults - but only 'once' + CreateDefaultItems(); + DBWriteContactSettingByte(0, MODULE, "DefaultsCreated", 1); + SaveOptions(); + } + +} + +static BOOL CALLBACK DlgProcAddItem(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + DisplayItem *di = (DisplayItem *)GetWindowLong(hwndDlg, GWL_USERDATA); + + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + di = (DisplayItem *)lParam; + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG)di); + + SetDlgItemText(hwndDlg, IDC_ED_LABEL, di->label); + SetDlgItemText(hwndDlg, IDC_ED_VALUE, di->value); + + CheckDlgButton(hwndDlg, IDC_CHK_LINEABOVE, di->line_above ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_VALNEWLINE, di->value_newline ? TRUE : FALSE); + SetFocus(GetDlgItem(hwndDlg, IDC_ED_LABEL)); + return TRUE; + case WM_COMMAND: + if(HIWORD(wParam) == BN_CLICKED) { + switch(LOWORD(wParam)) { + case IDOK: + GetDlgItemText(hwndDlg, IDC_ED_LABEL, di->label, LABEL_LEN); + GetDlgItemText(hwndDlg, IDC_ED_VALUE, di->value, VALUE_LEN); + + di->line_above = (IsDlgButtonChecked(hwndDlg, IDC_CHK_LINEABOVE) ? true : false); + di->value_newline = (IsDlgButtonChecked(hwndDlg, IDC_CHK_VALNEWLINE) ? true : false); + + EndDialog(hwndDlg, IDOK); + return TRUE; + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + return TRUE; + } + } + break; + } + + return 0; +} + +static BOOL CALLBACK DlgProcAddSubst(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + DisplaySubst *ds = (DisplaySubst *)GetWindowLong(hwndDlg, GWL_USERDATA); + + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + ds = (DisplaySubst *)lParam; + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG)ds); + + SetDlgItemText(hwndDlg, IDC_ED_LABEL, ds->name); + + switch(ds->type) { + case DVT_PROTODB: + CheckDlgButton(hwndDlg, IDC_CHK_PROTOMOD, TRUE); + SetDlgItemTextA(hwndDlg, IDC_ED_SETTING, ds->setting_name); + break; + case DVT_DB: + SetDlgItemTextA(hwndDlg, IDC_ED_MODULE, ds->module_name); + SetDlgItemTextA(hwndDlg, IDC_ED_SETTING, ds->setting_name); + break; + } + + { + int index, id; + for(int i = 0; i < num_tfuncs; i++) { + index = SendDlgItemMessage(hwndDlg, IDC_CMB_TRANSLATE, CB_ADDSTRING, (WPARAM)-1, (LPARAM)TranslateTS(translations[i].name)); + SendDlgItemMessage(hwndDlg, IDC_CMB_TRANSLATE, CB_SETITEMDATA, index, (LPARAM)translations[i].id); + } + for(int i = 0; i < num_tfuncs; i++) { + id = SendDlgItemMessage(hwndDlg, IDC_CMB_TRANSLATE, CB_GETITEMDATA, i, 0); + if(id == ds->translate_func_id) + SendDlgItemMessage(hwndDlg, IDC_CMB_TRANSLATE, CB_SETCURSEL, i, 0); + } + + } + + SendMessage(hwndDlg, WMU_ENABLE_MODULE_ENTRY, 0, 0); + SetFocus(GetDlgItem(hwndDlg, IDC_ED_LABEL)); + return TRUE; + case WMU_ENABLE_MODULE_ENTRY: + { + HWND hw = GetDlgItem(hwndDlg, IDC_CHK_PROTOMOD); + EnableWindow(hw, TRUE); + hw = GetDlgItem(hwndDlg, IDC_ED_MODULE); + EnableWindow(hw, !IsDlgButtonChecked(hwndDlg, IDC_CHK_PROTOMOD)); + hw = GetDlgItem(hwndDlg, IDC_ED_SETTING); + EnableWindow(hw, TRUE); + } + return TRUE; + case WM_COMMAND: + if ( HIWORD( wParam ) == CBN_SELCHANGE) { + return TRUE; + } else if(HIWORD(wParam) == BN_CLICKED) { + switch(LOWORD(wParam)) { + case IDC_CHK_PROTOMOD: + SendMessage(hwndDlg, WMU_ENABLE_MODULE_ENTRY, 0, 0); + break; + case IDOK: + GetDlgItemText(hwndDlg, IDC_ED_LABEL, ds->name, LABEL_LEN); + if(ds->name[0] == 0) { + MessageBox(hwndDlg, TranslateT("You must enter a label"), TranslateT("Invalid Substitution"), MB_OK | MB_ICONWARNING); + return TRUE; + } + + if(IsDlgButtonChecked(hwndDlg, IDC_CHK_PROTOMOD)) + ds->type = DVT_PROTODB; + else { + ds->type = DVT_DB; + GetDlgItemTextA(hwndDlg, IDC_ED_MODULE, ds->module_name, MODULE_NAME_LEN); + } + GetDlgItemTextA(hwndDlg, IDC_ED_SETTING, ds->setting_name, SETTING_NAME_LEN); + + { + int sel = SendDlgItemMessage(hwndDlg, IDC_CMB_TRANSLATE, CB_GETCURSEL, 0, 0); + ds->translate_func_id = SendDlgItemMessage(hwndDlg, IDC_CMB_TRANSLATE, CB_GETITEMDATA, sel, 0); + } + + EndDialog(hwndDlg, IDOK); + return TRUE; + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + return TRUE; + } + } + break; + } + + return 0; +} + +static BOOL CALLBACK DlgProcOpts2(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + { + int index; + DIListNode *di_node = options.di_list, *di_value; + while(di_node) { + di_value = (DIListNode *)malloc(sizeof(DIListNode)); + *di_value = *di_node; + if(di_value->di.label[0] == 0) + index = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_ADDSTRING, 0, (LPARAM)TranslateT("")); + else + index = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_ADDSTRING, 0, (LPARAM)di_value->di.label); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETITEMDATA, index, (LPARAM)di_value); + + di_node = di_node->next; + } + + DSListNode *ds_node = options.ds_list, *ds_value; + while(ds_node) { + ds_value = (DSListNode *)malloc(sizeof(DSListNode)); + *ds_value = *ds_node; + index = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_ADDSTRING, 0, (LPARAM)ds_value->ds.name); + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_SETITEMDATA, index, (LPARAM)ds_value); + + ds_node = ds_node->next; + } + } + + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + return FALSE; + case WMU_ENABLE_LIST_BUTTONS: + { + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCURSEL, 0, 0); + if(sel == -1) { + HWND hw = GetDlgItem(hwndDlg, IDC_BTN_REMOVE); + EnableWindow(hw, FALSE); + hw = GetDlgItem(hwndDlg, IDC_BTN_UP); + EnableWindow(hw, FALSE); + hw = GetDlgItem(hwndDlg, IDC_BTN_DOWN); + EnableWindow(hw, FALSE); + hw = GetDlgItem(hwndDlg, IDC_BTN_EDIT); + EnableWindow(hw, FALSE); + } else { + int count = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCOUNT, 0, 0); + HWND hw = GetDlgItem(hwndDlg, IDC_BTN_REMOVE); + EnableWindow(hw, TRUE); + hw = GetDlgItem(hwndDlg, IDC_BTN_UP); + EnableWindow(hw, sel > 0); + hw = GetDlgItem(hwndDlg, IDC_BTN_DOWN); + EnableWindow(hw, sel < count - 1); + hw = GetDlgItem(hwndDlg, IDC_BTN_EDIT); + EnableWindow(hw, TRUE); + } + + sel = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETCURSEL, 0, 0); + if(sel == -1) { + HWND hw = GetDlgItem(hwndDlg, IDC_BTN_REMOVE2); + EnableWindow(hw, FALSE); + hw = GetDlgItem(hwndDlg, IDC_BTN_EDIT2); + EnableWindow(hw, FALSE); + } else { + HWND hw = GetDlgItem(hwndDlg, IDC_BTN_REMOVE2); + EnableWindow(hw, TRUE); + hw = GetDlgItem(hwndDlg, IDC_BTN_EDIT2); + EnableWindow(hw, TRUE); + } + } + return TRUE; + case WM_COMMAND: + if ( HIWORD( wParam ) == LBN_SELCHANGE && LOWORD(wParam) == IDC_LST_ITEMS) { + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + } else if ( HIWORD( wParam ) == LBN_SELCHANGE && LOWORD(wParam) == IDC_LST_SUBST) { + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + } else if ( HIWORD( wParam ) == CBN_SELCHANGE) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } else if ( HIWORD( wParam ) == EN_CHANGE && ( HWND )lParam == GetFocus()) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } else if ( HIWORD( wParam) == LBN_DBLCLK && LOWORD(wParam) == IDC_LST_ITEMS) { + { + DIListNode *value; + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCURSEL, 0, 0); + if(sel != -1) { + value = (DIListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETITEMDATA, sel, 0); + if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ITEM), hwndDlg, DlgProcAddItem, (LPARAM)&value->di) == IDOK) { + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_DELETESTRING, (WPARAM)sel, 0); + + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_INSERTSTRING, sel, (LPARAM)value->di.label); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETITEMDATA, sel, (LPARAM)value); + + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETCURSEL, sel, 0); + + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + } + } else if ( HIWORD( wParam) == LBN_DBLCLK && LOWORD(wParam) == IDC_LST_SUBST) { + { + DSListNode *value; + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETCURSEL, 0, 0); + if(sel != -1) { + value = (DSListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETITEMDATA, sel, 0); + if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SUBST), hwndDlg, DlgProcAddSubst, (LPARAM)&value->ds) == IDOK) { + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_DELETESTRING, (WPARAM)sel, 0); + + sel = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_ADDSTRING, 0, (LPARAM)value->ds.name); + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_SETITEMDATA, sel, (LPARAM)value); + + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_SETCURSEL, sel, 0); + + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + } + } else if ( HIWORD( wParam ) == BN_CLICKED ) { + switch(LOWORD(wParam)) { + case IDC_BTN_ADD: + { + DIListNode *value = (DIListNode *)malloc(sizeof(DIListNode)); + memset(value, 0, sizeof(DIListNode)); + if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ITEM), hwndDlg, DlgProcAddItem, (LPARAM)&value->di) == IDOK) { + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCURSEL, 0, 0), index = sel + 1; + if(value->di.label[0] == 0) + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_INSERTSTRING, index, (LPARAM)TranslateT("")); + else + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_INSERTSTRING, index, (LPARAM)value->di.label); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETITEMDATA, index, (LPARAM)value); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETCURSEL, index, 0); + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + + } + return TRUE; + case IDC_BTN_REMOVE: + { + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCURSEL, 0, 0); + if(sel != -1) { + DIListNode *value = (DIListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETITEMDATA, sel, 0); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_DELETESTRING, (WPARAM)sel, 0); + free(value); + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + return TRUE; + case IDC_BTN_UP: + { + DIListNode *value_up; + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCURSEL, 0, 0); + if(sel > 0) { + value_up = (DIListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETITEMDATA, sel - 1, 0); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_DELETESTRING, (WPARAM)sel - 1, 0); + + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_INSERTSTRING, sel, (LPARAM)value_up->di.label); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETITEMDATA, sel, (LPARAM)value_up); + + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETCURSEL, sel - 1, 0); + } + } + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + return TRUE; + case IDC_BTN_DOWN: + { + DIListNode *value_down; + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCURSEL, 0, 0); + int count = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCOUNT, 0, 0); + if(sel < count - 1) { + value_down = (DIListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETITEMDATA, sel + 1, 0); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_DELETESTRING, (WPARAM)sel + 1, 0); + + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_INSERTSTRING, sel, (LPARAM)value_down->di.label); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETITEMDATA, sel, (LPARAM)value_down); + + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETCURSEL, sel + 1, 0); + } + } + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + return TRUE; + case IDC_BTN_EDIT: + { + DIListNode *value; + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCURSEL, 0, 0); + if(sel != -1) { + value = (DIListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETITEMDATA, sel, 0); + if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ITEM), hwndDlg, DlgProcAddItem, (LPARAM)&value->di) == IDOK) { + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_DELETESTRING, (WPARAM)sel, 0); + + if(value->di.label[0] == 0) + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_INSERTSTRING, sel, (LPARAM)TranslateT("")); + else + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_INSERTSTRING, sel, (LPARAM)value->di.label); + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETITEMDATA, sel, (LPARAM)value); + + SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_SETCURSEL, sel, 0); + + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + } + return TRUE; + + case IDC_BTN_ADD2: + { + DSListNode *value = (DSListNode *)malloc(sizeof(DSListNode)); + memset(value, 0, sizeof(DSListNode)); + if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SUBST), hwndDlg, DlgProcAddSubst, (LPARAM)&value->ds) == IDOK) { + int index = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_ADDSTRING, 0, (LPARAM)value->ds.name); + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_SETITEMDATA, index, (LPARAM)value); + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_SETCURSEL, index, 0); + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + + } + return TRUE; + case IDC_BTN_REMOVE2: + { + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETCURSEL, 0, 0); + if(sel != -1) { + DSListNode *value = (DSListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETITEMDATA, sel, 0); + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_DELETESTRING, (WPARAM)sel, 0); + free(value); + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + return TRUE; + case IDC_BTN_EDIT2: + { + DSListNode *value; + int sel = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETCURSEL, 0, 0); + if(sel != -1) { + value = (DSListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETITEMDATA, sel, 0); + if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SUBST), hwndDlg, DlgProcAddSubst, (LPARAM)&value->ds) == IDOK) { + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_DELETESTRING, (WPARAM)sel, 0); + + sel = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_ADDSTRING, 0, (LPARAM)value->ds.name); + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_SETITEMDATA, sel, (LPARAM)value); + + SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_SETCURSEL, sel, 0); + + SendMessage(hwndDlg, WMU_ENABLE_LIST_BUTTONS, 0, 0); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + } + return TRUE; + default: + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + return TRUE; + } + } + break; + case WM_NOTIFY: + if (((LPNMHDR)lParam)->code == (unsigned)PSN_APPLY ) { + DIListNode *di_node; + while(options.di_list) { + di_node = options.di_list; + options.di_list = options.di_list->next; + free(di_node); + } + + DIListNode *di_value; + options.di_count = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCOUNT, 0, 0); + for(int i = options.di_count - 1; i >= 0; i--) { + di_node = (DIListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETITEMDATA, i, 0); + di_value = (DIListNode *)malloc(sizeof(DIListNode)); + *di_value = *di_node; + + di_value->next = options.di_list; + options.di_list = di_value; + } + + DSListNode *ds_node; + while(options.ds_list) { + ds_node = options.ds_list; + options.ds_list = options.ds_list->next; + free(ds_node); + } + + DSListNode *ds_value; + options.ds_count = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETCOUNT, 0, 0); + for(int i = options.ds_count - 1; i >= 0; i--) { + ds_node = (DSListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETITEMDATA, i, 0); + ds_value = (DSListNode *)malloc(sizeof(DSListNode)); + *ds_value = *ds_node; + + ds_value->next = options.ds_list; + options.ds_list = ds_value; + } + + SaveOptions(); + return TRUE; + } + break; + case WM_DESTROY: + { + DIListNode *di_value; + int count = SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETCOUNT, 0, 0); + for(int i = 0; i < count; i++) { + di_value = (DIListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_ITEMS, LB_GETITEMDATA, i, 0); + free(di_value); + } + DSListNode *ds_value; + count = SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETCOUNT, 0, 0); + for(int i = 0; i < count; i++) { + ds_value = (DSListNode *)SendDlgItemMessage(hwndDlg, IDC_LST_SUBST, LB_GETITEMDATA, i, 0); + free(ds_value); + } + } + break; + } + + return 0; +} + + +static BOOL CALLBACK DlgProcOpts1(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + + CheckDlgButton(hwndDlg, IDC_CHK_NOFOCUS, options.show_no_focus ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_SBAR, options.status_bar_tips ? TRUE : FALSE); + + SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_ADDSTRING, 0, (LPARAM)TranslateT("Icon on left")); + SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_ADDSTRING, 0, (LPARAM)TranslateT("Icon on right")); + SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_ADDSTRING, 0, (LPARAM)TranslateT("No icon")); + SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_ADDSTRING, 0, (LPARAM)TranslateT("No title")); + SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_SETCURSEL, (int)options.title_layout, 0); + + SendDlgItemMessage(hwndDlg, IDC_CMB_POS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Bottom right")); + SendDlgItemMessage(hwndDlg, IDC_CMB_POS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Bottom left")); + SendDlgItemMessage(hwndDlg, IDC_CMB_POS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Top right")); + SendDlgItemMessage(hwndDlg, IDC_CMB_POS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Top left")); + SendDlgItemMessage(hwndDlg, IDC_CMB_POS, CB_SETCURSEL, (int)options.pos, 0); + + SendDlgItemMessage(hwndDlg, IDC_CMB_LV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Top")); + SendDlgItemMessage(hwndDlg, IDC_CMB_LV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Centre")); + SendDlgItemMessage(hwndDlg, IDC_CMB_LV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Bottom")); + switch(options.label_valign) { + case DT_TOP: SendDlgItemMessage(hwndDlg, IDC_CMB_LV, CB_SETCURSEL, 0, 0); break; + case DT_VCENTER: SendDlgItemMessage(hwndDlg, IDC_CMB_LV, CB_SETCURSEL, 1, 0); break; + case DT_BOTTOM: SendDlgItemMessage(hwndDlg, IDC_CMB_LV, CB_SETCURSEL, 2, 0); break; + } + + SendDlgItemMessage(hwndDlg, IDC_CMB_VV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Top")); + SendDlgItemMessage(hwndDlg, IDC_CMB_VV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Centre")); + SendDlgItemMessage(hwndDlg, IDC_CMB_VV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Bottom")); + switch(options.value_valign) { + case DT_TOP: SendDlgItemMessage(hwndDlg, IDC_CMB_VV, CB_SETCURSEL, 0, 0); break; + case DT_VCENTER: SendDlgItemMessage(hwndDlg, IDC_CMB_VV, CB_SETCURSEL, 1, 0); break; + case DT_BOTTOM: SendDlgItemMessage(hwndDlg, IDC_CMB_VV, CB_SETCURSEL, 2, 0); break; + } + + SendDlgItemMessage(hwndDlg, IDC_CMB_LH, CB_ADDSTRING, 0, (LPARAM)TranslateT("Left")); + SendDlgItemMessage(hwndDlg, IDC_CMB_LH, CB_ADDSTRING, 0, (LPARAM)TranslateT("Right")); + switch(options.label_halign) { + case DT_LEFT: SendDlgItemMessage(hwndDlg, IDC_CMB_LH, CB_SETCURSEL, 0, 0); break; + case DT_RIGHT: SendDlgItemMessage(hwndDlg, IDC_CMB_LH, CB_SETCURSEL, 1, 0); break; + } + + SendDlgItemMessage(hwndDlg, IDC_CMB_VH, CB_ADDSTRING, 0, (LPARAM)TranslateT("Left")); + SendDlgItemMessage(hwndDlg, IDC_CMB_VH, CB_ADDSTRING, 0, (LPARAM)TranslateT("Right")); + switch(options.value_halign) { + case DT_LEFT: SendDlgItemMessage(hwndDlg, IDC_CMB_VH, CB_SETCURSEL, 0, 0); break; + case DT_RIGHT: SendDlgItemMessage(hwndDlg, IDC_CMB_VH, CB_SETCURSEL, 1, 0); break; + } + + SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_ADDSTRING, 0, (LPARAM)TranslateT("No avatar")); + if(ServiceExists(MS_AV_DRAWAVATAR)) { + SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Left avatar")); + SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Right avatar")); + } else { + HWND hw = GetDlgItem(hwndDlg, IDC_CMB_AV); + EnableWindow(hw, FALSE); + hw = GetDlgItem(hwndDlg, IDC_SPIN_AVSIZE); + EnableWindow(hw, FALSE); + hw = GetDlgItem(hwndDlg, IDC_ED_AVSIZE); + EnableWindow(hw, FALSE); + } + SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_SETCURSEL, (int)options.av_layout, 0); + + SendDlgItemMessage(hwndDlg, IDC_SPIN_WIDTH, UDM_SETRANGE, 0, (LPARAM)MAKELONG(2048, 16)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_MINWIDTH, UDM_SETRANGE, 0, (LPARAM)MAKELONG(2048, 16)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_MAXHEIGHT, UDM_SETRANGE, 0, (LPARAM)MAKELONG(2048, 16)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_MINHEIGHT, UDM_SETRANGE, 0, (LPARAM)MAKELONG(2048, 16)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_TRANS, UDM_SETRANGE, 0, (LPARAM)MAKELONG(99, 0)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_AVSIZE, UDM_SETRANGE, 0, (LPARAM)MAKELONG(100, 16)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_INDENT, UDM_SETRANGE, 0, (LPARAM)MAKELONG(400, 0)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_PADDING, UDM_SETRANGE, 0, (LPARAM)MAKELONG(128, 0)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_TEXTPADDING, UDM_SETRANGE, 0, (LPARAM)MAKELONG(128, 0)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_AVPADDING, UDM_SETRANGE, 0, (LPARAM)MAKELONG(128, 0)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_HOVER, UDM_SETRANGE, 0, (LPARAM)MAKELONG(5000, 5)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_SBWIDTH, UDM_SETRANGE, 0, (LPARAM)MAKELONG(2048, 0)); + + SetDlgItemInt(hwndDlg, IDC_ED_WIDTH, options.win_width, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_MAXHEIGHT, options.win_max_height, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_MINWIDTH, options.min_width, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_MINHEIGHT, options.min_height, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_AVSIZE, options.av_size, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_INDENT, options.text_indent, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_PADDING, options.padding, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_TEXTPADDING, options.text_padding, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_AVPADDING, options.av_padding, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_HOVER, options.time_in, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_SBWIDTH, options.sidebar_width, FALSE); + + SetDlgItemInt(hwndDlg, IDC_ED_TRANS, options.opacity, FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_BORDER, options.border); + CheckDlgButton(hwndDlg, IDC_CHK_ROUNDCORNERS, options.round); + CheckDlgButton(hwndDlg, IDC_CHK_ROUNDCORNERSAV, options.av_round); + + CheckDlgButton(hwndDlg, IDC_CHK_ANIMATE, options.animate); + CheckDlgButton(hwndDlg, IDC_CHK_SHADOW, options.drop_shadow); + CheckDlgButton(hwndDlg, IDC_CHK_TRANSBG, options.trans_bg); + + return FALSE; + case WM_COMMAND: + if ( HIWORD( wParam ) == CBN_SELCHANGE) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } else if ( HIWORD( wParam ) == EN_CHANGE && ( HWND )lParam == GetFocus()) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } else if ( HIWORD( wParam ) == BN_CLICKED ) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + break; + case WM_NOTIFY: + if (((LPNMHDR)lParam)->code == (unsigned)PSN_APPLY ) { + BOOL trans; + int new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_WIDTH, &trans, FALSE); + if(trans) options.win_width = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_MINWIDTH, &trans, FALSE); + if(trans) options.min_width = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_MAXHEIGHT, &trans, FALSE); + if(trans) options.win_max_height = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_MINHEIGHT, &trans, FALSE); + if(trans) options.min_height = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_AVSIZE, &trans, FALSE); + if(trans) options.av_size = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_INDENT, &trans, FALSE); + if(trans) options.text_indent = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_PADDING, &trans, FALSE); + if(trans) options.padding = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_TEXTPADDING, &trans, FALSE); + if(trans) options.text_padding = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_AVPADDING, &trans, FALSE); + if(trans) options.av_padding = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_HOVER, &trans, FALSE); + if(trans) options.time_in = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_SBWIDTH, &trans, FALSE); + if(trans) options.sidebar_width = new_val; + + options.title_layout = (PopupTitleLayout)SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_GETCURSEL, 0, 0); + options.av_layout = (PopupAvLayout)SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_GETCURSEL, 0, 0); + options.pos = (PopupPosition)SendDlgItemMessage(hwndDlg, IDC_CMB_POS, CB_GETCURSEL, 0, 0); + + new_val = GetDlgItemInt(hwndDlg, IDC_ED_TRANS, &trans, FALSE); + if(trans) options.opacity = new_val; + options.border = IsDlgButtonChecked(hwndDlg, IDC_CHK_BORDER) && IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CHK_BORDER)) ? true : false; + options.round = IsDlgButtonChecked(hwndDlg, IDC_CHK_ROUNDCORNERS) && IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CHK_ROUNDCORNERS)) ? true : false; + options.av_round = IsDlgButtonChecked(hwndDlg, IDC_CHK_ROUNDCORNERSAV) && IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CHK_ROUNDCORNERSAV)) ? true : false; + options.animate = IsDlgButtonChecked(hwndDlg, IDC_CHK_ANIMATE) ? true : false; + options.drop_shadow = IsDlgButtonChecked(hwndDlg, IDC_CHK_SHADOW) ? true : false; + options.trans_bg = IsDlgButtonChecked(hwndDlg, IDC_CHK_TRANSBG) ? true : false; + + options.show_no_focus = IsDlgButtonChecked(hwndDlg, IDC_CHK_NOFOCUS) ? true : false; + options.status_bar_tips = IsDlgButtonChecked(hwndDlg, IDC_CHK_SBAR) ? true : false; + + switch(SendDlgItemMessage(hwndDlg, IDC_CMB_LV, CB_GETCURSEL, 0, 0)) { + case 0: options.label_valign = DT_TOP; break; + case 1: options.label_valign = DT_VCENTER; break; + case 2: options.label_valign = DT_BOTTOM; break; + } + switch(SendDlgItemMessage(hwndDlg, IDC_CMB_VV, CB_GETCURSEL, 0, 0)) { + case 0: options.value_valign = DT_TOP; break; + case 1: options.value_valign = DT_VCENTER; break; + case 2: options.value_valign = DT_BOTTOM; break; + } + + switch(SendDlgItemMessage(hwndDlg, IDC_CMB_LH, CB_GETCURSEL, 0, 0)) { + case 0: options.label_halign = DT_LEFT; break; + case 1: options.label_halign = DT_RIGHT; break; + } + switch(SendDlgItemMessage(hwndDlg, IDC_CMB_VH, CB_GETCURSEL, 0, 0)) { + case 0: options.value_halign = DT_LEFT; break; + case 1: options.value_halign = DT_RIGHT; break; + } + + SaveOptions(); + return TRUE; + } + break; + } + + return 0; +} + +int OptInit(WPARAM wParam, LPARAM lParam) { + OPTIONSDIALOGPAGE odp = { 0 }; +#define OPTIONPAGE_OLD_SIZE2 60 + DWORD mirVir = (DWORD)CallService(MS_SYSTEM_GETVERSION, 0, 0); + odp.cbSize = (mirVir >= 0x00060000 ? sizeof(odp) : OPTIONPAGE_OLD_SIZE2); + //odp.cbSize = sizeof(odp); + + odp.flags = ODPF_BOLDGROUPS; + //odp.flags |= ODPF_UNICODE; + odp.position = -790000000; + odp.hInstance = hInst; + + + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT1); + odp.pszTab = Translate("Appearance"); + odp.pszTitle = (mirVir >= 0x00060000 ? Translate("Tooltips") : Translate("Tooltips View"));; + odp.pszGroup = Translate("Customize"); + odp.nIDBottomSimpleControl = 0; + odp.pfnDlgProc = DlgProcOpts1; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT2); + odp.pszTab = Translate("Content"); + odp.pszTitle = (mirVir >= 0x00060000 ? Translate("Tooltips") : Translate("Tooltips Content"));; + odp.pszGroup = Translate("Customize"); + odp.nIDBottomSimpleControl = 0; + odp.pfnDlgProc = DlgProcOpts2; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + return 0; +} + +HANDLE hEventOptInit; + +void InitOptions() { + hEventOptInit = HookEvent(ME_OPT_INITIALISE, OptInit); +} + +void DeinitOptions() { + UnhookEvent(hEventOptInit); + + DIListNode *di_node = options.di_list; + while(options.di_list) { + di_node = options.di_list; + options.di_list = options.di_list->next; + free(di_node); + } + + DSListNode *ds_node = options.ds_list; + while(options.ds_list) { + ds_node = options.ds_list; + options.ds_list = options.ds_list->next; + free(ds_node); + } +} diff --git a/tipper/options.h b/tipper/options.h new file mode 100644 index 0000000..6fff79b --- /dev/null +++ b/tipper/options.h @@ -0,0 +1,74 @@ +#ifndef _OPTIONS_INC +#define _OPTIONS_INC + +#include "translations.h" + +#define LABEL_LEN 1024 +#define VALUE_LEN 8192 + +#define MODULE_NAME_LEN 512 +#define SETTING_NAME_LEN 512 + +typedef struct { + TCHAR label[LABEL_LEN]; + TCHAR value[VALUE_LEN]; + bool line_above, value_newline; +} DisplayItem; + +typedef enum {DVT_DB = 0, DVT_PROTODB = 1} DisplaySubstType; +typedef struct { + TCHAR name[LABEL_LEN]; + DisplaySubstType type; + char module_name[MODULE_NAME_LEN]; + char setting_name[SETTING_NAME_LEN]; + int translate_func_id; +} DisplaySubst; + +struct DSListNode { + DisplaySubst ds; + DSListNode *next; +}; + +struct DIListNode { + DisplayItem di; + DIListNode *next; +}; + +typedef enum {PAV_NONE=0, PAV_LEFT=1, PAV_RIGHT=2} PopupAvLayout; +typedef enum {PTL_LEFTICON=0, PTL_RIGHTICON=1, PTL_NOICON=2, PTL_NOTITLE=3} PopupTitleLayout; +typedef enum {PP_BOTTOMRIGHT=0, PP_BOTTOMLEFT=1, PP_TOPRIGHT=2, PP_TOPLEFT=3} PopupPosition; + +typedef struct { + int win_width, win_max_height, av_size; //tweety + int opacity; + bool border; + bool round, av_round; + bool animate; + bool drop_shadow; + bool trans_bg; + PopupTitleLayout title_layout; + PopupAvLayout av_layout; + int text_indent; + bool show_no_focus; + DSListNode *ds_list; + int ds_count; + DIListNode *di_list; + int di_count; + int time_in; + int padding, av_padding, text_padding; + PopupPosition pos; + int min_width, min_height; // no UI for these + int mouse_tollerance; + bool status_bar_tips; + int sidebar_width; + COLORREF bg_col, border_col, div_col, bar_col, title_col, label_col, value_col, sidebar_col; + int label_valign, label_halign, value_valign, value_halign; +} Options; + +extern Options options; + +void InitOptions(); +void LoadOptions(); +void DeinitOptions(); + +#endif diff --git a/tipper/popwin.cpp b/tipper/popwin.cpp new file mode 100644 index 0000000..c42467b --- /dev/null +++ b/tipper/popwin.cpp @@ -0,0 +1,837 @@ +#include "common.h" +#include "subst.h" +#include "popwin.h" +#include "message_pump.h" + +#define TITLE_TEXT_LEN 512 + +#define ANIM_STEPS 20 +#define ANIM_ELAPSE 10 + +#define CHECKMOUSE_ELAPSE 250 + +#define ID_TIMER_ANIMATE 0x0100 +#define ID_TIMER_CHECKMOUSE 0x0101 + +typedef struct { + TCHAR *swzLabel, *swzValue; + bool value_newline; + bool line_above; + int label_height, value_height, total_height; +} RowData; + +struct PopupWindowData { + HBRUSH bkBrush, barBrush; + HPEN bPen, dPen; + int tb_height, av_height, text_height, sm_height, lm_height, label_width; + int real_av_width, real_av_height; + //TCHAR *lm_text, *sm_text; + HANDLE hContact; + int iconIndex; + CLCINFOTIPEX clcit; + TCHAR swzTitle[TITLE_TEXT_LEN]; + RowData *rows; + int row_count; + int anim_step; + bool text_tip; + int indent, sb_width; + POINT start_cursor_pos; // work around bugs with hiding tips (timer check mouse position) +}; + +LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + PopupWindowData *pwd = (PopupWindowData *)GetWindowLong(hwnd, GWL_USERDATA); + + switch(uMsg) { + case WM_CREATE: + { + + CREATESTRUCT *cs = (CREATESTRUCT *)lParam; + pwd = (PopupWindowData *)malloc(sizeof(PopupWindowData)); + memset(pwd, 0, sizeof(PopupWindowData)); + pwd->clcit = *(CLCINFOTIPEX *)cs->lpCreateParams; + pwd->iconIndex = -1; + pwd->bkBrush = CreateSolidBrush(options.bg_col); + pwd->barBrush = CreateSolidBrush(options.sidebar_col); + pwd->bPen = options.border ? (HPEN)CreatePen(PS_SOLID, 1, options.border_col) : (HPEN)CreatePen(PS_SOLID, 1, options.bg_col); + pwd->dPen = (HPEN)CreatePen(PS_SOLID, 1, options.div_col); + + SetWindowLong(hwnd, GWL_USERDATA, (LONG)pwd); + + // work around bug hiding tips + GetCursorPos(&pwd->start_cursor_pos); + SetTimer(hwnd, ID_TIMER_CHECKMOUSE, CHECKMOUSE_ELAPSE, 0); + + if(pwd->clcit.proto) { + pwd->text_tip = false; + pwd->indent = options.text_indent; + pwd->sb_width = options.sidebar_width; + + MultiByteToWideChar(code_page, 0, pwd->clcit.proto, -1, pwd->swzTitle, 512); + + WORD status = CallProtoService(pwd->clcit.proto, PS_GETSTATUS, 0, 0); + char *strptr = (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)status, (LPARAM)0); + int size = MultiByteToWideChar(code_page, 0, strptr, -1, 0, 0); + if(size) { + wchar_t *swzText = (wchar_t *)malloc(size * sizeof(wchar_t)); + MultiByteToWideChar(code_page, 0, strptr, -1, swzText, size); + pwd->rows = (RowData *) realloc(pwd->rows, sizeof(RowData) * (pwd->row_count + 1)); + + pwd->rows[pwd->row_count].swzLabel = wcsdup(TranslateT("Status:")); + pwd->rows[pwd->row_count].swzValue = swzText; + pwd->rows[pwd->row_count].value_newline = false; + pwd->rows[pwd->row_count].line_above = false; + pwd->row_count++; + } + + if(status >= ID_STATUS_OFFLINE && status <= ID_STATUS_IDLE) { + char *status_msg = (char *)CallService(MS_AWAYMSG_GETSTATUSMSG, status, 0); + if(status_msg) { + int size = MultiByteToWideChar(code_page, 0, status_msg, -1, 0, 0); + if(size) { + wchar_t *swzText = (wchar_t *)malloc(size * sizeof(wchar_t)); + MultiByteToWideChar(code_page, 0, status_msg, -1, swzText, size); + StripBBCodesInPlace(swzText); + pwd->rows = (RowData *) realloc(pwd->rows, sizeof(RowData) * (pwd->row_count + 1)); + + pwd->rows[pwd->row_count].swzLabel = wcsdup(TranslateT("Status message:")); + pwd->rows[pwd->row_count].swzValue = swzText; + pwd->rows[pwd->row_count].value_newline = true; + pwd->rows[pwd->row_count].line_above = true; + pwd->row_count++; + } + mir_free(status_msg); + } + } + } else if(pwd->clcit.text) { + pwd->text_tip = true; + pwd->indent = 0; + pwd->sb_width = 0; + + + //MessageBox(0, swzText, _T("tip"), MB_OK); + + wchar_t buff[2048], *swzText = pwd->clcit.text; + int buff_pos, i = 0, size = _tcslen(pwd->clcit.text); + bool top_message = false; + + if(swzText[0] != _T('<')) { + while(swzText[i] != _T('\n') && swzText[i] != _T('\r') && i < size && i < 2048) { + buff[i] = swzText[i]; + i++; + } + buff[i] = 0; + + if(_tcslen(buff)) { + pwd->rows = (RowData *)realloc(pwd->rows, sizeof(RowData) * (pwd->row_count + 1)); + pwd->rows[pwd->row_count].line_above = false; + pwd->rows[pwd->row_count].value_newline = true; + pwd->rows[pwd->row_count].swzLabel = _T(""); + pwd->rows[pwd->row_count].swzValue = wcsdup(buff); + pwd->row_count++; + top_message = true; + } + } + + // parse bold bits into labels and the rest into items + while(i < size) { + while(i + 2 < size + && (swzText[i] != L'<' + || swzText[i + 1] != L'b' + || swzText[i + 2] != L'>')) + { + i++; + } + + i += 3; + + buff_pos = 0; + while(i + 3 < size + && buff_pos < 2048 + && (swzText[i] != L'<' + || swzText[i + 1] != L'/' + || swzText[i + 2] != L'b' + || swzText[i + 3] != L'>')) + { + if(swzText[i] != L'\t') + buff[buff_pos++] = swzText[i]; + i++; + } + + i += 4; + + buff[buff_pos] = 0; + + if(buff_pos) { + pwd->rows = (RowData *)realloc(pwd->rows, sizeof(RowData) * (pwd->row_count + 1)); + pwd->rows[pwd->row_count].value_newline = false; + pwd->rows[pwd->row_count].swzLabel = wcsdup(buff); + if(pwd->row_count == 1 && top_message) + pwd->rows[pwd->row_count].line_above = true; + else + pwd->rows[pwd->row_count].line_above = false; + + buff_pos = 0; + while(i < size + && buff_pos < 2048 + && swzText[i] != L'\n') + { + if(swzText[i] != L'\t' && swzText[i] != L'\r') + buff[buff_pos++] = swzText[i]; + i++; + } + buff[buff_pos] = 0; + + pwd->rows[pwd->row_count].swzValue = wcsdup(buff); + + pwd->row_count++; + } + + i++; + } + + if(pwd->row_count == 0) { + // single item + pwd->row_count = 1; + pwd->rows = (RowData *)malloc(sizeof(RowData)); + pwd->rows[0].line_above = pwd->rows[0].value_newline = false; + pwd->rows[0].swzLabel = 0; + pwd->rows[0].swzValue = swzText; + } + + free(pwd->clcit.text); + pwd->clcit.text = 0; + } else { + pwd->text_tip = false; + pwd->indent = options.text_indent; + pwd->sb_width = options.sidebar_width; + pwd->hContact = pwd->clcit.hItem; + pwd->iconIndex = (int)CallService(MS_CLIST_GETCONTACTICON, (WPARAM)pwd->hContact, 0); + + // don't use stored status message + DBDeleteContactSetting(pwd->hContact, MODULE, "TempStatusMsg"); + + { // get unicode name if possible, else ascii + wchar_t *swzCDN = (wchar_t *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)pwd->hContact, GCDNF_UNICODE); + char *szCDN = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)pwd->hContact, 0); + + if(szCDN) { + // detect if the clist provided unicode display name by comparing with non-unicode + if(swzCDN && strncmp(szCDN, (char *)swzCDN, strlen(szCDN)) != 0) { + wcsncpy(pwd->swzTitle, swzCDN, TITLE_TEXT_LEN); + } else { + // convert to unicode + //swzContactDisplayName = (wchar_t *) _alloca(sizeof(wchar_t) * (strlen(szCDN) + 1)); + int size = MultiByteToWideChar(code_page, 0, (char *) szCDN, -1, 0, 0); + if(size > 0) { + MultiByteToWideChar(code_page, 0, (char *) szCDN, -1, pwd->swzTitle, TITLE_TEXT_LEN); + } else { + wcsncpy(pwd->swzTitle, TranslateT("(Unknown)"), TITLE_TEXT_LEN); + } + } + pwd->swzTitle[TITLE_TEXT_LEN - 1] = 0; + } else { + wcscpy(pwd->swzTitle, TranslateT("(Unknown)")); + } + } + + SendMessage(hwnd, PUM_REFRESH_VALUES, 0, 0); + } + } + + // transparency +#ifdef WS_EX_LAYERED + SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); +#endif + +// not defined in my mingw winuser.h +#ifdef __GNUC__ +#define CS_DROPSHADOW 0x00020000 +#endif +#ifdef CS_DROPSHADOW + if (options.drop_shadow) SetClassLong(hwnd, GCL_STYLE, CS_DROPSHADOW); + else SetClassLong(hwnd, GCL_STYLE, 0); +#endif + +#ifdef LWA_ALPHA + if(MySetLayeredWindowAttributes) { + MySetLayeredWindowAttributes(hwnd, RGB(0,0,0), (int)(options.opacity / 100.0 * 255), LWA_ALPHA); + if(options.trans_bg) { + MySetLayeredWindowAttributes(hwnd, options.bg_col, 0, LWA_COLORKEY); + } + } +#endif + SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); // calculate window height + SendMessage(hwnd, PUM_CALCPOS, 0, 0); + + if(options.animate) + SetTimer(hwnd, ID_TIMER_ANIMATE, ANIM_ELAPSE, 0); + + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + UpdateWindow(hwnd); + /* + // move clist under? + { + HWND hwndClist = (HWND)CallService(MS_CLUI_GETHWND, 0, 0); + if(GetWindowLong(hwndClist, GWL_EXSTYLE) & WS_EX_TOPMOST) + SetWindowPos(hwndClist, hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + */ + // since tipper win is topmost, this should put it at top of topmost windows + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + return 0; + case WM_ERASEBKGND: + { + HDC hdc = (HDC) wParam; + RECT r, r_bar; + GetClientRect(hwnd, &r); + + // bg + FillRect(hdc, &r, pwd->bkBrush); + // sidebar + r_bar = r; + r_bar.right = r.left + pwd->sb_width; + FillRect(hdc, &r_bar, pwd->barBrush); + // border + if(options.border) { + + HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH)); + HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->bPen); + + int h = 0; + if(options.round) { + int v; + int w=14; + h=(r.right-r.left)>(w*2)?w:(r.right-r.left); + v=(r.bottom-r.top)>(w*2)?w:(r.bottom-r.top); + h=(htext_tip && options.av_layout != PAV_NONE && pwd->av_height) { + RECT avr; + avr.top = options.av_padding; + + if(options.av_layout == PAV_LEFT) { + avr.left = r.left + options.av_padding; + avr.right = avr.left + pwd->real_av_width; + r2.left += pwd->real_av_width + (2 * options.av_padding - options.padding); // padding re-added for text + } else if(options.av_layout == PAV_RIGHT) { + avr.right = r.right - options.av_padding; + avr.left = avr.right - pwd->real_av_width; + r2.right -= pwd->real_av_width + (2 * options.av_padding - options.padding); + } + + avr.bottom = avr.top + pwd->real_av_height; + + AVATARDRAWREQUEST adr = {0}; + adr.cbSize = sizeof(adr); + adr.hContact = pwd->hContact; + adr.hTargetDC = ps.hdc; + adr.rcDraw = avr; + adr.dwFlags = (options.av_round ? AVDRQ_ROUNDEDCORNER : 0); + if(!pwd->hContact) { // status bar tip? + adr.dwFlags |= AVDRQ_OWNPIC; + adr.szProto = pwd->clcit.proto; + } + adr.radius = (options.av_round ? 5 : 0); + + CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&adr); + } + + RECT tr; + tr.left = r2.left + options.padding; tr.right = r2.right - options.padding; tr.top = tr.bottom = 0; + if(!pwd->text_tip && options.title_layout != PTL_NOTITLE) { + // title icon + if(options.title_layout != PTL_NOICON) { + HICON hIcon = 0; + bool destroy_icon = true; + if(pwd->iconIndex != -1) hIcon = ImageList_GetIcon((HIMAGELIST)CallService(MS_CLIST_GETICONSIMAGELIST, 0, 0), pwd->iconIndex, 0); + else if(!pwd->hContact) { + WORD status = CallProtoService(pwd->clcit.proto, PS_GETSTATUS, 0, 0); + hIcon = LoadSkinnedProtoIcon(pwd->clcit.proto, status); + destroy_icon = false; + } + if(hIcon) { + int iconx; + if(options.title_layout == PTL_RIGHTICON) { + iconx = r2.right - 16 - options.padding; + tr.right -= 16 + options.padding; + } else { + iconx = r2.left + options.padding; + tr.left += 16 + options.padding; + } + DrawIconEx(ps.hdc, iconx, options.padding + (pwd->tb_height - options.padding - 16) / 2, hIcon, 16, 16, 0, NULL, DI_NORMAL); + if(destroy_icon) DestroyIcon(hIcon); + } + } + + // title text + if(hFontTitle) SelectObject(ps.hdc, (HGDIOBJ)hFontTitle); + + SetTextColor(ps.hdc, options.title_col); + + tr.top = r.top + options.padding; tr.bottom = tr.top + pwd->tb_height - options.padding; + DrawText(ps.hdc, pwd->swzTitle, wcslen(pwd->swzTitle), &tr, DT_VCENTER | DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX); + } + + // values + pwd->text_height = 0; + bool use_r = true; + int row_height; + for(int i = 0; i < pwd->row_count; i++) { + tr.top = tr.bottom; + use_r = (tr.top + options.text_padding >= pwd->av_height); + + if(use_r) { + if(pwd->rows[i].line_above) { + HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->dPen); + tr.top += options.text_padding; + Rectangle(hdc, r.left + options.padding + pwd->indent, tr.top, r.right - options.padding, tr.top + 1); + SelectObject(hdc, hOldPen); + } + tr.left = r.left + options.padding + pwd->indent; + if(pwd->rows[i].value_newline) + tr.right = r.right - options.padding; + else + tr.right = r.left + options.padding + pwd->indent + pwd->label_width; + } else { + if(pwd->rows[i].line_above) { + HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->dPen); + tr.top += options.text_padding; + Rectangle(hdc, r2.left + options.padding + pwd->indent, tr.top, r2.right - options.padding, tr.top + 1); + SelectObject(hdc, hOldPen); + } + tr.left = r2.left + options.padding + pwd->indent; + if(pwd->rows[i].value_newline) + tr.right = r2.right - options.padding; + else + tr.right = r2.left + options.padding + pwd->indent + pwd->label_width; + } + + if(pwd->rows[i].value_newline) + row_height = pwd->rows[i].label_height; + else + row_height = max(pwd->rows[i].label_height, pwd->rows[i].value_height); + + if(hFontLabels) SelectObject(hdc, (HGDIOBJ)hFontLabels); + if(pwd->rows[i].label_height) { + tr.top += options.text_padding; + tr.bottom = tr.top + row_height; + SetTextColor(ps.hdc, options.label_col); + DrawText(ps.hdc, pwd->rows[i].swzLabel, wcslen(pwd->rows[i].swzLabel), &tr, options.label_valign | ((options.label_halign == DT_RIGHT && !pwd->rows[i].value_newline) ? DT_RIGHT : DT_LEFT) | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX); + if(pwd->rows[i].value_newline) + tr.top = tr.bottom; + } else + tr.bottom = tr.top; + + if(pwd->rows[i].value_newline) + row_height = pwd->rows[i].value_height; + + if(hFontValues) SelectObject(hdc, (HGDIOBJ)hFontValues); + SetTextColor(ps.hdc, options.value_col); + if(use_r) { + if(pwd->rows[i].value_newline) + tr.left = r.left + options.padding + pwd->indent; + else + tr.left = r.left + options.padding + pwd->indent + pwd->label_width + options.padding; + tr.right = r.right - options.padding; + } else { + if(pwd->rows[i].value_newline) + tr.left = r2.left + options.padding + pwd->indent; + else + tr.left = r2.left + options.padding + pwd->indent + pwd->label_width + options.padding; + tr.right = r2.right - options.padding; + } + if(pwd->rows[i].value_height) { + if(pwd->rows[i].value_newline || !pwd->rows[i].label_height) tr.top += options.text_padding; + tr.bottom = tr.top + row_height; + DrawText(ps.hdc, pwd->rows[i].swzValue, wcslen(pwd->rows[i].swzValue), &tr, options.value_valign | options.value_halign | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX); + } + } + + EndPaint(hwnd, &ps); + //} + } + return 0; + case WM_DESTROY: + KillTimer(hwnd, ID_TIMER_CHECKMOUSE); + KillTimer(hwnd, ID_TIMER_ANIMATE); + ShowWindow(hwnd, SW_HIDE); + + DeleteObject(pwd->bkBrush); + DeleteObject(pwd->barBrush); + DeleteObject(pwd->bPen); + DeleteObject(pwd->dPen); + + for(int i = 0; i < pwd->row_count; i++) { + free(pwd->rows[i].swzLabel); + free(pwd->rows[i].swzValue); + } + free(pwd->rows); + + free(pwd); pwd = 0; + SetWindowLong(hwnd, GWL_USERDATA, 0); + + break; + case WM_TIMER: + if(wParam == ID_TIMER_ANIMATE) { + pwd->anim_step++; + if(pwd->anim_step == ANIM_STEPS) + KillTimer(hwnd, ID_TIMER_ANIMATE); + SendMessage(hwnd, PUM_UPDATERGN, 0, 0); + InvalidateRect(hwnd, 0, TRUE); + UpdateWindow(hwnd); + } else if(wParam == ID_TIMER_CHECKMOUSE) { + // workaround for tips that just won't go away + + POINT pt; + GetCursorPos(&pt); + + /* + // works well, except in e.g. options->events->ignore :( + bool hide = false; + if(pwd->text_tip) { + // tip may be off clist (e.g. systray) + if(pt.x != pwd->start_cursor_pos.x || pt.y != pwd->start_cursor_pos.y) // mouse has moved + hide = false; + } else { + // check window under cursor - hide if not clist + HWND hwnd_clist = (HWND)CallService(MS_CLUI_GETHWND, 0, 0), + hwnd_under = WindowFromPoint(pt); + if(hwnd_under != hwnd_clist && !IsChild(hwnd_clist, hwnd_under)) + hide = true; + } + if(hide) PostMPMessage(MUM_DELETEPOPUP, 0, 0); + */ + + if(abs(pt.x - pwd->start_cursor_pos.x) > options.mouse_tollerance + || abs(pt.y - pwd->start_cursor_pos.y) > options.mouse_tollerance) // mouse has moved beyond tollerance + { + PostMPMessage(MUM_DELETEPOPUP, 0, 0); + } + } + break; + + case PUM_SETSTATUSTEXT: + { + if(pwd && (HANDLE)wParam == pwd->hContact) { + // in case we have the status message in a row + DBWriteContactSettingTString(pwd->hContact, MODULE, "TempStatusMsg", (TCHAR*)lParam); + SendMessage(hwnd, PUM_REFRESH_VALUES, 0, 0); + } + } + return TRUE; + case PUM_SETAVATAR: + { + if(pwd && (HANDLE)wParam == pwd->hContact) { + SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); // calculate window height + SendMessage(hwnd, PUM_CALCPOS, 0, 0); + InvalidateRect(hwnd, 0, TRUE); + } + } + return TRUE; + case PUM_REFRESH_VALUES: + if(pwd && pwd->clcit.proto == 0 && pwd->text_tip == false) { + for(int i = 0; i < pwd->row_count; i++) { + free(pwd->rows[i].swzLabel); + free(pwd->rows[i].swzValue); + } + if(pwd->rows) { + free(pwd->rows); + pwd->rows = 0; + } + pwd->row_count = 0; + + DIListNode *node = options.di_list; + TCHAR buff_label[LABEL_LEN], buff[VALUE_LEN]; + while(node) { + if(GetLabelText(pwd->hContact, node->di, buff_label, LABEL_LEN) && GetValueText(pwd->hContact, node->di, buff, VALUE_LEN)) { + if(node->di.line_above // we have a line above + && pwd->row_count > 0 // and we're not the first row + && pwd->rows[pwd->row_count - 1].line_above // and above us there's a line above + && pwd->rows[pwd->row_count - 1].swzLabel[0] == 0 // with no label + && pwd->rows[pwd->row_count - 1].swzValue[0] == 0) // and no value + { + // overwrite item above + pwd->row_count--; + free(pwd->rows[pwd->row_count].swzLabel); + free(pwd->rows[pwd->row_count].swzValue); + } else + pwd->rows = (RowData *) realloc(pwd->rows, sizeof(RowData) * (pwd->row_count + 1)); + + pwd->rows[pwd->row_count].swzLabel = wcsdup(buff_label); + pwd->rows[pwd->row_count].swzValue = wcsdup(buff); + pwd->rows[pwd->row_count].value_newline = node->di.value_newline; + pwd->rows[pwd->row_count].line_above = node->di.line_above; + pwd->row_count++; + } + node = node->next; + } + + // if the last item is just a divider, remove it + if(pwd->row_count > 0 + && pwd->rows[pwd->row_count - 1].line_above // and above us there's a line above + && pwd->rows[pwd->row_count - 1].swzLabel[0] == 0 // with no label + && pwd->rows[pwd->row_count - 1].swzValue[0] == 0) // and no value + { + pwd->row_count--; + free(pwd->rows[pwd->row_count].swzLabel); + free(pwd->rows[pwd->row_count].swzValue); + + if(pwd->row_count == 0) + free(pwd->rows); + } + + SendMessage(hwnd, PUM_GETHEIGHT, 0, 0); // calculate window height + SendMessage(hwnd, PUM_CALCPOS, 0, 0); + InvalidateRect(hwnd, 0, TRUE); + } + return TRUE; + case PUM_GETHEIGHT: + { + int *pHeight = (int *)wParam; + HDC hdc = GetDC(hwnd); + SIZE size; + RECT r; + r.top = r.left = 0; + r.right = options.win_width; + int width = options.padding; + + // titlebar height + if(!pwd->text_tip && pwd->swzTitle && options.title_layout != PTL_NOTITLE) { + if(hFontTitle) SelectObject(hdc, (HGDIOBJ)hFontTitle); + GetTextExtentPoint32(hdc, pwd->swzTitle, wcslen(pwd->swzTitle), &size); + width += options.padding + size.cx; + if(options.title_layout != PTL_NOICON) { + pwd->tb_height = options.padding + max(size.cy, 16); + width += 16 + options.padding; + } else + pwd->tb_height = options.padding + size.cy; + } else + pwd->tb_height = 0; + + // avatar height + pwd->av_height = 0; + if(!pwd->text_tip && options.av_layout != PAV_NONE && ServiceExists(MS_AV_DRAWAVATAR)) { + AVATARCACHEENTRY *ace = 0; + if(pwd->hContact) ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)pwd->hContact, 0); + else ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)pwd->clcit.proto); + if(ace && (ace->dwFlags & AVS_BITMAP_VALID) && !(ace->dwFlags & AVS_HIDEONCLIST)) { + if(ace->bmHeight >= ace->bmWidth) { + pwd->real_av_height = options.av_size; + pwd->real_av_width = (int)(options.av_size * (ace->bmWidth / (double)ace->bmHeight)); + } else { + pwd->real_av_height = (int)(options.av_size * (ace->bmHeight / (double)ace->bmWidth)); + pwd->real_av_width = options.av_size; + } + pwd->av_height = options.av_padding * 2 + pwd->real_av_height; + width += pwd->real_av_width + (2 * options.av_padding - options.padding); + } + } + + int i; + // text height + pwd->text_height = pwd->label_width = 0; + // iterate once to find max label width for items with label and value on same line, but don't consider width of labels on a new line + for(i = 0; i < pwd->row_count; i++) { + if(pwd->rows[i].swzLabel && pwd->rows[i].value_newline == false) { + if(hFontLabels) SelectObject(hdc, (HGDIOBJ)hFontLabels); + GetTextExtentPoint32(hdc, pwd->rows[i].swzLabel, wcslen(pwd->rows[i].swzLabel), &size); + if(size.cx > pwd->label_width) + pwd->label_width = size.cx; + } + } + + for(i = 0; i < pwd->row_count; i++) { + if(hFontLabels) SelectObject(hdc, (HGDIOBJ)hFontLabels); + if(pwd->rows[i].swzLabel) + GetTextExtentPoint32(hdc, pwd->rows[i].swzLabel, wcslen(pwd->rows[i].swzLabel), &size); + else + size.cy = size.cx = 0; + + // save so we don't have to recalculate + pwd->rows[i].label_height = size.cy; + + if(hFontValues) SelectObject(hdc, (HGDIOBJ)hFontValues); + RECT smr; + smr.top = smr.bottom = 0; + smr.left = r.left + options.padding + pwd->indent; + smr.right = r.right; + if(pwd->tb_height + pwd->text_height + options.text_padding < pwd->av_height) + smr.right -= pwd->real_av_width + 2 * options.av_padding; + else + smr.right -= options.padding; + if(!pwd->rows[i].value_newline) smr.right -= pwd->label_width + options.padding; + if(pwd->rows[i].swzValue) + DrawText(hdc, pwd->rows[i].swzValue, wcslen(pwd->rows[i].swzValue), &smr, DT_CALCRECT | DT_VCENTER | DT_LEFT | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX); + + // save so we don't have to recalculate + pwd->rows[i].value_height = smr.bottom; + + pwd->rows[i].total_height = (pwd->rows[i].line_above ? options.text_padding : 0); + if(pwd->rows[i].value_newline) { + if(size.cy) pwd->rows[i].total_height += size.cy + options.text_padding; + if(smr.bottom) pwd->rows[i].total_height += smr.bottom + options.text_padding; + } else { + int maxheight = max(size.cy, smr.bottom); + if(maxheight) pwd->rows[i].total_height += maxheight + options.text_padding; + } + + // only consider this item's width, and include it's height, if it doesn't make the window too big + if(max(pwd->tb_height + pwd->text_height + options.padding + pwd->rows[i].total_height, pwd->av_height) <= options.win_max_height) { + if(width < options.win_width) { + int wid = options.padding + pwd->indent + (pwd->rows[i].value_newline ? max(size.cx, smr.right - smr.left) : pwd->label_width + options.padding + (smr.right - smr.left)); + if(pwd->tb_height + pwd->text_height + options.text_padding < pwd->av_height) + width = max(width, wid + options.av_size + 2 * options.av_padding); + else + width = max(width, wid + options.padding); + } + + pwd->text_height += pwd->rows[i].total_height; + } + } + + ReleaseDC(hwnd, hdc); + + int height = max(pwd->tb_height + pwd->text_height + options.padding, pwd->av_height); + + if(height < options.min_height) height = options.min_height; + // ignore minwidth for text tips + if((!pwd->text_tip) && width < options.min_width) width = options.min_width; + + if(height > options.win_max_height) height = options.win_max_height; + if(width > options.win_width) width = options.win_width; + + GetWindowRect(hwnd, &r); + if(r.right - r.left != width || r.bottom - r.top != height) { + + SetWindowPos(hwnd, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + SendMessage(hwnd, PUM_UPDATERGN, 0, 0); + + InvalidateRect(hwnd, 0, TRUE); + } + + if(pHeight) *pHeight = height; + } + return TRUE; + case PUM_UPDATERGN: + { + HRGN hRgn1; + RECT r; + + int v,h; + int w=11; + GetWindowRect(hwnd,&r); + r.right -= r.left; + r.left = 0; + r.bottom -= r.top; + r.top = 0; + + if(options.animate && pwd->anim_step < ANIM_STEPS) { + float frac = 1.0f - pwd->anim_step / (float)ANIM_STEPS; + int wi = r.right, hi = r.bottom; + + r.left += (int)(wi / 2.0f * frac + 0.5f); + r.right -= (int)(wi / 2.0f * frac + 0.5f); + r.top += (int)(hi / 2.0f * frac + 0.5f); + r.bottom -= (int)(hi / 2.0f * frac + 0.5f); + } + + // round corners + if(options.round) { + h=(r.right-r.left)>(w*2)?w:(r.right-r.left); + v=(r.bottom-r.top)>(w*2)?w:(r.bottom-r.top); + h=(hclcit.ptCursor, MONITOR_DEFAULTTONEAREST); + + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + + wa_rect = mi.rcWork; + + GetWindowRect(hwnd, &r); + + CURSORINFO ci = {sizeof(CURSORINFO)}; + GetCursorInfo(&ci); + + + + int x = 0, y = 0, width = (r.right - r.left), height = (r.bottom - r.top); + + switch(options.pos) { + case PP_BOTTOMRIGHT: + x = pwd->clcit.ptCursor.x + GetSystemMetrics(SM_CXSMICON); // cursor size is too large - use small icon size + y = pwd->clcit.ptCursor.y + GetSystemMetrics(SM_CYSMICON); + break; + case PP_BOTTOMLEFT: + x = pwd->clcit.ptCursor.x - width - GetSystemMetrics(SM_CXSMICON); + y = pwd->clcit.ptCursor.y + GetSystemMetrics(SM_CYSMICON); + break; + case PP_TOPRIGHT: + x = pwd->clcit.ptCursor.x + GetSystemMetrics(SM_CXSMICON); + y = pwd->clcit.ptCursor.y - height - GetSystemMetrics(SM_CYSMICON); + break; + case PP_TOPLEFT: + x = pwd->clcit.ptCursor.x - width - GetSystemMetrics(SM_CXSMICON); + y = pwd->clcit.ptCursor.y - height - GetSystemMetrics(SM_CYSMICON); + break; + } + + if(x + width + 8 > wa_rect.right) + x = wa_rect.right - width - 8; + if(y + height > wa_rect.bottom) + y = pwd->clcit.ptCursor.y - height - 8; + if(x - 8 < wa_rect.left) + x = wa_rect.left + 8; + if(y - 8 < wa_rect.top) + y = pwd->clcit.ptCursor.y + GetSystemMetrics(SM_CYSMICON); + + SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); + } + return TRUE; + } + + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +int AvatarChanged(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + PostMPMessage(MUM_GOTAVATAR, (WPARAM)hContact, 0); + return 0; +} + diff --git a/tipper/popwin.h b/tipper/popwin.h new file mode 100644 index 0000000..f354d63 --- /dev/null +++ b/tipper/popwin.h @@ -0,0 +1,17 @@ +#ifndef _POPWIN_INC +#define _POPWIN_INC + +#define POP_WIN_CLASS _T(MODULE) _T("MimTTClass") + +#define PUM_GETHEIGHT (WM_USER + 0x020) +#define PUM_CALCPOS (WM_USER + 0x021) +#define PUM_SETSTATUSTEXT (WM_USER + 0x022) +#define PUM_UPDATERGN (WM_USER + 0x023) +#define PUM_SETAVATAR (WM_USER + 0x024) +#define PUM_REFRESH_VALUES (WM_USER + 0x025) + +LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +int AvatarChanged(WPARAM wParam, LPARAM lParam); // exposed so hook/unhook is in main thread + +#endif diff --git a/tipper/resource.h b/tipper/resource.h new file mode 100644 index 0000000..dad8e31 --- /dev/null +++ b/tipper/resource.h @@ -0,0 +1,85 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by tipper.rc +// +#define IDD_OPT1 101 +#define IDD_SUBST 103 +#define IDD_ITEM 104 +#define IDD_OPT2 105 +#define IDC_ED_WIDTH 1005 +#define IDC_ED_MAXHEIGHT 1006 +#define IDC_SPIN_WIDTH 1007 +#define IDC_SPIN_MAXHEIGHT 1008 +#define IDC_ED_INDENT 1009 +#define IDC_SPIN_INDENT 1010 +#define IDC_ED_PADDING 1011 +#define IDC_SPIN_PADDING 1012 +#define IDC_ED_TRANS 1013 +#define IDC_SPIN_TRANS 1014 +#define IDC_CHK_BORDER 1015 +#define IDC_CHK_ROUNDCORNERS 1016 +#define IDC_ED_MINWIDTH 1017 +#define IDC_CHK_ANIMATE 1018 +#define IDC_CHK_TRANSBG 1019 +#define IDC_CHK_SHADOW 1020 +#define IDC_SPIN_MINWIDTH 1021 +#define IDC_ED_MINHEIGHT 1022 +#define IDC_SPIN_MINHEIGHT 1023 +#define IDC_ED_SBWIDTH 1024 +#define IDC_SPIN_SBWIDTH 1025 +#define IDC_ED_TEXTPADDING 1026 +#define IDC_ED_AVSIZE 1027 +#define IDC_SPIN_AVSIZE 1028 +#define IDC_ED_HOVER 1029 +#define IDC_SPIN_HOVER 1030 +#define IDC_SPIN_TEXTPADDING 1031 +#define IDC_CMB_ICON 1032 +#define IDC_CMB_AV 1033 +#define IDC_CMB_AV2 1034 +#define IDC_CMB_ICON2 1034 +#define IDC_CMB_POS 1034 +#define IDC_LST_ITEMS 1035 +#define IDC_ED_AVPADDING 1035 +#define IDC_BTN_ADD 1036 +#define IDC_SPIN_AVPADDING 1036 +#define IDC_BTN_REMOVE 1037 +#define IDC_CHK_ROUNDCORNERS2 1037 +#define IDC_CHK_ROUNDCORNERSAV 1037 +#define IDC_BTN_UP 1038 +#define IDC_BTN_DOWN 1039 +#define IDC_CHK_NOFOCUS 1040 +#define IDC_BTN_EDIT 1041 +#define IDC_LST_SUBST 1042 +#define IDC_BTN_ADD2 1043 +#define IDC_ED_MODULE 1044 +#define IDC_BTN_REMOVE2 1044 +#define IDC_CHK_PROTOMOD 1045 +#define IDC_BTN_UP2 1045 +#define IDC_CHK_RIGHTLABEL 1045 +#define IDC_ED_SETTING 1046 +#define IDC_CMB_TRANSLATE 1047 +#define IDC_BTN_EDIT2 1047 +#define IDC_ED_LABEL 1048 +#define IDC_CHK_STATUSMSG 1048 +#define IDC_CHK_SBAR 1048 +#define IDC_CHK_LASTMSG 1049 +#define IDC_ED_VALUE 1050 +#define IDC_CHK_LINEABOVE 1051 +#define IDC_CHK_VALNEWLINE 1052 +#define IDC_CUSTOM1 1052 +#define IDC_BORDERCOLOUR 1052 +#define IDC_CMB_LV 1053 +#define IDC_CMB_VV 1054 +#define IDC_CMB_LH 1055 +#define IDC_CMB_VH 1056 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1054 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/tipper/resource.rc b/tipper/resource.rc new file mode 100644 index 0000000..41f2ce9 --- /dev/null +++ b/tipper/resource.rc @@ -0,0 +1,6 @@ + +// this makes our dependencies work better +#include "version.h" + +#include "tipper.rc" +#include "version.rc" diff --git a/tipper/subst.cpp b/tipper/subst.cpp new file mode 100644 index 0000000..5eb6e49 --- /dev/null +++ b/tipper/subst.cpp @@ -0,0 +1,468 @@ +#include "common.h" +#include "subst.h" +#include + +void StripBBCodesInPlace(wchar_t *text) { + if(!DBGetContactSettingByte(0, MODULE, "StripBBCodes", 1)) + return; + + int read = 0, write = 0; + int len = wcslen(text); + + while(read <= len) { // copy terminating null too + while(read <= len && text[read] != L'[') { + if(text[read] != text[write]) text[write] = text[read]; + read++; write++; + } + if(read == len) break; + + if(len - read >= 3 && (wcsnicmp(text + read, L"[b]", 3) == 0 || wcsnicmp(text + read, L"[i]", 3) == 0)) + read += 3; + else if(len - read >= 4 && (wcsnicmp(text + read, L"[/b]", 4) == 0 || wcsnicmp(text + read, L"[/i]", 4) == 0)) + read += 4; + else if(len - read >= 6 && (wcsnicmp(text + read, L"[color", 6) == 0)) { + while(read < len && text[read] != L']') read++; + read++;// skip the ']' + } else if(len - read >= 8 && (wcsnicmp(text + read, L"[/color]", 8) == 0)) + read += 8; + else { + if(text[read] != text[write]) text[write] = text[read]; + read++; write++; + } + } +} + +DWORD last_message_timestamp(HANDLE hContact) { + DBEVENTINFO dbei = {0}; + dbei.cbSize = sizeof(dbei); + HANDLE hDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)hContact, 0); + while(hDbEvent) { + dbei.cbBlob = 0; + CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei); + if(dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT)) { + break; + } + hDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hDbEvent, 0); + } + + if(hDbEvent) return dbei.timestamp; + + return 0; +} + +void format_timestamp(DWORD ts, char *format, TCHAR *buff, int bufflen) { + if(unicode_system) { + TCHAR form[16]; + DBTIMETOSTRINGT dbt = {0}; + dbt.cbDest = bufflen; + dbt.szDest = buff; + MultiByteToWideChar(code_page, 0, format, -1, form, 16); + dbt.szFormat = form; + CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, (WPARAM)ts, (LPARAM)&dbt); + } else { + char buffA[512]; + DBTIMETOSTRING dbt = {0}; + dbt.cbDest = sizeof(buffA); + dbt.szDest = buffA; + dbt.szFormat = format; + CallService(MS_DB_TIME_TIMESTAMPTOSTRING, (WPARAM)ts, (LPARAM)&dbt); + MultiByteToWideChar(code_page, 0, buffA, -1, buff, bufflen); + } +} + +bool uid(HANDLE hContact, TCHAR *buff, int bufflen) { + CONTACTINFO ci; + ci.cbSize = sizeof(CONTACTINFO); + ci.hContact = hContact; + ci.szProto = 0;//(char *)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hcontact,0); + ci.dwFlag = CNF_UNIQUEID | (unicode_system ? CNF_UNICODE : 0); + if(!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci)) { + switch(ci.type) { + case CNFT_BYTE: + _ltot(ci.bVal, buff, 10); + break; + case CNFT_WORD: + _ltot(ci.wVal, buff, 10); + break; + case CNFT_DWORD: + _ltot(ci.dVal, buff, 10); + break; + case CNFT_ASCIIZ: + if(unicode_system) _tcsncpy(buff, ci.pszVal, bufflen); + else MultiByteToWideChar(code_page, 0, (char *)ci.pszVal, -1, buff, bufflen); + break; + default: + return false; + } + return true; + } + return false; +} + +TCHAR *GetLastMessageText(HANDLE hContact) { + DBEVENTINFO dbei = {0}; + dbei.cbSize = sizeof(dbei); + + HANDLE hDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)hContact, 0); + while(hDbEvent) { + dbei.cbBlob = 0; + CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei); + if(dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT)) { + break; + } + hDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hDbEvent, 0); + } + + if(hDbEvent) { + dbei.pBlob = (BYTE *)malloc(dbei.cbBlob); + CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei); + + if(dbei.cbBlob == 0 || dbei.pBlob == 0) return 0; + + wchar_t *msg; + unsigned int msglen = strlen((char *)dbei.pBlob) + 1; + + // here we detect the double-zero wide char zero terminator - in case of messages that are not unicode but have + // other data after the message (e.g. metacontact copied messages with source identifier) + bool dz = false; + for(unsigned int i = msglen; i < dbei.cbBlob; i++) { + if(dbei.pBlob[i] == 0 && dbei.pBlob[i - 1] == 0) { // safe since msglen + 1 above + dz = true; + break; + } + } + + // does blob contain unicode message? + if(msglen < dbei.cbBlob && dz && wcslen((wchar_t *)(&dbei.pBlob[msglen]))) { + // yes + msg = (wchar_t *)(&dbei.pBlob[msglen]); + wchar_t *ret = wcsdup(msg); + StripBBCodesInPlace(ret); + return ret; + } else { + // no, convert to unciode (allocate stack memory); + int size = MultiByteToWideChar(code_page, 0, (char *) dbei.pBlob, -1, 0, 0); + if(size > 0) { + msg = (wchar_t *) malloc(sizeof(wchar_t) * size); + MultiByteToWideChar(code_page, 0, (char *) dbei.pBlob, -1, msg, size); + } else { + msg = (wchar_t *) malloc(sizeof(wchar_t) * (wcslen(TranslateT("Empty message")) + 1)); + wcscpy(msg, TranslateT("Empty message")); + } + StripBBCodesInPlace(msg); + return msg; + } + + } + + return 0; +} + +TCHAR *GetStatusMessageText(HANDLE hContact) { + TCHAR *ret = 0; + DBVARIANT dbv; + if(!DBGetContactSettingTString(hContact, MODULE, "TempStatusMsg", &dbv)) { + if(_tcslen(dbv.ptszVal)) { + ret = _tcsdup(dbv.ptszVal); + StripBBCodesInPlace(ret); // todo - fix for ansi build + } else CallContactService(hContact, PSS_GETAWAYMSG, 0, 0); + DBFreeVariant(&dbv); + /* + // removed - people can use e.g. %raw:CList/StatusMsg% for SMR + } else if(!DBGetContactSettingTString(hContact, "CList", "StatusMsg", &dbv)) { + if(_tcslen(dbv.ptszVal)) ret = _tcsdup(dbv.ptszVal); + else CallContactService(hContact, PSS_GETAWAYMSG, 0, 0); + DBFreeVariant(&dbv); + */ + } else + CallContactService(hContact, PSS_GETAWAYMSG, 0, 0); + + return ret; +} + +bool GetSysSubstText(HANDLE hContact, TCHAR *raw_spec, TCHAR *buff, int bufflen) { + if (!_tcscmp(raw_spec, _T("uid"))) { + return uid(hContact, buff, bufflen); + } else if (!_tcscmp(raw_spec, _T("proto"))) { + char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if (szProto){ + MultiByteToWideChar(code_page, 0, szProto, -1, buff, bufflen); + return true; + } + } else if (!_tcscmp(raw_spec, _T("uidname"))) { + char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if (szProto){ + char *szUniqueId = (char*)CallProtoService(szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0); + if(szUniqueId) { + MultiByteToWideChar(code_page, 0, szUniqueId, -1, buff, bufflen); + return true; + } + } + } else if (!_tcscmp(raw_spec, _T("status_msg"))) { + TCHAR *msg = GetStatusMessageText(hContact); + if(msg) { + _tcsncpy(buff, msg, bufflen); + free(msg); + return true; + } + } else if (!_tcscmp(raw_spec, _T("last_msg"))) { + TCHAR *msg = GetLastMessageText(hContact); + if(msg) { + _tcsncpy(buff, msg, bufflen); + return true; + } + } else if (!_tcscmp(raw_spec, _T("meta_subname"))) { + // get contact list name of active subcontact + HANDLE hSubContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); + if(!hSubContact) return false; + + if(unicode_system) { // get unicode name if possible, else get ascii and convert + wchar_t *swzCDN = (wchar_t *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hSubContact, GCDNF_UNICODE); + if(swzCDN) { + wcsncpy(buff, swzCDN, bufflen); + return true; + } + } else { + char *szCDN = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hSubContact, 0); + if(szCDN) { + int size = MultiByteToWideChar(code_page, 0, (char *) szCDN, -1, 0, 0); + if(size > 0) { + MultiByteToWideChar(code_page, 0, (char *) szCDN, -1, buff, bufflen); + return true; + } + } + } + return false; + + } else if (!_tcscmp(raw_spec, _T("meta_subuid"))){ + HANDLE hSubContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); + if(!hSubContact) return false; + return uid(hSubContact, buff, bufflen); + } else if (!_tcscmp(raw_spec, _T("meta_subproto"))) { + // get protocol of active subcontact + HANDLE hSubContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); + if(!hSubContact) return false; + + char *szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hSubContact, 0); + if (szProto){ + MultiByteToWideChar(code_page, 0, szProto, -1, buff, bufflen); + return true; + } + } else if (!_tcscmp(raw_spec, _T("last_msg_time"))) { + DWORD ts = last_message_timestamp(hContact); + if(ts == 0) return false; + + format_timestamp(ts, "t", buff, bufflen); + return true; + } else if (!_tcscmp(raw_spec, _T("last_msg_date"))) { + DWORD ts = last_message_timestamp(hContact); + if(ts == 0) return false; + + format_timestamp(ts, "d", buff, bufflen); + return true; + } else if (!_tcscmp(raw_spec, _T("last_msg_reltime"))) { + DWORD ts = last_message_timestamp(hContact); + if(ts == 0) return false; + + DWORD t = (DWORD)time(0); + DWORD diff = (t - ts); + int d = (diff / 60 / 60 / 24); + int h = (diff - d * 60 * 60 * 24) / 60 / 60; + int m = (diff - d * 60 * 60 * 24 - h * 60 * 60) / 60; + if(d > 0) mir_sntprintf(buff, bufflen, TranslateT("%dd %dh %dm"), d, h, m); + else if(h > 0) mir_sntprintf(buff, bufflen, TranslateT("%dh %dm"), h, m); + else mir_sntprintf(buff, bufflen, TranslateT("%dm"), m); + + return true; + } + return false; +} + +bool GetSubstText(HANDLE hContact, const DisplaySubst &ds, TCHAR *buff, int bufflen) { + TranslateFunc *tfunc = 0; + for(int i = 0; i < num_tfuncs; i++) { + if(translations[i].id == (DWORD)ds.translate_func_id) { + tfunc = translations[i].tfunc; + break; + } + } + if(!tfunc) return false; + + switch(ds.type) { + case DVT_DB: + return tfunc(hContact, ds.module_name, ds.setting_name, buff, bufflen) != 0; + case DVT_PROTODB: + { + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if(proto) { + return tfunc(hContact, proto, ds.setting_name, buff, bufflen) != 0; + } + } + break; + } + return false; +} + +bool GetRawSubstText(HANDLE hContact, char *raw_spec, TCHAR *buff, int bufflen) { + int len = strlen(raw_spec); + for(int i = 0; i < len; i++) { + if(raw_spec[i] == '/') { + raw_spec[i] = 0; + if(strlen(raw_spec) == 0) { + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if(proto) + return translations[0].tfunc(hContact, proto, &raw_spec[i + 1], buff, bufflen) != 0; + else + return false; + } else + return translations[0].tfunc(hContact, raw_spec, &raw_spec[i + 1], buff, bufflen) != 0; + } + } + return false; +} + +bool ApplySubst(HANDLE hContact, const TCHAR *source, TCHAR *dest, int dest_len) { + // hack - allow empty strings before passing to variables (note - zero length strings return false after this) + if(dest && source &&_tcslen(source) == 0) { + dest[0] = 0; + return true; + } + + // pass to variables plugin if available + TCHAR *var_src = variables_parsedup((TCHAR *)source, 0, hContact); + //TCHAR *var_src = wcsdup(source); // disable variables + int source_len = _tcslen(var_src); + + int si = 0, di = 0, v = 0; + TCHAR vname[LABEL_LEN]; + TCHAR rep[VALUE_LEN], alt[VALUE_LEN]; + while(si < source_len && di < dest_len - 1) { + if(var_src[si] == _T('%')) { + si++; + v = 0; + while(si < source_len && v < LABEL_LEN) { + if(var_src[si] == _T('%')) { + // two %'s in a row in variable name disabled: e.g. %a%%b% - this is ambiguous] + //if(si + 1 < source_len && var_src[si + 1] == _T('%')) { + // si++; // skip first %, allow following code to add the second one to the variable name + //} else + break; + } + vname[v] = var_src[si]; + v++; si++; + } + if(v == 0) { // subst len is 0 - just a % symbol + dest[di] = _T('%'); + } else if(si < source_len) { // we found end % + vname[v] = 0; + + bool alt_subst = false; + bool subst = false; + + // apply only to specific protocol + TCHAR *p = _tcsrchr(vname, _T('^')); // use last '^', so if you want a ^ in alt text, you can just put a '^' on the end + if(p) { + *p = 0; + p++; + if(*p) { + bool negate = false; + if(*p == _T('!')) { + p++; + if(*p == 0) goto error; + negate = true; + } + + char sproto[256], *cp; + WideCharToMultiByte(code_page, 0, p, -1, sproto, 256, 0, 0); + cp = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if(cp == 0 || (negate ? stricmp(cp, sproto) == 0 : stricmp(cp, sproto) != 0)) + goto empty; + } + } + + // get alternate text, if subst fails + alt[0] = 0; + p = _tcschr(vname, _T('|')); // use first '|' - so you can use the '|' symbol in alt text + if(p) { + *p = 0; // clip alt from vname + alt_subst = true; + + p++; + _tcsncpy(alt, p, VALUE_LEN); + alt[VALUE_LEN - 1] = 0; + } + + // get subst text + if(v > 4 && _tcsncmp(vname, _T("raw:"), 4) == 0) { // raw db substitution + char raw_spec[LABEL_LEN]; + WideCharToMultiByte(code_page, 0, &vname[4], -1, raw_spec, LABEL_LEN, 0, 0); + subst = GetRawSubstText(hContact, raw_spec, rep, VALUE_LEN); + } else if(v > 4 && _tcsncmp(vname, _T("sys:"), 4) == 0) { // 'system' substitution + subst = GetSysSubstText(hContact, &vname[4], rep, VALUE_LEN); + } else { + // see if we can find the subst + DSListNode *ds_node = options.ds_list; + while(ds_node) { + if(_tcscmp(ds_node->ds.name, vname) == 0) + break; + + ds_node = ds_node->next; + } + if(!ds_node) goto error; // no such subst + + subst = GetSubstText(hContact, ds_node->ds, rep, VALUE_LEN); + } + + if(subst) { + int rep_len = _tcslen(rep); + wcsncpy(&dest[di], rep, min(rep_len, dest_len - di)); + di += rep_len - 1; // -1 because we inc at bottom of loop + } else if(alt_subst) { + int alt_len = _tcslen(alt); + wcsncpy(&dest[di], alt, min(alt_len, dest_len - di)); + di += alt_len - 1; // -1 because we inc at bottom of loop + } else + goto empty; // empty value + + } else // no end % - error + goto error; + + } else { + dest[di] = var_src[si]; + } + + si++; + di++; + } + + free(var_src); + dest[di] = 0; + + // check for a 'blank' string - just spaces etc + for(si = 0; si <= di; si++) { + if(dest[si] != 0 && dest[si] != _T(' ') && dest[si] != _T('\t') && dest[si] != _T('\r') && dest[si] != _T('\n')) + return true; + } + + return false; + +empty: + free(var_src); + return false; + +error: + dest[0] = _T('*'); + dest[1] = 0; + free(var_src); + return true; +} + +bool GetLabelText(HANDLE hContact, const DisplayItem &di, TCHAR *buff, int bufflen) { + return ApplySubst(hContact, di.label, buff, bufflen); + +} + +bool GetValueText(HANDLE hContact, const DisplayItem &di, TCHAR *buff, int bufflen) { + return ApplySubst(hContact, di.value, buff, bufflen); +} + diff --git a/tipper/subst.h b/tipper/subst.h new file mode 100644 index 0000000..7fbf5f1 --- /dev/null +++ b/tipper/subst.h @@ -0,0 +1,14 @@ +#ifndef _SUBST_INC +#define _SUBST_INC + +#include "options.h" +#include "translations.h" + +//TCHAR *GetLastMessageText(HANDLE hContact); +//TCHAR *GetStatusMessageText(HANDLE hContact); +bool GetLabelText(HANDLE hContact, const DisplayItem &di, TCHAR *buff, int bufflen); +bool GetValueText(HANDLE hContact, const DisplayItem &di, TCHAR *buff, int bufflen); + +void StripBBCodesInPlace(wchar_t *text); + +#endif diff --git a/tipper/tipper.cpp b/tipper/tipper.cpp new file mode 100644 index 0000000..cd8085b --- /dev/null +++ b/tipper/tipper.cpp @@ -0,0 +1,295 @@ +// popups2.cpp : Defines the entry point for the DLL application. +// + +#include "common.h" +#include "tipper.h" +#include "version.h" +#include "message_pump.h" +#include "options.h" +#include "popwin.h" + +HMODULE hInst = 0; +HANDLE mainThread = 0; + +int code_page = CP_ACP; + + +FontIDW font_id_title = {0}, font_id_labels = {0}, font_id_values = {0}; +ColourIDW colour_id_bg = {0}, colour_id_border = {0}, colour_id_divider = {0}, colour_id_sidebar = {0}; +HFONT hFontTitle = 0, hFontLabels = 0, hFontValues = 0; + +// hooked here so it's in the main thread +HANDLE hAvChangeEvent = 0, hShowTipEvent = 0, hHideTipEvent = 0, hAckEvent = 0, hFramesSBShow = 0, hFramesSBHide; + +HANDLE hShowTipService = 0, hShowTipWService = 0, hHideTipService = 0; + +struct MM_INTERFACE memoryManagerInterface = {0}; + +PLUGININFO pluginInfo={ + sizeof(PLUGININFO), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESC, + __AUTHOR, + __AUTHOREMAIL, + __COPYRIGHT, + __AUTHORWEB, + 0, //not transient + 0 //doesn't replace anything built-in +}; + +PLUGINLINK *pluginLink = 0; + +extern "C" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + hInst = hModule; + DisableThreadLibraryCalls(hInst); + return TRUE; +} + +extern "C" __declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion) +{ + return &pluginInfo; +} + +int ReloadFont(WPARAM wParam, LPARAM lParam) { + LOGFONT log_font; + + if(hFontTitle) DeleteObject(hFontTitle); + options.title_col = CallService(MS_FONT_GETW, (WPARAM)&font_id_title, (LPARAM)&log_font); + hFontTitle = CreateFontIndirect(&log_font); + if(hFontLabels) DeleteObject(hFontLabels); + options.label_col = CallService(MS_FONT_GETW, (WPARAM)&font_id_labels, (LPARAM)&log_font); + hFontLabels = CreateFontIndirect(&log_font); + if(hFontValues) DeleteObject(hFontValues); + options.value_col = CallService(MS_FONT_GETW, (WPARAM)&font_id_values, (LPARAM)&log_font); + hFontValues = CreateFontIndirect(&log_font); + + options.bg_col = CallService(MS_COLOUR_GETW, (WPARAM)&colour_id_bg, 0); + options.border_col = CallService(MS_COLOUR_GETW, (WPARAM)&colour_id_border, 0); + options.sidebar_col = CallService(MS_COLOUR_GETW, (WPARAM)&colour_id_sidebar, 0); + options.div_col = CallService(MS_COLOUR_GETW, (WPARAM)&colour_id_divider, 0); + + return 0; +} + +int ModulesLoaded(WPARAM wParam, LPARAM lParam) { + if(ServiceExists(MS_UPDATE_REGISTER)) { + // register with updater + Update update = {0}; + char szVersion[16]; + + update.cbSize = sizeof(Update); + + update.szComponentName = pluginInfo.shortName; + update.pbVersion = (BYTE *)CreateVersionString(pluginInfo.version, szVersion); + update.cpbVersion = strlen((char *)update.pbVersion); + + update.szUpdateURL = UPDATER_AUTOREGISTER; + + // these are the three lines that matter - the archive, the page containing the version string, and the text (or data) + // before the version that we use to locate it on the page + // (note that if the update URL and the version URL point to standard file listing entries, the backend xml + // data will be used to check for updates rather than the actual web page - this is not true for beta urls) + update.szBetaUpdateURL = "http://www.scottellis.com.au/miranda_plugins/tipper.zip"; + update.szBetaVersionURL = "http://www.scottellis.com.au/miranda_plugins/ver_tipper.html"; + update.pbBetaVersionPrefix = (BYTE *)"Tipper version "; + + update.cpbBetaVersionPrefix = strlen((char *)update.pbBetaVersionPrefix); + + CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); + } + + if(ServiceExists(MS_FONT_REGISTERW)) { + font_id_title.cbSize = sizeof(FontIDW); + font_id_title.flags = FIDF_ALLOWEFFECTS; + wcscpy(font_id_title.group, TranslateT("Tooltips")); + wcscpy(font_id_title.name, TranslateT("Title")); + strcpy(font_id_title.dbSettingsGroup, MODULE); + strcpy(font_id_title.prefix, "FontFirst"); + font_id_title.order = 0; + + font_id_title.deffontsettings.charset = DEFAULT_CHARSET; + font_id_title.deffontsettings.size = -14; + font_id_title.deffontsettings.style = DBFONTF_BOLD; + font_id_title.deffontsettings.colour = RGB(255, 0, 0); + font_id_title.flags |= FIDF_DEFAULTVALID; + + font_id_labels.cbSize = sizeof(FontIDW); + font_id_labels.flags = FIDF_ALLOWEFFECTS; + wcscpy(font_id_labels.group, TranslateT("Tooltips")); + wcscpy(font_id_labels.name, TranslateT("Labels")); + strcpy(font_id_labels.dbSettingsGroup, MODULE); + strcpy(font_id_labels.prefix, "FontLabels"); + font_id_labels.order = 1; + + font_id_labels.deffontsettings.charset = DEFAULT_CHARSET; + font_id_labels.deffontsettings.size = -12; + font_id_labels.deffontsettings.style = DBFONTF_ITALIC; + font_id_labels.deffontsettings.colour = RGB(128, 128, 128); + font_id_labels.flags |= FIDF_DEFAULTVALID; + + font_id_values.cbSize = sizeof(FontIDW); + font_id_values.flags = FIDF_ALLOWEFFECTS; + wcscpy(font_id_values.group, TranslateT("Tooltips")); + wcscpy(font_id_values.name, TranslateT("Values")); + strcpy(font_id_values.dbSettingsGroup, MODULE); + strcpy(font_id_values.prefix, "FontValues"); + font_id_values.order = 2; + + font_id_values.deffontsettings.charset = DEFAULT_CHARSET; + font_id_values.deffontsettings.size = -12; + font_id_values.deffontsettings.style = 0; + font_id_values.deffontsettings.colour = RGB(0, 0, 0); + font_id_values.flags |= FIDF_DEFAULTVALID; + + CallService(MS_FONT_REGISTERW, (WPARAM)&font_id_title, 0); + CallService(MS_FONT_REGISTERW, (WPARAM)&font_id_labels, 0); + CallService(MS_FONT_REGISTERW, (WPARAM)&font_id_values, 0); + + colour_id_bg.cbSize = sizeof(ColourIDW); + wcscpy(colour_id_bg.group, TranslateT("Tooltips")); + wcscpy(colour_id_bg.name, TranslateT("Background")); + strcpy(colour_id_bg.dbSettingsGroup, MODULE); + strcpy(colour_id_bg.setting, "ColourBg"); + colour_id_bg.defcolour = RGB(219, 219, 219); + colour_id_bg.order = 0; + + colour_id_border.cbSize = sizeof(ColourIDW); + wcscpy(colour_id_border.group, TranslateT("Tooltips")); + wcscpy(colour_id_border.name, TranslateT("Border")); + strcpy(colour_id_border.dbSettingsGroup, MODULE); + strcpy(colour_id_border.setting, "BorderCol"); + colour_id_border.defcolour = 0; + colour_id_border.order = 0; + + colour_id_divider.cbSize = sizeof(ColourIDW); + wcscpy(colour_id_divider.group, TranslateT("Tooltips")); + wcscpy(colour_id_divider.name, TranslateT("Dividers")); + strcpy(colour_id_divider.dbSettingsGroup, MODULE); + strcpy(colour_id_divider.setting, "DividerCol"); + colour_id_divider.defcolour = 0; + colour_id_divider.order = 0; + + colour_id_sidebar.cbSize = sizeof(ColourIDW); + wcscpy(colour_id_sidebar.group, TranslateT("Tooltips")); + wcscpy(colour_id_sidebar.name, TranslateT("Sidebar")); + strcpy(colour_id_sidebar.dbSettingsGroup, MODULE); + strcpy(colour_id_sidebar.setting, "SidebarCol"); + colour_id_sidebar.defcolour = RGB(192, 192, 192); + colour_id_sidebar.order = 0; + + CallService(MS_COLOUR_REGISTERW, (WPARAM)&colour_id_bg, 0); + CallService(MS_COLOUR_REGISTERW, (WPARAM)&colour_id_border, 0); + CallService(MS_COLOUR_REGISTERW, (WPARAM)&colour_id_divider, 0); + CallService(MS_COLOUR_REGISTERW, (WPARAM)&colour_id_sidebar, 0); + + ReloadFont(0, 0); + + HookEvent(ME_FONT_RELOAD, ReloadFont); + } else { + options.title_col = RGB(255, 0, 0); options.label_col = RGB(128, 128, 128), options.value_col = 0; + options.bg_col = RGB(219, 219, 219); + options.border_col = options.div_col = 0; + options.sidebar_col = RGB(192, 192, 192); + + LOGFONT lf = {0}; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfHeight = -14; + lf.lfWeight = FW_BOLD; + hFontTitle = CreateFontIndirect(&lf); + + lf.lfHeight = -12; + lf.lfWeight = 0; + lf.lfItalic = TRUE; + hFontLabels = CreateFontIndirect(&lf); + + lf.lfHeight = -12; + lf.lfWeight = 0; + lf.lfItalic = FALSE; + hFontValues = CreateFontIndirect(&lf); + } + + hAvChangeEvent = HookEvent(ME_AV_AVATARCHANGED, AvatarChanged); + hShowTipEvent = HookEvent(ME_CLC_SHOWINFOTIP, ShowTip); + hHideTipEvent = HookEvent(ME_CLC_HIDEINFOTIP, HideTip); + hAckEvent = HookEvent(ME_PROTO_ACK, ProtoAck); + + hFramesSBShow = HookEvent(ME_CLIST_FRAMES_SB_SHOW_TOOLTIP, FramesShowSBTip); + hFramesSBHide = HookEvent(ME_CLIST_FRAMES_SB_HIDE_TOOLTIP, FramesHideSBTip); + + LoadOptions(); + + // set 'time-in' + CallService(MS_CLC_SETINFOTIPHOVERTIME, options.time_in, 0); + + return 0; +} + +int Shutdown(WPARAM wParam, LPARAM lParam) { + if(hFramesSBShow) UnhookEvent(hFramesSBShow); + if(hFramesSBHide) UnhookEvent(hFramesSBHide); + if(hAvChangeEvent) UnhookEvent(hAvChangeEvent); + if(hShowTipEvent) UnhookEvent(hShowTipEvent); + if(hHideTipEvent) UnhookEvent(hHideTipEvent); + if(hAckEvent) UnhookEvent(hAckEvent); + + if(hShowTipService) DestroyServiceFunction(hShowTipService); + if(hShowTipWService) DestroyServiceFunction(hShowTipWService); + if(hHideTipService) DestroyServiceFunction(hHideTipService); + + DeinitMessagePump(); + + return 0; +} + +HANDLE hEventPreShutdown, hEventModulesLoaded; + +extern "C" int TIPPER_API Load(PLUGINLINK *link) { + pluginLink = link; + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &mainThread, THREAD_SET_CONTEXT, FALSE, 0); + + // get the internal malloc/free() + memoryManagerInterface.cbSize = sizeof(memoryManagerInterface); + CallService(MS_SYSTEM_GET_MMI, 0, (LPARAM)&memoryManagerInterface); + + // don't save status messages + CallService(MS_DB_SETSETTINGRESIDENT, (WPARAM)TRUE, (LPARAM)MODULE "/TempStatusMsg"); + + // Ensure that the common control DLL is loaded (for listview) + INITCOMMONCONTROLSEX icex; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx(&icex); + + if(ServiceExists(MS_LANGPACK_GETCODEPAGE)) + code_page = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0); + + InitTranslations(); + InitMessagePump(); + InitOptions(); + + // for compatibility with mToolTip status tooltips + hShowTipService = CreateServiceFunction("mToolTip/ShowTip", ShowTip); + hShowTipWService = CreateServiceFunction("mToolTip/ShowTipW", ShowTipW); + hHideTipService = CreateServiceFunction("mToolTip/HideTip", HideTip); + + hEventPreShutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, Shutdown); + hEventModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); + + return 0; +} + +extern "C" int TIPPER_API Unload() { + UnhookEvent(hEventPreShutdown); + UnhookEvent(hEventModulesLoaded); + + DeinitOptions(); + if(ServiceExists(MS_FONT_REGISTERW)) { + DeleteObject(hFontTitle); + DeleteObject(hFontLabels); + DeleteObject(hFontValues); + } // otherwise, no need to delete the handle + DeinitTranslations(); + return 0; +} diff --git a/tipper/tipper.h b/tipper/tipper.h new file mode 100644 index 0000000..94e4778 --- /dev/null +++ b/tipper/tipper.h @@ -0,0 +1,12 @@ +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the POPUPS2_EXPORTS +// symbol defined on the command line. this symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// POPUPS2_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +#ifdef TIPPER_EXPORTS +#define TIPPER_API __declspec(dllexport) +#else +#define TIPPER_API __declspec(dllimport) +#endif + diff --git a/tipper/tipper.mdsp b/tipper/tipper.mdsp new file mode 100644 index 0000000..7d09545 --- /dev/null +++ b/tipper/tipper.mdsp @@ -0,0 +1,107 @@ +[Project] +name=tipper +type=2 +defaultConfig=0 + + +[Debug] +// compiler +workingDirectory= +arguments= +intermediateFilesDirectory=Debug +outputFilesDirectory=Debug +compilerPreprocessor=UNICODE,_UNICODE,TIPPER_EXPORTS +extraCompilerOptions= +compilerIncludeDirectory=..\..\include +noWarning=0 +defaultWarning=0 +allWarning=1 +extraWarning=0 +isoWarning=0 +warningsAsErrors=0 +debugType=1 +debugLevel=2 +exceptionEnabled=1 +runtimeTypeEnabled=1 +optimizeLevel=0 + +// linker +libraryPath= +outputFilename=Debug\tipper.dll +libraries=unicows,gdi32,comctl32,ws2_32 +extraLinkerOptions= +ignoreStartupFile=0 +ignoreDefaultLibs=0 +stripExecutableFile=0 + +// archive +extraArchiveOptions= + +//resource +resourcePreprocessor= +resourceIncludeDirectory= +extraResourceOptions= + +[Release] +// compiler +workingDirectory= +arguments= +intermediateFilesDirectory=Release +outputFilesDirectory=Release +compilerPreprocessor=UNICODE,_UNICODE,TIPPER_EXPORTS +extraCompilerOptions= +compilerIncludeDirectory=..\..\include +noWarning=0 +defaultWarning=0 +allWarning=1 +extraWarning=0 +isoWarning=0 +warningAsErrors=0 +debugType=1 +debugLevel=2 +exceptionEnabled=0 +runtimeTypeEnabled=0 +optimizeLevel=4 + +// linker +libraryPath= +outputFilename=Release\tipper.dll +libraries=unicows,gdi32,comctl32,ws2_32 +extraLinkerOptions= +ignoreStartupFile=0 +ignoreDefaultLibs=0 +stripExecutableFile=1 + +// archive +extraArchiveOptions= + +//resource +resourcePreprocessor= +resourceIncludeDirectory= +extraResourceOptions= + +[Source] +1=message_pump.cpp +2=options.cpp +3=popwin.cpp +4=tipper.cpp +5=translations.cpp +6=subst.cpp +[Header] +1=m_tipper.h +2=message_pump.h +3=options.h +4=popwin.h +5=resource.h +6=stdafx.h +7=tipper.h +8=translations.h +9=version.h +10=subst.h +[Resource] +1=resource.rc +[Other] +[History] +tipper.cpp,3326 +message_pump.cpp,1325 +version.h,175 diff --git a/tipper/tipper.rc b/tipper/tipper.rc new file mode 100644 index 0000000..4cd63ae --- /dev/null +++ b/tipper/tipper.rc @@ -0,0 +1,239 @@ +// Microsoft Visual C++ generated resource script. +// +#include "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 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPT1 DIALOGEX 0, 0, 265, 221 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_ED_WIDTH,210,11,31,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_WIDTH,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,239,11,12,12 + EDITTEXT IDC_ED_MAXHEIGHT,210,39,31,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_MAXHEIGHT,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,239,39,12,12 + GROUPBOX "Options",IDC_STATIC,147,115,111,99 + EDITTEXT IDC_ED_TRANS,87,189,33,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_TRANS,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,118,189,13,12 + RTEXT "Opacity(%):",IDC_STATIC,13,192,69,8,0,WS_EX_RIGHT + CONTROL "Border",IDC_CHK_BORDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,156,156,84,9 + CONTROL "Round corners (window)",IDC_CHK_ROUNDCORNERS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,156,171,100,9 + CONTROL "Animate",IDC_CHK_ANIMATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,156,201,84,9 + CONTROL "Transparent background",IDC_CHK_TRANSBG,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,18,203,116,10,WS_EX_RIGHT + GROUPBOX "Layout",IDC_STATIC,7,3,116,130 + EDITTEXT IDC_ED_AVSIZE,210,67,31,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_AVSIZE,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,239,67,12,12 + COMBOBOX IDC_CMB_ICON,12,26,107,69,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CMB_AV,12,40,107,76,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_ED_INDENT,78,56,35,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_INDENT,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,109,56,12,12 + RTEXT "Text indent:",IDC_STATIC,26,59,46,8 + GROUPBOX "Window",IDC_STATIC,128,3,130,110 + RTEXT "Max width:",IDC_STATIC,152,14,52,8,0,WS_EX_RIGHT + RTEXT "Max height:",IDC_STATIC,147,41,57,8,0,WS_EX_RIGHT + RTEXT "Avatar size:",IDC_STATIC,147,69,57,8,0,WS_EX_RIGHT + CONTROL "Show if list not focused",IDC_CHK_NOFOCUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,136,98,117,11 + EDITTEXT IDC_ED_PADDING,78,70,35,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_PADDING,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,109,70,12,12 + RTEXT "General padding:",IDC_STATIC,11,72,60,8 + EDITTEXT IDC_ED_HOVER,210,81,31,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_HOVER,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,239,81,12,12 + RTEXT "Hover time:",IDC_STATIC,147,83,57,8,0,WS_EX_RIGHT + GROUPBOX "XP+",IDC_STATIC,6,182,137,32 + COMBOBOX IDC_CMB_POS,12,12,107,69,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Shadow",IDC_CHK_SHADOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,156,141,84,9 + CONTROL "Status bar tips",IDC_CHK_SBAR,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,156,126,85,9 + GROUPBOX "Alignment",IDC_STATIC,7,135,136,45 + COMBOBOX IDC_CMB_LV,49,146,43,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CMB_VV,49,162,43,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CMB_LH,96,146,43,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CMB_VH,96,162,43,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + RTEXT "Labels:",IDC_STATIC,11,148,34,8 + RTEXT "Values:",IDC_STATIC,11,164,34,8 + EDITTEXT IDC_ED_MINWIDTH,210,25,31,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_MINWIDTH,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,239,25,12,12 + RTEXT "Min width:",IDC_STATIC,152,27,52,8,0,WS_EX_RIGHT + EDITTEXT IDC_ED_MINHEIGHT,210,53,31,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_MINHEIGHT,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,239,52,12,12 + RTEXT "Min height:",IDC_STATIC,146,55,57,8,0,WS_EX_RIGHT + EDITTEXT IDC_ED_SBWIDTH,78,112,35,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_SBWIDTH,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,110,112,11,12 + RTEXT "Sidebar width:",IDC_STATIC,14,114,60,8 + EDITTEXT IDC_ED_TEXTPADDING,78,84,35,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_TEXTPADDING,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,110,84,12,12 + RTEXT "Text padding:",IDC_STATIC,12,85,59,8 + EDITTEXT IDC_ED_AVPADDING,78,98,35,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_AVPADDING,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,110,98,12,12 + RTEXT "Avatar padding:",IDC_STATIC,12,99,59,8 + CONTROL "Round corners (avatar)",IDC_CHK_ROUNDCORNERSAV,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,156,186,100,9 +END + +IDD_SUBST DIALOGEX 0, 0, 253, 182 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Substitution" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + RTEXT "Label:",IDC_STATIC,31,21,48,8,0,WS_EX_RIGHT + EDITTEXT IDC_ED_LABEL,84,19,115,14,ES_AUTOHSCROLL + CONTROL "Contact protocol module",IDC_CHK_PROTOMOD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,61,52,141,10 + RTEXT "Module:",IDC_STATIC,21,74,69,8,0,WS_EX_RIGHT + EDITTEXT IDC_ED_MODULE,95,71,115,14,ES_AUTOHSCROLL + RTEXT "Setting or prefix:",IDC_STATIC,21,92,69,8,0,WS_EX_RIGHT + EDITTEXT IDC_ED_SETTING,95,89,115,14,ES_AUTOHSCROLL + LTEXT "Translation:",IDC_STATIC,46,118,139,8 + COMBOBOX IDC_CMB_TRANSLATE,7,134,239,104,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,72,161,50,14 + PUSHBUTTON "Cancel",IDCANCEL,130,161,50,14 +END + +IDD_ITEM DIALOGEX 0, 0, 207, 178 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Item" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Label:",IDC_STATIC,32,17,52,8 + LTEXT "Value:",IDC_STATIC,32,49,52,8 + EDITTEXT IDC_ED_LABEL,36,30,136,13,ES_AUTOHSCROLL + EDITTEXT IDC_ED_VALUE,36,61,136,45,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + CONTROL "Draw a line above",IDC_CHK_LINEABOVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,53,112,101,10 + CONTROL "Value on a new line",IDC_CHK_VALNEWLINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,53,127,101,10 + DEFPUSHBUTTON "OK",IDOK,51,157,50,14 + PUSHBUTTON "Cancel",IDCANCEL,107,157,50,14 +END + +IDD_OPT2 DIALOGEX 0, 0, 289, 179 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Items",IDC_STATIC,7,3,135,169 + LISTBOX IDC_LST_ITEMS,12,13,78,153,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_BTN_ADD,92,13,48,15 + PUSHBUTTON "Delete",IDC_BTN_REMOVE,92,30,48,15 + PUSHBUTTON "Up",IDC_BTN_UP,92,47,48,15 + PUSHBUTTON "Down",IDC_BTN_DOWN,92,64,48,15 + GROUPBOX "Substitutions",IDC_STATIC,147,3,135,169 + PUSHBUTTON "Edit...",IDC_BTN_EDIT,92,81,48,15 + LISTBOX IDC_LST_SUBST,152,13,78,153,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_BTN_ADD2,232,13,48,15 + PUSHBUTTON "Delete",IDC_BTN_REMOVE2,232,30,48,15 + PUSHBUTTON "Edit...",IDC_BTN_EDIT2,232,81,48,15 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPT1, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 258 + TOPMARGIN, 7 + BOTTOMMARGIN, 214 + END + + IDD_SUBST, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 246 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_ITEM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 200 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + END + + IDD_OPT2, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 282 + TOPMARGIN, 7 + BOTTOMMARGIN, 172 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/tipper/tipper.sln b/tipper/tipper.sln new file mode 100644 index 0000000..a63a865 --- /dev/null +++ b/tipper/tipper.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tipper", "tipper.vcproj", "{2C818919-A38F-44FF-BD91-A6A204AC592A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2C818919-A38F-44FF-BD91-A6A204AC592A}.Debug|Win32.ActiveCfg = Debug|Win32 + {2C818919-A38F-44FF-BD91-A6A204AC592A}.Debug|Win32.Build.0 = Debug|Win32 + {2C818919-A38F-44FF-BD91-A6A204AC592A}.Release|Win32.ActiveCfg = Release|Win32 + {2C818919-A38F-44FF-BD91-A6A204AC592A}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tipper/tipper.vcproj b/tipper/tipper.vcproj new file mode 100644 index 0000000..9c3c48a --- /dev/null +++ b/tipper/tipper.vcproj @@ -0,0 +1,376 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tipper/translations.cpp b/tipper/translations.cpp new file mode 100644 index 0000000..f22e726 --- /dev/null +++ b/tipper/translations.cpp @@ -0,0 +1,654 @@ +#include "common.h" +#include "translations.h" +#include +#include + +int num_tfuncs = 0; +DBVTranslation *translations = 0; + +DWORD next_func_id; + +bool unicode_system; +HANDLE hServiceAdd; + +void AddTranslation(DBVTranslation *new_trans) { + num_tfuncs++; + + translations = (DBVTranslation *)realloc(translations, sizeof(DBVTranslation) * num_tfuncs); + translations[num_tfuncs - 1] = *new_trans; + + char setting[256]; +#ifdef _UNICODE + WideCharToMultiByte(code_page, 0, new_trans->name, -1, setting, 256, 0, 0); +#else + strncpy(setting, new_trans.name, 256); +#endif + + if(_tcscmp(new_trans->name, _T("[No translation]")) == 0) + translations[num_tfuncs - 1].id = 0; + else { + DWORD id = DBGetContactSettingDword(0, MODULE, setting, 0); + if(id != 0) { + translations[num_tfuncs - 1].id = id; + if(next_func_id <= id) next_func_id = id + 1; + } else { + translations[num_tfuncs - 1].id = next_func_id++; + DBWriteContactSettingDword(0, MODULE, setting, translations[num_tfuncs - 1].id); + } + + DBWriteContactSettingDword(0, MODULE, "NextFuncId", next_func_id); + } +} + +TCHAR *null_translation(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + DBVARIANT dbv; + buff[0] = 0; + if(ServiceExists(MS_DB_CONTACT_GETSETTING_STR)) { + if(DBGetContactSettingStringUtf(hContact, module_name, setting_name, &dbv)) + return 0; + } else { + if(DBGetContactSetting(hContact, module_name, setting_name, &dbv)) + return 0; + } + + switch(dbv.type) { + case DBVT_DELETED: + DBFreeVariant(&dbv); + return 0; + case DBVT_BYTE: + _itow(dbv.bVal, buff, 10); + break; + case DBVT_WORD: + _ltow(dbv.wVal, buff, 10); + break; + case DBVT_DWORD: + _ltow(dbv.dVal, buff, 10); + break; + case DBVT_ASCIIZ: + MultiByteToWideChar(code_page, 0, dbv.pszVal, -1, buff, bufflen); + buff[bufflen - 1] = 0; + break; + case DBVT_UTF8: + MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, buff, bufflen); + buff[bufflen - 1] = 0; + break; + //case DBVT_WCHAR: + //wcsncpy(buff, dbv.pwszVal, 1024); + //buff[bufflen - 1] = 0; + //break; + case DBVT_BLOB: + default: + DBFreeVariant(&dbv); + return 0; + + + } + DBFreeVariant(&dbv); + + if(wcslen(buff) == 0) + return 0; + return buff; +} + +TCHAR *timestamp_to_short_date(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + DWORD ts = DBGetContactSettingDword(hContact, module_name, setting_name, 0); + if(ts == 0) return 0; + + if(unicode_system) { + DBTIMETOSTRINGT dbt = {0}; + dbt.cbDest = bufflen; + dbt.szDest = buff; + dbt.szFormat = _T("d"); + CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, (WPARAM)ts, (LPARAM)&dbt); + } else { + char buffA[1024]; + DBTIMETOSTRING dbt = {0}; + dbt.cbDest = sizeof(buffA); + dbt.szDest = buffA; + dbt.szFormat = "d"; + CallService(MS_DB_TIME_TIMESTAMPTOSTRING, (WPARAM)ts, (LPARAM)&dbt); + MultiByteToWideChar(code_page, 0, buffA, -1, buff, bufflen); + } + + buff[bufflen - 1] = 0; + return buff; +} + +TCHAR *timestamp_to_long_date(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + DWORD ts = DBGetContactSettingDword(hContact, module_name, setting_name, 0); + if(ts == 0) return 0; + + if(unicode_system) { + DBTIMETOSTRINGT dbt = {0}; + dbt.cbDest = bufflen; + dbt.szDest = buff; + dbt.szFormat = _T("D"); + CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, (WPARAM)ts, (LPARAM)&dbt); + } else { + char buffA[1024]; + DBTIMETOSTRING dbt = {0}; + dbt.cbDest = sizeof(buffA); + dbt.szDest = buffA; + dbt.szFormat = "D"; + CallService(MS_DB_TIME_TIMESTAMPTOSTRING, (WPARAM)ts, (LPARAM)&dbt); + MultiByteToWideChar(code_page, 0, buffA, -1, buff, bufflen); + } + + buff[bufflen - 1] = 0; + return buff; +} + +TCHAR *timestamp_to_time(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + DWORD ts = DBGetContactSettingDword(hContact, module_name, setting_name, 0); + if(ts == 0) return 0; + + if(unicode_system) { + DBTIMETOSTRINGT dbt = {0}; + dbt.cbDest = bufflen; + dbt.szDest = buff; + dbt.szFormat = _T("s"); + CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, (WPARAM)ts, (LPARAM)&dbt); + } else { + char buffA[1024]; + DBTIMETOSTRING dbt = {0}; + dbt.cbDest = sizeof(buffA); + dbt.szDest = buffA; + dbt.szFormat = "s"; + CallService(MS_DB_TIME_TIMESTAMPTOSTRING, (WPARAM)ts, (LPARAM)&dbt); + MultiByteToWideChar(code_page, 0, buffA, -1, buff, bufflen); + } + + buff[bufflen - 1] = 0; + return buff; +} + +TCHAR *timestamp_to_time_no_secs(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + DWORD ts = DBGetContactSettingDword(hContact, module_name, setting_name, 0); + if(ts == 0) return 0; + + if(unicode_system) { + DBTIMETOSTRINGT dbt = {0}; + dbt.cbDest = bufflen; + dbt.szDest = buff; + dbt.szFormat = _T("t"); + CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT, (WPARAM)ts, (LPARAM)&dbt); + } else { + char buffA[1024]; + DBTIMETOSTRING dbt = {0}; + dbt.cbDest = sizeof(buffA); + dbt.szDest = buffA; + dbt.szFormat = "t"; + CallService(MS_DB_TIME_TIMESTAMPTOSTRING, (WPARAM)ts, (LPARAM)&dbt); + MultiByteToWideChar(code_page, 0, buffA, -1, buff, bufflen); + } + + return buff; +} + +TCHAR *timestamp_to_time_difference(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + DWORD ts = DBGetContactSettingDword(hContact, module_name, setting_name, 0); + DWORD t = (DWORD)time(0); + if(ts == 0) return 0; + + DWORD diff = (t - ts); + int d = (diff / 60 / 60 / 24); + int h = (diff - d * 60 * 60 * 24) / 60 / 60; + int m = (diff - d * 60 * 60 * 24 - h * 60 * 60) / 60; + if(d > 0) + mir_sntprintf(buff, bufflen, TranslateT("%dd %dh %dm"), d, h, m); + else if(h > 0) + mir_sntprintf(buff, bufflen, TranslateT("%dh %dm"), h, m); + else + mir_sntprintf(buff, bufflen, TranslateT("%dm"), m); + + return buff; +} + +TCHAR *seconds_to_time_difference(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + + DWORD diff = DBGetContactSettingDword(hContact, module_name, setting_name, 0); + int d = (diff / 60 / 60 / 24); + int h = (diff - d * 60 * 60 * 24) / 60 / 60; + int m = (diff - d * 60 * 60 * 24 - h * 60 * 60) / 60; + if(d > 0) + mir_sntprintf(buff, bufflen, TranslateT("%dd %dh %dm"), d, h, m); + else if(h > 0) + mir_sntprintf(buff, bufflen, TranslateT("%dh %dm"), h, m); + else + mir_sntprintf(buff, bufflen, TranslateT("%dm"), m); + + return buff; +} + +TCHAR *word_to_status_desc(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + int status = DBGetContactSettingWord(hContact, module_name, setting_name, ID_STATUS_OFFLINE); + char *strptr = (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)status, (LPARAM)0); + MultiByteToWideChar(code_page, 0, strptr, -1, buff, bufflen); + buff[bufflen - 1] = 0; + return buff; +} + +TCHAR *byte_to_yesno(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + DBVARIANT dbv; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(dbv.type == DBVT_BYTE) { + if(dbv.bVal != 0) + _tcsncpy(buff, _T("Yes"), bufflen); + else + _tcsncpy(buff, _T("No"), bufflen); + buff[bufflen - 1] = 0; + DBFreeVariant(&dbv); + return buff; + } + DBFreeVariant(&dbv); + } + return 0; +} + +TCHAR *byte_to_mf(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + BYTE val = DBGetContactSettingByte(hContact, module_name, setting_name, 0); + if(val == 'F') + _tcsncpy(buff, TranslateT("Female"), bufflen); + else if(val == 'M') + _tcsncpy(buff, TranslateT("Male"), bufflen); + else + return 0; + + buff[bufflen - 1] = 0; + return buff; +} + +TCHAR *word_to_country(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + char *cname = 0; + WORD cid = DBGetContactSettingWord(hContact, module_name, setting_name, (WORD)-1); + if(cid != (WORD)-1 && ServiceExists(MS_UTILS_GETCOUNTRYBYNUMBER) && (cname = (char *)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, cid, 0)) != 0) { + if(strcmp(cname, "Unknown") == 0) + return 0; + MultiByteToWideChar(code_page, 0, Translate(cname), -1, buff, bufflen); + buff[bufflen - 1] = 0; + return buff; + } + return 0; +} + +TCHAR *dword_to_ip(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + DWORD ip = DBGetContactSettingDword(hContact, module_name, setting_name, (WORD)0); + if(ip) { + in_addr in; + in.S_un.S_addr = htonl(ip); + MultiByteToWideChar(code_page, 0, inet_ntoa(in), -1, buff, bufflen); + buff[bufflen - 1] = 0; + return buff; + } + return 0; +} + +bool GetInt(const DBVARIANT &dbv, int *val) { + if(!val) return false; + + switch(dbv.type) { + case DBVT_BYTE: + if(val) *val = (int)dbv.bVal; + return true; + case DBVT_WORD: + if(val) *val = (int)dbv.wVal; + return true; + case DBVT_DWORD: + if(val) *val = (int)dbv.dVal; + return true; + } + return false; +} + +TCHAR *day_month_year_to_date(HANDLE hContact, const char *module_name, const char *prefix, TCHAR *buff, int bufflen) { + DBVARIANT dbv; + char setting_name[256]; + mir_snprintf(setting_name, 256, "%sDay", prefix); + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + int day = 0; + if(GetInt(dbv, &day)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sMonth", prefix); + int month = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(GetInt(dbv, &month)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sYear", prefix); + int year = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(GetInt(dbv, &year)) { + DBFreeVariant(&dbv); + + SYSTEMTIME st = {0}; + st.wDay = day; + st.wMonth = month; + st.wYear = year; + + GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, 0, buff, bufflen); + return buff; + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + return 0; +} + +TCHAR *day_month_year_to_age(HANDLE hContact, const char *module_name, const char *prefix, TCHAR *buff, int bufflen) { + DBVARIANT dbv; + char setting_name[256]; + mir_snprintf(setting_name, 256, "%sDay", prefix); + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + int day = 0; + if(GetInt(dbv, &day)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sMonth", prefix); + int month = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(GetInt(dbv, &month)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sYear", prefix); + int year = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(GetInt(dbv, &year)) { + DBFreeVariant(&dbv); + + SYSTEMTIME now; + GetLocalTime(&now); + + int age = now.wYear - year; + if(now.wMonth < month || (now.wMonth == month && now.wDay < day)) + age--; + mir_sntprintf(buff, bufflen, _T("%d"), age); + return buff; + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + return 0; +} + +TCHAR *hours_minutes_seconds_to_time(HANDLE hContact, const char *module_name, const char *prefix, TCHAR *buff, int bufflen) { + DBVARIANT dbv; + char setting_name[256]; + mir_snprintf(setting_name, 256, "%sHours", prefix); + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + int hours = 0; + if(GetInt(dbv, &hours)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sMinutes", prefix); + int minutes = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(GetInt(dbv, &minutes)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sSeconds", prefix); + int seconds = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + GetInt(dbv, &seconds); + DBFreeVariant(&dbv); + } + + SYSTEMTIME st = {0}; + st.wHour = hours; + st.wMinute = minutes; + st.wSecond = seconds; + + GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, 0, buff, bufflen); + return buff; + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + return 0; +} + +TCHAR *hours_minutes_to_time(HANDLE hContact, const char *module_name, const char *prefix, TCHAR *buff, int bufflen) { + DBVARIANT dbv; + char setting_name[256]; + mir_snprintf(setting_name, 256, "%sHours", prefix); + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + int hours = 0; + if(GetInt(dbv, &hours)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sMinutes", prefix); + int minutes = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(GetInt(dbv, &minutes)) { + DBFreeVariant(&dbv); + + SYSTEMTIME st = {0}; + st.wHour = hours; + st.wMinute = minutes; + + GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, 0, buff, bufflen); + return buff; + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + return 0; +} + +TCHAR *day_month_year_hours_minutes_seconds_to_time_difference(HANDLE hContact, const char *module_name, const char *prefix, TCHAR *buff, int bufflen) { + DBVARIANT dbv; + char setting_name[256]; + mir_snprintf(setting_name, 256, "%sDay", prefix); + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + int day = 0; + if(GetInt(dbv, &day)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sMonth", prefix); + int month = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(GetInt(dbv, &month)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sYear", prefix); + int year = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(GetInt(dbv, &year)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sHours", prefix); + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + int hours = 0; + if(GetInt(dbv, &hours)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sMinutes", prefix); + int minutes = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + if(GetInt(dbv, &minutes)) { + DBFreeVariant(&dbv); + mir_snprintf(setting_name, 256, "%sSeconds", prefix); + int seconds = 0; + if(!DBGetContactSetting(hContact, module_name, setting_name, &dbv)) { + GetInt(dbv, &seconds); + DBFreeVariant(&dbv); + } + + SYSTEMTIME st = {0}, st_now; + st.wDay = day; + st.wMonth = month; + st.wYear = year; + st.wHour = hours; + st.wMinute = minutes; + st.wSecond = seconds; + GetLocalTime(&st_now); + + FILETIME ft, ft_now; + SystemTimeToFileTime(&st, &ft); + SystemTimeToFileTime(&st_now, &ft_now); + + LARGE_INTEGER li, li_now; + li.HighPart = ft.dwHighDateTime; li.LowPart = ft.dwLowDateTime; + li_now.HighPart = ft_now.dwHighDateTime; li_now.LowPart = ft_now.dwLowDateTime; + + long diff = (long)((li_now.QuadPart - li.QuadPart) / (LONGLONG)10000000LL); + int y = diff / 60 / 60 / 24 / 365; + int d = (diff - y * 60 * 60 * 24 * 365) / 60 / 60 / 24; + int h = (diff - y * 60 * 60 * 24 * 365 - d * 60 * 60 * 24) / 60 / 60; + int m = (diff - y * 60 * 60 * 24 * 365 - d * 60 * 60 * 24 - h * 60 * 60) / 60; + if(y != 0) + mir_sntprintf(buff, bufflen, TranslateT("%dy %dd %dh %dm"), y, d, h, m); + else if(d != 0) + mir_sntprintf(buff, bufflen, TranslateT("%dd %dh %dm"), d, h, m); + else if(h != 0) + mir_sntprintf(buff, bufflen, TranslateT("%dh %dm"), h, m); + else + mir_sntprintf(buff, bufflen, TranslateT("%dm"), m); + + return buff; + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + } else + DBFreeVariant(&dbv); + } + return 0; +} + +TCHAR *empty_xStatus_name_to_default_name(HANDLE hContact, const char *module_name, const char *setting_name, TCHAR *buff, int bufflen) { + TCHAR szDefaultName[1024]; + ICQ_CUSTOM_STATUS xstatus={0}; + + if(null_translation(hContact, module_name, setting_name, buff, bufflen)) + return buff; + + int status = (int)DBGetContactSettingByte(hContact, module_name, "XStatusId", 0); + if(!status) return 0; + + xstatus.cbSize = sizeof(ICQ_CUSTOM_STATUS); + xstatus.flags = CSSF_MASK_NAME|CSSF_DEFAULT_NAME|CSSF_TCHAR; + xstatus.ptszName = szDefaultName; + xstatus.wParam = (WPARAM *)&status; + if(CallProtoService(module_name, PS_ICQ_GETCUSTOMSTATUSEX, 0, (LPARAM)&xstatus)) + return 0; + + _tcsncpy(buff, szDefaultName, bufflen); + buff[bufflen - 1] = 0; + + return buff; +} + +int ServiceAddTranslation(WPARAM wParam, LPARAM lParam) { + if(!lParam) return 1; + + DBVTranslation *trans = (DBVTranslation *)lParam; + AddTranslation(trans); + + return 0; +} + +void InitTranslations() { + + char szVer[128]; + unicode_system = (CallService(MS_SYSTEM_GETVERSIONTEXT, (WPARAM)sizeof(szVer), (LPARAM)szVer) == 0 && strstr(szVer, "Unicode")); + + next_func_id = DBGetContactSettingDword(0, MODULE, "NextFuncId", 1); + +#define INT_TRANS_COUNT 18 + DBVTranslation internal_translations[INT_TRANS_COUNT] = { + { + (TranslateFunc*)null_translation, + _T("[No translation]"), + }, + { + (TranslateFunc*)word_to_status_desc, + _T("WORD to status description") + }, + { + (TranslateFunc*)timestamp_to_time, + _T("DWORD timestamp to time") + }, + { + (TranslateFunc*)timestamp_to_time_difference, + _T("DWORD timestamp to time difference") + }, + { + (TranslateFunc*)byte_to_yesno, + _T("BYTE to Yes/No") + }, + { + (TranslateFunc*)byte_to_mf, + _T("BYTE to Male/Female (ICQ)") + }, + { + (TranslateFunc*)word_to_country, + _T("WORD to country name") + }, + { + (TranslateFunc*)dword_to_ip, + _T("DWORD to ip address") + }, + { + (TranslateFunc*)day_month_year_to_date, + _T("Day|Month|Year to date") + }, + { + (TranslateFunc*)day_month_year_to_age, + _T("Day|Month|Year to age") + }, + { + (TranslateFunc*)hours_minutes_seconds_to_time, + _T("Hours|Minutes|Seconds to time") + }, + { + (TranslateFunc*)day_month_year_hours_minutes_seconds_to_time_difference, + _T("Day|Month|Year|Hours|Minutes|Seconds to time difference") + }, + { + (TranslateFunc*)timestamp_to_time_no_secs, + _T("DWORD timestamp to time (no seconds)") + }, + { + (TranslateFunc*)hours_minutes_to_time, + _T("Hours|Minutes to time") + }, + { + (TranslateFunc*)timestamp_to_short_date, + _T("DWORD timestamp to date (short)") + }, + { + (TranslateFunc*)timestamp_to_long_date, + _T("DWORD timestamp to date (long)") + }, + { + (TranslateFunc*)empty_xStatus_name_to_default_name, + _T("xStatus: empty xStatus name to default name") + }, + { + (TranslateFunc*)seconds_to_time_difference, + _T("DWORD seconds to time difference") + } + }; + + for(int i = 0; i < INT_TRANS_COUNT; i++) AddTranslation(&internal_translations[i]); + + hServiceAdd = CreateServiceFunction(MS_TIPPER_ADDTRANSLATION, ServiceAddTranslation); +} + +void DeinitTranslations() { + DestroyServiceFunction(hServiceAdd); + free(translations); +} + diff --git a/tipper/translations.h b/tipper/translations.h new file mode 100644 index 0000000..66dc137 --- /dev/null +++ b/tipper/translations.h @@ -0,0 +1,14 @@ +#ifndef _TRANSLATIONS_INC +#define _TRANSLATIONS_INC + +#include "m_tipper.h" + +extern bool unicode_system; + +extern int num_tfuncs; +extern DBVTranslation *translations; + +void InitTranslations(); +void DeinitTranslations(); + +#endif diff --git a/tipper/version.h b/tipper/version.h new file mode 100644 index 0000000..a3a11e4 --- /dev/null +++ b/tipper/version.h @@ -0,0 +1,23 @@ +#ifndef __VERSION_H_INCLUDED +#define __VERSION_H_INCLUDED + +#define __MAJOR_VERSION 0 +#define __MINOR_VERSION 2 +#define __RELEASE_NUM 4 +#define __BUILD_NUM 1 + +#define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM +#define __FILEVERSION_STRING_DOTS __MAJOR_VERSION.__MINOR_VERSION.__RELEASE_NUM.__BUILD_NUM +#define __STRINGIFY(x) #x +#define __VERSION_STRING __STRINGIFY(__FILEVERSION_STRING_DOTS) + +#define __DESC "Tool Tip notification windows" +#define __AUTHOR "Scott Ellis" +#define __AUTHOREMAIL "mail@scottellis.com.au" +#define __COPYRIGHT "© 2005,2006 Scott Ellis" +#define __AUTHORWEB "http://www.scottellis.com.au" + +#define __PLUGIN_NAME "Tipper" +#define __FILENAME "tipper.dll" + +#endif //__VERSION_H_INCLUDED diff --git a/tipper/version.rc b/tipper/version.rc new file mode 100644 index 0000000..f8bed6d --- /dev/null +++ b/tipper/version.rc @@ -0,0 +1,33 @@ + +#include +#include "version.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION __FILEVERSION_STRING + PRODUCTVERSION __FILEVERSION_STRING + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Author", __AUTHOR + VALUE "FileDescription", __DESC + VALUE "InternalName", __PLUGIN_NAME + VALUE "LegalCopyright", __COPYRIGHT + VALUE "OriginalFilename", __FILENAME + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END -- cgit v1.2.3