{ From: chrfa@ida.liu.se (Christer Fahlgren) For all people out there wishing a Toolbar with TIP-capabilities, I've written a TIPBAR-unit which is the basic toolbar enhanced with tip capabilities. Since I'm no expert programmer I'd appreciate input on this. Info follows in the file included below. {**************************************************} { } { Turbo Pascal for Windows } { Tipbar unit - a toolbar with TIP-capabilities } { Copyleft (cl) 1994 by Christer Fahlgren } { } { This source is freeware! Please (ab)use, } { change, include in commercial programs, } { throw away, WHATEVER you feel like. } { } { This unit was written because I thought it } { would be cool If I could have tooltips myself. } { And I gathered that the best way of enhancing } { this unit would be to throw it out to the } { public programmer mob for scrutiny. } { I am no expert at this and It may contain } { errors for which I can take no respons- } { ibilities. However, please send me your } { comments, enhancements and bugs for my and } { your benefit. } { } { I also would like to say that I am a bit tired } { of all the people asking for money for petty } { source code. } { } { The inspiration for this unit was the tooltip } { C++ code which I found the other day. } { } { } { This toolbar relies on the original toolbar } { which resides in the \examples\win\toolbar } { directory in BP7. } { } { Enjoy! } { } { Christer Fahlgren } { Email: chrfa@ida.liu.se } { Snail: Västanågatan 26A:204 } { S-582 35 Linköping } { Sweden } {**************************************************} {-------------------------------------------------- Q:What do I do if I already use the toolbar unit in Borland Pascal??? A:First add Tipbar to your uses clause Then add a parameter to your init statement of your toolbar like this: Init(ParentWin, AName, Orient) -> Init(ParentWin, Aname, Orient, Delay) where Delay is the delay in milliseconds for the help to show up. Change occurences of TToolbar to TTipbar Change occurences of PToolbar to PTipbar Lastly you have to redefine your old ToolBarData statements with the new resource- name HelpToolBarData. This resource looks like the old ToolBarData but a null-terminated string is included for every tool. This string can maximum be 255 chars. Example: AHelpToolbar HelpToolBarData BEGIN 2 (Number of tools and spacers in this resource ) tb_open (id of a bitmap ) cm_open (menu id ) "Opens a file\0" (Text which is null-terminated ) tb_save (id of a bitmap ) cm_save (menu id ) "Saves the file\0" (Text which is null-terminated ) END ----------------------------------------------------------} {----------------------------------------------------------} { } { INFORMATION AND PRECAUTIONS } { --------------------------- } { The Store and Load methods are NOT tested! } { It uses one timer (a scarce resource in Win 3.x) } { } { This unit is NOT thoroughly tested and all use is at } { your own risk, BUT it SEEMS to work well. } {----------------------------------------------------------} {----------------------------------------------------------} { Possible ENHANCEMENTS } { --------------------- } { Store the font for the popupwindow in the toolbar } { instead of creating it each time in the HelpWindow } { No more than one calculating of the size of the text } { in the popupwindow. } {----------------------------------------------------------} unit TipBar; interface uses Winprocs, Wintypes, Objects, OWindows, Strings, Win31, toolbar; type PTipbar = ^TTipbar; PTipbutton = ^TTipbutton; PHelpWindow=^THelpWindow; { This is the definition to the Popupwindow which shows the tip} THelpWindow=object(TWindow) LogicalFont : TLOGFONT; ToolToHelp : PTipbutton; constructor Init (AParent : PTipbar; Tool : PTipbutton; WherePos : TPoint; Width, Height : Word); procedure Paint (DC : HDC; var PS : TPaintStruct); virtual; end; { This is a normal toolbarbutton with an extra string variable} TTipbutton=object(TToolbutton) HelpString:array[0..255] of char; end; { This is the definition of our new Tipbar } TTipbar = object(TToolbar) Help : Boolean; {Decides if we should help or not} Timer : Word; {Timer to use} ToolToHelp : PTipButton; {Points to the button to help, it is nil if no button needs help} PopWin : PHelpWindow; {Pointer to the Helpwindow if one exists} OldMouseCoord : Tpoint; {Helps remember the mousecoordinate} Delay : Word; constructor Init(AParent : PWindow; AName : PChar; Orient : Word; Delaypar : word); destructor done; virtual; constructor Load(var S: TStream); procedure Store(var S: TStream); virtual; function GetClassName: PChar; virtual; procedure GetWindowClass(var WC: TWndClass); virtual; procedure ReadResource; virtual; procedure Timermsg(Var Msg:TMessage); virtual wm_first+wm_timer; procedure GetToolUnder(P:Tpoint); procedure WMMouseMove(var Msg: TMessage); virtual wm_First + wm_MouseMove; function CreateTool(Num: Integer; Origin: TPoint; Command: Word; BitmapName: PChar): PTool; virtual; procedure EnableHelp; procedure DisableHelp; procedure ChangeDelay(Delaypar:Word); end; const RTipbar: TStreamRec = ( ObjType: 12302; VmtLink: Ofs(TypeOf(TTipbar)^); Load: @TTipbar.Load; Store: @TTipbar.Store); implementation constructor TTipbar.Init (AParent : PWindow; AName : PChar; Orient : Word; Delaypar : word); begin inherited Init(AParent,Aname,orient); Timer := 0; Delay := Delaypar; Popwin := nil; ToolToHelp := nil; Help := TRUE; {Default is to show help} end; destructor TTipbar.Done; begin KillTimer(Hwindow,1); inherited Done; end; procedure TTipbar.Enablehelp; begin Help:=TRUE; end; procedure TTipbar.Disablehelp; begin Help:=FALSE; end; procedure TTipbar.ChangeDelay(Delaypar:Word); begin Delay:=Delaypar; end; constructor TTipbar.Load(var S: TStream); var X: Integer; procedure RestoreStates(P : PTool); far; begin P^.Read(S); end; begin inherited Load(S); Attr.Style := ws_Child or ws_Visible or ws_Border ; SetFlags(wb_MDIChild, False); DefaultProc := @DefWindowProc; Capture := nil; S.Read(Orientation, SizeOf(Orientation)); Tools.Init(8,8); ResName := nil; S.Read(X, SizeOf(X)); if X = 0 then S.Read(PtrRec(ResName).Ofs, SizeOf(Word)) else ResName := S.StrRead; ReadResource; if Status <> em_InvalidChild then Tools.ForEach(@RestoreStates) else S.Status := stGetError; end; procedure TTipbar.Store(var S: TStream); var X: Integer; procedure SaveStates(P : PTool); far; begin P^.Write(S); end; begin inherited Store(S); S.Write(Orientation, SizeOf(Orientation)); if HiWord(Longint(ResName)) <> 0 then begin X := 1; S.Write(X, SizeOf(X)); S.StrWrite(ResName); end else begin X := 0; S.Write(X, SizeOf(X)); S.Write(PtrRec(ResName).Ofs, SizeOf(Word)); end; Tools.ForEach(@SaveStates); end; procedure TTipbar.ReadResource; type ResRec = record Bitmap, Command: Word; end; PResArray = ^TResArray; TResArray = array [1..$FFF0 div sizeof(ResRec)] of ResRec; var ResIdHandle: THandle; ResDataHandle: THandle; ResDataPtr: PResArray; Count: Word; X: Word; Origin: TPoint; BitInfo: TBitmap; P: PTool; begin ResIDHandle := FindResource(HInstance, ResName, 'HelpToolBarData'); ResDataHandle := LoadResource(HInstance, ResIDHandle); ResDataPtr := LockResource(ResDataHandle); if (ResIDHandle = 0) or (ResDataHandle = 0) or (ResDataPtr = nil) then begin Status := em_InvalidChild; Exit; end; X := 0; Origin.X := 2; Origin.Y := 2; Count := PWord(ResDataPtr)^; Inc(LongInt(ResDataPtr), SizeOf(Count)); { Skip Count } for X := 1 to Count do with ResDataPtr^[1] do begin P := CreateTool(X, Origin, Command, PChar(Bitmap)); if P <> nil then begin NextToolOrigin(X, Origin, P); Tools.Insert(P); end; Inc(Longint(ResDataPtr),sizeof(Resrec)); if Bitmap<>0 then begin Strcopy(PTipbutton(P)^.HelpString,Pchar(Resdataptr)); inc(LongInt(ResdataPtr),strlen(PTipbutton(P)^.HelpString)+1); end; end; Inc(Attr.H, 8); Inc(Attr.W, 8); UnlockResource(ResDataHandle); FreeResource(ResDataHandle); end; function TTipbar.CreateTool( Num : Integer; Origin : TPoint; Command : Word; BitmapName : PChar): PTool; begin if Word(BitmapName) = 0 then CreateTool := New(PToolSpacer, Init(@Self, Command)) else CreateTool := New(PTipButton, Init(@Self, Origin.X, Origin.Y, Command, BitmapName)); end; function TTipbar.GetClassName: PChar; begin GetClassName := 'Tipbar'; end; procedure TTipbar.GetWindowClass(var WC: TWndClass); begin TWindow.GetWindowClass(WC); WC.hbrBackground := GetStockObject(LtGray_Brush); end; procedure TTipbar.WMMouseMove(var Msg: TMessage); begin if Popwin<>nil then begin if not((OldMouseCoord.x=TPoint(Msg.Lparam).x) and (OldMouseCoord.y=TPoint(Msg.Lparam).y)) then begin Popwin^.Done; popwin:=NIL; end; end; If HELP then GetToolUnder(TPoint(Msg.Lparam)); if (Capture <> nil) then Capture^.ContinueCapture(TPoint(Msg.LParam)); end; procedure TTipbar.GetToolUnder(P:TPoint); function IsUnder(Item:PTool):boolean; far; begin IsUnder := Item^.HitTest(P); end; begin ToolToHelp := Tools.firstThat(@IsUnder); if ToolToHelp <> nil then begin SetTimer( Hwindow, 1, Delay, nil); OldMouseCoord.x := P.x; OldMouseCoord.y := P.y; end; end; procedure TTipbar.Timermsg(Var Msg:TMessage); var Co : Tpoint; begin GetCursorPos(Co); ScreenToClient(Hwindow,Co); GetToolUnder(Co); if (PopwIN = nil) and (ToolToHelp <> nil) then begin co.x := OldMouseCoord.x; co.y := OldMouseCoord.y; ClientToScreen(Hwindow,Co); Popwin := new(PHelpWindow,Init(@self,ToolToHelp,Co,10,10)); Application^.MakeWindow(Popwin); ShowWindow(Popwin^.HWindow,SW_SHOWNOACTIVATE); KillTimer(Hwindow,1); end; end; constructor THelpWindow.Init(AParent : PTipbar; Tool : PTipbutton; WherePos : TPoint; Width, Height : Word); var Odc:HDC; Rect:TRect; Font,OldFont:HFONT; begin with LogicalFont do begin lfHeight := 14; lfWidth := 0; lfEscapement := 0; lfOrientation := 0; lfWeight := FW_NORMAL; lfItalic := 0; lfUnderline := 0; lfStrikeOut := 0; lfCharSet := ANSI_CharSet; lfOutPrecision := Out_Default_Precis; lfClipPrecision := Clip_Default_Precis; lfQuality := Proof_Quality; lfPitchAndFamily:= Variable_Pitch or FF_Roman; StrCopy(lfFaceName,'Arial'); end; inherited Init(Aparent,nil); ToolToHelp := Tool; { Firstly, we will position the window att the x coordinate of the cursor and 10 pixels below the y} attr.x := Wherepos.x; attr.y := Wherepos.y+10; { Then, we calculate the height of the text but we set the maximum width to be 70 pixels} setRect(Rect,0,0,70,0); font := CreateFontIndirect(LogicalFont); Odc := GetDC(0); OldFont := Selectobject(odc,font); Drawtext(ODc, ToolToHelp^.HelpString, strlen(ToolToHelp^.HelpString), Rect, DT_LEFT or DT_CALCRECT or DT_WORDBREAK); {Now, we have the width and height of the text in Rect. Then we add a bit to center the text. } Attr.w := Rect.right+10; Attr.h := Rect.bottom+10; Selectobject(odc,OldFont); Deleteobject(Font); ReleaseDC(0,Odc); { The style is pretty important} Attr.Style := ws_border or ws_popup or ws_disabled ; end; procedure THelpWindow.Paint(DC: HDC; var PS: TPaintStruct); var Rect:TRect; Font,OldFont:HFONT; begin {First, we measure again the size of the text, unnecessary this should be stored in an instance variable} {instead. A future enhancement.} setRect(Rect,0,0,70,0); Font := CreateFontIndirect(LogicalFont); OldFont := Selectobject(dc,font); Drawtext(DC, ToolToHelp^.HelpString,strlen(ToolToHelp^.HelpString),Rect DT_LEFT or DT_CALCRECT or DT_WORDBREAK); OffsetRect(Rect,5,5); Drawtext(DC,ToolToHelp^.HelpString,strlen(ToolToHelp^.HelpString),Rect DT_LEFT or DT_WORDBREAK ); SelectObject(DC,OldFont); Deleteobject(Font); end; begin end.