LazMapViewer: Extend TLegalNoticePlugin to support embedded links and line-breaks. Update demos.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9553 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2024-12-28 12:52:00 +00:00
parent 230cdbbc3b
commit 2a66f07424
12 changed files with 412 additions and 159 deletions

View File

@ -25,8 +25,7 @@ object MainForm: TMainForm
Left = 427 Left = 427
Top = 141 Top = 141
object LegalNoticePlugin: TLegalNoticePlugin object LegalNoticePlugin: TLegalNoticePlugin
LegalNotice = '(c) OpenStreetMap and contributors' LegalNotice = '[https://www.openstreetmap.org (c) OpenStreetMap and contributors]'
LegalNoticeURL = 'https://www.openstreetmap.org'
end end
object DraggableMarkerPlugin: TDraggableMarkerPlugin object DraggableMarkerPlugin: TDraggableMarkerPlugin
end end

View File

@ -26,7 +26,6 @@ object MainForm: TMainForm
MapCenter.Latitude = 49 MapCenter.Latitude = 49
PluginManager = PluginManager PluginManager = PluginManager
POIImages = ImageList1 POIImages = ImageList1
UseThreads = True
Zoom = 3 Zoom = 3
OnMouseUp = MapViewMouseUp OnMouseUp = MapViewMouseUp
end end

View File

@ -79,7 +79,7 @@ const
DELTA = 5; // Tolerance of the HitTest DELTA = 5; // Tolerance of the HitTest
var var
layer: TMapLayer; layer: TMapLayer;
poi: TPointOfInterest; poi: TMapPointOfInterest;
RP: TRealPoint; RP: TRealPoint;
area: TRealArea; area: TRealArea;
list: TMapObjectList; list: TMapObjectList;
@ -97,7 +97,7 @@ begin
try try
if (list = nil) then if (list = nil) then
begin begin
poi := TPointOfInterest(layer.PointsOfInterest.Add); poi := TMapPointOfInterest(layer.PointsOfInterest.Add);
poi.Caption := 'Test ' + IntToStr(layer.PointsOfInterest.Count); poi.Caption := 'Test ' + IntToStr(layer.PointsOfInterest.Count);
poi.ImageIndex := Random(ImageList1.Count); poi.ImageIndex := Random(ImageList1.Count);
poi.Longitude := RP.Lon; poi.Longitude := RP.Lon;
@ -106,9 +106,9 @@ begin
begin begin
for i := list.Count-1 downto 0 do for i := list.Count-1 downto 0 do
begin begin
if list[i] is TPointOfInterest then if list[i] is TMapPointOfInterest then
begin begin
poi := TPointOfInterest(list[i]); poi := TMapPointOfInterest(list[i]);
poi.Free; poi.Free;
end; end;
end; end;

View File

@ -28,9 +28,6 @@
<Item> <Item>
<PackageName Value="lazMapViewerPkg"/> <PackageName Value="lazMapViewerPkg"/>
</Item> </Item>
<Item>
<PackageName Value="TAChartLazarusPkg"/>
</Item>
<Item> <Item>
<PackageName Value="LCL"/> <PackageName Value="LCL"/>
</Item> </Item>

View File

@ -10,7 +10,7 @@ uses
athreads, athreads,
{$ENDIF} {$ENDIF}
Interfaces, // this includes the LCL widgetset Interfaces, // this includes the LCL widgetset
Forms, tachartlazaruspkg, Unit1 Forms, Unit1
{ you can add units after this }; { you can add units after this };
{$R *.res} {$R *.res}

View File

