18-11-2017, Saat: 14:28
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
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:
İ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:
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:
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:
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;
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:
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:
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 Çü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:
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;
Projenin son halinin kaynak kodları:
https://yadi.sk/d/JhqOwRIy3PokNQ
Vakit buldukça bu konuda yeni şeyler paylaşmaya çalışacağım.
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
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:
İ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:
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:
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 Çü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.