7
votes

Is it possible to make readonly source code (eg., .pas & .dfm) writable from within the Delphi IDE? The right click option to make files Readonly/Writable within the IDE doesn't change the properties on the file system. Is there an IDE extension or similar that can achieve this?

A way to do this without having to integrate a source control system would be preferable. I am using Delphi XE and Delphi 6.

Thanks!

sse

3

3 Answers

8
votes

This is how I would do it.

Create a new package which will be installed into the IDE at designtime. If you have an existing package handy then you could carry on using it. Make sure the package requires the designide package. You can do this in the project manager, or just by viewing the project source and adding designide to the requires clause.

Now add the following unit to your package.

unit MakeEditable;

interface

procedure Register;

implementation

uses
  Windows, SysUtils, Menus, ToolsAPI;

type
  TMakeEditable = class(TObject)
  private
    FEditorServices: IOTAEditorServices;
    FFileMenu: TMenuItem;
    FMakeEditable: TMenuItem;
    function MenuItemWithCaptionLike(const Menu: TMenuItem; const DesiredCaption: string): TMenuItem;
    procedure MakeEditableClick(Sender: TObject);
  public
    constructor Create;
    destructor Destroy; override;
  end;

constructor TMakeEditable.Create;
var
  Index: Integer;
  PreviousMenuItem: TMenuItem;
begin
  inherited;
  FEditorServices := (BorlandIDEServices as IOTAEditorServices);
  FFileMenu := MenuItemWithCaptionLike((BorlandIDEServices as INTAServices40).MainMenu.Items, 'File');
  if Assigned(FFileMenu) then begin
    PreviousMenuItem := MenuItemWithCaptionLike(FFileMenu, 'Reopen');
    if Assigned(PreviousMenuItem) then begin
      Index := PreviousMenuItem.MenuIndex;
      if Index>=0 then begin
        FMakeEditable := TMenuItem.Create(FFileMenu);
        FMakeEditable.Caption := 'Ma&ke Editable';
        FMakeEditable.OnClick := MakeEditableClick;
        FFileMenu.Insert(Index, FMakeEditable);
      end;
    end;
  end;
end;

destructor TMakeEditable.Destroy;
begin
  FMakeEditable.Free;
  inherited;
end;

function TMakeEditable.MenuItemWithCaptionLike(const Menu: TMenuItem; const DesiredCaption: string): TMenuItem;
var
  i: Integer;
  Target, Found: string;
begin
  Target := StringReplace(LowerCase(Trim(DesiredCaption)), '&', '', [rfReplaceAll, rfIgnoreCase]);
  for i := 0 to Menu.Count-1 do begin
    Result := Menu.Items[i];
    Found := StringReplace(LowerCase(Trim(Result.Caption)), '&', '', [rfReplaceAll, rfIgnoreCase]);
    if Pos(Target, Found)>0 then begin
      exit;
    end;
  end;
  Result := nil;
end;

procedure TMakeEditable.MakeEditableClick(Sender: TObject);

  procedure MakeFileEditable(const FileName: string);
  var
    Attributes: DWORD;
  begin
    Attributes := GetFileAttributes(PChar(FileName));
    SetFileAttributes(PChar(FileName), Attributes and not FILE_ATTRIBUTE_READONLY);
  end;

var
  FileName: string;
  FileExt: string;
  LinkedFileName: string;
  EditBuffer: IOTAEditBuffer;

begin
  EditBuffer := FEditorServices.TopBuffer;
  FileName := EditBuffer.FileName;
  if FileExists(FileName) then begin
    MakeFileEditable(FileName);
    EditBuffer.IsReadOnly := False;

    FileExt := ExtractFileExt(FileName);
    if SameText(FileExt,'.dfm') then begin
      LinkedFileName := ChangeFileExt(FileName, '.pas');
    end else if SameText(FileExt,'.pas') then begin
      LinkedFileName := ChangeFileExt(FileName, '.dfm');
    end else begin
      LinkedFileName := '';
    end;
    if (LinkedFileName<>'') and FileExists(LinkedFileName) then begin
      MakeFileEditable(LinkedFileName);
    end;
  end;
end;

var
  MakeEditableInstance: TMakeEditable;

procedure Register;
begin
  MakeEditableInstance := TMakeEditable.Create;
end;

initialization

finalization
  MakeEditableInstance.Free;

end.

When you compile and install this package you will now have a new menu item on the File menu which both clears the read-only flag in the input buffer and makes the file writeable.

enter image description here

3
votes

You can call a .bat file from the tools menu. So you could write a .bat file to do the work for you, and call it with $EDNAME as the parameter. Your .bat file should see the filename as %1. Then you'd need a little bit of logic to change the read-only flag (attrib command?) and then see if there is a .dfm and do that one as well.

You could also (obviously) make a Delphi command-line app to do this, if you're not comfortable with .bat files.

This idea inspired by this article that talks about how to use a .bat file to integrate SVN commands with the Delphi tools menu: http://delphi.wikia.com/wiki/Adding_TortoiseSVN_to_the_Tools_menu

0
votes

Another idea: Just add an option to the tools menu: "open command prompt here". It lets you do all sorts of things from the command line, such as running the attrib command.

Add a new entry to the Tools menu, and use these settings:

Title: Open Command Prompt Here
Program: cmd.exe
Working Dir (leave blank)
Parameters:  cd $PATH($EDNAME)

Also, make yourself an "Open Folder Here" entry:

Title: Open Folder Here
Program: explorer.exe
Working Dir (leave blank)
Parameters: $PATH($EDNAME)