Extended TClipboard implementation for FMX and VCL (CCR.Clipboard)

I’ve just pushed to GitHub (first time, so be gentle…) an extended, multi-platform TClipboard implementation for newer Delphi versions:

https://github.com/chrisrolliston/CCR.Clipboard

Where the platform allows, supports delayed rendering, virtual files, change notifications, and inter-process TClipboard-based drag and drop. The code originates from the FMX TClipboard I published a few years back, though is much extended, and was refactored to support the VCL too (XE2+). For more info, check out the readme first…

https://github.com/chrisrolliston/CCR.Clipboard/blob/master/Readme.md

… followed by the wiki pages for discussion of individual features, together with known issues and limitations:

https://github.com/chrisrolliston/CCR.Clipboard/wiki

Disclaimer: supporting multiple FMX versions ain’t no fun, so if you come to try it in XE4 or whatever and have an issue, I may not be able to help you. Also, if you’re interested in drag and drop on OS X, consider using my code with any version lower than XE8 a ‘proof of concept’ only…

 

Advertisements

Pearls in the ‘nextgen’-ified RTL source

Back in September 2012, a post appeared in non-tech reporting Delphi’s PCRE wrapper to be many, many times slower than Python’s. With sample code attached, the problem was undeniable, though the immediate cause was soon identified: the Delphi wrapper’s failure to include a ‘don’t validate the UTF8’ flag (PCRE was traditionally a UTF-8 based library, so the Delphi wrapper was using UTF8String). Putting everyone’s work together, I posted a QC report. Soon after doing that an even better solution was noted, which was for the wrapper to wrap a newer version of PCRE that supported UTF-16 internally, i.e. Delphi’s native string encoding, and so allow avoiding UTF-8 roundtrips entirely.

To be fair to Embarcadero, the second solution might have been considered a bit problematic in practice, given PCRE’s UTF-16 mode was only 6 months old at that point, and using it may have been tricky for OS X. This is because on that platform, the Delphi wrapper uses the system PCRE dylib rather than statically linking equivalent C object files, due to the fact DCCOSX only consumes object files produced by the Windows C++Builder compiler (or at least, only did when I last looked into the matter). On the other hand, the additional flag fix involves adding just a couple of lines… so perhaps it could be implemented fairly quickly?

Alas, but it isn’t been implemented as yet. Oh well – I can see shipping iOS and Android support were much bigger fish to fry. Does this mean the unit in question hasn’t been touched at all? Oh no: it has been extensively fiddled about with due to the fact the UTF8String type was removed from the so-called ‘nextgen’ (i.e., LLVM-based) compilers. As such, an elegant UTF8String interface has been replaced with an ordinary string one that now has to use the ugly ‘marshaller’ API and TBytes internally. Even worse, it now includes pearls like the following:

function CopyBytes(const S: TBytes; Index, Count: Integer): TBytes;
var
  Len, I: Integer;
begin
  Len := Length(S);
  if Len = 0 then
    Result := TEncoding.UTF8.GetBytes('')
  else
  begin
    if Index < 0 then Index := 0
    else if Index > Len then Count := 0;
    Len := Len - Index;
    if Count <= 0 then
      Result := TEncoding.UTF8.GetBytes('')
    else
    begin
      if Count > Len then Count := Len;
      SetLength(Result, Count);
      for I := 0 to Count - 1 do
        Result[I] := S[Index + I];
    end;
  end;
end;

If you’re reading this and thinking ‘oh no – it looks like the Move procedure has been removed!’, don’t worry, because it hasn’t. Likewise, Delphi hasn’t suddenly gone all Java-esque and dropped the equation of an empty dynamic array with a nil one – i.e., this code:

    Result := TEncoding.UTF8.GetBytes('')

really is what it seems, namely an obscure way of assigning nil that if you step through it, passes through several method calls and IF tests to do the deed.

FMX TClipboard now supports iOS

I’ve just checked in a revision of my open source FMX TClipboard implementation that has an iOS backend. This supports the current rather than the FPC-based version of ‘Delphi for iOS’, however the Windows and OS X backends still compile with XE2 and above.

In essence, the new code wraps the native iOS clipboard API (UIPasteboard) and presents it in a fashion that closely follows the VCL clipboard interface, just like the existing Windows and OS X support did the same for desktop platforms. For some reason Apple in their wisdom decided to make the iOS clipboard API similar yet randomly different to the OS X one, so even though there’s not masses of code, it was a bit fiddly to implement. Anyhow, I’ve also knocked out a little demo similar to the previous desktop one:

TiOSClipboard demo

Using (say) Photos, you can copy an image to the clipboard and paste it into the demo. Conversely, you can from the demo itself copy either just the text entered, just the image, both the text and the image as two representations of the same clipboard item, or both the text and image as a custom clipboard format. The ‘List Formats on Clipboard’ button, as its name implies, then lists the formats currently on the clipboard. This is what I get after copying an image from Photos on the iOS simulator:

