diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index 6eb6928..ea9ac7a 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -28,7 +28,7 @@ object MainForm: TMainForm Margins.Top = 8 Margins.Right = 8 Margins.Bottom = 8 - ActivePage = tsFSX + ActivePage = tsAbout Align = alClient TabOrder = 1 object tsFSX: TTabSheet @@ -438,6 +438,7 @@ object MainForm: TMainForm object tsAbout: TTabSheet Caption = 'About' ImageIndex = 1 + ExplicitLeft = 12 object lblVersionCaption: TLabel Left = 16 Top = 67 @@ -486,6 +487,19 @@ object MainForm: TMainForm Height = 13 Caption = 'E-mail:' end + object lblProxy: TLabel + Left = 36 + Top = 368 + Width = 246 + Height = 13 + Caption = 'This might not work if you'#39're behind a proxy, sorry!' + Font.Charset = DEFAULT_CHARSET + Font.Color = clGrayText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + end object lblWebsiteLink: TLinkLabel Left = 75 Top = 111 @@ -508,6 +522,23 @@ object MainForm: TMainForm TabOrder = 1 OnLinkClick = lblLinkLinkClick end + object cbCheckUpdates: TCheckBox + Left = 16 + Top = 344 + Width = 305 + Height = 17 + Caption = ' Automatically check for &updates' + TabOrder = 2 + end + object btnCheckUpdates: TButton + Left = 344 + Top = 340 + Width = 83 + Height = 25 + Caption = '&Check now' + TabOrder = 3 + OnClick = btnCheckUpdatesClick + end end end object pnlG940: TPanel @@ -900,7 +931,7 @@ object MainForm: TMainForm Width = 75 Height = 25 Anchors = [akTop, akRight] - Caption = 'Retry' + Caption = '&Retry' TabOrder = 0 Visible = False OnClick = btnRetryClick diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index 2e29851..e07ffc1 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -15,6 +15,7 @@ uses OtlComm, OtlEventMonitor, OtlTaskControl, + OtlTask, pngimage, X2UtPersistIntf, @@ -23,10 +24,15 @@ uses LEDStateProvider; +const + CM_ASKAUTOUPDATE = WM_APP + 1; + + MSG_UPDATE = 1; + MSG_NOUPDATE = 2; + type TComboBoxArray = array[0..7] of TComboBoxEx; - TMainForm = class(TForm) imgStateNotFound: TImage; lblG940Throttle: TLabel; @@ -79,6 +85,9 @@ type lblEmailLink: TLinkLabel; lblWebsite: TLabel; lblEmail: TLabel; + cbCheckUpdates: TCheckBox; + btnCheckUpdates: TButton; + lblProxy: TLabel; procedure FormCreate(Sender: TObject); procedure btnRetryClick(Sender: TObject); @@ -88,6 +97,7 @@ type procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FunctionComboBoxChange(Sender: TObject); procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); + procedure btnCheckUpdatesClick(Sender: TObject); private FEventMonitor: TOmniEventMonitor; FStateConsumerTask: IOmniTaskControl; @@ -100,8 +110,10 @@ type procedure ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray); procedure ReadFSXExtra(AReader: IX2PersistReader); + procedure ReadAutoUpdate(AReader: IX2PersistReader); procedure WriteFunctions(AWriter: IX2PersistWriter; AComboBoxes: TComboBoxArray); procedure WriteFSXExtra(AWriter: IX2PersistWriter); + procedure WriteAutoUpdate(AWriter: IX2PersistWriter); procedure LoadDefaultProfile; procedure SaveDefaultProfile; @@ -114,6 +126,9 @@ type procedure UpdateMapping; + procedure CheckForUpdatesThread(const ATask: IOmniTask); + procedure CheckForUpdates(AReportNoUpdates: Boolean); + procedure EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage); procedure EventMonitorTerminated(const task: IOmniTaskControl); @@ -122,6 +137,8 @@ type procedure HandleProviderKilled(ATask: IOmniTaskControl; AMessage: TOmniMessage); procedure HandleProviderKilledFSX(ATask: IOmniTaskControl; AMessage: TOmniMessage); + procedure CMAskAutoUpdate(var Msg: TMessage); message CM_ASKAUTOUPDATE; + property EventMonitor: TOmniEventMonitor read FEventMonitor; property StateConsumerTask: IOmniTaskControl read FStateConsumerTask; end; @@ -134,8 +151,9 @@ uses ShellAPI, SysUtils, + IdException, + IdHTTP, OtlCommon, - OtlTask, X2UtApp, X2UtPersistRegistry, @@ -154,8 +172,10 @@ const TEXT_STATE_NOTFOUND = 'Not found'; TEXT_STATE_FOUND = 'Connected'; - KEY_DEFAULTPROFILE = '\Software\X2Software\G940LEDControl\DefaultProfile\'; + KEY_SETTINGS = '\Software\X2Software\G940LEDControl\'; + SECTION_DEFAULTPROFILE = 'DefaultProfile'; SECTION_FSX = 'FSX'; + SECTION_SETTINGS = 'Settings'; type @@ -221,6 +241,17 @@ begin end; +procedure TMainForm.CMAskAutoUpdate(var Msg: TMessage); +begin + if MessageBox(Self.Handle, 'I''m sorry to delay your flight, but I will only ask this once.'#13#10 + + 'Do you want to automatically check for updates?', 'Check for updates', MB_YESNO or MB_ICONQUESTION) = ID_YES then + begin + cbCheckUpdates.Checked := True; + CheckForUpdates(False); + end; +end; + + procedure TMainForm.SetDeviceState(const AMessage: string; AFound: Boolean); begin lblG940ThrottleState.Caption := AMessage; @@ -326,6 +357,32 @@ begin end; +procedure TMainForm.ReadAutoUpdate(AReader: IX2PersistReader); +var + checkUpdates: Boolean; + askAutoUpdate: Boolean; + +begin + askAutoUpdate := True; + + if AReader.BeginSection(SECTION_SETTINGS) then + try + if AReader.ReadBoolean('CheckUpdates', checkUpdates) then + begin + cbCheckUpdates.Checked := checkUpdates; + askAutoUpdate := False; + end; + finally + AReader.EndSection; + end; + + if askAutoUpdate then + PostMessage(Self.Handle, CM_ASKAUTOUPDATE, 0, 0) + else if cbCheckUpdates.Checked then + CheckForUpdates(False); +end; + + procedure TMainForm.WriteFunctions(AWriter: IX2PersistWriter; AComboBoxes: TComboBoxArray); var comboBox: TComboBoxEx; @@ -362,6 +419,17 @@ begin end; +procedure TMainForm.WriteAutoUpdate(AWriter: IX2PersistWriter); +begin + if AWriter.BeginSection(SECTION_SETTINGS) then + try + AWriter.WriteBoolean('CheckUpdates', cbCheckUpdates.Checked); + finally + AWriter.EndSection; + end; +end; + + procedure TMainForm.LoadDefaultProfile; var registryReader: TX2UtPersistRegistry; @@ -371,12 +439,19 @@ begin registryReader := TX2UtPersistRegistry.Create; try registryReader.RootKey := HKEY_CURRENT_USER; - registryReader.Key := KEY_DEFAULTPROFILE; + registryReader.Key := KEY_SETTINGS; reader := registryReader.CreateReader; - ReadFunctions(reader, FFSXComboBoxes); - ReadFSXExtra(reader); + if reader.BeginSection(SECTION_DEFAULTPROFILE) then + try + ReadFunctions(reader, FFSXComboBoxes); + ReadFSXExtra(reader); + finally + reader.EndSection; + end; + + ReadAutoUpdate(reader); finally FreeAndNil(registryReader); end; @@ -392,11 +467,18 @@ begin registryWriter := TX2UtPersistRegistry.Create; try registryWriter.RootKey := HKEY_CURRENT_USER; - registryWriter.Key := KEY_DEFAULTPROFILE; + registryWriter.Key := KEY_SETTINGS; writer := registryWriter.CreateWriter; - WriteFunctions(writer, FFSXComboBoxes); - WriteFSXExtra(writer); + if writer.BeginSection(SECTION_DEFAULTPROFILE) then + try + WriteFunctions(writer, FFSXComboBoxes); + WriteFSXExtra(writer); + finally + writer.EndSection; + end; + + WriteAutoUpdate(writer); finally FreeAndNil(registryWriter); end; @@ -426,12 +508,123 @@ begin end; +function FetchNextNumber(var AValue: string; out ANumber: Integer): Boolean; +var + dotPos: Integer; + number: string; + +begin + ANumber := 0; + + dotPos := AnsiPos('.', AValue); + if dotPos > 0 then + begin + number := Copy(AValue, 1, Pred(dotPos)); + Delete(AValue, 1, dotPos); + end + else + begin + number := AValue; + AValue := ''; + end; + + Result := TryStrToInt(number, ANumber); +end; + + +function VersionIsNewer(const AVersion1, AVersion2: string): Boolean; +var + version1: string; + version2: string; + number1: Integer; + number2: Integer; + +begin + if Length(AVersion1) = 0 then + begin + Result := True; + Exit; + end; + + Result := False; + version1 := AVersion1; + version2 := AVersion2; + + while (not Result) and + FetchNextNumber(version1, number1) and + FetchNextNumber(version2, number2) do + begin + if number2 > number1 then + Result := True + else if number2 < number1 then + Break; + end; +end; + + +procedure TMainForm.CheckForUpdatesThread(const ATask: IOmniTask); +var + httpClient: TIdHTTP; + msgSent: Boolean; + latestVersion: string; + +begin + msgSent := False; + try + httpClient := TIdHTTP.Create(nil); + try + latestVersion := httpClient.Get('http://g940.x2software.net/version'); + if VersionIsNewer(Format('%d.%d.%d', [App.Version.Major, App.Version.Minor, App.Version.Release]), latestVersion) then + ATask.Comm.Send(MSG_UPDATE) + else + begin + if ATask.Param.ByName('ReportNoUpdates').AsBoolean then + ATask.Comm.Send(MSG_NOUPDATE, True); + end; + + msgSent := True; + finally + FreeAndNil(httpClient); + end; + except + on E:EIdSilentException do; + on E:Exception do + begin + if not msgSent then + ATask.Comm.Send(MSG_NOUPDATE, False); + end; + end; +end; + + +procedure TMainForm.CheckForUpdates(AReportNoUpdates: Boolean); +begin + btnCheckUpdates.Enabled := False; + + CreateTask(CheckForUpdatesThread, 'CheckForUpdatesThread') + .MonitorWith(EventMonitor) + .SetParameter('ReportNoUpdates', AReportNoUpdates) + .Run; +end; + + procedure TMainForm.EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage); begin case msg.MsgID of MSG_NOTIFY_DEVICESTATE: HandleDeviceStateMessage(task, msg); MSG_RUN_IN_MAINTHREAD: HandleRunInMainThreadMessage(task, msg); MSG_PROVIDER_KILLED: HandleProviderKilled(task, msg); + + MSG_UPDATE: + if MessageBox(Self.Handle, 'An update is available on the G940 LED Control website.'#13#10'Do you want to go there now?', + 'Update available', MB_YESNO or MB_ICONINFORMATION) = ID_YES then + ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/#download'), nil, nil, SW_SHOWNORMAL); + + MSG_NOUPDATE: + if msg.MsgData.AsBoolean then + MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION) + else + MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING); end; end; @@ -442,7 +635,8 @@ begin begin FStateConsumerTask := nil; Close; - end; + end else if task.Name = 'CheckForUpdatesThread' then + btnCheckUpdates.Enabled := True; end; @@ -495,6 +689,11 @@ begin end; +procedure TMainForm.btnCheckUpdatesClick(Sender: TObject); +begin + CheckForUpdates(True); +end; + procedure TMainForm.btnFSXConnectClick(Sender: TObject); begin SaveDefaultProfile; diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 2e0394d..0510214 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -3,14 +3,14 @@ program G940LEDControl; uses Forms, MainFrm in 'Forms\MainFrm.pas' {MainForm}, - FSXLEDStateProvider in 'Units\FSXLEDStateProvider.pas', - LEDStateConsumer in 'Units\LEDStateConsumer.pas', - LEDStateProvider in 'Units\LEDStateProvider.pas', - LEDFunctionMap in 'Units\LEDFunctionMap.pas', - G940LEDStateConsumer in 'Units\G940LEDStateConsumer.pas', LogiJoystickDLL in '..\Shared\LogiJoystickDLL.pas', SimConnect in '..\Shared\SimConnect.pas', - ButtonSelectFrm in 'Forms\ButtonSelectFrm.pas' {ButtonSelectForm}; + ButtonSelectFrm in 'Forms\ButtonSelectFrm.pas' {ButtonSelectForm}, + FSXLEDStateProvider in 'Units\FSXLEDStateProvider.pas', + G940LEDStateConsumer in 'Units\G940LEDStateConsumer.pas', + LEDFunctionMap in 'Units\LEDFunctionMap.pas', + LEDStateConsumer in 'Units\LEDStateConsumer.pas', + LEDStateProvider in 'Units\LEDStateProvider.pas'; {$R *.res} diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 8b9dff9..4edf5e7 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -163,17 +163,17 @@
MainForm
- - - - -
ButtonSelectForm
dfm
+ + + + + Cfg_2 Base diff --git a/G940LEDControl/Units/LEDStateConsumer.pas b/G940LEDControl/Units/LEDStateConsumer.pas index 44d5d9b..e0aac12 100644 --- a/G940LEDControl/Units/LEDStateConsumer.pas +++ b/G940LEDControl/Units/LEDStateConsumer.pas @@ -11,19 +11,19 @@ uses const - MSG_CLEAR_FUNCTIONS = 1; - MSG_SET_FUNCTION = 2; - MSG_INITIALIZE_PROVIDER = 3; - MSG_FINALIZE_PROVIDER = 4; - MSG_PROCESS_MESSAGES = 5; - MSG_FINALIZE = 6; + MSG_CLEAR_FUNCTIONS = 1001; + MSG_SET_FUNCTION = 1002; + MSG_INITIALIZE_PROVIDER = 1003; + MSG_FINALIZE_PROVIDER = 1004; + MSG_PROCESS_MESSAGES = 1005; + MSG_FINALIZE = 1006; - MSG_PROVIDER_KILLED = 7; - MSG_RUN_IN_MAINTHREAD = 8; + MSG_PROVIDER_KILLED = 1007; + MSG_RUN_IN_MAINTHREAD = 1008; MSG_CONSUMER_OFFSET = MSG_RUN_IN_MAINTHREAD; - TIMER_PROCESSMESSAGES = 1; + TIMER_PROCESSMESSAGES = 1001; TIMER_CONSUMER_OFFSET = TIMER_PROCESSMESSAGES;