views:

212

answers:

5

Hello,

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.

+1  A: 

I don't think it can. You can determine the directory your EXE is running in easily enough, but your component at design-time is running as a part of the IDE. I doubt there's a way for the component to access project information through the IDE.

Mason Wheeler
That is not what I want hear. :-) I hoped that I can access the design API of the IDE from my component.
Heinz Z.
Take a look at OTAPI, which can access the design API of the IDE, but I don't think you can mix OTAPI functionality in with components on a form.
Mason Wheeler
@Heinz: Not from your component, as Mason said. You can from an IDE Expert using the Open Tools API (OTAPI).
Ken White
@Mason: Mixing OTAPI with components is possible (see my solution)
Heinz Z.
+1  A: 

A component cannot access your source path, because a component is placed in your application and run as a part of your application out of Delphi IDE.

If you want to have access to project path, or automate any process inside IDE; you have to write an IDE expert using OpenTools API, not a component.

vcldeveloper
+1  A: 

see

http://www.gexperts.org/otafaq.html#project

and then

www.href.com/pub/sw/ProjectOptions.html

may be it helps

just a friend
The link to gexperts otafaq was very helpful.
Heinz Z.
+1  A: 

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.
Heinz Z.
I'm pretty sure your GetCurrentUnitPath does not compile (at least the boolean expression for the while loop doesn't) and the while loop will never end. But the idea is interesting though, so I did upvote your answer.
Jeroen Pluimers
@Jeroen: Thank you for pointing this out. The stackoverflow.com editor cropped the line "while not ((idx < 0) or SysUtils.Supports(Module.GetModuleFileEditor(idx), IOTASourceEditor, SourceEditor) do". I have adjust the condition of the while-loop so stackoverflow.com shows it correct. By the way, the CurrentUnitPath is extracted (with modifications) from the DUnit wizzard which comes with delphi.
Heinz Z.
A: 

I'm working with Delphi 2010 and I was looking for one solution for a long time! I used only the function GetCurrentUnitPath implementation and it's worked very well!!! Thanks a lot! :)

Renato