@ -25,10 +25,10 @@ object MainForm: TMainForm
AnchorSideLeft.Side = asrBottom AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = ParamsPanel AnchorSideTop.Control = ParamsPanel
AnchorSideRight.Control = cmbPosition AnchorSideRight.Control = cmbPosition
Left = 75 Left = 253
Height = 23 Height = 23
Top = 0 Top = 0
Width = 601 Width = 423
Anchors = [akTop, akLeft, akRight] Anchors = [akTop, akLeft, akRight]
BorderSpacing.Left = 8 BorderSpacing.Left = 8
BorderSpacing.Right = 4 BorderSpacing.Right = 4
@ -37,13 +37,15 @@ object MainForm: TMainForm
OnChange = edLegalNoticeChange OnChange = edLegalNoticeChange
end end
object lblLegalNotice: TLabel object lblLegalNotice: TLabel
AnchorSideLeft.Control = ParamsPanel AnchorSideLeft.Control = rbRightMap
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = edLegalNotice AnchorSideTop.Control = edLegalNotice
AnchorSideTop.Side = asrCenter AnchorSideTop.Side = asrCenter
Left = 0 Left = 178
Height = 15 Height = 15
Top = 4 Top = 4
Width = 67 Width = 67
BorderSpacing.Left = 32
Caption = 'Legal notice:' Caption = 'Legal notice:'
end end
object btnSaveToImage: TButton object btnSaveToImage: TButton
@ -85,10 +87,10 @@ object MainForm: TMainForm
OnChange = cmbPositionChange OnChange = cmbPositionChange
end end
object cbShowMapCenter: TCheckBox object cbShowMapCenter: TCheckBox
AnchorSideLeft.Control = edLegalNotice AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = seOpacity AnchorSideTop.Control = seOpacity
AnchorSideTop.Side = asrCenter AnchorSideTop.Side = asrCenter
Left = 75 Left = 0
Height = 19 Height = 19
Top = 33 Top = 33
Width = 110 Width = 110
@ -103,7 +105,7 @@ object MainForm: TMainForm
AnchorSideLeft.Side = asrBottom AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = seOpacity AnchorSideTop.Control = seOpacity
AnchorSideTop.Side = asrCenter AnchorSideTop.Side = asrCenter
Left = 209 Left = 134
Height = 19 Height = 19
Top = 33 Top = 33
Width = 111 Width = 111
@ -119,7 +121,7 @@ object MainForm: TMainForm
AnchorSideLeft.Side = asrBottom AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = cbShowMapCenter AnchorSideTop.Control = cbShowMapCenter
AnchorSideTop.Side = asrCenter AnchorSideTop.Side = asrCenter
Left = 352 Left = 277
Height = 15 Height = 15
Top = 35 Top = 35
Width = 124 Width = 124
@ -131,10 +133,10 @@ object MainForm: TMainForm
AnchorSideLeft.Side = asrBottom AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = edLegalNotice AnchorSideTop.Control = edLegalNotice
AnchorSideTop.Side = asrBottom AnchorSideTop.Side = asrBottom
Left = 484 Left = 409
Height = 23 Height = 23
Top = 31 Top = 31
Width = 69 Width = 55
Alignment = taRightJustify Alignment = taRightJustify
BorderSpacing.Left = 8 BorderSpacing.Left = 8
BorderSpacing.Top = 8 BorderSpacing.Top = 8
@ -142,10 +144,37 @@ object MainForm: TMainForm
MaxValue = 100 MaxValue = 100
TabOrder = 5 TabOrder = 5
Value = 50 Value = 50
OnChange = FloatSpinEdit1Change OnChange = seOpacityChange
end
object rbLeftMap: TRadioButton
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = edLegalNotice
AnchorSideTop.Side = asrCenter
Left = 0
Height = 19
Top = 2
Width = 65
Caption = 'Left map'
Checked = True
TabOrder = 7
TabStop = True
OnChange = rbLeftMapChange
end
object rbRightMap: TRadioButton
AnchorSideLeft.Control = rbLeftMap
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = edLegalNotice
AnchorSideTop.Side = asrCenter
Left = 73
Height = 19
Top = 2
Width = 73
BorderSpacing.Left = 8
Caption = 'Right map'
TabOrder = 6
end end
end end
object Bevel1: TBevel object FormCenterBevel: TBevel
AnchorSideLeft.Control = Owner AnchorSideLeft.Control = Owner
AnchorSideLeft.Side = asrCenter AnchorSideLeft.Side = asrCenter
AnchorSideTop.Control = Owner AnchorSideTop.Control = Owner
@ -157,10 +186,10 @@ object MainForm: TMainForm
Anchors = [akTop, akLeft, akBottom] Anchors = [akTop, akLeft, akBottom]
Shape = bsSpacer Shape = bsSpacer
end end
object Panel2: TPanel object LeftPanel: TPanel
AnchorSideLeft.Control = Owner AnchorSideLeft.Control = Owner
AnchorSideTop.Control = Owner AnchorSideTop.Control = Owner
AnchorSideRight.Control = Bevel1 AnchorSideRight.Control = FormCenterBevel
AnchorSideBottom.Control = ParamsPanel AnchorSideBottom.Control = ParamsPanel
Left = 0 Left = 0
Height = 417 Height = 417
@ -170,8 +199,8 @@ object MainForm: TMainForm
BevelOuter = bvNone BevelOuter = bvNone
TabOrder = 1 TabOrder = 1
end end
object Panel3: TPanel object RightPanel: TPanel
AnchorSideLeft.Control = Bevel1 AnchorSideLeft.Control = FormCenterBevel
AnchorSideLeft.Side = asrBottom AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Owner AnchorSideTop.Control = Owner
AnchorSideRight.Control = Owner AnchorSideRight.Control = Owner

View File

