unit SplashAbout; { [SplashAbout] [1.3] Delphi 2005 March 2008 LICENSE The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at "http://www.mozilla.org/MPL/" Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is "[SplashAbout.pas]". The Initial Developer of the Original Code is Martin Holmes (Victoria, BC, Canada, "http://www.mholmes.com/"). Copyright (C) 2005-2008 Martin Holmes and the University of Victoria Computing and Media Centre. The code was co-developed for university and personal projects, and rights are shared by Martin Holmes and the University of Victoria. All Rights Reserved. } { Unit written by Martin Holmes, October 2005, using Delpi 2005, and only tested with Delphi 2005. Updated March 2008 to add the capability to pass in extra pieces of information for display in the About box. This unit aims to encapsulate a splash screen and an About box for an application. It includes an object for retrieving application version information from the executable itself through the Windows API. This information is displayed in the About box, but is also available to the main application through the SplashAbout object. Use it like this: In the application's main form, create a variable: SplashAbout: TSplashAbout; Include a suitable image in your app, perhaps as in a hidden TImage component. In the main form's Show event, call: SplashAbout := TSplashAbout.Create; Then make a call to show the Splash screen: SplashAbout.ShowSplash(imgSplash.Picture.Bitmap, 3, BoundsRect); Passing BoundsRect from the main form makes the splash screen show up centred on the main form's position, rather than on the screen. This is preferable, I think. The second parameter is the number of seconds to show the splash. The function automatically detects a command-line parameter of -nosplash, and suppresses itself if it finds one. In the main form's Close event, free the object: SplashAbout.Free; To show an About box, do this: SplashAbout.ShowAbout(imgSplash.Picture.Bitmap, BoundsRect, clWhite); Dependencies: VersionInfo (this has a class for getting version info about the running application). It would be feasible to use an instantiated TAppVersionInfo belonging to another object owned by the main form, but then this library wouldn't be portable. TntUnicode libraries (Troy Wolbrink). } interface uses Classes, Graphics, Forms, Windows, SysUtils, Controls, TntForms, TntStdCtrls, TntExtCtrls, ExtCtrls, TntClasses, VersionInfo, StdCtrls, ShellAPI; type {TSplashAbout} TSplashAbout = class(TObject) private FForm: TTntForm; FImage: TImage; FPic: Graphics.TBitmap; FRect: TRect; FAppVersionInfo: TAppVersionInfo; FuslExtraInfo: TTntStringList; FSuppressCmdParam: string; procedure FreeTheForm(Sender: TObject; var Action: TCloseAction); procedure ClickLinkLabel(Sender: TObject); protected procedure AddInfoText(Text: WideString; isLink: Boolean); public constructor Create; destructor Destroy; override; procedure ShowSplash(Pic: Graphics.TBitmap; Seconds: integer; ContextRect: TRect); procedure ShowAbout(Pic: Graphics.TBitmap; ContextRect: TRect; Background: TColor); procedure AddExtraInfoLine(wsLine: WideString; isLink: Boolean); procedure ClearExtraInfo; property AppVersionInfo: TAppVersionInfo read FAppVersionInfo; property SuppressCmdParam: string read FSuppressCmdParam write FSuppressCmdParam; end; implementation { TSplashAbout } procedure TSplashAbout.AddInfoText(Text: WideString; isLink: Boolean); var L: TTntLabel; begin if Length(Text) < 1 then Exit; L := TTntLabel.Create(FForm); with L do begin Parent := FForm; if isLink then begin Font.Style := [fsUnderline]; Font.Color := clBlue; Cursor := crHandPoint; onClick := ClickLinkLabel; end; Caption := Text; //Color := FForm.Color; Transparent := True; Alignment := taCenter; WordWrap := True; Left := 4; Top := FForm.ClientHeight + 1; Width := FForm.ClientWidth - 8; end; FForm.ClientHeight := FForm.ClientHeight + L.Height + 2; end; procedure TSplashAbout.FreeTheForm(Sender: TObject; var Action: TCloseAction); begin if FForm <> nil then FreeAndNil(FForm); end; constructor TSplashAbout.Create; begin inherited; FAppVersionInfo := TAppVersionInfo.Create; FuslExtraInfo := TTntStringList.Create; SuppressCmdParam := '-nosplash'; end; destructor TSplashAbout.Destroy; begin if FForm <> nil then FreeAndNil(FForm); FreeAndNil(FuslExtraInfo); FAppVersionInfo.Free; inherited; end; procedure TSplashAbout.ShowAbout(Pic: Graphics.TBitmap; ContextRect: TRect; Background: TColor); var L, T: integer; i: integer; isLink: Boolean; begin if FForm <> nil then FreeAndNil(FForm); FPic := Pic; FRect := ContextRect; FForm := TTntForm.Create(nil); try with FForm do begin BorderStyle := bsSingle; Caption := 'About ' + AppVersionInfo.ProductName; FormStyle := fsStayOnTop; Color := Background; Scaled := False; AutoScroll := False; OnClose := FreeTheForm; end; FImage := TImage.Create(FForm); with FImage do begin Parent := FForm; AutoSize := True; Left := 0; Top := 0; Picture.Bitmap.Assign(FPic); end; with FForm do begin //Set the size to the image, initially ClientWidth := FImage.Width; ClientHeight := FImage.Height; //Add the relevant info AddInfoText(AppVersionInfo.ProductName, False); AddInfoText(AppVersionInfo.DottedVersion, False); AddInfoText(AppVersionInfo.FileDescription, False); AddInfoText(AppVersionInfo.CompanyName, False); AddInfoText(AppVersionInfo.LegalCopyright, False); AddInfoText(AppVersionInfo.Comments, False); AddInfoText(AppVersionInfo.URL, True); //Add any extra info passed in. if FuslExtraInfo.Count > 0 then for i := 0 to FuslExtraInfo.Count-1 do begin isLink := (integer(FuslExtraInfo.Objects[i]) = 1); AddInfoText(FuslExtraInfo[i], isLink); end; // LockWindowUpdate(FForm.Handle); Show; //Set the size of the form to match the image and position it properly L := ((FRect.Right - FRect.Left) div 2) + FRect.Left - (Width div 2); T := ((FRect.Bottom - FRect.Top) div 2) + FRect.Top - (Height div 2); if L+Width > Screen.DesktopWidth then L := Screen.DesktopWidth - Width; if T+Height > Screen.DesktopHeight then T := Screen.DesktopHeight - Height; SetBounds(L, T, Width, Height); // LockWindowUpdate(0); Update; Application.ProcessMessages; end; finally end; end; procedure TSplashAbout.ShowSplash(Pic: Graphics.TBitmap; Seconds: integer; ContextRect: TRect); var L, T: integer; begin //Only show if the command line doesn't include a -nosplash parameter if (Pos(SuppressCmdParam, CmdLine) < 1) then begin FPic := Pic; FRect := ContextRect; FForm := TTntForm.Create(nil); try with FForm do begin BorderStyle := bsNone; FormStyle := fsStayOnTop; AutoScroll := False; Scaled := False; end; FImage := TImage.Create(FForm); with FImage do begin Parent := FForm; AutoSize := True; Left := 0; Top := 0; Picture.Bitmap.Assign(FPic); end; with FForm do begin {TODO: BUG! On dual monitors, if the ContextRect is on the second monitor, the splash screen fails to appear. This may be a bug in my own display card drive, or it may be more general. Get feedback from users on this.} // LockWindowUpdate(FForm.Handle); {Application.ProcessMessages; Show; Application.ProcessMessages;} ClientWidth := FImage.Width; ClientHeight := FImage.Height; //Set the size of the form to match the image and position it properly //Set the size of the form to match the image and position it properly L := ((FRect.Right - FRect.Left) div 2) + FRect.Left - (FImage.Width div 2); T := ((FRect.Bottom - FRect.Top) div 2) + FRect.Top - (FImage.Height div 2); if L+FImage.Width > Screen.DesktopWidth then L := Screen.DesktopWidth - FImage.Width; if T+FImage.Height > Screen.DesktopHeight then T := Screen.DesktopHeight - FImage.Height; SetBounds(L, T, FImage.Width, FImage.Height); Show; // LockWindowUpdate(0); Application.ProcessMessages; Update; Application.ProcessMessages; Sleep(Seconds * 1000); Hide; end; finally FreeAndNil(FForm); end; end; end; procedure TSplashAbout.ClickLinkLabel(Sender: TObject); begin ShellExecute(0, 'open', PChar(string(TTntLabel(Sender).Caption)), nil, nil, SW_NORMAL); end; procedure TSplashAbout.ClearExtraInfo; begin FuslExtraInfo.Clear; end; procedure TSplashAbout.AddExtraInfoLine(wsLine: WideString; isLink: Boolean); var intLink: integer; begin intLink := 0; if isLink then intLink := 1; FuslExtraInfo.AddObject(wsLine, Pointer(intLink)); end; end.