Search code examples
delphidelphi-2010componentsdesign-time

How can a component at designtime determine the project directory


I write a component which should store some information relative to the project directory. Every time a property of my component is changed it should write a file. So how can a component determine the current project directory at design time.

Thanks in advance

EDIT:
I want to generate a delphi source file every time a property of my component is changed, so that I always get the latest version when I compile my code. Think of it as a kind of code generator.

At the moment I set whole path and filename where the source should be stored but I prefer a relative path to the project (or the form/datamodule which contains my component) to make it easier to copy the project on different developer machines.


Solution

  • Thanks for the hints. Open Tools API is the way to go and using the Open Tools API from a component on a form at designtime is possible.

    So here is my solution:

    I need two units, one for the component and one for registering the component and the code which use the Open Tools API.

    Here comes the component unit:

    
    unit TestLabels;
    
    interface
    
    uses
      SysUtils, Classes, Windows, Controls, StdCtrls;
    
    type
      TTestLabel = class(TLabel)
      private
        FTestProperty: Boolean;
        procedure SetTestProperty(const Value: Boolean);
        procedure Changed;
      published
        property TestProperty: Boolean read FTestProperty write SetTestProperty;
      end;
    
    var
      OnGetUnitPath: TFunc;
    
    implementation
    
    { TTestLabel }
    
    procedure TTestLabel.Changed;
    begin
      if not (csDesigning in ComponentState) then
         Exit; // I only need the path at designtime
    
      if csLoading in ComponentState then
         Exit; // at this moment you retrieve the unit path which was current before
    
      if not Assigned(OnGetUnitPath) then
        Exit;
    
      // only for demonstration
      Caption := OnGetUnitPath;
      MessageBox(0, PChar(ExtractFilePath(OnGetUnitPath)), 'Path of current unit', 0);
    end;
    
    procedure TTestLabel.SetTestProperty(const Value: Boolean);
    begin
      if FTestProperty  Value then
      begin
        FTestProperty := Value;
        Changed;
      end;
    end;
    
    end.
    

    Here is the unit for registering the component and the call to the Open Tools API:

    
    unit TestLabelsReg;
    
    interface
    
    uses
      SysUtils, Classes, Controls, StdCtrls, TestLabels;
    
    procedure register;
    
    implementation
    
    uses
      ToolsAPI;
    
    function GetCurrentUnitPath: String;
    var
      ModuleServices: IOTAModuleServices;
      Module: IOTAModule;
      SourceEditor: IOTASourceEditor;
      idx: integer;
    
    begin
      Result := '';
      SourceEditor := nil;
    
      if SysUtils.Supports(BorlandIDEServices, IOTAModuleServices,
        ModuleServices) then
      begin
        Module := ModuleServices.CurrentModule;
    
        if System.Assigned(Module) then
        begin
          idx := Module.GetModuleFileCount - 1;
    
          // Iterate over modules till we find a source editor or list exhausted
          while (idx >= 0) and not SysUtils.Supports(Module.GetModuleFileEditor(idx), IOTASourceEditor, SourceEditor) do
            System.Dec(idx);
    
          // Success if list wasn't ehausted.
          if idx >= 0 then
            Result := ExtractFilePath(SourceEditor.FileName);
        end;
    
      end;
    
    end;
    
    procedure register;
    begin
      RegisterComponents('Samples', [TTestLabel]);
      TestLabels.OnGetUnitPath := GetCurrentUnitPath;
    end;
    
    end.