@ -7,34 +7,40 @@ interface
uses uses
Classes, SysUtils, Classes, SysUtils,
LCLIntf, Forms, Controls, Graphics, ExtCtrls, StdCtrls, Dialogs, Spin, LCLIntf, Forms, Controls, Graphics, ExtCtrls, StdCtrls, Dialogs, Spin,
TAGraph, TATools,
mvMapViewer, mvPluginCore, mvPlugins; mvMapViewer, mvPluginCore, mvPlugins;
type type
TMainForm = class(TForm) TMainForm = class(TForm)
Bevel1: TBevel; FormCenterBevel: TBevel;
btnSaveToImage: TButton; btnSaveToImage: TButton;
cbShowMapCenter: TCheckBox; cbShowMapCenter: TCheckBox;
cbShowLegalNotice: TCheckBox; cbShowLegalNotice: TCheckBox;
cmbPosition: TComboBox; cmbPosition: TComboBox;
edLegalNotice: TEdit; edLegalNotice: TEdit;
rbLeftMap: TRadioButton;
rbRightMap: TRadioButton;
seOpacity: TSpinEdit; seOpacity: TSpinEdit;
lblLegalNotice: TLabel; lblLegalNotice: TLabel;
lblOpacity: TLabel; lblOpacity: TLabel;
ParamsPanel: TPanel; ParamsPanel: TPanel;
Panel2: TPanel; LeftPanel: TPanel;
Panel3: TPanel; RightPanel: TPanel;
procedure btnSaveToImageClick(Sender: TObject); procedure btnSaveToImageClick(Sender: TObject);
procedure cbShowMapCenterChange(Sender: TObject); procedure cbShowMapCenterChange(Sender: TObject);
procedure cbShowLegalNoticeChange(Sender: TObject); procedure cbShowLegalNoticeChange(Sender: TObject);
procedure cmbPositionChange(Sender: TObject); procedure cmbPositionChange(Sender: TObject);
procedure edLegalNoticeChange(Sender: TObject); procedure edLegalNoticeChange(Sender: TObject);
procedure FloatSpinEdit1Change(Sender: TObject); procedure rbLeftMapChange(Sender: TObject);
procedure seOpacityChange(Sender: TObject);
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
private private
FMapView1: TMapView; FMapView1: TMapView;
FMapView2: TMapView; FMapView2: TMapView;
FPluginManager: TMvPluginManager; FPluginManager: TMvPluginManager;
FLegalNoticePlugin1: TLegalNoticePlugin;
FLegalNoticePlugin2: TLegalNoticePlugin;
FCenterMarkerPlugin: TCenterMarkerPlugin;
FLinkedMapsPlugin: TLinkedMapsPlugin;
public public
end; end;
@ -54,7 +60,7 @@ begin
FMapView1 := TMapView.Create(self); FMapView1 := TMapView.Create(self);
FMapView1.Align := alClient; FMapView1.Align := alClient;
FMapView1.Parent := Panel2; FMapView1.Parent := LeftPanel;
FMapView1.MapProvider := 'OpenStreetMap Mapnik'; FMapView1.MapProvider := 'OpenStreetMap Mapnik';
FMapView1.UseThreads := true; FMapView1.UseThreads := true;
FMapView1.Zoom := 9; FMapView1.Zoom := 9;
@ -65,7 +71,7 @@ begin
FMapView2 := TMapView.Create(self); FMapView2 := TMapView.Create(self);
FMapView2.Align := alClient; FMapView2.Align := alClient;
FMapView2.Parent := Panel3; FMapView2.Parent := RightPanel;
FMapView2.MapProvider := 'Maps for free'; FMapView2.MapProvider := 'Maps for free';
FMapView2.UseThreads := true; FMapView2.UseThreads := true;
FMapView2.Zoom := 9; FMapView2.Zoom := 9;
@ -74,13 +80,12 @@ begin
FMapView2.Active := true; FMapView2.Active := true;
FMapView2.PluginManager := FPluginManager; FMapView2.PluginManager := FPluginManager;
with TLegalNoticePlugin.Create(FPluginManager) do FLegalNoticePlugin1 := TLegalNoticePlugin.Create(FPluginManager);
with FLegalNoticePlugin1 do
begin begin
LegalNotice := '(c) OpenStreetMap and contributors'; LegalNotice := 'Map data from [https://www.openstreetmap.org/copyright OpenStreetMap and contributors]';
LegalNoticeURL := 'https://www.openstreetmap.org/copyright'; Spacing := 0;
Spacing := 5;
Font.Size := 8; Font.Size := 8;
Font.Color := clBlue;
BackgroundColor := clWhite; BackgroundColor := clWhite;
MapView := FMapView1; MapView := FMapView1;
@ -88,60 +93,79 @@ begin
seOpacity.Value := round(BackgroundOpacity * 100); seOpacity.Value := round(BackgroundOpacity * 100);
end; end;
with TLegalNoticePlugin.Create(FPluginManager) do FLegalNoticePlugin2 := TLegalNoticePlugin.Create(FPluginManager);
with FLegalNoticePlugin2 do
begin begin
LegalNotice := 'maps-for-free'; LegalNotice := '(c) [https://maps-for-free.com/html/about.html maps-for-free]';
LegalNoticeURL := 'https://maps-for-free.com/html/about.html'; Spacing := 0;
Spacing := 5;
Font.Size := 8; Font.Size := 8;
Font.Color := clBlue; Font.Color := clBlue;
BackgroundColor := clWhite; BackgroundColor := clWhite;
MapView := FMapView2; MapView := FMapView2;
end; end;
with TCenterMarkerPlugin.Create(FPluginManager) do FCenterMarkerPlugin := TCenterMarkerPlugin.Create(FPluginManager);
with FCenterMarkerPlugin do
begin begin
Size := 15; Size := 15;
Pen.Width := 3; Pen.Width := 3;
Pen.Color := clRed; Pen.Color := clRed;
end; end;
with TLinkedMapsPlugin.Create(FPluginManager) do ; FLinkedMapsPlugin := TLinkedMapsPlugin.Create(FPluginManager);
end; end;
procedure TMainForm.edLegalNoticeChange(Sender: TObject); procedure TMainForm.edLegalNoticeChange(Sender: TObject);
begin begin
(FPluginManager.Item[0] as TLegalNoticePlugin).LegalNotice := edLegalNotice.Text; if rbLeftMap.Checked then
FLegalNoticePlugin1.LegalNotice := edLegalNotice.Text;
if rbRightMap.Checked then
FLegalNoticePlugin2.LegalNotice := edLegalNotice.Text;
end; end;
procedure TMainForm.FloatSpinEdit1Change(Sender: TObject); procedure TMainForm.rbLeftMapChange(Sender: TObject);
begin begin
(FPluginManager.Item[0] as TLegalNoticePlugin).BackgroundOpacity := seOpacity.Value / 100; if rbLeftMap.Checked then
if FPluginManager.PluginList.Count > 1 then edLegalNotice.Text := FLegalNoticePlugin1.LegalNotice;
(FPluginManager.Item[1] as TLegalNoticePlugin).BackgroundOpacity := seOpacity.Value / 100; if rbRightMap.Checked then
edLegalNotice.Text := FLegalNoticePlugin2.LegalNotice;
end;
procedure TMainForm.seOpacityChange(Sender: TObject);
begin
if rbLeftMap.Checked then
FLegalNoticePlugin1.BackgroundOpacity := seOpacity.Value / 100;
if rbRightMap.Checked then
FLegalNoticePlugin2.BackgroundOpacity := seOpacity.Value / 100;
end; end;
procedure TMainForm.btnSaveToImageClick(Sender: TObject); procedure TMainForm.btnSaveToImageClick(Sender: TObject);
begin begin
if rbLeftMap.Checked then
FMapView1.SaveToFile(TPortableNetworkGraphic, 'map1.png'); FMapView1.SaveToFile(TPortableNetworkGraphic, 'map1.png');
if rbRightMap.Checked then
FMapView2.SaveToFile(TPortableNetworkGraphic, 'map2.png'); FMapView2.SaveToFile(TPortableNetworkGraphic, 'map2.png');
end; end;
procedure TMainForm.cbShowMapCenterChange(Sender: TObject); procedure TMainForm.cbShowMapCenterChange(Sender: TObject);
begin begin
(FPluginManager.Item[2] as TCenterMarkerPlugin).Enabled := cbShowMapCenter.Checked; FCenterMarkerPlugin.Enabled := cbShowMapCenter.Checked;
end; end;
procedure TMainForm.cbShowLegalNoticeChange(Sender: TObject); procedure TMainForm.cbShowLegalNoticeChange(Sender: TObject);
begin begin
(FPluginManager.Item[0] as TLegalNoticePlugin).Enabled := cbShowLegalNotice.Checked; if rbLeftMap.Checked then
(FPluginManager.Item[1] as TLegalNoticePlugin).Enabled := cbShowLegalNotice.Checked; FLegalNoticePlugin1.Enabled := cbShowLegalNotice.Checked;
if rbRightMap.Checked then
FLegalNoticePlugin2.Enabled := cbShowLegalNotice.Checked;
end; end;
procedure TMainForm.cmbPositionChange(Sender: TObject); procedure TMainForm.cmbPositionChange(Sender: TObject);
begin begin
(FPluginManager.Item[0] as TLegalNoticePlugin).Position := TLegalNoticePosition(cmbPosition.ItemIndex); if rbLeftMap.Checked then
(FPluginManager.Item[1] as TLegalNoticePlugin).Position := TLegalNoticePosition(cmbPosition.ItemIndex); FLegalNoticePlugin1.Position := TLegalNoticePosition(cmbPosition.ItemIndex);
if rbRightMap.Checked then
FLegalNoticePlugin2.Position := TLegalNoticePosition(cmbPosition.ItemIndex);
end; end;
end. end.

