From fa7713c335e74d468f1c8181f22ec0b9971f49d7 Mon Sep 17 00:00:00 2001 From: neo85 Date: Sat, 13 Feb 2021 17:37:50 +0100 Subject: [PATCH 01/21] ui directory --- snaptimer.lpi | 27 ++++----- snaptimer.lpr | 2 +- snaptimer.lps | 96 +++++++++++++++++++++++++++++++ about.lfm => ui/about.lfm | 0 about.lrt => ui/about.lrt | 0 about.pas => ui/about.pas | 0 floating.lfm => ui/floating.lfm | 0 floating.pas => ui/floating.pas | 0 mainform1.lfm => ui/mainform1.lfm | 0 mainform1.lrt => ui/mainform1.lrt | 0 mainform1.pas => ui/mainform1.pas | 0 settings.lfm => ui/settings.lfm | 0 settings.pas => ui/settings.pas | 0 version.inc | 2 +- 14 files changed, 110 insertions(+), 17 deletions(-) create mode 100644 snaptimer.lps rename about.lfm => ui/about.lfm (100%) rename about.lrt => ui/about.lrt (100%) rename about.pas => ui/about.pas (100%) rename floating.lfm => ui/floating.lfm (100%) rename floating.pas => ui/floating.pas (100%) rename mainform1.lfm => ui/mainform1.lfm (100%) rename mainform1.lrt => ui/mainform1.lrt (100%) rename mainform1.pas => ui/mainform1.pas (100%) rename settings.lfm => ui/settings.lfm (100%) rename settings.pas => ui/settings.pas (100%) diff --git a/snaptimer.lpi b/snaptimer.lpi index ce79283..eee14c5 100644 --- a/snaptimer.lpi +++ b/snaptimer.lpi @@ -26,6 +26,7 @@ + @@ -63,9 +64,6 @@ - - - @@ -84,34 +82,32 @@ - + - + - - - + - + - - - - + - - + + + + + @@ -122,6 +118,7 @@ + diff --git a/snaptimer.lpr b/snaptimer.lpr index 3f45b9a..8f18071 100644 --- a/snaptimer.lpr +++ b/snaptimer.lpr @@ -3,7 +3,7 @@ {$mode objfpc}{$H+} uses - Forms, Interfaces, MainForm1, About, settings; + Forms, Interfaces, MainForm1; {$R *.res} diff --git a/snaptimer.lps b/snaptimer.lps new file mode 100644 index 0000000..10e0f18 --- /dev/null +++ b/snaptimer.lps @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/about.lfm b/ui/about.lfm similarity index 100% rename from about.lfm rename to ui/about.lfm diff --git a/about.lrt b/ui/about.lrt similarity index 100% rename from about.lrt rename to ui/about.lrt diff --git a/about.pas b/ui/about.pas similarity index 100% rename from about.pas rename to ui/about.pas diff --git a/floating.lfm b/ui/floating.lfm similarity index 100% rename from floating.lfm rename to ui/floating.lfm diff --git a/floating.pas b/ui/floating.pas similarity index 100% rename from floating.pas rename to ui/floating.pas diff --git a/mainform1.lfm b/ui/mainform1.lfm similarity index 100% rename from mainform1.lfm rename to ui/mainform1.lfm diff --git a/mainform1.lrt b/ui/mainform1.lrt similarity index 100% rename from mainform1.lrt rename to ui/mainform1.lrt diff --git a/mainform1.pas b/ui/mainform1.pas similarity index 100% rename from mainform1.pas rename to ui/mainform1.pas diff --git a/settings.lfm b/ui/settings.lfm similarity index 100% rename from settings.lfm rename to ui/settings.lfm diff --git a/settings.pas b/ui/settings.pas similarity index 100% rename from settings.pas rename to ui/settings.pas diff --git a/version.inc b/version.inc index 89274e3..e0e84b5 100644 --- a/version.inc +++ b/version.inc @@ -1 +1 @@ -'0.2' \ No newline at end of file +'0.3' \ No newline at end of file From 51d0c8cc7555d93471bf29f20e5e225f0ab949f2 Mon Sep 17 00:00:00 2001 From: neo85 Date: Sat, 13 Feb 2021 17:40:15 +0100 Subject: [PATCH 02/21] settings file renamed to options File name should match the class/form. --- snaptimer.lpi | 4 +++- snaptimer.lps | 30 ++++++++++++++++++++++-------- ui/mainform1.pas | 2 +- ui/{settings.lfm => options.lfm} | 0 ui/{settings.pas => options.pas} | 4 ++-- 5 files changed, 28 insertions(+), 12 deletions(-) rename ui/{settings.lfm => options.lfm} (100%) rename ui/{settings.pas => options.pas} (99%) diff --git a/snaptimer.lpi b/snaptimer.lpi index eee14c5..917dfa4 100644 --- a/snaptimer.lpi +++ b/snaptimer.lpi @@ -102,11 +102,13 @@ + - + + diff --git a/snaptimer.lps b/snaptimer.lps index 10e0f18..9009816 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,7 +4,7 @@ - + @@ -40,7 +40,6 @@ - @@ -61,15 +60,14 @@ - - - - + + + + - @@ -77,8 +75,20 @@ + + + + + + + + + + + + - + @@ -87,6 +97,10 @@ + + + + diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 7fcd141..b46aa61 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -6,7 +6,7 @@ interface uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, - Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, IniFiles, Settings, MMSystem, + Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, IniFiles, Options, MMSystem, Windows, StrUtils, DateUtils; type diff --git a/ui/settings.lfm b/ui/options.lfm similarity index 100% rename from ui/settings.lfm rename to ui/options.lfm diff --git a/ui/settings.pas b/ui/options.pas similarity index 99% rename from ui/settings.pas rename to ui/options.pas index d1ed05a..5d8ff1e 100644 --- a/ui/settings.pas +++ b/ui/options.pas @@ -1,4 +1,4 @@ -unit settings; +unit Options; {$mode objfpc}{$H+} @@ -225,6 +225,6 @@ procedure TOptionsForm.TestTrayMsg(Sender: TObject); end; initialization - {$I settings.lrs} + {$I options.lrs} end. From d1213d8d455beea1dea63768e68813a4eb2535a4 Mon Sep 17 00:00:00 2001 From: neo85 Date: Mon, 15 Feb 2021 20:28:17 +0100 Subject: [PATCH 03/21] Config class --- model/config.pas | 385 +++++++++++++++++++++++++++++++++ snaptimer.lpi | 20 +- snaptimer.lps | 291 ++++++++++++++++++++----- ui/mainform1.lfm | 125 ++++++----- ui/mainform1.pas | 538 +++++++++++++++-------------------------------- ui/options.lfm | 2 +- ui/options.pas | 37 ++-- 7 files changed, 896 insertions(+), 502 deletions(-) create mode 100644 model/config.pas diff --git a/model/config.pas b/model/config.pas new file mode 100644 index 0000000..3e5bae9 --- /dev/null +++ b/model/config.pas @@ -0,0 +1,385 @@ +unit Config; + +{$mode objfpc} + +interface + +uses + Classes, SysUtils, Graphics; + +type + TPosition = (Center, Remember, TopLeft, TopRight, BottomLeft, BottomRight); + + TFontConfig = class + Name: String; + Charset: Integer; + Color: Integer; + BgColor: Integer; + Size: Integer; + Style: TFontStyles; + end; + + TConfig = class + private + // Main + FMinutes: Integer; + FAlwaysOnTop: boolean; + FMinToTray: boolean; + FAutoStart: boolean; + FHideSeconds: boolean; + FClickTime: boolean; + FDblClickTime: boolean; + FAutoRestart: boolean; + FLoopAudio: boolean; + FTickingOn: boolean; + FAutoSave: boolean; + FSecondsMode: boolean; + FCompactMode: boolean; + // Placement + FWndHeight: integer; + FWndWidth: integer; + FWndLeft: integer; + FWndTop: integer; + FWndPosition: TPosition; + // Alarms + FDoneMessage: string; + FDoneMessageEnabled: boolean; + FDoneTrayMsg: string; + FDoneTrayMsgEnabled: boolean; + FDoneAudio: string; + FDoneAudioEnabled: boolean; + FDoneApp: string; + FDoneAppEnabled: boolean; + // Font + FFont: TFontConfig; + FDefaultFont: TFontConfig; + procedure SetDoneMessage(Msg: String); + procedure SetDoneTrayMessage(Msg: String); + public + constructor Create; + destructor Free; + function Load : Boolean; + function Save : Boolean; + function GetDefaultFont : TFontConfig; + + // Main + property Minutes: Integer read FMinutes write FMinutes; + property AlwaysOnTop: boolean read FAlwaysOnTop write FAlwaysOnTop; + property MinToTray: boolean read FMinToTray write FMinToTray; + property AutoStart: boolean read FAutoStart write FAutoStart; + property HideSeconds: boolean read FHideSeconds write FHideSeconds; + property ClickTime: boolean read FClickTime write FClickTime; + property DblClickTime: boolean read FDblClickTime write FDblClickTime; + property AutoRestart: boolean read FAutoRestart write FAutoRestart; + property LoopAudio: boolean read FLoopAudio write FLoopAudio; + property TickingOn: boolean read FTickingOn write FTickingOn; + property AutoSave: boolean read FAutoSave write FAutoSave; + property SecondsMode: boolean read FSecondsMode write FSecondsMode; // Undocumented mode to treat minutes as seconds + property CompactMode: boolean read FCompactMode write FCompactMode; // not implemented + // Placement + property WndHeight: integer read FWndHeight write FWndHeight; + property WndWidth: integer read FWndWidth write FWndWidth; + property WndLeft: integer read FWndLeft write FWndLeft; + property WndTop: integer read FWndTop write FWndTop; + property WndPosition: TPosition read FWndPosition write FWndPosition; + // Alarm + property DoneMessage: string read FDoneMessage write SetDoneMessage; + property DoneMessageEnabled: boolean read FDoneMessageEnabled write FDoneMessageEnabled; + property DoneTrayMsg: string read FDoneTrayMsg write SetDoneTrayMessage; + property DoneTrayMsgEnabled: boolean read FDoneTrayMsgEnabled write FDoneTrayMsgEnabled; + property DoneAudio: string read FDoneAudio write FDoneAudio; + property DoneAudioEnabled: boolean read FDoneAudioEnabled write FDoneAudioEnabled; + property DoneApp: string read FDoneApp write FDoneApp; + property DoneAppEnabled: boolean read FDoneAppEnabled write FDoneAppEnabled; + // Fonts + property Font: TFontConfig read FFont write FFont; +end; + +function GetConfig : TConfig; + +implementation + +uses + IniFiles; + +// Default values +const + INI_SEC_MAIN = 'Main'; + INI_SEC_ALARMS = 'Alarms'; + INI_SEC_PLACEMENT = 'Placement'; + INI_SEC_FONTS = 'Fonts'; + INI_MINUTES = 'Minutes'; + INI_ALWAYS_ON_TOP = 'AlwaysOnTop'; + INI_MIN_TO_TRAY = 'MinToTray'; + INI_AUTOSTART = 'AutoStart'; + INI_HIDESECONDS = 'HideSeconds'; + INI_CLICKTIME = 'ClickTime'; + INI_DBLCLICKTIME = 'DoubleClickTime'; + INI_AUTORESTART = 'AutoRestart'; + INI_LOOP_AUDIO = 'LoopAudio'; + INI_TICKING_ON = 'TickingEnabled'; + INI_AUTOSAVE = 'AutoSave'; + INI_HEIGHT = 'WinHeight'; + INI_WIDTH = 'WinWidth'; + INI_LEFT = 'WinLeft'; + INI_TOP = 'WinTop'; + INI_POSITION = 'WinPosition'; + INI_DONE_MESSAGE = 'Message'; + INI_DONE_MESSAGE_ON = 'MessageEnabled'; + INI_DONE_TRAY_MESSAGE = 'TrayMessage'; + INI_DONE_TRAY_MESSAGE_ON = 'TrayMessageEnabled'; + INI_DONE_AUDIO = 'AudioFile'; + INI_DONE_AUDIO_ON = 'AudioFileEnabled'; + INI_DONE_APP = 'RunApp'; + INI_DONE_APP_ON = 'RunAppEnabled'; + INI_SECONDS_MODE = 'SecondsMode'; + INI_FONT_NAME = 'Name'; + INI_FONT_CHARSET = 'Charset'; + INI_FONT_COLOR = 'Color'; + INI_FONT_SIZE = 'Size'; + INI_FONT_STYLE = 'Style'; + INI_BG_COLOR = 'BgColor'; + + INI_FILE_NAME = 'SnapTimer.ini'; + DEF_TIME = 15; + DEF_DONE_MSG = 'Time''s up'; + DEF_DONE_MSG_ON = True; + DEF_DONE_TRAY_MSG = 'Countdown completed'; + DEF_DONE_TRAY_MSG_ON = False; + DEF_DONE_AUDIO = '.\sounds\alarm_clock_bell.wav'; + DEF_DONE_AUDIO_ON = False; + DEF_DONE_APP = ''; + DEF_DONE_APP_ON = False; + DEF_ALWAYS_ON_TOP = False; + DEF_MIN_TO_TRAY = False; + DEF_AUTOSTART = False; + DEF_HIDESECONDS = False; + DEF_CLICKTIME = True; + DEF_DBLCLICKTIME = True; + DEF_AUTORESTART = False; + DEF_LOOP_AUDIO = False; + DEF_TICKING_ON = False; + DEF_TICKING_PATH = '.\sounds\ticking\ticking.wav'; + DEF_AUTOSAVE = True; + DEF_SECONDS_MODE = False; + DEF_POSITION = Ord(Center); + DEF_HEIGHT = 149; + DEF_WIDTH = 214; + DEF_FONT_NAME = 'Arial'; + DEF_FONT_CHARSET = 0; + DEF_FONT_COLOR = clNavy; + DEF_FONT_BG_COLOR = clNone; + DEF_FONT_SIZE = 38; + DEF_FONT_STYLE = 0; + + var + ConfigInstance : TConfig; + + +procedure TConfig.SetDoneMessage(Msg: String); +begin + // TODO Set some reasonable length limit + FDoneMessage:= Msg; +end; + +procedure TConfig.SetDoneTrayMessage(Msg: String); +begin + // TODO Set some reasonable length limit + FDoneTrayMsg:= Msg; +end; + +function FontStylesToInt(FontStyles: TFontStyles): integer; +var + Mask: integer; + Style: TFontStyle; +begin + // Translate the set into a bit mask + Mask := 0; + for Style := Low(TFontStyle) to High(TFontStyle) do + if Style in FontStyles then + Mask := Mask or (1 shl Ord(Style)); + Result := Mask; +end; + +function IntToFontStyles(Mask: integer): TFontStyles; +var + i: integer; + StyleSet: TFontStyles; +begin + // Translate the bit mask into a set + StyleSet := []; + for i := 0 to Ord(High(TFontStyle)) do + if Mask and (1 shl i) <> 0 then + StyleSet := StyleSet + [TFontStyle(i)]; + Result := StyleSet; +end; + +constructor TConfig.Create; +begin + // Main + Minutes:= DEF_TIME; + AlwaysOnTop := DEF_ALWAYS_ON_TOP; + MinToTray:= DEF_MIN_TO_TRAY; + AutoStart:= DEF_AUTOSTART; + HideSeconds:= DEF_HIDESECONDS; + ClickTime:= DEF_CLICKTIME; + DblClickTime:= DEF_DBLCLICKTIME; + AutoRestart:= DEF_AUTORESTART; + LoopAudio:= DEF_LOOP_AUDIO; + TickingOn:= DEF_TICKING_ON; + AutoSave:= DEF_AUTOSAVE; + SecondsMode:= DEF_SECONDS_MODE; + CompactMode := False; + // Placement + WndPosition:= TPosition(DEF_POSITION); + WndWidth:= DEF_WIDTH; + WndHeight:= DEF_HEIGHT; + // Alarms + DoneMessage:= DEF_DONE_MSG; + DoneMessageEnabled:= DEF_DONE_MSG_ON; + DoneTrayMsg:= DEF_DONE_TRAY_MSG; + DoneTrayMsgEnabled:= DEF_DONE_TRAY_MSG_ON; + DoneAudio:= DEF_DONE_AUDIO; + DoneAudioEnabled:= DEF_DONE_AUDIO_ON; + DoneApp:= DEF_DONE_APP; + DoneAppEnabled:= DEF_DONE_APP_ON; + // Fonts + FFont:= TFontConfig.Create; + Font.Name:= DEF_FONT_NAME; + Font.Charset:= DEF_FONT_CHARSET; + Font.Color:= DEF_FONT_COLOR; + Font.BgColor:= DEF_FONT_BG_COLOR; + Font.Size:= DEF_FONT_SIZE; + Font.Style:= IntToFontStyles(DEF_FONT_STYLE); + + FDefaultFont:= TFontConfig.Create; + FDefaultFont.Name:= DEF_FONT_NAME; + FDefaultFont.Charset:= DEF_FONT_CHARSET; + FDefaultFont.Color:= DEF_FONT_COLOR; + FDefaultFont.BgColor:= DEF_FONT_BG_COLOR; + FDefaultFont.Size:= DEF_FONT_SIZE; + FDefaultFont.Style:= IntToFontStyles(DEF_FONT_STYLE); +end; + +destructor TConfig.Free; +begin + FFont.Free; + FDefaultFont.Free; +end; + +function TConfig.Load : Boolean; +var IniFile: TIniFile; +begin + try + IniFile := TIniFile.Create(INI_FILE_NAME); + // Main + Minutes := IniFile.ReadInteger(INI_SEC_MAIN, INI_MINUTES, DEF_TIME); + AlwaysOnTop := IniFile.ReadBool(INI_SEC_MAIN, INI_ALWAYS_ON_TOP, DEF_ALWAYS_ON_TOP); + MinToTray := IniFile.ReadBool(INI_SEC_MAIN, INI_MIN_TO_TRAY, DEF_MIN_TO_TRAY); + AutoStart := IniFile.ReadBool(INI_SEC_MAIN, INI_AUTOSTART, DEF_AUTOSTART); + HideSeconds := IniFile.ReadBool(INI_SEC_MAIN, INI_HIDESECONDS, DEF_HIDESECONDS); + ClickTime := IniFile.ReadBool(INI_SEC_MAIN, INI_CLICKTIME, DEF_CLICKTIME); + DblClickTime := IniFile.ReadBool(INI_SEC_MAIN, INI_DBLCLICKTIME, DEF_DBLCLICKTIME); + AutoRestart := IniFile.ReadBool(INI_SEC_MAIN, INI_AUTORESTART, DEF_AUTORESTART); + LoopAudio := IniFile.ReadBool(INI_SEC_MAIN, INI_LOOP_AUDIO, DEF_LOOP_AUDIO); + TickingOn := IniFile.ReadBool(INI_SEC_MAIN, INI_TICKING_ON, DEF_TICKING_ON); + AutoSave := IniFile.ReadBool(INI_SEC_MAIN, INI_AUTOSAVE, DEF_AUTOSAVE); + SecondsMode := IniFile.ReadBool(INI_SEC_MAIN, INI_SECONDS_MODE, DEF_SECONDS_MODE); + // Placement + WndHeight := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_HEIGHT, DEF_HEIGHT); + WndWidth := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_WIDTH, DEF_WIDTH); + WndLeft := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_LEFT, 0); + WndTop := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_TOP, 0); + WndPosition := TPosition(IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_POSITION, DEF_POSITION)); + // Alarm + DoneMessage := IniFile.ReadString(INI_SEC_ALARMS, INI_DONE_MESSAGE, DEF_DONE_MSG); + DoneMessageEnabled := IniFile.ReadBool(INI_SEC_ALARMS, INI_DONE_MESSAGE_ON, DEF_DONE_MSG_ON); + DoneTrayMsg := IniFile.ReadString(INI_SEC_ALARMS, INI_DONE_TRAY_MESSAGE, DEF_DONE_TRAY_MSG); + DoneTrayMsgEnabled := IniFile.ReadBool(INI_SEC_ALARMS, INI_DONE_TRAY_MESSAGE_ON, DEF_DONE_TRAY_MSG_ON); + DoneAudio := IniFile.ReadString(INI_SEC_ALARMS, INI_DONE_AUDIO, DEF_DONE_AUDIO); + DoneAudioEnabled := IniFile.ReadBool(INI_SEC_ALARMS, INI_DONE_AUDIO_ON, DEF_DONE_AUDIO_ON); + DoneApp := IniFile.ReadString(INI_SEC_ALARMS, INI_DONE_APP, DEF_DONE_APP); + DoneAppEnabled := IniFile.ReadBool(INI_SEC_ALARMS, INI_DONE_APP_ON, DEF_DONE_APP_ON); + // Fonts + Font.Name := IniFile.ReadString(INI_SEC_FONTS, INI_FONT_NAME, DEF_FONT_NAME); + Font.Charset := IniFile.ReadInteger(INI_SEC_FONTS, INI_FONT_CHARSET, DEF_FONT_CHARSET); + Font.Color := IniFile.ReadInteger(INI_SEC_FONTS, INI_FONT_COLOR, DEF_FONT_COLOR); + Font.BgColor := IniFile.ReadInteger(INI_SEC_FONTS, INI_BG_COLOR, DEF_FONT_BG_COLOR); + Font.Size := IniFile.ReadInteger(INI_SEC_FONTS, INI_FONT_SIZE, DEF_FONT_SIZE); + Font.Style := IntToFontStyles(IniFile.ReadInteger(INI_SEC_FONTS, INI_FONT_STYLE, DEF_FONT_STYLE)); + except + Result:= False; + end; + Result:= True; + + if Assigned(IniFile) then + IniFile.Free; +end; + +function TConfig.Save : Boolean; +var IniFile: TIniFile; +begin + try + IniFile := TIniFile.Create(INI_FILE_NAME); + IniFile.CacheUpdates := True; + // Main + IniFile.WriteInteger(INI_SEC_MAIN, INI_MINUTES, Minutes); + IniFile.WriteBool(INI_SEC_MAIN, INI_ALWAYS_ON_TOP, AlwaysOnTop); + IniFile.WriteBool(INI_SEC_MAIN, INI_MIN_TO_TRAY, MinToTray); + IniFile.WriteBool(INI_SEC_MAIN, INI_AUTOSTART, AutoStart); + IniFile.WriteBool(INI_SEC_MAIN, INI_HIDESECONDS, HideSeconds); + IniFile.WriteBool(INI_SEC_MAIN, INI_CLICKTIME, ClickTime); + IniFile.WriteBool(INI_SEC_MAIN, INI_DBLCLICKTIME, DblClickTime); + IniFile.WriteBool(INI_SEC_MAIN, INI_AUTORESTART, AutoRestart); + IniFile.WriteBool(INI_SEC_MAIN, INI_LOOP_AUDIO, LoopAudio); + IniFile.WriteBool(INI_SEC_MAIN, INI_TICKING_ON, TickingOn); + IniFile.WriteBool(INI_SEC_MAIN, INI_AUTOSAVE, AutoSave); + IniFile.WriteBool(INI_SEC_MAIN, INI_SECONDS_MODE, SecondsMode); + // Placement + IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_HEIGHT, WndHeight); + IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_WIDTH, WndWidth); + IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_LEFT, WndLeft); + IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_TOP, WndTop); + IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_POSITION, Ord(WndPosition)); + // Alarms + IniFile.WriteString(INI_SEC_ALARMS, INI_DONE_MESSAGE, DoneMessage); + IniFile.WriteBool(INI_SEC_ALARMS, INI_DONE_MESSAGE_ON, DoneMessageEnabled); + IniFile.WriteString(INI_SEC_ALARMS, INI_DONE_TRAY_MESSAGE, DoneTrayMsg); + IniFile.WriteBool(INI_SEC_ALARMS, INI_DONE_TRAY_MESSAGE_ON, DoneTrayMsgEnabled); + IniFile.WriteString(INI_SEC_ALARMS, INI_DONE_AUDIO, DoneAudio); + IniFile.WriteBool(INI_SEC_ALARMS, INI_DONE_AUDIO_ON, DoneAudioEnabled); + IniFile.WriteString(INI_SEC_ALARMS, INI_DONE_APP, DoneApp); + IniFile.WriteBool(INI_SEC_ALARMS, INI_DONE_APP_ON, DoneAppEnabled); + // Fonts + IniFile.WriteString(INI_SEC_FONTS, INI_FONT_NAME, Font.Name); + IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_CHARSET, Font.Charset); + IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_COLOR, Font.Color); + IniFile.WriteInteger(INI_SEC_FONTS, INI_BG_COLOR, Font.BgColor); + IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_SIZE, Font.Size); + IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_STYLE, FontStylesToInt(Font.Style)); + + IniFile.UpdateFile; + except + Result:= False; + end; + Result:= True; + + if Assigned(IniFile) then + IniFile.Free; +end; + +function TConfig.GetDefaultFont : TFontConfig; +begin + Result:= FDefaultFont; +end; + +function GetConfig : TConfig; +begin + Result:= ConfigInstance; +end; + +initialization + ConfigInstance:= TConfig.Create; + +end. + diff --git a/snaptimer.lpi b/snaptimer.lpi index 917dfa4..0064e5b 100644 --- a/snaptimer.lpi +++ b/snaptimer.lpi @@ -26,7 +26,7 @@ - + @@ -64,6 +64,9 @@ + + + @@ -82,7 +85,7 @@ - + @@ -101,15 +104,24 @@ + + + - + + + + + + + @@ -120,7 +132,7 @@ - + diff --git a/snaptimer.lps b/snaptimer.lps index 9009816..dca158c 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,107 +4,288 @@ - + - - + + - - + + - - - + + + + + - - + + - - - + + + + + - - + + + - + + + + + + + - + + - - - - - + + + + + + + - + - - - - - - + + + + + + - - + + + - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/ui/mainform1.lfm b/ui/mainform1.lfm index 58778bf..cc89aa5 100644 --- a/ui/mainform1.lfm +++ b/ui/mainform1.lfm @@ -1,8 +1,8 @@ object MainForm: TMainForm Left = 533 - Height = 363 + Height = 242 Top = 312 - Width = 392 + Width = 261 HorzScrollBar.Page = 274 HorzScrollBar.Range = 275 HorzScrollBar.Visible = False @@ -11,11 +11,10 @@ object MainForm: TMainForm VertScrollBar.Visible = False ActiveControl = Minutes Caption = 'SnapTimer' - ClientHeight = 333 - ClientWidth = 392 - Constraints.MinHeight = 105 - Constraints.MinWidth = 240 - DesignTimePPI = 144 + ClientHeight = 222 + ClientWidth = 261 + Constraints.MinHeight = 70 + Constraints.MinWidth = 160 KeyPreview = True Menu = MainMenu1 OnActivate = FormActivate @@ -28,13 +27,13 @@ object MainForm: TMainForm LCLVersion = '2.0.10.0' object TimeLabel: TLabel AnchorSideRight.Control = Minutes - Left = 10 - Height = 24 - Top = 8 - Width = 70 + Left = 7 + Height = 16 + Top = 5 + Width = 47 AutoSize = False - BorderSpacing.Left = 8 - BorderSpacing.Right = 3 + BorderSpacing.Left = 5 + BorderSpacing.Right = 2 Caption = 'Minutes:' FocusControl = Minutes Layout = tlCenter @@ -42,11 +41,11 @@ object MainForm: TMainForm ParentFont = False end object ImgStart: TImage - Left = 168 - Height = 38 + Left = 112 + Height = 25 Hint = 'Start' - Top = 3 - Width = 38 + Top = 2 + Width = 25 OnClick = ToggleCountdown ParentShowHint = False Picture.Data = { @@ -110,11 +109,11 @@ object MainForm: TMainForm ShowHint = True end object ImgReset: TImage - Left = 208 - Height = 38 + Left = 139 + Height = 25 Hint = 'Reset' - Top = 3 - Width = 38 + Top = 2 + Width = 25 OnClick = ResetCountdown ParentShowHint = False Picture.Data = { @@ -179,11 +178,11 @@ object MainForm: TMainForm ShowHint = True end object ImgPause: TImage - Left = 168 - Height = 38 + Left = 112 + Height = 25 Hint = 'Pause' - Top = 3 - Width = 38 + Top = 2 + Width = 25 OnClick = ToggleCountdown ParentShowHint = False Picture.Data = { @@ -252,10 +251,10 @@ object MainForm: TMainForm Visible = False end object ImgIconRunning: TImage - Left = 232 - Height = 22 - Top = 60 - Width = 15 + Left = 155 + Height = 15 + Top = 40 + Width = 10 Picture.Data = { 055449636F6E7E04000000000100010010100000010020006804000016000000 2800000010000000200000000100200000000000000000000000000000000000 @@ -298,10 +297,10 @@ object MainForm: TMainForm Visible = False end object ImgIconPaused: TImage - Left = 240 - Height = 22 - Top = 60 - Width = 15 + Left = 160 + Height = 15 + Top = 40 + Width = 10 Picture.Data = { 055449636F6E7E04000000000100010010100000010020006804000016000000 2800000010000000200000000100200000000000000000000000000000000000 @@ -344,10 +343,10 @@ object MainForm: TMainForm Visible = False end object ImgIconDone: TImage - Left = 240 - Height = 22 - Top = 60 - Width = 15 + Left = 160 + Height = 15 + Top = 40 + Width = 10 Picture.Data = { 055449636F6E7E04000000000100010010100000010020006804000016000000 2800000010000000200000000100200000000000000000000000000000000000 @@ -390,10 +389,10 @@ object MainForm: TMainForm Visible = False end object ImgIconMain: TImage - Left = 240 - Height = 22 - Top = 60 - Width = 15 + Left = 160 + Height = 15 + Top = 40 + Width = 10 Picture.Data = { 055449636F6E7E04000000000100010010100000010020006804000016000000 2800000010000000200000000100200000000000000000000000000000000000 @@ -437,11 +436,11 @@ object MainForm: TMainForm end object Minutes: TSpinEdit AnchorSideLeft.Side = asrBottom - Left = 88 - Height = 33 - Top = 3 - Width = 70 - BorderSpacing.Right = 8 + Left = 59 + Height = 23 + Top = 2 + Width = 47 + BorderSpacing.Right = 5 Increment = 5 MaxValue = 5999 OnChange = MinutesChanged @@ -450,20 +449,20 @@ object MainForm: TMainForm end object Count: TStaticText Cursor = crHandPoint - Left = 3 - Height = 301 + Left = 2 + Height = 190 Top = 32 - Width = 386 + Width = 257 Align = alBottom Alignment = taCenter Anchors = [akTop, akLeft, akRight] - BorderSpacing.Left = 3 - BorderSpacing.Top = 6 - BorderSpacing.Right = 3 + BorderSpacing.Left = 2 + BorderSpacing.Top = 4 + BorderSpacing.Right = 2 BorderStyle = sbsSunken Caption = '00:00:00' Font.CharSet = ANSI_CHARSET - Font.Height = -72 + Font.Height = -48 Font.Name = 'Arial Narrow' Font.Pitch = fpVariable Font.Quality = fqDraft @@ -477,8 +476,8 @@ object MainForm: TMainForm Enabled = False OnTimer = Countdown OnStartTimer = Countdown - Left = 288 - Top = 240 + Left = 192 + Top = 160 end object TrayIconMain: TTrayIcon BalloonFlags = bfInfo @@ -524,12 +523,12 @@ object MainForm: TMainForm } Visible = True OnClick = ToggleCountdown - Left = 168 - Top = 240 + Left = 112 + Top = 160 end object MainMenu1: TMainMenu - Left = 36 - Top = 156 + Left = 24 + Top = 104 object MenuFile: TMenuItem Caption = 'File' object MenuToggle: TMenuItem @@ -570,8 +569,8 @@ object MainForm: TMainForm end end object TrayMenu: TPopupMenu - Left = 36 - Top = 240 + Left = 24 + Top = 160 object MenuCount: TMenuItem Caption = 'Countdown' Enabled = False @@ -609,8 +608,8 @@ object MainForm: TMainForm end end object PopupMenuCompact: TPopupMenu - Left = 192 - Top = 156 + Left = 128 + Top = 104 object MenuCompact: TMenuItem Caption = 'Toggle compact mode' OnClick = ToggleCompact diff --git a/ui/mainform1.pas b/ui/mainform1.pas index b46aa61..8e36127 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -6,15 +6,13 @@ interface uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, - Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, IniFiles, Options, MMSystem, + Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, Options, MMSystem, Windows, StrUtils, DateUtils; type - TMode = (Timer, Stopwatch); - TPosition = (Center, Remember, TopLeft, TopRight, BottomLeft, BottomRight); + TMode = (Timer, Stopwatch); { TMainForm } - TMainForm = class(TForm) ImgIconMain: TImage; ImgIconRunning: TImage; @@ -76,103 +74,43 @@ TMainForm = class(TForm) procedure RunApp(Path: string); procedure ShowDoneMessage(Msg: string); procedure ShowTrayMessage(Msg: string); - function GetHandle(): HWND; - procedure SetDefaults(Sender: TObject); - function FontStylesToInt(Fnt: TFont): integer; - function IntToFontStyles(Mask: integer): TFontStyles; + //f GetHandle(): HWND; procedure SetFieldsVisible(showFields: boolean); - procedure UpdateAlwaysOnTop(onTop: boolean); procedure PlayTicking(); function IsInteger(S: String): boolean; - procedure UpdateTimeCaption(newSecondsMode: boolean); + //procedure UpdateTimeCaption(newSecondsMode: boolean); private { private declarations } - IniFile: TIniFile; - AlwaysOnTop: boolean; - MinToTray: boolean; - AutoStart: boolean; - HideSeconds: boolean; - ClickTime: boolean; - DblClickTime: boolean; - AutoRestart: boolean; - LoopAudio: boolean; AudioPlaying: boolean; - TickingOn: boolean; - AutoSave: boolean; - DoneMessage: string; - DoneMessageEnabled: boolean; - DoneTrayMsg: string; - DoneTrayMsgEnabled: boolean; - DoneAudio: string; - DoneAudioEnabled: boolean; - DoneApp: string; - DoneAppEnabled: boolean; - WndPosition: TPosition; - WndPositions: TStrings; - WndHeight: integer; - WndWidth: integer; - WndTop: integer; - WndLeft: integer; WndHandle: HWND; - SecondsMode: boolean; // Undocumented mode to treat minutes as seconds + WndPositions: TStrings; Mode: TMode; - CompactMode: boolean; FontSizeChanged : boolean; //FormerWidth : integer; //FormerHeight : integer; EndTime: TDateTime; StartTime: TDateTime; CountdownDone: boolean; + OnShowFormFirstTime: Boolean; + procedure ApplyConfig(APosition: Boolean); public { public declarations } end; var MainForm: TMainForm; - AppName: string; - IniFilename: string; + //AppName: string; + implementation +uses + Config; + { TMainForm } const APP_NAME = 'SnapTimer'; - INI_SEC_MAIN = 'Main'; - INI_SEC_ALARMS = 'Alarms'; - INI_SEC_PLACEMENT = 'Placement'; - INI_SEC_FONTS = 'Fonts'; - INI_MINUTES = 'Minutes'; - INI_ALWAYS_ON_TOP = 'AlwaysOnTop'; - INI_MIN_TO_TRAY = 'MinToTray'; - INI_AUTOSTART = 'AutoStart'; - INI_HIDESECONDS = 'HideSeconds'; - INI_CLICKTIME = 'ClickTime'; - INI_DBLCLICKTIME = 'DoubleClickTime'; - INI_AUTORESTART = 'AutoRestart'; - INI_LOOP_AUDIO = 'LoopAudio'; - INI_TICKING_ON = 'TickingEnabled'; - INI_AUTOSAVE = 'AutoSave'; - INI_HEIGHT = 'WinHeight'; - INI_WIDTH = 'WinWidth'; - INI_LEFT = 'WinLeft'; - INI_TOP = 'WinTop'; - INI_POSITION = 'WinPosition'; - INI_DONE_MESSAGE = 'Message'; - INI_DONE_MESSAGE_ON = 'MessageEnabled'; - INI_DONE_TRAY_MESSAGE = 'TrayMessage'; - INI_DONE_TRAY_MESSAGE_ON = 'TrayMessageEnabled'; - INI_DONE_AUDIO = 'AudioFile'; - INI_DONE_AUDIO_ON = 'AudioFileEnabled'; - INI_DONE_APP = 'RunApp'; - INI_DONE_APP_ON = 'RunAppEnabled'; - INI_SECONDS_MODE = 'SecondsMode'; - INI_FONT_NAME = 'Name'; - INI_FONT_CHARSET = 'Charset'; - INI_FONT_COLOR = 'Color'; - INI_FONT_SIZE = 'Size'; - INI_FONT_STYLE = 'Style'; - INI_BG_COLOR = 'BgColor'; // Menu items MENU_FILE = '&File'; @@ -192,45 +130,17 @@ implementation LBL_MINUTES = '&Minutes:'; LBL_SECONDS = '&Seconds:'; - // Default values - DEF_TIME = 15; - DEF_DONE_MSG = 'Time''s up'; - DEF_DONE_MSG_ON = True; - DEF_DONE_TRAY_MSG = 'Countdown completed'; - DEF_DONE_TRAY_MSG_ON = False; - DEF_DONE_AUDIO = '.\sounds\alarm_clock_bell.wav'; - DEF_DONE_AUDIO_ON = False; - DEF_DONE_APP = ''; - DEF_DONE_APP_ON = False; - DEF_ALWAYS_ON_TOP = False; - DEF_MIN_TO_TRAY = False; - DEF_AUTOSTART = False; - DEF_HIDESECONDS = False; - DEF_CLICKTIME = True; - DEF_DBLCLICKTIME = True; - DEF_AUTORESTART = False; - DEF_LOOP_AUDIO = False; - DEF_TICKING_ON = False; - DEF_TICKING_PATH = '.\sounds\ticking\ticking.wav'; - DEF_AUTOSAVE = True; - DEF_SECONDS_MODE = False; - DEF_POSITION = Ord(Center); - DEF_HEIGHT = 149; - DEF_WIDTH = 214; - DEF_FONT_NAME = 'Arial'; - DEF_FONT_CHARSET = 0; - DEF_FONT_COLOR = clNavy; - DEF_FONT_SIZE = 38; - DEF_FONT_STYLE = 0; - DEF_BG_COLOR = clNone; - // Messages MSG_OPEN_INI = 'An error occurred opening the .ini file, settings won''t be saved'; MSG_WRITE_INI = 'An error occurred trying to write to the .ini file, settings won''t be saved.'; procedure TMainForm.OnCreateForm(Sender: TObject); +var + Config: TConfig; begin + Config:= GetConfig; + MenuFile.Caption := MENU_FILE; MenuToggle.Caption := BTN_START; MenuReset.Caption := BTN_RESET; @@ -247,27 +157,7 @@ procedure TMainForm.OnCreateForm(Sender: TObject); TrayMenuExit.Caption := MENU_EXIT; // Set all defaults, then load from .ini - Minutes.Value := DEF_TIME; - AlwaysOnTop := DEF_ALWAYS_ON_TOP; - MinToTray := DEF_MIN_TO_TRAY; - AutoStart := DEF_AUTOSTART; - HideSeconds := DEF_HIDESECONDS; - ClickTime := DEF_CLICKTIME; - DblClickTime := DEF_DBLCLICKTIME; - AutoRestart := DEF_AUTORESTART; - LoopAudio := DEF_LOOP_AUDIO; - TickingOn := DEF_TICKING_ON; - AutoSave := DEF_AUTOSAVE; - DoneMessage := DEF_DONE_MSG; - DoneMessageEnabled := DEF_DONE_MSG_ON; - DoneTrayMsg := DEF_DONE_TRAY_MSG; - DoneTrayMsgEnabled := DEF_DONE_TRAY_MSG_ON; - DoneAudio := DEF_DONE_AUDIO; - DoneAudioEnabled := DEF_DONE_AUDIO_ON; - DoneApp := DEF_DONE_APP; - DoneAppEnabled := DEF_DONE_APP_ON; - SecondsMode := DEF_SECONDS_MODE; - WndPosition := TPosition(DEF_POSITION); + WndPositions := TStringList.Create; WndPositions.Add('Centered'); @@ -278,15 +168,14 @@ procedure TMainForm.OnCreateForm(Sender: TObject); WndPositions.Add('Bottom right'); Mode := Timer; - CompactMode := False; AudioPlaying := False; Self.DoubleBuffered := True; Count.DoubleBuffered := True; Timer1.Interval := 150; CountdownDone := False; + OnShowFormFirstTime:= True; - AppName := ExtractFileName(Application.ExeName); - IniFilename := ExtractFilePath(Application.ExeName) + ChangeFileExt(AppName, '.ini'); + //AppName := ExtractFileName(Application.ExeName); // TODO For some reason this isn't working at all //WriteLn('checking for options: ' + IntToStr(ParamCount)); @@ -302,70 +191,27 @@ procedure TMainForm.OnCreateForm(Sender: TObject); // WriteLn('ini option found'); //end; //end; - try - IniFile := TIniFile.Create(IniFilename); - Minutes.Value := IniFile.ReadInteger(INI_SEC_MAIN, INI_MINUTES, DEF_TIME); - AlwaysOnTop := IniFile.ReadBool(INI_SEC_MAIN, INI_ALWAYS_ON_TOP, DEF_ALWAYS_ON_TOP); - MinToTray := IniFile.ReadBool(INI_SEC_MAIN, INI_MIN_TO_TRAY, DEF_MIN_TO_TRAY); - AutoStart := IniFile.ReadBool(INI_SEC_MAIN, INI_AUTOSTART, DEF_AUTOSTART); - HideSeconds := IniFile.ReadBool(INI_SEC_MAIN, INI_HIDESECONDS, DEF_HIDESECONDS); - ClickTime := IniFile.ReadBool(INI_SEC_MAIN, INI_CLICKTIME, DEF_CLICKTIME); - DblClickTime := IniFile.ReadBool(INI_SEC_MAIN, INI_DBLCLICKTIME, DEF_DBLCLICKTIME); - AutoRestart := IniFile.ReadBool(INI_SEC_MAIN, INI_AUTORESTART, DEF_AUTORESTART); - LoopAudio := IniFile.ReadBool(INI_SEC_MAIN, INI_LOOP_AUDIO, DEF_LOOP_AUDIO); - TickingOn := IniFile.ReadBool(INI_SEC_MAIN, INI_TICKING_ON, DEF_TICKING_ON); - AutoSave := IniFile.ReadBool(INI_SEC_MAIN, INI_AUTOSAVE, DEF_AUTOSAVE); - SecondsMode := IniFile.ReadBool(INI_SEC_MAIN, INI_SECONDS_MODE, DEF_SECONDS_MODE); - DoneMessage := IniFile.ReadString(INI_SEC_ALARMS, INI_DONE_MESSAGE, DEF_DONE_MSG); - DoneMessageEnabled := IniFile.ReadBool(INI_SEC_ALARMS, INI_DONE_MESSAGE_ON, - DEF_DONE_MSG_ON); - DoneTrayMsg := IniFile.ReadString(INI_SEC_ALARMS, INI_DONE_TRAY_MESSAGE, - DEF_DONE_TRAY_MSG); - DoneTrayMsgEnabled := IniFile.ReadBool(INI_SEC_ALARMS, - INI_DONE_TRAY_MESSAGE_ON, DEF_DONE_TRAY_MSG_ON); - DoneAudio := IniFile.ReadString(INI_SEC_ALARMS, INI_DONE_AUDIO, DEF_DONE_AUDIO); - DoneAudioEnabled := IniFile.ReadBool(INI_SEC_ALARMS, INI_DONE_AUDIO_ON, - DEF_DONE_AUDIO_ON); - DoneApp := IniFile.ReadString(INI_SEC_ALARMS, INI_DONE_APP, DEF_DONE_APP); - DoneAppEnabled := IniFile.ReadBool(INI_SEC_ALARMS, INI_DONE_APP_ON, DEF_DONE_APP_ON); - WndHeight := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_HEIGHT, DEF_HEIGHT); - WndWidth := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_WIDTH, DEF_WIDTH); - WndPosition := TPosition(IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_POSITION, DEF_POSITION)); - WndLeft := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_LEFT, 0); - WndTop := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_TOP, 0); - - Count.Font.Quality := fqAntialiased; - Count.Font.Name := IniFile.ReadString(INI_SEC_FONTS, INI_FONT_NAME, - DEF_FONT_NAME); - Count.Font.CharSet := IniFile.ReadInteger(INI_SEC_FONTS, INI_FONT_CHARSET, - DEF_FONT_CHARSET); - Count.Font.Color := IniFile.ReadInteger(INI_SEC_FONTS, INI_FONT_COLOR, - DEF_FONT_COLOR); - Count.Font.Size := IniFile.ReadInteger(INI_SEC_FONTS, INI_FONT_SIZE, - DEF_FONT_SIZE); - Count.Font.Style := IntToFontStyles(IniFile.ReadInteger(INI_SEC_FONTS, - INI_FONT_STYLE, DEF_FONT_STYLE)); - Count.Color := IniFile.ReadInteger(INI_SEC_FONTS, INI_BG_COLOR, DEF_BG_COLOR); - - except + + if Config.Load = False then + begin + // When will this code execute? MessageDlg(MSG_OPEN_INI, mtError, [mbOK], 0); + Exit; end; - if Assigned(IniFile) then - IniFile.Free; // Use minutes argument if it's the only parameter and it's numeric if (ParamCount = 1) and (IsInteger(ParamStr(1))) then Minutes.Value := StrToInt(ParamStr(1)); - UpdateTimeCaption(SecondsMode); + //UpdateTimeCaption(SecondsMode); ApplyConfig will do this ResetCountdown(Sender); - if AutoStart then + if Config.AutoStart then ToggleCountdown(Sender); end; procedure TMainForm.ResetCountdown(Sender: TObject); begin - if (Sender.ClassName = Count.ClassName) and (not DblClickTime) then + if (Sender.ClassName = Count.ClassName) and (not GetConfig.DblClickTime) then Exit; // Turn off both looping audio and ticking StopAudio(); @@ -393,13 +239,13 @@ procedure TMainForm.EnableTimer(); TrayMenuToggle.Caption := BTN_PAUSE; TrayIconMain.Icon := ImgIconRunning.Picture.Icon; - if TickingOn then + if GetConfig.TickingOn then PlayTicking(); end; procedure TMainForm.SetTimer(); begin - if SecondsMode then + if GetConfig.SecondsMode then Timer1.Tag := Minutes.Value else Timer1.Tag := Minutes.Value * 60; @@ -407,36 +253,40 @@ procedure TMainForm.SetTimer(); end; procedure TMainForm.ShowOptions(Sender: TObject); +var + Config: TConfig; begin + Config:= GetConfig; + // TODO move this to TOptionsForm OptionsForm := TOptionsForm.Create(Self); with OptionsForm do begin - NotifyMsg.Text := DoneMessage; - NotifyMsgOn.Checked := DoneMessageEnabled; - - NotifyTrayMsg.Text := DoneTrayMsg; - NotifyTrayMsgOn.Checked := DoneTrayMsgEnabled; - - NotifyAudio.Text := DoneAudio; - NotifyAudioOn.Checked := DoneAudioEnabled; - - NotifyRunApp.Text := DoneApp; - NotifyRunAppOn.Checked := DoneAppEnabled; - - CheckAlwaysOnTop.Checked := AlwaysOnTop; - CheckMinToTray.Checked := MinToTray; - CheckAutostart.Checked := AutoStart; - CheckHideSeconds.Checked := HideSeconds; - CheckClicktime.Checked := ClickTime; - CheckDblClickTime.Checked := DblClickTime; - CheckAutoRestart.Checked := AutoRestart; - CheckLoopAudio.Checked := LoopAudio; - CheckTicking.Checked := TickingOn; - CheckAutoSave.Checked := AutoSave; - CheckSecondsMode.Checked := SecondsMode; + NotifyMsg.Text:= Config.DoneMessage; + NotifyMsgOn.Checked:= Config.DoneMessageEnabled; + + NotifyTrayMsg.Text:= Config.DoneTrayMsg; + NotifyTrayMsgOn.Checked:= Config.DoneTrayMsgEnabled; + + NotifyAudio.Text:= Config.DoneAudio; + NotifyAudioOn.Checked:= Config.DoneAudioEnabled; + + NotifyRunApp.Text:= Config.DoneApp; + NotifyRunAppOn.Checked:= Config.DoneAppEnabled; + + CheckAlwaysOnTop.Checked:= Config.AlwaysOnTop; + CheckMinToTray.Checked:= Config.MinToTray; + CheckAutostart.Checked:= Config.AutoStart; + CheckHideSeconds.Checked:= Config.HideSeconds; + CheckClicktime.Checked:= Config.ClickTime; + CheckDblClickTime.Checked:= Config.DblClickTime; + CheckAutoRestart.Checked:= Config.AutoRestart; + CheckLoopAudio.Checked:= Config.LoopAudio; + CheckTicking.Checked:= Config.TickingOn; + CheckAutoSave.Checked:= Config.AutoSave; + CheckSecondsMode.Checked:= Config.SecondsMode; PositionCombo.Items := WndPositions; - PositionCombo.ItemIndex := Ord(WndPosition); + PositionCombo.ItemIndex := Ord(Config.WndPosition); PageControl1.TabIndex := 0; @@ -451,28 +301,29 @@ procedure TMainForm.ShowOptions(Sender: TObject); if ShowModal = mrOk then begin - UpdateTimeCaption(CheckSecondsMode.Checked); - - DoneMessage := NotifyMsg.Text; - DoneMessageEnabled := NotifyMsgOn.Checked; - DoneTrayMsg := NotifyTrayMsg.Text; - DoneTrayMsgEnabled := NotifyTrayMsgOn.Checked; - DoneAudio := NotifyAudio.Text; - DoneAudioEnabled := NotifyAudioOn.Checked; - DoneApp := NotifyRunApp.Text; - DoneAppEnabled := NotifyRunAppOn.Checked; - AlwaysOnTop := CheckAlwaysOnTop.Checked; - MinToTray := CheckMinToTray.Checked; - AutoStart := CheckAutostart.Checked; - HideSeconds := CheckHideSeconds.Checked; - ClickTime := CheckClicktime.Checked; - DblClickTime := CheckDblClickTime.Checked; - AutoRestart := CheckAutoRestart.Checked; - LoopAudio := CheckLoopAudio.Checked; - TickingOn := CheckTicking.Checked; - AutoSave := CheckAutoSave.Checked; - SecondsMode := CheckSecondsMode.Checked; - WndPosition := TPosition(PositionCombo.ItemIndex); + // replace with ApplyConig(False) + //UpdateTimeCaption(CheckSecondsMode.Checked); + + Config.DoneMessage:= NotifyMsg.Text; + Config.DoneMessageEnabled:= NotifyMsgOn.Checked; + Config.DoneTrayMsg:= NotifyTrayMsg.Text; + Config.DoneTrayMsgEnabled:= NotifyTrayMsgOn.Checked; + Config.DoneAudio:= NotifyAudio.Text; + Config.DoneAudioEnabled:= NotifyAudioOn.Checked; + Config.DoneApp:= NotifyRunApp.Text; + Config.DoneAppEnabled:= NotifyRunAppOn.Checked; + Config.AlwaysOnTop:= CheckAlwaysOnTop.Checked; + Config.MinToTray:= CheckMinToTray.Checked; + Config.AutoStart:= CheckAutostart.Checked; + Config.HideSeconds:= CheckHideSeconds.Checked; + Config.ClickTime:= CheckClicktime.Checked; + Config.DblClickTime:= CheckDblClickTime.Checked; + Config.AutoRestart:= CheckAutoRestart.Checked; + Config.LoopAudio:= CheckLoopAudio.Checked; + Config.TickingOn:= CheckTicking.Checked; + Config.AutoSave:= CheckAutoSave.Checked; + Config.SecondsMode:= CheckSecondsMode.Checked; + Config.WndPosition:= TPosition(PositionCombo.ItemIndex); //if not (Count.Font.Size = f.Size) then //begin //FontSizeChanged := True; @@ -491,17 +342,17 @@ procedure TMainForm.ShowOptions(Sender: TObject); //if FontSizeChanged then OnShowForm(Sender); - UpdateAlwaysOnTop(AlwaysOnTop); + //UpdateAlwaysOnTop(AlwaysOnTop); if Timer1.Enabled then begin - if TickingOn then + if Config.TickingOn then PlayTicking else StopAudio(); end; - if AutoSave then + if Config.AutoSave then SaveSettings(Sender); // TODO Resize the window if necessary to accomodate larger text // Get the text size from the font for '00:00:00'? @@ -513,7 +364,7 @@ procedure TMainForm.ShowOptions(Sender: TObject); procedure TMainForm.ToggleCountdown(Sender: TObject); begin - if (Sender.ClassName = Count.ClassName) and (not ClickTime) then + if (Sender.ClassName = Count.ClassName) and (not GetConfig.ClickTime) then Exit; if Minutes.Value = 0 then @@ -547,7 +398,9 @@ procedure TMainForm.ToggleCountdown(Sender: TObject); end; procedure TMainForm.Countdown(Sender: TObject); +var Config: TConfig; begin + Config:= GetConfig; if MODE = Stopwatch then begin Timer1.Tag := SecondsBetween(StartTime, Now); @@ -562,16 +415,16 @@ procedure TMainForm.Countdown(Sender: TObject); DisableTimer(); CountdownDone := True; Application.Title := APP_NAME; - if DoneAudioEnabled then - PlayAudio(DoneAudio, LoopAudio); - if DoneAppEnabled then - RunApp(DoneApp); - if DoneTrayMsgEnabled then - ShowTrayMessage(DoneTrayMsg); - if DoneMessageEnabled then - ShowDoneMessage(DoneMessage); - - if AutoRestart then + if Config.DoneAudioEnabled then + PlayAudio(Config.DoneAudio, Config.LoopAudio); + if Config.DoneAppEnabled then + RunApp(Config.DoneApp); + if Config.DoneTrayMsgEnabled then + ShowTrayMessage(Config.DoneTrayMsg); + if Config.DoneMessageEnabled then + ShowDoneMessage(Config.DoneMessage); + + if Config.AutoRestart then ToggleCountdown(Sender) else TrayIconMain.Icon := ImgIconDone.Picture.Icon; @@ -593,36 +446,16 @@ procedure TMainForm.ShowAbout(Sender: TObject); procedure TMainForm.OnDestroyForm(Sender: TObject); begin - if AutoSave then + if GetConfig.AutoSave then SaveSettings(Sender); end; procedure TMainForm.OnShowForm(Sender: TObject); -var - //w: integer; - //h: integer; - wRect: TRect; - //cRect: TRect; - flags: integer; - -begin - Self.Position := poDesigned; - WndHandle := GetHandle(); - GetWindowRect(WndHandle, wRect); - flags := SWP_SHOWWINDOW; - - case WndPosition of - Remember: SetWindowPos(WndHandle, HWND_TOP, WndLeft, WndTop, WndWidth, WndHeight, flags); - TopLeft: SetWindowPos(WndHandle, HWND_TOP, 0, 0, WndWidth, WndHeight, flags); - TopRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - WndWidth, 0, WndWidth, WndHeight, flags); - BottomLeft: SetWindowPos(WndHandle, HWND_TOP, 0, Monitor.WorkareaRect.Bottom - WndHeight, WndWidth, WndHeight, flags); - BottomRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - WndWidth, - Monitor.WorkareaRect.Bottom - WndHeight, WndWidth, WndHeight, flags); - Center: - begin - SetWindowPos(WndHandle, HWND_TOP, 0, 0, WndWidth, WndHeight, SWP_SHOWWINDOW or SWP_NOMOVE); - Self.Position := poScreenCenter; - end; +begin + if OnShowFormFirstTime then + begin + OnShowFormFirstTime:= False; + ApplyConfig(True); end; // TODO Get this working for window size (getpreferred size gets form, not window) @@ -662,56 +495,23 @@ procedure TMainForm.OnShowForm(Sender: TObject); //exit; end; - UpdateAlwaysOnTop(AlwaysOnTop); + //UpdateAlwaysOnTop(AlwaysOnTop); end; procedure TMainForm.SaveSettings(Sender: TObject); var + Config: TConfig; wRect: TRect; begin - try - IniFile := TIniFile.Create(IniFilename); - IniFile.CacheUpdates := True; - IniFile.WriteInteger(INI_SEC_MAIN, INI_MINUTES, Minutes.Value); - IniFile.WriteBool(INI_SEC_MAIN, INI_ALWAYS_ON_TOP, AlwaysOnTop); - IniFile.WriteBool(INI_SEC_MAIN, INI_MIN_TO_TRAY, MinToTray); - IniFile.WriteBool(INI_SEC_MAIN, INI_AUTOSTART, AutoStart); - IniFile.WriteBool(INI_SEC_MAIN, INI_HIDESECONDS, HideSeconds); - IniFile.WriteBool(INI_SEC_MAIN, INI_CLICKTIME, ClickTime); - IniFile.WriteBool(INI_SEC_MAIN, INI_DBLCLICKTIME, DblClickTime); - IniFile.WriteBool(INI_SEC_MAIN, INI_AUTORESTART, AutoRestart); - IniFile.WriteBool(INI_SEC_MAIN, INI_LOOP_AUDIO, LoopAudio); - IniFile.WriteBool(INI_SEC_MAIN, INI_TICKING_ON, TickingOn); - IniFile.WriteBool(INI_SEC_MAIN, INI_AUTOSAVE, AutoSave); - IniFile.WriteBool(INI_SEC_MAIN, INI_SECONDS_MODE, SecondsMode); - - GetWindowRect(GetHandle(), wRect); - - IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_HEIGHT, wRect.Bottom - wRect.Top); - IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_WIDTH, wRect.Right - wRect.Left); - IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_LEFT, wRect.Left); - IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_TOP, wRect.Top); - IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_POSITION, Ord(WndPosition)); - - IniFile.WriteString(INI_SEC_ALARMS, INI_DONE_MESSAGE, DoneMessage); - IniFile.WriteBool(INI_SEC_ALARMS, INI_DONE_MESSAGE_ON, DoneMessageEnabled); - IniFile.WriteString(INI_SEC_ALARMS, INI_DONE_TRAY_MESSAGE, DoneTrayMsg); - IniFile.WriteBool(INI_SEC_ALARMS, INI_DONE_TRAY_MESSAGE_ON, DoneTrayMsgEnabled); - IniFile.WriteString(INI_SEC_ALARMS, INI_DONE_AUDIO, DoneAudio); - IniFile.WriteBool(INI_SEC_ALARMS, INI_DONE_AUDIO_ON, DoneAudioEnabled); - IniFile.WriteString(INI_SEC_ALARMS, INI_DONE_APP, DoneApp); - IniFile.WriteBool(INI_SEC_ALARMS, INI_DONE_APP_ON, DoneAppEnabled); - - IniFile.WriteString(INI_SEC_FONTS, INI_FONT_NAME, Count.Font.Name); - IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_CHARSET, Count.Font.CharSet); - IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_COLOR, Count.Font.Color); - IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_SIZE, Count.Font.Size); - IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_STYLE, FontStylesToInt(Count.Font)); - IniFile.WriteInteger(INI_SEC_FONTS, INI_BG_COLOR, Count.Color); - IniFile.UpdateFile; - except + Config:= GetConfig; + GetWindowRect(self.Handle, wRect); + Config.WndWidth:= wRect.Right - wRect.Left; + Config.WndHeight:= wRect.Bottom - wRect.Top; + Config.WndLeft:= wRect.Left; + Config.WndTop:= wRect.Top; + + if Config.Save = False then MessageDlg(MSG_WRITE_INI, mtError, [mbOK], 0); - end; end; procedure TMainForm.MinutesChanged(Sender: TObject); @@ -722,8 +522,9 @@ procedure TMainForm.MinutesChanged(Sender: TObject); procedure TMainForm.ToggleCompact(Sender: TObject); begin - SetFieldsVisible(CompactMode); - CompactMode := not CompactMode; + // todo +// SetFieldsVisible(GetConfig.CompactMode); +// CompactMode := not CompactMode; end; // TODO Figure out how to remove the fields from the form so they don't @@ -760,7 +561,7 @@ function TMainForm.SecondsToTime(Seconds: integer): string; h := Seconds div 3600; m := Seconds div 60 - h * 60; s := Seconds - (h * 3600 + m * 60); - if HideSeconds then + if GetConfig.HideSeconds then begin Result := SysUtils.Format('%.2d:%.2d', [h, m]); end @@ -803,7 +604,7 @@ procedure TMainForm.UpdateTime(); procedure TMainForm.FormWindowStateChange(Sender: TObject); begin - if not MinToTray then + if not GetConfig.MinToTray then Exit; if WindowState = wsMinimized then @@ -845,7 +646,8 @@ procedure TMainForm.StopAudio(); procedure TMainForm.PlayTicking(); begin - sndPlaySound(PChar(GetFilePath(DEF_TICKING_PATH)), SND_NODEFAULT or + // TODO replace hard-coded path with Config + sndPlaySound(PChar(GetFilePath('.\sounds\ticking\ticking.wav')), SND_NODEFAULT or SND_ASYNC or SND_LOOP); end; @@ -867,60 +669,15 @@ procedure TMainForm.ShowTrayMessage(Msg: string); procedure TMainForm.ShowDoneMessage(Msg: string); begin // http://msdn.microsoft.com/en-us/library/ms645505(VS.85).aspx - Windows.MessageBox(GetHandle(), pChar(msg), 'Done', + Windows.MessageBox(self.Handle, pChar(msg), 'Done', MB_SYSTEMMODAL or MB_SETFOREGROUND or MB_TOPMOST or MB_ICONINFORMATION); StopAudio(); end; -function TMainForm.GetHandle(): HWND; +{function TMainForm.GetHandle(): HWND; begin Result := Application.MainForm.Handle; -end; - -procedure TMainForm.SetDefaults(Sender: TObject); -begin - with Sender as TOptionsForm do - BgColor.ButtonColor := DEF_BG_COLOR; - f.Name := DEF_FONT_NAME; - f.Color := DEF_FONT_COLOR; - f.CharSet := DEF_FONT_CHARSET; - f.Size := DEF_FONT_SIZE; - f.Style := IntToFontStyles(DEF_FONT_STYLE); -end; - -function TMainForm.FontStylesToInt(Fnt: TFont): integer; -var - Mask: integer; - Style: TFontStyle; -begin - // Translate the set into a bit mask - Mask := 0; - for Style := Low(TFontStyle) to High(TFontStyle) do - if Style in Fnt.Style then - Mask := Mask or (1 shl Ord(Style)); - Result := Mask; -end; - -function TMainForm.IntToFontStyles(Mask: integer): TFontStyles; -var - i: integer; - StyleSet: TFontStyles; -begin - // Translate the bit mask into a set - StyleSet := []; - for i := 0 to Ord(High(TFontStyle)) do - if Mask and (1 shl i) <> 0 then - StyleSet := StyleSet + [TFontStyle(i)]; - Result := StyleSet; -end; - -procedure TMainForm.UpdateAlwaysOnTop(onTop: boolean); -begin - if onTop then - FormStyle:= fsSystemStayOnTop - else - FormStyle:= fsNormal -end; +end;} procedure TMainForm.FormActivate(Sender: TObject); begin @@ -942,7 +699,7 @@ function TMainForm.IsInteger(S: String): boolean; // TODO Only update the time if this value changed (otherwise it resets // the time every time you modify settings) // It's not working - test it thoroughly and fix it. -procedure TMainForm.UpdateTimeCaption(newSecondsMode: boolean); +{procedure TMainForm.UpdateTimeCaption(newSecondsMode: boolean); begin if newSecondsMode <> SecondsMode then begin @@ -953,6 +710,55 @@ procedure TMainForm.UpdateTimeCaption(newSecondsMode: boolean); SetTimer(); end; +end; } + + +procedure TMainForm.ApplyConfig(APosition: Boolean); +var + wRect: TRect; + flags: Integer; + Config: TConfig; +begin + Config:= GetConfig; + if Config.AlwaysOnTop then + FormStyle:= fsSystemStayOnTop + else + FormStyle:= fsNormal; + + if Config.SecondsMode then + TimeLabel.Caption := LBL_SECONDS + else + TimeLabel.Caption := LBL_MINUTES; + + Count.Font.Quality := fqAntialiased; + Count.Font.Name := Config.Font.Name; + Count.Font.CharSet := Config.Font.Charset; + Count.Font.Color := Config.Font.Color; + Count.Font.Size := Config.Font.Size; + Count.Font.Style := Config.Font.Style; + Count.Color := Config.Font.BgColor; + + if APosition then + begin + Self.Position := poDesigned; + WndHandle := self.Handle; + GetWindowRect(WndHandle, wRect); + flags := SWP_SHOWWINDOW; + + case Config.WndPosition of + Remember: SetWindowPos(WndHandle, HWND_TOP, Config.WndLeft, Config.WndTop, Config.WndWidth, Config.WndHeight, flags); + TopLeft: SetWindowPos(WndHandle, HWND_TOP, 0, 0, Config.WndWidth, Config.WndHeight, flags); + TopRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - Config.WndWidth, 0, Config.WndWidth, Config.WndHeight, flags); + BottomLeft: SetWindowPos(WndHandle, HWND_TOP, 0, Monitor.WorkareaRect.Bottom - Config.WndHeight, Config.WndWidth, Config.WndHeight, flags); + BottomRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - Config.WndWidth, + Monitor.WorkareaRect.Bottom - Config.WndHeight, Config.WndWidth, Config.WndHeight, flags); + Center: + begin + SetWindowPos(WndHandle, HWND_TOP, 0, 0, Config.WndWidth, Config.WndHeight, SWP_SHOWWINDOW or SWP_NOMOVE); + Self.Position := poScreenCenter; + end; + end; + end; end; initialization diff --git a/ui/options.lfm b/ui/options.lfm index 2cf15ee..63bd39e 100644 --- a/ui/options.lfm +++ b/ui/options.lfm @@ -465,7 +465,7 @@ object OptionsForm: TOptionsForm Top = 201 Width = 112 Caption = '&Defaults' - OnClick = SetDefaults + OnClick = SetFontDefaults ParentFont = False ParentShowHint = False ShowHint = True diff --git a/ui/options.pas b/ui/options.pas index 5d8ff1e..e90c343 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -5,7 +5,7 @@ interface uses - SysUtils, LResources, Forms, Graphics, Dialogs, StdCtrls, ComCtrls, Spin; + SysUtils, LResources, Forms, Graphics, Dialogs, StdCtrls, ComCtrls, Spin, Classes; type @@ -62,7 +62,7 @@ TOptionsForm = class(TForm) procedure GetAppFile(Sender: TObject); function GetFile(Orig: String; FilterStr: String; Dir : String): String; procedure EnableFields(Sender: TObject); - procedure SetDefaults(Sender: TObject); + procedure SetFontDefaults(Sender: TObject); procedure TestAudio(Sender: TObject); procedure TestMessage(Sender: TObject); procedure TestRunApp(Sender: TObject); @@ -71,17 +71,17 @@ TOptionsForm = class(TForm) procedure UpdateFonts(Sender: TObject); procedure UpdateFontSize(Sender: TObject); private - { private declarations } public { public declarations } end; var OptionsForm: TOptionsForm; - f: TFont; + f: TFont; // TODO make private implementation -uses MainForm1; + +uses MainForm1, Config; { TOptionsForm } // TODO Move all text strings to the top @@ -98,9 +98,9 @@ procedure TOptionsForm.FormKeyPress(Sender: TObject; var Key: char); procedure TOptionsForm.ChooseFont(Sender: TObject); var - dlg : TFontDialog; + dlg : TFontDialog; begin - dlg := TFontDialog.Create(nil); + dlg := TFontDialog.Create(nil); dlg.Font := f; if dlg.Execute then @@ -118,6 +118,7 @@ procedure TOptionsForm.ChooseFont(Sender: TObject); dlg.Free; end; + procedure TOptionsForm.UpdateFonts(Sender: TObject); begin FontName.Caption := f.Name + ' : 1234567890'; @@ -159,9 +160,10 @@ function TOptionsForm.GetFile(Orig: String; FilterStr: String; Dir : String): St begin dlg := TOpenDialog.Create(nil); dlg.FileName := ''; - if DirectoryExists(Dir) - then dlg.InitialDir := Dir - else dlg.InitialDir := GetCurrentDir; + if DirectoryExists(Dir) then + dlg.InitialDir := Dir + else + dlg.InitialDir := GetCurrentDir; dlg.Options := [ofFileMustExist]; dlg.Filter := FilterStr; dlg.FilterIndex := 1; @@ -180,7 +182,7 @@ function TOptionsForm.GetFile(Orig: String; FilterStr: String; Dir : String): St procedure TOptionsForm.EnableFields(Sender: TObject); begin - NotifyMsg.Enabled := NotifyMsgOn.Checked; + NotifyMsg.Enabled := NotifyMsgOn.Checked; NotifyMsgTest.Enabled := NotifyMsgOn.Checked; NotifyTrayMsg.Enabled := NotifyTrayMsgOn.Checked; NotifyTrayMsgTest.Enabled := NotifyTrayMsgOn.Checked; @@ -194,9 +196,18 @@ procedure TOptionsForm.EnableFields(Sender: TObject); NotifyRunTest.Enabled := NotifyRunAppOn.Checked; end; -procedure TOptionsForm.SetDefaults(Sender: TObject); + +procedure TOptionsForm.SetFontDefaults(Sender: TObject); +var + Cfg : TFontConfig; begin - (Self.Owner as TMainForm).SetDefaults(Self); + Cfg:= GetConfig.GetDefaultFont; + BgColor.ButtonColor:= Cfg.BgColor; + f.Name:= Cfg.Name; + f.Color:= Cfg.Color; + f.CharSet:= Cfg.Charset; + f.Size:= Cfg.Size; + f.Style:= Cfg.Style; UpdateFonts(Sender); end; From 549a8074e3605542c541078526f692e724986b91 Mon Sep 17 00:00:00 2001 From: neo85 Date: Tue, 16 Feb 2021 17:36:57 +0100 Subject: [PATCH 04/21] OptionsForm rewrite --- snaptimer.lps | 191 +++++++++++++----------- ui/mainform1.pas | 219 ++++++++------------------- ui/options.lfm | 376 +++++++++++++++++++++++------------------------ ui/options.pas | 212 +++++++++++++++++++------- 4 files changed, 507 insertions(+), 491 deletions(-) diff --git a/snaptimer.lps b/snaptimer.lps index dca158c..2286cdb 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,12 +4,12 @@ - + - - + + @@ -17,9 +17,9 @@ - + - + @@ -30,7 +30,7 @@ - + @@ -39,10 +39,10 @@ - - - - + + + + @@ -54,21 +54,21 @@ - - - - + + + + + - - - - - + + + + @@ -76,7 +76,7 @@ - + @@ -84,14 +84,14 @@ - + - + @@ -100,7 +100,7 @@ - + @@ -108,180 +108,197 @@ - + - + - - - + + + - - + + - + - - + - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - + - - + + - - - - diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 8e36127..0871ca4 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -74,16 +74,12 @@ TMainForm = class(TForm) procedure RunApp(Path: string); procedure ShowDoneMessage(Msg: string); procedure ShowTrayMessage(Msg: string); - //f GetHandle(): HWND; procedure SetFieldsVisible(showFields: boolean); procedure PlayTicking(); function IsInteger(S: String): boolean; - //procedure UpdateTimeCaption(newSecondsMode: boolean); private { private declarations } AudioPlaying: boolean; - WndHandle: HWND; - WndPositions: TStrings; Mode: TMode; FontSizeChanged : boolean; //FormerWidth : integer; @@ -92,7 +88,7 @@ TMainForm = class(TForm) StartTime: TDateTime; CountdownDone: boolean; OnShowFormFirstTime: Boolean; - procedure ApplyConfig(APosition: Boolean); + procedure ApplyConfig; public { public declarations } end; @@ -158,15 +154,6 @@ procedure TMainForm.OnCreateForm(Sender: TObject); // Set all defaults, then load from .ini - - WndPositions := TStringList.Create; - WndPositions.Add('Centered'); - WndPositions.Add('Remember position'); - WndPositions.Add('Top left'); - WndPositions.Add('Top right'); - WndPositions.Add('Bottom left'); - WndPositions.Add('Bottom right'); - Mode := Timer; AudioPlaying := False; Self.DoubleBuffered := True; @@ -198,6 +185,7 @@ procedure TMainForm.OnCreateForm(Sender: TObject); MessageDlg(MSG_OPEN_INI, mtError, [mbOK], 0); Exit; end; + Minutes.Value:= Config.Minutes; // Use minutes argument if it's the only parameter and it's numeric if (ParamCount = 1) and (IsInteger(ParamStr(1))) then @@ -255,111 +243,47 @@ procedure TMainForm.SetTimer(); procedure TMainForm.ShowOptions(Sender: TObject); var Config: TConfig; + Ok: Boolean; + r: TRect; begin - Config:= GetConfig; - // TODO move this to TOptionsForm OptionsForm := TOptionsForm.Create(Self); - with OptionsForm do + Ok:= OptionsForm.ShowModal = mrOk; + OptionsForm.Free; + + if Ok then begin - NotifyMsg.Text:= Config.DoneMessage; - NotifyMsgOn.Checked:= Config.DoneMessageEnabled; - - NotifyTrayMsg.Text:= Config.DoneTrayMsg; - NotifyTrayMsgOn.Checked:= Config.DoneTrayMsgEnabled; - - NotifyAudio.Text:= Config.DoneAudio; - NotifyAudioOn.Checked:= Config.DoneAudioEnabled; - - NotifyRunApp.Text:= Config.DoneApp; - NotifyRunAppOn.Checked:= Config.DoneAppEnabled; - - CheckAlwaysOnTop.Checked:= Config.AlwaysOnTop; - CheckMinToTray.Checked:= Config.MinToTray; - CheckAutostart.Checked:= Config.AutoStart; - CheckHideSeconds.Checked:= Config.HideSeconds; - CheckClicktime.Checked:= Config.ClickTime; - CheckDblClickTime.Checked:= Config.DblClickTime; - CheckAutoRestart.Checked:= Config.AutoRestart; - CheckLoopAudio.Checked:= Config.LoopAudio; - CheckTicking.Checked:= Config.TickingOn; - CheckAutoSave.Checked:= Config.AutoSave; - CheckSecondsMode.Checked:= Config.SecondsMode; - - PositionCombo.Items := WndPositions; - PositionCombo.ItemIndex := Ord(Config.WndPosition); - - PageControl1.TabIndex := 0; - - f := TFont.Create; - f.Name := Count.Font.Name; - f.Size := Count.Font.Size; - f.CharSet := Count.Font.CharSet; - f.Color := Count.Font.Color; - f.Style := Count.Font.Style; - BgColor.ButtonColor := Count.Color; - FontSizeChanged := False; - - if ShowModal = mrOk then + Config:= GetConfig; + GetWindowRect(MainForm.Handle, r); + Config.WndLeft:= r.Left; + Config.WndTop:= r.Top; + Config.WndWidth:= r.Right - r. Left; + Config.WndHeight:= r.Bottom - r.Top; + ApplyConfig; + + //if not (Count.Font.Size = f.Size) then + //begin + //FontSizeChanged := True; + //GetPreferredSize(FormerWidth, FormerHeight, True); + //ShowMessageFmt('width: %d, height: %d', [FormerWidth, FormerHeight]); + //end; + //Count.Font := f; + // TODO Get font colors and background color to be shown - what's up? + + //if FontSizeChanged then OnShowForm(Sender); + if Timer1.Enabled then begin - // replace with ApplyConig(False) - //UpdateTimeCaption(CheckSecondsMode.Checked); - - Config.DoneMessage:= NotifyMsg.Text; - Config.DoneMessageEnabled:= NotifyMsgOn.Checked; - Config.DoneTrayMsg:= NotifyTrayMsg.Text; - Config.DoneTrayMsgEnabled:= NotifyTrayMsgOn.Checked; - Config.DoneAudio:= NotifyAudio.Text; - Config.DoneAudioEnabled:= NotifyAudioOn.Checked; - Config.DoneApp:= NotifyRunApp.Text; - Config.DoneAppEnabled:= NotifyRunAppOn.Checked; - Config.AlwaysOnTop:= CheckAlwaysOnTop.Checked; - Config.MinToTray:= CheckMinToTray.Checked; - Config.AutoStart:= CheckAutostart.Checked; - Config.HideSeconds:= CheckHideSeconds.Checked; - Config.ClickTime:= CheckClicktime.Checked; - Config.DblClickTime:= CheckDblClickTime.Checked; - Config.AutoRestart:= CheckAutoRestart.Checked; - Config.LoopAudio:= CheckLoopAudio.Checked; - Config.TickingOn:= CheckTicking.Checked; - Config.AutoSave:= CheckAutoSave.Checked; - Config.SecondsMode:= CheckSecondsMode.Checked; - Config.WndPosition:= TPosition(PositionCombo.ItemIndex); - //if not (Count.Font.Size = f.Size) then - //begin - //FontSizeChanged := True; - //GetPreferredSize(FormerWidth, FormerHeight, True); - //ShowMessageFmt('width: %d, height: %d', [FormerWidth, FormerHeight]); - //end; - //Count.Font := f; - // TODO Get font colors and background color to be shown - what's up? - Count.Font.Name := f.Name; - Count.Font.Size := f.Size; - Count.Font.CharSet := f.CharSet; - Count.Font.Color := f.Color; - Count.Font.Style := f.Style; - Count.Font.Quality := fqAntialiased; - Count.Color := BgColor.ButtonColor; - - //if FontSizeChanged then OnShowForm(Sender); - - //UpdateAlwaysOnTop(AlwaysOnTop); - - if Timer1.Enabled then - begin - if Config.TickingOn then - PlayTicking - else - StopAudio(); - end; - - if Config.AutoSave then - SaveSettings(Sender); - // TODO Resize the window if necessary to accomodate larger text - // Get the text size from the font for '00:00:00'? - + if Config.TickingOn then + PlayTicking + else + StopAudio(); end; + + if Config.AutoSave then + SaveSettings(Sender); + // TODO Resize the window if necessary to accomodate larger text + // Get the text size from the font for '00:00:00'? + end; - OptionsForm.Free; end; procedure TMainForm.ToggleCountdown(Sender: TObject); @@ -455,7 +379,7 @@ procedure TMainForm.OnShowForm(Sender: TObject); if OnShowFormFirstTime then begin OnShowFormFirstTime:= False; - ApplyConfig(True); + ApplyConfig; end; // TODO Get this working for window size (getpreferred size gets form, not window) @@ -504,6 +428,9 @@ procedure TMainForm.SaveSettings(Sender: TObject); wRect: TRect; begin Config:= GetConfig; + + // These config values are not set in OptionsForm. + Config.Minutes:= Minutes.Value; GetWindowRect(self.Handle, wRect); Config.WndWidth:= wRect.Right - wRect.Left; Config.WndHeight:= wRect.Bottom - wRect.Top; @@ -646,7 +573,6 @@ procedure TMainForm.StopAudio(); procedure TMainForm.PlayTicking(); begin - // TODO replace hard-coded path with Config sndPlaySound(PChar(GetFilePath('.\sounds\ticking\ticking.wav')), SND_NODEFAULT or SND_ASYNC or SND_LOOP); end; @@ -674,11 +600,6 @@ procedure TMainForm.ShowDoneMessage(Msg: string); StopAudio(); end; -{function TMainForm.GetHandle(): HWND; -begin - Result := Application.MainForm.Handle; -end;} - procedure TMainForm.FormActivate(Sender: TObject); begin WindowState := wsNormal; @@ -696,28 +617,11 @@ function TMainForm.IsInteger(S: String): boolean; end; end; -// TODO Only update the time if this value changed (otherwise it resets -// the time every time you modify settings) -// It's not working - test it thoroughly and fix it. -{procedure TMainForm.UpdateTimeCaption(newSecondsMode: boolean); -begin - if newSecondsMode <> SecondsMode then - begin - if newSecondsMode then - TimeLabel.Caption := LBL_SECONDS - else - TimeLabel.Caption := LBL_MINUTES; - SetTimer(); - end; - -end; } - - -procedure TMainForm.ApplyConfig(APosition: Boolean); +procedure TMainForm.ApplyConfig; var - wRect: TRect; - flags: Integer; Config: TConfig; + WndHandle: HWND; + flags: Integer; begin Config:= GetConfig; if Config.AlwaysOnTop then @@ -738,26 +642,21 @@ procedure TMainForm.ApplyConfig(APosition: Boolean); Count.Font.Style := Config.Font.Style; Count.Color := Config.Font.BgColor; - if APosition then - begin - Self.Position := poDesigned; - WndHandle := self.Handle; - GetWindowRect(WndHandle, wRect); - flags := SWP_SHOWWINDOW; - - case Config.WndPosition of - Remember: SetWindowPos(WndHandle, HWND_TOP, Config.WndLeft, Config.WndTop, Config.WndWidth, Config.WndHeight, flags); - TopLeft: SetWindowPos(WndHandle, HWND_TOP, 0, 0, Config.WndWidth, Config.WndHeight, flags); - TopRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - Config.WndWidth, 0, Config.WndWidth, Config.WndHeight, flags); - BottomLeft: SetWindowPos(WndHandle, HWND_TOP, 0, Monitor.WorkareaRect.Bottom - Config.WndHeight, Config.WndWidth, Config.WndHeight, flags); - BottomRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - Config.WndWidth, - Monitor.WorkareaRect.Bottom - Config.WndHeight, Config.WndWidth, Config.WndHeight, flags); - Center: - begin - SetWindowPos(WndHandle, HWND_TOP, 0, 0, Config.WndWidth, Config.WndHeight, SWP_SHOWWINDOW or SWP_NOMOVE); - Self.Position := poScreenCenter; - end; - end; + WndHandle:= self.Handle; + Self.Position := poDesigned; + flags:= SWP_SHOWWINDOW; + case Config.WndPosition of + Remember: SetWindowPos(WndHandle, HWND_TOP, Config.WndLeft, Config.WndTop, Config.WndWidth, Config.WndHeight, flags); + TopLeft: SetWindowPos(WndHandle, HWND_TOP, 0, 0, Config.WndWidth, Config.WndHeight, flags); + TopRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - Config.WndWidth, 0, Config.WndWidth, Config.WndHeight, flags); + BottomLeft: SetWindowPos(WndHandle, HWND_TOP, 0, Monitor.WorkareaRect.Bottom - Config.WndHeight, Config.WndWidth, Config.WndHeight, flags); + BottomRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - Config.WndWidth, + Monitor.WorkareaRect.Bottom - Config.WndHeight, Config.WndWidth, Config.WndHeight, flags); + Center: + begin + SetWindowPos(WndHandle, HWND_TOP, 0, 0, Config.WndWidth, Config.WndHeight, SWP_SHOWWINDOW or SWP_NOMOVE); + Self.Position := poScreenCenter; + end; end; end; diff --git a/ui/options.lfm b/ui/options.lfm index 63bd39e..9caaea3 100644 --- a/ui/options.lfm +++ b/ui/options.lfm @@ -1,25 +1,25 @@ object OptionsForm: TOptionsForm Left = 465 - Height = 351 + Height = 234 Top = 319 - Width = 825 + Width = 550 BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Options' - ClientHeight = 351 - ClientWidth = 825 - DesignTimePPI = 144 + ClientHeight = 234 + ClientWidth = 550 KeyPreview = True - OnCreate = EnableFields + OnCreate = FormCreate + OnDestroy = FormDestroy OnKeyPress = FormKeyPress Position = poScreenCenter ShowInTaskBar = stNever LCLVersion = '2.0.10.0' object BtnOk: TButton - Left = 576 - Height = 38 - Top = 300 - Width = 112 + Left = 384 + Height = 25 + Top = 200 + Width = 75 Caption = '&OK' Default = True ModalResult = 1 @@ -27,10 +27,10 @@ object OptionsForm: TOptionsForm TabOrder = 1 end object BtnCancel: TButton - Left = 696 - Height = 38 - Top = 300 - Width = 112 + Left = 464 + Height = 25 + Top = 200 + Width = 75 Caption = '&Cancel' ModalResult = 2 ParentFont = False @@ -38,24 +38,24 @@ object OptionsForm: TOptionsForm end object PageControl1: TPageControl Left = 0 - Height = 288 + Height = 192 Top = 0 - Width = 828 - ActivePage = GeneralTab + Width = 552 + ActivePage = FontsTab ParentFont = False - TabIndex = 0 + TabIndex = 2 TabOrder = 0 object GeneralTab: TTabSheet Caption = 'General' - ClientHeight = 250 - ClientWidth = 820 + ClientHeight = 164 + ClientWidth = 544 ParentFont = False object CheckAutostart: TCheckBox - Left = 256 - Height = 29 + Left = 171 + Height = 19 Hint = 'Begin counting down when the application starts' - Top = 111 - Width = 150 + Top = 74 + Width = 100 Caption = '&Autostart timer' ParentFont = False ParentShowHint = False @@ -63,11 +63,11 @@ object OptionsForm: TOptionsForm TabOrder = 4 end object CheckMinToTray: TCheckBox - Left = 18 - Height = 29 + Left = 12 + Height = 19 Hint = 'Don''t show the application in the taskbar when minimized' - Top = 159 - Width = 158 + Top = 106 + Width = 106 Caption = '&Minimize to tray' ParentFont = False ParentShowHint = False @@ -75,23 +75,23 @@ object OptionsForm: TOptionsForm TabOrder = 2 end object GroupBox2: TGroupBox - Left = 6 - Height = 72 - Top = 19 - Width = 240 + Left = 4 + Height = 48 + Top = 13 + Width = 160 Caption = 'Starting position' - ClientHeight = 42 - ClientWidth = 236 + ClientHeight = 28 + ClientWidth = 156 ParentFont = False TabOrder = 0 object PositionCombo: TComboBox - Left = 9 - Height = 33 + Left = 6 + Height = 23 Hint = 'Where to place the application window when it starts' - Top = 3 - Width = 216 + Top = 2 + Width = 144 DropDownCount = 6 - ItemHeight = 25 + ItemHeight = 15 ParentFont = False ParentShowHint = False ShowHint = True @@ -100,11 +100,11 @@ object OptionsForm: TOptionsForm end end object CheckClickTime: TCheckBox - Left = 256 - Height = 29 + Left = 171 + Height = 19 Hint = 'Click the timer numerals to start/pause the timer' - Top = 159 - Width = 168 + Top = 106 + Width = 113 Caption = 'Cl&ick time to start' ParentFont = False ParentShowHint = False @@ -112,11 +112,11 @@ object OptionsForm: TOptionsForm TabOrder = 5 end object CheckDblClickTime: TCheckBox - Left = 256 - Height = 29 + Left = 171 + Height = 19 Hint = 'Double-click the timer numerals to reset the timer' - Top = 207 - Width = 233 + Top = 138 + Width = 156 Caption = '&Double-click time to reset' ParentFont = False ParentShowHint = False @@ -124,11 +124,11 @@ object OptionsForm: TOptionsForm TabOrder = 6 end object CheckAutoRestart: TCheckBox - Left = 522 - Height = 29 + Left = 348 + Height = 19 Hint = 'Start the timer again when the time reaches zero' - Top = 111 - Width = 239 + Top = 74 + Width = 161 Caption = 'Automatically &restart timer' ParentFont = False ParentShowHint = False @@ -136,11 +136,11 @@ object OptionsForm: TOptionsForm TabOrder = 8 end object CheckLoopAudio: TCheckBox - Left = 522 - Height = 29 + Left = 348 + Height = 19 Hint = 'Continue playing the audio file until timer is reset' - Top = 159 - Width = 170 + Top = 106 + Width = 113 Caption = '&Loop audio alarm' ParentFont = False ParentShowHint = False @@ -148,11 +148,11 @@ object OptionsForm: TOptionsForm TabOrder = 9 end object CheckAutoSave: TCheckBox - Left = 18 - Height = 29 + Left = 12 + Height = 19 Hint = 'Automatically save settings to the .ini file on exit' - Top = 207 - Width = 171 + Top = 138 + Width = 113 Caption = 'Auto&save settings' ParentFont = False ParentShowHint = False @@ -160,11 +160,11 @@ object OptionsForm: TOptionsForm TabOrder = 3 end object CheckAlwaysOnTop: TCheckBox - Left = 18 - Height = 29 + Left = 12 + Height = 19 Hint = 'Show the window even when it''s deactivated' - Top = 111 - Width = 144 + Top = 74 + Width = 95 Caption = 'Al&ways on top' ParentFont = False ParentShowHint = False @@ -172,11 +172,11 @@ object OptionsForm: TOptionsForm TabOrder = 1 end object CheckTicking: TCheckBox - Left = 522 - Height = 29 + Left = 348 + Height = 19 Hint = 'Play a ticking sound when timer is running' - Top = 204 - Width = 174 + Top = 136 + Width = 117 Caption = '&Play ticking sound' ParentFont = False ParentShowHint = False @@ -184,11 +184,11 @@ object OptionsForm: TOptionsForm TabOrder = 10 end object CheckSecondsMode: TCheckBox - Left = 522 - Height = 29 + Left = 348 + Height = 19 Hint = 'Enter seconds instead of minutes' - Top = 62 - Width = 204 + Top = 41 + Width = 135 Caption = '&Enable seconds mode' ParentFont = False ParentShowHint = False @@ -196,11 +196,11 @@ object OptionsForm: TOptionsForm TabOrder = 7 end object CheckHideSeconds: TCheckBox - Left = 256 - Height = 29 + Left = 171 + Height = 19 Hint = 'Begin counting down when the application starts' - Top = 62 - Width = 137 + Top = 41 + Width = 91 Caption = '&Hide seconds' ParentFont = False ParentShowHint = False @@ -210,148 +210,148 @@ object OptionsForm: TOptionsForm end object AlarmsTab: TTabSheet Caption = 'Alarms' - ClientHeight = 166 + ClientHeight = 164 ClientWidth = 544 ParentFont = False object GroupBox1: TGroupBox - Left = 6 - Height = 216 - Top = 15 - Width = 801 + Left = 4 + Height = 144 + Top = 10 + Width = 534 Caption = 'Notification actions' - ClientHeight = 186 - ClientWidth = 797 + ClientHeight = 124 + ClientWidth = 530 ParentFont = False TabOrder = 0 object NotifyRunAppOn: TCheckBox - Left = 8 - Height = 17 - Top = 138 - Width = 84 + Left = 5 + Height = 19 + Top = 92 + Width = 93 Caption = '&Run program:' OnClick = EnableFields ParentFont = False TabOrder = 10 end object NotifyAudioOn: TCheckBox - Left = 8 - Height = 17 - Top = 94 - Width = 88 + Left = 5 + Height = 19 + Top = 63 + Width = 94 Caption = '&Play wave file:' OnClick = EnableFields ParentFont = False TabOrder = 6 end object NotifyTrayMsgOn: TCheckBox - Left = 8 - Height = 17 - Top = 48 - Width = 104 + Left = 5 + Height = 19 + Top = 32 + Width = 113 Caption = '&Show tray popup:' OnClick = EnableFields ParentFont = False TabOrder = 3 end object NotifyMsgOn: TCheckBox - Left = 8 - Height = 17 - Top = 4 - Width = 101 + Left = 5 + Height = 19 + Top = 3 + Width = 110 Caption = '&Display message:' OnClick = EnableFields ParentFont = False TabOrder = 0 end object NotifyTrayMsgTest: TButton - Left = 678 - Height = 38 - Top = 45 - Width = 112 + Left = 452 + Height = 25 + Top = 30 + Width = 75 Caption = 'Test' OnClick = TestTrayMsg ParentFont = False TabOrder = 5 end object NotifyMsgTest: TButton - Left = 678 - Height = 38 - Top = 2 - Width = 112 + Left = 452 + Height = 25 + Top = 1 + Width = 75 Caption = 'Test' OnClick = TestMessage ParentFont = False TabOrder = 2 end object NotifyRunTest: TButton - Left = 678 - Height = 38 - Top = 135 - Width = 112 + Left = 452 + Height = 25 + Top = 90 + Width = 75 Caption = 'Test' OnClick = TestRunApp ParentFont = False TabOrder = 13 end object NotifyAudioTest: TButton - Left = 678 - Height = 38 - Top = 92 - Width = 112 + Left = 452 + Height = 25 + Top = 61 + Width = 75 Caption = 'Test' OnClick = TestAudio ParentFont = False TabOrder = 9 end object NotifyTrayMsg: TEdit - Left = 195 - Height = 21 - Top = 48 - Width = 472 + Left = 130 + Height = 23 + Top = 32 + Width = 315 ParentFont = False TabOrder = 4 end object NotifyRunBtn: TButton - Left = 558 - Height = 38 - Top = 135 - Width = 112 + Left = 372 + Height = 25 + Top = 90 + Width = 75 Caption = 'Browse' OnClick = GetAppFile ParentFont = False TabOrder = 12 end object NotifyRunApp: TEdit - Left = 195 - Height = 21 - Top = 138 - Width = 348 + Left = 130 + Height = 23 + Top = 92 + Width = 232 ParentFont = False TabOrder = 11 end object NotifyAudioBtn: TButton - Left = 558 - Height = 38 - Top = 92 - Width = 112 + Left = 372 + Height = 25 + Top = 61 + Width = 75 Caption = 'Browse' OnClick = GetAudioFile ParentFont = False TabOrder = 8 end object NotifyAudio: TEdit - Left = 195 - Height = 21 - Top = 94 - Width = 348 + Left = 130 + Height = 23 + Top = 63 + Width = 232 ParentFont = False TabOrder = 7 end object NotifyMsg: TEdit - Left = 195 - Height = 21 - Top = 4 - Width = 472 + Left = 130 + Height = 23 + Top = 3 + Width = 315 ParentFont = False TabOrder = 1 end @@ -359,34 +359,33 @@ object OptionsForm: TOptionsForm end object FontsTab: TTabSheet Caption = 'Font' - ClientHeight = 250 - ClientWidth = 820 - OnShow = UpdateFonts + ClientHeight = 164 + ClientWidth = 544 ParentFont = False object TimerFontBox: TGroupBox - Left = 6 - Height = 156 - Top = 15 - Width = 801 + Left = 4 + Height = 104 + Top = 10 + Width = 534 Caption = 'Timer font' - ClientHeight = 126 - ClientWidth = 797 + ClientHeight = 84 + ClientWidth = 530 ParentFont = False TabOrder = 0 object Label1: TLabel - Left = 9 - Height = 25 - Top = 9 - Width = 35 + Left = 6 + Height = 15 + Top = 6 + Width = 23 Caption = 'Size:' ParentColor = False ParentFont = False end object FontColor: TColorButton - Left = 252 - Height = 38 - Top = 2 - Width = 112 + Left = 168 + Height = 25 + Top = 1 + Width = 75 BorderWidth = 2 ButtonColorSize = 16 ButtonColor = clBlack @@ -394,64 +393,65 @@ object OptionsForm: TOptionsForm ParentFont = False end object BgColor: TColorButton - Left = 543 - Height = 38 - Top = 2 - Width = 112 + Left = 362 + Height = 25 + Top = 1 + Width = 75 BorderWidth = 2 ButtonColorSize = 16 ButtonColor = clSilver - OnColorChanged = UpdateFonts + OnColorChanged = UpdateFontBgColor ParentFont = False end object LabelTextColor: TLabel - Left = 189 - Height = 25 - Top = 9 - Width = 47 + Left = 126 + Height = 15 + Top = 6 + Width = 32 Caption = 'Color:' ParentColor = False ParentFont = False end object LabelTextColor1: TLabel - Left = 429 - Height = 25 - Top = 9 - Width = 99 + Left = 286 + Height = 15 + Top = 6 + Width = 67 Caption = 'Background:' ParentColor = False ParentFont = False end object FontSize: TSpinEdit - Left = 69 - Height = 33 - Top = 4 - Width = 70 + Left = 46 + Height = 23 + Top = 3 + Width = 47 MaxValue = 1000 OnChange = UpdateFontSize ParentFont = False TabOrder = 0 end - object FontName: TStaticText - Left = 141 - Height = 41 - Top = 70 - Width = 96 + object FontPreview: TStaticText + Left = 94 + Height = 28 + Top = 47 + Width = 66 Alignment = taCenter AutoSize = True - BorderSpacing.InnerBorder = 4 + BorderSpacing.InnerBorder = 3 Caption = '00:00:00' - Color = clCaptionText - Font.Height = -24 + Color = clDefault + Font.Height = -16 ParentFont = False ParentColor = False TabOrder = 2 + Transparent = False end object FontNameBtn: TButton - Left = 8 - Height = 38 - Top = 72 - Width = 112 + Left = 5 + Height = 25 + Top = 48 + Width = 75 Caption = '&Font...' OnClick = ChooseFont ParentFont = False @@ -459,11 +459,11 @@ object OptionsForm: TOptionsForm end end object BtnDefaults: TButton - Left = 15 - Height = 38 + Left = 10 + Height = 25 Hint = 'Reset font to default settings' - Top = 201 - Width = 112 + Top = 134 + Width = 75 Caption = '&Defaults' OnClick = SetFontDefaults ParentFont = False diff --git a/ui/options.pas b/ui/options.pas index e90c343..70ad474 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -31,7 +31,7 @@ TOptionsForm = class(TForm) BgColor: TColorButton; FontSize: TSpinEdit; Label1: TLabel; - FontName: TStaticText; + FontPreview: TStaticText; LabelTextColor: TLabel; LabelTextColor1: TLabel; TimerFontBox: TGroupBox; @@ -57,18 +57,20 @@ TOptionsForm = class(TForm) AlarmsTab: TTabSheet; FontsTab: TTabSheet; procedure ChooseFont(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure EnableFields(Sender: TObject); + procedure FormDestroy(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: char); procedure GetAudioFile(Sender: TObject); procedure GetAppFile(Sender: TObject); function GetFile(Orig: String; FilterStr: String; Dir : String): String; - procedure EnableFields(Sender: TObject); procedure SetFontDefaults(Sender: TObject); procedure TestAudio(Sender: TObject); procedure TestMessage(Sender: TObject); procedure TestRunApp(Sender: TObject); procedure TestTrayMsg(Sender: TObject); procedure UpdateFontColor(Sender: TObject); - procedure UpdateFonts(Sender: TObject); + procedure UpdateFontBgColor(Sender: TObject); procedure UpdateFontSize(Sender: TObject); private public @@ -77,13 +79,16 @@ TOptionsForm = class(TForm) var OptionsForm: TOptionsForm; - f: TFont; // TODO make private +// f: TFont; // TODO make private implementation -uses MainForm1, Config; +uses Controls, MainForm1, Config; { TOptionsForm } +const + FONT_PREVIEW_FONT_SIZE = 18; + // TODO Move all text strings to the top procedure TOptionsForm.GetAudioFile(Sender: TObject); @@ -101,49 +106,155 @@ procedure TOptionsForm.ChooseFont(Sender: TObject); dlg : TFontDialog; begin dlg := TFontDialog.Create(nil); - dlg.Font := f; - + dlg.Font:= FontPreview.Font; + dlg.Font.Size:= FontSize.Value; if dlg.Execute then begin - f.Name := dlg.Font.Name; - f.Size := dlg.Font.Size; - // TODO Only set color if user changed it (XP dialog doesn't support custom colors) - // Works fine on Win7 - //if not (f.Color = dlg.Font.Color) then f.Color:= dlg.Font.Color; - //f.Color:= dlg.Font.Color; - f.CharSet:= dlg.Font.CharSet; - f.Style:= dlg.Font.Style; - UpdateFonts(Sender); + FontSize.Value:= Dlg.Font.Size; + FontColor.ButtonColor:= Dlg.Font.Color; + FontPreview.Caption:= Dlg.Font.Name + ' : 1234567890'; + FontPreview.Font:= Dlg.Font; + FontPreview.Font.Size:= FONT_PREVIEW_FONT_SIZE; end; dlg.Free; end; +procedure TOptionsForm.FormCreate(Sender: TObject); +var Config: TConfig; + WndPositions: TStrings; +begin + Config:= GetConfig; + + PageControl1.TabIndex := 0; + + // Main + WndPositions := TStringList.Create; + WndPositions.Add('Centered'); + WndPositions.Add('Remember position'); + WndPositions.Add('Top left'); + WndPositions.Add('Top right'); + WndPositions.Add('Bottom left'); + WndPositions.Add('Bottom right'); + PositionCombo.Items := WndPositions; + PositionCombo.ItemIndex := Ord(Config.WndPosition); + + CheckAlwaysOnTop.Checked:= Config.AlwaysOnTop; + CheckMinToTray.Checked:= Config.MinToTray; + CheckAutoSave.Checked:= Config.AutoSave; + + CheckHideSeconds.Checked:= Config.HideSeconds; + CheckAutostart.Checked:= Config.AutoStart; + CheckClicktime.Checked:= Config.ClickTime; + CheckDblClickTime.Checked:= Config.DblClickTime; + + CheckSecondsMode.Checked:= Config.SecondsMode; + CheckAutoRestart.Checked:= Config.AutoRestart; + CheckLoopAudio.Checked:= Config.LoopAudio; + CheckTicking.Checked:= Config.TickingOn; + + // Alarms + NotifyMsg.Text:= Config.DoneMessage; + NotifyMsgOn.Checked:= Config.DoneMessageEnabled; + + NotifyTrayMsg.Text:= Config.DoneTrayMsg; + NotifyTrayMsgOn.Checked:= Config.DoneTrayMsgEnabled; + + NotifyAudio.Text:= Config.DoneAudio; + NotifyAudioOn.Checked:= Config.DoneAudioEnabled; + + NotifyRunApp.Text:= Config.DoneApp; + NotifyRunAppOn.Checked:= Config.DoneAppEnabled; + + // Fonts + FontSize.Value:= Config.Font.Size; + FontColor.ButtonColor:= Config.Font.Color; + BgColor.ButtonColor:= Config.Font.BgColor; + FontPreview.Caption:= Config.Font.Name + ' : 1234567890'; + FontPreview.Font.Name:= Config.Font.Name; + //FontPreview.Font.Size:= Config.Font.Size; // size is fixed + FontPreview.Font.Size:= FONT_PREVIEW_FONT_SIZE; + FontPreview.Font.CharSet:= Config.Font.Charset; + FontPreview.Font.Color:= Config.Font.Color; + FontPreview.Font.Style:= Config.Font.Style; + FontPreview.Color:= Config.Font.BgColor; + + EnableFields(Sender); +end; + +procedure TOptionsForm.FormDestroy(Sender: TObject); +var Config : TConfig; +begin + if ModalResult = mrOk then + begin + // Update Config + Config:= GetConfig; + // Main + Config.AlwaysOnTop:= CheckAlwaysOnTop.Checked; + Config.MinToTray:= CheckMinToTray.Checked; + Config.AutoStart:= CheckAutostart.Checked; + Config.HideSeconds:= CheckHideSeconds.Checked; + Config.ClickTime:= CheckClicktime.Checked; + Config.DblClickTime:= CheckDblClickTime.Checked; + Config.AutoRestart:= CheckAutoRestart.Checked; + Config.LoopAudio:= CheckLoopAudio.Checked; + Config.TickingOn:= CheckTicking.Checked; + Config.AutoSave:= CheckAutoSave.Checked; + Config.SecondsMode:= CheckSecondsMode.Checked; + + // Placement + Config.WndPosition:= TPosition(PositionCombo.ItemIndex); + + // Alarms + Config.DoneMessage:= NotifyMsg.Text; + Config.DoneMessageEnabled:= NotifyMsgOn.Checked; + Config.DoneTrayMsg:= NotifyTrayMsg.Text; + Config.DoneTrayMsgEnabled:= NotifyTrayMsgOn.Checked; + Config.DoneAudio:= NotifyAudio.Text; + Config.DoneAudioEnabled:= NotifyAudioOn.Checked; + Config.DoneApp:= NotifyRunApp.Text; + Config.DoneAppEnabled:= NotifyRunAppOn.Checked; + + // Fonts + Config.Font.Name:= FontPreview.Font.Name; + Config.Font.Charset:= FontPreview.Font.CharSet; + Config.Font.Color:= FontPreview.Font.Color; + Config.Font.BgColor:= FontPreview.Color; + Config.Font.Size:= FontSize.Value; // FontPreview has fixed size + Config.Font.Style:= FontPreview.Font.Style; + end; +end; -procedure TOptionsForm.UpdateFonts(Sender: TObject); +procedure TOptionsForm.EnableFields(Sender: TObject); begin - FontName.Caption := f.Name + ' : 1234567890'; - FontName.Font.Name := f.Name; - FontName.Font.Color := f.Color; - FontName.Font.CharSet := f.CharSet; - FontName.Font.Style := f.Style; - FontName.Font.Quality:= fqAntialiased; - FontName.Font.Size := 18; + NotifyMsg.Enabled := NotifyMsgOn.Checked; + NotifyMsgTest.Enabled := NotifyMsgOn.Checked; + NotifyTrayMsg.Enabled := NotifyTrayMsgOn.Checked; + NotifyTrayMsgTest.Enabled := NotifyTrayMsgOn.Checked; - FontName.Color := BgColor.ButtonColor; - FontSize.Value := f.Size; - FontColor.ButtonColor := f.Color; + NotifyAudio.Enabled := NotifyAudioOn.Checked; + NotifyAudioBtn.Enabled := NotifyAudioOn.Checked; + NotifyAudioTest.Enabled := NotifyAudioOn.Checked; + + NotifyRunApp.Enabled := NotifyRunAppOn.Checked; + NotifyRunBtn.Enabled := NotifyRunAppOn.Checked; + NotifyRunTest.Enabled := NotifyRunAppOn.Checked; end; + procedure TOptionsForm.UpdateFontSize(Sender: TObject); begin - f.Size := FontSize.Value; - UpdateFonts(Sender); + //FontPreview.Font.Size:= FontSize.Value; end; procedure TOptionsForm.UpdateFontColor(Sender: TObject); begin - f.Color := FontColor.ButtonColor; - UpdateFonts(Sender); + FontPreview.Font.Color:= FontColor.ButtonColor; +end; + +procedure TOptionsForm.UpdateFontBgColor(Sender: TObject); +begin + FontPreview.Color:= BgColor.ButtonColor; + end; procedure TOptionsForm.GetAppFile(Sender: TObject); @@ -180,35 +291,24 @@ function TOptionsForm.GetFile(Orig: String; FilterStr: String; Dir : String): St dlg.Free; end; -procedure TOptionsForm.EnableFields(Sender: TObject); -begin - NotifyMsg.Enabled := NotifyMsgOn.Checked; - NotifyMsgTest.Enabled := NotifyMsgOn.Checked; - NotifyTrayMsg.Enabled := NotifyTrayMsgOn.Checked; - NotifyTrayMsgTest.Enabled := NotifyTrayMsgOn.Checked; - - NotifyAudio.Enabled := NotifyAudioOn.Checked; - NotifyAudioBtn.Enabled := NotifyAudioOn.Checked; - NotifyAudioTest.Enabled := NotifyAudioOn.Checked; - - NotifyRunApp.Enabled := NotifyRunAppOn.Checked; - NotifyRunBtn.Enabled := NotifyRunAppOn.Checked; - NotifyRunTest.Enabled := NotifyRunAppOn.Checked; -end; procedure TOptionsForm.SetFontDefaults(Sender: TObject); var - Cfg : TFontConfig; -begin - Cfg:= GetConfig.GetDefaultFont; - BgColor.ButtonColor:= Cfg.BgColor; - f.Name:= Cfg.Name; - f.Color:= Cfg.Color; - f.CharSet:= Cfg.Charset; - f.Size:= Cfg.Size; - f.Style:= Cfg.Style; - UpdateFonts(Sender); + DefFont : TFontConfig; +begin + DefFont:= GetConfig.GetDefaultFont; + + FontSize.Value:= DefFont.Size; + FontColor.ButtonColor:= DefFont.Color; + BgColor.ButtonColor:= DefFont.BgColor; + FontPreview.Caption:= DefFont.Name + ' : 1234567890'; + FontPreview.Font.Name:= DefFont.Name; + //FontPreview.Font.Size:= DefFont.Size; + FontPreview.Font.CharSet:= DefFont.Charset; + FontPreview.Font.Color:= DefFont.Color; + FontPreview.Font.Style:= DefFont.Style; + FontPreview.Color:= DefFont.BgColor; end; procedure TOptionsForm.TestAudio(Sender: TObject); From 20e54a2e77badcaf92d27cb087c64aecc59994a2 Mon Sep 17 00:00:00 2001 From: neo85 Date: Wed, 17 Feb 2021 10:20:30 +0100 Subject: [PATCH 05/21] MyTimer class --- model/mytimer.pas | 181 ++++++++++++++++++++++ snaptimer.lpi | 6 +- snaptimer.lps | 222 ++++++++++++++++----------- snaptimer.lrs | 374 ---------------------------------------------- ui/mainform1.pas | 144 +++++++++--------- 5 files changed, 398 insertions(+), 529 deletions(-) create mode 100644 model/mytimer.pas delete mode 100644 snaptimer.lrs diff --git a/model/mytimer.pas b/model/mytimer.pas new file mode 100644 index 0000000..cbdeb01 --- /dev/null +++ b/model/mytimer.pas @@ -0,0 +1,181 @@ +unit mytimer; + +{$mode objfpc} + +interface + +uses + Classes, SysUtils, ExtCtrls; + +type + TMode = (Timer, Stopwatch); + TState = (Running, Stopped, Paused); + + // TODO find better name + TMyTimer = class + private + FTimer: TTimer; + // Start or End time, depending on the Mode. + FTime: TDateTime; + FSeconds: Integer; + + FState: TState; + FMode: TMode; + FOnSecondElapsed: TNotifyEvent; + FOnFinished: TNotifyEvent; + + function GetSeconds : Integer; + procedure SetSeconds(AValue: Integer); + function GetMinutes : Integer; + procedure SetMinutes(AValue: Integer); + + procedure OnTimer(Sender: TObject); + procedure UpdateSeconds; + procedure UpdateTime; + public + constructor Create; + destructor Free; + procedure Start; + procedure Pause; + procedure Resume; + procedure StartPauseResume; + procedure Stop; + + property Seconds: Integer read GetSeconds write SetSeconds; + property Minutes: Integer read GetMinutes write SetMinutes; + property State: TState read FState; + property Mode: TMode read FMode write FMode; + property OnSecondElapsed: TNotifyEvent read FOnSecondElapsed write FOnSecondElapsed; + property OnFinished: TNotifyEvent read FOnFinished write FOnFinished; +end; + +implementation + +uses DateUtils; + +function TMyTimer.GetSeconds : Integer; +begin + Result:= FSeconds; +end; + +procedure TMyTimer.SetSeconds(AValue: Integer); +begin + if State <> Stopped then + Raise Exception.Create('Timer is not in `Stopped` state'); + FSeconds:= AValue; +end; + +function TMyTimer.GetMinutes : Integer; +begin + Result:= FSeconds div 60; +end; + +procedure TMyTimer.SetMinutes(AValue: Integer); +begin + if State <> Stopped then + Raise Exception.Create('Timer is not in `Stopped` state'); + FSeconds:= AValue * 60; +end; + +procedure TMyTimer.OnTimer(Sender: TObject); +begin + if Mode = Timer then + begin + if Now >= FTime then + begin + FTimer.Enabled:= False; + FSeconds:= 0; + FState:= Stopped; + OnFinished(self); + end + else + UpdateSeconds; + end + else + begin + // Stopwatch + UpdateSeconds; + end +end; + +procedure TMyTimer.UpdateSeconds; +var s : Integer; +begin + s:= SecondsBetween(FTime, Now); + if s <> FSeconds then + begin + FSeconds:= s; + OnSecondElapsed(self); + end; +end; + +procedure TMyTimer.UpdateTime; +begin + if Mode = Timer then + FTime:= Now + (FSeconds * OneSecond) // FTime is the end time + else + FTime:= Now - (FSeconds * OneSecond) // FTime is the start time +end; + +constructor TMyTimer.Create; +begin + FTimer:= TTimer.Create(nil); + FTimer.Interval:= 160; + FTimer.OnTimer:= @OnTimer; + FMode:= Timer; +end; + +destructor TMyTimer.Free; +begin + FTimer.Free; +end; + + +procedure TMyTimer.Start; +begin + if State <> Stopped then + Raise Exception.Create('Timer is not in `Stopped` state'); + + FState:= Running; + UpdateTime; + FTimer.Enabled:= True; +end; + +procedure TMyTimer.Pause; +begin + if State <> Running then + Raise Exception.Create('Timer is not in `Running` state'); + + FState:= Paused; + FTimer.Enabled:= False; +end; + +procedure TMyTimer.Resume; +begin + if State <> Paused then + Raise Exception.Create('Timer is not in `Paused` state'); + + FState:= Running; + UpdateTime; + FTimer.Enabled:= True; +end; + +procedure TMyTimer.StartPauseResume; +begin + case State of + Running : Pause; + Stopped : Start; + Paused : Resume; + end; +end; + +procedure TMyTimer.Stop; +begin + FTimer.Enabled:= False; + FState:= Stopped; + // TODO reset FTime, FSeconds?? + // Call OnFinished? +end; + +end. + diff --git a/snaptimer.lpi b/snaptimer.lpi index 0064e5b..10cf8d2 100644 --- a/snaptimer.lpi +++ b/snaptimer.lpi @@ -85,7 +85,7 @@ - + @@ -122,6 +122,10 @@ + + + + diff --git a/snaptimer.lps b/snaptimer.lps index 2286cdb..76770a8 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,23 +4,22 @@ - + - - - + + + - + - - + @@ -30,7 +29,7 @@ - + @@ -39,10 +38,11 @@ - - - - + + + + + @@ -53,47 +53,52 @@ - - - - - - - + + + + - - - + + + + + + + + + + + - - + + - - + + - - + + @@ -101,204 +106,247 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + + + + + diff --git a/snaptimer.lrs b/snaptimer.lrs deleted file mode 100644 index f371f79..0000000 --- a/snaptimer.lrs +++ /dev/null @@ -1,374 +0,0 @@ -LazarusResources.Add('MAINICON','ICO',[ - #0#0#1#0#4#0' '#0#0#1#0#8#0#168#8#0#0'F'#0#0#0#16#16#0#0#1#0#8#0'h'#5#0#0#238 - +#8#0#0' '#0#0#1#0' '#0#168#16#0#0'V'#14#0#0#16#16#0#0#1#0' '#0'h'#4#0#0#254 - +#30#0#0'('#0#0#0' '#0#0#0'@'#0#0#0#1#0#8#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0'711'#0'<44'#0'A88'#0'C=;'#0'H?='#0'NFC'#0'OGE'#0'ULJ' - +#0'\RO'#0'nxz'#0'p{}'#0#175'k'#19#0#175'l'#19#0#175'l'#20#0#176'm'#22#0#177 - +'o'#24#0#178'p'#25#0#177'q'#29#0#180'q'#28#0#183'u!'#0#186'w$'#0#185'x%'#0 - +#177'v*'#0#178'x+'#0#181'y,'#0#188'z)'#0#184'|/'#0#190#127'.'#0#208#130','#0 - +#193#129'3'#0#196#128'2'#0#195#132'7'#0#198#132'6'#0#203#136';'#0#201#136'<' - +#0#204#136'<'#0#214#141':'#0#217#145'='#0#177#134'M'#0#183#140'S'#0#179#156 - +'~'#0#202#139'D'#0#205#141'D'#0#209#141'B'#0#209#143'G'#0#207#145'L'#0#218 - +#145'B'#0#218#148'A'#0#218#147'D'#0#210#145'I'#0#213#145'I'#0#211#147'M'#0 - +#217#151'N'#0#221#153'I'#0#219#155'M'#0#220#155'M'#0#221#156'N'#0#192#149'\' - +#0#212#153'T'#0#219#154'Q'#0#220#157'Q'#0#223#158'U'#0#209#155'Z'#0#217#158 - +'Z'#0#220#156'Y'#0#224#158'M'#0#224#155'Q'#0#227#159'W'#0#225#158'X'#0#222 - +#161'U'#0#226#162'Q'#0#225#167'Z'#0#224#161'^'#0#199#155'a'#0#197#158'j'#0 - +#219#163'b'#0#220#163'b'#0#222#167'g'#0#198#166'z'#0#230#165'a'#0#227#170'l' - +#0#228#171'm'#0#231#172'o'#0#234#173'l'#0#232#174'r'#0#237#176'q'#0#233#176 - +'v'#0#235#178'y'#0#237#183'~'#0#240#181'z'#0'm~'#129#0'n'#128#130#0't'#128 - +#128#0'r'#131#134#0'u'#131#133#0'q'#132#135#0'x'#132#133#0'q'#133#136#0'z' - +#134#136#0'}'#137#138#0#130#138#139#0#129#139#141#0#129#141#142#0#133#141#142 - +#0#131#144#145#0#132#144#146#0#133#147#148#0#137#145#144#0#142#151#152#0#148 - +#157#156#0#189#165#135#0#178#174#153#0#153#162#161#0#181#182#167#0#207#176 - +#130#0#238#184#128#0#242#186#130#0#216#197#156#0#242#193#141#0#249#198#146#0 - +#252#201#150#0#220#207#171#0#227#206#163#0#255#213#168#0#226#211#176#0#164 - +#191#200#0#154#204#235#0#153#205#236#0#172#211#230#0#166#211#233#0#162#210 - +#238#0#170#218#234#0#174#221#240#0#176#222#245#0#175#224#238#0#178#228#240#0 - +#182#228#241#0#182#231#244#0#184#231#244#0#183#232#241#0#183#232#245#0#185 - +#232#241#0#188#234#241#0#190#236#243#0#186#234#244#0#187#236#245#0#190#237 - +#246#0#185#228#250#0#191#236#249#0#190#242#253#0#210#213#196#0#196#217#216#0 - +#213#228#216#0#222#241#235#0#192#235#242#0#194#236#243#0#193#237#245#0#197 - +#237#244#0#196#239#248#0#195#240#247#0#198#241#247#0#200#240#245#0#205#241 - +#246#0#193#240#249#0#197#241#248#0#196#240#255#0#197#247#255#0#201#242#249#0 - +#206#243#248#0#202#244#249#0#205#245#250#0#207#245#252#0#202#249#255#0#205 - +#250#254#0#207#252#255#0#210#244#248#0#213#246#249#0#209#246#252#0#216#247 - +#250#0#215#248#251#0#210#249#254#0#214#249#253#0#208#253#255#0#214#254#255#0 - +#217#248#251#0#221#250#251#0#217#249#252#0#221#250#252#0#218#253#255#0#221 - +#253#254#0#231#238#224#0#226#243#236#0#225#254#254#0#229#255#255#0#233#255 - +#255#0#144#0'b'#0#176#0'x'#0#207#0#142#0#240#0#164#0#255#17#179#0#255'1'#190 - +#0#255'Q'#199#0#255'q'#209#0#255#145#220#0#255#177#229#0#255#209#240#0#255 - +#255#255#0#0#0#0#0','#0'/'#0'K'#0'P'#0'i'#0'p'#0#135#0#144#0#165#0#176#0#196 - +#0#207#0#225#0#240#0#240#17#255#0#242'1'#255#0#244'Q'#255#0#246'q'#255#0#247 - +#145#255#0#249#177#255#0#251#209#255#0#255#255#255#0#0#0#0#0#27#0'/'#0'-'#0 - +'P'#0'?'#0'p'#0'R'#0#144#0'c'#0#176#0'v'#0#207#0#136#0#240#0#153#17#255#0#166 - +'1'#255#0#180'Q'#255#0#194'q'#255#0#207#145#255#0#220#177#255#0#235#209#255#0 - +#255#255#255#0#0#0#0#0#8#0'/'#0#14#0'P'#0#21#0'p'#0#27#0#144#0'!'#0#176#0'&' - +#0#207#0','#0#240#0'>'#17#255#0'X1'#255#0'qQ'#255#0#140'q'#255#0#166#145#255 - +#0#191#177#255#0#218#209#255#0#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0''''#24#17#17#19#17#18#24''''#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0')'#18#20#31'$,,,,,$'#31#20#18')'#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#23#21'$389GGGGG973$'#21#23#0#0#0#0#0#0#0#0#0#0#0#0#0#0#17'!39H{' - +#191#195#195#195#195#195#191'{H93!'#17#0#0#0#0#0#0#0#0#0#0#0#0#19'25B}'#194 - +#194#194#194#194#195#194#194#194#194#194'}B52'#19#0#0#0#0#0#0#0#0#0#0#17'2v'#184#182#187#180#177#177#187#189#190#189 - +#179#177#177#177#179#177#177#180#187#182#184'v>+('#0#0#0#24'@6'#153#181#177 - +#177#177#176#177#189'he'#190#182#177#176#176#176#177#177#177#176#177#181#153 - +'6@'#24#0#0#0#16'S0'#183#181#176#178#176#176#176#184'l'#7'd'#190#182#177#176 - ,#176#176#176#176#176#176#178#183'0S'#16#0#0#0#16'U&'#175#172#178#182#178#176 - +#169#178#184'h'#4']'#189#178#169#169#169#176#178#181#181#172#174'0U'#15#0#0#0 - +#15'W&'#174#172'jki'#172#163#169#178#189'g'#1#10#181#169#163#163#171'iki'#178 - +#173'&W'#15#0#0#0#15'X%'#183#170#171#172#171#168#162#168#171#184'c'#2#11#181 - +#162#162#162#168#171#171#171#170#183'%X'#15#0#0#0#15'Y%'#175#161#162#162#162 - +#162#162#170#183'a'#3'a'#183#170#162#162#162#162#162#162#162#161#175'%Y'#15#0 - +#0#0#25'R1'#175#159#158#158#158#162#168#175'c'#5'c'#183#168#162#158#158#158 - +#158#158#158#158#165#175'1R'#25#0#0#0':;E'#151#150#159#160#159#165#173'd'#6 - +'d'#174#165#157#156#156#158#156#156#160#160#160#164#151'E;:'#0#0#0#0#26'Zs' - +#166#160'_'#165#173'g'#8'g'#173#160#157#155#156#155#155#155#155#160'_'#160 - +#166'sZ'#26#0#0#0#0#0#18'RC'#152#149#164#164'i'#9'j'#167#160#144#157#144#143 - +#143#143#143#144#144#147#149#152'CR'#18#0#0#0#0#0#0#30'u?'#148#146#164'ik' - +#167#147#144#149'^'#149#143#143#144#147#144#146#146#148'?u'#30#0#0#0#0#0#0#0 - +#25'@TO'#134#139#150'b'#147#145#142#147'b'#147#139#140#145'['#146#137#134'OV' - +'@'#25#0#0#0#0#0#0#0#0#14'tTK'#129#133#141#145#140#140#146'\'#146#140#140#145 - +#138#133#129'KTt'#14#0#0#0#0#0#0#0#0#0#0#19'uV.r'#131#130#132#135#136#137#136 - +#135#132#130#131'r.Zt'#19#0#0#0#0#0#0#0#0#0#0#0#0#14'MxD*p~'#128#127#127#127 - +#128'~p*DxL'#13#0#0#0#0#0#0#0#0#0#0#0#0#0#0#25' tyP/'#29#29#29#29#29'/Pyt ' - +#25#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#18#28'Nw|||||wN'#28#18#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0'J'#27#12#12#12#12#12#27'J'#0#0#0#0#0#0#0#0#0#0#0 - +#255#255#255#255#255#255#255#255#255#255#255#255#255#240#7#255#255#128#0#255 - +#255#0#0#127#254#0#0'?'#252#0#0#31#248#0#0#15#240#0#0#7#224#0#0#3#224#0#0#3 - +#224#0#0#3#192#0#0#1#192#0#0#1#192#0#0#1#192#0#0#1#192#0#0#1#192#0#0#1#192#0 - +#0#1#192#0#0#1#192#0#0#1#224#0#0#3#224#0#0#3#240#0#0#7#240#0#0#7#248#0#0#15 - +#252#0#0#31#254#0#0'?'#255#0#0#127#255#192#1#255#255#240#7#255'('#0#0#0#16#0 - +#0#0' '#0#0#0#1#0#8#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#180'~='#0#194#127'/'#0#173'~E'#0#167'~J'#0#200#134'9'#0#201#136':'#0#185#132 - +'C'#0#184#139'R'#0#150#129'e'#0#153#133'l'#0#163#134'`'#0#164#146'z'#0#162 - +#147'~'#0#167#150#127#0#180#153'v'#0#182#155'x'#0#185#158'z'#0#200#138'A'#0 - +#204#142'E'#0#209#143'B'#0#199#144'N'#0#206#145'I'#0#207#147'N'#0#209#145'G' - +#0#213#147'G'#0#215#149'G'#0#217#151'I'#0#217#153'O'#0#192#149'^'#0#199#152 - +']'#0#207#155'['#0#210#152'T'#0#221#157'V'#0#212#157'['#0#215#160']'#0#224 - +#160'Y'#0#204#158'd'#0#223#164'c'#0#221#167'e'#0#220#168'g'#0#220#168'h'#0 - +#215#169'u'#0#222#182'~'#0#226#168'i'#0#224#171'n'#0#229#170'l'#0#227#174't' - +#0'|'#138#138#0'}'#140#141#0#130#145#146#0#133#148#149#0#137#149#149#0#135 - +#152#154#0#137#153#154#0#181#167#139#0#181#167#140#0#185#167#137#0#142#166 - +#170#0#151#184#190#0#183#189#178#0#197#168#129#0#203#172#131#0#206#180#142#0 - +#215#192#151#0#214#195#152#0#210#197#157#0#225#197#149#0#205#197#170#0#220 - +#214#183#0#216#214#187#0#227#220#190#0#171#196#199#0#175#197#199#0#163#205 - +#214#0#168#208#215#0#173#211#219#0#174#213#219#0#175#211#222#0#191#208#208#0 - +#186#212#215#0#173#221#239#0#189#219#224#0#188#220#225#0#175#224#238#0#177 - +#226#238#0#180#230#241#0#186#232#242#0#190#234#241#0#191#236#243#0#185#232 - +#246#0#188#235#244#0#190#238#247#0#224#228#208#0#197#224#227#0#197#225#228#0 - +#203#228#230#0#207#230#230#0#201#235#237#0#194#233#242#0#194#236#243#0#195 - +#237#245#0#196#237#245#0#200#239#244#0#201#241#246#0#205#241#246#0#194#241 - +#248#0#199#242#249#0#201#242#248#0#206#243#248#0#203#247#251#0#205#245#250#0 - +#205#248#253#0#210#244#244#0#209#245#248#0#215#246#249#0#214#250#253#0#219 - +#249#251#0#220#250#250#0#217#249#252#0#220#250#252#0#220#252#254#0#228#238 - +#224#0#230#250#247#0#225#252#250#0#225#252#253#0#231#255#255#0#255#255#255#0 - +#0#0#0#0'/&'#0#0'PA'#0#0'p['#0#0#144't'#0#0#176#142#0#0#207#169#0#0#240#195#0 - +#0#255#210#17#0#255#216'1'#0#255#221'Q'#0#255#228'q'#0#255#234#145#0#255#240 - +#177#0#255#246#209#0#255#255#255#0#0#0#0#0'/'#20#0#0'P"'#0#0'p0'#0#0#144'>'#0 - +#0#176'M'#0#0#207'['#0#0#240'i'#0#0#255'y'#17#0#255#138'1'#0#255#157'Q'#0#255 - +#175'q'#0#255#193#145#0#255#210#177#0#255#229#209#0#255#255#255#0#0#0#0#0'/' - +#3#0#0'P'#4#0#0'p'#6#0#0#144#9#0#0#176#10#0#0#207#12#0#0#240#14#0#0#255' '#18 - +#0#255'>1'#0#255'\Q'#0#255'zq'#0#255#151#145#0#255#182#177#0#255#212#209#0 - +#255#255#255#0#0#0#0#0'/'#0#14#0'P'#0#23#0'p'#0'!'#0#144#0'+'#0#176#0'6'#0 - +#207#0'@'#0#240#0'I'#0#255#17'Z'#0#255'1p'#0#255'Q'#134#0#255'q'#156#0#255 - +#145#178#0#255#177#200#0#255#209#223#0#255#255#255#0#0#0#0#0'/'#0' '#0'P'#0 - +'6'#0'p'#0'L'#0#144#0'b'#0#176#0'x'#0#207#0#142#0#240#0#164#0#255#17#179#0 - +#255'1'#190#0#255'Q'#199#0#255'q'#209#0#255#145#220#0#255#177#229#0#255#209 - +#240#0#255#255#255#0#0#0#0#0','#0'/'#0'K'#0'P'#0'i'#0'p'#0#135#0#144#0#165#0 - +#176#0#196#0#207#0#225#0#240#0#240#17#255#0#242'1'#255#0#244'Q'#255#0#246'q' - +#255#0#247#145#255#0#249#177#255#0#251#209#255#0#255#255#255#0#0#0#0#0#27#0 - ,'/'#0'-'#0'P'#0'?'#0'p'#0'R'#0#144#0'c'#0#176#0'v'#0#207#0#136#0#240#0#153#17 - +#255#0#166'1'#255#0#180'Q'#255#0#194'q'#255#0#207#145#255#0#220#177#255#0#235 - +#209#255#0#255#255#255#0#0#0#0#0#8#0'/'#0#14#0'P'#0#21#0'p'#0#27#0#144#0'!'#0 - +#176#0'&'#0#207#0','#0#240#0'>'#17#255#0'X1'#255#0'qQ'#255#0#140'q'#255#0#166 - +#145#255#0#191#177#255#0#218#209#255#0#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#13#9#9#10#0#0#0#0#0#0#0#0#0#0#11#2#20#27#27#26#6#1#0#0#0 - +#0#0#0#0#3#26'Cz~~{G'''#5#0#0#0#0#0#4#28']a}~O}a|+'#6#0#0#0#0#24'Eyxux`vxxv)' - +#7#0#0#8')t^swwwssw_F'#24#0#0#18'ArrrI4trrrrq!'#14#0#19'BSHmr40oiRHp$'#12#0 - +#22'Bllho21lghln$'#12#0#21'@efk32kddffb!'#0#0#15'-cM65jYXXYLD'#22#0#0#0#23'?' - +'Z:\[;WKWP,'#16#0#0#0#0'&>NTVJUQ'#255#205#251#255#255#207#245#252#255#210#247#252#255#211#248 - +#253#255#210#247#252#255#208#244#249#255#206#243#248#255#209#245#250#255#215 - +#255#255#255#132#142#142#255'C=;'#255't'#128#128#255#218#255#255#255#209#246 - +#251#255#206#242#247#255#206#242#247#255#206#242#247#255#208#244#249#255#210 - +#247#252#255#211#248#253#255#210#247#252#255#207#245#252#255#205#251#255#255 - +#217#146'>'#255#232#174'r'#255#176'n'#22#255#0#0#0#22#0#0#0#0#0#0#0#22#176'm' - +#22#255#233#176'v'#255#217#144'='#255#203#250#255#255#207#246#252#255#131#144 - +#145#255#133#147#148#255#131#144#145#255#207#244#250#255#204#241#247#255#205 - +#242#247#255#208#246#252#255#218#255#255#255#129#139#141#255'711'#255'nxz' - +#255#211#250#255#255#205#242#247#255#204#241#246#255#204#241#246#255#207#244 - +#250#255#131#144#145#255#133#147#148#255#131#144#145#255#207#246#252#255#203 - +#250#255#255#217#144'='#255#233#176'v'#255#176'm'#22#255#0#0#0#22#0#0#0#0#0#0 - +#0#22#176'm'#22#255#235#178'y'#255#215#142';'#255#208#253#255#255#202#243#250 - +#255#205#245#250#255#206#246#251#255#205#245#250#255#202#242#247#255#201#240 - +#245#255#201#241#246#255#205#246#251#255#215#255#255#255'y'#135#137#255'<44' - +#255'p{}'#255#207#249#254#255#202#241#246#255#201#240#245#255#201#240#245#255 - +#202#242#247#255#205#245#250#255#206#246#251#255#205#245#250#255#202#243#250 - +#255#208#253#255#255#215#142';'#255#235#178'y'#255#176'm'#22#255#0#0#0#22#0#0 - +#0#0#0#0#0#19#176'm'#22#255#237#183'~'#255#214#141':'#255#207#252#255#255#198 - +#241#247#255#200#240#245#255#200#240#245#255#200#240#245#255#199#239#244#255 - +#200#240#245#255#202#244#249#255#210#253#255#255'x'#132#133#255'A88'#255'w' - +#131#133#255#210#254#255#255#202#243#248#255#199#239#244#255#199#239#244#255 - +#199#239#244#255#199#239#244#255#200#240#245#255#200#240#245#255#200#240#245 - +#255#198#241#247#255#207#252#255#255#214#141':'#255#237#183'~'#255#176'm'#22 - +#255#0#0#0#19#0#0#0#0#0#0#0#14#172'k'#22#230#227#170'l'#255#218#147'D'#255 - +#208#251#255#255#196#239#248#255#197#237#245#255#197#237#245#255#197#237#244 - +#255#198#238#245#255#200#241#249#255#207#251#255#255'{'#134#136#255'H?='#255 - +'z'#134#136#255#208#252#255#255#201#242#249#255#198#238#245#255#197#237#244 - +#255#197#237#244#255#197#237#244#255#197#237#244#255#197#237#244#255#197#237 - +#245#255#197#237#245#255#196#239#248#255#208#251#255#255#218#147'D'#255#227 - ,#170'l'#255#172'k'#22#230#0#0#0#14#0#0#0#0#0#0#0#7#166'i'#23#179#212#153'T' - +#255#225#158'X'#255#210#213#196#255#193#240#251#255#195#238#246#255#196#239 - +#246#255#196#238#246#255#197#241#248#255#203#249#255#255'~'#138#139#255'NFC' - +#255'}'#137#139#255#204#250#255#255#197#241#248#255#194#237#244#255#194#236 - +#243#255#194#236#243#255#194#236#243#255#194#236#243#255#194#236#243#255#195 - +#238#245#255#196#239#246#255#195#238#246#255#193#240#251#255#210#213#196#255 - +#225#158'X'#255#212#153'T'#255#166'i'#23#179#0#0#0#7#0#0#0#0#0#0#0#1#148'^' - +#22'V'#188'{*'#255#240#182'{'#255#207#176#130#255#196#240#255#255#194#240#248 - +#255'u'#131#133#255#197#241#249#255#200#248#255#255#128#141#143#255'ULJ'#255 - +#128#141#142#255#201#249#255#255#195#240#247#255#193#237#244#255#192#235#242 - +#255#192#235#242#255#192#235#242#255#192#235#242#255#192#235#242#255#192#235 - +#243#255#194#238#245#255'u'#131#133#255#194#240#248#255#196#240#255#255#207 - +#176#130#255#240#182'{'#255#188'{*'#255#148'^'#22'V'#0#0#0#1#0#0#0#0#0#0#0#0 - +#0#0#0#9#175'm'#22#247#228#171'm'#255#224#155'Q'#255#196#217#216#255#191#236 - +#249#255#191#239#247#255#194#242#250#255#130#142#142#255'\RO'#255#132#144#146 - +#255#198#247#255#255#192#239#246#255#191#237#244#255#192#238#245#255#191#236 - +#243#255#189#234#241#255#189#234#241#255#189#234#241#255#189#235#241#255#189 - +#235#242#255#190#236#243#255#191#239#246#255#191#236#249#255#196#217#216#255 - +#224#155'Q'#255#228#171'm'#255#175'm'#22#247#0#0#0#9#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#2#157'b'#23'f'#193#129'3'#255#242#186#131#255#209#155'Z'#255#185#228 - +#250#255#187#235#246#255#192#241#250#255#131#143#143#255#134#147#148#255#196 - +#247#255#255#190#237#246#255#188#235#243#255#191#238#247#255'r'#131#134#255 - +#191#238#247#255#188#234#242#255#187#233#241#255#189#235#243#255#189#236#245 - +#255#189#235#243#255#187#235#244#255#187#234#245#255#185#228#250#255#209#155 - +'Z'#255#242#186#130#255#193#129'3'#255#157'b'#23'g'#0#0#0#2#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#6#175'm'#23#231#217#159'['#255#234#173'l'#255#198#166'z' - +#255#177#222#245#255#184#231#244#255#190#242#253#255'q'#133#136#255#189#238 - +#247#255#185#232#242#255#185#232#242#255#189#237#247#255'q'#132#135#255#189 - +#237#247#255#185#232#242#255#184#232#241#255#187#235#244#255'm~'#129#255#187 - +#237#246#255#182#228#241#255#176#222#245#255#198#166'z'#255#237#176'q'#255 - +#217#159'['#255#175'm'#23#231#0#0#0#6#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#1#142'Z'#21''''#175'l'#20#255#238#184#128#255#235#174'm'#255#198#158 - +'k'#255#172#211#230#255#175#221#241#255#183#232#245#255#184#235#244#255#183 - +#232#241#255#183#232#241#255#187#236#245#255'n'#128#130#255#187#236#245#255 - +#183#232#241#255#183#232#241#255#184#235#244#255#182#231#244#255#174#221#240 - +#255#172#211#230#255#197#158'j'#255#235#174'm'#255#238#184#128#255#175'l'#20 - +#255'nE'#17#18#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#1#157'b'#23'8'#180'r'#28#255#239#186#131#255#237#177'r'#255#207#145'L'#255 - +#181#182#167#255#162#210#238#255#166#211#233#255#170#218#234#255#175#224#238 - +#255#178#228#240#255#179#229#242#255#178#228#240#255#175#224#238#255#170#218 - +#234#255#166#211#233#255#162#210#238#255#181#182#167#255#207#145'L'#255#240 - +#181'x'#255#239#186#130#255#180'r'#28#255#143'Z'#21'#'#0#0#0#1#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#147'\'#21'"'#175 - +'l'#20#255#220#163'b'#255#249#198#146#255#227#159'W'#255#202#139'D'#255#178 - +#174#153#255#164#191#200#255#153#205#236#255#154#204#235#255#154#204#235#255 - +#154#204#235#255#153#205#236#255#164#191#200#255#178#174#153#255#202#139'D' - +#255#227#159'W'#255#249#198#146#255#219#163'b'#255#175'l'#19#255#148']'#22'#' - +#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0'pF'#17#16#175'l'#22#230#195#132'7'#255#237#184#128#255 - +#252#201#150#255#230#165'a'#255#218#144'C'#255#208#130'-'#255#209#130','#255 - +#209#130','#255#209#130','#255#208#130'-'#255#218#144'C'#255#230#165'a'#255 - +#252#201#150#255#237#184#128#255#195#132'7'#255#175'l'#22#230'pF'#17#16#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2#167'i'#23'_'#175'm'#21#247#190#127'.' - +#255#222#167'g'#255#242#193#141#255#255#213#169#255#255#213#168#255#255#213 - +#168#255#255#213#168#255#255#213#169#255#242#193#141#255#222#167'g'#255#190 - +#127'.'#255#175'm'#21#247#167'i'#23'_'#0#0#0#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#167'i'#24'L'#172'k'#22#172#175'l'#21 - +#226#175'k'#19#255#175'k'#19#255#175'k'#19#255#175'k'#19#255#175'k'#19#255 - +#175'l'#21#226#172'k'#22#172#167'i'#24'L'#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#255#255#255#255#255#192#1#255 - +#255#0#0#127#254#0#0'?'#248#0#0#15#240#0#0#7#240#0#0#7#224#0#0#3#192#0#0#1 - +#192#0#0#1#128#0#0#0#128#0#0#0#128#0#0#0#128#0#0#0#128#0#0#0#128#0#0#0#128#0 - ,#0#0#128#0#0#0#128#0#0#0#128#0#0#0#128#0#0#0#128#0#0#0#128#0#0#0#192#0#0#1 - +#192#0#0#1#224#0#0#3#224#0#0#7#240#0#0#7#248#0#0#15#254#0#0'?'#255#0#0#127 - +#255#192#1#255'('#0#0#0#16#0#0#0' '#0#0#0#1#0' '#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2#0#0#0 - +#8#0#0#0#11#0#0#0#11#0#0#0#10#0#0#0#5#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 - +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2#0#0#0#18#25#16#4' Date: Wed, 17 Feb 2021 13:07:05 +0100 Subject: [PATCH 06/21] TUtils class --- model/mytimer.pas | 2 + model/utils.pas | 64 ++++++++++++++++++++++ snaptimer.lpi | 6 +- snaptimer.lpr | 2 +- snaptimer.lps | 137 +++++++++++++++++++++++++--------------------- ui/mainform1.pas | 63 ++++----------------- ui/options.pas | 11 ++-- 7 files changed, 162 insertions(+), 123 deletions(-) create mode 100644 model/utils.pas diff --git a/model/mytimer.pas b/model/mytimer.pas index cbdeb01..73d4ca8 100644 --- a/model/mytimer.pas +++ b/model/mytimer.pas @@ -120,9 +120,11 @@ procedure TMyTimer.UpdateTime; constructor TMyTimer.Create; begin FTimer:= TTimer.Create(nil); + FTimer.Enabled:= False; FTimer.Interval:= 160; FTimer.OnTimer:= @OnTimer; FMode:= Timer; + FState:= Stopped; end; destructor TMyTimer.Free; diff --git a/model/utils.pas b/model/utils.pas new file mode 100644 index 0000000..eb00194 --- /dev/null +++ b/model/utils.pas @@ -0,0 +1,64 @@ +unit utils; + +{$mode objfpc}{$H+} + +interface + +// TUtils class serves as a namespace. +type + TUtils = class + class function SecondsToTime(Seconds: integer; HideSeconds: Boolean): string; + class function GetFilePath(Path: string): string; + class procedure RunApp(Path: string); + class function IsInteger(S: String): boolean; + end; + +implementation + +uses + Classes, SysUtils, StrUtils, Forms, Windows; + +class function TUtils.SecondsToTime(Seconds: integer; HideSeconds: Boolean): string; +var + h, m, s: integer; +begin + h := Seconds div 3600; + m := Seconds div 60 - h * 60; + s := Seconds - (h * 3600 + m * 60); + if HideSeconds then + begin + Result := SysUtils.Format('%.2d:%.2d', [h, m]); + end + else + begin + Result := SysUtils.Format('%.2d:%.2d:%.2d', [h, m, s]); + end; +end; + +// TODO Interpret env vars in the path +class function TUtils.GetFilePath(Path: string): string; +begin + if AnsiStartsStr('.', Path) then + begin + Result := ExtractFilePath(Application.ExeName) + Path; + end else + Result := Path; +end; + +class procedure TUtils.RunApp(Path: string); +begin + ShellExecute(0, 'open', PChar(GetFilePath(Path)), nil, nil, SW_SHOWNORMAL); +end; + +class function TUtils.IsInteger(S: String): boolean; +begin + try + Result := True; + StrToInt(S); + except on E: EConvertError do + Result := False; + end; +end; + +end. + diff --git a/snaptimer.lpi b/snaptimer.lpi index 10cf8d2..9981d7c 100644 --- a/snaptimer.lpi +++ b/snaptimer.lpi @@ -85,7 +85,7 @@ - + @@ -126,6 +126,10 @@ + + + + diff --git a/snaptimer.lpr b/snaptimer.lpr index 8f18071..22de19c 100644 --- a/snaptimer.lpr +++ b/snaptimer.lpr @@ -3,7 +3,7 @@ {$mode objfpc}{$H+} uses - Forms, Interfaces, MainForm1; + Forms, Interfaces, MainForm1, utils; {$R *.res} diff --git a/snaptimer.lps b/snaptimer.lps index 76770a8..504b6a7 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,13 +4,13 @@ - + - - + + @@ -19,7 +19,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -40,9 +40,9 @@ - - - + + + @@ -54,9 +54,9 @@ - - - + + + @@ -65,14 +65,14 @@ - + - - - + + + @@ -111,8 +111,8 @@ - - + + @@ -211,142 +211,155 @@ - + - - + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - + - + - + - - + + - - + + - - - - - - - - - - + + diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 940dda1..0be36fa 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -7,7 +7,7 @@ interface uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, Options, MMSystem, - Windows, StrUtils, DateUtils, MyTimer; + Windows, DateUtils, MyTimer; type TMode = (Timer, Stopwatch); @@ -64,19 +64,15 @@ TMainForm = class(TForm) procedure ShowOptions(Sender: TObject); procedure ToggleCountdown(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: char); - function SecondsToTime(Seconds: integer): string; procedure FormWindowStateChange(Sender: TObject); procedure UpdateTime(); procedure SaveSettings(Sender: TObject); - class function GetFilePath(Path: string): string; procedure PlayAudio(Path: string; Loop: boolean); procedure StopAudio(); - procedure RunApp(Path: string); procedure ShowDoneMessage(Msg: string); procedure ShowTrayMessage(Msg: string); procedure SetFieldsVisible(showFields: boolean); procedure PlayTicking(); - function IsInteger(S: String): boolean; private { private declarations } MyTimer : TMyTimer; @@ -102,7 +98,7 @@ TMainForm = class(TForm) implementation uses - Config; + Config, Utils; { TMainForm } @@ -131,6 +127,7 @@ implementation MSG_OPEN_INI = 'An error occurred opening the .ini file, settings won''t be saved'; MSG_WRITE_INI = 'An error occurred trying to write to the .ini file, settings won''t be saved.'; + procedure TMainForm.OnCreateForm(Sender: TObject); var Config: TConfig; @@ -187,7 +184,7 @@ procedure TMainForm.OnCreateForm(Sender: TObject); end; // Use minutes argument if it's the only parameter and it's numeric - if (ParamCount = 1) and (IsInteger(ParamStr(1))) then + if (ParamCount = 1) and (TUtils.IsInteger(ParamStr(1))) then Minutes.Value := StrToInt(ParamStr(1)); //UpdateTimeCaption(SecondsMode); ApplyConfig will do this @@ -405,7 +402,7 @@ procedure TMainForm.Countdown(Sender: TObject); if Config.DoneAudioEnabled then PlayAudio(Config.DoneAudio, Config.LoopAudio); if Config.DoneAppEnabled then - RunApp(Config.DoneApp); + TUtils.RunApp(Config.DoneApp); if Config.DoneTrayMsgEnabled then ShowTrayMessage(Config.DoneTrayMsg); if Config.DoneMessageEnabled then @@ -497,22 +494,6 @@ procedure TMainForm.CloseApp(Sender: TObject); Close; end; -function TMainForm.SecondsToTime(Seconds: integer): string; -var - h, m, s: integer; -begin - h := Seconds div 3600; - m := Seconds div 60 - h * 60; - s := Seconds - (h * 3600 + m * 60); - if GetConfig.HideSeconds then - begin - Result := SysUtils.Format('%.2d:%.2d', [h, m]); - end - else - begin - Result := SysUtils.Format('%.2d:%.2d:%.2d', [h, m, s]); - end; -end; procedure TMainForm.FormKeyPress(Sender: TObject; var Key: char); begin @@ -530,7 +511,7 @@ procedure TMainForm.UpdateTime(); begin // Changing caption while it's not visible keeps time from flashing Count.Visible := False; - Count.Caption := SecondsToTime(Timer1.Tag); + Count.Caption := TUtils.SecondsToTime(Timer1.Tag, GetConfig.HideSeconds); Count.Visible := True; MenuCount.Caption := Count.Caption; if Timer1.Enabled then @@ -558,15 +539,7 @@ procedure TMainForm.FormWindowStateChange(Sender: TObject); end; end; -// TODO Interpret env vars in the path -class function TMainForm.GetFilePath(Path: string): string; -begin - if AnsiStartsStr('.', Path) then - begin - Result := ExtractFilePath(Application.ExeName) + Path; - end else - Result := Path; -end; + procedure TMainForm.PlayAudio(Path: string; Loop: boolean); begin @@ -575,10 +548,10 @@ procedure TMainForm.PlayAudio(Path: string; Loop: boolean); if Loop then begin AudioPlaying := True; - sndPlaySound(PChar(GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC or SND_LOOP); + sndPlaySound(PChar(TUtils.GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC or SND_LOOP); end else - sndPlaySound(PChar(GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC); + sndPlaySound(PChar(TUtils.GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC); end; procedure TMainForm.StopAudio(); @@ -589,15 +562,10 @@ procedure TMainForm.StopAudio(); procedure TMainForm.PlayTicking(); begin - sndPlaySound(PChar(GetFilePath('.\sounds\ticking\ticking.wav')), SND_NODEFAULT or + sndPlaySound(PChar(TUtils.GetFilePath('.\sounds\ticking\ticking.wav')), SND_NODEFAULT or SND_ASYNC or SND_LOOP); end; -procedure TMainForm.RunApp(Path: string); -begin - ShellExecute(0, 'open', PChar(GetFilePath(Path)), nil, nil, SW_SHOWNORMAL); -end; - procedure TMainForm.ShowTrayMessage(Msg: string); begin with TrayIconMain do @@ -616,17 +584,6 @@ procedure TMainForm.ShowDoneMessage(Msg: string); StopAudio(); end; - -function TMainForm.IsInteger(S: String): boolean; -begin - try - Result := True; - StrToInt(S); - except on E: EConvertError do - Result := False; - end; -end; - procedure TMainForm.ApplyConfig; var Config: TConfig; diff --git a/ui/options.pas b/ui/options.pas index 70ad474..a08d236 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -79,11 +79,10 @@ TOptionsForm = class(TForm) var OptionsForm: TOptionsForm; -// f: TFont; // TODO make private implementation -uses Controls, MainForm1, Config; +uses Controls, MainForm1, Config, Utils; { TOptionsForm } const @@ -325,14 +324,14 @@ procedure TOptionsForm.TestMessage(Sender: TObject); procedure TOptionsForm.TestRunApp(Sender: TObject); begin -with Self.Owner as TMainForm do - RunApp(NotifyRunApp.Text); + with Self.Owner as TMainForm do + TUtils.RunApp(NotifyRunApp.Text); end; procedure TOptionsForm.TestTrayMsg(Sender: TObject); begin -with Self.Owner as TMainForm do - ShowTrayMessage(NotifyTrayMsg.Text); + with Self.Owner as TMainForm do + ShowTrayMessage(NotifyTrayMsg.Text); end; initialization From ebd7df4b1d500b799510aa1c0258e36b316f1c94 Mon Sep 17 00:00:00 2001 From: neo85 Date: Thu, 18 Feb 2021 09:21:31 +0100 Subject: [PATCH 07/21] TMyTimer class integration --- model/config.pas | 53 +++--- model/mytimer.pas | 70 +++++--- model/utils.pas | 19 +- snaptimer.lps | 222 ++++++++++++----------- ui/mainform1.lfm | 23 +-- ui/mainform1.pas | 449 ++++++++++++++++++++-------------------------- ui/options.pas | 4 +- 7 files changed, 419 insertions(+), 421 deletions(-) diff --git a/model/config.pas b/model/config.pas index 3e5bae9..2710348 100644 --- a/model/config.pas +++ b/model/config.pas @@ -53,6 +53,7 @@ TConfig = class // Font FFont: TFontConfig; FDefaultFont: TFontConfig; + procedure SetDoneMessage(Msg: String); procedure SetDoneTrayMessage(Msg: String); public @@ -176,6 +177,32 @@ implementation ConfigInstance : TConfig; + function FontStylesToInt(FontStyles: TFontStyles): integer; + var + Mask: integer; + Style: TFontStyle; + begin + // Translate the set into a bit mask + Mask := 0; + for Style := Low(TFontStyle) to High(TFontStyle) do + if Style in FontStyles then + Mask := Mask or (1 shl Ord(Style)); + Result := Mask; + end; + + function IntToFontStyles(Mask: integer): TFontStyles; + var + i: integer; + StyleSet: TFontStyles; + begin + // Translate the bit mask into a set + StyleSet := []; + for i := 0 to Ord(High(TFontStyle)) do + if Mask and (1 shl i) <> 0 then + StyleSet := StyleSet + [TFontStyle(i)]; + Result := StyleSet; + end; + procedure TConfig.SetDoneMessage(Msg: String); begin // TODO Set some reasonable length limit @@ -188,31 +215,6 @@ procedure TConfig.SetDoneTrayMessage(Msg: String); FDoneTrayMsg:= Msg; end; -function FontStylesToInt(FontStyles: TFontStyles): integer; -var - Mask: integer; - Style: TFontStyle; -begin - // Translate the set into a bit mask - Mask := 0; - for Style := Low(TFontStyle) to High(TFontStyle) do - if Style in FontStyles then - Mask := Mask or (1 shl Ord(Style)); - Result := Mask; -end; - -function IntToFontStyles(Mask: integer): TFontStyles; -var - i: integer; - StyleSet: TFontStyles; -begin - // Translate the bit mask into a set - StyleSet := []; - for i := 0 to Ord(High(TFontStyle)) do - if Mask and (1 shl i) <> 0 then - StyleSet := StyleSet + [TFontStyle(i)]; - Result := StyleSet; -end; constructor TConfig.Create; begin @@ -373,6 +375,7 @@ function TConfig.GetDefaultFont : TFontConfig; Result:= FDefaultFont; end; + function GetConfig : TConfig; begin Result:= ConfigInstance; diff --git a/model/mytimer.pas b/model/mytimer.pas index 73d4ca8..a523802 100644 --- a/model/mytimer.pas +++ b/model/mytimer.pas @@ -9,7 +9,7 @@ interface type TMode = (Timer, Stopwatch); - TState = (Running, Stopped, Paused); + TState = (Ready, Running, Finished, Paused); // TODO find better name TMyTimer = class @@ -18,11 +18,11 @@ TMyTimer = class // Start or End time, depending on the Mode. FTime: TDateTime; FSeconds: Integer; - + FSecondsInitial: Integer; FState: TState; FMode: TMode; FOnSecondElapsed: TNotifyEvent; - FOnFinished: TNotifyEvent; + FOnStateChanged: TNotifyEvent; function GetSeconds : Integer; procedure SetSeconds(AValue: Integer); @@ -38,15 +38,17 @@ TMyTimer = class procedure Start; procedure Pause; procedure Resume; - procedure StartPauseResume; - procedure Stop; + procedure Toggle; + procedure Reset; property Seconds: Integer read GetSeconds write SetSeconds; property Minutes: Integer read GetMinutes write SetMinutes; property State: TState read FState; property Mode: TMode read FMode write FMode; property OnSecondElapsed: TNotifyEvent read FOnSecondElapsed write FOnSecondElapsed; - property OnFinished: TNotifyEvent read FOnFinished write FOnFinished; + property OnStateChanged: TNotifyEvent read FOnStateChanged write FOnStateChanged; + private + end; implementation @@ -60,9 +62,10 @@ function TMyTimer.GetSeconds : Integer; procedure TMyTimer.SetSeconds(AValue: Integer); begin - if State <> Stopped then - Raise Exception.Create('Timer is not in `Stopped` state'); + if State <> Ready then + Raise Exception.Create('Timer is not in `Ready` state'); FSeconds:= AValue; + FSecondsInitial:= AValue; end; function TMyTimer.GetMinutes : Integer; @@ -72,9 +75,10 @@ function TMyTimer.GetMinutes : Integer; procedure TMyTimer.SetMinutes(AValue: Integer); begin - if State <> Stopped then - Raise Exception.Create('Timer is not in `Stopped` state'); + if State <> Ready then + Raise Exception.Create('Timer is not in `Ready` or `Finished` state'); FSeconds:= AValue * 60; + FSecondsInitial:= FSeconds; end; procedure TMyTimer.OnTimer(Sender: TObject); @@ -85,8 +89,9 @@ procedure TMyTimer.OnTimer(Sender: TObject); begin FTimer.Enabled:= False; FSeconds:= 0; - FState:= Stopped; - OnFinished(self); + FState:= Finished; + if Assigned(FOnStateChanged) then + FOnStateChanged(self); end else UpdateSeconds; @@ -105,7 +110,8 @@ procedure TMyTimer.UpdateSeconds; if s <> FSeconds then begin FSeconds:= s; - OnSecondElapsed(self); + if Assigned(FOnSecondElapsed) then + FOnSecondElapsed(self); end; end; @@ -123,8 +129,12 @@ constructor TMyTimer.Create; FTimer.Enabled:= False; FTimer.Interval:= 160; FTimer.OnTimer:= @OnTimer; + FSeconds:= 10 * 60; + FSecondsInitial:= FSeconds; + FState:= Ready; FMode:= Timer; - FState:= Stopped; + FOnSecondElapsed:= nil; + FOnStateChanged:= nil; end; destructor TMyTimer.Free; @@ -135,49 +145,61 @@ destructor TMyTimer.Free; procedure TMyTimer.Start; begin - if State <> Stopped then - Raise Exception.Create('Timer is not in `Stopped` state'); + if State <> Ready then + Raise Exception.Create('Timer is not in `Ready` state'); FState:= Running; UpdateTime; + if Assigned(FOnStateChanged) then + FOnStateChanged(self); FTimer.Enabled:= True; end; procedure TMyTimer.Pause; begin if State <> Running then - Raise Exception.Create('Timer is not in `Running` state'); + Raise Exception.Create('Timer is not in `Running` state'); FState:= Paused; + if Assigned(FOnStateChanged) then + FOnStateChanged(self); FTimer.Enabled:= False; end; procedure TMyTimer.Resume; begin if State <> Paused then - Raise Exception.Create('Timer is not in `Paused` state'); + Raise Exception.Create('Timer is not in `Paused` state'); FState:= Running; UpdateTime; + if Assigned(FOnStateChanged) then + FOnStateChanged(self); FTimer.Enabled:= True; end; -procedure TMyTimer.StartPauseResume; +procedure TMyTimer.Toggle; begin case State of + Ready : Start; Running : Pause; - Stopped : Start; + Finished : Reset; Paused : Resume; end; end; -procedure TMyTimer.Stop; +procedure TMyTimer.Reset; +var PrevState: TState; begin + PrevState:= FState; FTimer.Enabled:= False; - FState:= Stopped; - // TODO reset FTime, FSeconds?? - // Call OnFinished? + FState:= Ready; + FSeconds:= FSecondsInitial; + // We want FOnStateChanged to be called even if we are in `Ready` state. + if Assigned(FOnStateChanged) {and (PrevState <> Ready)} then + FOnStateChanged(self); end; + end. diff --git a/model/utils.pas b/model/utils.pas index eb00194..1a0812d 100644 --- a/model/utils.pas +++ b/model/utils.pas @@ -11,12 +11,14 @@ TUtils = class class function GetFilePath(Path: string): string; class procedure RunApp(Path: string); class function IsInteger(S: String): boolean; + class procedure PlayAudio(Path: string; Loop: boolean); + class procedure StopAudio; end; implementation uses - Classes, SysUtils, StrUtils, Forms, Windows; + Classes, SysUtils, StrUtils, Forms, Windows, MMSystem; class function TUtils.SecondsToTime(Seconds: integer; HideSeconds: Boolean): string; var @@ -60,5 +62,20 @@ class function TUtils.IsInteger(S: String): boolean; end; end; + +class procedure TUtils.PlayAudio(Path: string; Loop: boolean); +begin + if Loop then + sndPlaySound(PChar(GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC or SND_LOOP) + else + sndPlaySound(PChar(GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC); +end; + +class procedure TUtils.StopAudio; +begin + sndPlaySound(nil, 0); +end; + + end. diff --git a/snaptimer.lps b/snaptimer.lps index 504b6a7..7884853 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,13 +4,13 @@ - + - + @@ -19,7 +19,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -39,10 +39,10 @@ - - - - + + + + @@ -54,51 +54,59 @@ - - - + + + - - - + + + - - - + + + + + + + + + + + - - + + - - + + - - + + @@ -106,131 +114,123 @@ - - + + - - - - - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - - - - - - @@ -247,119 +247,135 @@ + + + + + + + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - + + - + - + - + - + - + - + - + - + + + + + + + + + diff --git a/ui/mainform1.lfm b/ui/mainform1.lfm index cc89aa5..e60bcfe 100644 --- a/ui/mainform1.lfm +++ b/ui/mainform1.lfm @@ -1,7 +1,7 @@ object MainForm: TMainForm - Left = 533 + Left = 701 Height = 242 - Top = 312 + Top = 305 Width = 261 HorzScrollBar.Page = 274 HorzScrollBar.Range = 275 @@ -9,7 +9,7 @@ object MainForm: TMainForm VertScrollBar.Page = 137 VertScrollBar.Range = 26 VertScrollBar.Visible = False - ActiveControl = Minutes + ActiveControl = TimeEdit Caption = 'SnapTimer' ClientHeight = 222 ClientWidth = 261 @@ -26,7 +26,7 @@ object MainForm: TMainForm Position = poScreenCenter LCLVersion = '2.0.10.0' object TimeLabel: TLabel - AnchorSideRight.Control = Minutes + AnchorSideRight.Control = TimeEdit Left = 7 Height = 16 Top = 5 @@ -35,7 +35,7 @@ object MainForm: TMainForm BorderSpacing.Left = 5 BorderSpacing.Right = 2 Caption = 'Minutes:' - FocusControl = Minutes + FocusControl = TimeEdit Layout = tlCenter ParentColor = False ParentFont = False @@ -181,7 +181,7 @@ object MainForm: TMainForm Left = 112 Height = 25 Hint = 'Pause' - Top = 2 + Top = 0 Width = 25 OnClick = ToggleCountdown ParentShowHint = False @@ -434,7 +434,7 @@ object MainForm: TMainForm } Visible = False end - object Minutes: TSpinEdit + object TimeEdit: TSpinEdit AnchorSideLeft.Side = asrBottom Left = 59 Height = 23 @@ -443,7 +443,7 @@ object MainForm: TMainForm BorderSpacing.Right = 5 Increment = 5 MaxValue = 5999 - OnChange = MinutesChanged + OnChange = TimeEditChanged ParentFont = False TabOrder = 0 end @@ -472,13 +472,6 @@ object MainForm: TMainForm TabOrder = 1 Transparent = False end - object Timer1: TTimer - Enabled = False - OnTimer = Countdown - OnStartTimer = Countdown - Left = 192 - Top = 160 - end object TrayIconMain: TTrayIcon BalloonFlags = bfInfo PopUpMenu = TrayMenu diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 0be36fa..08ffc1c 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -6,12 +6,10 @@ interface uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, - Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, Options, MMSystem, - Windows, DateUtils, MyTimer; + Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, Options, Windows, DateUtils, + MyTimer; type - TMode = (Timer, Stopwatch); - { TMainForm } TMainForm = class(TForm) ImgIconMain: TImage; @@ -44,46 +42,39 @@ TMainForm = class(TForm) MenuEdit: TMenuItem; TrayMenu: TPopupMenu; TimeLabel: TLabel; - Minutes: TSpinEdit; + TimeEdit: TSpinEdit; Count: TStaticText; - Timer1: TTimer; TrayIconMain: TTrayIcon; procedure OnCreateForm(Sender: TObject); procedure OnDestroyForm(Sender: TObject); procedure OnShowForm(Sender: TObject); procedure FormActivate(Sender: TObject); - procedure ToggleCompact(Sender: TObject); - procedure MinutesChanged(Sender: TObject); - procedure ShowAbout(Sender: TObject); + procedure FormWindowStateChange(Sender: TObject); + procedure FormKeyPress(Sender: TObject; var Key: char); procedure CloseApp(Sender: TObject); - procedure Countdown(Sender: TObject); - procedure ResetCountdown(Sender: TObject); - procedure DisableTimer(); - procedure EnableTimer(); - procedure SetTimer(); - procedure ShowOptions(Sender: TObject); + procedure ToggleCountdown(Sender: TObject); - procedure FormKeyPress(Sender: TObject; var Key: char); - procedure FormWindowStateChange(Sender: TObject); + procedure ResetCountdown(Sender: TObject); + + procedure OnTick(Sender: TObject); + procedure OnTimerStateChanged(Sender: TObject); + procedure UpdateButtonsAndMenus; procedure UpdateTime(); + + procedure ToggleCompact(Sender: TObject); + procedure TimeEditChanged(Sender: TObject); + procedure ShowAbout(Sender: TObject); + procedure ShowOptions(Sender: TObject); procedure SaveSettings(Sender: TObject); - procedure PlayAudio(Path: string; Loop: boolean); - procedure StopAudio(); procedure ShowDoneMessage(Msg: string); procedure ShowTrayMessage(Msg: string); procedure SetFieldsVisible(showFields: boolean); procedure PlayTicking(); private - { private declarations } MyTimer : TMyTimer; - AudioPlaying: boolean; - Mode: TMode; FontSizeChanged : boolean; //FormerWidth : integer; //FormerHeight : integer; - EndTime: TDateTime; - StartTime: TDateTime; - CountdownDone: boolean; OnShowFormFirstTime: Boolean; procedure ApplyConfig; public @@ -133,6 +124,8 @@ procedure TMainForm.OnCreateForm(Sender: TObject); Config: TConfig; begin MyTimer:= TMyTimer.Create; + MyTimer.OnSecondElapsed:= @OnTick; + MyTimer.OnStateChanged:= @OnTimerStateChanged; Config:= GetConfig; MenuFile.Caption := MENU_FILE; MenuToggle.Caption := BTN_START; @@ -151,12 +144,8 @@ procedure TMainForm.OnCreateForm(Sender: TObject); // Set all defaults, then load from .ini - Mode := Timer; - AudioPlaying := False; Self.DoubleBuffered := True; Count.DoubleBuffered := True; - Timer1.Interval := 150; - CountdownDone := False; OnShowFormFirstTime:= True; //AppName := ExtractFileName(Application.ExeName); @@ -183,16 +172,15 @@ procedure TMainForm.OnCreateForm(Sender: TObject); Exit; end; - // Use minutes argument if it's the only parameter and it's numeric + // Use TimeEdit argument if it's the only parameter and it's numeric if (ParamCount = 1) and (TUtils.IsInteger(ParamStr(1))) then - Minutes.Value := StrToInt(ParamStr(1)); + TimeEdit.Value := StrToInt(ParamStr(1)); - //UpdateTimeCaption(SecondsMode); ApplyConfig will do this - ResetCountdown(Sender); if Config.AutoStart then ToggleCountdown(Sender); end; + procedure TMainForm.OnDestroyForm(Sender: TObject); begin if GetConfig.AutoSave then @@ -205,9 +193,9 @@ procedure TMainForm.OnShowForm(Sender: TObject); begin OnShowFormFirstTime:= False; ApplyConfig; - // ApplyConfig does not set MainForm dimensions and - Minutes.Value:= GetConfig.Minutes; - // TODO count value? + // ApplyConfig does not set MainForm dimensions and TimeEdit(TSpinEdit) + TimeEdit.Value:= GetConfig.Minutes; + MyTimer.Reset; // This will trigger OnTimerStateChanged end; // TODO Get this working for window size (getpreferred size gets form, not window) @@ -246,8 +234,6 @@ procedure TMainForm.OnShowForm(Sender: TObject); //ShowMessageFmt('width: %d, height: %d', [w, h]); //exit; end; - - //UpdateAlwaysOnTop(AlwaysOnTop); end; procedure TMainForm.FormActivate(Sender: TObject); @@ -257,49 +243,157 @@ procedure TMainForm.FormActivate(Sender: TObject); Show; end; +procedure TMainForm.FormWindowStateChange(Sender: TObject); +begin + if not GetConfig.MinToTray then + Exit; + + if WindowState = wsMinimized then + begin + WindowState := wsNormal; + ShowInTaskBar := stNever; + Hide; + end; +end; + +procedure TMainForm.FormKeyPress(Sender: TObject; var Key: char); +begin + // TMainForm's KeyPreview property must be set to True for this to work. + if key = #27 then + Close; // 27 = Escape + if (TimeEdit.Focused) and (key = #13) then + ToggleCountdown(Sender); +end; + +procedure TMainForm.CloseApp(Sender: TObject); +begin + OnDestroyForm(Sender); + Close; +end; + + +procedure TMainForm.ToggleCountdown(Sender: TObject); +begin + if (Sender.ClassName = Count.ClassName) and (not GetConfig.ClickTime) then + Exit; + + // We want to go from `Finished` state directly to `Running` state. (or not?) + if MyTimer.State = TState.Finished then + MyTimer.Reset; + + MyTimer.Toggle; +end; + procedure TMainForm.ResetCountdown(Sender: TObject); begin if (Sender.ClassName = Count.ClassName) and (not GetConfig.DblClickTime) then Exit; - // Turn off both looping audio and ticking - StopAudio(); - DisableTimer(); - SetTimer(); - TrayIconMain.Icon := ImgIconMain.Picture.Icon; + + MyTimer.Reset; end; -procedure TMainForm.DisableTimer(); +procedure TMainForm.OnTick(Sender: TObject); begin - Timer1.Enabled := False; - ImgPause.Visible := False; - ImgStart.Visible := True; - MenuToggle.Caption := BTN_START; - TrayMenuToggle.Caption := BTN_START; - StopAudio(); + UpdateTime(); +end; + +procedure TMainForm.OnTimerStateChanged(Sender: TObject); +var Config: TConfig; +begin + Config:= GetConfig; + + case MyTimer.State of + Ready: + begin + if Config.SecondsMode then + MyTimer.Seconds := TimeEdit.Value + else + MyTimer.Minutes:= TimeEdit.Value; + UpdateTime(); + UpdateButtonsAndMenus; + TrayIconMain.Icon:= ImgIconMain.Picture.Icon; + end; + + Running: + begin + UpdateButtonsAndMenus; + TrayIconMain.Icon:= ImgIconRunning.Picture.Icon; + if Config.TickingOn then + TUtils.PlayAudio('.\sounds\ticking\ticking.wav', True); + end; + + Paused: + begin + UpdateButtonsAndMenus; + TrayIconMain.Icon:= ImgIconPaused.Picture.Icon; + TUtils.StopAudio; + end; + + Finished: + begin + UpdateTime(); + UpdateButtonsAndMenus; + TUtils.StopAudio; + + Application.Title := APP_NAME; + if Config.DoneAudioEnabled then + TUtils.PlayAudio(Config.DoneAudio, Config.LoopAudio); + if Config.DoneAppEnabled then + TUtils.RunApp(Config.DoneApp); + if Config.DoneTrayMsgEnabled then + ShowTrayMessage(Config.DoneTrayMsg); + if Config.DoneMessageEnabled then + ShowDoneMessage(Config.DoneMessage); + if Config.AutoRestart then + ToggleCountdown(Sender) + else + TrayIconMain.Icon := ImgIconDone.Picture.Icon; + end; + end; end; -procedure TMainForm.EnableTimer(); +// When it comes to buttons and menus, there are only two states. +procedure TMainForm.UpdateButtonsAndMenus; begin - Timer1.Enabled := True; - ImgPause.Visible := True; - ImgStart.Visible := False; - MenuToggle.Caption := BTN_PAUSE; - TrayMenuToggle.Caption := BTN_PAUSE; - TrayIconMain.Icon := ImgIconRunning.Picture.Icon; - - if GetConfig.TickingOn then - PlayTicking(); + if MyTimer.State = TState.Running then + begin + ImgPause.Visible := True; + ImgStart.Visible := False; + MenuToggle.Caption := BTN_PAUSE; + TrayMenuToggle.Caption := BTN_PAUSE; + end + else + begin + ImgPause.Visible := False; + ImgStart.Visible := True; + MenuToggle.Caption := BTN_START; + TrayMenuToggle.Caption := BTN_START; + end; end; -procedure TMainForm.SetTimer(); +// TODO Animate icon between alarm icon and main so it blinks every second +// Instead of playing icon, animate the clock hands so they go around? +// http://delphi.about.com/od/kbwinshell/l/aa122501a.htm +procedure TMainForm.UpdateTime(); begin - if GetConfig.SecondsMode then - Timer1.Tag := Minutes.Value + // Changing caption while it's not visible keeps time from flashing + Count.Visible := False; + Count.Caption := TUtils.SecondsToTime(MyTimer.Seconds, GetConfig.HideSeconds); + Count.Visible := True; + MenuCount.Caption := Count.Caption; + if MyTimer.State = TState.Running then + begin + Application.Title := Count.Caption + ' - ' + APP_NAME; + TrayIconMain.Hint := Count.Caption; + end else - Timer1.Tag := Minutes.Value * 60; - UpdateTime(); + begin + Application.Title := APP_NAME; + TrayIconMain.Hint := APP_NAME; + end; end; + procedure TMainForm.ShowOptions(Sender: TObject); var Config: TConfig; @@ -330,12 +424,12 @@ procedure TMainForm.ShowOptions(Sender: TObject); // TODO Get font colors and background color to be shown - what's up? //if FontSizeChanged then OnShowForm(Sender); - if Timer1.Enabled then + if MyTimer.State = TState.Running then begin if Config.TickingOn then PlayTicking else - StopAudio(); + TUtils.StopAudio; end; if Config.AutoSave then @@ -346,83 +440,6 @@ procedure TMainForm.ShowOptions(Sender: TObject); end; end; -procedure TMainForm.ToggleCountdown(Sender: TObject); -begin - if (Sender.ClassName = Count.ClassName) and (not GetConfig.ClickTime) then - Exit; - - if Minutes.Value = 0 then - begin - Mode := Stopwatch; - StartTime := Now - (Timer1.Tag * OneSecond); - end - else - begin - Mode := Timer; - EndTime := Now + (Timer1.Tag * OneSecond); - end; - - if Timer1.Enabled then - begin - TrayIconMain.Icon := ImgIconPaused.Picture.Icon; - DisableTimer(); - end - else - begin - if CountdownDone then - begin - // TODO When restarting after countdown has elapsed, quickly jumps from - // starting number to next one - fix? - CountdownDone := False; - ResetCountdown(Sender); - EndTime := Now + (Timer1.Tag * OneSecond); - end; - EnableTimer(); - end -end; - -procedure TMainForm.Countdown(Sender: TObject); -var Config: TConfig; -begin - Config:= GetConfig; - if MODE = Stopwatch then - begin - Timer1.Tag := SecondsBetween(StartTime, Now); - UpdateTime(); - end - else - begin - if EndTime <= Now then - begin - Timer1.Tag := 0; - UpdateTime(); - DisableTimer(); - CountdownDone := True; - Application.Title := APP_NAME; - if Config.DoneAudioEnabled then - PlayAudio(Config.DoneAudio, Config.LoopAudio); - if Config.DoneAppEnabled then - TUtils.RunApp(Config.DoneApp); - if Config.DoneTrayMsgEnabled then - ShowTrayMessage(Config.DoneTrayMsg); - if Config.DoneMessageEnabled then - ShowDoneMessage(Config.DoneMessage); - - if Config.AutoRestart then - ToggleCountdown(Sender) - else - TrayIconMain.Icon := ImgIconDone.Picture.Icon; - end - else - UpdateTime(); - Timer1.Tag := SecondsBetween(EndTime, Now); - end; -end; - -{procedure TMainForm.Countdown(Sender: TObject); -var Config: TConfig; -begin -end;} procedure TMainForm.ShowAbout(Sender: TObject); var @@ -434,7 +451,6 @@ procedure TMainForm.ShowAbout(Sender: TObject); end; - procedure TMainForm.SaveSettings(Sender: TObject); var Config: TConfig; @@ -443,7 +459,7 @@ procedure TMainForm.SaveSettings(Sender: TObject); Config:= GetConfig; // These config values are not set in OptionsForm. - Config.Minutes:= Minutes.Value; + Config.Minutes:= TimeEdit.Value; GetWindowRect(self.Handle, wRect); Config.WndWidth:= wRect.Right - wRect.Left; Config.WndHeight:= wRect.Bottom - wRect.Top; @@ -454,9 +470,9 @@ procedure TMainForm.SaveSettings(Sender: TObject); MessageDlg(MSG_WRITE_INI, mtError, [mbOK], 0); end; -procedure TMainForm.MinutesChanged(Sender: TObject); +procedure TMainForm.TimeEditChanged(Sender: TObject); begin - if not Timer1.Enabled then + if (MyTimer.State <> TState.Running) and (MyTimer.State <> TState.Paused) then ResetCountdown(Sender); end; @@ -467,104 +483,6 @@ procedure TMainForm.ToggleCompact(Sender: TObject); // CompactMode := not CompactMode; end; - // TODO Figure out how to remove the fields from the form so they don't - // take up space anymore (and count can expand to top) - // Easier to create a new form, hide this one and show the other one - // Set border style to none too. -procedure TMainForm.SetFieldsVisible(showFields: boolean); -begin - if showFields then - begin - MainForm.Menu := MainMenu1; - end - else - begin - MainForm.Menu := nil; - TimeLabel.Visible := showFields; - Minutes.Visible := showFields; - ImgStart.Visible := showFields; - ImgReset.Visible := showFields; - ImgPause.Visible := showFields; - end; -end; - -procedure TMainForm.CloseApp(Sender: TObject); -begin - OnDestroyForm(Sender); - Close; -end; - - -procedure TMainForm.FormKeyPress(Sender: TObject; var Key: char); -begin - // TMainForm's KeyPreview property must be set to True for this to work. - if key = #27 then - Close; // 27 = Escape - if (Minutes.Focused) and (key = #13) then - ToggleCountdown(Sender); -end; - - // TODO Animate icon between alarm icon and main so it blinks every second - // Instead of playing icon, animate the clock hands so they go around? - // http://delphi.about.com/od/kbwinshell/l/aa122501a.htm -procedure TMainForm.UpdateTime(); -begin - // Changing caption while it's not visible keeps time from flashing - Count.Visible := False; - Count.Caption := TUtils.SecondsToTime(Timer1.Tag, GetConfig.HideSeconds); - Count.Visible := True; - MenuCount.Caption := Count.Caption; - if Timer1.Enabled then - begin - Application.Title := Count.Caption + ' - ' + APP_NAME; - TrayIconMain.Hint := Count.Caption; - end - else - begin - Application.Title := APP_NAME; - TrayIconMain.Hint := APP_NAME; - end; -end; - -procedure TMainForm.FormWindowStateChange(Sender: TObject); -begin - if not GetConfig.MinToTray then - Exit; - - if WindowState = wsMinimized then - begin - WindowState := wsNormal; - ShowInTaskBar := stNever; - Hide; - end; -end; - - - -procedure TMainForm.PlayAudio(Path: string; Loop: boolean); -begin - if AudioPlaying then - Exit; - if Loop then - begin - AudioPlaying := True; - sndPlaySound(PChar(TUtils.GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC or SND_LOOP); - end - else - sndPlaySound(PChar(TUtils.GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC); -end; - -procedure TMainForm.StopAudio(); -begin - sndPlaySound(nil, 0); - AudioPlaying := False; -end; - -procedure TMainForm.PlayTicking(); -begin - sndPlaySound(PChar(TUtils.GetFilePath('.\sounds\ticking\ticking.wav')), SND_NODEFAULT or - SND_ASYNC or SND_LOOP); -end; procedure TMainForm.ShowTrayMessage(Msg: string); begin @@ -581,9 +499,38 @@ procedure TMainForm.ShowDoneMessage(Msg: string); // http://msdn.microsoft.com/en-us/library/ms645505(VS.85).aspx Windows.MessageBox(self.Handle, pChar(msg), 'Done', MB_SYSTEMMODAL or MB_SETFOREGROUND or MB_TOPMOST or MB_ICONINFORMATION); - StopAudio(); + TUtils.StopAudio; +end; + +// TODO Figure out how to remove the fields from the form so they don't +// take up space anymore (and count can expand to top) +// Easier to create a new form, hide this one and show the other one +// Set border style to none too. +procedure TMainForm.SetFieldsVisible(showFields: boolean); +begin + if showFields then + begin + MainForm.Menu := MainMenu1; + end + else + begin + MainForm.Menu := nil; + TimeLabel.Visible := showFields; + TimeEdit.Visible := showFields; + ImgStart.Visible := showFields; + ImgReset.Visible := showFields; + ImgPause.Visible := showFields; + end; end; + + +procedure TMainForm.PlayTicking(); +begin + TUtils.PlayAudio('.\sounds\ticking\ticking.wav', True); +end; + + procedure TMainForm.ApplyConfig; var Config: TConfig; @@ -592,14 +539,14 @@ procedure TMainForm.ApplyConfig; begin Config:= GetConfig; if Config.AlwaysOnTop then - FormStyle:= fsSystemStayOnTop + FormStyle:= fsSystemStayOnTop else - FormStyle:= fsNormal; + FormStyle:= fsNormal; if Config.SecondsMode then - TimeLabel.Caption := LBL_SECONDS + TimeLabel.Caption := LBL_SECONDS else - TimeLabel.Caption := LBL_MINUTES; + TimeLabel.Caption := LBL_MINUTES; Count.Font.Quality := fqAntialiased; Count.Font.Name := Config.Font.Name; diff --git a/ui/options.pas b/ui/options.pas index a08d236..92258d7 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -312,8 +312,8 @@ procedure TOptionsForm.SetFontDefaults(Sender: TObject); procedure TOptionsForm.TestAudio(Sender: TObject); begin -with Self.Owner as TMainForm do - PlayAudio(NotifyAudio.Text, False); + with Self.Owner as TMainForm do + TUtils.PlayAudio(NotifyAudio.Text, False); end; procedure TOptionsForm.TestMessage(Sender: TObject); From 7ba843dc4fa05179c87998d3cf2cb2c0e6131236 Mon Sep 17 00:00:00 2001 From: neo85 Date: Thu, 18 Feb 2021 18:29:08 +0100 Subject: [PATCH 08/21] minor fixes --- snaptimer.lps | 145 ++++++++++++++++++++++++++--------------------- ui/mainform1.pas | 8 +-- 2 files changed, 84 insertions(+), 69 deletions(-) diff --git a/snaptimer.lps b/snaptimer.lps index 7884853..6dfeb25 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,7 +4,7 @@ - + @@ -40,8 +40,8 @@ - - + + @@ -63,7 +63,7 @@ - + @@ -78,10 +78,9 @@ - + - @@ -147,8 +146,8 @@ - - + + @@ -161,8 +160,8 @@ - - + + @@ -226,11 +225,10 @@ - + - @@ -254,128 +252,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - - - - - - - - - - - - diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 08ffc1c..c94348d 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -319,7 +319,7 @@ procedure TMainForm.OnTimerStateChanged(Sender: TObject); UpdateButtonsAndMenus; TrayIconMain.Icon:= ImgIconRunning.Picture.Icon; if Config.TickingOn then - TUtils.PlayAudio('.\sounds\ticking\ticking.wav', True); + PlayTicking(); end; Paused: @@ -345,9 +345,9 @@ procedure TMainForm.OnTimerStateChanged(Sender: TObject); if Config.DoneMessageEnabled then ShowDoneMessage(Config.DoneMessage); if Config.AutoRestart then - ToggleCountdown(Sender) - else - TrayIconMain.Icon := ImgIconDone.Picture.Icon; + ToggleCountdown(Sender) + else + TrayIconMain.Icon := ImgIconDone.Picture.Icon; end; end; end; From e896b63b9bbba8b554947ab09f543beff04c5e85 Mon Sep 17 00:00:00 2001 From: neo85 Date: Wed, 7 Apr 2021 10:51:29 +0200 Subject: [PATCH 09/21] MsgBox as Form Message box that is shown when timer has finished is now a Form instead of Windows.MessageBox. --- msgbox.lfm | 624 +++++++++++++++++++++++++++++++++++++++++++++++ snaptimer.lpi | 9 +- snaptimer.lps | 30 ++- ui/mainform1.pas | 10 +- ui/msgbox.lfm | 624 +++++++++++++++++++++++++++++++++++++++++++++++ ui/msgbox.pas | 59 +++++ 6 files changed, 1345 insertions(+), 11 deletions(-) create mode 100644 msgbox.lfm create mode 100644 ui/msgbox.lfm create mode 100644 ui/msgbox.pas diff --git a/msgbox.lfm b/msgbox.lfm new file mode 100644 index 0000000..77f6133 --- /dev/null +++ b/msgbox.lfm @@ -0,0 +1,624 @@ +object MsgBoxForm: TMsgBoxForm + Left = 819 + Height = 136 + Top = 314 + Width = 162 + BorderStyle = bsDialog + Caption = 'Done' + ClientHeight = 136 + ClientWidth = 162 + FormStyle = fsSystemStayOnTop + Icon.Data = { + 662500000000010004002020000001001800A80C000046000000101000000100 + 180068030000EE0C00002020000001002000A810000056100000101000000100 + 200068040000FE2000002800000020000000400000000100180000000000000C + 0000640000006400000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000B1864DB2782BB27019B2 + 7019B4711CB27019B1711DB2782BB1864D000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000B39C7EB1711DB77521C48032CC883CD18D42D1 + 8D42D18D42D18D42D18D42CC883CC48032B77521B1711DB39C7E000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000B1762ABA7724CC883CD59149DC9B4DDD9C4EE2A251E2 + A251E2A251E2A251E2A251DD9C4EDB9B4DD59149CC883CBA7724B1762A000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000B27019C68436D59149DD9C4EE1A75AE3CEA3E7EEE0E9FFFFE9 + FFFFE9FFFFE9FFFFE9FFFFE7EEE0E3CEA3E1A75ADD9C4ED59149C68436B27019 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000B4711CD29149D9974EE09E4DE2D3B0E5FFFFE5FFFFE5FFFFE5FFFFE5 + FFFFE9FFFFE5FFFFE5FFFFE5FFFFE5FFFFE5FFFFE2D3B0E09E4DD9974ED29149 + B4711C0000000000000000000000000000000000000000000000000000000000 + 00B27019D29149DB9A51DEA155E2F3ECE5FFFFE5FFFFE5FFFFE1FEFEE5FFFFE5 + FFFF99A2A1E5FFFFE5FFFFE1FEFEE5FFFFE5FFFFE5FFFFE2F3ECDEA155DB9A51 + D29149B27019000000000000000000000000000000000000000000000000B176 + 2AC9883CDC9D51DEA155E1FEFEE1FEFEE1FEFE949D9CE1FEFEDDFAFCE1FEFEE5 + FFFF99A2A1E5FFFFE1FEFEDDFAFCE1FEFE949D9CE1FEFEE1FEFEE1FEFEDEA155 + DC9D51C9883CB2782B000000000000000000000000000000000000BDA587BC7A + 29DC9C59DD9949DEF1EBDDFDFEDDFDFEDDFDFEE1FEFEE1FEFEDDFAFCDDFAFCE1 + FEFE949D9CE5FFFFDDFAFCDDFAFBDDFDFEE1FEFEDDFDFEDDFDFEDDFDFEDEF1EB + DD9949DC9C59BC7A29BDA587000000000000000000000000000000B1711DD393 + 4DDC9D51DCCFABDDFDFEDDFDFEDDFAFCD9F9FCD9F8FBD9F8FBD9F8FBD9F9FCDD + FAFCDDFDFEDDFAFCD9F8FBD9F8FBD9F8FBD9F9FCD9F8FBDDFAFCDDFDFEDAFDFF + DCCFABDC9D51D3934DB1711D000000000000000000000000000000B97825E0A1 + 5EDC9D51DAFDFFDAFDFF8E9798DAFDFFD8F7FAD8F7FAD9F8FBD9F8FBD9F8FBD7 + F8FBD9F8FBD8F7FAD8F7FAD8F7FAD8F7FAD8F7FAD8F7FADAFDFF8E9798DAFDFF + DAFDFFDC9D51E0A15EB97825000000000000000000000000B78C53CD8D44DF9E + 55D8C59CD6FEFFD6F9FDD9F9FCD7F8FBD5F6F9D5F6F9D9F9FCDAFDFFDDFDFEDA + FDFFD8F7FAD5F6F9D5F6F9D5F6F9D8F7FAD5F6F9D5F6F9D7F8FBD9F9FCD6F9FD + D6FEFFD8C59CDF9E55CD8D44B78C53000000000000000000B2782BD99E5ADD99 + 49D5E4D8D2F9FED5F6F9D5F6F9D5F6F9D2F4F8D5F6F9DAFDFF858D8E828A8BDD + FDFED6F9FDD5F6F9D2F4F8D2F4F8D2F4F8D5F6F9D5F6F9D5F6F9D2F4F8D5F6F9 + D2F9FED5E4D8DD9949D99E5AB2782B000000000000000000B16F18E7AC6FDA94 + 41D0FDFFD2F9FED2F4F8D1F6FCD2F4F8D2F4F8D2F4F8D6FEFF8991904F47457D + 898ADDFDFED6F9FDD5F6F9D2F4F8D2F4F8D2F4F8D2F4F8D2F4F8D2F4F8D2F4F8 + D1F6FCD0FDFFDA9441E7AC6FB16F18000000000000000000B16F18E8AE72D991 + 3DCFFCFFCFF5FCD1F6FCD6F9FDD1F6FCD2F4F8CEF3F8D1F6FCD6FEFF858D8E43 + 3D3B748080DAFDFFD1F6FCCEF3F8CEF3F8CEF3F8D2F4F8D1F6FCD2F9FED2F9FE + CFF5FCCDFAFEDA9441E8AE72B06D16000000000000000000B06D16E9B076D991 + 3DCDFAFECFF5FC849092859394839091CFF5FCCDF1F6CEF3F8D1F6FCDAFDFF81 + 8D8E3731316E787AD2F9FECEF3F8CDF1F6CDF1F6CDF5FA839091859394839091 + D1F6FCCAF9FFD9913DE9B076B06D16000000000000000000B06D16EBB279D68D + 3AD0FDFFCAF4F9CDF5FACFF5FCCDF5FAC9F2F9C8F0F5C9F2F9CDF5FAD6FEFF7A + 86883C3434707B7DD2F9FEC8F0F5C8F0F5C8F0F5C9F2F9CDF5FACDF5FACDF5FA + CAF4F9D0FDFFD68D3AEBB279B06D16000000000000000000B06D16EDB77ED68D + 3ACFFCFFC6F1F7C8F0F5C8F0F5C8F0F5C8F0F5C8F0F5CAF4F9D0FDFF78848541 + 3838788485D0FDFFCAF4F9C8F0F5C8F0F5C8F0F5C8F0F5C8F0F5C8F0F5C8F0F5 + C6F1F7CFFCFFD68D3AEDB77EB06D16000000000000000000B5792CE4AB6DDA93 + 44CFFCFFC4EFF8C5EDF4C5EDF4C5EDF4C8F0F5C9F2F9CFFCFF7A8688483F3D7A + 8688D0FDFFC9F2F9C8F0F5C5EDF4C5EDF4C5EDF4C5EDF4C5EDF4C5EDF4C5EDF4 + C5F1F8CFFCFFDA9344E4AB6DB5792C000000000000000000C0955CD49954E19E + 58D2D5C4BEF2FDC4EFF8C3F0F7C4EFF8C5F1F8CAF9FF7D898A4E46437D898ACD + FAFEC5F1F8C1EDF5C2ECF3C2ECF3C5EDF4C2ECF3C2ECF3C3F0F7C3F0F7C3F0F7 + C1F0F9D2D5C4E19E58D49954C0955C000000000000000000000000BC7A29F0B5 + 7ACFB082C4F0FFC3F0F7758385C5F1F8CAF9FF818D8E554C4A818D8ECAF9FFC3 + F0F7C1EDF5C0EBF2C2ECF3C0EBF2C0EBF2C0EBF2C0EBF2C3F0F7758385C3F0F7 + C4F0FFCFB082F0B57ABC7A29000000000000000000000000000000B1711DE4AB + 6DE09B51C4D9D8BFECF9C1F0F9C1F0F98390915C524F849092C5F7FFC3F0F7BE + ECF3C1EDF5BEECF3BCEAF1BCEAF1BCEAF1BCEAF1BEECF3BEECF3BEEDF6BFECF9 + C4D9D8E09B51E4AB6DB1711D000000000000000000000000000000000000C181 + 33F2BA82D19B5AB9E4FABBECF5C1F0F9839091859394C5F7FFBEEDF6BEECF3BF + ECF9728386BFECF9BCEAF1BCEAF1BEECF3BEEDF6BEECF3BBECF5BBECF5B9E4FA + D19B5AF2BA82C18133000000000000000000000000000000000000000000B579 + 2CD99E5AEAAD6CC6A67AB0DEF5B8E7F4BEF2FD718588BEEDF6BAEAF4B9E8F1BE + EDF6718588BEEDF6B8E7F4B7E8F1BAEAF46D7E81BBECF5B6E4F1B0DEF5C6A67A + EDB071D99E5AB5792C0000000000000000000000000000000000000000000000 + 00AF6C14EEB880EAAD6CC59E6AACD3E6AEDDF0B7E8F5BAEAF4B7E8F1B7E8F1BB + ECF56E8082BBECF5B7E8F1B7E8F1BAEAF4B6E7F4AEDDF0ACD3E6C59E6AEAAD6C + EEB880AF6C140000000000000000000000000000000000000000000000000000 + 00000000B4711CF2BA82EDB071CF914CB5B6A7A2D2EEA6D3E9AADAEAAFE0EEB2 + E4F0B6E4F1B2E4F0AFE0EEAADAEAA6D3E9A2D2EEB5B6A7CF914CF0B57AEEB880 + B4711C0000000000000000000000000000000000000000000000000000000000 + 00000000000000AF6C14DCA362F9C692E39F57CA8B44B2AE99A4BFC899CDEC9A + CCEB9ACCEB9ACCEB99CDECA4BFC8B2AE99CA8B44E39F57F9C692DBA362AF6C13 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000B5792CC38437EEB880FCC996E6A561DA9142D0822CD0 + 822CD0822CD0822CD0822CDA9142E6A561FCC996EEB880C38437B5792C000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000B1711DBE7F2EDEA767F2C18DFFD5A8FF + D5A8FFD5A8FFD5A8FFD5A8F2C18DDEA767BE7F2EB1711D000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000C79B61B87C2FAF6B13AF + 6B13AF6B13AF6B13AF6B13B87C2FC79B61000000000000000000000000000000 + 000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFF0 + 07FFFF8000FFFF00007FFE00003FFC00001FF800000FF0000007E0000003E000 + 0003E0000003C0000001C0000001C0000001C0000001C0000001C0000001C000 + 0001C0000001C0000001E0000003E0000003F0000007F0000007F800000FFC00 + 001FFE00003FFF00007FFFC001FFFFF007FF2800000010000000200000000100 + 1800000000000003000064000000640000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000A2937E96 + 816596816599856C000000000000000000000000000000000000000000000000 + 000000000000A38660C27F2FD18F42D99749D99749D79547C9883AB47E3D0000 + 00000000000000000000000000000000000000AD7E45D79547E1C595E4EEE0E7 + FFFFE7FFFFE6FAF7E3DCBEDDA765C88639000000000000000000000000000000 + A77E4AD9994FE0E4D0CFE6E6E1FCFDE7FFFFBFD0D0E1FCFDCFE6E6E1FCFADEB6 + 7EC9883A000000000000000000000000D19147DCD6B7DCFCFEDCFAFCDBF9FBDC + FAFCCBE4E6DCFAFADCFAFCDCFAFCDCFAFADCA868B98443000000000000B88B52 + DCA868D6FAFDC5E0E3D7F6F9D9F9FCD9F9FCD9F9FCD7F6F9D7F6F9D9F9FCC5E1 + E4D8D6BBD19147000000000000C88A41D6C398D1F5F8D1F5F8D1F5F8AFC5C789 + 9595D6FAFDD1F5F8D1F5F8D1F5F8D1F5F8D2F4F4DD9D56A7967F000000CC8E45 + D2C59DBCDCE1ABC4C7CEF3F8D1F5F88995957C8A8ACDF5FACDF1F6BDDBE0ABC4 + C7CDF8FDE0A059A4927A000000CE9149D2C59DC9F2F8C9F2F8C9F1F6CDF5FA82 + 91927D8C8DC9F2F8C8EFF4C9F1F6C9F2F8CBF7FBE0A059A4927A000000C7904E + D7C097C3EDF5C4EDF5C7F2F9859495829192C7F2F9C2ECF3C2ECF3C4EDF5C4ED + F5C9EBEDDD9D56000000000000B49976E0AB6EC2E9F2AED5DB89999A87989AC2 + F1F8BFECF3BEEAF1BEEAF1BFECF3ADD3DBCDC5AACE9149000000000000000000 + CF934ECEB48EB9E8F68EA6AABEEEF7BCEBF497B8BEBAE8F2A8D0D7BAE8F2BAD4 + D7E2A869B69B78000000000000000000000000DFA463CBAC83AFD3DEAFE0EEB4 + E6F1A3CDD6B1E2EEADDDEFB7BDB2E3AE74C7985D000000000000000000000000 + 000000000000D29854E5AA6CC5A881B5A78CB5A78BB9A789D7A975E2A869C095 + 5E000000000000000000000000000000000000000000000000B99E7ACF9B5BD7 + A05DD7A05DD49D5BCC9E64000000000000000000000000000000FFFF0000FC3F + 0000F00F0000E00700B2C003D291C0019A51800155E28000E5FF8000FFFF8000 + FFE18001E5FF8001FFFFC001A1E5E003E5FFF007FEFEF81FFFE5280000002000 + 0000400000000100200000000000001000006400000064000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000001000000070000000E00000013000000160000 + 0016000000160000001600000016000000130000000E00000007000000010000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000002000000090000001500000024000000320000003C000000410000 + 00430000004300000043000000410000003C0000003200000024000000150000 + 0009000000020000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000600000014000000280000003C6741107C996117C3AA6B19EAB2701AFFB270 + 19FFB27019FFB27019FFB2701AFFAA6B19EA996117C36741107C0000003C0000 + 0028000000140000000600000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000001000000070000 + 001C000000377349128AB06F1AF9B77521FFC48032FFCB883BFFD28D43FFD18D + 42FFD18D42FFD18D42FFD28D43FFCB883BFFC48032FFB77521FFB06F1AF97349 + 128A000000370000001C00000007000000010000000000000000000000000000 + 00000000000000000000000000000000000000000001000000080000001F1910 + 0446AB6C19EDBA7724FFCC883CFFD59048FFDB9B4DFFDD9C4EFFE2A352FFE2A2 + 51FFE2A250FFE2A251FFE2A352FFDD9C4EFFDB9B4DFFD59048FFCC883CFFBA77 + 24FFAB6C19ED191004460000001F000000080000000100000000000000000000 + 000000000000000000000000000000000000000000070000001F3D260956B26F + 19FFC68436FFD6934BFFDC9B4DFFE1A75AFFE3CEA3FFE7EEE0FFE9FFFFFFE9FF + FFFFE9FFFFFFE9FFFFFFE9FFFFFFE7EEE0FFE3CEA3FFE1A75AFFDC9B4DFFD693 + 4BFFC68436FFB26F19FF3D2609560000001F0000000700000000000000000000 + 0000000000000000000000000000000000060000001C3C260953B4711CFFD18F + 47FFD9974EFFE09E4DFFE2D3B0FFE6FFFFFFE5FFFFFFE4FFFFFFE4FFFFFFE6FF + FFFFE7FFFFFFE6FFFFFFE4FFFFFFE4FFFFFFE5FFFFFFE6FFFFFFE2D3B0FFE09E + 4DFFD9974EFFD18F47FFB4711CFF3C2609530000001C00000006000000000000 + 0000000000000000000000000002000000141E130541B16F18FFD29149FFDB99 + 50FFDFA256FFE2F3ECFFE4FFFFFFE5FFFFFFE3FFFFFFE2FDFEFFE3FDFEFFE6FF + FFFF99A1A1FFE6FFFFFFE3FDFEFFE2FDFEFFE3FFFFFFE5FFFFFFE4FFFFFFE2F3 + ECFFDFA256FFDB9950FFD29149FFB16F18FF1E13054100000014000000020000 + 000000000000000000000000000900000028AC6C19ECC9883CFFDB9B52FFDEA1 + 54FFE0FFFFFFE0FFFFFFE1FFFFFF959D9CFFE1FEFFFFDFFBFCFFE0FCFDFFE4FF + FFFF99A3A1FFE4FFFFFFE0FCFDFFDFFBFCFFE1FEFFFF959D9CFFE1FFFFFFE0FF + FFFFE0FFFFFFDEA154FFDB9B52FFC9883CFFAC6C19EC00000028000000090000 + 00000000000000000001000000157D4F1381BC7A28FFDC9C59FFDE9A49FFDEF1 + EBFFDEFFFFFFDEFCFDFFDEFCFDFFDFFDFEFFDEFCFDFFDDFAFBFFDEFBFCFFE1FF + FFFF949E9EFFE1FFFFFFDEFBFCFFDDFAFBFFDEFCFDFFDFFDFEFFDEFCFDFFDEFC + FDFFDEFFFFFFDEF1EBFFDE9A49FFDC9C59FFBC7A28FF7D4F1381000000150000 + 0001000000000000000700000024B06E19F9D3934DFFDD9C50FFDCCFABFFDBFF + FFFFDCFCFFFFDBFAFDFFDAF8FCFFDAF8FCFFDAF8FBFFDAF8FBFFDAF8FBFFDCFA + FDFFDDFBFEFFDCFAFDFFDAF8FBFFDAF8FBFFDAF8FBFFDAF8FCFFDAF8FCFFDBFA + FDFFDCFCFFFFDBFFFFFFDCCFABFFDD9C50FFD3934DFFB06E19F9000000240000 + 0007000000000000000E774B136BB97825FFE0A15EFFDC9E52FFD8FAFFFFDAFD + FFFF8E9798FFDAFAFDFFD8F7FAFFD8F7FAFFD8F7FAFFD9F8FBFFD9F8FBFFD8F8 + FBFFD8F8FBFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFFDAFA + FDFF8E9798FFDAFDFFFFD8FAFFFFDC9E52FFE0A15EFFB97825FF774B136B0000 + 000E00000000000000139E6417BCCD8D44FFDF9E55FFD8C59CFFD6FDFFFFD7F9 + FDFFD8F9FCFFD7F8FBFFD6F6F9FFD6F6F9FFD8F9FCFFDBFDFFFFDCFDFFFFD9FA + FDFFD7F7FAFFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FFD7F8 + FBFFD8F9FCFFD7F9FDFFD6FDFFFFD8C59CFFDF9E55FFCD8D44FF9E6417BC0000 + 00130000000000000016AB6B17E8DA9D5AFFDC984AFFD5E4D8FFD3F9FFFFD3F6 + F9FFD3F5F8FFD3F5F8FFD3F5F8FFD4F6F9FFD8FCFFFF868D8EFF828A8BFFDDFF + FFFFD6FAFDFFD3F6F9FFD3F5F8FFD3F5F8FFD3F5F8FFD3F5F8FFD3F5F8FFD3F5 + F8FFD3F5F8FFD3F6F9FFD3F9FFFFD5E4D8FFDD994BFFDA9D5AFFAB6B17E80000 + 00160000000000000016B16E17FFE7AC6FFFDA9441FFD0FFFFFFD1F7FCFFD2F5 + F9FFD2F5F9FFD2F5F9FFD1F4F8FFD2F5F9FFD6FBFFFF899190FF4F4745FF7C88 + 88FFDDFFFFFFD5F9FDFFD2F5F9FFD1F4F8FFD1F4F8FFD1F4F8FFD1F4F8FFD2F5 + F9FFD2F5F9FFD2F5F9FFD1F7FCFFD0FFFFFFDA9341FFE7AC6FFFB16E17FF0000 + 00160000000000000016B06E16FFE8AE72FFD9923EFFCDFBFFFFCFF5FCFFD2F7 + FCFFD3F8FDFFD2F7FCFFD0F4F9FFCEF3F8FFD1F5FAFFD7FFFFFF848E8EFF433D + 3BFF748080FFDAFFFFFFD1F6FBFFCEF2F7FFCEF2F7FFCEF2F7FFD0F4F9FFD2F7 + FCFFD3F8FDFFD2F7FCFFCFF5FCFFCDFBFFFFD9923EFFE8AE72FFB06E16FF0000 + 00160000000000000016B06D16FFE9B076FFD9903DFFCBFAFFFFCFF6FCFF8390 + 91FF859394FF839091FFCFF4FAFFCCF1F7FFCDF2F7FFD0F6FCFFDAFFFFFF818B + 8DFF373131FF6E787AFFD3FAFFFFCDF2F7FFCCF1F6FFCCF1F6FFCFF4FAFF8390 + 91FF859394FF839091FFCFF6FCFFCBFAFFFFD9903DFFE9B076FFB06D16FF0000 + 00160000000000000016B06D16FFEBB279FFD78E3BFFD0FDFFFFCAF3FAFFCDF5 + FAFFCEF6FBFFCDF5FAFFCAF2F7FFC9F0F5FFC9F1F6FFCDF6FBFFD7FFFFFF7987 + 89FF3C3434FF707B7DFFCFF9FEFFCAF1F6FFC9F0F5FFC9F0F5FFCAF2F7FFCDF5 + FAFFCEF6FBFFCDF5FAFFCAF3FAFFD0FDFFFFD78E3BFFEBB279FFB06D16FF0000 + 00160000000000000013B06D16FFEDB77EFFD68D3AFFCFFCFFFFC6F1F7FFC8F0 + F5FFC8F0F5FFC8F0F5FFC7EFF4FFC8F0F5FFCAF4F9FFD2FDFFFF788485FF4138 + 38FF778385FFD2FEFFFFCAF3F8FFC7EFF4FFC7EFF4FFC7EFF4FFC7EFF4FFC8F0 + F5FFC8F0F5FFC8F0F5FFC6F1F7FFCFFCFFFFD68D3AFFEDB77EFFB06D16FF0000 + 0013000000000000000EAC6B16E6E3AA6CFFDA9344FFD0FBFFFFC4EFF8FFC5ED + F5FFC5EDF5FFC5EDF4FFC6EEF5FFC8F1F9FFCFFBFFFF7B8688FF483F3DFF7A86 + 88FFD0FCFFFFC9F2F9FFC6EEF5FFC5EDF4FFC5EDF4FFC5EDF4FFC5EDF4FFC5ED + F4FFC5EDF5FFC5EDF5FFC4EFF8FFD0FBFFFFDA9344FFE3AA6CFFAC6B16E60000 + 000E0000000000000007A66917B3D49954FFE19E58FFD2D5C4FFC1F0FBFFC3EE + F6FFC4EFF6FFC4EEF6FFC5F1F8FFCBF9FFFF7E8A8BFF4E4643FF7D898BFFCCFA + FFFFC5F1F8FFC2EDF4FFC2ECF3FFC2ECF3FFC2ECF3FFC2ECF3FFC2ECF3FFC3EE + F5FFC4EFF6FFC3EEF6FFC1F0FBFFD2D5C4FFE19E58FFD49954FFA66917B30000 + 00070000000000000001945E1656BC7B2AFFF0B67BFFCFB082FFC4F0FFFFC2F0 + F8FF758385FFC5F1F9FFC8F8FFFF808D8FFF554C4AFF808D8EFFC9F9FFFFC3F0 + F7FFC1EDF4FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF3FFC2EE + F5FF758385FFC2F0F8FFC4F0FFFFCFB082FFF0B67BFFBC7B2AFF945E16560000 + 0001000000000000000000000009AF6D16F7E4AB6DFFE09B51FFC4D9D8FFBFEC + F9FFBFEFF7FFC2F2FAFF828E8EFF5C524FFF849092FFC6F7FFFFC0EFF6FFBFED + F4FFC0EEF5FFBFECF3FFBDEAF1FFBDEAF1FFBDEAF1FFBDEBF1FFBDEBF2FFBEEC + F3FFBFEFF6FFBFECF9FFC4D9D8FFE09B51FFE4AB6DFFAF6D16F7000000090000 + 00000000000000000000000000029D621766C18133FFF2BA83FFD19B5AFFB9E4 + FAFFBBEBF6FFC0F1FAFF838F8FFF869394FFC4F7FFFFBEEDF6FFBCEBF3FFBFEE + F7FF728386FFBFEEF7FFBCEAF2FFBBE9F1FFBDEBF3FFBDECF5FFBDEBF3FFBBEB + F4FFBBEAF5FFB9E4FAFFD19B5AFFF2BA82FFC18133FF9D621767000000020000 + 000000000000000000000000000000000006AF6D17E7D99F5BFFEAAD6CFFC6A6 + 7AFFB1DEF5FFB8E7F4FFBEF2FDFF718588FFBDEEF7FFB9E8F2FFB9E8F2FFBDED + F7FF718487FFBDEDF7FFB9E8F2FFB8E8F1FFBBEBF4FF6D7E81FFBBEDF6FFB6E4 + F1FFB0DEF5FFC6A67AFFEDB071FFD99F5BFFAF6D17E700000006000000000000 + 0000000000000000000000000000000000018E5A1527AF6C14FFEEB880FFEBAE + 6DFFC69E6BFFACD3E6FFAFDDF1FFB7E8F5FFB8EBF4FFB7E8F1FFB7E8F1FFBBEC + F5FF6E8082FFBBECF5FFB7E8F1FFB7E8F1FFB8EBF4FFB6E7F4FFAEDDF0FFACD3 + E6FFC59E6AFFEBAE6DFFEEB880FFAF6C14FF6E45111200000000000000000000 + 000000000000000000000000000000000000000000019D621738B4721CFFEFBA + 83FFEDB172FFCF914CFFB5B6A7FFA2D2EEFFA6D3E9FFAADAEAFFAFE0EEFFB2E4 + F0FFB3E5F2FFB2E4F0FFAFE0EEFFAADAEAFFA6D3E9FFA2D2EEFFB5B6A7FFCF91 + 4CFFF0B578FFEFBA82FFB4721CFF8F5A15230000000100000000000000000000 + 0000000000000000000000000000000000000000000000000001935C1522AF6C + 14FFDCA362FFF9C692FFE39F57FFCA8B44FFB2AE99FFA4BFC8FF99CDECFF9ACC + EBFF9ACCEBFF9ACCEBFF99CDECFFA4BFC8FFB2AE99FFCA8B44FFE39F57FFF9C6 + 92FFDBA362FFAF6C13FF945D1623000000010000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000007046 + 1110AF6C16E6C38437FFEDB880FFFCC996FFE6A561FFDA9043FFD0822DFFD182 + 2CFFD1822CFFD1822CFFD0822DFFDA9043FFE6A561FFFCC996FFEDB880FFC384 + 37FFAF6C16E67046111000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000002A769175FAF6D15F7BE7F2EFFDEA767FFF2C18DFFFFD5A9FFFFD5 + A8FFFFD5A8FFFFD5A8FFFFD5A9FFF2C18DFFDEA767FFBE7F2EFFAF6D15F7A769 + 175F000000020000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000001A769184CAC6B16ACAF6C15E2AF6B13FFAF6B + 13FFAF6B13FFAF6B13FFAF6B13FFAF6C15E2AC6B16ACA769184C000000010000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000FFFFFFFFFFC001FFFF00007FFE00003FF800000FF0000007F0000007E000 + 0003C0000001C000000180000000800000008000000080000000800000008000 + 000080000000800000008000000080000000800000008000000080000000C000 + 0001C0000001E0000003E0000007F0000007F800000FFE00003FFF00007FFFC0 + 01FF280000001000000020000000010020000000000000040000640000006400 + 0000000000000000000000000000000000000000000000000000000000000000 + 0002000000080000000B0000000B0000000A0000000500000000000000000000 + 0000000000000000000000000000000000000000000000000002000000121910 + 043C50330C8759380CA059380CA157360C9A4028096500000020000000070000 + 0000000000000000000000000000000000000000000206040122764B13ABC27F + 2FFED18F42FFD9974AFFD99749FFD79547FFCA883AFFAA6D23E0311F07620000 + 000C000000000000000000000000000000020F0902259D6521D5D59347FFE1C5 + 95FFE4EEE0FFE7FFFFFFE7FFFFFFE6FAF7FFE3DCBEFFDDA765FFC88639FF4B2F + 0B720000000A000000000000000000000012915D1CCBD9994FFFE0E4D0FFCFE6 + E6FFE1FDFEFFE3FDFEFFBFD0D0FFE1FCFDFFCFE6E6FFE1FCFAFFDEB67EFFC988 + 3BFF321F075A00000003000000024B2F0B6DD29147FFDCD6B7FFDCFCFEFFDCFA + FCFFDBF9FBFFDDFBFCFFCBE4E6FFDBF9FBFFDCFAFCFFDCFAFCFFDCFAFAFFDCA8 + 67FFAF7228DE0000001000000008A66D24C9DCA868FFD7FBFEFFC5E0E3FFD7F6 + F9FFD9F9FCFFD9F9FCFFD7F7FAFFD7F6F9FFD7F6F9FFD7F7FAFFC5E1E4FFD8D6 + BBFFD19147FF452B0A520000000BC7883DF9D6C398FFD2F6FBFFD2F5F8FFD2F5 + F8FFAFC5C7FF8A9695FFD6FAFCFFD2F4F8FFD2F4F8FFD2F4F8FFD2F5F8FFD2F4 + F4FFDE9D55FF57360B850000000BCC8E45FFD2C59EFFBCDCE1FFABC4C7FFCEF3 + F8FFD1F7FBFF889595FF7C8A8AFFCFF5FAFFCDF1F6FFBDDBE0FFABC4C7FFCDF8 + FDFFE0A058FF58360B8A0000000ACE9048FFD3C59CFFC9F2F8FFCAF2F7FFC8F0 + F5FFCCF6FAFF829091FF7D8C8DFFCAF3F8FFC8EFF4FFC9F1F6FFCAF2F7FFCBF7 + FBFFE1A15BFF58360B8A00000005C2853BE6D7C097FFC3EEF7FFC4EDF5FFC7F2 + F9FF859495FF829293FFC8F3F9FFC3ECF3FFC3ECF3FFC3EDF4FFC4EDF5FFC9EB + EDFFDC9D57FF54350B6C000000007F511595E0AB6EFFC2E9F2FFAED5DBFF8999 + 9AFF87989AFFC2F1F8FFC0ECF3FFBEEAF1FFBEEAF1FFBFECF3FFADD3DBFFCDC5 + AAFFCF924AFD311F0718000000003420071CCE914AF9CEB48EFFB9E8F6FF8EA6 + AAFFBEEEF7FFBCEBF4FF97B8BEFFBAE8F1FFA8D0D7FFBAE9F3FFBAD4D7FFE2A9 + 6AFF83541895000000000000000000000000764A1058DFA463FFCBAC83FFAFD3 + DEFFAFE0EEFFB4E6F1FFA3CDD6FFB1E2EEFFADDDEFFFB7BDB2FFE3AE74FFB87C + 31C83722080500000000000000000000000000000000905A134CD19650F9E5AA + 6CFFC5A881FFB5A78CFFB5A78BFFB9A789FFD7A975FFE3A868FFAA7027BD4A2E + 0B0900000000000000000000000000000000000000000000000053340B188555 + 1691CA8F47E3D7A05DFFD7A05DFFD39B57F8BB7E30BE72470E56000000000000 + 00000000000000000000F81F0000E0070000C003000080010000800000000000 + 00000000FFFF0000FFFF0000FFFF0000F1F10000B7B78000959580019393C001 + 9393E0039393F00F9696 + } + Position = poDesktopCenter + LCLVersion = '2.0.10.0' + object Button1: TButton + Left = 40 + Height = 26 + Top = 88 + Width = 88 + Caption = 'Ok' + OnClick = Button1Click + TabOrder = 0 + end + object Label1: TLabel + Left = 72 + Height = 15 + Top = 41 + Width = 53 + Caption = 'Time''s Up' + ParentColor = False + end + object Image1: TImage + Left = 24 + Height = 32 + Top = 33 + Width = 32 + Picture.Data = { + 055449636F6E662300000000010004002020000001000800A808000046000000 + 101000000100080068050000EE0800002020000001002000A8100000560E0000 + 101000000100200068040000FE1E000028000000200000004000000001000800 + 0000000000000000000000000000000000000000000000000000000037313100 + 3C34340041383800433D3B00483F3D004E4643004F474500554C4A005C524F00 + 6E787A00707B7D00AF6B1300AF6C1300AF6C1400B06D1600B16F1800B2701900 + B1711D00B4711C00B7752100BA772400B9782500B1762A00B2782B00B5792C00 + BC7A2900B87C2F00BE7F2E00D0822C00C1813300C4803200C3843700C6843600 + CB883B00C9883C00CC883C00D68D3A00D9913D00B1864D00B78C5300B39C7E00 + CA8B4400CD8D4400D18D4200D18F4700CF914C00DA914200DA944100DA934400 + D2914900D5914900D3934D00D9974E00DD994900DB9B4D00DC9B4D00DD9C4E00 + C0955C00D4995400DB9A5100DC9D5100DF9E5500D19B5A00D99E5A00DC9C5900 + E09E4D00E09B5100E39F5700E19E5800DEA15500E2A25100E1A75A00E0A15E00 + C79B6100C59E6A00DBA36200DCA36200DEA76700C6A67A00E6A56100E3AA6C00 + E4AB6D00E7AC6F00EAAD6C00E8AE7200EDB07100E9B07600EBB27900EDB77E00 + F0B57A006D7E81006E8082007480800072838600758385007184870078848500 + 718588007A8688007D898A00828A8B00818B8D00818D8E00858D8E0083909100 + 8490920085939400899190008E979800949D9C00BDA58700B2AE990099A2A100 + B5B6A700CFB08200EEB88000F2BA8200D8C59C00F2C18D00F9C69200FCC99600 + DCCFAB00E3CEA300FFD5A800E2D3B000A4BFC8009ACCEB0099CDEC00ACD3E600 + A6D3E900A2D2EE00AADAEA00AEDDF000B0DEF500AFE0EE00B2E4F000B6E4F100 + B6E7F400B8E7F400B7E8F100B7E8F500B9E8F100BCEAF100BEECF300BAEAF400 + BBECF500BEEDF600B9E4FA00BFECF900BEF2FD00D2D5C400C4D9D800D5E4D800 + DEF1EB00C0EBF200C2ECF300C1EDF500C5EDF400C4EFF800C3F0F700C6F1F700 + C8F0F500CDF1F600C1F0F900C5F1F800C4F0FF00C5F7FF00C9F2F900CEF3F800 + CAF4F900CDF5FA00CFF5FC00CAF9FF00CDFAFE00CFFCFF00D2F4F800D5F6F900 + D1F6FC00D8F7FA00D7F8FB00D2F9FE00D6F9FD00D0FDFF00D6FEFF00D9F8FB00 + DDFAFB00D9F9FC00DDFAFC00DAFDFF00DDFDFE00E7EEE000E2F3EC00E1FEFE00 + E5FFFF00E9FFFF0090006200B0007800CF008E00F000A400FF11B300FF31BE00 + FF51C700FF71D100FF91DC00FFB1E500FFD1F000FFFFFF00000000002C002F00 + 4B0050006900700087009000A500B000C400CF00E100F000F011FF00F231FF00 + F451FF00F671FF00F791FF00F9B1FF00FBD1FF00FFFFFF00000000001B002F00 + 2D0050003F007000520090006300B0007600CF008800F0009911FF00A631FF00 + B451FF00C271FF00CF91FF00DCB1FF00EBD1FF00FFFFFF000000000008002F00 + 0E005000150070001B0090002100B0002600CF002C00F0003E11FF005831FF00 + 7151FF008C71FF00A691FF00BFB1FF00DAD1FF00FFFFFF000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000027181111131112182700000000000000000000000000000000000000 + 002912141F242C2C2C2C2C241F14122900000000000000000000000000000000 + 1715243338394747474747393733241517000000000000000000000000000011 + 213339487BBFC3C3C3C3C3BF7B48393321110000000000000000000000001332 + 35427DC2C2C2C2C2C3C2C2C2C2C27D423532130000000000000000000011323C + 46C0C2C2C2C1C2C271C2C2C1C2C2C2C0463C3211000000000000000017233D46 + C1C1C16EC1BCC1C271C2C1BCC16EC1C1C1463D23180000000000006F1A41369A + BEBEBEC1C1BCBCC16EC2BCBABEC1BEBEBE9A36411A6F000000000012343D7ABE + BEBCBBB9B9B9BBBCBEBCB9B9B9BBB9BCBEBD7A3D3412000000000016493DBDBD + 6DBDB3B3B9B9B9B4B9B3B3B3B3B3B3BD6DBDBD3D491600000000282B3E76B8B6 + BBB4B1B1BBBDBEBDB3B1B1B1B3B1B1B4BBB6B8763E2B2800000018403699B5B1 + B1B1B0B1BD6865BEB6B1B0B0B0B1B1B1B0B1B599364018000000105330B7B5B0 + B2B0B0B0B86C0764BEB6B1B0B0B0B0B0B0B0B2B7305310000000105526AFACB2 + B6B2B0A9B2B868045DBDB2A9A9A9B0B2B5B5ACAE30550F0000000F5726AEAC6A + 6B69ACA3A9B2BD67010AB5A9A3A3AB696B69B2AD26570F0000000F5825B7AAAB + ACABA8A2A8ABB863020BB5A2A2A2A8ABABABAAB725580F0000000F5925AFA1A2 + A2A2A2A2AAB7610361B7AAA2A2A2A2A2A2A2A1AF25590F000000195231AF9F9E + 9E9EA2A8AF630563B7A8A29E9E9E9E9E9E9EA5AF3152190000003A3B4597969F + A09FA5AD640664AEA59D9C9C9E9C9CA0A0A0A497453B3A000000001A5A73A6A0 + 5FA5AD670867ADA09D9B9C9B9B9B9BA05FA0A6735A1A00000000001252439895 + A4A469096AA7A0909D908F8F8F8F90909395984352120000000000001E753F94 + 92A4696BA79390955E958F8F9093909292943F751E000000000000001940544F + 868B966293918E9362938B8C915B9289864F56401900000000000000000E7454 + 4B81858D918C8C925C928C8C918A85814B54740E000000000000000000001375 + 562E728382848788898887848283722E5A74130000000000000000000000000E + 4D78442A707E807F7F7F807E702A44784C0D0000000000000000000000000000 + 19207479502F1D1D1D1D1D2F5079742019000000000000000000000000000000 + 0000121C4E777C7C7C7C7C774E1C120000000000000000000000000000000000 + 000000004A1B0C0C0C0C0C1B4A0000000000000000000000FFFFFFFFFFFFFFFF + FFFFFFFFFFF007FFFF8000FFFF00007FFE00003FFC00001FF800000FF0000007 + E0000003E0000003E0000003C0000001C0000001C0000001C0000001C0000001 + C0000001C0000001C0000001C0000001E0000003E0000003F0000007F0000007 + F800000FFC00001FFE00003FFF00007FFFC001FFFFF007FF2800000010000000 + 2000000001000800000000000000000000000000000000000000000000000000 + 00000000B47E3D00C27F2F00AD7E4500A77E4A00C8863900C9883A00B9844300 + B88B52009681650099856C00A3866000A4927A00A2937E00A7967F00B4997600 + B69B7800B99E7A00C88A4100CC8E4500D18F4200C7904E00CE914900CF934E00 + D1914700D5934700D7954700D9974900D9994F00C0955E00C7985D00CF9B5B00 + D2985400DD9D5600D49D5B00D7A05D00E0A05900CC9E6400DFA46300DDA76500 + DCA86700DCA86800D7A97500DEB67E00E2A86900E0AB6E00E5AA6C00E3AE7400 + 7C8A8A007D8C8D0082919200859495008995950087989A0089999A00B5A78B00 + B5A78C00B9A789008EA6AA0097B8BE00B7BDB200C5A88100CBAC8300CEB48E00 + D7C09700D6C39800D2C59D00E1C59500CDC5AA00DCD6B700D8D6BB00E3DCBE00 + ABC4C700AFC5C700A3CDD600A8D0D700ADD3DB00AED5DB00AFD3DE00BFD0D000 + BAD4D700ADDDEF00BDDBE000BCDCE100AFE0EE00B1E2EE00B4E6F100BAE8F200 + BEEAF100BFECF300B9E8F600BCEBF400BEEEF700E0E4D000C5E0E300C5E1E400 + CBE4E600CFE6E600C9EBED00C2E9F200C2ECF300C3EDF500C4EDF500C8EFF400 + C9F1F600CDF1F600C2F1F800C7F2F900C9F2F800CEF3F800CBF7FB00CDF5FA00 + CDF8FD00D2F4F400D1F5F800D7F6F900D6FAFD00DBF9FB00DCFAFA00D9F9FC00 + DCFAFC00DCFCFE00E4EEE000E6FAF700E1FCFA00E1FCFD00E7FFFF00FFFFFF00 + 000000002F26000050410000705B000090740000B08E0000CFA90000F0C30000 + FFD21100FFD83100FFDD5100FFE47100FFEA9100FFF0B100FFF6D100FFFFFF00 + 000000002F1400005022000070300000903E0000B04D0000CF5B0000F0690000 + FF791100FF8A3100FF9D5100FFAF7100FFC19100FFD2B100FFE5D100FFFFFF00 + 000000002F030000500400007006000090090000B00A0000CF0C0000F00E0000 + FF201200FF3E3100FF5C5100FF7A7100FF979100FFB6B100FFD4D100FFFFFF00 + 000000002F000E00500017007000210090002B00B0003600CF004000F0004900 + FF115A00FF317000FF518600FF719C00FF91B200FFB1C800FFD1DF00FFFFFF00 + 000000002F0020005000360070004C0090006200B0007800CF008E00F000A400 + FF11B300FF31BE00FF51C700FF71D100FF91DC00FFB1E500FFD1F000FFFFFF00 + 000000002C002F004B0050006900700087009000A500B000C400CF00E100F000 + F011FF00F231FF00F451FF00F671FF00F791FF00F9B1FF00FBD1FF00FFFFFF00 + 000000001B002F002D0050003F007000520090006300B0007600CF008800F000 + 9911FF00A631FF00B451FF00C271FF00CF91FF00DCB1FF00EBD1FF00FFFFFF00 + 0000000008002F000E005000150070001B0090002100B0002600CF002C00F000 + 3E11FF005831FF007151FF008C71FF00A691FF00BFB1FF00DAD1FF00FFFFFF00 + 000000000000000000000000000000000000000000000D09090A000000000000 + 000000000B02141B1B1A060100000000000000031A437A7E7E7B472705000000 + 0000041C5D617D7E4F7D617C2B06000000001845797875786076787876290700 + 000829745E737777777373775F4618000012417272724934747272727271210E + 00134253486D7234306F69524870240C0016426C6C686F32316C67686C6E240C + 00154065666B33326B64646666622100000F2D634D36356A595858594C441600 + 0000173F5A3A5C5B3B574B57502C1000000000263E4E54564A55513C2F1E0000 + 00000000202E3D3837392A2C1D0000000000000000111F232322250000000000 + FFFF0000FC3F0000F00F0000E0070000C0030000C00100008001000080000000 + 80000000800000008001000080010000C0010000E0030000F0070000F81F0000 + 2800000020000000400000000100200000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000001000000070000000E00000013 + 0000001600000016000000160000001600000016000000130000000E00000007 + 0000000100000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000002000000090000001500000024000000320000003C + 00000041000000430000004300000043000000410000003C0000003200000024 + 0000001500000009000000020000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000600000014000000280000003C6741107C996117C3AA6B19EA + B2701AFFB27019FFB27019FFB27019FFB2701AFFAA6B19EA996117C36741107C + 0000003C00000028000000140000000600000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000001 + 000000070000001C000000377349128AB06F1AF9B77521FFC48032FFCB883BFF + D28D43FFD18D42FFD18D42FFD18D42FFD28D43FFCB883BFFC48032FFB77521FF + B06F1AF97349128A000000370000001C00000007000000010000000000000000 + 0000000000000000000000000000000000000000000000000000000100000008 + 0000001F19100446AB6C19EDBA7724FFCC883CFFD59048FFDB9B4DFFDD9C4EFF + E2A352FFE2A251FFE2A250FFE2A251FFE2A352FFDD9C4EFFDB9B4DFFD59048FF + CC883CFFBA7724FFAB6C19ED191004460000001F000000080000000100000000 + 000000000000000000000000000000000000000000000000000000070000001F + 3D260956B26F19FFC68436FFD6934BFFDC9B4DFFE1A75AFFE3CEA3FFE7EEE0FF + E9FFFFFFE9FFFFFFE9FFFFFFE9FFFFFFE9FFFFFFE7EEE0FFE3CEA3FFE1A75AFF + DC9B4DFFD6934BFFC68436FFB26F19FF3D2609560000001F0000000700000000 + 0000000000000000000000000000000000000000000000060000001C3C260953 + B4711CFFD18F47FFD9974EFFE09E4DFFE2D3B0FFE6FFFFFFE5FFFFFFE4FFFFFF + E4FFFFFFE6FFFFFFE7FFFFFFE6FFFFFFE4FFFFFFE4FFFFFFE5FFFFFFE6FFFFFF + E2D3B0FFE09E4DFFD9974EFFD18F47FFB4711CFF3C2609530000001C00000006 + 0000000000000000000000000000000000000002000000141E130541B16F18FF + D29149FFDB9950FFDFA256FFE2F3ECFFE4FFFFFFE5FFFFFFE3FFFFFFE2FDFEFF + E3FDFEFFE6FFFFFF99A1A1FFE6FFFFFFE3FDFEFFE2FDFEFFE3FFFFFFE5FFFFFF + E4FFFFFFE2F3ECFFDFA256FFDB9950FFD29149FFB16F18FF1E13054100000014 + 000000020000000000000000000000000000000900000028AC6C19ECC9883CFF + DB9B52FFDEA154FFE0FFFFFFE0FFFFFFE1FFFFFF959D9CFFE1FEFFFFDFFBFCFF + E0FCFDFFE4FFFFFF99A3A1FFE4FFFFFFE0FCFDFFDFFBFCFFE1FEFFFF959D9CFF + E1FFFFFFE0FFFFFFE0FFFFFFDEA154FFDB9B52FFC9883CFFAC6C19EC00000028 + 00000009000000000000000000000001000000157D4F1381BC7A28FFDC9C59FF + DE9A49FFDEF1EBFFDEFFFFFFDEFCFDFFDEFCFDFFDFFDFEFFDEFCFDFFDDFAFBFF + DEFBFCFFE1FFFFFF949E9EFFE1FFFFFFDEFBFCFFDDFAFBFFDEFCFDFFDFFDFEFF + DEFCFDFFDEFCFDFFDEFFFFFFDEF1EBFFDE9A49FFDC9C59FFBC7A28FF7D4F1381 + 0000001500000001000000000000000700000024B06E19F9D3934DFFDD9C50FF + DCCFABFFDBFFFFFFDCFCFFFFDBFAFDFFDAF8FCFFDAF8FCFFDAF8FBFFDAF8FBFF + DAF8FBFFDCFAFDFFDDFBFEFFDCFAFDFFDAF8FBFFDAF8FBFFDAF8FBFFDAF8FCFF + DAF8FCFFDBFAFDFFDCFCFFFFDBFFFFFFDCCFABFFDD9C50FFD3934DFFB06E19F9 + 0000002400000007000000000000000E774B136BB97825FFE0A15EFFDC9E52FF + D8FAFFFFDAFDFFFF8E9798FFDAFAFDFFD8F7FAFFD8F7FAFFD8F7FAFFD9F8FBFF + D9F8FBFFD8F8FBFFD8F8FBFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFF + D8F7FAFFDAFAFDFF8E9798FFDAFDFFFFD8FAFFFFDC9E52FFE0A15EFFB97825FF + 774B136B0000000E00000000000000139E6417BCCD8D44FFDF9E55FFD8C59CFF + D6FDFFFFD7F9FDFFD8F9FCFFD7F8FBFFD6F6F9FFD6F6F9FFD8F9FCFFDBFDFFFF + DCFDFFFFD9FAFDFFD7F7FAFFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FF + D6F6F9FFD7F8FBFFD8F9FCFFD7F9FDFFD6FDFFFFD8C59CFFDF9E55FFCD8D44FF + 9E6417BC000000130000000000000016AB6B17E8DA9D5AFFDC984AFFD5E4D8FF + D3F9FFFFD3F6F9FFD3F5F8FFD3F5F8FFD3F5F8FFD4F6F9FFD8FCFFFF868D8EFF + 828A8BFFDDFFFFFFD6FAFDFFD3F6F9FFD3F5F8FFD3F5F8FFD3F5F8FFD3F5F8FF + D3F5F8FFD3F5F8FFD3F5F8FFD3F6F9FFD3F9FFFFD5E4D8FFDD994BFFDA9D5AFF + AB6B17E8000000160000000000000016B16E17FFE7AC6FFFDA9441FFD0FFFFFF + D1F7FCFFD2F5F9FFD2F5F9FFD2F5F9FFD1F4F8FFD2F5F9FFD6FBFFFF899190FF + 4F4745FF7C8888FFDDFFFFFFD5F9FDFFD2F5F9FFD1F4F8FFD1F4F8FFD1F4F8FF + D1F4F8FFD2F5F9FFD2F5F9FFD2F5F9FFD1F7FCFFD0FFFFFFDA9341FFE7AC6FFF + B16E17FF000000160000000000000016B06E16FFE8AE72FFD9923EFFCDFBFFFF + CFF5FCFFD2F7FCFFD3F8FDFFD2F7FCFFD0F4F9FFCEF3F8FFD1F5FAFFD7FFFFFF + 848E8EFF433D3BFF748080FFDAFFFFFFD1F6FBFFCEF2F7FFCEF2F7FFCEF2F7FF + D0F4F9FFD2F7FCFFD3F8FDFFD2F7FCFFCFF5FCFFCDFBFFFFD9923EFFE8AE72FF + B06E16FF000000160000000000000016B06D16FFE9B076FFD9903DFFCBFAFFFF + CFF6FCFF839091FF859394FF839091FFCFF4FAFFCCF1F7FFCDF2F7FFD0F6FCFF + DAFFFFFF818B8DFF373131FF6E787AFFD3FAFFFFCDF2F7FFCCF1F6FFCCF1F6FF + CFF4FAFF839091FF859394FF839091FFCFF6FCFFCBFAFFFFD9903DFFE9B076FF + B06D16FF000000160000000000000016B06D16FFEBB279FFD78E3BFFD0FDFFFF + CAF3FAFFCDF5FAFFCEF6FBFFCDF5FAFFCAF2F7FFC9F0F5FFC9F1F6FFCDF6FBFF + D7FFFFFF798789FF3C3434FF707B7DFFCFF9FEFFCAF1F6FFC9F0F5FFC9F0F5FF + CAF2F7FFCDF5FAFFCEF6FBFFCDF5FAFFCAF3FAFFD0FDFFFFD78E3BFFEBB279FF + B06D16FF000000160000000000000013B06D16FFEDB77EFFD68D3AFFCFFCFFFF + C6F1F7FFC8F0F5FFC8F0F5FFC8F0F5FFC7EFF4FFC8F0F5FFCAF4F9FFD2FDFFFF + 788485FF413838FF778385FFD2FEFFFFCAF3F8FFC7EFF4FFC7EFF4FFC7EFF4FF + C7EFF4FFC8F0F5FFC8F0F5FFC8F0F5FFC6F1F7FFCFFCFFFFD68D3AFFEDB77EFF + B06D16FF00000013000000000000000EAC6B16E6E3AA6CFFDA9344FFD0FBFFFF + C4EFF8FFC5EDF5FFC5EDF5FFC5EDF4FFC6EEF5FFC8F1F9FFCFFBFFFF7B8688FF + 483F3DFF7A8688FFD0FCFFFFC9F2F9FFC6EEF5FFC5EDF4FFC5EDF4FFC5EDF4FF + C5EDF4FFC5EDF4FFC5EDF5FFC5EDF5FFC4EFF8FFD0FBFFFFDA9344FFE3AA6CFF + AC6B16E60000000E0000000000000007A66917B3D49954FFE19E58FFD2D5C4FF + C1F0FBFFC3EEF6FFC4EFF6FFC4EEF6FFC5F1F8FFCBF9FFFF7E8A8BFF4E4643FF + 7D898BFFCCFAFFFFC5F1F8FFC2EDF4FFC2ECF3FFC2ECF3FFC2ECF3FFC2ECF3FF + C2ECF3FFC3EEF5FFC4EFF6FFC3EEF6FFC1F0FBFFD2D5C4FFE19E58FFD49954FF + A66917B3000000070000000000000001945E1656BC7B2AFFF0B67BFFCFB082FF + C4F0FFFFC2F0F8FF758385FFC5F1F9FFC8F8FFFF808D8FFF554C4AFF808D8EFF + C9F9FFFFC3F0F7FFC1EDF4FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF2FF + C0EBF3FFC2EEF5FF758385FFC2F0F8FFC4F0FFFFCFB082FFF0B67BFFBC7B2AFF + 945E165600000001000000000000000000000009AF6D16F7E4AB6DFFE09B51FF + C4D9D8FFBFECF9FFBFEFF7FFC2F2FAFF828E8EFF5C524FFF849092FFC6F7FFFF + C0EFF6FFBFEDF4FFC0EEF5FFBFECF3FFBDEAF1FFBDEAF1FFBDEAF1FFBDEBF1FF + BDEBF2FFBEECF3FFBFEFF6FFBFECF9FFC4D9D8FFE09B51FFE4AB6DFFAF6D16F7 + 00000009000000000000000000000000000000029D621766C18133FFF2BA83FF + D19B5AFFB9E4FAFFBBEBF6FFC0F1FAFF838F8FFF869394FFC4F7FFFFBEEDF6FF + BCEBF3FFBFEEF7FF728386FFBFEEF7FFBCEAF2FFBBE9F1FFBDEBF3FFBDECF5FF + BDEBF3FFBBEBF4FFBBEAF5FFB9E4FAFFD19B5AFFF2BA82FFC18133FF9D621767 + 000000020000000000000000000000000000000000000006AF6D17E7D99F5BFF + EAAD6CFFC6A67AFFB1DEF5FFB8E7F4FFBEF2FDFF718588FFBDEEF7FFB9E8F2FF + B9E8F2FFBDEDF7FF718487FFBDEDF7FFB9E8F2FFB8E8F1FFBBEBF4FF6D7E81FF + BBEDF6FFB6E4F1FFB0DEF5FFC6A67AFFEDB071FFD99F5BFFAF6D17E700000006 + 0000000000000000000000000000000000000000000000018E5A1527AF6C14FF + EEB880FFEBAE6DFFC69E6BFFACD3E6FFAFDDF1FFB7E8F5FFB8EBF4FFB7E8F1FF + B7E8F1FFBBECF5FF6E8082FFBBECF5FFB7E8F1FFB7E8F1FFB8EBF4FFB6E7F4FF + AEDDF0FFACD3E6FFC59E6AFFEBAE6DFFEEB880FFAF6C14FF6E45111200000000 + 000000000000000000000000000000000000000000000000000000019D621738 + B4721CFFEFBA83FFEDB172FFCF914CFFB5B6A7FFA2D2EEFFA6D3E9FFAADAEAFF + AFE0EEFFB2E4F0FFB3E5F2FFB2E4F0FFAFE0EEFFAADAEAFFA6D3E9FFA2D2EEFF + B5B6A7FFCF914CFFF0B578FFEFBA82FFB4721CFF8F5A15230000000100000000 + 0000000000000000000000000000000000000000000000000000000000000001 + 935C1522AF6C14FFDCA362FFF9C692FFE39F57FFCA8B44FFB2AE99FFA4BFC8FF + 99CDECFF9ACCEBFF9ACCEBFF9ACCEBFF99CDECFFA4BFC8FFB2AE99FFCA8B44FF + E39F57FFF9C692FFDBA362FFAF6C13FF945D1623000000010000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000070461110AF6C16E6C38437FFEDB880FFFCC996FFE6A561FFDA9043FF + D0822DFFD1822CFFD1822CFFD1822CFFD0822DFFDA9043FFE6A561FFFCC996FF + EDB880FFC38437FFAF6C16E67046111000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000002A769175FAF6D15F7BE7F2EFFDEA767FFF2C18DFF + FFD5A9FFFFD5A8FFFFD5A8FFFFD5A8FFFFD5A9FFF2C18DFFDEA767FFBE7F2EFF + AF6D15F7A769175F000000020000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000001A769184CAC6B16ACAF6C15E2 + AF6B13FFAF6B13FFAF6B13FFAF6B13FFAF6B13FFAF6C15E2AC6B16ACA769184C + 0000000100000000000000000000000000000000000000000000000000000000 + 0000000000000000FFFFFFFFFFC001FFFF00007FFE00003FF800000FF0000007 + F0000007E0000003C0000001C000000180000000800000008000000080000000 + 8000000080000000800000008000000080000000800000008000000080000000 + 80000000C0000001C0000001E0000003E0000007F0000007F800000FFE00003F + FF00007FFFC001FF280000001000000020000000010020000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000002000000080000000B0000000B0000000A0000000500000000 + 0000000000000000000000000000000000000000000000000000000000000002 + 000000121910043C50330C8759380CA059380CA157360C9A4028096500000020 + 0000000700000000000000000000000000000000000000000000000206040122 + 764B13ABC27F2FFED18F42FFD9974AFFD99749FFD79547FFCA883AFFAA6D23E0 + 311F07620000000C000000000000000000000000000000020F0902259D6521D5 + D59347FFE1C595FFE4EEE0FFE7FFFFFFE7FFFFFFE6FAF7FFE3DCBEFFDDA765FF + C88639FF4B2F0B720000000A000000000000000000000012915D1CCBD9994FFF + E0E4D0FFCFE6E6FFE1FDFEFFE3FDFEFFBFD0D0FFE1FCFDFFCFE6E6FFE1FCFAFF + DEB67EFFC9883BFF321F075A00000003000000024B2F0B6DD29147FFDCD6B7FF + DCFCFEFFDCFAFCFFDBF9FBFFDDFBFCFFCBE4E6FFDBF9FBFFDCFAFCFFDCFAFCFF + DCFAFAFFDCA867FFAF7228DE0000001000000008A66D24C9DCA868FFD7FBFEFF + C5E0E3FFD7F6F9FFD9F9FCFFD9F9FCFFD7F7FAFFD7F6F9FFD7F6F9FFD7F7FAFF + C5E1E4FFD8D6BBFFD19147FF452B0A520000000BC7883DF9D6C398FFD2F6FBFF + D2F5F8FFD2F5F8FFAFC5C7FF8A9695FFD6FAFCFFD2F4F8FFD2F4F8FFD2F4F8FF + D2F5F8FFD2F4F4FFDE9D55FF57360B850000000BCC8E45FFD2C59EFFBCDCE1FF + ABC4C7FFCEF3F8FFD1F7FBFF889595FF7C8A8AFFCFF5FAFFCDF1F6FFBDDBE0FF + ABC4C7FFCDF8FDFFE0A058FF58360B8A0000000ACE9048FFD3C59CFFC9F2F8FF + CAF2F7FFC8F0F5FFCCF6FAFF829091FF7D8C8DFFCAF3F8FFC8EFF4FFC9F1F6FF + CAF2F7FFCBF7FBFFE1A15BFF58360B8A00000005C2853BE6D7C097FFC3EEF7FF + C4EDF5FFC7F2F9FF859495FF829293FFC8F3F9FFC3ECF3FFC3ECF3FFC3EDF4FF + C4EDF5FFC9EBEDFFDC9D57FF54350B6C000000007F511595E0AB6EFFC2E9F2FF + AED5DBFF89999AFF87989AFFC2F1F8FFC0ECF3FFBEEAF1FFBEEAF1FFBFECF3FF + ADD3DBFFCDC5AAFFCF924AFD311F0718000000003420071CCE914AF9CEB48EFF + B9E8F6FF8EA6AAFFBEEEF7FFBCEBF4FF97B8BEFFBAE8F1FFA8D0D7FFBAE9F3FF + BAD4D7FFE2A96AFF83541895000000000000000000000000764A1058DFA463FF + CBAC83FFAFD3DEFFAFE0EEFFB4E6F1FFA3CDD6FFB1E2EEFFADDDEFFFB7BDB2FF + E3AE74FFB87C31C83722080500000000000000000000000000000000905A134C + D19650F9E5AA6CFFC5A881FFB5A78CFFB5A78BFFB9A789FFD7A975FFE3A868FF + AA7027BD4A2E0B09000000000000000000000000000000000000000000000000 + 53340B1885551691CA8F47E3D7A05DFFD7A05DFFD39B57F8BB7E30BE72470E56 + 00000000000000000000000000000000F81FAC41E007AC41C003AC418001AC41 + 8000AC410000AC410000AC410000AC410000AC410000AC410000AC418000AC41 + 8001AC41C001AC41E003AC41F00FAC41 + } + end +end diff --git a/snaptimer.lpi b/snaptimer.lpi index 9981d7c..949bd61 100644 --- a/snaptimer.lpi +++ b/snaptimer.lpi @@ -85,7 +85,7 @@ - + @@ -130,6 +130,13 @@ + + + + + + + diff --git a/snaptimer.lps b/snaptimer.lps index 6dfeb25..8750c92 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,7 +4,7 @@ - + @@ -39,9 +39,9 @@ - - - + + + @@ -70,8 +70,8 @@ - - + + @@ -281,8 +281,20 @@ + + + + + + + + + + + + - + @@ -391,6 +403,10 @@ + + + + diff --git a/ui/mainform1.pas b/ui/mainform1.pas index c94348d..2522561 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -89,7 +89,7 @@ TMainForm = class(TForm) implementation uses - Config, Utils; + Config, Utils, MsgBox; { TMainForm } @@ -497,8 +497,12 @@ procedure TMainForm.ShowTrayMessage(Msg: string); procedure TMainForm.ShowDoneMessage(Msg: string); begin // http://msdn.microsoft.com/en-us/library/ms645505(VS.85).aspx - Windows.MessageBox(self.Handle, pChar(msg), 'Done', - MB_SYSTEMMODAL or MB_SETFOREGROUND or MB_TOPMOST or MB_ICONINFORMATION); + //Windows.MessageBox(self.Handle, pChar(msg), 'Done', + // MB_SYSTEMMODAL or MB_SETFOREGROUND or MB_TOPMOST or MB_ICONINFORMATION); + MsgboxForm := TMsgBoxForm.Create(Self); + MsgBoxForm.SetText(Msg); + MsgboxForm.ShowModal; + MsgboxForm.Release; TUtils.StopAudio; end; diff --git a/ui/msgbox.lfm b/ui/msgbox.lfm new file mode 100644 index 0000000..77f6133 --- /dev/null +++ b/ui/msgbox.lfm @@ -0,0 +1,624 @@ +object MsgBoxForm: TMsgBoxForm + Left = 819 + Height = 136 + Top = 314 + Width = 162 + BorderStyle = bsDialog + Caption = 'Done' + ClientHeight = 136 + ClientWidth = 162 + FormStyle = fsSystemStayOnTop + Icon.Data = { + 662500000000010004002020000001001800A80C000046000000101000000100 + 180068030000EE0C00002020000001002000A810000056100000101000000100 + 200068040000FE2000002800000020000000400000000100180000000000000C + 0000640000006400000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000B1864DB2782BB27019B2 + 7019B4711CB27019B1711DB2782BB1864D000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000B39C7EB1711DB77521C48032CC883CD18D42D1 + 8D42D18D42D18D42D18D42CC883CC48032B77521B1711DB39C7E000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000B1762ABA7724CC883CD59149DC9B4DDD9C4EE2A251E2 + A251E2A251E2A251E2A251DD9C4EDB9B4DD59149CC883CBA7724B1762A000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000B27019C68436D59149DD9C4EE1A75AE3CEA3E7EEE0E9FFFFE9 + FFFFE9FFFFE9FFFFE9FFFFE7EEE0E3CEA3E1A75ADD9C4ED59149C68436B27019 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000B4711CD29149D9974EE09E4DE2D3B0E5FFFFE5FFFFE5FFFFE5FFFFE5 + FFFFE9FFFFE5FFFFE5FFFFE5FFFFE5FFFFE5FFFFE2D3B0E09E4DD9974ED29149 + B4711C0000000000000000000000000000000000000000000000000000000000 + 00B27019D29149DB9A51DEA155E2F3ECE5FFFFE5FFFFE5FFFFE1FEFEE5FFFFE5 + FFFF99A2A1E5FFFFE5FFFFE1FEFEE5FFFFE5FFFFE5FFFFE2F3ECDEA155DB9A51 + D29149B27019000000000000000000000000000000000000000000000000B176 + 2AC9883CDC9D51DEA155E1FEFEE1FEFEE1FEFE949D9CE1FEFEDDFAFCE1FEFEE5 + FFFF99A2A1E5FFFFE1FEFEDDFAFCE1FEFE949D9CE1FEFEE1FEFEE1FEFEDEA155 + DC9D51C9883CB2782B000000000000000000000000000000000000BDA587BC7A + 29DC9C59DD9949DEF1EBDDFDFEDDFDFEDDFDFEE1FEFEE1FEFEDDFAFCDDFAFCE1 + FEFE949D9CE5FFFFDDFAFCDDFAFBDDFDFEE1FEFEDDFDFEDDFDFEDDFDFEDEF1EB + DD9949DC9C59BC7A29BDA587000000000000000000000000000000B1711DD393 + 4DDC9D51DCCFABDDFDFEDDFDFEDDFAFCD9F9FCD9F8FBD9F8FBD9F8FBD9F9FCDD + FAFCDDFDFEDDFAFCD9F8FBD9F8FBD9F8FBD9F9FCD9F8FBDDFAFCDDFDFEDAFDFF + DCCFABDC9D51D3934DB1711D000000000000000000000000000000B97825E0A1 + 5EDC9D51DAFDFFDAFDFF8E9798DAFDFFD8F7FAD8F7FAD9F8FBD9F8FBD9F8FBD7 + F8FBD9F8FBD8F7FAD8F7FAD8F7FAD8F7FAD8F7FAD8F7FADAFDFF8E9798DAFDFF + DAFDFFDC9D51E0A15EB97825000000000000000000000000B78C53CD8D44DF9E + 55D8C59CD6FEFFD6F9FDD9F9FCD7F8FBD5F6F9D5F6F9D9F9FCDAFDFFDDFDFEDA + FDFFD8F7FAD5F6F9D5F6F9D5F6F9D8F7FAD5F6F9D5F6F9D7F8FBD9F9FCD6F9FD + D6FEFFD8C59CDF9E55CD8D44B78C53000000000000000000B2782BD99E5ADD99 + 49D5E4D8D2F9FED5F6F9D5F6F9D5F6F9D2F4F8D5F6F9DAFDFF858D8E828A8BDD + FDFED6F9FDD5F6F9D2F4F8D2F4F8D2F4F8D5F6F9D5F6F9D5F6F9D2F4F8D5F6F9 + D2F9FED5E4D8DD9949D99E5AB2782B000000000000000000B16F18E7AC6FDA94 + 41D0FDFFD2F9FED2F4F8D1F6FCD2F4F8D2F4F8D2F4F8D6FEFF8991904F47457D + 898ADDFDFED6F9FDD5F6F9D2F4F8D2F4F8D2F4F8D2F4F8D2F4F8D2F4F8D2F4F8 + D1F6FCD0FDFFDA9441E7AC6FB16F18000000000000000000B16F18E8AE72D991 + 3DCFFCFFCFF5FCD1F6FCD6F9FDD1F6FCD2F4F8CEF3F8D1F6FCD6FEFF858D8E43 + 3D3B748080DAFDFFD1F6FCCEF3F8CEF3F8CEF3F8D2F4F8D1F6FCD2F9FED2F9FE + CFF5FCCDFAFEDA9441E8AE72B06D16000000000000000000B06D16E9B076D991 + 3DCDFAFECFF5FC849092859394839091CFF5FCCDF1F6CEF3F8D1F6FCDAFDFF81 + 8D8E3731316E787AD2F9FECEF3F8CDF1F6CDF1F6CDF5FA839091859394839091 + D1F6FCCAF9FFD9913DE9B076B06D16000000000000000000B06D16EBB279D68D + 3AD0FDFFCAF4F9CDF5FACFF5FCCDF5FAC9F2F9C8F0F5C9F2F9CDF5FAD6FEFF7A + 86883C3434707B7DD2F9FEC8F0F5C8F0F5C8F0F5C9F2F9CDF5FACDF5FACDF5FA + CAF4F9D0FDFFD68D3AEBB279B06D16000000000000000000B06D16EDB77ED68D + 3ACFFCFFC6F1F7C8F0F5C8F0F5C8F0F5C8F0F5C8F0F5CAF4F9D0FDFF78848541 + 3838788485D0FDFFCAF4F9C8F0F5C8F0F5C8F0F5C8F0F5C8F0F5C8F0F5C8F0F5 + C6F1F7CFFCFFD68D3AEDB77EB06D16000000000000000000B5792CE4AB6DDA93 + 44CFFCFFC4EFF8C5EDF4C5EDF4C5EDF4C8F0F5C9F2F9CFFCFF7A8688483F3D7A + 8688D0FDFFC9F2F9C8F0F5C5EDF4C5EDF4C5EDF4C5EDF4C5EDF4C5EDF4C5EDF4 + C5F1F8CFFCFFDA9344E4AB6DB5792C000000000000000000C0955CD49954E19E + 58D2D5C4BEF2FDC4EFF8C3F0F7C4EFF8C5F1F8CAF9FF7D898A4E46437D898ACD + FAFEC5F1F8C1EDF5C2ECF3C2ECF3C5EDF4C2ECF3C2ECF3C3F0F7C3F0F7C3F0F7 + C1F0F9D2D5C4E19E58D49954C0955C000000000000000000000000BC7A29F0B5 + 7ACFB082C4F0FFC3F0F7758385C5F1F8CAF9FF818D8E554C4A818D8ECAF9FFC3 + F0F7C1EDF5C0EBF2C2ECF3C0EBF2C0EBF2C0EBF2C0EBF2C3F0F7758385C3F0F7 + C4F0FFCFB082F0B57ABC7A29000000000000000000000000000000B1711DE4AB + 6DE09B51C4D9D8BFECF9C1F0F9C1F0F98390915C524F849092C5F7FFC3F0F7BE + ECF3C1EDF5BEECF3BCEAF1BCEAF1BCEAF1BCEAF1BEECF3BEECF3BEEDF6BFECF9 + C4D9D8E09B51E4AB6DB1711D000000000000000000000000000000000000C181 + 33F2BA82D19B5AB9E4FABBECF5C1F0F9839091859394C5F7FFBEEDF6BEECF3BF + ECF9728386BFECF9BCEAF1BCEAF1BEECF3BEEDF6BEECF3BBECF5BBECF5B9E4FA + D19B5AF2BA82C18133000000000000000000000000000000000000000000B579 + 2CD99E5AEAAD6CC6A67AB0DEF5B8E7F4BEF2FD718588BEEDF6BAEAF4B9E8F1BE + EDF6718588BEEDF6B8E7F4B7E8F1BAEAF46D7E81BBECF5B6E4F1B0DEF5C6A67A + EDB071D99E5AB5792C0000000000000000000000000000000000000000000000 + 00AF6C14EEB880EAAD6CC59E6AACD3E6AEDDF0B7E8F5BAEAF4B7E8F1B7E8F1BB + ECF56E8082BBECF5B7E8F1B7E8F1BAEAF4B6E7F4AEDDF0ACD3E6C59E6AEAAD6C + EEB880AF6C140000000000000000000000000000000000000000000000000000 + 00000000B4711CF2BA82EDB071CF914CB5B6A7A2D2EEA6D3E9AADAEAAFE0EEB2 + E4F0B6E4F1B2E4F0AFE0EEAADAEAA6D3E9A2D2EEB5B6A7CF914CF0B57AEEB880 + B4711C0000000000000000000000000000000000000000000000000000000000 + 00000000000000AF6C14DCA362F9C692E39F57CA8B44B2AE99A4BFC899CDEC9A + CCEB9ACCEB9ACCEB99CDECA4BFC8B2AE99CA8B44E39F57F9C692DBA362AF6C13 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000B5792CC38437EEB880FCC996E6A561DA9142D0822CD0 + 822CD0822CD0822CD0822CDA9142E6A561FCC996EEB880C38437B5792C000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000B1711DBE7F2EDEA767F2C18DFFD5A8FF + D5A8FFD5A8FFD5A8FFD5A8F2C18DDEA767BE7F2EB1711D000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000C79B61B87C2FAF6B13AF + 6B13AF6B13AF6B13AF6B13B87C2FC79B61000000000000000000000000000000 + 000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFF0 + 07FFFF8000FFFF00007FFE00003FFC00001FF800000FF0000007E0000003E000 + 0003E0000003C0000001C0000001C0000001C0000001C0000001C0000001C000 + 0001C0000001C0000001E0000003E0000003F0000007F0000007F800000FFC00 + 001FFE00003FFF00007FFFC001FFFFF007FF2800000010000000200000000100 + 1800000000000003000064000000640000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000A2937E96 + 816596816599856C000000000000000000000000000000000000000000000000 + 000000000000A38660C27F2FD18F42D99749D99749D79547C9883AB47E3D0000 + 00000000000000000000000000000000000000AD7E45D79547E1C595E4EEE0E7 + FFFFE7FFFFE6FAF7E3DCBEDDA765C88639000000000000000000000000000000 + A77E4AD9994FE0E4D0CFE6E6E1FCFDE7FFFFBFD0D0E1FCFDCFE6E6E1FCFADEB6 + 7EC9883A000000000000000000000000D19147DCD6B7DCFCFEDCFAFCDBF9FBDC + FAFCCBE4E6DCFAFADCFAFCDCFAFCDCFAFADCA868B98443000000000000B88B52 + DCA868D6FAFDC5E0E3D7F6F9D9F9FCD9F9FCD9F9FCD7F6F9D7F6F9D9F9FCC5E1 + E4D8D6BBD19147000000000000C88A41D6C398D1F5F8D1F5F8D1F5F8AFC5C789 + 9595D6FAFDD1F5F8D1F5F8D1F5F8D1F5F8D2F4F4DD9D56A7967F000000CC8E45 + D2C59DBCDCE1ABC4C7CEF3F8D1F5F88995957C8A8ACDF5FACDF1F6BDDBE0ABC4 + C7CDF8FDE0A059A4927A000000CE9149D2C59DC9F2F8C9F2F8C9F1F6CDF5FA82 + 91927D8C8DC9F2F8C8EFF4C9F1F6C9F2F8CBF7FBE0A059A4927A000000C7904E + D7C097C3EDF5C4EDF5C7F2F9859495829192C7F2F9C2ECF3C2ECF3C4EDF5C4ED + F5C9EBEDDD9D56000000000000B49976E0AB6EC2E9F2AED5DB89999A87989AC2 + F1F8BFECF3BEEAF1BEEAF1BFECF3ADD3DBCDC5AACE9149000000000000000000 + CF934ECEB48EB9E8F68EA6AABEEEF7BCEBF497B8BEBAE8F2A8D0D7BAE8F2BAD4 + D7E2A869B69B78000000000000000000000000DFA463CBAC83AFD3DEAFE0EEB4 + E6F1A3CDD6B1E2EEADDDEFB7BDB2E3AE74C7985D000000000000000000000000 + 000000000000D29854E5AA6CC5A881B5A78CB5A78BB9A789D7A975E2A869C095 + 5E000000000000000000000000000000000000000000000000B99E7ACF9B5BD7 + A05DD7A05DD49D5BCC9E64000000000000000000000000000000FFFF0000FC3F + 0000F00F0000E00700B2C003D291C0019A51800155E28000E5FF8000FFFF8000 + FFE18001E5FF8001FFFFC001A1E5E003E5FFF007FEFEF81FFFE5280000002000 + 0000400000000100200000000000001000006400000064000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000001000000070000000E00000013000000160000 + 0016000000160000001600000016000000130000000E00000007000000010000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000002000000090000001500000024000000320000003C000000410000 + 00430000004300000043000000410000003C0000003200000024000000150000 + 0009000000020000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000600000014000000280000003C6741107C996117C3AA6B19EAB2701AFFB270 + 19FFB27019FFB27019FFB2701AFFAA6B19EA996117C36741107C0000003C0000 + 0028000000140000000600000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000001000000070000 + 001C000000377349128AB06F1AF9B77521FFC48032FFCB883BFFD28D43FFD18D + 42FFD18D42FFD18D42FFD28D43FFCB883BFFC48032FFB77521FFB06F1AF97349 + 128A000000370000001C00000007000000010000000000000000000000000000 + 00000000000000000000000000000000000000000001000000080000001F1910 + 0446AB6C19EDBA7724FFCC883CFFD59048FFDB9B4DFFDD9C4EFFE2A352FFE2A2 + 51FFE2A250FFE2A251FFE2A352FFDD9C4EFFDB9B4DFFD59048FFCC883CFFBA77 + 24FFAB6C19ED191004460000001F000000080000000100000000000000000000 + 000000000000000000000000000000000000000000070000001F3D260956B26F + 19FFC68436FFD6934BFFDC9B4DFFE1A75AFFE3CEA3FFE7EEE0FFE9FFFFFFE9FF + FFFFE9FFFFFFE9FFFFFFE9FFFFFFE7EEE0FFE3CEA3FFE1A75AFFDC9B4DFFD693 + 4BFFC68436FFB26F19FF3D2609560000001F0000000700000000000000000000 + 0000000000000000000000000000000000060000001C3C260953B4711CFFD18F + 47FFD9974EFFE09E4DFFE2D3B0FFE6FFFFFFE5FFFFFFE4FFFFFFE4FFFFFFE6FF + FFFFE7FFFFFFE6FFFFFFE4FFFFFFE4FFFFFFE5FFFFFFE6FFFFFFE2D3B0FFE09E + 4DFFD9974EFFD18F47FFB4711CFF3C2609530000001C00000006000000000000 + 0000000000000000000000000002000000141E130541B16F18FFD29149FFDB99 + 50FFDFA256FFE2F3ECFFE4FFFFFFE5FFFFFFE3FFFFFFE2FDFEFFE3FDFEFFE6FF + FFFF99A1A1FFE6FFFFFFE3FDFEFFE2FDFEFFE3FFFFFFE5FFFFFFE4FFFFFFE2F3 + ECFFDFA256FFDB9950FFD29149FFB16F18FF1E13054100000014000000020000 + 000000000000000000000000000900000028AC6C19ECC9883CFFDB9B52FFDEA1 + 54FFE0FFFFFFE0FFFFFFE1FFFFFF959D9CFFE1FEFFFFDFFBFCFFE0FCFDFFE4FF + FFFF99A3A1FFE4FFFFFFE0FCFDFFDFFBFCFFE1FEFFFF959D9CFFE1FFFFFFE0FF + FFFFE0FFFFFFDEA154FFDB9B52FFC9883CFFAC6C19EC00000028000000090000 + 00000000000000000001000000157D4F1381BC7A28FFDC9C59FFDE9A49FFDEF1 + EBFFDEFFFFFFDEFCFDFFDEFCFDFFDFFDFEFFDEFCFDFFDDFAFBFFDEFBFCFFE1FF + FFFF949E9EFFE1FFFFFFDEFBFCFFDDFAFBFFDEFCFDFFDFFDFEFFDEFCFDFFDEFC + FDFFDEFFFFFFDEF1EBFFDE9A49FFDC9C59FFBC7A28FF7D4F1381000000150000 + 0001000000000000000700000024B06E19F9D3934DFFDD9C50FFDCCFABFFDBFF + FFFFDCFCFFFFDBFAFDFFDAF8FCFFDAF8FCFFDAF8FBFFDAF8FBFFDAF8FBFFDCFA + FDFFDDFBFEFFDCFAFDFFDAF8FBFFDAF8FBFFDAF8FBFFDAF8FCFFDAF8FCFFDBFA + FDFFDCFCFFFFDBFFFFFFDCCFABFFDD9C50FFD3934DFFB06E19F9000000240000 + 0007000000000000000E774B136BB97825FFE0A15EFFDC9E52FFD8FAFFFFDAFD + FFFF8E9798FFDAFAFDFFD8F7FAFFD8F7FAFFD8F7FAFFD9F8FBFFD9F8FBFFD8F8 + FBFFD8F8FBFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFFDAFA + FDFF8E9798FFDAFDFFFFD8FAFFFFDC9E52FFE0A15EFFB97825FF774B136B0000 + 000E00000000000000139E6417BCCD8D44FFDF9E55FFD8C59CFFD6FDFFFFD7F9 + FDFFD8F9FCFFD7F8FBFFD6F6F9FFD6F6F9FFD8F9FCFFDBFDFFFFDCFDFFFFD9FA + FDFFD7F7FAFFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FFD7F8 + FBFFD8F9FCFFD7F9FDFFD6FDFFFFD8C59CFFDF9E55FFCD8D44FF9E6417BC0000 + 00130000000000000016AB6B17E8DA9D5AFFDC984AFFD5E4D8FFD3F9FFFFD3F6 + F9FFD3F5F8FFD3F5F8FFD3F5F8FFD4F6F9FFD8FCFFFF868D8EFF828A8BFFDDFF + FFFFD6FAFDFFD3F6F9FFD3F5F8FFD3F5F8FFD3F5F8FFD3F5F8FFD3F5F8FFD3F5 + F8FFD3F5F8FFD3F6F9FFD3F9FFFFD5E4D8FFDD994BFFDA9D5AFFAB6B17E80000 + 00160000000000000016B16E17FFE7AC6FFFDA9441FFD0FFFFFFD1F7FCFFD2F5 + F9FFD2F5F9FFD2F5F9FFD1F4F8FFD2F5F9FFD6FBFFFF899190FF4F4745FF7C88 + 88FFDDFFFFFFD5F9FDFFD2F5F9FFD1F4F8FFD1F4F8FFD1F4F8FFD1F4F8FFD2F5 + F9FFD2F5F9FFD2F5F9FFD1F7FCFFD0FFFFFFDA9341FFE7AC6FFFB16E17FF0000 + 00160000000000000016B06E16FFE8AE72FFD9923EFFCDFBFFFFCFF5FCFFD2F7 + FCFFD3F8FDFFD2F7FCFFD0F4F9FFCEF3F8FFD1F5FAFFD7FFFFFF848E8EFF433D + 3BFF748080FFDAFFFFFFD1F6FBFFCEF2F7FFCEF2F7FFCEF2F7FFD0F4F9FFD2F7 + FCFFD3F8FDFFD2F7FCFFCFF5FCFFCDFBFFFFD9923EFFE8AE72FFB06E16FF0000 + 00160000000000000016B06D16FFE9B076FFD9903DFFCBFAFFFFCFF6FCFF8390 + 91FF859394FF839091FFCFF4FAFFCCF1F7FFCDF2F7FFD0F6FCFFDAFFFFFF818B + 8DFF373131FF6E787AFFD3FAFFFFCDF2F7FFCCF1F6FFCCF1F6FFCFF4FAFF8390 + 91FF859394FF839091FFCFF6FCFFCBFAFFFFD9903DFFE9B076FFB06D16FF0000 + 00160000000000000016B06D16FFEBB279FFD78E3BFFD0FDFFFFCAF3FAFFCDF5 + FAFFCEF6FBFFCDF5FAFFCAF2F7FFC9F0F5FFC9F1F6FFCDF6FBFFD7FFFFFF7987 + 89FF3C3434FF707B7DFFCFF9FEFFCAF1F6FFC9F0F5FFC9F0F5FFCAF2F7FFCDF5 + FAFFCEF6FBFFCDF5FAFFCAF3FAFFD0FDFFFFD78E3BFFEBB279FFB06D16FF0000 + 00160000000000000013B06D16FFEDB77EFFD68D3AFFCFFCFFFFC6F1F7FFC8F0 + F5FFC8F0F5FFC8F0F5FFC7EFF4FFC8F0F5FFCAF4F9FFD2FDFFFF788485FF4138 + 38FF778385FFD2FEFFFFCAF3F8FFC7EFF4FFC7EFF4FFC7EFF4FFC7EFF4FFC8F0 + F5FFC8F0F5FFC8F0F5FFC6F1F7FFCFFCFFFFD68D3AFFEDB77EFFB06D16FF0000 + 0013000000000000000EAC6B16E6E3AA6CFFDA9344FFD0FBFFFFC4EFF8FFC5ED + F5FFC5EDF5FFC5EDF4FFC6EEF5FFC8F1F9FFCFFBFFFF7B8688FF483F3DFF7A86 + 88FFD0FCFFFFC9F2F9FFC6EEF5FFC5EDF4FFC5EDF4FFC5EDF4FFC5EDF4FFC5ED + F4FFC5EDF5FFC5EDF5FFC4EFF8FFD0FBFFFFDA9344FFE3AA6CFFAC6B16E60000 + 000E0000000000000007A66917B3D49954FFE19E58FFD2D5C4FFC1F0FBFFC3EE + F6FFC4EFF6FFC4EEF6FFC5F1F8FFCBF9FFFF7E8A8BFF4E4643FF7D898BFFCCFA + FFFFC5F1F8FFC2EDF4FFC2ECF3FFC2ECF3FFC2ECF3FFC2ECF3FFC2ECF3FFC3EE + F5FFC4EFF6FFC3EEF6FFC1F0FBFFD2D5C4FFE19E58FFD49954FFA66917B30000 + 00070000000000000001945E1656BC7B2AFFF0B67BFFCFB082FFC4F0FFFFC2F0 + F8FF758385FFC5F1F9FFC8F8FFFF808D8FFF554C4AFF808D8EFFC9F9FFFFC3F0 + F7FFC1EDF4FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF3FFC2EE + F5FF758385FFC2F0F8FFC4F0FFFFCFB082FFF0B67BFFBC7B2AFF945E16560000 + 0001000000000000000000000009AF6D16F7E4AB6DFFE09B51FFC4D9D8FFBFEC + F9FFBFEFF7FFC2F2FAFF828E8EFF5C524FFF849092FFC6F7FFFFC0EFF6FFBFED + F4FFC0EEF5FFBFECF3FFBDEAF1FFBDEAF1FFBDEAF1FFBDEBF1FFBDEBF2FFBEEC + F3FFBFEFF6FFBFECF9FFC4D9D8FFE09B51FFE4AB6DFFAF6D16F7000000090000 + 00000000000000000000000000029D621766C18133FFF2BA83FFD19B5AFFB9E4 + FAFFBBEBF6FFC0F1FAFF838F8FFF869394FFC4F7FFFFBEEDF6FFBCEBF3FFBFEE + F7FF728386FFBFEEF7FFBCEAF2FFBBE9F1FFBDEBF3FFBDECF5FFBDEBF3FFBBEB + F4FFBBEAF5FFB9E4FAFFD19B5AFFF2BA82FFC18133FF9D621767000000020000 + 000000000000000000000000000000000006AF6D17E7D99F5BFFEAAD6CFFC6A6 + 7AFFB1DEF5FFB8E7F4FFBEF2FDFF718588FFBDEEF7FFB9E8F2FFB9E8F2FFBDED + F7FF718487FFBDEDF7FFB9E8F2FFB8E8F1FFBBEBF4FF6D7E81FFBBEDF6FFB6E4 + F1FFB0DEF5FFC6A67AFFEDB071FFD99F5BFFAF6D17E700000006000000000000 + 0000000000000000000000000000000000018E5A1527AF6C14FFEEB880FFEBAE + 6DFFC69E6BFFACD3E6FFAFDDF1FFB7E8F5FFB8EBF4FFB7E8F1FFB7E8F1FFBBEC + F5FF6E8082FFBBECF5FFB7E8F1FFB7E8F1FFB8EBF4FFB6E7F4FFAEDDF0FFACD3 + E6FFC59E6AFFEBAE6DFFEEB880FFAF6C14FF6E45111200000000000000000000 + 000000000000000000000000000000000000000000019D621738B4721CFFEFBA + 83FFEDB172FFCF914CFFB5B6A7FFA2D2EEFFA6D3E9FFAADAEAFFAFE0EEFFB2E4 + F0FFB3E5F2FFB2E4F0FFAFE0EEFFAADAEAFFA6D3E9FFA2D2EEFFB5B6A7FFCF91 + 4CFFF0B578FFEFBA82FFB4721CFF8F5A15230000000100000000000000000000 + 0000000000000000000000000000000000000000000000000001935C1522AF6C + 14FFDCA362FFF9C692FFE39F57FFCA8B44FFB2AE99FFA4BFC8FF99CDECFF9ACC + EBFF9ACCEBFF9ACCEBFF99CDECFFA4BFC8FFB2AE99FFCA8B44FFE39F57FFF9C6 + 92FFDBA362FFAF6C13FF945D1623000000010000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000007046 + 1110AF6C16E6C38437FFEDB880FFFCC996FFE6A561FFDA9043FFD0822DFFD182 + 2CFFD1822CFFD1822CFFD0822DFFDA9043FFE6A561FFFCC996FFEDB880FFC384 + 37FFAF6C16E67046111000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000002A769175FAF6D15F7BE7F2EFFDEA767FFF2C18DFFFFD5A9FFFFD5 + A8FFFFD5A8FFFFD5A8FFFFD5A9FFF2C18DFFDEA767FFBE7F2EFFAF6D15F7A769 + 175F000000020000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000001A769184CAC6B16ACAF6C15E2AF6B13FFAF6B + 13FFAF6B13FFAF6B13FFAF6B13FFAF6C15E2AC6B16ACA769184C000000010000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000FFFFFFFFFFC001FFFF00007FFE00003FF800000FF0000007F0000007E000 + 0003C0000001C000000180000000800000008000000080000000800000008000 + 000080000000800000008000000080000000800000008000000080000000C000 + 0001C0000001E0000003E0000007F0000007F800000FFE00003FFF00007FFFC0 + 01FF280000001000000020000000010020000000000000040000640000006400 + 0000000000000000000000000000000000000000000000000000000000000000 + 0002000000080000000B0000000B0000000A0000000500000000000000000000 + 0000000000000000000000000000000000000000000000000002000000121910 + 043C50330C8759380CA059380CA157360C9A4028096500000020000000070000 + 0000000000000000000000000000000000000000000206040122764B13ABC27F + 2FFED18F42FFD9974AFFD99749FFD79547FFCA883AFFAA6D23E0311F07620000 + 000C000000000000000000000000000000020F0902259D6521D5D59347FFE1C5 + 95FFE4EEE0FFE7FFFFFFE7FFFFFFE6FAF7FFE3DCBEFFDDA765FFC88639FF4B2F + 0B720000000A000000000000000000000012915D1CCBD9994FFFE0E4D0FFCFE6 + E6FFE1FDFEFFE3FDFEFFBFD0D0FFE1FCFDFFCFE6E6FFE1FCFAFFDEB67EFFC988 + 3BFF321F075A00000003000000024B2F0B6DD29147FFDCD6B7FFDCFCFEFFDCFA + FCFFDBF9FBFFDDFBFCFFCBE4E6FFDBF9FBFFDCFAFCFFDCFAFCFFDCFAFAFFDCA8 + 67FFAF7228DE0000001000000008A66D24C9DCA868FFD7FBFEFFC5E0E3FFD7F6 + F9FFD9F9FCFFD9F9FCFFD7F7FAFFD7F6F9FFD7F6F9FFD7F7FAFFC5E1E4FFD8D6 + BBFFD19147FF452B0A520000000BC7883DF9D6C398FFD2F6FBFFD2F5F8FFD2F5 + F8FFAFC5C7FF8A9695FFD6FAFCFFD2F4F8FFD2F4F8FFD2F4F8FFD2F5F8FFD2F4 + F4FFDE9D55FF57360B850000000BCC8E45FFD2C59EFFBCDCE1FFABC4C7FFCEF3 + F8FFD1F7FBFF889595FF7C8A8AFFCFF5FAFFCDF1F6FFBDDBE0FFABC4C7FFCDF8 + FDFFE0A058FF58360B8A0000000ACE9048FFD3C59CFFC9F2F8FFCAF2F7FFC8F0 + F5FFCCF6FAFF829091FF7D8C8DFFCAF3F8FFC8EFF4FFC9F1F6FFCAF2F7FFCBF7 + FBFFE1A15BFF58360B8A00000005C2853BE6D7C097FFC3EEF7FFC4EDF5FFC7F2 + F9FF859495FF829293FFC8F3F9FFC3ECF3FFC3ECF3FFC3EDF4FFC4EDF5FFC9EB + EDFFDC9D57FF54350B6C000000007F511595E0AB6EFFC2E9F2FFAED5DBFF8999 + 9AFF87989AFFC2F1F8FFC0ECF3FFBEEAF1FFBEEAF1FFBFECF3FFADD3DBFFCDC5 + AAFFCF924AFD311F0718000000003420071CCE914AF9CEB48EFFB9E8F6FF8EA6 + AAFFBEEEF7FFBCEBF4FF97B8BEFFBAE8F1FFA8D0D7FFBAE9F3FFBAD4D7FFE2A9 + 6AFF83541895000000000000000000000000764A1058DFA463FFCBAC83FFAFD3 + DEFFAFE0EEFFB4E6F1FFA3CDD6FFB1E2EEFFADDDEFFFB7BDB2FFE3AE74FFB87C + 31C83722080500000000000000000000000000000000905A134CD19650F9E5AA + 6CFFC5A881FFB5A78CFFB5A78BFFB9A789FFD7A975FFE3A868FFAA7027BD4A2E + 0B0900000000000000000000000000000000000000000000000053340B188555 + 1691CA8F47E3D7A05DFFD7A05DFFD39B57F8BB7E30BE72470E56000000000000 + 00000000000000000000F81F0000E0070000C003000080010000800000000000 + 00000000FFFF0000FFFF0000FFFF0000F1F10000B7B78000959580019393C001 + 9393E0039393F00F9696 + } + Position = poDesktopCenter + LCLVersion = '2.0.10.0' + object Button1: TButton + Left = 40 + Height = 26 + Top = 88 + Width = 88 + Caption = 'Ok' + OnClick = Button1Click + TabOrder = 0 + end + object Label1: TLabel + Left = 72 + Height = 15 + Top = 41 + Width = 53 + Caption = 'Time''s Up' + ParentColor = False + end + object Image1: TImage + Left = 24 + Height = 32 + Top = 33 + Width = 32 + Picture.Data = { + 055449636F6E662300000000010004002020000001000800A808000046000000 + 101000000100080068050000EE0800002020000001002000A8100000560E0000 + 101000000100200068040000FE1E000028000000200000004000000001000800 + 0000000000000000000000000000000000000000000000000000000037313100 + 3C34340041383800433D3B00483F3D004E4643004F474500554C4A005C524F00 + 6E787A00707B7D00AF6B1300AF6C1300AF6C1400B06D1600B16F1800B2701900 + B1711D00B4711C00B7752100BA772400B9782500B1762A00B2782B00B5792C00 + BC7A2900B87C2F00BE7F2E00D0822C00C1813300C4803200C3843700C6843600 + CB883B00C9883C00CC883C00D68D3A00D9913D00B1864D00B78C5300B39C7E00 + CA8B4400CD8D4400D18D4200D18F4700CF914C00DA914200DA944100DA934400 + D2914900D5914900D3934D00D9974E00DD994900DB9B4D00DC9B4D00DD9C4E00 + C0955C00D4995400DB9A5100DC9D5100DF9E5500D19B5A00D99E5A00DC9C5900 + E09E4D00E09B5100E39F5700E19E5800DEA15500E2A25100E1A75A00E0A15E00 + C79B6100C59E6A00DBA36200DCA36200DEA76700C6A67A00E6A56100E3AA6C00 + E4AB6D00E7AC6F00EAAD6C00E8AE7200EDB07100E9B07600EBB27900EDB77E00 + F0B57A006D7E81006E8082007480800072838600758385007184870078848500 + 718588007A8688007D898A00828A8B00818B8D00818D8E00858D8E0083909100 + 8490920085939400899190008E979800949D9C00BDA58700B2AE990099A2A100 + B5B6A700CFB08200EEB88000F2BA8200D8C59C00F2C18D00F9C69200FCC99600 + DCCFAB00E3CEA300FFD5A800E2D3B000A4BFC8009ACCEB0099CDEC00ACD3E600 + A6D3E900A2D2EE00AADAEA00AEDDF000B0DEF500AFE0EE00B2E4F000B6E4F100 + B6E7F400B8E7F400B7E8F100B7E8F500B9E8F100BCEAF100BEECF300BAEAF400 + BBECF500BEEDF600B9E4FA00BFECF900BEF2FD00D2D5C400C4D9D800D5E4D800 + DEF1EB00C0EBF200C2ECF300C1EDF500C5EDF400C4EFF800C3F0F700C6F1F700 + C8F0F500CDF1F600C1F0F900C5F1F800C4F0FF00C5F7FF00C9F2F900CEF3F800 + CAF4F900CDF5FA00CFF5FC00CAF9FF00CDFAFE00CFFCFF00D2F4F800D5F6F900 + D1F6FC00D8F7FA00D7F8FB00D2F9FE00D6F9FD00D0FDFF00D6FEFF00D9F8FB00 + DDFAFB00D9F9FC00DDFAFC00DAFDFF00DDFDFE00E7EEE000E2F3EC00E1FEFE00 + E5FFFF00E9FFFF0090006200B0007800CF008E00F000A400FF11B300FF31BE00 + FF51C700FF71D100FF91DC00FFB1E500FFD1F000FFFFFF00000000002C002F00 + 4B0050006900700087009000A500B000C400CF00E100F000F011FF00F231FF00 + F451FF00F671FF00F791FF00F9B1FF00FBD1FF00FFFFFF00000000001B002F00 + 2D0050003F007000520090006300B0007600CF008800F0009911FF00A631FF00 + B451FF00C271FF00CF91FF00DCB1FF00EBD1FF00FFFFFF000000000008002F00 + 0E005000150070001B0090002100B0002600CF002C00F0003E11FF005831FF00 + 7151FF008C71FF00A691FF00BFB1FF00DAD1FF00FFFFFF000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000027181111131112182700000000000000000000000000000000000000 + 002912141F242C2C2C2C2C241F14122900000000000000000000000000000000 + 1715243338394747474747393733241517000000000000000000000000000011 + 213339487BBFC3C3C3C3C3BF7B48393321110000000000000000000000001332 + 35427DC2C2C2C2C2C3C2C2C2C2C27D423532130000000000000000000011323C + 46C0C2C2C2C1C2C271C2C2C1C2C2C2C0463C3211000000000000000017233D46 + C1C1C16EC1BCC1C271C2C1BCC16EC1C1C1463D23180000000000006F1A41369A + BEBEBEC1C1BCBCC16EC2BCBABEC1BEBEBE9A36411A6F000000000012343D7ABE + BEBCBBB9B9B9BBBCBEBCB9B9B9BBB9BCBEBD7A3D3412000000000016493DBDBD + 6DBDB3B3B9B9B9B4B9B3B3B3B3B3B3BD6DBDBD3D491600000000282B3E76B8B6 + BBB4B1B1BBBDBEBDB3B1B1B1B3B1B1B4BBB6B8763E2B2800000018403699B5B1 + B1B1B0B1BD6865BEB6B1B0B0B0B1B1B1B0B1B599364018000000105330B7B5B0 + B2B0B0B0B86C0764BEB6B1B0B0B0B0B0B0B0B2B7305310000000105526AFACB2 + B6B2B0A9B2B868045DBDB2A9A9A9B0B2B5B5ACAE30550F0000000F5726AEAC6A + 6B69ACA3A9B2BD67010AB5A9A3A3AB696B69B2AD26570F0000000F5825B7AAAB + ACABA8A2A8ABB863020BB5A2A2A2A8ABABABAAB725580F0000000F5925AFA1A2 + A2A2A2A2AAB7610361B7AAA2A2A2A2A2A2A2A1AF25590F000000195231AF9F9E + 9E9EA2A8AF630563B7A8A29E9E9E9E9E9E9EA5AF3152190000003A3B4597969F + A09FA5AD640664AEA59D9C9C9E9C9CA0A0A0A497453B3A000000001A5A73A6A0 + 5FA5AD670867ADA09D9B9C9B9B9B9BA05FA0A6735A1A00000000001252439895 + A4A469096AA7A0909D908F8F8F8F90909395984352120000000000001E753F94 + 92A4696BA79390955E958F8F9093909292943F751E000000000000001940544F + 868B966293918E9362938B8C915B9289864F56401900000000000000000E7454 + 4B81858D918C8C925C928C8C918A85814B54740E000000000000000000001375 + 562E728382848788898887848283722E5A74130000000000000000000000000E + 4D78442A707E807F7F7F807E702A44784C0D0000000000000000000000000000 + 19207479502F1D1D1D1D1D2F5079742019000000000000000000000000000000 + 0000121C4E777C7C7C7C7C774E1C120000000000000000000000000000000000 + 000000004A1B0C0C0C0C0C1B4A0000000000000000000000FFFFFFFFFFFFFFFF + FFFFFFFFFFF007FFFF8000FFFF00007FFE00003FFC00001FF800000FF0000007 + E0000003E0000003E0000003C0000001C0000001C0000001C0000001C0000001 + C0000001C0000001C0000001C0000001E0000003E0000003F0000007F0000007 + F800000FFC00001FFE00003FFF00007FFFC001FFFFF007FF2800000010000000 + 2000000001000800000000000000000000000000000000000000000000000000 + 00000000B47E3D00C27F2F00AD7E4500A77E4A00C8863900C9883A00B9844300 + B88B52009681650099856C00A3866000A4927A00A2937E00A7967F00B4997600 + B69B7800B99E7A00C88A4100CC8E4500D18F4200C7904E00CE914900CF934E00 + D1914700D5934700D7954700D9974900D9994F00C0955E00C7985D00CF9B5B00 + D2985400DD9D5600D49D5B00D7A05D00E0A05900CC9E6400DFA46300DDA76500 + DCA86700DCA86800D7A97500DEB67E00E2A86900E0AB6E00E5AA6C00E3AE7400 + 7C8A8A007D8C8D0082919200859495008995950087989A0089999A00B5A78B00 + B5A78C00B9A789008EA6AA0097B8BE00B7BDB200C5A88100CBAC8300CEB48E00 + D7C09700D6C39800D2C59D00E1C59500CDC5AA00DCD6B700D8D6BB00E3DCBE00 + ABC4C700AFC5C700A3CDD600A8D0D700ADD3DB00AED5DB00AFD3DE00BFD0D000 + BAD4D700ADDDEF00BDDBE000BCDCE100AFE0EE00B1E2EE00B4E6F100BAE8F200 + BEEAF100BFECF300B9E8F600BCEBF400BEEEF700E0E4D000C5E0E300C5E1E400 + CBE4E600CFE6E600C9EBED00C2E9F200C2ECF300C3EDF500C4EDF500C8EFF400 + C9F1F600CDF1F600C2F1F800C7F2F900C9F2F800CEF3F800CBF7FB00CDF5FA00 + CDF8FD00D2F4F400D1F5F800D7F6F900D6FAFD00DBF9FB00DCFAFA00D9F9FC00 + DCFAFC00DCFCFE00E4EEE000E6FAF700E1FCFA00E1FCFD00E7FFFF00FFFFFF00 + 000000002F26000050410000705B000090740000B08E0000CFA90000F0C30000 + FFD21100FFD83100FFDD5100FFE47100FFEA9100FFF0B100FFF6D100FFFFFF00 + 000000002F1400005022000070300000903E0000B04D0000CF5B0000F0690000 + FF791100FF8A3100FF9D5100FFAF7100FFC19100FFD2B100FFE5D100FFFFFF00 + 000000002F030000500400007006000090090000B00A0000CF0C0000F00E0000 + FF201200FF3E3100FF5C5100FF7A7100FF979100FFB6B100FFD4D100FFFFFF00 + 000000002F000E00500017007000210090002B00B0003600CF004000F0004900 + FF115A00FF317000FF518600FF719C00FF91B200FFB1C800FFD1DF00FFFFFF00 + 000000002F0020005000360070004C0090006200B0007800CF008E00F000A400 + FF11B300FF31BE00FF51C700FF71D100FF91DC00FFB1E500FFD1F000FFFFFF00 + 000000002C002F004B0050006900700087009000A500B000C400CF00E100F000 + F011FF00F231FF00F451FF00F671FF00F791FF00F9B1FF00FBD1FF00FFFFFF00 + 000000001B002F002D0050003F007000520090006300B0007600CF008800F000 + 9911FF00A631FF00B451FF00C271FF00CF91FF00DCB1FF00EBD1FF00FFFFFF00 + 0000000008002F000E005000150070001B0090002100B0002600CF002C00F000 + 3E11FF005831FF007151FF008C71FF00A691FF00BFB1FF00DAD1FF00FFFFFF00 + 000000000000000000000000000000000000000000000D09090A000000000000 + 000000000B02141B1B1A060100000000000000031A437A7E7E7B472705000000 + 0000041C5D617D7E4F7D617C2B06000000001845797875786076787876290700 + 000829745E737777777373775F4618000012417272724934747272727271210E + 00134253486D7234306F69524870240C0016426C6C686F32316C67686C6E240C + 00154065666B33326B64646666622100000F2D634D36356A595858594C441600 + 0000173F5A3A5C5B3B574B57502C1000000000263E4E54564A55513C2F1E0000 + 00000000202E3D3837392A2C1D0000000000000000111F232322250000000000 + FFFF0000FC3F0000F00F0000E0070000C0030000C00100008001000080000000 + 80000000800000008001000080010000C0010000E0030000F0070000F81F0000 + 2800000020000000400000000100200000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000001000000070000000E00000013 + 0000001600000016000000160000001600000016000000130000000E00000007 + 0000000100000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000002000000090000001500000024000000320000003C + 00000041000000430000004300000043000000410000003C0000003200000024 + 0000001500000009000000020000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000600000014000000280000003C6741107C996117C3AA6B19EA + B2701AFFB27019FFB27019FFB27019FFB2701AFFAA6B19EA996117C36741107C + 0000003C00000028000000140000000600000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000001 + 000000070000001C000000377349128AB06F1AF9B77521FFC48032FFCB883BFF + D28D43FFD18D42FFD18D42FFD18D42FFD28D43FFCB883BFFC48032FFB77521FF + B06F1AF97349128A000000370000001C00000007000000010000000000000000 + 0000000000000000000000000000000000000000000000000000000100000008 + 0000001F19100446AB6C19EDBA7724FFCC883CFFD59048FFDB9B4DFFDD9C4EFF + E2A352FFE2A251FFE2A250FFE2A251FFE2A352FFDD9C4EFFDB9B4DFFD59048FF + CC883CFFBA7724FFAB6C19ED191004460000001F000000080000000100000000 + 000000000000000000000000000000000000000000000000000000070000001F + 3D260956B26F19FFC68436FFD6934BFFDC9B4DFFE1A75AFFE3CEA3FFE7EEE0FF + E9FFFFFFE9FFFFFFE9FFFFFFE9FFFFFFE9FFFFFFE7EEE0FFE3CEA3FFE1A75AFF + DC9B4DFFD6934BFFC68436FFB26F19FF3D2609560000001F0000000700000000 + 0000000000000000000000000000000000000000000000060000001C3C260953 + B4711CFFD18F47FFD9974EFFE09E4DFFE2D3B0FFE6FFFFFFE5FFFFFFE4FFFFFF + E4FFFFFFE6FFFFFFE7FFFFFFE6FFFFFFE4FFFFFFE4FFFFFFE5FFFFFFE6FFFFFF + E2D3B0FFE09E4DFFD9974EFFD18F47FFB4711CFF3C2609530000001C00000006 + 0000000000000000000000000000000000000002000000141E130541B16F18FF + D29149FFDB9950FFDFA256FFE2F3ECFFE4FFFFFFE5FFFFFFE3FFFFFFE2FDFEFF + E3FDFEFFE6FFFFFF99A1A1FFE6FFFFFFE3FDFEFFE2FDFEFFE3FFFFFFE5FFFFFF + E4FFFFFFE2F3ECFFDFA256FFDB9950FFD29149FFB16F18FF1E13054100000014 + 000000020000000000000000000000000000000900000028AC6C19ECC9883CFF + DB9B52FFDEA154FFE0FFFFFFE0FFFFFFE1FFFFFF959D9CFFE1FEFFFFDFFBFCFF + E0FCFDFFE4FFFFFF99A3A1FFE4FFFFFFE0FCFDFFDFFBFCFFE1FEFFFF959D9CFF + E1FFFFFFE0FFFFFFE0FFFFFFDEA154FFDB9B52FFC9883CFFAC6C19EC00000028 + 00000009000000000000000000000001000000157D4F1381BC7A28FFDC9C59FF + DE9A49FFDEF1EBFFDEFFFFFFDEFCFDFFDEFCFDFFDFFDFEFFDEFCFDFFDDFAFBFF + DEFBFCFFE1FFFFFF949E9EFFE1FFFFFFDEFBFCFFDDFAFBFFDEFCFDFFDFFDFEFF + DEFCFDFFDEFCFDFFDEFFFFFFDEF1EBFFDE9A49FFDC9C59FFBC7A28FF7D4F1381 + 0000001500000001000000000000000700000024B06E19F9D3934DFFDD9C50FF + DCCFABFFDBFFFFFFDCFCFFFFDBFAFDFFDAF8FCFFDAF8FCFFDAF8FBFFDAF8FBFF + DAF8FBFFDCFAFDFFDDFBFEFFDCFAFDFFDAF8FBFFDAF8FBFFDAF8FBFFDAF8FCFF + DAF8FCFFDBFAFDFFDCFCFFFFDBFFFFFFDCCFABFFDD9C50FFD3934DFFB06E19F9 + 0000002400000007000000000000000E774B136BB97825FFE0A15EFFDC9E52FF + D8FAFFFFDAFDFFFF8E9798FFDAFAFDFFD8F7FAFFD8F7FAFFD8F7FAFFD9F8FBFF + D9F8FBFFD8F8FBFFD8F8FBFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFFD8F7FAFF + D8F7FAFFDAFAFDFF8E9798FFDAFDFFFFD8FAFFFFDC9E52FFE0A15EFFB97825FF + 774B136B0000000E00000000000000139E6417BCCD8D44FFDF9E55FFD8C59CFF + D6FDFFFFD7F9FDFFD8F9FCFFD7F8FBFFD6F6F9FFD6F6F9FFD8F9FCFFDBFDFFFF + DCFDFFFFD9FAFDFFD7F7FAFFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FFD6F6F9FF + D6F6F9FFD7F8FBFFD8F9FCFFD7F9FDFFD6FDFFFFD8C59CFFDF9E55FFCD8D44FF + 9E6417BC000000130000000000000016AB6B17E8DA9D5AFFDC984AFFD5E4D8FF + D3F9FFFFD3F6F9FFD3F5F8FFD3F5F8FFD3F5F8FFD4F6F9FFD8FCFFFF868D8EFF + 828A8BFFDDFFFFFFD6FAFDFFD3F6F9FFD3F5F8FFD3F5F8FFD3F5F8FFD3F5F8FF + D3F5F8FFD3F5F8FFD3F5F8FFD3F6F9FFD3F9FFFFD5E4D8FFDD994BFFDA9D5AFF + AB6B17E8000000160000000000000016B16E17FFE7AC6FFFDA9441FFD0FFFFFF + D1F7FCFFD2F5F9FFD2F5F9FFD2F5F9FFD1F4F8FFD2F5F9FFD6FBFFFF899190FF + 4F4745FF7C8888FFDDFFFFFFD5F9FDFFD2F5F9FFD1F4F8FFD1F4F8FFD1F4F8FF + D1F4F8FFD2F5F9FFD2F5F9FFD2F5F9FFD1F7FCFFD0FFFFFFDA9341FFE7AC6FFF + B16E17FF000000160000000000000016B06E16FFE8AE72FFD9923EFFCDFBFFFF + CFF5FCFFD2F7FCFFD3F8FDFFD2F7FCFFD0F4F9FFCEF3F8FFD1F5FAFFD7FFFFFF + 848E8EFF433D3BFF748080FFDAFFFFFFD1F6FBFFCEF2F7FFCEF2F7FFCEF2F7FF + D0F4F9FFD2F7FCFFD3F8FDFFD2F7FCFFCFF5FCFFCDFBFFFFD9923EFFE8AE72FF + B06E16FF000000160000000000000016B06D16FFE9B076FFD9903DFFCBFAFFFF + CFF6FCFF839091FF859394FF839091FFCFF4FAFFCCF1F7FFCDF2F7FFD0F6FCFF + DAFFFFFF818B8DFF373131FF6E787AFFD3FAFFFFCDF2F7FFCCF1F6FFCCF1F6FF + CFF4FAFF839091FF859394FF839091FFCFF6FCFFCBFAFFFFD9903DFFE9B076FF + B06D16FF000000160000000000000016B06D16FFEBB279FFD78E3BFFD0FDFFFF + CAF3FAFFCDF5FAFFCEF6FBFFCDF5FAFFCAF2F7FFC9F0F5FFC9F1F6FFCDF6FBFF + D7FFFFFF798789FF3C3434FF707B7DFFCFF9FEFFCAF1F6FFC9F0F5FFC9F0F5FF + CAF2F7FFCDF5FAFFCEF6FBFFCDF5FAFFCAF3FAFFD0FDFFFFD78E3BFFEBB279FF + B06D16FF000000160000000000000013B06D16FFEDB77EFFD68D3AFFCFFCFFFF + C6F1F7FFC8F0F5FFC8F0F5FFC8F0F5FFC7EFF4FFC8F0F5FFCAF4F9FFD2FDFFFF + 788485FF413838FF778385FFD2FEFFFFCAF3F8FFC7EFF4FFC7EFF4FFC7EFF4FF + C7EFF4FFC8F0F5FFC8F0F5FFC8F0F5FFC6F1F7FFCFFCFFFFD68D3AFFEDB77EFF + B06D16FF00000013000000000000000EAC6B16E6E3AA6CFFDA9344FFD0FBFFFF + C4EFF8FFC5EDF5FFC5EDF5FFC5EDF4FFC6EEF5FFC8F1F9FFCFFBFFFF7B8688FF + 483F3DFF7A8688FFD0FCFFFFC9F2F9FFC6EEF5FFC5EDF4FFC5EDF4FFC5EDF4FF + C5EDF4FFC5EDF4FFC5EDF5FFC5EDF5FFC4EFF8FFD0FBFFFFDA9344FFE3AA6CFF + AC6B16E60000000E0000000000000007A66917B3D49954FFE19E58FFD2D5C4FF + C1F0FBFFC3EEF6FFC4EFF6FFC4EEF6FFC5F1F8FFCBF9FFFF7E8A8BFF4E4643FF + 7D898BFFCCFAFFFFC5F1F8FFC2EDF4FFC2ECF3FFC2ECF3FFC2ECF3FFC2ECF3FF + C2ECF3FFC3EEF5FFC4EFF6FFC3EEF6FFC1F0FBFFD2D5C4FFE19E58FFD49954FF + A66917B3000000070000000000000001945E1656BC7B2AFFF0B67BFFCFB082FF + C4F0FFFFC2F0F8FF758385FFC5F1F9FFC8F8FFFF808D8FFF554C4AFF808D8EFF + C9F9FFFFC3F0F7FFC1EDF4FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF2FFC0EBF2FF + C0EBF3FFC2EEF5FF758385FFC2F0F8FFC4F0FFFFCFB082FFF0B67BFFBC7B2AFF + 945E165600000001000000000000000000000009AF6D16F7E4AB6DFFE09B51FF + C4D9D8FFBFECF9FFBFEFF7FFC2F2FAFF828E8EFF5C524FFF849092FFC6F7FFFF + C0EFF6FFBFEDF4FFC0EEF5FFBFECF3FFBDEAF1FFBDEAF1FFBDEAF1FFBDEBF1FF + BDEBF2FFBEECF3FFBFEFF6FFBFECF9FFC4D9D8FFE09B51FFE4AB6DFFAF6D16F7 + 00000009000000000000000000000000000000029D621766C18133FFF2BA83FF + D19B5AFFB9E4FAFFBBEBF6FFC0F1FAFF838F8FFF869394FFC4F7FFFFBEEDF6FF + BCEBF3FFBFEEF7FF728386FFBFEEF7FFBCEAF2FFBBE9F1FFBDEBF3FFBDECF5FF + BDEBF3FFBBEBF4FFBBEAF5FFB9E4FAFFD19B5AFFF2BA82FFC18133FF9D621767 + 000000020000000000000000000000000000000000000006AF6D17E7D99F5BFF + EAAD6CFFC6A67AFFB1DEF5FFB8E7F4FFBEF2FDFF718588FFBDEEF7FFB9E8F2FF + B9E8F2FFBDEDF7FF718487FFBDEDF7FFB9E8F2FFB8E8F1FFBBEBF4FF6D7E81FF + BBEDF6FFB6E4F1FFB0DEF5FFC6A67AFFEDB071FFD99F5BFFAF6D17E700000006 + 0000000000000000000000000000000000000000000000018E5A1527AF6C14FF + EEB880FFEBAE6DFFC69E6BFFACD3E6FFAFDDF1FFB7E8F5FFB8EBF4FFB7E8F1FF + B7E8F1FFBBECF5FF6E8082FFBBECF5FFB7E8F1FFB7E8F1FFB8EBF4FFB6E7F4FF + AEDDF0FFACD3E6FFC59E6AFFEBAE6DFFEEB880FFAF6C14FF6E45111200000000 + 000000000000000000000000000000000000000000000000000000019D621738 + B4721CFFEFBA83FFEDB172FFCF914CFFB5B6A7FFA2D2EEFFA6D3E9FFAADAEAFF + AFE0EEFFB2E4F0FFB3E5F2FFB2E4F0FFAFE0EEFFAADAEAFFA6D3E9FFA2D2EEFF + B5B6A7FFCF914CFFF0B578FFEFBA82FFB4721CFF8F5A15230000000100000000 + 0000000000000000000000000000000000000000000000000000000000000001 + 935C1522AF6C14FFDCA362FFF9C692FFE39F57FFCA8B44FFB2AE99FFA4BFC8FF + 99CDECFF9ACCEBFF9ACCEBFF9ACCEBFF99CDECFFA4BFC8FFB2AE99FFCA8B44FF + E39F57FFF9C692FFDBA362FFAF6C13FF945D1623000000010000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000070461110AF6C16E6C38437FFEDB880FFFCC996FFE6A561FFDA9043FF + D0822DFFD1822CFFD1822CFFD1822CFFD0822DFFDA9043FFE6A561FFFCC996FF + EDB880FFC38437FFAF6C16E67046111000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000002A769175FAF6D15F7BE7F2EFFDEA767FFF2C18DFF + FFD5A9FFFFD5A8FFFFD5A8FFFFD5A8FFFFD5A9FFF2C18DFFDEA767FFBE7F2EFF + AF6D15F7A769175F000000020000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000001A769184CAC6B16ACAF6C15E2 + AF6B13FFAF6B13FFAF6B13FFAF6B13FFAF6B13FFAF6C15E2AC6B16ACA769184C + 0000000100000000000000000000000000000000000000000000000000000000 + 0000000000000000FFFFFFFFFFC001FFFF00007FFE00003FF800000FF0000007 + F0000007E0000003C0000001C000000180000000800000008000000080000000 + 8000000080000000800000008000000080000000800000008000000080000000 + 80000000C0000001C0000001E0000003E0000007F0000007F800000FFE00003F + FF00007FFFC001FF280000001000000020000000010020000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000002000000080000000B0000000B0000000A0000000500000000 + 0000000000000000000000000000000000000000000000000000000000000002 + 000000121910043C50330C8759380CA059380CA157360C9A4028096500000020 + 0000000700000000000000000000000000000000000000000000000206040122 + 764B13ABC27F2FFED18F42FFD9974AFFD99749FFD79547FFCA883AFFAA6D23E0 + 311F07620000000C000000000000000000000000000000020F0902259D6521D5 + D59347FFE1C595FFE4EEE0FFE7FFFFFFE7FFFFFFE6FAF7FFE3DCBEFFDDA765FF + C88639FF4B2F0B720000000A000000000000000000000012915D1CCBD9994FFF + E0E4D0FFCFE6E6FFE1FDFEFFE3FDFEFFBFD0D0FFE1FCFDFFCFE6E6FFE1FCFAFF + DEB67EFFC9883BFF321F075A00000003000000024B2F0B6DD29147FFDCD6B7FF + DCFCFEFFDCFAFCFFDBF9FBFFDDFBFCFFCBE4E6FFDBF9FBFFDCFAFCFFDCFAFCFF + DCFAFAFFDCA867FFAF7228DE0000001000000008A66D24C9DCA868FFD7FBFEFF + C5E0E3FFD7F6F9FFD9F9FCFFD9F9FCFFD7F7FAFFD7F6F9FFD7F6F9FFD7F7FAFF + C5E1E4FFD8D6BBFFD19147FF452B0A520000000BC7883DF9D6C398FFD2F6FBFF + D2F5F8FFD2F5F8FFAFC5C7FF8A9695FFD6FAFCFFD2F4F8FFD2F4F8FFD2F4F8FF + D2F5F8FFD2F4F4FFDE9D55FF57360B850000000BCC8E45FFD2C59EFFBCDCE1FF + ABC4C7FFCEF3F8FFD1F7FBFF889595FF7C8A8AFFCFF5FAFFCDF1F6FFBDDBE0FF + ABC4C7FFCDF8FDFFE0A058FF58360B8A0000000ACE9048FFD3C59CFFC9F2F8FF + CAF2F7FFC8F0F5FFCCF6FAFF829091FF7D8C8DFFCAF3F8FFC8EFF4FFC9F1F6FF + CAF2F7FFCBF7FBFFE1A15BFF58360B8A00000005C2853BE6D7C097FFC3EEF7FF + C4EDF5FFC7F2F9FF859495FF829293FFC8F3F9FFC3ECF3FFC3ECF3FFC3EDF4FF + C4EDF5FFC9EBEDFFDC9D57FF54350B6C000000007F511595E0AB6EFFC2E9F2FF + AED5DBFF89999AFF87989AFFC2F1F8FFC0ECF3FFBEEAF1FFBEEAF1FFBFECF3FF + ADD3DBFFCDC5AAFFCF924AFD311F0718000000003420071CCE914AF9CEB48EFF + B9E8F6FF8EA6AAFFBEEEF7FFBCEBF4FF97B8BEFFBAE8F1FFA8D0D7FFBAE9F3FF + BAD4D7FFE2A96AFF83541895000000000000000000000000764A1058DFA463FF + CBAC83FFAFD3DEFFAFE0EEFFB4E6F1FFA3CDD6FFB1E2EEFFADDDEFFFB7BDB2FF + E3AE74FFB87C31C83722080500000000000000000000000000000000905A134C + D19650F9E5AA6CFFC5A881FFB5A78CFFB5A78BFFB9A789FFD7A975FFE3A868FF + AA7027BD4A2E0B09000000000000000000000000000000000000000000000000 + 53340B1885551691CA8F47E3D7A05DFFD7A05DFFD39B57F8BB7E30BE72470E56 + 00000000000000000000000000000000F81FAC41E007AC41C003AC418001AC41 + 8000AC410000AC410000AC410000AC410000AC410000AC410000AC418000AC41 + 8001AC41C001AC41E003AC41F00FAC41 + } + end +end diff --git a/ui/msgbox.pas b/ui/msgbox.pas new file mode 100644 index 0000000..ab232d7 --- /dev/null +++ b/ui/msgbox.pas @@ -0,0 +1,59 @@ +unit msgbox; + +{$mode objfpc} + +interface + +uses + Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, + ExtCtrls; + +type + + { TMsgBoxForm } + + TMsgBoxForm = class(TForm) + Button1: TButton; + Image1: TImage; + Label1: TLabel; + procedure Button1Click(Sender: TObject); + private + + public + procedure SetText(Msg: String); + end; + +var + MsgBoxForm: TMsgBoxForm; + +implementation + +{ TMsgBoxForm } + +procedure TMsgBoxForm.Button1Click(Sender: TObject); +begin + Close; +end; + +procedure TMsgBoxForm.SetText(Msg: String); +const MinFormSize = 162; + TextThresholdWidth = 68; + DefButtonLeft = 40; +var TextW,Delta : Integer; +begin + Label1.Caption:= Msg; + TextW:= Label1.Canvas.GetTextWidth(Msg); + if TextW > TextThresholdWidth then + begin + Delta:= TextW - TextThresholdWidth; + Width:= MinFormSize + Delta; + Button1.Left:= DefButtonLeft + Delta div 2; + end; +end; + +initialization + //{$I msgbox.lrs} + {$R msgbox.lfm} + +end. + From 82c3e9d376d4225f2391de07f1ab3b6d6990ba48 Mon Sep 17 00:00:00 2001 From: neo85 Date: Thu, 8 Apr 2021 12:28:48 +0200 Subject: [PATCH 10/21] Default SysTray action Default SysTray action changed to show/hide application. --- snaptimer.lps | 290 ++++++++++++++++++++++++++--------------------- ui/mainform1.lfm | 9 +- ui/mainform1.pas | 59 +++++++--- 3 files changed, 206 insertions(+), 152 deletions(-) diff --git a/snaptimer.lps b/snaptimer.lps index 8750c92..6d4af4b 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,13 +4,13 @@ - + - + @@ -19,7 +19,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -39,10 +39,10 @@ - - - - + + + + @@ -56,7 +56,7 @@ - + @@ -65,14 +65,14 @@ - + - + @@ -80,32 +80,42 @@ - + + + + + + + + + + + - - + + - - + + - - + + @@ -113,300 +123,316 @@ - - - - - - - - + + + + + + + + + - - + + - - + + - - - - - - - + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + - - - - - - - + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - - + + - + - + - + - + - + - + - + + + + + + + + + diff --git a/ui/mainform1.lfm b/ui/mainform1.lfm index e60bcfe..2c968af 100644 --- a/ui/mainform1.lfm +++ b/ui/mainform1.lfm @@ -17,7 +17,6 @@ object MainForm: TMainForm Constraints.MinWidth = 160 KeyPreview = True Menu = MainMenu1 - OnActivate = FormActivate OnCreate = OnCreateForm OnDestroy = OnDestroyForm OnKeyPress = FormKeyPress @@ -515,7 +514,7 @@ object MainForm: TMainForm AC41 } Visible = True - OnClick = ToggleCountdown + OnClick = TrayIconMainClick Left = 112 Top = 160 end @@ -572,12 +571,12 @@ object MainForm: TMainForm Caption = '-' end object TrayMenuShow: TMenuItem - Caption = 'Show Application' - OnClick = FormActivate + Caption = 'Hide Application' + Default = True + OnClick = TrayIconMainClick end object TrayMenuToggle: TMenuItem Caption = 'Start/Pause' - Default = True OnClick = ToggleCountdown end object TrayMenuReset: TMenuItem diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 2522561..62c248b 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -48,9 +48,9 @@ TMainForm = class(TForm) procedure OnCreateForm(Sender: TObject); procedure OnDestroyForm(Sender: TObject); procedure OnShowForm(Sender: TObject); - procedure FormActivate(Sender: TObject); procedure FormWindowStateChange(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: char); + procedure TrayIconMainClick(Sender: TObject); procedure CloseApp(Sender: TObject); procedure ToggleCountdown(Sender: TObject); @@ -103,7 +103,8 @@ implementation MENU_OPTIONS = 'Op&tions'; MENU_HELP = '&Help'; MENU_ABOUT = '&About'; - TRAY_MENU_SHOW = 'Show application'; + TRAY_MENU_SHOW = 'Show Application'; + TRAY_MENU_HIDE = 'Hide Application'; // Buttons BTN_START = '&Start'; @@ -181,6 +182,7 @@ procedure TMainForm.OnCreateForm(Sender: TObject); end; + procedure TMainForm.OnDestroyForm(Sender: TObject); begin if GetConfig.AutoSave then @@ -236,24 +238,33 @@ procedure TMainForm.OnShowForm(Sender: TObject); end; end; -procedure TMainForm.FormActivate(Sender: TObject); -begin - WindowState := wsNormal; - ShowInTaskBar := stDefault; - Show; -end; - procedure TMainForm.FormWindowStateChange(Sender: TObject); begin - if not GetConfig.MinToTray then - Exit; - if WindowState = wsMinimized then begin - WindowState := wsNormal; - ShowInTaskBar := stNever; - Hide; + TrayMenuShow.Caption:= TRAY_MENU_SHOW; + if GetConfig.MinToTray then + begin + // SysTray hack + // https://forum.lazarus.freepascal.org/index.php/topic,2194.msg9843.html#msg9843 + WindowState:= wsNormal; + Hide; + ShowInTaskBar := stNever; + + // Restore WindowState to what it was + WindowState:= wsMinimized; + end; + end + else if WindowState = wsNormal then + begin + TrayMenuShow.Caption:= TRAY_MENU_HIDE; + if GetConfig.MinToTray then + begin + ShowInTaskBar := stDefault; + Show; + end; end; + end; procedure TMainForm.FormKeyPress(Sender: TObject; var Key: char); @@ -265,6 +276,23 @@ procedure TMainForm.FormKeyPress(Sender: TObject; var Key: char); ToggleCountdown(Sender); end; +procedure TMainForm.TrayIconMainClick(Sender: TObject); +begin + if WindowState = wsNormal then + begin + // On Windows setting wsMinimized does not minimize to taskbar. + //WindowState:= wsMinimized; + Application.Minimize; + end + else if WindowState = wsMinimized then + begin + // Setting WindowState to wsNormal does not call FormOnWindowStateChange + WindowState:= wsNormal; + FormWindowStateChange(Sender); + end; +end; + + procedure TMainForm.CloseApp(Sender: TObject); begin OnDestroyForm(Sender); @@ -352,6 +380,7 @@ procedure TMainForm.OnTimerStateChanged(Sender: TObject); end; end; + // When it comes to buttons and menus, there are only two states. procedure TMainForm.UpdateButtonsAndMenus; begin From 9f95a3782f103f0ca399f4fde7351efbfb271762 Mon Sep 17 00:00:00 2001 From: neo85 Date: Thu, 8 Apr 2021 13:07:41 +0200 Subject: [PATCH 11/21] Fixed window size Window size is now fixed, and depends on the font size. Count changed from TStatic to TPanel. --- snaptimer.lps | 104 +++++++++++++++++++++++------------------------ ui/mainform1.lfm | 20 ++++----- ui/mainform1.pas | 50 ++++------------------- 3 files changed, 71 insertions(+), 103 deletions(-) diff --git a/snaptimer.lps b/snaptimer.lps index 6d4af4b..9912121 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -10,7 +10,7 @@ - + @@ -19,7 +19,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -40,9 +40,9 @@ - - - + + + @@ -56,7 +56,7 @@ - + @@ -65,14 +65,14 @@ - + - + @@ -80,7 +80,7 @@ - + @@ -90,7 +90,7 @@ - + @@ -308,130 +308,130 @@ - + - - + + - - + + - + - + - + - - + + - - + + - - + + - - + + - - + + - + - + - + - - + + - + - + - - + + - + - + - + - - + + - + - + - + - + - + - + - + - + - + diff --git a/ui/mainform1.lfm b/ui/mainform1.lfm index 2c968af..2b2ff94 100644 --- a/ui/mainform1.lfm +++ b/ui/mainform1.lfm @@ -1,8 +1,8 @@ object MainForm: TMainForm - Left = 701 + Left = 703 Height = 242 Top = 305 - Width = 261 + Width = 259 HorzScrollBar.Page = 274 HorzScrollBar.Range = 275 HorzScrollBar.Visible = False @@ -10,9 +10,11 @@ object MainForm: TMainForm VertScrollBar.Range = 26 VertScrollBar.Visible = False ActiveControl = TimeEdit + BorderIcons = [biSystemMenu, biMinimize] + BorderStyle = bsSingle Caption = 'SnapTimer' ClientHeight = 222 - ClientWidth = 261 + ClientWidth = 259 Constraints.MinHeight = 70 Constraints.MinWidth = 160 KeyPreview = True @@ -446,30 +448,28 @@ object MainForm: TMainForm ParentFont = False TabOrder = 0 end - object Count: TStaticText + object Count: TPanel Cursor = crHandPoint Left = 2 Height = 190 Top = 32 - Width = 257 + Width = 255 Align = alBottom - Alignment = taCenter Anchors = [akTop, akLeft, akRight] BorderSpacing.Left = 2 BorderSpacing.Top = 4 BorderSpacing.Right = 2 - BorderStyle = sbsSunken + BevelOuter = bvNone Caption = '00:00:00' Font.CharSet = ANSI_CHARSET Font.Height = -48 Font.Name = 'Arial Narrow' Font.Pitch = fpVariable Font.Quality = fqDraft - OnClick = ToggleCountdown - OnDblClick = ResetCountdown ParentFont = False TabOrder = 1 - Transparent = False + OnClick = ToggleCountdown + OnDblClick = ResetCountdown end object TrayIconMain: TTrayIcon BalloonFlags = bfInfo diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 62c248b..f5fb7ef 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -12,6 +12,7 @@ interface type { TMainForm } TMainForm = class(TForm) + Count: TPanel; ImgIconMain: TImage; ImgIconRunning: TImage; ImgIconPaused: TImage; @@ -43,7 +44,6 @@ TMainForm = class(TForm) TrayMenu: TPopupMenu; TimeLabel: TLabel; TimeEdit: TSpinEdit; - Count: TStaticText; TrayIconMain: TTrayIcon; procedure OnCreateForm(Sender: TObject); procedure OnDestroyForm(Sender: TObject); @@ -72,9 +72,6 @@ TMainForm = class(TForm) procedure PlayTicking(); private MyTimer : TMyTimer; - FontSizeChanged : boolean; - //FormerWidth : integer; - //FormerHeight : integer; OnShowFormFirstTime: Boolean; procedure ApplyConfig; public @@ -199,43 +196,6 @@ procedure TMainForm.OnShowForm(Sender: TObject); TimeEdit.Value:= GetConfig.Minutes; MyTimer.Reset; // This will trigger OnTimerStateChanged end; - - // TODO Get this working for window size (getpreferred size gets form, not window) - // Get preferred size before font change, then after and add or subtract that delta from the - // window size? - // AutoSize is working on startup, just not when font size is changed. - // Need to find the method that will set the window to the preferred size, because the preferred size is correct - // setBounds does it, but it also requires - if FontSizeChanged then - begin - //AutoSize:= True; - //MainForm.FontChanged(Sender); - //GetPreferredSize(w, h, True); - //cRect := GetClientRect; - //cRect.Right := w; - //cRect.Bottom := h; - //AdjustClientRect(cRect); - - // These two lines cause an exception - why I don't know - //Count.Width:= w; - //Count.Height:= h; - - //InvalidatePreferredSize; - //Repaint; - //Resize; - //AdjustSize; - - //GetPreferredSize(w, h, True); - //ShowMessageFmt('width: %d, height: %d', [w, h]); - //ShowMessageFmt('width: %d, height: %d', [FormerWidth, FormerHeight]); - //WndWidth:= WndWidth + (w - FormerWidth); - //WndHeight:= WndHeight + (h - FormerHeight); - //SetBounds(Monitor.WorkareaRect.Right - w + rightAdjust, 0, w, h); - //WndWidth:= w; - //WndHeight:= h; - //ShowMessageFmt('width: %d, height: %d', [w, h]); - //exit; - end; end; procedure TMainForm.FormWindowStateChange(Sender: TObject); @@ -569,6 +529,7 @@ procedure TMainForm.ApplyConfig; Config: TConfig; WndHandle: HWND; flags: Integer; + tw, th: Integer; begin Config:= GetConfig; if Config.AlwaysOnTop then @@ -605,6 +566,13 @@ procedure TMainForm.ApplyConfig; Self.Position := poScreenCenter; end; end; + + Count.Canvas.GetTextSize('00:00:00', tw, th); + // TODO add some padding? + if tw < 168 then + tw:= 168; + self.ClientWidth:= tw + 8; + self.ClientHeight:= th + 32; end; initialization From 84a5547a3e39817b14a60de8808c8a1a70b20330 Mon Sep 17 00:00:00 2001 From: neo85 Date: Tue, 13 Apr 2021 16:40:44 +0200 Subject: [PATCH 12/21] Linux support Linus support - limited testing --- .gitignore | 1 + model/utils.pas | 195 +++++++++++++++++++++++++++++++++++++++++- snaptimer.lpi | 13 +-- snaptimer.lps | 215 +++++++++++++++++++++++------------------------ ui/mainform1.lfm | 25 +++--- ui/mainform1.pas | 58 ++++++------- ui/options.lfm | 190 ++++++++++++++++++++--------------------- ui/options.pas | 100 +++++++++++++--------- 8 files changed, 497 insertions(+), 300 deletions(-) diff --git a/.gitignore b/.gitignore index 0dcd404..df100be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ backup Deploy +SnapTimer *.o *.or *.ppu diff --git a/model/utils.pas b/model/utils.pas index 1a0812d..03ac371 100644 --- a/model/utils.pas +++ b/model/utils.pas @@ -1,9 +1,26 @@ unit utils; {$mode objfpc}{$H+} +{$modeswitch advancedrecords} interface +Uses Forms, Types; + + +// https://forum.lazarus.freepascal.org/index.php/topic,29450.msg186002.html?PHPSESSID=0l7k7l8bgpqdvcqcqetk17rt04#msg186002 +type + TRectHelper = record helper for TRect + private + function GetWidth: Integer; + procedure SetWidth(const Value: Integer); + function GetHeight: Integer; + procedure SetHeight(const Value: Integer); + public + property Width: Integer read GetWidth write SetWidth; + property Height: Integer read GetHeight write SetHeight; + end; + // TUtils class serves as a namespace. type TUtils = class @@ -11,6 +28,8 @@ TUtils = class class function GetFilePath(Path: string): string; class procedure RunApp(Path: string); class function IsInteger(S: String): boolean; + class function GetFormRect(Form: TForm) : TRect; + class procedure SetFormPos(Form: TForm; X: Integer; Y: Integer); class procedure PlayAudio(Path: string; Loop: boolean); class procedure StopAudio; end; @@ -18,7 +37,37 @@ TUtils = class implementation uses - Classes, SysUtils, StrUtils, Forms, Windows, MMSystem; + Classes, SysUtils, StrUtils, FileUtil +{$IFDEF WINDOWS} + ,Windows, MMSystem +{$ENDIF} +{$IFDEF LINUX} + ,Process, asyncprocess +{$ENDIF} +; + + +function TRectHelper.GetHeight: Integer; +begin + Result := Bottom - Top; +end; + +procedure TRectHelper.SetHeight(const Value: Integer); +begin + Bottom := Top + Value; +end; + +function TRectHelper.GetWidth: Integer; +begin + Result := Right - Left; +end; + +procedure TRectHelper.SetWidth(const Value: Integer); +begin + Right := Left + Value; +end; + + class function TUtils.SecondsToTime(Seconds: integer; HideSeconds: Boolean): string; var @@ -47,11 +96,36 @@ class function TUtils.GetFilePath(Path: string): string; Result := Path; end; + +{$IFDEF LINUX} +// https://stackoverflow.com/questions/48609416/shellexecute-equivalent-for-linux-as-target-platform +procedure ShlOpen( FileName: String ) ; +var prc: TProcess; +begin + prc:= TProcess.Create ( nil ) ; + prc.CommandLine:= 'xdg-open ' + FileName; + prc.Execute; + prc.free; +end ; +{$ENDIF} + class procedure TUtils.RunApp(Path: string); begin +{$IFDEF WINDOWS} ShellExecute(0, 'open', PChar(GetFilePath(Path)), nil, nil, SW_SHOWNORMAL); +{$ENDIF} + +{$IFDEF LINUX} + try + ShlOpen(Path); + except + On E: Exception do + E.CreateFmt('Failed to run: %s', [Path]); + end; +{$ENDIF} end; + class function TUtils.IsInteger(S: String): boolean; begin try @@ -63,17 +137,136 @@ class function TUtils.IsInteger(S: String): boolean; end; +class function TUtils.GetFormRect(Form: TForm) : TRect; +var r : TRect; +begin +{$IFDEF WINDOWS} + GetWindowRect(Form.Handle, r); + Result:= r; +{$ENDIF} + +{$IFDEF LINUX} + // TODO test this + r.SetWidth(Form.Width); + r.SetHeight(Form.height); + Result:= r; +{$ENDIF} +end; + +class procedure TUtils.SetFormPos(Form: TForm; X: Integer; Y: Integer); +begin +{$IFDEF WINDOWS} + // It seems that there is no difference. + //SetWindowPos(Form.Handle, HWND_TOP, X, Y, 0, 0, SWP_SHOWWINDOW or SWP_NOSIZE); + Form.Left:= X; + Form.Top:= Y; +{$ENDIF} + +{$IFDEF LINUX} + Form.Left:= X; + Form.Top:= Y; +{$ENDIF} +end; + + +// https://wiki.freepascal.org/Play_Sound_Multiplatform +// Modified +{$IFDEF LINUX} +procedure PlaySound(const szSoundFilename: string); +var + szNonWindowsPlayCommand: string; + SoundPlayerAsyncProcess: Tasyncprocess; +begin + szNonWindowsPlayCommand := ''; + // How to play in Linux? Use generic Linux commands + // Use asyncprocess to play sound as SND_ASYNC + // Try play + if (FindDefaultExecutablePath('play') <> '') then + szNonWindowsPlayCommand := 'play'; + // Try aplay + if (szNonWindowsPlayCommand = '') then + if (FindDefaultExecutablePath('aplay') <> '') then + szNonWindowsPlayCommand := 'aplay -q '; + // Try paplay + if (szNonWindowsPlayCommand = '') then + if (FindDefaultExecutablePath('paplay') <> '') then + szNonWindowsPlayCommand := 'paplay'; + // Try mplayer + if (szNonWindowsPlayCommand = '') then + if (FindDefaultExecutablePath('mplayer') <> '') then + szNonWindowsPlayCommand := 'mplayer -really-quiet '; + // Try CMus + if (szNonWindowsPlayCommand = '') then + if (FindDefaultExecutablePath('CMus') <> '') then + szNonWindowsPlayCommand := 'CMus '; + // Try pacat + if (szNonWindowsPlayCommand = '') then + if (FindDefaultExecutablePath('pacat') <> '') then + szNonWindowsPlayCommand := 'pacat -p '; + // Try ffplay + if (szNonWindowsPlayCommand = '') then + if (FindDefaultExecutablePath('ffplay') <> '') then + szNonWindowsPlayCommand := 'ffplay -autoexit -nodisp '; + // Try cvlc + if (szNonWindowsPlayCommand = '') then + if (FindDefaultExecutablePath('cvlc') <> '') then + szNonWindowsPlayCommand := 'cvlc -q --play-and-exit '; + // Try canberra-gtk-play + if (szNonWindowsPlayCommand = '') then + if (FindDefaultExecutablePath('canberra-gtk-play') <> '') then + szNonWindowsPlayCommand := 'canberra-gtk-play -c never -f '; + // Try Macintosh command? + if (szNonWindowsPlayCommand = '') then + if (FindDefaultExecutablePath('afplay') <> '') then + szNonWindowsPlayCommand := 'afplay'; + // proceed if we managed to find a valid command + if (szNonWindowsPlayCommand <> '') then + begin + SoundPlayerAsyncProcess := Tasyncprocess.Create(nil); + //SoundPlayerAsyncProcess.CurrentDirectory := ExtractFileDir(szSoundFilename); + SoundPlayerAsyncProcess.Executable := + FindDefaultExecutablePath(Copy2Space(szNonWindowsPlayCommand)); + SoundPlayerAsyncProcess.Parameters.Clear; + SoundPlayerAsyncProcess.Parameters.Add(szSoundFilename); + try + SoundPlayerAsyncProcess.Execute; + except + On E: Exception do + E.CreateFmt('Playstyle=paASync: Unable to Play %s Message:%s', [szSoundFilename, E.Message]); + end; + //FreeAndNil(SoundPlayerAsyncProcess); + end + else + raise Exception.CreateFmt('The play command %s does not work on your system', + [szNonWindowsPlayCommand]); +end; +{$ENDIF} + + class procedure TUtils.PlayAudio(Path: string; Loop: boolean); begin +{$IFDEF WINDOWS} if Loop then sndPlaySound(PChar(GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC or SND_LOOP) else sndPlaySound(PChar(GetFilePath(Path)), SND_NODEFAULT or SND_ASYNC); +{$ENDIF} + +{$IFDEF LINUX} + // TODO Loop + PlaySound(Path); +{$ENDIF} end; class procedure TUtils.StopAudio; begin +{$IFDEF WINDOWS} sndPlaySound(nil, 0); +{$ENDIF} + +{$IFDEF LINUX} + // TODO +{$ENDIF} end; diff --git a/snaptimer.lpi b/snaptimer.lpi index 949bd61..f721b0b 100644 --- a/snaptimer.lpi +++ b/snaptimer.lpi @@ -1,7 +1,7 @@ - + @@ -65,16 +65,9 @@ - + + - - - - - - - - diff --git a/snaptimer.lps b/snaptimer.lps index 9912121..86ee16c 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,13 +4,13 @@ - + - + @@ -18,18 +18,14 @@ - - + - - - - + @@ -39,10 +35,9 @@ - - - - + + + @@ -53,34 +48,40 @@ - - - - + + + + + + - - - - + + + + + - - - + + + + - - - + + + + + @@ -90,7 +91,7 @@ - + @@ -127,11 +128,10 @@ - - - + + + - @@ -156,11 +156,10 @@ - + - @@ -171,19 +170,17 @@ - + - - + - @@ -195,11 +192,10 @@ - + - @@ -305,134 +301,133 @@ - + - + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - diff --git a/ui/mainform1.lfm b/ui/mainform1.lfm index 2b2ff94..4070cb2 100644 --- a/ui/mainform1.lfm +++ b/ui/mainform1.lfm @@ -13,7 +13,7 @@ object MainForm: TMainForm BorderIcons = [biSystemMenu, biMinimize] BorderStyle = bsSingle Caption = 'SnapTimer' - ClientHeight = 222 + ClientHeight = 219 ClientWidth = 259 Constraints.MinHeight = 70 Constraints.MinWidth = 160 @@ -24,8 +24,7 @@ object MainForm: TMainForm OnKeyPress = FormKeyPress OnShow = OnShowForm OnWindowStateChange = FormWindowStateChange - Position = poScreenCenter - LCLVersion = '2.0.10.0' + LCLVersion = '1.6.0.4' object TimeLabel: TLabel AnchorSideRight.Control = TimeEdit Left = 7 @@ -438,7 +437,7 @@ object MainForm: TMainForm object TimeEdit: TSpinEdit AnchorSideLeft.Side = asrBottom Left = 59 - Height = 23 + Height = 27 Top = 2 Width = 47 BorderSpacing.Right = 5 @@ -451,7 +450,7 @@ object MainForm: TMainForm object Count: TPanel Cursor = crHandPoint Left = 2 - Height = 190 + Height = 187 Top = 32 Width = 255 Align = alBottom @@ -515,12 +514,12 @@ object MainForm: TMainForm } Visible = True OnClick = TrayIconMainClick - Left = 112 - Top = 160 + left = 112 + top = 160 end object MainMenu1: TMainMenu - Left = 24 - Top = 104 + left = 24 + top = 104 object MenuFile: TMenuItem Caption = 'File' object MenuToggle: TMenuItem @@ -561,8 +560,8 @@ object MainForm: TMainForm end end object TrayMenu: TPopupMenu - Left = 24 - Top = 160 + left = 24 + top = 160 object MenuCount: TMenuItem Caption = 'Countdown' Enabled = False @@ -600,8 +599,8 @@ object MainForm: TMainForm end end object PopupMenuCompact: TPopupMenu - Left = 128 - Top = 104 + left = 128 + top = 104 object MenuCompact: TMenuItem Caption = 'Toggle compact mode' OnClick = ToggleCompact diff --git a/ui/mainform1.pas b/ui/mainform1.pas index f5fb7ef..e4baf5b 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -6,7 +6,7 @@ interface uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, - Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, Options, Windows, DateUtils, + Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, Options, DateUtils, MyTimer; type @@ -336,6 +336,8 @@ procedure TMainForm.OnTimerStateChanged(Sender: TObject); ToggleCountdown(Sender) else TrayIconMain.Icon := ImgIconDone.Picture.Icon; + + Show; end; end; end; @@ -387,7 +389,7 @@ procedure TMainForm.ShowOptions(Sender: TObject); var Config: TConfig; Ok: Boolean; - r: TRect; + r : TRect; begin OptionsForm := TOptionsForm.Create(Self); Ok:= OptionsForm.ShowModal = mrOk; @@ -396,11 +398,11 @@ procedure TMainForm.ShowOptions(Sender: TObject); if Ok then begin Config:= GetConfig; - GetWindowRect(MainForm.Handle, r); + r:= TUtils.GetFormRect(self); Config.WndLeft:= r.Left; Config.WndTop:= r.Top; - Config.WndWidth:= r.Right - r. Left; - Config.WndHeight:= r.Bottom - r.Top; + Config.WndWidth:= r.Width; + Config.WndHeight:= r.Height; ApplyConfig; //if not (Count.Font.Size = f.Size) then @@ -412,7 +414,6 @@ procedure TMainForm.ShowOptions(Sender: TObject); //Count.Font := f; // TODO Get font colors and background color to be shown - what's up? - //if FontSizeChanged then OnShowForm(Sender); if MyTimer.State = TState.Running then begin if Config.TickingOn then @@ -423,9 +424,6 @@ procedure TMainForm.ShowOptions(Sender: TObject); if Config.AutoSave then SaveSettings(Sender); - // TODO Resize the window if necessary to accomodate larger text - // Get the text size from the font for '00:00:00'? - end; end; @@ -449,9 +447,9 @@ procedure TMainForm.SaveSettings(Sender: TObject); // These config values are not set in OptionsForm. Config.Minutes:= TimeEdit.Value; - GetWindowRect(self.Handle, wRect); - Config.WndWidth:= wRect.Right - wRect.Left; - Config.WndHeight:= wRect.Bottom - wRect.Top; + wRect:= TUtils.GetFormRect(self); + Config.WndWidth:= wRect.Width; + Config.WndHeight:= wRect.Height; Config.WndLeft:= wRect.Left; Config.WndTop:= wRect.Top; @@ -527,8 +525,6 @@ procedure TMainForm.PlayTicking(); procedure TMainForm.ApplyConfig; var Config: TConfig; - WndHandle: HWND; - flags: Integer; tw, th: Integer; begin Config:= GetConfig; @@ -550,29 +546,29 @@ procedure TMainForm.ApplyConfig; Count.Font.Style := Config.Font.Style; Count.Color := Config.Font.BgColor; - WndHandle:= self.Handle; - Self.Position := poDesigned; - flags:= SWP_SHOWWINDOW; - case Config.WndPosition of - Remember: SetWindowPos(WndHandle, HWND_TOP, Config.WndLeft, Config.WndTop, Config.WndWidth, Config.WndHeight, flags); - TopLeft: SetWindowPos(WndHandle, HWND_TOP, 0, 0, Config.WndWidth, Config.WndHeight, flags); - TopRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - Config.WndWidth, 0, Config.WndWidth, Config.WndHeight, flags); - BottomLeft: SetWindowPos(WndHandle, HWND_TOP, 0, Monitor.WorkareaRect.Bottom - Config.WndHeight, Config.WndWidth, Config.WndHeight, flags); - BottomRight: SetWindowPos(WndHandle, HWND_TOP, Monitor.WorkareaRect.Right - Config.WndWidth, - Monitor.WorkareaRect.Bottom - Config.WndHeight, Config.WndWidth, Config.WndHeight, flags); - Center: - begin - SetWindowPos(WndHandle, HWND_TOP, 0, 0, Config.WndWidth, Config.WndHeight, SWP_SHOWWINDOW or SWP_NOMOVE); - Self.Position := poScreenCenter; - end; - end; Count.Canvas.GetTextSize('00:00:00', tw, th); - // TODO add some padding? if tw < 168 then tw:= 168; self.ClientWidth:= tw + 8; self.ClientHeight:= th + 32; + + self.Position:= poDesigned; + case Config.WndPosition of + Remember: TUtils.SetFormPos(self, Config.WndLeft, Config.WndTop); + TopLeft: TUtils.SetFormPos(self, 0, 0); + TopRight: TUtils.SetFormPos(self, Monitor.WorkareaRect.Right - Width, 0); + BottomLeft: TUtils.SetFormPos(self, 0, Monitor.WorkareaRect.Bottom - Height); + BottomRight: TUtils.SetFormPos(self, Monitor.WorkareaRect.Right - Width, Monitor.WorkareaRect.Bottom - Height); + Center: + begin + Self.Position := poScreenCenter; + // After the main form was centered, we want to be able to position it and we + // don't want minimize/restore action to center it again. + Self.Position := poDesigned; + end; + end; + end; initialization diff --git a/ui/options.lfm b/ui/options.lfm index 9caaea3..71be8bf 100644 --- a/ui/options.lfm +++ b/ui/options.lfm @@ -1,24 +1,25 @@ object OptionsForm: TOptionsForm Left = 465 - Height = 234 - Top = 319 - Width = 550 + Height = 249 + Top = 322 + Width = 652 BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Options' - ClientHeight = 234 - ClientWidth = 550 + ClientHeight = 249 + ClientWidth = 652 + Font.Name = 'Default' KeyPreview = True OnCreate = FormCreate OnDestroy = FormDestroy OnKeyPress = FormKeyPress Position = poScreenCenter ShowInTaskBar = stNever - LCLVersion = '2.0.10.0' + LCLVersion = '1.6.0.4' object BtnOk: TButton - Left = 384 + Left = 488 Height = 25 - Top = 200 + Top = 208 Width = 75 Caption = '&OK' Default = True @@ -27,9 +28,9 @@ object OptionsForm: TOptionsForm TabOrder = 1 end object BtnCancel: TButton - Left = 464 + Left = 573 Height = 25 - Top = 200 + Top = 208 Width = 75 Caption = '&Cancel' ModalResult = 2 @@ -38,24 +39,24 @@ object OptionsForm: TOptionsForm end object PageControl1: TPageControl Left = 0 - Height = 192 + Height = 200 Top = 0 - Width = 552 + Width = 648 ActivePage = FontsTab ParentFont = False TabIndex = 2 TabOrder = 0 object GeneralTab: TTabSheet Caption = 'General' - ClientHeight = 164 - ClientWidth = 544 + ClientHeight = 169 + ClientWidth = 644 ParentFont = False object CheckAutostart: TCheckBox Left = 171 - Height = 19 + Height = 24 Hint = 'Begin counting down when the application starts' Top = 74 - Width = 100 + Width = 133 Caption = '&Autostart timer' ParentFont = False ParentShowHint = False @@ -64,10 +65,10 @@ object OptionsForm: TOptionsForm end object CheckMinToTray: TCheckBox Left = 12 - Height = 19 + Height = 24 Hint = 'Don''t show the application in the taskbar when minimized' Top = 106 - Width = 106 + Width = 136 Caption = '&Minimize to tray' ParentFont = False ParentShowHint = False @@ -80,18 +81,18 @@ object OptionsForm: TOptionsForm Top = 13 Width = 160 Caption = 'Starting position' - ClientHeight = 28 + ClientHeight = 29 ClientWidth = 156 ParentFont = False TabOrder = 0 object PositionCombo: TComboBox Left = 6 - Height = 23 + Height = 31 Hint = 'Where to place the application window when it starts' Top = 2 Width = 144 DropDownCount = 6 - ItemHeight = 15 + ItemHeight = 0 ParentFont = False ParentShowHint = False ShowHint = True @@ -101,10 +102,10 @@ object OptionsForm: TOptionsForm end object CheckClickTime: TCheckBox Left = 171 - Height = 19 + Height = 24 Hint = 'Click the timer numerals to start/pause the timer' Top = 106 - Width = 113 + Width = 146 Caption = 'Cl&ick time to start' ParentFont = False ParentShowHint = False @@ -113,10 +114,10 @@ object OptionsForm: TOptionsForm end object CheckDblClickTime: TCheckBox Left = 171 - Height = 19 + Height = 24 Hint = 'Double-click the timer numerals to reset the timer' Top = 138 - Width = 156 + Width = 200 Caption = '&Double-click time to reset' ParentFont = False ParentShowHint = False @@ -124,11 +125,11 @@ object OptionsForm: TOptionsForm TabOrder = 6 end object CheckAutoRestart: TCheckBox - Left = 348 - Height = 19 + Left = 400 + Height = 24 Hint = 'Start the timer again when the time reaches zero' - Top = 74 - Width = 161 + Top = 72 + Width = 211 Caption = 'Automatically &restart timer' ParentFont = False ParentShowHint = False @@ -136,11 +137,11 @@ object OptionsForm: TOptionsForm TabOrder = 8 end object CheckLoopAudio: TCheckBox - Left = 348 - Height = 19 + Left = 400 + Height = 24 Hint = 'Continue playing the audio file until timer is reset' - Top = 106 - Width = 113 + Top = 104 + Width = 145 Caption = '&Loop audio alarm' ParentFont = False ParentShowHint = False @@ -149,10 +150,10 @@ object OptionsForm: TOptionsForm end object CheckAutoSave: TCheckBox Left = 12 - Height = 19 + Height = 24 Hint = 'Automatically save settings to the .ini file on exit' Top = 138 - Width = 113 + Width = 150 Caption = 'Auto&save settings' ParentFont = False ParentShowHint = False @@ -161,10 +162,10 @@ object OptionsForm: TOptionsForm end object CheckAlwaysOnTop: TCheckBox Left = 12 - Height = 19 + Height = 24 Hint = 'Show the window even when it''s deactivated' Top = 74 - Width = 95 + Width = 122 Caption = 'Al&ways on top' ParentFont = False ParentShowHint = False @@ -172,11 +173,11 @@ object OptionsForm: TOptionsForm TabOrder = 1 end object CheckTicking: TCheckBox - Left = 348 - Height = 19 + Left = 400 + Height = 24 Hint = 'Play a ticking sound when timer is running' Top = 136 - Width = 117 + Width = 150 Caption = '&Play ticking sound' ParentFont = False ParentShowHint = False @@ -184,11 +185,11 @@ object OptionsForm: TOptionsForm TabOrder = 10 end object CheckSecondsMode: TCheckBox - Left = 348 - Height = 19 + Left = 400 + Height = 24 Hint = 'Enter seconds instead of minutes' Top = 41 - Width = 135 + Width = 174 Caption = '&Enable seconds mode' ParentFont = False ParentShowHint = False @@ -197,10 +198,10 @@ object OptionsForm: TOptionsForm end object CheckHideSeconds: TCheckBox Left = 171 - Height = 19 + Height = 24 Hint = 'Begin counting down when the application starts' Top = 41 - Width = 91 + Width = 117 Caption = '&Hide seconds' ParentFont = False ParentShowHint = False @@ -210,24 +211,24 @@ object OptionsForm: TOptionsForm end object AlarmsTab: TTabSheet Caption = 'Alarms' - ClientHeight = 164 - ClientWidth = 544 + ClientHeight = 169 + ClientWidth = 644 ParentFont = False object GroupBox1: TGroupBox Left = 4 - Height = 144 - Top = 10 - Width = 534 + Height = 138 + Top = 16 + Width = 574 Caption = 'Notification actions' - ClientHeight = 124 - ClientWidth = 530 + ClientHeight = 119 + ClientWidth = 570 ParentFont = False TabOrder = 0 object NotifyRunAppOn: TCheckBox Left = 5 - Height = 19 + Height = 24 Top = 92 - Width = 93 + Width = 120 Caption = '&Run program:' OnClick = EnableFields ParentFont = False @@ -235,9 +236,9 @@ object OptionsForm: TOptionsForm end object NotifyAudioOn: TCheckBox Left = 5 - Height = 19 + Height = 24 Top = 63 - Width = 94 + Width = 122 Caption = '&Play wave file:' OnClick = EnableFields ParentFont = False @@ -245,9 +246,9 @@ object OptionsForm: TOptionsForm end object NotifyTrayMsgOn: TCheckBox Left = 5 - Height = 19 + Height = 24 Top = 32 - Width = 113 + Width = 146 Caption = '&Show tray popup:' OnClick = EnableFields ParentFont = False @@ -255,16 +256,16 @@ object OptionsForm: TOptionsForm end object NotifyMsgOn: TCheckBox Left = 5 - Height = 19 + Height = 24 Top = 3 - Width = 110 + Width = 143 Caption = '&Display message:' OnClick = EnableFields ParentFont = False TabOrder = 0 end object NotifyTrayMsgTest: TButton - Left = 452 + Left = 488 Height = 25 Top = 30 Width = 75 @@ -274,7 +275,7 @@ object OptionsForm: TOptionsForm TabOrder = 5 end object NotifyMsgTest: TButton - Left = 452 + Left = 488 Height = 25 Top = 1 Width = 75 @@ -284,9 +285,9 @@ object OptionsForm: TOptionsForm TabOrder = 2 end object NotifyRunTest: TButton - Left = 452 + Left = 488 Height = 25 - Top = 90 + Top = 88 Width = 75 Caption = 'Test' OnClick = TestRunApp @@ -294,9 +295,9 @@ object OptionsForm: TOptionsForm TabOrder = 13 end object NotifyAudioTest: TButton - Left = 452 + Left = 488 Height = 25 - Top = 61 + Top = 56 Width = 75 Caption = 'Test' OnClick = TestAudio @@ -304,17 +305,17 @@ object OptionsForm: TOptionsForm TabOrder = 9 end object NotifyTrayMsg: TEdit - Left = 130 - Height = 23 + Left = 160 + Height = 27 Top = 32 Width = 315 ParentFont = False TabOrder = 4 end object NotifyRunBtn: TButton - Left = 372 + Left = 400 Height = 25 - Top = 90 + Top = 88 Width = 75 Caption = 'Browse' OnClick = GetAppFile @@ -322,17 +323,17 @@ object OptionsForm: TOptionsForm TabOrder = 12 end object NotifyRunApp: TEdit - Left = 130 - Height = 23 - Top = 92 + Left = 160 + Height = 27 + Top = 88 Width = 232 ParentFont = False TabOrder = 11 end object NotifyAudioBtn: TButton - Left = 372 + Left = 400 Height = 25 - Top = 61 + Top = 63 Width = 75 Caption = 'Browse' OnClick = GetAudioFile @@ -340,16 +341,16 @@ object OptionsForm: TOptionsForm TabOrder = 8 end object NotifyAudio: TEdit - Left = 130 - Height = 23 - Top = 63 + Left = 160 + Height = 27 + Top = 61 Width = 232 ParentFont = False TabOrder = 7 end object NotifyMsg: TEdit - Left = 130 - Height = 23 + Left = 160 + Height = 27 Top = 3 Width = 315 ParentFont = False @@ -359,8 +360,8 @@ object OptionsForm: TOptionsForm end object FontsTab: TTabSheet Caption = 'Font' - ClientHeight = 164 - ClientWidth = 544 + ClientHeight = 169 + ClientWidth = 644 ParentFont = False object TimerFontBox: TGroupBox Left = 4 @@ -368,21 +369,21 @@ object OptionsForm: TOptionsForm Top = 10 Width = 534 Caption = 'Timer font' - ClientHeight = 84 + ClientHeight = 85 ClientWidth = 530 ParentFont = False TabOrder = 0 object Label1: TLabel Left = 6 - Height = 15 + Height = 17 Top = 6 - Width = 23 + Width = 31 Caption = 'Size:' ParentColor = False ParentFont = False end object FontColor: TColorButton - Left = 168 + Left = 176 Height = 25 Top = 1 Width = 75 @@ -393,7 +394,7 @@ object OptionsForm: TOptionsForm ParentFont = False end object BgColor: TColorButton - Left = 362 + Left = 384 Height = 25 Top = 1 Width = 75 @@ -405,25 +406,25 @@ object OptionsForm: TOptionsForm end object LabelTextColor: TLabel Left = 126 - Height = 15 + Height = 17 Top = 6 - Width = 32 + Width = 41 Caption = 'Color:' ParentColor = False ParentFont = False end object LabelTextColor1: TLabel Left = 286 - Height = 15 + Height = 17 Top = 6 - Width = 67 + Width = 88 Caption = 'Background:' ParentColor = False ParentFont = False end object FontSize: TSpinEdit Left = 46 - Height = 23 + Height = 27 Top = 3 Width = 47 MaxValue = 1000 @@ -435,9 +436,8 @@ object OptionsForm: TOptionsForm Left = 94 Height = 28 Top = 47 - Width = 66 - Alignment = taCenter - AutoSize = True + Width = 292 + Anchors = [akTop, akBottom] BorderSpacing.InnerBorder = 3 Caption = '00:00:00' Color = clDefault diff --git a/ui/options.pas b/ui/options.pas index 92258d7..e5f5761 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -56,14 +56,14 @@ TOptionsForm = class(TForm) GeneralTab: TTabSheet; AlarmsTab: TTabSheet; FontsTab: TTabSheet; + function GetFile(Orig: String; FilterStr: String; Dir : String): String; + procedure GetAudioFile(Sender: TObject); + procedure GetAppFile(Sender: TObject); procedure ChooseFont(Sender: TObject); procedure FormCreate(Sender: TObject); procedure EnableFields(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: char); - procedure GetAudioFile(Sender: TObject); - procedure GetAppFile(Sender: TObject); - function GetFile(Orig: String; FilterStr: String; Dir : String): String; procedure SetFontDefaults(Sender: TObject); procedure TestAudio(Sender: TObject); procedure TestMessage(Sender: TObject); @@ -87,12 +87,59 @@ implementation const FONT_PREVIEW_FONT_SIZE = 18; +{$IFDEF WINDOWS} + SEPARATOR = '\'; +{$ENDIF} +{$IFDEF LINUX} + SEPARATOR = '/'; +{$ENDIF} + // TODO Move all text strings to the top +function TOptionsForm.GetFile(Orig: String; FilterStr: String; Dir : String): String; +var + dlg : TOpenDialog; + fname : String; + cwd : String; + relPath : String; +begin + dlg := TOpenDialog.Create(nil); + dlg.FileName := ''; + if DirectoryExists(Dir) then + dlg.InitialDir := Dir + else + dlg.InitialDir := GetCurrentDir; + dlg.Options := [ofFileMustExist]; + dlg.Filter := FilterStr; + dlg.FilterIndex := 1; + + if dlg.Execute then + begin + fname := dlg.FileName; + cwd := GetCurrentDir + SEPARATOR; + relPath := ExtractRelativePath(cwd, ExtractFilePath(fname)); + Result := '.' + SEPARATOR + relPath + ExtractFileName(fname); + end else begin + Result := Orig; + end; + dlg.Free; +end; + procedure TOptionsForm.GetAudioFile(Sender: TObject); begin - NotifyAudio.Text := GetFile(NotifyAudio.Text, 'Wave files (*.wav)|*.wav', GetCurrentDir + '\sounds'); + NotifyAudio.Text := GetFile(NotifyAudio.Text, 'Wave files (*.wav)|*.wav', GetCurrentDir + SEPARATOR + 'sounds'); +end; + +procedure TOptionsForm.GetAppFile(Sender: TObject); +begin +{$IFDEF WINDOWS} + NotifyRunApp.Text := GetFile(NotifyRunApp.Text, 'Executables (*.exe)|*.exe|All files (*.*)|*.*', GetCurrentDir); +{$ENDIF} + +{$IFDEF LINUX} + NotifyRunApp.Text := GetFile(NotifyRunApp.Text, 'All files (*.*)|*.*', GetCurrentDir); +{$ENDIF} end; procedure TOptionsForm.FormKeyPress(Sender: TObject; var Key: char); @@ -122,6 +169,7 @@ procedure TOptionsForm.FormCreate(Sender: TObject); var Config: TConfig; WndPositions: TStrings; begin + //Application.MessageBox(PChar(String(GetFontData(self.Font.Handle).Name)), ''); Config:= GetConfig; PageControl1.TabIndex := 0; @@ -168,7 +216,6 @@ procedure TOptionsForm.FormCreate(Sender: TObject); FontSize.Value:= Config.Font.Size; FontColor.ButtonColor:= Config.Font.Color; BgColor.ButtonColor:= Config.Font.BgColor; - FontPreview.Caption:= Config.Font.Name + ' : 1234567890'; FontPreview.Font.Name:= Config.Font.Name; //FontPreview.Font.Size:= Config.Font.Size; // size is fixed FontPreview.Font.Size:= FONT_PREVIEW_FONT_SIZE; @@ -176,8 +223,16 @@ procedure TOptionsForm.FormCreate(Sender: TObject); FontPreview.Font.Color:= Config.Font.Color; FontPreview.Font.Style:= Config.Font.Style; FontPreview.Color:= Config.Font.BgColor; + FontPreview.Caption:= Config.Font.Name + ' : 1234567890'; EnableFields(Sender); + +{$IFDEF LINUX} + // TODO: Not supported yet + // Create a thread that will call play command? + CheckLoopAudio.Checked:= False; + CheckLoopAudio.Enabled:= False; +{$ENDIF} end; procedure TOptionsForm.FormDestroy(Sender: TObject); @@ -256,41 +311,6 @@ procedure TOptionsForm.UpdateFontBgColor(Sender: TObject); end; -procedure TOptionsForm.GetAppFile(Sender: TObject); -begin - NotifyRunApp.Text := GetFile(NotifyRunApp.Text, 'Executables (*.exe)|*.exe|All files (*.*)|*.*', GetCurrentDir); -end; - -function TOptionsForm.GetFile(Orig: String; FilterStr: String; Dir : String): String; -var - dlg : TOpenDialog; - fname : String; - cwd : String; - relPath : String; -begin - dlg := TOpenDialog.Create(nil); - dlg.FileName := ''; - if DirectoryExists(Dir) then - dlg.InitialDir := Dir - else - dlg.InitialDir := GetCurrentDir; - dlg.Options := [ofFileMustExist]; - dlg.Filter := FilterStr; - dlg.FilterIndex := 1; - - if dlg.Execute then - begin - fname := dlg.FileName; - cwd := GetCurrentDir + '\'; - relPath := ExtractRelativePath(cwd, ExtractFilePath(fname)); - Result := '.\' + relPath + ExtractFileName(fname); - end else begin - Result := Orig; - end; - dlg.Free; -end; - - procedure TOptionsForm.SetFontDefaults(Sender: TObject); var From 6cc5b034040a2efe2b92d15770bb6c7b5e9b2ce3 Mon Sep 17 00:00:00 2001 From: neo85 Date: Wed, 14 Apr 2021 15:46:08 +0200 Subject: [PATCH 13/21] Bugfix --- ui/mainform1.pas | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/mainform1.pas b/ui/mainform1.pas index e4baf5b..95f25b7 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -337,7 +337,9 @@ procedure TMainForm.OnTimerStateChanged(Sender: TObject); else TrayIconMain.Icon := ImgIconDone.Picture.Icon; - Show; + // Show; this does not work well + WindowState:= wsNormal; + FormWindowStateChange(Sender); end; end; end; From 62cc2f4222849fbabdad1937d8a41e5df02fd6c9 Mon Sep 17 00:00:00 2001 From: neo85 Date: Wed, 14 Apr 2021 17:36:18 +0200 Subject: [PATCH 14/21] BugFix --- ui/mainform1.pas | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 95f25b7..c29dd25 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -74,6 +74,7 @@ TMainForm = class(TForm) MyTimer : TMyTimer; OnShowFormFirstTime: Boolean; procedure ApplyConfig; + procedure ShowForm; public { public declarations } end; @@ -245,11 +246,7 @@ procedure TMainForm.TrayIconMainClick(Sender: TObject); Application.Minimize; end else if WindowState = wsMinimized then - begin - // Setting WindowState to wsNormal does not call FormOnWindowStateChange - WindowState:= wsNormal; - FormWindowStateChange(Sender); - end; + ShowForm; end; @@ -337,9 +334,7 @@ procedure TMainForm.OnTimerStateChanged(Sender: TObject); else TrayIconMain.Icon := ImgIconDone.Picture.Icon; - // Show; this does not work well - WindowState:= wsNormal; - FormWindowStateChange(Sender); + ShowForm; end; end; end; @@ -573,6 +568,21 @@ procedure TMainForm.ApplyConfig; end; +procedure TMainForm.ShowForm; +begin + // Not sure why this behaviour is different. + +{$IFDEF WINDOWS} + // Setting WindowState to wsNormal does not call FormOnWindowStateChange + WindowState:= wsNormal; + FormWindowStateChange(nil); +{$ENDIF} + +{$IFDEF LINUX} + Show; +{$ENDIF} +end; + initialization {$I mainform1.lrs} end. From 1f2efcb057c3876221d1ba0e3552ddfa808f8b8f Mon Sep 17 00:00:00 2001 From: neo85 Date: Mon, 19 Apr 2021 14:07:10 +0200 Subject: [PATCH 15/21] Linux update + reordered methods --- ui/options.lfm | 32 +++++------ ui/options.pas | 151 ++++++++++++++++++++++++++----------------------- 2 files changed, 95 insertions(+), 88 deletions(-) diff --git a/ui/options.lfm b/ui/options.lfm index 71be8bf..3a464f1 100644 --- a/ui/options.lfm +++ b/ui/options.lfm @@ -15,7 +15,7 @@ object OptionsForm: TOptionsForm OnKeyPress = FormKeyPress Position = poScreenCenter ShowInTaskBar = stNever - LCLVersion = '1.6.0.4' + LCLVersion = '2.0.10.0' object BtnOk: TButton Left = 488 Height = 25 @@ -81,18 +81,18 @@ object OptionsForm: TOptionsForm Top = 13 Width = 160 Caption = 'Starting position' - ClientHeight = 29 + ClientHeight = 28 ClientWidth = 156 ParentFont = False TabOrder = 0 object PositionCombo: TComboBox Left = 6 - Height = 31 + Height = 23 Hint = 'Where to place the application window when it starts' Top = 2 Width = 144 DropDownCount = 6 - ItemHeight = 0 + ItemHeight = 15 ParentFont = False ParentShowHint = False ShowHint = True @@ -220,7 +220,7 @@ object OptionsForm: TOptionsForm Top = 16 Width = 574 Caption = 'Notification actions' - ClientHeight = 119 + ClientHeight = 118 ClientWidth = 570 ParentFont = False TabOrder = 0 @@ -360,8 +360,8 @@ object OptionsForm: TOptionsForm end object FontsTab: TTabSheet Caption = 'Font' - ClientHeight = 169 - ClientWidth = 644 + ClientHeight = 172 + ClientWidth = 640 ParentFont = False object TimerFontBox: TGroupBox Left = 4 @@ -369,15 +369,15 @@ object OptionsForm: TOptionsForm Top = 10 Width = 534 Caption = 'Timer font' - ClientHeight = 85 + ClientHeight = 84 ClientWidth = 530 ParentFont = False TabOrder = 0 object Label1: TLabel Left = 6 - Height = 17 + Height = 15 Top = 6 - Width = 31 + Width = 23 Caption = 'Size:' ParentColor = False ParentFont = False @@ -406,25 +406,25 @@ object OptionsForm: TOptionsForm end object LabelTextColor: TLabel Left = 126 - Height = 17 + Height = 15 Top = 6 - Width = 41 + Width = 32 Caption = 'Color:' ParentColor = False ParentFont = False end object LabelTextColor1: TLabel Left = 286 - Height = 17 + Height = 15 Top = 6 - Width = 88 + Width = 67 Caption = 'Background:' ParentColor = False ParentFont = False end object FontSize: TSpinEdit Left = 46 - Height = 27 + Height = 23 Top = 3 Width = 47 MaxValue = 1000 @@ -434,7 +434,7 @@ object OptionsForm: TOptionsForm end object FontPreview: TStaticText Left = 94 - Height = 28 + Height = 27 Top = 47 Width = 292 Anchors = [akTop, akBottom] diff --git a/ui/options.pas b/ui/options.pas index e5f5761..a29300b 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -56,14 +56,15 @@ TOptionsForm = class(TForm) GeneralTab: TTabSheet; AlarmsTab: TTabSheet; FontsTab: TTabSheet; + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure FormKeyPress(Sender: TObject; var Key: char); + function GetFile(Orig: String; FilterStr: String; Dir : String): String; procedure GetAudioFile(Sender: TObject); procedure GetAppFile(Sender: TObject); procedure ChooseFont(Sender: TObject); - procedure FormCreate(Sender: TObject); procedure EnableFields(Sender: TObject); - procedure FormDestroy(Sender: TObject); - procedure FormKeyPress(Sender: TObject; var Key: char); procedure SetFontDefaults(Sender: TObject); procedure TestAudio(Sender: TObject); procedure TestMessage(Sender: TObject); @@ -95,75 +96,6 @@ implementation {$ENDIF} -// TODO Move all text strings to the top - -function TOptionsForm.GetFile(Orig: String; FilterStr: String; Dir : String): String; -var - dlg : TOpenDialog; - fname : String; - cwd : String; - relPath : String; -begin - dlg := TOpenDialog.Create(nil); - dlg.FileName := ''; - if DirectoryExists(Dir) then - dlg.InitialDir := Dir - else - dlg.InitialDir := GetCurrentDir; - dlg.Options := [ofFileMustExist]; - dlg.Filter := FilterStr; - dlg.FilterIndex := 1; - - if dlg.Execute then - begin - fname := dlg.FileName; - cwd := GetCurrentDir + SEPARATOR; - relPath := ExtractRelativePath(cwd, ExtractFilePath(fname)); - Result := '.' + SEPARATOR + relPath + ExtractFileName(fname); - end else begin - Result := Orig; - end; - dlg.Free; -end; - -procedure TOptionsForm.GetAudioFile(Sender: TObject); -begin - NotifyAudio.Text := GetFile(NotifyAudio.Text, 'Wave files (*.wav)|*.wav', GetCurrentDir + SEPARATOR + 'sounds'); -end; - -procedure TOptionsForm.GetAppFile(Sender: TObject); -begin -{$IFDEF WINDOWS} - NotifyRunApp.Text := GetFile(NotifyRunApp.Text, 'Executables (*.exe)|*.exe|All files (*.*)|*.*', GetCurrentDir); -{$ENDIF} - -{$IFDEF LINUX} - NotifyRunApp.Text := GetFile(NotifyRunApp.Text, 'All files (*.*)|*.*', GetCurrentDir); -{$ENDIF} -end; - -procedure TOptionsForm.FormKeyPress(Sender: TObject; var Key: char); -begin - if key = #27 then Close; -end; - -procedure TOptionsForm.ChooseFont(Sender: TObject); -var - dlg : TFontDialog; -begin - dlg := TFontDialog.Create(nil); - dlg.Font:= FontPreview.Font; - dlg.Font.Size:= FontSize.Value; - if dlg.Execute then - begin - FontSize.Value:= Dlg.Font.Size; - FontColor.ButtonColor:= Dlg.Font.Color; - FontPreview.Caption:= Dlg.Font.Name + ' : 1234567890'; - FontPreview.Font:= Dlg.Font; - FontPreview.Font.Size:= FONT_PREVIEW_FONT_SIZE; - end; - dlg.Free; -end; procedure TOptionsForm.FormCreate(Sender: TObject); var Config: TConfig; @@ -232,6 +164,8 @@ procedure TOptionsForm.FormCreate(Sender: TObject); // Create a thread that will call play command? CheckLoopAudio.Checked:= False; CheckLoopAudio.Enabled:= False; + CheckTicking.Checked:= False; + CheckTicking.Enabled:= False; {$ENDIF} end; @@ -278,6 +212,79 @@ procedure TOptionsForm.FormDestroy(Sender: TObject); end; end; +procedure TOptionsForm.FormKeyPress(Sender: TObject; var Key: char); +begin + if key = #27 then Close; +end; + +// TODO Move all text strings to the top + +function TOptionsForm.GetFile(Orig: String; FilterStr: String; Dir : String): String; +var + dlg : TOpenDialog; + fname : String; + cwd : String; + relPath : String; +begin + dlg := TOpenDialog.Create(nil); + dlg.FileName := ''; + if DirectoryExists(Dir) then + dlg.InitialDir := Dir + else + dlg.InitialDir := GetCurrentDir; + dlg.Options := [ofFileMustExist]; + dlg.Filter := FilterStr; + dlg.FilterIndex := 1; + + if dlg.Execute then + begin + fname := dlg.FileName; + cwd := GetCurrentDir + SEPARATOR; + relPath := ExtractRelativePath(cwd, ExtractFilePath(fname)); + Result := '.' + SEPARATOR + relPath + ExtractFileName(fname); + end else begin + Result := Orig; + end; + dlg.Free; +end; + +procedure TOptionsForm.GetAudioFile(Sender: TObject); +begin + NotifyAudio.Text := GetFile(NotifyAudio.Text, 'Wave files (*.wav)|*.wav', GetCurrentDir + SEPARATOR + 'sounds'); +end; + +procedure TOptionsForm.GetAppFile(Sender: TObject); +begin +{$IFDEF WINDOWS} + NotifyRunApp.Text := GetFile(NotifyRunApp.Text, 'Executables (*.exe)|*.exe|All files (*.*)|*.*', GetCurrentDir); +{$ENDIF} + +{$IFDEF LINUX} + NotifyRunApp.Text := GetFile(NotifyRunApp.Text, 'All files (*.*)|*.*', GetCurrentDir); +{$ENDIF} +end; + + +procedure TOptionsForm.ChooseFont(Sender: TObject); +var + dlg : TFontDialog; +begin + dlg := TFontDialog.Create(nil); + dlg.Font:= FontPreview.Font; + dlg.Font.Size:= FontSize.Value; + if dlg.Execute then + begin + FontSize.Value:= Dlg.Font.Size; + FontColor.ButtonColor:= Dlg.Font.Color; + FontPreview.Caption:= Dlg.Font.Name + ' : 1234567890'; + FontPreview.Font:= Dlg.Font; + FontPreview.Font.Size:= FONT_PREVIEW_FONT_SIZE; + end; + dlg.Free; +end; + + + procedure TOptionsForm.EnableFields(Sender: TObject); begin NotifyMsg.Enabled := NotifyMsgOn.Checked; From 4a18f8c32d555dd26463b27f9981541370979be8 Mon Sep 17 00:00:00 2001 From: neo85 Date: Tue, 20 Apr 2021 18:02:42 +0200 Subject: [PATCH 16/21] Compact mode + refactoring Some functionality moved from MainForm to MainController. Added CompactModeFrom. --- controllers/maincontroller.pas | 282 +++++++++++++++++++++++++ model/config.pas | 6 +- model/mytimer.pas | 2 +- model/utils.pas | 39 +++- snaptimer.lpr | 3 +- snaptimer.lps | 372 ++++++++++++++++++--------------- ui/compactmode.lfm | 24 +++ ui/compactmode.pas | 47 +++++ ui/mainform1.lfm | 29 +-- ui/mainform1.pas | 253 +++------------------- ui/options.lfm | 106 +++++----- ui/options.pas | 22 +- 12 files changed, 695 insertions(+), 490 deletions(-) create mode 100644 controllers/maincontroller.pas create mode 100644 ui/compactmode.lfm create mode 100644 ui/compactmode.pas diff --git a/controllers/maincontroller.pas b/controllers/maincontroller.pas new file mode 100644 index 0000000..3c514d9 --- /dev/null +++ b/controllers/maincontroller.pas @@ -0,0 +1,282 @@ +unit MainController; + +{$mode objfpc} + +interface + +uses + Classes, SysUtils, MyTimer; + +type + TMainController = class + private + FCompactMode : Boolean; + FTimer : TMyTimer; + procedure SetCompactMode(Value: Boolean); + + procedure OnTick(Sender: TObject); + procedure OnTimerStateChanged(Sender: TObject); + procedure UpdateTime; + procedure UpdateButtonsAndMenus; + public + constructor Create; + + // Called from MainForm and CompactModeForm + procedure ToggleCountdown(Sender: TObject); + procedure ResetCountdown(Sender: TObject); + + procedure PlayTicking; + procedure ShowTrayMessage(Msg: string); + procedure ShowDoneMessage(Msg: string); + + //property CompactMode : Boolean read FCompactMode write SetCompactMode; // Do we need this??? + property CompactMode : Boolean read FCompactMode; + property Timer : TMyTimer read FTimer; + end; + + +function GetMainController : TMainController; + +implementation + +uses Forms, MainForm1, CompactMode, Utils, Config, MsgBox; + +const + APP_NAME = 'SnapTimer'; + + // TODO this is a duplicate + // Buttons + BTN_START = '&Start'; + BTN_PAUSE = '&Pause'; + BTN_RESET = '&Reset'; + +var + MainControllerInstance : TMainController; + +procedure TMainController.SetCompactMode(Value: Boolean); +var Pos : TPoint; +begin + if (Value = FCompactMode) or (GetConfig.CompactMode = False) then + Exit; + + if Value = True then + begin + // Note: Width and ClientWidth are the same + CompactModeForm.Width:= MainForm.Count.Width; + CompactModeForm.Height:= MainForm.Count.Height; + Pos:= TUtils.GetControlPos(MainForm.Count); + CompactModeForm.Top:= Pos.Y; + CompactModeForm.Left:= Pos.X; + CompactModeForm.Count.Caption:= MainForm.Count.Caption; + CompactModeForm.Count.Font:= MainForm.Count.Font; + CompactModeForm.Count.Color:= MainForm.Count.Color; + CompactModeForm.Visible:= True; + MainForm.Visible:= False; + end + else + begin + MainForm.Count.Caption:= CompactModeForm.Count.Caption; + CompactModeForm.Visible:= False; + MainForm.Visible:= True; + end; + + FCompactMode:= Value; +end; + +procedure TMainController.OnTick(Sender: TObject); +begin + UpdateTime; +end; + +procedure TMainController.OnTimerStateChanged(Sender: TObject); +var Config: TConfig; +begin + Config:= GetConfig; + + case Timer.State of + Ready: + begin + SetCompactMode(False); + if Config.SecondsMode then + Timer.Seconds := MainForm.TimeEdit.Value + else + Timer.Minutes:= MainForm.TimeEdit.Value; + UpdateTime(); + UpdateButtonsAndMenus; + MainForm.TrayIconMain.Icon:= MainForm.ImgIconMain.Picture.Icon; + end; + + Running: + begin + UpdateButtonsAndMenus; + MainForm.TrayIconMain.Icon:= MainForm.ImgIconRunning.Picture.Icon; + if Config.TickingOn then + PlayTicking(); + + SetCompactMode(True); + end; + + Paused: + begin + SetCompactMode(False); + UpdateButtonsAndMenus; + MainForm.TrayIconMain.Icon:= MainForm.ImgIconPaused.Picture.Icon; + TUtils.StopAudio; + end; + + Finished: + begin + SetCompactMode(False); + UpdateTime(); + UpdateButtonsAndMenus; + TUtils.StopAudio; + + Application.Title := APP_NAME; + if Config.DoneAudioEnabled then + TUtils.PlayAudio(Config.DoneAudio, Config.LoopAudio); + if Config.DoneAppEnabled then + TUtils.RunApp(Config.DoneApp); + if Config.DoneTrayMsgEnabled then + ShowTrayMessage(Config.DoneTrayMsg); + if Config.DoneMessageEnabled then + ShowDoneMessage(Config.DoneMessage); + if Config.AutoRestart then + ToggleCountdown(Sender) + else + MainForm.TrayIconMain.Icon := MainForm.ImgIconDone.Picture.Icon; + + MainForm.ShowForm; + end; + end; +end; + +// TODO Animate icon between alarm icon and main so it blinks every second +// Instead of playing icon, animate the clock hands so they go around? +// http://delphi.about.com/od/kbwinshell/l/aa122501a.htm +procedure TMainController.UpdateTime(); +var + Time : String; +begin + Time:= TUtils.SecondsToTime(Timer.Seconds, GetConfig.HideSeconds); + + if CompactMode then + begin + CompactModeForm.Count.Visible := False; + CompactModeForm.Count.Caption := Time; + CompactModeForm.Count.Visible := True; + end + else + begin + // Changing caption while it's not visible keeps time from flashing + MainForm.Count.Visible := False; + MainForm.Count.Caption := Time; + MainForm.Count.Visible := True; + end; + + MainForm.MenuCount.Caption := Time; + if Timer.State = TState.Running then + begin + Application.Title := Time + ' - ' + APP_NAME; + MainForm.TrayIconMain.Hint := Time; + end + else + begin + Application.Title := APP_NAME; + MainForm.TrayIconMain.Hint := APP_NAME; + end; +end; + +// When it comes to buttons and menus, there are only two states. +procedure TMainController.UpdateButtonsAndMenus; +begin + with MainForm do + begin + if Timer.State = TState.Running then + begin + ImgPause.Visible := True; + ImgStart.Visible := False; + MenuToggle.Caption := BTN_PAUSE; + TrayMenuToggle.Caption := BTN_PAUSE; + end + else + begin + ImgPause.Visible := False; + ImgStart.Visible := True; + MenuToggle.Caption := BTN_START; + TrayMenuToggle.Caption := BTN_START; + end; + end; +end; + + + + + +constructor TMainController.Create; +begin + FCompactMode:= False; + FTimer:= TMyTimer.Create; + FTimer.OnSecondElapsed:= @OnTick; + FTimer.OnStateChanged:= @OnTimerStateChanged; +end; + +procedure TMainController.ToggleCountdown(Sender: TObject); +begin + if ((Sender = MainForm.Count) or (Sender = CompactModeForm.Count)) and (GetConfig.ClickTime = False) then + Exit; + + // We want to go from `Finished` state directly to `Running` state. (or not?) + if Timer.State = TState.Finished then + Timer.Reset; + + Timer.Toggle; +end; + +procedure TMainController.ResetCountdown(Sender: TObject); +begin + if ((Sender = MainForm.Count) or (Sender = CompactModeForm.Count)) and (GetConfig.DblClickTime = False) then + Exit; + + // TODO compact mode + + Timer.Reset; +end; + +procedure TMainController.PlayTicking(); +begin + TUtils.PlayAudio('.\sounds\ticking\ticking.wav', True); +end; + +procedure TMainController.ShowTrayMessage(Msg: string); +begin + with MainForm.TrayIconMain do + begin + BalloonHint := Msg; + ShowBalloonHint; + BalloonTimeout := 4000; + end; +end; + +procedure TMainController.ShowDoneMessage(Msg: string); +begin + // http://msdn.microsoft.com/en-us/library/ms645505(VS.85).aspx + //Windows.MessageBox(self.Handle, pChar(msg), 'Done', + // MB_SYSTEMMODAL or MB_SETFOREGROUND or MB_TOPMOST or MB_ICONINFORMATION); + MsgboxForm := TMsgBoxForm.Create(MainForm); + MsgBoxForm.SetText(Msg); + MsgboxForm.ShowModal; + MsgboxForm.Release; + TUtils.StopAudio; +end; + + +function GetMainController : TMainController; +begin + Result:= MainControllerInstance; +end; + +initialization + MainControllerInstance:= TMainController.Create; + +end. + diff --git a/model/config.pas b/model/config.pas index 2710348..b4d9b5b 100644 --- a/model/config.pas +++ b/model/config.pas @@ -76,7 +76,7 @@ TConfig = class property TickingOn: boolean read FTickingOn write FTickingOn; property AutoSave: boolean read FAutoSave write FAutoSave; property SecondsMode: boolean read FSecondsMode write FSecondsMode; // Undocumented mode to treat minutes as seconds - property CompactMode: boolean read FCompactMode write FCompactMode; // not implemented + property CompactMode: boolean read FCompactMode write FCompactMode; // Placement property WndHeight: integer read FWndHeight write FWndHeight; property WndWidth: integer read FWndWidth write FWndWidth; @@ -134,6 +134,7 @@ implementation INI_DONE_APP = 'RunApp'; INI_DONE_APP_ON = 'RunAppEnabled'; INI_SECONDS_MODE = 'SecondsMode'; + INI_COMPACT_MODE = 'CompactMode'; INI_FONT_NAME = 'Name'; INI_FONT_CHARSET = 'Charset'; INI_FONT_COLOR = 'Color'; @@ -163,6 +164,7 @@ implementation DEF_TICKING_PATH = '.\sounds\ticking\ticking.wav'; DEF_AUTOSAVE = True; DEF_SECONDS_MODE = False; + DEF_COMPACT_MODE = False; DEF_POSITION = Ord(Center); DEF_HEIGHT = 149; DEF_WIDTH = 214; @@ -287,6 +289,7 @@ function TConfig.Load : Boolean; TickingOn := IniFile.ReadBool(INI_SEC_MAIN, INI_TICKING_ON, DEF_TICKING_ON); AutoSave := IniFile.ReadBool(INI_SEC_MAIN, INI_AUTOSAVE, DEF_AUTOSAVE); SecondsMode := IniFile.ReadBool(INI_SEC_MAIN, INI_SECONDS_MODE, DEF_SECONDS_MODE); + CompactMode := IniFile.ReadBool(INI_SEC_MAIN, INI_COMPACT_MODE, DEF_COMPACT_MODE); // Placement WndHeight := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_HEIGHT, DEF_HEIGHT); WndWidth := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_WIDTH, DEF_WIDTH); @@ -337,6 +340,7 @@ function TConfig.Save : Boolean; IniFile.WriteBool(INI_SEC_MAIN, INI_TICKING_ON, TickingOn); IniFile.WriteBool(INI_SEC_MAIN, INI_AUTOSAVE, AutoSave); IniFile.WriteBool(INI_SEC_MAIN, INI_SECONDS_MODE, SecondsMode); + IniFile.WriteBool(INI_SEC_MAIN, INI_COMPACT_MODE, CompactMode); // Placement IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_HEIGHT, WndHeight); IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_WIDTH, WndWidth); diff --git a/model/mytimer.pas b/model/mytimer.pas index a523802..511b0c3 100644 --- a/model/mytimer.pas +++ b/model/mytimer.pas @@ -1,4 +1,4 @@ -unit mytimer; +unit MyTimer; {$mode objfpc} diff --git a/model/utils.pas b/model/utils.pas index 03ac371..927e55b 100644 --- a/model/utils.pas +++ b/model/utils.pas @@ -5,7 +5,7 @@ interface -Uses Forms, Types; +Uses Forms, Types, Controls; // https://forum.lazarus.freepascal.org/index.php/topic,29450.msg186002.html?PHPSESSID=0l7k7l8bgpqdvcqcqetk17rt04#msg186002 @@ -29,7 +29,8 @@ TUtils = class class procedure RunApp(Path: string); class function IsInteger(S: String): boolean; class function GetFormRect(Form: TForm) : TRect; - class procedure SetFormPos(Form: TForm; X: Integer; Y: Integer); + class procedure SetControlPos(Control: TWinControl; X: Integer; Y: Integer); + class function GetControlPos(Control: TWinControl) : TPoint; class procedure PlayAudio(Path: string; Loop: boolean); class procedure StopAudio; end; @@ -153,21 +154,43 @@ class function TUtils.GetFormRect(Form: TForm) : TRect; {$ENDIF} end; -class procedure TUtils.SetFormPos(Form: TForm; X: Integer; Y: Integer); +class procedure TUtils.SetControlPos(Control: TWinControl; X: Integer; Y: Integer); begin {$IFDEF WINDOWS} // It seems that there is no difference. - //SetWindowPos(Form.Handle, HWND_TOP, X, Y, 0, 0, SWP_SHOWWINDOW or SWP_NOSIZE); - Form.Left:= X; - Form.Top:= Y; + //SetWindowPos(Control.Handle, HWND_TOP, X, Y, 0, 0, SWP_SHOWWINDOW or SWP_NOSIZE); + Control.Left:= X; + Control.Top:= Y; {$ENDIF} {$IFDEF LINUX} - Form.Left:= X; - Form.Top:= Y; + Control.Left:= X; + Control.Top:= Y; {$ENDIF} end; +class function TUtils.GetControlPos(Control: TWinControl) : TPoint; +var r : TRect; + p : TPoint; +begin +{$IFDEF WINDOWS} + GetWindowRect(Control.Handle, r); + Result.X:= r.Left; + Result.Y:= r.Top; + + // Slightly different results + //p.X:= Control.Left; + //p.Y:= Control.Top; + //Result:= Control.ClientToScreen(p); +{$ENDIF} + +{$IFDEF LINUX} + // TODO test + //p.X:= Control.Left; + //p.Y:= Control.Top; + //Result:= Control.ClientToScreen(p); +{$ENDIF} +end; // https://wiki.freepascal.org/Play_Sound_Multiplatform // Modified diff --git a/snaptimer.lpr b/snaptimer.lpr index 22de19c..1498a28 100644 --- a/snaptimer.lpr +++ b/snaptimer.lpr @@ -3,7 +3,7 @@ {$mode objfpc}{$H+} uses - Forms, Interfaces, MainForm1, utils; + Forms, Interfaces, MainForm1, utils, CompactMode; {$R *.res} @@ -11,6 +11,7 @@ Application.Title:='SnapTimer'; Application.Initialize; Application.CreateForm(TMainForm, MainForm); + Application.CreateForm(TCompactModeForm, CompactModeForm); Application.Run; end. diff --git a/snaptimer.lps b/snaptimer.lps index 86ee16c..3422a1b 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,13 +4,13 @@ - + - - + + @@ -18,14 +18,15 @@ - + + - + @@ -34,10 +35,9 @@ - - - - + + + @@ -48,10 +48,10 @@ - - - - + + + + @@ -59,28 +59,26 @@ - - - - - + + + + - - - - - + + + + - - - - + + + + @@ -89,345 +87,373 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + + + + + diff --git a/ui/compactmode.lfm b/ui/compactmode.lfm new file mode 100644 index 0000000..64d2577 --- /dev/null +++ b/ui/compactmode.lfm @@ -0,0 +1,24 @@ +object CompactModeForm: TCompactModeForm + Left = 631 + Height = 207 + Top = 246 + Width = 305 + BorderStyle = bsNone + ClientHeight = 207 + ClientWidth = 305 + FormStyle = fsSystemStayOnTop + LCLVersion = '2.0.10.0' + object Count: TPanel + Left = 0 + Height = 207 + Top = 0 + Width = 305 + Align = alBottom + Anchors = [akTop, akLeft, akRight, akBottom] + BevelOuter = bvNone + Caption = 'Count' + TabOrder = 0 + OnClick = CountClick + OnDblClick = CountDblClick + end +end diff --git a/ui/compactmode.pas b/ui/compactmode.pas new file mode 100644 index 0000000..92adccc --- /dev/null +++ b/ui/compactmode.pas @@ -0,0 +1,47 @@ +unit CompactMode; + +{$mode objfpc} + +interface + +uses + Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls; + +type + + { TCompactModeForm } + + TCompactModeForm = class(TForm) + Count: TPanel; + procedure CountClick(Sender: TObject); + procedure CountDblClick(Sender: TObject); + private + + public + + end; + +var + CompactModeForm: TCompactModeForm; + +implementation + +uses MainController; + +{ TCompactModeForm } + +procedure TCompactModeForm.CountClick(Sender: TObject); +begin + GetMainController.ToggleCountdown(Sender); +end; + +procedure TCompactModeForm.CountDblClick(Sender: TObject); +begin + GetMainController.ResetCountdown(Sender); +end; + +initialization + {$I compactmode.lrs} + +end. + diff --git a/ui/mainform1.lfm b/ui/mainform1.lfm index 4070cb2..96307ee 100644 --- a/ui/mainform1.lfm +++ b/ui/mainform1.lfm @@ -10,10 +10,11 @@ object MainForm: TMainForm VertScrollBar.Range = 26 VertScrollBar.Visible = False ActiveControl = TimeEdit + Anchors = [] BorderIcons = [biSystemMenu, biMinimize] BorderStyle = bsSingle Caption = 'SnapTimer' - ClientHeight = 219 + ClientHeight = 222 ClientWidth = 259 Constraints.MinHeight = 70 Constraints.MinWidth = 160 @@ -24,7 +25,7 @@ object MainForm: TMainForm OnKeyPress = FormKeyPress OnShow = OnShowForm OnWindowStateChange = FormWindowStateChange - LCLVersion = '1.6.0.4' + LCLVersion = '2.0.10.0' object TimeLabel: TLabel AnchorSideRight.Control = TimeEdit Left = 7 @@ -437,7 +438,7 @@ object MainForm: TMainForm object TimeEdit: TSpinEdit AnchorSideLeft.Side = asrBottom Left = 59 - Height = 27 + Height = 23 Top = 2 Width = 47 BorderSpacing.Right = 5 @@ -450,7 +451,7 @@ object MainForm: TMainForm object Count: TPanel Cursor = crHandPoint Left = 2 - Height = 187 + Height = 190 Top = 32 Width = 255 Align = alBottom @@ -514,12 +515,12 @@ object MainForm: TMainForm } Visible = True OnClick = TrayIconMainClick - left = 112 - top = 160 + Left = 112 + Top = 160 end object MainMenu1: TMainMenu - left = 24 - top = 104 + Left = 24 + Top = 104 object MenuFile: TMenuItem Caption = 'File' object MenuToggle: TMenuItem @@ -560,8 +561,8 @@ object MainForm: TMainForm end end object TrayMenu: TPopupMenu - left = 24 - top = 160 + Left = 24 + Top = 160 object MenuCount: TMenuItem Caption = 'Countdown' Enabled = False @@ -598,12 +599,4 @@ object MainForm: TMainForm OnClick = CloseApp end end - object PopupMenuCompact: TPopupMenu - left = 128 - top = 104 - object MenuCompact: TMenuItem - Caption = 'Toggle compact mode' - OnClick = ToggleCompact - end - end end diff --git a/ui/mainform1.pas b/ui/mainform1.pas index c29dd25..1921322 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -6,8 +6,7 @@ interface uses Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, - Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, Options, DateUtils, - MyTimer; + Menus, StdCtrls, Spin, ExtCtrls, About, LCLType, Options, DateUtils; type { TMainForm } @@ -22,11 +21,9 @@ TMainForm = class(TForm) MainMenu1: TMainMenu; MenuItem1: TMenuItem; MenuItem2: TMenuItem; - MenuCompact: TMenuItem; MenuReset: TMenuItem; MenuToggle: TMenuItem; MenuSave: TMenuItem; - PopupMenuCompact: TPopupMenu; ImgReset: TImage; TrayMenuOptions: TMenuItem; TrayMenuShow: TMenuItem; @@ -56,44 +53,31 @@ TMainForm = class(TForm) procedure ToggleCountdown(Sender: TObject); procedure ResetCountdown(Sender: TObject); - procedure OnTick(Sender: TObject); - procedure OnTimerStateChanged(Sender: TObject); - procedure UpdateButtonsAndMenus; - procedure UpdateTime(); - - procedure ToggleCompact(Sender: TObject); procedure TimeEditChanged(Sender: TObject); procedure ShowAbout(Sender: TObject); procedure ShowOptions(Sender: TObject); procedure SaveSettings(Sender: TObject); - procedure ShowDoneMessage(Msg: string); - procedure ShowTrayMessage(Msg: string); - procedure SetFieldsVisible(showFields: boolean); - procedure PlayTicking(); + + procedure ShowForm; private - MyTimer : TMyTimer; OnShowFormFirstTime: Boolean; procedure ApplyConfig; - procedure ShowForm; public { public declarations } end; var MainForm: TMainForm; - //AppName: string; implementation uses - Config, Utils, MsgBox; + Config, Utils, MsgBox, MainController, MyTimer; { TMainForm } const - APP_NAME = 'SnapTimer'; - // Menu items MENU_FILE = '&File'; MENU_EXIT = 'E&xit'; @@ -122,9 +106,6 @@ procedure TMainForm.OnCreateForm(Sender: TObject); var Config: TConfig; begin - MyTimer:= TMyTimer.Create; - MyTimer.OnSecondElapsed:= @OnTick; - MyTimer.OnStateChanged:= @OnTimerStateChanged; Config:= GetConfig; MenuFile.Caption := MENU_FILE; MenuToggle.Caption := BTN_START; @@ -195,7 +176,7 @@ procedure TMainForm.OnShowForm(Sender: TObject); ApplyConfig; // ApplyConfig does not set MainForm dimensions and TimeEdit(TSpinEdit) TimeEdit.Value:= GetConfig.Minutes; - MyTimer.Reset; // This will trigger OnTimerStateChanged + GetMainController.Timer.Reset; // This will trigger OnTimerStateChanged end; end; @@ -241,7 +222,7 @@ procedure TMainForm.TrayIconMainClick(Sender: TObject); begin if WindowState = wsNormal then begin - // On Windows setting wsMinimized does not minimize to taskbar. + // Windows: setting wsMinimized does not minimize to taskbar. //WindowState:= wsMinimized; Application.Minimize; end @@ -259,126 +240,12 @@ procedure TMainForm.CloseApp(Sender: TObject); procedure TMainForm.ToggleCountdown(Sender: TObject); begin - if (Sender.ClassName = Count.ClassName) and (not GetConfig.ClickTime) then - Exit; - - // We want to go from `Finished` state directly to `Running` state. (or not?) - if MyTimer.State = TState.Finished then - MyTimer.Reset; - - MyTimer.Toggle; + GetMainController.ToggleCountdown(Sender); end; procedure TMainForm.ResetCountdown(Sender: TObject); begin - if (Sender.ClassName = Count.ClassName) and (not GetConfig.DblClickTime) then - Exit; - - MyTimer.Reset; -end; - -procedure TMainForm.OnTick(Sender: TObject); -begin - UpdateTime(); -end; - -procedure TMainForm.OnTimerStateChanged(Sender: TObject); -var Config: TConfig; -begin - Config:= GetConfig; - - case MyTimer.State of - Ready: - begin - if Config.SecondsMode then - MyTimer.Seconds := TimeEdit.Value - else - MyTimer.Minutes:= TimeEdit.Value; - UpdateTime(); - UpdateButtonsAndMenus; - TrayIconMain.Icon:= ImgIconMain.Picture.Icon; - end; - - Running: - begin - UpdateButtonsAndMenus; - TrayIconMain.Icon:= ImgIconRunning.Picture.Icon; - if Config.TickingOn then - PlayTicking(); - end; - - Paused: - begin - UpdateButtonsAndMenus; - TrayIconMain.Icon:= ImgIconPaused.Picture.Icon; - TUtils.StopAudio; - end; - - Finished: - begin - UpdateTime(); - UpdateButtonsAndMenus; - TUtils.StopAudio; - - Application.Title := APP_NAME; - if Config.DoneAudioEnabled then - TUtils.PlayAudio(Config.DoneAudio, Config.LoopAudio); - if Config.DoneAppEnabled then - TUtils.RunApp(Config.DoneApp); - if Config.DoneTrayMsgEnabled then - ShowTrayMessage(Config.DoneTrayMsg); - if Config.DoneMessageEnabled then - ShowDoneMessage(Config.DoneMessage); - if Config.AutoRestart then - ToggleCountdown(Sender) - else - TrayIconMain.Icon := ImgIconDone.Picture.Icon; - - ShowForm; - end; - end; -end; - - -// When it comes to buttons and menus, there are only two states. -procedure TMainForm.UpdateButtonsAndMenus; -begin - if MyTimer.State = TState.Running then - begin - ImgPause.Visible := True; - ImgStart.Visible := False; - MenuToggle.Caption := BTN_PAUSE; - TrayMenuToggle.Caption := BTN_PAUSE; - end - else - begin - ImgPause.Visible := False; - ImgStart.Visible := True; - MenuToggle.Caption := BTN_START; - TrayMenuToggle.Caption := BTN_START; - end; -end; - -// TODO Animate icon between alarm icon and main so it blinks every second -// Instead of playing icon, animate the clock hands so they go around? -// http://delphi.about.com/od/kbwinshell/l/aa122501a.htm -procedure TMainForm.UpdateTime(); -begin - // Changing caption while it's not visible keeps time from flashing - Count.Visible := False; - Count.Caption := TUtils.SecondsToTime(MyTimer.Seconds, GetConfig.HideSeconds); - Count.Visible := True; - MenuCount.Caption := Count.Caption; - if MyTimer.State = TState.Running then - begin - Application.Title := Count.Caption + ' - ' + APP_NAME; - TrayIconMain.Hint := Count.Caption; - end - else - begin - Application.Title := APP_NAME; - TrayIconMain.Hint := APP_NAME; - end; + GetMainController.ResetCountdown(Sender); end; @@ -402,19 +269,10 @@ procedure TMainForm.ShowOptions(Sender: TObject); Config.WndHeight:= r.Height; ApplyConfig; - //if not (Count.Font.Size = f.Size) then - //begin - //FontSizeChanged := True; - //GetPreferredSize(FormerWidth, FormerHeight, True); - //ShowMessageFmt('width: %d, height: %d', [FormerWidth, FormerHeight]); - //end; - //Count.Font := f; - // TODO Get font colors and background color to be shown - what's up? - - if MyTimer.State = TState.Running then + if GetMainController.Timer.State = TState.Running then begin if Config.TickingOn then - PlayTicking + GetMainController.PlayTicking else TUtils.StopAudio; end; @@ -456,69 +314,25 @@ procedure TMainForm.SaveSettings(Sender: TObject); procedure TMainForm.TimeEditChanged(Sender: TObject); begin - if (MyTimer.State <> TState.Running) and (MyTimer.State <> TState.Paused) then + if (GetMainController.Timer.State <> TState.Running) and (GetMainController.Timer.State <> TState.Paused) then ResetCountdown(Sender); end; -procedure TMainForm.ToggleCompact(Sender: TObject); -begin - // todo -// SetFieldsVisible(GetConfig.CompactMode); -// CompactMode := not CompactMode; -end; - - -procedure TMainForm.ShowTrayMessage(Msg: string); -begin - with TrayIconMain do - begin - BalloonHint := Msg; - ShowBalloonHint; - BalloonTimeout := 4000; - end; -end; - -procedure TMainForm.ShowDoneMessage(Msg: string); -begin - // http://msdn.microsoft.com/en-us/library/ms645505(VS.85).aspx - //Windows.MessageBox(self.Handle, pChar(msg), 'Done', - // MB_SYSTEMMODAL or MB_SETFOREGROUND or MB_TOPMOST or MB_ICONINFORMATION); - MsgboxForm := TMsgBoxForm.Create(Self); - MsgBoxForm.SetText(Msg); - MsgboxForm.ShowModal; - MsgboxForm.Release; - TUtils.StopAudio; -end; - -// TODO Figure out how to remove the fields from the form so they don't -// take up space anymore (and count can expand to top) -// Easier to create a new form, hide this one and show the other one -// Set border style to none too. -procedure TMainForm.SetFieldsVisible(showFields: boolean); +procedure TMainForm.ShowForm; begin - if showFields then - begin - MainForm.Menu := MainMenu1; - end - else - begin - MainForm.Menu := nil; - TimeLabel.Visible := showFields; - TimeEdit.Visible := showFields; - ImgStart.Visible := showFields; - ImgReset.Visible := showFields; - ImgPause.Visible := showFields; - end; -end; - + // Not sure why this behaviour is different. +{$IFDEF WINDOWS} + // Setting WindowState to wsNormal does not call FormOnWindowStateChange + WindowState:= wsNormal; + FormWindowStateChange(nil); +{$ENDIF} -procedure TMainForm.PlayTicking(); -begin - TUtils.PlayAudio('.\sounds\ticking\ticking.wav', True); +{$IFDEF LINUX} + Show; +{$ENDIF} end; - procedure TMainForm.ApplyConfig; var Config: TConfig; @@ -552,11 +366,11 @@ procedure TMainForm.ApplyConfig; self.Position:= poDesigned; case Config.WndPosition of - Remember: TUtils.SetFormPos(self, Config.WndLeft, Config.WndTop); - TopLeft: TUtils.SetFormPos(self, 0, 0); - TopRight: TUtils.SetFormPos(self, Monitor.WorkareaRect.Right - Width, 0); - BottomLeft: TUtils.SetFormPos(self, 0, Monitor.WorkareaRect.Bottom - Height); - BottomRight: TUtils.SetFormPos(self, Monitor.WorkareaRect.Right - Width, Monitor.WorkareaRect.Bottom - Height); + Remember: TUtils.SetControlPos(self, Config.WndLeft, Config.WndTop); + TopLeft: TUtils.SetControlPos(self, 0, 0); + TopRight: TUtils.SetControlPos(self, Monitor.WorkareaRect.Right - Width, 0); + BottomLeft: TUtils.SetControlPos(self, 0, Monitor.WorkareaRect.Bottom - Height); + BottomRight: TUtils.SetControlPos(self, Monitor.WorkareaRect.Right - Width, Monitor.WorkareaRect.Bottom - Height); Center: begin Self.Position := poScreenCenter; @@ -568,21 +382,6 @@ procedure TMainForm.ApplyConfig; end; -procedure TMainForm.ShowForm; -begin - // Not sure why this behaviour is different. - -{$IFDEF WINDOWS} - // Setting WindowState to wsNormal does not call FormOnWindowStateChange - WindowState:= wsNormal; - FormWindowStateChange(nil); -{$ENDIF} - -{$IFDEF LINUX} - Show; -{$ENDIF} -end; - initialization {$I mainform1.lrs} end. diff --git a/ui/options.lfm b/ui/options.lfm index 3a464f1..c84b5d7 100644 --- a/ui/options.lfm +++ b/ui/options.lfm @@ -1,12 +1,12 @@ object OptionsForm: TOptionsForm Left = 465 - Height = 249 + Height = 287 Top = 322 Width = 652 BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Options' - ClientHeight = 249 + ClientHeight = 287 ClientWidth = 652 Font.Name = 'Default' KeyPreview = True @@ -19,7 +19,7 @@ object OptionsForm: TOptionsForm object BtnOk: TButton Left = 488 Height = 25 - Top = 208 + Top = 248 Width = 75 Caption = '&OK' Default = True @@ -28,9 +28,9 @@ object OptionsForm: TOptionsForm TabOrder = 1 end object BtnCancel: TButton - Left = 573 + Left = 568 Height = 25 - Top = 208 + Top = 248 Width = 75 Caption = '&Cancel' ModalResult = 2 @@ -39,24 +39,24 @@ object OptionsForm: TOptionsForm end object PageControl1: TPageControl Left = 0 - Height = 200 + Height = 232 Top = 0 - Width = 648 - ActivePage = FontsTab + Width = 652 + ActivePage = GeneralTab ParentFont = False - TabIndex = 2 + TabIndex = 0 TabOrder = 0 object GeneralTab: TTabSheet Caption = 'General' - ClientHeight = 169 + ClientHeight = 204 ClientWidth = 644 ParentFont = False object CheckAutostart: TCheckBox Left = 171 - Height = 24 + Height = 19 Hint = 'Begin counting down when the application starts' Top = 74 - Width = 133 + Width = 100 Caption = '&Autostart timer' ParentFont = False ParentShowHint = False @@ -65,10 +65,10 @@ object OptionsForm: TOptionsForm end object CheckMinToTray: TCheckBox Left = 12 - Height = 24 + Height = 19 Hint = 'Don''t show the application in the taskbar when minimized' Top = 106 - Width = 136 + Width = 106 Caption = '&Minimize to tray' ParentFont = False ParentShowHint = False @@ -102,10 +102,10 @@ object OptionsForm: TOptionsForm end object CheckClickTime: TCheckBox Left = 171 - Height = 24 + Height = 19 Hint = 'Click the timer numerals to start/pause the timer' Top = 106 - Width = 146 + Width = 113 Caption = 'Cl&ick time to start' ParentFont = False ParentShowHint = False @@ -114,10 +114,10 @@ object OptionsForm: TOptionsForm end object CheckDblClickTime: TCheckBox Left = 171 - Height = 24 + Height = 19 Hint = 'Double-click the timer numerals to reset the timer' Top = 138 - Width = 200 + Width = 156 Caption = '&Double-click time to reset' ParentFont = False ParentShowHint = False @@ -126,10 +126,10 @@ object OptionsForm: TOptionsForm end object CheckAutoRestart: TCheckBox Left = 400 - Height = 24 + Height = 19 Hint = 'Start the timer again when the time reaches zero' - Top = 72 - Width = 211 + Top = 74 + Width = 161 Caption = 'Automatically &restart timer' ParentFont = False ParentShowHint = False @@ -138,10 +138,10 @@ object OptionsForm: TOptionsForm end object CheckLoopAudio: TCheckBox Left = 400 - Height = 24 + Height = 19 Hint = 'Continue playing the audio file until timer is reset' - Top = 104 - Width = 145 + Top = 106 + Width = 113 Caption = '&Loop audio alarm' ParentFont = False ParentShowHint = False @@ -150,10 +150,10 @@ object OptionsForm: TOptionsForm end object CheckAutoSave: TCheckBox Left = 12 - Height = 24 + Height = 19 Hint = 'Automatically save settings to the .ini file on exit' Top = 138 - Width = 150 + Width = 113 Caption = 'Auto&save settings' ParentFont = False ParentShowHint = False @@ -162,10 +162,10 @@ object OptionsForm: TOptionsForm end object CheckAlwaysOnTop: TCheckBox Left = 12 - Height = 24 + Height = 19 Hint = 'Show the window even when it''s deactivated' Top = 74 - Width = 122 + Width = 95 Caption = 'Al&ways on top' ParentFont = False ParentShowHint = False @@ -174,10 +174,10 @@ object OptionsForm: TOptionsForm end object CheckTicking: TCheckBox Left = 400 - Height = 24 + Height = 19 Hint = 'Play a ticking sound when timer is running' - Top = 136 - Width = 150 + Top = 138 + Width = 117 Caption = '&Play ticking sound' ParentFont = False ParentShowHint = False @@ -186,10 +186,10 @@ object OptionsForm: TOptionsForm end object CheckSecondsMode: TCheckBox Left = 400 - Height = 24 + Height = 19 Hint = 'Enter seconds instead of minutes' - Top = 41 - Width = 174 + Top = 42 + Width = 135 Caption = '&Enable seconds mode' ParentFont = False ParentShowHint = False @@ -198,20 +198,28 @@ object OptionsForm: TOptionsForm end object CheckHideSeconds: TCheckBox Left = 171 - Height = 24 + Height = 19 Hint = 'Begin counting down when the application starts' Top = 41 - Width = 117 + Width = 91 Caption = '&Hide seconds' ParentFont = False ParentShowHint = False ShowHint = True TabOrder = 11 end + object CheckCompactMode: TCheckBox + Left = 12 + Height = 19 + Top = 170 + Width = 103 + Caption = 'Compact Mode' + TabOrder = 12 + end end object AlarmsTab: TTabSheet Caption = 'Alarms' - ClientHeight = 169 + ClientHeight = 212 ClientWidth = 644 ParentFont = False object GroupBox1: TGroupBox @@ -226,9 +234,9 @@ object OptionsForm: TOptionsForm TabOrder = 0 object NotifyRunAppOn: TCheckBox Left = 5 - Height = 24 + Height = 19 Top = 92 - Width = 120 + Width = 93 Caption = '&Run program:' OnClick = EnableFields ParentFont = False @@ -236,9 +244,9 @@ object OptionsForm: TOptionsForm end object NotifyAudioOn: TCheckBox Left = 5 - Height = 24 + Height = 19 Top = 63 - Width = 122 + Width = 94 Caption = '&Play wave file:' OnClick = EnableFields ParentFont = False @@ -246,9 +254,9 @@ object OptionsForm: TOptionsForm end object NotifyTrayMsgOn: TCheckBox Left = 5 - Height = 24 + Height = 19 Top = 32 - Width = 146 + Width = 113 Caption = '&Show tray popup:' OnClick = EnableFields ParentFont = False @@ -256,9 +264,9 @@ object OptionsForm: TOptionsForm end object NotifyMsgOn: TCheckBox Left = 5 - Height = 24 + Height = 19 Top = 3 - Width = 143 + Width = 110 Caption = '&Display message:' OnClick = EnableFields ParentFont = False @@ -306,7 +314,7 @@ object OptionsForm: TOptionsForm end object NotifyTrayMsg: TEdit Left = 160 - Height = 27 + Height = 23 Top = 32 Width = 315 ParentFont = False @@ -324,7 +332,7 @@ object OptionsForm: TOptionsForm end object NotifyRunApp: TEdit Left = 160 - Height = 27 + Height = 23 Top = 88 Width = 232 ParentFont = False @@ -342,7 +350,7 @@ object OptionsForm: TOptionsForm end object NotifyAudio: TEdit Left = 160 - Height = 27 + Height = 23 Top = 61 Width = 232 ParentFont = False @@ -350,7 +358,7 @@ object OptionsForm: TOptionsForm end object NotifyMsg: TEdit Left = 160 - Height = 27 + Height = 23 Top = 3 Width = 315 ParentFont = False diff --git a/ui/options.pas b/ui/options.pas index a29300b..3b1072d 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -16,6 +16,7 @@ TOptionsForm = class(TForm) BtnCancel: TButton; BtnDefaults: TButton; CheckAutoRestart: TCheckBox; + CheckCompactMode: TCheckBox; CheckHideSeconds: TCheckBox; CheckSecondsMode: TCheckBox; CheckAutoSave: TCheckBox; @@ -83,7 +84,7 @@ TOptionsForm = class(TForm) implementation -uses Controls, MainForm1, Config, Utils; +uses Controls, MainForm1, Config, Utils, MainController; { TOptionsForm } const @@ -119,17 +120,16 @@ procedure TOptionsForm.FormCreate(Sender: TObject); CheckAlwaysOnTop.Checked:= Config.AlwaysOnTop; CheckMinToTray.Checked:= Config.MinToTray; - CheckAutoSave.Checked:= Config.AutoSave; - - CheckHideSeconds.Checked:= Config.HideSeconds; CheckAutostart.Checked:= Config.AutoStart; + CheckHideSeconds.Checked:= Config.HideSeconds; CheckClicktime.Checked:= Config.ClickTime; CheckDblClickTime.Checked:= Config.DblClickTime; - - CheckSecondsMode.Checked:= Config.SecondsMode; CheckAutoRestart.Checked:= Config.AutoRestart; CheckLoopAudio.Checked:= Config.LoopAudio; CheckTicking.Checked:= Config.TickingOn; + CheckAutoSave.Checked:= Config.AutoSave; + CheckSecondsMode.Checked:= Config.SecondsMode; + CheckCompactMode.Checked:= Config.CompactMode; // Alarms NotifyMsg.Text:= Config.DoneMessage; @@ -188,6 +188,7 @@ procedure TOptionsForm.FormDestroy(Sender: TObject); Config.TickingOn:= CheckTicking.Checked; Config.AutoSave:= CheckAutoSave.Checked; Config.SecondsMode:= CheckSecondsMode.Checked; + Config.CompactMode:= CheckCompactMode.Checked; // Placement Config.WndPosition:= TPosition(PositionCombo.ItemIndex); @@ -345,20 +346,17 @@ procedure TOptionsForm.TestAudio(Sender: TObject); procedure TOptionsForm.TestMessage(Sender: TObject); begin -with Self.Owner as TMainForm do - ShowDoneMessage(NotifyMsg.Text); + GetMainController.ShowDoneMessage(NotifyMsg.Text); end; procedure TOptionsForm.TestRunApp(Sender: TObject); begin - with Self.Owner as TMainForm do - TUtils.RunApp(NotifyRunApp.Text); + TUtils.RunApp(NotifyRunApp.Text); end; procedure TOptionsForm.TestTrayMsg(Sender: TObject); begin - with Self.Owner as TMainForm do - ShowTrayMessage(NotifyTrayMsg.Text); + GetMainController.ShowTrayMessage(NotifyTrayMsg.Text); end; initialization From ec1319eba376f7b9163952203f09eac319060817 Mon Sep 17 00:00:00 2001 From: neo85 Date: Tue, 20 Apr 2021 18:06:04 +0200 Subject: [PATCH 17/21] Refactor - Consts unit --- controllers/maincontroller.pas | 11 +-------- snaptimer.lpr | 2 +- ui/consts.pas | 44 ++++++++++++++++++++++++++++++++++ ui/mainform1.pas | 26 +------------------- ui/options.pas | 10 +------- 5 files changed, 48 insertions(+), 45 deletions(-) create mode 100644 ui/consts.pas diff --git a/controllers/maincontroller.pas b/controllers/maincontroller.pas index 3c514d9..8574492 100644 --- a/controllers/maincontroller.pas +++ b/controllers/maincontroller.pas @@ -39,16 +39,7 @@ function GetMainController : TMainController; implementation -uses Forms, MainForm1, CompactMode, Utils, Config, MsgBox; - -const - APP_NAME = 'SnapTimer'; - - // TODO this is a duplicate - // Buttons - BTN_START = '&Start'; - BTN_PAUSE = '&Pause'; - BTN_RESET = '&Reset'; +uses Forms, MainForm1, CompactMode, Utils, Config, MsgBox, Consts; var MainControllerInstance : TMainController; diff --git a/snaptimer.lpr b/snaptimer.lpr index 1498a28..829f2ab 100644 --- a/snaptimer.lpr +++ b/snaptimer.lpr @@ -3,7 +3,7 @@ {$mode objfpc}{$H+} uses - Forms, Interfaces, MainForm1, utils, CompactMode; + Forms, Interfaces, MainForm1, utils, CompactMode, consts; {$R *.res} diff --git a/ui/consts.pas b/ui/consts.pas new file mode 100644 index 0000000..68e3697 --- /dev/null +++ b/ui/consts.pas @@ -0,0 +1,44 @@ +unit Consts; + +{$mode objfpc} + +interface + +const + APP_NAME = 'SnapTimer'; + + // Menu items + MENU_FILE = '&File'; + MENU_EXIT = 'E&xit'; + MENU_EDIT = '&Edit'; + MENU_OPTIONS = 'Op&tions'; + MENU_HELP = '&Help'; + MENU_ABOUT = '&About'; + TRAY_MENU_SHOW = 'Show Application'; + TRAY_MENU_HIDE = 'Hide Application'; + + // Buttons + BTN_START = '&Start'; + BTN_PAUSE = '&Pause'; + BTN_RESET = '&Reset'; + + // Labels + LBL_MINUTES = '&Minutes:'; + LBL_SECONDS = '&Seconds:'; + + // Messages + MSG_OPEN_INI = 'An error occurred opening the .ini file, settings won''t be saved'; + MSG_WRITE_INI = 'An error occurred trying to write to the .ini file, settings won''t be saved.'; + +{$IFDEF WINDOWS} + SEPARATOR = '\'; +{$ENDIF} +{$IFDEF LINUX} + SEPARATOR = '/'; +{$ENDIF} + + +implementation + +end. + diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 1921322..65f11ef 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -73,34 +73,10 @@ TMainForm = class(TForm) implementation uses - Config, Utils, MsgBox, MainController, MyTimer; + Config, Utils, MsgBox, MainController, MyTimer, Consts; { TMainForm } -const - // Menu items - MENU_FILE = '&File'; - MENU_EXIT = 'E&xit'; - MENU_EDIT = '&Edit'; - MENU_OPTIONS = 'Op&tions'; - MENU_HELP = '&Help'; - MENU_ABOUT = '&About'; - TRAY_MENU_SHOW = 'Show Application'; - TRAY_MENU_HIDE = 'Hide Application'; - - // Buttons - BTN_START = '&Start'; - BTN_PAUSE = '&Pause'; - BTN_RESET = '&Reset'; - - // Labels - LBL_MINUTES = '&Minutes:'; - LBL_SECONDS = '&Seconds:'; - - // Messages - MSG_OPEN_INI = 'An error occurred opening the .ini file, settings won''t be saved'; - MSG_WRITE_INI = 'An error occurred trying to write to the .ini file, settings won''t be saved.'; - procedure TMainForm.OnCreateForm(Sender: TObject); var diff --git a/ui/options.pas b/ui/options.pas index 3b1072d..4d926af 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -84,19 +84,11 @@ TOptionsForm = class(TForm) implementation -uses Controls, MainForm1, Config, Utils, MainController; +uses Controls, MainForm1, Config, Utils, MainController, Consts; { TOptionsForm } const FONT_PREVIEW_FONT_SIZE = 18; -{$IFDEF WINDOWS} - SEPARATOR = '\'; -{$ENDIF} -{$IFDEF LINUX} - SEPARATOR = '/'; -{$ENDIF} - - procedure TOptionsForm.FormCreate(Sender: TObject); var Config: TConfig; From 6ccd83b61ccfb1bb1ef5f0098acea1c796f1d565 Mon Sep 17 00:00:00 2001 From: neo85 Date: Thu, 22 Apr 2021 18:57:18 +0200 Subject: [PATCH 18/21] Compact Mode - Transparency --- controllers/maincontroller.pas | 3 +- model/config.pas | 99 +++++++- model/utils.pas | 8 +- snaptimer.lpi | 37 ++- snaptimer.lps | 399 ++++++++++++++++++--------------- ui/compactmode.lfm | 1 + ui/compactmode.pas | 52 ++++- ui/options.lfm | 111 +++++++-- ui/options.pas | 38 +++- 9 files changed, 513 insertions(+), 235 deletions(-) diff --git a/controllers/maincontroller.pas b/controllers/maincontroller.pas index 8574492..8cfb528 100644 --- a/controllers/maincontroller.pas +++ b/controllers/maincontroller.pas @@ -47,7 +47,7 @@ implementation procedure TMainController.SetCompactMode(Value: Boolean); var Pos : TPoint; begin - if (Value = FCompactMode) or (GetConfig.CompactMode = False) then + if (Value = FCompactMode) or (GetConfig.CompactMode.Enabled = False) then Exit; if Value = True then @@ -61,6 +61,7 @@ procedure TMainController.SetCompactMode(Value: Boolean); CompactModeForm.Count.Caption:= MainForm.Count.Caption; CompactModeForm.Count.Font:= MainForm.Count.Font; CompactModeForm.Count.Color:= MainForm.Count.Color; + CompactModeForm.SetTransparency(GetConfig.CompactMode); CompactModeForm.Visible:= True; MainForm.Visible:= False; end diff --git a/model/config.pas b/model/config.pas index b4d9b5b..2ba9c62 100644 --- a/model/config.pas +++ b/model/config.pas @@ -7,10 +7,32 @@ interface uses Classes, SysUtils, Graphics; -type - TPosition = (Center, Remember, TopLeft, TopRight, BottomLeft, BottomRight); +const + COMPACT_MODE_ALPHA_MIN = 10; - TFontConfig = class +type + TPosition = ( + Center, + Remember, + TopLeft, + TopRight, + BottomLeft, + BottomRight, + // Aliases + _TPositionFirst = Center, + _TPositionLast = BottomRight + ); + + TCompactModeTransparency = ( + None, + TransparentBackground, + AlphaBlending, + // Aliases + _TCompactModeTransparencyFirst = None, + _TCompactTransparencyLast = AlphaBlending + ); + + TFontConfig = class Name: String; Charset: Integer; Color: Integer; @@ -19,6 +41,12 @@ TFontConfig = class Style: TFontStyles; end; + TCompactModeConfig = class + Enabled: Boolean; + Transparency: TCompactModeTransparency; + AlphaValue: Byte; + end; + TConfig = class private // Main @@ -34,7 +62,6 @@ TConfig = class FTickingOn: boolean; FAutoSave: boolean; FSecondsMode: boolean; - FCompactMode: boolean; // Placement FWndHeight: integer; FWndWidth: integer; @@ -53,7 +80,10 @@ TConfig = class // Font FFont: TFontConfig; FDefaultFont: TFontConfig; + // Compact Mode + FCompactMode: TCompactModeConfig; + procedure SetWndPosition(Value: TPosition); procedure SetDoneMessage(Msg: String); procedure SetDoneTrayMessage(Msg: String); public @@ -76,13 +106,12 @@ TConfig = class property TickingOn: boolean read FTickingOn write FTickingOn; property AutoSave: boolean read FAutoSave write FAutoSave; property SecondsMode: boolean read FSecondsMode write FSecondsMode; // Undocumented mode to treat minutes as seconds - property CompactMode: boolean read FCompactMode write FCompactMode; // Placement property WndHeight: integer read FWndHeight write FWndHeight; property WndWidth: integer read FWndWidth write FWndWidth; property WndLeft: integer read FWndLeft write FWndLeft; property WndTop: integer read FWndTop write FWndTop; - property WndPosition: TPosition read FWndPosition write FWndPosition; + property WndPosition: TPosition read FWndPosition write SetWndPosition; // Alarm property DoneMessage: string read FDoneMessage write SetDoneMessage; property DoneMessageEnabled: boolean read FDoneMessageEnabled write FDoneMessageEnabled; @@ -94,6 +123,8 @@ TConfig = class property DoneAppEnabled: boolean read FDoneAppEnabled write FDoneAppEnabled; // Fonts property Font: TFontConfig read FFont write FFont; + // Compact Mode + property CompactMode: TCompactModeConfig read FCompactMode write FCompactMode; end; function GetConfig : TConfig; @@ -109,6 +140,7 @@ implementation INI_SEC_ALARMS = 'Alarms'; INI_SEC_PLACEMENT = 'Placement'; INI_SEC_FONTS = 'Fonts'; + INI_SEC_COMPACT_MODE = 'CompactMode'; INI_MINUTES = 'Minutes'; INI_ALWAYS_ON_TOP = 'AlwaysOnTop'; INI_MIN_TO_TRAY = 'MinToTray'; @@ -134,13 +166,15 @@ implementation INI_DONE_APP = 'RunApp'; INI_DONE_APP_ON = 'RunAppEnabled'; INI_SECONDS_MODE = 'SecondsMode'; - INI_COMPACT_MODE = 'CompactMode'; INI_FONT_NAME = 'Name'; INI_FONT_CHARSET = 'Charset'; INI_FONT_COLOR = 'Color'; INI_FONT_SIZE = 'Size'; INI_FONT_STYLE = 'Style'; INI_BG_COLOR = 'BgColor'; + INI_COMPACT_MODE_ENABLED = 'Enabled'; + INI_COMPACT_MODE_TRANSPARENCY = 'Transparency'; + INI_COMPACT_MODE_ALPHA_VALUE = 'AlphaValue'; INI_FILE_NAME = 'SnapTimer.ini'; DEF_TIME = 15; @@ -164,7 +198,6 @@ implementation DEF_TICKING_PATH = '.\sounds\ticking\ticking.wav'; DEF_AUTOSAVE = True; DEF_SECONDS_MODE = False; - DEF_COMPACT_MODE = False; DEF_POSITION = Ord(Center); DEF_HEIGHT = 149; DEF_WIDTH = 214; @@ -174,6 +207,9 @@ implementation DEF_FONT_BG_COLOR = clNone; DEF_FONT_SIZE = 38; DEF_FONT_STYLE = 0; + DEF_COMPACT_MODE = False; + DEF_COMPACT_MODE_TRANSPARENCY = TCompactModeTransparency.None; + DEF_COMPACT_MODE_ALPHA = 128; var ConfigInstance : TConfig; @@ -218,6 +254,17 @@ procedure TConfig.SetDoneTrayMessage(Msg: String); end; +procedure TConfig.SetWndPosition(Value: TPosition); +begin + if Value < _TPositionFirst then + Value:= _TPositionFirst; + + if Value > _TPositionLast then + Value:= _TPositionLast; + + FWndPosition:= Value; +end; + constructor TConfig.Create; begin // Main @@ -233,7 +280,6 @@ constructor TConfig.Create; TickingOn:= DEF_TICKING_ON; AutoSave:= DEF_AUTOSAVE; SecondsMode:= DEF_SECONDS_MODE; - CompactMode := False; // Placement WndPosition:= TPosition(DEF_POSITION); WndWidth:= DEF_WIDTH; @@ -255,6 +301,11 @@ constructor TConfig.Create; Font.BgColor:= DEF_FONT_BG_COLOR; Font.Size:= DEF_FONT_SIZE; Font.Style:= IntToFontStyles(DEF_FONT_STYLE); + // CompactMode + CompactMode := TCompactModeConfig.Create; + CompactMode.Enabled:= DEF_COMPACT_MODE; + CompactMode.Transparency:= DEF_COMPACT_MODE_TRANSPARENCY; + CompactMode.AlphaValue:= DEF_COMPACT_MODE_ALPHA; FDefaultFont:= TFontConfig.Create; FDefaultFont.Name:= DEF_FONT_NAME; @@ -271,6 +322,15 @@ destructor TConfig.Free; FDefaultFont.Free; end; +function ReadInt(InIfile: TIniFile; const Section, Ident: String; Min, Max, Default: Integer) : Integer; +begin + Result:= IniFile.ReadInteger(Section, Ident, Default); + if Result < Min then + Result:= Min; + if Result > Max then + Result:= Max; +end; + function TConfig.Load : Boolean; var IniFile: TIniFile; begin @@ -289,13 +349,12 @@ function TConfig.Load : Boolean; TickingOn := IniFile.ReadBool(INI_SEC_MAIN, INI_TICKING_ON, DEF_TICKING_ON); AutoSave := IniFile.ReadBool(INI_SEC_MAIN, INI_AUTOSAVE, DEF_AUTOSAVE); SecondsMode := IniFile.ReadBool(INI_SEC_MAIN, INI_SECONDS_MODE, DEF_SECONDS_MODE); - CompactMode := IniFile.ReadBool(INI_SEC_MAIN, INI_COMPACT_MODE, DEF_COMPACT_MODE); // Placement WndHeight := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_HEIGHT, DEF_HEIGHT); WndWidth := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_WIDTH, DEF_WIDTH); WndLeft := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_LEFT, 0); WndTop := IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_TOP, 0); - WndPosition := TPosition(IniFile.ReadInteger(INI_SEC_PLACEMENT, INI_POSITION, DEF_POSITION)); + WndPosition := TPosition(ReadInt(IniFile, INI_SEC_PLACEMENT, INI_POSITION, Ord(_TPositionFirst), Ord(_TPositionLast), DEF_POSITION)); // Alarm DoneMessage := IniFile.ReadString(INI_SEC_ALARMS, INI_DONE_MESSAGE, DEF_DONE_MSG); DoneMessageEnabled := IniFile.ReadBool(INI_SEC_ALARMS, INI_DONE_MESSAGE_ON, DEF_DONE_MSG_ON); @@ -312,6 +371,11 @@ function TConfig.Load : Boolean; Font.BgColor := IniFile.ReadInteger(INI_SEC_FONTS, INI_BG_COLOR, DEF_FONT_BG_COLOR); Font.Size := IniFile.ReadInteger(INI_SEC_FONTS, INI_FONT_SIZE, DEF_FONT_SIZE); Font.Style := IntToFontStyles(IniFile.ReadInteger(INI_SEC_FONTS, INI_FONT_STYLE, DEF_FONT_STYLE)); + // CompactMode + CompactMode.Enabled := IniFile.ReadBool(INI_SEC_COMPACT_MODE, INI_COMPACT_MODE_ENABLED, DEF_COMPACT_MODE); + CompactMode.Transparency:= TCompactModeTransparency(ReadInt(IniFile, INI_SEC_COMPACT_MODE, INI_COMPACT_MODE_TRANSPARENCY, + Ord(_TCompactModeTransparencyFirst), Ord(_TCompactTransparencyLast), Ord(DEF_COMPACT_MODE_TRANSPARENCY))); + CompactMode.AlphaValue:= ReadInt(Inifile, INI_SEC_COMPACT_MODE, INI_COMPACT_MODE_ALPHA_VALUE, COMPACT_MODE_ALPHA_MIN, 255, DEF_COMPACT_MODE_ALPHA); except Result:= False; end; @@ -319,6 +383,14 @@ function TConfig.Load : Boolean; if Assigned(IniFile) then IniFile.Free; + +{$IFDEF LINUX} + // Not supported on Linux + LoopAudio:= False; + TickingOn:= False; + if CompactMode.Transparency = TransparentBackground then + CompactMode.Transparency:= None; +{$ENDIF} end; function TConfig.Save : Boolean; @@ -340,7 +412,6 @@ function TConfig.Save : Boolean; IniFile.WriteBool(INI_SEC_MAIN, INI_TICKING_ON, TickingOn); IniFile.WriteBool(INI_SEC_MAIN, INI_AUTOSAVE, AutoSave); IniFile.WriteBool(INI_SEC_MAIN, INI_SECONDS_MODE, SecondsMode); - IniFile.WriteBool(INI_SEC_MAIN, INI_COMPACT_MODE, CompactMode); // Placement IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_HEIGHT, WndHeight); IniFile.WriteInteger(INI_SEC_PLACEMENT, INI_WIDTH, WndWidth); @@ -363,6 +434,10 @@ function TConfig.Save : Boolean; IniFile.WriteInteger(INI_SEC_FONTS, INI_BG_COLOR, Font.BgColor); IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_SIZE, Font.Size); IniFile.WriteInteger(INI_SEC_FONTS, INI_FONT_STYLE, FontStylesToInt(Font.Style)); + // CompactMode + IniFile.WriteBool(INI_SEC_COMPACT_MODE, INI_COMPACT_MODE_ENABLED, CompactMode.Enabled); + IniFile.WriteInteger(INI_SEC_COMPACT_MODE, INI_COMPACT_MODE_TRANSPARENCY, Ord(CompactMode.Transparency)); + IniFile.WriteInteger(INI_SEC_COMPACT_MODE, INI_COMPACT_MODE_ALPHA_VALUE, CompactMode.AlphaValue); IniFile.UpdateFile; except diff --git a/model/utils.pas b/model/utils.pas index 927e55b..5d4dabe 100644 --- a/model/utils.pas +++ b/model/utils.pas @@ -185,10 +185,10 @@ class function TUtils.GetControlPos(Control: TWinControl) : TPoint; {$ENDIF} {$IFDEF LINUX} - // TODO test - //p.X:= Control.Left; - //p.Y:= Control.Top; - //Result:= Control.ClientToScreen(p); + // TODO: This works on Lubuntu, bit of a hack + p.X:= Control.Left - 2; + p.Y:= Control.Top - 32; + Result:= Control.ClientToScreen(p); {$ENDIF} end; diff --git a/snaptimer.lpi b/snaptimer.lpi index f721b0b..078e324 100644 --- a/snaptimer.lpi +++ b/snaptimer.lpi @@ -1,7 +1,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -65,9 +65,16 @@ - + + + + + + + + @@ -78,7 +85,7 @@ - + @@ -93,6 +100,7 @@ + @@ -118,6 +126,7 @@ + @@ -130,6 +139,24 @@ + + + + + + + + + + + + + + + + + + @@ -140,7 +167,7 @@ - + diff --git a/snaptimer.lps b/snaptimer.lps index 3422a1b..4ecef4d 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -4,13 +4,13 @@ - + - + @@ -18,7 +18,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -35,9 +35,9 @@ - - - + + + @@ -49,9 +49,10 @@ - - - + + + + @@ -59,27 +60,29 @@ - - - - + + + + + - - - + + + + + - + - - + @@ -87,10 +90,9 @@ - + - - + @@ -99,361 +101,392 @@ - - - - + + + + + + - - - + + + + + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - - - - - - - - + - + + - - + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - - - - - - + + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + diff --git a/ui/compactmode.lfm b/ui/compactmode.lfm index 64d2577..432122a 100644 --- a/ui/compactmode.lfm +++ b/ui/compactmode.lfm @@ -9,6 +9,7 @@ object CompactModeForm: TCompactModeForm FormStyle = fsSystemStayOnTop LCLVersion = '2.0.10.0' object Count: TPanel + Cursor = crHandPoint Left = 0 Height = 207 Top = 0 diff --git a/ui/compactmode.pas b/ui/compactmode.pas index 92adccc..9dd5dc5 100644 --- a/ui/compactmode.pas +++ b/ui/compactmode.pas @@ -5,7 +5,7 @@ interface uses - Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls; + Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls, Config; type @@ -15,10 +15,11 @@ TCompactModeForm = class(TForm) Count: TPanel; procedure CountClick(Sender: TObject); procedure CountDblClick(Sender: TObject); - private - - public + procedure SetTransparency(Value: TCompactModeConfig); + protected + procedure SetColor(Value: TColor); override; + private end; var @@ -26,7 +27,11 @@ TCompactModeForm = class(TForm) implementation -uses MainController; +uses MainController +{$IFDEF WINDOWS} + ,Windows +{$ENDIF} + ; { TCompactModeForm } @@ -40,6 +45,43 @@ procedure TCompactModeForm.CountDblClick(Sender: TObject); GetMainController.ResetCountdown(Sender); end; + +procedure TCompactModeForm.SetTransparency(Value: TCompactModeConfig); +begin + case Value.Transparency of + None: + begin + AlphaBlend:= False; + end; + + TransparentBackground: + begin +{$IFDEF WINDOWS} + // Antialiased fonts create weird visual artifact. + Count.Font.Quality:= fqNonAntialiased; + SetWindowLongPtr(Self.Handle, GWL_EXSTYLE,GetWindowLongPtr(Self.Handle, GWL_EXSTYLE) or WS_EX_LAYERED); + SetLayeredWindowAttributes(Self.Handle, Count.Color, 0, LWA_COLORKEY); +{$ENDIF} + end; + + AlphaBlending: + begin + AlphaBlend:= True; + AlphaBlendValue:= Value.AlphaValue; + end; + end; +end; + +procedure TCompactModeForm.SetColor(Value: TColor); +begin + inherited SetColor(Value); +{$IFDEF WINDOWS} + if GetConfig.CompactMode.Transparency = TransparentBackground then + SetLayeredWindowAttributes(Self.Handle, Value, 0, LWA_COLORKEY); +{$ENDIF} +end; + + initialization {$I compactmode.lrs} diff --git a/ui/options.lfm b/ui/options.lfm index c84b5d7..b1784f0 100644 --- a/ui/options.lfm +++ b/ui/options.lfm @@ -1,12 +1,12 @@ object OptionsForm: TOptionsForm Left = 465 - Height = 287 + Height = 254 Top = 322 Width = 652 BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Options' - ClientHeight = 287 + ClientHeight = 254 ClientWidth = 652 Font.Name = 'Default' KeyPreview = True @@ -17,9 +17,9 @@ object OptionsForm: TOptionsForm ShowInTaskBar = stNever LCLVersion = '2.0.10.0' object BtnOk: TButton - Left = 488 + Left = 480 Height = 25 - Top = 248 + Top = 216 Width = 75 Caption = '&OK' Default = True @@ -30,7 +30,7 @@ object OptionsForm: TOptionsForm object BtnCancel: TButton Left = 568 Height = 25 - Top = 248 + Top = 216 Width = 75 Caption = '&Cancel' ModalResult = 2 @@ -39,16 +39,16 @@ object OptionsForm: TOptionsForm end object PageControl1: TPageControl Left = 0 - Height = 232 + Height = 208 Top = 0 Width = 652 - ActivePage = GeneralTab + ActivePage = TabSheet1 ParentFont = False - TabIndex = 0 + TabIndex = 3 TabOrder = 0 object GeneralTab: TTabSheet Caption = 'General' - ClientHeight = 204 + ClientHeight = 172 ClientWidth = 644 ParentFont = False object CheckAutostart: TCheckBox @@ -78,7 +78,7 @@ object OptionsForm: TOptionsForm object GroupBox2: TGroupBox Left = 4 Height = 48 - Top = 13 + Top = 12 Width = 160 Caption = 'Starting position' ClientHeight = 28 @@ -208,18 +208,10 @@ object OptionsForm: TOptionsForm ShowHint = True TabOrder = 11 end - object CheckCompactMode: TCheckBox - Left = 12 - Height = 19 - Top = 170 - Width = 103 - Caption = 'Compact Mode' - TabOrder = 12 - end end object AlarmsTab: TTabSheet Caption = 'Alarms' - ClientHeight = 212 + ClientHeight = 172 ClientWidth = 644 ParentFont = False object GroupBox1: TGroupBox @@ -369,7 +361,7 @@ object OptionsForm: TOptionsForm object FontsTab: TTabSheet Caption = 'Font' ClientHeight = 172 - ClientWidth = 640 + ClientWidth = 644 ParentFont = False object TimerFontBox: TGroupBox Left = 4 @@ -480,5 +472,84 @@ object OptionsForm: TOptionsForm TabOrder = 1 end end + object TabSheet1: TTabSheet + Caption = 'Compact Mode' + ClientHeight = 180 + ClientWidth = 644 + object CheckCompactMode: TCheckBox + Left = 16 + Height = 19 + Top = 16 + Width = 62 + Caption = 'Enabled' + OnClick = EnableFields + TabOrder = 0 + end + object AlphaLabel: TLabel + Left = 200 + Height = 15 + Top = 20 + Width = 31 + Caption = 'Alpha' + ParentColor = False + end + object AlphaValueTrackBar: TTrackBar + Left = 240 + Height = 25 + Top = 16 + Width = 104 + LineSize = 0 + Max = 255 + Min = 10 + Position = 128 + TickMarks = tmTopLeft + TickStyle = tsNone + TabOrder = 1 + end + object TransparencyRadioGroup: TRadioGroup + Left = 16 + Height = 112 + Top = 56 + Width = 168 + AutoFill = True + Caption = 'Transparency' + ChildSizing.LeftRightSpacing = 6 + ChildSizing.EnlargeHorizontal = crsHomogenousChildResize + ChildSizing.EnlargeVertical = crsHomogenousChildResize + ChildSizing.ShrinkHorizontal = crsScaleChilds + ChildSizing.ShrinkVertical = crsScaleChilds + ChildSizing.Layout = cclLeftToRightThenTopToBottom + ChildSizing.ControlsPerLine = 1 + ClientHeight = 92 + ClientWidth = 164 + TabOrder = 2 + object RadioTrNone: TRadioButton + Left = 6 + Height = 31 + Top = 0 + Width = 152 + Caption = 'None' + OnClick = EnableFields + TabOrder = 2 + end + object RadioTrBackground: TRadioButton + Left = 6 + Height = 31 + Top = 31 + Width = 152 + Caption = 'Transparent background' + TabOrder = 0 + end + object RadioTrAlphaBlending: TRadioButton + Left = 6 + Height = 30 + Top = 62 + Width = 152 + Caption = 'Alpha blending' + OnChange = EnableFields + TabOrder = 1 + end + end + end end end diff --git a/ui/options.pas b/ui/options.pas index 4d926af..e0d220d 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -5,7 +5,8 @@ interface uses - SysUtils, LResources, Forms, Graphics, Dialogs, StdCtrls, ComCtrls, Spin, Classes; + SysUtils, LResources, Forms, Graphics, Dialogs, StdCtrls, ComCtrls, Spin, + ExtCtrls, Classes; type @@ -33,8 +34,14 @@ TOptionsForm = class(TForm) FontSize: TSpinEdit; Label1: TLabel; FontPreview: TStaticText; + AlphaLabel: TLabel; LabelTextColor: TLabel; LabelTextColor1: TLabel; + RadioTrNone: TRadioButton; + RadioTrBackground: TRadioButton; + RadioTrAlphaBlending: TRadioButton; + TransparencyRadioGroup: TRadioGroup; + TabSheet1: TTabSheet; TimerFontBox: TGroupBox; PositionCombo: TComboBox; GroupBox1: TGroupBox; @@ -57,6 +64,7 @@ TOptionsForm = class(TForm) GeneralTab: TTabSheet; AlarmsTab: TTabSheet; FontsTab: TTabSheet; + AlphaValueTrackBar: TTrackBar; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: char); @@ -121,7 +129,6 @@ procedure TOptionsForm.FormCreate(Sender: TObject); CheckTicking.Checked:= Config.TickingOn; CheckAutoSave.Checked:= Config.AutoSave; CheckSecondsMode.Checked:= Config.SecondsMode; - CheckCompactMode.Checked:= Config.CompactMode; // Alarms NotifyMsg.Text:= Config.DoneMessage; @@ -149,18 +156,26 @@ procedure TOptionsForm.FormCreate(Sender: TObject); FontPreview.Color:= Config.Font.BgColor; FontPreview.Caption:= Config.Font.Name + ' : 1234567890'; + // Compact Mode + CheckCompactMode.Checked:= Config.CompactMode.Enabled; + case Config.CompactMode.Transparency of + None: RadioTrNone.Checked:= True; + TransparentBackground: RadioTrBackground.Checked:= True; + AlphaBlending: RadioTrAlphaBlending.Checked:= True; + end; + AlphaValueTrackBar.Position:= Config.CompactMode.AlphaValue; EnableFields(Sender); {$IFDEF LINUX} // TODO: Not supported yet // Create a thread that will call play command? - CheckLoopAudio.Checked:= False; CheckLoopAudio.Enabled:= False; - CheckTicking.Checked:= False; CheckTicking.Enabled:= False; + RadioTrBackground.Enabled:= False; {$ENDIF} end; + procedure TOptionsForm.FormDestroy(Sender: TObject); var Config : TConfig; begin @@ -180,7 +195,6 @@ procedure TOptionsForm.FormDestroy(Sender: TObject); Config.TickingOn:= CheckTicking.Checked; Config.AutoSave:= CheckAutoSave.Checked; Config.SecondsMode:= CheckSecondsMode.Checked; - Config.CompactMode:= CheckCompactMode.Checked; // Placement Config.WndPosition:= TPosition(PositionCombo.ItemIndex); @@ -202,6 +216,16 @@ procedure TOptionsForm.FormDestroy(Sender: TObject); Config.Font.BgColor:= FontPreview.Color; Config.Font.Size:= FontSize.Value; // FontPreview has fixed size Config.Font.Style:= FontPreview.Font.Style; + + // Compact Mode + Config.CompactMode.Enabled:= CheckCompactMode.Checked; + if RadioTrNone.Checked then + Config.CompactMode.Transparency:= None + else if RadioTrBackground.Checked then + Config.CompactMode.Transparency:= TransparentBackground + else if RadioTrAlphaBlending.Checked then + Config.CompactMode.Transparency:= AlphaBlending; + Config.CompactMode.AlphaValue:= AlphaValueTrackBar.Position; end; end; @@ -292,6 +316,10 @@ procedure TOptionsForm.EnableFields(Sender: TObject); NotifyRunApp.Enabled := NotifyRunAppOn.Checked; NotifyRunBtn.Enabled := NotifyRunAppOn.Checked; NotifyRunTest.Enabled := NotifyRunAppOn.Checked; + + TransparencyRadioGroup.Enabled:= CheckCompactMode.Checked; + AlphaLabel.Enabled:= CheckCompactMode.Checked And RadioTrAlphaBlending.Checked; + AlphaValueTrackBar.Enabled:= CheckCompactMode.Checked And RadioTrAlphaBlending.Checked; end; From 2441feab0cd2cbee6c67b976ca07edd3f9291638 Mon Sep 17 00:00:00 2001 From: neo85 Date: Fri, 23 Apr 2021 09:04:10 +0200 Subject: [PATCH 19/21] Small fix --- model/utils.pas | 29 ++----- snaptimer.lps | 199 ++++++++++++++++++++++++------------------------ 2 files changed, 106 insertions(+), 122 deletions(-) diff --git a/model/utils.pas b/model/utils.pas index 5d4dabe..68cdc01 100644 --- a/model/utils.pas +++ b/model/utils.pas @@ -156,40 +156,23 @@ class function TUtils.GetFormRect(Form: TForm) : TRect; class procedure TUtils.SetControlPos(Control: TWinControl; X: Integer; Y: Integer); begin -{$IFDEF WINDOWS} // It seems that there is no difference. //SetWindowPos(Control.Handle, HWND_TOP, X, Y, 0, 0, SWP_SHOWWINDOW or SWP_NOSIZE); Control.Left:= X; Control.Top:= Y; -{$ENDIF} - -{$IFDEF LINUX} - Control.Left:= X; - Control.Top:= Y; -{$ENDIF} end; class function TUtils.GetControlPos(Control: TWinControl) : TPoint; var r : TRect; p : TPoint; begin -{$IFDEF WINDOWS} - GetWindowRect(Control.Handle, r); - Result.X:= r.Left; - Result.Y:= r.Top; - - // Slightly different results - //p.X:= Control.Left; - //p.Y:= Control.Top; - //Result:= Control.ClientToScreen(p); -{$ENDIF} - -{$IFDEF LINUX} - // TODO: This works on Lubuntu, bit of a hack - p.X:= Control.Left - 2; - p.Y:= Control.Top - 32; + //GetWindowRect(Control.Handle, r); + //Result.X:= r.Left; + //Result.Y:= r.Top; + // No difference + p.X:= 0; + p.Y:= 0; Result:= Control.ClientToScreen(p); -{$ENDIF} end; // https://wiki.freepascal.org/Play_Sound_Multiplatform diff --git a/snaptimer.lps b/snaptimer.lps index 4ecef4d..672bdfa 100644 --- a/snaptimer.lps +++ b/snaptimer.lps @@ -10,7 +10,7 @@ - + @@ -18,7 +18,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -48,11 +48,10 @@ - - + - - + + @@ -60,29 +59,31 @@ - + - + - + - + - - - - + + + + + + @@ -92,7 +93,7 @@ - + @@ -101,10 +102,10 @@ - + - + @@ -113,19 +114,19 @@ - + - + - + - + @@ -134,7 +135,7 @@ - + @@ -142,14 +143,14 @@ - + - + @@ -158,7 +159,7 @@ - + @@ -166,13 +167,13 @@ - + - + @@ -180,50 +181,50 @@ - + - + - + - + - + - + - + - + @@ -231,21 +232,21 @@ - + - + - + @@ -253,7 +254,7 @@ - + @@ -261,28 +262,28 @@ - + - + - + - + @@ -290,28 +291,28 @@ - + - + - + - + @@ -319,28 +320,28 @@ - + - + - + - + @@ -348,7 +349,7 @@ - + @@ -356,48 +357,48 @@ - + - + - + - - + + - - + + - - + + - + - + - + @@ -405,87 +406,87 @@ - + - + - + - + - + - - + + - - + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + From f7716f01111cec5d27a1baf290534c9863f814fb Mon Sep 17 00:00:00 2001 From: neo85 Date: Fri, 23 Apr 2021 18:03:58 +0200 Subject: [PATCH 20/21] Minor code cleanup --- controllers/maincontroller.pas | 5 +---- model/config.pas | 1 + ui/mainform1.pas | 3 +-- ui/options.lfm | 1 - ui/options.pas | 6 ------ 5 files changed, 3 insertions(+), 13 deletions(-) diff --git a/controllers/maincontroller.pas b/controllers/maincontroller.pas index 8cfb528..3264f30 100644 --- a/controllers/maincontroller.pas +++ b/controllers/maincontroller.pas @@ -29,7 +29,6 @@ TMainController = class procedure ShowTrayMessage(Msg: string); procedure ShowDoneMessage(Msg: string); - //property CompactMode : Boolean read FCompactMode write SetCompactMode; // Do we need this??? property CompactMode : Boolean read FCompactMode; property Timer : TMyTimer read FTimer; end; @@ -178,9 +177,9 @@ procedure TMainController.UpdateTime(); end; end; -// When it comes to buttons and menus, there are only two states. procedure TMainController.UpdateButtonsAndMenus; begin + // When it comes to buttons and menus, there are only two states. with MainForm do begin if Timer.State = TState.Running then @@ -229,8 +228,6 @@ procedure TMainController.ResetCountdown(Sender: TObject); if ((Sender = MainForm.Count) or (Sender = CompactModeForm.Count)) and (GetConfig.DblClickTime = False) then Exit; - // TODO compact mode - Timer.Reset; end; diff --git a/model/config.pas b/model/config.pas index 2ba9c62..1c8733d 100644 --- a/model/config.pas +++ b/model/config.pas @@ -320,6 +320,7 @@ destructor TConfig.Free; begin FFont.Free; FDefaultFont.Free; + FCompactMode.Free; end; function ReadInt(InIfile: TIniFile; const Section, Ident: String; Min, Max, Default: Integer) : Integer; diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 65f11ef..6452c70 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -123,7 +123,6 @@ procedure TMainForm.OnCreateForm(Sender: TObject); if Config.Load = False then begin - // When will this code execute? MessageDlg(MSG_OPEN_INI, mtError, [mbOK], 0); Exit; end; @@ -198,7 +197,7 @@ procedure TMainForm.TrayIconMainClick(Sender: TObject); begin if WindowState = wsNormal then begin - // Windows: setting wsMinimized does not minimize to taskbar. + // Windows: setting WindowState to wsMinimized does not minimize to taskbar. //WindowState:= wsMinimized; Application.Minimize; end diff --git a/ui/options.lfm b/ui/options.lfm index b1784f0..cf99fa0 100644 --- a/ui/options.lfm +++ b/ui/options.lfm @@ -428,7 +428,6 @@ object OptionsForm: TOptionsForm Top = 3 Width = 47 MaxValue = 1000 - OnChange = UpdateFontSize ParentFont = False TabOrder = 0 end diff --git a/ui/options.pas b/ui/options.pas index e0d220d..1ab1c2b 100644 --- a/ui/options.pas +++ b/ui/options.pas @@ -81,7 +81,6 @@ TOptionsForm = class(TForm) procedure TestTrayMsg(Sender: TObject); procedure UpdateFontColor(Sender: TObject); procedure UpdateFontBgColor(Sender: TObject); - procedure UpdateFontSize(Sender: TObject); private public { public declarations } @@ -323,11 +322,6 @@ procedure TOptionsForm.EnableFields(Sender: TObject); end; -procedure TOptionsForm.UpdateFontSize(Sender: TObject); -begin - //FontPreview.Font.Size:= FontSize.Value; -end; - procedure TOptionsForm.UpdateFontColor(Sender: TObject); begin FontPreview.Font.Color:= FontColor.ButtonColor; From 3d2ceeaf59048264d729988fc4901b8a5f80dfe7 Mon Sep 17 00:00:00 2001 From: neo85 Date: Sat, 22 May 2021 15:27:45 +0200 Subject: [PATCH 21/21] CompactMode bugfix --- ui/mainform1.pas | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/mainform1.pas b/ui/mainform1.pas index 6452c70..8b66c03 100644 --- a/ui/mainform1.pas +++ b/ui/mainform1.pas @@ -195,6 +195,9 @@ procedure TMainForm.FormKeyPress(Sender: TObject; var Key: char); procedure TMainForm.TrayIconMainClick(Sender: TObject); begin + if GetMainController.CompactMode then + exit; // No hide or show + if WindowState = wsNormal then begin // Windows: setting WindowState to wsMinimized does not minimize to taskbar.