3 // purpose: KOL control sizercontrol and design grid
\r
4 // author: © 2004, Thaddy de Koning
\r
5 // Remarks: Tnx in part to Marco Cantu for the sizer idea in DDH3
\r
6 // copyrighted freeware.
\r
11 Windows, Messages, Kol;
\r
14 // Size and move commands for SysCommand
\r
19 SZ_TOPRIGHT = $F005;
\r
21 SZ_BOTTOMLEFT = $F007;
\r
22 SZ_BOTTOMRIGHT = $F008;
\r
33 PSizerdata=^ TSizerdata;
\r
34 TSizerdata= object(Tobj)
\r
36 FPosInfo: array [0..7] of TPosInfo;
\r
38 procedure Dopaint(sender:pControl;DC:HDC);
\r
44 THack = object(Tcontrol)
\r
47 PDesigner=^TDesigner;
\r
48 TDesigner=object(TStrlistEx)
\r
55 FOnControlChange: TonEvent;
\r
56 FOnDblClick:TOnEvent;
\r
57 FOnMouseDown:TOnMouse;
\r
60 procedure setactive(const Value: boolean);
\r
61 function PrepareClassname(aControl: PControl): String;
\r
62 function UniqueName(aName: String): String;
\r
63 procedure SetCurrent(const Value: pControl);
\r
64 procedure InternalControlChange(sender:pObj);
\r
65 procedure Setspacing(Space:cardinal = 8);
\r
67 procedure init;virtual;
\r
68 procedure DoKeyUp( Sender: PControl; var Key: Longint; Shift: DWORD );
\r
70 destructor destroy;virtual;
\r
71 procedure Connect(aName: String; aControl: pControl);
\r
72 procedure DisConnect(aControl: pControl);
\r
73 procedure Paintgrid(sender:pControl;DC:HDC);
\r
74 property Spacing:cardinal read FSpacing write setspacing;
\r
75 property Active:boolean read FActive write setactive;
\r
76 property Action:integer read FAction write Faction;
\r
77 property Current:pControl read FCurrent write SetCurrent;
\r
78 property OnControlChange:TOnEvent Read FOnControlchange write FOnControlChange;
\r
79 property OnDblClick:TonEvent read fOnDblClick write FOnDblClick;
\r
80 property OnMouseDown:TOnMouse read FOnMouseDown write FOnMouseDown;
\r
83 function NewSizerControl(AControl: PControl;aDesigner:PDesigner):PControl;
\r
84 function NewDesigner(aOwner:pControl):pDesigner;
\r
86 LocalDesigner:PDesigner;
\r
91 function DesignHandlerProc(Sender: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
\r
92 var MouseData:TMouseEventData;
\r
98 if Localdesigner.FOwner<> sender then LocalDesigner.current:=sender;
\r
101 if assigned(Localdesigner.OnMousedown) then
\r
102 // Borrowed from KOL.pas
\r
103 // enables us to pass on KOL mouse events back to the designer
\r
104 // without having to connect to true KOL eventproperties.
\r
107 Shift := Msg.wParam;
\r
108 if GetKeyState(VK_MENU) < 0 then
\r
109 Shift := Shift or MK_ALT;
\r
110 X := LoWord(Msg.lParam);
\r
111 Y := HiWord(Msg.lParam);
\r
113 StopHandling := true;
\r
114 Rslt := 0; // needed ?
\r
115 LocalDesigner.OnMousedown(sender,Mousedata);
\r
124 // TSizerControl methods
\r
125 function WndProcSizer( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
\r
132 Data:=PSizerdata(sender.CustomObj);
\r
134 with sender^, Data^ do
\r
136 case msg.message of
\r
139 Pt := MakePoint(loword(Msg.lparam), hiword(Msg.lparam));
\r
140 Pt := Screen2Client (Pt);
\r
143 if PtInRect (FPosInfo [I].rect, Pt) then
\r
145 // The value of rslt is passed on and makes
\r
146 // the system select the correct cursor
\r
147 // without us having to do anything more.
\r
148 Rslt := FPosInfo [I].pos;
\r
149 Direction:=FPosInfo[i].direction;
\r
157 InflateRect (R, -2, -2);
\r
158 Fcontrol.BoundsRect := R;
\r
159 FPosInfo [0].rect := MakeRect (0, 0, 5, 5);
\r
160 FPosInfo [1].rect := MakeRect (Width div 2 - 3, 0,
\r
161 Width div 2 + 2, 5);
\r
162 FPosInfo [2].rect := MakeRect (Width - 5, 0, Width, 5);
\r
163 FPosInfo [3].rect := MakeRect (Width - 5, Height div 2 - 3,
\r
164 Width, Height div 2 + 2);
\r
165 FPosInfo [4].rect := MakeRect (Width - 5, Height - 5,
\r
167 FPosInfo [5].rect := MakeRect (Width div 2 - 3, Height - 5,
\r
168 Width div 2 + 2, Height);
\r
169 FPosInfo [6].rect := MakeRect (0, Height - 5, 5, Height);
\r
170 FPosInfo [7].rect := MakeRect (0, Height div 2 - 3,
\r
171 5, Height div 2 + 2);
\r
174 Perform (wm_SysCommand, Direction, 0);
\r
176 Perform (wm_SysCommand, sz_move, 0);
\r
180 InflateRect (R, -2, -2);
\r
181 Fcontrol.Invalidate;
\r
182 Fcontrol.BoundsRect := R;
\r
190 function NewSizerControl(AControl: PControl;aDesigner:PDesigner):PControl;
\r
196 Result:=NewPaintBox(aControl);
\r
197 Result.ExStyle :=Result.ExStyle or ws_ex_transparent;
\r
198 Result.OnKeyUp:=aDesigner.DoKeyUp;
\r
199 if aDesigner.fowner<>aControl then
\r
200 With result^, Data^ do
\r
202 FControl := AControl;
\r
203 // set the size and position
\r
204 R := aControl.BoundsRect;
\r
205 InflateRect (R, 2, 2);
\r
208 Parent := aControl.Parent;
\r
209 // create the list of positions
\r
210 FPosInfo [0].pos := htTopLeft;
\r
211 FPosInfo [0].direction := sz_TopLeft;
\r
212 FPosInfo [1].pos := htTop;
\r
213 FPosInfo [1].direction := sz_Top;
\r
214 FPosInfo [2].pos := htTopRight;
\r
215 FPosInfo [2].direction := sz_TopRight;
\r
216 FPosInfo [3].pos := htRight;
\r
217 FPosInfo [3].direction := sz_Right;
\r
218 FPosInfo [4].pos := htBottomRight;
\r
219 FPosInfo [4].direction := sz_BottomRight;
\r
220 FPosInfo [5].pos := htBottom;
\r
221 FPosInfo [5].direction := sz_Bottom;
\r
222 FPosInfo [6].pos := htBottomLeft;
\r
223 FPosInfo [6].direction := sz_BottomLeft;
\r
224 FPosInfo [7].pos := htLeft;
\r
225 FPosInfo [7].direction := sz_Left;
\r
228 AttachProc(WndProcSizer);
\r
234 procedure TSizerData.DoPaint(sender:pControl;DC:HDC);
\r
238 // I simply use the current pen and brush
\r
239 with pSizerdata(sender.Customobj)^ do
\r
241 Rectangle(dc,FPosInfo [I].rect.Left, FPosInfo [I].rect.Top,
\r
242 FPosInfo [I].rect.Right, FPosInfo [I].rect.Bottom);
\r
245 procedure TDesigner.init;
\r
251 procedure TDesigner.Paintgrid(sender: pControl; DC: HDC);
\r
257 sender.canvas.FillRect(sender.canvas.cliprect);
\r
258 if Assigned(FOldPaint) then FOldPaint(sender,DC);
\r
261 MoveToEx(Dc,i, j,nil);
\r
262 LineTo(Dc,i + 1,j);
\r
264 until i > sender.ClientWidth;
\r
267 until j > sender.Clientheight;
\r
270 procedure TDesigner.Setspacing(Space: cardinal);
\r
277 function NewDesigner(aOwner:pControl):pDesigner;
\r
279 if Assigned(LocalDesigner) then
\r
281 result:=LocalDesigner;
\r
285 New(Result,Create);
\r
289 Connect('',Fowner);
\r
290 FOldPaint:=Fowner.OnPaint;
\r
291 LocalDesigner:=Result;
\r
292 //Result.Current:=aOwner;
\r
297 destructor TDesigner.destroy;
\r
300 FOwner.OnPaint:=FOldPaint;
\r
304 //Note: Make shure that whatever happens, all pointers are nil or valid!
\r
305 // Took a long time to debug spurious crashes.
\r
306 // So this is not excessively safe.
\r
307 procedure TDesigner.setactive(const Value: boolean);
\r
314 FOwner.OnPaint:=Paintgrid;
\r
317 if assigned(Fcurrent) then
\r
318 Fsizer:=NewSizerControl(Fcurrent,@self);
\r
319 for i:=0 to count -1 do
\r
320 if not Pcontrol(Objects[i]).IsprocAttached(DesignHandlerProc) then
\r
321 PControl(Objects[i]).AttachProc(DesignHandlerProc);
\r
327 for i:=0 to count -1 do
\r
328 PControl(Objects[i]).DetachProc(DesignHandlerProc);
\r
329 if assigned(Fsizer) then
\r
335 FOwner.OnPaint:=FOldPaint;
\r
340 procedure TDesigner.Connect(aName: String; aControl: pControl);
\r
342 if (IndexofObj(aControl) = -1) then
\r
345 aname := prepareClassname(aControl);
\r
346 AddObject(uniquename(aName), Cardinal(aControl));
\r
347 InternalControlChange(aControl);
\r
348 setcurrent(aControl);
\r
352 procedure TDesigner.DisConnect(aControl: pControl);
\r
356 Index := IndexOfObj(aControl);
\r
359 Delete(IndexOfObj(aControl));
\r
361 InternalControlChange(nil);
\r
364 procedure TDesigner.SetCurrent(const Value: pControl);
\r
366 if assigned(Fsizer) then
\r
371 if value <> nil then
\r
374 if (FActive =true) and (Fcurrent<>nil) and (Fcurrent<>FOwner) then
\r
375 Fsizer:=Newsizercontrol(Value,@self);
\r
376 InternalControlChange(Value);
\r
380 procedure TDesigner.InternalControlChange(sender: pObj);
\r
383 if Assigned(OnControlChange)then
\r
384 FOnControlChange(sender);
\r
387 procedure TDesigner.DoKeyUp(Sender: PControl; var Key: Integer;
\r
391 procedure DeleteControl(Index:integer);
\r
396 C:=Pcontrol(Objects[index]);
\r
397 if C.ChildCount > 0 then
\r
398 for i := c.Childcount - 1 downto 0 do
\r
399 if c <> Fowner then Deletecontrol(i);
\r
408 if Key = VK_DELETE then
\r
410 i:=IndexOfObj(LocalDesigner.Current);
\r
414 internalControlchange(nil);
\r
415 postmessage(sender.Handle,WM_CLOSE,0,0);
\r
424 // Converts an object name to a Delphi compatible control name that
\r
425 // is unique for the designer, i.e 'Button' becomes 'Button1',
\r
426 // the next button becomes 'Button2', always unless the
\r
427 // control is already named by the user in which case the name is preserved
\r
428 // unless there are conficts. In that case the control is silently
\r
429 // renamed with a digit suffix without raising exceptions.
\r
430 // Deleted names are re-used.
\r
431 // It's not a beauty but it works.
\r
432 // (A severe case of programming 48 hours without sleep)
\r
434 function TDesigner.UniqueName(aName: String): String;
\r
439 // Strip obj_ prefix and all other prefix+underscores from
\r
440 // subclassname property: 'obj_BUTTON' becomes 'Button'
\r
441 T := Lowercase(aName);
\r
442 while T <> '' do aName := Parse(T, '_');
\r
448 // Add at least a 1 to the name if the last char
\r
450 if not (aName[length(aName)] in ['0'..'9']) then
\r
451 Result := Format('%s%d', [aName, 1]);
\r
454 I := IndexOf(Result);
\r
458 Result := Format('%s%d', [aName, J]);
\r
463 // This is probably not complete yet.
\r
464 function TDesigner.PrepareClassname(aControl: PControl): String;
\r
466 Result := aControl.subclassname;
\r
468 if subClassname = 'obj_STATIC' then
\r
470 // Only place where panel and label differ
\r
472 if pHack(aControl).fSizeRedraw = True then
\r
473 Result := 'obj_LABEL'
\r
475 Result := 'obj_PANEL'
\r
477 else if subclassname = 'obj_BUTTON' then
\r
479 if Boolean(Style and BS_AUTOCHECKBOX) then
\r
480 Result := 'obj_CHECKBOX'
\r
481 else if Boolean(style and BS_RADIOBUTTON) then
\r
482 Result := 'obj_RADIOBOX'
\r
483 else if Boolean(style and BS_OWNERDRAW) then
\r
484 Result := 'obj_BITBTN'
\r
485 else if Boolean(style and BS_GROUPBOX) then
\r
486 Result := 'obj_GROUPBOX';
\r
488 else if indexofstr(Uppercase(subclassname), 'RICHEDIT')>-1 then
\r
489 Result := 'obj_RICHEDIT';
\r