Delphi Can

Orjinalini görmek için tıklayınız: Delphi IDE'sine Eklenti Yapmak
Şu anda (Arşiv) modunu görüntülemektesiniz. Orjinal Sürümü Görüntüle internal link
Delphi'nin  harika özelliklerinden bir tanesi de IDE'sine müdahale etmemize, eklenti yazmamıza vs. izin vermesidir. Bunun için Tools API adı verilen bir API kütüphanesi mevcut. 
Tools API sayesinde Delphi IDE'sinde bir çok değişiklik yapabiliriz. Örneğin menüsüne kendi menü elamanımız koyabiliriz, açılış ekranında değişiklik yapabiliriz, toolbar'da, editör'de, object inspector'da, kısacası IDE içerisindeki her yerde, hemen hemen tüm istediğimiz şeyleri yapabiliriz. Aslında ne kadar çok şey yapabileceğimize en güzel örnek CnPack ve GExperts'tir. Bu eklenti kütüphanelerini hemen hemen hepimiz kullanmışızdır. Neler yapılabildiğini biliyorsunuzdur. İşte bu kadar şeyin arkasında Tools API var. Dolayısıyla Tools API ile yapılabileceklerin sınırı yok gibi bir şey. Bu deryanın ben sadece çok az kısmıyla uğraştım. O yüzden burada da vereceğim örnekler çok basit örnekler olacak. Ama en azından bir başlangıç olur ümidindeyim Smile

Tools API ile ilgili dosyaların tamamı Delphi'nin kurulu olduğu dizinde, source\ToolsAPI klasörü içinde. Delphi 10.2 için yeri:
C:\Program Files (x86)\Embarcadero\Studio\19.0\source\ToolsAPI

Bu APIlerin ana dosyası ToolsApi.pas'tır. Aslında çok fazla kaynak olmayan bu konudaki en güzel kaynak bu dosyadır. Dediğim gibi, maalesef bu konuda yeterince kaynak/örnek yok. Hele hele Türkçe kaynak yok denecek kadar az. Delphi'nin yardım dokümanlarında ilgili kısım: Extending the IDE Using the Tools API
   
Tool API'de işimiz hep interface'ler ile. IDE'nin hangi kısmı ile ilgili değişiklik yapmak istiyorsanız onunla ilgili interface'i kullanmalısınız. 100'den fazla interface var. Bu interface'lere ToolsApi.pas içinden ulaşabilirsiniz. Bunlar genel olarak 2 kısıma ayrılmıştır: Native Tools API(NTA) ve Open Tools API(OTA). Nasıl bir eklenti yapacağınıza göre bunlardan hangi grubu kullanacağınıza siz karar verirsiniz. Yada iki gruptaki interface'ler birlikte kullanılabilir. 

Eklentiler bir paket(package) içinde oluşturulabildiği gibi, bir DLL olarak da oluşturulabilir. Üzerinde çalışması ve IDE'ye tanıtması daha kolay olduğu için ben package yöntemini göstereceğim.

Öncelikle bir package oluşturuyoruz: File -> New -> Package - Delphi.
Project Manager'da Project1.bpl adında projemiz oluşmuş olmalı. Bunu DelphiCanIDE.bpl olarak kaydedelim. Bu paket içinden Tool API'ye erişebilmemiz için designide paketini projemizin Requires bölümüne eklememiz gerekiyor. designide paketi:
C:\Program Files (x86)\Embarcadero\Studio\19.0\lib\win32\release\designide.dcp
Bunu projeye eklemek için Requires klasörü üzerinde sağ tıklayıp Add Reference'ı seçiyoruz. Daha sonra Package name alanına yukarıdaki dosyayı yazıyoruz:

nJnAVM.gif


İlk örnek olarak IDE'ye bir menu elemanı (TMenuItem) ekleyelim.
İlk olarak projemize bir Unit ekleyip kaydettikten sonra içeriğini aşağıdaki gibi dolduruyoruz:
unit DelphicanMenu;

interface

uses ToolsAPI;

type
 TDelphicanMenu = class(TNotifierObject, IOTAMenuWizard, IOTAWizard)
 public
   function GetIDString: string;
   function GetName: string;
   function GetState: TWizardState;
   procedure Execute;

   function GetMenuText: string;
 end;

procedure Register;

implementation

uses Vcl.Dialogs;

procedure Register;
begin
 RegisterPackageWizard(TDelphicanMenu.Create);
end;

{ TDelphicanMenu}

procedure TDelphicanMenu.Execute;
begin
 Showmessage('Merhaba Delphicanlar!');
end;

function TDelphicanMenu.GetIDString: string;
begin
 Result := 'SimaWB delphican.com'; // IDE eklentimiz için ayırt edici bir ID
end;

function TDelphicanMenu.GetMenuText: string;
begin
 Result := 'DelphiCan'; // Ekleyeceğimiz Menu Item'ın başlığı