View File

@ -10,7 +10,7 @@ object MainForm: TMainForm
OnCreate = FormCreate OnCreate = FormCreate
object MapView: TMapView object MapView: TMapView
Left = 0 Left = 0
Height = 257 Height = 261
Top = 0 Top = 0
Width = 451 Width = 451
Align = alClient Align = alClient
@ -22,35 +22,48 @@ object MainForm: TMainForm
PluginManager = PluginManager PluginManager = PluginManager
end end
object InfoPanel: TPanel object InfoPanel: TPanel
Left = 0 Left = 8
Height = 50 Height = 30
Top = 257 Top = 269
Width = 451 Width = 435
Align = alBottom Align = alBottom
ClientHeight = 50 AutoSize = True
ClientWidth = 451 BorderSpacing.Around = 8
BevelOuter = bvNone
ClientHeight = 30
ClientWidth = 435
TabOrder = 1 TabOrder = 1
object lblMessageLabel: TLabel object lblMessageLabel: TLabel
AnchorSideTop.Control = InfoPanel AnchorSideLeft.Control = InfoPanel
AnchorSideTop.Side = asrCenter AnchorSideTop.Control = Label1
Left = 16 AnchorSideTop.Side = asrBottom
Left = 0
Height = 15 Height = 15
Top = 18 Top = 15
Width = 146 Width = 146
Caption = 'UserdefinedPlugin Message' Caption = 'UserdefinedPlugin Message'
end end
object lblUserdefinedPluginMessage: TLabel object lblUserdefinedPluginMessage: TLabel
AnchorSideLeft.Control = lblMessageLabel AnchorSideLeft.Control = lblMessageLabel
AnchorSideLeft.Side = asrBottom AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = InfoPanel AnchorSideTop.Control = lblMessageLabel
AnchorSideTop.Side = asrCenter AnchorSideTop.Side = asrCenter
Left = 170 Left = 154
Height = 15 Height = 15
Top = 18 Top = 15
Width = 38 Width = 38
BorderSpacing.Left = 8 BorderSpacing.Left = 8
Caption = ' (none)' Caption = ' (none)'
end end
object Label1: TLabel
AnchorSideLeft.Control = InfoPanel
AnchorSideTop.Control = InfoPanel
Left = 0
Height = 15
Top = 0
Width = 106
Caption = 'Click into the map...'
end
end end
object PluginManager: TMvPluginManager object PluginManager: TMvPluginManager
Left = 262 Left = 262

View File

@ -13,6 +13,7 @@ type
{ TMainForm } { TMainForm }
TMainForm = class(TForm) TMainForm = class(TForm)
Label1: TLabel;
lblMessageLabel: TLabel; lblMessageLabel: TLabel;
lblUserdefinedPluginMessage: TLabel; lblUserdefinedPluginMessage: TLabel;
MapView: TMapView; MapView: TMapView;

View File

@ -5,7 +5,7 @@ unit mvPlugins;
interface interface
uses uses
Classes, SysUtils, Classes, SysUtils, Contnrs,
Graphics, Controls, LCLIntf, //LazLoggerBase, Graphics, Controls, LCLIntf, //LazLoggerBase,
mvMapViewer, mvDrawingEngine, mvPluginCore, mvGPSObj, mvTypes; mvMapViewer, mvDrawingEngine, mvPluginCore, mvGPSObj, mvTypes;
@ -45,33 +45,42 @@ type
TLegalNoticePosition = (lnpTopLeft, lnpTopRight, lnpBottomLeft, lnpBottomRight); TLegalNoticePosition = (lnpTopLeft, lnpTopRight, lnpBottomLeft, lnpBottomRight);
TLegalNoticePlugin = class(TMvMultiMapsDrawPlugin) TLegalNoticePlugin = class(TMvDrawPlugin)
private private
const const
DEFAULT_LEGALNOTICE_OPACITY = 0.55; DEFAULT_LEGALNOTICE_OPACITY = 0.55;
DEFAULT_LEGALNOTICE_SPACING = 4; DEFAULT_LEGALNOTICE_SPACING = 4;
type
TLegalNoticePart = class
Text: String;
URL: String;
Rect: TRect;
constructor Create(AText, AURL: String);
end;
TLegalNoticeParts = class(TFPObjectList);
private private
FLegalNotice: String; FLegalNotice: TCaption;
FLegalNoticeURL: String; FLegalNoticeParts: TLegalNoticeParts;
FBackgroundOpacity: Single; FBackgroundOpacity: Single;
FPosition: TLegalNoticePosition; FPosition: TLegalNoticePosition;
FSpacing: Integer; FSpacing: Integer;
FBackgroundColor: TColor; FBackgroundColor: TColor;
FMouseOverPart: Integer;
FURLFontColor: TColor;
private private
procedure SetBackgroundColor(AValue: TColor); procedure SetBackgroundColor(AValue: TColor);
procedure SetBackgroundOpacity(AValue: Single); procedure SetBackgroundOpacity(AValue: Single);
procedure SetLegalNotice(AValue: String); procedure SetLegalNotice(AValue: TCaption);
procedure SetLegalNoticeURL(AValue: String);
procedure SetPosition(AValue: TLegalNoticePosition); procedure SetPosition(AValue: TLegalNoticePosition);
procedure SetSpacing(AValue: Integer); procedure SetSpacing(AValue: Integer);
protected protected
procedure CalcClickableRect(AMapView: TMapView; out AClickableRect: TRect);
procedure Changed(Sender: TObject); procedure Changed(Sender: TObject);
procedure ExtractLegalNoticeParts(AMapView: TMapView);
function PointInURLPart(APoint: TPoint; out URL: String): Integer;
protected protected
procedure AfterDrawObjects(AMapView: TMapView; var Handled: Boolean); override; procedure AfterDrawObjects(AMapView: TMapView; var Handled: Boolean); override;
procedure MouseDown(AMapView: TMapView; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; procedure MouseDown(AMapView: TMapView; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState;
X, Y: Integer; var Handled: Boolean); override; X, Y: Integer; var Handled: Boolean); override;
procedure MouseEnter(AMapView: TMapView; var Handled: Boolean); override;
procedure MouseMove(AMapView: TMapView; {%H-}Shift: TShiftState; X, Y: Integer; procedure MouseMove(AMapView: TMapView; {%H-}Shift: TShiftState; X, Y: Integer;
var Handled: Boolean); override; var Handled: Boolean); override;
public public
@ -81,13 +90,12 @@ type
published published
property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor default clNone; property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor default clNone;
property BackgroundOpacity: Single read FBackgroundOpacity write SetBackgroundOpacity default DEFAULT_LEGALNOTICE_OPACITY; // 0..1 property BackgroundOpacity: Single read FBackgroundOpacity write SetBackgroundOpacity default DEFAULT_LEGALNOTICE_OPACITY; // 0..1
property LegalNotice: String read FLegalNotice write SetLegalNotice; property LegalNotice: TCaption read FLegalNotice write SetLegalNotice;
property LegalNoticeURL: String read FLegalNoticeURL write SetLegalNoticeURL;
property Position: TLegalNoticePosition read FPosition write SetPosition default lnpBottomRight; property Position: TLegalNoticePosition read FPosition write SetPosition default lnpBottomRight;
property Spacing: Integer read FSpacing write SetSpacing default DEFAULT_LEGALNOTICE_SPACING; property Spacing: Integer read FSpacing write SetSpacing default DEFAULT_LEGALNOTICE_SPACING;
property URLFontColor: TColor read FURLFontColor write FURLFontColor default clBlue;
// inherited properties // inherited properties
property Font; property Font;
property MapView;
end; end;
{ TDraggableMarkerPlugin } { TDraggableMarkerPlugin }
@ -324,17 +332,27 @@ end;
{ TLegalNoticePlugin } { TLegalNoticePlugin }
constructor TLegalNoticePlugin.TLegalNoticePart.Create(AText, AURL: String);
begin
inherited Create;
Text := AText;
URL := AURL;
end;
constructor TLegalNoticePlugin.Create(AOwner: TComponent); constructor TLegalNoticePlugin.Create(AOwner: TComponent);
begin begin
inherited Create(AOwner); inherited Create(AOwner);
FBackgroundColor := clNone; FBackgroundColor := clNone;
FBackgroundOpacity := DEFAULT_LEGALNOTICE_OPACITY; FBackgroundOpacity := DEFAULT_LEGALNOTICE_OPACITY;
FURLFontColor := clBlue;
FPosition := lnpBottomRight; FPosition := lnpBottomRight;
FSpacing := DEFAULT_LEGALNOTICE_SPACING; FSpacing := DEFAULT_LEGALNOTICE_SPACING;
FLegalNoticeParts := TLegalNoticeParts.Create;
end; end;
destructor TLegalNoticePlugin.Destroy; destructor TLegalNoticePlugin.Destroy;
begin begin
FLegalNoticeParts.Free;
inherited; inherited;
end; end;
@ -345,72 +363,239 @@ begin
FBackgroundColor := TLegalNoticePlugin(Source).BackgroundColor; FBackgroundColor := TLegalNoticePlugin(Source).BackgroundColor;
FBackgroundOpacity := TLegalNoticePlugin(Source).BackgroundOpacity; FBackgroundOpacity := TLegalNoticePlugin(Source).BackgroundOpacity;
FLegalNotice := TLegalNoticePlugin(Source).LegalNotice; FLegalNotice := TLegalNoticePlugin(Source).LegalNotice;
FLegalNoticeURL := TLegalNoticePlugin(Source).LegalNoticeURL;
FPosition := TLegalNoticePlugin(Source).Position; FPosition := TLegalNoticePlugin(Source).Position;
FSpacing := TLegalNoticePlugin(Source).Spacing; FSpacing := TLegalNoticePlugin(Source).Spacing;
FURLFontColor := TLegalNoticePlugin(Source).URLFontColor;
end; end;
inherited; inherited;
end; end;
procedure TLegalNoticePlugin.AfterDrawObjects(AMapView: TMapView; var Handled: Boolean); procedure TLegalNoticePlugin.AfterDrawObjects(AMapView: TMapView; var Handled: Boolean);
var var
x, y: Integer; i: Integer;
lClickableRect: TRect; lBounds: TRect;
lSavedFont: TMvFont; lSavedFont: TMvFont;
lSavedOpacity: Single; lSavedOpacity: Single;
part: TLegalNoticePart;
begin begin
if not Assigned(AMapView) then Exit; if not Assigned(AMapView) then Exit;
Handled := True; Handled := True;
CalcClickableRect(AMapView,lClickableRect);
x := lClickableRect.Left;
y := lClickableRect.Top;
lSavedFont := AMapView.DrawingEngine.GetFont; lSavedFont := AMapView.DrawingEngine.GetFont;
lSavedOpacity := AMapView.DrawingEngine.Opacity; lSavedOpacity := AMapView.DrawingEngine.Opacity;
try try
ExtractLegalNoticeParts(AMapView);
lBounds := Rect(MaxInt, MaxInt, -MaxInt, -MaxInt);
for i := 0 to FLegalNoticeParts.Count-1 do
begin
part := TLegalNoticePart(FLegalNoticeParts[i]);
if part.Rect.Left < lBounds.Left then lBounds.Left := part.Rect.Left;
if part.Rect.Top < lBounds.Top then lBounds.Top := part.Rect.Top;
if part.Rect.Right > lBounds.Right then lBounds.Right := part.Rect.Right;
if part.Rect.Bottom > lBounds.Bottom then lBounds.Bottom := part.Rect.Bottom;
end;
// Draw the common (semi-transparent) background of all parts
if FBackgroundColor <> clNone then if FBackgroundColor <> clNone then
begin begin
AMapView.DrawingEngine.Opacity := FBackgroundOpacity; AMapView.DrawingEngine.Opacity := FBackgroundOpacity;
AMapView.DrawingEngine.BrushStyle := bsSolid; AMapView.DrawingEngine.BrushStyle := bsSolid;
AMapView.DrawingEngine.BrushColor := ColorToRGB(FBackgroundColor); AMapView.DrawingEngine.BrushColor := ColorToRGB(FBackgroundColor);
with lClickableRect do with lBounds do
AMapView.DrawingEngine.FillRect(Left, Top, Right, Bottom); AMapView.DrawingEngine.FillRect(Left, Top, Right, Bottom);
end; end;
AMapView.DrawingEngine.BrushStyle := bsClear; AMapView.DrawingEngine.BrushStyle := bsClear;
// Draw the part texts
for i := 0 to FLegalNoticeParts.Count-1 do
begin
part := TLegalNoticePart(FLegalNoticeParts[i]);
if part.URL <> '' then
begin
if i = FMouseOverPart then
AMapView.DrawingEngine.SetFont(Font.Name, Font.Size, Font.Style + [fsUnderline], FURLFontColor)
else
AMapView.DrawingEngine.SetFont(Font.Name, Font.Size, Font.Style, FURLFontColor)
end
else
AMapView.DrawingEngine.SetFont(Font.Name, Font.Size, Font.Style, Font.Color); AMapView.DrawingEngine.SetFont(Font.Name, Font.Size, Font.Style, Font.Color);
AMapView.DrawingEngine.TextOut(x, y, FLegalNotice); AMapView.DrawingEngine.TextOut(part.Rect.Left, part.Rect.Top, part.Text);
end;
finally finally
AMapView.DrawingEngine.Opacity := lSavedOpacity; AMapView.DrawingEngine.Opacity := lSavedOpacity;
AMapView.DrawingEngine.SetFont(lSavedFont); AMapView.DrawingEngine.SetFont(lSavedFont);
end; end;
end; end;
procedure TLegalNoticePlugin.CalcClickableRect(AMapView: TMapView; out { LegalNotice can contain text and embedded URLs with text following the
AClickableRect: TRect); wikipedia mark-down.
Line breaks allowed as #13, #10, #13#10, or '\n'
Example:
'Map data from [https://openstreetmap.org/copyright OpenStreetMap and contributors]'
displayed as "Map data from OpenStreamMap and contributors"
Embedded URL (https://openstreetmap.org/copyright) is assigned to text
"OpenStreetMap and contributors" }
procedure TLegalNoticePlugin.ExtractLegalNoticeParts(AMapView: TMapView);
var var
P: PAnsiChar;
partType: (ptText, ptURL, ptURLText, prLineBreak);
txt: String;
url: String;
part: TLegalNoticePart;
savedFont: TMvFont;
R: TRect;
lineWidths: array of integer = nil;
sz: TSize; sz: TSize;
x, y: Integer; i, line, dx, dy, nLines: Integer;
lSavedFont: TMvFont;
begin begin
lSavedFont := AMapView.DrawingEngine.GetFont; FLegalNoticeParts.Clear;
if FLegalNotice = '' then
exit;
P := PChar(FLegalNotice);
partType := ptText;
txt := '';
url := '';
nLines := 1;
while true do
begin
case P^ of
#0 : break;
'[': begin
// entering a URL part
if partType = ptText then
begin
if (txt <> '') or (url <> '') then
begin
// Store away previously found txt and url
part := TLegalNoticePart.Create(txt, url);
FLegalNoticeParts.Add(part);
end;
partType := ptURL; // the next part will be in URL
txt := '';
url := '';
end;
end;
' ': if partType = ptURL then // in URL
begin
partType := ptURLText; // next part will be the text assigned to the URL
txt := '';
end else
txt := txt + P^;
']': if partType = ptURLText then
begin
if (txt <> '') and (url <> '') then
begin
// Store away url and its text
part := TLegalNoticePart.Create(txt, url);
FLegalNoticeParts.Add(part);
end;
partType := ptText; // next part will be normal text again
txt := '';
url := '';
end;
#13,
#10: begin
if P^ = #13 then
begin
inc(P);
if P^ <> #10 then dec(P);
end;
// Store away previously found text and url
FLegalNoticeParts.Add(TLegalNoticePart.Create(txt, url));
// Store #13 as indicator of a line break. Keep url.
FLegalNoticeParts.Add(TLegalNoticePart.Create(#13, ''));
txt := '';
inc(nLines);
end;
'\': begin
inc(P);
if (P^ in ['n', 'N']) then
begin
// Store away previously found text and url
FLegalNoticeParts.Add(TLegalNoticePart.Create(txt, url));
// Store #13 as indicator of a line break. Keep url.
FLegalNoticeParts.Add(TLegalNoticePart.Create(#13, ''));
txt := '';
inc(nLines);
end else
begin
dec(P);
txt := txt + P^;
end;
end;
else
if partType = ptURL then
url := url + P^
else
txt := txt + P^;
end;
inc(P);
end;
if (partType = ptText) and (txt <> '') then
begin
part := TLegalNoticePart.Create(txt, '');
FLegalNoticeParts.Add(part);
end;
// Measure pixel size of parts
savedFont := AMapView.DrawingEngine.GetFont;
try try
AMapView.DrawingEngine.SetFont(Font.Name, Font.Size, Font.Style, Font.Color); AMapView.DrawingEngine.SetFont(Font.Name, Font.Size, Font.Style, Font.Color);
sz := AMapView.DrawingEngine.TextExtent(FLegalNotice); R := Rect(0, 0, 0, 0);
SetLength(lineWidths, nLines);
line := 0;
lineWidths[line] := 0;
for i := 0 to FLegalNoticeParts.Count-1 do
begin
part := TLegalNoticePart(FLegalNoticeParts[i]);
sz := AMapView.DrawingEngine.TextExtent(part.Text);
R := Rect(R.Right, R.Top, R.Right + sz.CX, R.Top + sz.CY);
if R.Right > lineWidths[line] then
lineWidths[line] := R.Right;
if part.Text = #13 then // line break
begin
R := Rect(0, R.Bottom - sz.CY div 2, 0, R.Bottom);
inc(line);
lineWidths[line] := 0;
end;
part.Rect := R;
end;
finally
AMapView.DrawingEngine.SetFont(savedFont);
end;
// Calculate left/top corner of total text
case FPosition of case FPosition of
lnpTopLeft, lnpBottomLeft: lnpTopLeft, lnpBottomLeft:
x := FSpacing; dx := FSpacing;
lnpTopRight, lnpBottomRight: lnpTopRight, lnpBottomRight:
x := AMapView.Width - sz.CX - FSpacing; dx := AMapView.Width - FSpacing - lineWidths[0];
end; end;
case FPosition of case FPosition of
lnpTopLeft, lnpTopRight: lnpTopLeft, lnpTopRight:
y := FSpacing; dy := FSpacing;
lnpBottomLeft, lnpBottomRight: lnpBottomLeft, lnpBottomRight:
y := AMapView.Height - sz.CY - FSpacing; dy := AMapView.Height - R.Bottom - FSpacing;
end; end;
AClickableRect := Rect(x, y, x + sz.CX, y + sz.CY);
SetMapViewData(AMapView,AClickableRect,SizeOf(AClickableRect)); // Move text rectangles to correct position
finally line := 0;
AMapView.DrawingEngine.SetFont(lSavedFont); for i := 0 to FLegalNoticeParts.Count-1 do
begin
part := TLegalNoticePart(FLegalNoticeParts[i]);
if part.Text = #13 then
begin
inc(line);
if FPosition in [lnpTopRight, lnpBottomRight] then
dx := AMapView.Width - FSpacing - lineWidths[line];
end;
OffsetRect(part.Rect, dx, dy);
end; end;
end; end;
@ -422,53 +607,60 @@ end;
procedure TLegalNoticePlugin.MouseDown(AMapView: TMapView; Button: TMouseButton; procedure TLegalNoticePlugin.MouseDown(AMapView: TMapView; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer; var Handled: Boolean); Shift: TShiftState; X, Y: Integer; var Handled: Boolean);
var var
pt: TPoint; url: String;
lClickableRect : TRect;
begin begin
Unused(AMapView);
// The button down event is consumed by a different plugin, so do nothing here // The button down event is consumed by a different plugin, so do nothing here
if Handled then Exit; if Handled then Exit;
pt.X := X; if PointInURLPart(Point(X, Y), url) <> -1 then
pt.Y := Y;
if GetMapViewData(AMapView,lClickableRect,SizeOf(lClickableRect)) < SizeOf(lClickableRect) then
CalcClickableRect(AMapView,lClickableRect);
if PtInRect(lClickableRect, pt) and (FLegalNoticeURL <> '') then
begin begin
// The button down event is consumed by this plugin // The button down event is consumed by this plugin
OpenURL(FLegalNoticeURL); OpenURL(url);
Handled := True; Handled := True;
end; end;
end; end;
procedure TLegalNoticePlugin.MouseEnter(AMapView: TMapView; var Handled: Boolean);
var
lClickableRect : TRect;
begin
inherited;
CalcClickableRect(AMapView,lClickableRect);
end;
procedure TLegalNoticePlugin.MouseMove(AMapView: TMapView; Shift: TShiftState; procedure TLegalNoticePlugin.MouseMove(AMapView: TMapView; Shift: TShiftState;
X, Y: Integer; var Handled: Boolean); X, Y: Integer; var Handled: Boolean);
var var
lClickableRect : TRect; url: String;
begin begin
if GetMapViewData(AMapView,lClickableRect,SizeOf(lClickableRect)) < SizeOf(lClickableRect) then ExtractLegalNoticeParts(AMapView);
CalcClickableRect(AMapView, lClickableRect);
if PtInRect(lClickableRect, Point(X, Y)) and (not AMapView.Engine.InDrag) and if not (AMapView.Engine.InDrag) then
(FLegalNoticeURL <> '') then begin
FMouseOverPart := PointInURLPart(Point(X, Y), url);
if (FMouseOverPart <> -1) then
begin
if url <> '' then
begin begin
Font.Style := [fsUnderline];
AMapView.Cursor := crHandPoint; AMapView.Cursor := crHandPoint;
Handled := true; Handled := true;
end;
end else end else
begin
Font.Style := [];
if not Handled then if not Handled then
AMapView.Cursor := crDefault; AMapView.Cursor := crDefault;
end;
Update; Update;
end; end;
end;
function TLegalNoticePlugin.PointInURLPart(APoint: TPoint; out URL: String): Integer;
var
part: TLegalNoticePart;
begin
for Result := 0 to FLegalNoticeParts.Count-1 do
begin
part := TLegalNoticePart(FLegalNoticeParts[Result]);
if PtInRect(part.Rect, APoint) and (part.URL <> '') then
begin
uRL := part.URL;
exit;
end;
end;
URL := '';
Result := -1;
end;
procedure TLegalNoticePlugin.SetPosition(AValue: TLegalNoticePosition); procedure TLegalNoticePlugin.SetPosition(AValue: TLegalNoticePosition);
begin begin
@ -477,20 +669,13 @@ begin
Update; Update;
end; end;
procedure TLegalNoticePlugin.SetLegalNotice(AValue: String); procedure TLegalNoticePlugin.SetLegalNotice(AValue: TCaption);
begin begin
if FLegalNotice = AValue then Exit; if FLegalNotice = AValue then Exit;
FLegalNotice := AValue; FLegalNotice := AValue;
Update; Update;
end; end;
procedure TLegalNoticePlugin.SetLegalNoticeURL(AValue: String);
begin
if FLegalNoticeURL = AValue then Exit;
FLegalNoticeURL := AValue;
Update;
end;
procedure TLegalNoticePlugin.SetBackgroundColor(AValue: TColor); procedure TLegalNoticePlugin.SetBackgroundColor(AValue: TColor);
begin begin
if FBackgroundColor = AValue then Exit; if FBackgroundColor = AValue then Exit;
@ -512,6 +697,9 @@ begin
Update; Update;
end; end;
{ TDraggableMarkerPlugin }
function TDraggableMarkerPlugin.GetFirstMarkerAtMousePos(const AMapView: TMapView; function TDraggableMarkerPlugin.GetFirstMarkerAtMousePos(const AMapView: TMapView;
const AX, AY: Integer): TGPSPoint; const AX, AY: Integer): TGPSPoint;
var var

View File

@ -14,9 +14,9 @@ implementation
{$R mvmapviewer_icons.res} {$R mvmapviewer_icons.res}
uses uses
ImgList, Controls, ImgList,
mvTypes, mvGeoNames, mvMapViewer, mvDLEFpc, mvDLECache, mvPluginCore, mvTypes, mvGeoNames, mvMapViewer, mvDLEFpc, mvDLECache, mvPluginCore,
mvMapViewerPropEdits, mvPluginEditors; mvMapViewerPropEdits, mvPlugins, mvPluginEditors;
procedure Register; procedure Register;
var var
@ -55,9 +55,11 @@ begin
RegisterPropertyEditor(TypeInfo(Double), RegisterPropertyEditor(TypeInfo(Double),
TMapCenter,'Longitude', TLatLonDMSPropertyEditor); TMapCenter,'Longitude', TLatLonDMSPropertyEditor);
RegisterComponentEditor(TMvPluginManager, TMvPluginManagerComponentEditor);
RegisterPropertyEditor(TypeInfo(TMvPluginList), RegisterPropertyEditor(TypeInfo(TMvPluginList),
TMvPluginManager, 'PluginList', TMvPluginListPropertyEditor); TMvPluginManager, 'PluginList', TMvPluginListPropertyEditor);
RegisterComponentEditor(TMvPluginManager, TMvPluginManagerComponentEditor); RegisterPropertyEditor(TypeInfo(TCaption),
TLegalNoticePlugin, 'LegalNotice', TStringMultilinePropertyEditor);
for i := 0 to PluginClassRegistry.Count - 1 do for i := 0 to PluginClassRegistry.Count - 1 do
RegisterNoIcon([TMvCustomPluginClass(PluginClassRegistry.GetClass(i))]); RegisterNoIcon([TMvCustomPluginClass(PluginClassRegistry.GetClass(i))]);

View File

@ -34,14 +34,14 @@ type
FPluginManager: TMvPluginManager; FPluginManager: TMvPluginManager;
FMapView: TMapView; FMapView: TMapView;
FEnabled: Boolean; FEnabled: Boolean;
procedure SetEnabled(AValue: Boolean);
procedure SetMapView(AValue: TMapView);
procedure SetPluginManager(AValue: TMvPluginManager); procedure SetPluginManager(AValue: TMvPluginManager);
protected protected
function GetIndex: Integer; override; function GetIndex: Integer; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure ReadState(Reader: TReader); override; procedure ReadState(Reader: TReader); override;
procedure SetEnabled(AValue: Boolean); virtual;
procedure SetIndex(AValue: Integer); override; procedure SetIndex(AValue: Integer); override;
procedure SetMapView(AValue: TMapView); virtual;
procedure SetParentComponent(AParent: TComponent); override; procedure SetParentComponent(AParent: TComponent); override;
procedure Update; virtual; procedure Update; virtual;
protected protected
@ -158,6 +158,7 @@ type
destructor Destroy;override; destructor Destroy;override;
published published
property Enabled; property Enabled;
property MapView;
end; end;
{ TMvMultiMapsDrawPlugin } { TMvMultiMapsDrawPlugin }