1{%MainUnit ../clipbrd.pp} 2 3{****************************************************************************** 4 TClipBoard 5 ****************************************************************************** 6 7 ***************************************************************************** 8 This file is part of the Lazarus Component Library (LCL) 9 10 See the file COPYING.modifiedLGPL.txt, included in this distribution, 11 for details about the license. 12 ***************************************************************************** 13 14 The clipboard is able to work with the windows and gtk behaviour/features. 15} 16 17{$I clipbrd_html.inc} 18 19{ TClipboard } 20 21constructor TClipboard.Create; 22begin 23 // default: create a normal Clipboard 24 Create(ctClipboard); 25end; 26 27constructor TClipboard.Create(AClipboardType: TClipboardType); 28begin 29 //DebugLn('[TClipboard.Create] A ',ClipboardTypeName[AClipboardType],' Self=',DbgS(Self)); 30 inherited Create; 31 FClipboardType:=AClipboardType; 32end; 33 34destructor TClipboard.Destroy; 35begin 36 //DebugLn('[TClipboard.Destroy] A ',ClipboardTypeName[ClipboardType],' Self=',DbgS(Self)); 37 OnRequest:=nil; // this will notify the owner 38 if FAllocated then begin 39 ClipboardGetOwnership(ClipboardType,nil,0,nil); 40 FAllocated:=false; 41 end; 42 Clear; 43 inherited Destroy; 44 //DebugLn('[TClipboard.Destroy] END ',ClipboardTypeName[ClipboardType]); 45end; 46 47function TClipboard.IndexOfCachedFormatID(FormatID: TClipboardFormat; 48 CreateIfNotExists: boolean): integer; 49var 50 NewSize: integer; 51 FormatAdded: Boolean; 52begin 53 //DebugLn('[TClipboard.IndexOfCachedFormatID] A ',ClipboardTypeName[ClipboardType] 54 //,' Format=',FormatID,' CreateIfNotExists=',CreateIfNotExists); 55 if FormatID=0 then begin 56 Result:=-1; 57 if CreateIfNotExists then 58 raise Exception.Create( 59 'IndexOfCachedFormatID: Internal Error: invalid FormatID 0 for '+ 60 ClipboardTypeName[ClipboardType]); 61 end; 62 Result:=FCount-1; 63 while (Result>=0) and (FData[Result].FormatID<>FormatID) do 64 dec(Result); 65 FormatAdded:=false; 66 if (Result<0) and CreateIfNotExists then begin 67 // add new format 68 inc(FCount); 69 NewSize:=SizeOf(TClipboardData)*FCount; 70 ReallocMem(FData,NewSize); 71 Result:=FCount-1; 72 FData[Result].FormatID:=FormatID; 73 FData[Result].Stream:=TMemoryStream.Create; 74 FSupportedFormatsChanged:=true; 75 FormatAdded:=true; 76 end; 77 if not IsUpdating then begin 78 // CreateIfNotExists = true means changing the clipboard 79 // => we need OwnerShip for that 80 if CreateIfNotExists and (not GetOwnerShip) then begin 81 // getting ownership failed 82 if FormatAdded then begin 83 // undo: remove added format 84 // Note: This creates a little overhead in case of an error, but reduces 85 // overhead in case of everything works 86 FData[Result].Stream.Free; 87 NewSize:=SizeOf(TClipboardData)*FCount; 88 ReallocMem(FData,NewSize); 89 end; 90 Result:=-1; 91 raise Exception.Create('Unable to get clipboard ownership for '+ 92 ClipboardTypeName[ClipboardType]); 93 end; 94 end; 95 //DebugLn('[TClipboard.IndexOfCachedFormatID] END ',ClipboardTypeName[ClipboardType] 96 //,' Format=',FormatID,' CreateIfNotExists=',CreateIfNotExists,' Result=',Result); 97end; 98 99function TClipboard.AddFormat(FormatID: TClipboardFormat; 100 Stream: TStream): Boolean; 101// copy Stream to a MemoryStream, add it to cache and tell the interface object 102var 103 OldPosition: TStreamSeekType; 104 i: integer; 105begin 106 //DebugLn('[TClipboard.AddFormat - Stream] A ',ClipboardTypeName[ClipboardType],' Format=',FormatID); 107 Result:=false; 108 BeginUpdate; 109 try 110 i:=IndexOfCachedFormatID(FormatID,true); 111 if i<0 then exit; 112 if FData[i].Stream<>Stream then begin 113 if Stream<>nil then begin 114 OldPosition:=Stream.Position; 115 FData[i].Stream.LoadFromStream(Stream); 116 Stream.Position:=OldPosition; 117 end else 118 FData[i].Stream.Clear; 119 FSupportedFormatsChanged:=true; 120 end; 121 finally 122 Result:=EndUpdate; 123 end; 124end; 125 126function TClipboard.AddFormat(FormatID: TClipboardFormat; 127 var Buffer; Size: Integer): Boolean; 128var i: integer; 129begin 130 //DebugLn('[TClipboard.AddFormat - Buffer] A ',ClipboardTypeName[ClipboardType],' Format=',FormatID,' Size=',Size); 131 Result:=false; 132 BeginUpdate; 133 try 134 i:=IndexOfCachedFormatID(FormatID,true); 135 if i<0 then exit; 136 FData[i].Stream.Clear; 137 if Size>0 then 138 FData[i].Stream.Write(Buffer,Size); 139 finally 140 Result:=EndUpdate; 141 end; 142end; 143 144function TClipboard.SetFormat(FormatID: TClipboardFormat; 145 Stream: TStream): Boolean; 146// copy Stream to a MemoryStream, set the cache and tell the interface object 147begin 148 BeginUpdate; 149 try 150 Clear; 151 AddFormat(FormatID,Stream); 152 finally 153 Result:=EndUpdate; 154 end; 155end; 156 157procedure TClipboard.Clear; 158var i: integer; 159begin 160 //DebugLn('[TClipboard.Clear] A ',ClipboardTypeName[ClipboardType]); 161 if FData<>nil then begin 162 for i:=0 to FCount-1 do 163 FData[i].Stream.Free; 164 FreeMem(FData,SizeOf(TClipboardData)*FCount); 165 FData:=nil; 166 end; 167 FCount:=0; 168 //DebugLn('[TClipboard.Clear] END ',ClipboardTypeName[ClipboardType]); 169end; 170 171procedure TClipboard.Open; 172// Open and Closed must be balanced. 173// When the Clipboard is Open, it will not read/write from/to the interface. 174// Instead it will collect all changes until Close is called. 175// It will then try to commit all changes as one block. 176begin 177 BeginUpdate; 178end; 179 180 181procedure TClipboard.Close; 182begin 183 EndUpdate; 184end; 185 186procedure TClipboard.InternalOnRequest( 187 const RequestedFormatID: TClipboardFormat; AStream: TStream); 188begin 189 //DebugLn('[TClipboard.InternalOnRequest] A ',ClipboardTypeName[ClipboardType] 190 //,' RequestedFormatID=',RequestedFormatID,' AStream=',AStream<>nil,' Allocated=',FAllocated); 191 if not FAllocated then exit; 192 if (RequestedFormatID=0) then begin 193 // loosing ownership 194 FAllocated:=false; 195 if Assigned(FOnRequest) then FOnRequest(RequestedFormatID,AStream); 196 FOnRequest:=nil; 197 end else begin 198 GetFormat(RequestedFormatID,AStream); 199 end; 200end; 201 202function TClipboard.GetOwnerShip: boolean; 203var 204 FormatList: PClipboardFormat; 205 i: integer; 206begin 207 if (not FAllocated) or FSupportedFormatsChanged then begin 208 GetMem(FormatList,SizeOf(TClipboardFormat)*FCount); 209 for i:=0 to FCount-1 do 210 FormatList[i]:=FData[i].FormatID; 211 //DebugLn(['[TClipboard.GetOwnerShip] A ',ClipboardTypeName[ClipboardType],' Allocated=',FAllocated]); 212 FAllocated:=true; 213 if not ClipboardGetOwnerShip(ClipboardType,@InternalOnRequest,FCount, 214 FormatList) 215 then 216 FAllocated:=false; 217 FreeMem(FormatList); 218 FSupportedFormatsChanged:=false; 219 end; 220 Result:=FAllocated; 221 //DebugLn('[TClipboard.GetOwnerShip] END ',ClipboardTypeName[ClipboardType],' Allocated=',FAllocated); 222end; 223 224procedure TClipboard.SetOnRequest(AnOnRequest: TClipboardRequestEvent); 225begin 226 if Assigned(FOnRequest) then 227 // tell the old owner, that it lost the ownership 228 FOnRequest(0,nil); 229 FOnRequest:=AnOnRequest; 230end; 231 232procedure TClipboard.BeginUpdate; 233begin 234 Inc(FOpenRefCount); 235end; 236 237function TClipboard.EndUpdate: Boolean; 238begin 239 if FOpenRefCount = 0 then 240 RaiseGDBException('TClipboard.EndUpdate'); 241 Result:=true; 242 Dec(FOpenRefCount); 243 if FOpenRefCount = 0 then begin 244 if FSupportedFormatsChanged then begin 245 Result:=GetOwnerShip; 246 if not Result then 247 Clear; 248 end; 249 end; 250end; 251 252function TClipboard.IsUpdating: Boolean; 253begin 254 Result:=FOpenRefCount>0; 255end; 256 257function TClipboard.CanReadFromInterface: Boolean; 258begin 259 Result:=FAllocated and (not IsUpdating); 260end; 261 262function TClipboard.CanReadFromCache: Boolean; 263begin 264 Result:=FAllocated or IsUpdating; 265end; 266 267procedure TClipboard.OnDefaultFindClass(Reader: TReader; 268 const AClassName: string; var ComponentClass: TComponentClass); 269var 270 PersistentClass: TPersistentClass; 271begin 272 if Reader=nil then ; 273 PersistentClass:=FindClass(AClassName); 274 if (PersistentClass<>nil) and (PersistentClass.InheritsFrom(TComponent)) then 275 ComponentClass:=TComponentClass(PersistentClass); 276end; 277 278function TClipboard.GetFormat(FormatID: TClipboardFormat; 279 Stream: TStream): Boolean; 280// request data from interface object or copy cached data to Stream 281var i: integer; 282begin 283 //DebugLn('[TClipboard.GetFormat] A ',ClipboardTypeName[ClipboardType],' FormatID=',FormatID,' ',ClipboardFormatToMimeType(FormatID),' Allocated=',fAllocated); 284 Result:=false; 285 if Stream=nil then exit; 286 if FormatID=0 then exit; 287 if CanReadFromCache then begin 288 if Assigned(FOnRequest) then begin 289 FOnRequest(FormatID,Stream); 290 Result:=true; 291 end else begin 292 i:=IndexOfCachedFormatID(FormatID,false); 293 if i<0 then 294 Result:=false 295 else begin 296 FData[i].Stream.Position:=0; 297 if Stream is TMemoryStream then 298 TMemoryStream(Stream).SetSize(Stream.Position+FData[i].Stream.Size); 299 Stream.CopyFrom(FData[i].Stream,FData[i].Stream.Size); 300 Result:=true; 301 end; 302 end; 303 end else begin 304 // not the clipboard owner -> request data 305 Result:=ClipboardGetData(ClipboardType,FormatID,Stream); 306 end; 307 //DebugLn('[TClipboard.GetFormat] END ',ClipboardTypeName[ClipboardType],' FormatID=',FormatID,' Result=',Result); 308end; 309 310function TClipboard.SetComponent(Component: TComponent): Boolean; 311var 312 i: integer; 313 s: TMemoryStream; 314begin 315 BeginUpdate; 316 try 317 i:=IndexOfCachedFormatID(PredefinedClipboardFormat(pcfComponent),true); 318 s:=FData[i].Stream; 319 s.Clear; 320 WriteComponentAsBinaryToStream(s,Component); 321 s.Position:=0; 322 FSupportedFormatsChanged:=true; 323 finally 324 Result:=EndUpdate; 325 end; 326end; 327 328function TClipboard.SetComponentAsText(Component: TComponent): Boolean; 329var 330 MemStream: TMemoryStream; 331 s: string; 332begin 333 BeginUpdate; 334 MemStream:=nil; 335 try 336 MemStream:=TMemoryStream.Create; 337 WriteComponentAsTextToStream(MemStream,Component); 338 SetLength(s,MemStream.Size); 339 MemStream.Position:=0; 340 if s<>'' then 341 MemStream.Read(s[1],length(s)); 342 AsText:=s; 343 finally 344 MemStream.Free; 345 Result:=EndUpdate; 346 end; 347end; 348 349function TClipboard.GetComponent(Owner, Parent: TComponent): TComponent; 350begin 351 Result:=nil; 352 GetComponent(Result,@OnDefaultFindClass,Owner,Parent); 353end; 354 355procedure TClipboard.GetComponent(var RootComponent: TComponent; 356 OnFindComponentClass: TFindComponentClassEvent; Owner: TComponent; 357 Parent: TComponent); 358var 359 MemStream: TMemoryStream; 360begin 361 MemStream:=TMemoryStream.Create; 362 try 363 if GetFormat(PredefinedClipboardFormat(pcfComponent),MemStream) then begin 364 MemStream.Position := 0; 365 ReadComponentFromBinaryStream(MemStream,RootComponent, 366 OnFindComponentClass,Owner,Parent); 367 end; 368 finally 369 MemStream.Free; 370 end; 371end; 372 373procedure TClipboard.GetComponentAsText(var RootComponent: TComponent; 374 OnFindComponentClass: TFindComponentClassEvent; Owner: TComponent; 375 Parent: TComponent); 376var 377 s: String; 378 MemStream: TMemoryStream; 379begin 380 MemStream:=nil; 381 try 382 MemStream:=TMemoryStream.Create; 383 s:=AsText; 384 if s<>'' then 385 MemStream.Write(s[1],length(s)); 386 MemStream.Position:=0; 387 ReadComponentFromTextStream(MemStream,RootComponent,OnFindComponentClass, 388 Owner,Parent); 389 finally 390 MemStream.Free; 391 end; 392end; 393 394function TClipboard.SetBuffer(FormatID: TClipboardFormat; 395 var Buffer; Size: Integer): Boolean; 396var i: integer; 397begin 398 BeginUpdate; 399 try 400 i:=IndexOfCachedFormatID(FormatID,true); 401 FData[i].Stream.Clear; 402 if Size>0 then begin 403 FData[i].Stream.Write(Buffer,Size); 404 FData[i].Stream.Position:=0; 405 end; 406 FSupportedFormatsChanged:=true; 407 finally 408 Result:=EndUpdate; 409 end; 410end; 411 412procedure TClipboard.SetTextBuf(Buffer: PChar); 413begin 414 if Buffer=nil then Buffer:=#0; 415 SetBuffer(PredefinedClipboardFormat(pcfText),Buffer^,StrLen(Buffer)+1); 416end; 417 418function TClipboard.GetTextBuf(Buffer: PChar; BufSize: Integer): Integer; 419var MemStream: TMemoryStream; 420begin 421 Result:=0; 422 if (Buffer=nil) or (BufSize=0) then exit; 423 MemStream:=TMemoryStream.Create; 424 try 425 if GetFormat(PredefinedClipboardFormat(pcfText),MemStream) then begin 426 MemStream.Position:=0; 427 Result:=BufSize; 428 if Result>MemStream.Size then Result:=integer(MemStream.Size); 429 if Result>0 then 430 MemStream.Read(Buffer^,Result); 431 Buffer[Result]:=#0; 432 Result:=StrLen(Buffer); 433 end; 434 finally 435 MemStream.Free; 436 end; 437end; 438 439procedure TClipboard.SetAsText(const Value: string); 440var s: string; 441 i: Integer; 442begin 443 //DebugLn('[TClipboard.SetAsText] A ',ClipboardTypeName[ClipboardType],' "',Value,'"'); 444 if Assigned(FOnRequest) then exit; 445 if Value<>'' then 446 s:=Value 447 else 448 s:=#0; 449 Clear; 450 i := Length(Value); 451 if ClipboardFormatNeedsNullByte(pcfText) then 452 i := i + 1; 453 SetBuffer(PredefinedClipboardFormat(pcfText),s[1],i); 454 //DebugLn('[TClipboard.SetAsText] END ',ClipboardTypeName[ClipboardType],' "',Value,'"'); 455end; 456 457function TClipboard.GetAsText: string; 458var 459 MemStream: TMemoryStream; 460 ASize: int64; 461begin 462 //DebugLn('[TClipboard.GetAsText] A ',ClipboardTypeName[ClipboardType]); 463 Result:=''; 464 MemStream:=TMemoryStream.Create; 465 try 466 if GetFormat(PredefinedClipboardFormat(pcfText),MemStream) then begin 467 ASize:=MemStream.Size; 468 if (ASize>0) and (pchar(MemStream.Memory)[ASize-1]=#0) then 469 Dec(ASize); 470 MemStream.Position:=0; 471 SetLength(Result,ASize); 472 if ASize>0 then 473 MemStream.Read(Result[1],ASize); 474 end; 475 finally 476 MemStream.Free; 477 end; 478 //DebugLn('[TClipboard.GetAsText] END ',ClipboardTypeName[ClipboardType],' "',dbgstr(Result),'"'); 479end; 480 481procedure TClipboard.SupportedFormats(List: TStrings); 482var cnt, i: integer; 483 FormatList: PClipboardFormat; 484begin 485 //DebugLn('[TClipboard.SupportedFormats]'); 486 List.Clear; 487 if CanReadFromCache then begin 488 for i:=0 to FCount-1 do 489 List.Add(ClipboardFormatToMimeType(FData[i].FormatID)); 490 end else begin 491 FormatList:=nil; 492 if ClipboardGetFormats(ClipboardType,cnt,FormatList) then begin 493 for i:=0 to cnt-1 do 494 List.Add(ClipboardFormatToMimeType(FormatList[i])); 495 end; 496 if FormatList<>nil then FreeMem(FormatList); 497 end; 498end; 499 500procedure TClipboard.SupportedFormats(var AFormatCount: integer; 501 var FormatList: PClipboardFormat); 502var i: integer; 503begin 504 AFormatCount:=0; 505 FormatList:=nil; 506 if CanReadFromCache then begin 507 if (FCount>0) then begin 508 GetMem(FormatList,SizeOf(TClipBoardFormat)*FCount); 509 for i:=0 to FCount-1 do 510 FormatList[i]:=FData[i].FormatID; 511 AFormatCount:=FCount; 512 end; 513 end else begin 514 ClipboardGetFormats(ClipboardType,AFormatCount,FormatList); 515 end; 516end; 517 518function TClipboard.SetSupportedFormats(AFormatCount: integer; 519 FormatList: PClipboardFormat): Boolean; 520var i: integer; 521begin 522 BeginUpdate; 523 try 524 Clear; 525 FCount:=AFormatCount; 526 GetMem(FData,SizeOf(TClipboardData)*FCount); 527 for i:=0 to FCount-1 do begin 528 FData[i].FormatID:=FormatList[i]; 529 FData[i].Stream:=TMemoryStream.Create; 530 end; 531 FSupportedFormatsChanged:=true; 532 finally 533 Result:=EndUpdate; 534 end; 535end; 536 537function TClipboard.FindPictureFormatID: TClipboardFormat; 538var 539 List: PClipboardFormat; 540 cnt, i: integer; 541begin 542 //DebugLn('[TClipboard.FindPictureFormatID]'); 543 List:=nil; 544 Result:=0; 545 cnt:=0; 546 try 547 if not CanReadFromCache then begin 548 if not ClipboardGetFormats(ClipboardType,cnt,List) then 549 exit; 550 for i:=0 to cnt-1 do begin 551 Result:=List[i]; 552 if TPicture.SupportsClipboardFormat(Result) then 553 exit; 554 end; 555 end else begin 556 for i:=FCount-1 downto 0 do begin 557 Result:=FData[i].FormatID; 558 if TPicture.SupportsClipboardFormat(Result) then 559 exit; 560 end; 561 end; 562 finally 563 if List<>nil then FreeMem(List); 564 end; 565 Result:=0; 566end; 567 568function TClipboard.FindFormatID(const FormatName: string): TClipboardFormat; 569var 570 List: PClipboardFormat; 571 cnt, i: integer; 572begin 573 //DebugLn('[TClipboard.FindPictureFormatID]'); 574 List:=nil; 575 Result:=0; 576 cnt:=0; 577 try 578 if not CanReadFromCache then begin 579 if not ClipboardGetFormats(ClipboardType,cnt,List) then 580 exit; 581 for i:=0 to cnt-1 do begin 582 Result:=List[i]; 583 if CompareText(ClipboardFormatToMimeType(Result),FormatName)=0 then 584 exit; 585 end; 586 end else begin 587 for i:=FCount-1 downto 0 do begin 588 Result:=FData[i].FormatID; 589 if CompareText(ClipboardFormatToMimeType(Result),FormatName)=0 then 590 exit; 591 end; 592 end; 593 finally 594 if List<>nil then FreeMem(List); 595 end; 596 Result:=0; 597end; 598 599function TClipboard.HasPictureFormat: boolean; 600begin 601 Result:=FindPictureFormatID<>0; 602end; 603 604function TClipboard.HasFormat(FormatID: TClipboardFormat): Boolean; 605// ask widgetset 606var List: PClipboardFormat; 607 cnt, i: integer; 608begin 609 //DebugLn('[TClipboard.HasFormat] A ',ClipboardTypeName[ClipboardType],' Allocated=',FAllocated); 610 if FormatID<>0 then begin 611 if CanReadFromCache then 612 Result := (IndexOfCachedFormatID(FormatID,false)>=0) 613 else begin 614 if not ClipboardGetFormats(ClipboardType,cnt,List) then begin 615 Result:=false; 616 exit; 617 end; 618 i:=0; 619 //for i:=0 to cnt-1 do 620 //DebugLn('[TClipboard.HasFormat] ',FormatID,' ',List[i]); 621 while (i<cnt) and (List[i]<>FormatID) do inc(i); 622 Result := i<cnt; 623 if List<>nil then FreeMem(List); 624 end; 625 if not Result then begin 626 Result := (PredefinedClipboardFormat(pcfPicture)=FormatID) and (HasPictureFormat); 627 end; 628 end else 629 Result:=false; 630 //DebugLn('[TClipboard.HasFormat] END ',ClipboardTypeName[ClipboardType],' FormatID=',FormatID,' Result=',Result); 631end; 632 633function TClipboard.HasFormatName(const FormatName: string): Boolean; 634begin 635 Result:=FindFormatID(FormatName)<>0; 636end; 637 638procedure TClipboard.AssignToPicture(Dest: TPicture); 639var 640 FormatID: TClipboardFormat; 641begin 642 FormatID:=FindPictureFormatID; 643 if FormatID=0 then exit; 644 Dest.LoadFromClipboardFormatID(ClipboardType,FormatID); 645end; 646 647procedure TClipboard.AssignPicture(Source: TPicture); 648begin 649 AssignGraphic(Source.Graphic); 650end; 651 652function TClipboard.AssignToGraphic(Dest: TGraphic): boolean; 653var 654 MimeTypes: TStringList; 655 i: Integer; 656 GraphicFormatID: TClipboardFormat; 657begin 658 Result:=false; 659 MimeTypes:=TStringList.Create; 660 try 661 Dest.GetSupportedSourceMimeTypes(MimeTypes); 662 for i:=0 to MimeTypes.Count-1 do begin 663 GraphicFormatID:=FindFormatID(MimeTypes[i]); 664 if GraphicFormatID<>0 then begin 665 AssignToGraphic(Dest,GraphicFormatID); 666 Result:=true; 667 exit; 668 end; 669 end; 670 finally 671 MimeTypes.Free; 672 end; 673end; 674 675function TClipboard.AssignToGraphic(Dest: TGraphic; FormatID: TClipboardFormat 676 ): boolean; 677var 678 MemStream: TMemoryStream; 679begin 680 Result:=false; 681 if FormatID=0 then exit; 682 MemStream:=TMemoryStream.Create; 683 try 684 if not GetFormat(FormatID,MemStream) then exit; 685 MemStream.Position:=0; 686 Dest.LoadFromMimeStream(MemStream,ClipboardFormatToMimeType(FormatID)); 687 finally 688 MemStream.Free; 689 end; 690 Result:=true; 691end; 692 693procedure TClipboard.AssignGraphic(Source: TGraphic); 694var 695 MimeType: String; 696 FormatID: TClipboardFormat; 697begin 698 MimeType := Source.MimeType; 699 FormatID:=ClipboardRegisterFormat(MimeType); 700 if FormatID<>0 then 701 AssignGraphic(Source,FormatID); 702end; 703 704procedure TClipboard.AssignGraphic(Source: TGraphic; FormatID: TClipboardFormat); 705var 706 MemStream: TMemoryStream; 707begin 708 MemStream:=TMemoryStream.Create; 709 try 710 Source.SaveToStream(MemStream); 711 MemStream.Position:=0; 712 SetFormat(FormatID,MemStream); 713 finally 714 MemStream.Free; 715 end; 716end; 717 718procedure TClipboard.Assign(Source: TPersistent); 719begin 720 if Source is TPicture then 721 AssignPicture(TPicture(Source)) 722 else if Source is TGraphic then 723 AssignGraphic(TGraphic(Source)) 724 else 725 inherited Assign(Source); 726end; 727 728procedure TClipboard.AssignTo(Dest: TPersistent); 729begin 730 if Dest is TPicture then 731 AssignToPicture(TPicture(Dest)) 732 else if Dest is TGraphic then 733 AssignToGraphic(TGraphic(Dest)) 734 else 735 inherited AssignTo(Dest); 736end; 737 738function TClipboard.GetFormatCount: Integer; 739// ask widgetset 740var List: PClipboardFormat; 741begin 742 //DebugLn('[TClipboard.GetFormatCount]'); 743 if CanReadFromCache then 744 Result:=FCount 745 else begin 746 Result:=0; 747 if ClipboardGetFormats(ClipboardType,Result,List) then begin 748 if List<>nil then FreeMem(List); 749 end else 750 Result:=0; 751 end; 752end; 753 754function TClipboard.GetFormats(Index: Integer): TClipboardFormat; 755var 756 List: PClipboardFormat; 757 cnt: integer; 758begin 759 //DebugLn('[TClipboard.GetFormats] Index=',Index); 760 if CanReadFromCache then begin 761 if (Index<0) or (Index>=FCount) then 762 raise Exception.Create('TClipboard.GetFormats: Index out of bounds: Index=' 763 +IntToStr(Index)+' Count='+IntToStr(FCount)); 764 Result:=FData[Index].FormatID; 765 end else begin 766 if ClipboardGetFormats(ClipboardType,cnt,List) then begin 767 if (Index>=0) and (Index<cnt) then 768 Result:=List[Index] 769 else 770 Result:=0; 771 if List<>nil then FreeMem(List); 772 end else 773 Result:=0; 774 end; 775end; 776 777{ Retrieves html formatted text from the clipboard. If ExtractFragmentOnly is 778 true then only the relevant html fragment is returned, the rest of the html 779 string is dropped. The Office applications in Windows and Linux write the 780 full html code which can be retrieved with ExtractFragmentOnly = false. 781 In case of Windows, the MS header is automatically removed.} 782function TClipboard.GetAsHtml(ExtractFragmentOnly: Boolean): String; 783var 784 Stream: TMemoryStream; 785 bom: TBOM; 786 US: UnicodeString; 787begin 788 //debugln(['TClipboard.GetAsHtml: ExtractFragmentOnly = ',ExtractFragmentOnly]); 789 Result := ''; 790 if (CF_HTML = 0) or not HasFormat(CF_HTML) then 791 begin 792 //debugln(['TClipboard.GetAsHtml: CF_HTML= ',CF_HTML,' HasFormat(CF_HTML) = ',HasFormat(CF_HTML)]); 793 exit; 794 end; 795 796 Stream := TMemoryStream.Create; 797 try 798 if not GetFormat(CF_HTML, Stream) then 799 begin 800 //debugln(['TClipboard.GetAsHtml: GetFormat(CF_HTML, stream) = False']); 801 exit; 802 end; 803 Stream.Write(#0#0, Length(#0#0)); 804 805 bom := GetBomFromStream(Stream); 806 case Bom of 807 bomUtf8: 808 begin 809 Stream.Position := 3; 810 SetLength(Result, Stream.Size - 3); 811 Stream.Read(Result, Stream.Size - 3); 812 //ClipBoard may return a larger Stream than the size of the string 813 //this gets rid of it, since the string will end in a #0 (wide)char 814 Result := PAnsiChar(Result); 815 //debugln(['TClipboard.GetAsHtml: Found bomUtf8']); 816 end; 817 bomUTF16LE: 818 begin 819 Stream.Position := 2; 820 SetLength(US, Stream.Size - 2); 821 Stream.Read(US[1], Stream.Size - 2); 822 //ClipBoard may return a larger Stream than the size of the string 823 //this gets rid of it, since the string will end in a #0 (wide)char 824 US := PWideChar(US); 825 Result := Utf16ToUtf8(US); 826 //debugln(['TClipboard.GetAsHtml: FoundbomUtf16LE']); 827 end; 828 bomUtf16BE: 829 begin 830 //this may need swapping of WideChars???? 831 Stream.Position := 2; 832 SetLength(US, Stream.Size - 2); 833 Stream.Read(US[1], Stream.Size - 2); 834 //ClipBoard may return a larger Stream than the size of the string 835 //this gets rid of it, since the string will end in a #0 (wide)char 836 US := PWideChar(US); 837 Result := Utf16ToUtf8(US); 838 //debugln(['TClipboard.GetAsHtml: Found bomUtf16BE']); 839 end; 840 bomUndefined: 841 begin 842 //assume the first byte is part of the string and it is some AnsiString 843 //CF_HTML returns a string encoded as UTF-8 on Windows 844 Result := PAnsiChar(Stream.Memory); 845 //debugln(['TClipboard.GetAsHtml: Found bomUndefined']); 846 end; 847 end; 848 849 if (Result <> '') then begin 850 if ExtractFragmentOnly then 851 Result := ExtractHtmlFragmentFromClipBoardHtml(Result) 852 {$IFDEF WINDOWS} 853 else 854 Result := ExtractHtmlFromClipboardHtml(Result); 855 {$ENDIF} 856 end; 857 858 finally 859 Stream.Free; 860 end; 861end; 862 863{ Adds html-formatted text to the clipboard. The main Office applications in 864 Windows and Linux require a valid and complete html text (i.e. with <html> 865 and <body> tags), therefore we insert them if they are not present. 866 In case of Windows, a specific header will be added, 867 otherwise the format will not be recognized by the clipboard. 868 } 869procedure TClipboard.SetAsHtml(Html: String; const PlainText: String); 870var 871 Stream: TStream; 872 IsValid: Boolean; 873begin 874 if CF_HTML = 0 then 875 exit; 876 //If the HTML does not have correct <html><body> and closing </body></html> insert them 877 MaybeInsertHtmlAndBodyTags(HTML, IsValid); 878 if not IsValid then 879 exit; 880 881 {$IFDEF WINDOWS} 882 Stream := TStringStream.Create(InsertClipHeader(Html)); 883 {$ELSE} 884 Stream := TStringStream.Create(Html); 885 {$ENDIF} 886 try 887 Stream.Position := 0; 888 Clipboard.AddFormat(CF_HTML, Stream); 889 890 if (PlainText <> '') then 891 begin 892 Stream.Size := 0; 893 Stream.Position := 0; 894 Stream.WriteBuffer(Pointer(PlainText)^, Length(PlainText)+1); //Also write terminating zero 895 Stream.Position := 0; 896 ClipBoard.AddFormat(CF_TEXT, Stream); 897 end; 898 899 finally 900 Stream.Free; 901 end; 902end; 903 904procedure TClipboard.SetAsHtml(Html: String); 905begin 906 SetAsHtml(Html, ''); 907end; 908 909