end;

function TDelphicanMenu.GetName: string;
begin
 Result := 'DelphiCan Menu Item'; // IDE eklentimizin ismi
end;

function TDelphicanMenu.GetState: TWizardState;
begin
 Result := [wsEnabled]; // Eklentinin durumu
end;

end.

Kodlarımızı bu şekilde tamamladıktan sonra eklentimizi IDE'ye tanıtmadan önce hazırladığımız bu "design time package"a bir tanımlama(description) girmemiz faydalı olacaktır. Bunu yapmak için projemiz üzerinde sağ tıklayıp Options'a giriyoruz. Açılan pencereden Description'ı seçip Description alanına bir tanım giriyoruz:

lbpOXX.gif

Bu işlemi yaptıktan sonra projemizi derliyoruz. Ardında proje üzerinde sağ tıklayınca açılan menüden Install'ı seçtiğimizde paketimizin install edildiğine dair bir mesaj alırız. Artık paketimiz Delphi IDEsinin "design packages" listesinde yerini almıştır. Bunu görmek için ana menüde Component -> Install Packages 'i seçtiğimizde açılan listede yazdığımız paketin de olduğunu görürüz:

Ma2drM.png

Aynı zamanda Delphi ana menüsünde Help -> Help Wizards altında DelphiCan adında bir menü elemanının eklendiğini göreceksiniz. Buna tıkladığınızda yazdığımız mesaj karşınıza çıkacaktır. 

Yukarıda verdiğim kodlar içerisinde küçük açıklamalar yazsamda biraz daha açıklamak iyi olur sanırım:
Başlarken belirttiğim gibi Interface'leri kullandık. Bu örnekte kullanılanlar IOTAMenuWizard ve IOTAWizard.  IOTAWizard interface'i OTA kullanarak eklenti yapmak için kullanılması gereken ana interface'tir. Bunun fonksiyonları GetIDString, GetName, GetState ve Execute'tur. Biliyorsunuz interface'ten türetilmiş bir sınıf tanımladığınızda interface'in tüm fonksiyonlarını kendi sınıfınızda tanımlamanız gerekir. Biz de bu sebeple bu 4 fonksiyonu tanımladık. Eklentimiz IDE tarafından her çalıştırıldığında Execute metodu içindeki kodlar çalışır. Execute içinde istediğimiz kodu koşturabiliriz. Örneğin ikinci bir formumuz varsa onu gösterebiliriz, registry'e, veri tabanına bir şeyler yazabiliriz vs. 
Eklentimiz içinde bir de GetMenuText fonksiyonu tanımladık. Bunu da IOTAMenuWizard'ı kullandığımız için tanımlamak zorundaydık. Bu fonksiyonla menü elemanımızın ekranda görünen başlığını belirtmiş olduk.  
Eklentimizi IDE'ye tanıtmak için Register prosedürü kullanılır (Yeni bir bileşen yazılırken kullanıldığı gibi). Eklenti "install" aşamasında bu prosedür çalışır. Biz burada yeni tanımladığımız sınıfımızı IDE'ye tanıtmış olduk. 

Bu aşamaya kadar yapılanları aşağıdaki linkten indirebilirsiniz:
https://yadi.sk/d/CZupHDxX3PokHf



Delphi ana menüsünde istediğimiz bir yere MenuItem eklemek:

IOTAMenuWizard kullanarak Delphi menüsüne eleman eklediğimizde; eklenen eleman her zaman Help menüsü altına eklenir. Ana menünün faklı bir yerine TMenuItem eklemek istersek işler biraz değişir. Bu durumda yukarıda bahsi geçen NTA interface grubundan INTAServices'i kullanmamız gerekir. Ayrıca IOTAMenuWizard ile menüye ekleme yaptığımızda elemanın yok edilmesi(Free) otomatik olarak yapılıyordu. Şimdi ise kendi eklediğimiz menu elemanını kendimiz silmek durumundayız. 

Örnek olarak, View -> Tool Windows içine menü elemanımızı eklemek istersek:
Kodumuzda önce main menüdeki View'a ulaşmamız, sonrasında onun altındaki Tool Windows'a ulaşmamız gerekiyor. Onun altına da kendi menü elamanımızı ekleyeceğimiz için değişkenlerimiz şunlar olacak;
var
  NTAServices: INTAServices;
  ViewMenu: TMenuItem;
  ToolWindowsMenu: TMenuItem;
  DelphiCanMenuItem: TMenuItem;

INTAServices'i bir değişken(NTAServices) üzerinde kullanabilmemiz için aşağıdaki gibi kontrollü değişken atama komutunu kullanabiliriz. Buradan True değer döndükten sonra NTAServices.MainMenu ile Delphi'nin ana menüsüne ulaşmış oluruz. Sonrasında View ve onun içeriklerine ulaşmak için:
if Supports(BorlandIDEServices, INTAServices, NTAServices) then
begin
  ViewMenu := NTAServices.MainMenu.Items.Find('View');
  if ViewMenu <> nil then
  begin
    ToolWindowsMenu := ViewMenu.Find('Tool Windows');
    if ToolWindowsMenu <> nil then
    begin
      //
    end;
  end;
