{ /*************************************************************************** ColorPalette.pas ***************************************************************************/ ***************************************************************************** * * * See the file COPYING.modifiedLGPL, included in this distribution, * * for details about the copyright. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * ***************************************************************************** Author: Tom Gregorovic (_tom_@centrum.cz) Abstract: Color palette grid with custom palette support. The OnColorPick event is fired when user picks a color. The LoadPalette procedure loads custom palette. Custom palette example: $COLS 8 # sets count of palette grid columns 0,0,0 # inserts color r,g,b 255,255,255 $NONE # inserts empty palette grid cell $BLENDWB 128,128,128 3 # creates color gradient white -> color -> black with specified steps } unit ColorPalette; {$ifdef fpc} {$mode objfpc}{$H+} {$endif} interface uses Classes, SysUtils, LResources, Controls, Forms, Graphics, Math, LCLType; type TColorMouseEvent = procedure (Sender: TObject; AColor: TColor; Shift: TShiftState) of Object; { TCustomColorPalette } TCustomColorPalette = class(TGraphicControl) private FButtonHeight: Integer; FButtonWidth: Integer; FCols: Integer; FOnColorMouseMove: TColorMouseEvent; FOnColorPick: TColorMouseEvent; FRows: Integer; FColors: TList; MX, MY: integer; function GetColors(Index: Integer): TColor; procedure SetButtonHeight(const AValue: Integer); procedure SetButtonWidth(const AValue: Integer); procedure SetColors(Index: Integer; const AValue: TColor); procedure UpdateSize; protected procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X, Y:Integer); override; procedure MouseMove(Shift:TShiftState; X, Y:Integer); override; procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X, Y:Integer); override; procedure ColorPick(AColor: TColor; Shift: TShiftState); dynamic; procedure ColorMouseMove(AColor: TColor; Shift: TShiftState); dynamic; public PickedColor: TColor; PickShift: TShiftState; constructor Create(TheOwner: TComponent); override; destructor Destroy; override; procedure Paint; override; public procedure LoadPalette(const FileName: String); property ButtonWidth: Integer read FButtonWidth write SetButtonWidth; property ButtonHeight: Integer read FButtonHeight write SetButtonHeight; property Colors[Index: Integer]: TColor read GetColors write SetColors; property OnColorPick: TColorMouseEvent read FOnColorPick write FOnColorPick; property OnColorMouseMove: TColorMouseEvent read FOnColorMouseMove write FOnColorMouseMove; property Height stored False; property Width stored False; end; TColorPalette = class(TCustomColorPalette) published property Align; property Anchors; property BorderSpacing; property ButtonWidth; property ButtonHeight; property Color; property Constraints; property DragCursor; property DragKind; property DragMode; property Enabled; property Hint; property ParentColor; property ParentShowHint; property PopupMenu; property ShowHint; property Visible; property OnChangeBounds; property OnClick; property OnColorMouseMove; property OnColorPick; property OnDblClick; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnMouseEnter; property OnMouseLeave; property OnResize; end; procedure Register; implementation procedure Register; begin RegisterComponents('Misc', [TColorPalette]); end; { TCustomColorPalette } procedure TCustomColorPalette.SetButtonHeight(const AValue: Integer); begin if FButtonHeight = AValue then Exit; FButtonHeight := AValue; if FButtonHeight < 1 then FButtonHeight := 1; UpdateSize; end; function TCustomColorPalette.GetColors(Index: Integer): TColor; begin Result := TColor(FColors.Items[Index]); end; procedure TCustomColorPalette.SetButtonWidth(const AValue: Integer); begin if FButtonWidth = AValue then Exit; FButtonWidth := AValue; if FButtonWidth < 1 then FButtonWidth := 1; UpdateSize; end; procedure TCustomColorPalette.SetColors(Index: Integer; const AValue: TColor); begin FColors.Items[Index] := Pointer(AValue); end; procedure TCustomColorPalette.UpdateSize; begin if (FCols = 0) or (FColors.Count = 0) then FRows := 0 else FRows := Ceil(FColors.Count / FCols); SetBounds(Left, Top, FCols * FButtonWidth + 1, FRows * FButtonHeight + 1) end; procedure TCustomColorPalette.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited; MX := X; MY := Y; X := X div FButtonWidth; Y := Y div FButtonHeight; if X + Y * FCols < 0 then Exit; if X + Y * FCols < FColors.Count then begin PickedColor := TColor(FColors.Items[X + Y * FCols]); PickShift := Shift; end; end; procedure TCustomColorPalette.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if (PickedColor <> clNone) and (MX = X) and (MY = Y) then ColorPick(PickedColor, PickShift); inherited; end; procedure TCustomColorPalette.MouseMove(Shift: TShiftState; X, Y: Integer); var C: TColor; begin inherited; X := X div FButtonWidth; Y := Y div FButtonHeight; if X + Y * FCols < 0 then Exit; if X + Y * FCols < FColors.Count then begin C := TColor(FColors.Items[X + Y * FCols]); if C <> clNone then ColorMouseMove(C, Shift); end; end; procedure TCustomColorPalette.ColorPick(AColor: TColor; Shift: TShiftState); begin if Assigned(FOnColorPick) then FOnColorPick(Self, AColor, Shift); end; procedure TCustomColorPalette.ColorMouseMove(AColor: TColor; Shift: TShiftState); begin if Assigned(FOnColorMouseMove) then FOnColorMouseMove(Self, AColor, Shift); end; constructor TCustomColorPalette.Create(TheOwner: TComponent); begin inherited; FColors := TList.Create; FButtonWidth := 12; FButtonHeight := 12; ControlStyle := ControlStyle + [csFixedWidth, csFixedHeight]; FCols := 8; FColors.Add(Pointer(clBlack)); FColors.Add(Pointer(clGray)); FColors.Add(Pointer(clMaroon)); FColors.Add(Pointer(clOlive)); FColors.Add(Pointer(clGreen)); FColors.Add(Pointer(clTeal)); FColors.Add(Pointer(clNavy)); FColors.Add(Pointer(clPurple)); FColors.Add(Pointer(clWhite)); FColors.Add(Pointer(clSilver)); FColors.Add(Pointer(clRed)); FColors.Add(Pointer(clYellow)); FColors.Add(Pointer(clLime)); FColors.Add(Pointer(clAqua)); FColors.Add(Pointer(clBlue)); FColors.Add(Pointer(clFuchsia)); UpdateSize; end; destructor TCustomColorPalette.Destroy; begin FColors.Free; inherited; end; procedure TCustomColorPalette.Paint; var I, X, Y: Integer; begin Canvas.Pen.Color := clBlack; for I := 0 to Pred(FColors.Count) do begin Y := I div FCols; X := I mod FCols; if TColor(FColors.Items[I]) <> clNone then begin Canvas.Brush.Color := TColor(FColors.Items[I]); Canvas.Rectangle(Bounds(X * FButtonWidth, Y * FButtonHeight, FButtonWidth, FButtonHeight)); end; end; end; procedure TCustomColorPalette.LoadPalette(const FileName: String); var F: TextFile; Line: String; C: TColor; function ParseColor(var S: String): TColor; var R, G, B: Integer; I: Integer; begin R := StrToIntDef(Copy(S, 1, Pos(',', S) - 1), 0); Delete(S, 1, Pos(',', S)); G := StrToIntDef(Copy(S, 1, Pos(',', S) - 1), 0); Delete(S, 1, Pos(',', S)); S := TrimLeft(S); I := 1; while (I <= Length(S)) and (S[I] in ['0'..'9']) do Inc(I); B := StrToIntDef(Copy(S, 1, Pred(I)), 0); Delete(S, 1, Pred(I)); Result := RGBToColor(Max(0, Min(R, 255)), Max(0, Min(G, 255)), Max(0, Min(B, 255))); end; procedure BlendWBColor(Color: TColor; Steps: Integer); var I: Integer; R, G, B, NR, NG, NB: Byte; begin RedGreenBlue(Color, R, G, B); for I := 1 to Steps do begin NR := Round((R * I + 255 * (Steps + 1 - I)) / (Steps + 1)); NG := Round((G * I + 255 * (Steps + 1 - I)) / (Steps + 1)); NB := Round((B * I + 255 * (Steps + 1 - I)) / (Steps + 1)); FColors.Add(Pointer(RGBToColor(NR, NG, NB))); end; FColors.Add(Pointer(Color)); for I := Steps downto 1 do begin NR := Round(R * I / (Steps + 1)); NG := Round(G * I / (Steps + 1)); NB := Round(B * I / (Steps + 1)); FColors.Add(Pointer(RGBToColor(NR, NG, NB))); end; end; begin if not FileExists(FileName) then raise Exception.Create(Format('[TCustomColorPalette.LoadPalette] File not found: %s', [FileName])); AssignFile(F, FileName); try Reset(F); FColors.Clear; FCols := 1; while not EOF(F) do begin ReadLn(F, Line); Line := Trim(Line); if Length(Line) < 2 then Continue; if Line[1] = '#' then Continue; if Line[1] = '$' then begin if Copy(Line, 2, 4) = 'NONE' then FColors.Add(Pointer(clNone)); if Copy(Line, 2, 4) = 'COLS' then FCols := StrToIntDef(Copy(Line, 6, MaxInt), 8); if Copy(Line, 2, 7) = 'BLENDWB' then begin Delete(Line, 1, 8); C := ParseColor(Line); BlendWBColor(C, StrToInt(Line)); end; end else if Pos(',', Line) > 0 then FColors.Add(Pointer(ParseColor(Line))); end; finally Close(F); end; UpdateSize; end; initialization {$I colorpalette.lrs} end.