summaryrefslogtreecommitdiff
path: root/plugins/NotesAndReminders
diff options
context:
space:
mode:
authorKirill Volinsky <mataes2007@gmail.com>2012-07-10 18:21:45 +0000
committerKirill Volinsky <mataes2007@gmail.com>2012-07-10 18:21:45 +0000
commit6f8361aaf17045ff81149eeb22ed0a15b4d4ad94 (patch)
treefa77a5262c03a780b6b8b3e528fc815c1c6c0acf /plugins/NotesAndReminders
parente4ff24d8abc2af58b91585bc3ce221214d5a148b (diff)
added Notes&Reminders plugin
git-svn-id: http://svn.miranda-ng.org/main/trunk@891 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/NotesAndReminders')
-rw-r--r--plugins/NotesAndReminders/N&R - langpack.txt169
-rw-r--r--plugins/NotesAndReminders/N&R - readme.txt315
-rw-r--r--plugins/NotesAndReminders/NotesReminders.vcxproj125
-rw-r--r--plugins/NotesAndReminders/NotesReminders.vcxproj.filters64
-rw-r--r--plugins/NotesAndReminders/globals.h147
-rw-r--r--plugins/NotesAndReminders/hotkeys.cpp137
-rw-r--r--plugins/NotesAndReminders/icons/addremin.icobin0 -> 1406 bytes
-rw-r--r--plugins/NotesAndReminders/icons/bringfront.icobin0 -> 1406 bytes
-rw-r--r--plugins/NotesAndReminders/icons/captioni.icobin0 -> 766 bytes
-rw-r--r--plugins/NotesAndReminders/icons/captionn.icobin0 -> 766 bytes
-rw-r--r--plugins/NotesAndReminders/icons/deleteic.icobin0 -> 1406 bytes
-rw-r--r--plugins/NotesAndReminders/icons/deletere.icobin0 -> 1406 bytes
-rw-r--r--plugins/NotesAndReminders/icons/hidenote.icobin0 -> 766 bytes
-rw-r--r--plugins/NotesAndReminders/icons/noteicon.icobin0 -> 1406 bytes
-rw-r--r--plugins/NotesAndReminders/icons/playsound.icobin0 -> 318 bytes
-rw-r--r--plugins/NotesAndReminders/icons/reminder.icobin0 -> 1406 bytes
-rw-r--r--plugins/NotesAndReminders/icons/removeno.icobin0 -> 766 bytes
-rw-r--r--plugins/NotesAndReminders/icons/showhide.icobin0 -> 1406 bytes
-rw-r--r--plugins/NotesAndReminders/icons/viewnotes.icobin0 -> 3638 bytes
-rw-r--r--plugins/NotesAndReminders/icons/viewremi.icobin0 -> 3638 bytes
-rw-r--r--plugins/NotesAndReminders/main.cpp389
-rw-r--r--plugins/NotesAndReminders/miscutils.cpp246
-rw-r--r--plugins/NotesAndReminders/miscutils.h47
-rw-r--r--plugins/NotesAndReminders/notes.cpp2180
-rw-r--r--plugins/NotesAndReminders/options.cpp637
-rw-r--r--plugins/NotesAndReminders/reminders.cpp2890
-rw-r--r--plugins/NotesAndReminders/resource.h65
-rw-r--r--plugins/NotesAndReminders/resource.rc362
28 files changed, 7773 insertions, 0 deletions
diff --git a/plugins/NotesAndReminders/N&R - langpack.txt b/plugins/NotesAndReminders/N&R - langpack.txt
new file mode 100644
index 0000000000..ebb446c99b
--- /dev/null
+++ b/plugins/NotesAndReminders/N&R - langpack.txt
@@ -0,0 +1,169 @@
+Miranda Language Pack Version 1
+Language: English (EN)
+Locale: 0409
+Last-Modified-Using: Miranda 0.9.2
+Authors: d00mEr, Joe @ Whale, Georg Fischer
+Author-email: d00mEr@dir.bg, jokusoftware@gmail.com
+Plugins-included: Notes & Reminders 0.0.5.0
+
+[&Close]
+
+;Name In Options Page
+;--------------------
+[Notes & Reminders]
+[Plugins]
+[Alerts]
+[Reminder Notify]
+
+;Menu Strings
+;------------
+[Notes && Reminders]
+[&New Note]
+[&Show / Hide Notes]
+[Vie&w Notes]
+[Delete &All Notes]
+[New &Reminder]
+[&View Reminders]
+[D&elete All Reminders]
+[&Bring All to Front]
+
+;Hotkeys
+;-------
+[Bring All Notes to Front]
+[Toggle Notes Visibility]
+
+;Sounds
+;------
+[Reminder triggered]
+[Reminder triggered (Alternative 1)]
+[Reminder triggered (Alternative 2)]
+
+;Confirmation and Message Dialogs
+---------------------------------
+[Are you sure you want to delete all notes?]
+[Are you sure you want to delete this note?]
+[Are you sure you want to delete all reminders?]
+[Are you sure you want to delete this reminder?]
+[The specified time is invalid.]
+[The specified time offset is invalid.]
+[The specified time is invalid due to begin of daylight saving (summer time).]
+[Miranda could not load the Note & Reminders plugin, RICHED20.DLL is missing. If you are using Windows 95 or WINE please make sure you have riched20.dll installed. Press 'Yes' to continue loading Miranda.]
+
+;Note Window Popup Menu
+;----------------------
+[&Delete Note]
+[&Hide Note]
+[&Always On Top]
+[Appearance]
+[Background Color]
+[Text Color]
+[Font]
+[Custom...]
+[Reset]
+[Paste Title]
+[Reset Title]
+[Undo]
+[&Copy]
+[&Paste]
+[C&ut]
+[C&lear]
+
+;Note Window Popup Menu Color Presets
+-------------------------------------
+[Black]
+[Maroon]
+[Green]
+[Olive]
+[Navy]
+[Purple]
+[Teal]
+[Gray]
+[Silver]
+[Red]
+[Orange]
+[Lime]
+[Yellow]
+[Blue]
+[Fuchsia]
+[Aqua]
+[White]
+
+;Notes List Dialog and Popup Menu
+;-----------------------------------
+[Notes]
+[Date/Title]
+[Note text]
+[V]
+[T]
+[Edi&t Note]
+[&Visible]
+[Always &On Top]
+
+;New Reminder Dialog
+;-------------------
+[&Add Reminder]
+[&Update Reminder]
+[Add Reminder]
+[Trigger On Time && Date:]
+[Date]
+[Time]
+[Reminder Note:]
+[Reoccurence]
+[None]
+[Daily]
+[Weekly]
+[Monthly]
+[Minutes]
+[Hours]
+[Every]
+[Seconds]
+[Default]
+[Alternative 1]
+[Alternative 2]
+[Never]
+[None]
+
+;Reminder List Dialog and Popup Menu
+;-----------------------------------
+[Reminders]
+[Date of activation]
+[Reminder text]
+[Add New]
+[Edi&t Reminder]
+[&Delete Reminder]
+
+;Reminder Notify Dialog
+;----------------------
+[&Dismiss]
+[&Remind Again]
+[Reminder]
+[min.]
+[After:]
+[On Time && Date:]
+[Hour]
+[Day]
+[Days]
+[Week]
+
+;Options Dialog
+;--------------
+[Note Transparency (Minimum Win 2k/XP)]
+[Default Note Size]
+[Width (Pixels)]
+[Height (Pixels)]
+[Startup options]
+[Reset to defaults]
+[Hide notes at startup]
+[Add Contact list menu items]
+[(Requires Miranda restart)]
+[Send Reminders through E-mail / SMS :]
+[Open links with this program instead:]
+[Select Executable]
+[Show Scrollbar]
+[Show Buttons]
+[Title Date]
+[Title Time]
+[Reminder Options]
+[Add Reminder closes dialog]
+[Use MCI to play alert sounds]
+[(Using MCI allows different sounds to be played simultaneously, may not be needed or desired when a custom sound plugin is installed.)]
diff --git a/plugins/NotesAndReminders/N&R - readme.txt b/plugins/NotesAndReminders/N&R - readme.txt
new file mode 100644
index 0000000000..30ced4b8f1
--- /dev/null
+++ b/plugins/NotesAndReminders/N&R - readme.txt
@@ -0,0 +1,315 @@
+About
+-----
+New "Notes & Reminders" Plugin
+
+Version 0.0.5.0
+Updated by Georg Fischer for Miranda IM 0.9.2+ (may work on earlier versions
+possibly down to 0.7.0 but not tested) and VS2008, based on code:
+
+by Joe @ Whale (Joe Kucera) jokusoftware@gmail.com
+Originally by d00mEr (Lubomir Ivanov) d00mEr@dir.bg
+for Miranda ICQ 0.1.2.1+ written with Visual C++ 6.0 IMPORTANT: When upgrading to 0.0.5.0 (from 0.0.4.5-), it's recommended to make a backup of the Miranda DB file, because the format for notes and reminder DB data has changed. Allthough the new version should be able to read the old format, the old plugin version will not be able to read the new format, in case you need to downgrade plugin version again.
+Description
+-----------
+This plugin allows user to create Sticky Notes, to
+store some important data in a well visible place :)
+New features allow to create reminders. This is a
+note that will show up on specified date and time.
+The notes are completely customizable.
+
+Features
+--------
++ Sticky Notes (each Note is limited to 16000 chars)
++ Reminders (each Reminder Text is limited to 16000 chars)
++ Reminder time input displays a preset list with times. For the current date (if date is not
+ changed) it displays presets for 5, 10, 15, 30 minutes and then every full and half hour. When
+ the time input is in current date mode, you can also enter a delta time by first entering a +.
+ For example +5 would specify in 5 minutes, +1:15 would specify in 1 hour and 15 minutes.
++ Allow change of fonts and colors
++ Allow setting default size for Notes
++ Notes always stay on top of all windows (unless option is disabled)
++ Show and Hide all notes
++ Show or Hide notes at startup.
++ Bring all notes to front (for notes that are not on top and are hidden behind other windows).
++ Hot Keys for New Note, New Reminder, Show/Hide notes and Bring All Notes to Front
++ Manage Reminder list.
++ Manage Notes list.
++ Quick delete all notes and/or reminders
++ Transparent Notes (on Windows 2000 or newer)
++ Language translations (see langpack_N&R.txt for details)
++ Select between 3 custom sounds to play On Reminder
++ Reminder alert sound can optionally play repeatedly at specified interval until user responds
+ to alert (makes it less likely to miss important reminders)
++ Font Colors
++ Reminder notification via E-mail or SMS.
+
+To Do:
+------
+- Verify langpack for changes and new features introduced in 0.0.5.0
+- User configurable icons through the Miranda icon configuration functionality
+- Icons for : Fix/Move the note
+- Text formatting support for notes
+- "best fit" feature - by pressing one button I would adjust the size of the note automatically
+- double clicking a note's title-bar scrolls up the note (to be just the title bar).
+- import/export of all notes/alarms
+- Reminder (daily, weekly or monthly) without a specified time? i.e. when you start miranda...
+- Trigger on event (remind me when is user online)
+- Unicode support (Use Japanese texts was garbled)
+- Easier way to delete notes (like Ctrl-D or ctrl-shift-d)
+
+Installation
+------------
+Just copy the dll into Miranda's plugin subdirectory.
+
+Translation
+-----------
+see N&R-langpack.txt for details
+
+License
+-------
+Copyright (C) 2002 Lubomir Ivanov
+Copyright (C) 2005 Joe Kucera
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. 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.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+For more information, e-mail jokusoftware@gmail.com
+ d00mEr@dir.bg
+
+Changes
+-------
+0.0.5.1
+-------
+! BugFix: Notes window init issue that could result in scrollbar being shown when it shouldn't.
+! BugFix: Notes window init issue that could result in scrollbar position not being restored correctly
+ for notes that are suposed to be scrolled to top
++ Feature: Option to specify a custom program to use when opening links in notes. By default links
+ are opened using the system default web browser.
++ Feature: Set custom font on individual notes.
+
+-------
+0.0.5.0
+-------
+* (Temporary) Developer: Georg Fischer, I just updated it to work properly with the latest Miranda
+ version and added some features and polish. I don't know if I'll do much more work aside from immediate
+ bug fixes, at least on any regular basis.
+! BugFix: Custom frame drawing for notes to look nicer but also avoid issues especially with Aero.
+! BugFix: Changed window type/parent for notes so that they don't show up on Windows alt-tab list
+ and other issues.
+! BugFix: Cleaned up custom window drawing for notes windows, to hopefully work more solidly.
+! BugFix: Fixed pasting into notes window, so it doesn't paste text formatting (as formatting
+ is not currently supported and would be lost next time Miranda starts).
+! BugFix: Removed reminder reoccurrence dialog options, it would just confuse the user because
+ there is no support for that functionality in the code.
+! BugFix: Time handling reworked, which hopefully also fixes reported issue with reminders triggering at
+ wrong time (an hour early or later). Special handling for daylight saving (summer time) begin
+ and end was also added, which in some cases could lead to some issues on old Windows versions
+ which have outdated being and end dates/times.
+! BugFix: Potential crash bug with reminder list window and empty reminder note.
+! BugFix: Transparency slider (in options) now uses correct direction, previously 100% transparent was
+ actually 100% Opaque. Also clamped the internal max value to avoid 100% transparent windows.
+! BugFix: Creating note from a reminder could result in a crash or corrupted data if note was deleted
+ before the reminder.
+! BugFix: A few other unlisted bug/issue fixes.
++ Change: Changed DB format to be more flexible and easily allow adding features in the future without
+ breaking existing notes and reminders.
++ Change: Changed "Show notes at startup" to "Hide notes at startup" because that better matches
+ actual behavior.
++ Change: Changed the default menu item order to have New Note and New Reminder first as they're the
+ most commonly used commands.
++ Optimization: changed list iterations in various placed to directly iterate linked lists instead
+ of using index based for-loops.
++ Optimization: reminder list sorted by trigger time, which reduces update function to a single
+ check 99.99+% of the time.
++ Feature: Hotkeys, fonts and colors now configurable through the common config interface.
++ Feature: Notes save/restore their current scroll position.
++ Feature: Notes title bar is configurable to select prefered date and time formats or disable them.
++ Feature: Buttons in notes title bar can be disabled (all functions are also available in the context menu).
++ Feature: Bring All Notes to Front command, brings all visible notes in front of other windows
+ (without changing the on-top state).
++ Feature: Tweaked notes window context menu and added a couple of commands.
++ Feature: Option to make "Add Reminder" button in New Reminder dialog close the dialog (previously
+ it was required to click Close after Add Reminder to get rid of the dialog).
++ Feature: Reminder alerts now generate a Miranda system event, meaning it will blink the tray icon
+ which the user has to click in order to open the reminder dialog. Previously the reminder
+ dialog opened automatically which could interfere with the application currently being used.
++ Feature: Delete confirmation dialogs for all notes and reminder delete actions, to avoid accidental
+ deletion.
++ Feature: Reworked reminder date and time input controls. In particularily the time edit control works
+ more like it did in ICQ aswell as allowing user to enter custom values.
++ Feature: Per-reminder option to have alert sound repeat (until reminder event is acknowledged by
+ clicking the tray icon). Repeat interval can be selected between 5 and 60 seconds.
++ Feature: Set custom background and text color on individual notes.
++ Feature: Set custom title/caption on individual notes. (max 63 characters)
++ Feature: Select between 3 sound presets or disable sound completely for individual reminders.
++ Feature: Edit/Update reminders.
++ Feature: Update reminder message in reminder notify dialog, so that the text can be updated
+ before chosing to remind again.
++ Feature: Notes list dialog.
++ Feature: Notes/Reminders list dialogs resizable with persistent window and column sizes.
+
+-------
+0.0.4.5
+-------
+* New Developer: Joe @ Whale, jokusoftware@gmail.com, I took over the development
+ of this great plug-in, got sources of 0.0.4.0 from original author (newer were
+ lost), hopfully managed to add features of succeeding versions.
+! BugFix: Hopefully fixed all crash issues & memory leaks
+! BugFix: Fixed random reminder issue
++ Optimisation: minimised use of memory alloc/free (much faster loading)
++ Feature: reminder sound now configurable thru default Skin/Sounds module (EVents/Sounds)
+
+-------
+0.0.4.2
+-------
+! BugFix: Reminder -> SMS Notify now works.
++ Option: Show vertical scrollbar in Notes.
++ Feature: Create Note from content of Reminder Notify.
++ Feature: Icons for Delete Note, Hide Note
+
+-------
+0.0.4.1
+-------
+! BugFix: URL in Note problem fixed
+! Bugfix: Closing Reminder dialog will now mean "Remind me again"
+ You must press "Dismiss" to remove reminder
+! BugFix: Strange behaviour to add reminders + 1 hour.. (Now uses Localtime)
+! BugFix: Missing Langpack strings
+
+-------
+0.0.4.0
+-------
++ Totaly revriten everything!!! Moved to Microsoft (R) Visual C ++ 6.0
++ Added TopToolbar buttons for "New Reminder" and "New Note"
++ Notify via SMS to E-mail gateway (Only for Reminders)
+
+-------
+0.0.3.1
+-------
++ Sorry, i've forgot to remove debug :( That's why the plugin crashes
+ Now i've removed it :) Also i've relocated plugin to new base addres, so
+ there must not be a crash on start or exit (btw if you use any other
+ Delphi plugins, please contact autor to relocate them to a different
+ base addres than standart $4000000, or there will be a crash !!
+
+0.0.3.0
+-------
++ Reocurrence of Reminders (Daily, Weekly, Monthly)
++ Fixed bug with crash :(
+
+0.0.2.8
+-------
++ Changed DB structure again (fixed bug with Notes & Reminders size greater
+ than 4096 bytes). Now every Note & Reminder are stored into separate DB
+ setting, but every Note or Reminder is still limited to 4000 bytes :( sorry
+ for that. It is recomended you to clear all notes & Reminders to avoid errors :)
++ Implemented Purge function to free unused Notes & Reminders data from DB :)
++ Removed 'Empty' sign when a Note or Reminder have no text :)
++ Is the "Reminder On Top" bug still there? It works fine for me ?!? :)
+ (I've tested on Win ME, 2K Pro & XP)
+
+0.0.2.7
+-------
++ Fixed bug with Reminders not showing on top of all windows
+
+0.0.2.6
+-------
++ "Remind me again in:" now includes any date and(or) time in the future,
+ not just (5,10,15...etc min.).
++ Changed Name in Options dialog.
+
+0.0.2.5
+-------
++ Togle On-Top with the pin icon (Icon reflects status)
++ Change Font effects & colors (For Caption & Note body)
++ A "View Reminders" button in the "Add Reminder" box.
++ Rearanged TAB order :)
+
+0.0.2.4
+-------
++ Custom Sound to play On Reminder
+
+0.0.2.3
+-------
+[Warning!!! New Format of DB data! Incompatible with old versions]
+[It is recomended to delete all Notes and Reminders ]
+
++ Fixed bug - not showing corectly "Remind Again In :" Combo Box
++ Fixed bug - Trying to delete from empty list of reminders causes
+ error message.
++ Fixed bug - Problem when using Unicode characters
++ Other minor bug (or not bug) fixes :)
+
+0.0.2.2
+-------
++ Switched to Delphi 6 (smaller code)
++ There where so many feature requests for language translations :)
+ So i decided to implement this feature :)
++ I think finally fixed the on-top bug ?!?
+
+0.0.2.1
+-------
++ Fixed bug on 9X platforms (Didn't draw notes correct) :) Sorry, i forgot
+ to initialize length of an structure :)
++ Changed Name :)
++ Added transparency of Notes (Win 2K & XP)
+
+0.0.2.0
+-------
++ Reminder function implemented :)
++ Changed version to 2.0 :)
+
+0.0.1.6
+-------
++ Fixed structure in DB to store new features (Visible,On Top).
+ (Now DB Settings of plugin are incompatible with old versions,
+ so if you update , you will loose all notes).
++ Added Popup menu items for new features (Visible, On Top).
++ Minor changes in code to improve preformance. Smaller code :)
+
+0.0.1.5
+-------
++ Replaced Hot Keys with global Hot Keys.
++ Added option to change these Hot Keys.
++ Added Popup menu to Notes (Cut,Copy,Paste).
++ Added option to set default size of notes on create new.
++ Basic Implementation of reminders.
+
+0.0.1.4
+-------
++ Now saving Notes data on every change to avoid loosing notes if crash.
++ Fixed some bugs with window placement.
++ Added options for staying Always On Top.
++ Added Hot Keys to menu.
+
+0.0.1.3
+-------
++ Fixed bug with notes that have no text.
++ Added functions to Show/Hide Notes.
++ Added some icons :)
+
+0.0.1.2
+-------
++ Added Options dialog, to alow change of color and fonts for notes.
+
+0.0.1.1
+-------
++ Changed to save Notes data into Miranda database insted
+ of Windows registry.
+
+0.0.1.0
+-------
++ First release of this plugin. \ No newline at end of file
diff --git a/plugins/NotesAndReminders/NotesReminders.vcxproj b/plugins/NotesAndReminders/NotesReminders.vcxproj
new file mode 100644
index 0000000000..21a616736d
--- /dev/null
+++ b/plugins/NotesAndReminders/NotesReminders.vcxproj
@@ -0,0 +1,125 @@
+<?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="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{EEB57129-946C-4B98-8856-FDA501AE2A5E}</ProjectGuid>
+ <ProjectName>NotesReminders</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\Obj\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\Obj\$(ProjectName)\</IntDir>
+ <IgnoreImportLibrary>true</IgnoreImportLibrary>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;NETSEND_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <AdditionalIncludeDirectories>..\..\include;..\ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>globals.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\..\include\msapi</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;Gdi32.lib;winmm.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <BaseAddress>0x14310000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <SubSystem>Windows</SubSystem>
+ <AdditionalLibraryDirectories>$(SolutionDir)\lib</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;NETSEND_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <AdditionalIncludeDirectories>..\..\include;..\ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>global.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\..\include\msapi</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>Gdi32.lib;comctl32.lib;winmm.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OptimizeReferences>true</OptimizeReferences>
+ <BaseAddress>0x14310000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <AdditionalLibraryDirectories>$(SolutionDir)\lib</AdditionalLibraryDirectories>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="hotkeys.cpp" />
+ <ClCompile Include="main.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="miscutils.cpp" />
+ <ClCompile Include="notes.cpp" />
+ <ClCompile Include="options.cpp" />
+ <ClCompile Include="reminders.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="globals.h" />
+ <ClInclude Include="miscutils.h" />
+ <ClInclude Include="resource.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="N&amp;R - langpack.txt" />
+ <None Include="N&amp;R - readme.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/NotesAndReminders/NotesReminders.vcxproj.filters b/plugins/NotesAndReminders/NotesReminders.vcxproj.filters
new file mode 100644
index 0000000000..6ccf89abc3
--- /dev/null
+++ b/plugins/NotesAndReminders/NotesReminders.vcxproj.filters
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{0542c19a-ad49-4dd8-8643-47204fa16e58}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1e7a703d-e0f4-42ff-9e78-49de3375d4a5}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{38ee8469-7af5-4130-90a1-f4e8c620f475}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ <Filter Include="Custom Files">
+ <UniqueIdentifier>{81e2a913-4aac-4c50-aa77-6c231f934c76}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="hotkeys.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="miscutils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="notes.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="options.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="reminders.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="globals.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="miscutils.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="N&amp;R - langpack.txt">
+ <Filter>Custom Files</Filter>
+ </None>
+ <None Include="N&amp;R - readme.txt">
+ <Filter>Custom Files</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/NotesAndReminders/globals.h b/plugins/NotesAndReminders/globals.h
new file mode 100644
index 0000000000..f8cc794e4a
--- /dev/null
+++ b/plugins/NotesAndReminders/globals.h
@@ -0,0 +1,147 @@
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <windows.h>
+#include <commctrl.h>
+#include <memory.h>
+#include <time.h>
+#include <richedit.h>
+#include <stdio.h>
+#include <WinSock.h>
+#include <mmsystem.h>
+
+#include "win2k.h"
+#include "newpluginapi.h"
+#include "m_system.h"
+#include "m_database.h"
+#include "m_clist.h"
+#include "m_langpack.h"
+#include "m_options.h"
+#include "m_skin.h"
+#include "m_fontservice.h"
+#include "m_hotkeys.h"
+#include "m_icolib.h"
+
+#include "m_toptoolbar.h"
+
+#include "miscutils.h"
+#include "resource.h"
+
+// {842A6668-F9DA-4968-BFD7-D2BD9DF848EE}
+#define MIID_NNR {0x842a6668, 0xf9da, 0x4968, {0xbf, 0xd7, 0xd2, 0xbd, 0x9d, 0xf8, 0x48, 0xee}}
+
+#define MODULENAME "StickyNotes"
+#define SECTIONNAME "Notes & Reminders"
+
+// normal timer interval for reminder update processing
+#define REMINDER_UPDATE_INTERVAL 10000
+// short timer interval for reminder updates used as long as there are pending alarams in the event queue
+#define REMINDER_UPDATE_INTERVAL_SHORT 5000
+
+
+// font IDs used with LoadNRFont
+#define NR_FONTID_CAPTION 0
+#define NR_FONTID_BODY 1
+#define NR_FONTID_MAX NR_FONTID_BODY
+
+
+typedef struct {
+ HFONT hFont;
+ char size;
+ BYTE style; // see the DBFONTF_* flags
+ BYTE charset;
+ char szFace[LF_FACESIZE];
+} STICKYNOTEFONT;
+
+typedef struct {
+ HWND SNHwnd,REHwnd;
+ BOOL Visible,OnTop;
+ char *data;
+ ULARGE_INTEGER ID; // FILETIME in UTC
+ char *title;
+ BOOL CustomTitle;
+ DWORD BgColor; // custom bg color override (only valid if non-zero)
+ DWORD FgColor; // custom fg/text color override (only valid if non-zero)
+ STICKYNOTEFONT *pCustomFont;// custom (body) font override (NULL if default font is used)
+} STICKYNOTE;
+
+typedef struct {
+ HWND handle;
+ BOOL RemVisible;
+ DWORD uid;
+ char *Reminder;
+ ULARGE_INTEGER When; // FILETIME in UTC
+ UINT RepeatSound;
+ UINT RepeatSoundTTL;
+ int SoundSel; // -1 if sound disabled
+ BOOL SystemEventQueued;
+} REMINDERDATA;
+
+
+extern void CreateMsgWindow(void);
+extern void DestroyMsgWindow(void);
+
+extern STICKYNOTE* NewNote(int Ax,int Ay,int Aw,int Ah,char *Data,
+ ULARGE_INTEGER *ID,BOOL Visible,BOOL OnTop,int scrollV);
+extern void LoadNotes(BOOL bIsStartup);
+extern void SaveNotes(void);
+extern void DeleteNotes(void);
+extern void ShowHideNotes(void);
+extern void ListNotes(void);
+
+extern void NewReminder(void);
+extern void LoadReminders(void);
+extern void SaveReminders(void);
+extern void DeleteReminders(void);
+extern void ListReminders(void);
+extern BOOL CheckRemindersAndStart(void);
+
+extern void InitSettings(void);
+extern void TermSettings(void);
+extern int CALLBACK DlgProcOptions(HWND hdlg,UINT message,
+ WPARAM wParam,LPARAM lParam);
+extern void LoadNRFont(int i, LOGFONT *lf, COLORREF *colour);
+
+extern BOOL WS_Init();
+extern void WS_CleanUp();
+
+extern LPCSTR GetDateFormatStr();
+extern LPCSTR GetTimeFormatStr();
+
+extern HINSTANCE hinstance;
+extern HINSTANCE hmiranda;
+extern PLUGINLINK *pluginLink;
+
+extern BOOL g_CloseAfterAddReminder, g_UseDefaultPlaySound;
+extern HICON g_hReminderIcon;
+
+extern LOGFONT lfBody,lfCaption;
+extern HFONT hBodyFont,hCaptionFont;
+
+extern long BodyColor;
+extern long CaptionFontColor,BodyFontColor;
+
+extern BOOL g_ShowNotesAtStart,g_ShowScrollbar,g_AddContListMI,g_ShowNoteButtons;
+extern int g_NoteTitleDate, g_NoteTitleTime;
+
+extern int g_NoteWidth,g_NoteHeight;
+
+extern int g_Transparency;
+
+extern char *g_RemindSMS;
+
+extern BOOL g_isWin2kPlus;
+
+extern TCHAR *g_lpszAltBrowser;
+
+extern int g_reminderListGeom[4];
+extern int g_reminderListColGeom[2];
+extern int g_notesListGeom[4];
+extern int g_notesListColGeom[4];
+
+extern HWND HKHwnd;
+extern HANDLE hIconLibItem[];
+
+// these defs are only used to emphasize that SYSTEMTIMEtoFILETIME/FILETIMEtoSYSTEMTIME only convert the data type,
+// it does not apply any time conversion/correction like UTC to local etc. (if input is local, then output is local too)
+#define SYSTEMTIMEtoFILETIME SystemTimeToFileTime
+#define FILETIMEtoSYSTEMTIME FileTimeToSystemTime
diff --git a/plugins/NotesAndReminders/hotkeys.cpp b/plugins/NotesAndReminders/hotkeys.cpp
new file mode 100644
index 0000000000..a8bd8d3c53
--- /dev/null
+++ b/plugins/NotesAndReminders/hotkeys.cpp
@@ -0,0 +1,137 @@
+#include "globals.h"
+
+#define MSG_WND_CLASS "MIM_SNMsgWindow"
+
+
+HWND HKHwnd;
+
+
+enum KB_ACTIONS {KB_NEW_NOTE = 1, KB_TOGGLE_NOTES, KB_NEW_REMINDER};
+
+
+void RegisterKeyBindings()
+{
+ HOTKEYDESC desc;
+
+ ZeroMemory(&desc, sizeof(desc));
+ desc.cbSize = sizeof(desc);
+ desc.pszSection = SECTIONNAME;
+
+ desc.pszName = MODULENAME"/NewNote";
+ desc.pszDescription = "New Note";
+ desc.lParam = KB_NEW_NOTE;
+ desc.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, VK_INSERT);
+ desc.pszService = MODULENAME"/MenuCommandAddNew";
+ Hotkey_Register(&desc);
+
+ desc.pszName = MODULENAME"/ToggleNotesVis";
+ desc.pszDescription = "Toggle Notes Visibility";
+ desc.lParam = KB_TOGGLE_NOTES;
+ desc.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, VK_ADD);
+ desc.pszService = MODULENAME"/MenuCommandShowHide";
+ Hotkey_Register(&desc);
+
+ desc.pszName = MODULENAME"/BringNotesFront";
+ desc.pszDescription = "Bring All Notes to Front";
+ desc.lParam = KB_TOGGLE_NOTES;
+ desc.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, VK_HOME);
+ desc.pszService = MODULENAME"/MenuCommandBringAllFront";
+ Hotkey_Register(&desc);
+
+ desc.pszName = MODULENAME"/NewReminder";
+ desc.pszDescription = "New Reminder";
+ desc.lParam = KB_NEW_REMINDER;
+ desc.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, VK_SUBTRACT);
+ desc.pszService = MODULENAME"/MenuCommandNewReminder";
+ Hotkey_Register(&desc);
+}
+
+
+/*int HandleNRShortcuts(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ STICKYNOTE* PSN;
+
+ BOOL isShift = GetKeyState(VK_SHIFT) & 0x8000;
+ BOOL isAlt = GetKeyState(VK_MENU) & 0x8000;
+ BOOL isCtrl = (GetKeyState(VK_CONTROL) & 0x8000) && !isAlt;
+
+ int action;
+ MSG amsg;
+ amsg.hwnd = hwnd;
+ amsg.message = msg;
+ amsg.wParam = wParam;
+ amsg.lParam = lParam;
+
+ switch ( (action = CallService(MS_HOTKEY_CHECK, (WPARAM)&amsg, (LPARAM)SECTIONNAME)) )
+ {
+ case KB_NEW_NOTE:
+ PSN = NewNote(0,0,0,0,NULL,NULL,TRUE,TRUE,0);
+ SetFocus(PSN->REHwnd);
+ return FALSE;
+ case KB_TOGGLE_NOTES:
+ ShowHideNotes();
+ return FALSE;
+ case KB_NEW_REMINDER:
+ NewReminder();
+ return FALSE;
+ }
+
+ return -1;
+}*/
+
+
+int CALLBACK NotifyHotKeyWndProc(HWND AHwnd,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ BOOL b;
+
+ switch (Message)
+ {
+ case WM_TIMER:
+ {
+ KillTimer(HKHwnd,1026);
+ b = CheckRemindersAndStart();
+ SetTimer(HKHwnd,1026,b ? REMINDER_UPDATE_INTERVAL_SHORT : REMINDER_UPDATE_INTERVAL,0);
+
+ return FALSE;
+ }
+ }
+
+ return DefWindowProc(AHwnd,Message,wParam,lParam);
+}
+
+void CreateMsgWindow(void)
+{
+ HWND hParent = NULL;
+ WNDCLASSEX TWC = {0};
+
+ if (!GetClassInfoEx(hmiranda,MSG_WND_CLASS,&TWC))
+ {
+ TWC.style = 0;
+ TWC.cbClsExtra = 0;
+ TWC.cbWndExtra = 0;
+ TWC.hInstance = hmiranda;
+ TWC.hIcon = 0;
+ TWC.hCursor = 0;
+ TWC.hbrBackground = 0;
+ TWC.lpszMenuName = NULL;
+ TWC.lpszClassName = MSG_WND_CLASS;
+ TWC.cbSize = sizeof(TWC);
+ TWC.lpfnWndProc = (WNDPROC)NotifyHotKeyWndProc;
+ RegisterClassEx(&TWC);
+ }
+
+ if ( IsWinVer2000Plus() )
+ {
+ // win2k+ has special message-only windows support
+ hParent = HWND_MESSAGE;
+ }
+
+ HKHwnd = CreateWindowEx(WS_EX_TOOLWINDOW,MSG_WND_CLASS,_T("StickyNotes"),0,0,0,0,0,hParent,NULL,hmiranda,NULL);
+ SetTimer(HKHwnd,1026,REMINDER_UPDATE_INTERVAL,0);
+}
+
+void DestroyMsgWindow(void)
+{
+ KillTimer(HKHwnd,1026);
+ DestroyWindow(HKHwnd);
+}
diff --git a/plugins/NotesAndReminders/icons/addremin.ico b/plugins/NotesAndReminders/icons/addremin.ico
new file mode 100644
index 0000000000..0264fd1716
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/addremin.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/bringfront.ico b/plugins/NotesAndReminders/icons/bringfront.ico
new file mode 100644
index 0000000000..11984568a8
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/bringfront.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/captioni.ico b/plugins/NotesAndReminders/icons/captioni.ico
new file mode 100644
index 0000000000..c7543c075f
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/captioni.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/captionn.ico b/plugins/NotesAndReminders/icons/captionn.ico
new file mode 100644
index 0000000000..fd925f0660
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/captionn.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/deleteic.ico b/plugins/NotesAndReminders/icons/deleteic.ico
new file mode 100644
index 0000000000..5aa90434c1
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/deleteic.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/deletere.ico b/plugins/NotesAndReminders/icons/deletere.ico
new file mode 100644
index 0000000000..8560fc8f5e
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/deletere.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/hidenote.ico b/plugins/NotesAndReminders/icons/hidenote.ico
new file mode 100644
index 0000000000..bada524874
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/hidenote.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/noteicon.ico b/plugins/NotesAndReminders/icons/noteicon.ico
new file mode 100644
index 0000000000..5479ed29e7
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/noteicon.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/playsound.ico b/plugins/NotesAndReminders/icons/playsound.ico
new file mode 100644
index 0000000000..0e3711dbc5
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/playsound.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/reminder.ico b/plugins/NotesAndReminders/icons/reminder.ico
new file mode 100644
index 0000000000..d3bed91d4d
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/reminder.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/removeno.ico b/plugins/NotesAndReminders/icons/removeno.ico
new file mode 100644
index 0000000000..d2aae887a6
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/removeno.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/showhide.ico b/plugins/NotesAndReminders/icons/showhide.ico
new file mode 100644
index 0000000000..99b241d4fb
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/showhide.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/viewnotes.ico b/plugins/NotesAndReminders/icons/viewnotes.ico
new file mode 100644
index 0000000000..18057a372a
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/viewnotes.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/icons/viewremi.ico b/plugins/NotesAndReminders/icons/viewremi.ico
new file mode 100644
index 0000000000..b9e8bce9d5
--- /dev/null
+++ b/plugins/NotesAndReminders/icons/viewremi.ico
Binary files differ
diff --git a/plugins/NotesAndReminders/main.cpp b/plugins/NotesAndReminders/main.cpp
new file mode 100644
index 0000000000..010f7489aa
--- /dev/null
+++ b/plugins/NotesAndReminders/main.cpp
@@ -0,0 +1,389 @@
+#include "globals.h"
+
+HINSTANCE hinstance = NULL;
+HINSTANCE hmiranda = NULL;
+int hLangpack;
+
+HANDLE hkOptInit = NULL;
+HANDLE hkTopToolbarInit = NULL;
+HANDLE hkModulesLoaded = NULL;
+HANDLE hkFontChange = NULL;
+HANDLE hkColorChange = NULL;
+HMODULE hUserDll = NULL;
+HMODULE hRichedDll = NULL;
+HMODULE hKernelDll = NULL;
+
+extern TREEELEMENT *g_Stickies;
+extern TREEELEMENT *RemindersList;
+
+static PLUGININFOEX pluginInfo =
+{
+ sizeof(PLUGININFOEX),
+ "Sticky Notes & Reminders",
+ PLUGIN_MAKE_VERSION(0,0,5,1),
+ "Sticky Notes & Reminders Implementation for Miranda IM.",
+ "Joe Kucera, Lubomir Kolev Ivanov, Georg Fischer",
+ "jokusoftware@users.sourceforge.net; d00mEr@dir.bg",
+ "(C) 2003,2005 Joe Kucera, Lubomir Ivanov",
+ "http://d00mer.freeshell.org/miranda/",
+ UNICODE_AWARE,
+ MIID_NNR
+};
+
+
+void RegisterFontServiceFonts();
+void RegisterKeyBindings();
+int OpenTriggeredReminder(WPARAM w, LPARAM l);
+void BringAllNotesToFront(STICKYNOTE *pActive);
+void CloseNotesList();
+void CloseReminderList();
+
+int PluginMenuCommandAddNew(WPARAM w,LPARAM l)
+{
+ STICKYNOTE* PSN;
+ PSN = NewNote(0,0,0,0,NULL,NULL,TRUE,TRUE,0);
+ SetFocus(PSN->REHwnd);
+ return 0;
+}
+
+int PluginMenuCommandDeleteAll(WPARAM w,LPARAM l)
+{
+ if (g_Stickies && MessageBox(NULL, Translate("Are you sure you want to delete all notes?"), SECTIONNAME, MB_OKCANCEL) == IDOK)
+ DeleteNotes();
+ return 0;
+}
+
+int PluginMenuCommandShowHide(WPARAM w,LPARAM l)
+{
+ ShowHideNotes();
+ return 0;
+}
+
+int PluginMenuCommandViewNotes(WPARAM w,LPARAM l)
+{
+ ListNotes();
+ return 0;
+}
+
+int PluginMenuCommandAllBringFront(WPARAM w,LPARAM l)
+{
+ BringAllNotesToFront(NULL);
+ return 0;
+}
+
+int PluginMenuCommandNewReminder(WPARAM w,LPARAM l)
+{
+ NewReminder();
+ return 0;
+}
+
+int PluginMenuCommandViewReminders(WPARAM w,LPARAM l)
+{
+ ListReminders();
+ return 0;
+}
+
+int PluginMenuCommandDeleteReminders(WPARAM w,LPARAM l)
+{
+ if (RemindersList && MessageBox(NULL, Translate("Are you sure you want to delete all reminders?"), SECTIONNAME, MB_OKCANCEL) == IDOK)
+ DeleteReminders();
+ return 0;
+}
+
+struct
+{
+ char* szDescr;
+ char* szName;
+ int defIconID;
+}
+static const iconList[] =
+{
+ {"New Reminder", "AddReminder", IDI_ADDREMINDER},
+ {"Delete All Notes", "DeleteIcon", IDI_DELETEICON},
+ {"New Note", "NoteIcon", IDI_NOTEICON},
+ {"Show/Hide Notes", "ShowHide", IDI_SHOWHIDE},
+ {"On Top Caption Icon", "CaptionIcon", IDI_CAPTIONICON},
+ {"Delete All Reminders", "DeleteReminder", IDI_DELETEREMINDER},
+ {"View Reminders", "ViewReminders", IDI_VIEWREMINDERS},
+ {"Not on Top Caption Icon", "CaptionIconNotTop", IDI_CAPTIONICONNOTTOP},
+ {"Hide Note Icon", "HideNote", IDI_HIDENOTE},
+ {"Remove Note Icon", "RemoveNote", IDI_REMOVENOTE},
+ {"Reminder Icon", "Reminder", IDI_REMINDER},
+ {"Bring All to Front", "BringFront", IDI_BRINGFRONT},
+ {"Play Sound Icon", "PlaySound", IDI_PLAYSOUND},
+ {"View Notes", "ViewNotes", IDI_VIEWNOTES},
+ {"New Note", "NewNote", IDI_NOTEICON},
+ {"New Reminder", "NewReminder", IDI_ADDREMINDER}
+};
+
+HANDLE hIconLibItem[SIZEOF(iconList)];
+
+void InitIcons(void)
+{
+ int i;
+ char szSettingName[100];
+ SKINICONDESC sid = {0};
+ TCHAR szFile[MAX_PATH];
+
+ GetModuleFileName(hinstance, szFile, SIZEOF(szFile));
+
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.ptszDefaultFile = szFile;
+ sid.pszName = szSettingName;
+ sid.pszSection = MODULENAME;
+ sid.flags = SIDF_PATH_TCHAR;
+
+ for (i = 0; i < SIZEOF(iconList); i++)
+ {
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%s_%s", MODULENAME, iconList[i].szName);
+
+ sid.pszDescription = iconList[i].szDescr;
+ sid.iDefaultIndex = -iconList[i].defIconID;
+ hIconLibItem[i] = Skin_AddIcon(&sid);
+ }
+}
+
+int OnOptInitialise(WPARAM w, LPARAM L)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.position = 900002000;
+ odp.hInstance = hinstance;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_STNOTEOPTIONS);
+ odp.pszTitle = _T(SECTIONNAME);
+ odp.pszGroup = _T("Plugins");
+ odp.pfnDlgProc = DlgProcOptions;
+ Options_AddPage(w, &odp);
+ return 0;
+}
+
+int OnTopToolBarInit(WPARAM w,LPARAM L)
+{
+ TTBButton ttb = {0};
+ ttb.cbSize = sizeof(TTBButton);
+ ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP | TTBBF_ICONBYHANDLE;
+
+ ttb.hIconHandleUp = ttb.hIconHandleDn = hIconLibItem[14];
+ ttb.pszService = MODULENAME"/MenuCommandAddNew";
+ ttb.name = "Add New Note";
+ TopToolbar_AddButton(&ttb);
+
+ ttb.hIconHandleUp = ttb.hIconHandleDn = hIconLibItem[15];
+ ttb.pszService = MODULENAME"/MenuCommandNewReminder";
+ ttb.name = "Add New Reminder";
+ TopToolbar_AddButton(&ttb);
+
+ UnhookEvent(hkTopToolbarInit);
+ return 0;
+}
+
+static void InitServices()
+{
+ // register sounds
+
+ SkinAddNewSoundEx("AlertReminder", "Alerts", "Reminder triggered");
+ SkinAddNewSoundEx("AlertReminder2", "Alerts", "Reminder triggered (Alternative 1)");
+ SkinAddNewSoundEx("AlertReminder3", "Alerts", "Reminder triggered (Alternative 2)");
+
+ // register menu command services
+
+ CreateServiceFunction(MODULENAME"/MenuCommandAddNew",PluginMenuCommandAddNew);
+ CreateServiceFunction(MODULENAME"/MenuCommandShowHide",PluginMenuCommandShowHide);
+ CreateServiceFunction(MODULENAME"/MenuCommandViewNotes",PluginMenuCommandViewNotes);
+ CreateServiceFunction(MODULENAME"/MenuCommandDeleteAll",PluginMenuCommandDeleteAll);
+ CreateServiceFunction(MODULENAME"/MenuCommandBringAllFront",PluginMenuCommandAllBringFront);
+
+ //
+
+ CreateServiceFunction(MODULENAME"/MenuCommandNewReminder",PluginMenuCommandNewReminder);
+ CreateServiceFunction(MODULENAME"/MenuCommandViewReminders",PluginMenuCommandViewReminders);
+ CreateServiceFunction(MODULENAME"/MenuCommandDeleteReminders",PluginMenuCommandDeleteReminders);
+
+ // register misc
+
+ CreateServiceFunction(MODULENAME"/OpenTriggeredReminder",OpenTriggeredReminder);
+}
+
+int OnModulesLoaded(WPARAM wparam,LPARAM lparam)
+{
+ CLISTMENUITEM cmi = {0};
+
+ // register fonts and hotkeys
+
+ RegisterFontServiceFonts();
+ RegisterKeyBindings();
+
+ // register menus
+
+ cmi.cbSize = sizeof(cmi);
+ cmi.pszContactOwner = NULL;
+ cmi.pszPopupName = "Notes && Reminders";
+ cmi.flags = CMIM_ICON | CMIF_ICONFROMICOLIB;
+
+ cmi.position = 1600000000;
+ cmi.icolibItem = hIconLibItem[2];
+ cmi.pszName = "New &Note";
+ cmi.pszService = MODULENAME"/MenuCommandAddNew";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600000001;
+ cmi.icolibItem = hIconLibItem[0];
+ cmi.pszName = "New &Reminder";
+ cmi.pszService = MODULENAME"/MenuCommandNewReminder";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ //
+
+ cmi.position = 1600100000;
+ cmi.icolibItem = hIconLibItem[3];
+ cmi.pszName = "&Show / Hide Notes";
+ cmi.pszService = MODULENAME"/MenuCommandShowHide";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600100001;
+ cmi.icolibItem = hIconLibItem[13];
+ cmi.pszName = "Vie&w Notes";
+ cmi.pszService = MODULENAME"/MenuCommandViewNotes";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600100002;
+ cmi.icolibItem = hIconLibItem[1];
+ cmi.pszName = "&Delete All Notes";
+ cmi.pszService = MODULENAME"/MenuCommandDeleteAll";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600100003;
+ cmi.icolibItem = hIconLibItem[11];
+ cmi.pszName = "&Bring All to Front";
+ cmi.pszService = MODULENAME"/MenuCommandBringAllFront";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ //
+
+ cmi.position = 1600200000;
+ cmi.icolibItem = hIconLibItem[6];
+ cmi.pszName = "&View Reminders";
+ cmi.pszService = MODULENAME"/MenuCommandViewReminders";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600200001;
+ cmi.icolibItem = hIconLibItem[5];
+ cmi.pszName = "D&elete All Reminders";
+ cmi.pszService = MODULENAME"/MenuCommandDeleteReminders";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ // register misc
+
+ hkOptInit = HookEvent(ME_OPT_INITIALISE, OnOptInitialise);
+ hkTopToolbarInit = HookEvent("TopToolBar/ModuleLoaded", OnTopToolBarInit);
+ UnhookEvent(hkModulesLoaded);
+
+ // init vars and load all data
+
+ InitSettings();
+ CreateMsgWindow();
+ LoadNotes(TRUE);
+ LoadReminders();
+
+ return 0;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ CloseNotesList();
+ CloseReminderList();
+ SaveNotes();
+ SaveReminders();
+ DestroyMsgWindow();
+ WS_CleanUp();
+ TermSettings();
+
+ UnhookEvent(hkFontChange);
+ UnhookEvent(hkColorChange);
+
+ UnhookEvent(hkOptInit);
+
+ Skin_ReleaseIcon(g_hReminderIcon);
+ DeleteObject(hBodyFont);
+ DeleteObject(hCaptionFont);
+
+ if (hRichedDll)
+ FreeLibrary(hRichedDll);
+ if (hUserDll)
+ FreeLibrary(hUserDll);
+ if (hKernelDll)
+ FreeLibrary(hKernelDll);
+
+ return 0;
+}
+
+BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hinstance = hinst;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+ mir_getLP(&pluginInfo);
+
+ INITCOMMONCONTROLSEX ctrls = {0};
+ ctrls.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ ctrls.dwICC = ICC_DATE_CLASSES;
+ InitCommonControlsEx(&ctrls);
+
+ g_isWin2kPlus = IsWinVer2000Plus();
+
+ hRichedDll = LoadLibrary("RICHED20.DLL");
+ if (!hRichedDll)
+ {
+ if (MessageBox(0, Translate("Miranda could not load the Note & Reminders plugin, RICHED20.DLL is missing. If you are using Windows 95 or WINE please make sure you have riched20.dll installed. Press 'Yes' to continue loading Miranda."), SECTIONNAME, MB_YESNO | MB_ICONINFORMATION) != IDYES)
+ return 1;
+ return 0;
+ }
+
+ hUserDll = LoadLibrary("user32.dll");
+ if (hUserDll)
+ {
+ MySetLayeredWindowAttributes = (BOOL (WINAPI *)(HWND,COLORREF,BYTE,DWORD))GetProcAddress(hUserDll,"SetLayeredWindowAttributes");
+ MyMonitorFromWindow = (HANDLE (WINAPI*)(HWND,DWORD))GetProcAddress(hUserDll,"MonitorFromWindow");
+ }
+ else
+ {
+ MySetLayeredWindowAttributes = NULL;
+ MyMonitorFromWindow = NULL;
+ }
+
+ hKernelDll = LoadLibrary("kernel32.dll");
+ if (hKernelDll)
+ {
+ MyTzSpecificLocalTimeToSystemTime = (BOOL (WINAPI*)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME))GetProcAddress(hKernelDll,"TzSpecificLocalTimeToSystemTime");
+ MySystemTimeToTzSpecificLocalTime = (BOOL (WINAPI*)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME))GetProcAddress(hKernelDll,"SystemTimeToTzSpecificLocalTime");
+ }
+ else
+ {
+ MyTzSpecificLocalTimeToSystemTime = NULL;
+ MySystemTimeToTzSpecificLocalTime = NULL;
+ }
+
+ InitServices();
+ WS_Init();
+
+ hkModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED,OnModulesLoaded);
+ InitIcons();
+
+ return 0;
+} \ No newline at end of file
diff --git a/plugins/NotesAndReminders/miscutils.cpp b/plugins/NotesAndReminders/miscutils.cpp
new file mode 100644
index 0000000000..b4de98404c
--- /dev/null
+++ b/plugins/NotesAndReminders/miscutils.cpp
@@ -0,0 +1,246 @@
+#include "globals.h"
+
+BOOL (WINAPI *MySetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);
+HANDLE (WINAPI *MyMonitorFromWindow)(HWND,DWORD);
+BOOL (WINAPI *MyTzSpecificLocalTimeToSystemTime)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME);
+BOOL (WINAPI *MySystemTimeToTzSpecificLocalTime)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME);
+
+
+WORD ConvertHotKeyToControl(WORD HK)
+{
+ WORD R = 0;
+ if ((HK & MOD_CONTROL) == MOD_CONTROL) R = R | HOTKEYF_CONTROL;
+ if ((HK & MOD_ALT) == MOD_ALT) R = R | HOTKEYF_ALT;
+ if ((HK & MOD_SHIFT) == MOD_SHIFT) R = R | HOTKEYF_SHIFT;
+ return R;
+}
+
+WORD ConvertControlToHotKey(WORD HK)
+{
+ WORD R = 0;
+ if ((HK & HOTKEYF_CONTROL) == HOTKEYF_CONTROL) R = R | MOD_CONTROL;
+ if ((HK & HOTKEYF_ALT) == HOTKEYF_ALT) R = R | MOD_ALT;
+ if ((HK & HOTKEYF_SHIFT) == HOTKEYF_SHIFT) R = R | MOD_SHIFT;
+ return R;
+}
+
+void WriteSettingInt(HANDLE hContact,char *ModuleName,char *SettingName,int Value)
+{
+ DBCONTACTWRITESETTING cws = {0};
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_DWORD;
+ dbv.dVal = Value;
+ cws.szModule = ModuleName;
+ cws.szSetting = SettingName;
+ cws.value = dbv;
+ CallService(MS_DB_CONTACT_WRITESETTING,(DWORD)hContact,(DWORD)&cws);
+}
+
+int ReadSettingInt(HANDLE hContact,char *ModuleName,char *SettingName,int Default)
+{
+ DBCONTACTGETSETTING cws = {0};
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_DWORD;
+ dbv.dVal = Default;
+ cws.szModule = ModuleName;
+ cws.szSetting = SettingName;
+ cws.pValue = &dbv;
+ if (CallService(MS_DB_CONTACT_GETSETTING,(DWORD)hContact,(DWORD)&cws))
+ return Default;
+ else
+ return dbv.dVal;
+}
+
+void DeleteSetting(HANDLE hContact,char *ModuleName,char *SettingName)
+{
+ DBCONTACTGETSETTING dbcgs = {0};
+ dbcgs.szModule = ModuleName;
+ dbcgs.szSetting = SettingName;
+ dbcgs.pValue = NULL;
+ CallService(MS_DB_CONTACT_DELETESETTING,(DWORD)hContact,(DWORD)&dbcgs);
+}
+
+void FreeSettingBlob(WORD pSize,void * pbBlob)
+{
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_BLOB;
+ dbv.cpbVal = pSize;
+ dbv.pbVal = (BYTE*)pbBlob;
+ CallService(MS_DB_CONTACT_FREEVARIANT,0,(DWORD)&dbv);
+}
+
+void WriteSettingBlob(HANDLE hContact,char *ModuleName,char *SettingName,WORD pSize,void *pbBlob)
+{
+ DBCONTACTWRITESETTING cgs = {0};
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_BLOB;
+ dbv.cpbVal = pSize;
+ dbv.pbVal = (BYTE*)pbBlob;
+ cgs.szModule = ModuleName;
+ cgs.szSetting = SettingName;
+ cgs.value = dbv;
+ CallService(MS_DB_CONTACT_WRITESETTING,(DWORD)hContact,(DWORD)&cgs);
+}
+
+void ReadSettingBlob(HANDLE hContact, char *ModuleName, char *SettingName, WORD *pSize, void **pbBlob)
+{
+ DBCONTACTGETSETTING cgs = {0};
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_BLOB;
+// dbv.cpbVal = (LOBYTE(pSize) | HIBYTE((BYTE)pbBlob)); // this is not used anyway
+ cgs.szModule = ModuleName;
+ cgs.szSetting = SettingName;
+ cgs.pValue = &dbv;
+ if (CallService(MS_DB_CONTACT_GETSETTING,(DWORD)hContact,(DWORD)&cgs))
+ {
+ pSize = 0; // tohle asi ve 4.2 neni
+ pbBlob = NULL;
+ }
+ else
+ {
+ *pSize = LOWORD(dbv.cpbVal);
+ *pbBlob = (int*)dbv.pbVal;
+ }
+}
+
+void WriteSettingIntArray(HANDLE hContact,char *ModuleName,char *SettingName,const int *Value, int Size)
+{
+ WriteSettingBlob(hContact,ModuleName,SettingName,sizeof(int)*Size,(void*)Value);
+}
+
+BOOL ReadSettingIntArray(HANDLE hContact,char *ModuleName,char *SettingName,int *Value, int Size)
+{
+ WORD sz = 4096;
+ int *pData;
+ BOOL bResult;
+
+ ReadSettingBlob(hContact, ModuleName, SettingName, &sz, (void**)&pData);
+
+ if (!pData)
+ return FALSE;
+
+ bResult = FALSE;
+
+ if (sz == sizeof(int)*Size)
+ {
+ memcpy(Value, pData, sizeof(int)*Size);
+ bResult = TRUE;
+ }
+
+ FreeSettingBlob(sz,pData);
+
+ return bResult;
+}
+
+void TreeAdd(TREEELEMENT **root, void *Data)
+{
+ TREEELEMENT *NTE;
+ NTE = (TREEELEMENT*)malloc(sizeof(TREEELEMENT));
+ if (NTE)
+ {
+ NTE->ptrdata = Data;
+ NTE->next = *root;
+ *root = NTE;
+ }
+}
+
+void TreeAddSorted(TREEELEMENT **root,void *Data,int (*CompareCb)(TREEELEMENT*,TREEELEMENT*))
+{
+ TREEELEMENT *TTE,*Prev;
+ TREEELEMENT *NTE;
+
+ if (!*root)
+ {
+ // list empty, just add normally
+ TreeAdd(root,Data);
+ return;
+ }
+
+ NTE = (TREEELEMENT*)malloc(sizeof(TREEELEMENT));
+ if (!NTE)
+ return;
+
+ NTE->ptrdata = Data;
+ NTE->next = NULL;
+
+ // insert sorted
+
+ Prev = NULL;
+ TTE = *root;
+
+ while (TTE)
+ {
+ if (CompareCb(NTE, TTE) < 0)
+ {
+ if (Prev)
+ {
+ Prev->next = NTE;
+ NTE->next = TTE;
+ }
+ else
+ {
+ // first in list
+ NTE->next = TTE;
+ *root = NTE;
+ }
+ return;
+ }
+
+ Prev = TTE;
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ // add last
+ Prev->next = NTE;
+}
+
+void TreeDelete(TREEELEMENT **root,void *Item)
+{
+ TREEELEMENT *TTE,*Prev = NULL;
+ TTE = *root;
+ if (!TTE) return;
+ while ((TTE) && (TTE->ptrdata != Item))
+ {
+ Prev = TTE;
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+ if (TTE)
+ {
+ if (Prev)
+ Prev->next = TTE->next;
+ else
+ *root = (TREEELEMENT*)TTE->next;
+ SAFE_FREE((void**)&TTE);
+ }
+}
+
+void *TreeGetAt(TREEELEMENT *root,int Item)
+{
+ TREEELEMENT *TTE;
+ int I = 0;
+ if (!root) return NULL;
+ TTE = root;
+ while ((TTE) && (I != Item))
+ {
+ TTE = (TREEELEMENT*)TTE->next;
+ I++;
+ }
+ if (!TTE)
+ return NULL;
+ else
+ return TTE->ptrdata;
+}
+
+int TreeGetCount(TREEELEMENT *root)
+{
+ int I = 0;
+ TREEELEMENT *TTE;
+ if (!root) return 0;
+ TTE = root;
+ while (TTE)
+ {
+ TTE = (TREEELEMENT*)TTE->next;
+ I++;
+ }
+ return I;
+}
diff --git a/plugins/NotesAndReminders/miscutils.h b/plugins/NotesAndReminders/miscutils.h
new file mode 100644
index 0000000000..104de5313c
--- /dev/null
+++ b/plugins/NotesAndReminders/miscutils.h
@@ -0,0 +1,47 @@
+void WriteSettingInt(HANDLE hContact,char *ModuleName,
+ char *SettingName,int Value);
+int ReadSettingInt(HANDLE hContact,char *ModuleName,
+ char *SettingName,int Default);
+void ReadSettingBlob(HANDLE hContact, char *ModuleName,
+ char *SettingName, WORD *pSize, void **pbBlob);
+void WriteSettingBlob(HANDLE hContact,char *ModuleName,
+ char *SettingName,WORD pSize,void *pbBlob);
+void FreeSettingBlob(WORD pSize,void * pbBlob);
+BOOL ReadSettingBool(HANDLE hContact,char *ModuleName,
+ char *SettingName,BOOL Default);
+void WriteSettingBool(HANDLE hContact,char *ModuleName,
+ char *SettingName,BOOL Value);
+void DeleteSetting(HANDLE hContact,char *ModuleName,
+ char *SettingName);
+void WriteSettingIntArray(HANDLE hContact,char *ModuleName,
+ char *SettingName,const int *Value, int Size);
+BOOL ReadSettingIntArray(HANDLE hContact,char *ModuleName,
+ char *SettingName,int *Value, int Size);
+
+extern BOOL (WINAPI *MySetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);
+extern HANDLE (WINAPI *MyMonitorFromWindow)(HWND,DWORD);
+extern BOOL (WINAPI *MyTzSpecificLocalTimeToSystemTime)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME);
+extern BOOL (WINAPI *MySystemTimeToTzSpecificLocalTime)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME);
+
+WORD ConvertHotKeyToControl(WORD HK);
+WORD ConvertControlToHotKey(WORD HK);
+
+typedef struct {
+ void *ptrdata;
+ void *next;
+} TREEELEMENT;
+
+void TreeAdd(TREEELEMENT **root,void *Data);
+void TreeAddSorted(TREEELEMENT **root,void *Data,int (*CompareCb)(TREEELEMENT*,TREEELEMENT*));
+void TreeDelete(TREEELEMENT **root,void *Item);
+void *TreeGetAt(TREEELEMENT *root,int Item);
+int TreeGetCount(TREEELEMENT *root);
+
+static void __inline SAFE_FREE(void** p)
+{
+ if (*p)
+ {
+ free(*p);
+ *p = NULL;
+ }
+}
diff --git a/plugins/NotesAndReminders/notes.cpp b/plugins/NotesAndReminders/notes.cpp
new file mode 100644
index 0000000000..5f98768d8b
--- /dev/null
+++ b/plugins/NotesAndReminders/notes.cpp
@@ -0,0 +1,2180 @@
+#include "globals.h"
+
+#ifndef MONITOR_DEFAULTTONULL
+#define MONITOR_DEFAULTTONULL 0x00000000
+#endif
+
+
+// NotesData DB data params
+#define DATATAG_TEXT 1 // %s
+#define DATATAG_SCROLLPOS 2 // %u (specifies rich edit controls scroll post as first visible line)
+#define DATATAG_BGCOL 3 // %x (custom background color)
+#define DATATAG_FGCOL 4 // %x (custom text/fg colors)
+#define DATATAG_TITLE 5 // %s (custom note title)
+#define DATATAG_FONT 6 // %d:%u:%u:%s (custom font)
+
+
+#define MAX_TITLE_LEN 63
+#define MAX_NOTE_LEN 16384
+
+// delay before saving note changes (ms)
+#define NOTE_CHANGE_COMMIT_DELAY 1000
+
+
+#ifndef WS_EX_NOACTIVATE
+#define WS_EX_NOACTIVATE 0x08000000
+#endif
+#define IDM_REMOVENOTE 40001
+#define IDM_HIDENOTE 40002
+#define IDM_TOGGLEONTOP 40003
+#define IDM_UNDO 40004
+#define IDM_COPY 40005
+#define IDM_PASTE 40006
+#define IDM_CUT 40007
+#define IDM_CLEAR 40008
+#define WS_EX_LAYERED 0x00080000
+#define LWA_ALPHA 0x00000002
+
+#define IDC_LISTREMINDERS 1000
+#define IDC_LISTREMINDERS_HEADER 2000
+#define IDC_REMINDERDATA 1001
+#define IDC_ADDNEWREMINDER 1002
+#define IDC_CLOSE 1003
+#define WM_RELOAD (WM_USER + 100)
+
+#define NOTIFY_LIST() if (ListNotesVisible) PostMessage(LV,WM_RELOAD,0,0)
+
+#define PENLINK ENLINK *
+
+#define NOTE_WND_CLASS "MIM_StickyNote"
+
+
+#define IDM_COLORPRESET_BG 41000
+#define IDM_COLORPRESET_FG 41100
+
+
+static BOOL ListNotesVisible = FALSE;
+static HWND LV;
+
+
+struct ColorPreset
+{
+ TCHAR *szName;
+ COLORREF color;
+};
+
+static struct ColorPreset clrPresets[] =
+{
+ {"Black", RGB(0,0,0)},
+ {"Maroon", RGB(128,0,0)},
+ {"Green", RGB(0,128,0)},
+ {"Olive", RGB(128,128,0)},
+ {"Navy", RGB(0,0,128)},
+ {"Purple", RGB(128,0,128)},
+ {"Teal", RGB(0,128,128)},
+ {"Gray", RGB(128,128,128)},
+ {"Silver", RGB(192,192,192)},
+ {"Red", RGB(255,0,0)},
+ {"Orange", RGB(255,155,0)},
+ {"Lime", RGB(0,255,0)},
+ {"Yellow", RGB(255,255,0)},
+ {"Blue", RGB(0,0,255)},
+ {"Fuchsia", RGB(255,0,255)},
+ {"Aqua", RGB(0,255,255)},
+ {"White", RGB(255,255,255)}
+};
+
+
+TREEELEMENT *g_Stickies = NULL;
+
+
+int CALLBACK StickyNoteWndProc(HWND hdlg,UINT message,
+ WPARAM wParam,LPARAM lParam);
+int CALLBACK DlgProcViewNotes(HWND Dialog,UINT Message,WPARAM wParam,
+ LPARAM lParam);
+void JustSaveNotes(void);
+int PluginMenuCommandAddNew(WPARAM w,LPARAM l);
+int PluginMenuCommandDeleteAll(WPARAM w,LPARAM l);
+void GetTriggerTimeString(const ULARGE_INTEGER *When, char *s, UINT strSize, BOOL bUtc);
+void OnListResize(HWND Dialog);
+void UpdateGeomFromWnd(HWND Dialog, int *geom, int *colgeom, int nCols);
+void FileTimeToTzLocalST(const FILETIME *lpUtc, SYSTEMTIME *tmLocal);
+
+
+COLORREF GetCaptionColor(COLORREF bodyClr)
+{
+ const DWORD r = ((bodyClr & 0xff) * 4) / 5;
+ const DWORD g = (((bodyClr & 0xff00) * 4) / 5) & 0xff00;
+ const DWORD b = (((bodyClr & 0xff0000) * 4) / 5) & 0xff0000;
+
+ return (COLORREF)(r|g|b);
+}
+
+
+static void EnsureUniqueID(STICKYNOTE *TSN)
+{
+ TREEELEMENT *TTE;
+
+ if (!g_Stickies)
+ return;
+
+try_next:
+
+ // check existing notes if id is in use
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ if (((STICKYNOTE*)TTE->ptrdata)->ID.QuadPart == TSN->ID.QuadPart)
+ {
+ // id in use, try new (increases the ID/time stamp by 100 nanosecond steps until an unused time is found,
+ // allthough it's very unlikely that there will be duplicated id's it's better to make 100% sure)
+ TSN->ID.QuadPart++;
+ goto try_next;
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+}
+
+
+static void InitNoteTitle(STICKYNOTE *TSN)
+{
+ if (g_NoteTitleDate)
+ {
+ char TempStr[MAX_PATH];
+ SYSTEMTIME tm;
+ LCID lc = GetUserDefaultLCID();
+
+ TempStr[0] = 0;
+
+ memset(&tm, 0, sizeof(tm));
+ FileTimeToTzLocalST((FILETIME*)&TSN->ID, &tm);
+
+ if ( GetDateFormat(lc, 0, &tm, GetDateFormatStr(), TempStr, MAX_PATH) )
+ {
+ // append time if requested
+ if (g_NoteTitleTime)
+ {
+ int n = strlen(TempStr);
+ TempStr[n++] = ' ';
+ TempStr[n] = 0;
+
+ GetTimeFormat(MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),0), 0, &tm, GetTimeFormatStr(), TempStr+n, MAX_PATH-n);
+ }
+
+ TSN->title = _strdup(TempStr);
+ }
+ }
+
+ TSN->CustomTitle = FALSE;
+}
+
+
+static void InitStickyNoteLogFont(STICKYNOTEFONT *pCustomFont, LOGFONT *lf)
+{
+ if (!pCustomFont->size)
+ {
+ HDC hdc;
+
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, FALSE);
+ lf->lfHeight = 10;
+ hdc = GetDC(0);
+ lf->lfHeight = -MulDiv(lf->lfHeight,GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ ReleaseDC(0, hdc);
+ }
+ else
+ {
+ lf->lfHeight = pCustomFont->size;
+ }
+
+ _tcscpy(lf->lfFaceName, pCustomFont->szFace);
+
+ lf->lfWidth = lf->lfEscapement = lf->lfOrientation = 0;
+ lf->lfWeight = pCustomFont->style & DBFONTF_BOLD ? FW_BOLD : FW_NORMAL;
+ lf->lfItalic = (pCustomFont->style & DBFONTF_ITALIC) != 0;
+ lf->lfUnderline = (pCustomFont->style & DBFONTF_UNDERLINE) != 0;
+ lf->lfStrikeOut = (pCustomFont->style & DBFONTF_STRIKEOUT) != 0;
+ lf->lfCharSet = pCustomFont->charset;
+ lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf->lfQuality = DEFAULT_QUALITY;
+ lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+}
+
+static BOOL CreateStickyNoteFont(STICKYNOTEFONT *pCustomFont, LOGFONT *plf)
+{
+ LOGFONT lf = {0};
+
+ if (!plf)
+ {
+ InitStickyNoteLogFont(pCustomFont, &lf);
+ plf = &lf;
+ }
+
+ if (pCustomFont->hFont)
+ DeleteObject(pCustomFont->hFont);
+
+ pCustomFont->hFont = CreateFontIndirect(plf);
+
+ return pCustomFont->hFont != NULL;
+}
+
+
+STICKYNOTE* NewNoteEx(int Ax,int Ay,int Aw,int Ah,char *Data,ULARGE_INTEGER *ID,BOOL Visible,BOOL OnTop,int scrollV,COLORREF bgClr,COLORREF fgClr,char *Title,STICKYNOTEFONT *pCustomFont,BOOL bLoading)
+{
+ STICKYNOTE* TSN;
+ WNDCLASSEX TWC = {0};
+ WINDOWPLACEMENT TWP;
+ DWORD L1,L2;
+ SYSTEMTIME tm;
+ char TempStr[MAX_PATH] = {0};
+ char *TData;
+
+ const BOOL bIsStartup = Visible & 0x10000;
+ Visible &= ~0x10000;
+
+ if (Data) TData = Data; else TData = NULL;
+
+ if (!GetClassInfoEx(hmiranda, NOTE_WND_CLASS, &TWC))
+ {
+ TWC.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE;
+ TWC.cbClsExtra = 0;
+ TWC.cbWndExtra = 0;
+ TWC.hInstance = hmiranda;
+ TWC.hIcon = LoadIcon(0,IDI_APPLICATION);
+ TWC.hCursor = LoadCursor(0,IDC_ARROW);
+ TWC.hbrBackground = 0;
+ TWC.lpszMenuName = 0;
+ TWC.lpszClassName = NOTE_WND_CLASS;
+ TWC.cbSize = sizeof(WNDCLASSEX);
+ TWC.lpfnWndProc = (WNDPROC)StickyNoteWndProc;
+ if (!RegisterClassEx(&TWC)) return NULL;
+ }
+
+ if (!TData || Aw < 0 || Ah < 0)
+ {
+ TWP.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(GetDesktopWindow(),&TWP);
+ Aw = g_NoteWidth; Ah = g_NoteHeight;
+ Ax = ((TWP.rcNormalPosition.right - TWP.rcNormalPosition.left) / 2) - (Aw / 2);
+ Ay = ((TWP.rcNormalPosition.bottom - TWP.rcNormalPosition.top) / 2) - (Ah / 2);
+ }
+
+ TSN = (STICKYNOTE*)malloc(sizeof(STICKYNOTE));
+
+ if (ID)
+ {
+ TSN->ID = *ID;
+ }
+ else
+ {
+ GetSystemTime(&tm);
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&TSN->ID);
+ }
+
+ EnsureUniqueID(TSN);
+
+ TreeAdd(&g_Stickies,TSN);
+
+ if (!TData)
+ {
+ TData = _strdup("");
+ TSN->data = TData;
+ }
+ else
+ TSN->data = TData;
+
+ // init note title (time-stamp)
+ if (Title)
+ {
+ TSN->title = Title;
+ TSN->CustomTitle = TRUE;
+ }
+ else
+ {
+ TSN->title = NULL;
+ InitNoteTitle(TSN);
+ }
+
+ TSN->Visible = Visible;
+ TSN->OnTop = OnTop;
+
+ TSN->BgColor = bgClr;
+ TSN->FgColor = fgClr;
+
+ TSN->pCustomFont = pCustomFont;
+
+ L1 = WS_EX_TOOLWINDOW;
+ if (MySetLayeredWindowAttributes && g_Transparency < 255) L1 |= WS_EX_LAYERED;
+ if (OnTop) L1 |= WS_EX_TOPMOST;
+
+ L2 = WS_POPUP | WS_THICKFRAME | WS_CAPTION;
+
+ // NOTE: loaded note positions stem from GetWindowPlacement, which normally have a different coord space than
+ // CreateWindow/SetWindowPos, BUT since we now use WS_EX_TOOLWINDOW they use the same coord space so
+ // we don't have to worry about notes "drifting" between sessions
+ TSN->SNHwnd = CreateWindowEx(L1, NOTE_WND_CLASS, _T("StickyNote"), L2, Ax,Ay,Aw,Ah, NULL, 0, hmiranda, TSN);
+
+ if (MySetLayeredWindowAttributes && g_Transparency < 255)
+ MySetLayeredWindowAttributes(TSN->SNHwnd,0,(BYTE)g_Transparency,LWA_ALPHA);
+
+ // ensure that window is not placed off-screen (if previous session had different monitor count or resolution)
+ // NOTE: SetWindowPlacement should do this, but it's extremly flakey
+ if (Data)
+ {
+ if (MyMonitorFromWindow && !MyMonitorFromWindow(TSN->SNHwnd, MONITOR_DEFAULTTONULL) )
+ {
+ TWP.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(GetDesktopWindow(), &TWP);
+
+ if (Aw > 500) Aw = 500;
+ if (Ay < TWP.rcNormalPosition.left+10 || Ax > TWP.rcNormalPosition.right-120)
+ Ax = ((TWP.rcNormalPosition.right - TWP.rcNormalPosition.left) / 2) - (Aw / 2) + (rand() & 0x3f);
+ if (Ay < TWP.rcNormalPosition.top+50 || Ay > TWP.rcNormalPosition.bottom-50)
+ Ay = ((TWP.rcNormalPosition.bottom - TWP.rcNormalPosition.top) / 4) + (rand() & 0x1f);
+
+ SetWindowPos(TSN->SNHwnd, NULL, Ax, Ay, Aw, Ah, SWP_NOZORDER|SWP_NOACTIVATE);
+ }
+ }
+
+ if (Visible)
+ {
+ ShowWindow(TSN->SNHwnd, SW_SHOWNA);
+
+ // when loading notes (only at startup), place all non-top notes at the bottom so they don't cover other windows
+ if (Data && !OnTop && bIsStartup)
+ SetWindowPos(TSN->SNHwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_ASYNCWINDOWPOS);
+ }
+
+ if (scrollV)
+ {
+ SendMessage(TSN->REHwnd, EM_LINESCROLL, 0, scrollV);
+ }
+
+ // make sure that any event triggered by init doesn't cause a meaningless save
+ KillTimer(TSN->SNHwnd, 1025);
+
+ if (!bLoading)
+ {
+ NOTIFY_LIST();
+ }
+
+ return TSN;
+}
+
+STICKYNOTE* NewNote(int Ax,int Ay,int Aw,int Ah,char *Data,ULARGE_INTEGER *ID,BOOL Visible,BOOL OnTop,int scrollV)
+{
+ return NewNoteEx(Ax,Ay,Aw,Ah,Data,ID,Visible,OnTop,scrollV,0,0,NULL,NULL,FALSE);
+}
+
+void LoadNotes(BOOL bIsStartup)
+{
+ int I;
+ int NotesCount;
+ WORD Size;
+ char *Value = NULL, *TVal = NULL;
+ char ValueName[32];
+
+ g_Stickies = NULL;
+
+ NotesCount = ReadSettingInt(0,MODULENAME,"NotesData",0);
+
+ for (I = 0; I < NotesCount; I++)
+ {
+ char *DelPos;
+
+ sprintf(ValueName, "NotesData%d", I);
+
+ if (Value)
+ {
+ FreeSettingBlob(Size, Value);
+ Value = NULL;
+ }
+
+ Size = 65535; // does not get used
+
+ ReadSettingBlob(0, MODULENAME, ValueName, &Size, (void**)&Value);
+
+ if (!Size || !Value)
+ continue; // the setting could not be read from DB -> skip
+
+ if (Value[0] == 'X')
+ {
+ // new eXtended/fleXible data format
+
+ STICKYNOTE note = {0};
+ int i, rect[4];
+ int scrollV = 0;
+ STICKYNOTEFONT *pCustomFont = NULL;
+ DWORD flags;
+
+ DelPos = strchr(Value+1,0x1B);
+ if (DelPos)
+ *DelPos = 0;
+
+ // id:x:y:w:h:flags
+
+ TVal = strchr(Value+1, ':');
+ if (!TVal || (DelPos && TVal > DelPos))
+ continue;
+ *TVal++ = 0;
+
+ note.ID.QuadPart = _strtoui64(Value+1, NULL, 16);
+
+ for (i=0; i<4; i++)
+ {
+ char *sep = strchr(TVal, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+ *sep++ = 0;
+
+ rect[i] = strtol(TVal, NULL, 10);
+
+ TVal = sep;
+ }
+
+ flags = strtoul(TVal, NULL, 16);
+
+ if (flags & 1)
+ note.Visible = TRUE;
+ if (flags & 2)
+ note.OnTop = TRUE;
+
+ // optional \033 separated params
+ while (DelPos)
+ {
+ char *sep;
+ UINT tag;
+
+ TVal = DelPos + 1;
+ // find param end and make sure it's null-terminated (if end of data then it's already null-terminated)
+ DelPos = strchr(TVal, 0x1B);
+ if (DelPos)
+ *DelPos = 0;
+
+ // tag:<data>
+
+ sep = strchr(TVal, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+
+ tag = strtoul(TVal, NULL, 10);
+ TVal = sep + 1;
+
+ switch (tag)
+ {
+ case DATATAG_TEXT:
+ note.data = _strdup(TVal);
+ break;
+
+ case DATATAG_SCROLLPOS:
+ scrollV = (int)strtoul(TVal, NULL, 10);
+ break;
+
+ case DATATAG_BGCOL:
+ note.BgColor = strtoul(TVal, NULL, 16) | 0xff000000;
+ break;
+
+ case DATATAG_FGCOL:
+ note.FgColor = strtoul(TVal, NULL, 16) | 0xff000000;
+ break;
+
+ case DATATAG_TITLE:
+ if (strlen(TVal) > MAX_TITLE_LEN)
+ TVal[MAX_TITLE_LEN] = 0;
+ note.title = _strdup(TVal);
+ note.CustomTitle = TRUE;
+ break;
+
+ case DATATAG_FONT:
+ {
+ int fsize;
+ UINT fstyle, fcharset;
+
+ char *TVal2 = TVal;
+ sep = strchr(TVal2, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+ *sep++ = 0;
+ fsize = strtol(TVal2, NULL, 10);
+ TVal2 = sep;
+
+ sep = strchr(TVal2, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+ *sep++ = 0;
+ fstyle = strtoul(TVal2, NULL, 10);
+ TVal2 = sep;
+
+ sep = strchr(TVal2, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+ *sep++ = 0;
+ fcharset = strtoul(TVal2, NULL, 10);
+ TVal2 = sep;
+
+ if (TVal2 >= DelPos)
+ goto skip;
+
+ pCustomFont = (STICKYNOTEFONT*)malloc(sizeof(STICKYNOTEFONT));
+ pCustomFont->size = (char)fsize;
+ pCustomFont->style = (BYTE)fstyle;
+ pCustomFont->charset = (BYTE)fcharset;
+ _tcscpy(pCustomFont->szFace, TVal2);
+ pCustomFont->hFont = NULL;
+
+ if ( !CreateStickyNoteFont(pCustomFont, NULL) )
+ {
+ free(pCustomFont);
+ pCustomFont = NULL;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!note.data)
+ note.data = _strdup("");
+
+ note.Visible = note.Visible && (!bIsStartup || g_ShowNotesAtStart);
+ if (bIsStartup)
+ note.Visible |= 0x10000;
+
+ NewNoteEx(rect[0],rect[1],rect[2],rect[3],note.data,&note.ID,note.Visible,note.OnTop,scrollV,note.BgColor,note.FgColor,note.title,pCustomFont,TRUE);
+ }
+ else
+ {
+ // old format (for DB backward compatibility)
+
+ int Tx,Ty,Tw,Th,TV,OT;
+ BOOL V;
+ char *Data,*ID;
+ ULARGE_INTEGER newid;
+
+ OT = 1; TV = 1;
+ Tx = 100; Ty = 100;
+ Tw = 179; Th = 35;
+ Data = NULL; ID = NULL;
+
+ if (DelPos = strchr(Value,0x1B))
+ { // get first delimiter
+ int PartLen = DelPos - TVal;
+
+ Data = NULL;
+ ID = NULL;
+ TVal = Value;
+ DelPos[0] = 0x0;
+ Tx = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ Ty = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ Tw = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ Th = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ TV = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ OT = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ Data = _strdup(TVal);
+
+ TVal = DelPos + 1;
+ ID = TVal;
+
+ V = (BOOL)TV && (!bIsStartup || g_ShowNotesAtStart);
+
+ if (bIsStartup)
+ V |= 0x10000;
+
+ // convert old ID format to new
+ if ( strchr(ID, '-') )
+ {
+ // validate format (otherwise create new)
+ if (strlen(ID) < 19 || ID[2] != '-' || ID[5] != '-' || ID[10] != ' ' || ID[13] != ':' || ID[16] != ':')
+ {
+ ID = NULL;
+ }
+ else
+ {
+ SYSTEMTIME tm;
+
+ ID[2] = ID[5] = ID[10] = ID[13] = ID[16] = 0;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.wDay = (WORD)strtoul(ID, NULL, 10);
+ tm.wMonth = (WORD)strtoul(ID+3, NULL, 10);
+ tm.wYear = (WORD)strtoul(ID+6, NULL, 10);
+ tm.wHour = (WORD)strtoul(ID+11, NULL, 10);
+ tm.wMinute = (WORD)strtoul(ID+14, NULL, 10);
+ tm.wSecond = (WORD)strtoul(ID+17, NULL, 10);
+
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&newid);
+ }
+ }
+ else
+ {
+ ID = NULL;
+ }
+
+ NewNoteEx(Tx,Ty,Tw,Th,Data,ID?&newid:NULL,V,(BOOL)OT,0,0,0,NULL,NULL,TRUE);
+ }
+ }
+skip:;
+ }
+
+ if (Value)
+ FreeSettingBlob(Size, Value); // we do not leak on bad setting
+
+ NOTIFY_LIST();
+}
+
+void CloseNotesList()
+{
+ if (ListNotesVisible)
+ {
+ DestroyWindow(LV);
+ ListNotesVisible = FALSE;
+ }
+}
+
+static void PurgeNotesTree()
+{
+ STICKYNOTE *pt;
+
+ while (g_Stickies) // empty whole tree
+ {
+ pt = (STICKYNOTE*)g_Stickies->ptrdata;
+ if (pt->SNHwnd) DestroyWindow(pt->SNHwnd);
+ SAFE_FREE((void**)&pt->title);
+ SAFE_FREE((void**)&pt->data);
+ if (pt->pCustomFont)
+ {
+ DeleteObject(pt->pCustomFont->hFont);
+ free(pt->pCustomFont);
+ }
+ TreeDelete(&g_Stickies,pt);
+ SAFE_FREE((void**)&pt);
+ }
+ g_Stickies = NULL;
+}
+
+void SaveNotes(void)
+{
+ JustSaveNotes();
+ PurgeNotesTree();
+}
+
+void PurgeNotes(void)
+{
+ int NotesCount, I;
+ char ValueName[16];
+
+ NotesCount = ReadSettingInt(0,MODULENAME,"NotesData",0);
+ for(I = 0; I < NotesCount; I++)
+ {
+ sprintf(ValueName, "NotesData%d", I);
+ DBDeleteContactSetting(0,MODULENAME,ValueName);
+ }
+}
+
+void OnDeleteNote(HWND hdlg, STICKYNOTE *SN)
+{
+ if (MessageBox(hdlg, Translate("Are you sure you want to delete this note?"), SECTIONNAME, MB_OKCANCEL) == IDOK)
+ {
+ DestroyWindow(hdlg);
+ TreeDelete(&g_Stickies,SN);
+ SAFE_FREE((void**)&SN->data);
+ if (SN->pCustomFont)
+ {
+ DeleteObject(SN->pCustomFont->hFont);
+ free(SN->pCustomFont);
+ }
+ SAFE_FREE((void**)&SN);
+ JustSaveNotes();
+ }
+}
+
+void DeleteNotes(void)
+{
+ PurgeNotes();
+ WriteSettingInt(0, MODULENAME, "NotesData", 0);
+ PurgeNotesTree();
+}
+
+void ShowHideNotes(void)
+{
+ TREEELEMENT *TTE;
+ int bShow;
+ UINT nHideCount, nVisCount;
+ BOOL Visible;
+
+ if (!g_Stickies)
+ return;
+
+ // if some notes are hidden but others visible then first make all visible
+ // only toggle vis state if all are hidden or all are visible
+
+ nHideCount = nVisCount = 0;
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ if (((STICKYNOTE*)TTE->ptrdata)->Visible)
+ nVisCount++;
+ else
+ nHideCount++;
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ if (!nVisCount)
+ Visible = TRUE;
+ else if (!nHideCount)
+ Visible = FALSE;
+ else
+ Visible = TRUE;
+
+ bShow = Visible ? SW_SHOWNA : SW_HIDE;
+
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)TTE->ptrdata;
+
+ if ((!Visible) != (!SN->Visible))
+ {
+ ShowWindow(SN->SNHwnd, bShow);
+ SN->Visible = Visible;
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ JustSaveNotes();
+}
+
+void BringAllNotesToFront(STICKYNOTE *pActive)
+{
+ TREEELEMENT *TTE;
+
+ if (!g_Stickies)
+ return;
+
+ // NOTE: for some reason there are issues when bringing to top through hotkey while another app (like Explorer)
+ // is active, it refuses to move notes to top like it should with HWND_TOP. as a workaround still doesn't
+ // work 100% of the time, but at least more often, we first move not to top-most then for non-always-on-top
+ // notes we demote them back as a non top-most window
+
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)TTE->ptrdata;
+
+ if (SN->Visible && pActive != SN)
+ {
+ SetWindowPos(SN->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ if (!SN->OnTop)
+ SetWindowPos(SN->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ if (pActive)
+ {
+ SetWindowPos(pActive->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ if (!pActive->OnTop)
+ SetWindowPos(pActive->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ }
+}
+
+static void JustSaveNotesEx(STICKYNOTE *pModified)
+{
+ // pModified optionally points to the modified note that invoked the JustSaveNotesEx call
+
+ TREEELEMENT *TTE;
+ int I, NotesCount = TreeGetCount(g_Stickies);
+ int n, l;
+ char ValueName[32];
+ WINDOWPLACEMENT wp;
+ int TX,TY,TW,TH;
+ DWORD flags;
+ int SzT;
+ int scrollV;
+ char *tData, *Value;
+
+ const int OldNotesCount = ReadSettingInt(0, MODULENAME, "NotesData", 0);
+
+ WriteSettingInt(0, MODULENAME, "NotesData", NotesCount);
+
+ for (TTE = g_Stickies, I = 0; TTE; TTE = (TREEELEMENT*)TTE->next, I++)
+ {
+ STICKYNOTE *pNote = (STICKYNOTE*)TTE->ptrdata;
+ BOOL bDeleteTData = TRUE;
+ scrollV = 0;
+ tData = NULL;
+
+ // window pos and size
+ wp.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(pNote->SNHwnd, &wp);
+ TX = wp.rcNormalPosition.left;
+ TY = wp.rcNormalPosition.top;
+ TW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+ TH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+
+ // set flags
+ flags = 0;
+ if (pNote->Visible) flags |= 1;
+ if (pNote->OnTop) flags |= 2;
+
+ // get note text
+ SzT = SendMessage(pNote->REHwnd, WM_GETTEXTLENGTH, 0, 0);
+ if (SzT) // TODO: change to support unicode and rtf, use EM_STREAMOUT
+ {
+ if (SzT > MAX_NOTE_LEN) SzT = MAX_NOTE_LEN; // we want to be far below the 64k limit
+ tData = (char*)malloc(SzT+1);
+ if (tData)
+ SendMessage(pNote->REHwnd, WM_GETTEXT, SzT+1, (LPARAM)tData);
+ }
+
+ if (pNote == pModified)
+ {
+ // update the data of the modified note
+ if (pNote->data)
+ free(pNote->data);
+ pNote->data = tData ? tData : _strdup("");
+ bDeleteTData = FALSE;
+ }
+
+ if (!tData)
+ // empty note
+ SzT = 0;
+ else
+ // get current scroll position
+ scrollV = SendMessage(pNote->REHwnd, EM_GETFIRSTVISIBLELINE, 0, 0);
+
+ //
+
+ Value = (char*)malloc(SzT + 512);
+
+ if (!Value)
+ {
+ if (bDeleteTData)
+ SAFE_FREE((void**)&tData);
+ continue;
+ }
+
+ n = 0;
+
+ // data header
+ l = sprintf(Value, "X%I64x:%d:%d:%d:%d:%x", pNote->ID.QuadPart, TX, TY, TW, TH, flags);
+ if (l > 0) n += l;
+
+ // scroll pos
+ if (scrollV > 0)
+ {
+ l = sprintf(Value+n, "\033""%u:%u", DATATAG_SCROLLPOS, (UINT)scrollV);
+ if (l > 0) n += l;
+ }
+
+ // custom bg color
+ if (pNote->BgColor)
+ {
+ l = sprintf(Value+n, "\033""%u:%x", DATATAG_BGCOL, (UINT)(pNote->BgColor&0xffffff));
+ if (l > 0) n += l;
+ }
+
+ // custom fg color
+ if (pNote->FgColor)
+ {
+ l = sprintf(Value+n, "\033""%u:%x", DATATAG_FGCOL, (UINT)(pNote->FgColor&0xffffff));
+ if (l > 0) n += l;
+ }
+
+ if (pNote->pCustomFont)
+ {
+ l = sprintf(Value+n, "\033""%u:%d:%u:%u:%s", DATATAG_FONT,
+ (int)pNote->pCustomFont->size, (UINT)pNote->pCustomFont->style, (UINT)pNote->pCustomFont->charset,
+ pNote->pCustomFont->szFace);
+ if (l > 0) n += l;
+ }
+
+ // custom title
+ if (pNote->CustomTitle && pNote->title)
+ {
+ l = sprintf(Value+n, "\033""%u:%s", DATATAG_TITLE, pNote->title);
+ if (l > 0) n += l;
+ }
+
+ // note text (ALWAYS PUT THIS PARAM LAST)
+ if (tData)
+ {
+ l = sprintf(Value+n, "\033""%u:%s", DATATAG_TEXT, tData);
+ if (l > 0) n += l;
+ }
+
+ // clamp data size to WORD (including null terminator)
+ if (n >= 0xffff)
+ {
+ // huston, we have a problem, strip some reminder text
+ n = 0xfffe;
+ ValueName[0xffff] = 0;
+ }
+
+ sprintf(ValueName, "NotesData%d", NotesCount - I - 1); // we do not reverse notes in DB
+
+ WriteSettingBlob(0, MODULENAME, ValueName, (WORD)(n+1), Value);
+
+ SAFE_FREE((void**)&Value);
+ if (bDeleteTData)
+ SAFE_FREE((void**)&tData);
+
+ // make no save is queued for the note
+ if (pNote->SNHwnd)
+ KillTimer(pNote->SNHwnd, 1025);
+ }
+
+ // delete any left over DB note entries
+ for(; I < OldNotesCount; I++)
+ {
+ sprintf(ValueName, "NotesData%d", I);
+ DBDeleteContactSetting(0,MODULENAME,ValueName);
+ }
+
+ NOTIFY_LIST();
+}
+
+__inline void JustSaveNotes(void)
+{
+ JustSaveNotesEx(NULL);
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// Note Window
+
+static int FindMenuItem(HMENU h, LPTSTR lpszName)
+{
+ int n;
+ UINT i;
+ TCHAR s[128];
+
+ n = GetMenuItemCount(h);
+
+ if (n <= 0)
+ {
+ return -1;
+ }
+
+ // searches for a menu item based on name (used to avoid hardcoding item indices for sub-menus)
+ for (i=0; i<(UINT)n; i++)
+ {
+ if ( GetMenuString(h, i, s, 128, MF_BYPOSITION) )
+ {
+ if ( !_tcscmp(s, lpszName) )
+ {
+ return (int)i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static BOOL DoContextMenu(HWND AhWnd,WPARAM wParam,LPARAM lParam)
+{
+ int n, i;
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(AhWnd,"ctrldata");
+
+ HMENU hMenuLoad, FhMenu, hSub;
+ hMenuLoad = LoadMenu(hinstance,"MNU_NOTEPOPUP");
+ FhMenu = GetSubMenu(hMenuLoad,0);
+
+ if (SN->OnTop)
+ CheckMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_CHECKED|MF_BYCOMMAND);
+
+ EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_PASTETITLE, MF_BYCOMMAND | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
+
+ if (!SN->CustomTitle)
+ EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_RESETTITLE, MF_BYCOMMAND | MF_GRAYED);
+
+ // NOTE: names used for FindMenuItem would need to include & chars if such shortcuts are added to the menus
+
+ // color preset menu items uses features that require win2k or later, I'm too lazy to make a fallback path
+ // for obsolete OS versions (win95/98/ME users can still use the "Custom" menu option)
+ if (g_isWin2kPlus)
+ {
+ n = FindMenuItem(FhMenu, _T("Appearance"));
+ if (n >= 0 && (hSub = GetSubMenu(FhMenu, n)))
+ {
+ HMENU hBg = GetSubMenu(hSub, FindMenuItem(hSub, _T("Background Color")));
+ HMENU hFg = GetSubMenu(hSub, FindMenuItem(hSub, _T("Text Color")));
+
+ for (i=0; i<SIZEOF(clrPresets); i++)
+ InsertMenu(hBg, i, MF_BYPOSITION|MF_OWNERDRAW, IDM_COLORPRESET_BG+i, TranslateT(clrPresets[i].szName));
+
+ for (i=0; i<SIZEOF(clrPresets); i++)
+ InsertMenu(hFg, i, MF_BYPOSITION|MF_OWNERDRAW, IDM_COLORPRESET_FG+i, TranslateT(clrPresets[i].szName));
+ }
+ }
+
+ CallService(MS_LANGPACK_TRANSLATEMENU,(DWORD)FhMenu,0);
+ TrackPopupMenu(FhMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON,LOWORD(lParam),HIWORD(lParam),0,AhWnd,0);
+ DestroyMenu(hMenuLoad);
+
+ return TRUE;
+}
+
+static void MeasureColorPresetMenuItem(HWND hdlg, LPMEASUREITEMSTRUCT lpMeasureItem, struct ColorPreset *clrPresets)
+{
+ HDC hdc = GetDC(hdlg);
+ LPTSTR lpsz = TranslateT(clrPresets->szName);
+ SIZE sz;
+ GetTextExtentPoint32(hdc, lpsz, _tcslen(lpsz), &sz);
+ ReleaseDC(hdlg, hdc);
+
+ lpMeasureItem->itemWidth = 50 + sz.cx;
+ lpMeasureItem->itemHeight = (sz.cy+2)>18 ? sz.cy+2 : 18;
+}
+
+static void PaintColorPresetMenuItem(LPDRAWITEMSTRUCT lpDrawItem, struct ColorPreset *clrPresets)
+{
+ UINT n = lpDrawItem->itemID - IDM_COLORPRESET_BG;
+ RECT rect;
+ rect.left = lpDrawItem->rcItem.left + 50;
+ rect.top = lpDrawItem->rcItem.top;
+ rect.right = lpDrawItem->rcItem.right;
+ rect.bottom = lpDrawItem->rcItem.bottom;
+
+ if (lpDrawItem->itemState & ODS_SELECTED)
+ {
+ SetDCBrushColor(lpDrawItem->hDC, GetSysColor(COLOR_MENUHILIGHT));
+ FillRect(lpDrawItem->hDC, &lpDrawItem->rcItem, (HBRUSH)GetStockObject(DC_BRUSH));
+
+ SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ else
+ {
+ SetDCBrushColor(lpDrawItem->hDC, GetSysColor(COLOR_MENU));
+ FillRect(lpDrawItem->hDC, &lpDrawItem->rcItem, (HBRUSH)GetStockObject(DC_BRUSH));
+
+ SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_MENUTEXT));
+ }
+
+ SetBkMode(lpDrawItem->hDC, TRANSPARENT);
+ DrawText(lpDrawItem->hDC,clrPresets->szName,-1,&rect,DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS | DT_VCENTER);
+
+ {
+ int h = lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top;
+ rect.left = lpDrawItem->rcItem.left + 5;
+ rect.top = lpDrawItem->rcItem.top + ((h-14)>>1);
+ rect.right = rect.left + 40;
+ rect.bottom = rect.top + 14;
+
+ FrameRect(lpDrawItem->hDC, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ rect.left++; rect.top++;
+ rect.right--; rect.bottom--;
+ SetDCBrushColor(lpDrawItem->hDC, clrPresets->color);
+ FillRect(lpDrawItem->hDC, &rect, (HBRUSH)GetStockObject(DC_BRUSH));
+ }
+}
+
+static BOOL GetClipboardText_Title(char *pOut, int size)
+{
+ BOOL bResult = FALSE;
+
+ if ( OpenClipboard(NULL) )
+ {
+ HANDLE hData = GetClipboardData(CF_TEXT);
+ LPCSTR buffer;
+
+ if (hData && (buffer = (LPCSTR)GlobalLock(hData)))
+ {
+ // trim initial white spaces
+ while (*buffer && isspace(*buffer))
+ buffer++;
+
+ if (buffer)
+ {
+ char *p;
+
+ int n = (int)strlen(buffer);
+ if (n >= size)
+ n = size-1;
+ memcpy(pOut, buffer, n);
+ pOut[n] = 0;
+
+ // end string on line break and convert tabs to spaces
+ p = pOut;
+ while (*p)
+ {
+ if (*p == '\r' || *p == '\n')
+ {
+ *p = 0;
+ n = strlen(pOut);
+ break;
+ }
+ else if (*p == '\t')
+ {
+ *p = ' ';
+ }
+ p++;
+ }
+
+ // trim trailing white spaces
+ while (n && isspace(pOut[n-1]))
+ pOut[--n] = 0;
+
+ if (n)
+ bResult = TRUE;
+ }
+
+ GlobalUnlock(hData);
+ }
+
+ CloseClipboard();
+ }
+
+ return bResult;
+}
+
+static void SetNoteTextControl(STICKYNOTE *SN)
+{
+ CHARFORMAT CF = {0};
+ CF.cbSize = sizeof(CHARFORMAT);
+ CF.dwMask = CFM_COLOR;
+ CF.crTextColor = SN->FgColor ? (SN->FgColor&0xffffff) : BodyFontColor;
+ SendMessage(SN->REHwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF);
+
+ if (SN->data) // TODO: use EM_STREAMIN
+ SendMessage(SN->REHwnd, WM_SETTEXT, 0, (LPARAM)(SN->data));
+}
+
+
+static UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_INITDIALOG)
+ {
+ // hide color selector
+ ShowWindow(GetDlgItem(hdlg,0x443), SW_HIDE);
+ ShowWindow(GetDlgItem(hdlg,0x473), SW_HIDE);
+ TranslateDialogDefault(hdlg);
+ }
+
+ return 0;
+}
+
+
+int CALLBACK StickyNoteWndProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_CLOSE:
+ return TRUE;
+
+ case WM_SIZE:
+ {
+ HWND H;
+ RECT SZ;
+
+ GetClientRect(hdlg,&SZ);
+ H = GetDlgItem(hdlg,1);
+ MoveWindow(H, 0, 0, SZ.right,SZ.bottom, TRUE);
+
+ KillTimer(hdlg, 1025);
+ SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, 0);
+
+ return TRUE;
+ }
+ case WM_TIMER:
+ if (wParam == 1025)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg,"ctrldata");
+
+ KillTimer(hdlg, 1025);
+ JustSaveNotesEx(SN);
+ }
+ break;
+ case WM_MOVE:
+ {
+ KillTimer(hdlg, 1025);
+ SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, 0);
+ return TRUE;
+ }
+ case WM_CREATE:
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg,"ctrldata");
+
+ CREATESTRUCT *CS = (CREATESTRUCT *)lParam;
+ HWND H;
+ DWORD mystyle;
+
+ SN = (STICKYNOTE*)CS->lpCreateParams;
+ SetProp(hdlg,"ctrldata",(HANDLE)SN);
+ BringWindowToTop(hdlg);
+ mystyle = WS_CHILD | WS_VISIBLE | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN;
+ if (g_ShowScrollbar) mystyle |= WS_VSCROLL;
+ H = CreateWindow(RICHEDIT_CLASS, 0, mystyle, 0, 0, CS->cx-3-3, CS->cy-3-(3+14), hdlg, (HMENU)1, hmiranda, 0);
+ SN->REHwnd = H;
+ SendMessage(H, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
+ SendMessage(H, EM_LIMITTEXT, MAX_NOTE_LEN, 0);
+ SendMessage(H, WM_SETFONT, (WPARAM)(SN->pCustomFont ? SN->pCustomFont->hFont : hBodyFont), 1);
+ SendMessage(H, EM_SETEVENTMASK, 0, ENM_CHANGE | ENM_LINK);
+ SendMessage(H, EM_SETBKGNDCOLOR, 0, SN->BgColor ? (SN->BgColor&0xffffff) : BodyColor);
+ SendMessage(H, EM_AUTOURLDETECT, 1, 0);
+ SetNoteTextControl(SN);
+ return TRUE;
+ }
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *mm = (MINMAXINFO*)lParam;
+ // min width accomodates frame, buttons and some extra space for sanity
+ mm->ptMinTrackSize.x = 48+3+3+8 + 40;
+ // min height allows collapsing entire client area, only leaving frame and caption
+ mm->ptMinTrackSize.y = 3+3+14;
+ }
+ return 0;
+ case WM_ERASEBKGND:
+ // no BG needed as edit control takes up entire client area
+ return TRUE;
+ case WM_NCPAINT:
+ // make window borders have the same color as caption
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg,"ctrldata");
+
+ HBRUSH hBkBrush;
+ RECT rect, wr, r;
+ //HDC hdc = GetDCEx(hdlg, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN);
+ HDC hdc = GetWindowDC(hdlg);
+
+ GetWindowRect(hdlg, &wr);
+ if (wParam && wParam != 1)
+ {
+ SelectClipRgn(hdc, (HRGN)wParam);
+ OffsetClipRgn(hdc, -wr.left, -wr.top);
+ }
+
+ rect = wr;
+ OffsetRect(&rect, -wr.left, -wr.top);
+
+ if (g_isWin2kPlus)
+ {
+ hBkBrush = (HBRUSH)GetStockObject(DC_BRUSH);
+ SetDCBrushColor(hdc, GetCaptionColor((SN && SN->BgColor) ? SN->BgColor : BodyColor));
+ }
+ else
+ {
+ hBkBrush = (HBRUSH)CreateSolidBrush( GetCaptionColor((SN && SN->BgColor) ? SN->BgColor : BodyColor) );
+ }
+
+ //FillRect(hdc, &rect, hBkBrush);
+ // draw all frame sides separately to avoid filling client area (which flickers)
+ {
+ // top
+ r.left = rect.left; r.right = rect.right;
+ r.top = rect.top; r.bottom = r.top + 3+14;
+ FillRect(hdc, &r, hBkBrush);
+ // bottom
+ r.top = rect.bottom - 3; r.bottom = rect.bottom;
+ FillRect(hdc, &r, hBkBrush);
+ // left
+ r.left = rect.left; r.right = r.left + 3;
+ r.top = rect.top + 3+14; r.bottom = rect.bottom - 3;
+ FillRect(hdc, &r, hBkBrush);
+ // right
+ r.left = rect.right - 3; r.right = rect.right;
+ FillRect(hdc, &r, hBkBrush);
+ }
+ if (hBkBrush && !g_isWin2kPlus) DeleteObject(hBkBrush);
+
+ // paint title bar contents (time stamp and buttons)
+
+ if (SN && SN->title)
+ {
+ RECT R;
+ SelectObject(hdc,hCaptionFont);
+ R.top = 3+1; R.bottom = 3+11; R.left = 3+2; R.right = rect.right-3-1;
+ if (g_ShowNoteButtons)
+ R.right -= 48;
+
+ SetTextColor(hdc,SN->FgColor ? (SN->FgColor&0xffffff) : CaptionFontColor);
+ SetBkMode(hdc, TRANSPARENT);
+ DrawText(hdc,SN->title,-1,&R,DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS | DT_VCENTER);
+ }
+
+ if (g_ShowNoteButtons)
+ {
+ HICON hcIcon;
+ if (SN->OnTop)
+ hcIcon = Skin_GetIconByHandle(hIconLibItem[4]);
+ else
+ hcIcon = Skin_GetIconByHandle(hIconLibItem[7]);
+ DrawIcon(hdc, wr.right - wr.left - 16, 0 + 3, hcIcon);
+ Skin_ReleaseIcon(hcIcon);
+
+ hcIcon = Skin_GetIconByHandle(hIconLibItem[9]);
+ DrawIcon(hdc, wr.right - wr.left - 32, 1 + 3, hcIcon);
+ Skin_ReleaseIcon(hcIcon);
+
+ hcIcon = Skin_GetIconByHandle(hIconLibItem[8]);
+ DrawIcon(hdc, wr.right - wr.left - 48, 1 + 3, hcIcon);
+ Skin_ReleaseIcon(hcIcon);
+ }
+
+ if (wParam && wParam != 1)
+ {
+ SelectClipRgn(hdc, NULL);
+ }
+
+ ReleaseDC(hdlg, hdc);
+ return TRUE;
+ }
+ case WM_NCCALCSIZE:
+ {
+ RECT *pRect = wParam ? &((NCCALCSIZE_PARAMS*)lParam)->rgrc[0] : (RECT*)lParam;
+ pRect->bottom -= 3;
+ pRect->right -= 3;
+ pRect->left += 3;
+ pRect->top += 3+14;
+ return WVR_REDRAW;
+ }
+ case WM_NCACTIVATE:
+ // update window (so that parts that potentially became visible through activation get redrawn immediately)
+ RedrawWindow(hdlg, NULL, NULL, RDW_UPDATENOW);
+ return TRUE;
+ case WM_NOTIFY:
+ if (LOWORD(wParam) == 1)
+ {
+ char *Buff;
+ PENLINK PEnLnk = (PENLINK)lParam;
+
+ if (PEnLnk->msg == WM_LBUTTONDOWN)
+ {
+ SendDlgItemMessage(hdlg,1,EM_EXSETSEL,0,(LPARAM)&(PEnLnk->chrg));
+ Buff = (char*)malloc(PEnLnk->chrg.cpMax - PEnLnk->chrg.cpMin + 1);
+ SendDlgItemMessage(hdlg,1,EM_GETSELTEXT,0,(LPARAM)Buff);
+ if ((GetAsyncKeyState(VK_CONTROL) >> 15) != 0)
+ ShellExecute(hdlg,"open","iexplore",Buff,"",SW_SHOWNORMAL);
+ else if (g_lpszAltBrowser && *g_lpszAltBrowser)
+ ShellExecute(hdlg,"open",g_lpszAltBrowser,Buff,"",SW_SHOWNORMAL);
+ else
+ ShellExecute(hdlg,"open",Buff,"","",SW_SHOWNORMAL);
+ SAFE_FREE((void**)&Buff);
+ return TRUE;
+ }
+ return FALSE;
+ }
+ break;
+ case WM_NCHITTEST:
+ {
+ int r = DefWindowProc(hdlg,message,wParam,lParam);
+ // filter out potential hits on windows default title bar buttons
+ switch (r)
+ {
+ case HTSYSMENU:
+ case HTCLOSE:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ return HTCAPTION;
+ }
+ return r;
+ }
+ case WM_NCLBUTTONDOWN:
+ if (wParam == HTCAPTION && g_ShowNoteButtons)
+ {
+ long X,Y;
+ RECT rect;
+ int Tw;
+
+ GetWindowRect(hdlg, &rect);
+ Tw = rect.right - rect.left;
+
+ X = LOWORD(lParam) - rect.left;
+ Y = HIWORD(lParam) - rect.top;
+
+ if (X > Tw - 16)
+ {
+ SendMessage(hdlg,WM_COMMAND,IDM_TOGGLEONTOP,0);
+ return TRUE;
+ }
+ else if (X > Tw - 31 && X < Tw - 16)
+ {
+ SendMessage(hdlg,WM_COMMAND,IDM_REMOVENOTE,0);
+ return TRUE;
+ }
+ else if (X > Tw - 48 && X < Tw - 32)
+ {
+ SendMessage(hdlg,WM_COMMAND,IDM_HIDENOTE,0);
+ return TRUE;
+ }
+ }
+ return DefWindowProc(hdlg,message,wParam,lParam);
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpMeasureItem = (LPMEASUREITEMSTRUCT)lParam;
+
+ if (lpMeasureItem->CtlType != ODT_MENU)
+ break;
+
+ if (lpMeasureItem->itemID >= IDM_COLORPRESET_BG && lpMeasureItem->itemID <= IDM_COLORPRESET_BG+SIZEOF(clrPresets))
+ {
+ MeasureColorPresetMenuItem(hdlg, lpMeasureItem, clrPresets + (lpMeasureItem->itemID - IDM_COLORPRESET_BG));
+ return TRUE;
+ }
+ else if (lpMeasureItem->itemID >= IDM_COLORPRESET_FG && lpMeasureItem->itemID <= IDM_COLORPRESET_FG+SIZEOF(clrPresets))
+ {
+ MeasureColorPresetMenuItem(hdlg, lpMeasureItem, clrPresets + (lpMeasureItem->itemID - IDM_COLORPRESET_FG));
+ return TRUE;
+ }
+ }
+ break;
+ case WM_DRAWITEM:
+ if (!wParam)
+ {
+ LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT)lParam;
+
+ if (lpDrawItem->CtlType != ODT_MENU)
+ break;
+
+ if (lpDrawItem->itemID >= IDM_COLORPRESET_BG && lpDrawItem->itemID <= IDM_COLORPRESET_BG+SIZEOF(clrPresets))
+ {
+ PaintColorPresetMenuItem(lpDrawItem, clrPresets + (lpDrawItem->itemID - IDM_COLORPRESET_BG));
+ return TRUE;
+ }
+ else if (lpDrawItem->itemID >= IDM_COLORPRESET_FG && lpDrawItem->itemID <= IDM_COLORPRESET_FG+SIZEOF(clrPresets))
+ {
+ PaintColorPresetMenuItem(lpDrawItem, clrPresets + (lpDrawItem->itemID - IDM_COLORPRESET_FG));
+ return TRUE;
+ }
+ }
+ break;
+ case WM_COMMAND:
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg,"ctrldata");
+
+ HWND H;
+ UINT id;
+
+ switch ( HIWORD(wParam) )
+ {
+ case EN_CHANGE:
+ case EN_VSCROLL:
+ case EN_HSCROLL:
+ {
+ KillTimer(hdlg,1025);
+ SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, 0);
+ }
+ break;
+ }
+
+ id = (UINT) LOWORD(wParam);
+
+ H = SN->REHwnd;
+
+ if (id >= IDM_COLORPRESET_BG && id <= IDM_COLORPRESET_BG+SIZEOF(clrPresets))
+ {
+ SN->BgColor = clrPresets[id-IDM_COLORPRESET_BG].color | 0xff000000;
+ SendMessage(H, EM_SETBKGNDCOLOR, 0, (LPARAM)(SN->BgColor&0xffffff));
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ return FALSE;
+ }
+ else if (id >= IDM_COLORPRESET_FG && id <= IDM_COLORPRESET_FG+SIZEOF(clrPresets))
+ {
+ CHARFORMAT CF = {0};
+ SN->FgColor = clrPresets[id-IDM_COLORPRESET_FG].color | 0xff000000;
+ CF.cbSize = sizeof(CHARFORMAT);
+ CF.dwMask = CFM_COLOR;
+ CF.crTextColor = SN->FgColor & 0xffffff;
+ SendMessage(H, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ return FALSE;
+ }
+
+ switch (id)
+ {
+ case ID_CONTEXTMENUNOTEPOPUP_NEWNOTE:
+ {
+ PluginMenuCommandAddNew(0,0);
+ }
+ break;
+ case ID_APPEARANCE_CUSTOMBG:
+ {
+ COLORREF custclr[16] = {0};
+ CHOOSECOLOR cc = {0};
+ COLORREF orgclr = SN->BgColor ? (COLORREF)(SN->BgColor&0xffffff) : (COLORREF)(BodyColor&0xffffff);
+ cc.lStructSize = sizeof(cc);
+ cc.hwndOwner = SN->SNHwnd;
+ cc.rgbResult = orgclr;
+ cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT | CC_SOLIDCOLOR;
+ cc.lpCustColors = custclr;
+
+ if (ChooseColor(&cc) && cc.rgbResult != orgclr)
+ {
+ SN->BgColor = cc.rgbResult | 0xff000000;
+ SendMessage(H, EM_SETBKGNDCOLOR, 0, (LPARAM)(SN->BgColor&0xffffff));
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ break;
+ case ID_APPEARANCE_CUSTOMTEXT:
+ {
+ COLORREF custclr[16] = {0};
+ CHOOSECOLOR cc = {0};
+ COLORREF orgclr = SN->FgColor ? (COLORREF)(SN->FgColor&0xffffff) : (COLORREF)(BodyFontColor&0xffffff);
+ cc.lStructSize = sizeof(cc);
+ cc.hwndOwner = SN->SNHwnd;
+ cc.rgbResult = orgclr;
+ cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT | CC_SOLIDCOLOR;
+ cc.lpCustColors = custclr;
+
+ if (ChooseColor(&cc) && cc.rgbResult != orgclr)
+ {
+ CHARFORMAT CF = {0};
+ SN->FgColor = cc.rgbResult | 0xff000000;
+ CF.cbSize = sizeof(CHARFORMAT);
+ CF.dwMask = CFM_COLOR;
+ CF.crTextColor = SN->FgColor & 0xffffff;
+ SendMessage(H, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ break;
+ case ID_APPEARANCE_CUSTOMFONT:
+ {
+ CHOOSEFONT cf = {0};
+ LOGFONT lf = {0};
+
+ if (SN->pCustomFont)
+ InitStickyNoteLogFont(SN->pCustomFont, &lf);
+ else
+ LoadNRFont(NR_FONTID_BODY, &lf, NULL);
+
+ cf.lStructSize = sizeof(cf);
+ cf.hwndOwner = SN->SNHwnd;
+ cf.lpLogFont = &lf;
+ cf.Flags = CF_EFFECTS | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_ENABLEHOOK;
+ cf.lpfnHook = CFHookProc;
+
+ if ( ChooseFont(&cf) )
+ {
+ if (!SN->pCustomFont)
+ {
+ SN->pCustomFont = (STICKYNOTEFONT*)malloc(sizeof(STICKYNOTEFONT));
+ SN->pCustomFont->hFont = NULL;
+ }
+
+ SN->pCustomFont->size = (char)lf.lfHeight;
+ SN->pCustomFont->style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0) | (lf.lfUnderline ? DBFONTF_UNDERLINE : 0) | (lf.lfStrikeOut ? DBFONTF_STRIKEOUT : 0);
+ SN->pCustomFont->charset = lf.lfCharSet;
+ _tcscpy(SN->pCustomFont->szFace, lf.lfFaceName);
+
+ if ( !CreateStickyNoteFont(SN->pCustomFont, &lf) )
+ {
+ // failed
+ free(SN->pCustomFont);
+ SN->pCustomFont = NULL;
+ }
+
+ // clear text first to force a reformatting w.r.t scrollbar
+ SendMessage(H, WM_SETTEXT, 0, (LPARAM)"");
+ SendMessage(H, WM_SETFONT, (WPARAM)(SN->pCustomFont ? SN->pCustomFont->hFont : hBodyFont), FALSE);
+ SetNoteTextControl(SN);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ break;
+ case ID_BACKGROUNDCOLOR_RESET:
+ {
+ SN->BgColor = 0;
+ SendMessage(H, EM_SETBKGNDCOLOR, 0, (LPARAM)BodyColor);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ break;
+ case ID_TEXTCOLOR_RESET:
+ {
+ CHARFORMAT CF = {0};
+ SN->FgColor = 0;
+ CF.cbSize = sizeof(CHARFORMAT);
+ CF.dwMask = CFM_COLOR;
+ CF.crTextColor = BodyFontColor;
+ SendMessage(H, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ break;
+ case ID_FONT_RESET:
+ {
+ if (SN->pCustomFont)
+ {
+ DeleteObject(SN->pCustomFont->hFont);
+ free(SN->pCustomFont);
+ SN->pCustomFont = NULL;
+
+ // clear text first to force a reformatting w.r.t scrollbar
+ SendMessage(H, WM_SETTEXT, 0, (LPARAM)"");
+ SendMessage(H, WM_SETFONT, (WPARAM)hBodyFont, FALSE);
+ SetNoteTextControl(SN);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ break;
+ case ID_CONTEXTMENUNOTEPOPUP_PASTETITLE:
+ {
+ char s[MAX_TITLE_LEN+1];
+ if ( GetClipboardText_Title(s, sizeof(s)) )
+ {
+ if (SN->title)
+ free(SN->title);
+ SN->title = _strdup(s);
+ SN->CustomTitle = TRUE;
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ break;
+ case ID_CONTEXTMENUNOTEPOPUP_RESETTITLE:
+ if (SN->CustomTitle)
+ {
+ if (SN->title)
+ {
+ free(SN->title);
+ SN->title = NULL;
+ }
+ InitNoteTitle(SN);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ break;
+ case IDM_REMOVENOTE:
+ OnDeleteNote(hdlg, SN);
+ break;
+ case IDM_HIDENOTE:
+ {
+ SN->Visible = FALSE;
+ ShowWindow(hdlg,SW_HIDE);
+ JustSaveNotes();
+ }
+ break;
+ case IDM_COPY: SendMessage(H,WM_COPY,0,0); break;
+ case IDM_PASTE: SendMessage(H,WM_PASTE,0,0); break;
+ case IDM_CUT: SendMessage(H,WM_CUT,0,0); break;
+ case IDM_CLEAR: SendMessage(H,WM_CLEAR,0,0); break;
+ case IDM_UNDO: SendMessage(H,WM_UNDO,0,0); break;
+ case IDM_TOGGLEONTOP:
+ {
+ SN->OnTop = !SN->OnTop;
+ SetWindowPos(hdlg, SN->OnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0,0,0,0, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
+ RedrawWindow(hdlg, NULL, NULL, RDW_FRAME|RDW_INVALIDATE|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ break;
+ case ID_CONTEXTMENUNOTEPOPUP_VIEWNOTES:
+ ListNotes();
+ break;
+ case ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP:
+ BringAllNotesToFront(SN);
+ break;
+ }
+ return TRUE;
+ }
+ case WM_NCDESTROY:
+ {
+ RemoveProp(hdlg, "ctrldata");
+ }
+ break;
+ case WM_CONTEXTMENU:
+ if (DoContextMenu(hdlg,wParam,lParam)) return FALSE;
+
+ default:
+ return DefWindowProc(hdlg,message,wParam,lParam);
+ }
+ return FALSE;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// Notes List Dialog (uses same dialog template as reminder list)
+
+void ListNotes(void)
+{
+ if (!ListNotesVisible)
+ {
+ CreateDialog(hinstance, MAKEINTRESOURCE(IDD_LISTREMINDERS), 0, DlgProcViewNotes);
+ ListNotesVisible = TRUE;
+ }
+ else
+ {
+ BringWindowToTop(LV);
+ }
+}
+
+static void EditNote(STICKYNOTE *SN)
+{
+ if (!SN)
+ return;
+
+ if (!SN->Visible)
+ {
+ SN->Visible = TRUE;
+ JustSaveNotes();
+ }
+
+ SetWindowPos(SN->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ if (!SN->OnTop)
+ SetWindowPos(SN->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+
+ SetFocus(SN->REHwnd);
+}
+
+char* GetPreviewString(const char *lpsz)
+{
+ int l;
+ char *p;
+ const int MaxLen = 80;
+ static char s[80+8];
+
+ if (!lpsz)
+ return "";
+
+ // trim leading spaces
+ while ( iswspace(*lpsz) )
+ lpsz++;
+
+ l = strlen(lpsz);
+
+ if (!l)
+ return "";
+
+ if (l <= MaxLen)
+ {
+ strcpy(s, lpsz);
+ }
+ else
+ {
+ memcpy(s, lpsz, MaxLen);
+ s[MaxLen] = '.';
+ s[MaxLen+1] = '.';
+ s[MaxLen+2] = '.';
+ s[MaxLen+3] = 0;
+ }
+
+ if (!s)
+ return NULL;
+
+ // convert line breaks and tabs to spaces
+
+ p = s;
+
+ while (*p)
+ {
+ if ( iswspace(*p) )
+ *p = ' ';
+ p++;
+ }
+
+ return s;
+}
+
+static void InitListView(HWND AHLV)
+{
+ LV_ITEM lvTIt;
+ int I;
+ char *S;
+ char S1[128];
+ STICKYNOTE *pNote;
+ TREEELEMENT *TTE;
+
+ char *V = Translate("V");
+ char *T = Translate("T");
+
+ ListView_SetHoverTime(AHLV,700);
+ ListView_SetExtendedListViewStyle(AHLV,LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_TRACKSELECT);
+ ListView_DeleteAllItems(AHLV);
+
+ I = 0;
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ pNote = (STICKYNOTE*)TTE->ptrdata;
+
+ lvTIt.mask = LVIF_TEXT;
+
+ if (!pNote->CustomTitle || !pNote->title)
+ GetTriggerTimeString(&pNote->ID, S1, sizeof(S1), TRUE);
+
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 0;
+ lvTIt.pszText = (pNote->CustomTitle && pNote->title) ? pNote->title : S1;
+ lvTIt.cchTextMax = strlen(S1);
+ ListView_InsertItem(AHLV,&lvTIt);
+
+ if (pNote->Visible)
+ {
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 1;
+ lvTIt.pszText = V;
+ lvTIt.cchTextMax = strlen(lvTIt.pszText);
+ ListView_SetItem(AHLV,&lvTIt);
+ }
+
+ if (pNote->OnTop)
+ {
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 2;
+ lvTIt.pszText = T;
+ lvTIt.cchTextMax = strlen(lvTIt.pszText);
+ ListView_SetItem(AHLV,&lvTIt);
+ }
+
+ S = GetPreviewString(pNote->data);
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 3;
+ lvTIt.pszText = S;
+ lvTIt.cchTextMax = strlen(S);
+ ListView_SetItem(AHLV,&lvTIt);
+
+ I++;
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ ListView_SetItemState(AHLV,0,LVIS_SELECTED,LVIS_SELECTED);
+}
+
+static BOOL DoListContextMenu(HWND AhWnd,WPARAM wParam,LPARAM lParam,STICKYNOTE *pNote)
+{
+ HWND hwndListView;
+ HMENU hMenuLoad,FhMenu;
+ MENUITEMINFO mii;
+
+ hwndListView = (HWND)wParam;
+ if (hwndListView != GetDlgItem(AhWnd,IDC_LISTREMINDERS)) return FALSE;
+ hMenuLoad = LoadMenu(hinstance,"MNU_NOTELISTPOPUP");
+ FhMenu = GetSubMenu(hMenuLoad,0);
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = MFS_DEFAULT;
+ if (!pNote)
+ mii.fState |= MFS_GRAYED;
+ SetMenuItemInfo(FhMenu, ID_CONTEXTMENUNOTELISTVIEW_EDITNOTE, FALSE, &mii);
+
+ if (!pNote)
+ {
+ EnableMenuItem(FhMenu, IDM_REMOVENOTE, MF_GRAYED|MF_BYCOMMAND);
+ EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY, MF_GRAYED|MF_BYCOMMAND);
+ EnableMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_GRAYED|MF_BYCOMMAND);
+ }
+ else
+ {
+ if (pNote->Visible)
+ CheckMenuItem(FhMenu, ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY, MF_CHECKED|MF_BYCOMMAND);
+ if (pNote->OnTop)
+ CheckMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_CHECKED|MF_BYCOMMAND);
+ }
+
+ CallService(MS_LANGPACK_TRANSLATEMENU,(DWORD)FhMenu,0);
+ TrackPopupMenu(FhMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON,LOWORD(lParam),HIWORD(lParam),0,AhWnd,0);
+ DestroyMenu(hMenuLoad);
+
+ return TRUE;
+}
+
+
+int CALLBACK DlgProcViewNotes(HWND Dialog,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ LV_COLUMN lvCol;
+ NMLISTVIEW *NM;
+ char *S;
+ int I;
+
+ switch (Message)
+ {
+ case WM_SIZE:
+ {
+ OnListResize(Dialog);
+ UpdateGeomFromWnd(Dialog, g_notesListGeom, NULL, 0);
+ break;
+ }
+ case WM_MOVE:
+ UpdateGeomFromWnd(Dialog, g_notesListGeom, NULL, 0);
+ break;
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *mm = (MINMAXINFO*)lParam;
+ mm->ptMinTrackSize.x = 394;
+ mm->ptMinTrackSize.y = 300;
+ }
+ return 0;
+ case WM_RELOAD:
+ {
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
+ InitListView(GetDlgItem(Dialog,IDC_LISTREMINDERS));
+ return TRUE;
+ }
+ case WM_CONTEXTMENU:
+ {
+ HWND H;
+ STICKYNOTE *pNote = NULL;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ pNote = (STICKYNOTE*)TreeGetAt(g_Stickies, I);
+ }
+ }
+
+ if (DoListContextMenu(Dialog, wParam, lParam, pNote))
+ return TRUE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ HWND H;
+
+ HICON hIcon = Skin_GetIconByHandle(hIconLibItem[13], ICON_SMALL);
+ SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
+ hIcon = Skin_GetIconByHandle(hIconLibItem[13], ICON_BIG);
+ SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);
+
+ SetWindowText(Dialog, _T("Notes"));
+
+ TranslateDialogDefault(Dialog);
+
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+
+ S = Translate("Note text");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_notesListColGeom[3];
+ ListView_InsertColumn(H,0,&lvCol);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+
+ S = Translate("T");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_notesListColGeom[2];
+ ListView_InsertColumn(H,0,&lvCol);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+
+ S = Translate("V");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_notesListColGeom[1];
+ ListView_InsertColumn(H,0,&lvCol);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+
+ S = Translate("Date/Title");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_notesListColGeom[0];
+ ListView_InsertColumn(H,0,&lvCol);
+
+ InitListView(H);
+ SetWindowLong(GetDlgItem(H, 0), GWL_ID, IDC_LISTREMINDERS_HEADER);
+ LV = Dialog;
+
+ if (g_notesListGeom[1] && g_notesListGeom[2])
+ {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(Dialog, &wp);
+ wp.rcNormalPosition.left = g_notesListGeom[0];
+ wp.rcNormalPosition.top = g_notesListGeom[1];
+ wp.rcNormalPosition.right = g_notesListGeom[2] + g_notesListGeom[0];
+ wp.rcNormalPosition.bottom = g_notesListGeom[3] + g_notesListGeom[1];
+ SetWindowPlacement(Dialog, &wp);
+ }
+ return TRUE;
+ }
+ case WM_CLOSE:
+ {
+ DestroyWindow(Dialog);
+ ListNotesVisible = FALSE;
+ return TRUE;
+ }
+ case WM_DESTROY:
+ ListNotesVisible = FALSE;
+ CallService(MS_SKIN2_RELEASEICONBIG, (WPARAM)SendMessage(Dialog, WM_SETICON, ICON_BIG, (LPARAM)NULL), 0);
+ CallService(MS_SKIN2_RELEASEICON, (WPARAM)SendMessage(Dialog, WM_SETICON, ICON_SMALL, (LPARAM)NULL), 0);
+ return TRUE;
+ case WM_NOTIFY:
+ {
+ if (wParam == IDC_LISTREMINDERS)
+ {
+ NM = (NMLISTVIEW *)lParam;
+ switch (NM->hdr.code)
+ {
+ case LVN_ITEMCHANGED:
+ {
+ S = ((STICKYNOTE*)TreeGetAt(g_Stickies,NM->iItem))->data;
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,S);
+ }
+ break;
+ case NM_DBLCLK:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ EditNote((STICKYNOTE *)TreeGetAt(g_Stickies, I));
+ }
+ }
+ }
+ break;
+ }
+ }
+ else if (wParam == IDC_LISTREMINDERS_HEADER)
+ {
+ NMHEADER *NM = (NMHEADER*)lParam;
+ switch (NM->hdr.code)
+ {
+ case HDN_ENDTRACK:
+ UpdateGeomFromWnd(Dialog, NULL, g_notesListColGeom, SIZEOF(g_notesListColGeom));
+ break;
+ }
+ }
+ }
+ break;
+ case WM_COMMAND:
+ {
+ switch(LOWORD(wParam))
+ {
+ case ID_CONTEXTMENUNOTELISTVIEW_EDITNOTE:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ EditNote((STICKYNOTE*)TreeGetAt(g_Stickies, I));
+ }
+ }
+ }
+ return TRUE;
+ case ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)TreeGetAt(g_Stickies, I);
+ SN->Visible = !SN->Visible;
+ ShowWindow(SN->SNHwnd,SN->Visible?SW_SHOWNA:SW_HIDE);
+ JustSaveNotes();
+ }
+ }
+ }
+ return TRUE;
+ case IDM_TOGGLEONTOP:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)TreeGetAt(g_Stickies,I);
+ SN->OnTop = !SN->OnTop;
+ SetWindowPos(SN->SNHwnd, SN->OnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0,0,0,0, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_FRAME|RDW_INVALIDATE|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ }
+ return TRUE;
+ case IDC_CLOSE:
+ {
+ DestroyWindow(Dialog);
+ ListNotesVisible = FALSE;
+ return TRUE;
+ }
+ case ID_CONTEXTMENUNOTEPOPUP_NEWNOTE:
+ case IDC_ADDNEWREMINDER:
+ {
+ PluginMenuCommandAddNew(0,0);
+ return TRUE;
+ }
+ case ID_CONTEXTMENUNOTELISTVIEW_DELETEALLNOTES:
+ {
+ PluginMenuCommandDeleteAll(0,0);
+ return TRUE;
+ }
+ case IDM_REMOVENOTE:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ OnDeleteNote(Dialog, (STICKYNOTE*)TreeGetAt(g_Stickies, I));
+ }
+ }
+ }
+ return TRUE;
+ case ID_CONTEXTMENUNOTELISTVIEW_SHOW:
+ {
+ ShowHideNotes();
+ return TRUE;
+ }
+ case ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP:
+ {
+ BringAllNotesToFront(NULL);
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
diff --git a/plugins/NotesAndReminders/options.cpp b/plugins/NotesAndReminders/options.cpp
new file mode 100644
index 0000000000..8e15946db9
--- /dev/null
+++ b/plugins/NotesAndReminders/options.cpp
@@ -0,0 +1,637 @@
+#include "globals.h"
+
+#ifndef OFN_DONTADDTORECENT
+#define OFN_DONTADDTORECENT 0x02000000
+#endif
+
+
+// min allowed alpha (don't want 0 because it's a waste of resources as well as might confuse user)
+#define MIN_ALPHA 30
+
+
+#define IDC_RESET 1007
+#define IDC_SHOWNOTES 1010
+#define IDC_ADDCONTACTMENU 1011
+#define IDC_NOTEWIDTH 1012
+#define IDC_NOTEHEIGHT 1013
+#define IDC_TRANSTRACK 1014
+#define IDC_REMINDEMAIL 1017
+#define IDC_SHOWSCROLLBAR 1018
+#define IDC_SHOWBUTTONS 1019
+#define IDC_ADDREMINDERCLOSES 1020
+#define IDC_USEMCI 1023
+#define PSM_CHANGED (WM_USER + 104)
+
+
+extern HANDLE hkFontChange;
+extern HANDLE hkColorChange;
+
+BOOL g_CloseAfterAddReminder, g_UseDefaultPlaySound;
+HICON g_hReminderIcon = NULL;
+
+LOGFONT lfBody,lfCaption;
+HFONT hBodyFont = NULL, hCaptionFont = NULL;
+long BodyColor;
+long CaptionFontColor,BodyFontColor;
+BOOL g_ShowNotesAtStart,g_ShowScrollbar,g_AddContListMI,g_ShowNoteButtons;
+int g_NoteTitleDate, g_NoteTitleTime;
+int g_NoteWidth,g_NoteHeight;
+int g_Transparency;
+char *g_RemindSMS = NULL;
+BOOL g_isWin2kPlus = TRUE;
+
+TCHAR *g_lpszAltBrowser = NULL;
+
+int g_reminderListGeom[4] = {0};
+int g_reminderListColGeom[2] = { 150, 205 };
+int g_notesListGeom[4] = {0};
+int g_notesListColGeom[4] = { 150, 20, 20, 165 };
+
+
+#define NRCDEFAULT_BODYCLR RGB(255,255,0)
+
+
+struct DateFormat
+{
+ LPCSTR lpszUI;
+ LPCSTR lpszFmt;
+}
+static dateFormats[] =
+{
+ { "1981-12-31", "yyyy'-'MM'-'dd" },
+ { "31-12-1981", "dd'-'MM'-'yyyy" },
+ { "12-31-1981", "MM'-'dd'-'yyyy" },
+ { "1981-dec-31", "yyyy'-'MMM'-'dd" },
+ { "31-dec-1981", "dd'-'MMM'-'yyyy" },
+ { "dec-31-1981", "MMM'-'dd'-'yyyy" },
+ { "1981/12/31", "yyyy'/'MM'/'dd" },
+ { "31/12/1981", "dd'/'MM'/'yyyy" },
+ { "12/31/1981", "MM'/'dd'/'yyyy" },
+ { "1981/dec/31", "yyyy'/'MMM'/'dd" },
+ { "31/dec/1981", "dd'/'MMM'/'yyyy" },
+ { "dec/31/1981", "MMM'/'dd'/'yyyy" },
+ { "1981 dec 31", "yyyy MMM dd" },
+ { "31 dec 1981", "dd MMM yyyy" },
+ { "dec 31 1981", "MMM dd yyyy" }
+};
+
+struct TimeFormat
+{
+ LPCSTR lpszUI;
+ LPCSTR lpszFmt;
+}
+static timeFormats[] =
+{
+ { "19:30:00", "HH':'mm':'ss" },
+ { "19:30", "HH':'mm'" },
+ { "7:30:00 PM", "hh':'mm':'ss tt" },
+ { "7:30 PM", "hh':'mm tt" },
+ { "7:30:00P", "hh':'mm':'sst" },
+ { "7:30P", "hh':'mmt" }
+};
+
+
+struct FontOptionsList
+{
+ TCHAR *szDescr;
+ COLORREF defColour;
+ TCHAR *szDefFace;
+ BYTE defStyle;
+ char defSize;
+ TCHAR *szBkgName;
+}
+static fontOptionsList[] =
+{
+ {LPGENT("Sticky Note Caption"), RGB(0,0,0), _T("Small Fonts"), 0, 7, LPGENT("Sticky Note Background Color")},
+ //{LPGENT("Sticky Note Caption"), RGB(0,0,0), _T("Terminal"), 0, 6, LPGENT("Sticky Note Background Color")},
+ //{LPGENT("Sticky Note Caption"), RGB(0,0,0), _T("MS Serif"), 0, 7, LPGENT("Sticky Note Background Color")},
+ //{LPGENT("Sticky Note Body"), RGB(0,0,0), _T("Tahoma"), 0, 8, LPGENT("Sticky Note Background Color")},
+ {LPGENT("Sticky Note Body"), RGB(0,0,0), _T("System"), DBFONTF_BOLD, 10, LPGENT("Sticky Note Background Color")},
+};
+
+
+struct ColourOptionsList
+{
+ TCHAR *szName;
+ char *szSettingName;
+ COLORREF defColour;
+}
+static colourOptionsList[] =
+{
+ {LPGENT("Sticky Note Background Color"), "BodyColor", NRCDEFAULT_BODYCLR}
+};
+
+
+LPCSTR GetDateFormatStr()
+{
+ return dateFormats[g_NoteTitleDate ? g_NoteTitleDate-1 : 0].lpszFmt;
+}
+
+LPCSTR GetTimeFormatStr()
+{
+ return timeFormats[g_NoteTitleTime ? g_NoteTitleTime-1 : 0].lpszFmt;
+}
+
+#if defined( _UNICODE )
+static BYTE MsgDlgGetFontDefaultCharset(const TCHAR* szFont)
+{
+ return DEFAULT_CHARSET;
+}
+#else
+// get font charset according to current CP
+static BYTE MsgDlgGetCPDefaultCharset()
+{
+ switch (GetACP()) {
+ case 1250:
+ return EASTEUROPE_CHARSET;
+ case 1251:
+ return RUSSIAN_CHARSET;
+ case 1252:
+ return ANSI_CHARSET;
+ case 1253:
+ return GREEK_CHARSET;
+ case 1254:
+ return TURKISH_CHARSET;
+ case 1255:
+ return HEBREW_CHARSET;
+ case 1256:
+ return ARABIC_CHARSET;
+ case 1257:
+ return BALTIC_CHARSET;
+ case 1361:
+ return JOHAB_CHARSET;
+ case 874:
+ return THAI_CHARSET;
+ case 932:
+ return SHIFTJIS_CHARSET;
+ case 936:
+ return GB2312_CHARSET;
+ case 949:
+ return HANGEUL_CHARSET;
+ case 950:
+ return CHINESEBIG5_CHARSET;
+ default:
+ return DEFAULT_CHARSET;
+ }
+}
+
+static int CALLBACK EnumFontFamExProc(const LOGFONT *lpelfe, const TEXTMETRIC *lpntme, DWORD FontType, LPARAM lParam)
+{
+ *(int*)lParam = 1;
+ return 0;
+}
+
+// get font charset according to current CP, if available for specified font
+static BYTE MsgDlgGetFontDefaultCharset(const TCHAR* szFont)
+{
+ HDC hdc;
+ LOGFONT lf = {0};
+ int found = 0;
+
+ _tcscpy(lf.lfFaceName, szFont);
+ lf.lfCharSet = MsgDlgGetCPDefaultCharset();
+
+ // check if the font supports specified charset
+ hdc = GetDC(0);
+ EnumFontFamiliesEx(hdc, &lf, &EnumFontFamExProc, (LPARAM)&found, 0);
+ ReleaseDC(0, hdc);
+
+ if (found)
+ return lf.lfCharSet;
+ else // no, give default
+ return DEFAULT_CHARSET;
+}
+#endif
+
+
+static void InitFonts()
+{
+ ZeroMemory(&lfBody,sizeof(LOGFONT));
+ ZeroMemory(&lfCaption,sizeof(LOGFONT));
+
+ LoadNRFont(NR_FONTID_CAPTION, &lfCaption, (COLORREF*)&CaptionFontColor);
+ LoadNRFont(NR_FONTID_BODY, &lfBody, (COLORREF*)&BodyFontColor);
+
+ if (hBodyFont)
+ DeleteObject(hBodyFont);
+ if (hCaptionFont)
+ DeleteObject(hCaptionFont);
+
+ hBodyFont = CreateFontIndirect(&lfBody);
+ hCaptionFont = CreateFontIndirect(&lfCaption);
+}
+
+
+static int FS_FontsChanged(WPARAM wParam, LPARAM lParam)
+{
+ InitFonts();
+
+ SaveNotes();
+ LoadNotes(FALSE);
+
+ return 0;
+}
+
+static int FS_ColorChanged(WPARAM wParam, LPARAM lParam)
+{
+ LoadNRFont(NR_FONTID_CAPTION, &lfCaption, (COLORREF*)&CaptionFontColor);
+ LoadNRFont(NR_FONTID_BODY, &lfBody, (COLORREF*)&BodyFontColor);
+
+ BodyColor = DBGetContactSettingDword(NULL, MODULENAME, colourOptionsList[0].szSettingName, colourOptionsList[0].defColour);
+
+ SaveNotes();
+ LoadNotes(FALSE);
+
+ return 0;
+}
+
+void RegisterFontServiceFonts()
+{
+ HDC hDC;
+ int nFontScale;
+ FontIDT fontid = {0};
+ ColourIDT colorid = {0};
+ char szTemp[100];
+ int i;
+
+ fontid.cbSize = sizeof(FontIDT);
+
+ mir_sntprintf(fontid.group, SIZEOF(fontid.group), _T("%s"), LPGENT(SECTIONNAME));
+ mir_sntprintf(fontid.backgroundGroup, SIZEOF(fontid.backgroundGroup), _T("%s"), LPGENT(SECTIONNAME));
+ strncpy(fontid.dbSettingsGroup, MODULENAME, SIZEOF(fontid.dbSettingsGroup));
+ fontid.flags = FIDF_ALLOWREREGISTER | FIDF_DEFAULTVALID | FIDF_SAVEPOINTSIZE;
+
+ hDC = GetDC(NULL);
+ nFontScale = GetDeviceCaps(hDC, LOGPIXELSY);
+ ReleaseDC(NULL, hDC);
+
+ for (i = 0; i < SIZEOF(fontOptionsList); i++)
+ {
+ fontid.order = i;
+ mir_snprintf(szTemp, SIZEOF(szTemp), "Font%d", i);
+ strncpy(fontid.prefix, szTemp, SIZEOF(fontid.prefix));
+ _tcsncpy(fontid.name, fontOptionsList[i].szDescr, SIZEOF(fontid.name));
+ fontid.deffontsettings.colour = fontOptionsList[i].defColour;
+
+ fontid.deffontsettings.size = (char)-MulDiv(fontOptionsList[i].defSize, nFontScale, 72);
+ //fontid.deffontsettings.size = fontOptionsList[i].defSize;
+
+ fontid.deffontsettings.style = fontOptionsList[i].defStyle;
+ fontid.deffontsettings.charset = MsgDlgGetFontDefaultCharset(fontOptionsList[i].szDefFace);
+ _tcsncpy(fontid.deffontsettings.szFace, fontOptionsList[i].szDefFace, SIZEOF(fontid.deffontsettings.szFace));
+ _tcsncpy(fontid.backgroundName, fontOptionsList[i].szBkgName, SIZEOF(fontid.backgroundName));
+
+ FontRegister(&fontid);
+ }
+
+ colorid.cbSize = sizeof(ColourIDT);
+
+ mir_sntprintf(colorid.group, SIZEOF(colorid.group), _T("%s"), LPGENT(SECTIONNAME));
+ strncpy(colorid.dbSettingsGroup, MODULENAME, SIZEOF(fontid.dbSettingsGroup));
+ colorid.flags = 0;
+
+ for (i = 0; i < SIZEOF(colourOptionsList); i++)
+ {
+ colorid.order = i;
+ _tcsncpy(colorid.name, colourOptionsList[i].szName, SIZEOF(colorid.name));
+ colorid.defcolour = colourOptionsList[i].defColour;
+ strncpy(colorid.setting, colourOptionsList[i].szSettingName, SIZEOF(colorid.setting));
+
+ ColourRegister(&colorid);
+ }
+
+ hkFontChange = HookEvent(ME_FONT_RELOAD, FS_FontsChanged);
+ hkColorChange = HookEvent(ME_COLOUR_RELOAD, FS_ColorChanged);
+}
+
+void LoadNRFont(int i, LOGFONT *lf, COLORREF *colour)
+{
+ COLORREF col;
+ FontIDT fontid = {0};
+
+ fontid.cbSize = sizeof(fontid);
+ _tcsncpy(fontid.group, LPGENT(SECTIONNAME), SIZEOF(fontid.group));
+ _tcsncpy(fontid.name, fontOptionsList[i].szDescr, SIZEOF(fontid.name));
+
+ col = CallService(MS_FONT_GETT, (WPARAM)&fontid, (LPARAM)lf);
+
+ if (colour)
+ {
+ *colour = col;
+ }
+}
+
+
+static void TrimString(TCHAR *s)
+{
+ TCHAR *start;
+ TCHAR *end;
+ UINT n;
+
+ if (!s || !*s)
+ {
+ return;
+ }
+
+ start = s;
+ n = _tcslen(s) - 1;
+
+ end = s + n;
+
+ if (!_istspace(*start) && !_istspace(*end))
+ {
+ // nothing to trim
+ return;
+ }
+
+ // scan past leading spaces
+ while (*start && _istspace(*start)) start++;
+
+ if (!*start)
+ {
+ // empty string
+ *s = 0;
+ return;
+ }
+
+ // trim trailing spaces
+ while ( _istspace(*end) ) end--;
+ end[1] = 0;
+
+ if (start > s)
+ {
+ // remove leading spaces
+ memmove(s, start, ((UINT)(end-start)+2)*sizeof(TCHAR));
+ }
+}
+
+
+int CALLBACK DlgProcOptions(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ BOOL LB;
+ WORD SzT;
+ void *P;
+ int i;
+
+ switch (message)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hdlg);
+ SendDlgItemMessage(hdlg,IDC_TRANSTRACK,TBM_SETRANGE,TRUE,MAKELONG(0,255-MIN_ALPHA));
+ SendDlgItemMessage(hdlg,IDC_TRANSTRACK,TBM_SETPOS,TRUE,255-g_Transparency);
+ SendDlgItemMessage(hdlg,IDC_SHOWNOTES,BM_SETCHECK,(WPARAM)!g_ShowNotesAtStart,0);
+ SendDlgItemMessage(hdlg,IDC_SHOWBUTTONS,BM_SETCHECK,(WPARAM)g_ShowNoteButtons,0);
+ SendDlgItemMessage(hdlg,IDC_SHOWSCROLLBAR,BM_SETCHECK,(WPARAM)g_ShowScrollbar,0); // 4.2
+ SendDlgItemMessage(hdlg,IDC_ADDCONTACTMENU,BM_SETCHECK,(WPARAM)g_AddContListMI,0);
+ SetDlgItemInt(hdlg,IDC_NOTEWIDTH,g_NoteWidth,FALSE);
+ SetDlgItemInt(hdlg,IDC_NOTEHEIGHT,g_NoteHeight,FALSE);
+ SendDlgItemMessage(hdlg,IDC_ADDREMINDERCLOSES,BM_SETCHECK,(WPARAM)g_CloseAfterAddReminder,0);
+ SendDlgItemMessage(hdlg,IDC_USEMCI,BM_SETCHECK,(WPARAM)!g_UseDefaultPlaySound,0);
+
+ SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_RESETCONTENT,0,0);
+ SendDlgItemMessage(hdlg,IDC_COMBOTIME,CB_RESETCONTENT,0,0);
+ for (i=0; i<SIZEOF(dateFormats); i++)
+ SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_ADDSTRING,0,(LPARAM)dateFormats[i].lpszUI);
+ for (i=0; i<SIZEOF(timeFormats); i++)
+ SendDlgItemMessage(hdlg,IDC_COMBOTIME,CB_ADDSTRING,0,(LPARAM)timeFormats[i].lpszUI);
+ SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_ADDSTRING,0,(LPARAM)Translate("None"));
+ SendDlgItemMessage(hdlg,IDC_COMBOTIME,CB_ADDSTRING,0,(LPARAM)Translate("None"));
+
+ SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_SETCURSEL,(WPARAM)(g_NoteTitleDate ? g_NoteTitleDate-1 : SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_GETCOUNT,0,0)-1),0);
+ SendDlgItemMessage(hdlg,IDC_COMBOTIME,CB_SETCURSEL,(WPARAM)(g_NoteTitleTime ? g_NoteTitleTime-1 : SendDlgItemMessage(hdlg,IDC_COMBOTIME,CB_GETCOUNT,0,0)-1),0);
+
+ if (g_RemindSMS)
+ SetDlgItemText(hdlg,IDC_REMINDEMAIL,g_RemindSMS);
+ else
+ SetDlgItemText(hdlg,IDC_REMINDEMAIL,"");
+
+ SetDlgItemText(hdlg,IDC_EDIT_ALTBROWSER,g_lpszAltBrowser ? g_lpszAltBrowser : _T(""));
+ if (!MySetLayeredWindowAttributes)
+ { // layered UI not available
+ EnableWindow(GetDlgItem(hdlg,IDC_TRANSTRACK), FALSE);
+ }
+ return TRUE;
+ }
+ case WM_HSCROLL:
+ {
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY)
+ {
+ g_ShowNotesAtStart = !(BOOL)SendDlgItemMessage(hdlg,IDC_SHOWNOTES,BM_GETCHECK,0,0);
+ g_ShowNoteButtons = (BOOL)SendDlgItemMessage(hdlg,IDC_SHOWBUTTONS,BM_GETCHECK,0,0);
+ g_ShowScrollbar = (BOOL)SendDlgItemMessage(hdlg,IDC_SHOWSCROLLBAR,BM_GETCHECK,0,0); // 4.2
+ g_AddContListMI = (BOOL)SendDlgItemMessage(hdlg,IDC_ADDCONTACTMENU,BM_GETCHECK,0,0);
+ g_NoteWidth = GetDlgItemInt(hdlg,IDC_NOTEWIDTH,&LB,FALSE);
+ g_NoteHeight = GetDlgItemInt(hdlg,IDC_NOTEHEIGHT,&LB,FALSE);
+ g_Transparency = 255-SendDlgItemMessage(hdlg,IDC_TRANSTRACK,TBM_GETPOS,0,0);
+ g_CloseAfterAddReminder = (BOOL)SendDlgItemMessage(hdlg,IDC_ADDREMINDERCLOSES,BM_GETCHECK,0,0);
+ g_UseDefaultPlaySound = !(BOOL)SendDlgItemMessage(hdlg,IDC_USEMCI,BM_GETCHECK,0,0);
+ g_NoteTitleDate = (SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_GETCURSEL,0,0) + 1) % SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_GETCOUNT,0,0);
+ g_NoteTitleTime = (SendDlgItemMessage(hdlg,IDC_COMBOTIME,CB_GETCURSEL,0,0) + 1) % SendDlgItemMessage(hdlg,IDC_COMBOTIME,CB_GETCOUNT,0,0);
+ if (g_NoteWidth < 179)
+ {
+ g_NoteWidth = 179;
+ SetDlgItemInt(hdlg,IDC_NOTEWIDTH,g_NoteWidth,FALSE);
+ }
+ if (g_NoteHeight < 35)
+ {
+ g_NoteHeight = 35;
+ SetDlgItemInt(hdlg,IDC_NOTEHEIGHT,g_NoteHeight,FALSE);
+ }
+ SzT = (WORD)SendDlgItemMessage(hdlg,IDC_REMINDEMAIL,WM_GETTEXTLENGTH,0,0);
+ if (SzT != 0)
+ {
+ g_RemindSMS = (char*)realloc(g_RemindSMS,SzT+1);
+ SendDlgItemMessage(hdlg,IDC_REMINDEMAIL,WM_GETTEXT,SzT+1,(LPARAM)g_RemindSMS);
+ }
+ P = g_RemindSMS;
+ WriteSettingBlob(0,MODULENAME,"RemindEmail",SzT,P);
+
+ SzT = (WORD)SendDlgItemMessage(hdlg,IDC_EDIT_ALTBROWSER,WM_GETTEXTLENGTH,0,0);
+ if (SzT != 0)
+ {
+ g_lpszAltBrowser = (TCHAR*)mir_realloc(g_lpszAltBrowser,SzT+1);
+ SendDlgItemMessage(hdlg,IDC_EDIT_ALTBROWSER,WM_GETTEXT,SzT+1,(LPARAM)g_lpszAltBrowser);
+ TrimString(g_lpszAltBrowser);
+ if (!*g_lpszAltBrowser)
+ {
+ mir_free(g_lpszAltBrowser);
+ g_lpszAltBrowser = NULL;
+ }
+ }
+ else if (g_lpszAltBrowser)
+ {
+ mir_free(g_lpszAltBrowser);
+ g_lpszAltBrowser = NULL;
+ }
+ SetDlgItemText(hdlg,IDC_EDIT_ALTBROWSER,g_lpszAltBrowser ? g_lpszAltBrowser : _T(""));
+ if (g_lpszAltBrowser)
+ DBWriteContactSettingString(0,MODULENAME,"AltBrowser",g_lpszAltBrowser);
+ else
+ DBDeleteContactSetting(0,MODULENAME,"AltBrowser");
+
+ WriteSettingInt(0,MODULENAME,"ShowNotesAtStart",g_ShowNotesAtStart);
+ WriteSettingInt(0,MODULENAME,"ShowNoteButtons",g_ShowNoteButtons);
+ WriteSettingInt(0,MODULENAME,"ShowScrollbar",g_ShowScrollbar); // 4.2
+ WriteSettingInt(0,MODULENAME,"AddContactMenuItems",g_AddContListMI);
+ WriteSettingInt(0,MODULENAME,"NoteWidth",g_NoteWidth);
+ WriteSettingInt(0,MODULENAME,"NoteHeight",g_NoteHeight);
+ WriteSettingInt(0,MODULENAME,"Transparency",g_Transparency);
+ WriteSettingInt(0,MODULENAME,"NoteTitleDate",g_NoteTitleDate);
+ WriteSettingInt(0,MODULENAME,"NoteTitleTime",g_NoteTitleTime);
+ WriteSettingInt(0,MODULENAME,"CloseAfterAddReminder",g_CloseAfterAddReminder);
+ WriteSettingInt(0,MODULENAME,"UseMCI",!g_UseDefaultPlaySound);
+ SaveNotes();
+ LoadNotes(FALSE);
+ return TRUE;
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_BTN_BROWSEBROWSER:
+ {
+ TCHAR s[MAX_PATH];
+
+ OPENFILENAME ofn = {0};
+#if defined(WINVER) && _WIN32_WINNT >= 0x0500
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+#else
+ ofn.lStructSize = sizeof(ofn);
+#endif
+ ofn.hwndOwner = hdlg;
+ ofn.lpstrFilter = "Executable Files\0*.exe\0All Files\0*.*\0\0";
+ ofn.lpstrFile = s;
+ ofn.nMaxFile = SIZEOF(s);
+ ofn.lpstrTitle = Translate("Select Executable");
+ ofn.lpstrInitialDir = ".";
+ ofn.Flags = OFN_FILEMUSTEXIST|OFN_LONGNAMES;
+ if ( IsWinVer98Plus() )
+ {
+ ofn.Flags |= OFN_ENABLESIZING;
+ if (g_isWin2kPlus)
+ ofn.Flags |= OFN_DONTADDTORECENT;
+ }
+
+ SendDlgItemMessage(hdlg,IDC_EDIT_ALTBROWSER,WM_GETTEXT,(WPARAM)ofn.nMaxFile,(LPARAM)s);
+
+ if ( GetOpenFileName(&ofn) )
+ {
+ SetDlgItemText(hdlg,IDC_EDIT_ALTBROWSER,s);
+ }
+ }
+ break;
+ case IDC_RESET:
+ {
+ SAFE_FREE((void**)&g_RemindSMS);
+ SetDlgItemText(hdlg,IDC_REMINDEMAIL,"");
+ if (g_lpszAltBrowser)
+ {
+ mir_free(g_lpszAltBrowser);
+ g_lpszAltBrowser = NULL;
+ }
+ SetDlgItemText(hdlg,IDC_EDIT_ALTBROWSER,"");
+ g_ShowNotesAtStart = TRUE;
+ g_AddContListMI = TRUE;
+ g_ShowScrollbar = TRUE; // 4.2
+ g_ShowNoteButtons = TRUE;
+ g_NoteTitleDate = 1;
+ g_NoteTitleTime = 1;
+ g_CloseAfterAddReminder = TRUE;
+ g_UseDefaultPlaySound = FALSE;
+ SendDlgItemMessage(hdlg,IDC_SHOWNOTES,BM_SETCHECK,!g_ShowNotesAtStart,0);
+ SendDlgItemMessage(hdlg,IDC_ADDCONTACTMENU,BM_SETCHECK,g_AddContListMI,0);
+ SendDlgItemMessage(hdlg,IDC_SHOWSCROLLBAR,BM_SETCHECK,g_ShowScrollbar,0);
+ SendDlgItemMessage(hdlg,IDC_SHOWBUTTONS,BM_SETCHECK,(WPARAM)g_ShowNoteButtons,0);
+ SendDlgItemMessage(hdlg,IDC_ADDREMINDERCLOSES,BM_SETCHECK,g_CloseAfterAddReminder,0);
+ SendDlgItemMessage(hdlg,IDC_USEMCI,BM_SETCHECK,!g_UseDefaultPlaySound,0);
+ SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_SETCURSEL,(WPARAM)(g_NoteTitleDate-1),0);
+ SendDlgItemMessage(hdlg,IDC_COMBOTIME,CB_SETCURSEL,(WPARAM)(g_NoteTitleTime-1),0);
+ g_NoteWidth = 179; // 4.2
+ g_NoteHeight = 35;
+ SetDlgItemInt(hdlg,IDC_NOTEWIDTH,g_NoteWidth,FALSE);
+ SetDlgItemInt(hdlg,IDC_NOTEHEIGHT,g_NoteHeight,FALSE);
+ g_Transparency = 255;
+ SendDlgItemMessage(hdlg,IDC_TRANSTRACK,TBM_SETPOS,TRUE,0);
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0); // JK optim
+ return TRUE;
+ }
+ default:
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void InitSettings(void)
+{
+ void *P = NULL;
+ short Sz1;
+
+ Sz1 = MAX_PATH; P = NULL;
+ ReadSettingBlob(0, MODULENAME, "RemindEmail", (WORD*)&Sz1, &P);
+ if (!(Sz1 && P))
+ g_RemindSMS = NULL;
+ else
+ {
+ g_RemindSMS = (char*)malloc(Sz1+1);
+ ZeroMemory(g_RemindSMS,Sz1+1);
+ memcpy(g_RemindSMS,P,Sz1);
+ FreeSettingBlob(Sz1,P);
+ }
+
+ g_lpszAltBrowser = DBGetString(0,MODULENAME,"AltBrowser");
+
+ g_ShowNotesAtStart = (BOOL)ReadSettingInt(0,MODULENAME,"ShowNotesAtStart",1);
+ g_ShowNoteButtons = (BOOL)ReadSettingInt(0,MODULENAME,"ShowNoteButtons",1);
+ g_ShowScrollbar = (BOOL)ReadSettingInt(0,MODULENAME,"ShowScrollbar",1);
+ g_AddContListMI = (BOOL)ReadSettingInt(0,MODULENAME,"AddContactMenuItems",1);
+ g_NoteWidth = ReadSettingInt(0,MODULENAME,"NoteWidth",179);
+ g_NoteHeight = ReadSettingInt(0,MODULENAME,"NoteHeight",50);
+ g_Transparency = ReadSettingInt(0,MODULENAME,"Transparency",255);
+ g_NoteTitleDate = ReadSettingInt(0,MODULENAME,"NoteTitleDate",1);
+ g_NoteTitleTime = ReadSettingInt(0,MODULENAME,"NoteTitleTime",1);
+ g_CloseAfterAddReminder = (BOOL)ReadSettingInt(0,MODULENAME,"CloseAfterAddReminder",1);
+ g_UseDefaultPlaySound = !(BOOL)ReadSettingInt(0,MODULENAME,"UseMCI",1);
+
+ ReadSettingIntArray(0,MODULENAME,"ReminderListGeom",g_reminderListGeom,SIZEOF(g_reminderListGeom));
+ ReadSettingIntArray(0,MODULENAME,"ReminderListColGeom",g_reminderListColGeom,SIZEOF(g_reminderListColGeom));
+ ReadSettingIntArray(0,MODULENAME,"NotesListGeom",g_notesListGeom,SIZEOF(g_notesListGeom));
+ ReadSettingIntArray(0,MODULENAME,"NotesListColGeom",g_notesListColGeom,SIZEOF(g_notesListColGeom));
+
+ BodyColor = DBGetContactSettingDword(NULL, MODULENAME, colourOptionsList[0].szSettingName, colourOptionsList[0].defColour);
+
+ InitFonts();
+
+ g_hReminderIcon = Skin_GetIconByHandle(hIconLibItem[10]);
+
+ if (g_Transparency < MIN_ALPHA)
+ g_Transparency = MIN_ALPHA;
+ else if (g_Transparency > 255)
+ g_Transparency = 255;
+}
+
+void TermSettings(void)
+{
+ if (g_reminderListGeom[2] > 0 && g_reminderListGeom[3] > 0)
+ {
+ WriteSettingIntArray(0,MODULENAME,"ReminderListGeom",g_reminderListGeom,SIZEOF(g_reminderListGeom));
+ WriteSettingIntArray(0,MODULENAME,"ReminderListColGeom",g_reminderListColGeom,SIZEOF(g_reminderListColGeom));
+ }
+ if (g_notesListGeom[2] > 0 && g_notesListGeom[3] > 0)
+ {
+ WriteSettingIntArray(0,MODULENAME,"NotesListGeom",g_notesListGeom,SIZEOF(g_notesListGeom));
+ WriteSettingIntArray(0,MODULENAME,"NotesListColGeom",g_notesListColGeom,SIZEOF(g_notesListColGeom));
+ }
+
+ if (g_lpszAltBrowser)
+ {
+ mir_free(g_lpszAltBrowser);
+ g_lpszAltBrowser = NULL;
+ }
+}
diff --git a/plugins/NotesAndReminders/reminders.cpp b/plugins/NotesAndReminders/reminders.cpp
new file mode 100644
index 0000000000..0d545056f9
--- /dev/null
+++ b/plugins/NotesAndReminders/reminders.cpp
@@ -0,0 +1,2890 @@
+#include "globals.h"
+
+#define FILETIME_TICKS_PER_SEC ((ULONGLONG)10000000)
+
+#define MAX_REMINDER_LEN 16384
+
+
+// RemindersData DB data params
+#define DATATAG_TEXT 1 // %s
+#define DATATAG_SNDREPEAT 2 // %u (specifies seconds to wait between sound repeats, 0 if repeat is disabled)
+#define DATATAG_SNDSEL 3 // %d (which sound to use, default, alt1, alt2, -1 means no sound at all)
+
+
+#define IDC_DATE 1000
+#define IDC_TIME IDC_COMBOREMINDERTIME
+#define IDC_ADDREMINDER 1002
+#define IDC_CLOSE 1003
+#define IDC_REMINDER 1004
+#define IDC_LISTREMINDERS 1000
+#define IDC_LISTREMINDERS_HEADER 2000
+#define IDC_REMINDERDATA 1001
+#define IDC_ADDNEWREMINDER 1002
+#define IDC_REMDATA 1000
+#define IDC_DISMISS 1001
+#define IDC_REMINDAGAIN 1002
+#define IDC_REMINDAGAININ 1003
+#define IDC_AFTER 1004
+#define IDC_ONDATE 1005
+#define IDC_DATEAGAIN 1006
+#define IDC_TIMEAGAIN 1007
+#define IDC_VIEWREMINDERS 1007
+#define IDC_NONE 1008
+#define IDC_DAILY 1009
+#define IDC_WEEKLY 1010
+#define IDC_MONTHLY 1011
+#define IDM_NEWREMINDER 40001
+#define IDM_DELETEREMINDER 40002
+#define IDM_DELETEALLREMINDERS 40003
+#define WM_RELOAD (WM_USER + 100)
+
+#define NOTIFY_LIST() if (ListReminderVisible) PostMessage(LV,WM_RELOAD,0,0)
+
+
+TREEELEMENT *RemindersList = NULL;
+static UINT QueuedReminderCount = 0;
+static BOOL ListReminderVisible = FALSE;
+static BOOL NewReminderVisible = FALSE;
+static REMINDERDATA *pEditReminder = NULL;
+static HWND LV;
+static SOCKET S;
+
+int WS_Send(SOCKET s,char *data,int datalen);
+unsigned long WS_ResolveName(char *name,WORD *port,int defaultPort);
+
+int CALLBACK DlgProcNotifyReminder(HWND Dialog,UINT Message,
+ WPARAM wParam,LPARAM lParam);
+int CALLBACK DlgProcNewReminder(HWND Dialog,UINT Message,WPARAM wParam,
+ LPARAM lParam);
+int CALLBACK DlgProcViewReminders(HWND Dialog,UINT Message,WPARAM wParam,
+ LPARAM lParam);
+
+void Send(char *user, char *host, char *Msg, char* server);
+char* GetPreviewString(const char *lpsz);
+
+
+static int ReminderSortCb(TREEELEMENT *v1, TREEELEMENT *v2)
+{
+ return (((REMINDERDATA*)v1->ptrdata)->When.QuadPart < ((REMINDERDATA*)v2->ptrdata)->When.QuadPart) ? -1 : 1;
+}
+
+
+#ifndef WINXP_MINIMUM
+// TzSpecificLocalTimeToSystemTime/SystemTimeToTzSpecificLocalTime (re-)implemented to work on win2k and older
+
+static const int DaysInMonth[2][12] =
+{
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // normal year
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } // leap year
+};
+
+static __inline BOOL IsLeapYear(UINT Y)
+{
+ return !(Y & 3) && ((Y % 100) || !(Y % 400));
+}
+
+static int TZDateComp(const SYSTEMTIME *lpDate, const SYSTEMTIME *lpDateRef)
+{
+ int boundaryDay, day;
+
+ if (lpDate->wMonth < lpDateRef->wMonth)
+ return -1;
+ if (lpDate->wMonth > lpDateRef->wMonth)
+ return 1;
+
+ if (!lpDateRef->wYear)
+ {
+ const int week = (int)lpDateRef->wDay;
+ const WORD wFirst = (6 + lpDateRef->wDayOfWeek - lpDate->wDayOfWeek + lpDate->wDay) % 7 + 1;
+ boundaryDay = (int)wFirst + 7 * (week - 1);
+ if (boundaryDay > DaysInMonth[ IsLeapYear(lpDate->wYear) ][lpDate->wMonth-1])
+ boundaryDay -= 7;
+ }
+ else
+ boundaryDay = (int)lpDateRef->wDay;
+
+ boundaryDay = ((boundaryDay * 24 + (int)lpDateRef->wHour) * 60 + (int)lpDateRef->wMinute) * 60;
+ day = (((int)lpDate->wDay * 24 + (int)lpDate->wHour) * 60 + (int)lpDate->wMinute) * 60 + (int)lpDate->wSecond;
+
+ return (day < boundaryDay) ? -1 : (day > boundaryDay);
+}
+
+static UINT TZGetType(LPTIME_ZONE_INFORMATION lpTZI, ULARGE_INTEGER *lpFT, BOOL bLocal)
+{
+ if (lpTZI->DaylightDate.wMonth)
+ {
+ ULARGE_INTEGER ft = *lpFT;
+ SYSTEMTIME tm;
+ BOOL BeforeStandardDate, AfterDaylightDate;
+ UINT id;
+
+ if (!lpTZI->StandardDate.wMonth || (!lpTZI->StandardDate.wYear
+ && (!lpTZI->StandardDate.wDay || lpTZI->StandardDate.wDay > 5
+ || !lpTZI->DaylightDate.wDay || lpTZI->DaylightDate.wDay > 5)))
+ return TIME_ZONE_ID_INVALID;
+
+ if (!bLocal)
+ ft.QuadPart -= (LONGLONG)(lpTZI->Bias + lpTZI->DaylightBias) * (LONGLONG)600000000;
+
+ FileTimeToSystemTime((FILETIME*)&ft, &tm);
+
+ BeforeStandardDate = (TZDateComp(&tm, &lpTZI->StandardDate) < 0);
+
+ if (!bLocal)
+ {
+ ft.QuadPart -= (LONGLONG)(lpTZI->StandardBias - lpTZI->DaylightBias) * (LONGLONG)600000000;
+ FileTimeToSystemTime((FILETIME*)&ft, &tm);
+ }
+
+ AfterDaylightDate = (TZDateComp(&tm, &lpTZI->DaylightDate) >= 0);
+
+ id = TIME_ZONE_ID_STANDARD;
+ if (lpTZI->DaylightDate.wMonth < lpTZI->StandardDate.wMonth)
+ {
+ if (BeforeStandardDate && AfterDaylightDate)
+ id = TIME_ZONE_ID_DAYLIGHT;
+ }
+ else
+ {
+ if (BeforeStandardDate || AfterDaylightDate)
+ id = TIME_ZONE_ID_DAYLIGHT;
+ }
+
+ return id;
+ }
+
+ return TIME_ZONE_ID_UNKNOWN;
+}
+
+static BOOL TZGetBias(LPTIME_ZONE_INFORMATION lpTZI, ULARGE_INTEGER *lpFT, BOOL bLocal, LONG *pBias)
+{
+ LONG Bias = lpTZI->Bias;
+
+ switch ( TZGetType(lpTZI, lpFT, bLocal) )
+ {
+ case TIME_ZONE_ID_INVALID: return FALSE;
+ case TIME_ZONE_ID_DAYLIGHT: Bias += lpTZI->DaylightBias; break;
+ case TIME_ZONE_ID_STANDARD: Bias += lpTZI->StandardBias; break;
+ }
+
+ *pBias = Bias;
+
+ return TRUE;
+}
+
+#define TzSpecificLocalTimeToSystemTime _TzSpecificLocalTimeToSystemTime
+static BOOL _TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTZI, LPSYSTEMTIME lpLocal, LPSYSTEMTIME lpUtc)
+{
+ TIME_ZONE_INFORMATION tzi;
+ ULARGE_INTEGER ft;
+ LONG Bias;
+
+ // if possible use the real function (shouldn't be necessary, be feels more comfortable)
+ if (MyTzSpecificLocalTimeToSystemTime)
+ return MyTzSpecificLocalTimeToSystemTime(lpTZI, lpLocal, lpUtc);
+
+ if (!lpTZI)
+ {
+ if (GetTimeZoneInformation(&tzi) == TIME_ZONE_ID_INVALID)
+ return FALSE;
+ lpTZI = &tzi;
+ }
+
+ if (!SystemTimeToFileTime(lpLocal, (FILETIME*)&ft)
+ || !TZGetBias(lpTZI, &ft, TRUE, &Bias))
+ return FALSE;
+
+ ft.QuadPart += (LONGLONG)Bias * (LONGLONG)600000000;
+
+ return FileTimeToSystemTime((FILETIME*)&ft, lpUtc);
+}
+
+#define SystemTimeToTzSpecificLocalTime _SystemTimeToTzSpecificLocalTime
+static BOOL _SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION lpTZI, LPSYSTEMTIME lpUtc, LPSYSTEMTIME lpLocal)
+{
+ TIME_ZONE_INFORMATION tzi;
+ ULARGE_INTEGER ft;
+ LONG Bias;
+
+ // if possible use the real function (shouldn't be necessary, be feels more comfortable)
+ if (MySystemTimeToTzSpecificLocalTime)
+ return MySystemTimeToTzSpecificLocalTime(lpTZI, lpUtc, lpLocal);
+
+ if (!lpTZI)
+ {
+ if (GetTimeZoneInformation(&tzi) == TIME_ZONE_ID_INVALID)
+ return FALSE;
+ lpTZI = &tzi;
+ }
+
+ if (!SystemTimeToFileTime(lpUtc, (FILETIME*)&ft)
+ || !TZGetBias(lpTZI, &ft, FALSE, &Bias))
+ return FALSE;
+
+ ft.QuadPart -= (LONGLONG)Bias * (LONGLONG)600000000;
+
+ return FileTimeToSystemTime((FILETIME*)&ft, lpLocal);
+}
+
+#endif
+
+
+// time convertsion routines that take local time-zone specific daylight saving configuration into account
+// (unlike the standard FileTimeToLocalFileTime functions)
+
+void UtcToTzLocalFT(const FILETIME *lpUtc, FILETIME *lpLocal)
+{
+ SYSTEMTIME tm, tmLocal;
+ FILETIMEtoSYSTEMTIME(lpUtc, &tm);
+ SystemTimeToTzSpecificLocalTime(NULL, &tm, &tmLocal);
+ SYSTEMTIMEtoFILETIME(&tmLocal, lpLocal);
+}
+
+void TzLocalToUtcFT(const FILETIME *lpLocal, FILETIME *lpUtc)
+{
+ SYSTEMTIME tm, tmUtc;
+ FILETIMEtoSYSTEMTIME(lpLocal, &tm);
+ TzSpecificLocalTimeToSystemTime(NULL, &tm, &tmUtc);
+ SYSTEMTIMEtoFILETIME(&tmUtc, lpUtc);
+}
+
+void FileTimeToTzLocalST(const FILETIME *lpUtc, SYSTEMTIME *tmLocal)
+{
+ SYSTEMTIME tm;
+ FILETIMEtoSYSTEMTIME(lpUtc, &tm);
+ SystemTimeToTzSpecificLocalTime(NULL, &tm, tmLocal);
+}
+
+void TzLocalSTToFileTime(const SYSTEMTIME *tmLocal, FILETIME *lpUtc)
+{
+ SYSTEMTIME tm;
+ TzSpecificLocalTimeToSystemTime(NULL, (SYSTEMTIME*)tmLocal, &tm);
+ SYSTEMTIMEtoFILETIME(&tm, lpUtc);
+}
+
+/*void AddToTzLocalFT(FILETIME *lpLocal, UINT nSeconds)
+{
+ ULARGE_INTEGER utc;
+ TzLocalToUtcFT(lpLocal, (FILETIME*)&utc);
+ utc.QuadPart += (ULONGLONG)nSeconds * FILETIME_TICKS_PER_SEC;
+ UtcToTzLocalFT((FILETIME*)&utc, lpLocal);
+}*/
+
+/*static void AddToSystemTime(SYSTEMTIME *tm, UINT nSeconds)
+{
+ ULARGE_INTEGER li;
+ FILETIME ft;
+
+ SYSTEMTIMEtoFILETIME(tm, &ft);
+
+ li.HighPart = ft.dwHighDateTime;
+ li.LowPart = ft.dwLowDateTime;
+ li.QuadPart += (ULONGLONG)nSeconds * FILETIME_TICKS_PER_SEC;
+
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li, tm);
+}*/
+
+
+static DWORD CreateUid()
+{
+ DWORD uid;
+ TREEELEMENT *TTE;
+
+ if (!RemindersList)
+ return 1;
+
+ for (uid = 1; ; uid++)
+ {
+ // check existing reminders if uid is in use
+ TTE = RemindersList;
+ while (TTE)
+ {
+ if (((REMINDERDATA*)TTE->ptrdata)->uid == uid)
+ // uid in use
+ goto try_next;
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ return uid;
+
+try_next:;
+ }
+
+ // should never get here (unless someone has 4294967295 reminders)
+ return 0;
+}
+
+static REMINDERDATA* FindReminder(DWORD uid)
+{
+ TREEELEMENT *TTE;
+
+ if (!RemindersList)
+ return NULL;
+
+ TTE = RemindersList;
+ while (TTE)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ if (pReminder->uid == uid)
+ {
+ return pReminder;
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ return NULL;
+}
+
+
+static void RemoveReminderSystemEvent(REMINDERDATA *p)
+{
+ if (p->SystemEventQueued)
+ {
+ int i;
+
+ for (i=0; ; i++)
+ {
+ CLISTEVENT *pev;
+
+ pev = (CLISTEVENT*) CallService(MS_CLIST_GETEVENT,(WPARAM)INVALID_HANDLE_VALUE,i);
+ if (!pev)
+ break;
+
+ if ((ULONG)pev->lParam == p->uid && !pev->hContact
+ && pev->pszService && !strcmp(pev->pszService, MODULENAME"/OpenTriggeredReminder"))
+ {
+ if ( !CallService(MS_CLIST_REMOVEEVENT,(WPARAM)pev->hContact,(LPARAM)pev->hDbEvent) )
+ {
+ p->SystemEventQueued = FALSE;
+ if (QueuedReminderCount)
+ QueuedReminderCount--;
+ }
+ break;
+ }
+ }
+ }
+}
+
+void PurgeReminders(void)
+{
+ int ReminderCount,I;
+ char ValueName[32];
+
+ ReminderCount = ReadSettingInt(0,MODULENAME,"RemindersData",0);
+ for(I = 0;I < ReminderCount;I++)
+ {
+ sprintf(ValueName, "RemindersData%d", I);
+ DeleteSetting(0,MODULENAME,ValueName);
+ }
+}
+
+void JustSaveReminders(void)
+{
+ TREEELEMENT *TTE;
+ int I, n, l;
+ char *tmpReminder = NULL,*Value;
+ char ValueName[32];
+ int ReminderCount;
+ REMINDERDATA *pReminder;
+
+ const int OldReminderCount = ReadSettingInt(0, MODULENAME, "RemindersData", 0);
+
+ ReminderCount = TreeGetCount(RemindersList);
+
+ WriteSettingInt(0,MODULENAME, "RemindersData", ReminderCount);
+
+ for (TTE = RemindersList, I = 0; TTE; TTE = (TREEELEMENT*)TTE->next, I++)
+ {
+ pReminder = (REMINDERDATA*)TTE->ptrdata;
+ if (pReminder->Reminder && strlen(pReminder->Reminder))
+ tmpReminder = pReminder->Reminder;
+ else
+ tmpReminder = NULL;
+
+ if (!tmpReminder)
+ tmpReminder = "";
+
+ Value = (char*)malloc(strlen(tmpReminder) + 512);
+
+ if (!Value)
+ continue;
+
+ n = 0;
+
+ // data header (save 'When' with 1-second resolution, it's just a waste to have 100-nanosecond resolution
+ // which results in larger DB strings with no use)
+ l = sprintf(Value, "X%u:%I64x", pReminder->uid, pReminder->When.QuadPart/FILETIME_TICKS_PER_SEC);
+ if (l > 0) n += l;
+
+ // sound repeat
+ if (pReminder->RepeatSound)
+ {
+ l = sprintf(Value+n, "\033""%u:%u", DATATAG_SNDREPEAT, pReminder->RepeatSound);
+ if (l > 0) n += l;
+ }
+
+ // sound
+ if (pReminder->SoundSel)
+ {
+ l = sprintf(Value+n, "\033""%u:%d", DATATAG_SNDSEL, pReminder->SoundSel);
+ if (l > 0) n += l;
+ }
+
+ // reminder text/note (ALWAYS PUT THIS PARAM LAST)
+ if (tmpReminder && *tmpReminder)
+ {
+ l = sprintf(Value+n, "\033""%u:%s", DATATAG_TEXT, tmpReminder);
+ if (l > 0) n += l;
+ }
+
+ // clamp data size to WORD (including null terminator)
+ if (n >= 0xffff)
+ {
+ // huston, we have a problem, strip some reminder text
+ n = 0xfffe;
+ ValueName[0xffff] = 0;
+ }
+
+ sprintf(ValueName, "RemindersData%d", ReminderCount - I - 1); // do not want to reverse in DB
+
+ WriteSettingBlob(0, MODULENAME, ValueName, (WORD)(n+1), Value);
+
+ SAFE_FREE((void**)&Value);
+ }
+
+ // delete any left over DB reminder entries
+ for(; I < OldReminderCount; I++)
+ {
+ sprintf(ValueName, "RemindersData%d", I);
+ DBDeleteContactSetting(0,MODULENAME,ValueName);
+ }
+}
+
+void LoadReminders(void)
+{
+ int I,RemindersCount;
+ char *Value;
+ WORD Size;
+ char ValueName[32];
+ BOOL GenerateUids = FALSE;
+
+ RemindersList = NULL;
+ RemindersCount = ReadSettingInt(0, MODULENAME, "RemindersData", 0);
+
+ for (I = 0; I < RemindersCount; I++)
+ {
+ Size = 65535;
+ Value = NULL;
+ sprintf(ValueName, "RemindersData%d", I);
+
+ ReadSettingBlob(0, MODULENAME, ValueName, &Size, (void**)&Value);
+
+ if (Size && Value) // was the blob found
+ {
+ REMINDERDATA rem = {0};
+ char *TVal;
+ REMINDERDATA *TempRem;
+ char *DelPos = strchr(Value, 0x1B);
+
+ // ensure that read data is null-terminated
+ Value[(UINT)Size-1] = 0;
+
+ if (Value[0] == 'X')
+ {
+ // new eXtended/fleXible data format
+
+ if (DelPos)
+ *DelPos = 0;
+
+ // uid:when
+
+ TVal = strchr(Value+1, ':');
+ if (!TVal || (DelPos && TVal > DelPos))
+ continue;
+ *TVal++ = 0;
+
+ rem.uid = strtoul(Value+1, NULL, 10);
+ rem.When.QuadPart = _strtoui64(TVal, NULL, 16) * FILETIME_TICKS_PER_SEC;
+
+ // optional \033 separated params
+ while (DelPos)
+ {
+ char *sep;
+ UINT tag;
+
+ TVal = DelPos + 1;
+ // find param end and make sure it's null-terminated (if end of data then it's already null-terminated)
+ DelPos = strchr(TVal, 0x1B);
+ if (DelPos)
+ *DelPos = 0;
+
+ // tag:<data>
+
+ sep = strchr(TVal, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+
+ tag = strtoul(TVal, NULL, 10);
+ TVal = sep + 1;
+
+ switch (tag)
+ {
+ case DATATAG_TEXT:
+ rem.Reminder = _strdup(TVal);
+ break;
+
+ case DATATAG_SNDREPEAT:
+ rem.RepeatSound = strtoul(TVal, NULL, 10);
+ break;
+
+ case DATATAG_SNDSEL:
+ rem.SoundSel = strtol(TVal, NULL, 10);
+ if (rem.SoundSel > 2) rem.SoundSel = 2;
+ break;
+ }
+ }
+
+ if (rem.SoundSel < 0)
+ rem.RepeatSound = 0;
+ if (!rem.Reminder)
+ rem.Reminder = _strdup("");
+ }
+ else
+ {
+ // old format (for DB backward compatibility)
+
+ if (!DelPos)
+ continue;
+
+ DelPos[0] = 0;
+ // convert time_t to (local) FILETIME
+ {
+ SYSTEMTIME tm;
+ struct tm *stm;
+ time_t tt;
+
+ tt = (time_t)strtoul(Value, NULL, 10);
+ stm = localtime(&tt);
+ tm.wDayOfWeek = 0;
+ tm.wSecond = 0;
+ tm.wMilliseconds = 0;
+ tm.wHour = stm->tm_hour;
+ tm.wMinute = stm->tm_min;
+ tm.wSecond = stm->tm_sec;
+ tm.wYear = stm->tm_year + 1900;
+ tm.wMonth = stm->tm_mon + 1;
+ tm.wDay = stm->tm_mday;
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&rem.When);
+ }
+ TVal = DelPos + 1;
+ rem.Reminder = _strdup(TVal);
+ }
+
+ // queue uid generation if invalid uid is present
+ if (!rem.uid)
+ GenerateUids = TRUE;
+
+ TempRem = (REMINDERDATA*)malloc(sizeof(REMINDERDATA));
+ if (TempRem)
+ {
+ *TempRem = rem;
+ TreeAddSorted(&RemindersList, TempRem, ReminderSortCb);
+ }
+ else if (rem.Reminder)
+ {
+ free(rem.Reminder);
+ }
+skip:;
+ }
+
+ FreeSettingBlob(Size, Value);
+ }
+
+ // generate UIDs if there are any items with an invalid UID
+ if (GenerateUids && RemindersList)
+ {
+ TREEELEMENT *TTE;
+
+ TTE = RemindersList;
+ while (TTE)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ if (!pReminder->uid)
+ pReminder->uid = CreateUid();
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ JustSaveReminders();
+ }
+}
+
+
+/*void EscapeString(LPCSTR lpszSrc, char *s, int maxLen)
+{
+ maxLen -= 3;
+
+ *s++ = '"';
+
+ while (*lpszSrc && maxLen > 1)
+ {
+ switch (*lpszSrc)
+ {
+ case '\r': *s++ = '\\'; *s++ = 'r'; break;
+ case '\n': *s++ = '\\'; *s++ = 'n'; break;
+ case '"': *s++ = '\\'; *s++ = '"'; break;
+ case '\t': *s++ = '\\'; *s++ = 't'; break;
+ case '\\': *s++ = '\\'; *s++ = '\\'; break;
+ default:
+ *s++ = *lpszSrc;
+ }
+
+ lpszSrc++;
+ maxLen--;
+ }
+
+ *s++ = '"';
+ *s = 0;
+}
+
+void ExportReminders()
+{
+ LPCSTR lpsz;
+ TREEELEMENT *TTE;
+ char s[MAX_REMINDER_LEN+512];
+
+ if (!RemindersList)
+ return NULL;
+
+ // CSV header
+ lpsz = "TimeUTC,SoundSel,SoundRepeat,Description";
+ WriteFile(hFile, lpsz, strlen(lpsz), NULL, NULL);
+
+ TTE = RemindersList;
+ while (TTE)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ sprintf(s, "%I64u,%d,%d,", (pReminder->When.QuadPart-(ULONGLONG)116444736000000000)/FILETIME_TICKS_PER_SEC, pReminder->SoundSel, pReminder->RepeatSound);
+ WriteFile(hFile, s, strlen(s), NULL, NULL);
+
+ if (pReminder->Reminder)
+ {
+ EscapeString(pReminder->Reminder, s, sizeof(s));
+ WriteFile(hFile, s, strlen(s), NULL, NULL);
+ }
+
+ WriteFile(hFile, (LPCVOID)"\r\n", 2, NULL, NULL);
+
+ TTE = TTE->next;
+ }
+
+ return NULL;
+}*/
+
+
+void NewReminder(void)
+{
+ if (!NewReminderVisible)
+ {
+ NewReminderVisible = TRUE;
+ CreateDialog(hinstance, MAKEINTRESOURCE(IDD_ADDREMINDER), 0, DlgProcNewReminder);
+ }
+}
+
+void EditReminder(REMINDERDATA *p)
+{
+ if (!p)
+ return;
+
+ if (!NewReminderVisible && !p->SystemEventQueued)
+ {
+ if (!p->RemVisible)
+ {
+ p->RemVisible = TRUE;
+ NewReminderVisible = 2;
+ pEditReminder = p;
+ CreateDialog(hinstance, MAKEINTRESOURCE(IDD_ADDREMINDER), 0, DlgProcNewReminder);
+ }
+ else
+ {
+ BringWindowToTop(p->handle);
+ }
+ }
+}
+
+static void DeleteReminder(REMINDERDATA *p)
+{
+ if (!p)
+ return;
+
+ if (p->SystemEventQueued)
+ {
+ // remove pending system event
+ RemoveReminderSystemEvent(p);
+ }
+
+ TreeDelete(&RemindersList, p);
+ SAFE_FREE((void**)&p->Reminder);
+ SAFE_FREE((void**)&p);
+}
+
+void CloseReminderList()
+{
+ if (ListReminderVisible)
+ {
+ DestroyWindow(LV);
+ ListReminderVisible = FALSE;
+ }
+}
+
+static void PurgeReminderTree()
+{
+ REMINDERDATA *pt;
+
+ while (RemindersList) // empty whole tree
+ {
+ pt = (REMINDERDATA*)RemindersList->ptrdata;
+ if (pt->handle) DestroyWindow(pt->handle);
+ DeleteReminder(pt);
+ }
+ RemindersList = NULL;
+}
+
+void SaveReminders(void)
+{
+ JustSaveReminders();
+ PurgeReminderTree();
+}
+
+void DeleteReminders(void)
+{
+ PurgeReminders();
+ WriteSettingInt(0,MODULENAME,"RemindersData",0);
+ PurgeReminderTree();
+}
+
+void ListReminders(void)
+{
+ if (!ListReminderVisible)
+ {
+ CreateDialog(hinstance, MAKEINTRESOURCE(IDD_LISTREMINDERS), 0, DlgProcViewReminders);
+ ListReminderVisible = TRUE;
+ }
+ else
+ {
+ BringWindowToTop(LV);
+ }
+}
+
+
+void GetTriggerTimeString(const ULARGE_INTEGER *When, char *s, UINT strSize, BOOL bUtc)
+{
+ SYSTEMTIME tm;
+ LCID lc = GetUserDefaultLCID();
+
+ *s = 0;
+
+ memset(&tm, 0, sizeof(tm));
+ if (bUtc)
+ FileTimeToTzLocalST((const FILETIME*)When, &tm);
+ else
+ FILETIMEtoSYSTEMTIME((FILETIME*)When, &tm);
+
+ if ( GetDateFormat(lc, DATE_LONGDATE, &tm, NULL, s, strSize) )
+ {
+ // append time
+ {
+ int n = strlen(s);
+ s[n++] = ' ';
+ s[n] = 0;
+
+ if ( !GetTimeFormat(lc, LOCALE_NOUSEROVERRIDE|TIME_NOSECONDS, &tm, NULL, s+n, strSize-n) )
+ {
+ mir_snprintf(s+n, strSize-n, "%02d:%02d", tm.wHour, tm.wMinute);
+ }
+ }
+ }
+ else
+ {
+ mir_snprintf(s, strSize, "%d-%02d-%02d %02d:%02d", tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute);
+ }
+}
+
+
+int OpenTriggeredReminder(WPARAM w, LPARAM l)
+{
+ REMINDERDATA *pReminder;
+
+ if (!l)
+ return 0;
+
+ l = ((CLISTEVENT*)l)->lParam;
+
+ pReminder = (REMINDERDATA*)FindReminder((DWORD)l);
+ if (!pReminder || !pReminder->SystemEventQueued)
+ return 0;
+
+ pReminder->SystemEventQueued = FALSE;
+ if (QueuedReminderCount)
+ QueuedReminderCount--;
+
+ {
+ char S[MAX_PATH];
+ char S1[128];
+ HWND H;
+ GetTriggerTimeString(&pReminder->When, S1, sizeof(S1), TRUE);
+
+ pReminder->RemVisible = TRUE;
+
+ pReminder->handle = H = CreateDialog(hinstance, MAKEINTRESOURCE(IDD_NOTIFYREMINDER), 0, DlgProcNotifyReminder);
+
+ sprintf(S, "%s! - %s", Translate("Reminder"), S1);
+ SetWindowText(H, S);
+
+ if (pReminder->Reminder)
+ SetDlgItemText(H, IDC_REMDATA, pReminder->Reminder);
+
+ BringWindowToTop(H);
+ }
+
+ return 0;
+}
+
+static void SkinPlaySoundPoly(LPCSTR pszSoundName)
+{
+ if (g_UseDefaultPlaySound)
+ {
+ SkinPlaySound(pszSoundName);
+ return;
+ }
+
+ if (DBGetContactSettingByte(NULL, "SkinSoundsOff", pszSoundName, 0)==0) {
+ DBVARIANT dbv;
+
+ if (DBGetContactSettingString(NULL, "SkinSounds", pszSoundName, &dbv)==0) {
+ char szFull[MAX_PATH];
+
+ CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)dbv.pszVal, (LPARAM)szFull);
+
+ //NotifyEventHooks(hPlayEvent, 0, (LPARAM)szFull);
+ {
+ // use MCI device which allows multiple sounds playing at once
+ // NOTE: mciSendString does not like long paths names, must convert to short
+ char szShort[MAX_PATH];
+ char s[512];
+ GetShortPathNameA(szFull, szShort, sizeof(szShort));
+ mir_snprintf(s, sizeof(s), "play \"%s\"", szShort);
+ mciSendStringA(s, NULL, 0, NULL);
+ }
+
+ DBFreeVariant(&dbv);
+ }
+ }
+}
+
+static void UpdateReminderEvent(REMINDERDATA *pReminder, UINT nElapsedSeconds, BOOL *pHasPlayedSound)
+{
+ DWORD dwSoundMask;
+
+ if (pReminder->RepeatSound)
+ {
+ if (nElapsedSeconds >= pReminder->RepeatSoundTTL)
+ {
+ pReminder->RepeatSoundTTL = pReminder->RepeatSound;
+
+ dwSoundMask = 1 << pReminder->SoundSel;
+
+ if ( !(*pHasPlayedSound & dwSoundMask) )
+ {
+ switch (pReminder->SoundSel)
+ {
+ case 1: SkinPlaySoundPoly("AlertReminder2"); break;
+ case 2: SkinPlaySoundPoly("AlertReminder3"); break;
+ default:
+ SkinPlaySoundPoly("AlertReminder");
+ }
+
+ *pHasPlayedSound |= dwSoundMask;
+ }
+ }
+ else
+ {
+ pReminder->RepeatSoundTTL -= nElapsedSeconds;
+ }
+ }
+}
+
+static void FireReminder(REMINDERDATA *pReminder, BOOL *pHasPlayedSound)
+{
+ DWORD dwSoundMask;
+
+ if (pReminder->SystemEventQueued)
+ return;
+
+ // add a system event
+ {
+ CLISTEVENT ev = { 0 };
+
+ ev.cbSize = sizeof(ev);
+ ev.hIcon = g_hReminderIcon;
+ ev.flags = CLEF_URGENT;
+ ev.lParam = (LPARAM)pReminder->uid;
+ ev.pszService = MODULENAME"/OpenTriggeredReminder";
+ ev.pszTooltip = Translate("Reminder");
+
+ CallService(MS_CLIST_ADDEVENT,0,(LPARAM)&ev);
+ }
+
+ pReminder->SystemEventQueued = TRUE;
+ QueuedReminderCount++;
+
+ if (pReminder->SoundSel < 0)
+ {
+ // sound disabled
+ return;
+ }
+
+ dwSoundMask = 1 << pReminder->SoundSel;
+
+ pReminder->RepeatSoundTTL = pReminder->RepeatSound;
+
+ if ( !(*pHasPlayedSound & dwSoundMask) )
+ {
+ switch (pReminder->SoundSel)
+ {
+ case 1: SkinPlaySoundPoly("AlertReminder2"); break;
+ case 2: SkinPlaySoundPoly("AlertReminder3"); break;
+ default:
+ SkinPlaySoundPoly("AlertReminder");
+ }
+
+ *pHasPlayedSound |= dwSoundMask;
+ }
+}
+
+
+BOOL CheckRemindersAndStart(void)
+{
+ // returns TRUE if there are any triggered reminder with SystemEventQueued, this will shorten the update interval
+ // allowing sound repeats with shorter intervals
+
+ TREEELEMENT *TTE;
+ ULARGE_INTEGER curT;
+ BOOL bHasPlayedSound;
+ BOOL bResult;
+ BOOL bHasQueuedReminders;
+
+ if (!RemindersList)
+ return FALSE;
+
+ {
+ SYSTEMTIME tm;
+ GetSystemTime(&tm);
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&curT);
+ }
+
+ // NOTE: reminder list is sorted by trigger time, so we can early out on the first reminder > cur time
+
+ // quick check for normal case with no reminder ready to be triggered and no queued triggered reminders
+ // (happens 99.99999999999% of the time)
+ if (curT.QuadPart < ((REMINDERDATA*)RemindersList->ptrdata)->When.QuadPart && !QueuedReminderCount)
+ {
+ return FALSE;
+ }
+
+ bResult = FALSE;
+
+ // var used to avoid playing multiple alarm sounds during a single update
+ bHasPlayedSound = FALSE;
+
+ // if there are queued (triggered) reminders then iterate through entire list, becaue of WM_TIMECHANGE events
+ // and for example daylight saving changes it's possible for an already triggered event to end up with When>curT
+ bHasQueuedReminders = (QueuedReminderCount != 0);
+
+ // allthough count should always be correct, it's fool proof to just count them again in the loop below
+ QueuedReminderCount = 0;
+
+ TTE = RemindersList;
+ while (TTE && (bHasQueuedReminders || ((REMINDERDATA*)TTE->ptrdata)->When.QuadPart <= curT.QuadPart))
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ if (!pReminder->RemVisible)
+ {
+ if (pReminder->SystemEventQueued)
+ {
+ UpdateReminderEvent(pReminder, REMINDER_UPDATE_INTERVAL_SHORT/1000, &bHasPlayedSound);
+
+ QueuedReminderCount++;
+ bResult = TRUE;
+ }
+ else if (((REMINDERDATA*)TTE->ptrdata)->When.QuadPart <= curT.QuadPart)
+ {
+ if (!g_RemindSMS)
+ {
+ FireReminder(pReminder, &bHasPlayedSound);
+
+ if (pReminder->SystemEventQueued)
+ bResult = TRUE;
+ }
+ else
+ {
+ char* S2 = strchr(g_RemindSMS, '@');
+ char* S1 = (char*)malloc(S2 - g_RemindSMS);
+
+ strncpy(S1, g_RemindSMS, S2 - g_RemindSMS);
+ S1[S2 - g_RemindSMS]= 0x0;
+ S2++;
+ Send(S1, S2, pReminder->Reminder ? pReminder->Reminder : "", NULL);
+ SAFE_FREE((void**)&S1);
+ DeleteReminder(pReminder);
+ JustSaveReminders();
+ NOTIFY_LIST();
+ }
+ }
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ return bResult;
+}
+
+
+static LRESULT CALLBACK DatePickerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_RBUTTONDBLCLK:
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_CHAR:
+ case WM_INITMENUPOPUP:
+ case WM_PASTE:
+ return TRUE;
+ case WM_SYSKEYUP:
+ case WM_SYSKEYDOWN:
+ case WM_SYSCHAR:
+ return FALSE;
+ }
+
+ return CallWindowProc((WNDPROC)GetProp(hWnd, TEXT("OldWndProc")), hWnd, message, wParam, lParam);
+}
+
+static void InitDatePicker(HWND Dialog, UINT nIDDate)
+{
+ // subclass date picker to prevent user editing (should only use the dropdown calender to ensure valid dates)
+ WNDPROC pOldWndProc;
+ HWND hCtrl = GetDlgItem(Dialog, nIDDate);
+
+ // tweak style of picker
+ if ( IsWinVerVistaPlus() )
+ {
+ DWORD dw = SendDlgItemMessage(Dialog,nIDDate,DTM_GETMCSTYLE,0,0);
+ dw |= MCS_WEEKNUMBERS | MCS_NOSELCHANGEONNAV;
+ SendDlgItemMessage(Dialog,nIDDate,DTM_SETMCSTYLE,0,dw);
+ }
+
+#ifdef _WIN64
+ pOldWndProc = (WNDPROC)SetWindowLongPtr(hCtrl, GWLP_WNDPROC, (LONG_PTR)DatePickerWndProc);
+#else
+ pOldWndProc = (WNDPROC)SetWindowLong(hCtrl, GWL_WNDPROC, (LONG)DatePickerWndProc);
+#endif
+
+ SetProp(hCtrl, TEXT("OldWndProc"), pOldWndProc);
+}
+
+static BOOL ParseTime(LPCSTR s, int *hout, int *mout, BOOL bTimeOffset, BOOL bAllowOffsetOverride)
+{
+ // validate format: <WS><digit>[<digit>]<WS>[':'<WS><digit>[<digit>]]<WS>[p | P].*
+
+ // if bTimeOffset is FALSE the user may still enter a time offset by using + as the first char, the
+ // delta time will be returned in minutes (even > 60) and hout will always be -1
+
+ // if bTimeOffset is TRUE time is always interpreted as an offset (ignores PM indicator and defaults to minutes)
+
+ int h, m;
+ BOOL bOffset = bTimeOffset;
+
+ // read hour
+
+ while ( iswspace(*s) ) s++;
+
+ if (*s == '+')
+ {
+ if (!bTimeOffset)
+ {
+ if (!bAllowOffsetOverride)
+ return FALSE;
+
+ // treat value as an offset anyway
+ bOffset = TRUE;
+ }
+
+ s++;
+ while ( iswspace(*s) ) s++;
+ }
+
+ if ( !isdigit(*s) )
+ return FALSE;
+ h = (int)(*s-'0');
+ s++;
+
+ if (!bOffset)
+ {
+ if ( isdigit(*s) )
+ {
+ h = h * 10 + (int)(*s-'0');
+ s++;
+ }
+
+ if ( isdigit(*s) )
+ return FALSE;
+ }
+ else
+ {
+ // allow more than 2-digit numbers for offset
+ while ( isdigit(*s) )
+ {
+ h = h * 10 + (int)(*s-'0');
+ s++;
+ }
+ }
+
+ // find : separator
+
+ while ( iswspace(*s) ) s++;
+
+ if (*s == ':')
+ {
+ s++;
+
+ // read minutes
+
+ while ( iswspace(*s) ) s++;
+
+ if ( !isdigit(*s) )
+ return FALSE;
+ m = (int)(*s-'0');
+ s++;
+
+ if ( isdigit(*s) )
+ {
+ m = m * 10 + (int)(*s-'0');
+ s++;
+ }
+ }
+ else
+ {
+ if (bOffset)
+ {
+ // no : separator found, interpret the entered number as minutes and allow > 60
+
+ if (h < 0)
+ return FALSE;
+
+ if (bTimeOffset)
+ {
+ *hout = h / 60;
+ *mout = h % 60;
+ }
+ else
+ {
+ *mout = h;
+ *hout = -1;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ m = 0;
+ }
+ }
+
+ // validate time
+ if (bOffset)
+ {
+ if (h < 0)
+ return FALSE;
+ if (m < 0 || m > 59)
+ return FALSE;
+ }
+ else
+ {
+ if (h == 24)
+ h = 0;
+ else if (h < 0 || h > 23)
+ return FALSE;
+ if (m < 0 || m > 59)
+ return FALSE;
+ }
+
+ if (!bOffset)
+ {
+ // check for PM indicator (not strict, only checks for P char)
+
+ while ( iswspace(*s) ) s++;
+
+ if (*s == 'p' || *s == 'P')
+ {
+ if (h < 13)
+ h += 12;
+ else if (h == 12)
+ h = 0;
+ }
+ }
+ else if (!bTimeOffset)
+ {
+ // entered time is an offset
+
+ *mout = h * 60 + m;
+ *hout = -1;
+
+ return TRUE;
+ }
+
+ *hout = h;
+ *mout = m;
+
+ return TRUE;
+}
+
+// returns TRUE if combo box list displays time offsets ("23:34 (5 Minutes)" etc.)
+__inline static BOOL IsRelativeCombo(HWND Dialog, UINT nIDTime)
+{
+ return (int)SendDlgItemMessage(Dialog,nIDTime,CB_GETITEMDATA,0,0) >= 0;
+}
+
+static void PopulateTimeCombo(HWND Dialog, UINT nIDTime, BOOL bRelative, const SYSTEMTIME *tmUtc)
+{
+ // NOTE: may seem like a bit excessive time converstion and handling, but this is done in order
+ // to gracefully handle crossing daylight saving boundaries
+
+ SYSTEMTIME tm2;
+ ULARGE_INTEGER li;
+ ULONGLONG ref;
+ int i, n;
+ char s[64];
+ const ULONGLONG MinutesToFileTime = (ULONGLONG)60 * FILETIME_TICKS_PER_SEC;
+ LPCSTR lpszMinutes;
+ LPCSTR lpszHours;
+ WORD wCurHour, wCurMinute;
+
+ if (!bRelative)
+ {
+ SendDlgItemMessage(Dialog,nIDTime,CB_RESETCONTENT,0,0);
+
+ // ensure that we start on midnight local time
+ SystemTimeToTzSpecificLocalTime(NULL, (SYSTEMTIME*)tmUtc, &tm2);
+ tm2.wHour = 0;
+ tm2.wMinute = 0;
+ tm2.wSecond = 0;
+ tm2.wMilliseconds = 0;
+ TzSpecificLocalTimeToSystemTime(NULL, &tm2, &tm2);
+ SYSTEMTIMEtoFILETIME(&tm2, (FILETIME*)&li);
+
+ // from 00:00 to 23:30 in 30 minute steps
+ for (i=0; i<50; i++)
+ {
+ const int h = i>>1;
+ const int m = (i&1) ? 30 : 0;
+
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ sprintf(s, "%02d:%02d", (UINT)tm2.wHour, (UINT)tm2.wMinute);
+ n = SendDlgItemMessage(Dialog,nIDTime,CB_ADDSTRING,0,(LPARAM)s);
+ // item data contains time offset from midnight in seconds (bit 31 is set to flag that
+ // combo box items are absolute times and not relative times like below
+ SendDlgItemMessage(Dialog,nIDTime,CB_SETITEMDATA,(WPARAM)n,(LPARAM)((ULONG)((h*60+m)*60) | 0x80000000));
+
+ li.QuadPart += (ULONGLONG)30 * MinutesToFileTime;
+
+ if (tm2.wHour == 23 && tm2.wMinute >= 30)
+ break;
+ }
+
+ return;
+ }
+
+ //
+
+ SendDlgItemMessage(Dialog, nIDTime, CB_RESETCONTENT, 0, 0);
+
+ lpszMinutes = Translate("Minutes");
+ lpszHours = Translate("Hours");
+
+ SYSTEMTIMEtoFILETIME(tmUtc, (FILETIME*)&li);
+ ref = li.QuadPart;
+
+ // NOTE: item data contains offset from reference time (tmUtc) in seconds
+
+ // cur time
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ wCurHour = tm2.wHour;
+ wCurMinute = tm2.wMinute;
+ mir_snprintf(s, sizeof(s), "%02d:%02d", (UINT)tm2.wHour, (UINT)tm2.wMinute);
+ n = SendDlgItemMessage(Dialog,nIDTime,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDTime,CB_SETITEMDATA,(WPARAM)n,(LPARAM)((li.QuadPart-ref)/FILETIME_TICKS_PER_SEC));
+
+ // 5 minutes
+ li.QuadPart += (ULONGLONG)5 * MinutesToFileTime;
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ mir_snprintf(s, sizeof(s), "%02d:%02d (5 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, lpszMinutes);
+ n = SendDlgItemMessage(Dialog,nIDTime,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDTime,CB_SETITEMDATA,(WPARAM)n,(LPARAM)((li.QuadPart-ref)/FILETIME_TICKS_PER_SEC));
+
+ // 10 minutes
+ li.QuadPart += (ULONGLONG)5 * MinutesToFileTime;
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ mir_snprintf(s, sizeof(s), "%02d:%02d (10 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, lpszMinutes);
+ n = SendDlgItemMessage(Dialog,nIDTime,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDTime,CB_SETITEMDATA,(WPARAM)n,(LPARAM)((li.QuadPart-ref)/FILETIME_TICKS_PER_SEC));
+
+ // 15 minutes
+ li.QuadPart += (ULONGLONG)5 * MinutesToFileTime;
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ mir_snprintf(s, sizeof(s), "%02d:%02d (15 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, lpszMinutes);
+ n = SendDlgItemMessage(Dialog,nIDTime,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDTime,CB_SETITEMDATA,(WPARAM)n,(LPARAM)((li.QuadPart-ref)/FILETIME_TICKS_PER_SEC));
+
+ // 30 minutes
+ li.QuadPart += (ULONGLONG)15 * MinutesToFileTime;
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ mir_snprintf(s, sizeof(s), "%02d:%02d (30 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, lpszMinutes);
+ n = SendDlgItemMessage(Dialog,nIDTime,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDTime,CB_SETITEMDATA,(WPARAM)n,(LPARAM)((li.QuadPart-ref)/FILETIME_TICKS_PER_SEC));
+
+ // round +1h time to nearest even or half hour
+ li.QuadPart += (ULONGLONG)30 * MinutesToFileTime;
+ li.QuadPart = (li.QuadPart / (30 * MinutesToFileTime)) * (30 * MinutesToFileTime);
+
+ // add from +1 to +23.5 (in half hour steps) if crossing daylight saving boundary it may be 22.5 or 24.5 hours
+ for (i=0; i<50; i++)
+ {
+ UINT dt;
+
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+
+ if (i > 40)
+ {
+ UINT nLastEntry = ((UINT)wCurHour * 60 + (UINT)wCurMinute) / 30;
+ if (nLastEntry)
+ nLastEntry *= 30;
+ else
+ nLastEntry = 23*60 + 30;
+
+ if (((UINT)tm2.wHour * 60 + (UINT)tm2.wMinute) == nLastEntry)
+ break;
+ }
+
+ // icq-style display 1.0, 1.5 etc. hours even though that isn't accurate due to rounding
+ //mir_snprintf(s, sizeof(s), "%02d:%02d (%d.%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, 1+(i>>1), (i&1) ? 5 : 0, lpszHours);
+ // display delta time more accurately to match reformatting (that icq doesn't do)
+ dt = (UINT)((li.QuadPart/MinutesToFileTime) - (ref/MinutesToFileTime));
+ if (dt < 60)
+ mir_snprintf(s, sizeof(s), "%02d:%02d (%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, dt, lpszMinutes);
+ else
+ mir_snprintf(s, sizeof(s), "%02d:%02d (%d.%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, dt/60, ((dt%60)*10)/60, lpszHours);
+ n = SendDlgItemMessage(Dialog,nIDTime,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDTime,CB_SETITEMDATA,(WPARAM)n,(LPARAM)(dt*60));
+
+ li.QuadPart += (ULONGLONG)30 * MinutesToFileTime;
+ }
+}
+
+static void PopulateTimeOffsetCombo(HWND Dialog, UINT nIDCombo)
+{
+ int i, n;
+ LPCSTR lpszMinutes;
+ LPCSTR lpszHour;
+ LPCSTR lpszHours;
+ LPCSTR lpszDay;
+ LPCSTR lpszDays;
+ LPCSTR lpszWeek;
+ char s[MAX_PATH];
+
+ SendDlgItemMessage(Dialog,nIDCombo,CB_RESETCONTENT,0,0);
+
+ lpszMinutes = Translate("Minutes");
+ lpszHour = Translate("Hour");
+ lpszHours = Translate("Hours");
+ lpszDay = Translate("Day");
+ lpszDays = Translate("Days");
+ lpszWeek = Translate("Week");
+
+ // 5 - 55 minutes (in 5 minute steps)
+ for (i = 1; i < 12; i++)
+ {
+ mir_snprintf(s, sizeof(s), "%d %s", i*5, lpszMinutes);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,(WPARAM)n,(LPARAM)(i*5));
+ }
+
+ // 1 hour
+ mir_snprintf(s, sizeof(s), "1 %s", lpszHour);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,(WPARAM)n,(LPARAM)60);
+
+ // 2, 4, 8 hours
+ for (i = 2; i <= 8; i+=2)
+ {
+ mir_snprintf(s, sizeof(s), "%d %s", i, lpszHours);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,(WPARAM)n,(LPARAM)(i*60));
+ }
+
+ // 1 day
+ mir_snprintf(s, sizeof(s), "1 %s", lpszDay);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,(WPARAM)n,(LPARAM)(24*60));
+
+ // 2-4 days
+ for (i = 2; i <= 4; i++)
+ {
+ mir_snprintf(s, sizeof(s), "%d %s", i, lpszDays);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,(WPARAM)n,(LPARAM)(i*24*60));
+ }
+
+ // 1 week
+ mir_snprintf(s, sizeof(s), "1 %s", lpszWeek);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,(WPARAM)n,(LPARAM)(7*24*60));
+}
+
+// returns non-zero if specified time was inside "missing" hour of daylight saving
+// IMPORTANT: triggerRelUtcOut is only initialized if IsRelativeCombo() is TRUE and return value is 0
+static int ReformatTimeInputEx(HWND Dialog, UINT nIDTime, UINT nIDRefTime, int h, int m, const SYSTEMTIME *pDateLocal, ULARGE_INTEGER *triggerRelUtcOut)
+{
+ int n;
+ UINT dt;
+ char buf[64];
+ const ULONGLONG MinutesToFileTime = (ULONGLONG)60 * FILETIME_TICKS_PER_SEC;
+
+ if (h < 0)
+ {
+ // time value is an offset ('m' holds the offset in minutes)
+
+ if ( IsRelativeCombo(Dialog, nIDTime) )
+ {
+ ULONGLONG ref;
+ ULARGE_INTEGER li;
+ SYSTEMTIME tm;
+
+ // get reference time (UTC) from hidden control
+ {
+ GetDlgItemText(Dialog, nIDRefTime, buf, 30);
+ li.QuadPart = ref = _strtoui64(buf, NULL, 16);
+ }
+
+ // clamp delta time to 23.5 hours (coule be issues otherwise as relative combo only handles <24)
+ if (m > (23*60+30))
+ m = 23*60+30;
+
+ li.QuadPart += (ULONGLONG)(m * 60) * FILETIME_TICKS_PER_SEC;
+
+ FileTimeToTzLocalST((FILETIME*)&li, &tm);
+ h = (int)tm.wHour;
+ m = (int)tm.wMinute;
+
+ if (triggerRelUtcOut)
+ *triggerRelUtcOut = li;
+
+ dt = (UINT)((li.QuadPart/MinutesToFileTime) - (ref/MinutesToFileTime));
+
+ if (dt < 60)
+ mir_snprintf(buf, sizeof(buf), "%02d:%02d (%d %s)", h, m, dt, Translate("Minutes"));
+ else
+ mir_snprintf(buf, sizeof(buf), "%02d:%02d (%d.%d %s)", h, m, dt/60, ((dt%60)*10)/60, Translate("Hours"));
+
+ // search for preset
+ n = SendDlgItemMessage(Dialog, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf);
+ if (n != CB_ERR)
+ {
+ SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, (WPARAM)n, 0);
+ return 0;
+ }
+
+ SetDlgItemText(Dialog, nIDTime, buf);
+ }
+ else
+ {
+ // should never happen
+ SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, 0, 0);
+ }
+
+ return 0;
+ }
+
+ //
+
+ sprintf(buf, "%02d:%02d", h, m);
+
+ // search for preset first
+ n = SendDlgItemMessage(Dialog, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf);
+ if (n != CB_ERR)
+ {
+ SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, (WPARAM)n, 0);
+ return 0;
+ }
+
+ if ( IsRelativeCombo(Dialog, nIDTime) )
+ {
+ // date format is a time offset ("24:43 (5 Minutes)" etc.)
+
+ ULONGLONG ref;
+ SYSTEMTIME tmRefLocal;
+ SYSTEMTIME tmTriggerLocal, tmTriggerLocal2;
+
+ // get reference time (UTC) from hidden control
+ {
+ GetDlgItemText(Dialog, nIDRefTime, buf, 30);
+ ref = _strtoui64(buf, NULL, 16);
+ }
+
+ FileTimeToTzLocalST((FILETIME*)&ref, &tmRefLocal);
+
+ {
+ ULARGE_INTEGER li;
+ const UINT nRefT = (UINT)tmRefLocal.wHour * 60 + (UINT)tmRefLocal.wMinute;
+ const UINT nT = h * 60 + m;
+
+ tmTriggerLocal = tmRefLocal;
+ tmTriggerLocal.wHour = (WORD)h;
+ tmTriggerLocal.wMinute = (WORD)m;
+ tmTriggerLocal.wSecond = 0;
+ tmTriggerLocal.wMilliseconds = 0;
+
+ if (nT < nRefT)
+ {
+ // (this special case only works correctly if time can be returned in triggerRelUtcOut)
+ if (tmRefLocal.wHour == tmTriggerLocal.wHour && triggerRelUtcOut)
+ {
+ // check for special case if daylight saving ends in this hour, then interpret as within the next hour
+ TzLocalSTToFileTime(&tmTriggerLocal, (FILETIME*)&li);
+ li.QuadPart += (ULONGLONG)3600*FILETIME_TICKS_PER_SEC;
+ FileTimeToTzLocalST((FILETIME*)&li, &tmTriggerLocal2);
+ if ((tmTriggerLocal2.wHour*60+tmTriggerLocal2.wMinute) == (tmTriggerLocal.wHour*60+tmTriggerLocal.wMinute))
+ // special case detected
+ goto output_result;
+ }
+
+ // tomorrow (add 24h to local time)
+ SYSTEMTIMEtoFILETIME(&tmTriggerLocal, (FILETIME*)&li);
+ li.QuadPart += (ULONGLONG)(24*3600)*FILETIME_TICKS_PER_SEC;
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li, &tmTriggerLocal);
+ }
+
+ // clean up value for potential daylight saving boundary
+ TzLocalSTToFileTime(&tmTriggerLocal, (FILETIME*)&li);
+ FileTimeToTzLocalST((FILETIME*)&li, &tmTriggerLocal2);
+
+ // NOTE: win32 time functions will round hour downward if supplied hour does not exist due to daylight saving
+ // for example if supplied time is 02:30 on the night the clock is turned forward at 02:00 to 03:00, thus
+ // there never is a 02:30, the time functions will convert it to 01:30 (and not 03:30 as one might think)
+ // (02:00 would return 01:00)
+
+ // check for special case when the current time and requested time is inside the "missing" hour
+ // standard->daylight switch, so that the cleaned up time ends up being earlier than the current
+ // time even though it originally wasn't (see note above)
+ if ((tmTriggerLocal2.wHour*60+tmTriggerLocal2.wMinute) < (tmTriggerLocal.wHour*60+tmTriggerLocal.wMinute))
+ {
+ // special case detected, fall back to current time so at least the reminder won't be missed
+ // due to ending up at an undesired time (this way the user immediately notices something was wrong)
+ SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, 0, 0);
+invalid_dst:
+ MessageBox(Dialog, Translate("The specified time is invalid due to begin of daylight saving (summer time)."), SECTIONNAME, MB_OK|MB_ICONWARNING);
+ return 1;
+ }
+
+output_result:
+ if (triggerRelUtcOut)
+ *triggerRelUtcOut = li;
+
+ dt = (UINT)((li.QuadPart/MinutesToFileTime) - (ref/MinutesToFileTime));
+
+ if (dt < 60)
+ mir_snprintf(buf, sizeof(buf), "%02d:%02d (%d %s)", h, m, dt, Translate("Minutes"));
+ else
+ mir_snprintf(buf, sizeof(buf), "%02d:%02d (%d.%d %s)", h, m, dt/60, ((dt%60)*10)/60, Translate("Hours"));
+ }
+ }
+ else
+ {
+ // absolute time (00:00 to 23:59), clean up time to make sure it's not inside "missing" hour (will be rounded downard)
+
+ FILETIME ft;
+ SYSTEMTIME Date = *pDateLocal;
+ Date.wHour = h;
+ Date.wMinute = m;
+ Date.wSecond = 0;
+ Date.wMilliseconds = 0;
+
+ TzLocalSTToFileTime(&Date, &ft);
+ FileTimeToTzLocalST(&ft, &Date);
+
+ if ((int)Date.wHour != h || (int)Date.wMinute != m)
+ {
+ sprintf(buf, "%02d:%02d", (UINT)Date.wHour, (UINT)Date.wMinute);
+
+ // search for preset again
+ n = SendDlgItemMessage(Dialog, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf);
+ if (n != CB_ERR)
+ {
+ SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, (WPARAM)n, 0);
+ goto invalid_dst;
+ }
+
+ SetDlgItemText(Dialog, nIDTime, buf);
+
+ goto invalid_dst;
+ }
+ }
+
+ SetDlgItemText(Dialog, nIDTime, buf);
+
+ return 0;
+}
+
+static __inline int ReformatTimeInput(HWND Dialog, UINT nIDTime, UINT nIDRefTime, int h, int m, const SYSTEMTIME *pDateLocal)
+{
+ return ReformatTimeInputEx(Dialog, nIDTime, nIDRefTime, h, m, pDateLocal, NULL);
+}
+
+// in: pDate contains the desired trigger date in LOCAL time
+// out: pDate contains the resulting trigger time and date in UTC
+static BOOL GetTriggerTime(HWND Dialog, UINT nIDTime, UINT nIDRefTime, SYSTEMTIME *pDate)
+{
+ ULARGE_INTEGER li;
+ char buf[32];
+ int n;
+ int h, m;
+
+ // get reference (UTC) time from hidden control
+ {
+ GetDlgItemText(Dialog, nIDRefTime, buf, 30);
+ li.QuadPart = _strtoui64(buf, NULL, 16);
+ }
+
+ if ((n = SendDlgItemMessage(Dialog, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR)
+ {
+ // use preset value
+preset_value:;
+ if ( IsRelativeCombo(Dialog, nIDTime) )
+ {
+ // time offset from ref time ("24:43 (5 Minutes)" etc.)
+
+ UINT nDeltaSeconds = (UINT)SendDlgItemMessage(Dialog, nIDTime, CB_GETITEMDATA, (WPARAM)n, 0);
+ li.QuadPart += (ULONGLONG)nDeltaSeconds * FILETIME_TICKS_PER_SEC;
+
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li, pDate);
+
+ // if specified time is a small offset (< 10 Minutes) then retain current second count for better accuracy
+ // otherwise try to match specified time (which never contains seconds only even minutes) as close as possible
+ if (nDeltaSeconds >= 10*60)
+ {
+ pDate->wSecond = 0;
+ pDate->wMilliseconds = 0;
+ }
+ }
+ else
+ {
+ // absolute time (offset from midnight on pDate)
+
+ UINT nDeltaSeconds = (UINT)((ULONG)SendDlgItemMessage(Dialog, nIDTime, CB_GETITEMDATA, (WPARAM)n, 0) & ~0x80000000);
+ pDate->wHour = 0;
+ pDate->wMinute = 0;
+ pDate->wSecond = 0;
+ pDate->wMilliseconds = 0;
+ TzLocalSTToFileTime(pDate, (FILETIME*)&li);
+ li.QuadPart += (ULONGLONG)nDeltaSeconds * FILETIME_TICKS_PER_SEC;
+
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li, pDate);
+ }
+
+ return TRUE;
+ }
+
+ // user entered a custom value
+
+ GetDlgItemText(Dialog, nIDTime, buf, 30);
+
+ if ( !ParseTime(buf, &h, &m, FALSE, IsRelativeCombo(Dialog, nIDTime)) )
+ {
+ MessageBox(Dialog, Translate("The specified time is invalid."), SECTIONNAME, MB_OK|MB_ICONWARNING);
+ return FALSE;
+ }
+
+ if ( IsRelativeCombo(Dialog, nIDTime) )
+ {
+ // date has not been changed, the specified time is a time between reftime and reftime+24h
+
+ ULARGE_INTEGER li2;
+
+ if ( ReformatTimeInputEx(Dialog, nIDTime, nIDRefTime, h, m, pDate, &li2) )
+ return FALSE;
+
+ // check if reformatted value is a preset
+ if ((n = SendDlgItemMessage(Dialog, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR)
+ goto preset_value;
+
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li2, pDate);
+
+ return TRUE;
+ }
+ else
+ {
+ if ( ReformatTimeInputEx(Dialog, nIDTime, nIDRefTime, h, m, pDate, NULL) )
+ return FALSE;
+
+ // check if reformatted value is a preset
+ if ((n = SendDlgItemMessage(Dialog, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR)
+ goto preset_value;
+ }
+
+ // absolute time (on pDate)
+
+ pDate->wHour = h;
+ pDate->wMinute = m;
+ pDate->wSecond = 0;
+ pDate->wMilliseconds = 0;
+
+ TzLocalSTToFileTime(pDate, (FILETIME*)&li);
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li, pDate);
+
+ return TRUE;
+}
+
+static void OnDateChanged(HWND Dialog, UINT nDateID, UINT nTimeID, UINT nRefTimeID)
+{
+ // repopulate time combo list with regular times (not offsets like "23:32 (5 minutes)" etc.)
+
+ SYSTEMTIME Date, DateUtc;
+ int h = -1, m;
+ char s[32];
+
+ GetDlgItemText(Dialog, nTimeID, s, 30);
+
+ ParseTime(s, &h, &m, FALSE, FALSE);
+
+ SendDlgItemMessage(Dialog, nDateID, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date);
+
+ TzSpecificLocalTimeToSystemTime(NULL, &Date, &DateUtc);
+ PopulateTimeCombo(Dialog, nTimeID, FALSE, &DateUtc);
+
+ if (h < 0)
+ {
+ // parsing failed, default to current time
+ SYSTEMTIME tm;
+ GetLocalTime(&tm);
+ h = (UINT)tm.wHour;
+ m = (UINT)tm.wMinute;
+ }
+
+ ReformatTimeInput(Dialog, nTimeID, nRefTimeID, h, m, &Date);
+}
+
+
+int CALLBACK DlgProcNotifyReminder(HWND Dialog,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ int I;
+
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+ {
+ SYSTEMTIME tm;
+ ULARGE_INTEGER li;
+
+ GetSystemTime(&tm);
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&li);
+
+ TranslateDialogDefault(Dialog);
+
+ // save reference time in hidden control, is needed when adding reminder to properly detect if speicifed
+ // time wrapped around to tomorrow or not (dialog could in theory be open for a longer period of time
+ // which could potentially mess up things otherwise)
+ {
+ char s[32];
+ sprintf(s, "%I64x", li.QuadPart);
+ SetDlgItemText(Dialog, IDC_REFTIME, s);
+ }
+
+ BringWindowToTop(Dialog);
+
+ PopulateTimeOffsetCombo(Dialog, IDC_REMINDAGAININ);
+
+ ShowWindow(GetDlgItem(Dialog,IDC_REMINDAGAININ),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_DATEAGAIN),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_TIMEAGAIN),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_STATIC_DATE),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_STATIC_TIME),SW_HIDE);
+ SendDlgItemMessage(Dialog,IDC_AFTER,BM_SETCHECK,1,0);
+ SendDlgItemMessage(Dialog,IDC_ONDATE,BM_SETCHECK,0,0);
+ SendDlgItemMessage(Dialog,IDC_REMINDAGAININ,CB_SETCURSEL,0,0);
+
+ SendDlgItemMessage(Dialog, IDC_REMDATA, EM_LIMITTEXT, MAX_REMINDER_LEN, 0);
+
+ PopulateTimeCombo(Dialog, IDC_TIMEAGAIN, TRUE, &tm);
+
+ FileTimeToTzLocalST((FILETIME*)&li, &tm);
+
+ // make sure date picker uses reference time
+ SendDlgItemMessage(Dialog,IDC_DATEAGAIN,DTM_SETSYSTEMTIME,0,(LPARAM)&tm);
+ InitDatePicker(Dialog, IDC_DATEAGAIN);
+
+ SendDlgItemMessage(Dialog,IDC_TIMEAGAIN,CB_SETCURSEL,0,0);
+
+ return TRUE;
+ }
+ case WM_NCDESTROY:
+ RemoveProp(GetDlgItem(Dialog, IDC_DATEAGAIN), TEXT("OldWndProc"));
+ return TRUE;
+ case WM_NOTIFY:
+ {
+ if (wParam == IDC_DATEAGAIN)
+ {
+ NMLISTVIEW *NM = (NMLISTVIEW*)lParam;
+
+ switch (NM->hdr.code)
+ {
+ case DTN_DATETIMECHANGE:
+ OnDateChanged(Dialog, IDC_DATEAGAIN, IDC_TIMEAGAIN, IDC_REFTIME);
+ break;
+ }
+ }
+ }
+ break;
+ case WM_CLOSE:
+ {
+ int ReminderCount = TreeGetCount(RemindersList);
+ for (I = 0; I < ReminderCount; I++)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I);
+
+ if (pReminder->handle == Dialog)
+ {
+ DeleteReminder(pReminder);
+ JustSaveReminders();
+ break;
+ }
+ }
+ NOTIFY_LIST();
+ }
+ DestroyWindow(Dialog);
+ return TRUE;
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_TIMEAGAIN:
+ switch (HIWORD(wParam))
+ {
+ case CBN_KILLFOCUS:
+ // reformat displayed value
+ if (SendDlgItemMessage(Dialog, IDC_TIMEAGAIN, CB_GETCURSEL, 0, 0) == CB_ERR)
+ {
+ char buf[64];
+ int h, m;
+ GetDlgItemText(Dialog, IDC_TIMEAGAIN, buf, 30);
+
+ if ( ParseTime(buf, &h, &m, FALSE, IsRelativeCombo(Dialog, IDC_TIMEAGAIN)) )
+ {
+ SYSTEMTIME Date;
+ SendDlgItemMessage(Dialog,IDC_DATEAGAIN,DTM_GETSYSTEMTIME,0,(LPARAM)&Date);
+
+ ReformatTimeInput(Dialog, IDC_TIMEAGAIN, IDC_REFTIME, h, m, &Date);
+ }
+ else
+ {
+ SendDlgItemMessage(Dialog, IDC_TIMEAGAIN, CB_SETCURSEL, 0, 0);
+ }
+ }
+ break;
+ }
+ break;
+
+ case IDC_REMINDAGAININ:
+ switch (HIWORD(wParam))
+ {
+ case CBN_KILLFOCUS:
+ // reformat displayed value if it has been edited
+ if (SendDlgItemMessage(Dialog,IDC_REMINDAGAININ,CB_GETCURSEL,0,0) == CB_ERR)
+ {
+ char buf[64];
+ int h, m;
+ GetDlgItemText(Dialog, IDC_REMINDAGAININ, buf, 30);
+
+ if (ParseTime(buf, &h, &m, TRUE, TRUE) && h+m)
+ {
+ if (h)
+ {
+ LPCSTR lpszHours = Translate("Hours");
+ sprintf(buf, "%d:%02d %s", h, m, lpszHours);
+ }
+ else
+ {
+ LPCSTR lpszMinutes = Translate("Minutes");
+ sprintf(buf, "%d %s", m, lpszMinutes);
+ }
+ SetDlgItemText(Dialog, IDC_REMINDAGAININ, buf);
+ }
+ else
+ {
+ SendDlgItemMessage(Dialog,IDC_REMINDAGAININ,CB_SETCURSEL,0,0);
+ }
+ }
+ break;
+ }
+ break;
+
+ case IDC_AFTER:
+ {
+ ShowWindow(GetDlgItem(Dialog,IDC_REMINDAGAININ),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_DATEAGAIN),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_TIMEAGAIN),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_STATIC_DATE),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_STATIC_TIME),SW_HIDE);
+ return TRUE;
+ }
+ case IDC_ONDATE:
+ {
+ ShowWindow(GetDlgItem(Dialog,IDC_DATEAGAIN),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_TIMEAGAIN),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_STATIC_DATE),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_STATIC_TIME),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_REMINDAGAININ),SW_HIDE);
+ return TRUE;
+ }
+ case IDC_DISMISS:
+ {
+ int ReminderCount = TreeGetCount(RemindersList);
+ for (I = 0; I < ReminderCount; I++)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I);
+
+ if (pReminder->handle == Dialog)
+ {
+ DeleteReminder(pReminder);
+ JustSaveReminders();
+ break;
+ }
+ }
+ NOTIFY_LIST();
+ DestroyWindow(Dialog);
+ return TRUE;
+ }
+ case IDC_REMINDAGAIN:
+ {
+ int ReminderCount = TreeGetCount(RemindersList);
+ for (I = 0; I < ReminderCount; I++)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I);
+
+ if (pReminder->handle == Dialog)
+ {
+ if (SendDlgItemMessage(Dialog,IDC_AFTER,BM_GETCHECK,0,0) == BST_CHECKED)
+ {
+ // delta time
+
+ ULONGLONG TT;
+ SYSTEMTIME tm;
+ ULARGE_INTEGER li;
+ int n;
+
+ GetSystemTime(&tm);
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&li);
+
+ n = SendDlgItemMessage(Dialog,IDC_REMINDAGAININ,CB_GETCURSEL,0,0);
+ if (n != CB_ERR)
+ {
+ TT = SendDlgItemMessage(Dialog,IDC_REMINDAGAININ,CB_GETITEMDATA,(WPARAM)n,0) * 60;
+
+ if (TT >= 24*3600)
+ {
+ // selection is 1 day or more, take daylight saving boundaries into consideration
+ // (ie. 24 hour might actually be 23 or 25, in order for reminder to pop up at the
+ // same time tomorrow)
+ SYSTEMTIME tm2, tm3;
+ ULARGE_INTEGER li2 = li;
+ FileTimeToTzLocalST((FILETIME*)&li2, &tm2);
+ li2.QuadPart += (TT * FILETIME_TICKS_PER_SEC);
+ FileTimeToTzLocalST((FILETIME*)&li2, &tm3);
+ if (tm2.wHour != tm3.wHour || tm2.wMinute != tm3.wMinute)
+ {
+ // boundary crossed
+
+ // do a quick and dirty sanity check that times not more than 2 hours apart
+ if (abs((int)(tm3.wHour*60+tm3.wMinute)-(int)(tm2.wHour*60+tm2.wMinute)) <= 120)
+ {
+ // adjust TT so that same HH:MM is set
+ tm3.wHour = tm2.wHour;
+ tm3.wMinute = tm2.wMinute;
+ TzLocalSTToFileTime(&tm3, (FILETIME*)&li2);
+ TT = (li2.QuadPart - li.QuadPart) / FILETIME_TICKS_PER_SEC;
+ }
+ }
+ }
+ }
+ else
+ {
+ // parse user input
+ char s[32];
+ int h = 0, m = 0;
+ GetDlgItemText(Dialog, IDC_REMINDAGAININ, s, 30);
+ ParseTime(s, &h, &m, TRUE, TRUE);
+ m += h * 60;
+ if (!m)
+ {
+ MessageBox(Dialog, Translate("The specified time offset is invalid."), SECTIONNAME, MB_OK|MB_ICONWARNING);
+ return TRUE;
+ }
+
+ TT = m * 60;
+ }
+
+ pReminder->When = li;
+ pReminder->When.QuadPart += (TT * FILETIME_TICKS_PER_SEC);
+ }
+ else if (SendDlgItemMessage(Dialog,IDC_ONDATE,BM_GETCHECK,0,0) == BST_CHECKED)
+ {
+ SYSTEMTIME Date;
+
+ SendDlgItemMessage(Dialog,IDC_DATEAGAIN,DTM_GETSYSTEMTIME,0,(LPARAM)&Date);
+
+ if ( !GetTriggerTime(Dialog, IDC_TIMEAGAIN, IDC_REFTIME, &Date) )
+ break;
+
+ SYSTEMTIMEtoFILETIME(&Date, (FILETIME*)&pReminder->When);
+ }
+
+ // update reminder text
+ {
+ char *ReminderText = NULL;
+ int SzT = SendDlgItemMessage(Dialog,IDC_REMDATA,WM_GETTEXTLENGTH,0,0);
+ if (SzT)
+ {
+ if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN;
+ ReminderText = (char*)malloc(SzT+1);
+ SendDlgItemMessage(Dialog,IDC_REMDATA,WM_GETTEXT,SzT+1,(LPARAM)ReminderText);
+ }
+ if (pReminder->Reminder)
+ free(pReminder->Reminder);
+ pReminder->Reminder = ReminderText;
+ }
+
+ pReminder->RemVisible = FALSE;
+ pReminder->handle = NULL;
+
+ // re-insert tree item sorted
+ TreeDelete(&RemindersList,pReminder);
+ TreeAddSorted(&RemindersList,pReminder,ReminderSortCb);
+ JustSaveReminders();
+ break;
+ }
+ }
+ NOTIFY_LIST();
+ DestroyWindow(Dialog);
+ return TRUE;
+ }
+ case IDC_NONE:
+ {// create note from remainder
+ int ReminderCount = TreeGetCount(RemindersList);
+ for (I = 0; I < ReminderCount; I++)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I);
+
+ if (pReminder->handle == Dialog)
+ {
+ // get up-to-date reminder text
+ char *ReminderText = NULL;
+ int SzT = SendDlgItemMessage(Dialog,IDC_REMDATA,WM_GETTEXTLENGTH,0,0);
+ if (SzT)
+ {
+ if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN;
+ ReminderText = (char*)malloc(SzT+1);
+ SendDlgItemMessage(Dialog,IDC_REMDATA,WM_GETTEXT,SzT+1,(LPARAM)ReminderText);
+ }
+
+ SetFocus(NewNote(0, 0, -1, -1, ReminderText, 0, TRUE, TRUE, 0)->REHwnd);
+ break;
+ }
+ }
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+int CALLBACK DlgProcNewReminder(HWND Dialog,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ HICON hIcon = NULL;
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+ {
+ ULARGE_INTEGER li;
+ SYSTEMTIME tm;
+
+ if (NewReminderVisible == 2)
+ {
+ // opening the edit reminder dialog (uses same dialog resource as add reminder)
+ SetWindowText(Dialog, _T("Reminder"));
+ SetDlgItemText(Dialog, IDC_ADDREMINDER, _T("&Update Reminder"));
+ ShowWindow(GetDlgItem(Dialog, IDC_VIEWREMINDERS), SW_HIDE);
+
+ li = pEditReminder->When;
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li, &tm);
+ }
+ else
+ {
+ GetSystemTime(&tm);
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&li);
+ }
+
+ TranslateDialogDefault(Dialog);
+
+ // save reference time in hidden control, is needed when adding reminder to properly detect if speicifed
+ // time wrapped around to tomorrow or not (dialog could in theory be open for a longer period of time
+ // which could potentially mess up things otherwise)
+ {
+ char s[32];
+ sprintf(s, "%I64x", li.QuadPart);
+ SetDlgItemText(Dialog, IDC_REFTIME, s);
+ }
+
+ /*SendDlgItemMessage(Dialog,IDC_NONE,BM_SETCHECK,1,0);
+ SendDlgItemMessage(Dialog,IDC_DAILY,BM_SETCHECK,0,0);
+ SendDlgItemMessage(Dialog,IDC_WEEKLY,BM_SETCHECK,0,0);
+ SendDlgItemMessage(Dialog,IDC_MONTHLY,BM_SETCHECK,0,0);*/
+
+ PopulateTimeCombo(Dialog, IDC_TIME, NewReminderVisible != 2, &tm);
+
+ FileTimeToTzLocalST((FILETIME*)&li, &tm);
+
+ // make sure date picker uses reference time
+ SendDlgItemMessage(Dialog,IDC_DATE,DTM_SETSYSTEMTIME,0,(LPARAM)&tm);
+ InitDatePicker(Dialog, IDC_DATE);
+
+ SendDlgItemMessage(Dialog, IDC_REMINDER, EM_LIMITTEXT, MAX_REMINDER_LEN, 0);
+
+ if (NewReminderVisible == 2)
+ {
+ int n;
+ char s[32];
+ mir_snprintf(s, sizeof(s), "%02d:%02d", (UINT)tm.wHour, (UINT)tm.wMinute);
+
+ // search for preset first
+ n = SendDlgItemMessage(Dialog, IDC_TIME, CB_FINDSTRING, (WPARAM)-1, (LPARAM)s);
+ if (n != CB_ERR)
+ SendDlgItemMessage(Dialog, IDC_TIME, CB_SETCURSEL, (WPARAM)n, 0);
+ else
+ SetDlgItemText(Dialog, IDC_TIME, s);
+
+ SetDlgItemText(Dialog, IDC_REMINDER, pEditReminder->Reminder);
+ }
+ else
+ {
+ SendDlgItemMessage(Dialog,IDC_TIME,CB_SETCURSEL,0,0);
+ }
+
+ // populate sound repeat combo
+ {
+ char s[64];
+ int n;
+ LPCSTR lpszEvery = Translate("Every");
+ LPCSTR lpszSeconds = Translate("Seconds");
+
+ // NOTE: use multiples of REMINDER_UPDATE_INTERVAL_SHORT (currently 5 seconds)
+
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_ADDSTRING,0,(LPARAM)Translate("Never"));
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)0);
+
+ mir_snprintf(s, sizeof(s), "%s 5 %s", lpszEvery, lpszSeconds);
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)5);
+
+ mir_snprintf(s, sizeof(s), "%s 10 %s", lpszEvery, lpszSeconds);
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)10);
+
+ mir_snprintf(s, sizeof(s), "%s 15 %s", lpszEvery, lpszSeconds);
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)15);
+
+ mir_snprintf(s, sizeof(s), "%s 20 %s", lpszEvery, lpszSeconds);
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)20);
+
+ mir_snprintf(s, sizeof(s), "%s 30 %s", lpszEvery, lpszSeconds);
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)30);
+
+ mir_snprintf(s, sizeof(s), "%s 60 %s", lpszEvery, lpszSeconds);
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)60);
+
+ if (NewReminderVisible == 2 && pEditReminder->RepeatSound)
+ {
+ mir_snprintf(s, sizeof(s), "%s %d %s", lpszEvery, pEditReminder->RepeatSound, lpszSeconds);
+ SetDlgItemText(Dialog, IDC_COMBO_REPEATSND, s);
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETCURSEL,SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_FINDSTRINGEXACT,(WPARAM)-1,(LPARAM)s),0);
+ }
+ else
+ {
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETCURSEL,0,0);
+ }
+ }
+
+ // populate sound selection combo
+ {
+ int n = SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_ADDSTRING,0,(LPARAM)Translate("Default"));
+ SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)0);
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_ADDSTRING,0,(LPARAM)Translate("Alternative 1"));
+ SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)1);
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_ADDSTRING,0,(LPARAM)Translate("Alternative 2"));
+ SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)2);
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_ADDSTRING,0,(LPARAM)Translate("None"));
+ SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)-1);
+
+ if (NewReminderVisible == 2 && pEditReminder->SoundSel)
+ {
+ const UINT n = pEditReminder->SoundSel<0 ? 3 : pEditReminder->SoundSel;
+ SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETCURSEL,n,0);
+ }
+ else
+ {
+ SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETCURSEL,0,0);
+ }
+ }
+
+ hIcon = Skin_GetIconByHandle(hIconLibItem[12]);
+ SendDlgItemMessage(Dialog,IDC_BTN_PLAYSOUND,BM_SETIMAGE,(WPARAM)IMAGE_ICON,(LPARAM)hIcon);
+
+ if (NewReminderVisible == 2 && pEditReminder->SoundSel)
+ {
+ EnableWindow(GetDlgItem(Dialog, IDC_BTN_PLAYSOUND), pEditReminder->SoundSel>=0);
+ EnableWindow(GetDlgItem(Dialog, IDC_COMBO_REPEATSND), pEditReminder->SoundSel>=0);
+ }
+
+ if (NewReminderVisible == 2)
+ SetFocus( GetDlgItem(Dialog, IDC_ADDREMINDER) );
+ else
+ SetFocus( GetDlgItem(Dialog, IDC_REMINDER) );
+
+ return FALSE;
+ }
+ case WM_NCDESTROY:
+ RemoveProp(GetDlgItem(Dialog, IDC_DATE), TEXT("OldWndProc"));
+ return TRUE;
+ case WM_CLOSE:
+ {
+ if (NewReminderVisible == 2)
+ {
+ pEditReminder->RemVisible = FALSE;
+ }
+ DestroyWindow(Dialog);
+ NewReminderVisible = FALSE;
+ pEditReminder = NULL;
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if (wParam == IDC_DATE)
+ {
+ NMLISTVIEW *NM = (NMLISTVIEW*)lParam;
+
+ switch (NM->hdr.code)
+ {
+ case DTN_DATETIMECHANGE:
+ OnDateChanged(Dialog, IDC_DATE, IDC_TIME, IDC_REFTIME);
+ break;
+ }
+ }
+ }
+ break;
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_TIME:
+ switch (HIWORD(wParam))
+ {
+ case CBN_KILLFOCUS:
+ // reformat displayed value
+ if (SendDlgItemMessage(Dialog, IDC_TIME, CB_GETCURSEL, 0, 0) == CB_ERR)
+ {
+ char buf[64];
+ int h, m;
+ GetDlgItemText(Dialog, IDC_TIME, buf, 30);
+
+ if ( ParseTime(buf, &h, &m, FALSE, IsRelativeCombo(Dialog, IDC_TIME)) )
+ {
+ SYSTEMTIME Date;
+ SendDlgItemMessage(Dialog,IDC_DATE,DTM_GETSYSTEMTIME,0,(LPARAM)&Date);
+ ReformatTimeInput(Dialog, IDC_TIME, IDC_REFTIME, h, m, &Date);
+ }
+ else
+ {
+ SendDlgItemMessage(Dialog, IDC_TIME, CB_SETCURSEL, 0, 0);
+ }
+ }
+ break;
+ }
+ break;
+ case IDC_COMBO_SOUND:
+ switch (HIWORD(wParam))
+ {
+ case CBN_SELENDOK:
+ {
+ int n = SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0);
+ n = (int)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETITEMDATA, (WPARAM)n, 0);
+
+ EnableWindow(GetDlgItem(Dialog, IDC_BTN_PLAYSOUND), n>=0);
+ EnableWindow(GetDlgItem(Dialog, IDC_COMBO_REPEATSND), n>=0);
+ }
+ break;
+ }
+ break;
+
+ case IDC_BTN_PLAYSOUND:
+ {
+ int n = SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0);
+ n = (int)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETITEMDATA, (WPARAM)n, 0);
+ switch (n)
+ {
+ case 0: SkinPlaySound("AlertReminder"); break;
+ case 1: SkinPlaySound("AlertReminder2"); break;
+ case 2: SkinPlaySound("AlertReminder3"); break;
+ }
+ }
+ return TRUE;
+ case IDC_CLOSE:
+ {
+ if (NewReminderVisible == 2)
+ {
+ pEditReminder->RemVisible = FALSE;
+ }
+ DestroyWindow(Dialog);
+ NewReminderVisible = FALSE;
+ pEditReminder = NULL;
+ return TRUE;
+ }
+ case IDC_VIEWREMINDERS:
+ {
+ ListReminders();
+ return TRUE;
+ }
+ case IDC_ADDREMINDER:
+ {
+ char *ReminderText = NULL;
+ int SzT;
+ SYSTEMTIME Date;
+ REMINDERDATA* TempRem;
+ int RepeatSound;
+
+ SendDlgItemMessage(Dialog,IDC_DATE,DTM_GETSYSTEMTIME,0,(LPARAM)&Date);
+
+ if ( !GetTriggerTime(Dialog, IDC_TIME, IDC_REFTIME, &Date) )
+ break;
+
+ RepeatSound = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_GETCURSEL,0,0);
+ if (RepeatSound != CB_ERR)
+ RepeatSound = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_GETITEMDATA,(WPARAM)RepeatSound,0);
+ else
+ RepeatSound = 0;
+
+ SzT = SendDlgItemMessage(Dialog,IDC_REMINDER,WM_GETTEXTLENGTH,0,0);
+ if (SzT)
+ {
+ if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN;
+ ReminderText = (char*)malloc(SzT+1);
+ SendDlgItemMessage(Dialog,IDC_REMINDER,WM_GETTEXT,SzT+1,(LPARAM)ReminderText);
+ }
+
+ if (NewReminderVisible != 2)
+ {
+ // new reminder
+ TempRem = (REMINDERDATA*)malloc(sizeof(REMINDERDATA));
+ TempRem->uid = CreateUid();
+ SYSTEMTIMEtoFILETIME(&Date, (FILETIME*)&TempRem->When);
+ TempRem->Reminder = ReminderText;
+ TempRem->RemVisible = FALSE;
+ TempRem->SystemEventQueued = 0;
+ TempRem->RepeatSoundTTL = 0;
+ TempRem->SoundSel = (int)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETITEMDATA, (WPARAM)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0), 0);
+ TempRem->RepeatSound = TempRem->SoundSel<0 ? 0 : (UINT)RepeatSound;
+ TreeAddSorted(&RemindersList,TempRem,ReminderSortCb);
+ }
+ else
+ {
+ // update existing reminder
+
+ SYSTEMTIMEtoFILETIME(&Date, (FILETIME*)&pEditReminder->When);
+ if (pEditReminder->Reminder)
+ free(pEditReminder->Reminder);
+ pEditReminder->Reminder = ReminderText;
+ pEditReminder->SoundSel = (int)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETITEMDATA, (WPARAM)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0), 0);
+ pEditReminder->RepeatSound = pEditReminder->SoundSel<0 ? 0 : (UINT)RepeatSound;
+
+ // re-insert tree item sorted
+ TreeDelete(&RemindersList,pEditReminder);
+ TreeAddSorted(&RemindersList,pEditReminder,ReminderSortCb);
+
+ pEditReminder->RemVisible = FALSE;
+ }
+ SetDlgItemText(Dialog,IDC_REMINDER,"");
+ JustSaveReminders();
+ NOTIFY_LIST();
+
+ if (g_CloseAfterAddReminder || NewReminderVisible == 2)
+ {
+ DestroyWindow(Dialog);
+ NewReminderVisible = FALSE;
+ pEditReminder = NULL;
+ }
+ }
+ }
+ }
+ case WM_DESTROY:
+ {
+ Skin_ReleaseIcon(hIcon);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static void InitListView(HWND AHLV)
+{
+ LV_ITEM lvTIt;
+ int I;
+ char *S;
+ char S1[128];
+ REMINDERDATA *pReminder;
+ TREEELEMENT *TTE;
+
+ ListView_SetHoverTime(AHLV,700);
+ ListView_SetExtendedListViewStyle(AHLV,LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_TRACKSELECT);
+ ListView_DeleteAllItems(AHLV);
+
+ I = 0;
+ TTE = RemindersList;
+ while (TTE)
+ {
+ pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ lvTIt.mask = LVIF_TEXT;
+
+ GetTriggerTimeString(&pReminder->When, S1, sizeof(S1), TRUE);
+
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 0;
+ lvTIt.pszText = S1;
+ lvTIt.cchTextMax = strlen(S1);
+ ListView_InsertItem(AHLV,&lvTIt);
+ lvTIt.mask = LVIF_TEXT;
+ S = GetPreviewString(pReminder->Reminder);
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 1;
+ lvTIt.pszText = S;
+ lvTIt.cchTextMax = strlen(S);
+ ListView_SetItem(AHLV,&lvTIt);
+
+ I++;
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ ListView_SetItemState(AHLV,0,LVIS_SELECTED,LVIS_SELECTED);
+}
+
+void OnListResize(HWND Dialog)
+{
+ HWND hList, hText, hBtnNew, hBtnClose;
+ RECT lr, cr, tr, clsr;
+ int th, btnh, btnw, MARGIN;
+ POINT org = {0};
+
+ hList = GetDlgItem(Dialog, IDC_LISTREMINDERS);
+ hText = GetDlgItem(Dialog, IDC_REMINDERDATA);
+ hBtnNew = GetDlgItem(Dialog, IDC_ADDNEWREMINDER);
+ hBtnClose = GetDlgItem(Dialog, IDC_CLOSE);
+
+ ClientToScreen(Dialog, &org);
+ GetClientRect(Dialog, &cr);
+
+ GetWindowRect(hList, &lr); OffsetRect(&lr, -org.x, -org.y);
+ GetWindowRect(hText, &tr); OffsetRect(&tr, -org.x, -org.y);
+ GetWindowRect(hBtnClose, &clsr); OffsetRect(&clsr, -org.x, -org.y);
+
+ MARGIN = lr.left;
+
+ th = tr.bottom - tr.top;
+ btnh = clsr.bottom - clsr.top;
+ btnw = clsr.right - clsr.left;
+
+ clsr.left = cr.right - MARGIN - btnw;
+ clsr.top = cr.bottom - MARGIN - btnh;
+
+ tr.left = MARGIN;
+ tr.right = cr.right - MARGIN;
+ tr.bottom = clsr.top - MARGIN - 2;
+ tr.top = tr.bottom - th;
+
+ lr.right = cr.right - MARGIN;
+ lr.bottom = tr.top - 5;
+
+ MoveWindow(hList, lr.left, lr.top, lr.right-lr.left, lr.bottom-lr.top, FALSE);
+ MoveWindow(hText, tr.left, tr.top, tr.right-tr.left, tr.bottom-tr.top, FALSE);
+
+ MoveWindow(hBtnClose, clsr.left, clsr.top, btnw, btnh, FALSE);
+ clsr.left -= btnw + 2;
+ MoveWindow(hBtnNew, clsr.left, clsr.top, btnw, btnh, FALSE);
+
+ RedrawWindow(Dialog, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
+ //UpdateWindow(Dialog);
+}
+
+void UpdateGeomFromWnd(HWND Dialog, int *geom, int *colgeom, int nCols)
+{
+ if (geom)
+ {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(WINDOWPLACEMENT);
+
+ GetWindowPlacement(Dialog, &wp);
+
+ geom[0] = wp.rcNormalPosition.left;
+ geom[1] = wp.rcNormalPosition.top;
+ geom[2] = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+ geom[3] = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+ }
+
+ if (colgeom)
+ {
+ int i;
+ HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS);
+
+ for (i=0; i<nCols; i++)
+ {
+ colgeom[i] = ListView_GetColumnWidth(H, i);
+ }
+ }
+}
+
+static BOOL DoListContextMenu(HWND AhWnd,WPARAM wParam,LPARAM lParam,REMINDERDATA *pReminder)
+{
+ HWND hwndListView;
+ HMENU hMenuLoad,FhMenu;
+ MENUITEMINFO mii;
+
+ hwndListView = (HWND)wParam;
+ if (hwndListView != GetDlgItem(AhWnd,IDC_LISTREMINDERS)) return FALSE;
+ hMenuLoad = LoadMenu(hinstance,"MNU_REMINDERPOPUP");
+ FhMenu = GetSubMenu(hMenuLoad,0);
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = MFS_DEFAULT;
+ if (!pReminder || pReminder->SystemEventQueued)
+ mii.fState |= MFS_GRAYED;
+ SetMenuItemInfo(FhMenu, ID_CONTEXTMENUREMINDERLISTVIEW_EDIT, FALSE, &mii);
+
+ if (!pReminder)
+ EnableMenuItem(FhMenu, IDM_DELETEREMINDER, MF_GRAYED|MF_BYCOMMAND);
+
+ CallService(MS_LANGPACK_TRANSLATEMENU,(DWORD)FhMenu,0);
+ TrackPopupMenu(FhMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON,LOWORD(lParam),HIWORD(lParam),0,AhWnd,0);
+ DestroyMenu(hMenuLoad);
+ return TRUE;
+}
+
+int CALLBACK DlgProcViewReminders(HWND Dialog,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ LV_COLUMN lvCol;
+ NMLISTVIEW *NM;
+ char *S;
+ int I;
+
+ switch (Message)
+ {
+ case WM_SIZE:
+ {
+ OnListResize(Dialog);
+ UpdateGeomFromWnd(Dialog, g_reminderListGeom, NULL, 0);
+ break;
+ }
+ case WM_MOVE:
+ UpdateGeomFromWnd(Dialog, g_reminderListGeom, NULL, 0);
+ break;
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *mm = (MINMAXINFO*)lParam;
+ mm->ptMinTrackSize.x = 394;
+ mm->ptMinTrackSize.y = 300;
+ }
+ return 0;
+ case WM_RELOAD:
+ {
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
+ InitListView(GetDlgItem(Dialog,IDC_LISTREMINDERS));
+ return TRUE;
+ }
+ case WM_CONTEXTMENU:
+ {
+ HWND H;
+ REMINDERDATA *pReminder = NULL;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ pReminder = (REMINDERDATA*)TreeGetAt(RemindersList,I);
+ }
+ }
+
+ if ( DoListContextMenu(Dialog, wParam, lParam, pReminder) )
+ return TRUE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ HWND H;
+
+ HICON hIcon = Skin_GetIconByHandle(hIconLibItem[6], ICON_SMALL);
+ SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
+ hIcon = Skin_GetIconByHandle(hIconLibItem[6], ICON_BIG);
+ SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);
+
+ TranslateDialogDefault(Dialog);
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+ S = Translate("Reminder text");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_reminderListColGeom[1];
+ ListView_InsertColumn(H,0,&lvCol);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+ S = Translate("Date of activation");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_reminderListColGeom[0];
+ ListView_InsertColumn(H,0,&lvCol);
+ InitListView(H);
+ SetWindowLong(GetDlgItem(H, 0), GWL_ID, IDC_LISTREMINDERS_HEADER);
+ LV = Dialog;
+
+ if (g_reminderListGeom[1] && g_reminderListGeom[2])
+ {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(Dialog, &wp);
+ wp.rcNormalPosition.left = g_reminderListGeom[0];
+ wp.rcNormalPosition.top = g_reminderListGeom[1];
+ wp.rcNormalPosition.right = g_reminderListGeom[2] + g_reminderListGeom[0];
+ wp.rcNormalPosition.bottom = g_reminderListGeom[3] + g_reminderListGeom[1];
+ SetWindowPlacement(Dialog, &wp);
+ }
+ return TRUE;
+ }
+ case WM_CLOSE:
+ {
+ DestroyWindow(Dialog);
+ ListReminderVisible = FALSE;
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if (wParam == IDC_LISTREMINDERS)
+ {
+ NM = (NMLISTVIEW *)lParam;
+ switch (NM->hdr.code)
+ {
+ case LVN_ITEMCHANGED:
+ {
+ S = ((REMINDERDATA*)TreeGetAt(RemindersList,NM->iItem))->Reminder;
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,S);
+ }
+ break;
+ case NM_DBLCLK:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ EditReminder((REMINDERDATA*)TreeGetAt(RemindersList, I));
+ }
+ }
+ }
+ break;
+ }
+ }
+ else if (wParam == IDC_LISTREMINDERS_HEADER)
+ {
+ NMHEADER *NM = (NMHEADER*)lParam;
+ switch (NM->hdr.code)
+ {
+ case HDN_ENDTRACK:
+ UpdateGeomFromWnd(Dialog, NULL, g_reminderListColGeom, SIZEOF(g_reminderListColGeom));
+ break;
+ }
+ }
+ }
+ break;
+ case WM_COMMAND:
+ {
+ switch(LOWORD(wParam))
+ {
+ case ID_CONTEXTMENUREMINDERLISTVIEW_EDIT:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ EditReminder((REMINDERDATA*)TreeGetAt(RemindersList, I));
+ }
+ }
+ }
+ return TRUE;
+ case IDC_CLOSE:
+ {
+ DestroyWindow(Dialog);
+ ListReminderVisible = FALSE;
+ return TRUE;
+ }
+ case IDM_NEWREMINDER:
+ case IDC_ADDNEWREMINDER:
+ {
+ NewReminder();
+ return TRUE;
+ }
+ case IDM_DELETEALLREMINDERS:
+ if (RemindersList && MessageBox(Dialog, Translate("Are you sure you want to delete all reminders?"), SECTIONNAME, MB_OKCANCEL) == IDOK)
+ {
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
+ DeleteReminders();
+ InitListView(GetDlgItem(Dialog,IDC_LISTREMINDERS));
+ }
+ return TRUE;
+ case IDM_DELETEREMINDER:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1
+ && MessageBox(Dialog, Translate("Are you sure you want to delete this reminder?"), SECTIONNAME, MB_OKCANCEL) == IDOK)
+ {
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
+ DeleteReminder((REMINDERDATA*)TreeGetAt(RemindersList, I));
+ JustSaveReminders();
+ InitListView(H);
+ }
+ }
+ return TRUE;
+ }
+ }
+ }
+ case WM_DESTROY:
+ {
+ CallService(MS_SKIN2_RELEASEICONBIG, (WPARAM)SendMessage(Dialog, WM_SETICON, ICON_BIG, (LPARAM)NULL), 0);
+ CallService(MS_SKIN2_RELEASEICON, (WPARAM)SendMessage(Dialog, WM_SETICON, ICON_SMALL, (LPARAM)NULL), 0);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// Email/SMS and WinSock functions
+
+BOOL WS_Init()
+{
+ WSADATA wsd;
+ if (WSAStartup(MAKEWORD(2,2),&wsd)!=0) return FALSE;
+ return TRUE;
+}
+
+void WS_CleanUp()
+{
+ WSACleanup();
+}
+
+int WS_Send(SOCKET s,char *data,int datalen)
+{
+ int rlen;
+ if ((rlen = send(s,data,datalen,0)) == SOCKET_ERROR) return FALSE;
+ return TRUE;
+}
+
+unsigned long WS_ResolveName(char *name,WORD *port,int defaultPort)
+{
+ HOSTENT *lk;
+ char *pcolon,*nameCopy;
+ DWORD ret;
+ nameCopy=_strdup(name);
+ if(port != NULL) *port = defaultPort;
+ pcolon = strchr(nameCopy,':');
+ if(pcolon != NULL)
+ {
+ if(port != NULL) *port = atoi(pcolon+1);
+ *pcolon = 0;
+ }
+ if (inet_addr(nameCopy) == INADDR_NONE)
+ {
+ lk = gethostbyname(nameCopy);
+ if(lk == 0) return SOCKET_ERROR;
+ else {free(nameCopy); return *(u_long*)lk->h_addr_list[0];}
+ }
+ ret=inet_addr(nameCopy);
+ free(nameCopy);
+ return ret;
+}
+
+void Send(char *user, char *host, char *Msg, char *server)
+{
+ SOCKADDR_IN sockaddr;
+ WORD port;
+ char *ch = NULL;
+ S = socket(AF_INET,SOCK_STREAM,0);
+ if (!server) server = host;
+ if ((sockaddr.sin_addr.S_un.S_addr = WS_ResolveName(server,
+ &port,25))==SOCKET_ERROR) return;
+ sockaddr.sin_port = htons(port);
+ sockaddr.sin_family = AF_INET;
+ if(connect(S,(SOCKADDR*)&sockaddr,sizeof(sockaddr)) == SOCKET_ERROR) return;
+ ch = (char*)malloc(strlen(user) + strlen(host) + 16);
+ ch = (char*)realloc(ch,sprintf(ch,"rcpt to:%s@%s\r\n",user,host));
+ WS_Send(S,"mail from: \r\n",13);
+ WS_Send(S,ch,strlen(ch));
+ WS_Send(S,"data\r\n",6);
+ WS_Send(S,"From:<REM>\r\n\r\n",14);
+ WS_Send(S,Msg,strlen(Msg));
+ WS_Send(S,"\r\n.\r\n",5);
+ WS_Send(S,"quit\r\n",6);
+ SAFE_FREE((void**)&ch);
+ closesocket(S);
+}
diff --git a/plugins/NotesAndReminders/resource.h b/plugins/NotesAndReminders/resource.h
new file mode 100644
index 0000000000..0ddf8b4031
--- /dev/null
+++ b/plugins/NotesAndReminders/resource.h
@@ -0,0 +1,65 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by resource.rc
+//
+#define IDD_STNOTEOPTIONS 101
+#define IDD_ADDREMINDER 102
+#define IDD_NOTIFYREMINDER 103
+#define IDD_LISTREMINDERS 104
+#define IDI_ADDREMINDER 110
+#define IDI_DELETEICON 111
+#define IDI_NOTEICON 112
+#define IDI_SHOWHIDE 113
+#define IDI_CAPTIONICON 114
+#define IDI_DELETEREMINDER 115
+#define IDI_VIEWREMINDERS 116
+#define IDI_CAPTIONICONNOTTOP 117
+#define IDI_HIDENOTE 118
+#define IDI_REMOVENOTE 119
+#define IDI_REMINDER 120
+#define IDI_BRINGFRONT 121
+#define IDI_PLAYSOUND 122
+#define IDI_VIEWNOTES 123
+
+#define IDC_COMBODATE 1021
+#define IDC_COMBOTIME 1022
+#define IDC_COMBOREMINDERTIME 1023
+#define IDC_REFTIME 1024
+#define IDC_STATIC_DATE 1025
+#define IDC_STATIC_TIME 1026
+#define IDC_COMBO_REPEATSND 1028
+#define IDC_COMBO_SOUND 1029
+#define IDC_BTN_PLAYSOUND 1030
+#define IDC_EDIT_ALTBROWSER 1031
+#define IDC_BTN_BROWSEBROWSER 1032
+#define ID_CONTEXTMENUNOTEPOPUP_NEWNOTE 40009
+#define ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP 40010
+#define ID_CONTEXTMENUNOTEPOPUP_APPEARANCE 40011
+#define ID_APPEARANCE_BACKGROUNDCOLOR 40013
+#define ID_APPEARANCE_TEXTCOLOR 40014
+#define ID_APPEARANCE_CUSTOMTEXT 40015
+#define ID_APPEARANCE_CUSTOMBG 40016
+#define ID_BACKGROUNDCOLOR_RESET 40017
+#define ID_TEXTCOLOR_RESET 40018
+#define ID_CONTEXTMENUNOTEPOPUP_PASTETITLE 40019
+#define ID_CONTEXTMENUNOTEPOPUP_RESETTITLE 40020
+#define ID_CONTEXTMENUREMINDERLISTVIEW_EDIT 40021
+#define ID_CONTEXTMENUNOTELISTVIEW_EDITNOTE 40022
+#define ID_CONTEXTMENUNOTELISTVIEW_SHOW 40023
+#define ID_CONTEXTMENUNOTELISTVIEW_DELETEALLNOTES 40024
+#define ID_CONTEXTMENUNOTEPOPUP_VIEWNOTES 40025
+#define ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY 40026
+#define ID_APPEARANCE_FONT 40027
+#define ID_APPEARANCE_CUSTOMFONT 40028
+#define ID_FONT_RESET 40029
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 106
+#define _APS_NEXT_COMMAND_VALUE 40030
+#define _APS_NEXT_CONTROL_VALUE 1033
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plugins/NotesAndReminders/resource.rc b/plugins/NotesAndReminders/resource.rc
new file mode 100644
index 0000000000..d438fabc25
--- /dev/null
+++ b/plugins/NotesAndReminders/resource.rc
@@ -0,0 +1,362 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Neutral resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
+#ifdef _WIN32
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,5,1
+ PRODUCTVERSION 0,0,5,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "Comments", "New ""Notes And Reminders"" plugin for Miranda IM"
+ VALUE "CompanyName", "Jokusoftware, Digital Forcess"
+ VALUE "FileDescription", "New ""Notes And Reminders"""
+ VALUE "FileVersion", "0.0.5.1"
+ VALUE "InternalName", "NewNotesAndReminders"
+ VALUE "LegalCopyright", "(C) 2003,2005 by Joe Kucera, Lubomir Ivanov"
+ VALUE "LegalTrademarks", "Digital Forcess"
+ VALUE "OriginalFilename", "NewNR.dll"
+ VALUE "ProductName", "New ""Notes And Reminders"""
+ VALUE "ProductVersion", "0.0.5.1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
+
+#endif // Neutral resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_STNOTEOPTIONS DIALOGEX 47, 55, 301, 254
+STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE | WS_CAPTION
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Sticky Notes Options"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Default Note Size",IDC_STATIC,5,6,159,51
+ LTEXT "Width (Pixels)",IDC_STATIC,12,20,44,8
+ EDITTEXT 1012,70,20,62,12,ES_AUTOHSCROLL | ES_NUMBER
+ LTEXT "Height (Pixels)",IDC_STATIC,12,41,46,8
+ EDITTEXT 1013,70,39,62,12,ES_AUTOHSCROLL | ES_NUMBER
+ GROUPBOX "Notes Appearance",IDC_STATIC,5,61,159,72
+ CONTROL "Show Scrollbars",1018,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,12,77,67,10
+ CONTROL "Show Buttons",1019,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,88,77,64,10
+ LTEXT "Title Date",IDC_STATIC,12,96,32,8
+ COMBOBOX IDC_COMBODATE,53,95,92,149,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Title Time",IDC_STATIC,12,115,32,8
+ COMBOBOX IDC_COMBOTIME,53,114,92,130,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Startup options",IDC_STATIC,179,6,116,56
+ CONTROL "Hide notes at startup",1010,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | BS_FLAT | WS_TABSTOP,185,17,82,16
+ CONTROL "Add Contact list menu items",1011,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | BS_FLAT | WS_TABSTOP,185,35,103,10
+ CTEXT "(Requires Miranda restart)",IDC_STATIC,196,50,84,10
+ GROUPBOX "Reminder options",IDC_STATIC,179,66,116,90
+ CONTROL "Add Reminder closes dialog",1020,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,185,81,103,10
+ CONTROL "Use MCI to play alert sounds",1023,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,185,96,106,10
+ GROUPBOX "Note Transparency (Minimum Win 2k/XP)",IDC_STATIC,6,161,290,35
+ CONTROL "Slider1",1014,"msctls_trackbar32",TBS_ENABLESELRANGE | WS_TABSTOP,8,171,283,14
+ LTEXT "0%",IDC_STATIC,12,185,9,8
+ LTEXT "100%",IDC_STATIC,276,186,18,8
+ LTEXT "Send Reminders through E-mail / SMS :",IDC_STATIC,6,207,130,10
+ EDITTEXT 1017,137,204,158,14,ES_AUTOHSCROLL
+ LTEXT "Open links with this program instead:",IDC_STATIC,5,228,125,8
+ EDITTEXT IDC_EDIT_ALTBROWSER,138,225,140,14,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_BTN_BROWSEBROWSER,280,225,15,15
+ PUSHBUTTON "Reset to defaults",1007,32,140,94,14
+ CTEXT "(Using MCI allows different sounds to be played simultaneously, may not be needed or desired when a custom sound plugin is installed.)",IDC_STATIC,181,111,111,42
+END
+
+IDD_ADDREMINDER DIALOGEX 0, 0, 238, 199
+STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Add Reminder"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Trigger On Time && Date:",1005,4,2,146,52
+ LTEXT "Date",IDC_STATIC,10,18,24,8
+ CONTROL "DateTimePicker1",1000,"SysDateTimePick32",DTS_LONGDATEFORMAT | WS_TABSTOP,38,16,107,13
+ LTEXT "Time",IDC_STATIC,10,37,25,8
+ COMBOBOX IDC_COMBOREMINDERTIME,38,35,107,198,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Reminder Note:",1006,4,58,230,99
+ EDITTEXT 1004,9,70,220,81,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+ GROUPBOX "",IDC_STATIC,4,157,228,22,NOT WS_VISIBLE
+ CONTROL "None",1008,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE | WS_TABSTOP,13,167,45,8
+ CONTROL "Daily",1009,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE | WS_TABSTOP,68,167,45,8
+ CONTROL "Weekly",1010,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE | WS_TABSTOP,124,167,45,8
+ CONTROL "Monthly",1011,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE | WS_TABSTOP,178,167,45,8
+ LTEXT "Repeat Alert Sound",IDC_STATIC,31,166,90,8,0,WS_EX_RIGHT
+ COMBOBOX IDC_COMBO_REPEATSND,126,163,107,81,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Use Sound",IDC_STATIC,31,185,90,8,0,WS_EX_RIGHT
+ COMBOBOX IDC_COMBO_SOUND,126,182,90,81,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "",IDC_BTN_PLAYSOUND,220,183,13,12,BS_ICON
+ DEFPUSHBUTTON "&Add Reminder",1002,158,6,76,14
+ PUSHBUTTON "&Close",1003,158,23,76,14
+ PUSHBUTTON "&View Reminders",1007,158,40,76,14
+ LTEXT "reftime",IDC_REFTIME,0,191,22,8,NOT WS_VISIBLE | NOT WS_GROUP
+END
+
+IDD_NOTIFYREMINDER DIALOGEX 0, 0, 240, 146
+STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Reminder"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ EDITTEXT 1000,3,4,233,80,ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL
+ CONTROL "After:",1004,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,11,90,32,10,WS_EX_TRANSPARENT
+ CONTROL "On Time && Date:",1005,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,51,90,65,10,WS_EX_TRANSPARENT
+ GROUPBOX "",-1,4,90,148,52
+ COMBOBOX 1003,24,113,107,104,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Date",IDC_STATIC_DATE,10,106,23,8
+ CONTROL "DateTimePicker1",1006,"SysDateTimePick32",DTS_LONGDATEFORMAT | WS_TABSTOP,39,104,107,13
+ LTEXT "Time",IDC_STATIC_TIME,10,125,24,8
+ COMBOBOX 1007,39,123,107,198,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+ DEFPUSHBUTTON "&Remind Again",1002,161,92,75,14
+ PUSHBUTTON "&Create Note",1008,161,111,75,14
+ PUSHBUTTON "&Dismiss",1001,161,129,75,14
+ LTEXT "reftime",IDC_REFTIME,0,137,22,8,NOT WS_VISIBLE | NOT WS_GROUP
+END
+
+IDD_LISTREMINDERS DIALOGEX 0, 0, 258, 244
+STYLE DS_SETFONT | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Reminders"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ CONTROL "List1",1000,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,3,4,251,146
+ EDITTEXT 1001,3,153,251,69,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL
+ PUSHBUTTON "Add New",1002,151,227,50,14
+ DEFPUSHBUTTON "&Close",1003,203,227,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ADDREMINDER ICON "icons/addremin.ico"
+IDI_DELETEICON ICON "icons/deleteic.ico"
+IDI_NOTEICON ICON "icons/noteicon.ico"
+IDI_SHOWHIDE ICON "icons/showhide.ico"
+IDI_CAPTIONICON ICON "icons/captioni.ico"
+IDI_DELETEREMINDER ICON "icons/deletere.ico"
+IDI_VIEWREMINDERS ICON "icons/viewremi.ico"
+IDI_CAPTIONICONNOTTOP ICON "icons/captionn.ico"
+IDI_HIDENOTE ICON "icons/hidenote.ico"
+IDI_REMOVENOTE ICON "icons/removeno.ico"
+IDI_REMINDER ICON "icons/reminder.ico"
+IDI_BRINGFRONT ICON "icons/bringfront.ico"
+IDI_PLAYSOUND ICON "icons/playsound.ico"
+IDI_VIEWNOTES ICON "icons/viewnotes.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+MNU_REMINDERPOPUP MENU
+BEGIN
+ POPUP "Context Menu Reminder ListView"
+ BEGIN
+ MENUITEM "Edi&t Reminder", ID_CONTEXTMENUREMINDERLISTVIEW_EDIT
+ MENUITEM SEPARATOR
+ MENUITEM "New &Reminder", 40001
+ MENUITEM "&Delete Reminder", 40002
+ MENUITEM SEPARATOR
+ MENUITEM "D&elete All Reminders", 40003
+ END
+END
+
+MNU_NOTEPOPUP MENU
+BEGIN
+ POPUP "Context Menu Note Popup"
+ BEGIN
+ MENUITEM "&Hide Note", 40002
+ MENUITEM "&Always On Top", 40003
+ MENUITEM "&New Note", ID_CONTEXTMENUNOTEPOPUP_NEWNOTE
+ MENUITEM "&Delete Note", 40001
+ MENUITEM SEPARATOR
+ POPUP "Appearance"
+ BEGIN
+ POPUP "Background Color"
+ BEGIN
+ MENUITEM "Custom...", ID_APPEARANCE_CUSTOMBG
+ MENUITEM SEPARATOR
+ MENUITEM "Reset", ID_BACKGROUNDCOLOR_RESET
+ END
+ POPUP "Text Color"
+ BEGIN
+ MENUITEM "Custom...", ID_APPEARANCE_CUSTOMTEXT
+ MENUITEM SEPARATOR
+ MENUITEM "Reset", ID_TEXTCOLOR_RESET
+ END
+ POPUP "Font"
+ BEGIN
+ MENUITEM "Custom...", ID_APPEARANCE_CUSTOMFONT
+ MENUITEM SEPARATOR
+ MENUITEM "Reset", ID_FONT_RESET
+ END
+ END
+ MENUITEM "Paste Title", ID_CONTEXTMENUNOTEPOPUP_PASTETITLE
+ MENUITEM "Reset Title", ID_CONTEXTMENUNOTEPOPUP_RESETTITLE
+ MENUITEM SEPARATOR
+ MENUITEM "Undo", 40004
+ MENUITEM SEPARATOR
+ MENUITEM "&Copy", 40005
+ MENUITEM "&Paste", 40006
+ MENUITEM "C&ut", 40007
+ MENUITEM "C&lear", 40008
+ MENUITEM SEPARATOR
+ MENUITEM "&View Notes", ID_CONTEXTMENUNOTEPOPUP_VIEWNOTES
+ MENUITEM "&Bring All To Front", ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP
+ END
+END
+
+MNU_NOTELISTPOPUP MENU
+BEGIN
+ POPUP "Context Menu Note ListView"
+ BEGIN
+ MENUITEM "Edi&t Note", ID_CONTEXTMENUNOTELISTVIEW_EDITNOTE
+ MENUITEM SEPARATOR
+ MENUITEM "&Visible", ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY
+ MENUITEM "Always &On Top", 40003
+ MENUITEM "&New Note", ID_CONTEXTMENUNOTEPOPUP_NEWNOTE
+ MENUITEM "&Delete Note", 40001
+ MENUITEM SEPARATOR
+ MENUITEM "Delete &All Notes", ID_CONTEXTMENUNOTELISTVIEW_DELETEALLNOTES
+ MENUITEM SEPARATOR
+ MENUITEM "&Show / Hide Notes", ID_CONTEXTMENUNOTELISTVIEW_SHOW
+ MENUITEM "&Bring All To Front", ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ "IDD_STNOTEOPTIONS", DIALOG
+ BEGIN
+ VERTGUIDE, 5
+ VERTGUIDE, 12
+ VERTGUIDE, 70
+ VERTGUIDE, 88
+ VERTGUIDE, 179
+ VERTGUIDE, 185
+ VERTGUIDE, 295
+ BOTTOMMARGIN, 249
+ HORZGUIDE, 6
+ HORZGUIDE, 20
+ END
+
+ "IDD_ADDREMINDER", DIALOG
+ BEGIN
+ VERTGUIDE, 4
+ END
+
+ "IDD_NOTIFYREMINDER", DIALOG
+ BEGIN
+ VERTGUIDE, 161
+ VERTGUIDE, 236
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+