diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..89e42ea --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ['https://pag.ae/7ZhEY1xKr'] diff --git a/LGPD.md b/LGPD.md new file mode 100644 index 0000000..44686c9 --- /dev/null +++ b/LGPD.md @@ -0,0 +1,26 @@ +# LGPD Compliance + +The LGPD, or General Law for the Protection of Personal Data (Law No. 13.709/2018), is the Brazilian legislation that regulates the use, collection, storage and sharing of personal data of individuals in Brazil. + +## Introduction +**Delphi AI Developer** was developed for use in Delphi IDE in compliance with the guidelines of the General Law on the Protection of Personal Data (LGPD), ensuring that user data is not used by us. + +## Data Collection and Use +**Delphi AI Developer** uses Artificial Intelligence APIs to enhance the programming experience and facilitate interaction with the database structure. When operating, the plugin: +- **Sends code snippets and database structure to Artificial Intelligence APIs**, aiming to provide suggestions and automation in development. +- **Does not store any user information on external servers**, including code snippets, database structure or other personal data. + +## Database Structure Storage +The plugin **does not access, store or send data from the database** itself. Only the **database structure** is used, including the database name, table names, field names, field data types and field sizes. This structure is **stored locally in a JSON file** on the user's own computer, without being exposed to any server or external source. This local storage aims to optimize the plugin's performance and interaction with the APIs, while maintaining data security and privacy. + +## Data Subject Rights +Although Delphi AI Developer does not collect or store personal data, users can request more information about the functioning and data processing at any time. For questions related to the use of data by the tool, please contact **[contato@code4delphi.com.br](mailto:contato@code4delphi.com.br)**. + +## Segurança e Privacidade +O Delphi AI Developer adota medidas de segurança rigorosas para proteger as interações entre o plugin e as APIs de Inteligência Artificial, além de garantir que o arquivo JSON local seja seguro e acessível apenas ao usuário. Todo o processamento de dados é feito com transparência e alinhado aos princípios da LGPD. + +## Privacy Policy Updates +Any changes in LGPD compliance or data usage practices will be added to this page. + +## Contact +For questions or requests regarding the use of data by **Delphi AI Developer**, please contact **[contato@code4delphi.com.br](mailto:contato@code4delphi.com.br)**. diff --git a/Package/DelphiAIDeveloper.dpk b/Package/DelphiAIDeveloper.dpk index 8213a74..28b6eda 100644 --- a/Package/DelphiAIDeveloper.dpk +++ b/Package/DelphiAIDeveloper.dpk @@ -32,11 +32,16 @@ package DelphiAIDeveloper; requires rtl, designide, - RESTComponents; + RESTComponents, + vclFireDAC, + FireDAC, + FireDACCommonDriver, + FireDACCommon, + FireDACMySQLDriver, + FireDACIBDriver; contains DelphiAIDev.Register in '..\Src\DelphiAIDev.Register.pas', - DelphiAIDev.Chat.View in '..\Src\Chat\DelphiAIDev.Chat.View.pas' {DelphiAIDevChatView}, DelphiAIDev.Utils.OTA in '..\Src\Utils\DelphiAIDev.Utils.OTA.pas', DelphiAIDev.Utils in '..\Src\Utils\DelphiAIDev.Utils.pas', DelphiAIDev.Types in '..\Src\Types\DelphiAIDev.Types.pas', @@ -52,7 +57,9 @@ contains DelphiAIDev.Test in '..\Src\Test\DelphiAIDev.Test.pas', DelphiAIDev.Settings.View in '..\Src\Settings\DelphiAIDev.Settings.View.pas' {DelphiAIDevSettingsView}, DelphiAIDev.Settings in '..\Src\Settings\DelphiAIDev.Settings.pas', - DelphiAIDev.Chat in '..\Src\Chat\DelphiAIDev.Chat.pas', + DelphiAIDev.AI.Facade in '..\Src\AI\DelphiAIDev.AI.Facade.pas', + DelphiAIDev.Chat.View in '..\Src\Chat\DelphiAIDev.Chat.View.pas' {DelphiAIDevChatView}, + DelphiAIDev.Chat.ProcessResponse in '..\Src\Chat\DelphiAIDev.Chat.ProcessResponse.pas', DelphiAIDev.AI.Gemini in '..\Src\AI\DelphiAIDev.AI.Gemini.pas', DelphiAIDev.AI.ChatGPT in '..\Src\AI\DelphiAIDev.AI.ChatGPT.pas', DelphiAIDev.AI.Interfaces in '..\Src\AI\DelphiAIDev.AI.Interfaces.pas', @@ -61,19 +68,53 @@ contains DelphiAIDev.ModuleCreator in '..\Src\ModuleCreator\DelphiAIDev.ModuleCreator.pas', DelphiAIDev.ModuleCreator.Interfaces in '..\Src\ModuleCreator\DelphiAIDev.ModuleCreator.Interfaces.pas', DelphiAIDev.IDE.ImageListMain in '..\Src\IDE\ImageListMain\DelphiAIDev.IDE.ImageListMain.pas', - DelphiAIDev.IDE.Shortcuts in '..\Src\IDE\Shortcuts\DelphiAIDev.IDE.Shortcuts.pas', + DelphiAIDev.KeyboardBinding in '..\Src\KeyboardBinding\DelphiAIDev.KeyboardBinding.pas', DelphiAIDev.Utils.CnWizard in '..\Src\Utils\DelphiAIDev.Utils.CnWizard.pas', - DelphiAIDev.DefaultsQuestions.View in '..\Src\DefaultsQuestions\DelphiAIDev.DefaultsQuestions.View.pas' {DelphiAIDevDefaultsQuestionsView}, + DelphiAIDev.DB.Registers.View in '..\Src\DB\Registers\DelphiAIDev.DB.Registers.View.pas' {DelphiAIDevDBRegistersView}, DelphiAIDev.Utils.ListView in '..\Src\Utils\DelphiAIDev.Utils.ListView.pas', DelphiAIDev.Utils.GetIniPositionStr in '..\Src\Utils\DelphiAIDev.Utils.GetIniPositionStr.pas', DelphiAIDev.DefaultsQuestions.Fields in '..\Src\DefaultsQuestions\DelphiAIDev.DefaultsQuestions.Fields.pas', DelphiAIDev.DefaultsQuestions.Interfaces in '..\Src\DefaultsQuestions\DelphiAIDev.DefaultsQuestions.Interfaces.pas', DelphiAIDev.DefaultsQuestions.Model in '..\Src\DefaultsQuestions\DelphiAIDev.DefaultsQuestions.Model.pas', - DelphiAIDev.DefaultsQuestions.AddEdit.View in '..\Src\DefaultsQuestions\DelphiAIDev.DefaultsQuestions.AddEdit.View.pas' {DelphiAIDevDefaultsQuestionsAddEditView}, + DelphiAIDev.DB.Registers.AddEdit.View in '..\Src\DB\Registers\DelphiAIDev.DB.Registers.AddEdit.View.pas' {DelphiAIDevDBRegistersAddEditView}, DelphiAIDev.DefaultsQuestions.PopupMenu in '..\Src\DefaultsQuestions\DelphiAIDev.DefaultsQuestions.PopupMenu.pas', DelphiAIDev.Utils.ABMenuAction in '..\Src\Utils\DelphiAIDev.Utils.ABMenuAction.pas', DelphiAIDev.IDE.Splash in '..\Src\IDE\Splash\DelphiAIDev.IDE.Splash.pas', - DelphiAIDev.Chat.ProcessResponse in '..\Src\Chat\DelphiAIDev.Chat.ProcessResponse.pas', - DelphiAIDev.AI.Groq in '..\Src\AI\DelphiAIDev.AI.Groq.pas'; + DelphiAIDev.AI.Groq in '..\Src\AI\DelphiAIDev.AI.Groq.pas', + DelphiAIDev.PopupMenuProjects in '..\Src\PopupMenuProjects\DelphiAIDev.PopupMenuProjects.pas', + DelphiAIDev.PopupMenuProjects.Item in '..\Src\PopupMenuProjects\DelphiAIDev.PopupMenuProjects.Item.pas', + DelphiAIDev.DefaultsQuestions.View in '..\Src\DefaultsQuestions\DelphiAIDev.DefaultsQuestions.View.pas' {DelphiAIDevDefaultsQuestionsView}, + DelphiAIDev.DB.Registers.Model in '..\Src\DB\Registers\DelphiAIDev.DB.Registers.Model.pas', + DelphiAIDev.DB.Registers.Interfaces in '..\Src\DB\Registers\DelphiAIDev.DB.Registers.Interfaces.pas', + DelphiAIDev.DB.Registers.Fields in '..\Src\DB\Registers\DelphiAIDev.DB.Registers.Fields.pas', + DelphiAIDev.DefaultsQuestions.AddEdit.View in '..\Src\DefaultsQuestions\DelphiAIDev.DefaultsQuestions.AddEdit.View.pas' {DelphiAIDevDefaultsQuestionsAddEditView}, + DelphiAIDev.Utils.Crypt in '..\Src\Utils\DelphiAIDev.Utils.Crypt.pas', + C4D.Conn.Types in '..\Src\Conn\C4D.Conn.Types.pas', + C4D.Conn.Configs in '..\Src\Conn\C4D.Conn.Configs.pas', + C4D.Conn.Firedac in '..\Src\Conn\C4D.Conn.Firedac.pas', + C4D.Conn.Firedac.Query in '..\Src\Conn\C4D.Conn.Firedac.Query.pas', + C4D.Conn.Interfaces in '..\Src\Conn\C4D.Conn.Interfaces.pas', + C4D.Conn in '..\Src\Conn\C4D.Conn.pas', + C4D.Conn.Utils in '..\Src\Conn\C4D.Conn.Utils.pas', + DelphiAIDev.DB.Chat.View in '..\Src\DB\Chat\DelphiAIDev.DB.Chat.View.pas' {DelphiAIDevDBChatView}, + DelphiAIDev.MetaInfo in '..\Src\MetaInfo\DelphiAIDev.MetaInfo.pas', + DelphiAIDev.DB.References.View in '..\Src\DB\References\DelphiAIDev.DB.References.View.pas' {DelphiAIDevDBReferencesView}, + DelphiAIDev.Utils.DBGrids in '..\Src\Utils\DelphiAIDev.Utils.DBGrids.pas', + DelphiAIDev.AI.Ollama in '..\Src\AI\DelphiAIDev.AI.Ollama.pas', + DelphiAIDev.Projects.AddEdit.View in '..\Src\Projects\DelphiAIDev.Projects.AddEdit.View.pas' {DelphiAIDevProjectsAddEditView}, + DelphiAIDev.DB.Utils in '..\Src\DB\Utils\DelphiAIDev.DB.Utils.pas', + DelphiAIDev.PopupMenuProjects.OnExecute in '..\Src\PopupMenuProjects\DelphiAIDev.PopupMenuProjects.OnExecute.pas', + DelphiAIDev.Projects.Fields in '..\Src\Projects\DelphiAIDev.Projects.Fields.pas', + DelphiAIDev.Projects.Model in '..\Src\Projects\DelphiAIDev.Projects.Model.pas', + DelphiAIDev.Projects.Interfaces in '..\Src\Projects\DelphiAIDev.Projects.Interfaces.pas', + DelphiAIDev.CodeCompletion.Vars in '..\Src\CodeCompletion\DelphiAIDev.CodeCompletion.Vars.pas', + DelphiAIDev.IDE.OTAIDENotifier in '..\Src\IDE\OTAIDENotifier\DelphiAIDev.IDE.OTAIDENotifier.pas', + DelphiAIDev.IDE.OTAEditorNotifier in '..\Src\IDE\OTAEditorNotifier\DelphiAIDev.IDE.OTAEditorNotifier.pas', + DelphiAIDev.IDE.NTAEditViewNotifier in '..\Src\IDE\NTAEditViewNotifier\DelphiAIDev.IDE.NTAEditViewNotifier.pas', + DelphiAIDev.CodeCompletion.Search in '..\Src\CodeCompletion\DelphiAIDev.CodeCompletion.Search.pas', + DelphiAIDev.Test.Client in '..\Src\Test\DelphiAIDev.Test.Client.pas', + DelphiAIDev.CodeCompletion.KeyTab in '..\Src\CodeCompletion\DelphiAIDev.CodeCompletion.KeyTab.pas', + DelphiAIDev.AI.Response in '..\Src\AI\DelphiAIDev.AI.Response.pas', + DelphiAIDev.AI.Mistral in '..\Src\AI\DelphiAIDev.AI.Mistral.pas'; end. diff --git a/Package/DelphiAIDeveloper.dproj b/Package/DelphiAIDeveloper.dproj index ae30900..cd6848e 100644 --- a/Package/DelphiAIDeveloper.dproj +++ b/Package/DelphiAIDeveloper.dproj @@ -2,13 +2,14 @@ {7EF46619-47CC-4A26-9369-BCDD8C5ECFA8} DelphiAIDeveloper.dpk - 18.8 + 20.2 VCL True Debug Win32 - 1 + 1048577 Package + DelphiAIDeveloper true @@ -38,6 +39,11 @@ Base true + + true + Base + true + true Base @@ -67,33 +73,42 @@ System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) All DelphiAIDeveloper - modules\.dcp;modules\.dcu;modules;modules\restrequest4delphi\src;$(DCC_UnitSearchPath) + modules\.dcp;modules\.dcu;modules;modules\bcrypt\src;modules\restrequest4delphi\src;$(DCC_UnitSearchPath) 1046 CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= None android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar - rtl;bindengine;bindcomp;RESTComponents;$(DCC_UsePackage) + rtl;bindengine;bindcomp;RESTComponents;FireDAC;FireDACCommonDriver;FireDACCommon;FireDACIBDriver;$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png None android-support-v4.dex.jar;cloud-messaging.dex.jar;com-google-android-gms.play-services-ads-base.17.2.0.dex.jar;com-google-android-gms.play-services-ads-identifier.16.0.0.dex.jar;com-google-android-gms.play-services-ads-lite.17.2.0.dex.jar;com-google-android-gms.play-services-ads.17.2.0.dex.jar;com-google-android-gms.play-services-analytics-impl.16.0.8.dex.jar;com-google-android-gms.play-services-analytics.16.0.8.dex.jar;com-google-android-gms.play-services-base.16.0.1.dex.jar;com-google-android-gms.play-services-basement.16.2.0.dex.jar;com-google-android-gms.play-services-gass.17.2.0.dex.jar;com-google-android-gms.play-services-identity.16.0.0.dex.jar;com-google-android-gms.play-services-maps.16.1.0.dex.jar;com-google-android-gms.play-services-measurement-base.16.4.0.dex.jar;com-google-android-gms.play-services-measurement-sdk-api.16.4.0.dex.jar;com-google-android-gms.play-services-stats.16.0.1.dex.jar;com-google-android-gms.play-services-tagmanager-v4-impl.16.0.8.dex.jar;com-google-android-gms.play-services-tasks.16.0.1.dex.jar;com-google-android-gms.play-services-wallet.16.0.1.dex.jar;com-google-firebase.firebase-analytics.16.4.0.dex.jar;com-google-firebase.firebase-common.16.1.0.dex.jar;com-google-firebase.firebase-iid-interop.16.0.1.dex.jar;com-google-firebase.firebase-iid.17.1.1.dex.jar;com-google-firebase.firebase-measurement-connector.17.0.1.dex.jar;com-google-firebase.firebase-messaging.17.5.0.dex.jar;fmx.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar - rtl;bindengine;bindcomp;RESTComponents;$(DCC_UsePackage) + rtl;bindengine;bindcomp;RESTComponents;FireDAC;FireDACCommonDriver;FireDACCommon;FireDACIBDriver;$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png - rtl;bindengine;bindcomp;RESTComponents;$(DCC_UsePackage) + rtl;bindengine;bindcomp;RESTComponents;FireDAC;FireDACCommonDriver;FireDACCommon;FireDACIBDriver;FireDACMySQLDriver;$(DCC_UsePackage) Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) Debug true 1033 - rtl;bindengine;bindcomp;RESTComponents;$(DCC_UsePackage) + rtl;bindengine;bindcomp;RESTComponents;vclFireDAC;FireDAC;FireDACCommonDriver;FireDACCommon;FireDACIBDriver;FireDACMySQLDriver;$(DCC_UsePackage) .\dcu\ - rtl;bindengine;bindcomp;RESTComponents;$(DCC_UsePackage) + rtl;bindengine;bindcomp;RESTComponents;vclFireDAC;FireDAC;FireDACCommonDriver;FireDACCommon;FireDACIBDriver;FireDACMySQLDriver;$(DCC_UsePackage) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + 1033 + true DEBUG;$(DCC_Define) @@ -122,10 +137,13 @@ + + + + + + - -
DelphiAIDevChatView
-
@@ -149,10 +167,13 @@
DelphiAIDevSettingsView
- dfm
- + + +
DelphiAIDevChatView
+
+ @@ -161,41 +182,85 @@ - + - -
DelphiAIDevDefaultsQuestionsView
- dfm + +
DelphiAIDevDBRegistersView
- -
DelphiAIDevDefaultsQuestionsAddEditView
- dfm + +
DelphiAIDevDBRegistersAddEditView
- - - BITMAP - c4d_gear - + + + +
DelphiAIDevDefaultsQuestionsView
+
+ + + + +
DelphiAIDevDefaultsQuestionsAddEditView
+
+ + + + + + + + + +
DelphiAIDevDBChatView
+
+ + +
DelphiAIDevDBReferencesView
+ dfm +
+ + + +
DelphiAIDevProjectsAddEditView
+ dfm +
+ + + + + + + + + + + + + + BITMAP c4d_Logo - + BITMAP - c4d_logo_24x24 + c4d_database + + + BITMAP + c4d_database_add - + BITMAP - c4d_logo_48x48 + c4d_gear BITMAP @@ -205,10 +270,14 @@ BITMAP c4d_question - - Cfg_2 - Base - + + BITMAP + c4d_logo_24x24 + + + BITMAP + c4d_database_execute + Base @@ -216,6 +285,10 @@ Cfg_1 Base + + Cfg_2 + Base + Delphi.Personality.12 @@ -233,28 +306,15 @@ Microsoft Office XP Sample Automation Server Wrapper Components - - - - true - - - - - true - - - - - true - - - - - DelphiAIDeveloper.bpl - true - - + + + + + + + + + 1 @@ -263,16 +323,6 @@ 0 - - - classes - 1 - - - classes - 1 - - res\xml @@ -283,12 +333,6 @@ 1 - - - library\lib\armeabi-v7a - 1 - - library\lib\armeabi @@ -341,6 +385,16 @@ 1 + + + res\drawable-anydpi-v21 + 1 + + + res\drawable-anydpi-v21 + 1 + + res\values @@ -361,6 +415,66 @@ 1 + + + res\values-v31 + 1 + + + res\values-v31 + 1 + + + + + res\drawable-anydpi-v26 + 1 + + + res\drawable-anydpi-v26 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-anydpi-v33 + 1 + + + res\drawable-anydpi-v33 + 1 + + res\values @@ -371,6 +485,16 @@ 1 + + + res\values-night-v21 + 1 + + + res\values-night-v21 + 1 + + res\drawable @@ -391,6 +515,16 @@ 1 + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + res\drawable-ldpi @@ -531,6 +665,56 @@ 1 + + + res\drawable-anydpi-v24 + 1 + + + res\drawable-anydpi-v24 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-night-anydpi-v21 + 1 + + + res\drawable-night-anydpi-v21 + 1 + + + + + res\drawable-anydpi-v31 + 1 + + + res\drawable-anydpi-v31 + 1 + + + + + res\drawable-night-anydpi-v31 + 1 + + + res\drawable-night-anydpi-v31 + 1 + + 1 @@ -551,6 +735,10 @@ 1 .framework + + 1 + .framework + 0 @@ -564,6 +752,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -578,7 +770,7 @@ 1 .dylib - + 1 .dylib @@ -590,6 +782,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -608,7 +804,7 @@ 0 - + 0 @@ -617,394 +813,350 @@ 0 + + 0 + 0 - - - 1 - - + + 1 - + 1 - - + + + + + + Contents\Resources 1 - + + Contents\Resources 1 - + + Contents\Resources 1 - - - 1 - - + + + library\lib\armeabi-v7a 1 - + + library\lib\arm64-v8a 1 - - 1 1 - + 1 - - - - 1 - - + 1 - + 1 - - - + 1 - + 1 - - 1 + + 0 - - - 1 - - - 1 - - + + + library\lib\armeabi-v7a 1 - - + + 1 - + 1 - + 1 - + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 1 + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 1 - + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 1 - + + + + 1 1 - + 1 - - - 1 - - + + + Assets 1 - + + Assets 1 - - - 1 - - + + + Assets 1 - + + Assets 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - + - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - - - - + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - - - - Contents\Resources + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - Contents\Resources + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - library\lib\armeabi-v7a - 1 - - - library\lib\arm64-v8a - 1 - - - 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - 1 - - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - 1 - - - 1 - - - 0 - - - - library\lib\armeabi-v7a - 1 - - - - + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - Assets + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - Assets + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - Assets + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - Assets + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - + + + + + - - - - + + + + False @@ -1012,6 +1164,7 @@ False True False + True 12 diff --git a/Package/DelphiAIDeveloper.res b/Package/DelphiAIDeveloper.res index 9273b09..4a37369 100644 Binary files a/Package/DelphiAIDeveloper.res and b/Package/DelphiAIDeveloper.res differ diff --git a/Package/Img/c4d_database.bmp b/Package/Img/c4d_database.bmp new file mode 100644 index 0000000..ba3f71d Binary files /dev/null and b/Package/Img/c4d_database.bmp differ diff --git a/Package/Img/c4d_database_add.bmp b/Package/Img/c4d_database_add.bmp new file mode 100644 index 0000000..59e69bf Binary files /dev/null and b/Package/Img/c4d_database_add.bmp differ diff --git a/Package/Img/c4d_database_execute.bmp b/Package/Img/c4d_database_execute.bmp new file mode 100644 index 0000000..80f0a5f Binary files /dev/null and b/Package/Img/c4d_database_execute.bmp differ diff --git a/README.md b/README.md index c98fb58..9b37e71 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,21 @@ # Delphi AI Developer (Copilot) -Inspired by GitHub Copilot, Delphi AI Developer is a plugin that adds Artificial intelligence (AI) interaction capabilities to the Delphi IDE, using both the OpenAI API, Gemini API and Groq API, as well as offering offline AI support. +Inspired by GitHub Copilot, Delphi AI Developer is a plugin that adds Artificial intelligence (AI) interaction capabilities to the Delphi IDE, using both the OpenAI API, Gemini API, Mistral API and Groq API, as well as offering offline AI support. With Delphi AI Developer, you will have assistance in generating and refactoring code, facilitating and accelerating development. Receive suggestions for creating and improving code directly in the IDE and take advantage of the possibility of creating predefined questions to speed up your searches. -# 📞 Contacts - -

- - - -   - - - -   - - - -   - - - -   - - - -

- -## ⚙️ Installation +## 📞 Contacts + +[![Telegram](https://img.shields.io/badge/Telegram-Join-blue?logo=telegram)](https://t.me/Code4Delphi) +[![YouTube](https://img.shields.io/badge/YouTube-Join-red?logo=youtube&logoColor=red)](https://www.youtube.com/@code4delphi) +[![Instagram](https://img.shields.io/badge/Intagram-Follow-red?logo=instagram&logoColor=pink)](https://www.instagram.com/code4delphi/) +[![LinkedIn](https://img.shields.io/badge/LinkedIn-Connect-blue)](https://www.linkedin.com/in/cesar-cardoso-dev) +[![Blog](https://img.shields.io/badge/Blog-Code4Delphi-F00?logo=delphi)](https://code4delphi.com.br/blog/) +[![Course](https://img.shields.io/badge/Course-Delphi-F00?logo=delphi)](https://go.hotmart.com/U81331747Y?dp=1) +[![E-mail](https://img.shields.io/badge/E--mail-Send-yellowgreen?logo=maildotru&logoColor=yellowgreen)](mailto:contato@code4delphi.com.br) + +## 🚀 INSTALLATION 1 - Download Delphi AI Developer. You can download the .zip file or clone the project on your PC. ![image](https://github.com/Code4Delphi/Delphi-AI-Developer/assets/33873267/a32c9333-6d5a-4036-9891-b97778bca90a) @@ -51,25 +37,136 @@ Receive suggestions for creating and improving code directly in the IDE and take
-## ▶️ Demo Video -We have also created a video with details on how to download, install and use the plugin. The video is in Portuguese (ptBR), but we are providing subtitles and possibly a video in English. -* [https://www.youtube.com/watch?v=2NAlUFK2FGs](https://www.youtube.com/watch?v=2NAlUFK2FGs) +## ⚙️ PLUGIN SETTINGS +Access the menu “AI Developer” > “Settings” > Tab “Preferences” +![image](https://github.com/user-attachments/assets/c89d1145-4178-45ec-bd45-ecb13dfc37cc) + +1. **Language used in questions:** Indicate in which language you will ask questions in chats, so that the prompts generated by the Plugin are generated in the same language. +2. **AI default (Chat and Databases Chat):** Default AI when starting the IDE. +3. **Color to highlight Delphi/Pascal code:** Color to highlight Delphi/Pascal/SQL code in responses displayed on chat screens +4. **Default Prompts:** Prompts added in this field will be sent to the AIs along with the requests. This can significantly improve the quality of the responses. (Example prompt: Always return SQL commands in lowercase letters) + + +
+ +## ⚪ CONFIGURING AI ONLINE +You can choose between 3 APIs, Gemini (Google), ChatGPT (OpenAI) and Groq. Gemini and Groq APIs are free. + +Access the menu “AI Developer” > “Settings” > Tab “AI on-line” +![image](https://github.com/user-attachments/assets/2c3a45a2-94c8-4449-8c71-58246f6ca67f) + +1. Inform the desired model. +2. Click on the **"Generate API Key"** link to generate your key. +3. In this field you must enter the API access key. + +
+ +## 🟠 CODE COMPLETION + +To configure, access the menu “AI Developer” > “Settings” > Tab “Code Completion” + +![image](https://github.com/user-attachments/assets/fd26a49c-5969-4bbe-8a2e-32e9623bf1d7) + +1. Enables/disables use of Code Completion +2. **AI default:** Default AI that will be used by Code Completion +3. **Suggestion Code Color**: Color code suggested by plugin before being accepted +4. Shortcut to invoke Code Completion usage (requires restart of Delphi IDE) +5. **Default Prompts:** Prompts added in this field will be sent to the AIs along with the requests. This can significantly improve the quality of the responses. (Example prompt: Always return SQL commands in lowercase letters) + +- To use it, simply use the configured shortcut keys (default Alt+Enter) +- To **accept** the suggestion, simply use the Tab key. +![delphi-ai-developer-previa-02](https://github.com/user-attachments/assets/f09fe06a-4471-43e7-b99e-1ac701dad211) + + +
+ +## 🔵 AI CHAT INTERACTING WITH DELPHI IDE +Access the menu “AI Developer” > “Chat” or Ctrl+Shift+Alt+A +![image](https://github.com/user-attachments/assets/a8e58367-36c3-481f-8583-98d19cee68af) + +1. Select the desired AI to be used in the chat +2. Field where the question/prompt should be added +3. Field where the AI ​​response will be displayed +4. Access the menu with pre-registered questions (to register, access the menu: “AI Developer” > “Defaults Questions”) +5. By checking this option, the AI ​​will only return codes, without inserting comments or explanations. +6. By checking the "Use current unit code in query" option, the source code of the current unit will be used as a reference for the prompt sent to the AIs. +Note: If the current unit has any code selected, only the selected code snippet will be used as a reference, otherwise the entire unit code will be used. +7. Button that makes the request to the AIs +8. **Insert Selected Text at Cursor**: Inserts the selected text into the response, field in the IDE code editor (if there is no selection, use the entire response) +9. Create new unit with selected code (if there is no selection, use the entire response) +10. Copy Selected Text (if there is no selection, use the entire response) +11. Clean all and start a new chat +12. Opens a menu with additional options
-## 📄 Documentation under construction -**We will soon publish the complete documentation for the Plugin.** +## 🟣 CHAT FOR DATABASE INTERACTION +- To register Databases, access the menu “AI Developer” > “Databases Registers” +![image](https://github.com/user-attachments/assets/aacd2440-898a-4e7d-96fd-8dfa94c09c76) + +- **Generate reference with database** +- Note: This process must always be performed whenever a new field or table is added to the database. +![image](https://github.com/user-attachments/assets/8649509e-189b-46d2-bc0d-378ead1e8929) + +- Optional Step: Link Default Database to Project or Project Group +![image](https://github.com/user-attachments/assets/92812a6f-fdd6-45e0-b60c-b26669f166dc) + +- Chat for database + +![image](https://github.com/user-attachments/assets/f3aeb00e-cdcb-491d-b360-5c625c5c8f2f) + + +1. Select the desired database +2. Quick access to the reference generation screen for the selected database +3. Select the desired AI to be used in the chat +4. Field where the question/prompt should be added +5. Button that makes the request to the AIs +6. Field where the AI ​​response will be displayed +7. Button to execute the SQL command of the field with the response (field 6) +8. Grid with the response from the execution of the SQl command +9. Options for copying or exporting Grid data +10. Access the menu with pre-registered questions (to register, access the menu: “AI Developer” > “Defaults Questions”) +11. By checking this option, the AI ​​will only return SQL commands, without inserting comments or explanations. +12. By checking the "Use current unit code in query" option, the source code of the current unit will be used as a reference for the prompt sent to the AIs. +Note: If the current unit has any code selected, only the selected code snippet will be used as a reference, otherwise the entire unit code will be used. +13. **Insert Selected Text at Cursor**: Inserts the selected text into the response, field in the IDE code editor (if there is no selection, use the entire response) +14. Create new unit with selected code (if there is no selection, use the entire response) +15. Copy Selected Text (if there is no selection, use the entire response) +16. Clean all and start a new chat +17. Opens a menu with additional options + +
-# 💬 Contributions / Ideas / Bug Fixes -To submit a pull request, follow these steps: +## 🟤 AI OFF-LINE + +To use AI offline, follow these steps: +1. Install Ollama, which can be found at the following link: [https://ollama.com/download](https://ollama.com/download) +2. Choose the desired model. This can be done at the following link: [https://ollama.com/library](https://ollama.com/library) +![image](https://github.com/user-attachments/assets/1c259158-8118-421e-84ab-f6931b1438c0) +3. Open command prompt or terminal and run the command “ollama run ” and wait for the installation to finish +![image](https://github.com/user-attachments/assets/b5c12854-5ce4-4fe7-aa49-b0396f0d4040) +4. **To configure**, access the menu “AI Developer” > “Settings” > Tab “AI off-Line“ +![image](https://github.com/user-attachments/assets/f821ab05-743a-480f-a72b-f7ca3809ae6b) + + +
-1. Fork the project -2. Create a new branch (`git checkout -b minha-nova-funcionalidade`) -3. Make your changes -4. Make the commit (`git commit -am 'Functionality or adjustment message'`) -5. Push the branch (`git push origin Message about functionality or adjustment`) -6. Open a pull request +## ▶️ DEMO VIDEO +For more details about the plugin, watch our talk, voted one of the best of Embarcadero Conference 2024. The video is in Portuguese (pt-BR), but we are providing subtitles and possibly an English version. +* [https://www.youtube.com/live/7UcfPyqR2TY?si=bLw27HQP4diF7TCB&t=2822](https://www.youtube.com/live/7UcfPyqR2TY?si=bLw27HQP4diF7TCB&t=2822) + +
+ +## 💬 CONTRIBUTIONS / IDEAS / BUG FIXES +Any suggestions or help are welcome. Send us a pull request or open an [issue](/../../issues/). + +
+ +## 📄 LGPD Compliance +[Access information about LGPD](https://github.com/Code4Delphi/Delphi-AI-Developer/blob/master/LGPD.md) + +
+## ⚠️ LICENSE +`Delphi AI Developer` is free and open-source wizard licensed under the [MIT License](LICENSE). -## ⚠️ License -`Delphi AI Developer` is free and open-source wizard licensed under the [MIT License](https://github.com/Code4Delphi/Delphi-AI-Developer/blob/master/LICENSE). diff --git a/Src/AI/DelphiAIDev.AI.ChatGPT.pas b/Src/AI/DelphiAIDev.AI.ChatGPT.pas index 7d7e4a8..7409225 100644 --- a/Src/AI/DelphiAIDev.AI.ChatGPT.pas +++ b/Src/AI/DelphiAIDev.AI.ChatGPT.pas @@ -7,6 +7,7 @@ interface System.JSON, System.Classes, RESTRequest4D, + DelphiAIDev.Consts, DelphiAIDev.Utils, DelphiAIDev.Settings, DelphiAIDev.AI.Interfaces; @@ -15,11 +16,11 @@ interface TDelphiAIDevAIChatGPT = class(TInterfacedObject, IDelphiAIDevAI) private FSettings: TDelphiAIDevSettings; + FResponse: IDelphiAIDevAIResponse; protected - function GetResponse(const AQuestion: string): string; + function GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; public - class function New(const ASettings: TDelphiAIDevSettings): IDelphiAIDevAI; - constructor Create(const ASettings: TDelphiAIDevSettings); + constructor Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); end; implementation @@ -27,19 +28,14 @@ implementation const API_JSON_BODY_BASE = '{"model": "%s", "messages": [{"role": "user", "content": "%s"}], "stream": false, "max_tokens": 2048}'; -class function TDelphiAIDevAIChatGPT.New(const ASettings: TDelphiAIDevSettings): IDelphiAIDevAI; -begin - Result := Self.Create(ASettings); -end; - -constructor TDelphiAIDevAIChatGPT.Create(const ASettings: TDelphiAIDevSettings); +constructor TDelphiAIDevAIChatGPT.Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); begin FSettings := ASettings; + FResponse := AResponse; end; -function TDelphiAIDevAIChatGPT.GetResponse(const AQuestion: string): string; +function TDelphiAIDevAIChatGPT.GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; var - LQuestion: string; LResponse: IResponse; LJsonValueAll: TJSONValue; LJsonValueChoices: TJSONValue; @@ -48,30 +44,41 @@ function TDelphiAIDevAIChatGPT.GetResponse(const AQuestion: string): string; LJsonValueMessage: TJSONValue; LJsonObjMessage: TJSONObject; LItemChoices: Integer; + LResult: string; begin - Result := ''; - LQuestion := TUtils.AdjustQuestionToJson(AQuestion); //AQuestion.Replace(sLineBreak, '\n', [rfReplaceAll, rfIgnoreCase]); + Result := FResponse; LResponse := TRequest.New .BaseURL(FSettings.BaseUrlOpenAI) - .ContentType('application/json') - .Accept('application/json') + .ContentType(TConsts.APPLICATION_JSON) + .Accept(TConsts.APPLICATION_JSON) .Token('Bearer ' + FSettings.ApiKeyOpenAI) - .AddBody(Format(API_JSON_BODY_BASE, [FSettings.ModelOpenAI, LQuestion])) + .AddBody(Format(API_JSON_BODY_BASE, [FSettings.ModelOpenAI, AQuestion])) .Post; + FResponse.SetStatusCode(LResponse.StatusCode); + if LResponse.StatusCode <> 200 then - Exit('Question cannot be answered' + sLineBreak + 'Return: ' + LResponse.Content); + begin + FResponse.SetContentText('Question cannot be answered' + sLineBreak + 'Return: ' + LResponse.Content); + Exit; + end; LJsonValueAll := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LResponse.Content), 0); if not(LJsonValueAll is TJSONObject) then - Exit('The question cannot be answered, return object not found.' + sLineBreak + + begin + FResponse.SetContentText('The question cannot be answered, return object not found.' + sLineBreak + 'Return: ' + LResponse.Content); + Exit; + end; LJsonValueChoices := TJSONObject(LJsonValueAll).GetValue('choices'); if not(LJsonValueChoices is TJSONArray) then - Exit('The question cannot be answered, choices not found.' + sLineBreak + + begin + FResponse.SetContentText('The question cannot be answered, choices not found.' + sLineBreak + 'Return: ' + LResponse.Content); + Exit; + end; LJsonArrayChoices := LJsonValueChoices as TJSONArray; for LItemChoices := 0 to Pred(LJsonArrayChoices.Count) do @@ -89,10 +96,10 @@ function TDelphiAIDevAIChatGPT.GetResponse(const AQuestion: string): string; //GET MESSAGE LIKE TJSONObject LJsonObjMessage := LJsonValueMessage as TJSONObject; - Result := Result + TJSONString(LJsonObjMessage.GetValue('content')).Value.Trim + sLineBreak; + LResult := LResult + TJSONString(LJsonObjMessage.GetValue('content')).Value.Trim + sLineBreak; end; - Result := Result.Trim; + FResponse.SetContentText(LResult.Trim); end; end. diff --git a/Src/AI/DelphiAIDev.AI.Facade.pas b/Src/AI/DelphiAIDev.AI.Facade.pas new file mode 100644 index 0000000..d7f418d --- /dev/null +++ b/Src/AI/DelphiAIDev.AI.Facade.pas @@ -0,0 +1,91 @@ +unit DelphiAIDev.AI.Facade; + +interface + +uses + System.SysUtils, + System.Classes, + DelphiAIDev.Utils, + DelphiAIDev.Types, + DelphiAIDev.Settings, + DelphiAIDev.AI.Interfaces, + DelphiAIDev.AI.Response, + DelphiAIDev.AI.Gemini, + DelphiAIDev.AI.ChatGPT, + DelphiAIDev.AI.Groq, + DelphiAIDev.AI.Mistral, + DelphiAIDev.AI.Ollama; + +type + IDelphiAIDevAIResponse = DelphiAIDev.AI.Interfaces.IDelphiAIDevAIResponse; + + TDelphiAIDevAIFacade = class + private + FAiUse: TC4DAiAvailable; + FSettings: TDelphiAIDevSettings; + FResponse: IDelphiAIDevAIResponse; + public + constructor Create; + destructor Destroy; override; + function AiUse(const Value: TC4DAiAvailable): TDelphiAIDevAIFacade; + function ProcessSend(const AQuestion: string): TDelphiAIDevAIFacade; + function Response: IDelphiAIDevAIResponse; + end; + +implementation + +constructor TDelphiAIDevAIFacade.Create; +begin + FSettings := TDelphiAIDevSettings.GetInstance; + FSettings.LoadData; + FAiUse := FSettings.AIDefault; + FResponse := TDelphiAIDevAIResponse.New; +end; + +destructor TDelphiAIDevAIFacade.Destroy; +begin + inherited; +end; + +function TDelphiAIDevAIFacade.AiUse(const Value: TC4DAiAvailable): TDelphiAIDevAIFacade; +begin + Result := Self; + FAiUse := Value; +end; + +function TDelphiAIDevAIFacade.ProcessSend(const AQuestion: string): TDelphiAIDevAIFacade; +var + LQuestion: string; + LRequest: IDelphiAIDevAI; +begin + Result := Self; + LQuestion := TUtils.AdjustQuestionToJson(AQuestion); + + if TUtils.DebugMyIsOn then + TUtils.AddLogDeleteFileFirst(LQuestion, 'DelphiAIDevAI_ProcessSend'); + + case FAiUse of + TC4DAiAvailable.Gemini: + LRequest := TDelphiAIDevAIGemini.Create(FSettings, FResponse); + TC4DAiAvailable.OpenAI: + LRequest := TDelphiAIDevAIChatGPT.Create(FSettings, FResponse); + TC4DAiAvailable.Groq: + LRequest := TDelphiAIDevAIGroq.Create(FSettings, FResponse); + TC4DAiAvailable.Mistral: + LRequest := TDelphiAIDevAIMistral.Create(FSettings, FResponse); + TC4DAiAvailable.Ollama: + LRequest := TDelphiAIDevAIOllama.Create(FSettings, FResponse); + else + FResponse.SetContentText('Default AI not reported in Delphi AI Developer settings'); + Exit; + end; + + LRequest.GetResponse(LQuestion); +end; + +function TDelphiAIDevAIFacade.Response: IDelphiAIDevAIResponse; +begin + Result := FResponse; +end; + +end. diff --git a/Src/AI/DelphiAIDev.AI.Gemini.pas b/Src/AI/DelphiAIDev.AI.Gemini.pas index ea58252..ed284cf 100644 --- a/Src/AI/DelphiAIDev.AI.Gemini.pas +++ b/Src/AI/DelphiAIDev.AI.Gemini.pas @@ -7,19 +7,21 @@ interface System.JSON, System.Classes, RESTRequest4D, + DelphiAIDev.Consts, DelphiAIDev.Utils, DelphiAIDev.Settings, - DelphiAIDev.AI.Interfaces; + DelphiAIDev.AI.Interfaces, + DelphiAIDev.AI.Response; type TDelphiAIDevAIGemini = class(TInterfacedObject, IDelphiAIDevAI) private FSettings: TDelphiAIDevSettings; + FResponse: IDelphiAIDevAIResponse; protected - function GetResponse(const AQuestion: string): string; + function GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; public - class function New(const ASettings: TDelphiAIDevSettings): IDelphiAIDevAI; - constructor Create(const ASettings: TDelphiAIDevSettings); + constructor Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); end; implementation @@ -27,45 +29,49 @@ implementation const API_JSON_BODY_BASE = '{"contents": [{"parts": [ {"text": "%s"}]}]}'; -class function TDelphiAIDevAIGemini.New(const ASettings: TDelphiAIDevSettings): IDelphiAIDevAI; -begin - Result := Self.Create(ASettings); -end; - -constructor TDelphiAIDevAIGemini.Create(const ASettings: TDelphiAIDevSettings); +constructor TDelphiAIDevAIGemini.Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); begin FSettings := ASettings; + FResponse := AResponse; end; -function TDelphiAIDevAIGemini.GetResponse(const AQuestion: string): string; +function TDelphiAIDevAIGemini.GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; var LApiUrl: string; - LQuestion: string; LResponse: IResponse; LJsonValueAll: TJSONVALUE; LJsonArrayCandidates: TJsonArray; LJsonArrayParts: TJsonArray; LJsonObjContent: TJsonObject; LJsonObjParts: TJsonObject; - LItemCandidates, LItemParts: Integer; + LItemCandidates: Integer; + LItemParts: Integer; + LResult: string; begin - Result := ''; - LApiUrl := FSettings.BaseUrlGemini + FSettings.ModelGemini + '?key=' + FSettings.ApiKeyGemini; - LQuestion := TUtils.AdjustQuestionToJson(AQuestion); + Result := FResponse; + LApiUrl := FSettings.BaseUrlGemini + FSettings.ModelGemini + '?key=' + FSettings.ApiKeyGemini; LResponse := TRequest.New .BaseURL(LApiUrl) - .Accept('application/json') - .AddBody(Format(API_JSON_BODY_BASE, [LQuestion])) + .Accept(TConsts.APPLICATION_JSON) + .AddBody(Format(API_JSON_BODY_BASE, [AQuestion])) .Post; + FResponse.SetStatusCode(LResponse.StatusCode); + if LResponse.StatusCode <> 200 then - Exit('Question cannot be answered' + sLineBreak + 'Return: ' + LResponse.Content); + begin + FResponse.SetContentText('Question cannot be answered' + sLineBreak + 'Return: ' + LResponse.Content); + Exit; + end; LJsonValueAll := TJsonObject.ParseJSONValue(LResponse.Content); if not(LJsonValueAll is TJSONObject) then - Exit('The question cannot be answered, return object not found.' + sLineBreak + + begin + FResponse.SetContentText('The question cannot be answered, return object not found.' + sLineBreak + 'Return: ' + LResponse.Content); + Exit; + end; LJsonArrayCandidates := (LJsonValueAll as TJsonObject).GetValue('candidates'); for LItemCandidates := 0 to Pred(LJsonArrayCandidates.Count) do @@ -75,11 +81,11 @@ function TDelphiAIDevAIGemini.GetResponse(const AQuestion: string): string; for LItemParts := 0 to Pred(LJsonArrayParts.Count) do begin LJsonObjParts := LJsonArrayParts.Items[LItemParts] as TJsonObject; - Result := Result + LJsonObjParts.GetValue('text').Trim + sLineBreak; + LResult := LResult + LJsonObjParts.GetValue('text').Trim + sLineBreak; end; end; - Result := Result.Trim; + FResponse.SetContentText(LResult.Trim); end; end. diff --git a/Src/AI/DelphiAIDev.AI.Groq.pas b/Src/AI/DelphiAIDev.AI.Groq.pas index 599efde..f1a843b 100644 --- a/Src/AI/DelphiAIDev.AI.Groq.pas +++ b/Src/AI/DelphiAIDev.AI.Groq.pas @@ -7,6 +7,7 @@ interface System.JSON, System.Classes, RESTRequest4D, + DelphiAIDev.Consts, DelphiAIDev.Utils, DelphiAIDev.Settings, DelphiAIDev.AI.Interfaces; @@ -15,11 +16,11 @@ interface TDelphiAIDevAIGroq = class(TInterfacedObject, IDelphiAIDevAI) private FSettings: TDelphiAIDevSettings; + FResponse: IDelphiAIDevAIResponse; protected - function GetResponse(const AQuestion: string): string; + function GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; public - class function New(const ASettings: TDelphiAIDevSettings): IDelphiAIDevAI; - constructor Create(const ASettings: TDelphiAIDevSettings); + constructor Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); end; implementation @@ -27,54 +28,57 @@ implementation const API_JSON_BODY_BASE = '{"messages": [{"role": "user", "content": "%s"}], "model": "%s"}'; -class function TDelphiAIDevAIGroq.New(const ASettings: TDelphiAIDevSettings): IDelphiAIDevAI; -begin - Result := Self.Create(ASettings); -end; - -constructor TDelphiAIDevAIGroq.Create(const ASettings: TDelphiAIDevSettings); +constructor TDelphiAIDevAIGroq.Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); begin FSettings := ASettings; + FResponse := AResponse; end; -function TDelphiAIDevAIGroq.GetResponse(const AQuestion: string): string; +function TDelphiAIDevAIGroq.GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; var - LQuestion: string; LResponse: IResponse; LJsonValueAll: TJSONVALUE; LJsonArrayChoices: TJsonArray; LJsonObjMessage: TJsonObject; LContent: string; LItemChoices: Integer; + LResult: string; begin - Result := ''; - LQuestion := TUtils.AdjustQuestionToJson(AQuestion); + Result := FResponse; LResponse := TRequest.New .BaseURL(FSettings.BaseUrlGroq) - .ContentType('application/json') - .Accept('application/json') + .ContentType(TConsts.APPLICATION_JSON) + .Accept(TConsts.APPLICATION_JSON) .Token('Bearer ' + FSettings.ApiKeyGroq) - .AddBody(Format(API_JSON_BODY_BASE, [LQuestion, FSettings.ModelGroq])) + .AddBody(Format(API_JSON_BODY_BASE, [AQuestion, FSettings.ModelGroq])) .Post; + FResponse.SetStatusCode(LResponse.StatusCode); + if LResponse.StatusCode <> 200 then - Exit('Question cannot be answered' + sLineBreak + 'Return: ' + LResponse.Content); + begin + FResponse.SetContentText('Question cannot be answered' + sLineBreak + 'Return: ' + LResponse.Content); + Exit; + end; LJsonValueAll := TJsonObject.ParseJSONValue(LResponse.Content); if not(LJsonValueAll is TJSONObject) then - Exit('The question cannot be answered, return object not found.' + sLineBreak + + begin + FResponse.SetContentText('The question cannot be answered, return object not found.' + sLineBreak + 'Return: ' + LResponse.Content); + Exit; + end; LJsonArrayChoices := (LJsonValueAll as TJsonObject).GetValue('choices'); for LItemChoices := 0 to Pred(LJsonArrayChoices.Count) do begin LJsonObjMessage := LJsonArrayChoices.Items[LItemChoices].GetValue('message'); LContent := LJsonObjMessage.GetValue('content'); - Result := Result + LContent.Trim + sLineBreak; + LResult := LResult + LContent.Trim + sLineBreak; end; - Result := Result.Trim; + FResponse.SetContentText(LResult.Trim); end; end. diff --git a/Src/AI/DelphiAIDev.AI.Interfaces.pas b/Src/AI/DelphiAIDev.AI.Interfaces.pas index a06e489..71317c8 100644 --- a/Src/AI/DelphiAIDev.AI.Interfaces.pas +++ b/Src/AI/DelphiAIDev.AI.Interfaces.pas @@ -2,10 +2,22 @@ interface +uses + System.Classes; + type + IDelphiAIDevAIResponse = interface + ['{F9E7734A-33FA-4448-9F5D-A3680FFFFFB9}'] + function Clear: IDelphiAIDevAIResponse; + function SetStatusCode(const Value: Integer): IDelphiAIDevAIResponse; + function GetStatusCode: Integer; + function SetContentText(const Value: string): IDelphiAIDevAIResponse; + function GetContent: TStrings; + end; + IDelphiAIDevAI = interface ['{B82FACA6-66DA-4DC0-877F-8263B5C172C1}'] - function GetResponse(const AQuestion: string): string; + function GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; end; implementation diff --git a/Src/AI/DelphiAIDev.AI.Mistral.pas b/Src/AI/DelphiAIDev.AI.Mistral.pas new file mode 100644 index 0000000..572dcba --- /dev/null +++ b/Src/AI/DelphiAIDev.AI.Mistral.pas @@ -0,0 +1,84 @@ +unit DelphiAIDev.AI.Mistral; + +interface + +uses + System.SysUtils, + System.JSON, + System.Classes, + RESTRequest4D, + DelphiAIDev.Consts, + DelphiAIDev.Utils, + DelphiAIDev.Settings, + DelphiAIDev.AI.Interfaces; + +type + TDelphiAIDevAIMistral = class(TInterfacedObject, IDelphiAIDevAI) + private + FSettings: TDelphiAIDevSettings; + FResponse: IDelphiAIDevAIResponse; + protected + function GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; + public + constructor Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); + end; + +implementation + +const + API_JSON_BODY_BASE = '{"messages": [{"role": "user", "content": "%s"}], "model": "%s"}'; + +constructor TDelphiAIDevAIMistral.Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); +begin + FSettings := ASettings; + FResponse := AResponse; +end; + +function TDelphiAIDevAIMistral.GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; +var + LResponse: IResponse; + LJsonValueAll: TJSONVALUE; + LJsonArrayChoices: TJsonArray; + LJsonObjMessage: TJsonObject; + LContent: string; + LItemChoices: Integer; + LResult: string; +begin + Result := FResponse; + + LResponse := TRequest.New + .BaseURL(FSettings.BaseUrlMistral) + .ContentType(TConsts.APPLICATION_JSON) + .Accept(TConsts.APPLICATION_JSON) + .Token('Bearer ' + FSettings.ApiKeyMistral) + .AddBody(Format(API_JSON_BODY_BASE, [AQuestion, FSettings.ModelMistral])) + .Post; + + FResponse.SetStatusCode(LResponse.StatusCode); + + if LResponse.StatusCode <> 200 then + begin + FResponse.SetContentText('Question cannot be answered' + sLineBreak + 'Return: ' + LResponse.Content); + Exit; + end; + + LJsonValueAll := TJsonObject.ParseJSONValue(LResponse.Content); + if not(LJsonValueAll is TJSONObject) then + begin + FResponse.SetContentText('The question cannot be answered, return object not found.' + sLineBreak + + 'Return: ' + LResponse.Content); + Exit; + end; + + LJsonArrayChoices := (LJsonValueAll as TJsonObject).GetValue('choices'); + for LItemChoices := 0 to Pred(LJsonArrayChoices.Count) do + begin + LJsonObjMessage := LJsonArrayChoices.Items[LItemChoices].GetValue('message'); + LContent := LJsonObjMessage.GetValue('content'); + LResult := LResult + LContent.Trim + sLineBreak; + end; + + FResponse.SetContentText(LResult.Trim); +end; + +end. diff --git a/Src/AI/DelphiAIDev.AI.Ollama.pas b/Src/AI/DelphiAIDev.AI.Ollama.pas new file mode 100644 index 0000000..e1496bc --- /dev/null +++ b/Src/AI/DelphiAIDev.AI.Ollama.pas @@ -0,0 +1,96 @@ +unit DelphiAIDev.AI.Ollama; + +interface + +uses + System.SysUtils, + System.JSON, + System.Classes, + RESTRequest4D, + DelphiAIDev.Consts, + DelphiAIDev.Utils, + DelphiAIDev.Settings, + DelphiAIDev.AI.Interfaces; + +type + TDelphiAIDevAIOllama = class(TInterfacedObject, IDelphiAIDevAI) + private + FSettings: TDelphiAIDevSettings; + FResponse: IDelphiAIDevAIResponse; + protected + function GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; + public + constructor Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); + end; + +implementation + +const + API_JSON_BODY_BASE = + '{"model": "%s", '+ + '"messages": [{"role": "user", "content": "%s"}], '+ + '"options": {"seed": 101, "temperature": 0}, '+ + '"stream": false}'; + +constructor TDelphiAIDevAIOllama.Create(const ASettings: TDelphiAIDevSettings; const AResponse: IDelphiAIDevAIResponse); +begin + FSettings := ASettings; + FResponse := AResponse; +end; + +function TDelphiAIDevAIOllama.GetResponse(const AQuestion: string): IDelphiAIDevAIResponse; +var + LResponse: IResponse; + LJsonValueAll: TJSONVALUE; + LJsonValueMessage: TJSONValue; + LJsonObjMessage: TJsonObject; +begin + Result := FResponse; + + try + LResponse := TRequest.New + .BaseURL(FSettings.BaseUrlOllama) + .ContentType(TConsts.APPLICATION_JSON) + .Accept(TConsts.APPLICATION_JSON) + //.Token('Bearer ' + FSettings.ApiKeyOllama) + .AddBody(Format(API_JSON_BODY_BASE, [FSettings.ModelOllama, AQuestion])) + .Post; + except + on E: Exception do + begin + FResponse.SetStatusCode(LResponse.StatusCode) + .SetContentText('The question cannot be answered, return object not found.' + sLineBreak + + 'Return: ' + LResponse.Content); + Exit; + end; + end; + + FResponse.SetStatusCode(LResponse.StatusCode); + + if LResponse.StatusCode <> 200 then + begin + FResponse.SetContentText('Question cannot be answered' + sLineBreak + 'Return: ' + LResponse.Content); + Exit; + end; + + LJsonValueAll := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LResponse.Content), 0); + if not(LJsonValueAll is TJSONObject) then + begin + FResponse.SetContentText('The question cannot be answered, return object not found.' + sLineBreak + + 'Return: ' + LResponse.Content); + Exit; + end; + + LJsonValueMessage := (LJsonValueAll as TJSONObject).GetValue('message'); + if not(LJsonValueMessage is TJSONObject) then + begin + FResponse.SetContentText('The question cannot be answered, return object not found.' + sLineBreak + + 'Return: ' + LResponse.Content); + Exit; + end; + + LJsonObjMessage := LJsonValueMessage as TJSONObject; + FResponse.SetContentText(TJSONString(LJsonObjMessage.GetValue('content')).Value.Trim); +end; + +end. diff --git a/Src/AI/DelphiAIDev.AI.Response.pas b/Src/AI/DelphiAIDev.AI.Response.pas new file mode 100644 index 0000000..1481f03 --- /dev/null +++ b/Src/AI/DelphiAIDev.AI.Response.pas @@ -0,0 +1,75 @@ +unit DelphiAIDev.AI.Response; + +interface + +uses + System.SysUtils, + System.Classes, + DelphiAIDev.AI.Interfaces; + +type + TDelphiAIDevAIResponse = class(TInterfacedObject, IDelphiAIDevAIResponse) + private + FStatusCode: Integer; + FContent: TStrings; + protected + function Clear: IDelphiAIDevAIResponse; + function SetStatusCode(const Value: Integer): IDelphiAIDevAIResponse; + function GetStatusCode: Integer; + function SetContentText(const Value: string): IDelphiAIDevAIResponse; + function GetContent: TStrings; + public + class function New: IDelphiAIDevAIResponse; + constructor Create; + destructor Destroy; override; + end; + +implementation + +class function TDelphiAIDevAIResponse.New: IDelphiAIDevAIResponse; +begin + Result := Self.Create; +end; + +constructor TDelphiAIDevAIResponse.Create; +begin + FContent := TStringList.Create; + Self.Clear; +end; + +destructor TDelphiAIDevAIResponse.Destroy; +begin + FContent.Free; + inherited; +end; + +function TDelphiAIDevAIResponse.Clear: IDelphiAIDevAIResponse; +begin + Result := Self; + FStatusCode := 0; + FContent.Clear; +end; + +function TDelphiAIDevAIResponse.SetStatusCode(const Value: Integer): IDelphiAIDevAIResponse; +begin + Result := Self; + FStatusCode := Value; +end; + +function TDelphiAIDevAIResponse.GetStatusCode: Integer; +begin + Result := FStatusCode; +end; + +function TDelphiAIDevAIResponse.SetContentText(const Value: string): IDelphiAIDevAIResponse; +begin + Result := Self; + FContent.Text := Value; +end; + +function TDelphiAIDevAIResponse.GetContent: TStrings; +begin + Result := FContent; +end; + +end. diff --git a/Src/Chat/DelphiAIDev.Chat.ProcessResponse.pas b/Src/Chat/DelphiAIDev.Chat.ProcessResponse.pas index 73c75f9..060ef51 100644 --- a/Src/Chat/DelphiAIDev.Chat.ProcessResponse.pas +++ b/Src/Chat/DelphiAIDev.Chat.ProcessResponse.pas @@ -121,14 +121,14 @@ procedure TDelphiAIDevChatProcessResponse.BoldInWordsBetweenBacktick(const ALine if not LCodeStarted then begin - if(LCurrentLetter = BACKTICK)and(LNextLetter <> BACKTICK)then + if (LCurrentLetter = BACKTICK) and (LNextLetter <> BACKTICK) then begin LCodeStarted := True; Continue; end; end; - if(LCurrentLetter = BACKTICK)and(LNextLetter <> BACKTICK)then + if (LCurrentLetter = BACKTICK) and (LNextLetter <> BACKTICK) then begin LCodeStarted := False; FRichEdit.SelAttributes.Style := []; @@ -177,7 +177,7 @@ procedure TDelphiAIDevChatProcessResponse.BoldInWordsBetweenTwoAsterisk(const AL if not LCodeStarted then begin - if(LCurrentLetter = ASTERISK)and(LNextLetter = ASTERISK)then + if (LCurrentLetter = ASTERISK) and (LNextLetter = ASTERISK) then begin LCodeStarted := True; Inc(LPosLetter, 2); @@ -185,7 +185,7 @@ procedure TDelphiAIDevChatProcessResponse.BoldInWordsBetweenTwoAsterisk(const AL end; end; - if(LCurrentLetter = ASTERISK)and(LNextLetter = ASTERISK)then + if (LCurrentLetter = ASTERISK) and (LNextLetter = ASTERISK) then begin LCodeStarted := False; FRichEdit.SelAttributes.Style := []; diff --git a/Src/Chat/DelphiAIDev.Chat.View.dfm b/Src/Chat/DelphiAIDev.Chat.View.dfm index 9c5c1b6..e98bf09 100644 --- a/Src/Chat/DelphiAIDev.Chat.View.dfm +++ b/Src/Chat/DelphiAIDev.Chat.View.dfm @@ -12,7 +12,6 @@ object DelphiAIDevChatView: TDelphiAIDevChatView Font.Name = 'Tahoma' Font.Style = [] KeyPreview = True - OldCreateOrder = False Position = poScreenCenter ShowHint = True OnActivate = FormActivate @@ -22,7 +21,6 @@ object DelphiAIDevChatView: TDelphiAIDevChatView DesignSize = ( 975 661) - PixelsPerInch = 96 TextHeight = 13 object pnBack: TPanel Left = 0 @@ -67,7 +65,7 @@ object DelphiAIDevChatView: TDelphiAIDevChatView Ctl3D = True Font.Charset = ANSI_CHARSET Font.Color = clWindow - Font.Height = -12 + Font.Height = -13 Font.Name = 'Courier New' Font.Style = [] Lines.Strings = ( @@ -80,7 +78,6 @@ object DelphiAIDevChatView: TDelphiAIDevChatView ScrollBars = ssVertical ShowHint = True TabOrder = 1 - Zoom = 100 end object pnBackQuestion: TPanel Left = 18 @@ -109,7 +106,7 @@ object DelphiAIDevChatView: TDelphiAIDevChatView Align = alClient Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText - Font.Height = -11 + Font.Height = -13 Font.Name = 'Tahoma' Font.Style = [] ParentFont = False @@ -119,7 +116,7 @@ object DelphiAIDevChatView: TDelphiAIDevChatView OnKeyUp = mmQuestionKeyUp end end - object pnBackStatusBar: TPanel + object pnBackButtons: TPanel Left = 18 Top = 634 Width = 938 @@ -818,6 +815,16 @@ object DelphiAIDevChatView: TDelphiAIDevChatView Caption = 'Groq' OnClick = Gemini1Click end + object Mistral1: TMenuItem + Tag = 3 + Caption = 'Mistral' + OnClick = Gemini1Click + end + object Ollama1: TMenuItem + Tag = 4 + Caption = 'Ollama (offline)' + OnClick = Gemini1Click + end end object pMenuMoreActions: TPopupMenu Images = ImageList1 diff --git a/Src/Chat/DelphiAIDev.Chat.View.pas b/Src/Chat/DelphiAIDev.Chat.View.pas index fe424a3..7896092 100644 --- a/Src/Chat/DelphiAIDev.Chat.View.pas +++ b/Src/Chat/DelphiAIDev.Chat.View.pas @@ -26,7 +26,7 @@ interface Clipbrd, DelphiAIDev.Types, DelphiAIDev.Consts, - DelphiAIDev.Chat, + DelphiAIDev.AI.Facade, DelphiAIDev.Settings, DelphiAIDev.ModuleCreator, DelphiAIDev.DefaultsQuestions.PopupMenu, @@ -52,7 +52,7 @@ TDelphiAIDevChatView = class(TDockableForm) pMenuCurrentAI: TPopupMenu; Gemini1: TMenuItem; ChatGPT1: TMenuItem; - pnBackStatusBar: TPanel; + pnBackButtons: TPanel; lbCurrentAI: TLabel; StatusBar1: TStatusBar; pnCommands: TPanel; @@ -74,6 +74,8 @@ TDelphiAIDevChatView = class(TDockableForm) pMenuQuestions: TPopupMenu; btnCleanAll: TSpeedButton; Groq1: TMenuItem; + Mistral1: TMenuItem; + Ollama1: TMenuItem; procedure FormShow(Sender: TObject); procedure cBoxSizeFontKeyPress(Sender: TObject; var Key: Char); procedure Cut1Click(Sender: TObject); @@ -102,7 +104,7 @@ TDelphiAIDevChatView = class(TDockableForm) procedure Clear1Click(Sender: TObject); procedure btnCleanAllClick(Sender: TObject); private - FChat: TDelphiAIDevChat; + FAI: TDelphiAIDevAIFacade; FSettings: TDelphiAIDevSettings; FProcessResponse: TDelphiAIDevChatProcessResponse; FPopupMenuQuestions: TDelphiAIDevDefaultsQuestionsPopupMenu; @@ -128,7 +130,6 @@ TDelphiAIDevChatView = class(TDockableForm) procedure DoProcessClickInItemDefaultQuestions(ACodeOnly: Boolean; AQuestion: string); procedure ProcessWordWrap; procedure ConfScreenOnCreate; - procedure ValidateRegistrationOfSelectedAI; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; @@ -159,10 +160,10 @@ implementation procedure RegisterSelf; begin - if(not Assigned(DelphiAIDevChatView))then + if not Assigned(DelphiAIDevChatView) then DelphiAIDevChatView := TDelphiAIDevChatView.Create(nil); - if(@RegisterFieldAddress <> nil)then + if @RegisterFieldAddress <> nil then RegisterFieldAddress(DelphiAIDevChatView.Name, @DelphiAIDevChatView); RegisterDesktopFormClass(TDelphiAIDevChatView, DelphiAIDevChatView.Name, DelphiAIDevChatView.Name); @@ -170,7 +171,7 @@ procedure RegisterSelf; procedure Unregister; begin - if(@UnRegisterFieldAddress <> nil)then + if @UnRegisterFieldAddress <> nil then UnRegisterFieldAddress(@DelphiAIDevChatView); FreeAndNil(DelphiAIDevChatView); end; @@ -188,7 +189,7 @@ constructor TDelphiAIDevChatView.Create(AOwner: TComponent); AutoSave := True; SaveStateNecessary := True; - FChat := TDelphiAIDevChat.Create; + FAI := TDelphiAIDevAIFacade.Create; FSettings := TDelphiAIDevSettings.GetInstance; FProcessResponse := TDelphiAIDevChatProcessResponse.Create(mmReturn); FPopupMenuQuestions := TDelphiAIDevDefaultsQuestionsPopupMenu.Create; @@ -203,7 +204,7 @@ destructor TDelphiAIDevChatView.Destroy; Self.SaveMemoReturnInFile; FPopupMenuQuestions.Free; FProcessResponse.Free; - FChat.Free; + FAI.Free; inherited; end; @@ -241,6 +242,10 @@ procedure TDelphiAIDevChatView.ConfScreenOnCreate; ShapeCommands.Top := 0; ShapeCommands.Width := ShapeCommands.Parent.Width; ShapeCommands.Height := ShapeCommands.Parent.Height; + + {$IF CompilerVersion >= 34} //Sydney + pnWaitCaption.StyleElements := pnWaitCaption.StyleElements - [seFont]; + {$ENDIF} end; procedure TDelphiAIDevChatView.ConfScreenOnShow; @@ -279,7 +284,7 @@ procedure TDelphiAIDevChatView.mmQuestionChange(Sender: TObject); procedure TDelphiAIDevChatView.mmQuestionKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin - if (ssCtrl in Shift)and(Key = VK_RETURN) then + if (ssCtrl in Shift) and (Key = VK_RETURN) then begin btnSend.Click; Key := 0; @@ -288,7 +293,7 @@ procedure TDelphiAIDevChatView.mmQuestionKeyDown(Sender: TObject; var Key: Word; procedure TDelphiAIDevChatView.mmQuestionKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin - if (ssCtrl in Shift)and(Key = 65) then + if (ssCtrl in Shift) and (Key = 65) then begin mmQuestion.SelectAll; Key := 0; @@ -344,7 +349,7 @@ procedure TDelphiAIDevChatView.FormResize(Sender: TObject); procedure TDelphiAIDevChatView.FillMemoReturnWithFile; begin - if(FileExists(TUtils.GetPathFileChat))then + if FileExists(TUtils.GetPathFileChat) then mmReturn.Lines.LoadFromFile(TUtils.GetPathFileChat) end; @@ -423,7 +428,7 @@ procedure TDelphiAIDevChatView.ProcessSend; if mmQuestion.Lines.Text.Trim.IsEmpty then TUtils.ShowMsgAndAbort('No questions have been added', mmQuestion); - Self.ValidateRegistrationOfSelectedAI; + FSettings.ValidateFillingSelectedAI; mmReturn.Lines.Clear; Self.WaitingFormON; @@ -434,7 +439,10 @@ procedure TDelphiAIDevChatView.ProcessSend; LQuestion := TUtilsOTA.GetSelectedBlockOrAllCodeUnit.Trim + sLineBreak; if btnCodeOnly.ImageIndex = CodeOnly_ImageIndex_ON then - LQuestion := LQuestion + FSettings.LanguageQuestions.GetMsgCodeOnly; + LQuestion := LQuestion + FSettings.LanguageQuestions.GetMsgCodeOnly + sLineBreak; + + if not FSettings.DefaultPrompt.Trim.IsEmpty then + LQuestion := LQuestion + FSettings.DefaultPrompt + sLineBreak; LQuestion := LQuestion + mmQuestion.Lines.Text; @@ -443,7 +451,7 @@ procedure TDelphiAIDevChatView.ProcessSend; begin try try - FChat.ProcessSend(LQuestion); + FAI.AiUse(FSettings.AIDefault).ProcessSend(LQuestion); except on E: Exception do TThread.Synchronize(nil, @@ -460,7 +468,7 @@ procedure TDelphiAIDevChatView.ProcessSend; mmReturn.Lines.BeginUpdate; try //Optional use of one of the following lines - FProcessResponse.AddResponseComplete(FChat.Response); + FProcessResponse.AddResponseComplete(FAI.Response.GetContent); Self.Last; //Self.AddResponseSimple(FChat.Response.Text); finally @@ -478,37 +486,6 @@ procedure TDelphiAIDevChatView.ProcessSend; LTask.Start; end; -procedure TDelphiAIDevChatView.ValidateRegistrationOfSelectedAI; -const - MSG = '"%s" for IA %s not specified in settings.' + sLineBreak + sLineBreak + - 'Access menu > AI Developer > Settings'; -begin - case FSettings.AIDefault of - TC4DAIsAvailable.Gemini: - begin - if FSettings.BaseUrlGemini.Trim.IsEmpty then - TUtils.ShowMsgAndAbort(Format(MSG, ['Base URL', 'Gemini'])); - - if FSettings.ModelGemini.Trim.IsEmpty then - TUtils.ShowMsgAndAbort(Format(MSG, ['Model', 'Gemini'])); - - if FSettings.ApiKeyGemini.Trim.IsEmpty then - TUtils.ShowMsgAndAbort(Format(MSG, ['API Key', 'Gemini'])); - end; - TC4DAIsAvailable.OpenAI: - begin - if FSettings.BaseUrlOpenAI.Trim.IsEmpty then - TUtils.ShowMsgAndAbort(Format(MSG, ['Base URL', 'ChatGPT'])); - - if FSettings.ModelOpenAI.Trim.IsEmpty then - TUtils.ShowMsgAndAbort(Format(MSG, ['Model', 'ChatGPT'])); - - if FSettings.ApiKeyOpenAI.Trim.IsEmpty then - TUtils.ShowMsgAndAbort(Format(MSG, ['API Key', 'ChatGPT'])); - end; - end; -end; - procedure TDelphiAIDevChatView.AddResponseSimple(const AString: string); begin Self.Last; @@ -518,116 +495,6 @@ procedure TDelphiAIDevChatView.AddResponseSimple(const AString: string); Self.Last; end; -////Add line-by-line response to color where Delphi code is -//procedure TDelphiAIDevChatView.AddResponseComplete(const AStrings: TStrings); -//var -// LLineNum: Integer; -// LLineStr: string; -// LCodeStarted: Boolean; -//begin -// mmReturn.Lines.Clear; -// mmReturn.SelAttributes.Color := TUtilsOTA.ActiveThemeColorDefault; -// mmReturn.SelAttributes.Style := []; -// -// LCodeStarted := False; -// for LLineNum := 0 to Pred(AStrings.Count) do -// begin -// LLineStr := AStrings[LLineNum].TrimRight; -// -// if not LCodeStarted then -// begin -// if TUtils.CodeIdMarkBeginCode(LLineStr) then -// begin -// LCodeStarted := True; -// Continue; -// end; -// end; -// -// if LLineStr.Trim = TConsts.MARK_END then -// begin -// LCodeStarted := False; -// mmReturn.SelAttributes.Color := TUtilsOTA.ActiveThemeColorDefault; -// Continue; -// end; -// -// if LCodeStarted then -// begin -// if (FSettings.ColorHighlightCodeDelphiUse) and (FSettings.ColorHighlightCodeDelphi <> clNone) then -// mmReturn.SelAttributes.Color := FSettings.ColorHighlightCodeDelphi -// else -// mmReturn.SelAttributes.Color := TUtilsOTA.ActiveThemeForCode; -// end -// else -// mmReturn.SelAttributes.Color := TUtilsOTA.ActiveThemeColorDefault; -// -// //Optional use of one of the following lines -// //mmReturn.Lines.Add(LLineStr); -// Self.AddResponseLine(LLineStr); //.Replace(TConsts.MARK_BEGIN_PASCAL2, '', [rfReplaceAll, rfIgnoreCase]) -// end; -// Self.Last; -//end; -// -////Bold in words between Backtick -//procedure TDelphiAIDevChatView.AddResponseLine(const ALineStr: string); -//const -// BACKTICK = '`'; -//var -// i: Integer; -// LCurrentLetter: Char; -// LNextLetter: Char; -// LLineStarted: Boolean; -// LCodeStarted: Boolean; -//begin -// if not ALineStr.Contains(BACKTICK) then -// begin -// mmReturn.Lines.Add(IFThen(ALineStr.IsEmpty, ' ', ALineStr)); -// Exit; -// end; -// -// LLineStarted := False; -// LCodeStarted := False; -// for i := 0 to ALineStr.Length do -// begin -// LCurrentLetter := ALineStr[i]; -// LNextLetter := ALineStr[Succ(i)]; -// -// if not LCodeStarted then -// begin -// if(LCurrentLetter = BACKTICK)and(LNextLetter <> BACKTICK)then -// begin -// LCodeStarted := True; -// Continue; -// end; -// end; -// -// if(LCurrentLetter = BACKTICK)and(LNextLetter <> BACKTICK)then -// begin -// LCodeStarted := False; -// mmReturn.SelAttributes.Style := []; -// Continue; -// end; -// -// SendMessage(mmReturn.Handle, WM_VSCROLL, SB_BOTTOM, 0); -// if LCodeStarted then -// mmReturn.SelAttributes.Style := [fsBold] -// else -// mmReturn.SelAttributes.Style := []; -// -// if LLineStarted then -// mmReturn.SelText := LCurrentLetter -// else -// begin -// mmReturn.Lines.Add(''); -// mmReturn.SelText := LCurrentLetter; -// -// LLineStarted := True; -// end; -// SendMessage(mmReturn.Handle, WM_VSCROLL, SB_BOTTOM, 0); -// end; -// mmReturn.SelText := ' '; -// SendMessage(mmReturn.Handle, WM_VSCROLL, SB_BOTTOM, 0); -//end; - procedure TDelphiAIDevChatView.WaitingFormON; begin pnWait.Visible := False; @@ -746,13 +613,19 @@ procedure TDelphiAIDevChatView.pMenuCurrentAIPopup(Sender: TObject); Gemini1.Checked := False; ChatGPT1.Checked := False; Groq1.Checked := False; + Mistral1.Checked := False; + Ollama1.Checked := False; case FSettings.AIDefault of - TC4DAIsAvailable.Gemini: + TC4DAiAvailable.Gemini: Gemini1.Checked := True; - TC4DAIsAvailable.OpenAI: + TC4DAiAvailable.OpenAI: ChatGPT1.Checked := True; - TC4DAIsAvailable.Groq: + TC4DAiAvailable.Groq: Groq1.Checked := True; + TC4DAiAvailable.Mistral: + Mistral1.Checked := True; + TC4DAiAvailable.Ollama: + Ollama1.Checked := True; end; end; @@ -761,12 +634,16 @@ procedure TDelphiAIDevChatView.ConfLabelCurrentAI; lbCurrentAI.Caption := FSettings.AIDefault.ToString; case FSettings.AIDefault of - TC4DAIsAvailable.Gemini: + TC4DAiAvailable.Gemini: lbCurrentAI.Hint := FSettings.ModelGemini; - TC4DAIsAvailable.OpenAI: + TC4DAiAvailable.OpenAI: lbCurrentAI.Hint := FSettings.ModelOpenAI; - TC4DAIsAvailable.Groq: + TC4DAiAvailable.Groq: lbCurrentAI.Hint := FSettings.ModelGroq; + TC4DAiAvailable.Mistral: + lbCurrentAI.Hint := FSettings.ModelMistral; + TC4DAiAvailable.Ollama: + lbCurrentAI.Hint := FSettings.ModelOllama; end; lbCurrentAI.Repaint; @@ -779,10 +656,10 @@ procedure TDelphiAIDevChatView.Gemini1Click(Sender: TObject); begin //*SEVERAL LTag := TMenuItem(Sender).Tag; - if not(LTag in [0, 1, 2])then + if not(LTag in [0, 1, 2, 3, 4])then Exit; - FSettings.AIDefault := TC4DAIsAvailable(LTag); + FSettings.AIDefault := TC4DAiAvailable(LTag); FSettings.SaveData; Self.ConfLabelCurrentAI; end; diff --git a/Src/Chat/DelphiAIDev.Chat.pas b/Src/Chat/DelphiAIDev.Chat.pas deleted file mode 100644 index 614e282..0000000 --- a/Src/Chat/DelphiAIDev.Chat.pas +++ /dev/null @@ -1,62 +0,0 @@ -unit DelphiAIDev.Chat; - -interface - -uses - System.SysUtils, - System.Classes, - DelphiAIDev.Types, - DelphiAIDev.Settings, - DelphiAIDev.AI.Gemini, - DelphiAIDev.AI.ChatGPT, - DelphiAIDev.AI.Groq; - -type - TDelphiAIDevChat = class - private - FSettings: TDelphiAIDevSettings; - FResponse: TStrings; - public - constructor Create; - destructor Destroy; override; - procedure ProcessSend(const AQuestion: string); - function Response: TStrings; - end; - -implementation - -constructor TDelphiAIDevChat.Create; -begin - FSettings := TDelphiAIDevSettings.GetInstance; - FSettings.LoadData; - FResponse := TStringList.Create; -end; - -destructor TDelphiAIDevChat.Destroy; -begin - FResponse.Free; - inherited; -end; - -procedure TDelphiAIDevChat.ProcessSend(const AQuestion: string); -begin - FResponse.Clear; - - case FSettings.AIDefault of - TC4DAIsAvailable.Gemini: - FResponse.Text := TDelphiAIDevAIGemini.New(FSettings).GetResponse(AQuestion); - TC4DAIsAvailable.OpenAI: - FResponse.Text := TDelphiAIDevAIChatGPT.New(FSettings).GetResponse(AQuestion); - TC4DAIsAvailable.Groq: - FResponse.Text := TDelphiAIDevAIGroq.New(FSettings).GetResponse(AQuestion); - else - FResponse.Add('Default AI not reported in Delphi AI Developer settings'); - end; -end; - -function TDelphiAIDevChat.Response: TStrings; -begin - Result := FResponse; -end; - -end. diff --git a/Src/CodeCompletion/DelphiAIDev.CodeCompletion.KeyTab.pas b/Src/CodeCompletion/DelphiAIDev.CodeCompletion.KeyTab.pas new file mode 100644 index 0000000..1835795 --- /dev/null +++ b/Src/CodeCompletion/DelphiAIDev.CodeCompletion.KeyTab.pas @@ -0,0 +1,65 @@ +unit DelphiAIDev.CodeCompletion.KeyTab; + +interface + +uses + System.SysUtils, + System.Classes, + ToolsAPI, + DelphiAIDev.CodeCompletion.Vars; + +type + IDelphiAIDevCodeCompletionKeyTab = interface + ['{A032D6DE-E66D-4088-B384-5FEE82F65160}'] + procedure Process(const AContext: IOTAKeyContext); + end; + + TDelphiAIDevCodeCompletionKeyTab = class(TInterfacedObject, IDelphiAIDevCodeCompletionKeyTab) + private + FVars: TDelphiAIDevCodeCompletionVars; + protected + procedure Process(const AContext: IOTAKeyContext); + public + class function New: IDelphiAIDevCodeCompletionKeyTab; + constructor Create; + end; + +implementation + +class function TDelphiAIDevCodeCompletionKeyTab.New: IDelphiAIDevCodeCompletionKeyTab; +begin + Result := Self.Create; +end; + +constructor TDelphiAIDevCodeCompletionKeyTab.Create; +begin + FVars := TDelphiAIDevCodeCompletionVars.GetInstance; +end; + +procedure TDelphiAIDevCodeCompletionKeyTab.Process(const AContext: IOTAKeyContext); +var + i: Integer; + LTextLine: string; +begin + try + if FVars.Contents.Count > 1 then + AContext.EditBuffer.EditPosition.Delete(Pred(FVars.Contents.Count)); + + for i := 0 to Pred(FVars.Contents.Count) do + begin + LTextLine := FVars.Contents[i].Trim; + + if FVars.Contents.Count > 1 then + begin + AContext.EditBuffer.EditPosition.MoveEOL; + LTextLine := FVars.Contents[i]; + end; + + AContext.EditBuffer.EditPosition.InsertText(LTextLine + sLineBreak); + end; + finally + FVars.Clear; + end; +end; + +end. diff --git a/Src/CodeCompletion/DelphiAIDev.CodeCompletion.Search.pas b/Src/CodeCompletion/DelphiAIDev.CodeCompletion.Search.pas new file mode 100644 index 0000000..b7c6daf --- /dev/null +++ b/Src/CodeCompletion/DelphiAIDev.CodeCompletion.Search.pas @@ -0,0 +1,134 @@ +unit DelphiAIDev.CodeCompletion.Search; + +interface + +uses + System.SysUtils, + System.Classes, + Vcl.Forms, + Vcl.Controls, + ToolsAPI, + DelphiAIDev.Types, + DelphiAIDev.Consts, + DelphiAIDev.Settings, + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA, + DelphiAIDev.CodeCompletion.Vars, + DelphiAIDev.AI.Facade; + +type + IDelphiAIDevCodeCompletionSearch = interface + ['{5F8BDEE9-14DC-4C8C-BA7A-681A94844AD8}'] + procedure Process(const AContext: IOTAKeyContext); + end; + + TDelphiAIDevCodeCompletionSearch = class(TInterfacedObject, IDelphiAIDevCodeCompletionSearch) + private + FSettings: TDelphiAIDevSettings; + FQuestions: TStrings; + FAIRequest: TDelphiAIDevAIFacade; + FVars: TDelphiAIDevCodeCompletionVars; + FIOTAEditPosition: IOTAEditPosition; + procedure ProcessQuestions(const AContext: IOTAKeyContext); + procedure ProcessResponse; + protected + procedure Process(const AContext: IOTAKeyContext); + public + class function New: IDelphiAIDevCodeCompletionSearch; + constructor Create; + destructor Destroy; override; + end; + +implementation + +class function TDelphiAIDevCodeCompletionSearch.New: IDelphiAIDevCodeCompletionSearch; +begin + Result := Self.Create; +end; + +constructor TDelphiAIDevCodeCompletionSearch.Create; +begin + FSettings := TDelphiAIDevSettings.GetInstance; + FAIRequest := TDelphiAIDevAIFacade.Create; + FQuestions := TStringList.Create; + FVars := TDelphiAIDevCodeCompletionVars.GetInstance; +end; + +destructor TDelphiAIDevCodeCompletionSearch.Destroy; +begin + FQuestions.Free; + FAIRequest.Free; + inherited; +end; + +procedure TDelphiAIDevCodeCompletionSearch.Process(const AContext: IOTAKeyContext); +begin + FSettings.ValidateFillingSelectedAICodeCompletion(TShowMsg.No); + + Screen.Cursor := crHourGlass; + try + Self.ProcessQuestions(AContext); + + try + FAIRequest.AiUse(FSettings.CodeCompletionAIDefault).ProcessSend(FQuestions.Text); + except + Abort; + end; + + Self.ProcessResponse; + finally + Screen.Cursor := crDefault; + end; +end; + +procedure TDelphiAIDevCodeCompletionSearch.ProcessQuestions(const AContext: IOTAKeyContext); +begin + FQuestions.Clear; + FQuestions.Add(FSettings.LanguageQuestions.GetLanguageDefinition); + FQuestions.Add(FSettings.LanguageQuestions.GetMsgCodeCompletionSuggestion); + FQuestions.Add(FSettings.LanguageQuestions.GetMsgCodeOnly); + if not FSettings.CodeCompletionDefaultPrompt.Trim.IsEmpty then + FQuestions.Add(FSettings.CodeCompletionDefaultPrompt); + + FIOTAEditPosition := AContext.EditBuffer.EditPosition; + FIOTAEditPosition.InsertText(TConsts.TAG_CODE_COMPLETION); + try + FQuestions.Add(TUtilsOTA.GetSelectedBlockOrAllCodeUnit.Trim); + finally + FIOTAEditPosition.BackspaceDelete(TConsts.TAG_CODE_COMPLETION.Length); + end; +end; + +procedure TDelphiAIDevCodeCompletionSearch.ProcessResponse; +var + LRow: Integer; + LColumn: Integer; + LBlankTextLines: string; + i: Integer; +begin + if FAIRequest.Response.GetStatusCode <> 200 then + begin + TUtils.ShowMsg('Unable to perform AI request.', + Format('Code: %d %s Message: %s', [FAIRequest.Response.GetStatusCode, sLineBreak, FAIRequest.Response.GetContent.Text])); + Exit; + end; + + FVars.Module := TUtilsOTA.GetCurrentModule; + FVars.Contents.Text := TUtils.ConfReturnAI(FAIRequest.Response.GetContent.Text); + LRow := FIOTAEditPosition.Row; + LColumn := FIOTAEditPosition.Column; + + FVars.Row := LRow; + FVars.Column := LColumn; + FVars.LineIni := LRow; + FVars.LineEnd := FVars.LineIni + FVars.Contents.Count; + + LBlankTextLines := ''; + for i := 1 to Pred(FVars.Contents.Count) do + LBlankTextLines := LBlankTextLines + sLineBreak; + + FIOTAEditPosition.InsertText(LBlankTextLines); + FIOTAEditPosition.Move(FVars.LineIni, LColumn); +end; + +end. diff --git a/Src/CodeCompletion/DelphiAIDev.CodeCompletion.Vars.pas b/Src/CodeCompletion/DelphiAIDev.CodeCompletion.Vars.pas new file mode 100644 index 0000000..e4f13e6 --- /dev/null +++ b/Src/CodeCompletion/DelphiAIDev.CodeCompletion.Vars.pas @@ -0,0 +1,76 @@ +unit DelphiAIDev.CodeCompletion.Vars; + +interface + +uses + System.SysUtils, + System.Classes, + ToolsAPI; + +type + TDelphiAIDevCodeCompletionVars = class + private + FRelease: Boolean; + FModule: IOTAModule; + FLineIni: Integer; + FLineEnd: Integer; + FRow: Integer; + FColumn: Integer; + FContents: TStrings; + constructor Create; + public + class function GetInstance: TDelphiAIDevCodeCompletionVars; + destructor Destroy; override; + procedure Clear; + property Release: Boolean read FRelease write FRelease; + property Module: IOTAModule read FModule write FModule; + property LineIni: Integer read FLineIni write FLineIni; + property LineEnd: Integer read FLineEnd write FLineEnd; + property Row: Integer read FRow write FRow; + property Column: Integer read FColumn write FColumn; + property Contents: TStrings read FContents write FContents; + end; + +implementation + +var + Instance: TDelphiAIDevCodeCompletionVars; + +class function TDelphiAIDevCodeCompletionVars.GetInstance: TDelphiAIDevCodeCompletionVars; +begin + if not Assigned(Instance) then + Instance := Self.Create; + + Result := Instance; +end; + +constructor TDelphiAIDevCodeCompletionVars.Create; +begin + FContents := TStringList.Create; + Self.Clear; +end; + +destructor TDelphiAIDevCodeCompletionVars.Destroy; +begin + FContents.Free; + inherited; +end; + +procedure TDelphiAIDevCodeCompletionVars.Clear; +begin + FRelease := False; + FModule := nil; + FLineIni := 0; + FLineEnd := 0; + FRow := 0; + FColumn := 0; + FContents.Clear; +end; + +initialization + +finalization + if Assigned(Instance) then + FreeAndNil(Instance); + +end. diff --git a/Src/Conn/C4D.Conn.Configs.pas b/Src/Conn/C4D.Conn.Configs.pas new file mode 100644 index 0000000..48192cf --- /dev/null +++ b/Src/Conn/C4D.Conn.Configs.pas @@ -0,0 +1,184 @@ +unit C4D.Conn.Configs; + +interface + +uses + System.SysUtils, + System.Classes, + C4D.Conn.Types, + DelphiAIDev.Types; + +type + TC4DConnConfigs = class + private + FComponentConnection: TComponentConnection; + FDriverID: TC4DDriverID; + FHost: string; + FUserName: string; + FPassword: string; + FDatabase: string; + FPort: Integer; + FVendorLib: string; + procedure Clear; + public + constructor Create; + destructor Destroy; override; + function ComponentConnection: TComponentConnection; + function DriverID: TC4DDriverID; overload; + function DriverID(Value: TC4DDriverID): TC4DConnConfigs; overload; + function Host: string; overload; + function Host(Value: string): TC4DConnConfigs; overload; + function UserName: string; overload; + function UserName(Value: string): TC4DConnConfigs; overload; + function Password: string; overload; + function Password(Value: string): TC4DConnConfigs; overload; + function Database: string; overload; + function Database(Value: string): TC4DConnConfigs; overload; + function Port: Integer; overload; + function Port(Value: Integer): TC4DConnConfigs; overload; + function VendorLib: string; overload; + function VendorLib(Value: string): TC4DConnConfigs; overload; + function TestFieldFilling: TC4DConnConfigs; + end; + +implementation + +constructor TC4DConnConfigs.Create; +begin + Self.Clear; + + {$IFDEF C4D_ZEOS} + FComponentConnection := TComponentConnection.Zeos; + {$ELSE} + FComponentConnection := TComponentConnection.FireDac; + {$ENDIF} +end; + +destructor TC4DConnConfigs.Destroy; +begin + inherited; +end; + +procedure TC4DConnConfigs.Clear; +begin + FDriverID := TC4DDriverID.None;; + FHost := ''; + FUserName := ''; + FPassword := ''; + FDatabase := ''; + FPort := 0; + FVendorLib := ''; +end; + +function TC4DConnConfigs.ComponentConnection: TComponentConnection; +begin + if FComponentConnection = TComponentConnection.Empty then + raise Exception.Create('Component for connection to the bank not informed'); + + Result := FComponentConnection; +end; + +function TC4DConnConfigs.DriverID: TC4DDriverID; +begin + Result := FDriverID; +end; + +function TC4DConnConfigs.DriverID(Value: TC4DDriverID): TC4DConnConfigs; +begin + Result := Self; + FDriverID := Value; +end; + +function TC4DConnConfigs.Host: string; +begin + Result := FHost; +end; + +function TC4DConnConfigs.Host(Value: string): TC4DConnConfigs; +begin + Result := Self; + FHost := Value; +end; + +function TC4DConnConfigs.UserName: string; +begin + Result := FUserName; +end; + +function TC4DConnConfigs.UserName(Value: string): TC4DConnConfigs; +begin + Result := Self; + FUserName := Value; +end; + +function TC4DConnConfigs.Password: string; +begin + Result := FPassword; +end; + +function TC4DConnConfigs.Password(Value: string): TC4DConnConfigs; +begin + Result := Self; + FPassword := Value; +end; + +function TC4DConnConfigs.Database: string; +begin + Result := FDatabase; +end; + +function TC4DConnConfigs.Database(Value: string): TC4DConnConfigs; +begin + Result := Self; + FDatabase := Value; +end; + +function TC4DConnConfigs.Port: Integer; +begin + Result := FPort; +end; + +function TC4DConnConfigs.Port(Value: Integer): TC4DConnConfigs; +begin + Result := Self; + FPort := Value; +end; + +function TC4DConnConfigs.VendorLib: string; +begin + Result := FVendorLib; +end; + +function TC4DConnConfigs.VendorLib(Value: string): TC4DConnConfigs; +begin + Result := Self; + FVendorLib := Value; +end; + +function TC4DConnConfigs.TestFieldFilling: TC4DConnConfigs; +var + LTemp: string; +begin + Result := Self; + LTemp := ''; + + if FDriverID = TC4DDriverID.None then + LTemp := LTemp + 'Driver ID. '; + + if FHost.Trim.IsEmpty then + LTemp := LTemp + 'Host. '; + + if FUserName.Trim.IsEmpty then + LTemp := LTemp + 'User. '; + + if FPassword.Trim.IsEmpty then + LTemp := LTemp + 'Password. '; + + if FDatabase.Trim.IsEmpty then + LTemp := LTemp + 'Database name.'; + + if not LTemp.Trim.IsEmpty then + raise Exception.Create('To connect to the database, the following data must be filled in: ' + LTemp); +end; + +end. diff --git a/Src/Conn/C4D.Conn.Firedac.Query.pas b/Src/Conn/C4D.Conn.Firedac.Query.pas new file mode 100644 index 0000000..e998cc1 --- /dev/null +++ b/Src/Conn/C4D.Conn.Firedac.Query.pas @@ -0,0 +1,433 @@ +unit C4D.Conn.Firedac.Query; + +interface + +uses + System.SysUtils, + System.StrUtils, + System.Classes, + System.Generics.Collections, + Data.DB, + Firedac.Stan.Intf, + Firedac.Stan.Option, + Firedac.Stan.Param, + Firedac.Stan.Error, + Firedac.DatS, + Firedac.Phys.Intf, + Firedac.DApt.Intf, + Firedac.Stan.Async, + Firedac.DApt, + Firedac.Comp.DataSet, + Firedac.Comp.Client, + Firedac.Stan.Def, + C4D.Conn.Interfaces, + C4D.Conn.Firedac, + C4D.Conn.Utils; + +type + TC4DConnFiredacQuery = class(TInterfacedObject, IC4DConnQuery) + private + FC4DConnection: IC4DConnection; + FQuery: TFDQuery; + FListCond: TStringList; + FListCondParam: TDictionary; + FListGroup: TStringList; + FListOrder: TStringList; + FListLimit: TStringList; + procedure ProcessaListCond; + procedure ProcessaListGroup; + procedure ProcessaListOrder; + procedure ProcessListLimit; + protected + function Close: IC4DConnQuery; + function Clear: IC4DConnQuery; + function CloseClear: IC4DConnQuery; + function Add(Value: string): IC4DConnQuery; + function AddParam(Param: string; Value: Variant): IC4DConnQuery; + function AddCond(ACond: string): IC4DConnQuery; + function AddCondParam(Param: string; Value: Variant): IC4DConnQuery; + function AddGroup(ACond: string): IC4DConnQuery; + function AddOrder(ACond: string): IC4DConnQuery; + function AddLimit(ACond: string): IC4DConnQuery; + function Text(Value: string): IC4DConnQuery; + function SQL: TStrings; + function ExecSQL: IC4DConnQuery; overload; + function ExecSQL(const ASQL: string): IC4DConnQuery; overload; + function Open: IC4DConnQuery; overload; + function Open(const ASQL: string): IC4DConnQuery; overload; + + function DataSet: TDataSet; + function DataSource(Value: TDataSource): IC4DConnQuery; + function DataSourceMasterDetail(Value: TDataSource): IC4DConnQuery; + + function Append: IC4DConnQuery; + function Edit: IC4DConnQuery; + function Post: IC4DConnQuery; + function Delete: IC4DConnQuery; + + function Eof: Boolean; + function Bof: Boolean; + function Prior: IC4DConnQuery; + function Next: IC4DConnQuery; + function First: IC4DConnQuery; + function Last: IC4DConnQuery; + function IsEmpty: Boolean; + function RowsAffected: Integer; + function FieldByName(Value: string): TField; + function RecNo: Integer; + function RecordCount: Integer; + function RecordCountStr(ANumZerosLeft: Integer = 6): string; + function IndexFieldNames: string; overload; + function IndexFieldNames(Value: string): IC4DConnQuery; overload; + function GetLastAutoGenValue(const AName: string): Variant; + public + class function New(AC4DConnection: IC4DConnection; ANameQuery: string): IC4DConnQuery; + constructor Create(AC4DConnection: IC4DConnection; ANameQuery: string); + destructor Destroy; override; + end; + +implementation + +class function TC4DConnFiredacQuery.New(AC4DConnection: IC4DConnection; ANameQuery: string): IC4DConnQuery; +begin + Result := Self.Create(AC4DConnection, ANameQuery); +end; + +constructor TC4DConnFiredacQuery.Create(AC4DConnection: IC4DConnection; ANameQuery: string); +begin + FC4DConnection := AC4DConnection; + + FQuery := TFDQuery.Create(nil); + FQuery.Name := ANameQuery; + FQuery.FetchOptions.Mode := fmAll; + FQuery.Connection := TFDConnection(FC4DConnection.Component); + FQuery.Close; + FQuery.SQL.Clear; + + FListCond := TStringList.Create; + FListCondParam := TDictionary.Create; + FListGroup := TStringList.Create; + FListOrder := TStringList.Create; + FListLimit := TStringList.Create; +end; + +destructor TC4DConnFiredacQuery.Destroy; +begin + FListLimit.Free; + FListOrder.Free; + FListGroup.Free; + FListCondParam.Free; + FListCond.Free; + FreeAndNil(FQuery); + inherited; +end; + +function TC4DConnFiredacQuery.Close: IC4DConnQuery; +begin + Result := Self; + FQuery.Close; +end; + +function TC4DConnFiredacQuery.Clear: IC4DConnQuery; +begin + Result := Self; + FQuery.SQL.Clear; + FListCond.Clear; + FListCondParam.Clear; + FListLimit.Clear; + FListGroup.Clear; + FListOrder.Clear; +end; + +function TC4DConnFiredacQuery.CloseClear: IC4DConnQuery; +begin + Result := Self; + Self.Close; + Self.Clear; +end; + +function TC4DConnFiredacQuery.Add(Value: string): IC4DConnQuery; +begin + Result := Self; + FQuery.SQL.Add(Value); +end; + +function TC4DConnFiredacQuery.AddParam(Param: string; Value: Variant): IC4DConnQuery; +begin + Result := Self; + FQuery.ParamByName(Param).Value := Value; +end; + +function TC4DConnFiredacQuery.AddCond(ACond: string): IC4DConnQuery; +begin + Result := Self; + FListCond.Add(ACond); +end; + +function TC4DConnFiredacQuery.AddCondParam(Param: string; Value: Variant): IC4DConnQuery; +begin + Result := Self; + FListCondParam.Add(Param, Value); +end; + +function TC4DConnFiredacQuery.AddGroup(ACond: string): IC4DConnQuery; +begin + Result := Self; + FListGroup.Add(ACond); +end; + +function TC4DConnFiredacQuery.AddOrder(ACond: string): IC4DConnQuery; +begin + Result := Self; + FListOrder.Add(ACond); +end; + +function TC4DConnFiredacQuery.AddLimit(ACond: string): IC4DConnQuery; +begin + Result := Self; + FListLimit.Add(ACond); +end; + +function TC4DConnFiredacQuery.Text(Value: string): IC4DConnQuery; +begin + Result := Self; + FQuery.SQL.Text := Value; +end; + +function TC4DConnFiredacQuery.ExecSQL: IC4DConnQuery; +begin + Result := Self; + FQuery.ExecSQL; +end; + +function TC4DConnFiredacQuery.ExecSQL(const ASQL: string): IC4DConnQuery; +begin + Result := Self; + FQuery.ExecSQL(ASQL); +end; + +function TC4DConnFiredacQuery.Open: IC4DConnQuery; +begin + Result := Self; + Self.ProcessaListCond; + Self.ProcessaListGroup; + Self.ProcessaListOrder; + Self.ProcessListLimit; + FQuery.Open; +end; + +function TC4DConnFiredacQuery.Open(const ASQL: string): IC4DConnQuery; +begin + Result := Self; + FQuery.Close; + FQuery.SQL.Clear; + FQuery.SQL.Add(ASQL); + FQuery.Open; + //FQuery.Open(ASQL); +end; + +procedure TC4DConnFiredacQuery.ProcessaListCond; +var + i: Integer; + LKey: string; + LValue: Variant; + LCond: string; +begin + if FListCond.Count <= 0 then + Exit; + + LCond := ''; + for i := 0 to Pred(FListCond.Count)do + LCond := LCond + FListCond.Strings[i] + sLineBreak; + + LCond := TC4DConnUtils.SQLConfBusca(LCond); + FQuery.SQL.Add(LCond); + + for LKey in FListCondParam.Keys do + begin + if FListCondParam.TryGetValue(LKey, LValue) then + Self.AddParam(LKey, LValue); + end; +end; + +procedure TC4DConnFiredacQuery.ProcessaListGroup; +var + i: Integer; + LGroup: string; +begin + if FListGroup.Count <= 0 then + Exit; + + LGroup := ''; + for i := 0 to Pred(FListGroup.Count)do + LGroup := LGroup + FListGroup.Strings[i].Trim + ', '; + + if pos(LGroup, 'group by') <= 0 then + LGroup := 'group by ' + LGroup; + + LGroup := LGroup.Trim; + System.Delete(LGroup, LGroup.Length, 1); + FQuery.SQL.Add(LGroup); +end; + +procedure TC4DConnFiredacQuery.ProcessaListOrder; +var + i: Integer; + LOrdem: string; +begin + if FListOrder.Count <= 0 then + Exit; + + LOrdem := ''; + for i := 0 to Pred(FListOrder.Count)do + LOrdem := LOrdem + FListOrder.Strings[i].Trim + ', '; + + if pos(LOrdem, 'order by') <= 0 then + LOrdem := 'order by ' + LOrdem; + + LOrdem := LOrdem.Trim; + System.Delete(LOrdem, LOrdem.Length, 1); + FQuery.SQL.Add(LOrdem); +end; + +procedure TC4DConnFiredacQuery.ProcessListLimit; +var + i: Integer; +begin + for i := 0 to Pred(FListLimit.Count)do + FQuery.SQL.Add(FListLimit.Strings[i]); +end; + +function TC4DConnFiredacQuery.DataSet: TDataSet; +begin + Result := FQuery; +end; + +function TC4DConnFiredacQuery.DataSource(Value: TDataSource): IC4DConnQuery; +begin + Result := Self; + Value.DataSet := FQuery; +end; + +function TC4DConnFiredacQuery.DataSourceMasterDetail(Value: TDataSource): IC4DConnQuery; +begin + Result := Self; + FQuery.MasterSource := Value; +end; + +function TC4DConnFiredacQuery.Append: IC4DConnQuery; +begin + Result := Self; + FQuery.Append; +end; + +function TC4DConnFiredacQuery.Edit: IC4DConnQuery; +begin + Result := Self; + FQuery.Edit; +end; + +function TC4DConnFiredacQuery.Post: IC4DConnQuery; +begin + Result := Self; + FQuery.Post; +end; + +function TC4DConnFiredacQuery.Delete: IC4DConnQuery; +begin + Result := Self; + FQuery.Delete; +end; + +function TC4DConnFiredacQuery.Eof: Boolean; +begin + Result := FQuery.Eof; +end; + +function TC4DConnFiredacQuery.Bof: Boolean; +begin + Result := FQuery.Bof; +end; + +function TC4DConnFiredacQuery.Prior: IC4DConnQuery; +begin + Result := Self; + FQuery.Prior; +end; + +function TC4DConnFiredacQuery.Next: IC4DConnQuery; +begin + Result := Self; + FQuery.Next; +end; + +function TC4DConnFiredacQuery.First: IC4DConnQuery; +begin + Result := Self; + FQuery.First; +end; + +function TC4DConnFiredacQuery.Last: IC4DConnQuery; +begin + Result := Self; + FQuery.Last; +end; + +function TC4DConnFiredacQuery.IsEmpty: Boolean; +begin + Result := FQuery.IsEmpty; +end; + +function TC4DConnFiredacQuery.RowsAffected: Integer; +begin + Result := FQuery.RowsAffected; +end; + +function TC4DConnFiredacQuery.SQL: TStrings; +begin + Result := FQuery.SQL; +end; + +function TC4DConnFiredacQuery.FieldByName(Value: string): TField; +begin + Result := FQuery.FieldByName(Value); +end; + +function TC4DConnFiredacQuery.RecNo: Integer; +begin + Result := FQuery.RecNo; +end; + +function TC4DConnFiredacQuery.RecordCount: Integer; +begin + Result := FQuery.RecordCount; +end; + +function TC4DConnFiredacQuery.RecordCountStr(ANumZerosLeft: Integer = 6): string; +begin + Result := Format('%'+ ANumZerosLeft.ToString +'.'+ ANumZerosLeft.ToString +'d',[FQuery.RecordCount]); +end; + +function TC4DConnFiredacQuery.IndexFieldNames: string; +begin + Result := FQuery.IndexFieldNames; +end; + +function TC4DConnFiredacQuery.IndexFieldNames(Value: string): IC4DConnQuery; +begin + Result := Self; + FQuery.IndexFieldNames := Value; +end; + +//AName = PARA MYSQL E FIREDAC NOME DO CAMPO +function TC4DConnFiredacQuery.GetLastAutoGenValue(const AName: string): Variant; +begin + try + Result := FQuery.Connection.GetLastAutoGenValue(AName); + except + on E: Exception do + raise Exception.Create('Unable to retrieve the last code/id inserted into the database: ' + E.Message); + end; +end; + +end. diff --git a/Src/Conn/C4D.Conn.Firedac.pas b/Src/Conn/C4D.Conn.Firedac.pas new file mode 100644 index 0000000..c6ccf1a --- /dev/null +++ b/Src/Conn/C4D.Conn.Firedac.pas @@ -0,0 +1,203 @@ +unit C4D.Conn.Firedac; + +interface + +uses + {$IFNDEF CONSOLE} + Firedac.VCLUI.Wait, + {$ENDIF} + System.Classes, + System.SysUtils, + System.Generics.Collections, + Data.DB, + Firedac.Stan.Intf, + Firedac.Stan.Option, + Firedac.Stan.Error, + Firedac.UI.Intf, + Firedac.Phys.Intf, + Firedac.Stan.Def, + Firedac.Stan.Pool, + Firedac.Stan.Async, + Firedac.Phys, + Firedac.Comp.Client, + Firedac.DApt, + Firedac.Comp.UI, + //FIREBIRD + FireDAC.Phys.IBBase, + FireDAC.Phys.FB, + //MySQL + Firedac.Phys.MySQLDef, + Firedac.Phys.MySQL, + C4D.Conn.Interfaces, + C4D.Conn.Configs, + DelphiAIDev.Types, + DelphiAIDev.Utils; + +type + TC4DConnFiredac = class(TInterfacedObject, IC4DConnection) + private + [weak] + FC4DConnConfigs: TC4DConnConfigs; + FConnection: TFDConnection; + FMySQLDriverLink: TFDPhysMySQLDriverLink; + FFBDriverLink: TFDPhysFBDriverLink; + function TestFieldsComponentConnection: IC4DConnection; + procedure ConfigDrivers; + protected + function Component: TComponent; + function Open: IC4DConnection; + function Close: IC4DConnection; + function StartTransaction: IC4DConnection; + function Commit: IC4DConnection; + function Rollback: IC4DConnection; + function TestConnection: Boolean; + function LoadConnectionConfig: IC4DConnection; + public + class function New(AC4DConnConfigs: TC4DConnConfigs): IC4DConnection; + constructor Create(AC4DConnConfigs: TC4DConnConfigs); + destructor Destroy; override; + end; + +implementation + +class function TC4DConnFiredac.New(AC4DConnConfigs: TC4DConnConfigs): IC4DConnection; +begin + Result := Self.Create(AC4DConnConfigs); +end; + +constructor TC4DConnFiredac.Create(AC4DConnConfigs: TC4DConnConfigs); +begin + FC4DConnConfigs := AC4DConnConfigs; + FConnection := TFDConnection.Create(nil); + FConnection.LoginPrompt := False; + + FMySQLDriverLink := TFDPhysMySQLDriverLink.Create(nil); + FFBDriverLink := TFDPhysFBDriverLink.Create(nil); + + Self.LoadConnectionConfig; +end; + +destructor TC4DConnFiredac.Destroy; +begin + FFBDriverLink.Free; + FMySQLDriverLink.Free; + + FConnection.Close; + FreeAndNil(FConnection); + + inherited; +end; + +function TC4DConnFiredac.LoadConnectionConfig: IC4DConnection; +begin + Result := Self; + FConnection.Close; + FConnection.Params.Clear; + FConnection.Params.DriverID := FC4DConnConfigs.DriverID.ToStringID; //'MySQL'; + FConnection.Params.Database := FC4DConnConfigs.Database; + FConnection.Params.UserName := FC4DConnConfigs.UserName; + FConnection.Params.Password := FC4DConnConfigs.Password; + FConnection.Params.Add('Server=' + FC4DConnConfigs.Host); + if FC4DConnConfigs.Port > 0 then + FConnection.Params.Add('Port=' + FC4DConnConfigs.Port.ToString); + +// FConnection.TXOptions.AutoStart := True; +// FConnection.TXOptions.AutoStop := True; +// FConnection.TXOptions.AutoCommit := True; +// FConnection.TxOptions.StopOptions := [xoIfAutoStarted, xoFinishRetaining]; +// FConnection.UpdateOptions.AutoCommitUpdates := True; +// FConnection.UpdateOptions.RefreshMode := rmAll; + + Self.ConfigDrivers; +end; + +procedure TC4DConnFiredac.ConfigDrivers; +begin + FMySQLDriverLink.VendorLib := ''; + FFBDriverLink.VendorLib := ''; + + case FC4DConnConfigs.DriverID of + TC4DDriverID.MySQL: + FMySQLDriverLink.VendorLib := FC4DConnConfigs.VendorLib; + TC4DDriverID.Firebird: + FFBDriverLink.VendorLib := FC4DConnConfigs.VendorLib; + end; +end; + +function TC4DConnFiredac.Component: TComponent; +begin + Result := FConnection; +end; + +function TC4DConnFiredac.Open: IC4DConnection; +begin + Result := Self; + Self.TestFieldsComponentConnection; + FConnection.Open; +end; + +function TC4DConnFiredac.Close: IC4DConnection; +begin + Result := Self; + FConnection.Close; +end; + +function TC4DConnFiredac.StartTransaction: IC4DConnection; +begin + FConnection.StartTransaction; +end; + +function TC4DConnFiredac.Commit: IC4DConnection; +begin + FConnection.Commit; +end; + +function TC4DConnFiredac.Rollback: IC4DConnection; +begin + FConnection.Rollback; +end; + +function TC4DConnFiredac.TestConnection: Boolean; +var + LConnectedOld: Boolean; +begin + Self.TestFieldsComponentConnection; + try + Self.LoadConnectionConfig; + LConnectedOld := FConnection.Connected; + FConnection.Close; + FConnection.Open; + Result := FConnection.Connected; + + if FConnection.Connected <> LConnectedOld then + FConnection.Connected := LConnectedOld; + except + on E: exception do + raise exception.Create('Unsuccessful Connection! ' + sLineBreak + E.Message); + end; +end; + +function TC4DConnFiredac.TestFieldsComponentConnection: IC4DConnection; +var + LEmptyFields: string; +begin + Result := Self; + + LEmptyFields := ''; + if FC4DConnConfigs.Host.Trim.IsEmpty then + LEmptyFields := LEmptyFields + 'Host. '; + + if FC4DConnConfigs.UserName.Trim.IsEmpty then + LEmptyFields := LEmptyFields + 'UserName. '; + + if FC4DConnConfigs.Password.Trim.IsEmpty then + LEmptyFields := LEmptyFields + 'Password. '; + + if FC4DConnConfigs.Database.Trim.IsEmpty then + LEmptyFields := LEmptyFields + 'Database.'; + + if not LEmptyFields.Trim.IsEmpty then + raise exception.Create('To connect to the database, the following data must be filled in: ' + LEmptyFields); +end; + +end. diff --git a/Src/Conn/C4D.Conn.Interfaces.pas b/Src/Conn/C4D.Conn.Interfaces.pas new file mode 100644 index 0000000..bc5d018 --- /dev/null +++ b/Src/Conn/C4D.Conn.Interfaces.pas @@ -0,0 +1,82 @@ +unit C4D.Conn.Interfaces; + +interface + +uses + Data.DB, + System.Classes, + C4D.Conn.Configs; + +type + IC4DConnection = interface + ['{5B72EBFB-CE76-42F6-B716-226E5149F572}'] + function Component: TComponent; + function Open: IC4DConnection; + function Close: IC4DConnection; + function StartTransaction: IC4DConnection; + function Commit: IC4DConnection; + function Rollback: IC4DConnection; + + function TestConnection: Boolean; + function LoadConnectionConfig: IC4DConnection; + end; + + IC4DConnQuery = interface + ['{8C098AA7-288F-4458-AFDB-E44CC95E5441}'] + function Close: IC4DConnQuery; + function Clear: IC4DConnQuery; + function CloseClear: IC4DConnQuery; + function Add(Value: string): IC4DConnQuery; + function AddParam(Param: string; Value: Variant): IC4DConnQuery; + function AddCond(ACond: string): IC4DConnQuery; + function AddCondParam(Param: string; Value: Variant): IC4DConnQuery; + function AddGroup(ACond: string): IC4DConnQuery; + function AddOrder(ACond: string): IC4DConnQuery; + function AddLimit(ACond: string): IC4DConnQuery; + function Text(Value: string): IC4DConnQuery; + function SQL: TStrings; + function ExecSQL: IC4DConnQuery; overload; + function ExecSQL(const ASQL: string): IC4DConnQuery; overload; + function Open: IC4DConnQuery; overload; + function Open(const ASQL: string): IC4DConnQuery; overload; + + function DataSet: TDataSet; + function DataSource(Valeu: TDataSource): IC4DConnQuery; + function DataSourceMasterDetail(Value: TDataSource): IC4DConnQuery; + + function Append: IC4DConnQuery; + function Edit: IC4DConnQuery; + function Post: IC4DConnQuery; + function Delete: IC4DConnQuery; + + function Eof: Boolean; + function Bof: Boolean; + function Prior: IC4DConnQuery; + function Next: IC4DConnQuery; + function First: IC4DConnQuery; + function Last: IC4DConnQuery; + function IsEmpty: Boolean; + function RowsAffected: Integer; + function FieldByName(Value: string): TField; + function RecNo: Integer; + function RecordCount: Integer; + function RecordCountStr(ANumZerosLeft: Integer = 6): string; + function IndexFieldNames: string; overload; + function IndexFieldNames(Value: string): IC4DConnQuery; overload; + + //AName = PARA MYSQL E ZEOS NOME DA TABELA + //AName = PARA MYSQL E FIREDAC NOME DO CAMPO + function GetLastAutoGenValue(const AName: string): Variant; + end; + + IC4DConn = interface + ['{71342615-E414-4D66-9280-67218D5FB27A}'] + function Configs: TC4DConnConfigs; + function Connection: IC4DConnection; + function Query: IC4DConnQuery; overload; + function Query(const ANameQuery: string): IC4DConnQuery; overload; + end; + +implementation + +end. diff --git a/Src/Conn/C4D.Conn.Types.pas b/Src/Conn/C4D.Conn.Types.pas new file mode 100644 index 0000000..13f1d9d --- /dev/null +++ b/Src/Conn/C4D.Conn.Types.pas @@ -0,0 +1,12 @@ +unit C4D.Conn.Types; + +interface + +type + {$SCOPEDENUMS ON} + TComponentConnection = (Empty, FireDac {$IFDEF C4D_ZEOS}, Zeos {$ENDIF}); + {$SCOPEDENUMS OFF} + +implementation + +end. diff --git a/Src/Conn/C4D.Conn.Utils.pas b/Src/Conn/C4D.Conn.Utils.pas new file mode 100644 index 0000000..32dab91 --- /dev/null +++ b/Src/Conn/C4D.Conn.Utils.pas @@ -0,0 +1,57 @@ +unit C4D.Conn.Utils; + +interface + +uses + System.SysUtils, + System.StrUtils; + +type + TC4DConnUtils = class + public + class function SQLConfBusca(const ASql: string): string; + class function SQLTratar(const ASql: string): string; + end; + +implementation + +class function TC4DConnUtils.SQLConfBusca(const ASql: string): string; +begin + Result := ASql.Trim; + + if Result.IsEmpty then + Exit; + + if copy(Result, 1, 3) = 'and' then + begin + Delete(Result, 1, 3); + Result := 'where '+ Result; + end + else if copy(Result, 1, 2) = 'or' then + begin + Delete(Result, 1, 2); + Result := 'where '+ Result; + end + else if (copy(Result, 1, 5) <> 'where') + and(copy(Result, 1, 5) <> 'limit') + and(copy(Result, 1, 8) <> 'order by') + and(copy(Result, 1, 8) <> 'group by') + then + Result := 'where '+ Result; + + Result := Self.SQLTratar(Result) + ' '; +end; + +class function TC4DConnUtils.SQLTratar(const ASql: string): string; +begin + Result := ASql; + Result := StringReplace(Result, ' ALTER ', ' ALTERA ', [rfReplaceAll, rfIgnoreCase]); + Result := StringReplace(Result, 'ALTER TABLE', 'ALTERA TABLE', [rfReplaceAll, rfIgnoreCase]); + + Result := StringReplace(Result, ' DROP ', ' DROPE ', [rfReplaceAll, rfIgnoreCase]); + Result := StringReplace(Result, 'DROP TABLE', 'DROPE TABLE', [rfReplaceAll, rfIgnoreCase]); + Result := StringReplace(Result, 'DROP PROCEDURE', 'DROPE PROCEDURE', [rfReplaceAll, rfIgnoreCase]); + Result := StringReplace(Result, 'DROP FUNCTION', 'DROPE FUNCTION', [rfReplaceAll, rfIgnoreCase]); +end; + +end. diff --git a/Src/Conn/C4D.Conn.Zeos.Query.pas b/Src/Conn/C4D.Conn.Zeos.Query.pas new file mode 100644 index 0000000..434a5fb --- /dev/null +++ b/Src/Conn/C4D.Conn.Zeos.Query.pas @@ -0,0 +1,427 @@ +unit C4D.Conn.Zeos.Query; + +interface + +uses + System.SysUtils, + System.StrUtils, + System.Classes, + System.Generics.Collections, + ZAbstractConnection, + ZConnection, + Data.DB, + ZAbstractRODataset, + ZAbstractDataset, + ZDataset, + C4D.Conn.Interfaces, + C4D.Conn.Zeos, + C4D.Conn.Utils; + +type + TC4DConnZeosQuery = class(TInterfacedObject, IC4DConnQuery) + private + FC4DConnection: IC4DConnection; + FQuery: TZQuery; + FListCond: TStringList; + FListCondParam: TDictionary; + FListGroup: TStringList; + FListOrder: TStringList; + FListLimit: TStringList; + procedure ProcessaListCond; + procedure ProcessaListGroup; + procedure ProcessaListOrder; + procedure ProcessaListLimit; + protected + function Close: IC4DConnQuery; + function Clear: IC4DConnQuery; + function CloseClear: IC4DConnQuery; + function Add(Value: string): IC4DConnQuery; + function AddParam(Param: string; Value: Variant): IC4DConnQuery; + function AddCond(ACond: string): IC4DConnQuery; + function AddCondParam(Param: string; Value: Variant): IC4DConnQuery; + function AddGroup(ACond: string): IC4DConnQuery; + function AddOrder(ACond: string): IC4DConnQuery; + function AddLimit(ACond: string): IC4DConnQuery; + function Text(Value: string): IC4DConnQuery; + function SQL: TStrings; + function ExecSQL: IC4DConnQuery; overload; + function ExecSQL(const ASQL: string): IC4DConnQuery; overload; + function Open: IC4DConnQuery; overload; + function Open(const ASQL: string): IC4DConnQuery; overload; + + function DataSet: TDataSet; + function DataSource(Value: TDataSource): IC4DConnQuery; + function DataSourceMasterDetail(Value: TDataSource): IC4DConnQuery; + + function Append: IC4DConnQuery; + function Edit: IC4DConnQuery; + function Post: IC4DConnQuery; + function Delete: IC4DConnQuery; + + function Eof: Boolean; + function Bof: Boolean; + function Prior: IC4DConnQuery; + function Next: IC4DConnQuery; + function First: IC4DConnQuery; + function Last: IC4DConnQuery; + function IsEmpty: Boolean; + function RowsAffected: Integer; + function FieldByName(Value: string): TField; + function RecNo: Integer; + function RecordCount: Integer; + function RecordCountStr(ANumZerosLeft: Integer = 6): string; + function GetLastAutoGenValue(const AName: string): Variant; + public + class function New(AC4DConnection: IC4DConnection; ANameQuery: string): IC4DConnQuery; + constructor Create(AC4DConnection: IC4DConnection; ANameQuery: string); + destructor Destroy; override; + end; + +implementation + +class function TC4DConnZeosQuery.New(AC4DConnection: IC4DConnection; ANameQuery: string): IC4DConnQuery; +begin + Result := Self.Create(AC4DConnection, ANameQuery); +end; + +constructor TC4DConnZeosQuery.Create(AC4DConnection: IC4DConnection; ANameQuery: string); +begin + FC4DConnection := AC4DConnection; + + FQuery := TZQuery.Create(nil); + FQuery.Name := ANameQuery; + FQuery.Connection := TZConnection(FC4DConnection.Component); + + FQuery.Close; + FQuery.SQL.Clear; + + FListCond := TStringList.Create; + FListCondParam := TDictionary.Create; + FListGroup := TStringList.Create; + FListOrder := TStringList.Create; + FListLimit := TStringList.Create; +end; + +destructor TC4DConnZeosQuery.Destroy; +begin + FListLimit.Free; + FListOrder.Free; + FListGroup.Free; + FListCondParam.Free; + FListCond.Free; + FreeAndNil(FQuery); + inherited; +end; + +function TC4DConnZeosQuery.Close: IC4DConnQuery; +begin + Result := Self; + FQuery.Close; +end; + +function TC4DConnZeosQuery.Clear: IC4DConnQuery; +begin + Result := Self; + FQuery.SQL.Clear; + FListCond.Clear; + FListCondParam.Clear; + FListLimit.Clear; + FListGroup.Clear; + FListOrder.Clear; +end; + +function TC4DConnZeosQuery.CloseClear: IC4DConnQuery; +begin + Result := Self; + Self.Close; + Self.Clear; +end; + +function TC4DConnZeosQuery.Add(Value: string): IC4DConnQuery; +begin + Result := Self; + FQuery.SQL.Add(Value); +end; + +function TC4DConnZeosQuery.AddParam(Param: string; Value: Variant): IC4DConnQuery; +begin + Result := Self; + FQuery.ParamByName(Param).Value := Value; +end; + +function TC4DConnZeosQuery.AddCond(ACond: string): IC4DConnQuery; +begin + Result := Self; + FListCond.Add(ACond); +end; + +function TC4DConnZeosQuery.AddCondParam(Param: string; Value: Variant): IC4DConnQuery; +begin + Result := Self; + FListCondParam.Add(Param, Value); +end; + +function TC4DConnZeosQuery.AddGroup(ACond: string): IC4DConnQuery; +begin + Result := Self; + FListGroup.Add(ACond); +end; + +function TC4DConnZeosQuery.AddOrder(ACond: string): IC4DConnQuery; +begin + Result := Self; + FListOrder.Add(ACond); +end; + +function TC4DConnZeosQuery.AddLimit(ACond: string): IC4DConnQuery; +begin + Result := Self; + FListLimit.Add(ACond); +end; + +function TC4DConnZeosQuery.Text(Value: string): IC4DConnQuery; +begin + Result := Self; + FQuery.SQL.Text := Value; +end; + +function TC4DConnZeosQuery.ExecSQL: IC4DConnQuery; +begin + Result := Self; + FQuery.ExecSQL; +end; + +function TC4DConnZeosQuery.ExecSQL(const ASQL: string): IC4DConnQuery; +begin + Result := Self; + FQuery.Close; + FQuery.SQL.Clear; + FQuery.SQL.Add(ASQL); + FQuery.ExecSQL; +end; + +function TC4DConnZeosQuery.Open: IC4DConnQuery; +begin + Result := Self; + Self.ProcessaListCond; + Self.ProcessaListGroup; + Self.ProcessaListOrder; + Self.ProcessaListLimit; + FQuery.Open; +end; + +function TC4DConnZeosQuery.Open(const ASQL: string): IC4DConnQuery; +begin + Result := Self; + FQuery.Close; + FQuery.SQL.Clear; + FQuery.SQL.Add(ASQL); + FQuery.Open; +end; + +procedure TC4DConnZeosQuery.ProcessaListCond; +var + i: Integer; + LKey: string; + LValue: Variant; + LCond: string; +begin + if(FListCond.Count <= 0)then + Exit; + + LCond := ''; + for i := 0 to Pred(FListCond.Count)do + LCond := LCond + FListCond.Strings[i] + sLineBreak; + + LCond := TC4DConnUtils.SQLConfBusca(LCond); + FQuery.SQL.Add(LCond); + + for LKey in FListCondParam.Keys do + if FListCondParam.TryGetValue(LKey, LValue) then + Self.AddParam(LKey, LValue); +end; + +procedure TC4DConnZeosQuery.ProcessaListGroup; +var + i: Integer; + LGroup: string; +begin + if FListGroup.Count <= 0 then + Exit; + + LGroup := ''; + for i := 0 to Pred(FListGroup.Count)do + LGroup := LGroup + FListGroup.Strings[i].Trim + ', '; + + if pos('group by', LGroup) <= 0 then + LGroup := 'group by ' + LGroup; + + LGroup := LGroup.Trim; + System.Delete(LGroup, LGroup.Length, 1); + FQuery.SQL.Add(LGroup); +end; + +procedure TC4DConnZeosQuery.ProcessaListOrder; +var + i: Integer; + LOrdem: string; +begin + if FListOrder.Count <= 0 then + Exit; + + LOrdem := ''; + for i := 0 to Pred(FListOrder.Count)do + LOrdem := LOrdem + FListOrder.Strings[i].Trim + ', '; + + if pos('order by', LOrdem) <= 0 then + LOrdem := 'order by ' + LOrdem; + + LOrdem := LOrdem.Trim; + System.Delete(LOrdem, LOrdem.Length, 1); + FQuery.SQL.Add(LOrdem); +end; + +procedure TC4DConnZeosQuery.ProcessaListLimit; +var + i: Integer; +begin + for i := 0 to Pred(FListLimit.Count)do + FQuery.SQL.Add(FListLimit.Strings[i]); +end; + +function TC4DConnZeosQuery.DataSet: TDataSet; +begin + Result := FQuery; +end; + +function TC4DConnZeosQuery.DataSource(Value: TDataSource): IC4DConnQuery; +begin + Result := Self; + Value.DataSet := FQuery; +end; + +function TC4DConnZeosQuery.DataSourceMasterDetail(Value: TDataSource): IC4DConnQuery; +begin + Result := Self; + FQuery.DataSource := Value; +end; + +function TC4DConnZeosQuery.Append: IC4DConnQuery; +begin + Result := Self; + FQuery.Append; +end; + +function TC4DConnZeosQuery.Edit: IC4DConnQuery; +begin + Result := Self; + FQuery.Edit; +end; + +function TC4DConnZeosQuery.Post: IC4DConnQuery; +begin + Result := Self; + FQuery.Post; +end; + +function TC4DConnZeosQuery.Delete: IC4DConnQuery; +begin + Result := Self; + FQuery.Delete; +end; + +function TC4DConnZeosQuery.Eof: Boolean; +begin + Result := FQuery.Eof; +end; + +function TC4DConnZeosQuery.Bof: Boolean; +begin + Result := FQuery.Bof; +end; + +function TC4DConnZeosQuery.Prior: IC4DConnQuery; +begin + Result := Self; + FQuery.Prior; +end; + +function TC4DConnZeosQuery.Next: IC4DConnQuery; +begin + Result := Self; + FQuery.Next; +end; + +function TC4DConnZeosQuery.First: IC4DConnQuery; +begin + Result := Self; + FQuery.First; +end; + +function TC4DConnZeosQuery.Last: IC4DConnQuery; +begin + Result := Self; + FQuery.Last; +end; + +function TC4DConnZeosQuery.IsEmpty: Boolean; +begin + Result := FQuery.IsEmpty; +end; + +function TC4DConnZeosQuery.RowsAffected: Integer; +begin + Result := FQuery.RowsAffected +end; + +function TC4DConnZeosQuery.SQL: TStrings; +begin + Result := FQuery.SQL; +end; + +function TC4DConnZeosQuery.FieldByName(Value: string): TField; +begin + Result := FQuery.FieldByName(Value); +end; + +function TC4DConnZeosQuery.RecNo: Integer; +begin + Result := FQuery.RecNo; +end; + +function TC4DConnZeosQuery.RecordCount: Integer; +begin + Result := FQuery.RecordCount; +end; + +function TC4DConnZeosQuery.RecordCountStr(ANumZerosLeft: Integer = 6): string; +begin + Result := FQuery.RecordCount.ToString; +end; + +//AName = PARA MYSQL E ZEOS NOME DA TABELA +function TC4DConnZeosQuery.GetLastAutoGenValue(const AName: string): Variant; +var + LQuery: TZQuery; +begin + LQuery := TZQuery.Create(nil); + try + try + LQuery.Connection := FQuery.Connection; + LQuery.Close; + LQuery.SQL.Clear; + LQuery.SQL.Add('Select Last_Insert_ID() as Last_Insert_ID from ' + AName); + LQuery.Open; + + Result := LQuery.FieldByName('Last_Insert_ID').AsInteger; + LQuery.Close; + except + on E: Exception do + raise Exception.Create('Unable to retrieve the last code/id inserted into the database: ' + E.Message); + end; + finally + FreeAndNil(LQuery); + end; +end; + +end. diff --git a/Src/Conn/C4D.Conn.Zeos.pas b/Src/Conn/C4D.Conn.Zeos.pas new file mode 100644 index 0000000..eb411fd --- /dev/null +++ b/Src/Conn/C4D.Conn.Zeos.pas @@ -0,0 +1,160 @@ +unit C4D.Conn.Zeos; + +interface + +uses + System.Classes, + System.SysUtils, + System.Generics.Collections, + ZAbstractConnection, + ZConnection, + C4D.Conn.Interfaces, + C4D.Conn.Configs; + +type + TC4DConnZeos = class(TInterfacedObject, IC4DConnection) + private + [weak] + FC4DConnConfigs: TC4DConnConfigs; + FConnection: TZConnection; + function TestFieldsComponentConnection: IC4DConnection; + protected + function Component: TComponent; + function Open: IC4DConnection; + function Close: IC4DConnection; + function StartTransaction: IC4DConnection; + function Commit: IC4DConnection; + function Rollback: IC4DConnection; + function TestConnection: Boolean; + function TestConnectionOnly: Boolean; + function LoadConnectionConfig: IC4DConnection; + public + class function New(AC4DConnConfigs: TC4DConnConfigs): IC4DConnection; + constructor Create(AC4DConnConfigs: TC4DConnConfigs); + destructor Destroy; override; + end; + +implementation + +class function TC4DConnZeos.New(AC4DConnConfigs: TC4DConnConfigs): IC4DConnection; +begin + Result := Self.Create(AC4DConnConfigs); +end; + +constructor TC4DConnZeos.Create(AC4DConnConfigs: TC4DConnConfigs); +begin + FC4DConnConfigs := AC4DConnConfigs; + FConnection := TZConnection.Create(nil); + Self.LoadConnectionConfig; +end; + +destructor TC4DConnZeos.Destroy; +begin + FConnection.Connected := False; + FreeAndNil(FConnection); + + inherited; +end; + +function TC4DConnZeos.LoadConnectionConfig: IC4DConnection; +begin + Result := Self; + FConnection.Connected := False; + FConnection.Protocol := 'mysql'; + FConnection.Database := FC4DConnConfigs.Database; + FConnection.User := FC4DConnConfigs.UserName; + FConnection.Password := FC4DConnConfigs.Password; + FConnection.HostName := FC4DConnConfigs.Host; + FConnection.Port := FC4DConnConfigs.Port; + //FConnection.TransactIsolationLevel := tiReadCommitted; +end; + +function TC4DConnZeos.Component: TComponent; +begin + Result := FConnection; +end; + +function TC4DConnZeos.Open: IC4DConnection; +begin + Result := Self; + Self.TestFieldsComponentConnection; + FConnection.Connected := True; +end; + +function TC4DConnZeos.Close: IC4DConnection; +begin + Result := Self; + FConnection.Connected := False; +end; + +function TC4DConnZeos.StartTransaction: IC4DConnection; +begin + FConnection.StartTransaction; +end; + +function TC4DConnZeos.Commit: IC4DConnection; +begin + FConnection.Commit; +end; + +function TC4DConnZeos.Rollback: IC4DConnection; +begin + FConnection.Rollback; +end; + +function TC4DConnZeos.TestConnection: Boolean; +var + LConnectedOld: Boolean; +begin + Self.TestFieldsComponentConnection; + try + LConnectedOld := FConnection.Connected; + FConnection.Connected := True; + Result := FConnection.Connected; + if(FConnection.Connected <> LConnectedOld)then + FConnection.Connected := LConnectedOld; + except + on E: exception do + raise exception.Create('Conexo no pode ser realizada: ' + E.Message); + end; +end; + +function TC4DConnZeos.TestConnectionOnly: Boolean; +var + LConnectedOld: Boolean; +begin + Result := False; + try + LConnectedOld := FConnection.Connected; + FConnection.Connected := True; + Result := FConnection.Connected; + if(FConnection.Connected <> LConnectedOld)then + FConnection.Connected := LConnectedOld; + except + end; +end; + +function TC4DConnZeos.TestFieldsComponentConnection: IC4DConnection; +var + LEmptyFields: string; +begin + Result := Self; + LEmptyFields := ''; + + if FConnection.HostName.Trim.IsEmpty then + LEmptyFields := LEmptyFields + 'HostName. '; + + if FConnection.User.Trim.IsEmpty then + LEmptyFields := LEmptyFields + 'User. '; + + if FConnection.Password.Trim.IsEmpty then + LEmptyFields := LEmptyFields + 'Password. '; + + if FConnection.Database.Trim.IsEmpty then + LEmptyFields := LEmptyFields + 'Database.'; + + if not LEmptyFields.IsEmpty then + raise exception.Create('To connect to the database, the following data must be filled in: ' + LEmptyFields); +end; + +end. diff --git a/Src/Conn/C4D.Conn.pas b/Src/Conn/C4D.Conn.pas new file mode 100644 index 0000000..af36471 --- /dev/null +++ b/Src/Conn/C4D.Conn.pas @@ -0,0 +1,118 @@ +unit C4D.Conn; + +interface + +uses + System.SysUtils, + System.Classes, + C4D.Conn.Interfaces, + C4D.Conn.Types, + C4D.Conn.Configs, + {$IFDEF C4D_ZEOS} + C4D.Conn.Zeos, + C4D.Conn.Zeos.Query, + {$ENDIF} + C4D.Conn.Firedac, + C4D.Conn.Firedac.Query; + +type + IC4DConn = C4D.Conn.Interfaces.IC4DConn; + IC4DConnQuery = C4D.Conn.Interfaces.IC4DConnQuery; + + TC4DConn = class(TInterfacedObject, IC4DConn) + private + FC4DConnConfigs: TC4DConnConfigs; + FC4DConnInstance: IC4DConnection; + FCountNameQuery: Integer; + function CountNameQuery: Integer; + function QueryInternal(const ANameQuery: string): IC4DConnQuery; + protected + function Configs: TC4DConnConfigs; + function Connection: IC4DConnection; + function Query: IC4DConnQuery; overload; + function Query(const ANameQuery: string): IC4DConnQuery; overload; + public + class function New: IC4DConn; + constructor Create; + destructor Destroy; override; + end; + +//function C4DConn: IC4DConn; + +//var +// Instance: IC4DConn; + +implementation + +uses + DelphiAIDev.Utils; + +class function TC4DConn.New: IC4DConn; +begin + Result := Self.Create; +end; + +constructor TC4DConn.Create; +begin + FCountNameQuery := 0; + FC4DConnConfigs := TC4DConnConfigs.Create; +end; + +destructor TC4DConn.Destroy; +begin + FC4DConnConfigs.Free; + inherited; +end; + +//function C4DConn: IC4DConn; +//begin +// if not Assigned(Instance) then +// Instance := TC4DConn.New; +// +// Result := Instance; +//end; + +function TC4DConn.Configs: TC4DConnConfigs; +begin + Result := FC4DConnConfigs; +end; + +function TC4DConn.CountNameQuery: Integer; +begin + Inc(FCountNameQuery); + Result := FCountNameQuery; +end; + +function TC4DConn.Connection: IC4DConnection; +begin + {$IFDEF C4D_ZEOS} + if not Assigned(FC4DConnInstance) then + FC4DConnInstance := TC4DConnZeos.New(FC4DConnConfigs); + {$ELSE} + if not Assigned(FC4DConnInstance) then + FC4DConnInstance := TC4DConnFiredac.New(FC4DConnConfigs); + {$ENDIF} + + Result := FC4DConnInstance; +end; + +function TC4DConn.Query: IC4DConnQuery; +begin + Result := Self.QueryInternal('Query' + Self.CountNameQuery.ToString); +end; + +function TC4DConn.Query(const ANameQuery: string): IC4DConnQuery; +begin + Result := Self.QueryInternal(ANameQuery); +end; + +function TC4DConn.QueryInternal(const ANameQuery: string): IC4DConnQuery; +begin + {$IFDEF C4D_ZEOS} + Result := TC4DConnZeosQuery.New(Self.Connection, ANameQuery); + {$ELSE} + Result := TC4DConnFiredacQuery.New(Self.Connection, ANameQuery); + {$ENDIF} +end; + +end. diff --git a/Src/Consts/DelphiAIDev.Consts.pas b/Src/Consts/DelphiAIDev.Consts.pas index ea69fe7..c052748 100644 --- a/Src/Consts/DelphiAIDev.Consts.pas +++ b/Src/Consts/DelphiAIDev.Consts.pas @@ -5,40 +5,60 @@ interface type TConsts = class public const - SEMANTIC_VERSION = '1.8.0'; + SEMANTIC_VERSION = '2.4.0'; SEMANTIC_VERSION_LB = 'Version: ' + SEMANTIC_VERSION; WIN_CONTROL_FOCU_NIL = nil; DELPHI_AI_DEVELOPER_DPROJ = 'DelphiAIDeveloper.dproj'; DELPHI_AI_DEVELOPER_BPL = 'DelphiAIDeveloper.bpl'; C4D_PROJECT_GROUP1 = 'ProjectGroup1.groupproj'; NAME_FOLDER_TEMP = 'Temp'; + NAME_FOLDER_MetaInfo = 'MetaInfo'; GITHUB_Code4Delphi = 'https://github.com/Code4Delphi'; GITHUB_PROJECT = 'https://github.com/Code4Delphi/Delphi-AI-Developer'; + APPLICATION_JSON = 'application/json'; + SHORTCUT_CHAT_DEFAULT = 'Ctrl+Shift+Alt+A'; + CODE_COMPLETION_SUGGESTION_COLOR = $777777; + CODE_COMPLETION_SHORTCUT_INVOKE = 'Alt+Enter'; MARK_BEGIN_DELPHI = '```delphi'; MARK_BEGIN_PASCAL = '```objectpascal'; MARK_BEGIN_PASCAL2 = '``pascal'; + MARK_BEGIN_SQL = '```sql'; + MARK_BEGIN_SQL2 = '``sql'; MARK_END = '```'; + PREFIX_NAME_SEPARATOR = 'DelphiAIDevSeparator'; + TAG_CODE_COMPLETION = '//Suggestion_Code_Delphi'; //SETTINGS KEY_SETTINGS_IN_WINDOWS_REGISTRY = '\SOFTWARE\DelphiAIDeveloper'; BASE_URL_GEMINI_DEFAULT = 'https://generativelanguage.googleapis.com/'; MODEL_GEMINI_DEFAULT = 'v1/models/gemini-1.5-flash:generateContent'; BASE_URL_OPEN_AI = 'https://api.openai.com/v1/chat/completions/'; + MODEL_OPEN_AI_DEFAULT = 'gpt-4o-2024-08-06'; BASE_URL_GROQ = 'https://api.groq.com/openai/v1/chat/completions'; + MODEL_GROQ_DEFAULT = 'llama3-70b-8192'; //'llama3-8b-8192';; + + BASE_URL_Mistral = 'https://api.mistral.ai/v1/chat/completions'; + MODEL_Mistral_DEFAULT = 'mistral-small-latest'; + + BASE_URL_OLLAMA = 'http://localhost:11434/api/chat'; + MODEL_OLLAMA_DEFAULT = ''; //'tinyllama' 'mistral'; //NAMES FILES .INI FILE_INI_GENERAL_SETTINGS = 'delphi-ai-developer.ini'; //NAMES FILES .rtf FILE_RTF_CHAT = 'chat.rtf'; + FILE_RTF_CHAT_DB = 'chat_db.rtf'; - //NAMES FILE Database + //NAMES FILE JSON FILE_JSON_DEFAULTS_QUESTIONS = 'delphi_ai_developer_questions.json'; + FILE_JSON_DATABASES = 'delphi_ai_developer_databases.json'; + FILE_JSON_PROJECTS = 'delphi_ai_developer_projects.json'; //ABOUT AND SPLASH ABOUT_TITLE = 'Delphi AI Developer'; - ABOUT_COPY_RIGHT = 'Copyright 2024 Code4Delphi Team.'; + ABOUT_COPY_RIGHT = 'Copyright 2025 Code4Delphi Team.'; ABOUT_DESCRIPTION = 'Plugin designed to be used in the Delphi IDE.'; PLUGIN_LICENSE = 'MIT license'; IS_UNREGISTERED = False; @@ -50,6 +70,13 @@ TConsts = class ITEM_MENU_C4DDelphiAIDev_NAME = 'C4DDelphiAIDevItemMenu'; ITEM_MENU_C4DDelphiAIDev_CAPTION = 'AI Developer'; + //CODE4D-WIZARD INFORMATION + //ITEM_MENU_Code4D_CAPTION = 'Code4D'; + + //CAPTIONS ITENS POPUPMENU PROJ + ITEM_POPUP_MENU_PROJ_CAPTION = 'AI Developer'; + ITEM_POPUP_MENU_PROJ_EditInformation_CAPTION = 'Edit Information'; + //MAIN MENU IDE NAME AND CAPTIONS MENU_IDE_CHAT_NAME = 'DelphiAIDevChat1'; MENU_IDE_CHAT_CAPTION = 'Chat'; @@ -57,6 +84,11 @@ TConsts = class MENU_IDE_DEFAULTS_QUESTIONS_NAME = 'DelphiAIDevDefaultsQuestions1'; MENU_IDE_DEFAULTS_QUESTIONS_CAPTION = 'Defaults questions'; + MENU_IDE_DEFAULTS_DATABASES_ADD_NAME = 'DelphiAIDevDatabasesAdd1'; + MENU_IDE_DEFAULTS_DATABASES_ADD_CAPTION = 'Databases Registers'; + MENU_IDE_DEFAULTS_DATABASES_CHAT_NAME = 'DelphiAIDevDatabasesChat1'; + MENU_IDE_DEFAULTS_DATABASES_CHAT_CAPTION = 'Databases Chat'; + MENU_IDE_CHAT_SETTINGS_NAME = 'DelphiAIDevSettings1'; MENU_IDE_CHAT_SETTINGS_CAPTION = 'Settings'; MENU_IDE_SETTINGS_NAME = 'C4DWizarSettings1'; diff --git a/Src/DB/Chat/DelphiAIDev.DB.Chat.View.dfm b/Src/DB/Chat/DelphiAIDev.DB.Chat.View.dfm new file mode 100644 index 0000000..2fd2bc6 --- /dev/null +++ b/Src/DB/Chat/DelphiAIDev.DB.Chat.View.dfm @@ -0,0 +1,1044 @@ +object DelphiAIDevDBChatView: TDelphiAIDevDBChatView + Left = 0 + Top = 0 + BorderIcons = [biSystemMenu, biMaximize] + Caption = 'AI DB Chat' + ClientHeight = 661 + ClientWidth = 975 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + KeyPreview = True + Position = poScreenCenter + ShowHint = True + OnActivate = FormActivate + OnClose = FormClose + OnResize = FormResize + OnShow = FormShow + DesignSize = ( + 975 + 661) + TextHeight = 13 + object pnBack: TPanel + Left = 0 + Top = 0 + Width = 956 + Height = 661 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alClient + BevelEdges = [] + BevelOuter = bvNone + Padding.Left = 18 + Padding.Top = 15 + ParentBackground = False + TabOrder = 0 + object Splitter1: TSplitter + Left = 18 + Top = 153 + Width = 938 + Height = 3 + Cursor = crVSplit + Align = alTop + ExplicitLeft = 0 + ExplicitTop = 2 + ExplicitWidth = 528 + end + object Splitter2: TSplitter + Left = 18 + Top = 498 + Width = 938 + Height = 3 + Cursor = crVSplit + Align = alBottom + ExplicitLeft = 12 + ExplicitTop = 340 + end + object mmReturn: TRichEdit + AlignWithMargins = True + Left = 18 + Top = 183 + Width = 938 + Height = 285 + Hint = 'Response returned' + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + ParentCustomHint = False + Align = alClient + BevelInner = bvNone + BevelOuter = bvNone + Ctl3D = True + Font.Charset = ANSI_CHARSET + Font.Color = clWindow + Font.Height = -13 + Font.Name = 'Courier New' + Font.Style = [] + Lines.Strings = ( + 'iii' + 'www') + ParentCtl3D = False + ParentFont = False + ParentShowHint = False + PopupMenu = pMenuMemoReturn + ScrollBars = ssVertical + ShowHint = True + TabOrder = 1 + end + object pnBackQuestion: TPanel + Left = 18 + Top = 15 + Width = 938 + Height = 138 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alTop + BevelOuter = bvNone + Padding.Top = 5 + ParentBackground = False + TabOrder = 0 + object Label2: TLabel + Left = 0 + Top = 5 + Width = 938 + Height = 17 + Align = alTop + AutoSize = False + Caption = 'Question / prompt' + end + object mmQuestion: TMemo + AlignWithMargins = True + Left = 0 + Top = 22 + Width = 938 + Height = 116 + Hint = 'Insert question' + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alClient + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -13 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + TabOrder = 0 + OnChange = mmQuestionChange + OnKeyDown = mmQuestionKeyDown + OnKeyUp = mmQuestionKeyUp + end + end + object pnBackButtonsSearch: TPanel + Left = 18 + Top = 156 + Width = 938 + Height = 27 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alTop + BevelOuter = bvNone + Padding.Top = 3 + Padding.Bottom = 2 + ParentBackground = False + TabOrder = 2 + object lbCurrentAI: TLabel + Left = 0 + Top = 3 + Width = 56 + Height = 22 + Cursor = crHandPoint + Hint = 'AI being used' + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alLeft + Caption = 'lbCurrentAI' + PopupMenu = pMenuCurrentAI + OnClick = lbCurrentAIClick + ExplicitHeight = 13 + end + object btnSend: TButton + AlignWithMargins = True + Left = 864 + Top = 3 + Width = 74 + Height = 22 + Cursor = crHandPoint + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alRight + Caption = 'Send' + TabOrder = 0 + OnClick = btnSendClick + end + object pnBackConfigurableButtons: TPanel + Left = 395 + Top = 3 + Width = 466 + Height = 22 + Align = alRight + BevelOuter = bvNone + TabOrder = 1 + object btnUseCurrentUnitCode: TButton + AlignWithMargins = True + Left = 261 + Top = 0 + Width = 205 + Height = 22 + Cursor = crHandPoint + Hint = 'Use data from current unit in query' + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alRight + Caption = '* Use current unit code in query * ' + ImageIndex = 0 + Images = ImageList1 + TabOrder = 0 + OnClick = btnUseCurrentUnitCodeClick + end + object btnCodeOnly: TButton + AlignWithMargins = True + Left = 149 + Top = 0 + Width = 109 + Height = 22 + Cursor = crHandPoint + Hint = 'Return only code without comments or explanations' + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alRight + Caption = '* SQL only *' + ImageIndex = 3 + Images = ImageList1 + TabOrder = 1 + OnClick = btnCodeOnlyClick + end + object btnDefaultsQuestions: TButton + AlignWithMargins = True + Left = 56 + Top = 0 + Width = 90 + Height = 22 + Cursor = crHandPoint + Hint = 'Defaults Questions' + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alRight + Caption = '*Questions*' + ImageIndex = 4 + Images = ImageList1 + PopupMenu = pMenuQuestions + TabOrder = 2 + OnClick = btnDefaultsQuestionsClick + end + end + end + object pnGridBack: TPanel + Left = 18 + Top = 501 + Width = 938 + Height = 160 + Align = alBottom + BevelOuter = bvNone + ParentBackground = False + TabOrder = 3 + object DBGrid1: TDBGrid + Left = 0 + Top = 0 + Width = 938 + Height = 133 + Align = alClient + BorderStyle = bsNone + DataSource = DataSource1 + Options = [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit, dgTitleClick, dgTitleHotTrack] + PopupMenu = pMenuGrid + TabOrder = 0 + TitleFont.Charset = DEFAULT_CHARSET + TitleFont.Color = clWindowText + TitleFont.Height = -11 + TitleFont.Name = 'Tahoma' + TitleFont.Style = [] + OnDrawColumnCell = DBGrid1DrawColumnCell + OnTitleClick = DBGrid1TitleClick + end + object Panel9: TPanel + Left = 0 + Top = 133 + Width = 938 + Height = 27 + Align = alBottom + BevelOuter = bvNone + Padding.Left = 3 + Padding.Top = 3 + Padding.Right = 3 + Padding.Bottom = 3 + ParentBackground = False + TabOrder = 1 + object lbCount: TLabel + Left = 899 + Top = 3 + Width = 36 + Height = 21 + Align = alRight + Caption = '000000' + Layout = tlCenter + ExplicitHeight = 13 + end + object Label3: TLabel + Left = 863 + Top = 3 + Width = 36 + Height = 21 + Align = alRight + Caption = 'Count: ' + Layout = tlCenter + ExplicitHeight = 13 + end + end + end + object Panel1: TPanel + Left = 18 + Top = 471 + Width = 938 + Height = 27 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alBottom + BevelOuter = bvNone + Padding.Bottom = 2 + ParentBackground = False + TabOrder = 4 + object Label1: TLabel + Left = 337 + Top = 0 + Width = 85 + Height = 25 + Align = alLeft + Caption = ' Last generation: ' + Layout = tlCenter + ExplicitHeight = 13 + end + object lbLastGeneration: TLabel + AlignWithMargins = True + Left = 422 + Top = 0 + Width = 81 + Height = 25 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 5 + Margins.Bottom = 0 + Align = alLeft + Caption = 'lbLastGeneration' + Layout = tlCenter + ExplicitHeight = 13 + end + object btnExecuteSQL: TButton + AlignWithMargins = True + Left = 825 + Top = 0 + Width = 113 + Height = 25 + Cursor = crHandPoint + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alRight + Caption = 'Execute SQL' + ImageIndex = 5 + ImageMargins.Left = 5 + Images = ImageList1 + TabOrder = 0 + OnClick = btnExecuteSQLClick + end + object cBoxDatabases: TComboBox + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 331 + Height = 21 + Align = alLeft + Style = csDropDownList + TabOrder = 1 + OnClick = cBoxDatabasesClick + end + object btnGenerateDatabaseReference: TButton + AlignWithMargins = True + Left = 508 + Top = 0 + Width = 61 + Height = 25 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Bottom = 0 + Align = alLeft + Caption = 'Generate' + TabOrder = 2 + OnClick = btnGenerateDatabaseReferenceClick + end + end + end + object pnWait: TPanel + Left = 413 + Top = 208 + Width = 125 + Height = 35 + BevelOuter = bvNone + BiDiMode = bdLeftToRight + Caption = 'Wait for loading...' + ParentBiDiMode = False + ParentBackground = False + TabOrder = 1 + Visible = False + object ShapeWait: TShape + Left = 0 + Top = 0 + Width = 125 + Height = 35 + Align = alClient + Brush.Color = 16770222 + Pen.Color = 12615680 + Pen.Style = psInsideFrame + Pen.Width = 2 + ExplicitLeft = 32 + ExplicitTop = 19 + ExplicitWidth = 228 + ExplicitHeight = 49 + end + object pnWaitCaption: TPanel + Left = 0 + Top = 0 + Width = 125 + Height = 35 + Align = alClient + BevelOuter = bvNone + Caption = 'Wait for loading...' + TabOrder = 0 + end + end + object StatusBar1: TStatusBar + Left = 956 + Top = 0 + Width = 19 + Height = 661 + Align = alRight + Panels = < + item + Width = 50 + end> + end + object pnCommands: TPanel + Left = 947 + Top = 174 + Width = 26 + Height = 115 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Anchors = [akTop, akRight] + BevelOuter = bvNone + ParentBackground = False + TabOrder = 3 + DesignSize = ( + 26 + 115) + object ShapeCommands: TShape + Left = 0 + Top = 0 + Width = 26 + Height = 115 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Anchors = [akLeft, akTop, akRight, akBottom] + Brush.Style = bsClear + Pen.Color = clGray + Pen.Style = psInsideFrame + ExplicitHeight = 73 + end + object btnCopy: TSpeedButton + AlignWithMargins = True + Left = 0 + Top = 46 + Width = 26 + Height = 23 + Cursor = crHandPoint + Hint = 'Copy' + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alTop + Flat = True + Glyph.Data = { + 36030000424D3603000000000000360000002800000010000000100000000100 + 1800000000000003000000000000000000000000000000000000FF00FFFF00FF + FF00FFFF00FFFF00FFB58C8C8C5A5A8C5A5A8C5A5A8C5A5A8C5A5A8C5A5A8C5A + 5A8C5A5A8C5A5AFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58C8CFFF7E7F7 + EFDEF7EFDEF7EFDEF7EFDEF7EFDEF7EFDEF7E7CE8C5A5AFF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFB58C8CF7EFDEF7DECEF7DEC6F7DEC6F7DEC6F7DEC6EFDE + CEEFDECE8C5A5AFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58C8CFFF7E7FF + D6A5FFD6A5FFD6A5FFD6A5FFD6A5FFD6A5EFDECE8C5A5AFF00FFFF00FFB58C8C + 8C5A5A8C5A5A8C5A5AB58C8CFFF7EFF7DEC6F7DEC6F7DEC6F7DEC6F7DEBDF7E7 + CEEFDECE9C6B63FF00FFFF00FFB58C8CFFF7E7F7EFDEF7EFDEB58C8CFFF7EFF7 + E7CEF7DEC6F7DEC6F7DEC6F7DEC6F7E7D6EFDECE9C6B6BFF00FFFF00FFB58C8C + F7EFDEF7DECEF7DEC6B58C8CFFFFF7FFD6A5FFD6A5FFD6A5FFD6A5FFD6A5FFD6 + A5EFE7D6A57B73FF00FFFF00FFB58C8CFFF7E7FFD6A5FFD6A5B58C8CFFFFF7FF + E7D6FFE7D6F7E7D6F7E7CEFFE7D6FFF7E7EFDEDEA57B73FF00FFFF00FFB58C8C + FFF7EFF7DEC6F7DEC6B58C8CFFFFFFFFFFFFFFFFFFFFFFF7FFFFF7EFDEDED6C6 + C6BDADADB58473FF00FFFF00FFB58C8CFFF7EFF7E7CEF7DEC6B58C8CFFFFFFFF + FFFFFFFFFFFFFFF7FFFFF7B58C8CB58C8CB58C8CB58C8CFF00FFFF00FFB58C8C + FFFFF7FFD6A5FFD6A5B58C8CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB58C8CEFB5 + 6BC68C7BFF00FFFF00FFFF00FFB58C8CFFFFF7FFE7D6FFE7D6B58C8CB58C8CB5 + 8C8CB58C8CB58C8CB58C8CB58C8CBD8484FF00FFFF00FFFF00FFFF00FFB58C8C + FFFFFFFFFFFFFFFFFFFFFFF7FFFFF7EFDEDED6C6C6BDADADB58473FF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FFB58C8CFFFFFFFFFFFFFFFFFFFFFFF7FFFFF7B5 + 8C8CB58C8CB58C8CB58C8CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58C8C + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB58C8CEFB56BC68C7BFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FFB58C8CB58C8CB58C8CB58C8CB58C8CB58C8CB5 + 8C8CBD8484FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF} + OnClick = btnCopyClick + ExplicitLeft = -8 + ExplicitTop = 55 + end + object btnInsertAtCursor: TSpeedButton + AlignWithMargins = True + Left = 0 + Top = 0 + Width = 26 + Height = 23 + Cursor = crHandPoint + Hint = 'Insert Selected Text at Cursor' + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alTop + Flat = True + Glyph.Data = { + 8E010000424D8E010000000000008E0000002800000010000000100000000100 + 08000000000000010000120B0000120B0000160000001600000000000000FFFF + FF00FF00FF00811E0000BD4C0000FFFBF800FFE9D300FFF6ED00FEE0C000FEE3 + C500FEE6CB00FFF0E000FFF3E600FEDEB800FEEAD100FEEED900FFF7ED00FEFB + F700FFFAF200FFFEFB00004B000031C758000202020202020202020202020202 + 02020404040404040402020202020202020211100B0E090D0402020202020202 + 020213120C0F0A08040202020202020202020105070B06090402020202020202 + 0202030303030303040202020202020202020202020202020202140204030303 + 03030202020202020214140204100B0E090D0202020202021415140204120C0F + 0A08020202020202021414020405070B06090202020202020202140204030303 + 03030303030303030402020202020202020211100B0E090D0402020202020202 + 020213120C0F0A08040202020202020202020105070B06090402020202020202 + 020204040404040404020202020202020202} + OnClick = btnInsertAtCursorClick + end + object btnMoreActions: TSpeedButton + AlignWithMargins = True + Left = 0 + Top = 92 + Width = 26 + Height = 23 + Cursor = crHandPoint + Hint = 'More actions...' + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alTop + Caption = '...' + Flat = True + PopupMenu = pMenuMoreActions + OnClick = btnMoreActionsClick + ExplicitTop = 46 + end + object btnCreateNewUnit: TSpeedButton + AlignWithMargins = True + Left = 0 + Top = 23 + Width = 26 + Height = 23 + Cursor = crHandPoint + Hint = 'Create new unit with selected code' + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alTop + Flat = True + Glyph.Data = { + 36030000424D3603000000000000360000002800000010000000100000000100 + 1800000000000003000000000000000000000000000000000000FF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFAD7384B58484B58484B58484B5 + 8484B58484B58484B58484B58484B58484B58484B58484FF00FFFF00FFFF00FF + FF00FFAD7384FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFB58484FF00FFFF00FFFF00FFFF00FFAD7384FFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB58484FF00FFFF00FFFF00FF + FF00FFAD7384FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFB58484FF00FFFF00FFFF00FFFF00FFAD7384FFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB58484FF00FFFF00FFFF00FF + FF00FFAD7384FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFB58484FF00FFFF00FFFF00FFFF00FFAD7384FFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB58484FF00FFFF00FFFF00FF + FF00FFAD7384FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFB58484FF00FFFF00FFFF00FF3184FF3184FF3184FFF7F7F7F7F7F7F7 + F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7B58484FF00FFFF00FFFF00FF + 3184FF42B5F73184FFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEF + EFEFEFEFB58484FF00FF3184FF3184FF3184FF42B5F73184FF3184FF3184FFE7 + E7E7E7E7E7E7E7E7E7E7E7B58473B5948CB58C84B58484FF00FF3184FF8CD6F7 + B5DEF7B5DEF7B5DEF78CD6F73184FFDEDEDEDEDEDEDEDEDEC6C6C6B58473FFFF + FFFFFFFFB58484FF00FF3184FF3184FF3184FFB5DEF73184FF3184FF3184FFD6 + D6D6D6D6D6D6D6D6C6C6C6B58473FFFFFFB58484FF00FFFF00FFFF00FFFF00FF + 3184FFB5DEF73184FFCECECECECECECECECECECECECECECEC6C6C6B58473B584 + 84FF00FFFF00FFFF00FFFF00FFFF00FF3184FF3184FF3184FFEFD6C6EFD6C6EF + D6C6EFD6C6EFD6C6D6BDB5B58473FF00FFFF00FFFF00FFFF00FF} + OnClick = btnCreateNewUnitClick + end + object btnCleanAll: TSpeedButton + AlignWithMargins = True + Left = 0 + Top = 69 + Width = 26 + Height = 23 + Cursor = crHandPoint + Hint = 'Clean all and start a new chat' + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alTop + Flat = True + Glyph.Data = { + 1A020000424D1A020000000000001A0100002800000010000000100000000100 + 08000000000000010000210B0000210B00003900000039000000EA8F3100B66A + 5000AB735800C07B5800EAA15800FFB65800AB6A6000B67B6000C0846000A16A + 6A00A1736A00C0846A00A17B7300B68F7B00EAC07B00F4C07B00B68F8400EAC0 + 8400F4C08400F4CA8400B6988F00C0988F00F4CA8F00C0A19800CAA19800B6AB + 9800F4CA9800F4D49800CAABA100D4ABA100D4B6A100E0B6A100EAB6A100EAC0 + A100F4CAA100F4D4A100FFD4A100EAC0AB00F4D4AB00E0D4B600F4D4B600F4E0 + B600E0C0C000E0CAC000F4E0C000E0CACA00F4E0CA00FFE0CA00FFEACA00FFEA + D400FFEAE000FFF4E000FFF4EA00FFF4F400FFFFF400FF00FF00FFFFFF003737 + 0909090909090909090909090937373710302926231A16110E0E0E1309373737 + 10302C2826221611110E0E110937373714322E2C2826221A11110E1109373737 + 14332E2C292823221A11110E093737371736322E2E2C2826221A111109373737 + 183834322E2C2928261A1616093737371C383534312E2C292826221A09373737 + 1C38383534322E2C28262323093737371D3838383532312E2C28282209373737 + 1E3838383835323131302719093737371F383838383834342E0D0C0A09373737 + 1F383838383838362A0204000137373725383838383838382B07050337373737 + 1F353434343434342A070B37373737371F212121211F1F211C0637373737} + OnClick = btnCleanAllClick + ExplicitTop = 92 + end + end + object ImageList1: TImageList + Left = 96 + Top = 200 + Bitmap = { + 494C010106003000040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 + 0000000000003600000028000000400000002000000001002000000000000020 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000F0CAA600F0CAA600C0C0C000C0C0C000CCCC9900F0CAA6000000 + 000000000000000000000000000000000000000000FF000000FF000000FF0000 + 00FF000000FF000000FF000000FF000000FF000000FF00660000003300000033 + 00000033000000660000000000FF000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000F0CA + A600D7D7D700E3E3E300E3E3E300CBCBCB00C0C0C000C0C0C000B2B2B200A4A0 + A000F0CAA600000000000000000000000000000000FF000000FF000000FF0066 + 9900006699000066990000669900006699000066000000800000009900000099 + 0000008000000066000000330000000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000F0CAA600EAEA + EA00F0FBFF00F0CAA600CC666600CC663300CC663300CC666600CC999900B2B2 + B200A4A0A000CC9999000000000000000000000000FF000000FF0066990033CC + FF0000CCFF0033CCFF0066CCFF0000663300009933000099330099CC99003399 + 3300009900000099000000660000006600000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000F0CAA600F1F1F100FFFF + FF00CC996600CC330000CC333300FFCCCC00F0CAA600CC330000CC330000CC66 + 6600B2B2B200B2B2B200F0CAA60000000000000000FF0066990066FFFF0033CC + FF0000CCFF0033CCFF0066CCFF000099000000CC330000993300FFFFFF00FFFF + FF0033CC66000099000000990000006600000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000E3E3E300FFFFFF00CC99 + 6600CC330000CC333300CC663300CC999900CC999900CC333300CC333300CC33 + 0000CC996600C0C0C000C0C0C00000000000000000FF0066990066FFFF0033CC + FF0000CCFF0033CCFF0066CCFF000099000033CC660000993300FFFFFF00FFFF + FF00FFFFFF0033CC330000990000006600000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000F0CAA600FFFFFF00FFECCC00CC66 + 3300CC333300CC333300CC663300CC999900CC996600CC333300CC663300CC33 + 3300CC663300B2B2B200CBCBCB00F0CAA600000000FF0066990066FFFF0033CC + FF0000CCFF0033CCFF0066CCFF000099000066CC990000993300FFFFFF00FFFF + FF0033CC66000099000000990000006600000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000FFCCCC00FFFFFF00CC999900CC66 + 3300CC663300CC663300CC663300F8F8F800EAEAEA00CC663300CC330000CC66 + 3300CC330000CC996600D7D7D700F0CAA600000000FF0066990099FFFF0099FF + FF00CCFFFF00CCFFFF00F0FBFF003399330066CC990066CC660099CC990033CC + 6600009933000099330000993300006600000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000FFCCCC00FFFFFF00CC996600CC66 + 3300CC663300CC663300CC663300F0CAA600FFFFFF00F0CAA600CC663300CC33 + 0000CC330000CC666600EAEAEA00F0CAA600000000FF0066990099FFFF003399 + CC000099CC000099CC000099CC00008080000099000099CC990066CC990066CC + 660033CC66000099330000993300000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000FFCCCC00FFFFFF00CC996600CC66 + 3300CC663300CC663300CC663300CC663300F0CAA600FFFFFF00FFECCC00CC66 + 3300CC330000CC666600F1F1F100F0CAA600000000FF0066990033CCCC0033CC + FF0000CCFF0033CCFF0066CCFF0066CCFF003399990000993300009900000099 + 00000099000000993300000000FF000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000FFCCCC00FFFFFF00F0CAA600CC66 + 3300CC993300CC996600CC663300CC663300CC663300F0CAA600FFFFFF00CC99 + 6600CC330000CC996600F0FBFF00F0CAA600000000FF0066990066FFFF0033CC + FF0000CCFF0033CCFF0066CCFF0066CCFF003399CC000099CC00006699000000 + 00FF000000FF000000FF000000FF000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000FFCCCC00F8F8F800EAEAEA00CC99 + 6600F0CAA600FFFFFF00CC996600CC663300CC663300CC996600FFFFFF00CC99 + 9900CC663300FFCCCC00F1F1F100F0CAA600000000FF0066990066FFFF0033CC + FF0000CCFF0033CCFF0066CCFF0066CCFF003399CC000099CC00006699000000 + 00FF000000FF000000FF000000FF000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000E3E3E300FFFFFF00F0CA + A600FF999900FFFFFF00F8F8F800F0CAA600F0CAA600F8F8F800F8F8F800CC66 + 3300CC996600FFFFFF00FFCCCC0000000000000000FF0066990066FFFF0033CC + FF0000CCFF0033CCFF0066CCFF0066CCFF003399CC000099CC00006699000000 + 00FF000000FF000000FF000000FF000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000F0CAA600F1F1F100FFFF + FF00F0CAA600F0CAA600FFECCC00F8F8F800F8F8F800FFECCC00CC996600CC99 + 9900FFFFFF00EAEAEA00FFCCCC0000000000000000FF0066990066FFFF0066FF + FF0066FFFF0066FFFF0066FFFF0099FFFF0033CCFF000099CC00006699000000 + 00FF000000FF000000FF000000FF000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000F0CAA600F1F1 + F100FFFFFF00FFECCC00F0CAA600F0CAA600F0CAA600F0CAA600FFCCCC00FFFF + FF00EAEAEA00F0CAA6000000000000000000000000FF00669900FFFFFF00F0FB + FF00CCFFFF0099FFFF0099FFFF0099FFFF0099FFFF0066FFFF00006699000000 + 00FF000000FF000000FF000000FF000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000F0CA + A600EAEAEA00F1F1F100FFFFFF00FFFFFF00FFFFFF00F8F8F800F8F8F800E3E3 + E300E3E3E300000000000000000000000000000000FF000000FF00669900F0FB + FF00CCFFFF00CCFFFF0099FFFF0099FFFF0099FFFF0000669900000000FF0000 + 00FF000000FF000000FF000000FF000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000F0CAA600F0CAA600FFCCCC00FFCCCC00F0CAA600F0CAA6000000 + 000000000000000000000000000000000000000000FF000000FF000000FF0066 + 99000066990000669900006699000066990000669900000000FF000000FF0000 + 00FF000000FF000000FF000000FF000000FF0000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000999999009999 + 9900999999009999990000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000555555005555 + 5500555555005555550000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000009696 + 9600969696009696960000000000000000000000000099999900E3E3E300CCCC + CC00C0C0C000C0C0C00096969600000000000000000000000000000000004D4D + 4D004D4D4D004D4D4D0000000000000000000000000055555500CBCBCB00A4A0 + A00096969600969696004D4D4D00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000666666006666 + 6600000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000006600000066 + 0000000000000000000000000000000000000000000000000000969696009999 + 9900A4A0A000B2B2B2009696960096969600000000009999990096969600CBCB + CB00B2B2B200A4A0A000868686000000000000000000000000004D4D4D005555 + 550066666600777777004D4D4D004D4D4D0000000000555555004D4D4D009999 + 9900808080006666660042424200000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000066666600777777007777 + 7700000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000660000008000000066 + 0000000000000000000000000000000000000000000096969600C0C0C0009999 + 9900B2B2B200A4A0A000B2B2B200B2B2B20096969600CCCCCC00CBCBCB009696 + 960096969600868686000000000000000000000000004D4D4D00868686005555 + 5500777777005F5F5F0066996600777777004D4D4D00A4A0A000999999004D4D + 4D004D4D4D003939390000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000006666660086868600868686007777 + 7700666666000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000066000000800000009900000080 + 00000033000000000000000000000000000096969600C0C0C000E3E3E3009999 + 9900B2B2B200A4A0A000A4A0A000B2B2B20096969600B2B2B200CBCBCB00D7D7 + D700D7D7D700D7D7D700B2B2B200868686004D4D4D0096969600C0C0C0005555 + 5500777777005F5F5F0033993300666699004D4D4D00FF990000FFCC3300FFCC + 6600FFCC6600CC99990077777700393939000000000000000000000000000000 + 0000000000000000000000000000666666009696960096969600808080008686 + 8600666666000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000006600000099330000990000008000000080 + 00000066000000000000000000000000000096969600EAEAEA00D7D7D7009696 + 9600B2B2B200B2B2B200A4A0A000969696009696960099999900A4A0A000C0C0 + C000C0C0C000C0C0C000C0C0C000868686004D4D4D00CCCCCC00B2B2B2005555 + 550077777700777777006666990033669900006699003366660099993300FF99 + 3300FF993300FF993300FF993300424242000000000000000000000000000000 + 0000000000000000000077777700A4A0A000A4A0A00077777700666666008686 + 8600808080000000000000000000000000000000000000000000000000000000 + 0000000000000000000000660000009933000099330000660000003300000080 + 00000080000000000000000000000000000096969600EAEAEA00D7D7D7008686 + 8600999999009999990096969600A4A0A000B2B2B200A4A0A00096969600B2B2 + B200CCCCCC00CBCBCB00B2B2B200868686004D4D4D00CCCCCC00B2B2B2000033 + 99003366660033666600006699000066CC003399FF000066CC00006699009999 + 6600FFCC6600FFCC3300FF993300393939000000000000000000000000000000 + 0000000000000000000000000000868686007777770000000000000000007777 + 7700969696006666660000000000000000000000000000000000000000000000 + 0000000000000000000000000000009933000066000000000000000000000066 + 00000099000000660000000000000000000096969600EAEAEA00D7D7D7008686 + 86009696960099999900C0C0C000B2B2B20096969600A4A0A000A4A0A0008686 + 8600C0C0C000D7D7D700C0C0C000808080004D4D4D00CCCCCC00B2B2B2000033 + 99000066CC000066CC003399FF003399CC000066990066666600336699000033 + 990086868600FFCC6600FF993300333333000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000868686008080800000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000080000000660000000000000000000096969600EAEAEA00D7D7D7008686 + 8600A4A0A000B2B2B200B2B2B2009696960096969600CCCCCC00DDDDDD00C0C0 + C00099999900D7D7D700CBCBCB00777777004D4D4D00D7D7D700B2B2B2000033 + 99000066FF000099FF003366CC00336666004D4D4D00FFCC6600FFCC99006699 + 990033669900FFCC6600FFCC3300292929000000000000000000333333003333 + 3300000000000000000000000000333333003333330000000000000000000000 + 00003333330033333300333333000000000000000000000000000000FF000000 + FF000000000000000000000000000000FF000000FF0000000000000000000000 + 00000000FF000000FF000000FF000000000096969600F1F1F100DDDDDD008686 + 8600A4A0A000B2B2B200A4A0A0009696960096969600B2B2B200C0C0C000CBCB + CB00C0C0C000CCCCCC00CBCBCB00777777004D4D4D00DDDDDD00C0C0C0000033 + 99000066FF000099FF000066CC00336699004D4D4D008080800099996600A4A0 + A00099996600CCCC6600CC996600222222000000000033333300000000000000 + 0000333333000000000033333300000000000000000033333300000000003333 + 330000000000808080003333330000000000000000000000FF00000000000000 + 00000000FF00000000000000FF0000000000000000000000FF00000000000000 + FF0000000000006600000000FF000000000096969600FFFFFF00E3E3E3008686 + 860086868600868686008686860086868600B2B2B20096969600969696009696 + 9600969696009696960080808000000000004D4D4D00F1F1F100CBCBCB000033 + 990000339900003399000033990000339900666699004D4D4D004D4D4D004D4D + 4D004D4D4D004D4D4D0039393900000000000000000033333300000000000000 + 0000000000000000000033333300000000000000000033333300000000003333 + 330000000000000000003333330000000000000000000000FF00000000000000 + 000000000000000000000000FF0000000000000000000000FF00000000000000 + FF0000000000000000000000FF000000000096969600F1F1F100C0C0C0009999 + 9900CCCCCC0099999900A4A0A000B2B2B200C0C0C00096969600000000000000 + 0000000000000000000000000000000000004D4D4D00DDDDDD00969696005555 + 5500A4A0A000555555005F5F5F0077777700969696004D4D4D00000000000000 + 0000000000000000000000000000000000000000000033333300000000000000 + 0000333333000000000033333300000000000000000033333300000000003333 + 330000000000000000003333330066666600000000000000FF00000000000000 + 00000000FF00000000000000FF0000000000000000000000FF00000000000000 + FF0000000000000000000000FF000033000096969600CCCCCC00F1F1F100C0C0 + C000B2B2B200B2B2B200C0C0C000A4A0A000A4A0A00096969600000000000000 + 0000000000000000000000000000000000004D4D4D00A4A0A000DDDDDD009696 + 9600777777008080800096969600666666005F5F5F004D4D4D00000000000000 + 0000000000000000000000000000000000000000000000000000333333003333 + 3300000000000000000000000000333333003333330000000000000000000000 + 00003333330033333300333333006666660000000000000000000000FF000000 + FF000000000000000000000000000000FF000000FF0000000000000000000000 + 00000000FF000000FF000000FF000033000096969600FFFFFF00FFFFFF00F8F8 + F800DDDDDD00CCCCCC00C0C0C000C0C0C000C0C0C00096969600000000000000 + 0000000000000000000000000000000000004D4D4D00FFFFFF00F1F1F100E3E3 + E300C0C0C000A4A0A0008686860080808000808080004D4D4D00000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000033333300000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000FF0000000000A4A0A00096969600FFFFFF00FFFF + FF00FFFFFF00EAEAEA00CCCCCC00969696009696960000000000000000000000 + 000000000000000000000000000000000000666666004D4D4D00FFFFFF00FFFF + FF00F8F8F800D7D7D700A4A0A0004D4D4D004D4D4D0000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000033333300000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000FF00000000000000000000000000969696009696 + 9600969696009696960096969600000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000004D4D4D004D4D + 4D004D4D4D004D4D4D004D4D4D00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000424D3E000000000000003E000000 + 2800000040000000200000000100010000000000000100000000000000000000 + 000000000000000000000000FFFFFF00F81FFF8300000000E007E00100000000 + C003C00000000000800180000000000080018000000000000000800000000000 + 0000800000000000000080010000000000008003000000000000801F00000000 + 0000801F000000008001801F000000008001801F00000000C003801F00000000 + E007C03F00000000F81FE07F00000000FFC3FFC3FFFFFFFFE381E381FFCFFFCF + C081C081FF8FFF8F80038003FF07FF0700000000FE07FE0700000000FC07FC07 + 00000000FE63FE6300000000FFF3FFF300000000CE71CE7100000000B5A9B5A9 + 00010001BDADBDAD003F003FB5ACB5AC003F003FCE70CE70003F003FFFFDFFFD + 007F007FFFFDFFFDC1FFC1FFFFFFFFFF00000000000000000000000000000000 + 000000000000} + end + object pMenuMemoReturn: TPopupMenu + Left = 120 + Top = 248 + object Cut1: TMenuItem + Caption = 'Cut' + ShortCut = 16472 + OnClick = Cut1Click + end + object Copy1: TMenuItem + Caption = 'Copy' + ShortCut = 16451 + OnClick = Copy1Click + end + object Paste1: TMenuItem + Caption = 'Paste' + ShortCut = 16470 + OnClick = Paste1Click + end + object N2: TMenuItem + Caption = '-' + end + object WordWrap1: TMenuItem + AutoCheck = True + Caption = 'WordWrap' + OnClick = WordWrap1Click + end + object N1: TMenuItem + Caption = '-' + end + object SelectAll1: TMenuItem + Caption = 'Select all' + ShortCut = 16449 + OnClick = SelectAll1Click + end + object Clear1: TMenuItem + Caption = 'Clear' + ShortCut = 16430 + OnClick = Clear1Click + end + end + object pMenuCurrentAI: TPopupMenu + OnPopup = pMenuCurrentAIPopup + Left = 26 + Top = 231 + object Gemini1: TMenuItem + Caption = 'Gemini' + OnClick = Gemini1Click + end + object ChatGPT1: TMenuItem + Tag = 1 + Caption = 'ChatGPT' + OnClick = Gemini1Click + end + object Groq1: TMenuItem + Tag = 2 + Caption = 'Groq' + OnClick = Gemini1Click + end + object Mistral1: TMenuItem + Tag = 3 + Caption = 'Mistral' + OnClick = Gemini1Click + end + object Ollama1: TMenuItem + Tag = 4 + Caption = 'Ollama (offline)' + OnClick = Gemini1Click + end + end + object pMenuMoreActions: TPopupMenu + Images = ImageList1 + Left = 824 + Top = 40 + object SaveContentToFile1: TMenuItem + Caption = 'Save content to file' + OnClick = SaveContentToFile1Click + end + end + object pMenuQuestions: TPopupMenu + Left = 530 + Top = 531 + end + object DataSource1: TDataSource + Left = 170 + Top = 464 + end + object pMenuGrid: TPopupMenu + Left = 794 + Top = 472 + object CopyCurrentColumn1: TMenuItem + Caption = 'Copy current column' + ShortCut = 16451 + OnClick = CopyCurrentColumn1Click + end + object CopyCurrentLine1: TMenuItem + Caption = 'Copy current line' + OnClick = CopyCurrentLine1Click + end + object CopyAllGridData: TMenuItem + Caption = 'Copy all grid data' + OnClick = CopyAllGridDataClick + end + object N3: TMenuItem + Caption = '-' + end + object SaveAllGridDataAsCSV: TMenuItem + Caption = 'Save all grid data as CSV' + OnClick = SaveAllGridDataAsCSVClick + end + object SaveAllGridDataAsTXT: TMenuItem + Caption = 'Save all grid data as TXT' + OnClick = SaveAllGridDataAsTXTClick + end + end +end diff --git a/Src/DB/Chat/DelphiAIDev.DB.Chat.View.pas b/Src/DB/Chat/DelphiAIDev.DB.Chat.View.pas new file mode 100644 index 0000000..22f5e27 --- /dev/null +++ b/Src/DB/Chat/DelphiAIDev.DB.Chat.View.pas @@ -0,0 +1,944 @@ +unit DelphiAIDev.DB.Chat.View; + +interface + +uses + System.SysUtils, + System.StrUtils, + System.Variants, + System.Classes, + System.JSON, + System.Threading, + Vcl.Graphics, + Vcl.Controls, + Vcl.Forms, + Vcl.Dialogs, + System.ImageList, + Vcl.ImgList, + DockForm, + Vcl.StdCtrls, + Vcl.ExtCtrls, + Vcl.ComCtrls, + Vcl.Menus, + Vcl.Buttons, + Winapi.Windows, + Winapi.Messages, + Clipbrd, + DelphiAIDev.Types, + DelphiAIDev.Consts, + DelphiAIDev.AI.Facade, + DelphiAIDev.Settings, + DelphiAIDev.ModuleCreator, + DelphiAIDev.DefaultsQuestions.PopupMenu, + DelphiAIDev.Chat.ProcessResponse, + Data.DB, + Vcl.Grids, + Vcl.DBGrids, + DelphiAIDev.DB.Registers.Model, + DelphiAIDev.DB.Registers.Fields, + C4D.Conn, + DelphiAIDev.Utils.DBGrids, + DelphiAIDev.Projects.Model, + DelphiAIDev.DB.References.View; + +type + TDelphiAIDevDBChatView = class(TDockableForm) + ImageList1: TImageList; + pMenuMemoReturn: TPopupMenu; + Cut1: TMenuItem; + Copy1: TMenuItem; + Paste1: TMenuItem; + SelectAll1: TMenuItem; + pnBack: TPanel; + pnBackQuestion: TPanel; + mmQuestion: TMemo; + N1: TMenuItem; + mmReturn: TRichEdit; + Splitter1: TSplitter; + pnWait: TPanel; + ShapeWait: TShape; + pnWaitCaption: TPanel; + pMenuCurrentAI: TPopupMenu; + Gemini1: TMenuItem; + ChatGPT1: TMenuItem; + pnBackButtonsSearch: TPanel; + lbCurrentAI: TLabel; + StatusBar1: TStatusBar; + pnCommands: TPanel; + btnCopy: TSpeedButton; + btnInsertAtCursor: TSpeedButton; + btnMoreActions: TSpeedButton; + ShapeCommands: TShape; + btnSend: TButton; + pMenuMoreActions: TPopupMenu; + SaveContentToFile1: TMenuItem; + btnCreateNewUnit: TSpeedButton; + Clear1: TMenuItem; + N2: TMenuItem; + WordWrap1: TMenuItem; + pnBackConfigurableButtons: TPanel; + btnUseCurrentUnitCode: TButton; + btnCodeOnly: TButton; + btnDefaultsQuestions: TButton; + pMenuQuestions: TPopupMenu; + btnCleanAll: TSpeedButton; + Groq1: TMenuItem; + Mistral1: TMenuItem; + pnGridBack: TPanel; + DBGrid1: TDBGrid; + Splitter2: TSplitter; + DataSource1: TDataSource; + pMenuGrid: TPopupMenu; + CopyCurrentColumn1: TMenuItem; + CopyCurrentLine1: TMenuItem; + CopyAllGridData: TMenuItem; + N3: TMenuItem; + SaveAllGridDataAsCSV: TMenuItem; + SaveAllGridDataAsTXT: TMenuItem; + Panel1: TPanel; + btnExecuteSQL: TButton; + cBoxDatabases: TComboBox; + Label1: TLabel; + lbLastGeneration: TLabel; + Panel9: TPanel; + lbCount: TLabel; + Label3: TLabel; + Ollama1: TMenuItem; + btnGenerateDatabaseReference: TButton; + Label2: TLabel; + procedure FormShow(Sender: TObject); + procedure cBoxSizeFontKeyPress(Sender: TObject; var Key: Char); + procedure Cut1Click(Sender: TObject); + procedure Copy1Click(Sender: TObject); + procedure Paste1Click(Sender: TObject); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + procedure btnSendClick(Sender: TObject); + procedure mmQuestionKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure btnCopyClick(Sender: TObject); + procedure btnInsertAtCursorClick(Sender: TObject); + procedure SelectAll1Click(Sender: TObject); + procedure mmQuestionChange(Sender: TObject); + procedure mmQuestionKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure FormActivate(Sender: TObject); + procedure lbCurrentAIClick(Sender: TObject); + procedure Gemini1Click(Sender: TObject); + procedure pMenuCurrentAIPopup(Sender: TObject); + procedure btnMoreActionsClick(Sender: TObject); + procedure SaveContentToFile1Click(Sender: TObject); + procedure btnCreateNewUnitClick(Sender: TObject); + procedure btnUseCurrentUnitCodeClick(Sender: TObject); + procedure FormResize(Sender: TObject); + procedure WordWrap1Click(Sender: TObject); + procedure btnCodeOnlyClick(Sender: TObject); + procedure btnDefaultsQuestionsClick(Sender: TObject); + procedure Clear1Click(Sender: TObject); + procedure btnCleanAllClick(Sender: TObject); + procedure btnExecuteSQLClick(Sender: TObject); + procedure cBoxDatabasesClick(Sender: TObject); + procedure DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); + procedure CopyCurrentColumn1Click(Sender: TObject); + procedure CopyCurrentLine1Click(Sender: TObject); + procedure CopyAllGridDataClick(Sender: TObject); + procedure SaveAllGridDataAsCSVClick(Sender: TObject); + procedure SaveAllGridDataAsTXTClick(Sender: TObject); + procedure DBGrid1TitleClick(Column: TColumn); + procedure btnGenerateDatabaseReferenceClick(Sender: TObject); + private + FAI: TDelphiAIDevAIFacade; + FSettings: TDelphiAIDevSettings; + FProcessResponse: TDelphiAIDevChatProcessResponse; + FPopupMenuQuestions: TDelphiAIDevDefaultsQuestionsPopupMenu; + FbtnUseCurrentUnitCodeWidth: Integer; + FbtnCodeOnlyWidth: Integer; + FbtnDefaultsQuestionsWidth: Integer; + FQuestionOnShow: string; + FConn: IC4DConn; + FQueryExecuteSQL: IC4DConnQuery; + procedure FillMemoReturnWithFile; + procedure SaveMemoReturnInFile; + procedure InitializeRichEditReturn; + procedure ProcessSend; + procedure AddResponseSimple(const AString: string); + procedure Last; + function GetSelectedTextOrAllFromReturn: string; + function GetSelectedTextOrAllOrAbort: string; + procedure WaitingFormOFF; + procedure WaitingFormON; + procedure ConfLabelCurrentAI; + procedure ConfScreenOnShow; + procedure ChangeUseCurrentUnitCode; + procedure ChangeCodeOnly; + procedure AddItemsPopupMenuQuestion; + procedure DoProcessClickInItemDefaultQuestions(ACodeOnly: Boolean; AQuestion: string); + procedure ProcessWordWrap; + procedure ConfScreenOnCreate; + procedure ReloadDatabases; + procedure FillDateLastReferences; + function GetFieldDBSelected: TDelphiAIDevDBRegistersFields; + function GetJsonDatabase: string; + procedure HandleErrorExecutingSQLCommand(const E: Exception); + procedure ValidateIfDatabaseSelected; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + property QuestionOnShow: string write FQuestionOnShow; + end; + +var + DelphiAIDevDBChatView: TDelphiAIDevDBChatView; + +procedure RegisterSelf; +procedure Unregister; +procedure DelphiAIDevDBChatViewShowDockableForm; + +implementation + +uses + DeskUtil, + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA, + DelphiAIDev.DB.Utils; + +{$R *.dfm} + +const + UseCurrentUnitCode_ImageIndex_OFF = 0; + UseCurrentUnitCode_ImageIndex_ON = 1; + CodeOnly_ImageIndex_OFF = 2; + CodeOnly_ImageIndex_ON = 3; + +procedure RegisterSelf; +begin + if not Assigned(DelphiAIDevDBChatView) then + DelphiAIDevDBChatView := TDelphiAIDevDBChatView.Create(nil); + + if @RegisterFieldAddress <> nil then + RegisterFieldAddress(DelphiAIDevDBChatView.Name, @DelphiAIDevDBChatView); + + RegisterDesktopFormClass(TDelphiAIDevDBChatView, DelphiAIDevDBChatView.Name, DelphiAIDevDBChatView.Name); +end; + +procedure Unregister; +begin + if @UnRegisterFieldAddress <> nil then + UnRegisterFieldAddress(@DelphiAIDevDBChatView); + FreeAndNil(DelphiAIDevDBChatView); +end; + +procedure DelphiAIDevDBChatViewShowDockableForm; +begin + ShowDockableForm(DelphiAIDevDBChatView); + FocusWindow(DelphiAIDevDBChatView); +end; + +constructor TDelphiAIDevDBChatView.Create(AOwner: TComponent); +begin + inherited; + DeskSection := Self.Name; + AutoSave := True; + SaveStateNecessary := True; + + FAI := TDelphiAIDevAIFacade.Create; + FSettings := TDelphiAIDevSettings.GetInstance; + FProcessResponse := TDelphiAIDevChatProcessResponse.Create(mmReturn); + FPopupMenuQuestions := TDelphiAIDevDefaultsQuestionsPopupMenu.Create; + FQuestionOnShow := ''; + + FConn := TC4DConn.New; + FQueryExecuteSQL := FConn.Query.DataSource(DataSource1); + + Self.ConfScreenOnCreate; + Self.FillMemoReturnWithFile; + TUtilsDBGrids.ConfDBGrid(DBGrid1); +end; + +destructor TDelphiAIDevDBChatView.Destroy; +begin + Self.SaveMemoReturnInFile; + FPopupMenuQuestions.Free; + FProcessResponse.Free; + FAI.Free; + inherited; +end; + +procedure TDelphiAIDevDBChatView.FormShow(Sender: TObject); +begin + Self.ConfScreenOnShow; + Self.InitializeRichEditReturn; + Self.ProcessWordWrap; + Self.AddItemsPopupMenuQuestion; + TUtils.MemoFocusOnTheEnd(mmQuestion); + Self.ReloadDatabases; +end; + +procedure TDelphiAIDevDBChatView.FormActivate(Sender: TObject); +begin + Self.ConfLabelCurrentAI; + + if not FQuestionOnShow.Trim.IsEmpty then + begin + mmQuestion.Lines.Clear; + mmQuestion.Lines.Add(FQuestionOnShow); + FQuestionOnShow := ''; + end; +end; + +procedure TDelphiAIDevDBChatView.FormClose(Sender: TObject; var Action: TCloseAction); +begin + Self.WaitingFormOFF; +end; + +procedure TDelphiAIDevDBChatView.DBGrid1DrawColumnCell(Sender: TObject; + const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); +begin + TUtilsDBGrids.DrawColumnCell(TDBGrid(Sender), Rect, DataCol, Column, Vcl.Grids.TGridDrawState(State)); +end; + +procedure TDelphiAIDevDBChatView.ConfScreenOnCreate; +begin + mmReturn.Lines.Clear; + + pnWait.Visible := False; + FbtnUseCurrentUnitCodeWidth := btnUseCurrentUnitCode.Width; + FbtnCodeOnlyWidth := btnCodeOnly.Width; + FbtnDefaultsQuestionsWidth := btnDefaultsQuestions.Width; + + ShapeCommands.Left := 0; + ShapeCommands.Top := 0; + ShapeCommands.Width := ShapeCommands.Parent.Width; + ShapeCommands.Height := ShapeCommands.Parent.Height; + + {$IF CompilerVersion >= 34} //Sydney + pnWaitCaption.StyleElements := pnWaitCaption.StyleElements - [seFont]; + {$ENDIF} +end; + +procedure TDelphiAIDevDBChatView.ConfScreenOnShow; +begin + TUtilsOTA.IDEThemingAll(TDelphiAIDevDBChatView, Self); + btnMoreActions.Font.Color := TUtilsOTA.ActiveThemeColorDefault; + + Self.Constraints.MinWidth := 200; + Self.Constraints.MinHeight := 300; +end; + +procedure TDelphiAIDevDBChatView.AddItemsPopupMenuQuestion; +begin + FPopupMenuQuestions + .ProcessClickInItem(DoProcessClickInItemDefaultQuestions) + .CreateMenus(pMenuQuestions); +end; + +procedure TDelphiAIDevDBChatView.DoProcessClickInItemDefaultQuestions(ACodeOnly: Boolean; AQuestion: string); +begin + if ACodeOnly then + btnCodeOnly.ImageIndex := CodeOnly_ImageIndex_ON + else + btnCodeOnly.ImageIndex := CodeOnly_ImageIndex_OFF; + + mmQuestion.Lines.Add(AQuestion); +end; + +procedure TDelphiAIDevDBChatView.mmQuestionChange(Sender: TObject); +begin + if mmQuestion.Lines.Count >= 7 then + mmQuestion.ScrollBars := ssVertical + else + mmQuestion.ScrollBars := ssNone; +end; + +procedure TDelphiAIDevDBChatView.mmQuestionKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + if (ssCtrl in Shift)and(Key = VK_RETURN) then + begin + btnSend.Click; + Key := 0; + end +end; + +procedure TDelphiAIDevDBChatView.mmQuestionKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + if (ssCtrl in Shift) and (Key = 65) then + begin + mmQuestion.SelectAll; + Key := 0; + end; +end; + +procedure TDelphiAIDevDBChatView.FormResize(Sender: TObject); +var + LWidth: Integer; +const + CAPTION_UseCurrentUnitCode = 'Use current unit code in query'; + CAPTION_CodeOnly = 'SQL only'; + CAPTION_DefaultsQuestions = 'Questions'; +begin + if Self.Width > 620 then + begin + btnUseCurrentUnitCode.Caption := CAPTION_UseCurrentUnitCode; + btnUseCurrentUnitCode.Width := FbtnUseCurrentUnitCodeWidth; + btnUseCurrentUnitCode.ImageAlignment := TImageAlignment.iaLeft; + + btnCodeOnly.Caption := CAPTION_CodeOnly; + btnCodeOnly.Width := FbtnCodeOnlyWidth; + btnCodeOnly.ImageAlignment := TImageAlignment.iaLeft; + + btnDefaultsQuestions.Caption := CAPTION_DefaultsQuestions; + btnDefaultsQuestions.Width := FbtnDefaultsQuestionsWidth; + btnDefaultsQuestions.ImageAlignment := TImageAlignment.iaLeft; + end + else + begin + LWidth := btnSend.Width; + if Self.Width < 405 then + LWidth := 24; + + btnUseCurrentUnitCode.Caption := ''; + btnUseCurrentUnitCode.Width := LWidth; + btnUseCurrentUnitCode.ImageAlignment := TImageAlignment.iaCenter; + + btnCodeOnly.Caption := ''; + btnCodeOnly.Width := LWidth; + btnCodeOnly.ImageAlignment := TImageAlignment.iaCenter; + + btnDefaultsQuestions.Caption := ''; + btnDefaultsQuestions.Width := LWidth; + btnDefaultsQuestions.ImageAlignment := TImageAlignment.iaCenter; + end; +end; + +procedure TDelphiAIDevDBChatView.FillMemoReturnWithFile; +begin + if FileExists(TUtils.GetPathFileChatDB) then + mmReturn.Lines.LoadFromFile(TUtils.GetPathFileChatDB) +end; + +procedure TDelphiAIDevDBChatView.SaveMemoReturnInFile; +begin + mmReturn.Lines.SaveToFile(TUtils.GetPathFileChatDB); +end; + +procedure TDelphiAIDevDBChatView.SelectAll1Click(Sender: TObject); +begin + mmReturn.SelectAll; +end; + +procedure TDelphiAIDevDBChatView.cBoxSizeFontKeyPress(Sender: TObject; var Key: Char); +begin + if not CharInSet(Key, ['0'..'9', #8]) then + Key := #0; +end; + +procedure TDelphiAIDevDBChatView.Cut1Click(Sender: TObject); +begin + mmReturn.CutToClipboard; +end; + +procedure TDelphiAIDevDBChatView.Copy1Click(Sender: TObject); +begin + mmReturn.CopyToClipboard; +end; + +procedure TDelphiAIDevDBChatView.Paste1Click(Sender: TObject); +begin + mmReturn.PasteFromClipboard; +end; + +procedure TDelphiAIDevDBChatView.btnUseCurrentUnitCodeClick(Sender: TObject); +begin + Self.ChangeUseCurrentUnitCode; +end; + +procedure TDelphiAIDevDBChatView.btnDefaultsQuestionsClick(Sender: TObject); +begin + pMenuQuestions.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y); +end; + +procedure TDelphiAIDevDBChatView.ChangeUseCurrentUnitCode; +begin + if btnUseCurrentUnitCode.ImageIndex = UseCurrentUnitCode_ImageIndex_OFF then + btnUseCurrentUnitCode.ImageIndex := UseCurrentUnitCode_ImageIndex_ON + else + btnUseCurrentUnitCode.ImageIndex := UseCurrentUnitCode_ImageIndex_OFF; +end; + +procedure TDelphiAIDevDBChatView.btnCodeOnlyClick(Sender: TObject); +begin + Self.ChangeCodeOnly; +end; + +procedure TDelphiAIDevDBChatView.ChangeCodeOnly; +begin + if btnCodeOnly.ImageIndex = CodeOnly_ImageIndex_OFF then + btnCodeOnly.ImageIndex := CodeOnly_ImageIndex_ON + else + btnCodeOnly.ImageIndex := CodeOnly_ImageIndex_OFF; +end; + +procedure TDelphiAIDevDBChatView.ReloadDatabases; +var + LGuidDatabaseDefault: string; +begin + LGuidDatabaseDefault := TDelphiAIDevProjectsModel.New.ReadFilePathCurrentProject.GuidDatabaseDefault; + + TDelphiAIDevDBUtils.FillComboBoxDataBases(cBoxDatabases, LGuidDatabaseDefault); + Self.FillDateLastReferences; +end; + +procedure TDelphiAIDevDBChatView.AddResponseSimple(const AString: string); +begin + Self.Last; + mmReturn.SelAttributes.Color := TUtilsOTA.ActiveThemeColorDefault; + mmReturn.SelAttributes.Style := []; + mmReturn.Lines.Add(AString); + Self.Last; +end; + +procedure TDelphiAIDevDBChatView.WaitingFormON; +begin + pnWait.Visible := False; + TUtils.CenterPanel(pnWait, mmReturn); + pnWait.Visible := True; +end; + +procedure TDelphiAIDevDBChatView.WordWrap1Click(Sender: TObject); +begin + Self.ProcessWordWrap; +end; + +procedure TDelphiAIDevDBChatView.ProcessWordWrap; +begin + if WordWrap1.Checked then + mmReturn.ScrollBars := ssVertical + else + mmReturn.ScrollBars := ssBoth; +end; + +procedure TDelphiAIDevDBChatView.WaitingFormOFF; +begin + pnWait.Visible := False; +end; + +procedure TDelphiAIDevDBChatView.Last; +begin + SendMessage(mmReturn.Handle, WM_VSCROLL, SB_BOTTOM, 0); +end; + +procedure TDelphiAIDevDBChatView.lbCurrentAIClick(Sender: TObject); +begin + pMenuCurrentAI.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y); +end; + +function TDelphiAIDevDBChatView.GetSelectedTextOrAllFromReturn: string; +begin + if not mmReturn.SelText.Trim.IsEmpty then + Result := mmReturn.SelText + else + Result := mmReturn.Lines.Text; +end; + +function TDelphiAIDevDBChatView.GetSelectedTextOrAllOrAbort: string; +begin + Result := Self.GetSelectedTextOrAllFromReturn; + if Result.Trim.IsEmpty then + TUtils.ShowMsgAndAbort('There is no data to be used in this action'); +end; + +procedure TDelphiAIDevDBChatView.btnInsertAtCursorClick(Sender: TObject); +var + LText: string; +begin + LText := Self.GetSelectedTextOrAllOrAbort; + TUtilsOTA.DeleteBlockTextSelectedInEditor; + TUtilsOTA.InsertBlockTextIntoEditor(LText); +end; + +procedure TDelphiAIDevDBChatView.btnCopyClick(Sender: TObject); +var + LText: string; +begin + LText := Self.GetSelectedTextOrAllOrAbort; + Clipboard.AsText := LText; +end; + +procedure TDelphiAIDevDBChatView.btnCreateNewUnitClick(Sender: TObject); +var + LText: string; +begin + LText := Self.GetSelectedTextOrAllOrAbort; + TDelphiAIDevModuleCreator.New.CreateNewUnit(LText); +end; + +procedure TDelphiAIDevDBChatView.SaveContentToFile1Click(Sender: TObject); +var + LFileName: string; +begin + Self.GetSelectedTextOrAllOrAbort; + + LFileName := TUtils.GetFileName('rtf'); + mmReturn.Lines.SaveToFile(LFileName); + TUtils.ShowV('File saved successfully'); +end; + +procedure TDelphiAIDevDBChatView.Clear1Click(Sender: TObject); +begin + mmReturn.Lines.Clear; +end; + +procedure TDelphiAIDevDBChatView.btnMoreActionsClick(Sender: TObject); +begin + pMenuMoreActions.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y); +end; + +procedure TDelphiAIDevDBChatView.InitializeRichEditReturn; +begin + mmReturn.SelAttributes.Name := 'Courier New'; + mmReturn.SelAttributes.Size := 10; + + if TUtilsOTA.ActiveThemeIsDark then + begin + mmReturn.Color := $004A4136; + mmReturn.SelAttributes.Color := clWhite; + end + else + begin + mmReturn.Color := clWindow; + mmReturn.SelAttributes.Color := clWindowText; + end; +end; + +procedure TDelphiAIDevDBChatView.pMenuCurrentAIPopup(Sender: TObject); +begin + Gemini1.Checked := False; + ChatGPT1.Checked := False; + Groq1.Checked := False; + Mistral1.Checked := False; + Ollama1.Checked := False; + case FSettings.AIDefault of + TC4DAiAvailable.Gemini: + Gemini1.Checked := True; + TC4DAiAvailable.OpenAI: + ChatGPT1.Checked := True; + TC4DAiAvailable.Groq: + Groq1.Checked := True; + TC4DAiAvailable.Mistral: + Mistral1.Checked := True; + TC4DAiAvailable.Ollama: + Ollama1.Checked := True; + end; +end; + +procedure TDelphiAIDevDBChatView.ConfLabelCurrentAI; +begin + lbCurrentAI.Caption := FSettings.AIDefault.ToString; + + case FSettings.AIDefault of + TC4DAiAvailable.Gemini: + lbCurrentAI.Hint := FSettings.ModelGemini; + TC4DAiAvailable.OpenAI: + lbCurrentAI.Hint := FSettings.ModelOpenAI; + TC4DAiAvailable.Groq: + lbCurrentAI.Hint := FSettings.ModelGroq; + TC4DAiAvailable.Mistral: + lbCurrentAI.Hint := FSettings.ModelMistral; + TC4DAiAvailable.Ollama: + lbCurrentAI.Hint := FSettings.ModelOllama; + end; + + lbCurrentAI.Repaint; + Self.Repaint; +end; + +procedure TDelphiAIDevDBChatView.Gemini1Click(Sender: TObject); +var + LTag: Integer; +begin + //*SEVERAL + LTag := TMenuItem(Sender).Tag; + if not(LTag in [0, 1, 2, 3, 4])then + Exit; + + FSettings.AIDefault := TC4DAiAvailable(LTag); + FSettings.SaveData; + Self.ConfLabelCurrentAI; +end; + +procedure TDelphiAIDevDBChatView.btnCleanAllClick(Sender: TObject); +begin + mmQuestion.Lines.Clear; + mmReturn.Lines.Clear; +end; + +procedure TDelphiAIDevDBChatView.cBoxDatabasesClick(Sender: TObject); +begin + Self.FillDateLastReferences; +end; + +procedure TDelphiAIDevDBChatView.FillDateLastReferences; +begin + lbLastGeneration.Caption := ''; + + if cBoxDatabases.Items.Count <= 0 then + Exit; + + lbLastGeneration.Caption := TUtils.DateTimeToStrEmpty(Self.GetFieldDBSelected.LastReferences); +end; + +procedure TDelphiAIDevDBChatView.btnGenerateDatabaseReferenceClick(Sender: TObject); +var + LView: TDelphiAIDevDBReferencesView; + LFields: TDelphiAIDevDBRegistersFields; +begin + Self.ValidateIfDatabaseSelected; + + LFields := Self.GetFieldDBSelected; + if LFields = nil then + Exit; + + LView := TDelphiAIDevDBReferencesView.Create(nil); + try + LView.Fields := LFields; + if LView.ShowModal = mrOk then + Self.FillDateLastReferences; + finally + LView.Free; + end; +end; + +procedure TDelphiAIDevDBChatView.ValidateIfDatabaseSelected; +begin + if cBoxDatabases.Items.Count <= 0 then + TUtils.ShowMsgAndAbort('Please select a database to continue', cBoxDatabases); +end; + +function TDelphiAIDevDBChatView.GetFieldDBSelected: TDelphiAIDevDBRegistersFields; +begin + Self.ValidateIfDatabaseSelected; + Result := TDelphiAIDevDBRegistersFields(cBoxDatabases.Items.Objects[cBoxDatabases.ItemIndex]); +end; + +procedure TDelphiAIDevDBChatView.btnSendClick(Sender: TObject); +begin + Self.ProcessSend; +end; + +procedure TDelphiAIDevDBChatView.ProcessSend; +var + LTask: ITask; + LQuestion: string; +begin + if mmQuestion.Lines.Text.Trim.IsEmpty then + TUtils.ShowMsgAndAbort('No questions have been added', mmQuestion); + + FSettings.ValidateFillingSelectedAI; + + mmReturn.Lines.Clear; + + LQuestion := FSettings.LanguageQuestions.GetLanguageDefinition; + + if btnUseCurrentUnitCode.ImageIndex = UseCurrentUnitCode_ImageIndex_ON then + LQuestion := TUtilsOTA.GetSelectedBlockOrAllCodeUnit.Trim + sLineBreak; + + if btnCodeOnly.ImageIndex = CodeOnly_ImageIndex_ON then + LQuestion := LQuestion + FSettings.LanguageQuestions.GetMsgSQLOnly + sLineBreak; + + LQuestion := LQuestion + FSettings.LanguageQuestions.GetMsgJSONIsDatabaseStructure(Self.GetFieldDBSelected.DriverID.ToString); + LQuestion := LQuestion + Self.GetJsonDatabase + sLineBreak; + LQuestion := LQuestion + FSettings.LanguageQuestions.GetMsgJSONInformedAnswerQuestion; + if not FSettings.DefaultPrompt.Trim.IsEmpty then + LQuestion := LQuestion + FSettings.DefaultPrompt + sLineBreak; + LQuestion := LQuestion + mmQuestion.Lines.Text; + + Self.WaitingFormON; + LTask := TTask.Create( + procedure + begin + try + try + FAI.AiUse(FSettings.AIDefault).ProcessSend(LQuestion); + except + on E: Exception do + TThread.Synchronize(nil, + procedure + begin + Self.AddResponseSimple('Unable to perform processing.' + sLineBreak + TUtils.GetExceptionMessage(E)); + Abort; + end); + end; + + TThread.Synchronize(nil, + procedure + begin + mmReturn.Lines.BeginUpdate; + try + //Optional use of one of the following lines + FProcessResponse.AddResponseComplete(FAI.Response.GetContent); + Self.Last; + //Self.AddResponseSimple(FChat.Response.Text); + finally + mmReturn.Lines.EndUpdate; + end; + end); + finally + TThread.Synchronize(nil, + procedure + begin + Self.WaitingFormOFF; + end); + end; + end); + LTask.Start; +end; + +function TDelphiAIDevDBChatView.GetJsonDatabase: string; +var + LFileName: string; + LStringList: TStringList; +begin + LFileName := TUtils.GetPathFolderMetaInfo + Self.GetFieldDBSelected.Guid + '.json'; + + if not FileExists(LFileName) then + TUtils.ShowMsgAndAbort('File with database structure not found', LFileName); + + LStringList := TStringList.Create; + try + LStringList.LoadFromFile(LFileName); + Result := LStringList.Text; + finally + LStringList.Free; + end; +end; + +procedure TDelphiAIDevDBChatView.btnExecuteSQLClick(Sender: TObject); +var + LCommand: string; + LField: TDelphiAIDevDBRegistersFields; +begin + Self.ValidateIfDatabaseSelected; + + LCommand := Trim(mmReturn.Lines.Text); + if LCommand.IsEmpty then + TUtils.ShowMsgAndAbort('No SQL command informed'); + + Screen.Cursor := crHourGlass; + try + lbCount.Caption := '000000'; + LField := Self.GetFieldDBSelected; + + FConn.Configs + .DriverID(LField.DriverID) + .Host(LField.Host) + .UserName(LField.User) + .Password(LField.Password) + .Port(LField.Port) + .Database(LField.DatabaseName) + .VendorLib(LField.VendorLib); + + try + if not FConn.Connection.TestConnection then + TUtils.ShowMsgAndAbort('Connection refused'); + except + on E: exception do + TUtils.ShowMsgErrorAndAbort(E.Message); + end; + + try + FQueryExecuteSQL.CloseClear.Add(LCommand).Open; + except + on E: Exception do + Self.HandleErrorExecutingSQLCommand(E); + end; + + lbCount.Caption := FQueryExecuteSQL.RecordCountStr; + finally + Screen.Cursor := crDefault; + end; +end; + +procedure TDelphiAIDevDBChatView.HandleErrorExecutingSQLCommand(const E: Exception); +var + LCommand: string; + LMsg: string; +begin + LCommand := Trim(mmReturn.Lines.Text).ToLower; + LMsg := 'Unable to execute command.'; + + if LCommand.Contains('insert') then + LMsg := LMsg + sLineBreak + 'Insert commands are blocked'; + + if LCommand.Contains('update') then + LMsg := LMsg + sLineBreak + 'Update commands are blocked'; + + if LCommand.Contains('delete') then + LMsg := LMsg + sLineBreak + 'Delete commands are blocked'; + + if LCommand.Contains('drop') then + LMsg := LMsg + sLineBreak + 'Drop commands are blocked'; + + if LCommand.Contains('create') then + LMsg := LMsg + sLineBreak + 'Create commands are blocked'; + + if LMsg.Trim.IsEmpty then + LMsg := LMsg + sLineBreak + E.Message; + + TUtils.ShowMsgErrorAndAbort(LMsg.Trim, E.Message); +end; + +procedure TDelphiAIDevDBChatView.CopyCurrentColumn1Click(Sender: TObject); +begin + TUtilsDBGrids.DBGridToClipboardCurrentColumn(DBGrid1); +end; + +procedure TDelphiAIDevDBChatView.CopyCurrentLine1Click(Sender: TObject); +begin + TUtilsDBGrids.DBGridToClipboardCurrentLine(DBGrid1); +end; + +procedure TDelphiAIDevDBChatView.CopyAllGridDataClick(Sender: TObject); +begin + TUtilsDBGrids.DBGridToClipboardAll(DBGrid1); +end; + +procedure TDelphiAIDevDBChatView.SaveAllGridDataAsCSVClick(Sender: TObject); +begin + if DataSource1.DataSet.IsEmpty then + TUtils.ShowMsgAndAbort('There is no data to be exported'); + + TUtilsDBGrids.DBGridToCSV(DBGrid1); + TUtils.ShowV('File saved successfully'); +end; + +procedure TDelphiAIDevDBChatView.SaveAllGridDataAsTXTClick(Sender: TObject); +begin + if DataSource1.DataSet.IsEmpty then + TUtils.ShowMsgAndAbort('There is no data to be exported'); + + TUtilsDBGrids.DBGridToTxt(DBGrid1); + TUtils.ShowV('File saved successfully'); +end; + +procedure TDelphiAIDevDBChatView.DBGrid1TitleClick(Column: TColumn); +var + LCampo: string; + LOrdem: string; +begin + if DataSource1.DataSet.IsEmpty then + Exit; + + LCampo := Column.FieldName.Trim; + if (LCampo.IsEmpty) or (Column.Field.FieldKind = fkCalculated) then + Exit; + + LOrdem := LCampo + ':D'; + if FQueryExecuteSQL.IndexFieldNames.Contains(':D') then + LOrdem := LCampo; + + FQueryExecuteSQL.IndexFieldNames(LOrdem); +end; + +initialization + +finalization + Unregister; + +end. diff --git a/Src/DB/References/DelphiAIDev.DB.References.View.dfm b/Src/DB/References/DelphiAIDev.DB.References.View.dfm new file mode 100644 index 0000000..fd6623d --- /dev/null +++ b/Src/DB/References/DelphiAIDev.DB.References.View.dfm @@ -0,0 +1,136 @@ +object DelphiAIDevDBReferencesView: TDelphiAIDevDBReferencesView + Left = 0 + Top = 0 + BorderIcons = [biSystemMenu] + Caption = 'IA Developer - Databases References' + ClientHeight = 161 + ClientWidth = 295 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + KeyPreview = True + OldCreateOrder = False + Position = poScreenCenter + OnCreate = FormCreate + OnKeyDown = FormKeyDown + OnShow = FormShow + PixelsPerInch = 96 + TextHeight = 13 + object Bevel2: TBevel + AlignWithMargins = True + Left = 0 + Top = 122 + Width = 295 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alBottom + Shape = bsTopLine + ExplicitLeft = -87 + ExplicitTop = 264 + ExplicitWidth = 665 + end + object pnButtons: TPanel + Left = 0 + Top = 126 + Width = 295 + Height = 35 + Align = alBottom + BevelEdges = [beLeft, beRight, beBottom] + BevelOuter = bvNone + Padding.Left = 2 + Padding.Top = 2 + Padding.Right = 2 + Padding.Bottom = 2 + ParentBackground = False + TabOrder = 0 + object btnGenerate: TButton + AlignWithMargins = True + Left = 67 + Top = 2 + Width = 110 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Bottom = 0 + Align = alRight + Caption = 'Generate' + TabOrder = 0 + OnClick = btnGenerateClick + end + object btnClose: TButton + AlignWithMargins = True + Left = 180 + Top = 2 + Width = 110 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Bottom = 0 + Align = alRight + Caption = 'Close' + TabOrder = 1 + OnClick = btnCloseClick + end + end + object pnBody: TPanel + Left = 0 + Top = 0 + Width = 295 + Height = 122 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 1 + object Bevel1: TBevel + AlignWithMargins = True + Left = 0 + Top = 118 + Width = 295 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alBottom + Shape = bsTopLine + ExplicitTop = 444 + ExplicitWidth = 676 + end + object Label1: TLabel + Left = 37 + Top = 22 + Width = 79 + Height = 13 + Caption = 'Last generation:' + end + object lbLastGeneration: TLabel + Left = 122 + Top = 22 + Width = 81 + Height = 13 + Caption = 'lbLastGeneration' + end + object ckAddFieldSize: TCheckBox + Left = 37 + Top = 53 + Width = 99 + Height = 17 + Caption = 'Add Field Length' + TabOrder = 0 + end + object ckCompressData: TCheckBox + Left = 37 + Top = 78 + Width = 93 + Height = 17 + Caption = 'Compress Data' + TabOrder = 1 + end + end +end diff --git a/Src/DB/References/DelphiAIDev.DB.References.View.pas b/Src/DB/References/DelphiAIDev.DB.References.View.pas new file mode 100644 index 0000000..03eb343 --- /dev/null +++ b/Src/DB/References/DelphiAIDev.DB.References.View.pas @@ -0,0 +1,113 @@ +unit DelphiAIDev.DB.References.View; + +interface + +uses + Winapi.Windows, + Winapi.Messages, + System.SysUtils, + System.Variants, + System.Classes, + Vcl.Graphics, + Vcl.Controls, + Vcl.Forms, + Vcl.Dialogs, + Vcl.StdCtrls, + Vcl.ExtCtrls, + DelphiAIDev.Types, + DelphiAIDev.DB.Registers.Fields, + DelphiAIDev.MetaInfo; + +type + TDelphiAIDevDBReferencesView = class(TForm) + Bevel2: TBevel; + pnButtons: TPanel; + btnGenerate: TButton; + btnClose: TButton; + pnBody: TPanel; + Bevel1: TBevel; + Label1: TLabel; + lbLastGeneration: TLabel; + ckAddFieldSize: TCheckBox; + ckCompressData: TCheckBox; + procedure FormCreate(Sender: TObject); + procedure FormShow(Sender: TObject); + procedure btnCloseClick(Sender: TObject); + procedure btnGenerateClick(Sender: TObject); + procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + private + FFields: TDelphiAIDevDBRegistersFields; + procedure FillScreenFields; + public + property Fields: TDelphiAIDevDBRegistersFields read FFields write FFields; + end; + +implementation + +uses + DelphiAIDev.Consts, + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA; + +{$R *.dfm} + +procedure TDelphiAIDevDBReferencesView.FormCreate(Sender: TObject); +begin + Self.ModalResult := mrCancel; + TUtilsOTA.IDEThemingAll(TDelphiAIDevDBReferencesView, Self); +end; + +procedure TDelphiAIDevDBReferencesView.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + case Key of + VK_F4: + if ssAlt in Shift then + Key := 0; + VK_ESCAPE: + if Shift = [] then + btnClose.Click; + end; +end; + +procedure TDelphiAIDevDBReferencesView.FormShow(Sender: TObject); +begin + Self.FillScreenFields; +end; + +procedure TDelphiAIDevDBReferencesView.FillScreenFields; +begin + lbLastGeneration.Caption := 'Never'; + if FFields.LastReferences > 0 then + lbLastGeneration.Caption := DateTimeToStr(FFields.LastReferences); +end; + +procedure TDelphiAIDevDBReferencesView.btnGenerateClick(Sender: TObject); +var + LMetaInfo: TDelphiAIDevMetaInfo; +begin + Screen.Cursor := crHourGlass; + try + LMetaInfo := TDelphiAIDevMetaInfo.Create(FFields); + try + LMetaInfo.AddFieldLength := ckAddFieldSize.Checked; + LMetaInfo.CompressData := ckCompressData.Checked; + LMetaInfo.Process; + finally + LMetaInfo.Free; + end; + finally + Screen.Cursor := crDefault; + end; + + Self.Close; + Self.ModalResult := mrOk; +end; + +procedure TDelphiAIDevDBReferencesView.btnCloseClick(Sender: TObject); +begin + Self.Close; + Self.ModalResult := mrCancel; +end; + +end. + diff --git a/Src/DB/Registers/DelphiAIDev.DB.Registers.AddEdit.View.dfm b/Src/DB/Registers/DelphiAIDev.DB.Registers.AddEdit.View.dfm new file mode 100644 index 0000000..db50f99 --- /dev/null +++ b/Src/DB/Registers/DelphiAIDev.DB.Registers.AddEdit.View.dfm @@ -0,0 +1,287 @@ +object DelphiAIDevDBRegistersAddEditView: TDelphiAIDevDBRegistersAddEditView + Left = 0 + Top = 0 + BorderIcons = [biSystemMenu] + Caption = 'IA Developer - Databases - [action]' + ClientHeight = 303 + ClientWidth = 665 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + KeyPreview = True + OldCreateOrder = False + Position = poScreenCenter + OnCreate = FormCreate + OnKeyDown = FormKeyDown + OnShow = FormShow + PixelsPerInch = 96 + TextHeight = 13 + object Bevel2: TBevel + AlignWithMargins = True + Left = 0 + Top = 264 + Width = 665 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alBottom + Shape = bsTopLine + ExplicitLeft = -5 + ExplicitTop = 476 + ExplicitWidth = 676 + end + object Panel1: TPanel + Left = 0 + Top = 268 + Width = 665 + Height = 35 + Align = alBottom + BevelEdges = [beLeft, beRight, beBottom] + BevelOuter = bvNone + Padding.Left = 2 + Padding.Top = 2 + Padding.Right = 2 + Padding.Bottom = 2 + ParentBackground = False + TabOrder = 1 + object btnConfirm: TButton + AlignWithMargins = True + Left = 437 + Top = 2 + Width = 110 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Bottom = 0 + Align = alRight + Caption = 'Confirm' + TabOrder = 0 + OnClick = btnConfirmClick + end + object btnClose: TButton + AlignWithMargins = True + Left = 550 + Top = 2 + Width = 110 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Bottom = 0 + Align = alRight + Caption = 'Close' + TabOrder = 1 + OnClick = btnCloseClick + end + object btnTestConnection: TButton + AlignWithMargins = True + Left = 2 + Top = 2 + Width = 110 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Bottom = 0 + Align = alLeft + Caption = 'Test Connection' + TabOrder = 2 + OnClick = btnTestConnectionClick + end + end + object Panel9: TPanel + Left = 0 + Top = 0 + Width = 665 + Height = 264 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object Bevel1: TBevel + AlignWithMargins = True + Left = 0 + Top = 260 + Width = 665 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alBottom + Shape = bsTopLine + ExplicitTop = 444 + ExplicitWidth = 676 + end + object Label2: TLabel + Left = 234 + Top = 25 + Width = 43 + Height = 13 + Caption = 'Driver ID' + end + object Label3: TLabel + Left = 31 + Top = 25 + Width = 53 + Height = 13 + Caption = 'Description' + end + object Label1: TLabel + Left = 437 + Top = 25 + Width = 22 + Height = 13 + Caption = 'Host' + end + object Label5: TLabel + Left = 31 + Top = 69 + Width = 22 + Height = 13 + Caption = 'User' + end + object Label6: TLabel + Left = 234 + Top = 69 + Width = 46 + Height = 13 + Caption = 'Password' + end + object Label7: TLabel + Left = 437 + Top = 69 + Width = 20 + Height = 13 + Caption = 'Port' + end + object Label4: TLabel + Left = 31 + Top = 119 + Width = 46 + Height = 13 + Caption = 'Database' + end + object Label8: TLabel + Left = 31 + Top = 164 + Width = 68 + Height = 13 + Caption = 'VendorLib (dll)' + end + object lbAddLocalDatabase: TLabel + Left = 465 + Top = 25 + Width = 99 + Height = 13 + Cursor = crHandPoint + Caption = '[Add local database]' + OnClick = lbAddLocalDatabaseClick + end + object cBoxDriverID: TComboBox + Left = 234 + Top = 41 + Width = 200 + Height = 21 + Style = csDropDownList + TabOrder = 1 + end + object edtDescription: TEdit + Left = 31 + Top = 41 + Width = 200 + Height = 21 + TabOrder = 0 + end + object edtHost: TEdit + Left = 437 + Top = 41 + Width = 200 + Height = 21 + TabOrder = 2 + end + object edtUser: TEdit + Left = 31 + Top = 85 + Width = 200 + Height = 21 + TabOrder = 3 + end + object edtPassword: TEdit + Left = 234 + Top = 85 + Width = 164 + Height = 21 + PasswordChar = '*' + TabOrder = 4 + end + object edtPort: TEdit + Left = 437 + Top = 85 + Width = 200 + Height = 21 + NumbersOnly = True + TabOrder = 5 + end + object edtDatabase: TEdit + Left = 31 + Top = 135 + Width = 581 + Height = 21 + TabOrder = 6 + end + object ckVisible: TCheckBox + Left = 31 + Top = 212 + Width = 53 + Height = 17 + Cursor = crHandPoint + Caption = 'Visible' + TabOrder = 8 + end + object edtPasswordView: TButton + Left = 400 + Top = 84 + Width = 34 + Height = 23 + Cursor = crHandPoint + Caption = 'View' + TabOrder = 9 + TabStop = False + OnClick = edtPasswordViewClick + end + object btnVendorLibSearch: TButton + Left = 613 + Top = 179 + Width = 24 + Height = 23 + Cursor = crHandPoint + Caption = '...' + TabOrder = 10 + TabStop = False + OnClick = btnVendorLibSearchClick + end + object edtVendorLib: TEdit + Left = 31 + Top = 180 + Width = 581 + Height = 21 + TabOrder = 7 + end + object btnDatabaseSearch: TButton + Left = 613 + Top = 134 + Width = 24 + Height = 23 + Cursor = crHandPoint + Caption = '...' + TabOrder = 11 + TabStop = False + OnClick = btnDatabaseSearchClick + end + end +end diff --git a/Src/DB/Registers/DelphiAIDev.DB.Registers.AddEdit.View.pas b/Src/DB/Registers/DelphiAIDev.DB.Registers.AddEdit.View.pas new file mode 100644 index 0000000..d3436a1 --- /dev/null +++ b/Src/DB/Registers/DelphiAIDev.DB.Registers.AddEdit.View.pas @@ -0,0 +1,220 @@ +unit DelphiAIDev.DB.Registers.AddEdit.View; + +interface + +uses + Winapi.Windows, + System.SysUtils, + System.Classes, + Vcl.Controls, + Vcl.Forms, + Vcl.StdCtrls, + Vcl.ExtCtrls, + Vcl.Menus, + Vcl.ComCtrls, + DelphiAIDev.Types, + DelphiAIDev.DB.Registers.Fields, + C4D.Conn; + +type + TDelphiAIDevDBRegistersAddEditView = class(TForm) + Panel1: TPanel; + btnConfirm: TButton; + btnClose: TButton; + Panel9: TPanel; + Bevel1: TBevel; + Bevel2: TBevel; + Label2: TLabel; + cBoxDriverID: TComboBox; + Label3: TLabel; + edtDescription: TEdit; + Label1: TLabel; + edtHost: TEdit; + Label5: TLabel; + edtUser: TEdit; + Label6: TLabel; + edtPassword: TEdit; + Label7: TLabel; + edtPort: TEdit; + Label4: TLabel; + edtDatabase: TEdit; + Label8: TLabel; + ckVisible: TCheckBox; + lbAddLocalDatabase: TLabel; + edtPasswordView: TButton; + btnTestConnection: TButton; + btnVendorLibSearch: TButton; + edtVendorLib: TEdit; + btnDatabaseSearch: TButton; + procedure btnCloseClick(Sender: TObject); + procedure btnConfirmClick(Sender: TObject); + procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure FormShow(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure lbAddLocalDatabaseClick(Sender: TObject); + procedure edtPasswordViewClick(Sender: TObject); + procedure btnTestConnectionClick(Sender: TObject); + procedure btnVendorLibSearchClick(Sender: TObject); + procedure btnDatabaseSearchClick(Sender: TObject); + private + FFields: TDelphiAIDevDBRegistersFields; + procedure FillcBoxDriverID; + procedure FillScreenFields; + procedure ValidateFillingFields; + public + property Fields: TDelphiAIDevDBRegistersFields read FFields write FFields; + end; + +implementation + +uses + DelphiAIDev.Consts, + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA, + DelphiAIDev.DB.Registers.Model; + +{$R *.dfm} + +procedure TDelphiAIDevDBRegistersAddEditView.FormCreate(Sender: TObject); +begin + Self.ModalResult := mrCancel; + TUtilsOTA.IDEThemingAll(TDelphiAIDevDBRegistersAddEditView, Self); + Self.FillcBoxDriverID; +end; + +procedure TDelphiAIDevDBRegistersAddEditView.FormShow(Sender: TObject); +begin + Self.FillScreenFields; + edtDescription.SetFocus; +end; + +procedure TDelphiAIDevDBRegistersAddEditView.FillcBoxDriverID; +begin + cBoxDriverID.Items.Clear; + TUtils.DriverIDFillItemsTStrings(cBoxDriverID.Items); +end; + +procedure TDelphiAIDevDBRegistersAddEditView.lbAddLocalDatabaseClick(Sender: TObject); +begin + edtHost.Text := 'localhost'; +end; + +procedure TDelphiAIDevDBRegistersAddEditView.FillScreenFields; +begin + cBoxDriverID.ItemIndex := cBoxDriverID.Items.IndexOf(FFields.DriverID.ToString); + edtDescription.Text := FFields.Description; + edtHost.Text := FFields.Host; + edtUser.Text := FFields.User; + edtPassword.Text := FFields.Password; + edtPort.Text := FFields.Port.ToString; + edtDatabase.Text := FFields.DatabaseName; + edtVendorLib.Text := FFields.VendorLib; + ckVisible.Checked := FFields.Visible; +end; + +procedure TDelphiAIDevDBRegistersAddEditView.btnCloseClick(Sender: TObject); +begin + Self.Close; + Self.ModalResult := mrCancel; +end; + +procedure TDelphiAIDevDBRegistersAddEditView.btnConfirmClick(Sender: TObject); +begin + Self.ValidateFillingFields; + + FFields.DriverID := TUtils.StrToDriverID(cBoxDriverID.Text); + FFields.Description := edtDescription.Text; + FFields.Host := edtHost.Text; + FFields.User := edtUser.Text; + FFields.Password := edtPassword.Text; + FFields.Port := StrToIntDef(edtPort.Text, 0); + FFields.DatabaseName := edtDatabase.Text; + FFields.VendorLib := edtVendorLib.Text; + FFields.Visible := ckVisible.Checked; + + TDelphiAIDevDBRegistersModel.New.SaveOrEditData(FFields); + + Self.Close; + Self.ModalResult := mrOK; +end; + +procedure TDelphiAIDevDBRegistersAddEditView.ValidateFillingFields; +begin + if Trim(edtDescription.Text).IsEmpty then + TUtils.ShowMsgAndAbort('No informed Description', edtDescription); + + if cBoxDriverID.ItemIndex < 0 then + TUtils.ShowMsgAndAbort('No informed Driver ID', cBoxDriverID); + + if TUtils.StrToDriverID(cBoxDriverID.Text) = TC4DDriverID.None then + TUtils.ShowMsgAndAbort('Select a DriverID', cBoxDriverID); + + if Trim(edtHost.Text).IsEmpty then + TUtils.ShowMsgAndAbort('No informed Host', edtHost); + + if Trim(edtUser.Text).IsEmpty then + TUtils.ShowMsgAndAbort('No informed User', edtUser); + + if Trim(edtDatabase.Text).IsEmpty then + TUtils.ShowMsgAndAbort('No informed Database', edtDatabase); +end; + +procedure TDelphiAIDevDBRegistersAddEditView.edtPasswordViewClick(Sender: TObject); +begin + if edtPassword.PasswordChar = '*' then + edtPassword.PasswordChar := #0 + else + edtPassword.PasswordChar := '*'; +end; + +procedure TDelphiAIDevDBRegistersAddEditView.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + case Key of + VK_F4: + if ssAlt in Shift then + Key := 0; + VK_ESCAPE: + if Shift = [] then + btnClose.Click; + end; +end; + +procedure TDelphiAIDevDBRegistersAddEditView.btnTestConnectionClick(Sender: TObject); +var + LConn: IC4DConn; +begin + Screen.Cursor := crHourGlass; + try + LConn := TC4DConn.New; + LConn.Configs + .DriverID(TUtils.StrToDriverID(cBoxDriverID.Text)) + .Host(edtHost.Text) + .UserName(edtUser.Text) + .Password(edtPassword.Text) + .Port(StrToIntDef(edtPort.Text, 0)) + .Database(edtDatabase.Text) + .VendorLib(edtVendorLib.Text); + + try + if LConn.Connection.TestConnection then + TUtils.ShowV('Connection Successful'); + except + on E: exception do + TUtils.ShowError(E.Message); + end; + finally + Screen.Cursor := crDefault; + end; +end; + +procedure TDelphiAIDevDBRegistersAddEditView.btnDatabaseSearchClick(Sender: TObject); +begin + edtDatabase.Text := TUtils.SelectFile(edtDatabase.Text); +end; + +procedure TDelphiAIDevDBRegistersAddEditView.btnVendorLibSearchClick(Sender: TObject); +begin + edtVendorLib.Text := TUtils.SelectFile(edtVendorLib.Text); +end; + +end. diff --git a/Src/DB/Registers/DelphiAIDev.DB.Registers.Fields.pas b/Src/DB/Registers/DelphiAIDev.DB.Registers.Fields.pas new file mode 100644 index 0000000..6e2af9c --- /dev/null +++ b/Src/DB/Registers/DelphiAIDev.DB.Registers.Fields.pas @@ -0,0 +1,77 @@ +unit DelphiAIDev.DB.Registers.Fields; + +interface + +uses + DelphiAIDev.Types; + +type + TDelphiAIDevDBRegistersFields = class + private + FGuid: string; + FDriverID: TC4DDriverID; + FDescription: string; + FHost: string; + FUser: string; + FPassword: string; + FPort: Integer; + FDatabaseName: string; + FVisible: Boolean; + FVendorLib: string; + FLastReferences: TDateTime; + public + constructor Create; + procedure Clear; + procedure GetDataFromOtherObject(const AOtherObj: TDelphiAIDevDBRegistersFields); + property Guid: string read FGuid write FGuid; + property DriverID: TC4DDriverID read FDriverID write FDriverID; + property Description: string read FDescription write FDescription; + property Host: string read FHost write FHost; + property User: string read FUser write FUser; + property Password: string read FPassword write FPassword; + property Port: Integer read FPort write FPort; + property DatabaseName: string read FDatabaseName write FDatabaseName; + property Visible: Boolean read FVisible write FVisible; + property VendorLib: string read FVendorLib write FVendorLib; + property LastReferences: TDateTime read FLastReferences write FLastReferences; + end; + +implementation + +constructor TDelphiAIDevDBRegistersFields.Create; +begin + Self.Clear; +end; + +procedure TDelphiAIDevDBRegistersFields.Clear; +begin + FGuid := ''; + FDriverID := TC4DDriverID.None; + FDescription := ''; + FHost := ''; + FUser := ''; + FPassword := ''; + FPort := 0; + FDatabaseName := ''; + FVisible := True; + FVendorLib := ''; + FLastReferences := 0; +end; + +procedure TDelphiAIDevDBRegistersFields.GetDataFromOtherObject(const AOtherObj: TDelphiAIDevDBRegistersFields); +begin + FGuid := AOtherObj.Guid; + FDriverID := AOtherObj.DriverID; + FDescription := AOtherObj.Description; + FHost := AOtherObj.Host; + FUser := AOtherObj.User; + FPassword := AOtherObj.Password; + FPort := AOtherObj.Port; + FDatabaseName := AOtherObj.DatabaseName; + FVisible := AOtherObj.Visible; + FVendorLib := AOtherObj.VendorLib; + FLastReferences := AOtherObj.LastReferences; +end; + + +end. diff --git a/Src/DB/Registers/DelphiAIDev.DB.Registers.Interfaces.pas b/Src/DB/Registers/DelphiAIDev.DB.Registers.Interfaces.pas new file mode 100644 index 0000000..64ac8f0 --- /dev/null +++ b/Src/DB/Registers/DelphiAIDev.DB.Registers.Interfaces.pas @@ -0,0 +1,21 @@ +unit DelphiAIDev.DB.Registers.Interfaces; + +interface + +uses + System.SysUtils, + DelphiAIDev.Types, + DelphiAIDev.DB.Registers.Fields; + +type + IDelphiAIDevDatabasesModel = interface + ['{3399A776-4B23-4CFC-8992-568AE07FE065}'] + function ReadGuid(const AGuid: string): TDelphiAIDevDBRegistersFields; + procedure ReadData(AProc: TProc; const AAutoFreeField: TAutoFreeField = TAutoFreeField.Yes); + procedure SaveOrEditData(AFields: TDelphiAIDevDBRegistersFields); + procedure RemoveData(const AGuid: string); + end; + +implementation + +end. diff --git a/Src/DB/Registers/DelphiAIDev.DB.Registers.Model.pas b/Src/DB/Registers/DelphiAIDev.DB.Registers.Model.pas new file mode 100644 index 0000000..8f61607 --- /dev/null +++ b/Src/DB/Registers/DelphiAIDev.DB.Registers.Model.pas @@ -0,0 +1,362 @@ +unit DelphiAIDev.DB.Registers.Model; + +interface + +uses + System.SysUtils, + System.Classes, + System.JSON, + Rest.JSON, + DelphiAIDev.Utils, + DelphiAIDev.Types, + DelphiAIDev.DB.Registers.Interfaces, + DelphiAIDev.DB.Registers.Fields, + DelphiAIDev.Utils.Crypt; + +type + TDelphiAIDevDBRegistersModel = class(TInterfacedObject, IDelphiAIDevDatabasesModel) + private + procedure SaveData(AFields: TDelphiAIDevDBRegistersFields); + procedure EditData(AFields: TDelphiAIDevDBRegistersFields); + procedure FillField(const AJSONObjItem: TJSONObject; var AField: TDelphiAIDevDBRegistersFields); + protected + function ReadGuid(const AGuid: string): TDelphiAIDevDBRegistersFields; + procedure ReadData(AProc: TProc; const AAutoFreeField: TAutoFreeField = TAutoFreeField.Yes); + procedure SaveOrEditData(AFields: TDelphiAIDevDBRegistersFields); + procedure RemoveData(const AGuid: string); + public + class function New: IDelphiAIDevDatabasesModel; + constructor Create; + end; + +implementation + +const + GUID = 'guid'; + DRIVER_ID = 'driver_id'; + DESCRIPTION = 'description'; + HOST = 'host'; + USER = 'user'; + PASSWORD = 'password'; + PORT = 'port'; + DATABASE_NAME = 'database_name'; + VENDOR_LIB = 'vendor_lib'; + VISIBLE = 'visible'; + LAST_REFERENCE = 'last_reference'; + +class function TDelphiAIDevDBRegistersModel.New: IDelphiAIDevDatabasesModel; +begin + Result := Self.Create; +end; + +constructor TDelphiAIDevDBRegistersModel.Create; +begin + // +end; + +function TDelphiAIDevDBRegistersModel.ReadGuid(const AGuid: string): TDelphiAIDevDBRegistersFields; +var + LStringList: TStringList; + LJSONObjItem: TJSONObject; + LJSONArray: TJsonArray; + i: Integer; +begin + Result := TDelphiAIDevDBRegistersFields.Create; + + if not FileExists(TUtils.GetPathFileJSONDatabases) then + Exit; + + LStringList := TStringList.Create; + try + LStringList.LoadFromFile(TUtils.GetPathFileJSONDatabases); + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + finally + LStringList.Free; + end; + + try + for i := 0 to Pred(LJSONArray.Count) do + begin + if not(LJSONArray.Items[i] is TJSONObject) then + Continue; + + LJSONObjItem := LJSONArray.Items[i] as TJSONObject; + + if LJSONObjItem.GetValue(GUID) = nil then + Continue; + + if LJSONObjItem.GetValue(GUID) <> AGuid then + Continue; + + Self.FillField(LJSONObjItem, Result); + Break; + end; + finally + LJSONArray.Free; + end; +end; + +procedure TDelphiAIDevDBRegistersModel.FillField(const AJSONObjItem: TJSONObject; + var AField: TDelphiAIDevDBRegistersFields); +begin + AField.Clear; + AField.Guid := AJSONObjItem.GetValue(GUID); + + if AJSONObjItem.GetValue(DRIVER_ID) <> nil then + AField.DriverID := TC4DDriverID(AJSONObjItem.GetValue(DRIVER_ID)); + + if AJSONObjItem.GetValue(DESCRIPTION) <> nil then + AField.Description := AJSONObjItem.GetValue(DESCRIPTION); + + if AJSONObjItem.GetValue(HOST) <> nil then + AField.Host := AJSONObjItem.GetValue(HOST); + + if AJSONObjItem.GetValue(USER) <> nil then + AField.User := AJSONObjItem.GetValue(USER); + + if AJSONObjItem.GetValue(PASSWORD) <> nil then + AField.Password := TUtilsCrypt.Decrypt(AJSONObjItem.GetValue(PASSWORD)); + + if AJSONObjItem.GetValue(PORT) <> nil then + AField.Port := AJSONObjItem.GetValue(PORT); + + if AJSONObjItem.GetValue(DATABASE_NAME) <> nil then + AField.DatabaseName := AJSONObjItem.GetValue(DATABASE_NAME); + + if AJSONObjItem.GetValue(VENDOR_LIB) <> nil then + AField.VendorLib := AJSONObjItem.GetValue(VENDOR_LIB); + + if AJSONObjItem.GetValue(VISIBLE) <> nil then + AField.Visible := AJSONObjItem.GetValue(VISIBLE); + + if AJSONObjItem.GetValue(LAST_REFERENCE) <> nil then + AField.LastReferences := StrToDateTimeDef(AJSONObjItem.GetValue(LAST_REFERENCE), 0); +end; + +procedure TDelphiAIDevDBRegistersModel.ReadData(AProc: TProc; + const AAutoFreeField: TAutoFreeField = TAutoFreeField.Yes); +var + LStringList: TStringList; + LJSONObjItem: TJSONObject; + LJSONArray: TJsonArray; + i: Integer; + LFields: TDelphiAIDevDBRegistersFields; +begin + LFields := TDelphiAIDevDBRegistersFields.Create; + try + if not FileExists(TUtils.GetPathFileJSONDatabases) then + begin + AProc(LFields); + Exit; + end; + + LStringList := TStringList.Create; + try + LStringList.LoadFromFile(TUtils.GetPathFileJSONDatabases); + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + finally + LStringList.Free; + end; + + try + for i := 0 to Pred(LJSONArray.Count) do + begin + if not(LJSONArray.Items[i] is TJSONObject) then + Continue; + + LJSONObjItem := LJSONArray.Items[i] as TJSONObject; + + if LJSONObjItem.GetValue(GUID) = nil then + Continue; + + Self.FillField(LJSONObjItem, LFields); + + AProc(LFields); + end; + finally + LJSONArray.Free; + end; + finally + if AAutoFreeField = TAutoFreeField.Yes then + LFields.Free; + end; +end; + +procedure TDelphiAIDevDBRegistersModel.SaveOrEditData(AFields: TDelphiAIDevDBRegistersFields); +begin + if AFields.Guid.Trim.IsEmpty then + Self.SaveData(AFields) + else + Self.EditData(AFields); +end; + +procedure TDelphiAIDevDBRegistersModel.SaveData(AFields: TDelphiAIDevDBRegistersFields); +var + LStringList: TStringList; + LJSONArray: TJSONArray; + LJSONObject: TJSONObject; +begin + LStringList := TStringList.Create; + try + if FileExists(TUtils.GetPathFileJSONDatabases) then + LStringList.LoadFromFile(TUtils.GetPathFileJSONDatabases); + + LJSONArray := TJSONArray.Create; + try + if string(LStringList.Text).Trim.StartsWith('[') then + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + + LJSONObject := TJSONObject.Create; + LJSONObject.AddPair(GUID, TUtils.GetGuidStr); + LJSONObject.AddPair(DRIVER_ID, TJSONNumber.Create(Integer(AFields.DriverID))); + LJSONObject.AddPair(DESCRIPTION, AFields.Description); + LJSONObject.AddPair(HOST, AFields.Host); + LJSONObject.AddPair(USER, AFields.User); + LJSONObject.AddPair(PASSWORD, TUtilsCrypt.Encrypt(AFields.Password)); + LJSONObject.AddPair(PORT, TJSONNumber.Create(AFields.Port)); + LJSONObject.AddPair(DATABASE_NAME, AFields.DatabaseName); + LJSONObject.AddPair(VENDOR_LIB, AFields.VendorLib); + LJSONObject.AddPair(VISIBLE, TJSONBool.Create(AFields.Visible)); + LJSONObject.AddPair(LAST_REFERENCE, TJSONString.Create(TUtils.DateTimeToStrEmpty(AFields.LastReferences))); + LJSONArray.AddElement(LJSONObject); + + {$IF CompilerVersion <= 32.0} //Tokyo + LStringList.Text := LJSONArray.ToJSON; + {$ELSE} + LStringList.Text := LJSONArray.Format(2); + {$ENDIF} + finally + LJSONArray.Free; + end; + + LStringList.SaveToFile(TUtils.GetPathFileJSONDatabases); + finally + LStringList.Free; + end; +end; + +procedure TDelphiAIDevDBRegistersModel.EditData(AFields: TDelphiAIDevDBRegistersFields); +var + LStringList: TStringList; + LJSONArray: TJSONArray; + LJSONObjItem: TJSONObject; + i: Integer; +begin + LStringList := TStringList.Create; + try + if FileExists(TUtils.GetPathFileJSONDatabases) then + LStringList.LoadFromFile(TUtils.GetPathFileJSONDatabases); + + LJSONArray := TJSONArray.Create; + try + if string(LStringList.Text).Trim.StartsWith('[') then + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + + for i := 0 to Pred(LJSONArray.Count) do + begin + if not(LJSONArray.Items[i] is TJSONObject) then + Continue; + + LJSONObjItem := LJSONArray.Items[i] as TJSONObject; + + if LJSONObjItem.GetValue(GUID) = AFields.Guid then + begin + LJSONObjItem.RemovePair(DRIVER_ID).Free; + LJSONObjItem.AddPair(DRIVER_ID, TJSONNumber.Create(Integer(AFields.DriverID))); + + LJSONObjItem.RemovePair(DESCRIPTION).Free; + LJSONObjItem.AddPair(DESCRIPTION, AFields.Description); + + LJSONObjItem.RemovePair(HOST).Free; + LJSONObjItem.AddPair(HOST, AFields.Host); + + LJSONObjItem.RemovePair(USER).Free; + LJSONObjItem.AddPair(USER, AFields.User); + + LJSONObjItem.RemovePair(PASSWORD).Free; + LJSONObjItem.AddPair(PASSWORD, TUtilsCrypt.Encrypt(AFields.Password)); + + LJSONObjItem.RemovePair(PORT).Free; + LJSONObjItem.AddPair(PORT, TJSONNumber.Create(AFields.Port)); + + LJSONObjItem.RemovePair(DATABASE_NAME).Free; + LJSONObjItem.AddPair(DATABASE_NAME, AFields.DatabaseName); + + LJSONObjItem.RemovePair(VENDOR_LIB).Free; + LJSONObjItem.AddPair(VENDOR_LIB, AFields.VendorLib); + + LJSONObjItem.RemovePair(VISIBLE).Free; + LJSONObjItem.AddPair(VISIBLE, TJSONBool.Create(AFields.Visible)); + + LJSONObjItem.RemovePair(LAST_REFERENCE).Free; + LJSONObjItem.AddPair(LAST_REFERENCE, TJSONString.Create(TUtils.DateTimeToStrEmpty(AFields.LastReferences))); + + Break; + end; + end; + + {$IF CompilerVersion <= 32.0} //Tokyo + LStringList.Text := LJSONArray.ToJSON; + {$ELSE} + LStringList.Text := LJSONArray.Format(2); + {$ENDIF} + finally + LJSONArray.Free; + end; + + LStringList.SaveToFile(TUtils.GetPathFileJSONDatabases); + finally + LStringList.Free; + end; +end; + +procedure TDelphiAIDevDBRegistersModel.RemoveData(const AGuid: string); +var + LStringList: TStringList; + LJSONArray: TJSONArray; + LJSONObjItem: TJSONObject; + i: Integer; +begin + if AGuid.Trim.IsEmpty then + Exit; + + if not FileExists(TUtils.GetPathFileJSONDatabases) then + Exit; + + LStringList := TStringList.Create; + try + LStringList.LoadFromFile(TUtils.GetPathFileJSONDatabases); + if not string(LStringList.Text).Trim.StartsWith('[') then + Exit; + + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + try + for i := 0 to Pred(LJSONArray.Count) do + begin + if not(LJSONArray.Items[i] is TJSONObject) then + Continue; + + LJSONObjItem := LJSONArray.Items[i] as TJSONObject; + if LJSONObjItem.GetValue(GUID) = AGuid then + begin + LJSONArray.Remove(i); + Break; + end; + end; + + {$IF CompilerVersion <= 32.0} //Tokyo + LStringList.Text := LJSONArray.ToJSON; + {$ELSE} + LStringList.Text := LJSONArray.Format(2); + {$ENDIF} + finally + LJSONArray.Free; + end; + + LStringList.SaveToFile(TUtils.GetPathFileJSONDatabases); + finally + LStringList.Free; + end; +end; + +end. diff --git a/Src/DB/Registers/DelphiAIDev.DB.Registers.View.dfm b/Src/DB/Registers/DelphiAIDev.DB.Registers.View.dfm new file mode 100644 index 0000000..0f5b3e9 --- /dev/null +++ b/Src/DB/Registers/DelphiAIDev.DB.Registers.View.dfm @@ -0,0 +1,245 @@ +object DelphiAIDevDBRegistersView: TDelphiAIDevDBRegistersView + Left = 0 + Top = 0 + BorderIcons = [biSystemMenu] + Caption = 'IA Developer - Database registration' + ClientHeight = 561 + ClientWidth = 1032 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + KeyPreview = True + OldCreateOrder = False + Position = poScreenCenter + OnCreate = FormCreate + OnKeyDown = FormKeyDown + OnShow = FormShow + PixelsPerInch = 96 + TextHeight = 13 + object Panel1: TPanel + Left = 0 + Top = 507 + Width = 1032 + Height = 35 + Align = alBottom + BevelEdges = [beLeft, beRight, beBottom] + BevelOuter = bvNone + Padding.Left = 2 + Padding.Top = 2 + Padding.Right = 2 + Padding.Bottom = 2 + ParentBackground = False + TabOrder = 0 + object btnEdit: TButton + AlignWithMargins = True + Left = 124 + Top = 2 + Width = 120 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 2 + Margins.Bottom = 0 + Align = alLeft + Caption = 'Edit' + TabOrder = 0 + OnClick = btnEditClick + end + object btnClose: TButton + AlignWithMargins = True + Left = 918 + Top = 2 + Width = 110 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 2 + Margins.Bottom = 0 + Align = alRight + Caption = 'Close' + TabOrder = 1 + OnClick = btnCloseClick + end + object btnAdd: TButton + AlignWithMargins = True + Left = 2 + Top = 2 + Width = 120 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 2 + Margins.Bottom = 0 + Align = alLeft + Caption = 'Add new' + TabOrder = 2 + OnClick = btnAddClick + end + object btnRemove: TButton + AlignWithMargins = True + Left = 246 + Top = 2 + Width = 120 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 2 + Margins.Bottom = 0 + Align = alLeft + Caption = 'Remove' + TabOrder = 3 + OnClick = btnRemoveClick + end + object btnGenerateDatabaseReference: TButton + AlignWithMargins = True + Left = 368 + Top = 2 + Width = 165 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Bottom = 0 + Align = alLeft + Caption = 'Generate database reference' + TabOrder = 4 + OnClick = btnGenerateDatabaseReferenceClick + end + end + object ListView: TListView + Left = 0 + Top = 50 + Width = 1032 + Height = 457 + Align = alClient + Columns = < + item + Caption = 'Description' + Width = 200 + end + item + Caption = 'Driver ID' + Width = 70 + end + item + Caption = 'Host' + Width = 100 + end + item + Caption = 'User' + Width = 90 + end + item + Alignment = taCenter + Caption = 'Port' + Width = 60 + end + item + Caption = 'Database name' + Width = 200 + end + item + Alignment = taCenter + Caption = 'Visible' + Width = 45 + end + item + Caption = 'Last generation of reference' + Width = 180 + end + item + Caption = 'Password' + MaxWidth = 1 + Width = 1 + end + item + Caption = 'VendorLib' + MaxWidth = 1 + Width = 1 + end + item + Caption = 'Guid' + MaxWidth = 1 + Width = 1 + end> + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -12 + Font.Name = 'Tahoma' + Font.Style = [] + ReadOnly = True + RowSelect = True + ParentFont = False + SortType = stText + TabOrder = 1 + ViewStyle = vsReport + OnColumnClick = ListViewColumnClick + OnDblClick = ListViewDblClick + OnKeyDown = ListViewKeyDown + OnSelectItem = ListViewSelectItem + end + object pnTop: TPanel + Left = 0 + Top = 0 + Width = 1032 + Height = 50 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alTop + BevelOuter = bvNone + TabOrder = 2 + object btnSearch: TButton + AlignWithMargins = True + Left = 947 + Top = 14 + Width = 75 + Height = 27 + Cursor = crHandPoint + Margins.Left = 2 + Margins.Top = 14 + Margins.Right = 10 + Margins.Bottom = 9 + Align = alRight + Caption = 'Search' + TabOrder = 0 + OnClick = btnSearchClick + end + object edtSearch: TEdit + AlignWithMargins = True + Left = 10 + Top = 15 + Width = 935 + Height = 25 + Margins.Left = 10 + Margins.Top = 15 + Margins.Right = 0 + Margins.Bottom = 10 + Align = alClient + AutoSize = False + TabOrder = 1 + OnKeyDown = edtSearchKeyDown + end + end + object StatusBar1: TStatusBar + Left = 0 + Top = 542 + Width = 1032 + Height = 19 + Panels = < + item + Width = 50 + end + item + Width = 50 + end> + end +end diff --git a/Src/DB/Registers/DelphiAIDev.DB.Registers.View.pas b/Src/DB/Registers/DelphiAIDev.DB.Registers.View.pas new file mode 100644 index 0000000..56f68c9 --- /dev/null +++ b/Src/DB/Registers/DelphiAIDev.DB.Registers.View.pas @@ -0,0 +1,395 @@ +unit DelphiAIDev.DB.Registers.View; + +interface + +uses + Winapi.Windows, + System.SysUtils, + System.StrUtils, + System.Classes, + Vcl.Controls, + Vcl.Forms, + Vcl.StdCtrls, + Vcl.ExtCtrls, + Vcl.ComCtrls, + C4D.Conn, + DelphiAIDev.Utils.ListView, + DelphiAIDev.DB.Registers.Model, + DelphiAIDev.DB.Registers.Fields, + DelphiAIDev.DB.Registers.AddEdit.View, + DelphiAIDev.DB.References.View; + +type + TDelphiAIDevDBRegistersView = class(TForm) + Panel1: TPanel; + btnEdit: TButton; + btnClose: TButton; + ListView: TListView; + pnTop: TPanel; + btnSearch: TButton; + edtSearch: TEdit; + StatusBar1: TStatusBar; + btnAdd: TButton; + btnRemove: TButton; + btnGenerateDatabaseReference: TButton; + procedure FormCreate(Sender: TObject); + procedure FormShow(Sender: TObject); + procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure btnCloseClick(Sender: TObject); + procedure btnSearchClick(Sender: TObject); + procedure ListViewSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); + procedure ListViewDblClick(Sender: TObject); + procedure ListViewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure btnEditClick(Sender: TObject); + procedure btnAddClick(Sender: TObject); + procedure btnRemoveClick(Sender: TObject); + procedure edtSearchKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure ListViewColumnClick(Sender: TObject; Column: TListColumn); + procedure btnGenerateDatabaseReferenceClick(Sender: TObject); + private + FUtilsListView: IDelphiAIDevUtilsListView; + FMadeChanges: Boolean; + procedure ReloadData; + procedure ReloadDataInternal; + procedure FillStatusBar(AItem: TListItem); + procedure FillFieldsWithSelectedItem(var AFields: TDelphiAIDevDBRegistersFields); + public + property MadeChanges: Boolean read FMadeChanges; + end; + +implementation + +uses + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA, + DelphiAIDev.Types; + +{$R *.dfm} + +const + C_INDEX_SUBITEM_DriverId = 0; + C_INDEX_SUBITEM_Host = 1; + C_INDEX_SUBITEM_User = 2; + C_INDEX_SUBITEM_Port = 3; + C_INDEX_SUBITEM_DatabaseName = 4; + C_INDEX_SUBITEM_Visible = 5; + C_INDEX_SUBITEM_LastReferences = 6; + C_INDEX_SUBITEM_Password = 7; + C_INDEX_SUBITEM_VendorLib = 8; + C_INDEX_SUBITEM_Guid = 9; + +procedure TDelphiAIDevDBRegistersView.FormCreate(Sender: TObject); +begin + TUtilsOTA.IDEThemingAll(TDelphiAIDevDBRegistersView, Self); + FUtilsListView := TDelphiAIDevUtilsListView.New(ListView); +end; + +procedure TDelphiAIDevDBRegistersView.FormShow(Sender: TObject); +begin + Self.ReloadData; + + if ListView.Items.Count > 0 then + ListView.Items.Item[0].Selected := True; + FMadeChanges := False; + edtSearch.SetFocus; + + FUtilsListView + .InvertOrder(True) + .SortStyle(TDelphiAIDevUtilsListViewSortStyle.AlphaNum) + .ColumnIndex(C_INDEX_SUBITEM_DatabaseName + 1) + .CustomSort; +end; + +procedure TDelphiAIDevDBRegistersView.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + case Key of + VK_F4: + if ssAlt in Shift then + Key := 0; + VK_ESCAPE: + if Shift = [] then + btnClose.Click; + VK_DOWN, VK_UP: + begin + if ListView <> ActiveControl then + begin + case Key of + VK_DOWN: + if ListView.ItemIndex < Pred(ListView.Items.Count) then + ListView.ItemIndex := ListView.ItemIndex + 1; + VK_UP: + if ListView.ItemIndex > 0 then + ListView.ItemIndex := ListView.ItemIndex - 1; + end; + Key := 0; + end; + end; + end; +end; + +procedure TDelphiAIDevDBRegistersView.btnCloseClick(Sender: TObject); +begin + Self.Close; +end; + +procedure TDelphiAIDevDBRegistersView.edtSearchKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + if Key = VK_RETURN then + Self.ReloadData; +end; + +procedure TDelphiAIDevDBRegistersView.btnSearchClick(Sender: TObject); +begin + Self.ReloadData; +end; + +procedure TDelphiAIDevDBRegistersView.ReloadData; +begin + Screen.Cursor := crHourGlass; + try + Self.ReloadDataInternal; + finally + Screen.Cursor := crDefault; + end; +end; + +procedure TDelphiAIDevDBRegistersView.ReloadDataInternal; +var + LStrSearch: string; + LListItem: TListItem; + LGuidSelected: string; +begin + LStrSearch := LowerCase(edtSearch.Text); + + if ListView.Selected <> nil then + LGuidSelected := ListView.Items[ListView.Selected.Index].SubItems[C_INDEX_SUBITEM_Guid]; + + ListView.Clear; + + TDelphiAIDevDBRegistersModel.New.ReadData( + procedure(AFields: TDelphiAIDevDBRegistersFields) + begin + if AFields.Description.Trim.IsEmpty then + Exit; + + //if (AFields.Kind = TC4DQuestionKind.ItemMenuNormal) and (AFields.Question.Trim.IsEmpty) then + // Exit; + + if (LStrSearch.Trim.IsEmpty) + or(AFields.Description.ToLower.Contains(LStrSearch)) + or(AFields.Host.ToLower.Contains(LStrSearch)) + or(AFields.DatabaseName.ToLower.Contains(LStrSearch)) + then + begin + LListItem := ListView.Items.Add; + LListItem.Caption := AFields.Description; + LListItem.ImageIndex := -1; + LListItem.SubItems.Add(AFields.DriverID.ToString); + LListItem.SubItems.Add(AFields.Host); + LListItem.SubItems.Add(AFields.User); + LListItem.SubItems.Add(AFields.Port.ToString); + LListItem.SubItems.Add(AFields.DatabaseName); + LListItem.SubItems.Add(TUtils.BoolToStrC4D(AFields.Visible)); + LListItem.SubItems.Add(TUtils.DateTimeToStrEmpty(AFields.LastReferences)); + LListItem.SubItems.Add(AFields.Password); + LListItem.SubItems.Add(AFields.VendorLib); + LListItem.SubItems.Add(AFields.Guid); + end; + end + ); + + FUtilsListView + .InvertOrder(False) + .CustomSort; + + if not LGuidSelected.Trim.IsEmpty then + TUtils.FindListVewItem(ListView, C_INDEX_SUBITEM_Guid, LGuidSelected); + + Self.FillStatusBar(ListView.Selected); +end; + +procedure TDelphiAIDevDBRegistersView.ListViewSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); +begin + Self.FillStatusBar(Item); +end; + +procedure TDelphiAIDevDBRegistersView.FillFieldsWithSelectedItem(var AFields: TDelphiAIDevDBRegistersFields); +var + LListItem: TListItem; +begin + AFields.Clear; + if ListView.Selected = nil then + Exit; + + LListItem := ListView.Items[ListView.Selected.Index]; + AFields.Description := LListItem.Caption; + AFields.DriverID := TUtils.StrToDriverID(LListItem.SubItems[C_INDEX_SUBITEM_DriverId]); + AFields.Host := LListItem.SubItems[C_INDEX_SUBITEM_Host]; + AFields.User := LListItem.SubItems[C_INDEX_SUBITEM_User]; + AFields.Port := StrToIntDef(LListItem.SubItems[C_INDEX_SUBITEM_Port], 0); + AFields.DatabaseName := LListItem.SubItems[C_INDEX_SUBITEM_DatabaseName]; + AFields.Visible := TUtils.StrToBoolC4D(LListItem.SubItems[C_INDEX_SUBITEM_Visible]); + AFields.LastReferences := StrToDateTimeDef(LListItem.SubItems[C_INDEX_SUBITEM_LastReferences], 0); + AFields.Password := LListItem.SubItems[C_INDEX_SUBITEM_Password]; + AFields.VendorLib := LListItem.SubItems[C_INDEX_SUBITEM_VendorLib]; + AFields.Guid := LListItem.SubItems[C_INDEX_SUBITEM_Guid]; +end; + +procedure TDelphiAIDevDBRegistersView.FillStatusBar(AItem: TListItem); +var + LIndex: Integer; + LDatabaseName: string; +begin + LIndex := -1; + LDatabaseName := ''; + if AItem <> nil then + begin + LIndex := AItem.Index; + LDatabaseName := ListView.Items[LIndex].SubItems[C_INDEX_SUBITEM_DatabaseName]; + end; + + StatusBar1.Panels[0].Text := Format('%d of %d', [LIndex + 1, ListView.Items.Count]); + StatusBar1.Panels[1].Text := LDatabaseName; +end; + +procedure TDelphiAIDevDBRegistersView.ListViewColumnClick(Sender: TObject; Column: TListColumn); +var + LSortStyle: TDelphiAIDevUtilsListViewSortStyle; +begin + LSortStyle := TDelphiAIDevUtilsListViewSortStyle.AlphaNum; + case Column.Index of + C_INDEX_SUBITEM_Port + 1: + LSortStyle := TDelphiAIDevUtilsListViewSortStyle.Numeric; + end; + + FUtilsListView + .InvertOrder(True) + .SortStyle(LSortStyle) + .ColumnIndex(Column.Index) + .CustomSort; +end; + +procedure TDelphiAIDevDBRegistersView.ListViewDblClick(Sender: TObject); +begin + btnEdit.Click +end; + +procedure TDelphiAIDevDBRegistersView.ListViewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + if Key = VK_RETURN then + btnEdit.Click +end; + +procedure TDelphiAIDevDBRegistersView.btnAddClick(Sender: TObject); +var + LFields: TDelphiAIDevDBRegistersFields; + LView: TDelphiAIDevDBRegistersAddEditView; +begin + LFields := TDelphiAIDevDBRegistersFields.Create; + try + LView := TDelphiAIDevDBRegistersAddEditView.Create(nil); + try + LView.Caption := string(LView.Caption).Replace('[action]', 'Adding', [rfReplaceAll, rfIgnoreCase]); + LView.Fields := LFields; + + if LView.ShowModal <> mrOk then + Exit; + + FMadeChanges := True; + finally + LView.Free; + end; + Self.ReloadData; + finally + LFields.Free; + end; +end; + +procedure TDelphiAIDevDBRegistersView.btnEditClick(Sender: TObject); +var + LFields: TDelphiAIDevDBRegistersFields; + LView: TDelphiAIDevDBRegistersAddEditView; +begin + if ListView.Selected = nil then + Exit; + + LFields := TDelphiAIDevDBRegistersFields.Create; + try + Self.FillFieldsWithSelectedItem(LFields); + + if LFields.Description.Trim.IsEmpty then + TUtils.ShowMsgErrorAndAbort('Description not found'); + + LView := TDelphiAIDevDBRegistersAddEditView.Create(nil); + try + LView.Caption := string(LView.Caption).Replace('[action]', 'Editing', [rfReplaceAll, rfIgnoreCase]); + LView.Fields := LFields; + if LView.ShowModal <> mrOk then + Exit; + + FMadeChanges := True; + finally + LView.Free; + end; + Self.ReloadData; + finally + LFields.Free; + end; +end; + +procedure TDelphiAIDevDBRegistersView.btnRemoveClick(Sender: TObject); +var + LGuid: string; +begin + if ListView.Selected = nil then + Exit; + + LGuid := ListView.Items[ListView.Selected.Index].SubItems[C_INDEX_SUBITEM_Guid]; + if LGuid.Trim.IsEmpty then + TUtils.ShowMsgErrorAndAbort('Guid not found'); + +// if TC4DWizardOpenExternalModel.New.ExistGuidInIniFile(LId) then +// TUtils.ShowMsgAndAbort('This registration cannot be deleted, as it is linked to other registration(s)'); + + if not TUtils.ShowQuestion2('Confirm remove?') then + Exit; + + Screen.Cursor := crHourGlass; + try + TDelphiAIDevDBRegistersModel.New.RemoveData(LGuid); + Self.ReloadData; + finally + FMadeChanges := True; + Screen.Cursor := crDefault; + end; +end; + +procedure TDelphiAIDevDBRegistersView.btnGenerateDatabaseReferenceClick(Sender: TObject); +var + LView: TDelphiAIDevDBReferencesView; + LFields: TDelphiAIDevDBRegistersFields; +begin + if ListView.Selected = nil then + Exit; + + LFields := TDelphiAIDevDBRegistersFields.Create; + try + Self.FillFieldsWithSelectedItem(LFields); + + if LFields.Description.Trim.IsEmpty then + TUtils.ShowMsgErrorAndAbort('Description not found'); + + LView := TDelphiAIDevDBReferencesView.Create(nil); + try + LView.Fields := LFields; + if LView.ShowModal = mrOk then + Self.ReloadData; + finally + LView.Free; + end; + finally + LFields.Free; + end; +end; + +end. diff --git a/Src/DB/Utils/DelphiAIDev.DB.Utils.pas b/Src/DB/Utils/DelphiAIDev.DB.Utils.pas new file mode 100644 index 0000000..649afbd --- /dev/null +++ b/Src/DB/Utils/DelphiAIDev.DB.Utils.pas @@ -0,0 +1,73 @@ +unit DelphiAIDev.DB.Utils; + +interface + +uses + System.SysUtils, + Vcl.StdCtrls; + +type + TDelphiAIDevDBUtils = class + private + public + class procedure ClearComboBox(const AComboBox: TComboBox); + class procedure FillComboBoxDatabases(const AComboBox: TComboBox; const AGuidDatabaseDefault: string); + end; + +implementation + +uses + DelphiAIDev.DB.Registers.Fields, + DelphiAIDev.DB.Registers.Model; + +class procedure TDelphiAIDevDBUtils.ClearComboBox(const AComboBox: TComboBox); +var + i: Integer; + LObj: TObject; +begin + for i := 0 to Pred(AComboBox.Items.Count) do + begin + if not Assigned(AComboBox.Items.Objects[i]) then + Continue; + + LObj := AComboBox.Items.Objects[i]; + + if LObj is TDelphiAIDevDBRegistersFields then + TDelphiAIDevDBRegistersFields(LObj).Free; + end; + + AComboBox.Items.Clear; +end; + +class procedure TDelphiAIDevDBUtils.FillComboBoxDatabases(const AComboBox: TComboBox; const AGuidDatabaseDefault: string); +var + LField: TDelphiAIDevDBRegistersFields; + LFieldDefault: TDelphiAIDevDBRegistersFields; +begin + Self.ClearComboBox(AComboBox); + LFieldDefault := nil; + + TDelphiAIDevDBRegistersModel.New.ReadData( + procedure(AFields: TDelphiAIDevDBRegistersFields) + begin + if (not AFields.Visible) or (AFields.Description.Trim.IsEmpty) then + Exit; + + if AFields.Visible then + begin + LField := TDelphiAIDevDBRegistersFields.Create; + LField.GetDataFromOtherObject(AFields); + AComboBox.Items.AddObject(LField.Description, LField); + + if LField.Guid = AGuidDatabaseDefault then + LFieldDefault := LField; + end; + end + ); + + AComboBox.ItemIndex := 0; + if LFieldDefault <> nil then + AComboBox.ItemIndex := AComboBox.Items.IndexOfObject(LFieldDefault); +end; + +end. diff --git a/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.AddEdit.View.dfm b/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.AddEdit.View.dfm index fd3cdd7..b659efd 100644 --- a/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.AddEdit.View.dfm +++ b/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.AddEdit.View.dfm @@ -89,7 +89,6 @@ object DelphiAIDevDefaultsQuestionsAddEditView: TDelphiAIDevDefaultsQuestionsAdd BevelOuter = bvNone ParentBackground = False TabOrder = 0 - ExplicitTop = -2 DesignSize = ( 674 350) diff --git a/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.AddEdit.View.pas b/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.AddEdit.View.pas index 8b41b12..b114e88 100644 --- a/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.AddEdit.View.pas +++ b/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.AddEdit.View.pas @@ -52,6 +52,7 @@ TDelphiAIDevDefaultsQuestionsAddEditView = class(TForm) procedure MenuMasterLoad; procedure MenuMasterClear; procedure ConfFieldsKind; + procedure FillScreenFields; public property Fields: TDelphiAIDevDefaultsQuestionsFields read FFields write FFields; end; @@ -91,12 +92,7 @@ procedure TDelphiAIDevDefaultsQuestionsAddEditView.FormDestroy(Sender: TObject); procedure TDelphiAIDevDefaultsQuestionsAddEditView.FormShow(Sender: TObject); begin - cBoxKind.ItemIndex := cBoxKind.Items.IndexOf(FFields.Kind.ToString); - edtCaption.Text := FFields.Caption; - mmQuestion.Lines.Text := FFields.Question; - edtOrder.Text := FFields.Order.Tostring; - ckVisible.Checked := FFields.Visible; - ckCodeOnly.Checked := FFields.CodeOnly; + Self.FillScreenFields; Self.ConfFieldsKind; Self.MenuMasterLoad; @@ -106,6 +102,16 @@ procedure TDelphiAIDevDefaultsQuestionsAddEditView.FormShow(Sender: TObject); cBoxKind.SetFocus; end; +procedure TDelphiAIDevDefaultsQuestionsAddEditView.FillScreenFields; +begin + cBoxKind.ItemIndex := cBoxKind.Items.IndexOf(FFields.Kind.ToString); + edtCaption.Text := FFields.Caption; + mmQuestion.Lines.Text := FFields.Question; + edtOrder.Text := FFields.Order.ToString; + ckVisible.Checked := FFields.Visible; + ckCodeOnly.Checked := FFields.CodeOnly; +end; + procedure TDelphiAIDevDefaultsQuestionsAddEditView.MenuMasterClear; var I: Integer; @@ -134,7 +140,7 @@ procedure TDelphiAIDevDefaultsQuestionsAddEditView.MenuMasterLoad; LFields: TDelphiAIDevDefaultsQuestionsFields; LItemIndex: Integer; begin - if(AFields.Kind <> TC4DQuestionKind.MenuMasterOnly)then + if AFields.Kind <> TC4DQuestionKind.MenuMasterOnly then Exit; LFields := TDelphiAIDevDefaultsQuestionsFields.Create; @@ -145,7 +151,7 @@ procedure TDelphiAIDevDefaultsQuestionsAddEditView.MenuMasterLoad; //if (FFields.IdParent > 0)and(FFields.IdParent = LFields.IdParent) then // LItemIndexDefault := LItemIndex; - if(FFields.GuidMenuMaster = LFields.Guid)then + if FFields.GuidMenuMaster = LFields.Guid then LItemIndexDefault := LItemIndex; end ); @@ -161,13 +167,13 @@ procedure TDelphiAIDevDefaultsQuestionsAddEditView.btnCloseClick(Sender: TObject procedure TDelphiAIDevDefaultsQuestionsAddEditView.btnConfirmClick(Sender: TObject); begin - if(cBoxKind.ItemIndex <= 0)then + if cBoxKind.ItemIndex <= 0 then TUtils.ShowMsgAndAbort('No informed Kind', cBoxKind); if Trim(edtCaption.Text).IsEmpty then TUtils.ShowMsgAndAbort('No informed Caption', edtCaption); - if (mmQuestion.Enabled) and (Trim(mmQuestion.Lines.Text).IsEmpty )then + if (mmQuestion.Enabled) and (Trim(mmQuestion.Lines.Text).IsEmpty)then TUtils.ShowMsgAndAbort('No informed Question', mmQuestion); FFields.Kind := TUtils.StrToDefaultsQuestionsKind(cBoxKind.Text); @@ -178,8 +184,8 @@ procedure TDelphiAIDevDefaultsQuestionsAddEditView.btnConfirmClick(Sender: TObje FFields.CodeOnly := ckCodeOnly.Checked; FFields.GuidMenuMaster := ''; - if(cBoxMenuMaster.ItemIndex >= 0)then - if(TDelphiAIDevDefaultsQuestionsFields(cBoxMenuMaster.Items.Objects[cBoxMenuMaster.ItemIndex]) <> nil)then + if cBoxMenuMaster.ItemIndex >= 0 then + if TDelphiAIDevDefaultsQuestionsFields(cBoxMenuMaster.Items.Objects[cBoxMenuMaster.ItemIndex]) <> nil then FFields.GuidMenuMaster := TDelphiAIDevDefaultsQuestionsFields(cBoxMenuMaster.Items.Objects[cBoxMenuMaster.ItemIndex]).Guid; TDelphiAIDevDefaultsQuestionsModel.New.SaveOrEditData(FFields); @@ -199,7 +205,7 @@ procedure TDelphiAIDevDefaultsQuestionsAddEditView.ConfFieldsKind; mmQuestion.Enabled := True; cBoxMenuMaster.Enabled := True; - if(cBoxKind.Text = TC4DQuestionKind.Separators.ToString)then + if cBoxKind.Text = TC4DQuestionKind.Separators.ToString then begin FLastCaption := edtCaption.Text; edtCaption.Text := '-'; @@ -209,9 +215,9 @@ procedure TDelphiAIDevDefaultsQuestionsAddEditView.ConfFieldsKind; mmQuestion.Lines.Clear; mmQuestion.Enabled := False; end - else if(cBoxKind.Text = TC4DQuestionKind.MenuMasterOnly.ToString)then + else if cBoxKind.Text = TC4DQuestionKind.MenuMasterOnly.ToString then begin - if(edtCaption.Text = '-')and(not FLastCaption.Trim.IsEmpty)then + if (edtCaption.Text = '-') and (not FLastCaption.Trim.IsEmpty) then edtCaption.Text := FLastCaption; FLastQuestion := mmQuestion.Lines.Text; @@ -224,29 +230,27 @@ procedure TDelphiAIDevDefaultsQuestionsAddEditView.ConfFieldsKind; end else begin - if(edtCaption.Text = '-')and(not FLastCaption.Trim.IsEmpty)then + if (edtCaption.Text = '-') and (not FLastCaption.Trim.IsEmpty) then edtCaption.Text := FLastCaption; - if(mmQuestion.Text = '')and(not FLastQuestion.Trim.IsEmpty)then + if (mmQuestion.Text = '') and (not FLastQuestion.Trim.IsEmpty) then mmQuestion.Text := FLastQuestion; end; - if(cBoxKind.Text <> TC4DQuestionKind.MenuMasterOnly.ToString)then - begin - if(cBoxMenuMaster.ItemIndex <= 0)then + if cBoxKind.Text <> TC4DQuestionKind.MenuMasterOnly.ToString then + if cBoxMenuMaster.ItemIndex <= 0 then cBoxMenuMaster.ItemIndex := FLastItemIndexMenuMaster; - end; end; procedure TDelphiAIDevDefaultsQuestionsAddEditView.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin case Key of VK_F4: - if ssAlt in Shift then - Key := 0; + if ssAlt in Shift then + Key := 0; VK_ESCAPE: - if Shift = [] then - btnClose.Click; + if Shift = [] then + btnClose.Click; end; end; diff --git a/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.PopupMenu.pas b/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.PopupMenu.pas index 8d1c903..6614170 100644 --- a/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.PopupMenu.pas +++ b/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.PopupMenu.pas @@ -107,8 +107,8 @@ procedure TDelphiAIDevDefaultsQuestionsPopupMenu.CreateMenuItemsList; for LOrder2 in LListOrder do for LItem2 in FList do begin - if(LItem2.Order = LOrder2)then - if(not LItem2.Created)and(LItem2.GuidMenuMaster.Trim = AFields.Guid.Trim)then + if LItem2.Order = LOrder2 then + if (not LItem2.Created) and (LItem2.GuidMenuMaster.Trim = AFields.Guid.Trim) then begin LMenuItem2 := Self.CreateSubMenu(AMenuItem, LItem2); LItem2.Created := True; @@ -129,10 +129,10 @@ procedure TDelphiAIDevDefaultsQuestionsPopupMenu.CreateMenuItemsList; try for LItem in FList do begin - if(LItem.Order <= 0)then + if LItem.Order <= 0 then LItem.Order := 9999; - if(not LListOrder.Contains(LItem.Order))then //(LItem.Order > 0)and + if not LListOrder.Contains(LItem.Order) then //(LItem.Order > 0)and LListOrder.Add(LItem.Order); end; @@ -142,8 +142,8 @@ procedure TDelphiAIDevDefaultsQuestionsPopupMenu.CreateMenuItemsList; for LOrder in LListOrder do for LItem in FList do begin - if(LItem.Order = LOrder)then - if(not LItem.Created)and(LITem.GuidMenuMaster.Trim.IsEmpty)then + if LItem.Order = LOrder then + if (not LItem.Created) and (LITem.GuidMenuMaster.Trim.IsEmpty) then begin LMenuItem := Self.CreateSubMenu(nil, LItem); LItem.Created := True; @@ -156,8 +156,8 @@ procedure TDelphiAIDevDefaultsQuestionsPopupMenu.CreateMenuItemsList; for LOrder in LListOrder do for LItem in FList do begin - if(LItem.Order = LOrder)then - if(not LItem.Created)then + if LItem.Order = LOrder then + if not LItem.Created then begin LMenuItem := Self.CreateSubMenu(nil, LItem); LItem.Created := True; diff --git a/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.View.pas b/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.View.pas index b69f5c9..296fcec 100644 --- a/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.View.pas +++ b/Src/DefaultsQuestions/DelphiAIDev.DefaultsQuestions.View.pas @@ -81,7 +81,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.FormShow(Sender: TObject); begin Self.ReloadData; - if(ListView.Items.Count > 0)then + if ListView.Items.Count > 0 then ListView.Items.Item[0].Selected := True; FMadeChanges := False; edtSearch.SetFocus; @@ -104,14 +104,14 @@ procedure TDelphiAIDevDefaultsQuestionsView.FormKeyDown(Sender: TObject; var Key btnClose.Click; VK_DOWN, VK_UP: begin - if(ListView <> ActiveControl)then + if ListView <> ActiveControl then begin case Key of VK_DOWN: - if(ListView.ItemIndex < Pred(ListView.Items.Count))then + if ListView.ItemIndex < Pred(ListView.Items.Count) then ListView.ItemIndex := ListView.ItemIndex + 1; VK_UP: - if(ListView.ItemIndex > 0)then + if ListView.ItemIndex > 0 then ListView.ItemIndex := ListView.ItemIndex - 1; end; Key := 0; @@ -127,7 +127,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.btnCloseClick(Sender: TObject); procedure TDelphiAIDevDefaultsQuestionsView.edtSearchKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin - if(Key = VK_RETURN)then + if Key = VK_RETURN then Self.ReloadData; end; @@ -159,7 +159,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.ReloadDataInternal; begin LStrSearch := LowerCase(edtSearch.Text); - if(ListView.Selected <> nil)then + if ListView.Selected <> nil then LGuid := ListView.Items[ListView.Selected.Index].SubItems[C_INDEX_SUBITEM_Guid]; ListView.Clear; @@ -173,7 +173,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.ReloadDataInternal; if (AFields.Kind = TC4DQuestionKind.ItemMenuNormal) and (AFields.Question.Trim.IsEmpty) then Exit; - if(LStrSearch.Trim.IsEmpty) + if (LStrSearch.Trim.IsEmpty) or(AFields.Caption.ToLower.Contains(LStrSearch)) or(AFields.Question.ToLower.Contains(LStrSearch)) then @@ -196,7 +196,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.ReloadDataInternal; .InvertOrder(False) .CustomSort; - if(not LGuid.Trim.IsEmpty)then + if not LGuid.Trim.IsEmpty then TUtils.FindListVewItem(ListView, C_INDEX_SUBITEM_Guid, LGuid); Self.FillStatusBar(ListView.Selected); @@ -212,7 +212,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.FillFieldsWithSelectedItem(var AFiel LListItem: TListItem; begin AFields.Clear; - if(ListView.Selected = nil)then + if ListView.Selected = nil then Exit; LListItem := ListView.Items[ListView.Selected.Index]; @@ -233,7 +233,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.FillStatusBar(AItem: TListItem); begin LIndex := -1; LQuestion := ''; - if(AItem <> nil)then + if AItem <> nil then begin LIndex := AItem.Index; LQuestion := ListView.Items[LIndex].SubItems[C_INDEX_SUBITEM_Question]; @@ -248,7 +248,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.ListViewColumnClick(Sender: TObject; LSortStyle: TDelphiAIDevUtilsListViewSortStyle; begin LSortStyle := TDelphiAIDevUtilsListViewSortStyle.AlphaNum; - case(Column.Index)of + case Column.Index of C_INDEX_SUBITEM_Order + 1: LSortStyle := TDelphiAIDevUtilsListViewSortStyle.Numeric; end; @@ -267,7 +267,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.ListViewDblClick(Sender: TObject); procedure TDelphiAIDevDefaultsQuestionsView.ListViewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin - if(Key = VK_RETURN)then + if Key = VK_RETURN then btnEdit.Click end; @@ -284,7 +284,7 @@ procedure TDelphiAIDevDefaultsQuestionsView.btnAddClick(Sender: TObject); LView.Caption := string(LView.Caption).Replace('[action]', 'Adding', [rfReplaceAll, rfIgnoreCase]); LView.Fields := LFields; - if(LView.ShowModal <> mrOk)then + if LView.ShowModal <> mrOk then Exit; FMadeChanges := True; @@ -302,21 +302,21 @@ procedure TDelphiAIDevDefaultsQuestionsView.btnEditClick(Sender: TObject); LFields: TDelphiAIDevDefaultsQuestionsFields; LView: TDelphiAIDevDefaultsQuestionsAddEditView; begin - if(ListView.Selected = nil)then + if ListView.Selected = nil then Exit; LFields := TDelphiAIDevDefaultsQuestionsFields.Create; try Self.FillFieldsWithSelectedItem(LFields); - if(LFields.Caption.Trim.IsEmpty)then + if LFields.Caption.Trim.IsEmpty then TUtils.ShowMsgErrorAndAbort('Caption not found'); LView := TDelphiAIDevDefaultsQuestionsAddEditView.Create(nil); try LView.Caption := string(LView.Caption).Replace('[action]', 'Editing', [rfReplaceAll, rfIgnoreCase]); LView.Fields := LFields; - if(LView.ShowModal <> mrOk)then + if LView.ShowModal <> mrOk then Exit; FMadeChanges := True; @@ -333,17 +333,17 @@ procedure TDelphiAIDevDefaultsQuestionsView.btnRemoveClick(Sender: TObject); var LGuid: string; begin - if(ListView.Selected = nil)then + if ListView.Selected = nil then Exit; LGuid := ListView.Items[ListView.Selected.Index].SubItems[C_INDEX_SUBITEM_Guid]; - if(LGuid.Trim.IsEmpty)then + if LGuid.Trim.IsEmpty then TUtils.ShowMsgErrorAndAbort('Guid not found'); -// if(TC4DWizardOpenExternalModel.New.ExistGuidInIniFile(LId))then +// if TC4DWizardOpenExternalModel.New.ExistGuidInIniFile(LId) then // TUtils.ShowMsgAndAbort('This registration cannot be deleted, as it is linked to other registration(s)'); - if(not TUtils.ShowQuestion2('Confirm remove?'))then + if not TUtils.ShowQuestion2('Confirm remove?') then Exit; Screen.Cursor := crHourGlass; diff --git a/Src/DelphiAIDev.Register.pas b/Src/DelphiAIDev.Register.pas index 709d890..6a38c15 100644 --- a/Src/DelphiAIDev.Register.pas +++ b/Src/DelphiAIDev.Register.pas @@ -4,8 +4,11 @@ interface uses DelphiAIDev.Chat.View, + DelphiAIDev.DB.Chat.View, DelphiAIDev.MainMenu.Register, - DelphiAIDev.IDE.Shortcuts; + DelphiAIDev.KeyboardBinding, + DelphiAIDev.PopupMenuProjects, + DelphiAIDev.IDE.OTAIDENotifier; procedure Register; @@ -14,8 +17,11 @@ implementation procedure Register; begin DelphiAIDev.Chat.View.RegisterSelf; + DelphiAIDev.DB.Chat.View.RegisterSelf; DelphiAIDev.MainMenu.Register.RegisterSelf; - DelphiAIDev.IDE.Shortcuts.RefreshRegister; + DelphiAIDev.KeyboardBinding.RefreshRegister; + DelphiAIDev.PopupMenuProjects.RegisterSelf; + DelphiAIDev.IDE.OTAIDENotifier.RegisterSelf; end; end. diff --git a/Src/IDE/ImageListMain/DelphiAIDev.IDE.ImageListMain.pas b/Src/IDE/ImageListMain/DelphiAIDev.IDE.ImageListMain.pas index ca39d1f..efe47d8 100644 --- a/Src/IDE/ImageListMain/DelphiAIDev.IDE.ImageListMain.pas +++ b/Src/IDE/ImageListMain/DelphiAIDev.IDE.ImageListMain.pas @@ -13,6 +13,9 @@ TDelphiAIDevIDEImageListMain = class FImgIndexGear: Integer; FImgIndexMessage: Integer; FImgQuestion: Integer; + FImgDatabase: Integer; + FImgDatabaseAdd: Integer; + FImgDatabaseExecute: Integer; constructor Create; public class function GetInstance: TDelphiAIDevIDEImageListMain; @@ -20,6 +23,9 @@ TDelphiAIDevIDEImageListMain = class property ImgIndexGear: Integer read FImgIndexGear; property ImgIndexMessage: Integer read FImgIndexMessage; property ImgQuestion: Integer read FImgQuestion; + property ImgDatabase: Integer read FImgDatabase; + property ImgDatabaseAdd: Integer read FImgDatabaseAdd; + property ImgDatabaseExecute: Integer read FImgDatabaseExecute; end; implementation @@ -32,7 +38,7 @@ implementation class function TDelphiAIDevIDEImageListMain.GetInstance: TDelphiAIDevIDEImageListMain; begin - if(not Assigned(Instance))then + if not Assigned(Instance) then Instance := Self.Create; Result := Instance; end; @@ -43,13 +49,16 @@ constructor TDelphiAIDevIDEImageListMain.Create; FImgIndexGear := TUtilsOTA.AddImgIDEResourceName('c4d_gear'); FImgIndexMessage := TUtilsOTA.AddImgIDEResourceName('c4d_message'); FImgQuestion := TUtilsOTA.AddImgIDEResourceName('c4d_question'); + FImgDatabase := TUtilsOTA.AddImgIDEResourceName('c4d_database'); + FImgDatabaseAdd := TUtilsOTA.AddImgIDEResourceName('c4d_database_add'); + FImgDatabaseExecute := TUtilsOTA.AddImgIDEResourceName('c4d_database_execute'); end; initialization Instance := TDelphiAIDevIDEImageListMain.GetInstance; finalization - if(Assigned(Instance))then + if Assigned(Instance) then Instance.Free; end. diff --git a/Src/IDE/NTAEditViewNotifier/DelphiAIDev.IDE.NTAEditViewNotifier.pas b/Src/IDE/NTAEditViewNotifier/DelphiAIDev.IDE.NTAEditViewNotifier.pas new file mode 100644 index 0000000..82bbc5d --- /dev/null +++ b/Src/IDE/NTAEditViewNotifier/DelphiAIDev.IDE.NTAEditViewNotifier.pas @@ -0,0 +1,210 @@ +unit DelphiAIDev.IDE.NTAEditViewNotifier; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + System.Types, + System.SyncObjs, + System.RegularExpressions, + Vcl.Dialogs, + Vcl.Graphics, + Vcl.Imaging.pngimage, + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA, + DelphiAIDev.Consts, + DelphiAIDev.CodeCompletion.Vars, + DelphiAIDev.Settings, + ToolsAPI; + +type + TDelphiAIDevIDENTAEditViewNotifier = class(TInterfacedObject, IOTANotifier, INTAEditViewNotifier) + private + LVars: TDelphiAIDevCodeCompletionVars; + protected + FIOTAEditView: IOTAEditView; + FIndex: Integer; + procedure RemoveNotifier; + public + constructor Create(FileName: string; AEditView: IOTAEditView); + destructor Destroy; override; + + { INTAEditViewNotifier } + ///EditorIdle chamado aps alguma ao ter ocorrido na visualizao + ///(edio, movimento do cursor, etc.) e um perodo de tempo ter passado sem que outra + ///ao acontecesse. Isso aproximadamente equivalente ao momento em que o Code + ///Insight acionado (e est vinculado configurao de atraso do Code Insight) + procedure EditorIdle(const View: IOTAEditView); + procedure BeginPaint(const View: IOTAEditView; var FullRepaint: Boolean); + procedure PaintLine(const View: IOTAEditView; LineNumber: Integer; + const LineText: PAnsiChar; const TextWidth: Word; const LineAttributes: TOTAAttributeArray; + const Canvas: TCanvas; const TextRect: TRect; const LineRect: TRect; const CellSize: TSize); + ///EndPaint chamado depois que todas as linhas foram repintadas. + ///Use isso para limpar quaisquer estruturas de dados que foram mantidas + ///ao longo da pintura das linhas + procedure EndPaint(const View: IOTAEditView); + + //IOTANotifier + procedure AfterSave; + procedure BeforeSave; + procedure Modified; + procedure Destroyed; + + //TDelphiAIDevIDENTAEditViewNotifier + procedure ClearLinesNotUsed; + end; + +implementation + +constructor TDelphiAIDevIDENTAEditViewNotifier.Create(FileName: string; AEditView: IOTAEditView); +begin + inherited Create; + FIOTAEditView := AEditView; + FIndex := FIOTAEditView.AddNotifier(Self); + LVars := TDelphiAIDevCodeCompletionVars.GetInstance; +end; + +destructor TDelphiAIDevIDENTAEditViewNotifier.Destroy; +begin + Self.RemoveNotifier; + inherited; +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.Destroyed; +begin + Self.RemoveNotifier; +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.AfterSave; +begin + +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.BeforeSave; +begin + +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.ClearLinesNotUsed; +var + LView: IOTAEditView; + LColCurrent: Integer; + LLineCurrent: Integer; +begin + ////LVars.Release := False; + if LVars.Module = nil then + Exit; + + //** + if LVars.Contents.Count <= 1 then + Exit; + //** + + LView := TUtilsOTA.GetIOTAEditView(LVars.Module); + LColCurrent := LView.CursorPos.Col; + LLineCurrent := LView.CursorPos.Line; + try + LView.Buffer.EditPosition.Move(LVars.LineIni, 2); + LView.Buffer.EditPosition.Delete(Pred(LVars.Contents.Count)); + finally + LView.Buffer.EditPosition.Move(LLineCurrent, LColCurrent); + end; +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.BeginPaint(const View: IOTAEditView; var FullRepaint: Boolean); +begin + FullRepaint := True; +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.EditorIdle(const View: IOTAEditView); +var + LRow: Integer; + LColumn: Integer; +begin + if LVars.LineIni <= 0 then + Exit; + + TUtilsOTA.GetCursorPosition(LRow, LColumn); + + if (LRow <> LVars.Row) or (LColumn <> LVars.Column) then + begin + TUtils.AddLog('EditorIdle'); + + ////LVars.Release := True; + try + Self.ClearLinesNotUsed; + finally + LVars.Clear; + end; + end; +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.EndPaint(const View: IOTAEditView); +begin + +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.Modified; +begin + +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.PaintLine(const View: IOTAEditView; LineNumber: Integer; + const LineText: PAnsiChar; const TextWidth: Word; const LineAttributes: TOTAAttributeArray; + const Canvas: TCanvas; const TextRect: TRect; const LineRect: TRect; const CellSize: TSize); +var + LLineText: string; +begin + if LineNumber < 1 then + Exit; + + LLineText := string(LineText); + + //AQUI VERIFICA SE A LINHA NAO FOR VAZIA NAO ADICIONA O TEXTO DA SUGESTAO + //if not LLineText.Trim.IsEmpty then Exit; + + //if LineNumber <> View.CursorPos.Line then Exit; + +// if LVars.Release then +// begin +// try +// Self.ClearLinesNotUsed; +// finally +// LVars.Clear; +// end; +// end; + + if (LineNumber >= LVars.LineIni) and (LineNumber < LVars.LineEnd) then + begin + Canvas.Brush.Style := bsClear; + Canvas.Font.Color := $777777; + if TDelphiAIDevSettings.GetInstance.CodeCompletionSuggestionColorUse then + Canvas.Font.Color := TDelphiAIDevSettings.GetInstance.CodeCompletionSuggestionColor; + + try + LLineText := LVars.Contents[LineNumber - LVars.LineIni]; + Canvas.TextOut(TextRect.Left, TextRect.Top, LLineText.TrimRight); + except on E: Exception do + if TUtils.DebugMyIsOn then + TUtils.AddLog('Exception in TDelphiAIDevIDENTAEditViewNotifier.PaintLine: ' + sLineBreak + + 'LineNumber: ' + LineNumber.ToString + sLineBreak + + 'LineIni: ' + LVars.LineIni.ToString + sLineBreak + + 'LineEnd: ' + LVars.LineEnd.ToString + sLineBreak + + E.Message); + end; + end; +end; + +procedure TDelphiAIDevIDENTAEditViewNotifier.RemoveNotifier; +begin + if Assigned(FIOTAEditView) and (FIndex >= 0) then + begin + FIOTAEditView.RemoveNotifier(FIndex); + FIndex := -1; + FIOTAEditView := nil; + end; +end; + +end. diff --git a/Src/IDE/OTAEditorNotifier/DelphiAIDev.IDE.OTAEditorNotifier.pas b/Src/IDE/OTAEditorNotifier/DelphiAIDev.IDE.OTAEditorNotifier.pas new file mode 100644 index 0000000..f1a7998 --- /dev/null +++ b/Src/IDE/OTAEditorNotifier/DelphiAIDev.IDE.OTAEditorNotifier.pas @@ -0,0 +1,92 @@ +unit DelphiAIDev.IDE.OTAEditorNotifier; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + System.Types, + System.SyncObjs, + System.RegularExpressions, + Vcl.Dialogs, + Vcl.Graphics, + Vcl.Imaging.pngimage, + ToolsAPI, + DelphiAIDev.Utils, + DelphiAIDev.Consts, + DelphiAIDev.CodeCompletion.Vars, + DelphiAIDev.IDE.NTAEditViewNotifier; + +type + TDelphiAIDevIDEOTAEditorNotifier = class(TNotifierObject, IOTANotifier, IOTAEditorNotifier) + private + FIOTASourceEditor: IOTASourceEditor; + FINTAEditViewNotifierList: TList; + FIndex: Integer; + procedure RemoveNotifiers; + public + constructor Create(ASourceEditor: IOTASourceEditor); + destructor Destroy; override; + { IOTANotifier } + procedure Destroyed; + { IOTAEditorNotifier } + procedure ViewNotification(const View: IOTAEditView; Operation: TOperation); + procedure ViewActivated(const View: IOTAEditView); + end; + +implementation + +constructor TDelphiAIDevIDEOTAEditorNotifier.Create(ASourceEditor: IOTASourceEditor); +var + i: Integer; +begin + inherited Create; + FINTAEditViewNotifierList := TList.Create; + FIOTASourceEditor := ASourceEditor; + + FIndex := FIOTASourceEditor.AddNotifier(Self); + for i := 0 to Pred(FIOTASourceEditor.EditViewCount) do + FINTAEditViewNotifierList.Add(TDelphiAIDevIDENTAEditViewNotifier.Create(FIOTASourceEditor.FileName, FIOTASourceEditor.EditViews[i])); +end; + +destructor TDelphiAIDevIDEOTAEditorNotifier.Destroy; +begin + RemoveNotifiers; + FINTAEditViewNotifierList.Free; + inherited; +end; + +procedure TDelphiAIDevIDEOTAEditorNotifier.Destroyed; +begin + RemoveNotifiers; +end; + +procedure TDelphiAIDevIDEOTAEditorNotifier.RemoveNotifiers; +var + i: Integer; +begin + for i := 0 to Pred(FINTAEditViewNotifierList.Count) do + FINTAEditViewNotifierList[i].Destroyed; + FINTAEditViewNotifierList.Clear; + + if Assigned(FIOTASourceEditor) and (FIndex >= 0) then + begin + FIOTASourceEditor.RemoveNotifier(FIndex); + FIndex := -1; + FIOTASourceEditor := nil; + end; +end; + +procedure TDelphiAIDevIDEOTAEditorNotifier.ViewActivated(const View: IOTAEditView); +begin + +end; + +procedure TDelphiAIDevIDEOTAEditorNotifier.ViewNotification(const View: IOTAEditView; Operation: TOperation); +begin + if Operation = opInsert then + FINTAEditViewNotifierList.Add(TDelphiAIDevIDENTAEditViewNotifier.Create(FIOTASourceEditor.FileName, View)); +end; + +end. diff --git a/Src/IDE/OTAIDENotifier/DelphiAIDev.IDE.OTAIDENotifier.pas b/Src/IDE/OTAIDENotifier/DelphiAIDev.IDE.OTAIDENotifier.pas new file mode 100644 index 0000000..d31ed98 --- /dev/null +++ b/Src/IDE/OTAIDENotifier/DelphiAIDev.IDE.OTAIDENotifier.pas @@ -0,0 +1,134 @@ +unit DelphiAIDev.IDE.OTAIDENotifier; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + System.Types, + System.SyncObjs, + System.RegularExpressions, + Vcl.Dialogs, + Vcl.Graphics, + Vcl.Imaging.pngimage, + ToolsAPI, + DelphiAIDev.Utils, + DelphiAIDev.Consts, + DelphiAIDev.Types, + DelphiAIDev.CodeCompletion.Vars, + DelphiAIDev.IDE.OTAEditorNotifier; + +type + TDelphiAIDevIDEOTAIDENotifier = class(TNotifierObject, IOTANotifier, IOTAIDENotifier) + private + FEditorNotifiers: TList; + protected + procedure FileNotification(NotifyCode: TOTAFileNotification;const FileName: string; var Cancel: Boolean); + procedure BeforeCompile(const Project: IOTAProject; var Cancel: Boolean); overload; + procedure AfterCompile(Succeeded: Boolean); overload; + public + constructor Create; + destructor Destroy; override; + end; + + procedure RegisterSelf; + +implementation + +var + Index: Integer; + +procedure RegisterSelf; +begin + Index := (BorlandIDEServices as IOTAServices).AddNotifier(TDelphiAIDevIDEOTAIDENotifier.Create); +end; + +procedure TDelphiAIDevIDEOTAIDENotifier.AfterCompile(Succeeded: Boolean); +begin + // +end; + +procedure TDelphiAIDevIDEOTAIDENotifier.BeforeCompile(const Project: IOTAProject; var Cancel: Boolean); +begin + Cancel := False; +end; + +constructor TDelphiAIDevIDEOTAIDENotifier.Create; +var + LIOTAModuleServices: IOTAModuleServices; + i: Integer; + j: Integer; + LIOTAModule: IOTAModule; + LIOTASourceEditor: IOTASourceEditor; + LExt: string; +begin + FEditorNotifiers := TList.Create; + LIOTAModuleServices := BorlandIDEServices as IOTAModuleServices; + for i := 0 to Pred(LIOTAModuleServices.ModuleCount) do + begin + LIOTAModule := LIOTAModuleServices.Modules[i]; + + for j := 0 to Pred(LIOTAModule.ModuleFileCount) do + if Supports(LIOTAModule.ModuleFileEditors[j], IOTASourceEditor, LIOTASourceEditor) then + begin + LExt := ExtractFileExt(LIOTASourceEditor.FileName).ToLower; + if LExt.Equals(TC4DExtensionsFiles.PAS.ToStringWithPoint) then + FEditorNotifiers.Add(TDelphiAIDevIDEOTAEditorNotifier.Create(LIOTASourceEditor)); + end; + end; + + inherited; +end; + +destructor TDelphiAIDevIDEOTAIDENotifier.Destroy; +var + i: Integer; +begin + for i := 0 to Pred(FEditorNotifiers.Count) do + FEditorNotifiers[i].Destroyed; + FEditorNotifiers.Free; + inherited; +end; + +procedure TDelphiAIDevIDEOTAIDENotifier.FileNotification(NotifyCode: TOTAFileNotification; const FileName: string; var Cancel: Boolean); +var + LIOTAModule: IOTAModule; + LExtension: string; + i: Integer; + LIOTASourceEditor: IOTASourceEditor; +begin + Cancel := False; + + if NotifyCode = ofnFileOpened then + begin + LIOTAModule := (BorlandIDEServices as IOTAModuleServices).FindModule(FileName); + + if not Assigned(LIOTAModule) then + Exit; + + for i := 0 to Pred(LIOTAModule.ModuleFileCount) do + if Supports(LIOTAModule.ModuleFileEditors[i], IOTASourceEditor, LIOTASourceEditor) then + begin + LExtension := ExtractFileExt(LIOTASourceEditor.FileName).ToLower; + if LExtension.Equals(TC4DExtensionsFiles.PAS.ToStringWithPoint) then + FEditorNotifiers.Add(TDelphiAIDevIDEOTAEditorNotifier.Create(LIOTASourceEditor)); + end; + end; + + if (NotifyCode <> ofnFileOpened) and (NotifyCode <> ofnFileClosing) then + Exit; + + LExtension := ExtractFileExt(FileName).ToLower; + if (LExtension <> TC4DExtensionsFiles.DPROJ.ToStringWithPoint) + and (LExtension <> TC4DExtensionsFiles.GROUPPROJ.ToStringWithPoint) + then + Exit; +end; + +initialization + +finalization + if Index >= 0 then + (BorlandIDEServices as IOTAServices).RemoveNotifier(Index); +end. diff --git a/Src/IDE/Shortcuts/DelphiAIDev.IDE.Shortcuts.pas b/Src/IDE/Shortcuts/DelphiAIDev.IDE.Shortcuts.pas deleted file mode 100644 index 6ca9253..0000000 --- a/Src/IDE/Shortcuts/DelphiAIDev.IDE.Shortcuts.pas +++ /dev/null @@ -1,124 +0,0 @@ -unit DelphiAIDev.IDE.Shortcuts; - -interface - -uses - System.SysUtils, - System.Classes, - Winapi.Windows, - Vcl.Menus, - ToolsAPI, - DelphiAIDev.Utils.CnWizard, - DelphiAIDev.Chat.View; - -type - TDelphiAIDevIDEShortcuts = class(TNotifierObject, IOTAKeyboardBinding) - private - //procedure KeyProcBlockReturn(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); - procedure KeyProcBlockReturnAndAlt(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); - protected - function GetBindingType: TBindingType; - function GetDisplayName: string; - function GetName: string; - procedure BindKeyboard(const BindingServices: IOTAKeyBindingServices); - public - class function New: IOTAKeyboardBinding; - end; - -procedure RefreshRegister; - -implementation - -uses - DelphiAIDev.Utils, - DelphiAIDev.Utils.OTA; - -var - Index: Integer = -1; - -procedure RegisterSelf; -begin - if Index < 0 then - Index := TUtilsOTA.GetIOTAKeyboardServices.AddKeyboardBinding(TDelphiAIDevIDEShortcuts.New); -end; - -procedure UnRegisterSelf; -begin - if Index >= 0 then - begin - TUtilsOTA.GetIOTAKeyboardServices.RemoveKeyboardBinding(Index); - Index := -1; - end; -end; - -procedure RefreshRegister; -begin - UnRegisterSelf; - RegisterSelf; -end; - -class function TDelphiAIDevIDEShortcuts.New: IOTAKeyboardBinding; -begin - Result := Self.Create; -end; - -function TDelphiAIDevIDEShortcuts.GetBindingType: TBindingType; -begin - Result := btPartial; -end; - -function TDelphiAIDevIDEShortcuts.GetDisplayName: string; -begin - Result := Self.ClassName; -end; - -function TDelphiAIDevIDEShortcuts.GetName: string; -begin - Result := Self.ClassName; -end; - -procedure TDelphiAIDevIDEShortcuts.BindKeyboard(const BindingServices: IOTAKeyBindingServices); -begin -// if TUtilsOTA.CurrentProjectIsDelphiAIDeveloperDPROJ then -// Exit; - - //BindingServices.AddKeyBinding([Shortcut(VK_RETURN, [])], Self.KeyProcBlockReturn, nil); - BindingServices.AddKeyBinding([Shortcut(VK_RETURN, [ssAlt])], Self.KeyProcBlockReturnAndAlt, nil); -end; - -//procedure TDelphiAIDevIDEShortcuts.KeyProcBlockReturn(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); -//begin -// if KeyCode <> Shortcut(VK_RETURN, []) then -// Exit; -// -// TUtils.AddLog(GetCurrentLineOrBlock(CnOtaGetTopMostEditView)); -// BindingResult := TKeyBindingResult.krNextProc; //krUnhandled; -//end; - -procedure TDelphiAIDevIDEShortcuts.KeyProcBlockReturnAndAlt(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); -var - LTextCurrentLineOrBlock: string; -begin - if KeyCode <> Shortcut(VK_RETURN, [ssAlt]) then - Exit; - - //LTextCurrentLineOrBlock := Context.EditBuffer.EditBlock.Text; - LTextCurrentLineOrBlock := GetCurrentLineOrBlock(CnOtaGetTopMostEditView).Trim; - if LTextCurrentLineOrBlock.Trim.IsEmpty then - Exit; - - if copy(LTextCurrentLineOrBlock, 1, 2) = '//' then - LTextCurrentLineOrBlock := copy(LTextCurrentLineOrBlock, 3, LTextCurrentLineOrBlock.Length); - - DelphiAIDev.Chat.View.DelphiAIDevChatView.QuestionOnShow := LTextCurrentLineOrBlock; - DelphiAIDev.Chat.View.DelphiAIDevChatViewShowDockableForm; - - BindingResult := TKeyBindingResult.krUnhandled; //krNextProc; -end; - -initialization - -finalization - UnRegisterSelf; - -end. diff --git a/Src/KeyboardBinding/DelphiAIDev.KeyboardBinding.pas b/Src/KeyboardBinding/DelphiAIDev.KeyboardBinding.pas new file mode 100644 index 0000000..ffa28da --- /dev/null +++ b/Src/KeyboardBinding/DelphiAIDev.KeyboardBinding.pas @@ -0,0 +1,167 @@ +unit DelphiAIDev.KeyboardBinding; + +interface + +uses + System.SysUtils, + System.Classes, + Winapi.Windows, + Vcl.Menus, + ToolsAPI, + DelphiAIDev.Consts, + DelphiAIDev.Utils.CnWizard, + DelphiAIDev.Chat.View, + DelphiAIDev.CodeCompletion.Search, + DelphiAIDev.CodeCompletion.KeyTab, + DelphiAIDev.Settings; + +type + TDelphiAIDevKeyboardBinding = class(TNotifierObject, IOTAKeyboardBinding) + private + procedure KeyAltHome(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); + procedure KeyTab(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); + procedure CodeCompletionSearch(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); + //procedure HandleKeyBinding(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); + protected + function GetBindingType: TBindingType; + function GetDisplayName: string; + function GetName: string; + procedure BindKeyboard(const BindingServices: IOTAKeyBindingServices); + public + class function New: IOTAKeyboardBinding; + end; + +procedure RefreshRegister; + +implementation + +uses + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA, + DelphiAIDev.CodeCompletion.Vars; + +var + Index: Integer = -1; + +procedure RegisterSelf; +begin + if Index < 0 then + Index := TUtilsOTA.GetIOTAKeyboardServices.AddKeyboardBinding(TDelphiAIDevKeyboardBinding.New); +end; + +procedure UnRegisterSelf; +begin + if Index >= 0 then + begin + TUtilsOTA.GetIOTAKeyboardServices.RemoveKeyboardBinding(Index); + Index := -1; + end; +end; + +procedure RefreshRegister; +begin + UnRegisterSelf; + RegisterSelf; +end; + +class function TDelphiAIDevKeyboardBinding.New: IOTAKeyboardBinding; +begin + Result := Self.Create; +end; + +function TDelphiAIDevKeyboardBinding.GetBindingType: TBindingType; +begin + Result := btPartial; +end; + +function TDelphiAIDevKeyboardBinding.GetDisplayName: string; +begin + Result := Self.ClassName; +end; + +function TDelphiAIDevKeyboardBinding.GetName: string; +begin + Result := Self.ClassName; +end; + +procedure TDelphiAIDevKeyboardBinding.BindKeyboard(const BindingServices: IOTAKeyBindingServices); +var + LShortcut: string; +begin + //if TUtilsOTA.CurrentProjectIsDelphiAIDeveloperDPROJ then + // Exit; + + LShortcut := TConsts.CODE_COMPLETION_SHORTCUT_INVOKE; + if not Trim(TDelphiAIDevSettings.GetInstance.CodeCompletionShortcutInvoke).IsEmpty then + LShortcut := TDelphiAIDevSettings.GetInstance.CodeCompletionShortcutInvoke; + + BindingServices.AddKeyBinding([TextToShortCut(LShortcut)], Self.CodeCompletionSearch, nil); + + BindingServices.AddKeyBinding([Shortcut(VK_TAB, [])], Self.KeyTab, nil); + BindingServices.AddKeyBinding([Shortcut(VK_HOME, [ssAlt])], Self.KeyAltHome, nil); + +// //** +// BindingServices.AddKeyBinding([], HandleKeyBinding, nil); +// //** +end; + +////** +//procedure TDelphiAIDevKeyboardBinding.HandleKeyBinding(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); +//var +// Key: Word; +//begin +// //Key := Word(KeyCode and $FF); // Extrai o cdigo da tecla +// //TUtils.AddLog('Tecla pressionada: ' + IntToStr(Key)); +// TUtils.ShowMsg('Aqui'); +// BindingResult := TKeyBindingResult.krNextProc +//end; +////** + +procedure TDelphiAIDevKeyboardBinding.CodeCompletionSearch(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); +begin + try + if TDelphiAIDevSettings.GetInstance.CodeCompletionUse then + TDelphiAIDevCodeCompletionSearch.New.Process(Context); + finally + //BindingResult := TKeyBindingResult.krUnhandled; + BindingResult := TKeyBindingResult.krNextProc; //krHandled; + end; +end; + +procedure TDelphiAIDevKeyboardBinding.KeyTab(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); +begin +// if KeyCode <> Shortcut(VK_TAB, []) then +// Exit; + + BindingResult := TKeyBindingResult.krUnhandled; + + if TDelphiAIDevCodeCompletionVars.GetInstance.LineIni > 0 then + begin + TDelphiAIDevCodeCompletionKeyTab.New.Process(Context); + BindingResult := TKeyBindingResult.krNextProc; //krHandled; + end; +end; + +procedure TDelphiAIDevKeyboardBinding.KeyAltHome(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult); +var + LTextCurrentLineOrBlock: string; +begin + LTextCurrentLineOrBlock := GetCurrentLineOrBlock(CnOtaGetTopMostEditView).Trim; + if LTextCurrentLineOrBlock.Trim.IsEmpty then + Exit; + + if copy(LTextCurrentLineOrBlock, 1, 2) = '//' then + LTextCurrentLineOrBlock := copy(LTextCurrentLineOrBlock, 3, LTextCurrentLineOrBlock.Length); + + DelphiAIDev.Chat.View.DelphiAIDevChatView.QuestionOnShow := LTextCurrentLineOrBlock; + DelphiAIDev.Chat.View.DelphiAIDevChatViewShowDockableForm; + + BindingResult := TKeyBindingResult.krUnhandled; +end; + +initialization + +finalization + UnRegisterSelf; + +end. diff --git a/Src/MainMenu/DelphiAIDev.MainMenu.Clicks.pas b/Src/MainMenu/DelphiAIDev.MainMenu.Clicks.pas index f07deb0..5dd7993 100644 --- a/Src/MainMenu/DelphiAIDev.MainMenu.Clicks.pas +++ b/Src/MainMenu/DelphiAIDev.MainMenu.Clicks.pas @@ -10,10 +10,11 @@ interface type TDelphiAIDevIDEMainMenuClicks = class - private public class procedure ChatClick(Sender: TObject); class procedure DefaultsQuestionsClick(Sender: TObject); + class procedure DatabasesAddClick(Sender: TObject); + class procedure DatabasesChatClick(Sender: TObject); class procedure SettingsClick(Sender: TObject); class procedure AboutClick(Sender: TObject); end; @@ -25,10 +26,11 @@ implementation DelphiAIDev.Utils.OTA, DelphiAIDev.Chat.View, DelphiAIDev.DefaultsQuestions.View, + DelphiAIDev.DB.Registers.View, + DelphiAIDev.DB.Chat.View, DelphiAIDev.Settings.View, DelphiAIDev.View.About; - class procedure TDelphiAIDevIDEMainMenuClicks.ChatClick(Sender: TObject); begin DelphiAIDev.Chat.View.DelphiAIDevChatViewShowDockableForm; @@ -46,6 +48,23 @@ class procedure TDelphiAIDevIDEMainMenuClicks.DefaultsQuestionsClick(Sender: TOb end; end; +class procedure TDelphiAIDevIDEMainMenuClicks.DatabasesAddClick(Sender: TObject); +var + LView: TDelphiAIDevDBRegistersView; +begin + LView := TDelphiAIDevDBRegistersView.Create(nil); + try + LView.ShowModal; + finally + FreeAndNil(LView); + end; +end; + +class procedure TDelphiAIDevIDEMainMenuClicks.DatabasesChatClick(Sender: TObject); +begin + DelphiAIDev.DB.Chat.View.DelphiAIDevDBChatViewShowDockableForm; +end; + class procedure TDelphiAIDevIDEMainMenuClicks.SettingsClick(Sender: TObject); begin DelphiAIDevSettingsView := TDelphiAIDevSettingsView.Create(nil); diff --git a/Src/MainMenu/DelphiAIDev.MainMenu.Register.pas b/Src/MainMenu/DelphiAIDev.MainMenu.Register.pas index aa7c6f0..d5e88c7 100644 --- a/Src/MainMenu/DelphiAIDev.MainMenu.Register.pas +++ b/Src/MainMenu/DelphiAIDev.MainMenu.Register.pas @@ -39,7 +39,7 @@ procedure RegisterSelf; begin DelphiAIDevIDEMainMenuRegister := TDelphiAIDevIDEMainMenuRegister.Create; - if(Supports(TObject(DelphiAIDevIDEMainMenuRegister), IOTAWizard, LIOTAWizard))then + if Supports(TObject(DelphiAIDevIDEMainMenuRegister), IOTAWizard, LIOTAWizard) then IndexPlugin := TUtilsOTA.GetIOTAWizardServices.AddWizard(LIOTAWizard); end; diff --git a/Src/MainMenu/DelphiAIDev.MainMenu.pas b/Src/MainMenu/DelphiAIDev.MainMenu.pas index 65d8dac..2c25baf 100644 --- a/Src/MainMenu/DelphiAIDev.MainMenu.pas +++ b/Src/MainMenu/DelphiAIDev.MainMenu.pas @@ -39,7 +39,7 @@ implementation class function TDelphiAIDevIDEMainMenu.GetInstance: IDelphiAIDevIDEMainMenu; begin - if(not Assigned(Instance))then + if not Assigned(Instance) then Instance := Self.Create; Result := Instance; end; @@ -51,45 +51,56 @@ constructor TDelphiAIDevIDEMainMenu.Create; destructor TDelphiAIDevIDEMainMenu.Destroy; begin - if(Assigned(FMenuItemC4D))then + if Assigned(FMenuItemC4D) then FreeAndNil(FMenuItemC4D); inherited; end; procedure TDelphiAIDevIDEMainMenu.CreateMenus; +var + LImgList: TDelphiAIDevIDEImageListMain; begin + LImgList := TDelphiAIDevIDEImageListMain.GetInstance; + Self.CreateMenuDelphiAIDeveloperInIDEMenu; Self.CreateSubMenu(TConsts.MENU_IDE_CHAT_NAME, TConsts.MENU_IDE_CHAT_CAPTION, TDelphiAIDevIDEMainMenuClicks.ChatClick, - TDelphiAIDevIDEImageListMain.GetInstance.ImgIndexMessage, - Self.GetShortCutStrChat - ); + LImgList.ImgIndexMessage, + Self.GetShortCutStrChat); - Self.CreateSubMenu('C4DSeparator40', '-', nil); + Self.CreateSubMenu(TConsts.PREFIX_NAME_SEPARATOR + '10', '-', nil); Self.CreateSubMenu(TConsts.MENU_IDE_DEFAULTS_QUESTIONS_NAME, TConsts.MENU_IDE_DEFAULTS_QUESTIONS_CAPTION, TDelphiAIDevIDEMainMenuClicks.DefaultsQuestionsClick, - TDelphiAIDevIDEImageListMain.GetInstance.ImgQuestion - ); + LImgList.ImgQuestion); + + Self.CreateSubMenu(TConsts.PREFIX_NAME_SEPARATOR + '20', '-', nil); - Self.CreateSubMenu('C4DSeparator50', '-', nil); + Self.CreateSubMenu(TConsts.MENU_IDE_DEFAULTS_DATABASES_ADD_NAME, + TConsts.MENU_IDE_DEFAULTS_DATABASES_ADD_Caption, + TDelphiAIDevIDEMainMenuClicks.DatabasesAddClick, + LImgList.ImgDatabaseAdd); + + Self.CreateSubMenu(TConsts.MENU_IDE_DEFAULTS_DATABASES_CHAT_NAME, + TConsts.MENU_IDE_DEFAULTS_DATABASES_CHAT_CAPTION, + TDelphiAIDevIDEMainMenuClicks.DatabasesChatClick, + LImgList.ImgDatabaseExecute); + + Self.CreateSubMenu(TConsts.PREFIX_NAME_SEPARATOR + '30', '-', nil); Self.CreateSubMenu(TConsts.MENU_IDE_SETTINGS_NAME, TConsts.MENU_IDE_SETTINGS_CAPTION, TDelphiAIDevIDEMainMenuClicks.SettingsClick, - TDelphiAIDevIDEImageListMain.GetInstance.ImgIndexGear - ); - + LImgList.ImgIndexGear); - Self.CreateSubMenu('C4DSeparator90', '-', nil); + Self.CreateSubMenu(TConsts.PREFIX_NAME_SEPARATOR + '40', '-', nil); Self.CreateSubMenu(TConsts.MENU_IDE_ABOUT_NAME, TConsts.MENU_IDE_ABOUT_CAPTION, TDelphiAIDevIDEMainMenuClicks.AboutClick, - TDelphiAIDevIDEImageListMain.GetInstance.ImgIndexC4D_Logo - ); + LImgList.ImgIndexC4D_Logo); end; procedure TDelphiAIDevIDEMainMenu.CreateMenuDelphiAIDeveloperInIDEMenu; @@ -98,7 +109,7 @@ procedure TDelphiAIDevIDEMainMenu.CreateMenuDelphiAIDeveloperInIDEMenu; LMenuItemTools: TMenuItem; begin FMenuItemC4D := TMenuItem(FMainMenuIDE.FindComponent(TConsts.ITEM_MENU_C4DDelphiAIDev_NAME)); - if(Assigned(FMenuItemC4D))then + if Assigned(FMenuItemC4D) then FreeAndNil(FMenuItemC4D); FMenuItemC4D := TMenuItem.Create(FMainMenuIDE); @@ -106,14 +117,14 @@ procedure TDelphiAIDevIDEMainMenu.CreateMenuDelphiAIDeveloperInIDEMenu; FMenuItemC4D.Caption := TConsts.ITEM_MENU_C4DDelphiAIDev_CAPTION; LMenuItemTabs := FMainMenuIDE.Items.Find('Tabs'); - if(Assigned(LMenuItemTabs))then + if Assigned(LMenuItemTabs) then begin FMainMenuIDE.Items.Insert(FMainMenuIDE.Items.IndexOf(LMenuItemTabs), FMenuItemC4D); Exit; end; LMenuItemTools := FMainMenuIDE.Items.Find('Tools'); - if(Assigned(LMenuItemTools))then + if Assigned(LMenuItemTools) then begin FMainMenuIDE.Items.Insert(FMainMenuIDE.Items.IndexOf(LMenuItemTools) + 1, FMenuItemC4D); Exit; @@ -138,13 +149,13 @@ function TDelphiAIDevIDEMainMenu.CreateSubMenu(AName: string; ACaption: string; function TDelphiAIDevIDEMainMenu.GetShortCutStrChat: string; begin - Result := 'Ctrl+Shift+Alt+A'; + Result := TConsts.SHORTCUT_CHAT_DEFAULT; end; initialization finalization - if(Assigned(Instance))then + if Assigned(Instance) then Instance := nil; end. diff --git a/Src/MetaInfo/DelphiAIDev.MetaInfo.pas b/Src/MetaInfo/DelphiAIDev.MetaInfo.pas new file mode 100644 index 0000000..e12b6dc --- /dev/null +++ b/Src/MetaInfo/DelphiAIDev.MetaInfo.pas @@ -0,0 +1,219 @@ +unit DelphiAIDev.MetaInfo; + +interface + +uses + System.SysUtils, + System.Classes, + System.Json, + FireDAC.Stan.Intf, + FireDAC.Stan.Option, + FireDAC.Stan.Param, + FireDAC.Stan.Error, + FireDAC.DatS, + FireDAC.Phys.Intf, + FireDAC.DApt.Intf, + FireDAC.Stan.Async, + FireDAC.DApt, + Data.DB, + FireDAC.Comp.DataSet, + FireDAC.Comp.Client, + C4D.Conn, + DelphiAIDev.Utils, + DelphiAIDev.Types, + DelphiAIDev.DB.Registers.Fields, + DelphiAIDev.DB.Registers.Model; + +type + TDelphiAIDevMetaInfo = class + private + FField: TDelphiAIDevDBRegistersFields; + FC4DConn: IC4DConn; + FMetaInfoTables: TFDMetaInfoQuery; + FMetaInfoFields: TFDMetaInfoQuery; + FAddFieldLength: Boolean; + FCompressData: Boolean; + + FKeyName: string; + FKeyTYPE: string; + FKeyLength: string; + FKeyColumns: string; + + procedure ConfigConn; + procedure SaveJsonInFolder(const AJSONObject: TJSONObject); + procedure SaveGenerationDataToField; + function GetInstructionsNamesKeysCompress: string; + procedure ProcessNamesKeys; + public + constructor Create(const AField: TDelphiAIDevDBRegistersFields); + destructor Destroy; override; + procedure Process; + + property AddFieldLength: Boolean read FAddFieldLength write FAddFieldLength; + property CompressData: Boolean read FCompressData write FCompressData; + end; + +implementation + +const + KEY_NAME = 'name'; + KEY_TYPE = 'type'; + KEY_LENGTH = 'length'; + KEY_COLUMNS = 'columns'; + + KEY_NAME_SHORT = 'n'; + KEY_TYPE_SHORT = 't'; + KEY_LENGTH_SHORT = 'l'; + KEY_COLUMNS_SHORT = 'c'; + +constructor TDelphiAIDevMetaInfo.Create(const AField: TDelphiAIDevDBRegistersFields); +begin + FAddFieldLength := False; + FCompressData := False; + + FField := AField; + FC4DConn := TC4DConn.New; + Self.ConfigConn; + + FMetaInfoTables := TFDMetaInfoQuery.Create(nil); + FMetaInfoTables.Connection := TFDConnection(FC4DConn.Connection.Component); + FMetaInfoTables.MetaInfoKind := mkTables; + + FMetaInfoFields := TFDMetaInfoQuery.Create(nil); + FMetaInfoFields.Connection := TFDConnection(FC4DConn.Connection.Component); + FMetaInfoFields.MetaInfoKind := mkTableFields; +end; + +destructor TDelphiAIDevMetaInfo.Destroy; +begin + FMetaInfoFields.Free; + FMetaInfoTables.Free; + inherited; +end; + +procedure TDelphiAIDevMetaInfo.ConfigConn; +begin + FC4DConn.Configs + .DriverID(FField.DriverID) + .Host(FField.Host) + .UserName(FField.User) + .Password(FField.Password) + .Port(FField.Port) + .Database(FField.DatabaseName) + .VendorLib(FField.VendorLib); +end; + +procedure TDelphiAIDevMetaInfo.ProcessNamesKeys; +begin + FKeyName := KEY_NAME; + FKeyTYPE := KEY_TYPE; + FKeyLength := KEY_LENGTH; + FKeyColumns := KEY_COLUMNS; + + if FCompressData then + begin + FKeyName := KEY_NAME_SHORT; + FKeyTYPE := KEY_TYPE_SHORT; + FKeyLength := KEY_LENGTH_SHORT; + FKeyColumns := KEY_COLUMNS_SHORT; + end; +end; + +function TDelphiAIDevMetaInfo.GetInstructionsNamesKeysCompress: string; +begin + Result := 'Some JSON keys have been abbreviated, here is the legend for the abbreviations: ' + + Format('%s = %s; ', [KEY_NAME_SHORT, KEY_NAME]) + + Format('%s = %s; ', [KEY_TYPE_SHORT, KEY_TYPE]) + + Format('%s = %s; ', [KEY_LENGTH_SHORT, KEY_LENGTH]) + + Format('%s = %s. ', [KEY_COLUMNS_SHORT, KEY_COLUMNS]); +end; + +procedure TDelphiAIDevMetaInfo.Process; +var + LJSONObjAll: TJSONObject; + LJSONArrayTables: TJSONArray; + LJSONObjTable: TJSONObject; + LJSONArrayColumns: TJSONArray; + LJSONObjColumn: TJSONObject; +begin + Self.ProcessNamesKeys; + FC4DConn.Connection.Open; + + FMetaInfoTables.Open; + if FMetaInfoTables.IsEmpty then + TUtils.ShowMsgAndAbort('No tables could be found in the current connection'); + + LJSONObjAll := TJSONObject.Create; + try + if FCompressData then + LJSONObjAll.AddPair('instructions', Self.GetInstructionsNamesKeysCompress); + + LJSONObjAll.AddPair('SGBD (database)', FField.DriverID.ToString); + LJSONObjAll.AddPair('database name', FField.DatabaseName); + + LJSONArrayTables := TJSONArray.Create; + + FMetaInfoTables.First; + while not FMetaInfoTables.Eof do + begin + FMetaInfoFields.Close; + FMetaInfoFields.ObjectName := FMetaInfoTables.FieldByName('TABLE_NAME').AsString; + FMetaInfoFields.Open; + + LJSONArrayColumns := TJSONArray.Create; + FMetaInfoFields.First; + while not FMetaInfoFields.Eof do + begin + LJSONObjColumn := TJSONObject.Create; + LJSONObjColumn.AddPair(FKeyName, TJSONString.Create(FMetaInfoFields.FieldByName('COLUMN_NAME').AsString)); + LJSONObjColumn.AddPair(FKeyType, TJSONString.Create(FMetaInfoFields.FieldByName('COLUMN_TYPENAME').AsString)); + if FAddFieldLength then + LJSONObjColumn.AddPair(FKeyLength, TJSONNumber.Create(FMetaInfoFields.FieldByName('COLUMN_LENGTH').AsInteger)); + + LJSONArrayColumns.AddElement(LJSONObjColumn); + FMetaInfoFields.Next; + end; + + LJSONObjTable := TJSONObject.Create; + LJSONObjTable.AddPair(FKeyName, TJSONString.Create(FMetaInfoTables.FieldByName('TABLE_NAME').AsString)); + LJSONObjTable.AddPair(FKeyColumns, LJSONArrayColumns); + LJSONArrayTables.AddElement(LJSONObjTable); + + FMetaInfoTables.Next; + end; + + LJSONObjAll.AddPair('tables', LJSONArrayTables); + Self.SaveJsonInFolder(LJSONObjAll); + Self.SaveGenerationDataToField; + + TUtils.ShowV('Process completed'); + finally + LJSONObjAll.Free; + end; +end; + +procedure TDelphiAIDevMetaInfo.SaveGenerationDataToField; +begin + FField.LastReferences := Now; + TDelphiAIDevDBRegistersModel.New.SaveOrEditData(FField); +end; + +procedure TDelphiAIDevMetaInfo.SaveJsonInFolder(const AJSONObject: TJSONObject); +var + LStringList: TStringList; +begin + LStringList := TStringList.Create; + try +// {$IF CompilerVersion <= 32.0} //Tokyo +// LStringList.Text := AJSONObject.ToJSON; +// {$ELSE} +// LStringList.Text := AJSONObject.Format(2); +// {$ENDIF} + LStringList.Text := AJSONObject.ToString; + LStringList.SaveToFile(TUtils.GetPathFolderMetaInfo + FField.Guid + '.json'); + finally + LStringList.Free; + end; +end; + +end. diff --git a/Src/PopupMenuProjects/DelphiAIDev.PopupMenuProjects.Item.pas b/Src/PopupMenuProjects/DelphiAIDev.PopupMenuProjects.Item.pas new file mode 100644 index 0000000..2e3857b --- /dev/null +++ b/Src/PopupMenuProjects/DelphiAIDev.PopupMenuProjects.Item.pas @@ -0,0 +1,173 @@ +unit DelphiAIDev.PopupMenuProjects.Item; + +interface + +uses + System.SysUtils, + System.Classes, + ToolsAPI, + DelphiAIDev.Types; + +type + TDelphiAIDevPopupMenuProjectsItem = class(TNotifierObject, IOTALocalMenu, IOTAProjectManagerMenu) + private + FCaption: string; + FIsMultiSelectable: Boolean; + FChecked: Boolean; + FEnabled: Boolean; + FHelpContext: Integer; + FName: string; + FParent: string; + FPosition: Integer; + FVerb: string; + protected + FProject: IOTAProject; + FOnExecute: TC4DWizardMenuContextList; + function GetCaption: string; + function GetChecked: Boolean; + function GetEnabled: Boolean; + function GetHelpContext: Integer; + function GetName: string; + function GetParent: string; + function GetPosition: Integer; + function GetVerb: string; + procedure SetCaption(const Value: string); + procedure SetChecked(Value: Boolean); + procedure SetEnabled(Value: Boolean); + procedure SetHelpContext(Value: Integer); + procedure SetName(const Value: string); + procedure SetParent(const Value: string); + procedure SetPosition(Value: Integer); + procedure SetVerb(const Value: string); + function GetIsMultiSelectable: Boolean; + procedure SetIsMultiSelectable(Value: Boolean); + procedure Execute(const MenuContextList: IInterfaceList); virtual; + function PreExecute(const MenuContextList: IInterfaceList): Boolean; + function PostExecute(const MenuContextList: IInterfaceList): Boolean; + public + class function New(OnExecute: TC4DWizardMenuContextList): IOTAProjectManagerMenu; overload; + constructor Create(OnExecute: TC4DWizardMenuContextList); overload; + end; + +implementation + +class function TDelphiAIDevPopupMenuProjectsItem.New(OnExecute: TC4DWizardMenuContextList): IOTAProjectManagerMenu; +begin + Result := Self.Create(OnExecute); +end; + +constructor TDelphiAIDevPopupMenuProjectsItem.Create(OnExecute: TC4DWizardMenuContextList); +begin + FOnExecute := OnExecute; + FEnabled := True; + FChecked := False; + FIsMultiSelectable := False; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.Execute(const MenuContextList: IInterfaceList); +begin + if Assigned(FOnExecute) then + FOnExecute(MenuContextList); +end; + +function TDelphiAIDevPopupMenuProjectsItem.GetCaption: string; +begin + Result := FCaption; +end; + +function TDelphiAIDevPopupMenuProjectsItem.GetChecked: Boolean; +begin + Result := FChecked; +end; + +function TDelphiAIDevPopupMenuProjectsItem.GetEnabled: Boolean; +begin + Result := FEnabled; +end; + +function TDelphiAIDevPopupMenuProjectsItem.GetHelpContext: Integer; +begin + Result := FHelpContext; +end; + +function TDelphiAIDevPopupMenuProjectsItem.GetIsMultiSelectable: Boolean; +begin + Result := FIsMultiSelectable; +end; + +function TDelphiAIDevPopupMenuProjectsItem.GetName: string; +begin + Result := FName; +end; + +function TDelphiAIDevPopupMenuProjectsItem.GetParent: string; +begin + Result := FParent; +end; + +function TDelphiAIDevPopupMenuProjectsItem.GetPosition: Integer; +begin + Result := FPosition; +end; + +function TDelphiAIDevPopupMenuProjectsItem.GetVerb: string; +begin + Result := FVerb; +end; + +function TDelphiAIDevPopupMenuProjectsItem.PostExecute(const MenuContextList: IInterfaceList): Boolean; +begin + Result := True; +end; + +function TDelphiAIDevPopupMenuProjectsItem.PreExecute(const MenuContextList: IInterfaceList): Boolean; +begin + Result := True; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.SetCaption(const Value: string); +begin + FCaption := Value; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.SetChecked(Value: Boolean); +begin + FChecked := Value; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.SetEnabled(Value: Boolean); +begin + FEnabled := Value; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.SetHelpContext(Value: Integer); +begin + FHelpContext := Value; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.SetIsMultiSelectable(Value: Boolean); +begin + FIsMultiSelectable := Value; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.SetName(const Value: string); +begin + FName := Value; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.SetParent(const Value: string); +begin + FParent := Value; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.SetPosition(Value: Integer); +begin + FPosition := Value; +end; + +procedure TDelphiAIDevPopupMenuProjectsItem.SetVerb(const Value: string); +begin + FVerb := Value; +end; + +end. diff --git a/Src/PopupMenuProjects/DelphiAIDev.PopupMenuProjects.OnExecute.pas b/Src/PopupMenuProjects/DelphiAIDev.PopupMenuProjects.OnExecute.pas new file mode 100644 index 0000000..74cba54 --- /dev/null +++ b/Src/PopupMenuProjects/DelphiAIDev.PopupMenuProjects.OnExecute.pas @@ -0,0 +1,50 @@ +unit DelphiAIDev.PopupMenuProjects.OnExecute; + +interface + +uses + System.SysUtils, + System.Classes, + ToolsAPI; + +type + TDelphiAIDevPopupMenuProjectsOnExecute = class + private + FIOTAProject: IOTAProject; + procedure CheckFileNameProject; + public + procedure EditInformation(const MenuContextList: IInterfaceList); + property OTAProject: IOTAProject write FIOTAProject; + end; + +implementation + +uses + DelphiAIDev.Utils, + DelphiAIDev.Projects.AddEdit.View; + +procedure TDelphiAIDevPopupMenuProjectsOnExecute.CheckFileNameProject; +begin + if FIOTAProject.FileName.Trim.IsEmpty then + TUtils.ShowMsgAndAbort('Project file name is empty'); + +// if not System.SysUtils.FileExists(FIOTAProject.FileName) then +// TUtils.ShowMsgAndAbort('Project file not found'); +end; + +procedure TDelphiAIDevPopupMenuProjectsOnExecute.EditInformation(const MenuContextList: IInterfaceList); +var + LView: TDelphiAIDevProjectsAddEditView; +begin + Self.CheckFileNameProject; + + LView := TDelphiAIDevProjectsAddEditView.Create(nil); + try + LView.OTAProject := FIOTAProject; + LView.ShowModal; + finally + LView.Free; + end; +end; + +end. diff --git a/Src/PopupMenuProjects/DelphiAIDev.PopupMenuProjects.pas b/Src/PopupMenuProjects/DelphiAIDev.PopupMenuProjects.pas new file mode 100644 index 0000000..8fb874a --- /dev/null +++ b/Src/PopupMenuProjects/DelphiAIDev.PopupMenuProjects.pas @@ -0,0 +1,119 @@ +unit DelphiAIDev.PopupMenuProjects; + +interface + +uses + System.SysUtils, + System.Classes, + ToolsAPI, + DelphiAIDev.Types, + DelphiAIDev.PopupMenuProjects.Item, + DelphiAIDev.PopupMenuProjects.OnExecute; + +type + TC4DWizardIDEPopupMenuNotifier = class(TNotifierObject, IOTAProjectMenuItemCreatorNotifier) + private + FOnExecute: TDelphiAIDevPopupMenuProjectsOnExecute; + FIOTAProject: IOTAProject; + FPosition: Integer; + function AddItemInMenu(const ACaption: string): IOTAProjectManagerMenu; + function AddSubItemInMenu(const ACaption: string; const AOnExecute: TC4DWizardMenuContextList = nil; + const AChecked: Boolean = False): IOTAProjectManagerMenu; + protected + procedure AddMenu(const Project: IOTAProject; const IdentList: TStrings; + const ProjectManagerMenuList: IInterfaceList; IsMultiSelect: Boolean); + public + class function New: IOTAProjectMenuItemCreatorNotifier; + constructor Create; + destructor Destroy; override; + end; + +procedure RegisterSelf; + +implementation + +uses + DelphiAIDev.Consts, + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA; + +var + Index: Integer = -1; + +procedure RegisterSelf; +begin + Index := TUtilsOTA + .GetIOTAProjectManager + .AddMenuItemCreatorNotifier(TC4DWizardIDEPopupMenuNotifier.New); +end; + +class function TC4DWizardIDEPopupMenuNotifier.New: IOTAProjectMenuItemCreatorNotifier; +begin + Result := Self.Create; +end; + +constructor TC4DWizardIDEPopupMenuNotifier.Create; +begin + FOnExecute := TDelphiAIDevPopupMenuProjectsOnExecute.Create; +end; + +destructor TC4DWizardIDEPopupMenuNotifier.Destroy; +begin + FOnExecute.Free; + inherited; +end; + +procedure TC4DWizardIDEPopupMenuNotifier.AddMenu(const Project: IOTAProject; const IdentList: TStrings; + const ProjectManagerMenuList: IInterfaceList; IsMultiSelect: Boolean); +begin + if not Assigned(ProjectManagerMenuList) then + Exit; + + if IdentList.IndexOf(sProjectContainer) >= 0 then + FPosition := pmmpUninstall + else if IdentList.IndexOf(sProjectGroupContainer) >= 0 then + FPosition := pmmpRename + else + Exit; + + FIOTAProject := Project; + FOnExecute.OTAProject := FIOTAProject; + + FPosition := FPosition + 201; + ProjectManagerMenuList.Add(Self.AddItemInMenu('-')); + ProjectManagerMenuList.Add(Self.AddItemInMenu(TConsts.ITEM_POPUP_MENU_PROJ_CAPTION)); + + ProjectManagerMenuList.Add(Self.AddSubItemInMenu(TConsts.ITEM_POPUP_MENU_PROJ_EditInformation_CAPTION, + FOnExecute.EditInformation)); +end; + +function TC4DWizardIDEPopupMenuNotifier.AddItemInMenu(const ACaption: string): IOTAProjectManagerMenu; +begin + Result := TDelphiAIDevPopupMenuProjectsItem.New({$IF CompilerVersion = 30.0} TC4DWizardMenuContextList(nil) {$ELSE} nil {$ENDIF}); + Result.Caption := ACaption; + Result.Verb := ACaption; + Result.Parent := ''; + Result.Position := TUtils.IncInt(FPosition); + Result.Checked := False; + Result.IsMultiSelectable := False; +end; + +function TC4DWizardIDEPopupMenuNotifier.AddSubItemInMenu(const ACaption: string; + const AOnExecute: TC4DWizardMenuContextList = nil; const AChecked: Boolean = False): IOTAProjectManagerMenu; +begin + Result := TDelphiAIDevPopupMenuProjectsItem.New(AOnExecute); + Result.Caption := ACaption; + Result.Verb := ACaption; + Result.Parent := TConsts.ITEM_POPUP_MENU_PROJ_CAPTION; + Result.Position := TUtils.IncInt(FPosition); + Result.Checked := AChecked; + Result.IsMultiSelectable := False; +end; + +initialization + +finalization + if Index >= 0 then + TUtilsOTA.GetIOTAProjectManager.RemoveMenuItemCreatorNotifier(Index); + +end. diff --git a/Src/Projects/DelphiAIDev.Projects.AddEdit.View.dfm b/Src/Projects/DelphiAIDev.Projects.AddEdit.View.dfm new file mode 100644 index 0000000..bc09ff6 --- /dev/null +++ b/Src/Projects/DelphiAIDev.Projects.AddEdit.View.dfm @@ -0,0 +1,158 @@ +object DelphiAIDevProjectsAddEditView: TDelphiAIDevProjectsAddEditView + Left = 0 + Top = 0 + BorderIcons = [biSystemMenu] + Caption = 'IA Developer - Project Config' + ClientHeight = 230 + ClientWidth = 629 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + KeyPreview = True + OldCreateOrder = False + Position = poScreenCenter + OnClose = FormClose + OnCreate = FormCreate + OnDestroy = FormDestroy + OnKeyDown = FormKeyDown + OnShow = FormShow + PixelsPerInch = 96 + TextHeight = 13 + object Bevel2: TBevel + AlignWithMargins = True + Left = 0 + Top = 191 + Width = 629 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alBottom + Shape = bsTopLine + ExplicitLeft = -87 + ExplicitTop = 264 + ExplicitWidth = 665 + end + object pnBody: TPanel + Left = 0 + Top = 0 + Width = 629 + Height = 191 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object Bevel1: TBevel + AlignWithMargins = True + Left = 0 + Top = 187 + Width = 629 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alBottom + Shape = bsTopLine + ExplicitTop = 444 + ExplicitWidth = 676 + end + object Label1: TLabel + Left = 24 + Top = 135 + Width = 79 + Height = 13 + Caption = 'Last generation:' + end + object lbLastGeneration: TLabel + Left = 109 + Top = 135 + Width = 81 + Height = 13 + Caption = 'lbLastGeneration' + end + object Label2: TLabel + Left = 24 + Top = 43 + Width = 55 + Height = 13 + Caption = 'Nickname' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object Label3: TLabel + Left = 24 + Top = 95 + Width = 84 + Height = 13 + Caption = 'Database Default' + end + object cBoxDatabaseDefault: TComboBox + Left = 24 + Top = 111 + Width = 577 + Height = 21 + Style = csDropDownList + TabOrder = 1 + OnClick = cBoxDatabaseDefaultClick + end + object edtNickname: TEdit + Left = 24 + Top = 59 + Width = 577 + Height = 21 + TabOrder = 0 + end + end + object pnButtons: TPanel + Left = 0 + Top = 195 + Width = 629 + Height = 35 + Align = alBottom + BevelEdges = [beLeft, beRight, beBottom] + BevelOuter = bvNone + Padding.Left = 2 + Padding.Top = 2 + Padding.Right = 2 + Padding.Bottom = 2 + ParentBackground = False + TabOrder = 1 + object btnConfirm: TButton + AlignWithMargins = True + Left = 401 + Top = 2 + Width = 110 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Bottom = 0 + Align = alRight + Caption = 'Confirm' + TabOrder = 0 + OnClick = btnConfirmClick + end + object btnClose: TButton + AlignWithMargins = True + Left = 514 + Top = 2 + Width = 110 + Height = 31 + Cursor = crHandPoint + Margins.Left = 0 + Margins.Top = 0 + Margins.Bottom = 0 + Align = alRight + Caption = 'Close' + TabOrder = 1 + OnClick = btnCloseClick + end + end +end diff --git a/Src/Projects/DelphiAIDev.Projects.AddEdit.View.pas b/Src/Projects/DelphiAIDev.Projects.AddEdit.View.pas new file mode 100644 index 0000000..a619608 --- /dev/null +++ b/Src/Projects/DelphiAIDev.Projects.AddEdit.View.pas @@ -0,0 +1,173 @@ +unit DelphiAIDev.Projects.AddEdit.View; + +interface + +uses + Winapi.Windows, + Winapi.Messages, + System.SysUtils, + System.Variants, + System.Classes, + Vcl.Graphics, + Vcl.Controls, + Vcl.Forms, + Vcl.Dialogs, + Vcl.StdCtrls, + Vcl.ExtCtrls, + ToolsAPI, + DelphiAIDev.DB.Registers.Model, + DelphiAIDev.DB.Registers.Fields, + DelphiAIDev.Projects.Model, + DelphiAIDev.Projects.Fields; + +type + TDelphiAIDevProjectsAddEditView = class(TForm) + Bevel2: TBevel; + pnBody: TPanel; + Bevel1: TBevel; + Label1: TLabel; + lbLastGeneration: TLabel; + pnButtons: TPanel; + btnConfirm: TButton; + btnClose: TButton; + cBoxDatabaseDefault: TComboBox; + Label2: TLabel; + edtNickname: TEdit; + Label3: TLabel; + procedure FormCreate(Sender: TObject); + procedure FormShow(Sender: TObject); + procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure btnCloseClick(Sender: TObject); + procedure btnConfirmClick(Sender: TObject); + procedure cBoxDatabaseDefaultClick(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + private + FOTAProject: IOTAProject; + FProjectsFields: TDelphiAIDevProjectsFields; + procedure FillScreenFields; + procedure FillcBoxDatabases; + procedure FillDateLastReferences; + function GetFieldDBSelected: TDelphiAIDevDBRegistersFields; + procedure ValidateFillingFields; + public + property OTAProject: IOTAProject write FOTAProject; + end; + +implementation + +uses + DelphiAIDev.Consts, + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA, + DelphiAIDev.DB.Utils; + +{$R *.dfm} + +procedure TDelphiAIDevProjectsAddEditView.FormCreate(Sender: TObject); +begin + Self.ModalResult := mrCancel; + TUtilsOTA.IDEThemingAll(TDelphiAIDevProjectsAddEditView, Self); +end; + +procedure TDelphiAIDevProjectsAddEditView.FormDestroy(Sender: TObject); +begin + // +end; + +procedure TDelphiAIDevProjectsAddEditView.FormShow(Sender: TObject); +begin + FProjectsFields := TDelphiAIDevProjectsModel.New.ReadFilePath(FOTAProject.FileName); + Self.FillScreenFields; +end; + +procedure TDelphiAIDevProjectsAddEditView.FormClose(Sender: TObject; var Action: TCloseAction); +begin + if Assigned(FProjectsFields)then + FProjectsFields.Free; +end; + +procedure TDelphiAIDevProjectsAddEditView.FillScreenFields; +begin + Screen.Cursor := crHourGlass; + try + edtNickname.Text := FProjectsFields.Nickname; + Self.FillcBoxDatabases; + finally + Screen.Cursor := crDefault; + end; +end; + +procedure TDelphiAIDevProjectsAddEditView.FillcBoxDatabases; +begin + TDelphiAIDevDBUtils.FillComboBoxDataBases(cBoxDatabaseDefault, FProjectsFields.GuidDatabaseDefault); + Self.FillDateLastReferences; +end; + +procedure TDelphiAIDevProjectsAddEditView.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + case Key of + VK_F4: + if ssAlt in Shift then + Key := 0; + VK_ESCAPE: + if Shift = [] then + btnClose.Click; + end; +end; + +procedure TDelphiAIDevProjectsAddEditView.cBoxDatabaseDefaultClick(Sender: TObject); +begin + Self.FillDateLastReferences; +end; + +procedure TDelphiAIDevProjectsAddEditView.FillDateLastReferences; +begin + lbLastGeneration.Caption := ''; + + if cBoxDatabaseDefault.Items.Count < 0 then + Exit; + + lbLastGeneration.Caption := TUtils.DateTimeToStrEmpty(Self.GetFieldDBSelected.LastReferences); +end; + +function TDelphiAIDevProjectsAddEditView.GetFieldDBSelected: TDelphiAIDevDBRegistersFields; +begin + Result := TDelphiAIDevDBRegistersFields(cBoxDatabaseDefault.Items.Objects[cBoxDatabaseDefault.ItemIndex]); +end; + +procedure TDelphiAIDevProjectsAddEditView.btnCloseClick(Sender: TObject); +begin + Self.Close; + Self.ModalResult := mrCancel; +end; + +procedure TDelphiAIDevProjectsAddEditView.btnConfirmClick(Sender: TObject); +begin + Self.ValidateFillingFields; + + Screen.Cursor := crHourGlass; + try + FProjectsFields.FilePath := FOTAProject.FileName; + FProjectsFields.Nickname := edtNickname.Text; + FProjectsFields.GuidDatabaseDefault := Self.GetFieldDBSelected.Guid; + + TDelphiAIDevProjectsModel.New.SaveOrEditData(FProjectsFields); + finally + Screen.Cursor := crDefault; + end; + + Self.Close; + Self.ModalResult := mrOk; +end; + +procedure TDelphiAIDevProjectsAddEditView.ValidateFillingFields; +begin + if Trim(edtNickname.Text).IsEmpty then + TUtils.ShowMsgAndAbort('No informed Nickname', edtNickname); + +// if cBoxDatabaseDefault.ItemIndex < 0 then +// TUtils.ShowMsgAndAbort('No informed Database', cBoxDatabaseDefault); +end; + +end. diff --git a/Src/Projects/DelphiAIDev.Projects.Fields.pas b/Src/Projects/DelphiAIDev.Projects.Fields.pas new file mode 100644 index 0000000..2b6ac26 --- /dev/null +++ b/Src/Projects/DelphiAIDev.Projects.Fields.pas @@ -0,0 +1,48 @@ +unit DelphiAIDev.Projects.Fields; + +interface + +uses + DelphiAIDev.Types; + +type + TDelphiAIDevProjectsFields = class + private + FGuid: string; + FFilePath: string; + FNickname: string; + FGuidDatabaseDefault: string; + public + constructor Create; + procedure Clear; + procedure GetDataFromOtherObject(const AOtherObj: TDelphiAIDevProjectsFields); + property Guid: string read FGuid write FGuid; + property FilePath: string read FFilePath write FFilePath; + property Nickname: string read FNickname write FNickname; + property GuidDatabaseDefault: string read FGuidDatabaseDefault write FGuidDatabaseDefault; + end; + +implementation + +constructor TDelphiAIDevProjectsFields.Create; +begin + Self.Clear; +end; + +procedure TDelphiAIDevProjectsFields.Clear; +begin + FGuid := ''; + FFilePath := ''; + FNickname := ''; + FGuidDatabaseDefault := ''; +end; + +procedure TDelphiAIDevProjectsFields.GetDataFromOtherObject(const AOtherObj: TDelphiAIDevProjectsFields); +begin + FGuid := AOtherObj.Guid; + FNickname := AOtherObj.Nickname; + FFilePath := AOtherObj.FilePath; + FGuidDatabaseDefault := AOtherObj.GuidDatabaseDefault; +end; + +end. diff --git a/Src/Projects/DelphiAIDev.Projects.Interfaces.pas b/Src/Projects/DelphiAIDev.Projects.Interfaces.pas new file mode 100644 index 0000000..36caa95 --- /dev/null +++ b/Src/Projects/DelphiAIDev.Projects.Interfaces.pas @@ -0,0 +1,23 @@ +unit DelphiAIDev.Projects.Interfaces; + +interface + +uses + System.SysUtils, + DelphiAIDev.Types, + DelphiAIDev.Projects.Fields; + +type + IDelphiAIDevProjectsModel = interface + ['{D919A407-4301-447F-A267-6965153DC01A}'] + function ReadGuid(const AGuid: string): TDelphiAIDevProjectsFields; + function ReadFilePathCurrentProject: TDelphiAIDevProjectsFields; + function ReadFilePath(const AFilePath: string): TDelphiAIDevProjectsFields; + procedure ReadData(AProc: TProc; const AAutoFreeField: TAutoFreeField = TAutoFreeField.Yes); + procedure SaveOrEditData(AFields: TDelphiAIDevProjectsFields); + procedure RemoveData(const AGuid: string); + end; + +implementation + +end. diff --git a/Src/Projects/DelphiAIDev.Projects.Model.pas b/Src/Projects/DelphiAIDev.Projects.Model.pas new file mode 100644 index 0000000..1a9469e --- /dev/null +++ b/Src/Projects/DelphiAIDev.Projects.Model.pas @@ -0,0 +1,358 @@ +unit DelphiAIDev.Projects.Model; + +interface + +uses + System.SysUtils, + System.Classes, + System.JSON, + Rest.JSON, + DelphiAIDev.Utils, + DelphiAIDev.Utils.OTA, + DelphiAIDev.Types, + DelphiAIDev.Projects.Interfaces, + DelphiAIDev.Projects.Fields; + +type + TDelphiAIDevProjectsModel = class(TInterfacedObject, IDelphiAIDevProjectsModel) + private + procedure SaveData(AFields: TDelphiAIDevProjectsFields); + procedure EditData(AFields: TDelphiAIDevProjectsFields); + procedure FillField(const AJSONObjItem: TJSONObject; var AField: TDelphiAIDevProjectsFields); + protected + function ReadGuid(const AGuid: string): TDelphiAIDevProjectsFields; + function ReadFilePathCurrentProject: TDelphiAIDevProjectsFields; + function ReadFilePath(const AFilePath: string): TDelphiAIDevProjectsFields; + procedure ReadData(AProc: TProc; const AAutoFreeField: TAutoFreeField = TAutoFreeField.Yes); + procedure SaveOrEditData(AFields: TDelphiAIDevProjectsFields); + procedure RemoveData(const AGuid: string); + public + class function New: IDelphiAIDevProjectsModel; + constructor Create; + end; + +implementation + +const + GUID = 'guid'; + FILE_PATH = 'file_path'; + NICKNAME = 'Nickname'; + GUID_DATABASE_DEFAULT = 'guid_database_default'; + +class function TDelphiAIDevProjectsModel.New: IDelphiAIDevProjectsModel; +begin + Result := Self.Create; +end; + +constructor TDelphiAIDevProjectsModel.Create; +begin + // +end; + +function TDelphiAIDevProjectsModel.ReadGuid(const AGuid: string): TDelphiAIDevProjectsFields; +var + LStringList: TStringList; + LJSONObjItem: TJSONObject; + LJSONArray: TJsonArray; + i: Integer; +begin + Result := TDelphiAIDevProjectsFields.Create; + + if not FileExists(TUtils.GetPathFileJSONProjects) then + Exit; + + LStringList := TStringList.Create; + try + LStringList.LoadFromFile(TUtils.GetPathFileJSONProjects); + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + finally + LStringList.Free; + end; + + try + for i := 0 to Pred(LJSONArray.Count) do + begin + if not(LJSONArray.Items[i] is TJSONObject) then + Continue; + + LJSONObjItem := LJSONArray.Items[i] as TJSONObject; + + if LJSONObjItem.GetValue(GUID) = nil then + Continue; + + if LJSONObjItem.GetValue(GUID) <> AGuid then + Continue; + + Self.FillField(LJSONObjItem, Result); + Break; + end; + finally + LJSONArray.Free; + end; +end; + +function TDelphiAIDevProjectsModel.ReadFilePathCurrentProject: TDelphiAIDevProjectsFields; +begin + Result := Self.ReadFilePath(TUtilsOTA.GetCurrentProjectFileName); +end; + +function TDelphiAIDevProjectsModel.ReadFilePath(const AFilePath: string): TDelphiAIDevProjectsFields; +var + LStringList: TStringList; + LJSONObjItem: TJSONObject; + LJSONArray: TJsonArray; + i: Integer; +begin + Result := TDelphiAIDevProjectsFields.Create; + + if AFilePath.Trim.IsEmpty then + Exit; + + if not FileExists(TUtils.GetPathFileJSONProjects) then + Exit; + + LStringList := TStringList.Create; + try + LStringList.LoadFromFile(TUtils.GetPathFileJSONProjects); + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + finally + LStringList.Free; + end; + + try + for i := 0 to Pred(LJSONArray.Count) do + begin + if not(LJSONArray.Items[i] is TJSONObject) then + Continue; + + LJSONObjItem := LJSONArray.Items[i] as TJSONObject; + + if LJSONObjItem.GetValue(FILE_PATH) = nil then + Continue; + + if LJSONObjItem.GetValue(FILE_PATH) <> AFilePath then + Continue; + + Self.FillField(LJSONObjItem, Result); + Break; + end; + finally + LJSONArray.Free; + end; +end; + +procedure TDelphiAIDevProjectsModel.FillField(const AJSONObjItem: TJSONObject; + var AField: TDelphiAIDevProjectsFields); +begin + AField.Clear; + AField.Guid := AJSONObjItem.GetValue(GUID); + + if AJSONObjItem.GetValue(FILE_PATH) <> nil then + AField.FilePath := AJSONObjItem.GetValue(FILE_PATH); + + if AJSONObjItem.GetValue(NICKNAME) <> nil then + AField.Nickname := AJSONObjItem.GetValue(NICKNAME); + + if AJSONObjItem.GetValue(GUID_DATABASE_DEFAULT) <> nil then + AField.GuidDatabaseDefault := AJSONObjItem.GetValue(GUID_DATABASE_DEFAULT); +end; + +procedure TDelphiAIDevProjectsModel.ReadData(AProc: TProc; + const AAutoFreeField: TAutoFreeField = TAutoFreeField.Yes); +var + LStringList: TStringList; + LJSONObjItem: TJSONObject; + LJSONArray: TJsonArray; + i: Integer; + LFields: TDelphiAIDevProjectsFields; +begin + LFields := TDelphiAIDevProjectsFields.Create; + try + if not FileExists(TUtils.GetPathFileJSONProjects) then + begin + AProc(LFields); + Exit; + end; + + LStringList := TStringList.Create; + try + LStringList.LoadFromFile(TUtils.GetPathFileJSONProjects); + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + finally + LStringList.Free; + end; + + try + for i := 0 to Pred(LJSONArray.Count) do + begin + if not(LJSONArray.Items[i] is TJSONObject) then + Continue; + + LJSONObjItem := LJSONArray.Items[i] as TJSONObject; + + if LJSONObjItem.GetValue(GUID) = nil then + Continue; + + Self.FillField(LJSONObjItem, LFields); + + AProc(LFields); + end; + finally + LJSONArray.Free; + end; + finally + if AAutoFreeField = TAutoFreeField.Yes then + LFields.Free; + end; +end; + +procedure TDelphiAIDevProjectsModel.SaveOrEditData(AFields: TDelphiAIDevProjectsFields); +begin + if AFields.Guid.Trim.IsEmpty then + Self.SaveData(AFields) + else + Self.EditData(AFields); +end; + +procedure TDelphiAIDevProjectsModel.SaveData(AFields: TDelphiAIDevProjectsFields); +var + LStringList: TStringList; + LJSONArray: TJSONArray; + LJSONObject: TJSONObject; +begin + LStringList := TStringList.Create; + try + if FileExists(TUtils.GetPathFileJSONProjects) then + LStringList.LoadFromFile(TUtils.GetPathFileJSONProjects); + + LJSONArray := TJSONArray.Create; + try + if string(LStringList.Text).Trim.StartsWith('[') then + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + + LJSONObject := TJSONObject.Create; + LJSONObject.AddPair(GUID, TUtils.GetGuidStr); + LJSONObject.AddPair(FILE_PATH, AFields.FilePath); + LJSONObject.AddPair(NICKNAME, AFields.Nickname); + LJSONObject.AddPair(GUID_DATABASE_DEFAULT, AFields.GuidDatabaseDefault); + LJSONArray.AddElement(LJSONObject); + + {$IF CompilerVersion <= 32.0} //Tokyo + LStringList.Text := LJSONArray.ToJSON; + {$ELSE} + LStringList.Text := LJSONArray.Format(2); + {$ENDIF} + finally + LJSONArray.Free; + end; + + LStringList.SaveToFile(TUtils.GetPathFileJSONProjects); + finally + LStringList.Free; + end; +end; + +procedure TDelphiAIDevProjectsModel.EditData(AFields: TDelphiAIDevProjectsFields); +var + LStringList: TStringList; + LJSONArray: TJSONArray; + LJSONObjItem: TJSONObject; + i: Integer; +begin + LStringList := TStringList.Create; + try + if FileExists(TUtils.GetPathFileJSONProjects) then + LStringList.LoadFromFile(TUtils.GetPathFileJSONProjects); + + LJSONArray := TJSONArray.Create; + try + if string(LStringList.Text).Trim.StartsWith('[') then + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + + for i := 0 to Pred(LJSONArray.Count) do + begin + if not(LJSONArray.Items[i] is TJSONObject) then + Continue; + + LJSONObjItem := LJSONArray.Items[i] as TJSONObject; + + if LJSONObjItem.GetValue(GUID) = AFields.Guid then + begin + LJSONObjItem.RemovePair(FILE_PATH).Free; + LJSONObjItem.AddPair(FILE_PATH, AFields.FilePath); + + LJSONObjItem.RemovePair(NICKNAME).Free; + LJSONObjItem.AddPair(NICKNAME, AFields.Nickname); + + LJSONObjItem.RemovePair(GUID_DATABASE_DEFAULT).Free; + LJSONObjItem.AddPair(GUID_DATABASE_DEFAULT, AFields.GuidDatabaseDefault); + + Break; + end; + end; + + {$IF CompilerVersion <= 32.0} //Tokyo + LStringList.Text := LJSONArray.ToJSON; + {$ELSE} + LStringList.Text := LJSONArray.Format(2); + {$ENDIF} + finally + LJSONArray.Free; + end; + + LStringList.SaveToFile(TUtils.GetPathFileJSONProjects); + finally + LStringList.Free; + end; +end; + +procedure TDelphiAIDevProjectsModel.RemoveData(const AGuid: string); +var + LStringList: TStringList; + LJSONArray: TJSONArray; + LJSONObjItem: TJSONObject; + i: Integer; +begin + if AGuid.Trim.IsEmpty then + Exit; + + if not FileExists(TUtils.GetPathFileJSONProjects) then + Exit; + + LStringList := TStringList.Create; + try + LStringList.LoadFromFile(TUtils.GetPathFileJSONProjects); + if not string(LStringList.Text).Trim.StartsWith('[') then + Exit; + + LJSONArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(LStringList.Text), 0) as TJSONArray; + try + for i := 0 to Pred(LJSONArray.Count) do + begin + if not(LJSONArray.Items[i] is TJSONObject) then + Continue; + + LJSONObjItem := LJSONArray.Items[i] as TJSONObject; + if LJSONObjItem.GetValue(GUID) = AGuid then + begin + LJSONArray.Remove(i); + Break; + end; + end; + + {$IF CompilerVersion <= 32.0} //Tokyo + LStringList.Text := LJSONArray.ToJSON; + {$ELSE} + LStringList.Text := LJSONArray.Format(2); + {$ENDIF} + finally + LJSONArray.Free; + end; + + LStringList.SaveToFile(TUtils.GetPathFileJSONProjects); + finally + LStringList.Free; + end; +end; + +end. diff --git a/Src/Settings/DelphiAIDev.Settings.View.dfm b/Src/Settings/DelphiAIDev.Settings.View.dfm index 3be8253..b023496 100644 --- a/Src/Settings/DelphiAIDev.Settings.View.dfm +++ b/Src/Settings/DelphiAIDev.Settings.View.dfm @@ -2,601 +2,33 @@ object DelphiAIDevSettingsView: TDelphiAIDevSettingsView Left = 0 Top = 0 Caption = 'Delphi AI Developer - Settings' - ClientHeight = 578 - ClientWidth = 632 + ClientHeight = 676 + ClientWidth = 670 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] - OldCreateOrder = False + KeyPreview = True Position = poScreenCenter OnClose = FormClose OnCreate = FormCreate OnKeyDown = FormKeyDown OnShow = FormShow - PixelsPerInch = 96 TextHeight = 13 object pnBackAll: TPanel Left = 0 Top = 0 - Width = 632 - Height = 578 + Width = 670 + Height = 676 Align = alClient BevelOuter = bvNone TabOrder = 0 - object pnBody: TPanel - Left = 0 - Top = 0 - Width = 632 - Height = 543 - Align = alClient - BevelOuter = bvNone - ParentBackground = False - TabOrder = 0 - object gBoxGemini: TGroupBox - Left = 0 - Top = 97 - Width = 632 - Height = 133 - Align = alTop - Caption = ' Gemini (Google) ' - ParentBackground = False - TabOrder = 1 - object pnGeminiBack: TPanel - AlignWithMargins = True - Left = 5 - Top = 18 - Width = 622 - Height = 110 - Align = alClient - BevelOuter = bvNone - ParentBackground = False - TabOrder = 0 - object Label5: TLabel - Left = 16 - Top = 5 - Width = 45 - Height = 13 - Caption = 'Base URL' - end - object Label6: TLabel - Left = 16 - Top = 48 - Width = 37 - Height = 13 - Caption = 'API key' - end - object Label7: TLabel - Left = 267 - Top = 5 - Width = 28 - Height = 13 - Caption = 'Model' - end - object lbLinkGemini01: TLabel - Left = 16 - Top = 89 - Width = 86 - Height = 13 - Cursor = crHandPoint - Hint = 'https://aistudio.google.com/app/apikey' - Caption = 'Generate API Key' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ParentShowHint = False - ShowHint = True - OnClick = lbLinkGpt01Click - end - object lbLinkGemini02: TLabel - Left = 128 - Top = 89 - Width = 72 - Height = 13 - Cursor = crHandPoint - Hint = 'https://ai.google.dev/gemini-api/docs/api-overview#curl_1' - Caption = 'Documentation' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ParentShowHint = False - ShowHint = True - OnClick = lbLinkGpt01Click - end - object btnApiKeyGeminiView: TSpeedButton - Left = 582 - Top = 63 - Width = 23 - Height = 22 - Cursor = crHandPoint - Hint = 'Show/Hide API Key' - Flat = True - Glyph.Data = { - 36030000424D3603000000000000360000002800000010000000100000000100 - 18000000000000030000120B0000120B00000000000000000000FF00FF4A667C - BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 - FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF - 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF - 2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 - FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF - 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF - 51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00 - FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF - 00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF - FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3 - B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD - EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF - FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF - FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1 - C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF - FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF - E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4 - C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF - FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4 - C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8 - DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF - FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890 - 86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF - 00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF} - ParentShowHint = False - ShowHint = True - OnClick = btnApiKeyGeminiViewClick - end - object lbLinkGemini03: TLabel - Left = 232 - Top = 89 - Width = 67 - Height = 13 - Cursor = crHandPoint - Hint = 'https://ai.google.dev/gemini-api/docs/models/gemini?hl=pt-br' - Caption = 'Gemini Models' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ParentShowHint = False - ShowHint = True - OnClick = lbLinkGpt01Click - end - object edtBaseUrlGemini: TEdit - Left = 16 - Top = 20 - Width = 249 - Height = 21 - TabOrder = 0 - end - object edtApiKeyGemini: TEdit - Left = 16 - Top = 64 - Width = 563 - Height = 21 - PasswordChar = '*' - TabOrder = 2 - end - object cBoxModelGemini: TComboBox - Left = 267 - Top = 20 - Width = 333 - Height = 21 - TabOrder = 1 - Items.Strings = ( - 'v1/models/gemini-1.5-flash:generateContent' - 'v1beta/models/gemini-pro:generateContent' - 'v1beta/models/gemini-1.5-flash:generateContent') - end - end - end - object GroupBox2: TGroupBox - Left = 0 - Top = 0 - Width = 632 - Height = 97 - Align = alTop - Caption = ' Preferences ' - ParentBackground = False - TabOrder = 0 - object Label11: TLabel - Left = 272 - Top = 23 - Width = 48 - Height = 13 - Caption = 'AI default' - end - object Label4: TLabel - Left = 21 - Top = 23 - Width = 133 - Height = 13 - Caption = 'Language used in questions' - end - object cBoxAIDefault: TComboBox - Left = 272 - Top = 38 - Width = 333 - Height = 21 - Style = csDropDownList - TabOrder = 1 - Items.Strings = ( - '') - end - object ColorBoxColorHighlightCodeDelphi: TColorBox - Left = 218 - Top = 66 - Width = 133 - Height = 22 - TabOrder = 3 - end - object ckColorHighlightCodeDelphiUse: TCheckBox - Left = 21 - Top = 68 - Width = 194 - Height = 17 - Caption = 'Color to highlight Delphi/Pascal code' - TabOrder = 2 - OnClick = ckColorHighlightCodeDelphiUseClick - end - object cBoxLanguageQuestions: TComboBox - Left = 21 - Top = 38 - Width = 249 - Height = 21 - Hint = 'What is the standard language for questions?' - Style = csDropDownList - ParentShowHint = False - ShowHint = True - TabOrder = 0 - end - end - object gBoxOpenAI: TGroupBox - Left = 0 - Top = 230 - Width = 632 - Height = 133 - Align = alTop - Caption = ' ChatGPT (OpenAI)' - ParentBackground = False - TabOrder = 2 - object pnOpenAIBack: TPanel - AlignWithMargins = True - Left = 5 - Top = 18 - Width = 622 - Height = 110 - Align = alClient - BevelOuter = bvNone - ParentBackground = False - TabOrder = 0 - object Label1: TLabel - Left = 16 - Top = 5 - Width = 45 - Height = 13 - Caption = 'Base URL' - end - object Label3: TLabel - Left = 16 - Top = 48 - Width = 37 - Height = 13 - Caption = 'API key' - end - object Label2: TLabel - Left = 478 - Top = 5 - Width = 28 - Height = 13 - Caption = 'Model' - end - object lbLinkGpt01: TLabel - Left = 16 - Top = 90 - Width = 86 - Height = 13 - Cursor = crHandPoint - Hint = 'https://platform.openai.com/api-keys' - Caption = 'Generate API Key' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ParentShowHint = False - ShowHint = True - OnClick = lbLinkGpt01Click - end - object btnApiKeyOpenAIView: TSpeedButton - Left = 582 - Top = 63 - Width = 23 - Height = 22 - Cursor = crHandPoint - Hint = 'Show/Hide API Key' - Flat = True - Glyph.Data = { - 36030000424D3603000000000000360000002800000010000000100000000100 - 18000000000000030000120B0000120B00000000000000000000FF00FF4A667C - BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 - FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF - 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF - 2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 - FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF - 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF - 51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00 - FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF - 00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF - FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3 - B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD - EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF - FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF - FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1 - C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF - FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF - E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4 - C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF - FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4 - C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8 - DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF - FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890 - 86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF - 00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF} - ParentShowHint = False - ShowHint = True - OnClick = btnApiKeyOpenAIViewClick - end - object lbLinkGpt02: TLabel - Left = 128 - Top = 90 - Width = 72 - Height = 13 - Cursor = crHandPoint - Hint = 'https://platform.openai.com/docs/api-reference/making-requests' - Caption = 'Documentation' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ParentShowHint = False - ShowHint = True - OnClick = lbLinkGpt01Click - end - object edtBaseUrlOpenAI: TEdit - Left = 16 - Top = 21 - Width = 460 - Height = 21 - TabOrder = 0 - end - object edtApiKeyOpenAI: TEdit - Left = 16 - Top = 64 - Width = 563 - Height = 21 - PasswordChar = '*' - TabOrder = 2 - end - object cBoxModelOpenAI: TComboBox - Left = 478 - Top = 21 - Width = 122 - Height = 21 - Style = csDropDownList - TabOrder = 1 - Items.Strings = ( - 'gpt-3.5-turbo' - 'gpt-3.5-turbo-16k' - 'gpt-4' - 'gpt-4-32k') - end - end - end - object gboxData: TGroupBox - Left = 0 - Top = 498 - Width = 632 - Height = 45 - Align = alBottom - Caption = ' Data ' - Padding.Left = 2 - Padding.Top = 5 - Padding.Bottom = 3 - TabOrder = 3 - object btnOpenDataFolder: TButton - Left = 4 - Top = 20 - Width = 122 - Height = 20 - Cursor = crHandPoint - Align = alLeft - Caption = 'Open Data Folder' - TabOrder = 0 - OnClick = btnOpenDataFolderClick - end - end - object gBoxGroq: TGroupBox - Left = 0 - Top = 363 - Width = 632 - Height = 135 - Align = alClient - Caption = ' Groq ' - ParentBackground = False - TabOrder = 4 - object pnGroqBack: TPanel - AlignWithMargins = True - Left = 5 - Top = 18 - Width = 622 - Height = 112 - Align = alClient - BevelOuter = bvNone - ParentBackground = False - TabOrder = 0 - object Label8: TLabel - Left = 16 - Top = 5 - Width = 45 - Height = 13 - Caption = 'Base URL' - end - object Label9: TLabel - Left = 16 - Top = 48 - Width = 37 - Height = 13 - Caption = 'API key' - end - object Label10: TLabel - Left = 478 - Top = 5 - Width = 28 - Height = 13 - Caption = 'Model' - end - object lbLinkGroq01: TLabel - Left = 16 - Top = 90 - Width = 86 - Height = 13 - Cursor = crHandPoint - Hint = 'https://console.groq.com/keys' - Caption = 'Generate API Key' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ParentShowHint = False - ShowHint = True - OnClick = lbLinkGpt01Click - end - object btnApiKeyGroqView: TSpeedButton - Left = 582 - Top = 63 - Width = 23 - Height = 22 - Cursor = crHandPoint - Hint = 'Show/Hide API Key' - Flat = True - Glyph.Data = { - 36030000424D3603000000000000360000002800000010000000100000000100 - 18000000000000030000120B0000120B00000000000000000000FF00FF4A667C - BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 - FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF - 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF - 2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 - FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF - 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF - 51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00 - FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF - 00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF - FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3 - B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD - EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF - FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF - FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1 - C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF - FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF - E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4 - C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF - FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4 - C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8 - DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF - FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890 - 86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF - 00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF} - ParentShowHint = False - ShowHint = True - OnClick = btnApiKeyGroqViewClick - end - object lbLinkGroq02: TLabel - Left = 128 - Top = 90 - Width = 72 - Height = 13 - Cursor = crHandPoint - Hint = 'https://console.groq.com/docs/quickstart' - Caption = 'Documentation' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ParentShowHint = False - ShowHint = True - OnClick = lbLinkGpt01Click - end - object lbLinkGroq03: TLabel - Left = 232 - Top = 89 - Width = 59 - Height = 13 - Cursor = crHandPoint - Hint = 'https://console.groq.com/docs/models' - Caption = 'Groq Models' - Font.Charset = DEFAULT_CHARSET - Font.Color = clBlue - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ParentShowHint = False - ShowHint = True - OnClick = lbLinkGpt01Click - end - object edtBaseUrlGroq: TEdit - Left = 16 - Top = 21 - Width = 460 - Height = 21 - TabOrder = 0 - end - object edtApiKeyGroq: TEdit - Left = 16 - Top = 64 - Width = 563 - Height = 21 - PasswordChar = '*' - TabOrder = 2 - end - object cBoxModelGroq: TComboBox - Left = 478 - Top = 21 - Width = 122 - Height = 21 - Style = csDropDownList - TabOrder = 1 - Items.Strings = ( - 'llama3-8b-8192' - 'llama3-70b-8192' - 'llama3-groq-8b-8192-tool-use-preview' - 'llama3-groq-70b-8192-tool-use-preview' - 'mixtral-8x7b-32768' - 'gemma-7b-it' - 'gemma2-9b-it' - 'whisper-large-v3') - end - end - end - end object pnBottom: TPanel Left = 0 - Top = 543 - Width = 632 + Top = 641 + Width = 670 Height = 35 Margins.Left = 0 Margins.Top = 0 @@ -609,16 +41,17 @@ object DelphiAIDevSettingsView: TDelphiAIDevSettingsView Padding.Right = 2 Padding.Bottom = 2 ParentBackground = False - TabOrder = 1 + TabOrder = 0 object lbRestoreDefaults: TLabel AlignWithMargins = True Left = 16 - Top = 12 + Top = 7 Width = 80 - Height = 18 + Height = 21 Cursor = crHandPoint Margins.Left = 16 - Margins.Top = 10 + Margins.Top = 5 + Margins.Bottom = 5 Align = alLeft Caption = 'Restore defaults' Font.Charset = DEFAULT_CHARSET @@ -635,7 +68,7 @@ object DelphiAIDevSettingsView: TDelphiAIDevSettingsView end object btnConfirm: TButton AlignWithMargins = True - Left = 404 + Left = 442 Top = 2 Width = 110 Height = 31 @@ -650,7 +83,7 @@ object DelphiAIDevSettingsView: TDelphiAIDevSettingsView end object btnClose: TButton AlignWithMargins = True - Left = 517 + Left = 555 Top = 2 Width = 110 Height = 31 @@ -664,5 +97,1232 @@ object DelphiAIDevSettingsView: TDelphiAIDevSettingsView OnClick = btnCloseClick end end + object pnMyControl: TPanel + Left = 0 + Top = 0 + Width = 670 + Height = 641 + Align = alClient + BevelOuter = bvNone + TabOrder = 1 + object pnMyControlButtons: TPanel + Left = 0 + Top = 0 + Width = 670 + Height = 30 + Align = alTop + BevelEdges = [beBottom] + BevelOuter = bvNone + TabOrder = 0 + object Bevel5: TBevel + AlignWithMargins = True + Left = 0 + Top = 26 + Width = 670 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alBottom + Shape = bsTopLine + ExplicitTop = 218 + ExplicitWidth = 632 + end + object btnPreferences: TButton + AlignWithMargins = True + Left = 3 + Top = 2 + Width = 95 + Height = 23 + Cursor = crHandPoint + Margins.Top = 2 + Margins.Right = 0 + Margins.Bottom = 1 + Align = alLeft + Caption = 'Preferences' + TabOrder = 0 + OnClick = btnPreferencesClick + end + object btnIAsOnline: TButton + AlignWithMargins = True + Left = 101 + Top = 2 + Width = 95 + Height = 23 + Cursor = crHandPoint + Margins.Top = 2 + Margins.Right = 0 + Margins.Bottom = 1 + Align = alLeft + Caption = 'AI on-line' + TabOrder = 1 + OnClick = btnIAsOnlineClick + end + object btnIAsOffline: TButton + AlignWithMargins = True + Left = 199 + Top = 2 + Width = 95 + Height = 23 + Cursor = crHandPoint + Margins.Top = 2 + Margins.Right = 0 + Margins.Bottom = 1 + Align = alLeft + Caption = 'AI off-Line' + TabOrder = 2 + OnClick = btnIAsOfflineClick + end + object btnCodeCompletion: TButton + AlignWithMargins = True + Left = 297 + Top = 2 + Width = 95 + Height = 23 + Cursor = crHandPoint + Margins.Top = 2 + Margins.Right = 0 + Margins.Bottom = 1 + Align = alLeft + Caption = 'Code Completion' + TabOrder = 3 + OnClick = btnCodeCompletionClick + end + end + object pnBody: TPanel + Left = 0 + Top = 30 + Width = 670 + Height = 611 + Align = alClient + BevelOuter = bvNone + TabOrder = 1 + end + end + end + object PageControl1: TPageControl + Left = 0 + Top = 33 + Width = 667 + Height = 521 + ActivePage = TabSheet2 + TabOrder = 1 + object TabSheet1: TTabSheet + Caption = 'Preferences' + object pnPreferencesBack: TPanel + Left = 0 + Top = 0 + Width = 659 + Height = 493 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object GroupBox2: TGroupBox + Left = 0 + Top = 0 + Width = 659 + Height = 493 + Align = alClient + Caption = ' Preferences ' + ParentBackground = False + TabOrder = 0 + object Label11: TLabel + Left = 272 + Top = 23 + Width = 186 + Height = 13 + Caption = 'AI default (Chat and Databases Chat) ' + end + object Label4: TLabel + Left = 21 + Top = 23 + Width = 133 + Height = 13 + Caption = 'Language used in questions' + end + object Label19: TLabel + Left = 21 + Top = 122 + Width = 72 + Height = 13 + Caption = 'Default Prompt' + end + object cBoxAIDefault: TComboBox + Left = 272 + Top = 38 + Width = 333 + Height = 21 + Style = csDropDownList + TabOrder = 1 + Items.Strings = ( + '') + end + object ColorBoxColorHighlightCodeDelphi: TColorBox + Left = 21 + Top = 87 + Width = 194 + Height = 22 + TabOrder = 3 + end + object ckColorHighlightCodeDelphiUse: TCheckBox + Left = 21 + Top = 67 + Width = 194 + Height = 17 + Caption = 'Color to highlight Delphi/Pascal code' + TabOrder = 2 + OnClick = ckColorHighlightCodeDelphiUseClick + end + object cBoxLanguageQuestions: TComboBox + Left = 21 + Top = 38 + Width = 249 + Height = 21 + Hint = 'What is the standard language for questions?' + Style = csDropDownList + ParentShowHint = False + ShowHint = True + TabOrder = 0 + end + object gboxData: TGroupBox + Left = 2 + Top = 433 + Width = 655 + Height = 58 + Align = alBottom + Caption = ' Data ' + Padding.Left = 2 + Padding.Top = 5 + Padding.Bottom = 3 + TabOrder = 4 + object btnOpenDataFolder: TButton + Left = 4 + Top = 20 + Width = 122 + Height = 33 + Cursor = crHandPoint + Align = alLeft + Caption = 'Open Data Folder' + TabOrder = 0 + OnClick = btnOpenDataFolderClick + end + end + object mmDefaultPrompt: TMemo + Left = 21 + Top = 139 + Width = 617 + Height = 201 + ScrollBars = ssVertical + TabOrder = 5 + end + end + end + end + object TabSheet2: TTabSheet + Caption = 'AI on-line' + ImageIndex = 1 + object pnIAsOnLineBack: TPanel + Left = 0 + Top = 0 + Width = 659 + Height = 493 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object Bevel1: TBevel + AlignWithMargins = True + Left = 0 + Top = 489 + Width = 659 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alBottom + Shape = bsTopLine + ExplicitTop = 158 + ExplicitWidth = 441 + end + object Bevel2: TBevel + AlignWithMargins = True + Left = 0 + Top = 505 + Width = 659 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alTop + Shape = bsTopLine + ExplicitTop = 475 + ExplicitWidth = 632 + end + object Bevel3: TBevel + AlignWithMargins = True + Left = 0 + Top = 253 + Width = 659 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alTop + Shape = bsTopLine + ExplicitTop = 347 + ExplicitWidth = 632 + end + object Bevel4: TBevel + AlignWithMargins = True + Left = 0 + Top = 124 + Width = 659 + Height = 1 + Margins.Left = 0 + Margins.Top = 0 + Margins.Right = 0 + Align = alTop + Shape = bsTopLine + ExplicitTop = 218 + ExplicitWidth = 632 + end + object gBoxGemini: TGroupBox + Left = 0 + Top = 0 + Width = 659 + Height = 124 + Align = alTop + Caption = ' Gemini (Google) ' + ParentBackground = False + TabOrder = 0 + object pnGeminiBack: TPanel + AlignWithMargins = True + Left = 5 + Top = 18 + Width = 649 + Height = 101 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object Label5: TLabel + Left = 16 + Top = 5 + Width = 45 + Height = 13 + Caption = 'Base URL' + end + object Label6: TLabel + Left = 16 + Top = 44 + Width = 37 + Height = 13 + Caption = 'API key' + end + object Label7: TLabel + Left = 267 + Top = 5 + Width = 28 + Height = 13 + Caption = 'Model' + end + object lbLinkGemini01: TLabel + Left = 16 + Top = 83 + Width = 86 + Height = 13 + Cursor = crHandPoint + Hint = 'https://aistudio.google.com/app/apikey' + Caption = 'Generate API Key' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object lbLinkGemini02: TLabel + Left = 128 + Top = 83 + Width = 72 + Height = 13 + Cursor = crHandPoint + Hint = 'https://ai.google.dev/gemini-api/docs/api-overview#curl_1' + Caption = 'Documentation' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object btnApiKeyGeminiView: TSpeedButton + Left = 582 + Top = 59 + Width = 23 + Height = 22 + Cursor = crHandPoint + Hint = 'Show/Hide API Key' + Flat = True + Glyph.Data = { + 36030000424D3603000000000000360000002800000010000000100000000100 + 18000000000000030000120B0000120B00000000000000000000FF00FF4A667C + BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF + 2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + 51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF + 00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3 + B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD + EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF + FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1 + C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF + FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF + E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4 + C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4 + C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8 + DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890 + 86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF + 00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF} + ParentShowHint = False + ShowHint = True + OnClick = btnApiKeyGeminiViewClick + end + object lbLinkGemini03: TLabel + Left = 232 + Top = 83 + Width = 67 + Height = 13 + Cursor = crHandPoint + Hint = 'https://ai.google.dev/gemini-api/docs/models/gemini?hl=pt-br' + Caption = 'Gemini Models' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object edtBaseUrlGemini: TEdit + Left = 16 + Top = 20 + Width = 249 + Height = 21 + TabOrder = 0 + end + object edtApiKeyGemini: TEdit + Left = 16 + Top = 60 + Width = 563 + Height = 21 + PasswordChar = '*' + TabOrder = 2 + end + object cBoxModelGemini: TComboBox + Left = 267 + Top = 20 + Width = 333 + Height = 21 + TabOrder = 1 + Items.Strings = ( + 'v1/models/gemini-1.5-flash:generateContent' + 'v1beta/models/gemini-pro:generateContent' + 'v1beta/models/gemini-1.5-flash:generateContent') + end + end + end + object gBoxOpenAI: TGroupBox + Left = 0 + Top = 128 + Width = 659 + Height = 125 + Align = alTop + Caption = ' ChatGPT (OpenAI)' + ParentBackground = False + TabOrder = 1 + object pnOpenAIBack: TPanel + AlignWithMargins = True + Left = 5 + Top = 18 + Width = 649 + Height = 102 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object Label1: TLabel + Left = 16 + Top = 5 + Width = 45 + Height = 13 + Caption = 'Base URL' + end + object Label3: TLabel + Left = 16 + Top = 44 + Width = 37 + Height = 13 + Caption = 'API key' + end + object Label2: TLabel + Left = 374 + Top = 5 + Width = 28 + Height = 13 + Caption = 'Model' + end + object lbLinkGpt01: TLabel + Left = 16 + Top = 84 + Width = 86 + Height = 13 + Cursor = crHandPoint + Hint = 'https://platform.openai.com/api-keys' + Caption = 'Generate API Key' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object btnApiKeyOpenAIView: TSpeedButton + Left = 582 + Top = 59 + Width = 23 + Height = 22 + Cursor = crHandPoint + Hint = 'Show/Hide API Key' + Flat = True + Glyph.Data = { + 36030000424D3603000000000000360000002800000010000000100000000100 + 18000000000000030000120B0000120B00000000000000000000FF00FF4A667C + BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF + 2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + 51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF + 00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3 + B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD + EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF + FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1 + C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF + FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF + E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4 + C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4 + C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8 + DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890 + 86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF + 00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF} + ParentShowHint = False + ShowHint = True + OnClick = btnApiKeyOpenAIViewClick + end + object lbLinkGpt02: TLabel + Left = 128 + Top = 84 + Width = 72 + Height = 13 + Cursor = crHandPoint + Hint = 'https://platform.openai.com/docs/api-reference/making-requests' + Caption = 'Documentation' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object edtBaseUrlOpenAI: TEdit + Left = 16 + Top = 21 + Width = 356 + Height = 21 + TabOrder = 0 + end + object edtApiKeyOpenAI: TEdit + Left = 16 + Top = 60 + Width = 563 + Height = 21 + PasswordChar = '*' + TabOrder = 2 + end + object cBoxModelOpenAI: TComboBox + Left = 374 + Top = 21 + Width = 226 + Height = 21 + TabOrder = 1 + Items.Strings = ( + 'gpt-3.5-turbo' + 'gpt-3.5-turbo-16k' + 'gpt-4' + 'gpt-4o-2024-05-13' + 'gpt-4o-2024-08-06' + 'gpt-4-turbo') + end + end + end + object gBoxGroq: TGroupBox + Left = 0 + Top = 257 + Width = 659 + Height = 124 + Align = alTop + Caption = ' Groq ' + ParentBackground = False + TabOrder = 2 + object pnGroqBack: TPanel + AlignWithMargins = True + Left = 5 + Top = 18 + Width = 649 + Height = 101 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object Label8: TLabel + Left = 16 + Top = 5 + Width = 45 + Height = 13 + Caption = 'Base URL' + end + object Label9: TLabel + Left = 16 + Top = 44 + Width = 37 + Height = 13 + Caption = 'API key' + end + object Label10: TLabel + Left = 374 + Top = 5 + Width = 28 + Height = 13 + Caption = 'Model' + end + object lbLinkGroq01: TLabel + Left = 16 + Top = 84 + Width = 86 + Height = 13 + Cursor = crHandPoint + Hint = 'https://console.groq.com/keys' + Caption = 'Generate API Key' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object btnApiKeyGroqView: TSpeedButton + Left = 582 + Top = 59 + Width = 23 + Height = 22 + Cursor = crHandPoint + Hint = 'Show/Hide API Key' + Flat = True + Glyph.Data = { + 36030000424D3603000000000000360000002800000010000000100000000100 + 18000000000000030000120B0000120B00000000000000000000FF00FF4A667C + BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF + 2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + 51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF + 00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3 + B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD + EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF + FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1 + C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF + FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF + E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4 + C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4 + C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8 + DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890 + 86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF + 00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF} + ParentShowHint = False + ShowHint = True + OnClick = btnApiKeyGroqViewClick + end + object lbLinkGroq02: TLabel + Left = 128 + Top = 84 + Width = 72 + Height = 13 + Cursor = crHandPoint + Hint = 'https://console.groq.com/docs/quickstart' + Caption = 'Documentation' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object lbLinkGroq03: TLabel + Left = 232 + Top = 84 + Width = 59 + Height = 13 + Cursor = crHandPoint + Hint = 'https://console.groq.com/docs/models' + Caption = 'Groq Models' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object edtBaseUrlGroq: TEdit + Left = 16 + Top = 21 + Width = 356 + Height = 21 + TabOrder = 0 + end + object edtApiKeyGroq: TEdit + Left = 16 + Top = 60 + Width = 563 + Height = 21 + PasswordChar = '*' + TabOrder = 2 + end + object cBoxModelGroq: TComboBox + Left = 374 + Top = 21 + Width = 226 + Height = 21 + TabOrder = 1 + Items.Strings = ( + 'llama3-8b-8192' + 'llama3-70b-8192' + 'llama3-groq-8b-8192-tool-use-preview' + 'llama3-groq-70b-8192-tool-use-preview' + 'mixtral-8x7b-32768' + 'gemma-7b-it' + 'gemma2-9b-it' + 'whisper-large-v3') + end + end + end + object gBoxMistral: TGroupBox + Left = 0 + Top = 381 + Width = 659 + Height = 124 + Align = alTop + Caption = ' Mistral ' + ParentBackground = False + TabOrder = 3 + object pnMistralBack: TPanel + AlignWithMargins = True + Left = 5 + Top = 18 + Width = 649 + Height = 101 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object Label20: TLabel + Left = 16 + Top = 5 + Width = 45 + Height = 13 + Caption = 'Base URL' + end + object Label21: TLabel + Left = 16 + Top = 44 + Width = 37 + Height = 13 + Caption = 'API key' + end + object Label22: TLabel + Left = 374 + Top = 5 + Width = 28 + Height = 13 + Caption = 'Model' + end + object lbLinkMistral01: TLabel + Left = 16 + Top = 84 + Width = 86 + Height = 13 + Cursor = crHandPoint + Hint = 'https://console.mistral.ai/api-keys/' + Caption = 'Generate API Key' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object btnApiKeyMistralView: TSpeedButton + Left = 582 + Top = 59 + Width = 23 + Height = 22 + Cursor = crHandPoint + Hint = 'Show/Hide API Key' + Flat = True + Glyph.Data = { + 36030000424D3603000000000000360000002800000010000000100000000100 + 18000000000000030000120B0000120B00000000000000000000FF00FF4A667C + BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF + 2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + 51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF + 00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3 + B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD + EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF + FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1 + C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF + FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF + E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4 + C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4 + C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8 + DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890 + 86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF + 00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF} + ParentShowHint = False + ShowHint = True + OnClick = btnApiKeyMistralViewClick + end + object lbLinkMistral02: TLabel + Left = 128 + Top = 84 + Width = 72 + Height = 13 + Cursor = crHandPoint + Hint = 'https://docs.mistral.ai/getting-started/quickstart/' + Caption = 'Documentation' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object lbLinkMistral03: TLabel + Left = 232 + Top = 84 + Width = 67 + Height = 13 + Cursor = crHandPoint + Hint = 'https://docs.mistral.ai/getting-started/models/models_overview/' + Caption = 'Mistral Models' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object edtBaseUrlMistral: TEdit + Left = 16 + Top = 21 + Width = 356 + Height = 21 + TabOrder = 0 + end + object edtApiKeyMistral: TEdit + Left = 16 + Top = 60 + Width = 563 + Height = 21 + PasswordChar = '*' + TabOrder = 2 + end + object cBoxModelMistral: TComboBox + Left = 374 + Top = 21 + Width = 226 + Height = 21 + TabOrder = 1 + Items.Strings = ( + 'codestral-latest' + 'mistral-large-latest' + 'ministral-3b-latest' + 'ministral-8b-latest' + 'mistral-small-latest' + 'open-codestral-mamba') + end + end + end + end + end + object TabSheet3: TTabSheet + Caption = 'AI off-Line' + ImageIndex = 2 + object pnIAsOffLineBack: TPanel + Left = 0 + Top = 0 + Width = 659 + Height = 493 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object GroupBox1: TGroupBox + Left = 0 + Top = 0 + Width = 659 + Height = 152 + Align = alTop + Caption = ' Ollama (offline) ' + ParentBackground = False + TabOrder = 0 + object Panel1: TPanel + AlignWithMargins = True + Left = 5 + Top = 18 + Width = 649 + Height = 129 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object Label12: TLabel + Left = 16 + Top = 5 + Width = 45 + Height = 13 + Caption = 'Base URL' + end + object Label13: TLabel + Left = 16 + Top = 44 + Width = 29 + Height = 13 + Caption = 'Token' + end + object Label14: TLabel + Left = 374 + Top = 5 + Width = 28 + Height = 13 + Caption = 'Model' + end + object btnApiKeyOllamaView: TSpeedButton + Left = 582 + Top = 59 + Width = 23 + Height = 22 + Cursor = crHandPoint + Hint = 'Show/Hide API Key' + Flat = True + Glyph.Data = { + 36030000424D3603000000000000360000002800000010000000100000000100 + 18000000000000030000120B0000120B00000000000000000000FF00FF4A667C + BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF + 2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF + 00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + 51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00 + FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF + 00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF + FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3 + B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD + EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF + FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1 + C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF + FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF + E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4 + C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4 + C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8 + DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF + FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890 + 86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF + 00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF} + ParentShowHint = False + ShowHint = True + OnClick = btnApiKeyOllamaViewClick + end + object lbLinkOllama02: TLabel + Left = 128 + Top = 84 + Width = 72 + Height = 13 + Cursor = crHandPoint + Hint = 'https://github.com/ollama/ollama/blob/main/docs/api.md' + Caption = 'Documentation' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object lbLinkOllama03: TLabel + Left = 232 + Top = 84 + Width = 68 + Height = 13 + Cursor = crHandPoint + Hint = 'https://ollama.com/library' + Caption = 'Ollama Models' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object lbLinkOllama01: TLabel + Left = 16 + Top = 84 + Width = 82 + Height = 13 + Cursor = crHandPoint + Hint = 'https://ollama.com/download' + Caption = 'Ollama Download' + Font.Charset = DEFAULT_CHARSET + Font.Color = clBlue + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + ParentShowHint = False + ShowHint = True + OnClick = lbLinkGpt01Click + end + object edtBaseUrlOllama: TEdit + Left = 16 + Top = 21 + Width = 356 + Height = 21 + TabOrder = 0 + end + object edtApiKeyOllama: TEdit + Left = 16 + Top = 60 + Width = 563 + Height = 21 + PasswordChar = '*' + TabOrder = 2 + end + object cBoxModelOllama: TComboBox + Left = 374 + Top = 21 + Width = 226 + Height = 21 + TabOrder = 1 + Items.Strings = ( + 'llama2' + 'llama3' + 'llama3.1' + 'codellama' + 'tinyllama' + 'mistral') + end + end + end + end + end + object TabSheet4: TTabSheet + Caption = 'Code Completion' + ImageIndex = 3 + object pnCodeCompletionBack: TPanel + Left = 0 + Top = 0 + Width = 659 + Height = 493 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object GroupBox3: TGroupBox + Left = 0 + Top = 0 + Width = 659 + Height = 493 + Align = alClient + Caption = ' Code Completion (BETA) ' + ParentBackground = False + TabOrder = 0 + object Panel2: TPanel + AlignWithMargins = True + Left = 5 + Top = 18 + Width = 649 + Height = 470 + Align = alClient + BevelOuter = bvNone + ParentBackground = False + TabOrder = 0 + object Label15: TLabel + Left = 16 + Top = 29 + Width = 48 + Height = 13 + Caption = 'AI default' + end + object Label16: TLabel + Left = 16 + Top = 138 + Width = 231 + Height = 13 + Caption = 'Shortcut for invoke (Delphi IDE restart required)' + end + object Label17: TLabel + Left = 16 + Top = 443 + Width = 316 + Height = 13 + Caption = '* Attention! This feature is in Beta and may be unstable.' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object Label18: TLabel + Left = 16 + Top = 187 + Width = 72 + Height = 13 + Caption = 'Default Prompt' + end + object ckCodeCompletionUse: TCheckBox + Left = 16 + Top = 5 + Width = 111 + Height = 17 + Cursor = crHandPoint + Caption = 'Code Completion' + TabOrder = 0 + end + object cBoxCodeCompletionAIDefault: TComboBox + Left = 16 + Top = 48 + Width = 333 + Height = 21 + Style = csDropDownList + TabOrder = 1 + Items.Strings = ( + '') + end + object ColorBoxCodeCompletionSuggestionColor: TColorBox + Left = 16 + Top = 102 + Width = 333 + Height = 22 + TabOrder = 2 + end + object ckCodeCompletionSuggestionColorUse: TCheckBox + Left = 16 + Top = 81 + Width = 131 + Height = 17 + Cursor = crHandPoint + Caption = 'Suggestion Code Color' + TabOrder = 3 + OnClick = ckCodeCompletionSuggestionColorUseClick + end + object edtCodeCompletionShortcutInvoke: TEdit + Left = 16 + Top = 155 + Width = 333 + Height = 21 + TabOrder = 4 + end + object mmCodeCompletionDefaultPrompt: TMemo + Left = 16 + Top = 204 + Width = 617 + Height = 201 + ScrollBars = ssVertical + TabOrder = 5 + end + end + end + end + end end end diff --git a/Src/Settings/DelphiAIDev.Settings.View.pas b/Src/Settings/DelphiAIDev.Settings.View.pas index 764b65e..5703288 100644 --- a/Src/Settings/DelphiAIDev.Settings.View.pas +++ b/Src/Settings/DelphiAIDev.Settings.View.pas @@ -14,9 +14,12 @@ interface Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, + Vcl.Buttons, + Vcl.ComCtrls, + Vcl.Menus, DelphiAIDev.Settings, DelphiAIDev.Types, - Vcl.Buttons; + DelphiAIDev.Consts; type TDelphiAIDevSettingsView = class(TForm) @@ -24,40 +27,66 @@ TDelphiAIDevSettingsView = class(TForm) btnConfirm: TButton; btnClose: TButton; pnBackAll: TPanel; - pnBody: TPanel; + lbRestoreDefaults: TLabel; + PageControl1: TPageControl; + TabSheet1: TTabSheet; + TabSheet2: TTabSheet; + pnMyControl: TPanel; + pnMyControlButtons: TPanel; + btnPreferences: TButton; + btnIAsOnline: TButton; + pnPreferencesBack: TPanel; + GroupBox2: TGroupBox; + Label11: TLabel; + Label4: TLabel; + cBoxAIDefault: TComboBox; + ColorBoxColorHighlightCodeDelphi: TColorBox; + ckColorHighlightCodeDelphiUse: TCheckBox; + cBoxLanguageQuestions: TComboBox; + TabSheet3: TTabSheet; + pnIAsOffLineBack: TPanel; + GroupBox1: TGroupBox; + Panel1: TPanel; + Label12: TLabel; + Label13: TLabel; + Label14: TLabel; + btnApiKeyOllamaView: TSpeedButton; + lbLinkOllama02: TLabel; + lbLinkOllama03: TLabel; + lbLinkOllama01: TLabel; + edtBaseUrlOllama: TEdit; + edtApiKeyOllama: TEdit; + cBoxModelOllama: TComboBox; + TabSheet4: TTabSheet; + pnCodeCompletionBack: TPanel; + pnIAsOnLineBack: TPanel; + Bevel1: TBevel; + Bevel2: TBevel; + Bevel3: TBevel; + Bevel4: TBevel; gBoxGemini: TGroupBox; pnGeminiBack: TPanel; Label5: TLabel; Label6: TLabel; Label7: TLabel; lbLinkGemini01: TLabel; + lbLinkGemini02: TLabel; + btnApiKeyGeminiView: TSpeedButton; + lbLinkGemini03: TLabel; edtBaseUrlGemini: TEdit; edtApiKeyGemini: TEdit; cBoxModelGemini: TComboBox; - lbLinkGemini02: TLabel; - GroupBox2: TGroupBox; gBoxOpenAI: TGroupBox; pnOpenAIBack: TPanel; Label1: TLabel; Label3: TLabel; Label2: TLabel; lbLinkGpt01: TLabel; + btnApiKeyOpenAIView: TSpeedButton; + lbLinkGpt02: TLabel; edtBaseUrlOpenAI: TEdit; edtApiKeyOpenAI: TEdit; cBoxModelOpenAI: TComboBox; - Label11: TLabel; - cBoxAIDefault: TComboBox; - btnApiKeyGeminiView: TSpeedButton; - btnApiKeyOpenAIView: TSpeedButton; - lbLinkGpt02: TLabel; - lbRestoreDefaults: TLabel; - ColorBoxColorHighlightCodeDelphi: TColorBox; - ckColorHighlightCodeDelphiUse: TCheckBox; - lbLinkGemini03: TLabel; - Label4: TLabel; - cBoxLanguageQuestions: TComboBox; - gboxData: TGroupBox; - btnOpenDataFolder: TButton; gBoxGroq: TGroupBox; pnGroqBack: TPanel; Label8: TLabel; @@ -66,10 +95,42 @@ TDelphiAIDevSettingsView = class(TForm) lbLinkGroq01: TLabel; btnApiKeyGroqView: TSpeedButton; lbLinkGroq02: TLabel; + lbLinkGroq03: TLabel; edtBaseUrlGroq: TEdit; edtApiKeyGroq: TEdit; cBoxModelGroq: TComboBox; - lbLinkGroq03: TLabel; + pnBody: TPanel; + btnIAsOffline: TButton; + btnCodeCompletion: TButton; + Bevel5: TBevel; + GroupBox3: TGroupBox; + Panel2: TPanel; + ckCodeCompletionUse: TCheckBox; + Label15: TLabel; + cBoxCodeCompletionAIDefault: TComboBox; + ColorBoxCodeCompletionSuggestionColor: TColorBox; + ckCodeCompletionSuggestionColorUse: TCheckBox; + Label16: TLabel; + gboxData: TGroupBox; + btnOpenDataFolder: TButton; + edtCodeCompletionShortcutInvoke: TEdit; + Label17: TLabel; + Label18: TLabel; + mmCodeCompletionDefaultPrompt: TMemo; + Label19: TLabel; + mmDefaultPrompt: TMemo; + gBoxMistral: TGroupBox; + pnMistralBack: TPanel; + Label20: TLabel; + Label21: TLabel; + Label22: TLabel; + lbLinkMistral01: TLabel; + btnApiKeyMistralView: TSpeedButton; + lbLinkMistral02: TLabel; + lbLinkMistral03: TLabel; + edtBaseUrlMistral: TEdit; + edtApiKeyMistral: TEdit; + cBoxModelMistral: TComboBox; procedure FormCreate(Sender: TObject); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure btnCloseClick(Sender: TObject); @@ -81,8 +142,15 @@ TDelphiAIDevSettingsView = class(TForm) procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure lbRestoreDefaultsClick(Sender: TObject); procedure ckColorHighlightCodeDelphiUseClick(Sender: TObject); - procedure btnOpenDataFolderClick(Sender: TObject); procedure btnApiKeyGroqViewClick(Sender: TObject); + procedure btnApiKeyOllamaViewClick(Sender: TObject); + procedure btnPreferencesClick(Sender: TObject); + procedure btnIAsOnlineClick(Sender: TObject); + procedure btnIAsOfflineClick(Sender: TObject); + procedure btnCodeCompletionClick(Sender: TObject); + procedure btnOpenDataFolderClick(Sender: TObject); + procedure ckCodeCompletionSuggestionColorUseClick(Sender: TObject); + procedure btnApiKeyMistralViewClick(Sender: TObject); private FSettings: TDelphiAIDevSettings; procedure SaveSettings; @@ -91,6 +159,11 @@ TDelphiAIDevSettingsView = class(TForm) procedure ConfigFieldsColorHighlightDelphi; procedure FillcBoxLanguageQuestions; procedure FillcBoxAIDefault; + procedure FillcBoxCodeCompletionAIDefault; + procedure ShowPanel(const AButton: TButton; const APanel: TPanel); + procedure PanelsSetParent; + procedure ValidateCodeCompletionShortcutInvoke; + procedure ConfigFieldsCodeCompletionSuggestionColor; public end; @@ -111,15 +184,16 @@ procedure TDelphiAIDevSettingsView.FormCreate(Sender: TObject); TUtilsOTA.IDEThemingAll(TDelphiAIDevSettingsView, Self); FSettings := TDelphiAIDevSettings.GetInstance; + Self.PanelsSetParent; Self.FillcBoxLanguageQuestions; Self.FillcBoxAIDefault; + Self.FillcBoxCodeCompletionAIDefault; end; procedure TDelphiAIDevSettingsView.FormShow(Sender: TObject); begin FSettings.LoadData; Self.ConfigScreen; - //FSettings.LoadData; Self.LoadSettings; end; @@ -139,13 +213,22 @@ procedure TDelphiAIDevSettingsView.FillcBoxLanguageQuestions; procedure TDelphiAIDevSettingsView.FillcBoxAIDefault; var - LItem: TC4DAIsAvailable; + LItem: TC4DAiAvailable; begin cBoxAIDefault.Items.Clear; - for LItem := Low(TC4DAIsAvailable) to High(TC4DAIsAvailable) do + for LItem := Low(TC4DAiAvailable) to High(TC4DAiAvailable) do cBoxAIDefault.Items.Add(LItem.ToString); end; +procedure TDelphiAIDevSettingsView.FillcBoxCodeCompletionAIDefault; +var + LItem: TC4DAiAvailable; +begin + cBoxCodeCompletionAIDefault.Items.Clear; + for LItem := Low(TC4DAiAvailable) to High(TC4DAiAvailable) do + cBoxCodeCompletionAIDefault.Items.Add(LItem.ToString); +end; + procedure TDelphiAIDevSettingsView.ConfigScreen; var LColor: TColor; @@ -159,7 +242,13 @@ procedure TDelphiAIDevSettingsView.ConfigScreen; lbLinkGroq01.Font.Color := LColor; lbLinkGroq02.Font.Color := LColor; lbLinkGroq03.Font.Color := LColor; + lbLinkMistral01.Font.Color := LColor; + lbLinkMistral02.Font.Color := LColor; + lbLinkMistral03.Font.Color := LColor; lbRestoreDefaults.Font.Color := LColor; + lbLinkOllama01.Font.Color := LColor; + lbLinkOllama02.Font.Color := LColor; + lbLinkOllama03.Font.Color := LColor; end; procedure TDelphiAIDevSettingsView.btnApiKeyGeminiViewClick(Sender: TObject); @@ -177,6 +266,11 @@ procedure TDelphiAIDevSettingsView.btnApiKeyGroqViewClick(Sender: TObject); TUtils.TogglePasswordChar(edtApiKeyGroq); end; +procedure TDelphiAIDevSettingsView.btnApiKeyMistralViewClick(Sender: TObject); +begin + TUtils.TogglePasswordChar(edtApiKeyMistral); +end; + procedure TDelphiAIDevSettingsView.btnCloseClick(Sender: TObject); begin Self.Close; @@ -197,7 +291,7 @@ procedure TDelphiAIDevSettingsView.FormKeyDown(Sender: TObject; var Key: Word; S procedure TDelphiAIDevSettingsView.lbLinkGpt01Click(Sender: TObject); begin - //**Several + //**SEVERAL TUtils.OpenLink(TLabel(Sender).Hint.Trim); end; @@ -206,27 +300,60 @@ procedure TDelphiAIDevSettingsView.lbRestoreDefaultsClick(Sender: TObject); LApiKeyGemini: string; LApiKeyOpenAI: string; LApiKeyGroq: string; + LApiKeyMistral: string; + LApiKeyOllama: string; begin LApiKeyGemini := FSettings.ApiKeyGemini; LApiKeyOpenAI := FSettings.ApiKeyOpenAI; LApiKeyGroq := FSettings.ApiKeyGroq; + LApiKeyMistral := FSettings.ApiKeyMistral; + LApiKeyOllama := FSettings.ApiKeyOllama; FSettings.LoadDefaults; FSettings.ApiKeyGemini := LApiKeyGemini; FSettings.ApiKeyOpenAI := LApiKeyOpenAI; FSettings.ApiKeyGroq := LApiKeyGroq; + FSettings.ApiKeyMistral := LApiKeyMistral; + FSettings.ApiKeyOllama := LApiKeyOllama; Self.LoadSettings; end; procedure TDelphiAIDevSettingsView.btnConfirmClick(Sender: TObject); begin + Self.ValidateCodeCompletionShortcutInvoke; + Self.SaveSettings; Self.Close; Self.ModalResult := mrOk; end; +procedure TDelphiAIDevSettingsView.ValidateCodeCompletionShortcutInvoke; +var + LShortcutStr: string; + LShortCut: TShortCut; +begin + LShortcutStr := Trim(edtCodeCompletionShortcutInvoke.Text); + if LShortcutStr.Length = 1 then + raise Exception.Create('Invalid CodeCompletion shortcut'); + + LShortCut := TextToShortCut(LShortcutStr); + + if ShortCutToText(LShortCut).Trim.IsEmpty then + edtCodeCompletionShortcutInvoke.Text := TConsts.CODE_COMPLETION_SHORTCUT_INVOKE; +end; + +procedure TDelphiAIDevSettingsView.ckCodeCompletionSuggestionColorUseClick(Sender: TObject); +begin + Self.ConfigFieldsCodeCompletionSuggestionColor; +end; + +procedure TDelphiAIDevSettingsView.ConfigFieldsCodeCompletionSuggestionColor; +begin + ColorBoxCodeCompletionSuggestionColor.Enabled := ckCodeCompletionSuggestionColorUse.Checked; +end; + procedure TDelphiAIDevSettingsView.ckColorHighlightCodeDelphiUseClick(Sender: TObject); begin Self.ConfigFieldsColorHighlightDelphi; @@ -245,27 +372,61 @@ procedure TDelphiAIDevSettingsView.LoadSettings; ckColorHighlightCodeDelphiUse.Checked := FSettings.ColorHighlightCodeDelphiUse; ColorBoxColorHighlightCodeDelphi.Selected := FSettings.ColorHighlightCodeDelphi; Self.ConfigFieldsColorHighlightDelphi; + mmDefaultPrompt.Lines.Text := FSettings.DefaultPrompt; + + ckCodeCompletionUse.Checked := FSettings.CodeCompletionUse; + cBoxCodeCompletionAIDefault.ItemIndex := Integer(FSettings.CodeCompletionAIDefault); + ckCodeCompletionSuggestionColorUse.Checked := FSettings.CodeCompletionSuggestionColorUse; + ColorBoxCodeCompletionSuggestionColor.Selected := FSettings.CodeCompletionSuggestionColor; + edtCodeCompletionShortcutInvoke.Text := FSettings.CodeCompletionShortcutInvoke; + mmCodeCompletionDefaultPrompt.Lines.Text := FSettings.CodeCompletionDefaultPrompt; edtBaseUrlGemini.Text := FSettings.BaseUrlGemini; cBoxModelGemini.ItemIndex := cBoxModelGemini.Items.IndexOf(FSettings.ModelGemini); + if cBoxModelGemini.ItemIndex < 0 then + cBoxModelGemini.Text := FSettings.ModelGemini; edtApiKeyGemini.Text := FSettings.ApiKeyGemini; edtBaseUrlOpenAI.Text := FSettings.BaseUrlOpenAI; cBoxModelOpenAI.ItemIndex := cBoxModelOpenAI.Items.IndexOf(FSettings.ModelOpenAI); + if cBoxModelOpenAI.ItemIndex < 0 then + cBoxModelOpenAI.Text := FSettings.ModelOpenAI; edtApiKeyOpenAI.Text := FSettings.ApiKeyOpenAI; edtBaseUrlGroq.Text := FSettings.BaseUrlGroq; cBoxModelGroq.ItemIndex := cBoxModelGroq.Items.IndexOf(FSettings.ModelGroq); + if cBoxModelGroq.ItemIndex < 0 then + cBoxModelGroq.Text := FSettings.ModelGroq; edtApiKeyGroq.Text := FSettings.ApiKeyGroq; + + edtBaseUrlMistral.Text := FSettings.BaseUrlMistral; + cBoxModelMistral.ItemIndex := cBoxModelMistral.Items.IndexOf(FSettings.ModelMistral); + if cBoxModelMistral.ItemIndex < 0 then + cBoxModelMistral.Text := FSettings.ModelMistral; + edtApiKeyMistral.Text := FSettings.ApiKeyMistral; + + edtBaseUrlOllama.Text := FSettings.BaseUrlOllama; + cBoxModelOllama.ItemIndex := cBoxModelOllama.Items.IndexOf(FSettings.ModelOllama); + if cBoxModelOllama.ItemIndex < 0 then + cBoxModelOllama.Text := FSettings.ModelOllama; + edtApiKeyOllama.Text := FSettings.ApiKeyOllama; end; procedure TDelphiAIDevSettingsView.SaveSettings; begin FSettings.LanguageQuestions := TC4DLanguage(cBoxLanguageQuestions.ItemIndex); - FSettings.AIDefault := TC4DAIsAvailable(cBoxAIDefault.ItemIndex); + FSettings.AIDefault := TC4DAiAvailable(cBoxAIDefault.ItemIndex); FSettings.ColorHighlightCodeDelphiUse := ckColorHighlightCodeDelphiUse.Checked; FSettings.ColorHighlightCodeDelphi := ColorBoxColorHighlightCodeDelphi.Selected; + FSettings.DefaultPrompt := mmDefaultPrompt.Lines.Text; + + FSettings.CodeCompletionUse := ckCodeCompletionUse.Checked; + FSettings.CodeCompletionAIDefault := TC4DAiAvailable(cBoxCodeCompletionAIDefault.ItemIndex); + FSettings.CodeCompletionSuggestionColorUse := ckCodeCompletionSuggestionColorUse.Checked; + FSettings.CodeCompletionSuggestionColor := ColorBoxCodeCompletionSuggestionColor.Selected; + FSettings.CodeCompletionShortcutInvoke := edtCodeCompletionShortcutInvoke.Text; + FSettings.CodeCompletionDefaultPrompt := mmCodeCompletionDefaultPrompt.Lines.Text; FSettings.BaseUrlGemini := edtBaseUrlGemini.Text; FSettings.ModelGemini := cBoxModelGemini.Text; @@ -279,18 +440,82 @@ procedure TDelphiAIDevSettingsView.SaveSettings; FSettings.ModelGroq := cBoxModelGroq.Text; FSettings.ApiKeyGroq := edtApiKeyGroq.Text; + FSettings.BaseUrlMistral := edtBaseUrlMistral.Text; + FSettings.ModelMistral := cBoxModelMistral.Text; + FSettings.ApiKeyMistral := edtApiKeyMistral.Text; + + FSettings.BaseUrlOllama := edtBaseUrlOllama.Text; + FSettings.ModelOllama := cBoxModelOllama.Text; + FSettings.ApiKeyOllama := edtApiKeyOllama.Text; + FSettings.SaveData; end; +procedure TDelphiAIDevSettingsView.btnApiKeyOllamaViewClick(Sender: TObject); +begin + TUtils.TogglePasswordChar(edtApiKeyOllama); +end; + +procedure TDelphiAIDevSettingsView.PanelsSetParent; +begin + pnPreferencesBack.Visible := False; + pnIAsOnLineBack.Visible := False; + pnIAsOffLineBack.Visible := False; + pnCodeCompletionBack.Visible := False; + + pnPreferencesBack.Parent := pnBody; + pnIAsOnLineBack.Parent := pnBody; + pnIAsOffLineBack.Parent := pnBody; + pnCodeCompletionBack.Parent := pnBody; + PageControl1.Visible := False; + + btnPreferences.Click; +end; + +procedure TDelphiAIDevSettingsView.btnPreferencesClick(Sender: TObject); +begin + Self.ShowPanel(TButton(Sender), pnPreferencesBack); +end; + +procedure TDelphiAIDevSettingsView.btnIAsOnlineClick(Sender: TObject); +begin + Self.ShowPanel(TButton(Sender), pnIAsOnLineBack); +end; + procedure TDelphiAIDevSettingsView.btnOpenDataFolderClick(Sender: TObject); var LPathFolder: string; begin LPathFolder := TUtils.GetPathFolderRoot; - if(not DirectoryExists(LPathFolder))then + if not DirectoryExists(LPathFolder) then TUtils.ShowMsg('Forder not found: ' + LPathFolder); TUtils.OpenFolder(LPathFolder); end; +procedure TDelphiAIDevSettingsView.btnIAsOfflineClick(Sender: TObject); +begin + Self.ShowPanel(TButton(Sender), pnIAsOffLineBack); +end; + +procedure TDelphiAIDevSettingsView.btnCodeCompletionClick(Sender: TObject); +begin + Self.ShowPanel(TButton(Sender), pnCodeCompletionBack); +end; + +procedure TDelphiAIDevSettingsView.ShowPanel(const AButton: TButton; const APanel: TPanel); +begin + btnPreferences.Default := False; + btnIAsOnline.Default := False; + btnIAsOffline.Default := False; + btnCodeCompletion.Default := False; + AButton.Default := True; + + pnPreferencesBack.Visible := False; + pnIAsOnLineBack.Visible := False; + pnIAsOffLineBack.Visible := False; + pnCodeCompletionBack.Visible := False; + APanel.Visible := True; +end; + end. diff --git a/Src/Settings/DelphiAIDev.Settings.pas b/Src/Settings/DelphiAIDev.Settings.pas index f5bc2c9..7b73466 100644 --- a/Src/Settings/DelphiAIDev.Settings.pas +++ b/Src/Settings/DelphiAIDev.Settings.pas @@ -15,10 +15,17 @@ interface type TDelphiAIDevSettings = class private const - FIELD_LanguageQuestions = 'LanguageQuestions'; + FIELD_LanguageQuestions = 'LanguageQuestions'; FIELD_AIDefault = 'AIDefault'; FIELD_ColorHighlightCodeDelphiUse = 'ColorHighlightCodeDelphiUse'; - FIELD_ColorHighlightCodeDelphi = 'ColorHighlightCodeDelphi'; + FIELD_ColorHighlightCodeDelphi = 'ColorHighlightCodeDelphi'; + FIELD_DefaultPrompt = 'DefaultPrompt'; + FIELD_CodeCompletionUse = 'CodeCompletionUse'; + FIELD_CodeCompletionAIDefault = 'CodeCompletionAIDefault'; + FIELD_CodeCompletionSuggestionColorUse = 'CodeCompletionSuggestionColorUse'; + FIELD_CodeCompletionSuggestionColor = 'CodeCompletionSuggestionColor'; + FIELD_CodeCompletionShortcutInvoke = 'CodeCompletionShortcutInvoke'; + FIELD_CodeCompletionDefaultPrompt = 'CodeCompletionDefaultPrompt'; FIELD_BaseUrlGemini = 'BaseUrlGemini'; FIELD_ModelGemini = 'ModelGemini'; FIELD_ApiKeyGemini = 'ApiKeyGemini'; @@ -28,11 +35,25 @@ TDelphiAIDevSettings = class FIELD_BaseUrlGroq = 'BaseUrlGroq'; FIELD_ModelGroq = 'ModelGroq'; FIELD_ApiKeyGroq = 'ApiKeyGroq'; + FIELD_BaseUrlMistral = 'BaseUrlMistral'; + FIELD_ModelMistral = 'ModelMistral'; + FIELD_ApiKeyMistral = 'ApiKeyMistral'; + FIELD_BaseUrlOllama = 'BaseUrlOllama'; + FIELD_ModelOllama = 'ModelOllama'; + FIELD_ApiKeyOllama = 'ApiKeyOllama'; private FLanguageQuestions: TC4DLanguage; - FAIDefault: TC4DAIsAvailable; + FAIDefault: TC4DAiAvailable; FColorHighlightCodeDelphiUse: Boolean; FColorHighlightCodeDelphi: TColor; + FDefaultPrompt: string; + + FCodeCompletionUse: Boolean; + FCodeCompletionAIDefault: TC4DAiAvailable; + FCodeCompletionSuggestionColorUse: Boolean; + FCodeCompletionSuggestionColor: TColor; + FCodeCompletionShortcutInvoke: string; + FCodeCompletionDefaultPrompt: string; FBaseUrlGemini: string; FModelGemini: string; @@ -45,17 +66,38 @@ TDelphiAIDevSettings = class FBaseUrlGroq: string; FModelGroq: string; FApiKeyGroq: string; + + FBaseUrlMistral: string; + FModelMistral: string; + FApiKeyMistral: string; + + FBaseUrlOllama: string; + FModelOllama: string; + FApiKeyOllama: string; + constructor Create; + procedure ValidateFillingSelectedAIInternal( + const AShowMsg: TShowMsg; const AAiUse: TC4DAiAvailable); public class function GetInstance: TDelphiAIDevSettings; procedure LoadDefaults; procedure SaveData; procedure LoadData; + procedure ValidateFillingSelectedAI(const AShowMsg: TShowMsg = TShowMsg.Yes); + procedure ValidateFillingSelectedAICodeCompletion(const AShowMsg: TShowMsg = TShowMsg.Yes); property LanguageQuestions: TC4DLanguage read FLanguageQuestions write FLanguageQuestions; - property AIDefault: TC4DAIsAvailable read FAIDefault write FAIDefault; + property AIDefault: TC4DAiAvailable read FAIDefault write FAIDefault; property ColorHighlightCodeDelphiUse: Boolean read FColorHighlightCodeDelphiUse write FColorHighlightCodeDelphiUse; property ColorHighlightCodeDelphi: TColor read FColorHighlightCodeDelphi write FColorHighlightCodeDelphi; + property DefaultPrompt: string read FDefaultPrompt write FDefaultPrompt; + + property CodeCompletionUse: Boolean read FCodeCompletionUse write FCodeCompletionUse; + property CodeCompletionAIDefault: TC4DAiAvailable read FCodeCompletionAIDefault write FCodeCompletionAIDefault; + property CodeCompletionSuggestionColorUse: Boolean read FCodeCompletionSuggestionColorUse write FCodeCompletionSuggestionColorUse; + property CodeCompletionSuggestionColor: TColor read FCodeCompletionSuggestionColor write FCodeCompletionSuggestionColor; + property CodeCompletionShortcutInvoke: string read FCodeCompletionShortcutInvoke write FCodeCompletionShortcutInvoke; + property CodeCompletionDefaultPrompt: string read FCodeCompletionDefaultPrompt write FCodeCompletionDefaultPrompt; property BaseUrlGemini: string read FBaseUrlGemini write FBaseUrlGemini; property ModelGemini: string read FModelGemini write FModelGemini; @@ -68,6 +110,14 @@ TDelphiAIDevSettings = class property BaseUrlGroq: string read FBaseUrlGroq write FBaseUrlGroq; property ModelGroq: string read FModelGroq write FModelGroq; property ApiKeyGroq: string read FApiKeyGroq write FApiKeyGroq; + + property BaseUrlMistral: string read FBaseUrlMistral write FBaseUrlMistral; + property ModelMistral: string read FModelMistral write FModelMistral; + property ApiKeyMistral: string read FApiKeyMistral write FApiKeyMistral; + + property BaseUrlOllama: string read FBaseUrlOllama write FBaseUrlOllama; + property ModelOllama: string read FModelOllama write FModelOllama; + property ApiKeyOllama: string read FApiKeyOllama write FApiKeyOllama; end; implementation @@ -90,22 +140,38 @@ constructor TDelphiAIDevSettings.Create; procedure TDelphiAIDevSettings.LoadDefaults; begin FLanguageQuestions := TC4DLanguage.ptBR; - FAIDefault := TC4DAIsAvailable.Groq; + FAIDefault := TC4DAiAvailable.Groq; FColorHighlightCodeDelphiUse := False; FColorHighlightCodeDelphi := clNone; + FDefaultPrompt := ''; + + FCodeCompletionUse := False; + FCodeCompletionAIDefault := TC4DAiAvailable.Gemini; + FCodeCompletionSuggestionColorUse := False; + FCodeCompletionSuggestionColor := TConsts.CODE_COMPLETION_SUGGESTION_COLOR; + FCodeCompletionShortcutInvoke := TConsts.CODE_COMPLETION_SHORTCUT_INVOKE; + FCodeCompletionDefaultPrompt := ''; FBaseUrlGemini := TConsts.BASE_URL_GEMINI_DEFAULT; FModelGemini := TConsts.MODEL_GEMINI_DEFAULT; FApiKeyGemini := ''; FBaseUrlOpenAI := TConsts.BASE_URL_OPEN_AI; - FModelOpenAI := 'gpt-3.5-turbo'; + FModelOpenAI := TConsts.MODEL_OPEN_AI_DEFAULT; FApiKeyOpenAI := ''; FBaseUrlGroq := TConsts.BASE_URL_GROQ; - FModelGroq := 'llama3-8b-8192'; + FModelGroq := TConsts.MODEL_GROQ_DEFAULT; FApiKeyGroq := ''; + + FBaseUrlMistral := TConsts.BASE_URL_Mistral; + FModelMistral := TConsts.MODEL_Mistral_DEFAULT; + FApiKeyMistral := ''; + + FBaseUrlOllama := TConsts.BASE_URL_OLLAMA; + FModelOllama := TConsts.MODEL_OLLAMA_DEFAULT; + FApiKeyOllama := ''; end; procedure TDelphiAIDevSettings.SaveData; @@ -124,6 +190,14 @@ procedure TDelphiAIDevSettings.SaveData; LReg.WriteBool(FIELD_ColorHighlightCodeDelphiUse, FColorHighlightCodeDelphiUse); LReg.WriteString(FIELD_ColorHighlightCodeDelphi, ColorToString(FColorHighlightCodeDelphi)); + LReg.WriteString(FIELD_DefaultPrompt, FDefaultPrompt); + + LReg.WriteBool(FIELD_CodeCompletionUse, FCodeCompletionUse); + LReg.WriteInteger(FIELD_CodeCompletionAIDefault, Integer(FCodeCompletionAIDefault)); + LReg.WriteBool(FIELD_CodeCompletionSuggestionColorUse, FCodeCompletionSuggestionColorUse); + LReg.WriteString(FIELD_CodeCompletionSuggestionColor, ColorToString(FCodeCompletionSuggestionColor)); + LReg.WriteString(FIELD_CodeCompletionShortcutInvoke, FCodeCompletionShortcutInvoke); + LReg.WriteString(FIELD_CodeCompletionDefaultPrompt, FCodeCompletionDefaultPrompt); LReg.WriteString(FIELD_BaseUrlGemini, FBaseUrlGemini); LReg.WriteString(FIELD_ModelGemini, FModelGemini); @@ -136,6 +210,14 @@ procedure TDelphiAIDevSettings.SaveData; LReg.WriteString(FIELD_BaseUrlGroq, FBaseUrlGroq); LReg.WriteString(FIELD_ModelGroq, FModelGroq); LReg.WriteString(FIELD_ApiKeyGroq, FApiKeyGroq); + + LReg.WriteString(FIELD_BaseUrlMistral, FBaseUrlMistral); + LReg.WriteString(FIELD_ModelMistral, FModelMistral); + LReg.WriteString(FIELD_ApiKeyMistral, FApiKeyMistral); + + LReg.WriteString(FIELD_BaseUrlOllama, FBaseUrlOllama); + LReg.WriteString(FIELD_ModelOllama, FModelOllama); + LReg.WriteString(FIELD_ApiKeyOllama, FApiKeyOllama); finally LReg.Free; end; @@ -152,14 +234,14 @@ procedure TDelphiAIDevSettings.LoadData; LReg.CloseKey; LReg.RootKey := HKEY_CURRENT_USER; - if not(LReg.OpenKey(TConsts.KEY_SETTINGS_IN_WINDOWS_REGISTRY, False)) then + if not LReg.OpenKey(TConsts.KEY_SETTINGS_IN_WINDOWS_REGISTRY, False) then Exit; if LReg.ValueExists(FIELD_LanguageQuestions) then FLanguageQuestions := TC4DLanguage(LReg.ReadInteger(FIELD_LanguageQuestions)); if LReg.ValueExists(FIELD_AIDefault) then - FAIDefault := TC4DAIsAvailable(LReg.ReadInteger(FIELD_AIDefault)); + FAIDefault := TC4DAiAvailable(LReg.ReadInteger(FIELD_AIDefault)); //COLOR FOR HIGHLIGHT CODE DELPHI/PASCAL if LReg.ValueExists(FIELD_ColorHighlightCodeDelphiUse) then @@ -169,6 +251,29 @@ procedure TDelphiAIDevSettings.LoadData; FColorHighlightCodeDelphi := TUtils.StringToColorDef(LReg.ReadString(FIELD_ColorHighlightCodeDelphi), TUtilsOTA.ActiveThemeForCode); + if LReg.ValueExists(FIELD_DefaultPrompt) then + FDefaultPrompt := LReg.ReadString(FIELD_DefaultPrompt); + + //Code Completion + if LReg.ValueExists(FIELD_CodeCompletionUse) then + FCodeCompletionUse := LReg.ReadBool(FIELD_CodeCompletionUse); + + if LReg.ValueExists(FIELD_CodeCompletionAIDefault) then + FCodeCompletionAIDefault := TC4DAiAvailable(LReg.ReadInteger(FIELD_CodeCompletionAIDefault)); + + if LReg.ValueExists(FIELD_CodeCompletionSuggestionColorUse) then + FCodeCompletionSuggestionColorUse := LReg.ReadBool(FIELD_CodeCompletionSuggestionColorUse); + + if LReg.ValueExists(FIELD_CodeCompletionSuggestionColor) then + FCodeCompletionSuggestionColor := TUtils.StringToColorDef(LReg.ReadString(FIELD_CodeCompletionSuggestionColor), + TUtilsOTA.ActiveThemeForCode); + + if LReg.ValueExists(FIELD_CodeCompletionShortcutInvoke) then + FCodeCompletionShortcutInvoke := LReg.ReadString(FIELD_CodeCompletionShortcutInvoke); + + if LReg.ValueExists(FIELD_CodeCompletionDefaultPrompt) then + FCodeCompletionDefaultPrompt := LReg.ReadString(FIELD_CodeCompletionDefaultPrompt); + //GEMINI if LReg.ValueExists(FIELD_BaseUrlGemini) then FBaseUrlGemini := LReg.ReadString(FIELD_BaseUrlGemini); @@ -198,6 +303,26 @@ procedure TDelphiAIDevSettings.LoadData; if LReg.ValueExists(FIELD_ApiKeyGroq) then fApiKeyGroq := LReg.ReadString(FIELD_ApiKeyGroq); + + //MISTRAL + if LReg.ValueExists(FIELD_BaseUrlMistral) then + fBaseUrlMistral := LReg.ReadString(FIELD_BaseUrlMistral); + + if LReg.ValueExists(FIELD_ModelMistral) then + fModelMistral := LReg.ReadString(FIELD_ModelMistral); + + if LReg.ValueExists(FIELD_ApiKeyMistral) then + fApiKeyMistral := LReg.ReadString(FIELD_ApiKeyMistral); + + //OLLAMA + if LReg.ValueExists(FIELD_BaseUrlOllama) then + fBaseUrlOllama := LReg.ReadString(FIELD_BaseUrlOllama); + + if LReg.ValueExists(FIELD_ModelOllama) then + fModelOllama := LReg.ReadString(FIELD_ModelOllama); + + if LReg.ValueExists(FIELD_ApiKeyOllama) then + fApiKeyOllama := LReg.ReadString(FIELD_ApiKeyOllama); except Self.LoadDefaults; end; @@ -206,6 +331,85 @@ procedure TDelphiAIDevSettings.LoadData; end; end; +procedure TDelphiAIDevSettings.ValidateFillingSelectedAI(const AShowMsg: TShowMsg = TShowMsg.Yes); +begin + Self.ValidateFillingSelectedAIInternal(AShowMsg, FAIDefault); +end; + +procedure TDelphiAIDevSettings.ValidateFillingSelectedAICodeCompletion(const AShowMsg: TShowMsg = TShowMsg.Yes); +begin + Self.ValidateFillingSelectedAIInternal(AShowMsg, FCodeCompletionAIDefault); +end; + +procedure TDelphiAIDevSettings.ValidateFillingSelectedAIInternal( + const AShowMsg: TShowMsg; const AAiUse: TC4DAiAvailable); +const + MSG = '"%s" for IA %s not specified in settings.' + sLineBreak + sLineBreak + + 'Access menu > AI Developer > Settings'; + + procedure ShowMsgInternal(const AArgs: array of const);//(const AMsg: string); + begin + if AShowMsg = TShowMsg.Yes then + TUtils.ShowMsg(Format(MSG, AArgs)); + Abort; + end; +begin + case AAiUse of + TC4DAiAvailable.Gemini: + begin + if FBaseUrlGemini.Trim.IsEmpty then + ShowMsgInternal(['Base URL', 'Gemini']); + + if FModelGemini.Trim.IsEmpty then + ShowMsgInternal(['Model', 'Gemini']); + + if FApiKeyGemini.Trim.IsEmpty then + ShowMsgInternal(['API Key', 'Gemini']); + end; + TC4DAiAvailable.OpenAI: + begin + if FBaseUrlOpenAI.Trim.IsEmpty then + ShowMsgInternal(['Base URL', 'ChatGPT']); + + if FModelOpenAI.Trim.IsEmpty then + ShowMsgInternal(['Model', 'ChatGPT']); + + if FApiKeyOpenAI.Trim.IsEmpty then + ShowMsgInternal(['API Key', 'ChatGPT']); + end; + TC4DAiAvailable.Groq: + begin + if FBaseUrlGroq.Trim.IsEmpty then + ShowMsgInternal(['Base URL', 'Groq']); + + if FModelGroq.Trim.IsEmpty then + ShowMsgInternal(['Model', 'Groq']); + + if FApiKeyGroq.Trim.IsEmpty then + ShowMsgInternal(['API Key', 'Groq']); + end; + TC4DAiAvailable.Mistral: + begin + if FBaseUrlMistral.Trim.IsEmpty then + ShowMsgInternal(['Base URL', 'Mistral']); + + if FModelMistral.Trim.IsEmpty then + ShowMsgInternal(['Model', 'Mistral']); + + if FApiKeyMistral.Trim.IsEmpty then + ShowMsgInternal(['API Key', 'Mistral']); + end; + TC4DAiAvailable.Ollama: + begin + if FBaseUrlOllama.Trim.IsEmpty then + ShowMsgInternal(['Base URL', 'Ollama']); + + if FModelOllama.Trim.IsEmpty then + ShowMsgInternal(['Model', 'Ollama']); + end; + end; +end; + initialization finalization diff --git a/Src/Test/DelphiAIDev.Test.Client.pas b/Src/Test/DelphiAIDev.Test.Client.pas new file mode 100644 index 0000000..5c67f6a --- /dev/null +++ b/Src/Test/DelphiAIDev.Test.Client.pas @@ -0,0 +1,39 @@ +unit DelphiAIDev.Test.Client; + +interface + +type + TDelphiAIDevTestClient = class + private + FNome: string; + FCPF: string; + FEndereco: string; + FIdade: Integer; + public + constructor Create(ANome: string; ACpf: string; AEndereco: string); + property Nome: string read FNome write FNome; + property CPF: string read FCPF write FCPF; + property Endereco: string read FEndereco write FEndereco; + property Idade: Integer read FIdade write FIdade; + procedure LimparVariaveis; + end; + +implementation + + +{ TDelphiAIDevTestClient } + +constructor TDelphiAIDevTestClient.Create(ANome, ACpf, AEndereco: string); +begin + +end; + +procedure TDelphiAIDevTestClient.LimparVariaveis; +begin + FNome := ''; + FCPF := ''; + FEndereco := ''; + FIdade := 0; +end; + +end. diff --git a/Src/Test/DelphiAIDev.Test.pas b/Src/Test/DelphiAIDev.Test.pas index 4fc715f..16205ab 100644 --- a/Src/Test/DelphiAIDev.Test.pas +++ b/Src/Test/DelphiAIDev.Test.pas @@ -5,9 +5,33 @@ interface type TDelphiAIDevTest = class private + FNome: string; + FEndereco: string; + FBairro: string; + FNumero: Integer; + FTelefone: string; + FEmail: string; + FDataNascimento: TDateTime; + FApelido: string; + FCep: Integer; public - end; + property Nome: string read FNome write FNome; + property Endereco: string read FEndereco write FEndereco; + property Bairro: string read FBairro write FBairro; + property Numero: Integer read FNumero write FNumero; + property Telefone: string read FTelefone write FTelefone; + property Email: string read FEmail write FEmail; + property DataNascimento: TDateTime read FDataNascimento write FDataNascimento; + property Apelido: string read FApelido write FApelido; + property Cep: Integer read FCep write FCep; + procedure Clear; + end; implementation +procedure TDelphiAIDevTest.Clear; +begin + +end; + end. diff --git a/Src/Types/DelphiAIDev.Types.pas b/Src/Types/DelphiAIDev.Types.pas index 7ea08fb..08a8b70 100644 --- a/Src/Types/DelphiAIDev.Types.pas +++ b/Src/Types/DelphiAIDev.Types.pas @@ -8,19 +8,24 @@ interface System.TypInfo; type + TC4DWizardMenuContextList = procedure(const MenuContextList: IInterfaceList) of object; + {$SCOPEDENUMS ON} - TC4DAIsAvailable = (Gemini, OpenAI, Groq); + TC4DAiAvailable = (Gemini, OpenAI, Groq, Mistral, Ollama); TC4DLanguage = (en, ptBR, es); - TC4DExtensionsFiles = (None, PAS, DFM, FMX, DPR, DPK, DPROJ, ZIP, BMP, INI, ALL); + TC4DExtensionsFiles = (None, PAS, DFM, FMX, DPR, DPK, DPROJ, GROUPPROJ, ZIP, BMP, INI, ALL); TC4DExtensionsOfFiles = set of TC4DExtensionsFiles; - TC4DExtensionsCommon = (rtf); + TC4DExtensionsCommon = (rtf, csv, txt); TC4DIcon = (Information, Question, Warning, Error, Success); TC4DButtons = (OK, OK_Cancel); TC4DBtnFocu = (OK, Cancel); TC4DQuestionKind = (None, ItemMenuNormal, MenuMasterOnly, Separators); + TC4DDriverID = (None, MySQL, Firebird); + TAutoFreeField = (Yes, No); + TShowMsg = (Yes, No); {$SCOPEDENUMS OFF} - TC4DAIsAvailableHelper = record helper for TC4DAIsAvailable + TC4DAiAvailableHelper = record helper for TC4DAiAvailable function ToString: string; function ToStringWithCreator: string; end; @@ -29,6 +34,10 @@ TC4DLanguageHelper = record helper for TC4DLanguage function ToString: string; function GetLanguageDefinition: string; function GetMsgCodeOnly: string; + function GetMsgSQLOnly: string; + function GetMsgJSONIsDatabaseStructure(ASGBDName: string): string; + function GetMsgJSONInformedAnswerQuestion: string; + function GetMsgCodeCompletionSuggestion: string; end; TC4DExtensionsFilesHelper = record helper for TC4DExtensionsFiles @@ -49,33 +58,38 @@ TC4DQuestionKindHelper = record helper for TC4DQuestionKind function ToString: string; end; + TC4DDriverIDHelper = record helper for TC4DDriverID + function ToString: string; + function ToStringID: string; + end; + implementation uses DelphiAIDev.Consts; { TAIsAvailableHelper } -function TC4DAIsAvailableHelper.ToString: string; +function TC4DAiAvailableHelper.ToString: string; begin case Self of - TC4DAIsAvailable.Gemini: + TC4DAiAvailable.Gemini: Result := 'Gemini'; - TC4DAIsAvailable.OpenAI: + TC4DAiAvailable.OpenAI: Result := 'ChatGPT'; else - Result := GetEnumName(TypeInfo(TC4DAIsAvailable), Integer(Self)); + Result := GetEnumName(TypeInfo(TC4DAiAvailable), Integer(Self)); end; end; -function TC4DAIsAvailableHelper.ToStringWithCreator: string; +function TC4DAiAvailableHelper.ToStringWithCreator: string; begin case Self of - TC4DAIsAvailable.Gemini: + TC4DAiAvailable.Gemini: Result := 'Gemini (Google)'; - TC4DAIsAvailable.OpenAI: + TC4DAiAvailable.OpenAI: Result := 'ChatGPT (OpenAI)'; else - Result := GetEnumName(TypeInfo(TC4DAIsAvailable), Integer(Self)); + Result := GetEnumName(TypeInfo(TC4DAiAvailable), Integer(Self)); end; end; @@ -107,13 +121,82 @@ function TC4DLanguageHelper.GetLanguageDefinition: string; function TC4DLanguageHelper.GetMsgCodeOnly: string; begin - Result := 'Faa a seguinte ao sem adicionar comentrios:' + sLineBreak; + //Result := 'Faa a seguinte ao sem adicionar nenhum comentrios'; + Result := 'Responda a pergunta sem adicionar nenhum comentrios, retorne apenas o cdigo fonte'; + case Self of + TC4DLanguage.en: + Result := 'Perform the following action without adding comments:'; + TC4DLanguage.es: + Result := 'Realice la siguiente accin sin agregar comentarios:'; + end; + + Result := Result + sLineBreak; +end; + +function TC4DLanguageHelper.GetMsgSQLOnly: string; +begin + Result := 'Faa a seguinte ao sem adicionar comentrios e retorne apenas os Comandos SQLs:'; + case Self of + TC4DLanguage.en: + Result := 'Perform the following action without adding comments, returning only the SQL commands:'; + TC4DLanguage.es: + Result := 'Realice la siguiente accin sin agregar comentarios, devolviendo solo comandos SQL:'; + end; + + Result := Result + sLineBreak; +end; + +function TC4DLanguageHelper.GetMsgJSONIsDatabaseStructure(ASGBDName: string): string; +begin + Result := 'O seguinte JSON se refere a estrutura SQL de um banco de dados '; + case Self of + TC4DLanguage.en: + Result := 'The following JSON refers to the SQL structure of a database '; + TC4DLanguage.es: + Result := 'El siguiente JSON hace referencia a la estructura SQL de una base de datos '; + end; + + Result := Result + ASGBDName + ' ' + sLineBreak; +end; + +function TC4DLanguageHelper.GetMsgJSONInformedAnswerQuestion: string; +begin + Result := 'Com base nesta estrutura responda a seguinte pergunta e retorne o comando SQL correspondente:'; case Self of TC4DLanguage.en: - Result := 'Perform the following action without adding comments:' + sLineBreak; + Result := 'Based on the structure that was provided, answer the following question and return the corresponding SQL command:'; TC4DLanguage.es: - Result := 'Realice la siguiente accin sin agregar comentarios:' + sLineBreak; + Result := 'Segn la estructura proporcionada, responda la siguiente pregunta y devuelva el comando SQL correspondiente:'; end; + + Result := Result + sLineBreak; +end; + +function TC4DLanguageHelper.GetMsgCodeCompletionSuggestion: string; +begin + Result := 'Com base no seguinte cdigo delphi, de uma sugesto ' + + 'de cdigo para ser adicionado onde esta o comentrio %s ' + sLineBreak + + 'Importante: antes do implementation e dentro dos especificadores '+ + 'private, protected, public, published deve-se adicionar apenas declaraes '+ + 'e nunca se deve adicionar implementaes de cdigos delphi que contenham begin. ' + sLineBreak + + 'Nas sugestes, nunca repita um cdigo que j exista. '; + case Self of + TC4DLanguage.en: + Result := 'Based on the following Delphi code, give a suggestion of code to be added where the comment %s is ' + sLineBreak + + 'Important: before implementation and within the '+ + 'private, protected, public, published specifiers, you should only add declarations '+ + 'and never add Delphi code implementations that contain begin. '+ sLineBreak + + 'In suggestions, never repeat code that already exists. '; + TC4DLanguage.es: + Result := 'Basado en el siguiente cdigo Delphi, se agregar una sugerencia de cdigo donde est el comentario %s ' + sLineBreak + + 'Importante: antes de la implementacin y dentro de los especificadores '+ + 'privados, protegidos, pblicos y publicados, solo se deben agregar declaraciones '+ + 'y nunca se deben agregar implementaciones de cdigos Delphi que contienen begin. ' + sLineBreak + + 'En sugerencias, nunca repita cdigo que ya existe. '; + end; + + Result := Format(Result, [TConsts.TAG_CODE_COMPLETION]); + Result := Result + sLineBreak; end; { TC4DExtensionsFilesHelper } @@ -134,30 +217,32 @@ function TC4DExtensionsOfFilesHelper.ContainsStr(const AExtension: string): Bool begin Result := False; LExtension := AExtension.Trim.ToLower; - if(LExtension = '.pas')then + if LExtension = TC4DExtensionsFiles.PAS.ToStringWithPoint then Result := TC4DExtensionsFiles.PAS in Self - else if(LExtension = '.dfm')then + else if LExtension = TC4DExtensionsFiles.DFM.ToStringWithPoint then Result := TC4DExtensionsFiles.DFM in Self - else if(LExtension = '.fmx')then + else if LExtension = TC4DExtensionsFiles.FMX.ToStringWithPoint then Result := TC4DExtensionsFiles.FMX in Self - else if(LExtension = '.dpr')then + else if LExtension = TC4DExtensionsFiles.DPR.ToStringWithPoint then Result := TC4DExtensionsFiles.DPR in Self - else if(LExtension = '.dpk')then + else if LExtension = TC4DExtensionsFiles.DPK.ToStringWithPoint then Result := TC4DExtensionsFiles.DPK in Self - else if(LExtension = '.dproj')then + else if LExtension = TC4DExtensionsFiles.DPROJ.ToStringWithPoint then Result := TC4DExtensionsFiles.DPROJ in Self - else if(LExtension = '.zip')then + else if LExtension = TC4DExtensionsFiles.GROUPPROJ.ToStringWithPoint then + Result := TC4DExtensionsFiles.GROUPPROJ in Self + else if LExtension =TC4DExtensionsFiles.ZIP.ToStringWithPoint then Result := TC4DExtensionsFiles.ZIP in Self - else if(LExtension = '.bmp')then + else if LExtension = TC4DExtensionsFiles.BMP.ToStringWithPoint then Result := TC4DExtensionsFiles.BMP in Self - else if(LExtension = '.ini')then + else if LExtension = TC4DExtensionsFiles.INI.ToStringWithPoint then Result := TC4DExtensionsFiles.INI in Self; end; { TC4DExtensionsCommonHelper } function TC4DExtensionsCommonHelper.ToString: string; begin - Result := GetEnumName(TypeInfo(TC4DExtensionsFiles), Integer(Self)).ToLower; + Result := GetEnumName(TypeInfo(TC4DExtensionsCommon), Integer(Self)).ToLower; end; function TC4DExtensionsCommonHelper.ToStringWithPoint: string; @@ -168,14 +253,29 @@ function TC4DExtensionsCommonHelper.ToStringWithPoint: string; { TC4DQuestionKindHelper } function TC4DQuestionKindHelper.ToString: string; begin - if(Self = TC4DQuestionKind.ItemMenuNormal)then + if Self = TC4DQuestionKind.ItemMenuNormal then Exit('Item menu normal') - else if(Self = TC4DQuestionKind.MenuMasterOnly)then + else if Self = TC4DQuestionKind.MenuMasterOnly then Exit('Menu master only') - else if(Self = TC4DQuestionKind.Separators)then + else if Self = TC4DQuestionKind.Separators then Exit('Separator') else Result := GetEnumName(TypeInfo(TC4DQuestionKind), Integer(Self)); end; +{ TC4DDriverIDHelper } + +function TC4DDriverIDHelper.ToString: string; +begin + Result := GetEnumName(TypeInfo(TC4DDriverID), Integer(Self)); +end; + +function TC4DDriverIDHelper.ToStringID: string; +begin + if Self = TC4DDriverID.Firebird then + Exit('FB'); + + Result := GetEnumName(TypeInfo(TC4DDriverID), Integer(Self)); +end; + end. diff --git a/Src/Utils/DelphiAIDev.Utils.ABMenuAction.pas b/Src/Utils/DelphiAIDev.Utils.ABMenuAction.pas index 4e4a820..46f533a 100644 --- a/Src/Utils/DelphiAIDev.Utils.ABMenuAction.pas +++ b/Src/Utils/DelphiAIDev.Utils.ABMenuAction.pas @@ -28,23 +28,23 @@ implementation function GetMenuItemOfSender(Sender: TObject): TMenuItem; begin Result := nil; - if(Sender.ClassType.ClassName = TMenuItem.ClassName)then + if Sender.ClassType.ClassName = TMenuItem.ClassName then Result := TMenuItem(Sender) - else if(Sender.ClassType.ClassName = TABMenuAction.ClassName)then + else if Sender.ClassType.ClassName = TABMenuAction.ClassName then Result := TABMenuAction(Sender).MenuItem end; {TABMenuAction} destructor TABMenuAction.Destroy; begin - if(Assigned(FMenuItem))then + if Assigned(FMenuItem) then FMenuItem.RemoveFreeNotification(Self); inherited; end; procedure TABMenuAction.ExecuteTarget(Target: TObject); begin - if(Assigned(FMenuItem))then + if Assigned(FMenuItem) then FMenuItem.Click; end; @@ -55,7 +55,7 @@ function TABMenuAction.HandlesTarget(Target: TObject): Boolean; procedure TABMenuAction.Notification(AComponent: TComponent; Operation: TOperation); begin - if(Operation = opRemove) and (AComponent = FMenuItem)then + if (Operation = opRemove) and (AComponent = FMenuItem) then FMenuItem := nil; end; diff --git a/Src/Utils/DelphiAIDev.Utils.Crypt.pas b/Src/Utils/DelphiAIDev.Utils.Crypt.pas new file mode 100644 index 0000000..4efc1a4 --- /dev/null +++ b/Src/Utils/DelphiAIDev.Utils.Crypt.pas @@ -0,0 +1,43 @@ +unit DelphiAIDev.Utils.Crypt; + +interface + +uses + System.SysUtils; + +type + TUtilsCrypt = class + public + class function Decrypt(Value: string): string; + class function Encrypt(Value: string): string; + end; + +implementation + +const + SEQUENCE01 = '1234567890ABCDEFGHIJLMNOPQRSTUVXZWYKabcdefghijlmnopqrstuvxzwyk'; + SEQUENCE02 = '1MTPkSXCgvRb23eQ0JzarhAE64FD95BpsYKqom7wVtUuHxiZdWcflLIjGnO8yN'; + +class function TUtilsCrypt.Encrypt(Value: string): string; +var + i: Integer; +begin + for i := 1 to Length(Value)do + if Pos(Value[i], SEQUENCE01) <> 0 then + Value[i] := SEQUENCE02[Pos(Value[i], SEQUENCE01)]; + + Result := Value; +end; + +class function TUtilsCrypt.Decrypt(Value: string): string; +var + i: Integer; +begin + for i := 1 to Length(Value) do + if Pos(Value[i], SEQUENCE02) <> 0 then + Value[i] := SEQUENCE01[Pos(Value[i], SEQUENCE02)]; + + Result := Value; +end; + +end. diff --git a/Src/Utils/DelphiAIDev.Utils.DBGrids.pas b/Src/Utils/DelphiAIDev.Utils.DBGrids.pas new file mode 100644 index 0000000..d310562 --- /dev/null +++ b/Src/Utils/DelphiAIDev.Utils.DBGrids.pas @@ -0,0 +1,226 @@ +unit DelphiAIDev.Utils.DBGrids; + +interface + +uses + System.SysUtils, + System.Types, + System.Classes, + Data.DB, + Vcl.DBGrids, + Vcl.Grids, + Vcl.Graphics, + Vcl.Clipbrd, + DelphiAIDev.Utils, + DelphiAIDev.Utils.Ota, + DelphiAIDev.Types; + +type + TUtilsDBGrids = class + private + class procedure DrawColumnCellLight(const ADBGrid: TDBGrid; const ARect: TRect; const ADataCol: Integer; const AColumn: TColumn; + const AState: TGridDrawState); + class procedure DrawColumnCellDark(const ADBGrid: TDBGrid; const ARect: TRect; const ADataCol: Integer; const AColumn: TColumn; + const AState: TGridDrawState); + public + class procedure DBGridToClipboardCurrentColumn(const ADBGrid: TDBGrid); + class procedure DBGridToClipboardCurrentLine(ADBGrid: TDBGrid; ACopyTitle: Boolean = True); + class procedure DBGridToClipboardAll(ADBGrid: TDBGrid; ACopyTitle: Boolean = True); + class procedure DBGridToCSV(ADBGrid: TDBGrid; ACopyTitle: Boolean = True); + class procedure DBGridToTxt(ADBGrid: TDBGrid; ACopyTitle: Boolean = True); + class procedure DBGridToFile(ADBGrid: TDBGrid; const AExtension: string; + const ASeparator: string; ACopyTitle: Boolean = True); + class function DBGridToString(ADBGrid: TDBGrid; const AExtension: string; + ACopyTitle: Boolean = True): string; + + class procedure ConfDBGrid(const ADBGrid: TDBGrid); + class procedure DrawColumnCell(const ADBGrid: TDBGrid; const ARect: TRect; const ADataCol: Integer; const AColumn: TColumn; + const AState: TGridDrawState); + end; + +implementation + +class procedure TUtilsDBGrids.DBGridToClipboardCurrentColumn(const ADBGrid: TDBGrid); +var + LText: string; +begin + if ADBGrid.DataSource.DataSet.IsEmpty then + Exit; + + if ADBGrid.SelectedIndex < 0 then + Exit; + + LText := ADBGrid.Columns[ADBGrid.SelectedIndex].Field.AsString; + ClipBoard.Clear; + ClipBoard.SetTextBuf(PWideChar(LText)); +end; + +class procedure TUtilsDBGrids.DBGridToClipboardCurrentLine(ADBGrid: TDBGrid; ACopyTitle: Boolean = True); +var + LText: string; + LContCol: Integer; +begin + if ADBGrid.DataSource.DataSet.IsEmpty then + Exit; + + LText := ''; + if ACopyTitle then + begin + LText := ADBGrid.Columns[0].Title.Caption; + for LContCol := 1 to Pred(ADBGrid.Columns.Count) do + LText := format('%s|%s', [LText, ADBGrid.Columns[LContCol].Title.Caption]); + + LText := LText + sLineBreak; + end; + + LText := LText + ADBGrid.Columns[0].Field.AsString; + for LContCol := 1 to Pred(ADBGrid.Columns.Count) do + LText := format('%s|%s', [LText, ADBGrid.Columns[LContCol].Field.AsString]); + + LText := LText + sLineBreak; + ClipBoard.Clear; + ClipBoard.SetTextBuf(PWideChar(LText)); +end; + +class procedure TUtilsDBGrids.DBGridToClipboardAll(ADBGrid: TDBGrid; ACopyTitle: Boolean = True); +var + LText: string; +begin + LText := Self.DBGridToString(ADBGrid, ';', ACopyTitle); + ClipBoard.Clear; + ClipBoard.SetTextBuf(PWideChar(LText)); +end; + +class procedure TUtilsDBGrids.DBGridToCSV(ADBGrid: TDBGrid; ACopyTitle: Boolean = True); +begin + Self.DBGridToFile(ADBGrid, TC4DExtensionsCommon.csv.ToString, ';', ACopyTitle); +end; + +class procedure TUtilsDBGrids.DBGridToTxt(ADBGrid: TDBGrid; ACopyTitle: Boolean = True); +begin + Self.DBGridToFile(ADBGrid, TC4DExtensionsCommon.txt.ToString, '|', ACopyTitle); +end; + +class procedure TUtilsDBGrids.DBGridToFile(ADBGrid: TDBGrid; const AExtension: string; + const ASeparator: string; ACopyTitle: Boolean = True); +var + LStrings: TStringList; + LFileName: string; +begin + LFileName := TUtils.GetFileName(AExtension); + + LStrings := TStringList.Create; + try + LStrings.Text := Self.DBGridToString(ADBGrid, ASeparator, ACopyTitle); + LStrings.SaveToFile(LFileName); + finally + LStrings.Free; + end; +end; + +class function TUtilsDBGrids.DBGridToString(ADBGrid: TDBGrid; const AExtension: string; + ACopyTitle: Boolean = True): string; +var + LContCol: Integer; + LBookMarkCurrent: TBookMark; +begin + Result := ''; + if ACopyTitle then + begin + Result := ADBGrid.Columns[0].Title.Caption; + for LContCol := 1 to Pred(ADBGrid.Columns.Count) do + Result := format('%s%s%s', [Result, AExtension, ADBGrid.Columns[LContCol].Title.Caption]); + + Result := Result + sLineBreak; + end; + + LBookMarkCurrent := ADBGrid.DataSource.DataSet.GetBookmark; + ADBGrid.DataSource.DataSet.DisableControls; + try + ADBGrid.DataSource.DataSet.First; + while not ADBGrid.DataSource.DataSet.Eof do + begin + Result := Result + ADBGrid.Columns[0].Field.AsString; + for LContCol := 1 to Pred(ADBGrid.Columns.Count) do + Result := format('%s%s%s', [Result, AExtension, ADBGrid.Columns[LContCol].Field.AsString]); + Result := Result + sLineBreak; + ADBGrid.DataSource.DataSet.Next; + end; + + ADBGrid.DataSource.DataSet.GotoBookMark(LBookMarkCurrent); + ADBGrid.DataSource.DataSet.FreeBookMark(LBookMarkCurrent); + finally + ADBGrid.DataSource.DataSet.EnableControls; + end; +end; + +class procedure TUtilsDBGrids.DrawColumnCell(const ADBGrid: TDBGrid; const ARect: TRect; + const ADataCol: Integer; const AColumn: TColumn; const AState: TGridDrawState); +begin + if TUtilsOTA.ActiveThemeIsDark then + Self.DrawColumnCellDark(ADBGrid, ARect, ADataCol, AColumn, AState) + else + Self.DrawColumnCellLight(ADBGrid, ARect, ADataCol, AColumn, AState); +end; + +class procedure TUtilsDBGrids.DrawColumnCellLight(const ADBGrid: TDBGrid; const ARect: TRect; + const ADataCol: Integer; const AColumn: TColumn; const AState: TGridDrawState); +begin + ADBGrid.Canvas.Brush.Color := $00E6ECEC; //$00E6ECEC; + //EMULA dgRowSelect + if ARect.Top = TStringGrid(ADBGrid).CellRect(0, TStringGrid(ADBGrid).Row).Top then + begin + ADBGrid.Canvas.FillRect(ARect); + ADBGrid.Canvas.Brush.Color := $0056BBF9; + ADBGrid.Canvas.Font.Color := clWindowText; + ADBGrid.DefaultDrawDataCell(ARect, AColumn.Field, AState) + end + else if Odd(ADBGrid.DataSource.DataSet.RecNo) then + ADBGrid.Canvas.Brush.Color := clwhite; + + + ADBGrid.DefaultDrawColumnCell(ARect, ADataCol, AColumn, AState); +end; + +class procedure TUtilsDBGrids.DrawColumnCellDark(const ADBGrid: TDBGrid; const ARect: TRect; const ADataCol: Integer; const AColumn: TColumn; const AState: TGridDrawState); +begin + ADBGrid.Canvas.Brush.Color := $00322F2D; //$004A4136; //$00E6ECEC + ADBGrid.Canvas.Font.Color := clWindow; + //EMULA dgRowSelect + if ARect.Top = TStringGrid(ADBGrid).CellRect(0, TStringGrid(ADBGrid).Row).Top then + begin + ADBGrid.Canvas.FillRect(ARect); + ADBGrid.Canvas.Brush.Color := $00C08000; //$005B4224; + ADBGrid.Canvas.Font.Color := clWindowText; + ADBGrid.DefaultDrawDataCell(ARect, AColumn.Field, AState) + end + else if Odd(ADBGrid.DataSource.DataSet.RecNo) then + ADBGrid.Canvas.Brush.Color := $004A4132; //$00535353; + + //ADBGrid.Canvas.FillRect(ARect); + + ADBGrid.DefaultDrawColumnCell(ARect, ADataCol, AColumn, AState); +end; + +class procedure TUtilsDBGrids.ConfDBGrid(const ADBGrid: TDBGrid); +begin + ADBGrid.Options := ADBGrid.Options + [dgColumnResize]; + ADBGrid.Options := ADBGrid.Options + [dgTitleClick]; + ADBGrid.Options := ADBGrid.Options - [dgRowSelect]; + ADBGrid.TitleFont.Style := ADBGrid.TitleFont.Style + [fsBold]; + ADBGrid.TitleFont.Name := 'Arial'; + ADBGrid.TitleFont.Color := clWindowText; + ADBGrid.DrawingStyle := TGridDrawingStyle.gdsThemed; + + if TUtilsOTA.ActiveThemeIsDark then + begin + ADBGrid.TitleFont.Color := clWindow; + ADBGrid.DrawingStyle := TGridDrawingStyle.gdsGradient; + ADBGrid.GradientStartColor := $005C5143; + ADBGrid.GradientEndColor := $00342D25; //$00FFF1D5; + + ADBGrid.Color := $00322F2D; + end; +end; + +end. diff --git a/Src/Utils/DelphiAIDev.Utils.GetIniPositionStr.pas b/Src/Utils/DelphiAIDev.Utils.GetIniPositionStr.pas index 8b3bdc4..cc155cc 100644 --- a/Src/Utils/DelphiAIDev.Utils.GetIniPositionStr.pas +++ b/Src/Utils/DelphiAIDev.Utils.GetIniPositionStr.pas @@ -75,8 +75,8 @@ function TDelphiAIDevUtilsGetIniPositionStr.TextIgnore(Value: string): IDelphiAI //ESTA ROTINA CONFERE DE A LETRA ANTERIOR/POSTERIOR A PALAVRA FECHA COM VAZIO OU NO LETRA NEM NUMERO procedure TDelphiAIDevUtilsGetIniPositionStr.CheckWholeWord; begin - if((IfThen(FColCurrent = 0, '', Copy(FStrOriginal, FColCurrent - 1, 1)) <> '')and(Copy(FStrOriginal, FColCurrent - 1, 1)[1] in ['0'..'9', 'A'..'Z', 'a'..'z'])) - or((IfThen(FColCurrent = 0, '', Copy(FStrOriginal, FColCurrent + Length(FStrToLocate), 1)) <> '')and(Copy(FStrOriginal, FColCurrent + Length(FStrToLocate), 1)[1] in ['0'..'9', 'A'..'Z', 'a'..'z'])) + if ((IfThen(FColCurrent = 0, '', Copy(FStrOriginal, FColCurrent - 1, 1)) <> '') and (Copy(FStrOriginal, FColCurrent - 1, 1)[1] in ['0'..'9', 'A'..'Z', 'a'..'z'])) + or ((IfThen(FColCurrent = 0, '', Copy(FStrOriginal, FColCurrent + Length(FStrToLocate), 1)) <> '') and (Copy(FStrOriginal, FColCurrent + Length(FStrToLocate), 1)[1] in ['0'..'9', 'A'..'Z', 'a'..'z'])) then FFound := False; end; @@ -89,13 +89,13 @@ function TDelphiAIDevUtilsGetIniPositionStr.GetWholeWord: string; LChar: Char; begin Result := ''; - if(FStrToLocate = ' ')then + if FStrToLocate = ' ' then Exit; Result := FStrOriginal[FColCurrent]; LColIni := FColCurrent; - if(LColIni > 0)then + if LColIni > 0 then begin Dec(LColIni); LChar := FStrOriginal[LColIni]; @@ -109,7 +109,7 @@ function TDelphiAIDevUtilsGetIniPositionStr.GetWholeWord: string; LColEnd := FColCurrent; LLastColLine := FStrOriginal.Length; - if(LColEnd < LLastColLine)then + if LColEnd < LLastColLine then begin Inc(LColEnd); LChar := FStrOriginal[LColEnd]; @@ -127,21 +127,21 @@ procedure TDelphiAIDevUtilsGetIniPositionStr.CheckWordIgnore; LStrValidate: string; LStrIgnore: string; begin - if(FTextIgnore.Trim.IsEmpty)then + if FTextIgnore.Trim.IsEmpty then Exit; LStrValidate := Self.GetWholeWord; - if(LStrValidate.Trim.IsEmpty)then + if LStrValidate.Trim.IsEmpty then Exit; LStrIgnore := FTextIgnore; - if(not FCaseSensitive)then + if not FCaseSensitive then begin LStrValidate := LStrValidate.ToLower; LStrIgnore := LStrIgnore.ToLower; end; - if(LStrValidate.Contains(LStrIgnore))then + if LStrValidate.Contains(LStrIgnore) then FFound := False; end; @@ -157,21 +157,21 @@ function TDelphiAIDevUtilsGetIniPositionStr.GetInitialPosition(const AStrOrigina begin FColCurrent := LColCurrent; - if(FCaseSensitive)then + if FCaseSensitive then FFound := FStrToLocate = Copy(FStrOriginal, FColCurrent, Length(FStrToLocate)) else FFound := AnsiUpperCase(FStrToLocate) = AnsiUpperCase(Copy(FStrOriginal, FColCurrent, Length(FStrToLocate))); - if(FFound)and(FWholeWordOnly)then + if (FFound) and (FWholeWordOnly) then Self.CheckWholeWord; - if(FFound)then + if FFound then Self.CheckWordIgnore; - if(FFound)then + if FFound then begin Result := FColCurrent - 1; - if(Result < 0)then + if Result < 0 then Result := 0; Break; end; diff --git a/Src/Utils/DelphiAIDev.Utils.ListView.pas b/Src/Utils/DelphiAIDev.Utils.ListView.pas index 375e1b0..5ec432b 100644 --- a/Src/Utils/DelphiAIDev.Utils.ListView.pas +++ b/Src/Utils/DelphiAIDev.Utils.ListView.pas @@ -73,18 +73,18 @@ function IsValidDate(Astring: string; var ADateTime: TDateTime): Boolean; function CompareDates(dt1, dt2: TDateTime): Integer; begin Result := -1; - if(dt1 > dt2)then + if dt1 > dt2 then Result := 1 - else if(dt1 = dt2)then + else if dt1 = dt2 then Result := 0; end; function CompareNumeric(AInt1, AInt2: Integer): Integer; begin Result := -1; - if(AInt1 > AInt2)then + if AInt1 > AInt2 then Result := 1 - else if(AInt1 = AInt2)then + else if AInt1 = AInt2 then Result := 0; end; {$ENDREGION} @@ -101,11 +101,11 @@ procedure TDelphiAIDevUtilsListView.FindAndSelectItems(const AStrFind: string; LStrSource: string; LStrFind: string; begin - if(FListView.Items.Count <= 0)then + if FListView.Items.Count <= 0 then Exit; LStrFind := AStrFind.ToLower; - if(LStrFind.Trim.IsEmpty)then + if LStrFind.Trim.IsEmpty then Exit; LUtilsGetIniPositionStr := TDelphiAIDevUtilsGetIniPositionStr.New @@ -113,20 +113,20 @@ procedure TDelphiAIDevUtilsListView.FindAndSelectItems(const AStrFind: string; .CaseSensitive(False); LIndexCurrent := 0; - if(FListView.Selected <> nil)then + if FListView.Selected <> nil then LIndexCurrent := FListView.Selected.Index + 1; for i := LIndexCurrent to Pred(FListView.Items.Count) do begin LListItem := FListView.Items[i]; - if(ANumColumn = 0)then + if ANumColumn = 0 then LStrSource := LowerCase(LListItem.Caption) - else if(ANumColumn > 0)then + else if ANumColumn > 0 then LStrSource := LowerCase(LListItem.SubItems[Pred(ANumColumn)]); LColIni := 0; LColIni := LUtilsGetIniPositionStr.GetInitialPosition(LStrSource, LStrFind, LColIni); - if(LColIni > -1)then + if LColIni > -1 then begin FListView.Selected := LListItem; LListItem.MakeVisible(False); @@ -134,9 +134,9 @@ procedure TDelphiAIDevUtilsListView.FindAndSelectItems(const AStrFind: string; end; end; - if(AAutoRestart)then + if AAutoRestart then begin - if(FListView.Items.Count > 0)then + if FListView.Items.Count > 0 then FListView.Items.Item[0].Selected := True; Self.FindAndSelectItems(AStrFind, ANumColumn, False); end; @@ -147,19 +147,19 @@ procedure TDelphiAIDevUtilsListView.CopyIndexListView(const AIndexSubItem: Integ LListItemSel: TListItem; LStrCopy: string; begin - if(AIndexSubItem < -1)then + if AIndexSubItem < -1 then Exit; - if(FListView.Selected = nil)then + if FListView.Selected = nil then Exit; LListItemSel := FListView.Items[FListView.Selected.Index]; - if(AIndexSubItem = -1)then + if AIndexSubItem = -1 then LStrCopy := LListItemSel.Caption else LStrCopy := LListItemSel.SubItems[AIndexSubItem]; - if(not LStrCopy.Trim.IsEmpty)then + if not LStrCopy.Trim.IsEmpty then Clipboard.AsText := LStrCopy; end; @@ -171,41 +171,39 @@ function ListViewCustomSortProc(Item1, Item2: TListItem; SortColumn: Integer): I LDhItem1, LDhItem2: TDateTime; begin Result := 0; - if(Item1 = nil)or(Item2 = nil)then + if (Item1 = nil) or (Item2 = nil) then Exit; - case(SortColumn)of + case SortColumn of -1: //COMPARE CAPTIONS begin LStrItem1 := Item1.Caption; LStrItem2 := Item2.Caption; end; - else //COMPARE SUBITEMS + else //COMPARE SUBITEMS LStrItem1 := ''; LStrItem2 := ''; //CHECK RANGE - if(SortColumn < Item1.SubItems.Count)then + if SortColumn < Item1.SubItems.Count then LStrItem1 := Item1.SubItems[SortColumn]; - if(SortColumn < Item2.SubItems.Count)then + if SortColumn < Item2.SubItems.Count then LStrItem2 := Item2.SubItems[SortColumn] end; //SORT STYLES - case(TDelphiAIDevUtilsListView.FSortStyle)of + case TDelphiAIDevUtilsListView.FSortStyle of TDelphiAIDevUtilsListViewSortStyle.AlphaNum: - begin Result := lstrcmp(PChar(LStrItem1), PChar(LStrItem2)); - end; TDelphiAIDevUtilsListViewSortStyle.Numeric: begin LValidItem1 := IsValidNumber(LStrItem1, LIntItem1); LValidItem2 := IsValidNumber(LStrItem2, LIntItem2); Result := ord(LValidItem1 or LValidItem2); - if(Result <> 0)then + if Result <> 0 then begin - if(LIntItem1 = 0)then + if LIntItem1 = 0 then LIntItem1 := 99999999; - if(LIntItem2 = 0)then + if LIntItem2 = 0 then LIntItem2 := 99999999; Result := CompareNumeric(LIntItem2, LIntItem1); end; @@ -215,13 +213,13 @@ function ListViewCustomSortProc(Item1, Item2: TListItem; SortColumn: Integer): I LValidItem1 := IsValidDate(LStrItem1, LDhItem1); LValidItem2 := IsValidDate(LStrItem2, LDhItem2); Result := ord(LValidItem1 or LValidItem2); - if(Result <> 0)then + if Result <> 0 then Result := CompareDates(LDhItem1, LDhItem2); end; end; //SORT DIRECTION - if(not TDelphiAIDevUtilsListView.FSortOrder[SortColumn + 1])then + if not TDelphiAIDevUtilsListView.FSortOrder[SortColumn + 1] then Result := - Result; end; @@ -262,7 +260,7 @@ function TDelphiAIDevUtilsListView.ColumnIndex(const AColumnIndex: Integer): IDe procedure TDelphiAIDevUtilsListView.CustomSort; begin - if(FInvertOrder)then + if FInvertOrder then FSortOrder[FColumnIndex] := not FSortOrder[FColumnIndex]; FListView.CustomSort(@ListViewCustomSortProc, FColumnIndex -1); end; diff --git a/Src/Utils/DelphiAIDev.Utils.OTA.pas b/Src/Utils/DelphiAIDev.Utils.OTA.pas index 11f4cce..68ebe78 100644 --- a/Src/Utils/DelphiAIDev.Utils.OTA.pas +++ b/Src/Utils/DelphiAIDev.Utils.OTA.pas @@ -18,6 +18,8 @@ interface TUtilsOTA = class private public + class procedure GetCursorPosition(var ALine, AColumn: Integer); + class procedure GetCursorPositionPixel(var AX, AY: Integer); class function CurrentProjectIsDelphiAIDeveloperDPROJ: Boolean; class function CurrentModuleIsReadOnly: Boolean; class procedure SaveAllModifiedModules; @@ -40,7 +42,7 @@ TUtilsOTA = class class function ActiveThemeForCode: TColor; class function ActiveThemeIsDark: Boolean; class function GetIOTAFormEditor(const AIOTAModule: IOTAModule): IOTAFormEditor; - {$IF CompilerVersion >= 32.0} //Tokyo + {$IF CompilerVersion > 32.0} //Tokyo class function GetIOTAIDEThemingServices: IOTAIDEThemingServices; class function GetIOTAIDEThemingServices250: IOTAIDEThemingServices250; {$ENDIF} @@ -88,6 +90,95 @@ implementation uses DelphiAIDev.Utils; +class procedure TUtilsOTA.GetCursorPosition(var ALine, AColumn: Integer); +var + LOTAEditorServices: IOTAEditorServices; + LIOTAEditView: IOTAEditView; + LOTAEditPos: TOTAEditPos; + //LIOTAEditPosition: IOTAEditPosition; +begin + ALine := 0; + AColumn := 0; + + LOTAEditorServices := Self.GetIOTAEditorServices; + if not Assigned(LOTAEditorServices) then + Exit; + + LIOTAEditView := LOTAEditorServices.TopView; + if Assigned(LIOTAEditView) then + begin + LOTAEditPos := LIOTAEditView.CursorPos; + ALine := LOTAEditPos.Line; + AColumn := LOTAEditPos.Col; + + {LIOTAEditPosition := LIOTAEditView.Position; + if Assigned(LIOTAEditPosition) then + begin + ALine := LIOTAEditPosition.Row; + AColumn := LIOTAEditPosition.Column; + //TUtils.AddLog(Format('%d : %d', [ALine, AColumn])); + end;} + end; +end; + +class procedure TUtilsOTA.GetCursorPositionPixel(var AX, AY: Integer); +var + LIOTAEditorServices: IOTAEditorServices; + LIOTAEditView: IOTAEditView; + LIOTAEditPosition: IOTAEditPosition; + LLineHeight: Integer; + LCharWidth: Integer; + LEditorWindow: TCustomForm; + LHDC: HDC; + LCanvas: TCanvas; + LTextMetric: TTextMetric; +begin + // Obtm a instncia do Editor Services + LIOTAEditorServices := BorlandIDEServices as IOTAEditorServices; + + if Assigned(LIOTAEditorServices) then + begin + // Obtm a visualizao de edio ativa + LIOTAEditView := LIOTAEditorServices.TopView; + if Assigned(LIOTAEditView) then + begin + // Obtm a posio do cursor + LIOTAEditPosition := LIOTAEditView.Position; + if Assigned(LIOTAEditPosition) then + begin + // Obtm a janela do editor + LEditorWindow := LIOTAEditView.GetEditWindow.Form; + + // Cria um DC (Device Context) para a janela do editor + LHDC := GetDC(LEditorWindow.Handle); + try + LCanvas := TCanvas.Create; + try + LCanvas.Handle := LHDC; + + // Obtm as mtricas do texto (altura da linha e largura do caractere) + GetTextMetrics(LCanvas.Handle, LTextMetric); + LLineHeight := LTextMetric.tmHeight; + LCharWidth := LTextMetric.tmAveCharWidth; + finally + LCanvas.Free; + end; + finally + ReleaseDC(LEditorWindow.Handle, LHDC); + end; + + // Calcula a posio em pixels + AX := (LIOTAEditPosition.Column - 1) * LCharWidth; + AY := (LIOTAEditPosition.Row - 1) * LLineHeight; + + // Agora voc tem a posio do cursor em pixels + //ShowMessage(Format('Cursor est em X: %d, Y: %d pixels', [PixelX, PixelY])); + TUtils.AddLog(Format('Cursor est em X: %d, Y: %d pixels', [AX, AY])); + end; + end; + end; +end; + class function TUtilsOTA.CurrentProjectIsDelphiAIDeveloperDPROJ: Boolean; var LIOTAProject: IOTAProject; @@ -95,7 +186,7 @@ class function TUtilsOTA.CurrentProjectIsDelphiAIDeveloperDPROJ: Boolean; Result := False; LIOTAProject := Self.GetCurrentProject; - if(LIOTAProject = nil)then + if LIOTAProject = nil then Exit; Result := TUtils.FileNameIsDelphiAIDeveloperDPROJ(LIOTAProject.FileName); @@ -108,7 +199,7 @@ class function TUtilsOTA.CurrentModuleIsReadOnly: Boolean; Result := False; LIOTAEditBuffer := Self.GetIOTAEditBufferCurrentModule; - if(LIOTAEditBuffer = nil)then + if LIOTAEditBuffer = nil then Exit; Result := LIOTAEditBuffer.IsReadOnly; @@ -140,7 +231,7 @@ class function TUtilsOTA.AddImgIDEResourceName(AResourceName: string): Integer; LMaskColor: TColor; begin Result := -1; - if(FindResource(hInstance, PChar(AResourceName), RT_BITMAP) <= 0)then + if FindResource(hInstance, PChar(AResourceName), RT_BITMAP) <= 0 then Exit; LBitmap := TBitmap.Create; @@ -218,10 +309,7 @@ class function TUtilsOTA.EditorAsString(AIOTAModule: IOTAModule): string; LOTASourceEditor := Self.GetIOTASourceEditor(AIOTAModule); if LOTASourceEditor = nil then - begin - //TUtils.ShowMsgSynchronize('Unable to get SourceEditor.'); Exit; - end; LIOTAEditReader := LOTASourceEditor.CreateReader; try @@ -248,7 +336,7 @@ class procedure TUtilsOTA.DeleteBlockTextSelectedInEditor; begin LIOTAEditorServices := Self.GetIOTAEditorServices; LIOTAEditView := LIOTAEditorServices.TopView; - if(LIOTAEditView = nil)then + if LIOTAEditView = nil then TUtils.ShowMsgAndAbort('No projects or files selected'); LIOTAEditBlock := LIOTAEditView.Block; @@ -268,9 +356,9 @@ class procedure TUtilsOTA.InsertBlockTextIntoEditor(const AText: string); var LOTAEditorServices: IOTAEditorServices; LIOTAEditView: IOTAEditView; + LOTAEditPos: TOTAEditPos; LPosition: Longint; LOTACharPos: TOTACharPos; - LOTAEditPos: TOTAEditPos; LIOTAEditWriter: IOTAEditWriter; begin LOTAEditorServices := Self.GetIOTAEditorServices; @@ -295,7 +383,7 @@ class function TUtilsOTA.GetBlockTextSelect: string; begin Result := ''; LIOTAEditorServices := Self.GetIOTAEditorServices; - if(LIOTAEditorServices.TopView <> nil)then + if LIOTAEditorServices.TopView <> nil then Result := LIOTAEditorServices.TopView.GetBlock.Text; end; @@ -308,10 +396,10 @@ class function TUtilsOTA.GetSelectedBlockOrAllCodeUnit: string; class procedure TUtilsOTA.OpenFilePathInIDE(AFilePath: string); begin - if(not FileExists(AFilePath))then + if not FileExists(AFilePath) then Exit; - if(TUtils.IsProject(AFilePath))then + if TUtils.IsProject(AFilePath) then Self.GetIOTAActionServices.OpenProject(AFilePath, True) else Self.GetIOTAActionServices.OpenFile(AFilePath); @@ -328,7 +416,7 @@ class function TUtilsOTA.RefreshProject: Boolean; begin Result := True; LIOTAProject := GetCurrentProject; - if(LIOTAProject = nil)then + if LIOTAProject = nil then Exit(False); LIOTAProject.Refresh(False); end; @@ -339,14 +427,14 @@ class function TUtilsOTA.RefreshModule: Boolean; begin Result := True; LIOTAModule := GetCurrentModule; - if(LIOTAModule = nil)then + if LIOTAModule = nil then Exit(False); LIOTAModule.Refresh(False); end; class procedure TUtilsOTA.RefreshProjectOrModule; begin - if(not Self.RefreshProject)then + if not Self.RefreshProject then Self.RefreshModule; end; @@ -358,7 +446,7 @@ class function TUtilsOTA.FileIsOpenInIDE(const APathFile: string): Boolean; i: Integer; begin Result := False; - if(APathFile.Trim.IsEmpty)then + if APathFile.Trim.IsEmpty then Exit; LIOTAModuleServices := Self.GetIOTAModuleServices; @@ -374,7 +462,7 @@ class function TUtilsOTA.FileIsOpenInIDE(const APathFile: string): Boolean; Continue; Result := SameFileName(APathFile, LIOTAModule.FileName); - if(Result)then + if Result then Exit; end; end; @@ -384,27 +472,27 @@ class function TUtilsOTA.CheckIfExistFileInCurrentsProjectGroups(const ANameFile LIOTAModuleServices: IOTAModuleServices; LIOTAModuleCurrent: IOTAModule; LOTAProjectGroup: IOTAProjectGroup; - LIOTAProjectCurrent: IOTAProject; LContModule: Integer; + LIOTAProjectCurrent: IOTAProject; LContProject: Integer; LContFile: Integer; LFilePath: string; begin Result := False; LIOTAModuleServices := Self.GetIOTAModuleServices; - if(LIOTAModuleServices = nil)then + if LIOTAModuleServices = nil then Exit; - if(LIOTAModuleServices.ModuleCount = 0)then + if LIOTAModuleServices.ModuleCount = 0 then Exit; for LContModule := 0 to Pred(LIOTAModuleServices.ModuleCount) do begin LIOTAModuleCurrent := LIOTAModuleServices.Modules[LContModule]; - if(ExtractFileName(LIOTAModuleCurrent.FileName) = ANameFileWithExtension)then + if ExtractFileName(LIOTAModuleCurrent.FileName) = ANameFileWithExtension then Exit(True); - if(Supports(LIOTAModuleCurrent, IOTAProjectGroup, LOTAProjectGroup))then + if Supports(LIOTAModuleCurrent, IOTAProjectGroup, LOTAProjectGroup) then begin for LContProject := 0 to Pred(LOTAProjectGroup.ProjectCount) do begin @@ -412,10 +500,10 @@ class function TUtilsOTA.CheckIfExistFileInCurrentsProjectGroups(const ANameFile for LContFile := 0 to Pred(LIOTAProjectCurrent.GetModuleCount) do begin LFilePath := LIOTAProjectCurrent.GetModule(LContFile).FileName; - if(LFilePath.Trim.IsEmpty)then + if LFilePath.Trim.IsEmpty then Continue; - if(ExtractFileName(LFilePath) = ANameFileWithExtension)then + if ExtractFileName(LFilePath) = ANameFileWithExtension then Exit(True); end; end; @@ -424,7 +512,7 @@ class function TUtilsOTA.CheckIfExistFileInCurrentsProjectGroups(const ANameFile end; class procedure TUtilsOTA.IDEThemingAll(AFormClass: TCustomFormClass; AForm: TForm); -{$IF CompilerVersion >= 32.0} //Tokyo +{$IF CompilerVersion > 32.0} //Tokyo var i: Integer; LIOTAIDEThemingServices250: IOTAIDEThemingServices250; @@ -433,14 +521,14 @@ class procedure TUtilsOTA.IDEThemingAll(AFormClass: TCustomFormClass; AForm: TFo AForm.Constraints.MinHeight := AForm.Height; AForm.Constraints.MinWidth := AForm.Width; - {$IF CompilerVersion >= 32.0} + {$IF CompilerVersion > 32.0} LIOTAIDEThemingServices250 := Self.GetIOTAIDEThemingServices250; LIOTAIDEThemingServices250.RegisterFormClass(AFormClass); LIOTAIDEThemingServices250.ApplyTheme(AForm); for i := 0 to Pred(AForm.ComponentCount) do begin - if(AForm.Components[i] is TPanel)then + if AForm.Components[i] is TPanel then TPanel(AForm.Components[i]).ParentBackground := True; LIOTAIDEThemingServices250.ApplyTheme(AForm.Components[i]); @@ -467,7 +555,7 @@ class function TUtilsOTA.ActiveThemeIsDark: Boolean; const THEME_DARK = 'dark'; begin - {$IF CompilerVersion >= 32.0} //Tokyo + {$IF CompilerVersion > 32.0} //Tokyo Result := Self.GetIOTAIDEThemingServices.ActiveTheme.ToLower.Equals(THEME_DARK); {$ELSE} Result := False; @@ -481,14 +569,14 @@ class function TUtilsOTA.GetIOTAFormEditor(const AIOTAModule: IOTAModule): IOTAF LIOTAFormEditor: IOTAFormEditor; begin Result := nil; - if(not Assigned(AIOTAModule))then + if not Assigned(AIOTAModule) then Exit; for i := 0 to Pred(AIOTAModule.GetModuleFileCount) do begin LIOTAEditor := AIOTAModule.GetModuleFileEditor(i); - if(Supports(LIOTAEditor, IOTAFormEditor, LIOTAFormEditor))then + if Supports(LIOTAEditor, IOTAFormEditor, LIOTAFormEditor) then begin Result := LIOTAFormEditor; Break; @@ -496,29 +584,29 @@ class function TUtilsOTA.GetIOTAFormEditor(const AIOTAModule: IOTAModule): IOTAF end; end; -{$IF CompilerVersion >= 32.0} //Tokyo +{$IF CompilerVersion > 32.0} //Tokyo class function TUtilsOTA.GetIOTAIDEThemingServices: IOTAIDEThemingServices; begin - if(not Supports(BorlandIDEServices, IOTAIDEThemingServices, Result))then + if not Supports(BorlandIDEServices, IOTAIDEThemingServices, Result) then raise Exception.Create('Interface not supported: IOTAIDEThemingServices'); end; class function TUtilsOTA.GetIOTAIDEThemingServices250: IOTAIDEThemingServices250; begin - if(not Supports(BorlandIDEServices, IOTAIDEThemingServices250, Result))then + if not Supports(BorlandIDEServices, IOTAIDEThemingServices250, Result) then raise Exception.Create('Interface not supported: IOTAIDEThemingServices250'); end; {$ENDIF} class function TUtilsOTA.GetIOTACompileServices: IOTACompileServices; begin - if(not Supports(BorlandIDEServices, IOTACompileServices, Result))then + if not Supports(BorlandIDEServices, IOTACompileServices, Result) then raise Exception.Create('Interface not supported: IOTACompileServices'); end; class function TUtilsOTA.GetIOTAWizardServices: IOTAWizardServices; begin - if(not Supports(BorlandIDEServices, IOTAWizardServices, Result))then + if not Supports(BorlandIDEServices, IOTAWizardServices, Result) then raise Exception.Create('Interface not supported: IOTAWizardServices'); end; @@ -528,11 +616,11 @@ class function TUtilsOTA.GetIOTAEditView(AIOTAModule: IOTAModule): IOTAEditView; LIOTAEditView: IOTAEditView; begin LIOTASourceEditor := Self.GetIOTASourceEditor(AIOTAModule); - if(LIOTASourceEditor = nil)then + if LIOTASourceEditor = nil then Exit; LIOTAEditView := Self.GetIOTAEditView(LIOTASourceEditor); - if(LIOTAEditView = nil)then + if LIOTAEditView = nil then Exit; //LIOTASourceEditor.Show; Result := LIOTAEditView; @@ -544,10 +632,10 @@ class function TUtilsOTA.GetIOTAEditView(AIOTASourceEditor: IOTASourceEditor): I begin Result := nil; - if(not Supports(AIOTASourceEditor, IOTAEditBuffer, LIOTAEditBuffer))then + if not Supports(AIOTASourceEditor, IOTAEditBuffer, LIOTAEditBuffer) then raise Exception.Create('Interface not supported: IOTAEditBuffer'); - if(LIOTAEditBuffer <> nil)then + if LIOTAEditBuffer <> nil then Result := LIOTAEditBuffer.TopView else if AIOTASourceEditor.EditViewCount > 0 then Result := AIOTASourceEditor.EditViews[0]; @@ -562,7 +650,7 @@ class function TUtilsOTA.GetIOTASourceEditor(AIOTAModule: IOTAModule): IOTASourc LIOTAModule := AIOTAModule; for i := 0 to Pred(LIOTAModule.ModuleFileCount) do begin - if(LIOTAModule.ModuleFileEditors[i].QueryInterface(IOTASourceEditor, Result) = S_OK)then + if LIOTAModule.ModuleFileEditors[i].QueryInterface(IOTASourceEditor, Result) = S_OK then Break; end; end; @@ -570,7 +658,7 @@ class function TUtilsOTA.GetIOTASourceEditor(AIOTAModule: IOTAModule): IOTASourc class function TUtilsOTA.GetIOTASourceEditor(AIOTAEditor: IOTAEditor): IOTASourceEditor; begin Result := nil; - if(not Supports(AIOTAEditor, IOTASourceEditor, Result))then + if not Supports(AIOTAEditor, IOTASourceEditor, Result) then raise Exception.Create('Interface not supported: IOTASourceEditor'); end; @@ -595,9 +683,8 @@ class function TUtilsOTA.GetIOTASourceEditor(AIOTAModule: IOTAModule; const AFil Result := nil; end; end; - begin - if(not Assigned(AIOTAModule))then + if not Assigned(AIOTAModule) then begin Result := nil; Exit; @@ -606,11 +693,11 @@ class function TUtilsOTA.GetIOTASourceEditor(AIOTAModule: IOTAModule; const AFil for i := 0 to Pred(AIOTAModule.GetModuleFileCount) do begin LIOTAEditor := GetFileEditorForModule(AIOTAModule, i); - if(Supports(LIOTAEditor, IOTASourceEditor, LIOTASourceEditor))then + if Supports(LIOTAEditor, IOTASourceEditor, LIOTASourceEditor) then begin - if(Assigned(LIOTASourceEditor))then + if Assigned(LIOTASourceEditor) then begin - if(AFileName = '')or(SameFileName(LIOTASourceEditor.FileName, AFileName))then + if (AFileName = '') or (SameFileName(LIOTASourceEditor.FileName, AFileName)) then begin Result := LIOTASourceEditor; Exit; @@ -628,7 +715,7 @@ class function TUtilsOTA.GetIOTAEditBufferCurrentModule: IOTAEditBuffer; Result := nil; LIOTAModule := Self.GetCurrentModule; - if(LIOTAModule = nil)then + if LIOTAModule = nil then Exit; Result := TUtilsOTA.GetIOTAEditBuffer(LIOTAModule); @@ -640,58 +727,58 @@ class function TUtilsOTA.GetIOTAEditBuffer(AIOTAModule: IOTAModule): IOTAEditBuf begin Result := nil; LIOTASourceEditor := Self.GetIOTASourceEditor(AIOTAModule); - if(LIOTASourceEditor = nil)then + if LIOTASourceEditor = nil then Exit; - if(not Supports(LIOTASourceEditor, IOTAEditBuffer, Result))then + if not Supports(LIOTASourceEditor, IOTAEditBuffer, Result) then raise Exception.Create('Interface not supported: IOTAEditBuffer'); end; class function TUtilsOTA.GetIOTAMessageServices: IOTAMessageServices; begin - if(not Supports(BorlandIDEServices, IOTAMessageServices, Result))then + if not Supports(BorlandIDEServices, IOTAMessageServices, Result) then raise Exception.Create('Interface not supported: IOTAMessageServices'); end; class function TUtilsOTA.GetIOTAProjectManager: IOTAProjectManager; begin - if(not Supports(BorlandIDEServices, IOTAProjectManager, Result))then + if not Supports(BorlandIDEServices, IOTAProjectManager, Result) then raise Exception.Create('Interface not supported: IOTAProjectManager'); end; class function TUtilsOTA.GetIOTAKeyboardServices: IOTAKeyboardServices; begin - if(not Supports(BorlandIDEServices, IOTAKeyboardServices, Result))then + if not Supports(BorlandIDEServices, IOTAKeyboardServices, Result) then raise Exception.Create('Interface not supported: IOTAKeyboardServices'); end; class function TUtilsOTA.GetIOTAServices: IOTAServices; begin - if(not Supports(BorlandIDEServices, IOTAServices, Result))then + if not Supports(BorlandIDEServices, IOTAServices, Result) then raise Exception.Create('Interface not supported: IOTAServices'); end; class function TUtilsOTA.GetIOTAActionServices: IOTAActionServices; begin - if(not Supports(BorlandIDEServices, IOTAActionServices, Result))then + if not Supports(BorlandIDEServices, IOTAActionServices, Result) then raise Exception.Create('Interface not supported: IOTAActionServices'); end; class function TUtilsOTA.GetINTAServices: INTAServices; begin - if(not Supports(BorlandIDEServices, INTAServices, Result))then + if not Supports(BorlandIDEServices, INTAServices, Result) then raise Exception.Create('Interface not supported: INTAServices'); end; class function TUtilsOTA.GetIOTAModuleServices: IOTAModuleServices; begin - if(not Supports(BorlandIDEServices, IOTAModuleServices, Result))then + if not Supports(BorlandIDEServices, IOTAModuleServices, Result) then raise Exception.Create('Interface not supported: IOTAModuleServices'); end; class function TUtilsOTA.GetIOTAEditorServices: IOTAEditorServices; begin - if(not Supports(BorlandIDEServices, IOTAEditorServices, Result))then + if not Supports(BorlandIDEServices, IOTAEditorServices, Result) then raise Exception.Create('Interface not supported: IOTAEditorServices'); end; @@ -701,7 +788,7 @@ class function TUtilsOTA.GetCurrentModule: IOTAModule; begin Result := nil; LIOTAModuleServices := Self.GetIOTAModuleServices; - if(LIOTAModuleServices <> nil)then + if LIOTAModuleServices <> nil then Result := LIOTAModuleServices.CurrentModule; end; @@ -711,7 +798,7 @@ class function TUtilsOTA.GetCurrentModuleFileName: string; begin Result := ''; LIOTAModule := Self.GetCurrentModule; - if(Assigned(LIOTAModule))then + if Assigned(LIOTAModule) then Result := LIOTAModule.FileName.Trim; end; @@ -721,7 +808,7 @@ class function TUtilsOTA.GetModule(const AFileName: string): IOTAModule; begin Result := nil; LIOTAModuleServices := Self.GetIOTAModuleServices; - if(LIOTAModuleServices <> nil)then + if LIOTAModuleServices <> nil then Result := LIOTAModuleServices.FindModule(AFileName); end; @@ -736,7 +823,7 @@ class function TUtilsOTA.GetCurrentProject: IOTAProject; begin Result := nil; LIOTAProjectGroup := Self.GetCurrentProjectGroup; - if(not Assigned(LIOTAProjectGroup))then + if not Assigned(LIOTAProjectGroup) then Exit; try @@ -752,7 +839,7 @@ class function TUtilsOTA.GetCurrentProjectFileName: string; begin Result := ''; LIOTAProject := Self.GetCurrentProject; - if(Assigned(LIOTAProject))then + if Assigned(LIOTAProject) then Result := LIOTAProject.FileName.Trim; end; @@ -765,7 +852,7 @@ class function TUtilsOTA.GetProjectName(const AIOTAProject: IOTAProject): string for i := 0 to Pred(AIOTAProject.ModuleFileCount) do begin LExt := LowerCase(ExtractFileExt(AIOTAProject.ModuleFileEditors[i].FileName)); - if(LExt = TC4DExtensionsFiles.DPR.ToString)or(LExt = TC4DExtensionsFiles.DPK.ToString) Then + if (LExt = TC4DExtensionsFiles.DPR.ToString) or (LExt = TC4DExtensionsFiles.DPK.ToString) Then begin Result := ChangeFileExt(Result, LExt); Break; @@ -788,7 +875,7 @@ class function TUtilsOTA.GetFileNameDprOrDpkIfDproj(const AIOTAModule: IOTAModul LFileName := AIOTAModule.ModuleFileEditors[i].FileName; LExt := ExtractFileExt(LFileName); - if(LExt = TC4DExtensionsFiles.DPR.ToStringWithPoint)or(LExt = TC4DExtensionsFiles.DPK.ToStringWithPoint)then + if (LExt = TC4DExtensionsFiles.DPR.ToStringWithPoint) or (LExt = TC4DExtensionsFiles.DPK.ToStringWithPoint) then Result := LFileName; end; end; @@ -800,7 +887,7 @@ class function TUtilsOTA.GetCurrentProjectOptions: IOTAProjectOptions; begin Result := nil; LIOTAProject := Self.GetCurrentProject; - if(LIOTAProject = nil)then + if LIOTAProject = nil then Exit; Result := LIOTAProject.ProjectOptions; @@ -811,7 +898,7 @@ class function TUtilsOTA.GetCurrentOutputDir: string; LIOTAProjectOptions: IOTAProjectOptions; begin LIOTAProjectOptions := Self.GetCurrentProjectOptions; - if(LIOTAProjectOptions = nil)then + if LIOTAProjectOptions = nil then Exit; Result := VarToStr(LIOTAProjectOptions.Values['OutputDir']); @@ -822,8 +909,8 @@ class function TUtilsOTA.GetCurrentProjectOptionsConfigurations: IOTAProjectOpti LIOTAProjectOptions: IOTAProjectOptions; begin LIOTAProjectOptions := Self.GetCurrentProjectOptions; - if(LIOTAProjectOptions <> nil)then - if(Supports(LIOTAProjectOptions, IOTAProjectOptionsConfigurations, Result))then + if LIOTAProjectOptions <> nil then + if Supports(LIOTAProjectOptions, IOTAProjectOptionsConfigurations, Result) then Exit; Result := nil; @@ -861,19 +948,19 @@ class procedure TUtilsOTA.GetAllFilesFromProjectGroup(AListFiles: TStrings; LFilterIsProject: Boolean; begin LIOTAModuleServices := Self.GetIOTAModuleServices; - if(LIOTAModuleServices = nil)then + if LIOTAModuleServices = nil then Exit; - if(LIOTAModuleServices.ModuleCount = 0)then + if LIOTAModuleServices.ModuleCount = 0 then Exit; LFilterIsProjectGroup := False; LFilterIsProject := False; - if(not AFilePathProjectOrGroupForFilter.Trim.IsEmpty)then + if not AFilePathProjectOrGroupForFilter.Trim.IsEmpty then begin - if(TUtils.IsProjectGroup(AFilePathProjectOrGroupForFilter))then + if TUtils.IsProjectGroup(AFilePathProjectOrGroupForFilter) then LFilterIsProjectGroup := True - else if(TUtils.IsProject(AFilePathProjectOrGroupForFilter)) + else if TUtils.IsProject(AFilePathProjectOrGroupForFilter) or(TUtils.IsDPROJ(AFilePathProjectOrGroupForFilter)) then LFilterIsProject := True; @@ -883,25 +970,25 @@ class procedure TUtilsOTA.GetAllFilesFromProjectGroup(AListFiles: TStrings; begin LIOTAModuleCurrent := LIOTAModuleServices.Modules[LContModule]; LFilePath := LIOTAModuleCurrent.FileName; - if(Supports(LIOTAModuleCurrent, IOTAProjectGroup, LOTAProjectGroup))then + if Supports(LIOTAModuleCurrent, IOTAProjectGroup, LOTAProjectGroup) then begin - if(LFilterIsProjectGroup)and(LFilePath <> AFilePathProjectOrGroupForFilter)then + if (LFilterIsProjectGroup) and (LFilePath <> AFilePathProjectOrGroupForFilter) then Continue; for LContProject := 0 to Pred(LOTAProjectGroup.ProjectCount) do begin LIOTAProjectCurrent := LOTAProjectGroup.Projects[LContProject]; - if(LFilterIsProject)and(LIOTAProjectCurrent.FileName <> AFilePathProjectOrGroupForFilter)then + if (LFilterIsProject) and (LIOTAProjectCurrent.FileName <> AFilePathProjectOrGroupForFilter) then Continue; for LContFile := 0 to Pred(LIOTAProjectCurrent.GetModuleCount) do begin LFilePath := LIOTAProjectCurrent.GetModule(LContFile).FileName; - if(LFilePath.Trim.IsEmpty)then + if LFilePath.Trim.IsEmpty then Continue; - if(not AC4DExtensions.ContainsStr(ExtractFileExt(LFilePath)))then + if not AC4DExtensions.ContainsStr(ExtractFileExt(LFilePath)) then Continue; AListFiles.Add(LFilePath); diff --git a/Src/Utils/DelphiAIDev.Utils.pas b/Src/Utils/DelphiAIDev.Utils.pas index cb19999..68067a3 100644 --- a/Src/Utils/DelphiAIDev.Utils.pas +++ b/Src/Utils/DelphiAIDev.Utils.pas @@ -11,6 +11,8 @@ interface System.StrUtils, System.Classes, System.TypInfo, + System.JSON, + System.Generics.Collections, Vcl.Controls, Vcl.Forms, Vcl.Graphics, @@ -29,9 +31,14 @@ TUtils = class public class function GetExceptionMessage(const E: Exception): string; class function StrToDefaultsQuestionsKind(Value: string): TC4DQuestionKind; + class function StrToDriverID(Value: string): TC4DDriverID; + class procedure DriverIDFillItemsTStrings(AStrings: TStrings); class procedure DefaultsQuestionsKindFillItemsTStrings(AStrings: TStrings); class function AdjustQuestionToJson(const AValue: string): string; + class function DebugMyIsOn: Boolean; class procedure AddLog(const AMessage: string); + class procedure AddLogDeleteFileFirst(const AMessage: string; const ANameFile: string); + class procedure AddLogInFileTxt(const AMessage: string; const ANameFile: string; ADeleteFileFirst: Boolean = False); class function GetFileName(const AExtension: string): string; class procedure MemoFocusOnTheEnd(const AMemo: TMemo); class function IfThenColor(const Conditional: Boolean; const AColorTrue, AColorFalse: TColor): TColor; @@ -78,8 +85,12 @@ TUtils = class class function GetPathFolderRoot: string; //class function GetPathFileIniGeneralSettings: string; class function GetPathFileChat: string; + class function GetPathFileChatDB: string; class function GetPathFileJSONDefaultsQuestions: string; + class function GetPathFileJSONDatabases: string; + class function GetPathFileJSONProjects: string; class function CreateIfNecessaryAndGetPathFolderTemp: string; + class function GetPathFolderMetaInfo: string; class function GetGuidStr: string; class function GuidToFileName(const AGuid: string; const AExtension: string): string; class function GetNamespace(AText: string): string; @@ -137,16 +148,6 @@ implementation DelphiAIDev.View.Dialog, DelphiAIDev.WaitingScreen; -//Winapi.WinInet -//class function TUtils.TestInternetConnection: Boolean; -//var -// LFlags: DWord; -//begin -// Result := InternetGetConnectedState(@LFlags, 0); -// if Result then -// Result := InternetCheckConnection('http://google.com', 1, 0); -//end; - class function TUtils.GetExceptionMessage(const E: Exception): string; begin Result := E.Message; @@ -176,6 +177,26 @@ class procedure TUtils.DefaultsQuestionsKindFillItemsTStrings(AStrings: TStrings AStrings.Add(LItem.ToString); end; +class function TUtils.StrToDriverID(Value: string): TC4DDriverID; +begin + Result := TC4DDriverID.None; + if Value = TC4DDriverID.MySQL.ToString then + Result := TC4DDriverID.MySQL + else if Value = TC4DDriverID.Firebird.ToString then + Result := TC4DDriverID.Firebird +end; + +class procedure TUtils.DriverIDFillItemsTStrings(AStrings: TStrings); +var + LItem: TC4DDriverID; +begin + if AStrings = nil then + Exit; + + for LItem := Low(TC4DDriverID) to High(TC4DDriverID) do + AStrings.Add(LItem.ToString); +end; + class function TUtils.AdjustQuestionToJson(const AValue: string): string; begin Result := AValue @@ -185,32 +206,54 @@ class function TUtils.AdjustQuestionToJson(const AValue: string): string; Result := Result.Replace('\\"', '\"', [rfReplaceAll, rfIgnoreCase]); end; +class function TUtils.DebugMyIsOn: Boolean; +begin + Result := FileExists('C:\Temp\DelphiAIDev\DebugOn.c4d'); +end; + class procedure TUtils.AddLog(const AMessage: string); +begin + Self.AddLogInFileTxt(AMessage, FormatDateTime('yyyy-mm-dd', Now)); +end; + +class procedure TUtils.AddLogDeleteFileFirst(const AMessage: string; const ANameFile: string); +begin + Self.AddLogInFileTxt(AMessage, ANameFile, True); +end; + +class procedure TUtils.AddLogInFileTxt(const AMessage: string; const ANameFile: string; ADeleteFileFirst: Boolean = False); const - DIRECTORY = 'C:\TempLog\DelphiAIDev\'; + DIRECTORY = 'C:\Temp\DelphiAIDev\Logs\'; var LFileName: string; LTextFile: TextFile; begin try - if not(DirectoryExists(DIRECTORY)) then + if not DirectoryExists(DIRECTORY) then ForceDirectories(DIRECTORY); - LFileName := DIRECTORY + FormatDateTime('yyyy-mm-dd', Now) + '.txt'; + LFileName := DIRECTORY + ANameFile + '.txt'; + + if ADeleteFileFirst then + begin + if FileExists(LFileName)then + DeleteFile(LFileName); + end; + AssignFile(LTextFile, LFileName); if not FileExists(LFileName)then Rewrite(LTextFile); Append(LTextFile); - Writeln(LTextFile, AMessage); + Writeln(LTextFile, Format('%s: %s', [DateTimeToStr(Now), AMessage])); CloseFile(LTextFile); except -// on E: Exception do -// ShowMsg('Unable to generate log. Message: ' + E.Message + sLineBreak + 'Filename: ' + LFileName); + //on E: Exception do + // ShowMsg('Unable to generate log. Message: ' + E.Message + sLineBreak + 'Filename: ' + LFileName); end; end; -class function TUtils.GetFileName(const AExtension: string): string; +class function TUtils.GetFileName(const AExtension: string): string; var LFileName: string; LSaveDialog: TSaveDialog; @@ -310,8 +353,8 @@ class procedure TUtils.CenterPanel(const APanel: TPanel; const AWinControl: TWin if AWinControl = nil then Exit; - LCenterX := AWinControl.Width div 2; - LCenterY := AWinControl.Height div 2; + LCenterX := AWinControl.Left + (AWinControl.Width div 2); + LCenterY := AWinControl.Top + (AWinControl.Height div 2); APanel.Left := LCenterX - (APanel.Width div 2); APanel.Top := LCenterY - (APanel.Height div 2); @@ -328,17 +371,43 @@ class procedure TUtils.TogglePasswordChar(const AEdit: TEdit); class function TUtils.CodeIdMarkBeginCode(const AValue: string): Boolean; begin Result := (AValue.Trim = TConsts.MARK_BEGIN_DELPHI) - or(AValue.Trim = TConsts.MARK_BEGIN_PASCAL); + or(AValue.Trim = TConsts.MARK_BEGIN_PASCAL) + or(AValue.Trim = TConsts.MARK_BEGIN_SQL); //or(AValue.Trim = TConsts.MARK_BEGIN_PASCAL2); end; class function TUtils.ConfReturnAI(const AValue: string): string; +var + LStrings: TStrings; + LStrLine: string; + i: Integer; begin Result := AValue.Trim .Replace(TConsts.MARK_BEGIN_DELPHI, '', [rfReplaceAll, rfIgnoreCase]) .Replace(TConsts.MARK_BEGIN_PASCAL, '', [rfReplaceAll, rfIgnoreCase]) //.Replace(TConsts.MARK_BEGIN_PASCAL2, '', [rfReplaceAll, rfIgnoreCase]) + .Replace(TConsts.MARK_BEGIN_SQL, '', [rfReplaceAll, rfIgnoreCase]) + .Replace(TConsts.MARK_BEGIN_SQL2, '', [rfReplaceAll, rfIgnoreCase]) .Replace(TConsts.MARK_END, '', [rfReplaceAll, rfIgnoreCase]); + + LStrings := TStringList.Create; + try + LStrings.Text := Result; + + Result := ''; + for i := 0 to Pred(LStrings.Count) do + begin + LStrLine := LStrings[i]; + if (i = 0) and ((LStrLine.Trim.IsEmpty)or(LStrLine = sLineBreak)) then + Continue; + + Result := Result + LStrLine + sLineBreak; + end; + + Result := Result.TrimRight; + finally + LStrings.Free; + end; end; class function TUtils.ProcessTextForEditor(const AText: string): string; @@ -399,7 +468,7 @@ class function TUtils.FileNameIsDelphiAIDeveloperDPROJ(const AFileName: string): class procedure TUtils.RemoveBlankSpaceInBegin(var AValue: string; const ACount: Integer); begin - if(ACount <= 0)then + if ACount <= 0 then Exit; if Trim(copy(AValue, 1, ACount)).IsEmpty then @@ -441,9 +510,9 @@ class procedure TUtils.ExplodeList(const AText, ASeparator: string; AStrings: TS I := 1; while(I <= LLengthText)do begin - if(Copy(LText, I, LLengthSeparator) = ASeparator)or(I = LLengthText)then + if (Copy(LText, I, LLengthSeparator) = ASeparator) or (I = LLengthText) then begin - if(I = LLengthText)then + if I = LLengthText then LItem := LItem + StringReplace(LText[I], ASeparator, '', [rfReplaceAll, rfIgnoreCase]); AStrings.Add(Trim(LItem)); @@ -466,7 +535,7 @@ class procedure TUtils.MemoVerticalCenter(AMemo: TMemo; ANumLines: Integer; ATex AMemo.Lines.Text := AText.Trim; LLinesCount := AMemo.Lines.Count; AMemo.ScrollBars := System.UITypes.TScrollStyle.ssVertical; - if(LLinesCount < ANumLines)then + if LLinesCount < ANumLines then begin AMemo.ScrollBars := System.UITypes.TScrollStyle.ssNone; AMemo.Lines.Clear; @@ -483,14 +552,14 @@ class function TUtils.StatusBarNumPanelDblClick(AStatusBar: TStatusBar): Integer LNumPanel: Integer; begin LNumPanel := 0; - if(not AStatusBar.SimplePanel)and(AStatusBar.Panels.Count > 0)then + if (not AStatusBar.SimplePanel) and (AStatusBar.Panels.Count > 0) then begin LPointMouse := AStatusBar.ScreenToClient(Mouse.CursorPos); LWidth := 0; for LNumPanel := 0 to AStatusBar.Panels.Count - 2 do begin LWidth := LWidth + AStatusBar.Panels[LNumPanel].Width; - if(LPointMouse.X <= LWidth)then + if LPointMouse.X <= LWidth then Break; end; end; @@ -510,7 +579,7 @@ class function TUtils.StrToBoolC4D(Value: string): Boolean; class function TUtils.RemoveCommentAfterTwoBars(Value: string): string; begin Result := Value; - if(Result.Contains('//'))then + if Result.Contains('//') then Result := Copy(Result, 1, (Pos('//', Result) - 1)); end; @@ -518,12 +587,12 @@ class procedure TUtils.FindListVewItem(AListView: TListView; AIndexSubItem: Inte var I: Integer; begin - if(AStrFind.Trim.IsEmpty)then + if AStrFind.Trim.IsEmpty then Exit; for I := 0 to Pred(AListView.Items.Count)do begin - if(AListView.Items[I].SubItems[AIndexSubItem] = AStrFind)then + if AListView.Items[I].SubItems[AIndexSubItem] = AStrFind then begin AListView.ItemIndex := I; AListView.SetFocus; @@ -575,23 +644,23 @@ class function TUtils.SelectFile(const ADefaultFile: string; const ADefaultExt: LOpenDialog := TOpenDialog.Create(nil); try LOpenDialog.Title := 'C4D - Select a file'; - if(not ADefaultFile.Trim.IsEmpty)then + if not ADefaultFile.Trim.IsEmpty then begin LFolder := ExtractFilePath(ADefaultFile); - if(System.SysUtils.DirectoryExists(LFolder))then + if System.SysUtils.DirectoryExists(LFolder) then LOpenDialog.InitialDir := LFolder; - if(System.SysUtils.FileExists(ADefaultFile))then + if System.SysUtils.FileExists(ADefaultFile) then LOpenDialog.FileName := ExtractFileName(ADefaultFile); end; - if(ADefaultExt <> TC4DExtensionsFiles.All)then + if ADefaultExt <> TC4DExtensionsFiles.All then begin LOpenDialog.DefaultExt := ADefaultExt.ToString; LOpenDialog.Filter := Format('Arquivo %s|*.%s', [ADefaultExt.ToString.ToUpper, ADefaultExt.ToString]); end; - if(not LOpenDialog.Execute)then + if not LOpenDialog.Execute then Exit(ADefaultFile); Result := LOpenDialog.FileName; finally @@ -611,12 +680,12 @@ class function TUtils.SelectFolder(const ADefaultFolder: string; const ADefaultF LFileOpenDialog.Title := 'Delphi AI Developer - Select a folder'; LFileOpenDialog.Options := [fdoPickFolders]; - if(not ADefaultFolder.Trim.IsEmpty)and(System.SysUtils.DirectoryExists(ADefaultFolder))then + if (not ADefaultFolder.Trim.IsEmpty) and (System.SysUtils.DirectoryExists(ADefaultFolder)) then LFileOpenDialog.DefaultFolder := ADefaultFolder; - if(not LFileOpenDialog.Execute)then + if not LFileOpenDialog.Execute then begin - if(ADefaultFolderIfCancel)then + if ADefaultFolderIfCancel then Result := ADefaultFolder; Exit; end; @@ -640,7 +709,7 @@ class function TUtils.StringToColorDef(AValue: string; AColorDefault: TColor = c class function TUtils.DateTimeToStrEmpty(AValue: TDateTime): string; begin Result := ''; - if(AValue > 0)then + if AValue > 0 then Result := DateTimeToStr(AValue); end; @@ -649,19 +718,19 @@ class function TUtils.DirectoryDelete(AFullPath: string): Boolean; LSr: TSearchRec; LFullName: string; begin - if(not System.SysUtils.DirectoryExists(AFullPath))then + if not System.SysUtils.DirectoryExists(AFullPath) then Exit(False); try Result := True; - if(FindFirst(AFullPath + '\*.*', faAnyFile, LSr) = 0)then + if FindFirst(AFullPath + '\*.*', faAnyFile, LSr) = 0 then begin try repeat LFullName := IncludeTrailingPathDelimiter(AFullPath) + LSr.Name; - if(LSr.Name <> '.')and(LSr.Name <> '..')then + if (LSr.Name <> '.') and (LSr.Name <> '..') then begin - if((LSr.Attr and faDirectory) = 0)then + if ((LSr.Attr and faDirectory) = 0) then Result := System.SysUtils.DeleteFile(LFullName) else Result := DirectoryDelete(LFullName); @@ -681,7 +750,7 @@ class function TUtils.DirectoryOrFileMove(AFrom, ATo: string): Boolean; begin Result := False; try - if(MoveFile(PWideChar(AFrom), PWideChar(ATo)))then + if MoveFile(PWideChar(AFrom), PWideChar(ATo)) then Result := True; except on E: Exception do @@ -707,7 +776,7 @@ class procedure TUtils.OpenFile(AFilePath: string); class procedure TUtils.OpenFileOrFolder(APath: string); begin - if(FileExists(APath))then + if FileExists(APath) then Self.OpenFile(APath) else Self.OpenFolder(APath); @@ -745,15 +814,37 @@ class function TUtils.GetPathFileChat: string; Result := Self.GetPathFolderRoot + TConsts.FILE_RTF_CHAT; end; +class function TUtils.GetPathFileChatDB: string; +begin + Result := Self.GetPathFolderRoot + TConsts.FILE_RTF_CHAT_DB; +end; + class function TUtils.GetPathFileJSONDefaultsQuestions: string; begin Result := Self.GetPathFolderRoot + TConsts.FILE_JSON_DEFAULTS_QUESTIONS; end; +class function TUtils.GetPathFileJSONDatabases: string; +begin + Result := Self.GetPathFolderRoot + TConsts.FILE_JSON_DATABASES; +end; + +class function TUtils.GetPathFileJSONProjects: string; +begin + Result := Self.GetPathFolderRoot + TConsts.FILE_JSON_PROJECTS; +end; + class function TUtils.CreateIfNecessaryAndGetPathFolderTemp: string; begin Result := Self.GetPathFolderRoot + TConsts.NAME_FOLDER_TEMP; - if(not DirectoryExists(Result))then + if not DirectoryExists(Result) then + ForceDirectories(Result); +end; + +class function TUtils.GetPathFolderMetaInfo: string; +begin + Result := IncludeTrailingPathDelimiter(Self.GetPathFolderRoot + TConsts.NAME_FOLDER_MetaInfo); + if not DirectoryExists(Result) then ForceDirectories(Result); end; @@ -774,7 +865,7 @@ class function TUtils.GuidToFileName(const AGuid: string; const AExtension: stri class function TUtils.GetNamespace(AText: string): string; begin Result := ''; - if(ContainsStr(AText, '.'))then + if ContainsStr(AText, '.') then Result := Copy(AText, 1, Pos('.', AText)); end; @@ -786,23 +877,23 @@ class function TUtils.GetTextBetween(AText, ADelimitador1, ADelimitador2: string begin Result := ''; LText := AText; - if(ACaseSensitive)then + if ACaseSensitive then LPosIni := Pos(ADelimitador1, LText) else LPosIni := Pos(AnsiUpperCase(ADelimitador1), AnsiUpperCase(LText)); - if(LPosIni > 0)then + if LPosIni > 0 then LText := Copy(LText, LPosIni, Length(LText)); - if(ACaseSensitive)then + if ACaseSensitive then LPosFim := Pos(ADelimitador2, LText) else LPosFim := Pos(AnsiUpperCase(ADelimitador2), AnsiUpperCase(LText)); - if(LPosFim > 0)then + if LPosFim > 0 then LText := Copy(LText, 1, LPosFim + Length(ADelimitador2) - 1); - if(LPosIni > 0)or(LPosFim > 0)then + if (LPosIni > 0) or (LPosFim > 0) then Result := LText; end; @@ -852,9 +943,9 @@ class function TUtils.ChangeLastComma(AValue: string; ANewLastChar: Char): strin begin Result := AValue; AValue := AValue.TrimRight; - if(not AValue.IsEmpty)then + if not AValue.IsEmpty then begin - if(RightStr(AValue, 1) = ',')then + if RightStr(AValue, 1) = ',' then begin Delete(AValue, AValue.Length, 1); Result := AValue + ANewLastChar; @@ -866,9 +957,9 @@ class function TUtils.RemoveLastChar(AValue: string; AChar: Char): string; begin Result := AValue; AValue := AValue.Trim; - if(not AValue.IsEmpty)then + if not AValue.IsEmpty then begin - if(RightStr(AValue, 1) = AChar)then + if RightStr(AValue, 1) = AChar then begin Delete(AValue, AValue.Length, 1); Result := AValue; @@ -889,7 +980,7 @@ class function TUtils.RemoveAccents(AValue: string): string; I: Integer; begin for I := 1 to Length(AValue) do - if(Pos(AValue[I], WITH_ACCENTS) <> 0)then + if Pos(AValue[I], WITH_ACCENTS) <> 0 then AValue[I] := OUT_ACCENTS[Pos(AValue[I], WITH_ACCENTS)]; Result := AValue; @@ -903,7 +994,7 @@ class function TUtils.SwapSymbols(AValue: string): string; I: Integer; begin for I := 1 to Length(AValue)do - if(Pos(AValue[I], SYMBOLS_OLD) <> 0)then + if Pos(AValue[I], SYMBOLS_OLD) <> 0 then AValue[I] := SYMBOLS_NEW[Pos(AValue[I], SYMBOLS_OLD)]; Result := AValue; @@ -992,7 +1083,7 @@ class function TUtils.IsDPK(const AFilePath: string): Boolean; class function TUtils.IsProjectGroup(const AFilePath: string): Boolean; begin - Result := ExtractFileExt(AFilePath).ToLower = '.groupproj'; + Result := ExtractFileExt(AFilePath).ToLower = TC4DExtensionsFiles.GROUPPROJ.ToStringWithPoint; end; class function TUtils.IsProject(const AFilePath: string): Boolean; diff --git a/Src/View/DelphiAIDev.View.About.dfm b/Src/View/DelphiAIDev.View.About.dfm index 056b279..3a7fe14 100644 --- a/Src/View/DelphiAIDev.View.About.dfm +++ b/Src/View/DelphiAIDev.View.About.dfm @@ -2103,15 +2103,16 @@ object DelphiAIDevViewAbout: TDelphiAIDevViewAbout TabOrder = 0 OnClick = btnOKClick end - object btnTeste: TButton + object btnTest: TButton Left = 2 Top = 2 Width = 75 Height = 31 Align = alLeft - Caption = 'Teste' + Caption = 'Test' TabOrder = 1 - OnClick = btnTesteClick + Visible = False + OnClick = btnTestClick end end end diff --git a/Src/View/DelphiAIDev.View.About.pas b/Src/View/DelphiAIDev.View.About.pas index b694cdb..e4f78e0 100644 --- a/Src/View/DelphiAIDev.View.About.pas +++ b/Src/View/DelphiAIDev.View.About.pas @@ -32,7 +32,7 @@ TDelphiAIDevViewAbout = class(TForm) imgGithub: TImage; pnButtons: TPanel; btnOK: TButton; - btnTeste: TButton; + btnTest: TButton; Panel1: TPanel; lbDonateToCode4Delphi: TLabel; imgDonate: TImage; @@ -44,7 +44,7 @@ TDelphiAIDevViewAbout = class(TForm) procedure lbSiteCode4DelphiMouseLeave(Sender: TObject); procedure lbGitHubCode4DelphiClick(Sender: TObject); procedure FormCreate(Sender: TObject); - procedure btnTesteClick(Sender: TObject); + procedure btnTestClick(Sender: TObject); procedure lbDonateToCode4DelphiClick(Sender: TObject); private @@ -121,7 +121,7 @@ procedure TDelphiAIDevViewAbout.lbSiteCode4DelphiMouseMove(Sender: TObject; Shif TLabel(Sender).Font.Style := TLabel(Sender).Font.Style + [fsUnderline]; end; -procedure TDelphiAIDevViewAbout.btnTesteClick(Sender: TObject); +procedure TDelphiAIDevViewAbout.btnTestClick(Sender: TObject); begin // end; diff --git a/Src/View/DelphiAIDev.View.Dialog.dfm b/Src/View/DelphiAIDev.View.Dialog.dfm index 63d2404..441b4d7 100644 --- a/Src/View/DelphiAIDev.View.Dialog.dfm +++ b/Src/View/DelphiAIDev.View.Dialog.dfm @@ -100,8 +100,8 @@ object DelphiAIDevViewDialog: TDelphiAIDevViewDialog object lbMsg: TLabel Left = 32 Top = 0 - Width = 432 - Height = 133 + Width = 30 + Height = 14 Align = alClient Alignment = taCenter Caption = 'lbMsg' @@ -114,8 +114,6 @@ object DelphiAIDevViewDialog: TDelphiAIDevViewDialog PopupMenu = PopupMenu1 Layout = tlCenter WordWrap = True - ExplicitWidth = 30 - ExplicitHeight = 14 end object pnDetailsLabel: TPanel Left = 0 @@ -132,7 +130,7 @@ object DelphiAIDevViewDialog: TDelphiAIDevViewDialog Left = 389 Top = 0 Width = 68 - Height = 17 + Height = 14 Cursor = crHandPoint Margins.Left = 0 Margins.Top = 0 @@ -149,14 +147,13 @@ object DelphiAIDevViewDialog: TDelphiAIDevViewDialog ParentFont = False Layout = tlCenter OnClick = lbViewDetailsClick - ExplicitHeight = 14 end object lbViewDetails02: TLabel AlignWithMargins = True Left = 462 Top = 0 Width = 12 - Height = 17 + Height = 14 Cursor = crHandPoint Margins.Left = 0 Margins.Top = 0 @@ -173,7 +170,6 @@ object DelphiAIDevViewDialog: TDelphiAIDevViewDialog ParentFont = False Layout = tlCenter OnClick = lbViewDetailsClick - ExplicitHeight = 14 end end object pnImg: TPanel diff --git a/Src/View/DelphiAIDev.View.Dialog.pas b/Src/View/DelphiAIDev.View.Dialog.pas index 556a0a7..4ffb25a 100644 --- a/Src/View/DelphiAIDev.View.Dialog.pas +++ b/Src/View/DelphiAIDev.View.Dialog.pas @@ -112,7 +112,7 @@ procedure TDelphiAIDevViewDialog.FormKeyDown(Sender: TObject; var Key: Word; Shi VK_ESCAPE: if Shift = [] then begin - if(btnCancel.Visible)then + if btnCancel.Visible then btnCancel.Click else Self.Close; @@ -132,7 +132,7 @@ procedure TDelphiAIDevViewDialog.ConfHeightForm; begin pnDetailsLabel.Visible := True; pnDetails.Visible := True; - if(FDetails.Trim.IsEmpty)then + if FDetails.Trim.IsEmpty then begin pnDetailsLabel.Visible := False; pnDetails.Visible := False; @@ -149,14 +149,14 @@ procedure TDelphiAIDevViewDialog.ConfButtons; begin btnCancel.Visible := FButtons = TC4DButtons.OK_Cancel; btnOK.SetFocus; - if(btnCancel.Visible)and(FBtnFocu = TC4DBtnFocu.Cancel)then + if (btnCancel.Visible) and (FBtnFocu = TC4DBtnFocu.Cancel) then btnCancel.SetFocus; end; procedure TDelphiAIDevViewDialog.lbViewDetailsClick(Sender: TObject); begin try - if(mmDetails.Visible)then + if mmDetails.Visible then begin mmDetails.Visible := False; lbViewDetails02.Caption := '>>'; diff --git a/Src/View/DelphiAIDev.View.Memo.pas b/Src/View/DelphiAIDev.View.Memo.pas index cb7a9f8..becf793 100644 --- a/Src/View/DelphiAIDev.View.Memo.pas +++ b/Src/View/DelphiAIDev.View.Memo.pas @@ -56,7 +56,7 @@ procedure TDelphiAIDevViewMemo.mmMensagemKeyDown(Sender: TObject; var Key: Word; const KEY_A = $41; begin - if(Key = KEY_A)and(Shift = [ssCtrl])then + if (Key = KEY_A) and (Shift = [ssCtrl]) then mmMensagem.SelectAll; end; diff --git a/Src/WaitingScreen/DelphiAIDev.WaitingScreen.View.pas b/Src/WaitingScreen/DelphiAIDev.WaitingScreen.View.pas index 4657d7c..6faad8d 100644 --- a/Src/WaitingScreen/DelphiAIDev.WaitingScreen.View.pas +++ b/Src/WaitingScreen/DelphiAIDev.WaitingScreen.View.pas @@ -47,7 +47,7 @@ procedure TDelphiAIDevWaitingScreenView.FormCreate(Sender: TObject); procedure TDelphiAIDevWaitingScreenView.FormShow(Sender: TObject); begin lbMsg.Caption := C_MSG_DEFAULT; - if(not FMsg.Trim.IsEmpty)then + if not FMsg.Trim.IsEmpty then lbMsg.Caption := FMsg; Self.BringToFront; @@ -55,7 +55,7 @@ procedure TDelphiAIDevWaitingScreenView.FormShow(Sender: TObject); procedure TDelphiAIDevWaitingScreenView.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin - if(Key = VK_F4)and(ssAlt in Shift)then + if (Key = VK_F4) and (ssAlt in Shift) then Key := 0; end; diff --git a/Src/WaitingScreen/DelphiAIDev.WaitingScreen.pas b/Src/WaitingScreen/DelphiAIDev.WaitingScreen.pas index 0c5906f..82123a3 100644 --- a/Src/WaitingScreen/DelphiAIDev.WaitingScreen.pas +++ b/Src/WaitingScreen/DelphiAIDev.WaitingScreen.pas @@ -25,7 +25,7 @@ implementation class function TDelphiAIDevWaitingScreen.GetInstance: TDelphiAIDevWaitingScreen; begin - if(not Assigned(Instance))then + if not Assigned(Instance) then Instance := Self.Create; Result := Instance; end; @@ -42,7 +42,7 @@ destructor TDelphiAIDevWaitingScreen.Destroy; procedure TDelphiAIDevWaitingScreen.Show(const AMsg: string = ''); begin - if(not Assigned(FDelphiAIDevWaitingScreenView))then + if not Assigned(FDelphiAIDevWaitingScreenView) then FDelphiAIDevWaitingScreenView := TDelphiAIDevWaitingScreenView.Create(nil); FDelphiAIDevWaitingScreenView.Msg := AMsg; FDelphiAIDevWaitingScreenView.Show; @@ -57,7 +57,7 @@ procedure TDelphiAIDevWaitingScreen.Close; initialization finalization - if(Assigned(Instance))then + if Assigned(Instance) then FreeAndNil(Instance); end.