On first experimenting with FMX and OS X back last September/October, I wrote both a FMX TClipboard implementation and a TCustomIniFile descendant that delegates to the Mac Preferences API. The latter serves as a rough analogue of TRegistryIniFile on Windows; as for the former, I wrote it because the stock FMX clipboard support is crap. If anyone is interested, the source is available on Google Code here (the SVN URL is http://delphi-foundations.googlecode.com/svn/trunk/FMX%20Utilities/). Suffice to say, I don’t claim any particular originality (another FMX TClipboard implementation is floating about here for example), however both classes were written without reference to alternative implementations.
In terms of approach, my Mac Preferences wrapper uses the native typing as appropriate when writing, but string-based auto-conversions as required on read. To that extent, it behaves similarly to TRegistryIniFile.
In itself, the Mac Preferences API only directly supports a single level of settings – in other words, a preferences file (‘plist’) is just a flat list of keys and values. This is a problem for any TCustomIniFile descendant that wraps it, since the TCustomIniFile interface assumes at least two levels (‘sections’, then keys-and-values). In order to create a hierarchy, the Apple-promoted workaround is to write a CFDictionary (or NSDictionary, if using the Objective-C interface) for a ‘value’. However, this is a rather impractical solution due to dubious API decisions on Apple’s part – in order to change a single value more than one level down, a CFDictionary-based approach requires you to load the whole branch, copy it to a ‘mutable’ CFDictionary, make the amendment, then rewrite the whole branch again.
Instead of that, I have adopted the approach taken by both TextWrangler and Microsoft Word, which is to fake a preference hierarchy by using a delimiter character embedded in the ‘real’ key names. By default a colon is used, like TextWrangler (Word uses a backslash), though you can change this by setting the Delimiter property. If you write a value for the ‘Width’ key under the ‘General’ section, then, what actually happens is that a plist key called ‘General:Width’ is set.
Other than that, all of the TCustomIniFile interface is supported, with native implementations of ReadBinaryStream and WriteBinaryStream too. Lastly, since the Preferences API requires writes to be explicitly flushed, so does my wrapper, in the form of a call to the UpdateFile method. This is just like TMemIniFile, which requires you call UpdateFile to affect any changes too.
For more info about TMacPreferencesIniFile, check out either the source and/or a small demo I’ve written for it:
My FMX TClipboard class has the aim of providing the capabilities of the VCL equivalent for both Windows and OS X, no more no less: for example, it has an AsText property and HasFormat method, can be assigned to and from an FMX TBitmap, has basic custom clipboard format support, and supports adding multiple formats concurrently using the Open and Close methods. Its core functionality is used like this:
uses CCR.FMXClipboard; procedure TForm1.FormCreate(Sender: TObject); begin Application.OnIdle := ApplicationIdle; end; procedure TForm1.ApplicationIdle(Sender: TObject; var Done: Boolean); begin btnPasteImage.Enabled := Clipboard.HasFormat(cfBitmap); btnPasteText.Enabled := Clipboard.HasFormat(cfText); end; procedure TForm1.btnCopyImageClick(Sender: TObject); begin Clipboard.Assign(ImageControl1.Bitmap); end; procedure TForm1.btnCopyTextClick(Sender: TObject); begin Clipboard.AsText := Memo1.Text; end; procedure TForm1.btnCopyImageAndTextClick(Sender: TObject); begin Clipboard.Open; try Clipboard.AsText := Memo1.Text; Clipboard.Assign(ImageControl1.Bitmap); finally Clipboard.Close; end; end; procedure TForm1.btnPasteImageClick(Sender: TObject); begin ImageControl1.Bitmap.Assign(Clipboard); end; procedure TForm1.btnPasteTextClick(Sender: TObject); begin Memo1.Text := Clipboard.AsText; end;
The cfText and cfBitmap thing imitates how the main FMX framework relabels the Windows ‘virtual key’ constants (i.e., VK_LEFT becomes vkLeft and so on). However, cfXXC values are typed (albeit weakly) to a TClipboardFormat opaque type. On Windows, instances of this type directly map to unsigned integer IDs like CF_TEXT and CF_BITMAP; on OS X, they are CFStringRef instances that hold OS X string IDs under the bonnet.
In order to register a custom format, you must call the global Clipboard object’s RegisterFormat method, passing a string identifier. Technically, OS X doesn’t require custom formats to be ‘registered’ at all. However, RegisterFormat provides consistency between platforms, returning a TClipboardFormat value you can use with HasFormat, various Assign overloads, and ToBytes and ToStream. The accompanying demo illutrates their use: