unitswitcher/Source/CmpSwDialog.pas

570 lines
15 KiB
ObjectPascal

unit CmpSwDialog;
interface
uses
ActnList,
Classes,
ComCtrls,
Contnrs,
Controls,
ExtCtrls,
Graphics,
ImgList,
IniFiles,
Menus,
StdCtrls,
Windows,
BaseSwDialog,
BaseSwObjects,
CmpSwFilters;
type
TCmpSwStyleVisitor = class(TBaseSwStyleVisitor)
private
FImageList: TImageList;
FImageMap: TStringHash;
protected
procedure VisitItem(const AItem: TBaseSwItem); override;
function GetComponentPackage(const AClassName: String): String;
function LoadComponentImage(const APackageName, AClassName: String): Integer;
procedure ResizeBitmap(const ABitmap: Graphics.TBitmap; const AWidth, AHeight: Integer);
public
constructor Create(AImageList: TImageList);
destructor Destroy(); override;
end;
TfrmCmpSwDialog = class(TfrmBaseSwDialog)
pmnItemsSep1: TMenuItem;
pmnItemsFilters: TMenuItem;
pmnItemsFilterSelected: TMenuItem;
actFilterSelected: TAction;
pnlFilters: TPanel;
gbFilters: TGroupBox;
btnMoreFilters: TButton;
pmnMoreFilters: TPopupMenu;
actSortByName: TAction;
actSortByType: TAction;
pmnItemsSortByName: TMenuItem;
pmnItemsSortByType: TMenuItem;
pmnItemsSep2: TMenuItem;
btnConfiguration: TButton;
procedure btnMoreFiltersClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure SortExecute(Sender: TObject);
procedure btnConfigurationClick(Sender: TObject);
private
FClassFilteredList: TBaseSwItemList;
FClassFilter: TCmpSwComponentClassFilter;
FFilterCheckBoxes: TObjectList;
FOtherGroup: TCmpSwFilterGroup;
protected
function InternalExecute(): TBaseSwItemList; override;
function CreateStyleVisitor(): TBaseSwStyleVisitor; override;
function GetBaseItemList(): TBaseSwItemList; override;
function AllowEmptyResult(): Boolean; override;
function ColorsEnabled(): Boolean; override;
function Wildchars(): Boolean; override;
procedure LoadSettings(); override;
procedure SaveSettings(); override;
procedure SettingsChanged(); override;
procedure DrawItemText(ACanvas: TCanvas; AItem: TBaseSwItem; ARect: TRect; AState: TOwnerDrawState); override;
procedure UpdateClassFilter();
procedure SortList();
procedure BuildFilterCheckboxes();
function CreateFilterMenuItem(AParent: TMenuItem; AGroup: TCmpSwFilterGroup; AItemIndex: Integer): TMenuItem;
procedure FilterCheckBoxClick(Sender: TObject);
procedure FilterMenuItemClick(Sender: TObject);
property ClassFilter: TCmpSwComponentClassFilter read FClassFilter;
property ClassFilteredList: TBaseSwItemList read FClassFilteredList;
end;
implementation
uses
SysUtils,
ToolsAPI,
CmpSwConfiguration,
CmpSwObjects,
CmpSwSettings;
{$R *.dfm}
{ TCmpSwStyleVisitor }
constructor TCmpSwStyleVisitor.Create(AImageList: TImageList);
begin
inherited Create();
FImageList := AImageList;
FImageMap := TStringHash.Create();
end;
destructor TCmpSwStyleVisitor.Destroy();
begin
FreeAndNil(FImageMap);
inherited;
end;
procedure TCmpSwStyleVisitor.VisitItem(const AItem: TBaseSwItem);
var
component: TCmpSwComponent;
package: String;
begin
inherited;
component := (AItem as TCmpSwComponent);
ImageIndex := FImageMap.ValueOf(component.ComponentClass);
if ImageIndex = -1 then
begin
package := GetComponentPackage(component.ComponentClass);
if Length(package) > 0 then
ImageIndex := LoadComponentImage(package, component.ComponentClass);
end;
if ImageIndex = -2 then
ImageIndex := -1;
end;
function TCmpSwStyleVisitor.GetComponentPackage(const AClassName: String): String;
var
packageServices: IOTAPackageServices;
packageIndex: Integer;
componentIndex: Integer;
begin
Result := '';
packageServices := (BorlandIDEServices as IOTAPackageServices);
for packageIndex := Pred(packageServices.PackageCount) downto 0 do
begin
for componentIndex := Pred(packageServices.ComponentCount[packageIndex]) downto 0 do
begin
if SameText(packageServices.ComponentNames[packageIndex, componentIndex],
AClassName) then
begin
Result := packageServices.PackageNames[packageIndex];
{ Delphi 7 doesn't add the .bpl extension, BDS 2006 does, let's just
take the safe route and check }
if not SameText(ExtractFileExt(Result), '.bpl') then
Result := Result + '.bpl';
Break;
end;
end;
end;
end;
function TCmpSwStyleVisitor.LoadComponentImage(const APackageName, AClassName: String): Integer;
var
packageHandle: THandle;
bitmapHandle: THandle;
bitmap: Graphics.TBitmap;
transpColor: TColor;
begin
Result := -1;
packageHandle := LoadLibrary(PChar(APackageName));
if packageHandle <> 0 then
try
{ BDS includes 16x16 versions of the component bitmaps, try those first }
bitmapHandle := LoadBitmap(packageHandle, PChar(AClassName + '16'));
if bitmapHandle = 0 then
bitmapHandle := LoadBitmap(packageHandle, PChar(AClassName));
if bitmapHandle <> 0 then
begin
bitmap := Graphics.TBitmap.Create();
try
bitmap.Handle := bitmapHandle;
transpColor := bitmap.TransparentColor;
if (bitmap.Width <> FImageList.Width) or
(bitmap.Height <> FImageList.Height) then
begin
ResizeBitmap(bitmap, FImageList.Width, FImageList.Height);
end;
Result := FImageList.AddMasked(bitmap, transpColor);
finally
FreeAndNil(bitmap);
end;
end;
finally
FreeLibrary(packageHandle);
end;
if Result = -1 then
Result := -2;
FImageMap.Add(AClassName, Result);
end;
procedure TCmpSwStyleVisitor.ResizeBitmap(const ABitmap: Graphics.TBitmap;
const AWidth, AHeight: Integer);
var
tempBitmap: Graphics.TBitmap;
begin
tempBitmap := Graphics.TBitmap.Create();
try
tempBitmap.PixelFormat := pf24bit;
tempBitmap.Width := AWidth;
tempBitmap.Height := AHeight;
tempBitmap.Canvas.CopyRect(Rect(0, 0, AWidth, AHeight), ABitmap.Canvas,
Rect(0, 0, ABitmap.Width, ABitmap.Height));
ABitmap.Assign(tempBitmap);
finally
FreeAndNil(tempBitmap);
end;
end;
{ TfrmCmpSwDialog }
function TfrmCmpSwDialog.InternalExecute(): TBaseSwItemList;
begin
FClassFilteredList := TBaseSwItemList.Create();
FClassFilter := TCmpSwComponentClassFilter.Create();
FFilterCheckBoxes := TObjectList.Create();
FOtherGroup := TCmpSwFilterGroup.Create(nil);
try
ClassFilter.Groups := Settings.FilterGroups;
FOtherGroup.Name := 'Other';
FOtherGroup.Enabled := False;
FOtherGroup.Visible := False;
Result := inherited InternalExecute();
finally
FreeAndNil(FOtherGroup);
FreeAndNil(FFilterCheckBoxes);
FreeAndNil(FClassFilter);
FreeAndNil(FClassFilteredList);
end;
end;
procedure TfrmCmpSwDialog.FormShow(Sender: TObject);
begin
UpdateClassFilter();
inherited;
end;
function TfrmCmpSwDialog.CreateStyleVisitor(): TBaseSwStyleVisitor;
begin
Result := TCmpSwStyleVisitor.Create(ilsTypes);
end;
procedure TfrmCmpSwDialog.DrawItemText(ACanvas: TCanvas; AItem: TBaseSwItem; ARect: TRect; AState: TOwnerDrawState);
var
text: String;
textRect: TRect;
begin
inherited;
{ Calculate item text rectangle }
text := GetItemDisplayName(AItem);
textRect := ARect;
DrawText(ACanvas.Handle, PChar(text), Length(text), textRect, DT_SINGLELINE or
DT_LEFT or DT_VCENTER or DT_END_ELLIPSIS or DT_CALCRECT);
textRect.Left := textRect.Right;
textRect.Right := ARect.Right - 2;
{ Draw component class text }
if not (odSelected in AState) then
ACanvas.Font.Color := clGrayText;
text := (AItem as TCmpSwComponent).ComponentClass;
DrawText(ACanvas.Handle, PChar(text), Length(text), textRect, DT_SINGLELINE or
DT_RIGHT or DT_VCENTER or DT_END_ELLIPSIS);
end;
procedure TfrmCmpSwDialog.UpdateClassFilter();
var
groupIndex: Integer;
itemIndex: Integer;
begin
{ Update / extend the menu }
itemIndex := 0;
for groupIndex := 0 to Pred(ClassFilter.Groups.Count) do
begin
if ClassFilter.Groups[groupIndex].Visible then
begin
CreateFilterMenuItem(pmnItemsFilters, ClassFilter.Groups[groupIndex], itemIndex);
Inc(itemIndex);
end;
end;
CreateFilterMenuItem(pmnItemsFilters, FOtherGroup, itemIndex);
Inc(itemIndex);
{ Remove excess menu items }
for groupIndex := Pred(pmnItemsFilters.Count) downto itemIndex do
pmnItemsFilters.Delete(groupIndex);
itemIndex := 0;
for groupIndex := 0 to Pred(ClassFilter.Groups.Count) do
begin
if not ClassFilter.Groups[groupIndex].Visible then
begin
CreateFilterMenuItem(pmnMoreFilters.Items, ClassFilter.Groups[groupIndex], itemIndex);
Inc(itemIndex);
end;
end;
CreateFilterMenuItem(pmnMoreFilters.Items, FOtherGroup, itemIndex);
Inc(itemIndex);
for groupIndex := Pred(pmnMoreFilters.Items.Count) downto itemIndex do
pmnMoreFilters.Items.Delete(groupIndex);
BuildFilterCheckboxes();
btnMoreFilters.Visible := (pmnMoreFilters.Items.Count > 0);
ClassFilteredList.Clone(ItemList);
ClassFilter.FilterUnmatched := FOtherGroup.Enabled;
ClassFilter.FilterList(ClassFilteredList);
SortList();
end;
function TfrmCmpSwDialog.GetBaseItemList(): TBaseSwItemList;
begin
Result := ClassFilteredList;
end;
procedure TfrmCmpSwDialog.BuildFilterCheckboxes();
var
checkBox: TCheckBox;
checkBoxTop: Integer;
childIndex: Integer;
group: TCmpSwFilterGroup;
groupIndex: Integer;
begin
for childIndex := Pred(gbFilters.ControlCount) downto 0 do
if gbFilters.Controls[childIndex] is TCheckBox then
gbFilters.Controls[childIndex].Free;
// #ToDo3 (MvR) 11-12-2007: get rid of a few "magic" numbers
checkBoxTop := 16;
for groupIndex := 0 to Pred(ClassFilter.Groups.Count) do
begin
group := ClassFilter.Groups[groupIndex];
if group.Visible then
begin
checkBox := TCheckBox.Create(Self);
checkBox.Top := checkBoxTop;
checkBox.Left := 12;
checkBox.Caption := StringReplace(group.Name, '&', '&&', [rfReplaceAll]);
checkBox.Checked := not group.Enabled;
checkBox.Tag := Integer(group);
checkBox.OnClick := FilterCheckBoxClick;
checkBox.Width := gbFilters.ClientWidth - 24;
checkBox.Anchors := [akLeft, akTop, akRight];
checkBox.Parent := gbFilters;
Inc(checkBoxTop, 20);
end;
end;
btnMoreFilters.BringToFront();
pnlFilters.Height := (2 * pnlFilters.BorderWidth) + checkBoxTop + 11;
pnlButtons.Top := pnlFilters.Top + pnlFilters.Height;
sbStatus.Top := MaxInt;
end;
function TfrmCmpSwDialog.CreateFilterMenuItem(AParent: TMenuItem; AGroup: TCmpSwFilterGroup; AItemIndex: Integer): TMenuItem;
begin
if (AItemIndex = -1) or (AItemIndex >= AParent.Count) then
begin
Result := TMenuItem.Create(Self);
Result.OnClick := FilterMenuItemClick;
AParent.Add(Result);
end else
Result := AParent[AItemIndex];
Result.Caption := StringReplace(AGroup.Name, '&', '&&', [rfReplaceAll]);
Result.Checked := not AGroup.Enabled;
Result.Tag := Integer(AGroup);
end;
procedure TfrmCmpSwDialog.FilterCheckBoxClick(Sender: TObject);
var
checkBox: TCheckBox;
group: TCmpSwFilterGroup;
begin
checkBox := (Sender as TCheckBox);
group := TCmpSwFilterGroup(checkBox.Tag);
if checkBox.Checked = group.Enabled then
begin
group.Enabled := not checkBox.Checked;
UpdateClassFilter();
UpdateSubFilters();
end;
end;
procedure TfrmCmpSwDialog.FilterMenuItemClick(Sender: TObject);
var
menuItem: TMenuItem;
group: TCmpSwFilterGroup;
begin
menuItem := (Sender as TMenuItem);
group := TCmpSwFilterGroup(menuItem.Tag);
group.Enabled := menuItem.Checked;
menuItem.Checked := not menuItem.Checked;
UpdateClassFilter();
UpdateSubFilters();
end;
procedure TfrmCmpSwDialog.btnMoreFiltersClick(Sender: TObject);
var
buttonPos: TPoint;
begin
buttonPos := btnMoreFilters.ClientToScreen(Point(btnMoreFilters.Width, 0));
pmnMoreFilters.Popup(buttonPos.X, buttonPos.Y);
end;
procedure TfrmCmpSwDialog.LoadSettings();
begin
Self.ClientWidth := Settings.Dialog.Width;
Self.ClientHeight := Settings.Dialog.Height;
MRUList.Assign(Settings.Dialog.MRUList);
inherited LoadSettings();
end;
procedure TfrmCmpSwDialog.SaveSettings();
begin
// #ToDo2 (MvR) 12-12-2007: save 'sort by ...'
Settings.Dialog.Width := Self.ClientWidth;
Settings.Dialog.Height := Self.ClientHeight;
Settings.Dialog.MRUList.Assign(MRUList);
Settings.Save();
inherited SaveSettings();
end;
function TfrmCmpSwDialog.AllowEmptyResult(): Boolean;
begin
Result := Settings.Filter.AllowEmptyResult;
end;
function TfrmCmpSwDialog.ColorsEnabled(): Boolean;
begin
Result := inherited ColorsEnabled();
end;
function TfrmCmpSwDialog.Wildchars(): Boolean;
begin
Result := Settings.Filter.Wildchars;
end;
procedure TfrmCmpSwDialog.SortExecute(Sender: TObject);
begin
(Sender as TAction).Checked := True;
SortList();
UpdateSubFilters();
end;
function SortByName(Item1, Item2: Pointer): Integer;
begin
Result := CompareText(TCmpSwComponent(Item1).Name, TCmpSwComponent(Item2).Name);
end;
function SortByType(Item1, Item2: Pointer): Integer;
begin
Result := CompareText(TCmpSwComponent(Item1).ComponentClass, TCmpSwComponent(Item2).ComponentClass);
end;
procedure TfrmCmpSwDialog.SortList();
begin
if actSortByType.Checked then
ClassFilteredList.Sort(SortByType)
else
ClassFilteredList.Sort(SortByName);
end;
procedure TfrmCmpSwDialog.btnConfigurationClick(Sender: TObject);
begin
if TfrmCmpSwConfiguration.Execute() then
begin
SettingsChanged();
UpdateClassFilter();
UpdateSubFilters();
end;
end;
procedure TfrmCmpSwDialog.SettingsChanged;
begin
inherited;
end;
end.