summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tipper/common.h82
-rw-r--r--tipper/docs/autoexec_Tipper(Omniwolf).ini339
-rw-r--r--tipper/docs/autoexec_Tipper(Tweety).ini377
-rw-r--r--tipper/docs/licence_Tipper.txt6
-rw-r--r--tipper/docs/m_tipper.h23
-rw-r--r--tipper/docs/readme_tipper.txt143
-rw-r--r--tipper/m_tipper.h19
-rw-r--r--tipper/message_pump.cpp201
-rw-r--r--tipper/message_pump.h41
-rw-r--r--tipper/options.cpp1126
-rw-r--r--tipper/options.h74
-rw-r--r--tipper/popwin.cpp837
-rw-r--r--tipper/popwin.h17
-rw-r--r--tipper/resource.h85
-rw-r--r--tipper/resource.rc6
-rw-r--r--tipper/subst.cpp468
-rw-r--r--tipper/subst.h14
-rw-r--r--tipper/tipper.cpp295
-rw-r--r--tipper/tipper.h12
-rw-r--r--tipper/tipper.mdsp107
-rw-r--r--tipper/tipper.rc239
-rw-r--r--tipper/tipper.sln20
-rw-r--r--tipper/tipper.vcproj376
-rw-r--r--tipper/translations.cpp654
-rw-r--r--tipper/translations.h14
-rw-r--r--tipper/version.h23
-rw-r--r--tipper/version.rc33
27 files changed, 5631 insertions, 0 deletions
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 <windows.h>
+#include <process.h>
+#include <commctrl.h>
+#include <malloc.h>
+#include <stdlib.h>
+
+#define MIRANDA_VER 0x0600 // for tabbed options
+
+#include <newpluginapi.h>
+#include <statusmodes.h>
+#include <m_database.h>
+#include <m_langpack.h>
+#include <m_options.h>
+#include <m_system.h>
+#include <m_idle.h>
+#include <m_skin.h>
+#include <m_clui.h>
+#include <m_clist.h>
+#include <m_clc.h>
+#include <m_cluiframes.h>
+#include <m_awaymsg.h>
+#include <stdio.h>
+#include <m_utils.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_protomod.h>
+#include <m_contacts.h>
+
+#include <m_popup.h>
+
+#include <m_updater.h>
+#include <m_fontservicew.h>
+#include <m_avatars.h>
+#include <m_variables.h>
+
+#include <m_notify.h>
+
+#include <m_smr.h>
+
+#include <m_metacontacts.h>
+
+#include <m_icq.h>
+
+#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
+<prefix>Day|Month|Year to date=d8
+<prefix>Day|Month|Year to age=d9
+<prefix>Hours|Minutes|Seconds to time=d10
+<prefix>Day|Month|Year|Hours|Minutes|Seconds to time difference=d11
+DWORD timestamp to time (no seconds)=d12
+<prefix>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
+<prefix>Day|Month|Year to date=d8
+<prefix>Day|Month|Year to age=d9
+<prefix>Hours|Minutes|Seconds to time=d10
+<prefix>Day|Month|Year|Hours|Minutes|Seconds to time difference=d11
+DWORD timestamp to time (no seconds)=d12
+<prefix>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:<db module>/<db setting>%
+
+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:<name>%
+
+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 <commctrl.h>
+
+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("<No Label>"));
+ 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("<No Label>"));
+ 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("<No Label>"));
+ 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=(h<v)?h:v;
+ //} else {
+ // Rectangle(hdc, r.left, r.top, (r.right - r.left), (r.bottom - r.top));
+ }
+ RoundRect(hdc, 0, 0, (r.right - r.left), (r.bottom - r.top), h, h);
+
+ SelectObject(hdc, hOldBrush);
+ SelectObject(hdc, hOldPen);
+ }
+ }
+ return TRUE;
+ case WM_PAINT:
+ {
+ RECT r, r2;
+ //if(GetUpdateRect(hwnd, &r, TRUE)) {
+ PAINTSTRUCT ps;
+ BeginPaint(hwnd, &ps);
+ HDC hdc = ps.hdc;
+ GetClientRect(hwnd, &r);
+ r2 = r;
+
+ // text background
+ //SetBkColor(ps.hdc, options.bg_col);
+ SetBkMode(hdc, TRANSPARENT);
+
+ // avatar
+ if(!pwd->text_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=(h<v)?h:v;
+ } else
+ h = 0;
+ hRgn1=CreateRoundRectRgn(r.left,r.top,r.right + 1,r.bottom + 1,h,h);
+ SetWindowRgn(hwnd,hRgn1,FALSE);
+ }
+ return TRUE;
+ case PUM_CALCPOS:
+ {
+ RECT wa_rect, r;
+
+ HMONITOR hMonitor;
+ hMonitor = MonitorFromPoint(pwd->clcit.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 <time.h>
+
+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 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="tipper"
+ ProjectGUID="{2C818919-A38F-44FF-BD91-A6A204AC592A}"
+ RootNamespace="tipper"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;TIPPER_EXPORTS;_UNICODE;UNICODE;MICROSOFT_LAYER_FOR_UNICODE=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="common.h"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="unicows.lib comctl32.lib ws2_32.lib"
+ OutputFile="..\..\bin\$(ConfigurationName)\plugins\$(ProjectName).dll"
+ LinkIncremental="2"
+ GenerateManifest="false"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories="../../include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;TIPPER_EXPORTS;_UNICODE;UNICODE;MICROSOFT_LAYER_FOR_UNICODE=1"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="common.h"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="unicows.lib comctl32.lib ws2_32.lib"
+ OutputFile="..\..\bin\$(ConfigurationName)\plugins\$(ProjectName).dll"
+ LinkIncremental="1"
+ GenerateManifest="false"
+ IgnoreDefaultLibraryNames="Kernel32.lib Advapi32.lib User32.lib Gdi32.lib Shell32.lib Comdlg32.lib Version.lib Mpr.lib Rasapi32.lib Winmm.lib Winspool.lib Vfw32.lib Secur32.lib Oleacc.lib Oledlg.lib Sensapi.lib"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ EmbedManifest="false"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\message_pump.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\options.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\popwin.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\subst.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\tipper.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\translations.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\common.h"
+ >
+ </File>
+ <File
+ RelativePath=".\message_pump.h"
+ >
+ </File>
+ <File
+ RelativePath=".\options.h"
+ >
+ </File>
+ <File
+ RelativePath=".\popwin.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\subst.h"
+ >
+ </File>
+ <File
+ RelativePath=".\tipper.h"
+ >
+ </File>
+ <File
+ RelativePath=".\translations.h"
+ >
+ </File>
+ <File
+ RelativePath=".\version.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\resource.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\tipper.rc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\version.rc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
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 <winsock.h>
+#include <time.h>
+
+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("<prefix>Day|Month|Year to date")
+ },
+ {
+ (TranslateFunc*)day_month_year_to_age,
+ _T("<prefix>Day|Month|Year to age")
+ },
+ {
+ (TranslateFunc*)hours_minutes_seconds_to_time,
+ _T("<prefix>Hours|Minutes|Seconds to time")
+ },
+ {
+ (TranslateFunc*)day_month_year_hours_minutes_seconds_to_time_difference,
+ _T("<prefix>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("<prefix>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 <windows.h>
+#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