Format List

Technically, the iOS clipboard, like the OS X one, can have multiple items, each with multiple representations. Since the multiple-item concept doesn’t exist on Windows (indeed, it didn’t exist on OS X originally either), my class is only concerned with the first item, which is what most applications only bother with anyhow.

If you want the code, the SVN URL for it and a few other pieces is the following:

http://delphi-foundations.googlecode.com/svn/trunk/FMX%20Utilities/

The core files are now CCR.FMXClipboard.pas, CCR.FMXClipboard.Apple.pasCCR.FMXClipboard.iOS.pas, CCR.FMXClipboard.Mac.pas and CCR.FMXClipboard.Win.pas, and together they stand alone.

My FMX TClipboard and TMacPreferencesIniFile implementations now compiling in XE4

A bit belatedly, but my FireMonkey TClipboard and TMacPreferencesIniFile/TApplePreferencesIniFile (*) implementations are now compiling with XE4 (thanks goes to Ken Schafer for prodding me in the case of the former). See my post from nearly a year ago for the details:

https://delphihaven.wordpress.com/2012/07/27/fmx-tclipboard-and-tmacpreferencesinifile/

With respect to TClipboard, I’ve also done the following:

  • Added a cfPNG TClipboardFormat identifier (this is the same as cfBitmap on OS X).
  • Fixed a bad assumption about bitmap pitches on Windows that was causing issues for some people.
  • Switched to using CF_DIBV5 internally when reading or writing bitmaps on Windows.
  • When assigning to a bitmap, cfPNG is now looked for first (this makes things works better with MS Word), and correspondingly, HasFormat(cfBitmap) now also checks for cfPNG as a special case.
  • On Windows again, when a source bitmap includes transparency, the outputted DIB has this transparency removed, however at the same time a PNG representation is added that maintains the original alpha channel.
  • For greater flexibility (e.g. when there is only PDF data on the clipboard), the Mac implementation may now use NSImage as an intermediary when assigning to a TBitmap.
  • For both Windows and OS X, added GetFormats and GetFormatName methods. Where the former returns an array of TClipboardFormat, the latter converts a TClipboardFormat to a string:
procedure TfrmClipboardDemo.btnListClick(Sender: TObject);
var
  Format: TClipboardFormat;
  S: string;
begin
  for Format in Clipboard.GetFormats do
    S := S + sLineBreak + Clipboard.GetFormatName(Format);
  if S = '' then
    S := 'Nothing is currently on the clipboard.'
  else
    S := 'The following formats are on the clipboard:' + S;
  MessageDlg(S, TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0)
end;

This is the result when I copy a word on a webpage in Safari:

One word copied from Safari

As this example hints at, it’s better to use Apple’s ‘UTI’ format for custom clipboard identifiers on OS X (e.g. com.mycompany.formatname, though ultimately just something.something, or something.something.something, etc.). While things still work if you don’t, failing to do so will lead the OS to allocate a parallel ‘dynamic’ UTI for you (e.g. dyn.a1oad0fg1bber13hthat600s0nand0nand0n). As such, I’ve tweaked the demo accordingly – click the ‘Copy as Custom Clipboard Format’ button then the ‘List’ one, and you get this:

Custom format list

(*) Update (17/9/13): as the code actually worked for iOS too, I’ve renamed the class to TApplePreferencesIniFile and its unit to CCR.PrefsIniFile.Apple.pas; the CreateUserPreferencesIniFile helper function has also now moved to CCR.PrefsIniFile.pas. For XE5, I’ve also written a TAndroidPreferencesIniFile class – see here.

How to have a blue FMX TPanel, redux

Back in the XE2 timeframe, I published a short post about giving a TPanel a custom colour. While the solution still works in XE4, give or take an extra unit in the uses clause or use of the TAlphaColors.ColorName syntax rather than claColorName, nowadays I’d emphasise the first part of my original advice. This was quite simple – don’t try and give a TPanel a custom colour in the first place! If you want an irregularly styled TPanel, use a TRectangle instead, whose colours you can customise to your heart’s content:

  1. Add a TRectangle to the form.
  2. Change Stroke.Color to Gray.
  3. Change Fill.Color to whatever you want.
  4. At runtime, change the colour with code like this:
MyRectangle.Fill.Color := TAlphaColors.Blue;

Annoyingly, and dating from the infamous XE2 update 4, TRectangle is made to pretend it can’t parent other controls at designtime. As such, when a TRectangle is selected and you double click on a control type in the Tool Palette, the new control is added parented to the rectangle’s parent, not the rectangle itself. However, you can use the Structure pane top left to reparent controls as you wish.

As an aside, this highlights a key difference between FMX and the VCL: in FMX, all controls can potentially be parents to any other. Once you grasp this point, obsessing over wanting to use a TPanel specifically, even though you don’t want the normal TPanel ‘look’, is completely illogical.