end;

Artık Tool Windows menüsüne ulaştığımıza göre buna kendi menu elemanımızı ekleyebiliriz. Bir TMenuItem oluşturup buna bir başlık verip, ToolWindowsMenu içerisine insert edeceğiz:
if ToolWindowsMenu <> nil then
begin
 DelphiCanMenuItem := TMenuItem.Create(ToolWindowsMenu);
 DelphiCanMenuItem.Caption := 'DelphiCan';
 ToolWindowsMenu.Insert(ToolWindowsMenu.Count, DelphiCanMenuItem);
end;

Projemizi bu haliyle derleyip, tekrar "install" edersek Delphi ana menüsünde View -> Tool Windows -> DelhpiCan menü seçeneğinin oluştuğunu görürüz. Tabi ki bu menü seçildiğinde hiç bir şey yapmayacaktır Smile Çünkü menü elamanımızın OnClick olayına her hangi bir atama yapmadık. Bunu yapmak için kod bloğumuz içinde  yeni bir sınıf oluşturup tanımlamasını şu şekilde yapalım:
TMethodContainer = class(TObject)
 procedure OnMenuClick(Sender: TObject);
end;

implementation

procedure TMethodContainer.OnMenuClick(Sender: TObject);
begin
   
end;

Artık bir TMethodContainer değişkeni tanımlayıp bunun OnMenuClick'ini menu elemanımızın OnClick'ine atayabiliriz. Ayrıca menümüze kısayol tuş kombinasyonu ve resim de ekleyebiliriz. Resim olarak Delphi IDEsinin kendi içinde bulunan resimlerden kullandım. Aslında tıpkı normal VCL uygulamalarında olduğu gibi istediğimiz resmi de atayabiliriz ama şimdilik konuyu daha fazla dağıtmayalım. Kodumuzun son halini komple yazmak gerekirse;

unit DelpicanMenu;

interface

uses ToolsAPI, System.SysUtils, Vcl.Menus;

type
 TMethodContainer = class(TObject)
   procedure OnMenuClick(Sender: TObject);
 end;

var
 NTAServices: INTAServices;
 ViewMenu: TMenuItem;
 ToolWindowsMenu: TMenuItem;
 DelphiCanMenuItem: TMenuItem;

implementation

uses Vcl.Dialogs;

procedure Initialize(Sender: TObject);
var
 aMethodContainer: TMethodContainer;
begin
 if Supports(BorlandIDEServices, INTAServices, NTAServices) then
 begin
   aMethodContainer := TMethodContainer.Create;
   try
     ViewMenu := NTAServices.MainMenu.Items.Find('View');
     if ViewMenu <> nil then
     begin
       ToolWindowsMenu := ViewMenu.Find('Tool Windows');
       if ToolWindowsMenu <> nil then
       begin
         DelphiCanMenuItem := TMenuItem.Create(ToolWindowsMenu);
         DelphiCanMenuItem.Caption := 'DelphiCan';
         DelphiCanMenuItem.ImageIndex := 16;
         DelphiCanMenuItem.OnClick := aMethodContainer.OnMenuClick;
         DelphiCanMenuItem.ShortCut := TextToShortCut('Ctrl+Shift+Alt+D');
         ToolWindowsMenu.Insert(ToolWindowsMenu.Count, DelphiCanMenuItem);
       end;
     end;
  finally
    aMethodContainer.Free;
  end;
 end;
end;

procedure Finalize(Sender: TObject);
begin
 if Assigned(DelphiCanMenuItem) then
   DelphiCanMenuItem.Free;
end;

{ TMethodContainer }

procedure TMethodContainer.OnMenuClick(Sender: TObject);
begin
 ShowMessage('Merhaba Delphicanlar!');
end;

initialization
 Initialize(nil);

finalization
 Finalize(nil);

end.

Projenin son halinin kaynak kodları:
https://yadi.sk/d/JhqOwRIy3PokNQ



Vakit buldukça bu konuda yeni şeyler paylaşmaya çalışacağım.
Az bilinen bir konuya ışık tuttunuz, teşekkürler @SimaWB hocam.
Ellerinize sağlık. TMethodContainer'ı Free etmeyi de unutmamak gerekir ?
(18-11-2017, Saat: 17:59)Tuğrul HELVACI Adlı Kullanıcıdan Alıntı: [ -> ]Ellerinize sağlık. TMethodContainer'ı Free etmeyi de unutmamak gerekir ?

Uyarı için teşekkürler. Konu anlatmak için parça parça yazınca gözden kaçabiliyor Smile