summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/m_help.h78
-rw-r--r--plugins/ContextHelp/ContextHelp.vcxproj44
-rw-r--r--plugins/ContextHelp/ContextHelp.vcxproj.filters176
-rw-r--r--plugins/ContextHelp/docs/Help-Developer.txt93
-rw-r--r--plugins/ContextHelp/docs/Help-License.txt278
-rw-r--r--plugins/ContextHelp/docs/Help-Readme.txt196
-rw-r--r--plugins/ContextHelp/docs/Help-Translation.txt217
-rw-r--r--plugins/ContextHelp/docs/helppack_english.txt193
-rw-r--r--plugins/ContextHelp/docs/helppack_sample.txt256
-rw-r--r--plugins/ContextHelp/res/resource.rc296
-rw-r--r--plugins/ContextHelp/res/version.rc38
-rw-r--r--plugins/ContextHelp/src/datastore.cpp590
-rw-r--r--plugins/ContextHelp/src/dlgboxsubclass.cpp492
-rw-r--r--plugins/ContextHelp/src/help.h146
-rw-r--r--plugins/ContextHelp/src/helpdlg.cpp735
-rw-r--r--plugins/ContextHelp/src/helppack.cpp520
-rw-r--r--plugins/ContextHelp/src/main.cpp128
-rw-r--r--plugins/ContextHelp/src/options.cpp797
-rw-r--r--plugins/ContextHelp/src/resource.h61
-rw-r--r--plugins/ContextHelp/src/stdafx.cxx18
-rw-r--r--plugins/ContextHelp/src/stdafx.h52
-rw-r--r--plugins/ContextHelp/src/streaminout.cpp771
-rw-r--r--plugins/ContextHelp/src/utils.cpp430
-rw-r--r--plugins/ContextHelp/src/version.h53
24 files changed, 6658 insertions, 0 deletions
diff --git a/include/m_help.h b/include/m_help.h
new file mode 100644
index 0000000000..893beced0f
--- /dev/null
+++ b/include/m_help.h
@@ -0,0 +1,78 @@
+/*
+
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_HELP_H__
+#define M_HELP_H__
+
+/* Enable/disable the help context menu for a specific control. v0.2.0.0+
+Note: You normally do not need to call this, read below.
+You can can use this to deactivate the appearance of the help context menu
+being shown when the user right clicks on an control.
+You can use this service to disable the context menu.
+
+You do *not* need to use this service when you would like to show
+a context menu by yourself, just handle WM_CONTEXTMENU correctly instead.
+You need to return TRUE in your DlgProc or 0 in your WndProc, indicating 'message handled'.
+
+The context menu is disabled by default on the following controls (looks silly on multi-component controls):
+ListView, TreeView, Statusbar, Toolbar, CLC
+AutoTips are disabled by default for controls stating DLGC_WANTALLKEYS or DLGC_HASSETSEL at
+WM_GETDLGCODE (autotips are annoying on edits).
+ wParam=(WPARAM)(HWND)hwndCtl
+ lParam=(LPARAM)flags (see below)
+Returns 0 on success or nonzero on failure
+*/
+#define MS_HELP_SETCONTEXTSTATE "Help/SetContextState"
+#define HCSF_CONTEXTMENU 0x01 // show help context menu for this control
+#define HCSF_AUTOTIP 0x02 // show automatic help tip on hover for this control
+ // only works for non-editable
+#if !defined(HELP_NOHELPERFUNCTIONS)
+__inline static int Help_SetContextState(HWND hwndCtl,DWORD flags) {
+ if(!ServiceExists(MS_HELP_SETCONTEXTSTATE)) return -1;
+ return CallService(MS_HELP_SETCONTEXTSTATE,(WPARAM)hwndCtl,flags);
+}
+#endif
+
+/* Show a help tooltip for a specific control or dialog. v0.2.0.0+
+You can call this if you would like to show help at a specific time.
+ wParam=(WPARAM)(HWND)hwndCtl
+ lParam=0 (unused)
+Returns 0 on success or nonzero on failure.
+The service fails when the help tooltip cannot be instantiated.
+*/
+#define MS_HELP_SHOWHELP "Help/ShowHelp"
+
+/* Show the download language dialog. v0.2.1.0+
+ wParam=lParam=0
+The dialog can't have a parent due to it's asynchronous nature.
+If the language window is already opened it will be
+brought to front instead (returns success).
+Returns 0 on success, nonzero otherwise.
+*/
+#define MS_HELP_SHOWLANGDIALOG "Help/ShowLangDialog"
+
+
+#ifndef HELP_NOSETTINGS
+#define SETTING_AUTOTIPSENABLED_DEFAULT 0
+#define SETTING_AUTOTIPDELAY_DEFAULT 4000
+#define SETTING_ENABLEHELPUPDATES_DEFAULT 1
+#endif
+
+#endif // M_HELP_H__
diff --git a/plugins/ContextHelp/ContextHelp.vcxproj b/plugins/ContextHelp/ContextHelp.vcxproj
new file mode 100644
index 0000000000..fe1576584b
--- /dev/null
+++ b/plugins/ContextHelp/ContextHelp.vcxproj
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{BBD98F45-86E9-4C3C-958D-319FC4C5761D}</ProjectGuid>
+ <ProjectName>ContextHelp</ProjectName>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PlatformToolset>
+ </PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PlatformToolset>
+ </PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <PlatformToolset>
+ </PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <PlatformToolset>
+ </PlatformToolset>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(ProjectDir)..\..\build\vc.common\plugin.props" />
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/ContextHelp/ContextHelp.vcxproj.filters b/plugins/ContextHelp/ContextHelp.vcxproj.filters
new file mode 100644
index 0000000000..364cb2ef67
--- /dev/null
+++ b/plugins/ContextHelp/ContextHelp.vcxproj.filters
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(ProjectDir)..\..\build\vc.common\common.filters" />
+ <ItemGroup>
+ <ClCompile Include="src\stdafx.cxx">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ <ClCompile Include="src\datastore.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\dlgboxsubclass.cpp" />
+ <ClCompile Include="src\helpdlg.cpp" />
+ <ClCompile Include="src\helppack.cpp" />
+ <ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\options.cpp" />
+ <ClCompile Include="src\streaminout.cpp" />
+ <ClCompile Include="src\unzip.cpp" />
+ <ClCompile Include="src\update.cpp" />
+ <ClCompile Include="src\utils.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="src\help.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\m_help.h" />
+ <ClInclude Include="src\resource.h" />
+ <ClInclude Include="src\unzip.h" />
+ <ClInclude Include="src\version.h" />
+ <ClInclude Include="src\help.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\m_help.h" />
+ <ClInclude Include="src\resource.h" />
+ <ClInclude Include="src\unzip.h" />
+ <ClInclude Include="src\version.h" />
+ <ClInclude Include="src\help.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\m_help.h" />
+ <ClInclude Include="src\resource.h" />
+ <ClInclude Include="src\unzip.h" />
+ <ClInclude Include="src\version.h" />
+ <ClInclude Include="src\help.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\m_help.h" />
+ <ClInclude Include="src\resource.h" />
+ <ClInclude Include="src\unzip.h" />
+ <ClInclude Include="src\version.h" />
+ <ClInclude Include="src\help.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\m_help.h" />
+ <ClInclude Include="src\resource.h" />
+ <ClInclude Include="src\unzip.h" />
+ <ClInclude Include="src\version.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="res\resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="res\version.rc" />
+ <ResourceCompile Include="res\resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="res\version.rc" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/ContextHelp/docs/Help-Developer.txt b/plugins/ContextHelp/docs/Help-Developer.txt
new file mode 100644
index 0000000000..28326ee710
--- /dev/null
+++ b/plugins/ContextHelp/docs/Help-Developer.txt
@@ -0,0 +1,93 @@
+
+Help Plugin 0.2.1.2 for Miranda IM 0.6+
+------------------------------------------------------------------------
+ Developer Information
+
+ Contents: -------------------------------
+ | Help Packs, Translation, Debug Symbols,
+ | Coding Language, Rebase Info
+
+Help Packs
+-----------------
+ Help texts are stored in a file named 'helppack_*.txt'
+ To use the file, place it in the same directory as miranda32.exe, and call it
+ helppack_*.txt where * can be anything, however it is recommended that it
+ be the language.
+
+ An entry in the help file looks like this:
+
+ [context_identifier]
+ Some text shown as title=Some text<br>that might contain simple html code
+
+ IMPORTANT HINT:
+ When you press the 'Ctrl' key while opening up the context help tooltip
+ it will display the help identifier used in the help file, instead of
+ the help text. This is helpful when writing your help file.
+ When you press 'Ctrl'-C while a helptip is opened, its text will get
+ copied to clipboard.
+
+ A list of available HTML tags that might be helpful when writing
+ help texts:
+ styles: <i>italic</i>, <b>bold</b>, <u>underlined</u>
+ linebreak: <br>
+ sizes: <big>large text</big>, <small>tiny text</small>
+ hyperlinks: <a href="url">link title</a>
+ paragraph: <p>text</p>
+ colors: <font color="#colorcode">colmred text</font>
+ instead of #hexcode you can also use the following 16 default color names:
+ black, maroon, green, olive, navy, purple, teal, silver,
+ gray, red, lime, yellow, blue, fuchsia, aqua, white
+
+ Be careful when using those html tags.
+ Do not use them massively, instead pick them when appropriate to
+ guide the reader. Keep in mind that it is still a *help* text.
+
+ Some special symbols must be specified differently:
+ instead of < use &lt;
+ instead of > use &gt;
+ instead of & use &amp;
+ instead of " use &quot;
+ A space can be also specified as &nbsp;
+
+ To specify a specific Unicode character
+ you can use a special numeric representation:
+ &#[x][Number];
+ Examples: &#945; and &#x3B1;
+ When you place an x in in front the number needs to be hexadecimal.
+
+ Please refer to 'helppack_sample.txt' for an example help file.
+
+Translation
+-----------------
+ Translation strings for language pack maintainers can be found in
+ 'Help-Translation.txt'.
+
+Debug Symbols
+-----------------
+ Debug symbols are also available for debugging purposes.
+ Copy the PDB-files into the same directory as the
+ corresponding DLL-files.
+ To debug crashes the supplied MAP-file file might be helpful.
+
+Coding Language
+-----------------
+ Help Plugin was written using Microsoft Visual C++ 6.0 SP6
+ and the Microsoft Platform SDK shipped along with it.
+
+ The project files for VS 7 and 8 (*.vcproj and *.vspscc) are not
+ kept up-to-date.
+ The working project files are the ones of VS 6 (*.dsw and *.dsp).
+
+Rebase Info
+-----------------
+ Help Plugin has set its base address to:
+ 0x20100000
+
+ Please avoid using this base address for your plugins because it will
+ slow down the start of Miranda IM.
+ Try to choose a unique base address for each plugin you create.
+
+ Using Microsoft Visual C++, the base address can be configured at:
+ 'Project' -> 'Settings' -> 'Linker' -> 'Output' -> 'Base Address'
+
+H. Herkenrath (hrathh at users.sourceforge.net)
diff --git a/plugins/ContextHelp/docs/Help-License.txt b/plugins/ContextHelp/docs/Help-License.txt
new file mode 100644
index 0000000000..a726a52df1
--- /dev/null
+++ b/plugins/ContextHelp/docs/Help-License.txt
@@ -0,0 +1,278 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/plugins/ContextHelp/docs/Help-Readme.txt b/plugins/ContextHelp/docs/Help-Readme.txt
new file mode 100644
index 0000000000..20859ca7a4
--- /dev/null
+++ b/plugins/ContextHelp/docs/Help-Readme.txt
@@ -0,0 +1,196 @@
+
+Help Plugin 0.2.1.2
+------------------------------------------------------------------------
+ Plugin for Miranda IM 0.6 and +
+
+ Plugin Info: ----------------------------
+ | Version: 0.2.1.2
+ | Filename: help.dll
+ | Authors: R. Hughes, H. Herkenrath (hrathh at users.sourceforge.net)
+ | Description: Provides context sensitive help in all of the Miranda IM dialog boxes.
+
+ Contents: -------------------------------
+ | Features, Requirements, Usage, Installation,
+ | Bugs and Wishes, Version History,
+ | Translation, License and Copyright
+
+Features
+----------------------
+ + Display help tooltips on request for every component in a window
+ + Tooltips always occur below the element to show help for
+ + All help texts are stored in a Help Pack file ('helppack_<language>.txt')
+ + Allows to switch installed Help Packs easily
+ + Displays all information about the Help Packs in one place
+ + Shows which Help Pack includes support for what plugins
+ + Auto-Updates for all installed Help Packs
+ + Wizard helping you to download and install your native language
+ + Right-click on a help pack entry to be able to uninstall it
+ + Help API file for developers to control the help in their dialogs
+ + Full Unicode support
+ + Automatic installation of all files, just unzip into Plugins directory
+ + Supported plugins: Database Editor, Country Flags
+
+Requirements
+----------------------
+ -> Miranda IM 0.6+:
+ Miranda IM is needed in version 0.6 or greater.
+
+Usage
+----------------------
+ Provides support for context-sensitive help in all of Miranda's dialog boxes.
+ Right click, press F1 or use the ? button in the title bar.
+
+ When a dialog supports context sensitive help, you can right click
+ on every control element to get more information about it.
+ Dialogs that don't have a minimize or maximize box in their caption do also show
+ an additional button with a '?'-symbol in their window caption.
+ The F1 key is also recognized as a context help call in dialogs.
+
+ The used Help Pack file can be selected here:
+ 'Options' -> 'Customize' -> 'Help'
+
+Installation
+----------------------
+ Find 'miranda32.exe' on your computer.
+
+ Just copy all the contents of the zip-file as they are into the 'Plugins' directory
+ in the Miranda IM folder.
+ The Help Plugin will detect the files and move them into the appropriate
+ directories on it's first run.
+
+ You can also do all the installation by hand, if you want to:
+
+ Main Plugin: Copy the file 'help.dll' into the 'Plugins' directory
+ in the Miranda IM folder.
+ The file 'helppack_english.txt' needs to copied into the main directory
+ of Miranda IM.
+
+ The Unicode version of the plugin will only work on Windows NT/2000/XP,
+ Windows Server 2000, Windows Vista or later with an installed
+ Unicode version of Miranda IM.
+ To use it on Windows 95/98/Me, please download the ANSI version of the plugin.
+
+ Documentation: The txt-files should be moved along with the SDK-zip into the
+ 'Docs' directory in the Miranda IM folder.
+
+ That's it!
+
+Bugs and Wishes
+----------------------
+ Feel free to mail me your wishes about 'Language Pack Manager' and tell
+ me all the bugs you may find.
+ My email address is: hrathh at users.sourceforge.net
+
+Version History
+----------------------
+ 0.2.1.2 - CTRL-key: copies a proposed ctl title for ctl identifier
+ - corrected user-agent
+ - minor fixes
+ 0.2.1.1 - corrected exported version number
+ - temporarily deactivated the download wizard on first run
+ (as there is nothing yet to be downloaded)
+ - removed 'cache expiry' setting from options, auto-detects file change
+ 0.2.1.0 - added auto-updates
+ - added native language download wizard
+ - updated to use new miranda headers
+ - added automatic installation for helppack files and docs
+ (move them to correct directory)
+ - obeys win context ids when set
+ - synced with last official help.dll
+ - added v0.8 support
+ 0.2.0.6 - minor fix for ownerdefined window classes
+ - hyperlink cursor was incorrectly destroyed
+ - corrected <a></a> tag parsing when improperly formated
+ - hyperlinks now use correct system color (was problem with richedit)
+ 0.2.0.5 - fixed focus stealing due to WM_ACTIVATE recursion
+ - adjustments for helptips about editable controls (autotip)
+ - fix for WM_HELP causing an additional WM_LBUTTONUP (fixes help about buttons)
+ - fixed issue with SHBrowseForFiles and other 'owned' windows
+ - corrected richedit offset (approx. 2px)
+ - shows endellipsis if title text is too long
+ - adjusted spinner control handling
+ - small fix for caching
+ - disallow empty title/text
+ - fixed <p></p> tag parsing
+ - removed unnecessary services of 0.2.0.0
+ - minor cleanups
+ 0.2.0.4 - minor fix for unhooking
+ - pressing ctrl-c on tip copies helptip text to clipboard
+ - minor locale related fix
+ 0.2.0.1 - Added: optional shows autotips (see option page, experimental, feature might be dropped eventually)
+ - Fixed: discovered minor problem with threading conncurrency
+ - Modified: now also shows '?' box for minizable dialogs (especially options dialog)
+ - Minor fixes
+ 0.2.0.0 - New generation release
+ - Now the plugin uses a text file (helppack_*.txt) to store its data
+ The server method was defunct and nobody ever updated/stored
+ help texts. You even needed a password.
+ Now the functionality is similar to the langpacks.
+ The syntax of the helppack files is kept *very* similar to the
+ langpack ones
+ - Updated: control type detection to recgnize newer controls
+ - Replaced: netlib/networking code with file reading code
+ - Added: Help API (m_help.h) for developers
+ - Added: Full unicode support
+ - Fixed: Help support for MessageBoxes (was buggy)
+ - Added: press 'Ctrl' while showing the help tooltip to get
+ the identifier used in the helppack file
+ - Fixed: DlgId handling was not well though out
+ - Fixed: some memory leaks in low memory conditions in conjunction with realloc
+ - Fixed: some rare occuring errors were not handled
+ - Fixed: help on help tooltip is disabled (was buggy)
+ - Fixed: some unicode problems
+ - Fixed: thread safety (MS_SYSTEM_THREAD_PUSH/POP was not used)
+ - Fixed: now also supports generating IDs for IDC_STATIC(-1) controls
+ - Fixed: created some services to avoid conflicting context menus
+ - Fixed: codepage usage
+ - Replaced: uses miranda's simple memory problems catcher
+ - Verified all code, used SAME PROGRAMMING STYLE AS ORIGINAL
+ - Fixed: some minor issues with unchecked return values
+ - Changed: Moved the Cache Expiry setting to the options (was hardcoded)
+ - Some other minor fixes and updated code
+ 0.1.2.2 - previous help plugin using http requests
+
+Translation
+----------------------
+ Translation strings and available bad words strings can be found
+ in 'Help-Translation.txt' of the SDK package.
+
+License and Copyright
+----------------------
+ Help Plugin is released under the terms of the
+ GNU General Public License.
+ See 'Help-License.txt' for more details.
+
+ Copyright (c) 2002 Richard Hughes, 2005-2006 Heiko Herkenrath.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+H. Herkenrath (hrathh at users.sourceforge.net)
diff --git a/plugins/ContextHelp/docs/Help-Translation.txt b/plugins/ContextHelp/docs/Help-Translation.txt
new file mode 100644
index 0000000000..ed3dc1d27b
--- /dev/null
+++ b/plugins/ContextHelp/docs/Help-Translation.txt
@@ -0,0 +1,217 @@
+
+Help Plugin 0.2.1.2 for Miranda IM 0.6+
+------------------------------------------------------------------------
+ Translator Information
+
+ Contents: -------------------------------
+ | Help Texts, Plugin Texts, String Listing
+
+Help Texts
+-----------------------------
+ The help texts can be translated using the help pack files.
+ To get an explanation on how such a file needs to be formated
+ please refer to 'helppack_sample.txt'.
+
+Plugin Texts
+-----------------------------
+ The Help Plugin can be translated with the Miranda IM
+ language files.
+
+ Put the following strings in a file called 'langpack_<language>.txt'
+ in the Miranda IM directory and translate them into your
+ language.
+ If you need more info on Miranda IM language files visit:
+ http://miranda.svn.sourceforge.net/viewvc/*checkout*/miranda/trunk/miranda/i18n/readme.txt
+
+String Listing
+-----------------------------
+
+; --- Plugin: Help Plugin 0.2.1.2 (German) ---
+
+; Translation by hrathh
+; Please report any mistakes or missing strings in here.
+
+[Help]
+Hilfe
+[Provides context sensitive help in the Miranda IM dialog boxes.]
+Bietet kontextsensitive Hilfe in allen Dialogen von Miranda IM.
+
+; Errors
+[Help Plugin]
+Hilfe-Plugin
+[The Help Plugin can not be loaded. It requires Miranda IM %hs or later.]
+Das Hilfe-Plugin kann nicht geladen werden. Es benötigt Miranda IM %hs oder neuer.
+[The Help Plugin can not be loaded, riched20.dll is missing. If you are using Windows 95 or WINE please make sure you have riched20.dll installed.\n\nPress 'Yes' to continue loading the Help Plugin.]
+Das Hilfe-Plugin kann nicht geladen werden, da riched20.dll fehlt. Sofern Windows 95 oder WINE verwendet wird stellen Sie bitte sicher, dass riched20.dll installiert ist.\n\nKlicken Sie auf "Ja" um das Hilfe-Plugin trotzdem zu laden.
+
+; Context Menu
+[&What's this?]
+Direkt&hilfe
+[&What's this Dialog?]
+Direkt&hilfe zum Dialog
+
+; Tooltip
+[Loading...]
+Bitte warten...
+[No Help Pack installed!]
+Keine Hilfe-Datei installiert!
+[No help available for this item.]
+Diesem Eintrag ist kein Hilfethema zugeordnet.
+[&Copy]
+&Kopieren
+
+; Advanced Options
+;[Customize]
+[Help]
+Hilfe
+[Advanced]
+Erweitert
+[Context Help]
+Context-Sensitive Hilfe
+[&Automatically show help when hovered]
+&Automatisch Hilfe anbieten bei Stillstand der Maus
+[&Delay:]
+&Verzögerung:
+[milliseconds]
+Millisekunden
+
+; Language Options
+;[Customize]
+;[Help]
+[Language]
+Sprachen
+[&Remove...]
+&Entfernen...
+[Installed Languages]
+Installierte Sprachen
+[File]
+Datei
+[built-in]
+enthalten
+[Author(s):]
+Autor(en):
+[E-mail:]
+E-Mail:
+[Last modified using:]
+Verwendung von:
+[Date:]
+Datum:
+;[Version:]
+;[Locale:]
+[(incompatible)]
+(inkompatibel)
+[Unknown]
+Unbekannt
+[Current]
+Aktuell
+[Not included:]
+Nicht enthalten:
+;[%hs (%s)]
+[All installed plugins are included.]
+Alle installierten Plugins sind enthalten.
+;[No Help Pack installed!]
+[Download more Help Packs]
+Weitere Hilfedateien herunterladen
+[Check for new &versions of Help Packs periodically]
+Regelmäßig auf neue Hilfedatei-&Versionen überprüfen
+[&Download Language]
+Sprache &herunterladen
+
+; All names of the language packs can be translated
+[English (default)]
+Englisch (Standard)
+
+; Update Notify
+[Help Pack Update Now Available]
+Update für Hilfedatei ist jetzt verfügbar
+[A new version of a Help Pack is now available. Click the install button to download and install this new update.]
+Eine neue Version einer Hilfedatei ist verfügbar. Klicken Sie auf "Jetzt installieren" um das neue Update herunterzuladen und zu installieren.
+[Language:]
+Sprache:
+[Current:]
+Aktuell:
+[New:]
+Neu:
+[&Install Now]
+Jetzt &installieren
+;[Close]
+[Help Pack Update succeeded]
+Hilfedatei-Update erfolgreich
+[The help pack "%s" has been sucessfully downloaded and installed.]
+Die Hilfedatei "%s" wurde erfolgreich heruntergeladen und aktualisiert.
+[Help Pack Update failed]
+Hilfedatei-Update fehlgeschlagen
+[The help pack "%s" could not be downloaded or extracted.]
+Die Hilfedatei "%s" konnte nicht heruntergeladen oder extrahiert werden.
+
+; Download Language
+[Select your help language]
+Sprache der Hilfetexte wählen
+[Help is available in different languages.\nHere is a list of all available languages of the file listing, please select your native &language:]
+Hilfe is in vielen Sprachen verfügbar.\nEs folgt eine Liste aller verfügbaren Sprachen auf der Website. Bitte wählen Sie Ihre gewünschte &Sprache aus:
+[Download &all languages]
+&Alle Sprachen herunterladen
+[Note: This will download and install the newest help pack available for the selected language. There might be help packs from other authors available on the file listing.]
+Hinweis: Es wird die jeweils neueste Hilfedatei für die gewählte Sprache heruntergeladen. Es könnten noch weitere Hilfedateien von anderen Autoren auf der Website verfügbar sein.
+[downloading]
+downloaden
+;[OK]
+;[Cancel]
+[Help Pack Download finished]
+Hilfedatei-Download beendet
+[The download succeeded!]
+Der Download war erfolgreich!
+[The download failed!\n\nThe help pack could not be downloaded or extracted.]
+Der Download schlug fehl!\n\nDie Hilfedatei konnte nicht heruntergeladen oder extrahiert werden.
+
+; All language subcategory names on the website can be translated
+;[English (default)]
+[Chinese]
+Chinesisch
+[Portuguese]
+Portugiesisch
+[French]
+Französisch
+[Hebrew]
+Hebräisch
+[Hungarian]
+Ungarisch
+[Italian]
+Italienisch
+[Spanish]
+Spanisch
+[Ukrainian]
+Ukrainisch
+[Russian]
+Russisch
+[German]
+Deutsch
+[Dutch]
+Holländisch
+[Bulgarian]
+Bulgarisch
+[Czech]
+Tschechisch
+[Korean]
+Koreanisch
+[Polish]
+Polnisch
+[Turkish]
+Türkisch
+[Swedish]
+Schwedisch
+[Japanese]
+Japanisch
+[Belarusian]
+Weißrussisch
+[Danish]
+Dänisch
+[Finnish]
+Finnisch
+[Croatian]
+Kroatisch
+
+; ---
+
+
+H. Herkenrath (hrathh at users.sourceforge.net)
diff --git a/plugins/ContextHelp/docs/helppack_english.txt b/plugins/ContextHelp/docs/helppack_english.txt
new file mode 100644
index 0000000000..ef7ffb1bc1
--- /dev/null
+++ b/plugins/ContextHelp/docs/helppack_english.txt
@@ -0,0 +1,193 @@
+Miranda Help Pack Version 1
+Language: English (default)
+Locale: 0809
+Last-Modified-Using: Miranda IM 0.6
+Authors: hrathh, -pv-
+Author-email: hrathh at users.sourceforge.net
+Plugins-included: clist_classic,clist_nicer
+
+; options dialog
+[miranda32:0@AgEAAgIAAgMAD6IEA0IF]
+Options=In this window you can view and modify all settings relevant for Miranda IM, its Protocols and all other enabled plugins.
+[miranda32:1377@AgEAAgIAAgMAD6IEA0IF]
+Select a subentry from the list=When you see this you have not selected any option page in the list on the left or the selected item does not have any options to set.
+[miranda32:1@AgEAAgIAAgMAD6IEA0IF]
+OK=Saves all the changes you have made on the various opened option pages and closes the options window.
+[miranda32:2@AgEAAgIAAgMAD6IEA0IF]
+Cancel=Discards changes you have made and closes the options window.
+[miranda32:3@AgEAAgIAAgMAD6IEA0IF]
+Apply=Saves all the changes you have made on the various opened option pages without closing the options window.
+[miranda32:1346@AgEAAgIAAgMAD6IEA0IF]
+Show expert options=Enable this to get more option possibilities shown which are normally not interesting for average users.
+[miranda32:1186@AgEAAgIAAgMAD6IEA0IF]
+List with option pages=Shows all available option categories. Pick one category to get its options shown on the right side of the list.
+
+; sounds options
+[miranda32:0@AowEAo0EE7AED3kG]
+Sounds=On this options page you can view and modify all audible events of Miranda IM and its plugins.
+[miranda32:-2@AowEAo0EE7AED3kG]
+[miranda32:1657@AowEAo0EE7AED3kG]
+List of all sounds=Shows all available audible events to which sounds can be associated.<br>To specify a sound file select an item in the list. A checkmark means playing the sound is enabled for this event.
+[miranda32:1660@AowEAo0EE7AED3kG]
+Sound Information=View or modify the sound file associated with the event selected in the list above.
+[miranda32:1009@AowEAo0EE7AED3kG]
+[miranda32:1010@AowEAo0EE7AED3kG]
+Name=The name of the sound event as also shown in the list above. Summarizes section name and event name.
+[miranda32:1661@AowEAo0EE7AED3kG]
+[miranda32:1659@AowEAo0EE7AED3kG]
+Location=Shows the path of the sound file selected by clicking the <b>Change...</b> button. When no sound file was yet selected <i>&lt;not specified&gt;</i> is shown instead.
+[miranda32:1165@AowEAo0EE7AED3kG]
+Preview=Plays the sound file specified at <b>Location</b>.
+[miranda32:1164@AowEAo0EE7AED3kG]
+Change...=Opens up a window where you can select a sound file to be played when the sound event occurs.
+[miranda32:1200@AowEAo0EE7AED3kG]
+Download more sounds=Opens up your default browser pointing it to the <i>Sounds</i> download section at Miranda IM's website.
+
+; plugins options
+[miranda32:0@DowGB40GB48GB5EGE5IGE5QG]
+Plugins=On this options page you can view and modify the state of the modules installed for Miranda IM.
+[miranda32:1676@DowGB40GB48GB5EGE5IGE5QG]
+List of plugins=In the list you find all installed modules whether they are running or disabled. This list includes protocols, the database driver and contact list interfaces. You can enable or disable any module you like by ticking or unticking its list item.<br><i>You need to restart Miranda IM after enabling or disabling a module for the changes to take effect!</i>
+[miranda32:1680@DowGB40GB48GB5EGE5IGE5QG]
+Plugin Information=Shows all a descriptive information about the plugin you have selected in the list above. When nothing is selected this part is empty.
+[miranda32:-2@DowGB40GB48GB5EGE5IGE5QG]
+[miranda32:1677@DowGB40GB48GB5EGE5IGE5QG]
+Description=A description of the plugin you selected in the list above.
+[miranda32:-3@DowGB40GB48GB5EGE5IGE5QG]
+[miranda32:1679@DowGB40GB48GB5EGE5IGE5QG]
+Author(s)=Shows all people involved in developing the selected plugin.
+[miranda32:-5@DowGB40GB48GB5EGE5IGE5QG]
+[miranda32:1684@DowGB40GB48GB5EGE5IGE5QG]
+E-mail=Shows the E-mail address of the plugin maintainer.
+[miranda32:-6@DowGB40GB48GB5EGE5IGE5QG]
+[miranda32:1682@DowGB40GB48GB5EGE5IGE5QG]
+Homepage=The homepage of the plugin. This usually refers to a page belonging to the addons section at the website of Miranda IM.
+[miranda32:1692@DowGB40GB48GB5EGE5IGE5QG]
+Please restart Miranda IM=You need to restart Miranda IM for your changes to take effect. This occurs when you enabled or disabled a plugin in the list above.
+
+; ignore options
+[miranda32:0@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:-10@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Ignore=On this options page you can ignore or un-ignore events of all protocols in Miranda IM.
+[miranda32:-2@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:1079@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+List of events being ignored=List of all contacts in your database you can toggle any ignore settings for each use by clicking the small icons on their right.<br>Icon slots not being enabled for contacts are not appropriate for them.<p><b>*** All contacts ***</b> helps you to set ignore settings for all the contact in the list.<br>To do this just for contact belonging to a group, set the settings for their group item instead.</p><p><b>*** Unknown contacts ***</b> refers to all contact to added by you to your contact list, but who are trying to contact you though.
+[miranda32:1375@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:-3@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Ignore messages=When this icon is displayed on the right side of a contact, all incoming messages sent by him/her will be dropped. You will not be notified.
+[miranda32:1376@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:-4@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Ignore URLs=When this icon is displayed on the right side of a contact, all incoming URL messages sent by him/her will be dropped. You will not be notified.
+[miranda32:1206@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:-5@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Ignore files=When this icon is displayed on the right side of a contact, all file transfers initiated by him/her will be automatically denied.
+[miranda32:1@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:-6@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Suppress auth requests=When this icon is displayed on the right side of a contact, all requests for authorisation to add you to his/her contact list will be suppressed.
+[miranda32:1207@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:-7@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Suppress online notification=When this icon is displayed on the right side of a contact, you will never be notified when the contact goes online.
+[miranda32:1542@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:-11@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Suppress added notification=When this icon is displayed on the right side of a contact, you will not be notified when the contact added you to his/her contact list sending you a notification.
+[miranda32:1208@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:-8@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Ignore all=Clicking this icon next to a contact will set all possible ignorable events to being ignored.
+[miranda32:1209@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+[miranda32:-9@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Ignore none=Clicking this icon next to a contact will un-ignore all events for it. No event will be ignored or be suppressed in the future.
+[miranda32:1380@Bl8FBmAFBrYEBgEABrcEBrgEBrkEFDcEBgYG]
+Only ticked contacts will be shown=The list above also shows some checkboxes in front of the contacts and groups.<br>Only contacts with a checkmark in front of them will actually be shown on your contact list interface. All un-ticked ones will be hidden.
+
+; contact display options
+[miranda32:0@D9IE]
+[miranda32:-2@D9IE]
+Contact Display=On this options page you can set how the contact names on your list will be displayed.
+[miranda32:-3@D9IE]
+[miranda32:1234@D9IE]
+Name display order=Normally contacts are displayed by their nickname of the contact list. However, this behaviour can be adjusted.<br>The list you see display the order in which the contact lists tried to get a name for a contact. If the first element in the shown list does not provide a name the next list item below is queried.<p><b>My custom name</b> is not movable. The customized name of a contact you entered by hand will always be shown regardless what you set here.</p><p>When all ways to get a name for a contact do fail it will be displayed as <b>'(Unknown Contact)'. That item is not movable, too.</p>
+
+
+; ### contribution by -pv-, thanks a lot!!! ###
+; [x] checked for typos
+; [x] added dialog tooltip
+; [x] added some unhandled context-identifiers
+
+; file transfers options
+[miranda32:0@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+File Transfers=On this options page you can view and modify all settings regarding to file transfers that can be initiated by most of the protocols of Miranda IM.
+[miranda32:-2@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Receiving files=These options control the file receiving process itself.
+[miranda32:-3@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+[miranda32:1213@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Received files folder=Type in a path where received files will be stored in or use the button on the right labeled with '<b>...</b>'.
+[miranda32:-7@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Variables Allowed=You do not need to provide a fixed path where all received files go in. Instead, you can enrich your path specification by using some variables refering to the user the file is received from.<br><br>Allowed variables are:<br><b>%userid%</b> gets replaced by the contact's UID specify to the protocol.<br><b>%nick%</b> represents the nickname of the contact.<br><b>%proto%</b> stands for the protocol name associated withe the contact.
+[miranda32:1475@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Browse for folder=Opens a window where you can choose a folder to store received files into.
+[miranda32:1484@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Auto-accept files from people on my contact list=When this is ticked and someone from your contact list is trying to send you a file, the transfer will be accepted automatically.
+[miranda32:1005@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Minimize the file transfer window=Enable this to have the transfer window minimized each time you accept the transfer or when it got accepted automatically according to the <b>Auto-accept incoming files from people on my contact list</b> option.
+[miranda32:1004@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Close window when transfer completes=After a file or the group of files is successfully transferred, enabling this will close the window automatically
+[miranda32:1520@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Virus scanner=Using these options you can have your preferred anti-virus software scan downloaded files as soon as they are downloaded.
+[miranda32:-4@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+[miranda32:1489@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+[miranda32:1490@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+[miranda32:1491@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Scan files=You can have your preferred anti-virus software scan downloaded files as soon as they are downloaded. If you choose <b>When all files have been downloaded</b> or <b>As each file finishes downloading</b> you have to specify a command-line to your scanner application. Some well-known command-lines will be predefined for you.
+[miranda32:1492@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+[miranda32:1485@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+[miranda32:1493@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Command line=Enter a file-system path to your anti-virus scanner including parameters to scan the files or folders here.<br>When a well-known scanner is found on your system some, a command-line will be predefined for you. Pick the one you like to use.<br>Use <i>%f</i> as filename parameter. The variable gets replaced by the actual file path to be checked.
+[miranda32:1488@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Warn me before opening a file that has not been scanned=Displays a warning if some of the received files were not scanned.
+[miranda32:1476@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Browse for files=Brings up a window where you can easily browse the file-system to search for your anti-virus command-line application.
+[miranda32:-5@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+[miranda32:1516@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+[miranda32:-6@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+If incoming files already exists=Choose how to treat a file that has the same location and filename as the file which is going to be received. When you choose <b>Ask me</b> you need to make this decision every time such a conflict occurs.<br>You will <u>always</u> be asked when a file already exists for a contact not added to your contact list.
+[miranda32:1501@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Overwrite=When this is enabled conflicting files will be overwritten silently.
+[miranda32:1497@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Resume=The protocol will try to resume file transfer assuming the already existing file was caused by a dropped file transfer. It will be tried to download the previous file correctly.
+[miranda32:1519@B70EAsMFA8wFA+0DA+wDBNMFBNIFBNEFCc0FAsQFA9AFBOwFBNkFBN0FBO8F]
+Rename=The new file being downloaded will get renamed so it no longer has the same name as an already existing file. The file will have <i>(1)</i> etc. appended to its name.
+
+; hotkeys options (clist_classic, clist_nicer)
+; [x] checked for typos
+; [x] added dialog tooltip
+; [x] added some unhandled context-identifiers
+
+[clist_classic:0@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_classic:-2@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_nicer:0@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+[clist_nicer:-2@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+Hotkeys=On this options page you can set some basic global hotkeys for Miranda IM.
+[clist_classic:1100@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_classic:1162@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_nicer:1170@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+[clist_nicer:1162@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+Show/Hide=Hotkey to show or hide the main contact list window as when clicking the tray icon.<br>To enable check the box. Enable the edit box on the right and assign a hotkey by pressing the desired combination.<br>The default combination is <b>Ctrl + Shift + A</b>.
+[clist_classic:1102@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_classic:1163@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_nicer:1119@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+[clist_nicer:1083@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+Read Message=Hotkey to open the message window when a incoming message is notified in the system tray.<br>To enable check the box. Enable the edit box on the right and assign a hotkey by pressing the desired combination.<br>The default combination is <b>Ctrl + Shift + I</b>.
+[clist_classic:1104@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_classic:1164@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_classic:1106@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_classic:1188@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_nicer:1101@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+[clist_nicer:1084@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+[clist_nicer:1106@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+[clist_nicer:1139@A5IEF4oEA18EFzsEA00EFzwEB1IEA3MEA5YEFz4E]
+Net Search=You can make Miranda IM work together with your favourite search engine. Check the <B>Net Search</b> box, enter a keystroke into the hotkey box and put the URL to your search engine into the <b>URL</b> box. Alternatively, you can use the given default search engine. You can fine-tune the behaviour by checking <b>Open in new browser window.</b> in which case a new and not an existing browser window will be used.<br>The default browser will be used for this action.<br>The default combination is <b>Ctrl + Shift + S</b>.
+[clist_classic:1105@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+[clist_classic:1165@A0wEF4oEA04EF4sEA1AEF4wEB1IEA6QEA1EEF40E]
+Show Options=Hotkey to open the options window of Miranda IM just as opening it via the main menu.<br>To enable check the box. Enable the edit box on the right and assign a hotkey by pressing the desired combination.<br>The default combination is <b>Ctrl + Shift + O</b>.
+
+
diff --git a/plugins/ContextHelp/docs/helppack_sample.txt b/plugins/ContextHelp/docs/helppack_sample.txt
new file mode 100644
index 0000000000..4489b4ba96
--- /dev/null
+++ b/plugins/ContextHelp/docs/helppack_sample.txt
@@ -0,0 +1,256 @@
+Miranda Help Pack Version 1
+Language: English (sample)
+Locale: 0809
+Last-Modified-Using: Miranda IM 0.6
+Authors: hrathh
+Author-email: hrathh at users.sourceforge.net
+Plugins-included: help
+
+; This is a sample help package, documenting the file format.
+; For some example help text look at the end of the file.
+
+
+; *** GENERAL ***
+; Blank lines and lines starting with ; are ignored.
+; You cannot use "[identifier] ;this is a comment"
+; The first line must be maintained as-is, or the file will not be recognised
+; Subsequent lines are in HTTP header format. Do not translate anything before
+; the :
+;
+; You normally do not need to restart Miranda IM for changes in this file to take effect.
+; When you add a new context identifier to this file it will be loaded on next request.
+; However, when you update an existing context identifier and you opened up the
+; associated dialog before, you might need to wait until the cache period has elapsed.
+; The chache can be configured in the help options.
+;
+; Spaces are trimmed from the beginning and end of all strings before parsing.
+; Context identifiers that cannot be found in this file will show "No help available".
+;
+; If a context identifier is duplicated, the first occurence in the file will be used.
+
+
+
+; *** HELP TEXTS ***
+; Help texts are formated using regular text which also can contain
+; some simple HTML code.
+;
+; An entry in the help file looks like this:
+;
+; [context_identifier]
+; Some text shown as title=Some text<br>that might contain simple html code
+;
+; A list of available HTML tags that might be helpful when writing
+; help texts:
+; styles: <i>italic</i>, <b>bold</b>, <u>underlined</u>
+; linebreak: <br>
+; sizes: <big>large text</big>, <small>tiny text</small>
+; hyperlinks: <a href="url">link title</a>
+; paragraph: <p>text</p>
+; colors: <font color="#hexcode">colored text</font>
+; instead of #hexcode you can also use the following 16 default color names:
+; black, maroon, green, olive, navy, purple, teal, silver,
+; gray, red, lime, yellow, blue, fuchsia, aqua, white
+
+; Be careful when using those html tags.
+; Do not use them massively, instead pick them when appropriate to
+; guide the reader. Keep in mind that it is still a *help* text.
+;
+; Some special symbols must be specified differently:
+; instead of < use &lt;
+; instead of > use &gt;
+; instead of & use &amp;
+; instead of " use &quot;
+; A space can be also specified as &nbsp;
+;
+; To specify a specific Unicode character
+; you can use a special numeric representation:
+; &#[x][Number];
+; Examples: &#945; &#x3B1;
+; When you place an x in in front the number needs to be hexadecimal.
+
+; If you maintain a help package and want to find out for which element you can write a help text
+; just do the following:
+; When you press the 'ctrl' key while opening up the context help tooltip
+; it will display the help identifier used in the help file instead of
+; the help text. You can use this identifier to add your help text to the text file.
+; When you press 'ctrl'+C when a helptip is already opened, its text will get
+; copied to clipboard.
+;
+
+; If you would like to assign one single help text to multiple context identifiers just group the
+; identifiers together. The following shows for both identifiers the same text:
+; [identifier1]
+; [identifier2]
+; title=text
+
+
+
+; *** GUIDELINES ***
+; (Subject to change)
+; * Avoid the words 'This is...' or 'This shows...' or something like that at the beginning of
+; a help tip. It is a bit annoying when every tip begins with the same words.
+; Example: A tip could begin immediately by 'Shows... or 'Allows you...')
+; * Try to use efficient language to express everything is a quick manner.
+; * Use correct language ;) I know this is difficult.
+; Hint: MS Word helps a lot for language checking the whole helppack ;)
+; Use English (GB) for the default english helppack.
+; * Bundle together logically individable cuntext-identifiers.
+; Radio buttons should not show a different text for each. Instead, create one single helptext
+; for all grouped together radio buttons. However, this is not a 'fixed' quideline. Let's see when it is appropriate.
+; * Always use the term 'Miranda IM' (not just 'Miranda') when you refer to
+; the whole application. We have to keep this consistent somehow.
+; * Please try to avoid refering to 'Miranda IM' as a whole.
+; Try to reformulate the sentence when you would like to use that term.
+; When reformulating is not possible, its usage is all right.
+
+; Some guidelines about links in the help texts:
+; Those <a href> tags should be used _very_ rarely.
+; I think a good rule is: No use of href tags in general. Use them only in exceptional and reasonable situations.
+; Do not set links just because it is possible.
+; Ah...and only link to offical sources as help.miranda-im.org (at least for the default helppack)
+; Linking to following pages is general no good idea:
+; - inofficial pages
+: - (wiki) pages being still empty or almost empty
+; - forum threads (forums do not provide instant help, summarize important points instead)
+; The help yearning user wan't to work with the window and not dig through some webpage.
+; If he would like to visit the forum he would not do so directly.
+; Acceptable links are (for example):
+; - wiki links to help.miranda-im.org
+; Warning: A link to http://help.miranda-im.org/Plugin:SmileyAdd is not a good idea.
+; The user does not need general info about the plugin anymore because he already installed it!
+; Another Warning: Just a link in the helptip is not very helpful.
+; Links can only be an enrichment and not sole purpose on the tip.
+;
+; A very good academic ;) example for a link would be the following:
+; http://help.miranda-im.org/Installing_smileys_in_Miranda
+; Which could be placed on the tooltip of the '...' button of the 'Events'->'Smileys' page.
+; Of course, with additional text describing it.
+
+
+
+; *** HEADER ***
+; The used Help Pack file can be selected using the Language Pack Manager plugin.
+;
+; The information that is shown in the options about your
+; Help Pack file is taken from the header provided in that file.
+; If you are the author of a langpack file, please make sure the information
+; you give there accurate and does follow the offical rules.
+;
+; The information that is shown in the options about your
+; Help Pack file is taken from the header provided in that file.
+; If you are the author of a langpack file, please make sure the information
+; you give there accurate and does follow the offical rules.
+;
+; The official names of the header tags and what they specify are described here:
+; http://svn.sourceforge.net/viewcvs.cgi/*checkout*/miranda/trunk/miranda/i18n/readme.txt
+; They are exactly the same as the ones for a Language Pack file.
+;
+; Following, I provide a summary of the available tags and
+; I also provide some ideas on how those data should/could be formated.
+; You do not need to follow those suggestions, but I think it would
+; make the provided information more comfortable to read and more informative.
+
+; 'Language' header:
+; A full description of the language in the file, in English, e.g.
+; "English (UK)", "English (US) humorous" or "German".
+; This field should really be in in English so everybody knows what is is about.
+; The string is translated using the current langpack before is is displayed.
+;
+; Just provide a country abbreviation in brackets if it is useful.
+; A value of "French (fr-FR)" is not very senseful. "German (AT)" would be a useful example.
+; Just be a bit smart to find out when those brackets are necessary and when not.
+; Several langpack files for the same language are possible using this field, e.g.
+; "German" pack using "Sie" and "German (informal)" using "Du".
+
+; 'Locale' header:
+; The Windows language code. A complete list is available at
+; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/intl/nls_238z.asp
+; Please make sure this is a correct value and not the english one!
+
+; 'Last-Modified-Using' header:
+; Please keep this line up-to-date, containing the last version of Miranda IM
+; you made changes with, so people can compare files containing the same
+; language to see which is the most up-to-date. The formatting of the value is
+; unimportant, as long as it's human-readable.
+; However, it is more comfortable to read when all Language Packs use
+; the same format.
+; I think we could agree upon the following format: "Miranda IM 0.5"
+; When you used an alpha build the following format fits well:
+; "Miranda IM 0.5 alpha build #60" (as shown in About dialog).
+
+; 'Authors' header:
+; A list of people who have worked on this file. When you do something, add (or
+; move) your name to the front, and put a comma (", ") between each name, e.g.
+; "Hari Seldon, Gaal Dornick"
+; By the nature of the options dialog the place to display this value is very limited.
+; Important and actively developing people should be listed first.
+; Real names should be prefered instead of just nicknames.
+
+; 'Author-email' header:
+; Should contain the e-mail of the person that last changed the file only, on the
+; assumption that that person is qualified to manage the file.
+; It is enough when the user knows one single email address he can write to.
+; If you would like that email address to be obfuscated and not to be retrievable by
+; spambots or viruses scanning harddrives, you can use " at " instead of "@", e.g.
+; "joe.farmer at miranda-im.org". This will be recognized by the plugin.
+
+; 'Plugins-included' header:
+; Contains a list of the plugins that are also translated in this file. It
+; should be a comma-delimited list of the plugin DLL filenames!, e.g.:
+; Plugins-included: splitmsgdialogs,import,historyp
+; As stated in the official specification!
+; You really should follow standards here.
+; The Language Pack Manager uses this list to display a list of installed plugins
+; that are not included in this package.
+; "miranda32" or something like that does not really need to be listed here.
+; The list is case-insensitive, but it is a good idea to use exactly the same
+; case of the DLL file.
+; Just use filenames of the plugin DLLs here, e.g.
+; the plugin named "AimOSCAR.dll" should be listed as "AimOSCAR".
+; It is a good idea to even include plugins into this list that do not have any dialogs
+; where help can be shown, as "png2dib.dll".
+; The inclusion list states which plugins are fully covered by context help texts.
+; "png2dib" is, so it should be listed in the header.
+
+
+
+; ** FILENAME ***
+; Something about the 'helppack_*.txt' file name:
+; To use the file, place it in the same directory as miranda32.exe, and call it
+; helppack_*.txt where * can be anything, however it is recommended that it
+; be the language.
+; It makes sense when the * part of the file name is in English and lower case, e.g.
+; "langpack_chineset.txt" for the "Chinese (Traditional)" package.
+; The file name is never displayed so there should be no problems with it being english.
+; Please avoid using any other character than a-z, 0-9 in this name to keep it portable.
+; Important: Do not include a version number in the file name!
+; It is very hard to update the file when the file name changes all the time.
+; This might also result in multiple files of the same language
+; listed in the options.
+
+
+
+; *** EXAMPLES ***
+; The rest of the file are some example help texts.
+; All identifiers of the help plugin's option page are listed.
+
+; Note: I used nonsense text, no productive helptexts...
+
+[help:0@C+kDC+oD]
+dialog help=this is the option pane with the help options
+
+[help:-2@C+kDC+oD]
+[help:-3@C+kDC+oD]
+Cache Expiry together with context help group=some description here and a <a href="http://forums.miranda-im.org">link</a>.
+
+;[help:1001@C+kDC+oD]
+;editbox example=here you can <i>input</i> some value.
+
+[help:1002@C+kDC+oD]
+spin example=here you can press up to move <font color="red">one up</font>, down to move down.
+
+[help:-4@C+kDC+oD]
+static example=the value is stored in seconds, etc.
+
+[help:-5@C+kDC+oD]
+another example=text here
diff --git a/plugins/ContextHelp/res/resource.rc b/plugins/ContextHelp/res/resource.rc
new file mode 100644
index 0000000000..115df3e643
--- /dev/null
+++ b/plugins/ContextHelp/res/resource.rc
@@ -0,0 +1,296 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "..\src\resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Englisch (GB) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+"..\\src\\resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+"#include ""afxres.h""\r\n"
+"\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+"\r\n"
+"\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+#if defined(APSTUDIO_INVOKED) || !defined(EDITOR)
+IDD_HELP DIALOGEX 0, 0, 203, 75
+STYLE DS_SETFOREGROUND | DS_FIXEDSYS | DS_NOFAILCREATE | WS_POPUP |
+ WS_BORDER
+EXSTYLE WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "",IDC_CARETSUCKER,227,47,50,14
+ EDITTEXT IDC_CTLTEXT,3,3,197,10,ES_MULTILINE | ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+ CONTROL "",IDC_TEXT,"RichEdit20A",WS_VSCROLL | WS_TABSTOP |
+ 0x1844,3,13,197,59
+END
+#endif
+
+IDD_SHADOW DIALOGEX 0, 0, 203, 75
+STYLE WS_POPUP
+EXSTYLE WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+END
+
+IDD_OPT_ADV DIALOGEX 0, 0, 191, 48
+STYLE DS_FIXEDSYS | DS_NOFAILCREATE | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Context Help",IDC_STATIC,0,0,191,48
+ CONTROL "&Automatically show help when hovered",
+ IDC_AUTOTIPSENABLED,"Button",BS_AUTOCHECKBOX | BS_TOP |
+ WS_GROUP | WS_TABSTOP,10,13,174,9
+ CONTROL "&Delay:",IDC_AUTOTIPDELAYLABEL,"Static",
+ SS_LEFTNOWORDWRAP,24,28,60,8
+ EDITTEXT IDC_AUTOTIPDELAY,84,26,41,13,ES_AUTOHSCROLL | ES_NUMBER
+ CONTROL "",IDC_AUTOTIPDELAYSPIN,"msctls_updown32",
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY |
+ UDS_ARROWKEYS | UDS_NOTHOUSANDS | WS_TABSTOP,124,26,11,
+ 14
+ CONTROL "milliseconds",IDC_AUTOTIPDELAYMS,"Static",
+ SS_LEFTNOWORDWRAP | SS_NOPREFIX,136,28,48,8
+END
+
+#if defined(APSTUDIO_INVOKED) || defined(EDITOR)
+#if defined(APSTUDIO_INVOKED)
+IDD_HELP$(EDITOR) DIALOGEX 0, 0, 326, 144
+#else
+IDD_HELP DIALOGEX 0, 0, 326, 144
+#endif
+STYLE DS_SETFOREGROUND | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER |
+ WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Help"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Ctl ID: %d",IDC_CTLID,"Static",SS_LEFTNOWORDWRAP |
+ WS_GROUP,5,5,76,8
+ RTEXT "Dialog ID: %s",IDC_DLGID,82,5,239,8,SS_NOPREFIX
+ CONTROL "Type: %s",IDC_CTLTYPE,"Static",SS_LEFTNOWORDWRAP |
+ WS_GROUP,5,15,76,8
+ RTEXT "Module: %s",IDC_MODULE,82,15,239,8,SS_NOPREFIX
+ EDITTEXT IDC_CTLTEXT,5,26,316,12,ES_AUTOHSCROLL
+ CONTROL "",IDC_TEXT,"RichEdit20A",WS_VSCROLL | WS_TABSTOP |
+ 0x1044,5,40,316,99,WS_EX_CLIENTEDGE
+END
+#endif
+
+IDD_UPDATENOTIFY DIALOG DISCARDABLE 0, 0, 259, 115
+STYLE DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE |
+ WS_CAPTION | WS_SYSMENU
+CAPTION "Help Pack Update Now Available"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "A new version of a Help Pack is now available. Click the install button to download and install this new update.",
+ IDC_STATIC,8,7,243,21,SS_NOPREFIX
+ CONTROL "Language:",IDC_LANGUAGELABEL,"Static",SS_SIMPLE |
+ SS_NOPREFIX | WS_GROUP,8,29,65,8
+ EDITTEXT IDC_LANGUAGE,78,29,173,8,ES_AUTOHSCROLL | ES_READONLY |
+ NOT WS_BORDER
+ CONTROL "Current:",IDC_STATIC,"Static",SS_SIMPLE | SS_NOPREFIX |
+ WS_GROUP,8,43,65,8
+ EDITTEXT IDC_CURRENTVERSION,78,43,173,8,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+ CONTROL "New:",IDC_NEWVERSIONLABEL,"Static",SS_SIMPLE |
+ SS_NOPREFIX | WS_GROUP,8,58,65,8
+ EDITTEXT IDC_NEWVERSION,78,58,173,8,ES_AUTOHSCROLL | ES_READONLY |
+ NOT WS_BORDER
+ PUSHBUTTON "&Install Now",IDOK,7,73,66,15,WS_GROUP
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,7,92,245,1
+ DEFPUSHBUTTON "Close",IDCANCEL,202,96,50,15
+END
+
+IDD_DOWNLOADLANG DIALOG DISCARDABLE 0, 0, 200, 126
+STYLE DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE |
+ WS_CAPTION | WS_SYSMENU
+CAPTION "Select your help language"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Help is available in different languages.\nHere is a list of all available languages of the file listing, please select your native &language:",
+ IDC_STATIC,5,5,189,26
+ COMBOBOX IDC_LANGCOMBO,46,36,106,126,CBS_DROPDOWNLIST |
+ CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Download &all languages",IDC_DOWNLOADALL,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,46,53,148,10
+ LTEXT "Note: This will download and install the newest help pack available for the selected language. There might be help packs from other authors available on the file listing.",
+ IDC_STATIC,5,69,189,34,SS_NOPREFIX | WS_DISABLED
+ CTEXT "",IDC_LOADING,1,109,86,8,SS_NOPREFIX | SS_CENTERIMAGE |
+ NOT WS_VISIBLE
+ PUSHBUTTON "OK",IDOK,89,106,50,14,WS_DISABLED
+ DEFPUSHBUTTON "Cancel",IDCANCEL,144,106,50,15
+END
+
+IDD_OPT_LANG DIALOGEX 0, 0, 301, 227
+STYLE DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "",IDC_LANGLIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER |
+ WS_BORDER | WS_GROUP | WS_TABSTOP,2,3,299,61
+ GROUPBOX "",IDC_LANGINFOFRAME,2,68,298,119,WS_GROUP
+ CTEXT "No Help Pack installed!",IDC_NOPACK,5,75,289,108,
+ SS_NOPREFIX | SS_CENTERIMAGE | NOT WS_VISIBLE
+ RTEXT "Author(s):",IDC_LANGAUTHORSLABEL,5,78,66,9,SS_NOPREFIX
+ EDITTEXT IDC_LANGAUTHORS,74,78,220,18,ES_MULTILINE |
+ ES_AUTOVSCROLL | ES_READONLY | NOT WS_BORDER |
+ WS_VSCROLL
+ RTEXT "E-mail:",IDC_LANGEMAILLABEL,5,99,66,8,SS_NOPREFIX
+ CONTROL "",IDC_LANGEMAIL,"Hyperlink",WS_TABSTOP,76,99,207,10
+ RTEXT "Last modified using:",IDC_LANGMODUSINGLABEL,5,112,66,9,
+ SS_NOPREFIX
+ EDITTEXT IDC_LANGMODUSING,75,112,207,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+ RTEXT "Date:",IDC_LANGDATELABEL,5,125,66,9,SS_NOPREFIX
+ EDITTEXT IDC_LANGDATE,75,125,66,12,ES_AUTOHSCROLL | ES_READONLY |
+ NOT WS_BORDER
+ RTEXT "Version:",IDC_LANGVERSIONLABEL,150,125,39,9,SS_NOPREFIX |
+ NOT WS_VISIBLE
+ EDITTEXT IDC_LANGVERSION,194,125,88,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_VISIBLE | NOT WS_BORDER
+ RTEXT "Locale:",IDC_LANGLOCALELABEL,5,137,66,9,SS_NOPREFIX
+ EDITTEXT IDC_LANGLOCALE,75,137,207,12,ES_READONLY | NOT WS_BORDER
+ RTEXT "Not included:",IDC_LANGNOTINCLUDEDLABEL,5,150,66,10,
+ SS_NOPREFIX
+ LISTBOX IDC_LANGNOTINCLUDED,75,150,219,33,NOT LBS_NOTIFY |
+ LBS_SORT | LBS_HASSTRINGS | LBS_USETABSTOPS |
+ LBS_NOINTEGRALHEIGHT | LBS_DISABLENOSCROLL | NOT
+ WS_BORDER | WS_VSCROLL | WS_TABSTOP,WS_EX_TRANSPARENT
+ CONTROL "Download more Help Packs",IDC_MORELANG,"Hyperlink",
+ WS_GROUP | WS_TABSTOP | 0x1,2,191,299,10
+ CONTROL "Check for new &versions of Help Packs periodically",
+ IDC_ENABLEHELPUPDATES,"Button",BS_AUTOCHECKBOX |
+ WS_GROUP | WS_TABSTOP,2,214,209,10
+ PUSHBUTTON "&Download Language",IDC_DOWNLOADLANG,213,213,86,14,
+ WS_GROUP
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_HELP, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 200
+ TOPMARGIN, 3
+ BOTTOMMARGIN, 72
+ END
+
+ IDD_SHADOW, DIALOG
+ BEGIN
+ RIGHTMARGIN, 201
+ BOTTOMMARGIN, 73
+ END
+
+ IDD_OPT_ADV, DIALOG
+ BEGIN
+ VERTGUIDE, 10
+ VERTGUIDE, 24
+ VERTGUIDE, 84
+ VERTGUIDE, 126
+ VERTGUIDE, 184
+ END
+
+ "IDD_HELP$(EDITOR)", DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 321
+ TOPMARGIN, 5
+ BOTTOMMARGIN, 139
+ HORZGUIDE, 15
+ END
+
+ IDD_UPDATENOTIFY, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 252
+ VERTGUIDE, 74
+ TOPMARGIN, 7
+ END
+
+ IDD_DOWNLOADLANG, DIALOG
+ BEGIN
+ VERTGUIDE, 5
+ VERTGUIDE, 46
+ VERTGUIDE, 194
+ HORZGUIDE, 106
+ HORZGUIDE, 121
+ END
+
+ IDD_OPT_LANG, DIALOG
+ BEGIN
+ VERTGUIDE, 2
+ VERTGUIDE, 5
+ VERTGUIDE, 70
+ VERTGUIDE, 75
+ VERTGUIDE, 282
+ VERTGUIDE, 294
+ HORZGUIDE, 160
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // Englisch (GB) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/plugins/ContextHelp/res/version.rc b/plugins/ContextHelp/res/version.rc
new file mode 100644
index 0000000000..5bfbab4754
--- /dev/null
+++ b/plugins/ContextHelp/res/version.rc
@@ -0,0 +1,38 @@
+// Microsoft Visual C++ generated resource script.
+//
+#ifdef APSTUDIO_INVOKED
+#error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+#include "afxres.h"
+#include "..\src\version.h"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION __FILEVERSION_STRING
+ PRODUCTVERSION __FILEVERSION_STRING
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x0L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "FileDescription", __DESCRIPTION
+ VALUE "InternalName", __PLUGIN_NAME
+ VALUE "LegalCopyright", __COPYRIGHT
+ VALUE "OriginalFilename", __FILENAME
+ VALUE "ProductName", __PLUGIN_NAME
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
diff --git a/plugins/ContextHelp/src/datastore.cpp b/plugins/ContextHelp/src/datastore.cpp
new file mode 100644
index 0000000000..84c7474e21
--- /dev/null
+++ b/plugins/ContextHelp/src/datastore.cpp
@@ -0,0 +1,590 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+extern HWND hwndHelpDlg;
+
+struct DlgControlData {
+ int id;
+ int type;
+ TCHAR *szTitle;
+ char *szText; // ANSI or UTF-8 depending on _UNICODE defined (for RichEdit)
+};
+
+struct DialogData {
+ char *szId;
+ char *szModule;
+ struct DlgControlData *control;
+ int controlCount;
+ DWORD timeLoaded, timeLastUsed;
+ int changes;
+ LCID locale;
+ UINT defaultCodePage;
+ int isLocaleRTL;
+};
+
+static struct DialogData *dialogCache = NULL;
+static int dialogCacheCount = 0;
+static CRITICAL_SECTION csDialogCache;
+static HANDLE hServiceFileChange, hFileChange;
+
+#define DIALOGCACHEEXPIRY 10*60*1000 // delete from cache after those milliseconds
+
+static INT_PTR ServiceFileChanged(WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+
+ EnterCriticalSection(&csDialogCache);
+ for (int i = 0; i < dialogCacheCount; i++)
+ dialogCache[i].timeLastUsed = 0;
+ LeaveCriticalSection(&csDialogCache);
+
+ if ((HANDLE)wParam != NULL)
+ FindNextChangeNotification((HANDLE)wParam);
+
+ return 0;
+}
+
+void InitDialogCache(void)
+{
+ TCHAR szFilePath[MAX_PATH], *p;
+
+ InitializeCriticalSection(&csDialogCache);
+
+ hServiceFileChange = CreateServiceFunction("Help/HelpPackChanged", ServiceFileChanged);
+ hFileChange = INVALID_HANDLE_VALUE;
+ if (GetModuleFileName(NULL, szFilePath, sizeof(szFilePath))) {
+ p = _tcsrchr(szFilePath, _T('\\'));
+ if (p != NULL)
+ *(p + 1) = _T('\0');
+ hFileChange = FindFirstChangeNotification(szFilePath, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
+ if (hFileChange != INVALID_HANDLE_VALUE)
+ CallService(MS_SYSTEM_WAITONHANDLE, (WPARAM)hFileChange, (LPARAM)"Help/HelpPackChanged");
+ }
+}
+
+static void FreeDialogCacheEntry(struct DialogData *entry)
+{
+ for (int i = 0; i < entry->controlCount; i++) {
+ mir_free(entry->control[i].szText); // does NULL check
+ mir_free(entry->control[i].szTitle); // does NULL check
+ }
+ mir_free(entry->control); // does NULL check
+ mir_free(entry->szId); // does NULL check
+ mir_free(entry->szModule); // does NULL check
+}
+
+void FreeDialogCache(void)
+{
+ if (hFileChange != INVALID_HANDLE_VALUE) {
+ CallService(MS_SYSTEM_REMOVEWAIT, (WPARAM)hFileChange, 0);
+ FindCloseChangeNotification(hFileChange);
+ }
+ DestroyServiceFunction(hServiceFileChange);
+
+ DeleteCriticalSection(&csDialogCache);
+ for (int i = 0; i < dialogCacheCount; i++)
+ FreeDialogCacheEntry(&dialogCache[i]);
+ dialogCacheCount = 0;
+ mir_free(dialogCache); // does NULL check
+ dialogCache = NULL;
+}
+
+/**************************** LOAD HELP ***************************/
+
+struct LoaderThreadStartParams {
+ HWND hwndCtl;
+ char *szDlgId;
+ char *szModule;
+ int ctrlId;
+};
+
+static void LoaderThread(void *arg)
+{
+ LoaderThreadStartParams *dtsp = (LoaderThreadStartParams *)arg;
+
+ FILE *fp;
+ char line[4096];
+ char *pszLine, *pszColon, *pszBuf;
+ int startOfLine = 0;
+
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind;
+ TCHAR szDir[MAX_PATH];
+ TCHAR szSearch[MAX_PATH];
+ TCHAR *p;
+ int success = 0;
+
+ struct DialogData dialog;
+ struct DlgControlData *control;
+ void *buf;
+ ZeroMemory(&dialog, sizeof(dialog));
+
+ if (GetModuleFileName(NULL, szDir, sizeof(szDir))) {
+ p = _tcsrchr(szDir, _T('\\'));
+ if (p)
+ *p = _T('\0');
+ mir_sntprintf(szSearch, _T("%s\\helppack_*.txt"), szDir);
+
+ hFind = FindFirstFile(szSearch, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName)<4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+ mir_sntprintf(szSearch, _T("%s\\%s"), szDir, wfd.cFileName);
+ success = 1;
+ break;
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+ if (!success) {
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ return;
+ }
+
+ fp = _tfopen(szSearch, _T("rt"));
+ if (fp == NULL) {
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ return;
+ }
+ fgets(line, sizeof(line), fp);
+ TrimString(line);
+ if (lstrcmpA(line, "Miranda Help Pack Version 1")) {
+ fclose(fp);
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ return;
+ }
+
+ // headers
+ dialog.locale = LOCALE_USER_DEFAULT;
+ dialog.defaultCodePage = CP_ACP;
+ while (!feof(fp)) {
+ startOfLine = ftell(fp);
+ if (fgets(line, sizeof(line), fp) == NULL)
+ break;
+ TrimString(line);
+ if (IsEmpty(line) || line[0] == ';' || line[0] == '\0')
+ continue;
+ if (line[0] == '[')
+ break;
+ pszColon = strchr(line, ':');
+ if (pszColon == NULL) {
+ fclose(fp);
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ return;
+ }
+ *pszColon = '\0';
+
+ // locale
+ if (!lstrcmpA(line, "Locale")) {
+ char szCP[6];
+ TrimString(pszColon + 1);
+ dialog.locale = MAKELCID((USHORT)strtol(pszColon + 1, NULL, 16), SORT_DEFAULT);
+ // codepage
+ if (GetLocaleInfoA(dialog.locale, LOCALE_IDEFAULTANSICODEPAGE, szCP, sizeof(szCP))) {
+ szCP[5] = '\0'; // codepages have 5 digits at max
+ dialog.defaultCodePage = atoi(szCP);
+ }
+ }
+ }
+
+ // RTL flag
+ // see also: http://blogs.msdn.com/michkap/archive/2006/03/03/542963.aspx
+ {
+ LOCALESIGNATURE sig;
+ dialog.isLocaleRTL = 0;
+ if (GetLocaleInfo(dialog.locale, LOCALE_FONTSIGNATURE, (LPTSTR)&sig, sizeof(sig) / sizeof(TCHAR)))
+ dialog.isLocaleRTL = (sig.lsUsb[3] & 0x8000000); // Win2000+: testing for 'Layout progress: horizontal from right to left' bit
+ switch (PRIMARYLANGID(LANGIDFROMLCID(dialog.locale))) { // prior to Win2000
+ case LANG_ARABIC:
+ case LANG_HEBREW:
+ case LANG_FARSI:
+ dialog.isLocaleRTL = 1;
+ }
+ }
+
+ // body
+ fseek(fp, startOfLine, SEEK_SET);
+ success = 1;
+ control = NULL;
+ while (!feof(fp)) {
+ if (fgets(line, sizeof(line), fp) == NULL)
+ break;
+ if (IsEmpty(line) || line[0] == ';' || line[0] == '\0')
+ continue;
+ TrimStringSimple(line);
+
+ if (line[0] == '[' && line[lstrlenA(line) - 1] == ']') {
+ pszLine = line + 1;
+ line[lstrlenA(line) - 1] = '\0';
+
+ // module
+ pszColon = strrchr(pszLine, ':');
+ if (pszColon == NULL)
+ continue;
+ *pszColon = '\0';
+ pszColon++;
+ TrimString(pszLine);
+ if (lstrcmpiA(dtsp->szModule, pszLine))
+ continue;
+ pszBuf = pszLine;
+
+ // dlgid
+ pszLine = pszColon;
+ pszColon = strrchr(pszLine, '@');
+ if (pszColon == NULL)
+ continue;
+ *pszColon = '\0';
+ pszColon++;
+ TrimString(pszColon);
+ if (lstrcmpA(dtsp->szDlgId, pszColon))
+ continue;
+
+ if (dialog.szModule == NULL && dialog.szId == NULL) {
+ dialog.szModule = mir_strdup(pszBuf);
+ dialog.szId = mir_strdup(pszColon);
+ if (dialog.szId == NULL || dialog.szModule == NULL) {
+ success = 0;
+ break;
+ }
+ }
+ buf = (struct DlgControlData*)mir_realloc(dialog.control, sizeof(struct DlgControlData)*(dialog.controlCount + 1));
+ if (buf == NULL) {
+ success = 0;
+ break;
+ }
+ dialog.controlCount++;
+ dialog.control = (struct DlgControlData*)buf;
+ control = &dialog.control[dialog.controlCount - 1];
+ ZeroMemory(control, sizeof(*control));
+
+ // ctlid
+ TrimString(pszLine);
+ control->id = atoi(pszLine);
+ }
+ else if (control != NULL) {
+ pszLine = line;
+
+ // ctltext
+ pszColon = strchr(pszLine, '=');
+ if (pszColon == NULL)
+ continue;
+ *pszColon = '\0';
+ pszColon++;
+ TrimString(pszColon);
+ TrimString(pszLine);
+ if (*pszColon == '\0' || *pszLine == '\0')
+ continue;
+ int size = lstrlenA(pszLine) + 1;
+ control->szTitle = (WCHAR*)mir_alloc(size*sizeof(WCHAR));
+ if (control->szTitle != NULL) {
+ *control->szTitle = _T('\0');
+ MultiByteToWideChar(dialog.defaultCodePage, 0, pszLine, -1, control->szTitle, size);
+ }
+
+ // text
+ control->szText = mir_utf8encodecp(pszColon, dialog.defaultCodePage);
+ control = NULL; // control done
+ }
+ }
+ fclose(fp);
+
+ if (success) {
+ int i, dialogInserted = 0;
+
+ dialog.timeLoaded = dialog.timeLastUsed = GetTickCount();
+ EnterCriticalSection(&csDialogCache);
+ for (i = 0; i < dialogCacheCount; i++) {
+ if (dialogCache[i].timeLastUsed && dialogCache[i].timeLastUsed<(dialog.timeLoaded - DIALOGCACHEEXPIRY)) {
+ FreeDialogCacheEntry(&dialogCache[i]);
+ if (dialogInserted || !dialog.controlCount) {
+ MoveMemory(dialogCache + i, dialogCache + i + 1, sizeof(struct DialogData)*(dialogCacheCount - i - 1));
+ dialogCacheCount--;
+ buf = (struct DialogData*)mir_realloc(dialogCache, sizeof(struct DialogData)*dialogCacheCount);
+ if (buf != NULL)
+ dialogCache = (struct DialogData*)buf;
+ else if (!dialogCacheCount)
+ dialogCache = NULL;
+ }
+ else {
+ dialogInserted = 1;
+ dialogCache[i] = dialog;
+ }
+ }
+ }
+ if (dialog.controlCount && !dialogInserted) {
+ buf = (struct DialogData*)mir_realloc(dialogCache, sizeof(struct DialogData)*(dialogCacheCount + 1));
+ if (buf != NULL) {
+ dialogCacheCount++;
+ dialogCache = (struct DialogData*)buf;
+ dialogCache[dialogCacheCount - 1] = dialog;
+ dialogInserted = 1;
+ }
+ }
+ LeaveCriticalSection(&csDialogCache);
+
+ if (!dialogInserted) {
+ mir_free(dialog.szId); // does NULL check
+ mir_free(dialog.szModule); // does NULL check
+ mir_free(dialog.control); // does NULL check
+ }
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADED, 0, (LPARAM)dtsp->hwndCtl);
+ }
+ if (!success) {
+ mir_free(dialog.szId); // does NULL check
+ mir_free(dialog.szModule); // does NULL check
+ mir_free(dialog.control); // does NULL check
+ if (!Miranda_Terminated() && IsWindow(hwndHelpDlg))
+ PostMessage(hwndHelpDlg, M_HELPLOADFAILED, 0, (LPARAM)dtsp->hwndCtl);
+ }
+
+ mir_free(dtsp->szDlgId);
+ mir_free(dtsp->szModule);
+ mir_free(dtsp);
+}
+
+// mir_free() the return value
+char *CreateControlIdentifier(const char *pszDlgId, const char *pszModule, int ctrlId, HWND hwndCtl)
+{
+ int size;
+ char *szId;
+ TCHAR szDefCtlText[128];
+ GetControlTitle(hwndCtl, szDefCtlText, sizeof(szDefCtlText));
+ size = lstrlenA(pszModule) + lstrlenA(pszDlgId) + sizeof(szDefCtlText) + 22;
+ szId = (char*)mir_alloc(size);
+ mir_snprintf(szId, size, "[%s:%i@%s]\r\n%S%s", pszModule, ctrlId, pszDlgId, szDefCtlText, szDefCtlText[0] ? "=" : "");
+
+ return szId;
+}
+
+int GetControlHelp(HWND hwndCtl, const char *pszDlgId, const char *pszModule, int ctrlId, TCHAR **ppszTitle, char **ppszText, int *pType, LCID *pLocaleID, UINT *pCodePage, BOOL *pIsRTL, DWORD flags)
+{
+ int i, j, useNext = 0;
+ struct LoaderThreadStartParams *dtsp;
+
+ EnterCriticalSection(&csDialogCache);
+ for (i = 0; i < dialogCacheCount; i++) {
+ if (!(flags&GCHF_DONTLOAD)) {
+ if (!dialogCache[i].timeLastUsed || dialogCache[i].timeLastUsed<(GetTickCount() - DIALOGCACHEEXPIRY)) {
+ struct DialogData *buf;
+ FreeDialogCacheEntry(&dialogCache[i]);
+ MoveMemory(dialogCache + i, dialogCache + i + 1, sizeof(struct DialogData)*(dialogCacheCount - i - 1));
+ dialogCacheCount--;
+ buf = (struct DialogData*)mir_realloc(dialogCache, sizeof(struct DialogData)*dialogCacheCount);
+ if (buf != NULL)
+ dialogCache = (struct DialogData*)buf;
+ else if (!dialogCacheCount)
+ dialogCache = NULL;
+ i--;
+ continue;
+ }
+ }
+ if (lstrcmpA(pszDlgId, dialogCache[i].szId))
+ break;
+ for (j = 0; j < dialogCache[i].controlCount; j++) {
+ if (ctrlId == dialogCache[i].control[j].id || useNext || dialogCache[i].control[j].id == 0) {
+ if (dialogCache[i].control[j].szTitle == NULL) {
+ useNext = 1;
+ continue;
+ }
+ if (ppszTitle)
+ *ppszTitle = dialogCache[i].control[j].szTitle;
+ if (ppszText)
+ *ppszText = dialogCache[i].control[j].szText;
+ if (pType)
+ *pLocaleID = dialogCache[i].locale;
+ if (pCodePage)
+ *pCodePage = CP_UTF8;
+ if (pIsRTL)
+ *pIsRTL = dialogCache[i].isLocaleRTL;
+ if (dialogCache[i].control[j].id != ctrlId && !useNext)
+ continue;
+ dialogCache[i].timeLastUsed = GetTickCount();
+ LeaveCriticalSection(&csDialogCache);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ if (ppszTitle)
+ *ppszTitle = NULL;
+ if (ppszText)
+ *ppszText = NULL;
+ if (pType)
+ *pType = CTLTYPE_UNKNOWN;
+ if (pLocaleID)
+ *pLocaleID = LOCALE_USER_DEFAULT;
+ if (pCodePage)
+ *pCodePage = CP_ACP;
+
+ if (!(flags&GCHF_DONTLOAD)) {
+ dtsp = (struct LoaderThreadStartParams*)mir_alloc(sizeof(struct LoaderThreadStartParams));
+ if (dtsp == NULL) {
+ LeaveCriticalSection(&csDialogCache);
+ return 0;
+ }
+ dtsp->szDlgId = mir_strdup(pszDlgId);
+ dtsp->szModule = mir_strdup(pszModule);
+ if (dtsp->szDlgId == NULL || dtsp->szModule == NULL) {
+ mir_free(dtsp->szDlgId); // does NULL check
+ mir_free(dtsp->szModule); // does NULL check
+ mir_free(dtsp);
+ LeaveCriticalSection(&csDialogCache);
+ return 0;
+ }
+ dtsp->ctrlId = ctrlId;
+ dtsp->hwndCtl = hwndCtl;
+ mir_forkthread(LoaderThread, dtsp);
+ }
+ LeaveCriticalSection(&csDialogCache);
+
+ return 1;
+}
+
+/**************************** SAVE HELP ***************************/
+
+#ifdef EDITOR
+void SetControlHelp(const char *pszDlgId, const char *pszModule, int ctrlId, TCHAR *pszTitle, char *pszText, int type)
+{
+ int i, j;
+ int found = 0;
+ void *buf;
+
+ EnterCriticalSection(&csDialogCache);
+ j = 0;
+ for (i = 0; i < dialogCacheCount; i++) {
+ if (lstrcmpA(pszDlgId, dialogCache[i].szId))
+ continue;
+ for (j = 0; j < dialogCache[i].controlCount; j++) {
+ if (ctrlId == dialogCache[i].control[j].id) {
+ mir_free(dialogCache[i].control[j].szTitle); // does NULL check
+ mir_free(dialogCache[i].control[j].szText); // does NULL check
+ dialogCache[i].control[j].szTitle = NULL;
+ dialogCache[i].control[j].szText = NULL;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ buf = (struct DlgControlData*)mir_realloc(dialogCache[i].control, sizeof(struct DlgControlData)*(dialogCache[i].controlCount + 1));
+ if (buf == NULL) {
+ LeaveCriticalSection(&csDialogCache);
+ return;
+ }
+ dialogCache[i].control = (struct DlgControlData*)buf;
+ j = dialogCache[i].controlCount++;
+ found = 1;
+ }
+ break;
+ }
+ if (!found) {
+ buf = (struct DialogData*)mir_realloc(dialogCache, sizeof(struct DialogData)*(dialogCacheCount + 1));
+ if (buf == NULL) {
+ LeaveCriticalSection(&csDialogCache);
+ return;
+ }
+ dialogCache = (struct DialogData*)buf;
+ dialogCache[i].control = (struct DlgControlData*)mir_alloc(sizeof(struct DlgControlData));
+ if (dialogCache[i].control == NULL) {
+ LeaveCriticalSection(&csDialogCache);
+ return;
+ }
+ dialogCache[i].controlCount = 1;
+ i = dialogCacheCount;
+ j = 0;
+
+ dialogCache[i].szId = mir_strdup(pszDlgId);
+ dialogCache[i].szModule = mir_strdup(pszModule);
+ if (dialogCache[i].szId == NULL || dialogCache[i].szModule == NULL) {
+ mir_free(dialogCache[i].szId); // does NULL check
+ mir_free(dialogCache[i].szModule); // does NULL check
+ LeaveCriticalSection(&csDialogCache);
+ return;
+ }
+ dialogCacheCount++;
+ dialogCache[i].timeLoaded = 0;
+ }
+ dialogCache[i].control[j].szTitle = mir_tstrdup(pszTitle); // does NULL arg check
+ dialogCache[i].control[j].szText = mir_strdup(pszText); // does NULL arg check
+ dialogCache[i].control[j].type = type;
+ dialogCache[i].control[j].id = ctrlId;
+ dialogCache[i].timeLastUsed = GetTickCount();
+ dialogCache[i].changes = 1;
+ LeaveCriticalSection(&csDialogCache);
+}
+
+static void DialogCacheSaveThread(void *unused)
+{
+ int success = 0;
+ UNREFERENCED_PARAMETER(unused);
+
+ // TODO: port the following code to write to the helppack file instead
+ // (netlib code already removed)
+ /*
+ { WCHAR *szDecoded;
+ char *szEncoded=mir_strdup(dialogCache[i].control[j].szText);
+ if (mir_utf8decode(szEncoded, &szDecoded)) {
+ [...]
+ mir_free(szDecoded)
+ }
+ mir_free(szEncoded); // does NULL check
+ }
+
+
+ int i, j;
+ struct ResizableCharBuffer data={0}, dlgId={0};
+
+ for (i = 0; i < dialogCacheCount; i++) {
+ if(!dialogCache[i].changes) continue;
+
+ AppendToCharBuffer(&data, "t <dialog id=\"%s\" module=\"%s\">\r\n", dialogCache[i].szId, dialogCache[i].szModule);
+ for (j = 0; j < dialogCache[i].controlCount; j++) {
+ AppendToCharBuffer(&data, "<control id=%d type=%d>\r\n", dialogCache[i].control[j].id, dialogCache[i].control[j].type);
+ if (dialogCache[i].control[j].szTitle)
+ AppendToCharBuffer(&data, "<title>%s</title>\r\n", dialogCache[i].control[j].szTitle);
+ if (dialogCache[i].control[j].szText)
+ AppendToCharBuffer(&data, "<text>%s</text>\r\n", dialogCache[i].control[j].szText);
+ AppendToCharBuffer(&data, "</control>\r\n");
+ }
+ AppendToCharBuffer(&data, "</dialog>");
+ }
+
+ if(success) {
+ dialogCache[i].changes = 0;
+ dialogCache[i].timeLoaded = GetTickCount();
+ }
+ */
+ MessageBoxEx(NULL, success ? TranslateT("Help saving complete.") : TranslateT("Help saving failed!"), TranslateT("Help Editor"), (success ? MB_ICONINFORMATION : MB_ICONERROR) | MB_OK | MB_SETFOREGROUND | MB_TOPMOST | MB_TASKMODAL, LANGIDFROMLCID(Langpack_GetDefaultLocale()));
+}
+
+void SaveDialogCache(void)
+{
+ mir_forkthread(DialogCacheSaveThread, 0);
+}
+#endif // defined EDITOR
diff --git a/plugins/ContextHelp/src/dlgboxsubclass.cpp b/plugins/ContextHelp/src/dlgboxsubclass.cpp
new file mode 100644
index 0000000000..ab0a63f4dd
--- /dev/null
+++ b/plugins/ContextHelp/src/dlgboxsubclass.cpp
@@ -0,0 +1,492 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+#define SC_CONTEXTHELP_SEPARATOR SC_SEPARATOR+1
+#define SC_CONTEXTHELP_DIALOG SC_CONTEXTHELP+1
+
+#define PROP_CONTEXTSTATE _T("HelpPlugin_ContextState")
+#define PROPF_MENUFORCED 0x01 // always show help context menu for ctl (override default)
+#define PROPF_MENUDISABLED 0x02 // never show help context menu for ctl
+#define PROPF_AUTOTIPFORCED 0x04 // always show autotip for ctl (override default)
+#define PROPF_AUTOTIPDISABLED 0x08 // never show autotip for ctl
+
+extern HINSTANCE hInst;
+extern HWND hwndHelpDlg;
+extern WORD settingAutoTipDelay;
+static HHOOK hMessageHook, hKeyboardHook, hEatNextMouseHook = NULL;
+static HANDLE hServiceShowHelp, hServiceSetContext;
+
+struct DlgBoxSubclassData {
+ HWND hwndDlg;
+ WNDPROC pfnOldWndProc;
+ DWORD flags;
+} static *dlgBoxSubclass = NULL;
+static int dlgBoxSubclassCount = 0;
+static CRITICAL_SECTION csDlgBoxSubclass;
+
+#define DBSDF_MINIMIZABLE 0x01 // WS_MINIMIZEBOX style was set on hooked window
+#define DBSDF_MAXIMIZABLE 0x02 // WS_MAXIMIZEBOX style was set on hooked window
+
+struct FindChildAtPointData {
+ HWND hwnd;
+ POINT pt;
+ int bestArea;
+};
+
+// ChildWindowFromPoint() messes up with group boxes
+static INT_PTR CALLBACK FindChildAtPointEnumProc(HWND hwnd, LPARAM lParam)
+{
+ if (IsWindowVisible(hwnd)) {
+ struct FindChildAtPointData *fcap = (struct FindChildAtPointData*)lParam;
+ RECT rcVisible, rc, rcParent;
+ GetWindowRect(hwnd, &rc);
+ GetWindowRect(GetParent(hwnd), &rcParent);
+ IntersectRect(&rcVisible, &rcParent, &rc);
+ if (PtInRect(&rcVisible, fcap->pt)) {
+ int thisArea = (rc.bottom - rc.top)*(rc.right - rc.left);
+ if (thisArea && (thisArea<fcap->bestArea || fcap->bestArea == 0)) {
+ fcap->bestArea = thisArea;
+ fcap->hwnd = hwnd;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// IsChild() messes up with owned windows
+int IsRealChild(HWND hwndParent, HWND hwnd)
+{
+ while (hwnd != NULL) {
+ if (hwnd == hwndParent)
+ return 1;
+ if (hwndParent == GetWindow(hwnd, GW_OWNER))
+ return 0;
+ hwnd = GetParent(hwnd);
+ }
+
+ return 0;
+}
+
+static BOOL CALLBACK RemovePropForAllChildsEnumProc(HWND hwnd, LPARAM lParam)
+{
+ RemoveProp(hwnd, (TCHAR*)lParam);
+
+ return TRUE;
+}
+
+static HWND hwndMouseMoveDlg = NULL;
+static UINT idMouseMoveTimer = 0;
+static LONG cursorPos = MAKELONG(-1, -1);
+static int openedAutoTip = 0;
+
+static void CALLBACK NoMouseMoveForDelayTimerProc(HWND hwnd, UINT msg, UINT idTimer, DWORD time)
+{
+ POINT pt;
+ HWND hwndCtl;
+ struct FindChildAtPointData fcap;
+ UNREFERENCED_PARAMETER(msg);
+ UNREFERENCED_PARAMETER(time);
+
+ KillTimer(hwnd, idTimer);
+ if (idMouseMoveTimer != idTimer)
+ return;
+ idMouseMoveTimer = 0;
+ if (!settingAutoTipDelay || !IsWindow(hwndMouseMoveDlg))
+ return;
+
+ ZeroMemory(&fcap, sizeof(fcap));
+ if (!GetCursorPos(&pt))
+ return;
+ // ChildWindowFromPoint() messes up with group boxes
+ fcap.hwnd = NULL;
+ fcap.pt = pt;
+ EnumChildWindows(hwndMouseMoveDlg, (WNDENUMPROC)FindChildAtPointEnumProc, (LPARAM)&fcap);
+ hwndCtl = fcap.hwnd;
+ if (hwndCtl == NULL) {
+ ScreenToClient(hwndMouseMoveDlg, &pt);
+ hwndCtl = ChildWindowFromPointEx(hwndMouseMoveDlg, pt, CWP_SKIPINVISIBLE);
+ if (hwndCtl == NULL)
+ return;
+ }
+
+ LONG flags = (LONG)GetProp(hwndCtl, PROP_CONTEXTSTATE);
+ if (flags&PROPF_AUTOTIPDISABLED)
+ return;
+ flags = SendMessage(hwndCtl, WM_GETDLGCODE, (WPARAM)VK_RETURN, (LPARAM)NULL);
+ if (flags&DLGC_HASSETSEL || flags&DLGC_WANTALLKEYS)
+ return; // autotips on edits are annoying
+
+ CURSORINFO ci;
+ BOOL(WINAPI *pfnGetCursorInfo)(CURSORINFO*);
+ ci.cbSize = sizeof(ci);
+ *(FARPROC*)&pfnGetCursorInfo = GetProcAddress(GetModuleHandleA("USER32"), "GetCursorInfo");
+ // if(pfnGetCursorInfo && IsWinVer2000Plus()) // call not safe for WinNT4
+ if (pfnGetCursorInfo(&ci) && !(ci.flags&CURSOR_SHOWING))
+ return;
+
+ if (IsRealChild(hwndMouseMoveDlg, hwndCtl) && hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ return;
+ openedAutoTip = 1;
+ PostMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hwndCtl);
+ }
+}
+
+static LRESULT CALLBACK KeyboardInputHookProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code == HC_ACTION && idMouseMoveTimer != 0) {
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ }
+ return CallNextHookEx(hKeyboardHook, code, wParam, lParam);
+}
+
+// workaround for WM_HELP (SC_CONTEXTHELP causes an additional WM_LBUTTONUP when selecting)
+static LRESULT CALLBACK EatNextMouseButtonUpHookProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code >= 0 && wParam == WM_LBUTTONUP) {
+ UnhookWindowsHookEx(hEatNextMouseHook); // unhook ourselves
+ hEatNextMouseHook = NULL;
+ return -1;
+ }
+ return CallNextHookEx(hEatNextMouseHook, code, wParam, lParam);
+}
+
+static LRESULT CALLBACK DialogBoxSubclassProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WNDPROC pfnWndProc;
+ DWORD flags;
+ int i;
+
+ EnterCriticalSection(&csDlgBoxSubclass);
+ for (i = 0; i<dlgBoxSubclassCount; i++)
+ if (dlgBoxSubclass[i].hwndDlg == hwndDlg)
+ break;
+ if (i == dlgBoxSubclassCount) {
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ return 0;
+ }
+ pfnWndProc = dlgBoxSubclass[i].pfnOldWndProc;
+ flags = dlgBoxSubclass[i].flags;
+ if (msg == WM_NCDESTROY) {
+ struct DlgBoxSubclassData *buf;
+ MoveMemory(dlgBoxSubclass + i, dlgBoxSubclass + i + 1, sizeof(struct DlgBoxSubclassData)*(dlgBoxSubclassCount - i - 1));
+ dlgBoxSubclassCount--;
+ buf = (struct DlgBoxSubclassData*)mir_realloc(dlgBoxSubclass, sizeof(struct DlgBoxSubclassData)*dlgBoxSubclassCount);
+ if (buf != NULL)
+ dlgBoxSubclass = buf;
+ else if (!dlgBoxSubclassCount)
+ dlgBoxSubclass = NULL;
+ }
+ LeaveCriticalSection(&csDlgBoxSubclass);
+
+ switch (msg) {
+ case WM_INITMENUPOPUP:
+ if (flags&DBSDF_MINIMIZABLE || flags&DBSDF_MAXIMIZABLE) {
+ HMENU hMenu = GetSystemMenu(hwndDlg, FALSE);
+ if ((HMENU)wParam != hMenu)
+ break;
+ int isMin = IsIconic(hwndDlg);
+ int isMax = IsZoomed(hwndDlg);
+ EnableMenuItem(hMenu, SC_RESTORE, MF_BYCOMMAND | (isMin || isMax) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hMenu, SC_MINIMIZE, MF_BYCOMMAND | (flags&DBSDF_MINIMIZABLE && !isMin) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hMenu, SC_MAXIMIZE, MF_BYCOMMAND | (flags&DBSDF_MAXIMIZABLE && !isMax) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hMenu, SC_SIZE, MF_BYCOMMAND | (GetWindowLongPtr(hwndDlg, GWL_STYLE)&WS_THICKFRAME && !isMin && !isMax) ? MF_ENABLED : MF_GRAYED);
+ }
+ break;
+ case WM_MOUSEMOVE: // TrackMouseEvent() would disturb too much
+ if (!settingAutoTipDelay)
+ break;
+ if (cursorPos == lParam)
+ break;
+ cursorPos = lParam;
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MOUSEWHEEL:
+ if (!settingAutoTipDelay)
+ break;
+ if (msg != WM_MOUSEMOVE && !idMouseMoveTimer)
+ break;
+ if (openedAutoTip && IsWindow(hwndHelpDlg))
+ DestroyWindow(hwndHelpDlg);
+ openedAutoTip = 0;
+ hwndMouseMoveDlg = hwndDlg;
+ if (hwndHelpDlg == NULL)
+ idMouseMoveTimer = SetTimer(NULL, idMouseMoveTimer, settingAutoTipDelay, (TIMERPROC)NoMouseMoveForDelayTimerProc);
+ break;
+ case WM_CAPTURECHANGED:
+ if ((HWND)lParam == hwndDlg)
+ break;
+ case WM_SHOWWINDOW:
+ case WM_WINDOWPOSCHANGING:
+ case WM_MOVING:
+ case WM_SIZING:
+ case WM_CANCELMODE:
+ case WM_CHILDACTIVATE:
+ case WM_MOUSEACTIVATE:
+ case WM_ACTIVATEAPP:
+ case WM_ACTIVATE:
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ break;
+ case WM_SYSCOMMAND:
+ if ((UINT)wParam == SC_CONTEXTHELP_DIALOG) { // alt. "What's this Dialog?"
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ if (hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ break;
+ }
+ SendMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hwndDlg);
+ return 0;
+ }
+ break;
+ case WM_CONTEXTMENU:
+ {
+ POINT pt;
+ HWND hwndCtl;
+ struct FindChildAtPointData fcap;
+
+ // workaround for badly coded plugins that do display a context menu
+ // and pass the message to DefWindowProc afterwards (doing a "break;").
+ if (GetTickCount() - GetMessageTime()>10)
+ return 0;
+
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+
+ ZeroMemory(&fcap, sizeof(fcap));
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ // ChildWindowFromPoint() messes up with group boxes
+ fcap.hwnd = NULL;
+ fcap.pt = pt;
+ EnumChildWindows(hwndDlg, (WNDENUMPROC)FindChildAtPointEnumProc, (LPARAM)&fcap);
+ hwndCtl = fcap.hwnd;
+ if (hwndCtl == NULL) {
+ ScreenToClient(hwndDlg, &pt);
+ hwndCtl = ChildWindowFromPointEx(hwndDlg, pt, CWP_SKIPINVISIBLE);
+ if (hwndCtl == NULL)
+ break;
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ }
+ {
+ LONG flags = (LONG)GetProp(hwndCtl, PROP_CONTEXTSTATE);
+ if (flags&PROPF_MENUDISABLED)
+ break;
+ else if (!(flags&PROPF_MENUFORCED)) {
+ int type = GetControlType(hwndCtl);
+ // showing a context menu on these looks silly (multi components)
+ if (type == CTLTYPE_TOOLBAR || type == CTLTYPE_LISTVIEW || type == CTLTYPE_TREEVIEW || type == CTLTYPE_STATUSBAR || type == CTLTYPE_CLC)
+ break;
+ }
+ }
+ if (IsRealChild(hwndDlg, hwndCtl)) {
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, SC_CONTEXTHELP, (hwndCtl == hwndDlg) ? TranslateT("&What's this Dialog?") : TranslateT("&What's this?"));
+ if (TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_HORPOSANIMATION | TPM_VERPOSANIMATION | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, hwndDlg, NULL)) {
+ if (hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL) {
+ DestroyMenu(hMenu);
+ break;
+ }
+ }
+ SendMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hwndCtl);
+ }
+ DestroyMenu(hMenu);
+ }
+ return 0;
+ }
+ case WM_HELP:
+ {
+ HELPINFO *hi = (HELPINFO*)lParam;
+ if (hi->iContextType != HELPINFO_WINDOW) break;
+ // fix for SHBrowseForFolder() dialog, which sends unhandled help to parent
+ if (!IsRealChild(hwndDlg, (HWND)hi->hItemHandle))
+ break;
+
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+
+ if (!IsWindow(hwndHelpDlg)) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ break;
+ }
+ SendMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)hi->hItemHandle);
+ // we need to eat the next WM_LBUTTONDOWN (if invoked by mouse)
+ if (GetKeyState(GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON) & 0x8000 && hEatNextMouseHook == NULL)
+ hEatNextMouseHook = SetWindowsHookEx(WH_MOUSE, EatNextMouseButtonUpHookProc, NULL, GetCurrentThreadId());
+ return TRUE;
+ }
+ case WM_NCDESTROY:
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ EnumChildWindows(hwndDlg, RemovePropForAllChildsEnumProc, (LPARAM)PROP_CONTEXTSTATE);
+ {
+ TCHAR text[64];
+ mir_sntprintf(text, sizeof(text), _T("unhooked window 0x%X for context help\n"), hwndDlg);
+ OutputDebugString(text);
+ }
+ SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)pfnWndProc);
+ break;
+ }
+ return CallWindowProc(pfnWndProc, hwndDlg, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK HelpSendMessageHookProc(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code >= 0) {
+ CWPSTRUCT *msg = (CWPSTRUCT*)lParam;
+ switch (msg->message) {
+ case WM_INITDIALOG: // dialogs and message boxes
+ if (GetClassLong(msg->hwnd, GCW_ATOM) != 32770) // class="#32770"
+ break;
+ if (msg->hwnd == hwndHelpDlg || (DLGPROC)GetWindowLongPtr(msg->hwnd, DWLP_DLGPROC) == HelpDlgProc)
+ break;
+#ifndef EDITOR
+ if ((DLGPROC)GetWindowLongPtr(msg->hwnd, DWLP_DLGPROC) == ShadowDlgProc)
+ break;
+#endif
+ {
+ LONG_PTR style, exStyle;
+ struct DlgBoxSubclassData *buf;
+
+ exStyle = GetWindowLongPtr(msg->hwnd, GWL_EXSTYLE);
+ if (exStyle&WS_EX_CONTEXTHELP)
+ break;
+ style = GetWindowLongPtr(msg->hwnd, GWL_STYLE);
+
+ EnterCriticalSection(&csDlgBoxSubclass);
+ buf = (struct DlgBoxSubclassData*)mir_realloc(dlgBoxSubclass, sizeof(struct DlgBoxSubclassData)*(dlgBoxSubclassCount + 1));
+ if (buf == NULL) {
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ break;
+ }
+ dlgBoxSubclass = buf;
+ dlgBoxSubclass[dlgBoxSubclassCount].hwndDlg = msg->hwnd;
+ dlgBoxSubclass[dlgBoxSubclassCount].pfnOldWndProc = (WNDPROC)SetWindowLongPtr(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)DialogBoxSubclassProc);
+ dlgBoxSubclass[dlgBoxSubclassCount].flags = 0;
+
+ // WS_EX_CONTEXTHELP cannot be used in conjunction WS_MINIMIZEBOX or WS_MAXIMIZEBOX
+ // solution: switch off WS_MINIMIZEBOX or WS_MAXIMIZEBOX when only one of them is present
+ if (!(style & WS_MINIMIZEBOX) || !(style & WS_MAXIMIZEBOX)) {
+ if (style & WS_MINIMIZEBOX)
+ dlgBoxSubclass[dlgBoxSubclassCount].flags |= DBSDF_MINIMIZABLE;
+ if (style & WS_MAXIMIZEBOX)
+ dlgBoxSubclass[dlgBoxSubclassCount].flags |= DBSDF_MAXIMIZABLE;
+ SetWindowLongPtr(msg->hwnd, GWL_STYLE, style&(~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX)));
+ SetWindowLongPtr(msg->hwnd, GWL_EXSTYLE, (LONG_PTR)exStyle | WS_EX_CONTEXTHELP);
+ }
+ dlgBoxSubclassCount++;
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ }
+ {
+ HMENU hMenu;
+ hMenu = GetSystemMenu(msg->hwnd, FALSE);
+ if (hMenu != NULL && AppendMenu(hMenu, MF_SEPARATOR, SC_CONTEXTHELP_SEPARATOR, NULL)) {
+ AppendMenu(hMenu, MF_STRING, SC_CONTEXTHELP, TranslateT("&What's this?"));
+ AppendMenu(hMenu, MF_STRING, SC_CONTEXTHELP_DIALOG, TranslateT("&What's this Dialog?"));
+ }
+ }
+ {
+ TCHAR text[64];
+ mir_sntprintf(text, sizeof(text), _T("hooked window 0x%X for context help\n"), msg->hwnd);
+ OutputDebugString(text);
+ }
+ break;
+ }
+ }
+ return CallNextHookEx(hMessageHook, code, wParam, lParam);
+}
+
+static INT_PTR ServiceShowHelp(WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+ if (!IsWindow((HWND)wParam))
+ return 1;
+ if (idMouseMoveTimer)
+ KillTimer(NULL, idMouseMoveTimer);
+ idMouseMoveTimer = 0;
+ if (hwndHelpDlg == NULL) {
+ hwndHelpDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_HELP), NULL, HelpDlgProc);
+ if (hwndHelpDlg == NULL)
+ return 2;
+ }
+ PostMessage(hwndHelpDlg, M_CHANGEHELPCONTROL, 0, (LPARAM)wParam);
+
+ return 0;
+}
+
+static INT_PTR ServiceSetContextState(WPARAM wParam, LPARAM lParam)
+{
+ int i;
+ LONG flags;
+ EnterCriticalSection(&csDlgBoxSubclass);
+ for (i = 0; i < dlgBoxSubclassCount; i++)
+ if (IsRealChild(dlgBoxSubclass[i].hwndDlg, (HWND)wParam))
+ break;
+ if (i == dlgBoxSubclassCount) {
+ LeaveCriticalSection(&csDlgBoxSubclass);
+
+ return 2;
+ }
+ LeaveCriticalSection(&csDlgBoxSubclass);
+ flags = (lParam&HCSF_CONTEXTMENU) ? PROPF_MENUFORCED : PROPF_MENUDISABLED;
+ flags |= (lParam&HCSF_AUTOTIP) ? PROPF_AUTOTIPFORCED : PROPF_AUTOTIPDISABLED;
+
+ return !SetProp((HWND)wParam, PROP_CONTEXTSTATE, (HANDLE)flags);
+}
+
+int InstallDialogBoxHook(void)
+{
+ InitializeCriticalSection(&csDlgBoxSubclass);
+ hServiceShowHelp = CreateServiceFunction(MS_HELP_SHOWHELP, ServiceShowHelp);
+ hServiceSetContext = CreateServiceFunction(MS_HELP_SETCONTEXTSTATE, ServiceSetContextState);
+ hMessageHook = SetWindowsHookEx(WH_CALLWNDPROC, HelpSendMessageHookProc, NULL, GetCurrentThreadId()); // main thread
+ hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardInputHookProc, NULL, GetCurrentThreadId()); // main thread
+
+ return hMessageHook == NULL;
+}
+
+int RemoveDialogBoxHook(void)
+{
+ DestroyServiceFunction(hServiceShowHelp); // does NULL check
+ DestroyServiceFunction(hServiceSetContext); // does NULL check
+ UnhookWindowsHookEx(hMessageHook);
+ if (hKeyboardHook)
+ UnhookWindowsHookEx(hMessageHook);
+ if (hEatNextMouseHook)
+ UnhookWindowsHookEx(hEatNextMouseHook);
+ DeleteCriticalSection(&csDlgBoxSubclass);
+ for (int i = 0; i<dlgBoxSubclassCount; i++)
+ SetWindowLongPtr(dlgBoxSubclass[i].hwndDlg, GWLP_WNDPROC, (LONG_PTR)dlgBoxSubclass[i].pfnOldWndProc);
+ mir_free(dlgBoxSubclass); // does NULL check
+
+ return 0;
+}
diff --git a/plugins/ContextHelp/src/help.h b/plugins/ContextHelp/src/help.h
new file mode 100644
index 0000000000..cd6e095e7e
--- /dev/null
+++ b/plugins/ContextHelp/src/help.h
@@ -0,0 +1,146 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef __CONTEXTHELP_HELP_H__
+#define __CONTEXTHELP_HELP_H__
+
+// dlgboxsubclass.c
+int InstallDialogBoxHook(void);
+int RemoveDialogBoxHook(void);
+
+// utils.c
+#define CTLTYPE_UNKNOWN 0
+#define CTLTYPE_DIALOG 1
+#define CTLTYPE_BUTTON 2
+#define CTLTYPE_CHECKBOX 3
+#define CTLTYPE_RADIO 4
+#define CTLTYPE_TEXT 5
+#define CTLTYPE_IMAGE 6
+#define CTLTYPE_EDIT 7
+#define CTLTYPE_GROUP 8
+#define CTLTYPE_COMBO 9
+#define CTLTYPE_LIST 10
+#define CTLTYPE_SPINEDIT 11
+#define CTLTYPE_PROGRESS 12
+#define CTLTYPE_SLIDER 13
+#define CTLTYPE_LISTVIEW 14
+#define CTLTYPE_TREEVIEW 15
+#define CTLTYPE_DATETIME 16
+#define CTLTYPE_IP 17
+#define CTLTYPE_STATUSBAR 18
+#define CTLTYPE_HYPERLINK 19
+#define CTLTYPE_CLC 20
+#define CTLTYPE_SCROLL 21
+#define CTLTYPE_ANIMATION 22
+#define CTLTYPE_HOTKEY 23
+#define CTLTYPE_TABS 24
+#define CTLTYPE_COLOUR 25
+#define CTLTYPE_TOOLBAR 26
+#define CTLTYPE_SIZEGRIP 27
+extern const TCHAR *szControlTypeNames[];
+int GetControlType(HWND hwndCtl);
+HWND GetControlDialog(HWND hwndCtl);
+int GetControlTitle(HWND hwndCtl, TCHAR *pszTitle, int cchTitle);
+char *GetControlModuleName(HWND hwndCtl);
+int GetControlID(HWND hwndCtl);
+char *CreateDialogIdString(HWND hwndDlg);
+struct ResizableCharBuffer {
+ char *sz;
+ int iEnd, cbAlloced;
+};
+void AppendCharToCharBuffer(struct ResizableCharBuffer *rcb, char c);
+void AppendToCharBuffer(struct ResizableCharBuffer *rcb, const char *fmt, ...);
+
+// helpdlg.c
+#define M_CHANGEHELPCONTROL (WM_APP+0x100)
+#define M_HELPLOADED (WM_APP+0x101)
+#ifdef EDITOR
+#define M_SAVECOMPLETE (WM_APP+0x102)
+#endif
+#define M_LOADHELP (WM_APP+0x103)
+#define M_HELPLOADFAILED (WM_APP+0x104)
+#define M_CLIPBOARDCOPY (WM_APP+0x105)
+INT_PTR CALLBACK HelpDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK ShadowDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// streaminout.c
+void StreamInHtml(HWND hwndEdit, const char *szHtml, UINT codepage, COLORREF clrBkgrnd);
+#ifdef EDITOR
+char *StreamOutHtml(HWND hwndEdit);
+#endif
+#define TEXTSIZE_BIG 18 // in half points
+#define TEXTSIZE_NORMAL 16
+#define TEXTSIZE_SMALL 13
+#ifndef EDITOR
+void FreeHyperlinkData(void);
+int IsHyperlink(LONG cpPos, LONG *pcpMin, LONG *pcpMax, char **ppszLink);
+#endif
+
+// datastore.c
+void InitDialogCache(void);
+void FreeDialogCache(void);
+#define GCHF_DONTLOAD 1
+int GetControlHelp(HWND hwndCtl, const char *pszDlgId, const char *pszModule, int ctrlId, TCHAR **ppszTitle, char **ppszText, int *pType, LCID *pLocaleID, UINT *pCodePage, BOOL *pIsRTL, DWORD flags);
+#ifdef EDITOR
+void SetControlHelp(const char *pszDlgId, const char *pszModule, int ctrlId, TCHAR *pszTitle, char *pszText, int type);
+void SaveDialogCache(void);
+#else
+char *CreateControlIdentifier(const char *pszDlgId, const char *pszModule, int ctrlId, HWND hwndCtl);
+#endif
+void RegisterFileChange(void);
+void CloseFileChange(void);
+
+// options.c
+void ReloadLangOptList(void);
+void InitOptions(void);
+void UninitOptions(void);
+
+// helppack.c
+void TrimStringSimple(char *str);
+void TrimString(char *str);
+BOOL IsEmpty(const char *str);
+typedef struct {
+ TCHAR szLanguage[64];
+ LCID Locale;
+ WORD codepage;
+ char szAuthors[1024];
+ char szAuthorEmail[128];
+ char szLastModifiedUsing[64];
+ char szPluginsIncluded[4080];
+ char szVersion[21];
+ char szFLName[128];
+ FILETIME ftFileDate;
+ TCHAR szFileName[MAX_PATH]; /* just the file name itself */
+ BYTE flags; /* see HPIF_* flags */
+} HELPPACK_INFO;
+#define HPF_ENABLED 0x01 // pack is enabled
+#define HPF_NOLOCALE 0x02 // pack has no valid locale
+#define HPF_DEFAULT 0x04 // pack is english default
+BOOL GetPackPath(TCHAR *pszPath, int nSize, BOOL fEnabledPacks, const TCHAR *pszFile);
+typedef INT_PTR(CALLBACK *ENUM_PACKS_CALLBACK)(HELPPACK_INFO *pack, WPARAM wParam, LPARAM lParam);
+BOOL EnumPacks(ENUM_PACKS_CALLBACK callback, const TCHAR *pszFilePattern, const char *pszFileVersionHeader, WPARAM wParam, LPARAM lParam);
+BOOL IsPluginIncluded(const HELPPACK_INFO *pack, char *pszFileBaseName);
+BOOL EnablePack(const HELPPACK_INFO *pack, const TCHAR *pszFilePattern);
+void CorrectPacks(const TCHAR *pszFilePattern, const TCHAR *pszDefaultFile, BOOL fDisableAll);
+
+/* update.c
+INT_PTR ServiceShowLangDialog(WPARAM wParam, LPARAM lPARAM);
+void InitUpdate(void);
+void UninitUpdate(void);*/
+
+#endif // __CONTEXTHELP_HELP_H__ \ No newline at end of file
diff --git a/plugins/ContextHelp/src/helpdlg.cpp b/plugins/ContextHelp/src/helpdlg.cpp
new file mode 100644
index 0000000000..b035777a0f
--- /dev/null
+++ b/plugins/ContextHelp/src/helpdlg.cpp
@@ -0,0 +1,735 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+#include <richedit.h>
+
+extern HINSTANCE hInst;
+HWND hwndHelpDlg;
+
+static int HelpDialogResize(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL *urc)
+{
+ UNREFERENCED_PARAMETER(hwndDlg);
+ UNREFERENCED_PARAMETER(lParam);
+ switch (urc->wId) {
+ case IDC_CTLTEXT:
+#ifdef EDITOR
+ case IDC_DLGID:
+ case IDC_MODULE:
+#endif
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+ case IDC_TEXT:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+#ifndef EDITOR
+INT_PTR CALLBACK ShadowDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ BOOL(WINAPI *pfnSetLayeredWindowAttributes)(HWND, COLORREF, BYTE, DWORD);
+ *(FARPROC*)&pfnSetLayeredWindowAttributes = GetProcAddress(GetModuleHandleA("USER32"), "SetLayeredWindowAttributes");
+ if (!pfnSetLayeredWindowAttributes) {
+ *(HANDLE*)lParam = NULL; // hwndShadowDlg reset
+ DestroyWindow(hwndDlg);
+ return FALSE;
+ }
+ EnableWindow(hwndDlg, FALSE);
+ SetWindowLongPtr(hwndDlg, GWL_EXSTYLE, GetWindowLongPtr(hwndDlg, GWL_EXSTYLE) | WS_EX_LAYERED);
+ pfnSetLayeredWindowAttributes(hwndDlg, RGB(0, 0, 0), 96, LWA_ALPHA);
+ return FALSE;
+ }
+ case WM_CTLCOLORDLG:
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOWFRAME);
+ }
+ return FALSE;
+}
+
+// in client coordinates
+int GetCharRangeRect(HWND hwndEdit, LONG *cpMin, LONG cpMax, RECT *rcRange)
+{
+ LONG cpLineBreak;
+
+ LONG nLine, nLinePrev;
+ if (*cpMin>cpMax)
+ return 1;
+ nLine = SendMessage(hwndEdit, EM_EXLINEFROMCHAR, 0, *cpMin);
+ for (cpLineBreak = *cpMin + 1; cpLineBreak <= cpMax; cpLineBreak++) {
+ nLinePrev = nLine;
+ nLine = SendMessage(hwndEdit, EM_EXLINEFROMCHAR, 0, cpLineBreak);
+ if (nLine != nLinePrev)
+ break;
+ }
+ cpMax = cpLineBreak - 1;
+
+ POINTL pt;
+ if (SendMessage(hwndEdit, EM_SETTYPOGRAPHYOPTIONS, 0, 0)) { // test for richedit v3.0
+ SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)&pt, *cpMin);
+ rcRange->left = pt.x;
+ rcRange->top = pt.y;
+ SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)&pt, cpMax);
+ rcRange->right = pt.x;
+ rcRange->bottom = pt.y;
+ }
+ else {
+ DWORD pos;
+ pos = SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)*cpMin, 0);
+ POINTSTOPOINT(pt, MAKEPOINTS(pos));
+ rcRange->left = pt.x;
+ rcRange->top = pt.y;
+ pos = SendMessage(hwndEdit, EM_POSFROMCHAR, (WPARAM)cpMax, 0);
+ POINTSTOPOINT(pt, MAKEPOINTS(pos));
+ rcRange->right = pt.x;
+ rcRange->bottom = pt.y;
+ }
+
+ FORMATRANGE fr;
+ ZeroMemory(&fr, sizeof(fr));
+ fr.chrg.cpMin = *cpMin;
+ fr.chrg.cpMax = cpMax;
+ fr.hdc = fr.hdcTarget = GetDC(hwndEdit);
+ if (fr.hdc == NULL)
+ return 1;
+ SendMessage(hwndEdit, EM_FORMATRANGE, 0, (LPARAM)&fr);
+ PostMessage(hwndEdit, EM_FORMATRANGE, 0, 0); // clear memory
+ rcRange->bottom += MulDiv(fr.rc.bottom, GetDeviceCaps(fr.hdc, LOGPIXELSY), 1440); // twips to pixels
+ ReleaseDC(hwndEdit, fr.hdc);
+
+ *cpMin = cpLineBreak;
+ return 0;
+}
+
+static HCURSOR hHandCursor;
+static LRESULT CALLBACK HelpSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_KEYDOWN:
+ if (GetKeyState(VK_CONTROL) & 0x8000 && wParam == 'C') {
+ SendMessage(GetParent(hwnd), M_CLIPBOARDCOPY, 0, 0);
+ return 0;
+ }
+ break;
+ case WM_LBUTTONDBLCLK:
+ DestroyWindow(GetParent(hwnd));
+ return 0;
+ case WM_CONTEXTMENU:
+ return DefWindowProc(hwnd, msg, wParam, lParam); // redirect to parent
+ case WM_SETCURSOR: // not available via EN_MSGFILTER
+ if (GetDlgCtrlID((HWND)wParam) == IDC_TEXT) {
+ POINT pt;
+ DWORD pos;
+ CHARRANGE rng;
+ pos = GetMessagePos();
+ POINTSTOPOINT(pt, MAKEPOINTS(pos));
+ ScreenToClient((HWND)wParam, &pt);
+ pos = SendMessage((HWND)wParam, EM_CHARFROMPOS, 0, (WPARAM)&pt);
+ if (IsHyperlink(pos, &rng.cpMin, &rng.cpMax, NULL)) {
+ RECT rc;
+ while (!GetCharRangeRect((HWND)wParam, &rng.cpMin, rng.cpMax, &rc))
+ if (PtInRect(&rc, pt)) {
+ SetCursor(hHandCursor);
+ return TRUE;
+ }
+ }
+ }
+ break;
+ }
+ return CallWindowProc((WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA), hwnd, msg, wParam, lParam);
+}
+#endif // !defined EDITOR
+
+INT_PTR CALLBACK HelpDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndCtl = (HWND)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ static LCID locale;
+#ifndef EDITOR
+ static HWND hwndShadowDlg;
+ static HWND hwndToolTip;
+#endif
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ hwndHelpDlg = hwndDlg;
+#ifdef EDITOR
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DLGID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_DLGID), GWL_STYLE) | SS_ENDELLIPSIS);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
+ { RECT rcDlg, rcWork;
+ if (SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, FALSE) && GetWindowRect(hwndDlg, &rcDlg))
+ SetWindowPos(hwndDlg, 0, rcDlg.left, rcWork.bottom - rcDlg.bottom + rcDlg.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ }
+#else
+ {
+ RECT rc, rcBuf;
+ SendDlgItemMessage(hwndDlg, IDC_CTLTEXT, EM_GETRECT, 0, (LPARAM)&rcBuf);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_GETRECT, 0, (LPARAM)&rc);
+ rc.left = rcBuf.left; // sync richedit offset with edit
+ rc.right = rcBuf.right;
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETRECTNP, 0, (LPARAM)&rc);
+ }
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWLP_USERDATA, SetWindowLong(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWLP_WNDPROC, (LONG)HelpSubclassProc));
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CARETSUCKER), GWLP_USERDATA, SetWindowLong(GetDlgItem(hwndDlg, IDC_CARETSUCKER), GWLP_WNDPROC, (LONG)HelpSubclassProc));
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_MOUSEEVENTS | ENM_REQUESTRESIZE);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_INFOBK));
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_TEXT), GWLP_USERDATA, SetWindowLong(GetDlgItem(hwndDlg, IDC_TEXT), GWLP_WNDPROC, (LONG)HelpSubclassProc));
+ hwndShadowDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SHADOW), hwndDlg, ShadowDlgProc, (LPARAM)&hwndShadowDlg);
+ hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndDlg, NULL, hInst, NULL);
+ if (hwndToolTip != NULL) {
+ SetWindowPos(hwndToolTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ SendMessage(hwndToolTip, TTM_SETTIPBKCOLOR, GetSysColor(COLOR_WINDOW), 0); // yelleow on yellow looks silly
+ SendMessage(hwndToolTip, TTM_SETDELAYTIME, TTDT_AUTOMATIC, GetDoubleClickTime() * 3);
+ }
+ hHandCursor = (HCURSOR)LoadImage(NULL, IDC_HAND, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+ if (hHandCursor == NULL) // use fallback out of miranda32.exe
+ hHandCursor = (HCURSOR)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(214), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+#endif
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETLANGOPTIONS, 0, SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_GETLANGOPTIONS, 0, 0) | IMF_UIFONTS);
+ SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ return TRUE;
+ case WM_SIZE:
+ {
+ /* UTILRESIZEDIALOG urd;
+ ZeroMemory(&urd,sizeof(urd));
+ urd.cbSize=sizeof(urd);
+ urd.hInstance=hInst;
+ urd.hwndDlg=hwndDlg;
+ urd.lpTemplate=MAKEINTRESOURCEA(IDD_HELP);
+ urd.pfnResizer=HelpDialogResize;
+ CallService(MS_UTILS_RESIZEDIALOG,0,(LPARAM)&urd); */
+ Utils_ResizeDialog(hwndDlg, hInst, MAKEINTRESOURCEA(IDD_HELP), HelpDialogResize);
+ InvalidateRect(hwndDlg, NULL, TRUE);
+#ifdef EDITOR
+ break;
+#endif
+ }
+#ifndef EDITOR
+ case WM_MOVE:
+ if (IsWindow(hwndShadowDlg)) {
+ RECT rc;
+ HRGN hRgnShadow, hRgnDlg;
+ if (!GetWindowRect(hwndDlg, &rc))
+ break;
+ hRgnShadow = CreateRectRgnIndirect(&rc);
+ if (hRgnShadow == NULL)
+ break;
+ OffsetRgn(hRgnShadow, 5, 5);
+ hRgnDlg = CreateRectRgnIndirect(&rc);
+ if (hRgnDlg == NULL)
+ break;
+ if (CombineRgn(hRgnShadow, hRgnShadow, hRgnDlg, RGN_DIFF) == ERROR) {
+ DeleteObject(hRgnShadow);
+ DeleteObject(hRgnDlg);
+ break;
+ }
+ DeleteObject(hRgnDlg);
+ OffsetRgn(hRgnShadow, -rc.left - 5, -rc.top - 5);
+ SetWindowRgn(hwndShadowDlg, hRgnShadow, FALSE); // system gets ownership of hRgnShadow
+ SetWindowPos(hwndShadowDlg, HWND_TOPMOST, rc.left + 5, rc.top + 5, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW);
+ SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+ break;
+ case WM_KEYDOWN:
+ if (GetKeyState(VK_CONTROL) & 0x8000 && wParam == 'C') {
+ SendMessage(hwndDlg, M_CLIPBOARDCOPY, 0, 0);
+ return TRUE;
+ }
+ break;
+ case WM_CONTEXTMENU:
+ {
+ HMENU hMenu;
+ POINT pt;
+ POINTSTOPOINT(pt, MAKEPOINTS(lParam));
+ hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, WM_COPY, TranslateT("&Copy"));
+ if (TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_HORPOSANIMATION | TPM_VERPOSANIMATION | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, hwndDlg, NULL))
+ SendMessage(hwndDlg, M_CLIPBOARDCOPY, 0, 0);
+ DestroyMenu(hMenu);
+ }
+ return 0;
+ case WM_LBUTTONDBLCLK:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ case M_CLIPBOARDCOPY:
+ {
+ HWND hwnd = GetFocus();
+ if (hwnd == GetDlgItem(hwndDlg, IDC_CTLTEXT) || hwnd == GetDlgItem(hwndDlg, IDC_TEXT)) {
+ CHARRANGE sel;
+ ZeroMemory(&sel, sizeof(sel));
+ SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel.cpMin, (LPARAM)&sel.cpMax);
+ if (sel.cpMin != sel.cpMax) {
+ SendMessage(hwnd, WM_COPY, 0, 0);
+ return TRUE;
+ }
+ }
+ }
+ if (OpenClipboard(hwndDlg)) {
+ HGLOBAL hglb;
+ int cch, len;
+ EmptyClipboard();
+ hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(LCID));
+ if (hglb != NULL) {
+ LCID *plocale = (LCID*)GlobalLock(hglb); // look at !!
+ if (plocale != NULL) {
+ *plocale = locale;
+ GlobalUnlock(hglb);
+ if (!SetClipboardData(CF_LOCALE, hglb))
+ GlobalFree(hglb); // shell takes ownership
+ }
+ else
+ GlobalFree(hglb);
+ }
+ cch = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_CTLTEXT)) + GetWindowTextLength(GetDlgItem(hwndDlg, IDC_TEXT)) + 3;
+ hglb = GlobalAlloc(GMEM_MOVEABLE, (cch + 1)*sizeof(TCHAR));
+ if (hglb != NULL) {
+ TCHAR *pszText = (TCHAR*)GlobalLock(hglb);
+ if (pszText != NULL) {
+ if (!GetWindowText(GetDlgItem(hwndDlg, IDC_CTLTEXT), pszText, cch - 2)) pszText[0] = _T('\0');
+ len = lstrlen(pszText);
+ if (GetWindowText(GetDlgItem(hwndDlg, IDC_TEXT), pszText + len + 2, cch - 2 - len) && len) {
+ pszText[len] = _T('\r');
+ pszText[len + 1] = _T('\n');
+ }
+ GlobalUnlock(hglb);
+ if (!SetClipboardData(CF_UNICODETEXT, hglb))
+ GlobalFree(hglb); // shell takes ownership
+ }
+ else GlobalFree(hglb);
+ }
+ CloseClipboard();
+ }
+ return TRUE;
+ case WM_CTLCOLORDLG:
+ case WM_CTLCOLORSTATIC:
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_INFOTEXT));
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_INFOBK));
+ return (BOOL)GetSysColorBrush(COLOR_INFOBK);
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) != WA_INACTIVE)
+ break;
+ if (GetParent((HWND)lParam) == hwndDlg)
+ break;
+ // fall through
+ case WM_SYSCOLORCHANGE:
+ case WM_ACTIVATEAPP:
+ PostMessage(hwndDlg, WM_CLOSE, 0, 0); // no DestroyWindow() here! would cause recursion
+ break;
+#endif // !defined EDITOR
+ case M_CHANGEHELPCONTROL:
+ if (hwndCtl == (HWND)lParam)
+ break;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ hwndCtl = (HWND)lParam;
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, NULL);
+#ifdef EDITOR
+ {
+ TCHAR text[1024];
+ char *szModule, *szDlgId;
+
+ GetControlTitle(hwndCtl, text, sizeof(text));
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, text);
+ mir_sntprintf(text, sizeof(text), TranslateT("Control ID: %d"), GetControlID(hwndCtl));
+ SetDlgItemText(hwndDlg, IDC_CTLID, text);
+
+ szDlgId = CreateDialogIdString(GetControlDialog(hwndCtl));
+ mir_sntprintf(text, sizeof(text), TranslateT("Dialog ID: %hs"), (szDlgId != NULL) ? szDlgId : Translate("Unknown"));
+ mir_free(szDlgId); // does NULL check
+ SetDlgItemText(hwndDlg, IDC_DLGID, text);
+
+ mir_sntprintf(text, sizeof(text), TranslateT("Type: %s"), TranslateTS(szControlTypeNames[GetControlType(hwndCtl)]));
+ SetDlgItemText(hwndDlg, IDC_CTLTYPE, text);
+
+ szModule = GetControlModuleName(hwndCtl);
+ mir_sntprintf(text, sizeof(text), TranslateT("Module: %hs"), szModule ? szModule : Translate("Unknown"));
+ mir_free(szModule); // does NULL check
+ SetDlgItemText(hwndDlg, IDC_MODULE, text);
+ }
+#endif // defined EDITOR
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+ SendMessage(hwndDlg, M_LOADHELP, 0, 0);
+#ifdef EDITOR
+ ShowWindow(hwndDlg, SW_SHOWNORMAL);
+#else
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_REQUESTRESIZE, 0, 0);
+ return FALSE;
+#endif
+ return TRUE;
+ case M_HELPLOADFAILED:
+ if (hwndCtl != (HWND)lParam)
+ break;
+#ifdef EDITOR
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT), TRUE);
+ {
+ TCHAR text[2024];
+ GetControlTitle(hwndCtl, text, sizeof(text));
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, text);
+ }
+#else
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("No Help Pack installed!"));
+#endif
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+ MessageBeep(MB_ICONERROR);
+ break;
+#ifdef EDITOR
+ case M_SAVECOMPLETE:
+#endif
+ case M_HELPLOADED:
+ if (hwndCtl != (HWND)lParam)
+ break;
+ case M_LOADHELP:
+ {
+ TCHAR *szTitle;
+ char *szText;
+ char *szDlgId, *szModule;
+ UINT codepage;
+ BOOL isRTL;
+ int id, loading;
+#ifdef EDITOR
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT), TRUE);
+#endif
+ szDlgId = CreateDialogIdString(GetControlDialog(hwndCtl));
+ szModule = GetControlModuleName(hwndCtl);
+ id = GetControlID(hwndCtl);
+#ifndef EDITOR
+ // show id string instead of help text when 'ctrl' key pressed
+ if (msg == M_LOADHELP && GetAsyncKeyState(VK_CONTROL) & 0x8000) {
+ char *buf;
+ HWND hwnd;
+ buf = CreateControlIdentifier(szDlgId ? szDlgId : "unknown", szModule ? szModule : "unknown", id, hwndCtl);
+ hwnd = GetDlgItem(hwndDlg, IDC_CTLTEXT);
+ SetWindowTextA(hwnd, buf); // accepts NULL
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+ mir_free(buf); // does NULL check
+ mir_free(szDlgId); // does NULL check
+ mir_free(szModule); // does NULL check
+ break;
+ }
+#endif
+ if (szDlgId == NULL || szModule == NULL) {
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+#ifndef EDITOR
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("No help available for this item."));
+#endif
+ mir_free(szDlgId); // does NULL check
+ mir_free(szModule); // does NULL check
+ break;
+ }
+
+ loading = GetControlHelp(hwndCtl, szDlgId, szModule, id, &szTitle, &szText, NULL, &locale, &codepage, &isRTL, (msg == M_HELPLOADED) ? GCHF_DONTLOAD : 0);
+ if (!loading) {
+ if (szText)
+ StreamInHtml(GetDlgItem(hwndDlg, IDC_TEXT), szText, codepage, RGB(255, 0, 0));
+ else
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+ if (szTitle) {
+ TCHAR buf[128];
+ RECT rc;
+ HFONT hFontPrev;
+ DWORD exStyle;
+ HWND hwndCtlText;
+ HDC hdc;
+ hwndCtlText = GetDlgItem(hwndDlg, IDC_CTLTEXT);
+ exStyle = GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWL_EXSTYLE);
+ hdc = GetDC(hwndCtlText);
+ {
+ DWORD(WINAPI *pfnGetLayout)(HDC); // obey right-to-left languages
+ *(FARPROC*)&pfnGetLayout = GetProcAddress(GetModuleHandleA("GDI32"), "GetLayout");
+ if (pfnGetLayout) isRTL = (isRTL && !pfnGetLayout(hdc));
+ if (isRTL)
+ exStyle |= WS_EX_RTLREADING | WS_EX_RIGHT;
+ else
+ exStyle &= ~(WS_EX_RTLREADING | WS_EX_RIGHT);
+ }
+ mir_sntprintf(buf, sizeof(buf) - 4, _T("%s"), szTitle);
+ if (hdc != NULL && hwndCtlText != NULL) {
+ SendMessage(hwndCtlText, EM_GETRECT, 0, (LPARAM)&rc);
+ hFontPrev = (HFONT)SelectObject(hdc, (HFONT)SendMessage(hwndCtlText, WM_GETFONT, 0, 0)); // look at !!
+ // doesn't actually draw the string due to DT_CALCRECT
+ DrawTextEx(hdc, buf, -1, &rc, DT_MODIFYSTRING | DT_CALCRECT | DT_EDITCONTROL | DT_END_ELLIPSIS | DT_INTERNAL | (isRTL ? (DT_RTLREADING | DT_RIGHT) : DT_LEFT) | DT_NOPREFIX | DT_SINGLELINE, NULL);
+ SelectObject(hdc, hFontPrev);
+ ReleaseDC(hwndCtlText, hdc);
+ }
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CTLTEXT), GWL_EXSTYLE, exStyle);
+ SetWindowText(hwndCtlText, buf);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, NULL);
+ }
+ else {
+ if (msg == M_HELPLOADED) {
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+#ifndef EDITOR
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("No help available for this item."));
+#endif
+ }
+ else {
+#ifdef EDITOR
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT), FALSE);
+ SetDlgItemText(hwndDlg, IDC_TEXT, TranslateT("Loading..."));
+#else
+ SetDlgItemText(hwndDlg, IDC_CTLTEXT, TranslateT("Loading..."));
+ SetDlgItemText(hwndDlg, IDC_TEXT, NULL);
+#endif
+ }
+ }
+ mir_free(szDlgId);
+ mir_free(szModule);
+ break;
+ }
+ case WM_NOTIFY:
+ switch (((NMHDR*)lParam)->idFrom) {
+ case IDC_TEXT:
+ switch (((NMHDR*)lParam)->code) {
+#ifdef EDITOR
+ case EN_MSGFILTER:
+ switch (((MSGFILTER*)lParam)->msg) {
+ case WM_CHAR:
+ switch (((MSGFILTER*)lParam)->wParam) {
+ case 'B' - 'A' + 1:
+ case 'I' - 'A' + 1:
+ case 'U' - 'A' + 1:
+ case 'H' - 'A' + 1:
+ case 'L' - 'A' + 1:
+ case 'S' - 'A' + 1:
+ case 'G' - 'A' + 1:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ break;
+ case WM_KEYDOWN:
+ {
+ CHARFORMAT cf;
+ int changes = 0;
+
+ ZeroMemory(&cf, sizeof(cf));
+ if (!(GetKeyState(VK_CONTROL) & 0x8000))
+ break;
+ cf.cbSize = sizeof(cf);
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_GETCHARFORMAT, TRUE, (LPARAM)&cf);
+ switch (((MSGFILTER*)lParam)->wParam) {
+ case 'B':
+ cf.dwEffects ^= CFE_BOLD;
+ cf.dwMask = CFM_BOLD;
+ changes = 1;
+ break;
+ case 'I':
+ cf.dwEffects ^= CFE_ITALIC;
+ cf.dwMask = CFM_ITALIC;
+ changes = 1;
+ break;
+ case 'U':
+ cf.dwEffects ^= CFE_UNDERLINE;
+ cf.dwMask = CFM_UNDERLINE;
+ changes = 1;
+ break;
+ case 'L':
+ {
+ CHOOSECOLOR cc;
+ COLORREF custCol[16];
+ ZeroMemory(&custCol, sizeof(custCol));
+ ZeroMemory(&cc, sizeof(cc));
+ cc.lStructSize = sizeof(cc);
+ cc.hwndOwner = hwndDlg;
+ cc.lpCustColors = custCol;
+ cc.rgbResult = cf.crTextColor;
+ cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT;
+ if (!ChooseColor(&cc))
+ break;
+ cf.crTextColor = 0;
+ cf.dwEffects = 0;
+ if (cc.rgbResult)
+ cf.crTextColor = cc.rgbResult;
+ else cf.dwEffects = CFE_AUTOCOLOR;
+ cf.dwMask = CFM_COLOR;
+ changes = 1;
+ break;
+ }
+ case 'H':
+ cf.dwEffects ^= CFE_STRIKEOUT;
+ cf.dwMask = CFM_STRIKEOUT;
+ changes = 1;
+ break;
+ case VK_OEM_PLUS:
+ cf.yHeight = ((GetKeyState(VK_SHIFT) & 0x8000) ? TEXTSIZE_BIG : TEXTSIZE_NORMAL) * 10;
+ cf.dwMask = CFM_SIZE;
+ changes = 1;
+ break;
+ case VK_OEM_MINUS:
+ cf.yHeight = TEXTSIZE_SMALL * 10;
+ cf.dwMask = CFM_SIZE;
+ changes = 1;
+ break;
+ case 'S':
+ {
+ TCHAR szTitle[1024];
+ char *szText, *szDlgId, *szModule;
+ if (!GetDlgItemText(hwndDlg, IDC_CTLTEXT, szTitle, sizeof(szTitle)))
+ break;
+ szDlgId = CreateDialogIdString(GetControlDialog(hwndCtl));
+ if (szDlgId == NULL)
+ break;
+ szText = StreamOutHtml(GetDlgItem(hwndDlg, IDC_TEXT));
+ szModule = GetControlModuleName(hwndCtl);
+ if (szModule == NULL) {
+ mir_free(szDlgId);
+ break;
+ }
+ SetControlHelp(szDlgId, szModule, GetControlID(hwndCtl), szTitle, szText, GetControlType(hwndCtl));
+ mir_free(szText);
+ mir_free(szDlgId);
+ mir_free(szModule);
+ SaveDialogCache();
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ case 'G':
+ SendMessage(hwndDlg, M_LOADHELP, 0, 0);
+ SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ if (changes) {
+ SendDlgItemMessage(hwndDlg, IDC_TEXT, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ break;
+ }
+ }
+ break;
+#else // defined EDITOR
+ case EN_MSGFILTER:
+ {
+ MSGFILTER *msgf = (MSGFILTER*)lParam;
+ switch (msgf->msg) {
+ case WM_LBUTTONUP:
+ {
+ POINT pt;
+ DWORD pos;
+ CHARRANGE sel;
+ char *pszLink;
+ HWND hwndEdit = msgf->nmhdr.hwndFrom;
+
+ ZeroMemory(&sel, sizeof(sel));
+ SendMessage(msgf->nmhdr.hwndFrom, EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (sel.cpMin != sel.cpMax)
+ break;
+ POINTSTOPOINT(pt, MAKEPOINTS(msgf->lParam));
+ pos = SendMessage(hwndEdit, EM_CHARFROMPOS, 0, (WPARAM)&pt);
+ if (IsHyperlink(pos, NULL, NULL, &pszLink)) {
+ Utils_OpenUrl(pszLink); // pszLink is MBCS string in CP_ACP
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ }
+ break;
+ case WM_MOUSEMOVE: // register hyperlink tooltips when current
+ if (hwndToolTip != NULL) {
+ POINTL pt;
+ DWORD pos;
+ CHARRANGE rng;
+ char *pszLink;
+
+ POINTSTOPOINT(pt, MAKEPOINTS(msgf->lParam));
+ pos = SendMessage(msgf->nmhdr.hwndFrom, EM_CHARFROMPOS, 0, (WPARAM)&pt);
+ if (IsHyperlink(pos, &rng.cpMin, &rng.cpMax, &pszLink)) { // pszLink is MBCS in CP_ACP
+ TTTOOLINFOA ti = { 0 };
+ ti.cbSize = sizeof(ti);
+ ti.hwnd = msgf->nmhdr.hwndFrom;
+ ti.uId = (UINT)rng.cpMin;
+ if (!SendMessage(hwndToolTip, TTM_GETTOOLINFOA, 0, (LPARAM)&ti)) {
+ LONG cpRectMin;
+ ti.uFlags = TTF_SUBCLASS;
+ ti.lpszText = pszLink;
+ cpRectMin = rng.cpMin;
+ while (!GetCharRangeRect(ti.hwnd, &rng.cpMin, rng.cpMax, &ti.rect)) {
+ ti.uId = (UINT)cpRectMin;
+ SendMessage(hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
+ cpRectMin = rng.cpMin;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+ case EN_REQUESTRESIZE:
+ {
+ RECT rcDlg, rcEdit, rcCtl, rcNew;
+ REQRESIZE *rr = (REQRESIZE*)lParam;
+ POINT ptScreenBottomRight;
+
+ if (!GetWindowRect(hwndDlg, &rcDlg))
+ break;
+ if (!GetWindowRect(hwndCtl, &rcCtl))
+ break;
+ if (!GetWindowRect(GetDlgItem(hwndDlg, IDC_TEXT), &rcEdit))
+ break;
+ rcNew.left = rcCtl.left + 30;
+ rcNew.top = rcCtl.bottom + 10;
+ rcNew.right = rcNew.left + (rr->rc.right - rr->rc.left) + (rcDlg.right - rcDlg.left) - (rcEdit.right - rcEdit.left) + (GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_TEXT), GWL_STYLE)&WS_VSCROLL ? GetSystemMetrics(SM_CXVSCROLL) : 0);
+ if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_TEXT)))
+ rcNew.bottom = rcNew.top + min(GetSystemMetrics(SM_CYSCREEN) / 5, (rr->rc.bottom - rr->rc.top) + (rcDlg.bottom - rcDlg.top) - (rcEdit.bottom - rcEdit.top));
+ else
+ rcNew.bottom = rcNew.top + min(GetSystemMetrics(SM_CYSCREEN) / 5, (rcDlg.bottom - rcDlg.top) - (rcEdit.bottom - rcEdit.top));
+ if (GetSystemMetrics(SM_CXVIRTUALSCREEN)) {
+ ptScreenBottomRight.x = GetSystemMetrics(SM_CXVIRTUALSCREEN) + GetSystemMetrics(SM_XVIRTUALSCREEN);
+ ptScreenBottomRight.y = GetSystemMetrics(SM_CYVIRTUALSCREEN) + GetSystemMetrics(SM_YVIRTUALSCREEN);
+ }
+ else {
+ ptScreenBottomRight.x = GetSystemMetrics(SM_CXSCREEN);
+ ptScreenBottomRight.y = GetSystemMetrics(SM_CYSCREEN);
+ }
+ if (rcNew.right >= ptScreenBottomRight.x)
+ OffsetRect(&rcNew, ptScreenBottomRight.x - rcNew.right, 0);
+ if (rcNew.bottom >= ptScreenBottomRight.y)
+ OffsetRect(&rcNew, 0, ptScreenBottomRight.y - rcNew.bottom);
+ SetWindowPos(hwndDlg, 0, rcNew.left, rcNew.top, rcNew.right - rcNew.left, rcNew.bottom - rcNew.top, SWP_NOZORDER);
+ ShowWindow(hwndDlg, SW_SHOWNORMAL);
+ break;
+ }
+#endif // defined EDITOR
+ }
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL: // WM_CLOSE
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ }
+ break;
+ case WM_DESTROY:
+#ifndef EDITOR
+ FreeHyperlinkData();
+ if (IsWindow(hwndShadowDlg))
+ DestroyWindow(hwndShadowDlg);
+ if (hwndToolTip != NULL)
+ DestroyWindow(hwndToolTip);
+#endif
+ hwndHelpDlg = NULL;
+ break;
+ }
+ return FALSE;
+}
diff --git a/plugins/ContextHelp/src/helppack.cpp b/plugins/ContextHelp/src/helppack.cpp
new file mode 100644
index 0000000000..c8a21105aa
--- /dev/null
+++ b/plugins/ContextHelp/src/helppack.cpp
@@ -0,0 +1,520 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+/**************************** LOAD PACK ***************************/
+
+void TrimStringSimple(char *str)
+{
+ if (str[lstrlenA(str) - 1] == '\n')
+ str[lstrlenA(str) - 1] = '\0';
+ if (str[lstrlenA(str) - 1] == '\r')
+ str[lstrlenA(str) - 1] = '\0';
+}
+
+void TrimString(char *str)
+{
+ int len, start;
+ len = lstrlenA(str);
+ while (str[0] != '\0' && ((unsigned char)str[len - 1] <= ' '))
+ str[--len] = 0;
+ for (start = 0; str[start] && ((unsigned char)str[start] <= ' '); start++);
+ MoveMemory(str, str + start, len - start + 1);
+}
+
+BOOL IsEmpty(const char *str)
+{
+ int i;
+ for (i = 0; str[i] != '\0'; i++)
+ if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n')
+ return FALSE;
+ return TRUE;
+}
+
+static void CleanupLanguage(char *szLanguage, LCID locale)
+{
+ char *p;
+ if (PRIMARYLANGID(LANGIDFROMLCID(locale)) != LANG_ENGLISH) {
+ /* remove any appended ' (default)' */
+ p = strstr(szLanguage, " (default)");
+ if (p != NULL)
+ *p = '\0';
+ }
+}
+
+static void CleanupAuthors(char *szAuthors)
+{
+ char *p, *p2;
+ /* remove trailing dot (if any) */
+ p = &szAuthors[lstrlenA(szAuthors) - 1];
+ if (*p == '.')
+ *p = '\0';
+ /* remove any extra info in parentheses, which is ok
+ * but makes the list very long for some packs */
+ for (;;) {
+ p = strchr(szAuthors, '(');
+ p2 = strchr(szAuthors, ')');
+ if (p == NULL || p2 == NULL) {
+ p = strchr(szAuthors, '[');
+ p2 = strchr(szAuthors, ']');
+ if (p == NULL || p2 == NULL)
+ break;
+ }
+ if (*(p - 1) == ' ')
+ --p;
+ MoveMemory(p, p2 + 1, lstrlenA(p2 + 1) + 1);
+ }
+}
+
+static void CleanupEmail(char *szAuthorEmail)
+{
+ char c, *p, *pAt;
+ /* replace ' dot ' with '.' (may be removed) */
+ p = strstr(szAuthorEmail, " dot ");
+ if (p != NULL) {
+ *p = '.';
+ MoveMemory(p + 1, p + 5, lstrlenA(p + 5) + 1);
+ }
+ /* also allow ' at ' instead of '@' for obfuscation */
+ p = strstr(szAuthorEmail, " at ");
+ if (p != NULL) {
+ *p = '@';
+ MoveMemory(p + 1, p + 4, lstrlenA(p + 4) + 1);
+ }
+ /* is valid? */
+ pAt = strchr(szAuthorEmail, '@');
+ if (pAt == NULL) {
+ szAuthorEmail[0] = '\0';
+ return;
+ }
+ /* strip-off extra text except exactly one email address
+ * this is needed as a click on the email addres brings up the mail client */
+ for (c = ' ';; c = ',') {
+ p = strchr(pAt, c);
+ if (p != NULL)
+ *p = '\0';
+ p = strrchr(szAuthorEmail, c);
+ if (p != NULL)
+ MoveMemory(szAuthorEmail, p + 1, lstrlenA(p + 1) + 1);
+ if (c == ',')
+ break;
+ }
+ p = strstr(szAuthorEmail, "__");
+ if (p != NULL)
+ MoveMemory(szAuthorEmail, p + 2, lstrlenA(p + 2) + 1);
+ /* lower case */
+ CharLowerA(szAuthorEmail);
+ /* 'none' specified */
+ if (!lstrcmpiA(szAuthorEmail, "none"))
+ szAuthorEmail[0] = '\0';
+}
+
+static void CleanupLastModifiedUsing(char *szLastModifiedUsing, int nSize)
+{
+ char *p;
+ /* remove 'Unicode', as it doesn't matter */
+ p = strstr(szLastModifiedUsing, " Unicode");
+ if (p != NULL) MoveMemory(p, p + 8, lstrlenA(p + 8) + 1);
+ /* use 'Miranda IM' instead of 'Miranda' */
+ p = strstr(szLastModifiedUsing, "Miranda");
+ if (p != NULL && strncmp(p + 7, " IM", 3)) {
+ MoveMemory(p + 10, p + 7, lstrlenA(p + 7) + 1);
+ CopyMemory(p + 7, " IM", 3);
+ }
+ /* use 'Plugin' instead of 'plugin' */
+ p = strstr(szLastModifiedUsing, " plugin");
+ if (p != NULL)
+ CopyMemory(p, " Plugin", 7);
+ /* remove 'v' prefix */
+ p = strstr(szLastModifiedUsing, " v0.");
+ if (p != NULL)
+ MoveMemory(p + 1, p + 2, lstrlenA(p + 2) + 1);
+ /* default if empty */
+ if (!szLastModifiedUsing[0]) {
+ lstrcpynA(szLastModifiedUsing, MIRANDANAME" ", nSize);
+ CallService(MS_SYSTEM_GETVERSIONTEXT, nSize - lstrlenA(szLastModifiedUsing), (LPARAM)szLastModifiedUsing + lstrlenA(szLastModifiedUsing));
+ }
+}
+
+// pack struct should be initialized to zero before call
+// pack->szFileName needs to be filled in before call
+static BOOL LoadPackData(HELPPACK_INFO *pack, BOOL fEnabledPacks, const char *pszFileVersionHeader)
+{
+ FILE *fp;
+ TCHAR szFileName[MAX_PATH];
+ char line[4096], *pszColon, *buf;
+ char szLanguageA[64]; /* same size as pack->szLanguage */
+ /*
+ Miranda Help Pack Version 1
+ Language: (optional)
+ Locale: 0809
+ Authors: Miranda NG Development Team (multiple tags allowed)
+ Author-email: project-info at miranda-ng.org (" at " instead of "@" allowed)
+ Last-Modified-Using: Miranda IM 0.7
+ Plugins-included: (multiple tags allowed)
+ X-FLName: name as used on the file listing (non-standard extension)
+ X-Version: 1.2.3.4 (non-standard extension)
+ see 'Help-Translation.txt' for some header quidelines
+ */
+ if (!GetPackPath(szFileName, sizeof(szFileName), fEnabledPacks, pack->szFileName))
+ return FALSE;
+ fp = _tfopen(szFileName, _T("rt"));
+ if (fp == NULL)
+ return FALSE;
+ fgets(line, sizeof(line), fp);
+ TrimString(line);
+ if (lstrcmpA(line, pszFileVersionHeader)) {
+ fclose(fp);
+ return FALSE;
+ }
+ pack->flags = HPF_NOLOCALE;
+ pack->Locale = LOCALE_USER_DEFAULT;
+ szLanguageA[0] = '\0';
+ while (!feof(fp)) {
+ if (fgets(line, sizeof(line), fp) == NULL)
+ break;
+ TrimString(line);
+ if (IsEmpty(line) || line[0] == ';' || line[0] == '\0')
+ continue;
+ if (line[0] == '[')
+ break;
+ pszColon = strchr(line, ':');
+ if (pszColon == NULL)
+ continue;
+ *pszColon = '\0';
+ TrimString(pszColon + 1);
+ if (!lstrcmpA(line, "Language") && !pack->szLanguage[0])
+ lstrcpynA(szLanguageA, pszColon + 1, sizeof(szLanguageA)); /* buffer safe */
+ else if (!lstrcmpA(line, "Last-Modified-Using") && !pack->szLastModifiedUsing[0])
+ lstrcpynA(pack->szLastModifiedUsing, pszColon + 1, sizeof(pack->szLastModifiedUsing)); /* buffer safe */
+ else if (!lstrcmpA(line, "Authors")) {
+ buf = pack->szAuthors + lstrlenA(pack->szAuthors); /* allow multiple tags */
+ if ((sizeof(pack->szAuthors) - lstrlenA(pack->szAuthors))>0) /* buffer safe */
+ mir_snprintf(buf, sizeof(pack->szAuthors) - lstrlenA(pack->szAuthors), (pack->szAuthors[0] == '\0') ? "%s" : " %s", pszColon + 1);
+ }
+ else if (!lstrcmpA(line, "Author-email") && !pack->szAuthorEmail[0])
+ lstrcpynA(pack->szAuthorEmail, pszColon + 1, sizeof(pack->szAuthorEmail)); /* buffer safe */
+ else if (!lstrcmpA(line, "Locale") && pack->flags&HPF_NOLOCALE) {
+ pack->Locale = MAKELCID((USHORT)strtol(pszColon + 1, NULL, 16), SORT_DEFAULT);
+ if (pack->Locale)
+ pack->flags &= ~HPF_NOLOCALE;
+ }
+ else if (!lstrcmpA(line, "Plugins-included")) {
+ buf = pack->szPluginsIncluded + lstrlenA(pack->szPluginsIncluded); /* allow multiple tags */
+ if ((sizeof(pack->szPluginsIncluded) - lstrlenA(pack->szPluginsIncluded))>0) /* buffer safe */
+ mir_snprintf(buf, sizeof(pack->szPluginsIncluded) - lstrlenA(pack->szPluginsIncluded), (pack->szPluginsIncluded[0] == '\0') ? "%s" : ", %s", CharLowerA(pszColon + 1));
+ }
+ else if (!lstrcmpA(line, "X-Version") && !pack->szVersion[0])
+ lstrcpynA(pack->szVersion, pszColon + 1, sizeof(pack->szVersion)); /* buffer safe */
+ else if (!lstrcmpA(line, "X-FLName") && !pack->szFLName[0])
+ lstrcpynA(pack->szFLName, pszColon + 1, sizeof(pack->szFLName)); /* buffer safe */
+ }
+ /* default */
+ if (PRIMARYLANGID(LANGIDFROMLCID(pack->Locale)) == LANG_ENGLISH && strstr(szLanguageA, " (default)") != NULL)
+ pack->flags |= HPF_DEFAULT;
+ CleanupLanguage(szLanguageA, pack->Locale);
+ CleanupAuthors(pack->szAuthors);
+ CleanupEmail(pack->szAuthorEmail);
+ CleanupLastModifiedUsing(pack->szLastModifiedUsing, sizeof(pack->szLastModifiedUsing));
+ /* codepage */
+ if (!(pack->flags&HPF_NOLOCALE))
+ if (GetLocaleInfoA(pack->Locale, LOCALE_IDEFAULTANSICODEPAGE, line, 6))
+ pack->codepage = (WORD)atoi(line); /* CP_ACP on error */
+ /* language */
+ MultiByteToWideChar(pack->codepage, 0, szLanguageA, -1, pack->szLanguage, sizeof(pack->szLanguage));
+ /* ensure the pack always has a language name */
+ if (!pack->szLanguage[0] && !GetLocaleInfo(pack->Locale, LOCALE_SENGLANGUAGE, pack->szLanguage, sizeof(pack->szLanguage))) {
+ TCHAR *p;
+ lstrcpyn(pack->szLanguage, pack->szFileName, sizeof(pack->szLanguage)); /* buffer safe */
+ p = _tcsrchr(pack->szLanguage, _T('.'));
+ if (p != NULL)
+ *p = '\0';
+ }
+ /* ensure the pack always has a filelisting name */
+ if (!pack->szFLName[0])
+ lstrcatA(lstrcpyA(pack->szFLName, szLanguageA), " Help Pack"); /* buffer safe */
+ fclose(fp);
+
+ return TRUE;
+}
+
+/**************************** ENUM PACKS **************************/
+
+BOOL GetPackPath(TCHAR *pszPath, int nSize, BOOL fEnabledPacks, const TCHAR *pszFile)
+{
+ TCHAR *p;
+ /* main path */
+ if (!GetModuleFileName(NULL, pszPath, nSize))
+ return FALSE;
+ p = _tcsrchr(pszPath, _T('\\'));
+ if (p != NULL)
+ *(p + 1) = _T('\0');
+ /* subdirectory */
+ if (!fEnabledPacks) {
+ if (nSize<(lstrlen(pszPath) + 10))
+ return FALSE;
+ lstrcat(pszPath, _T("Languages\\"));
+ }
+ /* file name */
+ if (pszFile != NULL) {
+ if (nSize < (lstrlen(pszFile) + 11))
+ return FALSE;
+ lstrcat(pszPath, pszFile);
+ }
+
+ return TRUE;
+}
+
+// callback is allowed to be NULL
+// returns TRUE if any pack exists except default
+BOOL EnumPacks(ENUM_PACKS_CALLBACK callback, const TCHAR *pszFilePattern, const char *pszFileVersionHeader, WPARAM wParam, LPARAM lParam)
+{
+ BOOL fPackFound = FALSE;
+ BOOL res = FALSE;
+ HELPPACK_INFO pack;
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind;
+
+ /* enabled packs */
+ if (GetPackPath(pack.szFileName, sizeof(pack.szFileName), TRUE, pszFilePattern)) {
+ hFind = FindFirstFile(pack.szFileName, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if ((lstrlen(wfd.cFileName) < 4) || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+
+ /* get data */
+ ZeroMemory(&pack, sizeof(pack));
+ lstrcpy(pack.szFileName, CharLower(wfd.cFileName)); /* buffer safe */
+ if (LoadPackData(&pack, TRUE, pszFileVersionHeader)) {
+ pack.ftFileDate = wfd.ftLastWriteTime;
+
+ /* enabled? */
+ if (!fPackFound)
+ pack.flags |= HPF_ENABLED;
+ fPackFound = TRUE;
+
+ /* callback */
+ if (callback != NULL)
+ res = callback(&pack, wParam, lParam);
+ if (!res) {
+ FindClose(hFind);
+ return FALSE;
+ }
+ }
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+
+ /* disabled packs */
+ if (GetPackPath(pack.szFileName, sizeof(pack.szFileName), FALSE, pszFilePattern)) {
+ hFind = FindFirstFile(pack.szFileName, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+
+ /* get data */
+ ZeroMemory(&pack, sizeof(pack));
+ lstrcpy(pack.szFileName, CharLower(wfd.cFileName)); /* buffer safe */
+ if (LoadPackData(&pack, FALSE, pszFileVersionHeader)) {
+ pack.ftFileDate = wfd.ftLastWriteTime;
+ fPackFound = TRUE;
+
+ /* callback */
+ if (callback != NULL)
+ res = callback(&pack, wParam, lParam);
+ if (!res) {
+ FindClose(hFind);
+ return FALSE;
+ }
+ }
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+
+ return fPackFound;
+}
+
+BOOL IsPluginIncluded(const HELPPACK_INFO *pack, char *pszFileBaseName)
+{
+ char *p;
+ if (!lstrcmpiA(pszFileBaseName, "png2dib") || !lstrcmpiA(pszFileBaseName, "loadavatars"))
+ return TRUE; /* workaround: does not need no translation */
+ for (p = (char*)pack->szPluginsIncluded;;) {
+ p = strstr(p, CharLowerA(pszFileBaseName));
+ if (p == NULL)
+ return FALSE;
+ if (p == pack->szPluginsIncluded || *(p - 1) == ' ' || *(p - 1) == ',') {
+ p += lstrlenA(pszFileBaseName) + 1;
+ if (*p == ',' || *p == ' ' || *p == 0)
+ return TRUE;
+ }
+ else
+ p += lstrlenA(pszFileBaseName) + 1;
+ }
+
+ return FALSE;
+}
+
+/**************************** SWITCH PACKS ************************/
+
+BOOL EnablePack(const HELPPACK_INFO *pack, const TCHAR *pszFilePattern)
+{
+ TCHAR szFrom[MAX_PATH], szDest[MAX_PATH];
+
+ /* disable previous pack */
+ if (GetPackPath(szFrom, sizeof(szFrom), TRUE, pszFilePattern)) {
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind = FindFirstFile(szFrom, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+ /* ensure dir exists */
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, NULL))
+ CreateDirectory(szFrom, NULL);
+ /* move file */
+ if (GetPackPath(szFrom, sizeof(szFrom), TRUE, wfd.cFileName))
+ if (GetPackPath(szDest, sizeof(szDest), FALSE, wfd.cFileName))
+ if (!MoveFile(szFrom, szDest) && GetLastError() == ERROR_ALREADY_EXISTS) {
+ DeleteFile(szDest);
+ MoveFile(szFrom, szDest);
+ }
+ break;
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+
+ /* enable current pack */
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, pack->szFileName))
+ if (GetPackPath(szDest, sizeof(szDest), TRUE, pack->szFileName))
+ return MoveFile(szFrom, szDest);
+
+ return FALSE;
+}
+
+void CorrectPacks(const TCHAR *pszFilePattern, const TCHAR *pszDefaultFile, BOOL fDisableAll)
+{
+ TCHAR szFrom[MAX_PATH], szDest[MAX_PATH], szDir[MAX_PATH], *pszFile;
+ BOOL fDirCreated = FALSE, fOneEnabled = FALSE;
+ HANDLE hFind;
+ WIN32_FIND_DATA wfd;
+
+ /* main path */
+ if (!GetModuleFileName(NULL, szDir, sizeof(szDir)))
+ return;
+ pszFile = _tcsrchr(szDir, _T('\\'));
+ if (pszFile != NULL)
+ *pszFile = _T('\0');
+
+ /* move wrongly placed packs from 'Plugins' to 'Language' */
+ mir_sntprintf(szFrom, sizeof(szFrom), _T("%s\\Plugins\\%s"), szDir, pszFilePattern);
+ hFind = FindFirstFile(szFrom, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+
+ /* ensure dir exists */
+ if (!fDirCreated && GetPackPath(szFrom, sizeof(szFrom), FALSE, NULL))
+ fDirCreated = CreateDirectory(szFrom, NULL);
+
+ /* move file */
+ if (GetPackPath(szDest, sizeof(szDest), FALSE, wfd.cFileName)) {
+ mir_sntprintf(szFrom, sizeof(szFrom), _T("%s\\Plugins\\%s"), szDir, wfd.cFileName);
+ if (!MoveFile(szFrom, szDest) && GetLastError() == ERROR_ALREADY_EXISTS) {
+ DeleteFile(szDest);
+ MoveFile(szFrom, szDest);
+ }
+ }
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+
+ /* disable all packs except one */
+ if (GetPackPath(szFrom, sizeof(szFrom), TRUE, pszFilePattern)) {
+ hFind = FindFirstFile(szFrom, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+ /* skip first file */
+ fOneEnabled = TRUE;
+ if (!fDisableAll) {
+ fDisableAll = TRUE;
+ continue;
+ }
+ /* ensure dir exists */
+ if (!fDirCreated && GetPackPath(szFrom, sizeof(szFrom), FALSE, NULL))
+ fDirCreated = CreateDirectory(szFrom, NULL);
+ /* move file */
+ if (GetPackPath(szFrom, sizeof(szFrom), TRUE, wfd.cFileName))
+ if (GetPackPath(szDest, sizeof(szDest), FALSE, wfd.cFileName)) {
+ if (!MoveFile(szFrom, szDest) && GetLastError() == ERROR_ALREADY_EXISTS) {
+ DeleteFile(szDest);
+ MoveFile(szFrom, szDest);
+ }
+ }
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+
+ /* ensure one is enabled if installed */
+ if (!fOneEnabled) {
+ /* try to move english */
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, pszDefaultFile))
+ if (GetPackPath(szDest, sizeof(szDest), TRUE, pszDefaultFile))
+ fOneEnabled = MoveFile(szFrom, szDest);
+ /* fallback on other one */
+ if (!fOneEnabled)
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, pszFilePattern)) {
+ hFind = FindFirstFile(szFrom, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if (lstrlen(wfd.cFileName) < 4 || wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.'))
+ continue;
+ /* move first file */
+ if (GetPackPath(szFrom, sizeof(szFrom), FALSE, wfd.cFileName))
+ if (GetPackPath(szDest, sizeof(szDest), TRUE, wfd.cFileName))
+ MoveFile(szFrom, szDest);
+ break;
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+ }
+}
diff --git a/plugins/ContextHelp/src/main.cpp b/plugins/ContextHelp/src/main.cpp
new file mode 100644
index 0000000000..66cc756c67
--- /dev/null
+++ b/plugins/ContextHelp/src/main.cpp
@@ -0,0 +1,128 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+HINSTANCE hInst;
+extern HWND hwndHelpDlg;
+
+int hLangpack;
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __AUTHOREMAIL,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {B16DF0B2-A08F-4bc6-8FA2-3D5FFFA2708D}
+ { 0xb16df0b2, 0xa08f, 0x4bc6, { 0x8f, 0xa2, 0x3d, 0x5f, 0xff, 0xa2, 0x70, 0x8d } }
+};
+
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD, LPVOID)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+static void InstallFile(const TCHAR *pszFileName, const TCHAR *pszDestSubDir)
+{
+ TCHAR szFileFrom[MAX_PATH + 1], szFileTo[MAX_PATH + 1], *p;
+ HANDLE hFile;
+
+ if (!GetModuleFileName(hInst, szFileFrom, sizeof(szFileFrom) - lstrlen(pszFileName)))
+ return;
+ p = _tcsrchr(szFileFrom, _T('\\'));
+ if (p != NULL)
+ *(++p) = 0;
+ lstrcat(szFileFrom, pszFileName); /* buffer safe */
+
+ hFile = CreateFile(szFileFrom, 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return;
+ CloseHandle(hFile);
+
+ if (!GetModuleFileName(NULL, szFileTo, sizeof(szFileTo) - lstrlen(pszDestSubDir) - lstrlen(pszFileName)))
+ return;
+ p = _tcsrchr(szFileTo, _T('\\'));
+ if (p != NULL)
+ *(++p) = 0;
+ lstrcat(szFileTo, pszDestSubDir); /* buffer safe */
+ CreateDirectory(szFileTo, NULL);
+ lstrcat(szFileTo, pszFileName); /* buffer safe */
+
+ if (!MoveFile(szFileFrom, szFileTo) && GetLastError() == ERROR_ALREADY_EXISTS) {
+ DeleteFile(szFileTo);
+ MoveFile(szFileFrom, szFileTo);
+ }
+}
+
+
+extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD)
+{
+ return &pluginInfo;
+}
+
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+ mir_getLP(&pluginInfo);
+
+ INITCOMMONCONTROLSEX icc = { 0 };
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_UPDOWN_CLASS | ICC_TREEVIEW_CLASSES;
+ if (!InitCommonControlsEx(&icc))
+ return 1;
+
+ if (LoadLibrary(_T("riched20.dll")) == NULL) /* richedit v2.0 (Win98/NT4), v3.0 (WinXP/2000/Me+) */
+ if (IDYES != MessageBoxEx(NULL,
+ TranslateT("The Help Plugin can not be loaded, riched20.dll is missing. If you are using WINE please make sure you have riched20.dll installed.\n\nPress 'Yes' to continue loading Miranda NG."),
+ TranslateT("Help Plugin"), MB_YESNO | MB_ICONWARNING | MB_SETFOREGROUND | MB_TOPMOST | MB_TASKMODAL, LANGIDFROMLCID(Langpack_GetDefaultLocale())))
+ return 1;
+
+ if (InstallDialogBoxHook())
+ return 1;
+
+ InitOptions();
+ InitDialogCache();
+ // InitUpdate();
+
+ // InstallFile(_T("Help-Readme.txt"),_T("Docs\\"));
+ // InstallFile(_T("Help-License.txt"),_T("Docs\\"));
+ // InstallFile(_T("Help-SDK.zip"),_T("Docs\\"));
+
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ UninitOptions();
+ // UninitUpdate();
+ RemoveDialogBoxHook();
+
+ if (hwndHelpDlg != NULL)
+ DestroyWindow(hwndHelpDlg);
+ FreeDialogCache();
+
+ return 0;
+}
diff --git a/plugins/ContextHelp/src/options.cpp b/plugins/ContextHelp/src/options.cpp
new file mode 100644
index 0000000000..1078bd2e4e
--- /dev/null
+++ b/plugins/ContextHelp/src/options.cpp
@@ -0,0 +1,797 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+typedef PLUGININFOEX* (__cdecl *pfnMirandaPluginInfo)(DWORD);
+
+WORD settingAutoTipDelay; /* auto show help after those milliseconds, 0 if disabled */
+extern HINSTANCE hInst;
+static HANDLE hHookOptInit;
+static HWND hwndLangOpt;
+
+/**************************** LANGUAGE OPTS ***********************/
+
+/* these are only there for a short time to
+* debug those radio buttons an Win9x */
+#define BOX(str) BOX2("%s (err:%i)",str,GetLastError())
+#define BOX2(fmt,p1,p2) { char str[256]; mir_snprintf(str,fmt,p1,p2); MessageBoxA(NULL,str,"dbg",0); }
+
+// ImageList_Destroy() the return value
+// refresh on WM_THEMECHANGED
+static HIMAGELIST CreateRadioImages(COLORREF clrBk, COLORREF clrText)
+{
+ /* draw bitmap */
+ HDC hdcScreen = GetDC(NULL);
+ if (hdcScreen == NULL)
+ return NULL;
+
+ HIMAGELIST himl = NULL;
+ HDC hdc = CreateCompatibleDC(NULL); // compatible to screen
+ if (hdc != NULL) {
+ SIZE size = { GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON) };
+ RECT rc;
+ SetRect(&rc, 0, 0, 2 * size.cx, size.cy);
+ HBITMAP hbm = CreateCompatibleBitmap(hdcScreen, rc.right, rc.bottom);
+ if (hbm != NULL) {
+ HBITMAP hbmPrev = (HBITMAP)SelectObject(hdc, hbm);
+ if (hbmPrev != NULL) { // error on select?
+ HTHEME hTheme = OpenThemeData(NULL, L"Button");
+ SetRect(&rc, 0, 0, size.cx, size.cy);
+ /* unchecked */
+ if (!DrawThemeBackground(hTheme, hdc, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL, &rc, NULL)) {
+ /* checked */
+ OffsetRect(&rc, size.cx, 0);
+ if (!DrawThemeBackground(hTheme, hdc, BP_RADIOBUTTON, RBS_CHECKEDNORMAL, &rc, NULL))
+ himl = ImageList_Create(size.cx, size.cy, ILC_COLOR32 | ILC_MASK, 3, 0);
+ }
+ CloseThemeData(hTheme);
+
+ /* the classic way */
+ if (himl == NULL) {
+ HBRUSH hbrBk = CreateSolidBrush(clrBk);
+ if (hbrBk != NULL) {
+ FillRect(hdc, &rc, hbrBk);
+ DeleteObject(hbrBk);
+ HDC hdcMono = CreateCompatibleDC(hdc);
+ if (hdcMono != NULL) {
+ HBITMAP hbmMono = CreateBitmap(rc.right, rc.bottom, 1, 1, NULL);
+ if (hbmMono != NULL) {
+ HBITMAP hbmPrevMono = (HBITMAP)SelectObject(hdcMono, hbmMono);
+ if (hbmPrevMono != NULL) { /* error on select? */
+ /* draws a black-and-white mask (see docs)
+ * we need to colorize it using BitBlt with text and background color */
+ COLORREF clrPrevText = SetTextColor(hdc, clrText);
+ COLORREF clrPrevBk = SetBkColor(hdc, clrBk);
+ /* check mark is slightly smaller than icon size */
+ RECT rcRadio;
+ SetRect(&rcRadio, 0, 0, GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));
+ if (rcRadio.right>size.cx)
+ rcRadio.right = size.cx;
+ if (rcRadio.bottom>size.cy)
+ rcRadio.bottom = size.cy;
+ SetRect(&rc, ((size.cx - rcRadio.right) / 2) + 1, ((size.cy - rcRadio.bottom) / 2) + 1, rcRadio.right + 1, rcRadio.bottom + 1);
+ /* unchecked */
+ if (BitBlt(hdcMono, 0, 0, rcRadio.right, rcRadio.bottom, NULL, 0, 0, WHITENESS)) { /* white back */
+ if (DrawFrameControl(hdcMono, &rcRadio, DFC_BUTTON, DFCS_BUTTONRADIO | DFCS_FLAT)) {
+ if (BitBlt(hdc, rc.left, rc.top, rcRadio.right, rcRadio.bottom, hdcMono, 0, 0, SRCCOPY | NOMIRRORBITMAP)) {
+ /* checked */
+ OffsetRect(&rc, size.cx, 0);
+ if (BitBlt(hdcMono, 0, 0, rcRadio.right, rcRadio.bottom, NULL, 0, 0, WHITENESS)) {/* white back */
+ if (DrawFrameControl(hdcMono, &rcRadio, DFC_BUTTON, DFCS_BUTTONRADIO | DFCS_FLAT | DFCS_CHECKED)) {
+ if (BitBlt(hdc, rc.left, rc.top, rcRadio.right, rcRadio.bottom, hdcMono, 0, 0, SRCCOPY | NOMIRRORBITMAP)) {
+ himl = ImageList_Create(size.cx, size.cy, ILC_COLOR | ILC_MASK, 3, 0);
+ if (himl == NULL)
+ BOX("img list creation failed");
+ }
+ }
+ else
+ BOX("second DrawFrameControl() failed");
+ }
+ else
+ BOX("second BitBlt() failed");
+ }
+ else
+ BOX("intermediate BitBlt() failed");
+ }
+ else
+ BOX("DrawFrameControl() failed");
+ }
+ else
+ BOX("first BitBlt() failed");
+ /* restore */
+ SetBkColor(hdc, clrPrevBk);
+ SetTextColor(hdc, clrPrevText);
+ SelectObject(hdcMono, hbmPrevMono);
+ }
+ else
+ BOX("hbmPrevMono==NULL");
+ DeleteObject(hbmMono);
+ }
+ else
+ BOX("hbmMono==NULL");
+ DeleteDC(hdcMono);
+ }
+ else
+ BOX("hdcMono==NULL");
+ }
+ }
+ SelectObject(hdc, hbmPrev);
+ /* create imagelist */
+ if (himl != NULL) {
+ if (ImageList_AddMasked(himl, hbm, clrBk) == -1)
+ BOX("addmasked failed");
+ }
+ else
+ BOX("Win9x: drawing code not reached");
+ }
+ DeleteObject(hbm);
+ }
+ DeleteDC(hdc);
+ }
+ ReleaseDC(NULL, hdcScreen);
+
+ return himl;
+}
+
+static void CleanupPluginName(char *szShortName)
+{
+ char *p;
+ /* strip-off anything in brackets */
+ for (p = szShortName; *p != '\0'; ++p)
+ if (*p == '(' || *p == '[') {
+ *p = '\0';
+ break;
+ }
+ /* remove trailing space */
+ int len = lstrlenA(szShortName);
+ while (szShortName[0] != '\0' && szShortName[len - 1] == ' ')
+ szShortName[--len] = 0;
+}
+
+static void DisplayNotIncludedPlugins(HWND hwndListBox, const HELPPACK_INFO *pack)
+{
+ /* enum plugins */
+ TCHAR szDir[MAX_PATH];
+ if (GetModuleFileName(NULL, szDir, sizeof(szDir))) {
+ TCHAR *p = _tcsrchr(szDir, _T('\\'));
+ if (p != NULL)
+ *p = _T('\0');
+ TCHAR szSearch[MAX_PATH];
+ mir_sntprintf(szSearch, sizeof(szSearch), _T("%s\\Plugins\\*.dll"), szDir);
+ WIN32_FIND_DATA wfd;
+ HANDLE hFind = FindFirstFile(szSearch, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ DWORD mirandaVersion = CallService(MS_SYSTEM_GETVERSION, 0, 0);
+ SendMessage(hwndListBox, LB_SETLOCALE, Langpack_GetDefaultLocale(), 0); /* for sort order */
+ SendMessage(hwndListBox, LB_INITSTORAGE, 128, lstrlenA(pack->szPluginsIncluded)); /* speed up */
+ do {
+ if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ if ((lstrlen(wfd.cFileName)<4) || (wfd.cFileName[lstrlen(wfd.cFileName) - 4] != _T('.')))
+ continue;
+ /* file name */
+ lstrcpy(szSearch, wfd.cFileName); /* buffer safe */
+ p = _tcsrchr(szSearch, _T('.'));
+ if (p != NULL)
+ *p = _T('\0');
+ char cFileNameA[MAX_PATH];
+ cFileNameA[0] = '\0';
+ WideCharToMultiByte(CP_ACP, 0, szSearch, -1, cFileNameA, sizeof(cFileNameA), NULL, NULL);
+ if (IsPluginIncluded(pack, cFileNameA))
+ continue;
+
+ /* friendly name of the plugin */
+ mir_sntprintf(szSearch, sizeof(szSearch), _T("%s\\Plugins\\%s"), szDir, wfd.cFileName);
+ HMODULE hModule = GetModuleHandle(szSearch);
+ BOOL fNeedsFree = (hModule == NULL);
+ if (hModule == NULL) {
+ hModule = LoadLibrary(szSearch);
+ if (hModule == NULL)
+ continue;
+ }
+
+ /* plugin info */
+ pfnMirandaPluginInfo pFunc = (pfnMirandaPluginInfo)GetProcAddress(hModule, "MirandaPluginInfoEx");
+ if (pFunc != NULL) { /* both structs have the same header */
+ PLUGININFOEX *pluginInfo = pFunc(mirandaVersion);
+ if (pluginInfo != NULL && pluginInfo->cbSize >= sizeof(PLUGININFOEX) && pluginInfo->shortName != NULL) {
+ char buf[128];
+ lstrcpynA(buf, pluginInfo->shortName, sizeof(buf)); /* buffer safe */
+ CleanupPluginName(buf);
+
+ TCHAR buf2[128];
+ mir_sntprintf(buf2, sizeof(buf2), _T("%hs (%s)"), buf, CharLower(wfd.cFileName));
+ SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)buf2);
+ }
+ }
+ if (fNeedsFree)
+ FreeLibrary(hModule);
+ } while (FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+ /* all are included? */
+ if (!SendMessage(hwndListBox, LB_GETCOUNT, 0, 0))
+ SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TranslateT("All installed plugins are included."));
+}
+
+// pack is allowed to be NULL
+static void DisplayPackInfo(HWND hwndDlg, const HELPPACK_INFO *pack)
+{
+ /* show/hide controls */
+ if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_NOPACK)) == (pack != NULL)) {
+ const int controls[] = { IDC_LANGNOTINCLUDEDLABEL, IDC_LANGNOTINCLUDED, IDC_LANGDATELABEL, IDC_LANGDATE,
+ IDC_LANGLOCALELABEL, IDC_LANGLOCALE, IDC_LANGVERSIONLABEL, IDC_LANGVERSION,
+ IDC_LANGMODUSINGLABEL, IDC_LANGMODUSING, IDC_LANGAUTHORSLABEL, IDC_LANGAUTHORS,
+ IDC_LANGEMAILLABEL, IDC_LANGEMAIL };
+ for (int i = 0; i < sizeof(controls); i++)
+ ShowWindow(GetDlgItem(hwndDlg, controls[i]), (pack != NULL) ? SW_SHOW : SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_NOPACK), (pack != NULL) ? SW_HIDE : SW_SHOW);
+ SetDlgItemText(hwndDlg, IDC_LANGINFOFRAME, NULL);
+ }
+ if (pack == NULL)
+ return;
+ /* compute not-included from included list */
+ SendDlgItemMessage(hwndDlg, IDC_LANGNOTINCLUDED, LB_RESETCONTENT, 0, 0);
+ DisplayNotIncludedPlugins(GetDlgItem(hwndDlg, IDC_LANGNOTINCLUDED), pack);
+ /* locale string */
+ if (!(pack->flags&HPF_NOLOCALE)) {
+ TCHAR szLocaleName[128];
+ szLocaleName[0] = _T('\0');
+ /* can't use LOCALE_SNAME as it is not present on pre WinVista */
+ if (!GetLocaleInfo(pack->Locale, LOCALE_SISO639LANGNAME, szLocaleName, sizeof(szLocaleName))) { /* Win98/NT4+ */
+ if (!GetLocaleInfo(pack->Locale, LOCALE_SLANGUAGE, szLocaleName, sizeof(szLocaleName))) /* not unique! */
+ szLocaleName[0] = _T('\0');
+ }
+ else {
+ if (GetLocaleInfo(pack->Locale, LOCALE_SISO3166CTRYNAME, &szLocaleName[3], sizeof(szLocaleName) - 3)) /* Win98/NT4+ */
+ szLocaleName[2] = _T('-');
+ }
+ /* add some note if its incompatible */
+ if (szLocaleName[0]) {
+ if (!IsValidLocale(pack->Locale, LCID_INSTALLED)) {
+ TCHAR *pszIncompat;
+ pszIncompat = TranslateT("(incompatible)");
+ szLocaleName[sizeof(szLocaleName) - lstrlen(pszIncompat) - 1] = 0;
+ lstrcat(lstrcat(szLocaleName, _T(" ")), pszIncompat); /* buffer safe */
+ }
+ SetDlgItemText(hwndDlg, IDC_LANGLOCALE, szLocaleName);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_LANGLOCALE, TranslateT("Unknown"));
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_LANGLOCALE, TranslateT("Current"));
+
+ /* file date */
+ {
+ SYSTEMTIME stFileDate;
+ TCHAR szDate[128];
+ szDate[0] = _T('\0');
+ if (FileTimeToSystemTime(&pack->ftFileDate, &stFileDate))
+ GetDateFormat(Langpack_GetDefaultLocale(), DATE_SHORTDATE, &stFileDate, NULL, szDate, sizeof(szDate));
+ SetDlgItemText(hwndDlg, IDC_LANGDATE, szDate);
+ }
+
+ /* version */
+ SetDlgItemTextA(hwndDlg, IDC_LANGVERSION, pack->szVersion);
+ if (pack->szVersion[0] && pack->szFLName[0]) {
+ if (!IsWindowVisible(GetDlgItem(hwndDlg, IDC_LANGVERSIONLABEL))) {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LANGVERSIONLABEL), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LANGVERSION), SW_SHOW);
+ }
+ }
+ else {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LANGVERSIONLABEL), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LANGVERSION), SW_HIDE);
+ }
+
+ /* general */
+ SetDlgItemTextA(hwndDlg, IDC_LANGMODUSING, pack->szLastModifiedUsing);
+ SetDlgItemTextA(hwndDlg, IDC_LANGAUTHORS, pack->szAuthors);
+ SetDlgItemTextA(hwndDlg, IDC_LANGEMAIL, pack->szAuthorEmail);
+ SetDlgItemText(hwndDlg, IDC_LANGINFOFRAME, TranslateTS(pack->szLanguage));
+}
+
+#define M_RELOADLIST (WM_APP+1)
+#define M_SHOWFILECOL (WM_APP+2)
+
+static void DeletePackFile(HWND hwndDlg, HWND hwndList, int iItem, HELPPACK_INFO *pack)
+{
+ SHFILEOPSTRUCT sfo = { 0 };
+ sfo.hwnd = hwndDlg;
+ sfo.wFunc = FO_DELETE;
+ sfo.fFlags = FOF_SIMPLEPROGRESS | FOF_SILENT; /* silent = no progress */
+ /* double zero terminated */
+ TCHAR szFileName[MAX_PATH];
+ if (GetPackPath(szFileName, sizeof(szFileName) - 1, pack->flags&HPF_ENABLED, pack->szFileName)) {
+ szFileName[lstrlen(szFileName) + 1] = _T('\0');
+ sfo.pFrom = szFileName;
+ /* ask to delete file */
+ if (!SHFileOperation(&sfo) && !sfo.fAnyOperationsAborted) {
+ LVITEM lvi;
+ lvi.iItem = iItem;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_STATE;
+ lvi.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED | LVIS_FOCUSED;
+ if (ListView_GetItem(hwndList, &lvi)) {
+ ListView_DeleteItem(hwndList, iItem);
+ /* enable/select next item at same position */
+ int nCount = ListView_GetItemCount(hwndList);
+ lvi.iItem = (iItem < nCount) ? iItem : iItem - 1;
+ if (lvi.iItem != -1) {
+ ListView_SetItemState(hwndList, lvi.iItem, lvi.state, lvi.stateMask);
+ /* enable pack file */
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ if (ListView_GetItem(hwndList, &lvi))
+ EnablePack((HELPPACK_INFO*)lvi.lParam, _T("helppack_*.txt"));
+ }
+ else
+ DisplayPackInfo(hwndDlg, NULL);
+ if (nCount == 1)
+ SendMessage(hwndDlg, M_SHOWFILECOL, 0, FALSE);
+ }
+ }
+ }
+}
+
+static INT_PTR CALLBACK InsertPackItemEnumProc(HELPPACK_INFO *pack, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+
+ HELPPACK_INFO *pack2 = (HELPPACK_INFO*)mir_alloc(sizeof(HELPPACK_INFO));
+ if (pack2 == NULL)
+ return FALSE;
+ CopyMemory(pack2, pack, sizeof(HELPPACK_INFO));
+
+ /* country flag icon */
+ LVITEM lvi;
+ lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
+ if ((HIMAGELIST)lParam != NULL) {
+ HICON hIcon;
+ if (pack->flags&HPF_DEFAULT) /* not really needed, but to be consistent with LangMan */
+ hIcon = (HICON)CallService(MS_FLAGS_CREATEMERGEDFLAGICON, CTRY_UNITED_STATES, CTRY_UNITED_KINGDOM);
+ else {
+ int countryId = 0xFFFF; /* Unknown */
+ TCHAR szBuf[6];
+ /* get country id from locale */
+ if (!(pack->flags&HPF_NOLOCALE))
+ if (GetLocaleInfo(pack->Locale, LOCALE_ICOUNTRY, szBuf, sizeof(szBuf)))
+ countryId = _ttoi(szBuf);
+ hIcon = (HICON)CallService(MS_FLAGS_LOADFLAGICON, countryId, 0);
+ }
+ lvi.iImage = (hIcon == NULL) ? -1 : ImageList_AddIcon((HIMAGELIST)lParam, hIcon);
+ lvi.mask |= LVIF_IMAGE;
+ }
+
+ /* insert */
+ lvi.iItem = lvi.iSubItem = 0;
+ lvi.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED;
+ lvi.state = INDEXTOSTATEIMAGEMASK((pack->flags&HPF_ENABLED) ? 2 : 1);
+ if (pack->flags&HPF_ENABLED)
+ lvi.state |= LVIS_SELECTED | LVIS_FOCUSED;
+ lvi.pszText = TranslateTS(pack->szLanguage);
+ lvi.lParam = (LPARAM)pack2;
+ ListView_InsertItem((HWND)wParam, &lvi);
+
+ return TRUE;
+}
+
+static int CALLBACK CompareListItem(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ int cmp = CompareString((LCID)lParamSort, 0, ((HELPPACK_INFO*)lParam1)->szLanguage, -1, ((HELPPACK_INFO*)lParam2)->szLanguage, -1);
+ if (cmp)
+ cmp -= 2;
+ if (!cmp) {
+ if (((HELPPACK_INFO*)lParam1)->flags&HPF_ENABLED)
+ cmp = -1;
+ else if (((HELPPACK_INFO*)lParam2)->flags&HPF_ENABLED)
+ cmp = 1;
+ }
+
+ return cmp;
+}
+
+static INT_PTR CALLBACK LangOptDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_LANGLIST);
+ LVITEM lvi;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ hwndLangOpt = hwndDlg;
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP);
+ ListView_SetImageList(hwndList, CreateRadioImages(ListView_GetBkColor(hwndList), ListView_GetTextColor(hwndList)), LVSIL_STATE); /* auto-destroyed */
+ {
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT;
+ lvc.pszText = TranslateT("Installed Languages");
+ ListView_InsertColumn(hwndList, 0, &lvc);
+ }
+ if (ServiceExists(MS_FLAGS_LOADFLAGICON))
+ ListView_SetImageList(hwndList, ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR24, 8, 8), LVSIL_SMALL);
+ CorrectPacks(_T("helppack_*.txt"), _T("helppack_english.txt"), FALSE);
+ CheckDlgButton(hwndDlg, IDC_ENABLEHELPUPDATES, db_get_b(NULL, "HelpPlugin", "EnableHelpUpdates", SETTING_ENABLEHELPUPDATES_DEFAULT) != 0);
+ SendMessage(hwndDlg, M_RELOADLIST, 0, 0);
+ SendMessage(hwndDlg, M_SHOWFILECOL, 0, 1);
+ return TRUE;
+ }
+ case M_RELOADLIST:
+ {
+ /* init list */
+ ListView_DeleteAllItems(hwndList);
+ ListView_DeleteColumn(hwndList, 1); /* if present */
+ {
+ HIMAGELIST himl = ListView_GetImageList(hwndList, LVSIL_SMALL);
+ ImageList_RemoveAll(himl);
+ /* enum all packs */
+ EnumPacks(InsertPackItemEnumProc, _T("helppack_*.txt"), "Miranda Help Pack Version 1", (WPARAM)hwndList, (LPARAM)himl);
+ /* nothing installed */
+ if (!ListView_GetItemCount(hwndList))
+ DisplayPackInfo(hwndDlg, NULL);
+ /* make it use current langpack locale for sort */
+ ListView_SortItems(hwndList, CompareListItem, Langpack_GetDefaultLocale());
+ CheckDlgButton(hwndDlg, IDC_ENABLEHELPUPDATES, db_get_b(NULL, "HelpPlugin", "EnableHelpUpdates", SETTING_ENABLEHELPUPDATES_DEFAULT) != 0);
+ /* show selection */
+ int iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
+ if (iItem != -1)
+ ListView_EnsureVisible(hwndList, iItem, FALSE);
+ }
+ return TRUE;
+ }
+ case M_SHOWFILECOL:
+ if ((BOOL)lParam && ListView_GetItemCount(hwndList)>1) {
+ /* add column */
+ LVCOLUMN lvc;
+ ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE_USEHEADER);
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
+ lvc.pszText = TranslateT("File");
+ lvc.cx = 160;
+ ListView_InsertColumn(hwndList, lvc.iSubItem = 1, &lvc);
+ ListView_SetColumnWidth(hwndList, 0, ListView_GetColumnWidth(hwndList, 0) - lvc.cx);
+
+ /* add text */
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ for (lvi.iItem = 0; ListView_GetItem(hwndList, &lvi); ++lvi.iItem) {
+ HELPPACK_INFO *pack = (HELPPACK_INFO*)lvi.lParam;
+ ListView_SetItemText(hwndList, lvi.iItem, 1, pack->szFileName);
+ }
+ }
+ else {
+ ListView_DeleteColumn(hwndList, 1);
+ ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE_USEHEADER);
+ }
+ return TRUE;
+
+ case WM_THEMECHANGED:
+ case WM_SETTINGCHANGE:
+ {
+ HIMAGELIST himl = ListView_SetImageList(hwndList, CreateRadioImages(ListView_GetBkColor(hwndList), ListView_GetTextColor(hwndList)), LVSIL_STATE); /* auto-destroyed */
+ if (himl != NULL)
+ ImageList_Destroy(himl);
+ }
+ break;
+
+ case WM_CTLCOLORLISTBOX: /* mimic readonly edit */
+ return (BOOL)SendMessage(hwndDlg, WM_CTLCOLORSTATIC, wParam, lParam);
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_LANGEMAIL:
+ {
+ char buf[512];
+ lstrcpyA(buf, "mailto:");
+ if (GetWindowTextA(GetDlgItem(hwndDlg, LOWORD(wParam)), &buf[7], sizeof(buf) - 7))
+ // CallService(MS_UTILS_OPENURL,FALSE,(LPARAM)buf); // look at !!
+ return TRUE;
+ }
+
+ case IDC_MORELANG:
+ // CallService(MS_UTILS_OPENURL,TRUE,(LPARAM)"http://miranda-ng.org/"); //look at !!
+ return TRUE;
+
+ case IDC_ENABLEHELPUPDATES:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); /* enable apply */
+ return TRUE;
+
+ case IDC_DOWNLOADLANG:
+ // ServiceShowLangDialog(0, 0);
+ return TRUE;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ if (GetDlgCtrlID((HWND)wParam) == IDC_LANGLIST) {
+ /* get item */
+ LVHITTESTINFO hti;
+ POINTSTOPOINT(hti.pt, MAKEPOINTS(lParam));
+ if (hti.pt.x == -1 && hti.pt.y == -1) {
+ /* keyboard invoked */
+ hti.iItem = ListView_GetNextItem((HWND)wParam, -1, LVNI_SELECTED);
+ if (hti.iItem != -1)
+ break;
+
+ RECT rc;
+ if (!ListView_GetItemRect((HWND)wParam, hti.iItem, &rc, LVIR_SELECTBOUNDS))
+ break;
+
+ hti.pt.x = rc.left + (rc.right - rc.left) / 2;
+ hti.pt.y = rc.top + (rc.bottom - rc.top) / 2;
+ ClientToScreen((HWND)wParam, &hti.pt);
+ }
+ else {
+ ScreenToClient((HWND)wParam, &hti.pt);
+ if (ListView_HitTest((HWND)wParam, &hti) == -1 || !(hti.flags&LVHT_ONITEM))
+ break;
+ POINTSTOPOINT(hti.pt, MAKEPOINTS(lParam));
+ }
+ /* param */
+ lvi.iItem = hti.iItem;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ if (!ListView_GetItem((HWND)wParam, &lvi))
+ break;
+
+ /* context menu */
+ HELPPACK_INFO *pack = (HELPPACK_INFO*)lvi.lParam;
+ HMENU hContextMenu = CreatePopupMenu();
+ if (hContextMenu != NULL) {
+ AppendMenu(hContextMenu, MF_STRING, 2, TranslateT("&Remove..."));
+ if (TrackPopupMenuEx(hContextMenu, TPM_RETURNCMD | TPM_NONOTIFY | TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_HORPOSANIMATION | TPM_VERPOSANIMATION, hti.pt.x, hti.pt.y, (HWND)wParam, NULL))
+ DeletePackFile(hwndDlg, (HWND)wParam, hti.iItem, pack);
+ DestroyMenu(hContextMenu);
+ }
+ return TRUE;
+ }
+ break;
+
+ case WM_NOTIFYFORMAT:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, NFR_UNICODE);
+ return TRUE;
+
+ case WM_NOTIFY:
+ NMHDR *nmhdr = (NMHDR*)lParam;
+ switch (nmhdr->idFrom) {
+ case IDC_LANGLIST:
+ switch (nmhdr->code) {
+ case LVN_DELETEITEM:
+ LVITEM lvi;
+ lvi.iItem = ((NMLISTVIEW*)lParam)->iItem; /* nmlv->lParam is invalid */
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ if (ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ mir_free((HELPPACK_INFO*)lvi.lParam);
+ break;
+
+ case LVN_ITEMCHANGED:
+ {
+ NMLISTVIEW *nmlv = (NMLISTVIEW*)lParam;
+ if (!(nmlv->uChanged&LVIF_STATE))
+ break;
+
+ /* display info and check radio item */
+ if (nmlv->uNewState&LVIS_SELECTED && !(nmlv->uOldState&LVIS_SELECTED)) {
+ ListView_SetItemState(nmhdr->hwndFrom, nmlv->iItem, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
+ DisplayPackInfo(hwndDlg, (HELPPACK_INFO*)nmlv->lParam);
+ }
+ /* disable all other radio items */
+ else if (nmlv->uNewState&INDEXTOSTATEIMAGEMASK(2)) {
+ for (int iItem = ListView_GetItemCount(nmhdr->hwndFrom) - 1; iItem != -1; --iItem)
+ if (iItem != nmlv->iItem)
+ ListView_SetItemState(nmhdr->hwndFrom, iItem, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK);
+
+ /* enable apply */
+ if (nmlv->uOldState)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ break;
+
+ case LVN_KEYDOWN:
+ {
+ int iItem = ListView_GetNextItem(nmhdr->hwndFrom, -1, LVNI_SELECTED);
+ switch (((NMLVKEYDOWN*)lParam)->wVKey) {
+ case VK_SPACE:
+ ListView_SetItemState(nmhdr->hwndFrom, iItem, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
+ break;
+
+ case VK_DELETE:
+ lvi.iItem = iItem;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ if (ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ DeletePackFile(hwndDlg, nmhdr->hwndFrom, iItem, (HELPPACK_INFO*)lvi.lParam);
+ break;
+ }
+ }
+ break;
+
+ case NM_CLICK:
+ LVHITTESTINFO hti;
+ lParam = GetMessagePos();
+ POINTSTOPOINT(hti.pt, MAKEPOINTS(lParam));
+ ScreenToClient(nmhdr->hwndFrom, &hti.pt);
+ if (ListView_HitTest(nmhdr->hwndFrom, &hti) != -1)
+ if (hti.flags&(LVHT_ONITEMSTATEICON | LVHT_ONITEMICON)) /* one of them */
+ ListView_SetItemState(nmhdr->hwndFrom, hti.iItem, LVIS_SELECTED, LVIS_SELECTED);
+ } /* switch nmhdr->code */
+ break;
+
+ case 0:
+ switch (nmhdr->code) {
+ case PSN_APPLY:
+ lvi.mask = LVIF_STATE | LVIF_PARAM;
+ lvi.stateMask = LVIS_STATEIMAGEMASK;
+ lvi.iSubItem = 0;
+ for (lvi.iItem = 0; ListView_GetItem(hwndList, &lvi); ++lvi.iItem) {
+ HELPPACK_INFO *pack = (HELPPACK_INFO*)lvi.lParam;
+ if (lvi.state&INDEXTOSTATEIMAGEMASK(2)) {
+ EnablePack(pack, _T("helppack_*.txt"));
+ pack->flags |= HPF_ENABLED;
+ }
+ else
+ pack->flags &= ~HPF_ENABLED;
+ }
+ db_set_b(NULL, "HelpPlugin", "EnableAutoUpdates", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_ENABLEHELPUPDATES) != 0));
+ return TRUE;
+ }
+ } /* switch nmhdr->idFrom */
+ break;
+ }
+ return FALSE;
+}
+
+void ReloadLangOptList(void)
+{
+ if (hwndLangOpt != NULL) {
+ SendMessage(hwndLangOpt, M_RELOADLIST, 0, 0);
+ SendMessage(hwndLangOpt, M_SHOWFILECOL, 0, 1);
+ }
+}
+
+/**************************** ADV OPTS ****************************/
+
+static INT_PTR CALLBACK AdvOptDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ WORD val = settingAutoTipDelay;
+ if (!val)
+ val = db_get_w(NULL, "HelpPlugin", "AutoTipDelay", SETTING_AUTOTIPDELAY_DEFAULT);
+ CheckDlgButton(hwndDlg, IDC_AUTOTIPSENABLED, settingAutoTipDelay != 0);
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETRANGE, 0, MAKELPARAM(10000, 500));
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAY, EM_SETLIMITTEXT, (WPARAM)5, 0);
+ {
+ UDACCEL uda = { 0, 500 };
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETACCEL, 1, (LPARAM)&uda);
+ }
+ SetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, val, FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETPOS, 0, MAKELPARAM(val, 0));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYLABEL), settingAutoTipDelay != 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAY), settingAutoTipDelay != 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYSPIN), settingAutoTipDelay != 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYMS), settingAutoTipDelay != 0);
+ }
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_AUTOTIPSENABLED:
+ {
+ BOOL checked = IsDlgButtonChecked(hwndDlg, IDC_AUTOTIPSENABLED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYLABEL), checked);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAY), checked);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYSPIN), checked);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_AUTOTIPDELAYMS), checked);
+ break;
+ }
+ case IDC_AUTOTIPDELAY:
+ if (HIWORD(wParam) == EN_KILLFOCUS) {
+ WORD minVal = HIWORD(SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_GETRANGE, 0, 0));
+ if (GetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, NULL, FALSE)<minVal) {
+ SetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, minVal, FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETPOS, 0, MAKELPARAM(minVal, 0));
+ }
+ }
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus())
+ return TRUE;
+ break;
+ }
+ PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); // enable apply
+ break;
+ case WM_NOTIFYFORMAT:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, NFR_UNICODE);
+ return TRUE;
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ {
+ WORD minVal;
+ minVal = HIWORD(SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_GETRANGE, 0, 0));
+ if (GetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, NULL, FALSE) < minVal) {
+ SetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, minVal, FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_AUTOTIPDELAYSPIN, UDM_SETPOS, 0, MAKELPARAM(minVal, 0));
+ }
+ settingAutoTipDelay = (WORD)GetDlgItemInt(hwndDlg, IDC_AUTOTIPDELAY, NULL, FALSE);
+ db_set_w(NULL, "HelpPlugin", "AutoTipDelay", settingAutoTipDelay);
+
+ if (!IsDlgButtonChecked(hwndDlg, IDC_AUTOTIPSENABLED))
+ settingAutoTipDelay = 0;
+ db_set_b(NULL, "HelpPlugin", "AutoTipsEnabled", (BYTE)(settingAutoTipDelay != 0));
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/**************************** INIT ********************************/
+
+static int HelpOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.position = 1200000080;
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_LANG);
+ odp.ptszGroup = LPGENT("Customize"); // autotranslated
+ odp.ptszTitle = LPGENT("Help"); // autotranslated
+
+ odp.ptszTab = LPGENT("Language"); // autotranslated
+ odp.flags = ODPF_TCHAR | ODPF_BOLDGROUPS;
+ odp.pfnDlgProc = LangOptDlgProc;
+ Options_AddPage(wParam, &odp);
+
+#ifdef EDITOR
+ odp.ptszTab = _T("Help Editor"); /* autotranslated */
+#else
+ odp.ptszTab = _T("Advanced"); /* autotranslated */
+#endif
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ADV);
+ odp.pfnDlgProc = AdvOptDlgProc;
+ Options_AddPage(wParam, &odp);
+
+ return 0;
+}
+
+void InitOptions(void)
+{
+ settingAutoTipDelay = 0;
+ if (db_get_b(NULL, "HelpPlugin", "AutoTipsEnabled", SETTING_AUTOTIPSENABLED_DEFAULT))
+ settingAutoTipDelay = db_get_w(NULL, "HelpPlugin", "AutoTipDelay", SETTING_AUTOTIPDELAY_DEFAULT);
+
+ hwndLangOpt = NULL;
+ hHookOptInit = HookEvent(ME_OPT_INITIALISE, HelpOptInit);
+ CorrectPacks(_T("helppack_*.txt"), _T("helppack_english.txt"), FALSE);
+}
+
+void UninitOptions(void)
+{
+ UnhookEvent(hHookOptInit);
+} \ No newline at end of file
diff --git a/plugins/ContextHelp/src/resource.h b/plugins/ContextHelp/src/resource.h
new file mode 100644
index 0000000000..e9f9b0622f
--- /dev/null
+++ b/plugins/ContextHelp/src/resource.h
@@ -0,0 +1,61 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDD_HELP 101
+#define IDD_SHADOW 102
+#define IDD_OPT_LANG 103
+#define IDD_OPT_ADV 104
+#define IDD_UPDATENOTIFY 105
+#define IDD_DOWNLOADLANG 106
+#define IDC_CTLTEXT 1001
+#define IDC_TEXT 1002
+#define IDC_CTLTYPE 1003
+#define IDC_CTLID 1004
+#define IDC_MODULE 1005
+#define IDC_DLGID 1006
+#define IDC_CARETSUCKER 1007
+#define IDC_LANGLIST 1008
+#define IDC_LANGINFOFRAME 1009
+#define IDC_LANGAUTHORSLABEL 1010
+#define IDC_LANGAUTHORS 1011
+#define IDC_LANGEMAILLABEL 1012
+#define IDC_LANGEMAIL 1013
+#define IDC_LANGMODUSINGLABEL 1014
+#define IDC_LANGMODUSING 1015
+#define IDC_LANGDATELABEL 1016
+#define IDC_LANGDATE 1017
+#define IDC_LANGVERSIONLABEL 1018
+#define IDC_LANGVERSION 1019
+#define IDC_LANGLOCALELABEL 1020
+#define IDC_LANGLOCALE 1021
+#define IDC_LANGNOTINCLUDEDLABEL 1022
+#define IDC_LANGNOTINCLUDED 1023
+#define IDC_MORELANG 1024
+#define IDC_NOPACK 1025
+#define IDC_ENABLEHELPUPDATES 1026
+#define IDC_DOWNLOADLANG 1027
+#define IDC_LANGCOMBO 1028
+#define IDC_DOWNLOADALL 1029
+#define IDC_LOADING 1030
+#define IDC_LANGUAGELABEL 1031
+#define IDC_LANGUAGE 1032
+#define IDC_CURRENTVERSION 1033
+#define IDC_NEWVERSION 1034
+#define IDC_NEWVERSIONLABEL 1035
+#define IDC_AUTOTIPSENABLED 1036
+#define IDC_AUTOTIPDELAY 1037
+#define IDC_AUTOTIPDELAYSPIN 1038
+#define IDC_AUTOTIPDELAYLABEL 1039
+#define IDC_AUTOTIPDELAYMS 1040
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 107
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1041
+#define _APS_NEXT_SYMED_VALUE 2001
+#endif
+#endif
diff --git a/plugins/ContextHelp/src/stdafx.cxx b/plugins/ContextHelp/src/stdafx.cxx
new file mode 100644
index 0000000000..0830d91bb3
--- /dev/null
+++ b/plugins/ContextHelp/src/stdafx.cxx
@@ -0,0 +1,18 @@
+/*
+Copyright (C) 2012-16 Miranda NG project (http://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
diff --git a/plugins/ContextHelp/src/stdafx.h b/plugins/ContextHelp/src/stdafx.h
new file mode 100644
index 0000000000..20cac3503d
--- /dev/null
+++ b/plugins/ContextHelp/src/stdafx.h
@@ -0,0 +1,52 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef CONTEXTHELP_COMMONHEADERS_H
+#define CONTEXTHELP_COMMONHEADERS_H
+
+#include <windows.h>
+#include <Uxtheme.h>
+#include <Vsstyle.h>
+
+#include <newpluginapi.h>
+//#include <win2k.h>
+#include <m_system_cpp.h>
+#include <m_options.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_langpack.h>
+#include <m_utils.h>
+#include <m_flags.h>
+#include <m_netlib.h>
+#include <m_skin.h>
+#include <m_icolib.h>
+#include <m_clist.h>
+
+//#define EDITOR
+#include <m_help.h>
+#include "help.h"
+#include "version.h"
+#include "resource.h"
+
+extern HINSTANCE hInstance;
+
+#define USERAGENT_VERSION "0.2.1.2"
+
+
+#endif //CONTEXTHELP_COMMONHEADERS_H \ No newline at end of file
diff --git a/plugins/ContextHelp/src/streaminout.cpp b/plugins/ContextHelp/src/streaminout.cpp
new file mode 100644
index 0000000000..460bc655c2
--- /dev/null
+++ b/plugins/ContextHelp/src/streaminout.cpp
@@ -0,0 +1,771 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+
+#include <richedit.h>
+
+struct EditStreamData {
+ PBYTE pbBuff;
+ int cbBuff;
+ int iCurrent;
+};
+
+#ifndef EDITOR
+struct HyperlinkData {
+ CHARRANGE range;
+ char *szLink;
+} static *hyperlink = NULL;
+static int hyperlinkCount = 0;
+#endif
+
+static DWORD CALLBACK EditStreamInRtf(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+ struct EditStreamData *esd = (struct EditStreamData*)dwCookie;
+ *pcb = min(esd->cbBuff - esd->iCurrent, cb);
+ CopyMemory(pbBuff, esd->pbBuff, *pcb);
+ esd->iCurrent += *pcb;
+
+ return 0;
+}
+
+#ifdef EDITOR
+static DWORD CALLBACK EditStreamOutRtf(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+ struct EditStreamData *esd = (struct EditStreamData*)dwCookie;
+ PBYTE buf = (PBYTE)mir_realloc(esd->pbBuff, esd->cbBuff + cb + 1);
+ if (buf == NULL)
+ return 0;
+ esd->pbBuff = buf;
+ esd->cbBuff += cb;
+ CopyMemory(esd->pbBuff + esd->iCurrent, pbBuff, cb);
+ esd->iCurrent += cb;
+ esd->pbBuff[esd->iCurrent] = '\0';
+ *pcb = cb;
+
+ return 0;
+}
+#endif
+
+struct {
+ const char *szSym;
+ char ch;
+} static const htmlSymbolChars[] = {
+ { "lt", '<' },
+ { "gt", '>' },
+ { "amp", '&' },
+ { "quot", '\"' },
+ { "nbsp", ' ' },
+};
+
+struct {
+ const char *szName;
+ const char *szClr;
+} static const htmlColourNames[] = {
+ { "black", "000000" },
+ { "maroon", "800000" },
+ { "green", "008000" },
+ { "olive", "808000" },
+ { "navy", "000080" },
+ { "purple", "800080" },
+ { "teal", "008080" },
+ { "silver", "C0C0C0" },
+ { "gray", "808080" },
+ { "red", "FF0000" },
+ { "lime", "00FF00" },
+ { "yellow", "FFFF00" },
+ { "blue", "0000FF" },
+ { "fuchsia", "FF00FF" },
+ { "aqua", "00FFFF" },
+ { "white", "FFFFFF" },
+};
+
+// a quick test to see who's read their comp.lang.c FAQ:
+#define stringize2(n) #n
+#define stringize(n) stringize2(n)
+
+struct {
+ const char *szHtml;
+ const char *szRtf;
+} static const simpleHtmlRtfConversions[] = {
+ { "i", "i" },
+ { "/i", "i0" },
+ { "b", "b" },
+ { "/b", "b0" },
+ { "u", "ul" },
+ { "/u", "ul0" },
+ { "big", "fs" stringize(TEXTSIZE_BIG) },
+ { "/big", "fs" stringize(TEXTSIZE_NORMAL) },
+ { "small", "fs" stringize(TEXTSIZE_SMALL) },
+ { "/small", "fs" stringize(TEXTSIZE_NORMAL) },
+ { "/font", "cf0" }
+};
+
+// mir_free() the return value
+char *GetHtmlTagAttribute(const char *pszTag, const char *pszAttr)
+{
+ int iAttrName, iAttrNameEnd, iAttrEquals, iAttrValue, iAttrValueEnd, iAttrEnd;
+ int attrLen = lstrlenA(pszAttr);
+
+ for (iAttrName = 0; !isspace(pszTag[iAttrName]) && pszTag[iAttrName] != '>'; iAttrName++);
+ for (;;) {
+ for (; isspace(pszTag[iAttrName]); iAttrName++);
+ if (pszTag[iAttrName] == '>' || pszTag[iAttrName] == '\0')
+ break;
+ for (iAttrNameEnd = iAttrName; isalnum(pszTag[iAttrNameEnd]); iAttrNameEnd++);
+ for (iAttrEquals = iAttrNameEnd; isspace(pszTag[iAttrEquals]); iAttrEquals++);
+ if (pszTag[iAttrEquals] != '=') {
+ iAttrName = iAttrEquals;
+ continue;
+ }
+ for (iAttrValue = iAttrEquals + 1; isspace(pszTag[iAttrValue]); iAttrValue++);
+ if (pszTag[iAttrValue] == '>' || pszTag[iAttrValue] == '\0')
+ break;
+ if (pszTag[iAttrValue] == '"' || pszTag[iAttrValue] == '\'') {
+ for (iAttrValueEnd = iAttrValue + 1; pszTag[iAttrValueEnd] && pszTag[iAttrValueEnd] != pszTag[iAttrValue]; iAttrValueEnd++);
+ iAttrValue++;
+ iAttrEnd = iAttrValueEnd + 1;
+ }
+ else {
+ for (iAttrValueEnd = iAttrValue; pszTag[iAttrValueEnd] && pszTag[iAttrValueEnd] != '>' && !isspace(pszTag[iAttrValueEnd]); iAttrValueEnd++);
+ iAttrEnd = iAttrValueEnd;
+ }
+ if (pszTag[iAttrValueEnd] == '\0')
+ break;
+ if (attrLen == iAttrNameEnd - iAttrName && !_strnicmp(pszAttr, pszTag + iAttrName, attrLen)) {
+ char *szValue;
+ szValue = (char*)mir_alloc(iAttrValueEnd - iAttrValue + 1);
+ if (szValue != NULL) {
+ CopyMemory(szValue, pszTag + iAttrValue, iAttrValueEnd - iAttrValue);
+ szValue[iAttrValueEnd - iAttrValue] = '\0';
+ }
+ return szValue;
+ }
+ iAttrName = iAttrEnd;
+ }
+ return NULL;
+}
+
+void StreamInHtml(HWND hwndEdit, const char *szHtml, UINT codepage, COLORREF clrBkgrnd)
+{
+ EDITSTREAM stream;
+ struct EditStreamData esd;
+ struct ResizableCharBuffer header, body;
+ COLORREF *colourTbl = NULL;
+ int colourTblCount = 0;
+ const char *pszHtml;
+ char *szThisTagHref = NULL;
+ int keywordAtBeginning = 1, paragraphBefore = 0, lineBreakBefore = 1;
+ int charCount = 0;
+
+ ZeroMemory(&stream, sizeof(stream));
+ ZeroMemory(&esd, sizeof(esd));
+ ZeroMemory(&header, sizeof(header));
+ ZeroMemory(&body, sizeof(body));
+
+#ifndef EDITOR
+ FreeHyperlinkData();
+#endif
+ AppendToCharBuffer(&header, "{\\rtf1\\ansi\\ansicpg%u\\deff0{\\fonttbl{\\f0 Tahoma;}}", codepage);
+ for (pszHtml = szHtml; *pszHtml != '\0';) {
+ if (*pszHtml == '<') {
+ const char *pszTagEnd;
+ int iNameEnd, i;
+ char szTagName[16];
+
+ pszTagEnd = strchr(pszHtml + 1, '>');
+ if (pszTagEnd == NULL)
+ break;
+ for (iNameEnd = 1; pszHtml[iNameEnd] != '\0' && pszHtml[iNameEnd] != '>' && !isspace(pszHtml[iNameEnd]); iNameEnd++);
+ CopyMemory(szTagName, pszHtml + 1, min(sizeof(szTagName), iNameEnd));
+ szTagName[min(sizeof(szTagName), iNameEnd) - 1] = '\0';
+
+ for (i = 0; i < sizeof(simpleHtmlRtfConversions); i++) {
+ if (!lstrcmpiA(szTagName, simpleHtmlRtfConversions[i].szHtml)) {
+ AppendToCharBuffer(&body, "\\%s ", simpleHtmlRtfConversions[i].szRtf);
+ break;
+ }
+ }
+ if (i == sizeof(simpleHtmlRtfConversions)) {
+ if (!lstrcmpiA(szTagName, "br")) {
+ AppendToCharBuffer(&body, "\\par ");
+ charCount++; // linebreaks are characters
+ lineBreakBefore = 1;
+ }
+ else if (!lstrcmpiA(szTagName, "p") || !lstrcmpiA(szTagName, "/p")) {
+ if (charCount)
+ paragraphBefore = 1;
+ }
+ else if (!lstrcmpiA(szTagName, "a")) {
+ mir_free(szThisTagHref); // does NULL check
+ szThisTagHref = GetHtmlTagAttribute(pszHtml, "href");
+#ifdef EDITOR
+ if (szThisTagHref != NULL)
+ AppendToCharBuffer(&body, "\\strike ");
+#else
+ if (szThisTagHref != NULL) {
+ struct HyperlinkData *buf = (struct HyperlinkData*)mir_realloc(hyperlink, sizeof(struct HyperlinkData)*(hyperlinkCount + 1));
+ if (buf != NULL) { // hyperlinkCount increased at </a>
+ hyperlink = buf;
+ hyperlink[hyperlinkCount].range.cpMin = paragraphBefore ? (charCount + 2) : charCount;
+ hyperlink[hyperlinkCount].range.cpMax = -1;
+ hyperlink[hyperlinkCount].szLink = NULL;
+ }
+ else {
+ mir_free(szThisTagHref);
+ szThisTagHref = NULL;
+ }
+ }
+#endif
+ }
+ else if (!lstrcmpiA(szTagName, "/a")) {
+ if (szThisTagHref) {
+#ifdef EDITOR
+ AppendToCharBuffer(&body, ":%s\\strike0 ", szThisTagHref);
+ mir_free(szThisTagHref);
+#else
+ mir_utf8decodecp(szThisTagHref, CP_ACP, NULL);
+ hyperlink[hyperlinkCount].range.cpMax = charCount;
+ hyperlink[hyperlinkCount].szLink = szThisTagHref;
+ hyperlinkCount++;
+#endif
+ szThisTagHref = NULL;
+ }
+ }
+ else if (!lstrcmpiA(szTagName, "font")) {
+ char *szColour = GetHtmlTagAttribute(pszHtml, "color");
+ if (szColour != NULL) {
+ int i, freeColour = 1;
+ if (szColour[0] != '#' || lstrlenA(szColour) != 7) {
+ for (i = 0; i < sizeof(htmlColourNames); i++) {
+ if (!lstrcmpiA(szColour, htmlColourNames[i].szName)) {
+ mir_free(szColour);
+ szColour = (char*)htmlColourNames[i].szClr;
+ freeColour = 0;
+ break;
+ }
+ }
+ }
+ else
+ szColour++;
+ if (szColour != NULL) {
+ COLORREF colour;
+ char szRed[3], szGreen[3], szBlue[3];
+ szRed[0] = szColour[0]; szRed[1] = szColour[1]; szRed[2] = '\0';
+ szGreen[0] = szColour[2]; szGreen[1] = szColour[3]; szGreen[2] = '\0';
+ szBlue[0] = szColour[4]; szBlue[1] = szColour[5]; szBlue[2] = '\0';
+ colour = RGB(strtol(szRed, NULL, 16), strtol(szGreen, NULL, 16), strtol(szBlue, NULL, 16));
+ if (freeColour)
+ mir_free(szColour);
+#ifndef EDITOR
+ if (colour != clrBkgrnd) { // ensure color is visible
+#else
+ UNREFERENCED_PARAMETER(clrBkgrnd);
+#endif // !defined EDITOR
+ for (i = 0; i < colourTblCount; i++)
+ if (colourTbl[i] == colour)
+ break;
+ if (i == colourTblCount) {
+ COLORREF *buf = (COLORREF*)mir_realloc(colourTbl, (colourTblCount + 1)*sizeof(COLORREF));
+ if (buf != NULL) {
+ colourTbl = buf;
+ colourTblCount++;
+ colourTbl[i] = colour;
+ }
+ }
+ AppendToCharBuffer(&body, "\\cf%d ", i + 2);
+#ifndef EDITOR
+ }
+#endif
+ }
+ }
+ } // endif font
+ }
+ pszHtml = pszTagEnd + 1;
+ }
+ else if (*pszHtml == '&') {
+ const char *pszTagEnd;
+ char szTag[16];
+ int i;
+
+ pszTagEnd = strchr(pszHtml + 1, ';');
+ if (pszTagEnd == NULL)
+ break;
+ CopyMemory(szTag, pszHtml + 1, min(sizeof(szTag), pszTagEnd - pszHtml));
+ szTag[min(sizeof(szTag), pszTagEnd - pszHtml) - 1] = '\0';
+ if (szTag[0] == '#') {
+ int ch;
+ if (szTag[1] == 'x' || szTag[1] == 'X')
+ ch = strtol(szTag + 2, NULL, 16);
+ else
+ ch = strtol(szTag + 1, NULL, 10);
+ if (ch >= 0x100)
+ AppendToCharBuffer(&body, "\\u%d", ch);
+ else
+ AppendToCharBuffer(&body, "\\'%02x ", ch);
+ }
+ else {
+ for (i = 0; i < sizeof(htmlSymbolChars); i++) {
+ if (!lstrcmpiA(szTag, htmlSymbolChars[i].szSym)) {
+ AppendCharToCharBuffer(&body, htmlSymbolChars[i].ch);
+ charCount++;
+ break;
+ }
+ }
+ }
+ pszHtml = pszTagEnd + 1;
+ }
+ else if (*pszHtml != ' ' || (!lineBreakBefore && !paragraphBefore)) {
+ lineBreakBefore = 0;
+ if (paragraphBefore) {
+ AppendToCharBuffer(&body, "\\par\\par ");
+ charCount += 2; // linebreaks are characters
+ paragraphBefore = 0;
+ }
+ if ((BYTE)*pszHtml >= ' ')
+ charCount++;
+ if (*pszHtml == '\\' || *pszHtml == '{' || *pszHtml == '}')
+ AppendCharToCharBuffer(&body, '\\');
+ AppendCharToCharBuffer(&body, *pszHtml++);
+ }
+ else
+ pszHtml++;
+ }
+ mir_free(szThisTagHref); // does NULL check
+
+ {
+ COLORREF clr = GetSysColorBrush(COLOR_HOTLIGHT) ? GetSysColor(COLOR_HOTLIGHT) : RGB(0, 0, 255);
+ AppendToCharBuffer(&header, "{\\colortbl ;\\red%d\\green%d\\blue%d;", GetRValue(clr), GetGValue(clr), GetBValue(clr));
+ for (int i = 0; i < colourTblCount; i++)
+ AppendToCharBuffer(&header, "\\red%d\\green%d\\blue%d;", GetRValue(colourTbl[i]), GetGValue(colourTbl[i]), GetBValue(colourTbl[i]));
+ }
+ AppendToCharBuffer(&header, "}\\pard\\fs16\\uc0");
+ if (keywordAtBeginning)
+ AppendCharToCharBuffer(&header, ' ');
+ mir_free(colourTbl); // does NULL check
+
+ if (header.sz != NULL) {
+ AppendToCharBuffer(&header, "%s}", body.sz ? body.sz : "");
+ esd.pbBuff = (PBYTE)header.sz;
+ esd.cbBuff = header.iEnd;
+ stream.dwCookie = (DWORD)&esd;
+ stream.pfnCallback = (EDITSTREAMCALLBACK)EditStreamInRtf;
+ SendMessage(hwndEdit, EM_STREAMIN, SF_RTF, (LPARAM)&stream);
+ mir_free(header.sz);
+ }
+ mir_free(body.sz); // does NULL check
+
+#ifndef EDITOR
+ CHARFORMAT cf;
+ ZeroMemory(&cf, sizeof(cf));
+ cf.cbSize = sizeof(cf);
+ cf.dwMask = CFM_UNDERLINE | CFM_COLOR; // CFE_LINK always uses RGB(0,0,255) instead of GetSysColor(COLOR_HOTLIGHT)
+ cf.dwEffects = CFE_UNDERLINE; // and ignores CFM_COLOR, so selfimplementing
+ cf.crTextColor = GetSysColorBrush(COLOR_HOTLIGHT) ? GetSysColor(COLOR_HOTLIGHT) : RGB(0, 0, 255);
+ for (int i = 0; i < hyperlinkCount; i++) {
+ SendMessage(hwndEdit, EM_EXSETSEL, 0, (LPARAM)&hyperlink[i].range);
+ SendMessage(hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+ }
+ SendMessage(hwndEdit, EM_SETSEL, 0, 0);
+#endif
+}
+
+#ifndef EDITOR
+void FreeHyperlinkData(void)
+{
+ for (int i = 0; i<hyperlinkCount; i++)
+ mir_free(hyperlink[i].szLink); // does NULL check
+ mir_free(hyperlink); // does NULL check
+ hyperlink = NULL;
+ hyperlinkCount = 0;
+}
+
+int IsHyperlink(LONG cpPos, LONG *pcpMin, LONG *pcpMax, char **ppszLink)
+{
+ for (int i = 0; i<hyperlinkCount; i++)
+ if (cpPos >= hyperlink[i].range.cpMin && cpPos <= hyperlink[i].range.cpMax) {
+ if (pcpMin)
+ *pcpMin = hyperlink[i].range.cpMin;
+ if (pcpMax)
+ *pcpMax = hyperlink[i].range.cpMax;
+ if (ppszLink)
+ *ppszLink = hyperlink[i].szLink;
+
+ return 1;
+ }
+ if (pcpMin)
+ *pcpMin = -1;
+ if (pcpMax)
+ *pcpMax = -1;
+ if (ppszLink)
+ *ppszLink = NULL;
+
+ return 0;
+}
+
+#endif // !defined EDITOR
+
+#ifdef EDITOR
+struct RtfGroupStackData {
+ BYTE bold, italic, underline, strikeout;
+ BYTE isDestination, isColourTbl, isFontTbl;
+ int colour;
+ int fontSize;
+ int unicodeSkip;
+ int charset;
+};
+
+char *StreamOutHtml(HWND hwndEdit)
+{
+ EDITSTREAM stream;
+ struct EditStreamData esd;
+ struct ResizableCharBuffer htmlOut, hyperlink, *output;
+ COLORREF *colourTbl = NULL;
+ int colourTblCount = 0;
+ struct RtfGroupStackData *groupStack;
+ int groupLevel;
+ int inFontTag = 0, inAnchorTag = 0, inBigTag = 0, inSmallTag = 0, lineBreakBefore = 0;
+ char *pszRtf;
+ int *fontTblCharsets = NULL;
+ int fontTblCount = 0;
+ int normalTextSize = 0;
+ void *buf;
+
+ ZeroMemory(&stream, sizeof(stream));
+ ZeroMemory(&esd, sizeof(esd));
+ ZeroMemory(&htmlOut, sizeof(htmlOut));
+ ZeroMemory(&hyperlink, sizeof(hyperlink));
+ ZeroMemory(&output, sizeof(output));
+
+ stream.dwCookie = (DWORD)&esd;
+ stream.pfnCallback = EditStreamOutRtf;
+ SendMessage(hwndEdit, EM_STREAMOUT, (WPARAM)(CP_UTF8 << 16) | SF_USECODEPAGE | SF_RTFNOOBJS | SFF_PLAINRTF, (LPARAM)&stream);
+ if (esd.pbBuff == NULL)
+ return NULL;
+
+ output = &htmlOut;
+ groupStack = (struct RtfGroupStackData*)mir_calloc(sizeof(struct RtfGroupStackData));
+ if (groupStack != NULL) {
+ groupLevel = 0;
+ groupStack[0].unicodeSkip = 1;
+ for (pszRtf = (char*)esd.pbBuff; *pszRtf != '\0';) {
+ if (*pszRtf == '{') {
+ buf = (struct RtfGroupStackData*)mir_realloc(groupStack, sizeof(struct RtfGroupStackData)*(groupLevel + 2));
+ if (buf == NULL)
+ break;
+ groupStack = (struct RtfGroupStackData*)buf;
+ groupStack[groupLevel] = groupStack[groupLevel];
+ groupLevel++;
+ pszRtf++;
+ }
+ else if (*pszRtf == '}') {
+ groupLevel--;
+ if (groupStack[groupLevel].bold != groupStack[groupLevel + 1].bold)
+ AppendToCharBuffer(output, groupStack[groupLevel].bold ? "<b>" : "</b>");
+ if (groupStack[groupLevel].italic != groupStack[groupLevel + 1].italic)
+ AppendToCharBuffer(output, groupStack[groupLevel].bold ? "<i>" : "</i>");
+ if (groupStack[groupLevel].underline != groupStack[groupLevel + 1].underline)
+ AppendToCharBuffer(output, groupStack[groupLevel].bold ? "<u>" : "</u>");
+ if (groupStack[groupLevel].strikeout != groupStack[groupLevel + 1].strikeout && groupStack[groupLevel + 1].strikeout)
+ if (inAnchorTag) {
+ AppendToCharBuffer(output, "</a>");
+ inAnchorTag = 0;
+ }
+ if (groupStack[groupLevel].colour != groupStack[groupLevel + 1].colour)
+ if (inFontTag) {
+ AppendToCharBuffer(output, "</font>");
+ inFontTag = 0;
+ }
+ if (groupStack[groupLevel].fontSize != groupStack[groupLevel + 1].fontSize) {
+ if (inBigTag) {
+ AppendToCharBuffer(output, "</big>");
+ inBigTag = 0;
+ }
+ if (inSmallTag) {
+ AppendToCharBuffer(output, "</small>");
+ inSmallTag = 0;
+ }
+ if (groupStack[groupLevel].fontSize<normalTextSize) {
+ AppendToCharBuffer(output, "<small>");
+ inSmallTag = 1;
+ }
+ else if (groupStack[groupLevel].fontSize>normalTextSize) {
+ AppendToCharBuffer(output, "<big>"); inBigTag = 1;
+ }
+ }
+ if (groupLevel == 0)
+ break;
+ pszRtf++;
+ }
+ else if (*pszRtf == '\\' && pszRtf[1] == '*') {
+ groupStack[groupLevel].isDestination = 1;
+ pszRtf += 2;
+ }
+ else if (*pszRtf == '\\' && pszRtf[1] == '\'') {
+ char szHex[3] = "\0\0";
+ char szChar[2];
+ szHex[0] = pszRtf[2];
+ if (pszRtf[2])
+ szHex[1] = pszRtf[3];
+ else
+ pszRtf--;
+ szChar[0] = (char)strtol(szHex, NULL, 16); szChar[1] = '\0';
+ if (groupStack[groupLevel].charset) {
+ WCHAR szwChar[2];
+ CHARSETINFO csi;
+ TranslateCharsetInfo((PDWORD)groupStack[groupLevel].charset, &csi, TCI_SRCCHARSET);
+ MultiByteToWideChar(csi.ciACP, 0, szChar, 1, szwChar, 2);
+ AppendToCharBuffer(output, "&#%u;", (WORD)szwChar[0]);
+ }
+ else
+ AppendToCharBuffer(output, "&#%u;", (BYTE)szChar[0]);
+ pszRtf += 4;
+ }
+ else if (*pszRtf == '\\' && isalpha(pszRtf[1])) {
+ char szControlWord[32];
+ int iWordEnd;
+ int hasParam = 0;
+ int param = -1;
+
+ for (iWordEnd = 1; isalpha(pszRtf[iWordEnd]); iWordEnd++);
+ CopyMemory(szControlWord, pszRtf + 1, min(sizeof(szControlWord), iWordEnd));
+ szControlWord[min(sizeof(szControlWord), iWordEnd) - 1] = '\0';
+ if (isdigit(pszRtf[iWordEnd]) || pszRtf[iWordEnd] == '-') {
+ hasParam = 1;
+ param = strtol(pszRtf + iWordEnd, &pszRtf, 10);
+ }
+ else
+ pszRtf = pszRtf + iWordEnd;
+ if (*pszRtf == ' ')
+ pszRtf++;
+ if (!lstrcmpiA(szControlWord, "colortbl")) {
+ groupStack[groupLevel].isColourTbl = 1;
+ buf = (COLORREF*)mir_realloc(colourTbl, sizeof(COLORREF));
+ if (buf != NULL) {
+ colourTbl = (COLORREF*)buf;
+ colourTblCount = 1;
+ colourTbl[0] = 0;
+ }
+ groupStack[groupLevel].isDestination = 1;
+ }
+ else if (!lstrcmpiA(szControlWord, "fonttbl")) {
+ groupStack[groupLevel].isFontTbl = 1;
+ groupStack[groupLevel].isDestination = 1;
+ }
+ else if (!lstrcmpiA(szControlWord, "stylesheet")) {
+ groupStack[groupLevel].isDestination = 1;
+ }
+ else if (!lstrcmpiA(szControlWord, "red")) {
+ if (!hasParam || !colourTblCount)
+ break;
+ colourTbl[colourTblCount - 1] &= ~RGB(255, 0, 0);
+ colourTbl[colourTblCount - 1] |= RGB(param, 0, 0);
+ }
+ else if (!lstrcmpiA(szControlWord, "green")) {
+ if (!hasParam || !colourTblCount)
+ break;
+ colourTbl[colourTblCount - 1] &= ~RGB(0, 255, 0);
+ colourTbl[colourTblCount - 1] |= RGB(0, param, 0);
+ }
+ else if (!lstrcmpiA(szControlWord, "blue")) {
+ if (!hasParam || !colourTblCount)
+ break;
+ colourTbl[colourTblCount - 1] &= ~RGB(0, 0, 255);
+ colourTbl[colourTblCount - 1] |= RGB(0, 0, param);
+ }
+ else if (!lstrcmpiA(szControlWord, "f")) {
+ if (groupStack[groupLevel].isFontTbl) {
+ buf = (int*)mir_realloc(fontTblCharsets, sizeof(int)*(fontTblCount + 1));
+ if (buf != NULL) {
+ fontTblCharsets = (int*)buf;
+ fontTblCharsets[fontTblCount] = 0;
+ fontTblCount++;
+ }
+ }
+ else {
+ if (hasParam && param >= 0 && param<fontTblCount)
+ groupStack[groupLevel].charset = fontTblCharsets[param];
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "fcharset")) {
+ if (groupStack[groupLevel].isFontTbl && fontTblCount && hasParam)
+ fontTblCharsets[fontTblCount - 1] = param;
+ }
+ else if (!lstrcmpiA(szControlWord, "cf")) {
+ if (inFontTag)
+ AppendToCharBuffer(output, "</font>");
+ if (hasParam && param) {
+ int i;
+ char szColour[7];
+ wsprintfA(szColour, "%02x%02x%02x", GetRValue(colourTbl[param]), GetGValue(colourTbl[param]), GetBValue(colourTbl[param]));
+ for (i = 0; i < sizeof(htmlColourNames); i++) {
+ if (!lstrcmpiA(szColour, htmlColourNames[i].szClr)) {
+ AppendToCharBuffer(output, "<font color=\"%s\">", htmlColourNames[i].szName);
+ break;
+ }
+ }
+ if (i == sizeof(htmlColourNames))
+ AppendToCharBuffer(output, "<font color=\"#%s\">", szColour);
+ inFontTag = 1;
+ groupStack[groupLevel].colour = param;
+ }
+ else
+ groupStack[groupLevel].colour = 0;
+ }
+ else if (!lstrcmpiA(szControlWord, "fs")) {
+ if (normalTextSize == 0 && hasParam) {
+ normalTextSize = param;
+ groupStack[0].fontSize = normalTextSize;
+ }
+ if (inBigTag) {
+ AppendToCharBuffer(output, "</big>");
+ inBigTag = 0;
+ }
+ if (inSmallTag) {
+ AppendToCharBuffer(output, "</small>");
+ inSmallTag = 0;
+ }
+ if (hasParam) {
+ groupStack[groupLevel].fontSize = param;
+ if (groupStack[groupLevel].fontSize<normalTextSize) {
+ AppendToCharBuffer(output, "<small>"); inSmallTag = 1;
+ }
+ else if (groupStack[groupLevel].fontSize>normalTextSize) {
+ AppendToCharBuffer(output, "<big>"); inBigTag = 1;
+ }
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "uc")) {
+ if (hasParam)
+ groupStack[groupLevel].unicodeSkip = param;
+ }
+ else if (!lstrcmpiA(szControlWord, "u")) {
+ if (hasParam) {
+ AppendToCharBuffer(output, "&#%u;", param);
+ pszRtf += groupStack[groupLevel].unicodeSkip;
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "b")) {
+ if (!hasParam || param) {
+ groupStack[groupLevel].bold = 1;
+ AppendToCharBuffer(output, "<b>");
+ }
+ else {
+ groupStack[groupLevel].bold = 0;
+ AppendToCharBuffer(output, "</b>");
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "i")) {
+ if (!hasParam || param) {
+ groupStack[groupLevel].italic = 1;
+ AppendToCharBuffer(output, "<i>");
+ }
+ else {
+ groupStack[groupLevel].italic = 0;
+ AppendToCharBuffer(output, "</i>");
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "ul")) {
+ if (!hasParam || param) {
+ groupStack[groupLevel].underline = 1;
+ AppendToCharBuffer(output, "<u>");
+ }
+ else {
+ groupStack[groupLevel].underline = 0;
+ AppendToCharBuffer(output, "</u>");
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "ulnone")) {
+ groupStack[groupLevel].underline = 0;
+ AppendToCharBuffer(output, "</u>");
+ }
+ else if (!lstrcmpiA(szControlWord, "strike")) {
+ if (!hasParam || param) {
+ groupStack[groupLevel].strikeout = 1;
+ mir_free(hyperlink.sz); // does NULL check
+ hyperlink.iEnd = hyperlink.cbAlloced = 0;
+ hyperlink.sz = NULL;
+ output = &hyperlink;
+ }
+ else {
+ groupStack[groupLevel].strikeout = 0;
+ if (hyperlink.iEnd && hyperlink.sz != NULL) {
+ char *pszColon;
+ output = &htmlOut;
+ pszColon = strchr(hyperlink.sz, ':');
+ if (pszColon == NULL)
+ pszColon = "";
+ else
+ *pszColon++ = '\0';
+ AppendToCharBuffer(output, "<a href=\"%s\">%s</a>", pszColon, hyperlink.sz);
+ mir_free(hyperlink.sz);
+ hyperlink.iEnd = hyperlink.cbAlloced = 0;
+ hyperlink.sz = NULL;
+ }
+ }
+ }
+ else if (!lstrcmpiA(szControlWord, "par")) {
+ if (lineBreakBefore)
+ AppendToCharBuffer(output, "<br>");
+ lineBreakBefore = 1; // richedit puts a \par right at the end
+ }
+ }
+ else {
+ int i;
+ if (*pszRtf == '\\')
+ pszRtf++;
+ if (!groupStack[groupLevel].isDestination) {
+ if (lineBreakBefore && (BYTE)*pszRtf >= ' ') {
+ AppendToCharBuffer(output, "<br>");
+ lineBreakBefore = 0;
+ }
+ if (*pszRtf == ' ')
+ AppendCharToCharBuffer(output, *pszRtf);
+ else {
+ for (i = 0; i < sizeof(htmlSymbolChars); i++) {
+ if (*pszRtf == htmlSymbolChars[i].ch) {
+ AppendToCharBuffer(output, "&%s;", htmlSymbolChars[i].szSym);
+ break;
+ }
+ }
+ if (i == sizeof(htmlSymbolChars))
+ AppendCharToCharBuffer(output, *pszRtf);
+ }
+ }
+ else if (groupStack[groupLevel].isColourTbl && *pszRtf == ';') {
+ buf = (COLORREF*)mir_realloc(colourTbl, sizeof(COLORREF)*(colourTblCount + 2));
+ if (buf != NULL) {
+ colourTbl = (COLORREF*)buf;
+ colourTbl[colourTblCount] = 0;
+ colourTblCount++;
+ }
+ }
+ pszRtf++;
+ }
+ }
+ mir_free(groupStack);
+ }
+ mir_free(colourTbl); // does NULL check
+ mir_free(fontTblCharsets); // does NULL check
+ mir_free(hyperlink.sz); // does NULL check
+
+ mir_free(esd.pbBuff);
+ return htmlOut.sz;
+}
+#endif // defined EDITOR
diff --git a/plugins/ContextHelp/src/utils.cpp b/plugins/ContextHelp/src/utils.cpp
new file mode 100644
index 0000000000..51d643c774
--- /dev/null
+++ b/plugins/ContextHelp/src/utils.cpp
@@ -0,0 +1,430 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "stdafx.h"
+
+#include <m_button.h>
+
+#define CHARBUFFER_ALLOCSTEP 512 // allocated buffer increase in bytes
+
+#ifdef EDITOR
+extern const TCHAR *szControlTypeNames[] = {
+ _T("Unknown"), _T("Dialog Box"), _T("Button"), _T("Check Box"), _T("Radio Button"),
+ _T("Text"), _T("Image"), _T("Edit Box"), _T("Group Box"), _T("Combo Box"),
+ _T("List Box"), _T("Spin Edit Box"), _T("Progress Bar"), _T("Slider"), _T("List View"),
+ _T("Tree View"), _T("Date/Time Picker"), _T("IP Address"), _T("Status Bar"), _T("Hyperlink"),
+ _T("Contact List"), _T("Scroll Bar"), _T("Animation"), _T("Hotkey"), _T("Tabs"),
+ _T("Colour Picker"), _T("Tool Bar"), _T("Combo Edit Box"), _T("Size Grip") };
+#endif
+
+int GetControlType(HWND hwndCtl)
+{
+ TCHAR szClassName[32];
+ DWORD style;
+
+ if (GetClassLong(hwndCtl, GCW_ATOM) == 32770)
+ return CTLTYPE_DIALOG;
+ if (!GetClassName(hwndCtl, szClassName, sizeof(szClassName)))
+ return CTLTYPE_UNKNOWN;
+ if (!lstrcmpi(szClassName, _T("MDIClient")))
+ return CTLTYPE_DIALOG;
+ else if (!lstrcmpi(szClassName, _T("Static"))) {
+ if (GetClassName(GetParent(hwndCtl), szClassName, sizeof(szClassName)) && !lstrcmpi(szClassName, _T("ComboBox")) || !lstrcmpi(szClassName, WC_COMBOBOXEX))
+ return CTLTYPE_COMBO;
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ switch (style&SS_TYPEMASK) {
+ case SS_BITMAP:
+ case SS_BLACKFRAME:
+ case SS_BLACKRECT:
+ case SS_ENHMETAFILE:
+ case SS_ETCHEDFRAME:
+ case SS_ETCHEDHORZ:
+ case SS_ETCHEDVERT:
+ case SS_WHITEFRAME:
+ case SS_WHITERECT:
+ case SS_GRAYFRAME:
+ case SS_GRAYRECT:
+ case SS_ICON:
+ case SS_OWNERDRAW:
+ return CTLTYPE_IMAGE;
+ }
+ return CTLTYPE_TEXT;
+ }
+ else if (GetClassLong(hwndCtl, GCW_ATOM) == 32772) // WinNT/2000/XP: icon titles
+ return CTLTYPE_IMAGE; // class="#32772"
+ else if (!lstrcmpi(szClassName, _T("Button"))) {
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ switch (style & 0x24) {
+ case BS_CHECKBOX:
+ case BS_AUTOCHECKBOX:
+ case BS_3STATE:
+ case BS_AUTO3STATE:
+ if (style&BS_PUSHLIKE)
+ break;
+ return CTLTYPE_CHECKBOX;
+ case BS_RADIOBUTTON:
+ case BS_AUTORADIOBUTTON:
+ if (style&BS_PUSHLIKE)
+ break;
+ return CTLTYPE_RADIO;
+ case BS_GROUPBOX:
+ return CTLTYPE_GROUP;
+ }
+ return CTLTYPE_BUTTON;
+ }
+ else if (!lstrcmpi(szClassName, MIRANDABUTTONCLASS))
+ return CTLTYPE_BUTTON;
+ else if (!lstrcmpi(szClassName, _T("Edit"))) {
+ if (GetClassName(GetParent(hwndCtl), szClassName, sizeof(szClassName)) && !lstrcmpi(szClassName, _T("ComboBox")))
+ return CTLTYPE_COMBO;
+ if (GetClassName(GetWindow(hwndCtl, GW_HWNDNEXT), szClassName, sizeof(szClassName)) && !lstrcmpi(szClassName, UPDOWN_CLASS))
+ if ((HWND)SendMessage(GetWindow(hwndCtl, GW_HWNDNEXT), UDM_GETBUDDY, 0, 0) == hwndCtl)
+ return CTLTYPE_SPINEDIT;
+ return CTLTYPE_EDIT;
+ }
+ else if (!_tcsnicmp(szClassName, _T("RichEdit"), 8))
+ return CTLTYPE_EDIT; // RICHEDIT,RichEdit20A,RichEdit20W,RichEdit50W and future versions
+ else if (!lstrcmpi(szClassName, _T("ListBox"))) {
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ if (style&LBS_COMBOBOX)
+ return CTLTYPE_COMBO;
+ return CTLTYPE_LIST;
+ }
+ else if (!lstrcmpi(szClassName, _T("ComboLBox")) || !lstrcmpi(szClassName, _T("ComboBox")) || !lstrcmpi(szClassName, WC_COMBOBOXEX))
+ return CTLTYPE_COMBO;
+ else if (!lstrcmpi(szClassName, _T("ScrollBar"))) {
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ if (style&SBS_SIZEBOX)
+ return CTLTYPE_SIZEGRIP;
+ return CTLTYPE_SCROLL;
+ }
+ else if (!lstrcmpi(szClassName, WC_PAGESCROLLER))
+ return CTLTYPE_SCROLL;
+ else if (!lstrcmpi(szClassName, UPDOWN_CLASS)) {
+ if (GetClassName((HWND)SendMessage(hwndCtl, UDM_GETBUDDY, 0, 0), szClassName, sizeof(szClassName)) && !lstrcmpi(szClassName, _T("Edit")))
+ return CTLTYPE_SPINEDIT;
+ return CTLTYPE_SCROLL;
+ }
+ else if (!lstrcmpi(szClassName, PROGRESS_CLASS))
+ return CTLTYPE_PROGRESS;
+ else if (!lstrcmpi(szClassName, TRACKBAR_CLASS))
+ return CTLTYPE_SLIDER;
+ else if (!lstrcmpi(szClassName, WC_LISTVIEW) || !lstrcmpi(szClassName, WC_HEADER))
+ return CTLTYPE_LISTVIEW;
+ else if (!lstrcmpi(szClassName, WC_TREEVIEW))
+ return CTLTYPE_TREEVIEW;
+ else if (!lstrcmpi(szClassName, DATETIMEPICK_CLASS) || !lstrcmpi(szClassName, MONTHCAL_CLASS))
+ return CTLTYPE_DATETIME;
+ else if (!lstrcmpi(szClassName, WC_IPADDRESS))
+ return CTLTYPE_IP;
+ else if (!lstrcmpi(szClassName, STATUSCLASSNAME))
+ return CTLTYPE_STATUSBAR;
+ else if (!lstrcmpi(szClassName, (LPCWSTR)CLISTCONTROL_CLASS))
+ return CTLTYPE_CLC; // look at !! casting
+ else if (!lstrcmpi(szClassName, WNDCLASS_HYPERLINK) || !lstrcmpi(szClassName, _T("SysLink")))
+ return CTLTYPE_HYPERLINK;
+ else if (!lstrcmpi(szClassName, ANIMATE_CLASS))
+ return CTLTYPE_ANIMATION;
+ else if (!lstrcmpi(szClassName, HOTKEY_CLASS))
+ return CTLTYPE_HOTKEY;
+ else if (!lstrcmpi(szClassName, WC_TABCONTROL))
+ return CTLTYPE_TABS;
+ else if (!lstrcmpi(szClassName, (LPCWSTR)WNDCLASS_COLOURPICKER))
+ return CTLTYPE_COLOUR; // look at !! casting
+ else if (!lstrcmpi(szClassName, TOOLBARCLASSNAME) || !lstrcmpi(szClassName, REBARCLASSNAME))
+ return CTLTYPE_TOOLBAR;
+ switch (SendMessage(hwndCtl, WM_GETDLGCODE, 0, (LPARAM)NULL) & 0x2070) {
+ case DLGC_RADIOBUTTON:
+ return CTLTYPE_RADIO;
+ case DLGC_DEFPUSHBUTTON:
+ case DLGC_UNDEFPUSHBUTTON:
+ case DLGC_BUTTON:
+ return CTLTYPE_BUTTON;
+ }
+ hwndCtl = GetWindow(hwndCtl, GW_CHILD); // check for owner-extended control
+ if (hwndCtl != NULL)
+ return GetControlType(hwndCtl);
+
+ return CTLTYPE_UNKNOWN;
+}
+
+HWND GetControlDialog(HWND hwndCtl)
+{
+ TCHAR szClassName[32];
+ while (hwndCtl != NULL) {
+ if (GetClassLong(hwndCtl, GCW_ATOM) == 32770)
+ return hwndCtl;
+ if (GetClassName(hwndCtl, szClassName, sizeof(szClassName)))
+ if (!lstrcmpi(szClassName, _T("MDIClient")))
+ return hwndCtl;
+ hwndCtl = GetParent(hwndCtl);
+ }
+
+ return hwndCtl;
+}
+
+// never fails
+int GetControlTitle(HWND hwndCtl, TCHAR *pszTitle, int cchTitle)
+{
+ TCHAR *p;
+ int res = 0;
+ if (cchTitle)
+ pszTitle[0] = _T('\0');
+ switch (GetControlType(hwndCtl)) {
+ case CTLTYPE_DIALOG:
+ case CTLTYPE_BUTTON:
+ case CTLTYPE_CHECKBOX:
+ case CTLTYPE_RADIO:
+ case CTLTYPE_GROUP:
+ case CTLTYPE_TEXT:
+ case CTLTYPE_HYPERLINK:
+ res = GetWindowText(hwndCtl, pszTitle, cchTitle);
+ }
+ hwndCtl = GetWindow(hwndCtl, GW_HWNDPREV);
+ if (hwndCtl)
+ switch (GetControlType(hwndCtl)) {
+ case CTLTYPE_TEXT:
+ case CTLTYPE_GROUP:
+ res = GetWindowText(hwndCtl, pszTitle, cchTitle);
+ }
+ if (res)
+ for (p = pszTitle; *p != 0; p++) {
+ // strip-off ampersand (&) prefix character
+ if (*p == _T('&') && *(p + 1) != _T('&')) {
+ MoveMemory(p, p + 1, (lstrlen(p + 1) + 1)*sizeof(TCHAR));
+ res--;
+ if (*(p + 1) == 0)
+ break;
+ }
+ // strip-off last ':'
+ if (*p == _T(':') && *(p + 1) == 0) {
+ *p = 0;
+ res--;
+ break;
+ }
+ }
+
+ return res;
+}
+
+// mir_free() the return value
+char *GetControlModuleName(HWND hwndCtl)
+{
+ char szModule[512], szMainModule[512];
+ char *pszFile, *buf;
+
+ if (!GetModuleFileNameA(NULL, szMainModule, sizeof(szMainModule)))
+ return NULL;
+ buf = strrchr(szMainModule, '\\');
+ if (buf != NULL)
+ *buf = '\0';
+ else
+ buf = szMainModule;
+
+ do {
+ if (!GetModuleFileNameA((HINSTANCE)GetWindowLongPtr(hwndCtl, GWLP_HINSTANCE), szModule, sizeof(szModule)))
+ return NULL;
+ pszFile = strrchr(szModule, '\\');
+ if (pszFile != NULL) {
+ *pszFile = '\0';
+ pszFile++;
+ }
+ else
+ pszFile = szModule;
+ if (lstrlenA(szModule)>lstrlenA(szMainModule))
+ szModule[lstrlenA(szMainModule)] = '\0';
+ if (!lstrcmpiA(szModule, szMainModule))
+ break; // found miranda module
+ hwndCtl = GetParent(hwndCtl);
+ } while (hwndCtl != NULL);
+
+ buf = strrchr(pszFile, '.');
+ if (buf != NULL)
+ *buf++ = '\0';
+
+ return mir_strdup(pszFile);
+}
+
+
+struct CreateControlIdData {
+ int id;
+ HWND hwndCtl;
+};
+
+static BOOL CALLBACK CreateCtlIdEnumProc(HWND hwnd, LPARAM lParam)
+{
+ struct CreateControlIdData* ccid = (struct CreateControlIdData*)lParam;
+ TCHAR szClassName[32];
+ if (GetClassLong(hwnd, GCW_ATOM) == 32770) // class="#32770"
+ return TRUE;
+ if (GetClassName(hwnd, szClassName, sizeof(szClassName)))
+ if (!lstrcmpi(szClassName, _T("MDIClient")))
+ return TRUE;
+ if (GetWindowLongPtr(hwnd, GWL_ID) <= 0 || GetWindowLongPtr(hwnd, GWL_ID) == 0xFFFF)
+ ccid->id--;
+ if (hwnd == ccid->hwndCtl)
+ ccid->hwndCtl = NULL;
+
+ return ccid->hwndCtl != NULL;
+}
+
+int GetControlID(HWND hwndCtl)
+{
+ struct CreateControlIdData ccid;
+ TCHAR szClassName[32];
+
+ // obey context ID when set (rarely)
+ ccid.id = GetWindowContextHelpId(hwndCtl);
+ if (ccid.id != 0)
+ return ccid.id;
+
+ if (GetClassName(hwndCtl, szClassName, sizeof(szClassName))) {
+ if (!lstrcmpi(szClassName, UPDOWN_CLASS)) { // handle spinner controls as a whole
+ DWORD style;
+ HWND hwndBuddy;
+ style = GetWindowLongPtr(hwndCtl, GWL_STYLE);
+ if (style&UDS_ALIGNRIGHT || style&UDS_ALIGNLEFT) {
+ hwndBuddy = (HWND)SendMessage(hwndCtl, UDM_GETBUDDY, 0, 0);
+ if (hwndBuddy != NULL)
+ hwndCtl = hwndBuddy;
+ }
+ }
+ else if (GetClassLong(hwndCtl, GCW_ATOM) == 32770 || !lstrcmpi(szClassName, _T("MDIClient")))
+ return 0; // ensure this is always unset
+ }
+ ccid.id = GetWindowLongPtr(hwndCtl, GWL_ID);
+ if (ccid.id <= 0 || ccid.id == 0xFFFF) {
+ ccid.id = -1;
+ ccid.hwndCtl = hwndCtl;
+ EnumChildWindows(GetParent(hwndCtl), CreateCtlIdEnumProc, (LPARAM)&ccid);
+ if (ccid.hwndCtl != NULL)
+ return -1;
+ }
+ return ccid.id;
+}
+
+// mir_free() the return value
+static char *Base64Encode(PBYTE pBuf, int cbBuf)
+{
+ /*NETLIBBASE64 nlb64;
+ nlb64.pbDecoded=pBuf;
+ nlb64.cbDecoded=cbBuf;
+ nlb64.cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded);
+ nlb64.pszEncoded=(char*)mir_alloc(nlb64.cchEncoded);
+ if(nlb64.pszEncoded==NULL || !CallService(MS_NETLIB_BASE64ENCODE,0,(LPARAM)&nlb64)) {
+ mir_free(nlb64.pszEncoded); // does NULL check
+ return NULL;
+ }
+ return nlb64.pszEncoded;*/
+
+ return (char*)mir_base64_encode(pBuf, cbBuf);
+}
+
+struct CreateDialogIdBinaryData {
+ int alloced, count;
+ PBYTE buf;
+ HWND hwndParent;
+};
+
+static INT_PTR CALLBACK CreateDlgIdBinEnumProc(HWND hwnd, LPARAM lParam)
+{
+ struct CreateDialogIdBinaryData *cdib = (struct CreateDialogIdBinaryData*)lParam;
+ int type;
+
+ if (GetParent(hwnd) != cdib->hwndParent)
+ return TRUE;
+ type = GetControlType(hwnd);
+ if (type == CTLTYPE_DIALOG || type == CTLTYPE_TEXT || type == CTLTYPE_GROUP)
+ return TRUE;
+ if (cdib->count + 3>cdib->alloced) {
+ PBYTE buf2;
+ buf2 = (PBYTE)mir_realloc(cdib->buf, cdib->alloced + 32);
+ if (buf2 == NULL)
+ return FALSE;
+ cdib->alloced += 32;
+ cdib->buf = buf2;
+ }
+ cdib->buf[cdib->count] = (BYTE)type;
+ *(PWORD)(cdib->buf + cdib->count + 1) = (WORD)GetWindowLongPtr(hwnd, GWL_ID);
+ cdib->count += 3;
+
+ return TRUE;
+}
+
+// mir_free() the return value
+char *CreateDialogIdString(HWND hwndDlg)
+{
+ struct CreateDialogIdBinaryData cdib;
+ char *szRet;
+
+ ZeroMemory(&cdib, sizeof(cdib));
+ if (hwndDlg == NULL)
+ return NULL;
+ cdib.hwndParent = hwndDlg;
+ EnumChildWindows(hwndDlg, (WNDENUMPROC)CreateDlgIdBinEnumProc, (LPARAM)&cdib);
+ if (cdib.buf == NULL)
+ return NULL;
+ szRet = Base64Encode(cdib.buf, cdib.count);
+ mir_free(cdib.buf);
+
+ return szRet;
+}
+
+
+void AppendCharToCharBuffer(struct ResizableCharBuffer *rcb, char c)
+{
+ if (rcb->cbAlloced <= rcb->iEnd + 1) {
+ char* buf = (char*)mir_realloc(rcb->sz, (rcb->cbAlloced + CHARBUFFER_ALLOCSTEP));
+ if (buf == NULL)
+ return;
+ rcb->sz = buf;
+ rcb->cbAlloced += CHARBUFFER_ALLOCSTEP;
+ }
+ rcb->sz[rcb->iEnd++] = c;
+ rcb->sz[rcb->iEnd] = '\0';
+}
+
+void AppendToCharBuffer(struct ResizableCharBuffer *rcb, const char *fmt, ...)
+{
+ va_list va;
+ int charsDone;
+ char *buf;
+
+ if (rcb->cbAlloced == 0) {
+ buf = (char*)mir_alloc(CHARBUFFER_ALLOCSTEP);
+ if (buf == NULL)
+ return;
+ rcb->sz = buf;
+ rcb->cbAlloced = CHARBUFFER_ALLOCSTEP;
+ }
+ va_start(va, fmt);
+ for (;;) {
+ charsDone = mir_vsnprintf(rcb->sz + rcb->iEnd, rcb->cbAlloced - rcb->iEnd, fmt, va);
+ if (charsDone >= 0)
+ break; // returns -1 when buffer not large enough
+ buf = (char*)mir_realloc(rcb->sz, rcb->cbAlloced + CHARBUFFER_ALLOCSTEP);
+ if (buf == NULL) {
+ charsDone = 0;
+ break;
+ }
+ rcb->sz = buf;
+ rcb->cbAlloced += CHARBUFFER_ALLOCSTEP;
+ }
+ va_end(va);
+ rcb->iEnd += charsDone;
+}
diff --git a/plugins/ContextHelp/src/version.h b/plugins/ContextHelp/src/version.h
new file mode 100644
index 0000000000..a1007a2174
--- /dev/null
+++ b/plugins/ContextHelp/src/version.h
@@ -0,0 +1,53 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+#define NEEDED_MIRANDA_VERSION PLUGIN_MAKE_VERSION(0,6,0,0)
+#define NEEDED_MIRANDA_VERSION_STR "0.6"
+#define PLUGIN_VERSION PLUGIN_MAKE_VERSION(0,2,1,2)
+#define FILE_VERSION 0,2,1,2
+
+#ifdef _DEBUG
+ #define FILE_VERSION_STR "0.2.1.3 alpha"
+ #define USERAGENT_VERSION "0.2.1.3 (alpha)"
+#else
+ #define FILE_VERSION_STR "0.2.1.2"
+ #define USERAGENT_VERSION "0.2.1.2"
+#endif
+
+#define PLUGIN_EMAIL "hrathh users.sourceforge.net"
+#define PLUGIN_EMAIL_ATT_POS 7 // position of the @-sign in the email adress above
+
+ #define PLUGIN_WEBSITE "http://addons.miranda-im.org/details.php?action=viewfile&id=3475"
+*/
+
+#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 2
+#define __RELEASE_NUM 1
+#define __BUILD_NUM 2
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "Context help"
+#define __FILENAME "ContextHelp.dll"
+#define __DESCRIPTION "Provides context sensitive help in all of the Miranda NG dialog boxes."
+#define __AUTHOR "Richard Hughes, H. Herkenrath, Miranda NG Team"
+#define __AUTHOREMAIL ""
+#define __AUTHORWEB "http://miranda-ng.org/p/ContextHelp/"
+#define __COPYRIGHT "© 2002 Richard Hughes, 2005-2007 H. Herkenrath, 2016-16 Miranda NG Team"