Author: Harry Stahl Delphi and FireMonkey are registered...
Transcript of Author: Harry Stahl Delphi and FireMonkey are registered...
Author:HarryStahlPublisher:HarryStahlLocation:BonnCopyright(2015),Allrightsreserved
DelphiandFireMonkeyareregisteredtrademarksofEmbarcadero.Apple,OSX,iOS,iPad,andiPhonearetrademarksofApple.WindowsisaregisteredtrademarkofMicrosoft.
ShouldIhaveusedthisbookatrademark,withoutreferringtotheowner,pleasesendmeashortmessage,Iwilladditimmediately.
1stedition
Content
Foreword
Introduction
Thebook
Theauthor
Contactinformation
Chapter1:UsingtheFireMonkeycomponents
Section1:GettingStarted
Section2:NewFireMonkeyproject
FireMonkeydesktopapplication(MultiDeviceApplication)
UsingtheMultiDeviceDesigner(FireUI)
ForminheritancewiththeMultiDeviceDesigner
Reverttoinheritedsettings
CreatingaPlatform-specificeventhandlerwiththeMultiDeviceDesigner
Section3:SelectedFireMonkeycomponents
TButton(mitTrimming)
TEdit(withoutPasswordChar)
TForm(furthermorewithcaption)
TFrame
TPanel
TCheckbox,TRadioButton(IsChecked)
TSwitch
TImage
TImageControl
TImageViewer
TImageViewer(tousewithLiveBindings-Designer)
TLabel(NewpropertyFontColor)
TImageList(Notavailable-butcompensationpossible)
TListBox(noTCheckListbox,butShowCheckboxes)
AllComponents(excepttheform)
SeveralComponents(Propertieswithadditionaltype-qualifying)
TMenuItem(withoutImageIndex)
TMainMenu(HandlingMACmenus)
TMemo(CaretPosition,noModified,FindNext-replacement)
TDropTarget(howDrag&DropworksinFireMonkey)
TRichEdit(Notavailalbe-butreplacementvia3rd-partypossible)
TPageControl(Notavailable-butreplacementavailable)
TStringGrid(worksdifferent)
TGrid(ImageandotherelementsintheGrid)
TStringGrid-alternative(TMSFMXGrid)
THeader(nosections,butitems)
THeaderControl(isnotavailableunderFireMonkey)
TProgressBar(not“position”butvalue)
TTabControl(noOwnerdraw)
TTrackbar(helpfulproperty“tracking”)
TSpeedButton(withoutBitmap)
TStatusbar(awaytocompensatethemissing“Panels”)
MessageDlg(e.g.notdirectlyusablewithmtWarning)
Section4:TheFireMonkeyStyle-Designer
a)UsingtheStylesEditor
b)StylesinFireMonkey-anoverview
c)ConvertVCLStylestoFireMonkeyStyles
d)UsingFireMonkeyStyles
Section5:ConvertVCLprogramsintoFireMonkeyprograms
a)WorkingwiththeMidaBasicEdition
b)WorkingwiththeMidaPro/StudioVersion
c)StrategicapproachtoworkingwiththeMidaConverter
Chapter2:TipsandtricksforCross-PlatformDevelopment
Section1:startingotherprograms
Section2:Gettheprogramdirectoryandprogramdatadirectory
Section3:Catchtotheprogrampassedstart-upparameters
Section4:“HelloWorld”-Multilingualprogramsandnewmarkets
Section5:ApplysandboxingandEntitlementsproperly
Section6:SandboxingandpersistentaccesstoBookmarks
Section7:UseMACAPIs(POSIX,COREandCocoa)inDelphi
Chapter3:RequirementsforCross-PlatformDevelopment
Section1:SettingupWindowsPCandMACPC
Section2:EnablingMACOSXPlatform
Section3:Provisioninganddeployment
1.SubmissiontotheAPPLEAppStore
2.Createa.dmgfilefordistributionoutsidetheAppleAppStore
3.CreateyourownsetuppackagewithApplicationDeveloperID/Installer
a)RequestaDeveloperIDcertificateandanApplicationDeveloperInstallerID
b)WorkingwiththecodesigningtoolandPackageMaker
Chapter4:WorkingwithGraphicsinFireMonkey
1.FireMonkeyTBitmapversusWindowsTBitmap
2.TBitmapDatainsteadScanLineforbitmapmanipulation
3.ChangethealphachannelofaTBitmap
4.Drawonthecanvasofabitmap
5.Turngraphics,flip,invertorcolortogray
6.Drawingabitmapscaled
Chapter5:UsefulthirdpartycomponentsforFireMonkey
1.TMS-Components
2.ReportgeneratorFastReportFMX
3.RemObjects-ApplicationFramework(Hydra)
4.Othercomponents
Chapter6:Howto-tips&tricksforFMX
R1…Getthedisplayresolution?
R2…CheckiftheEscape,CtrlorAltkeyispressed
R3…UsefoldernamesunderWindowsandMACproperly
R4…Usesearchmaskfor“allfiles”inWindowsandMACproperly
R5…Avoidloopingsymlinkfolders(Alias)
R6…Inwhichsituationsfilesymlinksfunctionsplayaroleotherwise
R7…Determinethecontrolunderthemouseposition
R8…findoutonwhichMACOSXoperatingsystemtheprogramisrunning
R9…determinethecurrentusernameinMacOSX/Windows
R10…Sendfilesasanattachmentofane-mailwiththesystemmailprogram
R11…providetheuserwithhelpfilesunderWin&MAC
R12…AfteruploadingtoAppStore:Invalidbinary-causesandremedies
R13…Applicationrejected:Somereasonsforrefusal,whichyoucanavoid
R14…UsingActiveControl
R15…ReplaceOnDrawItemeventoftheListBoxfromVCLwiththeOnPaintingeventoftheTListBoxItems
R16…LoadBitmapfromresourcefile(forretinadisplay)
R17…Swapitemsinalistbox
R18…SwapitemsinaListboxviaDrag&Drop
R19…UsingFMXfunctionsinaVCLapplicationviaDLL
R20…DrawtextinTGridright,orcentered
R21…DrawtextinTStringGridright,orcentered
R22…Dealingwiththe“visible”propertyofcontrols
R23…PreventunintendedshorteningofTLabeltext
R24…UsehintsinFireMonkey:Howitgoes
R25…Determinethedocumentdirectory
R26…Improvethefontquality(especiallyonWindows)
R27…Selectafolderwithadialog
R28…GetaccesstoacellcontrolofTGrids
R29…Showpop-upmenuataspecialposition
R30…Storeadditionalinformationinstandardobjects
R31…Draganddroptextfromexternalsource(egbrowser)toaTEditbox
R32…Acolumninastringgridshouldoccupytheremainingspace
Chapter7:UpgradingfromDelphiXE3-XE6toXE7
Chapter8:Outlook
Attachment1:UnitHSW.FMXSandbox.pas
Atachment2:NewlyimplementedOpenandSavedialogsforsandboxing
Foreword
AfterthefirstversionofDelphiwithFireMonkeyinDelphiXE2inlate2011,ithappenedinquicksuccession:attheendof2012alreadyXE4waspublishedandatendofSeptember2013itwasfollowedbyDelphiXE3,April2014thenXE5,April2014XE6andinSeptember2014thenXE7.AndXE8willbeexpectedinApril2015.
WithXE7youcan’tonlydevelopprogramsforWindowsandMacOSX,butalsoforIOSandAndroid.Thesearefantasticopportunitiesthatarenowpossiblewithasingledevelopmentenvironment.Nevertheless,theIOSandAndroiddevelopmentisnotthesubjectofthisbook.ItremainsasinthepreviousversionofthebooktoDelphiXE3-XE5incross-platformdevelopmentforWindowsandMACOSX.ForIOSandAndroid,Imuststillrefertootherdeals(wheretheyexist).
WhetheryouarestillbeginnersforFireMonkeyprogrammingorhavealreadygainedexperienceinthisfield,Iamassumingthatyouwillfindinformationinthebookthatwillhelpinthedevelopmentofyourprojects.
IfyouarestillworkingwithDelphiXE3-XE6,Irecommendthatyoupurchasethepreviousversionofthisbook,becausefromthebeginningofXE7alothaschangedinFireMonkeyandDelphi.Itwouldbebest,ifyouwouldhavethecurrentcompilerversionXE7available(orXE8,whenitisout).
ExperienceduserswhopreviouslydevelopedforWindowsarefaced-whenmigratingtoFireMonkey-withaseriesofquestionsthataresometimesnoteasytoanswer.Firstofall,thereistheestablishmentoftheconnectionbetweenWindowsPCandMACandthevarioussettingdialogsthatneedtobefoundout.
OftenthereareonlysmalldifferencesbetweenFireMonkey-andVCLcomponents,butbecauseofthat,onefailsindevelopmentwork.Itconsumesalotoftimetofindthedifferences.Thisiswhatthebookismadefor.Itexplainsthesmallandlargedifferencesamongthemostimportantofthewell-knownVCLcomponents.
TheuseofFireMonkeycomponentsmakesespeciallysense,iftheyareusedforcross-platformdevelopment(besidethis,ifexceptionalgraphicsprocessingisrequired).ButunderWindowsandMACsomefunctionsarecompletelydifferent,forexamplepassingparametersatthestartoftheprogram.
Tosaveyouhoursofsearch,besuretofindsomeoftheanswersyouneedinthisbook.Thisbookisalso“underconstruction”foranextendedperiodoftime(usuallyuntilthereisanewDelphiXExversionpublished).
IftheMACisnewtoyou,aswellyouwon’tevenknowbasicinformationaboutthehandlingoffiles,availablestoragelocationsorotherwiserequireddevelopertools.It’sallhereinthebook,atleastwiththemostimportantinformation.
Ifyouhaveanysuggestionsontopicswhichshouldbeincludedhere,writeanemailtotheauthor.
Theonlywarning:Thisbookisnotaboutthetopic“databases”.Thisisduetothepersonalcircumstancethattheauthordoesn’tusedatabasecomponentsofDelphibutapersonalsolutionforworkwithdatabases.
Onelastnote:ThebookwasoriginallywrittenbymeinGermanlanguage.AfterthatIhavetranslateditintoEnglishbymyself.Pleaseexcuse,ifoneortheothertranslationisnotasgallantasitshouldbe.Iwouldbepleasedtoreceivehintsconcerningpossibleimprovements.
BonninApril2015
IntroductionTheInternetisablessingandacurse.Youwillfindeverything,butatthesametimeyouarelost.Inadditiontosearchingtheinternet,personalstudiestothesourcesofDelphiandtheMACAPIlibrarieswerenecessarytointroducethesolutionsofferedinthebookhere.I’musingthesesolutionsinmyownprograms,suchasin“TEditorforMAC”
http://www.hastasoft.de/TEditor.htm,
withwhichonecanreadandwriteANSItextandUNICODEindifferentformats.Formeitisanindispensabletool,becauseforexampletheMACtexteditorcan’treadWindowsANSItexts(German“Umlaut”problem).
ThebookThisbookwillbeonceagainonlyavailableinanelectronic-kindle-version(lateraprintversionisplanned).ButitwillbealsoavailableinPDFformattodownloadfrommyDevpagewebsite(http://www.devpage.de).TheKindleversionhasgottheadvantagethatyoucanusethebookacrossmultipledevices.
Howtoreadthebook:IfyouhavenotsetupyourMactoworkwithDelphiyet,maybeyoustartwiththerelevantchapter(Chapter3,Section1).Ifyouhavealreadycompletedthesetup,youcanjuststartreadingfromthebeginning.
Note:Tofacilitatethesearchofspecificissues(e.g.informationonupdatesofthisbook),insomeplacesaheading“Quickfinderxxx”wasbeinsertedasreference(wherexxxisanumber).
TheauthorI’malittlelongertimeunderwaywithsoftwaredevelopment.IstartedwithTurboPascalversion3.0intheeighties,thenIbought5.0TurboPascalfor700DM,whichwasatthattimealotofmoneyforme.ApartfromasmalldetourtoVisualBasic,afterthisIremainedfaithfultoDelphiwithanyversionfromnumber1tothecurrentversionXE7.AllDoberenz/KowalskiDelphibooksareinmylibrary,aswellasanythingyoucangetaboutDelphibyElmarWarkenandprettymucheverythingfromtheC&LPublishing.AlsobooksbyMarcoCantuandBobSwartIcallmyown.
BynowI’vewrittendozensofprogramswithDelphi.Withityoucandeveloprobustandsophisticatedprogramswhichmorethan5,000customerswith10,000userscanacknowledge.
Contactinformation
Youcanfindmygeneralwebpageatwww.hastasoft.de,aspecialsitefordevelopersatwww.devpage.de,whereyouwillalsofindabriefdescriptionofsomeofmytoolsthatcanyouassistinprogramdevelopment.
Bye-mail,[email protected].
Chapter1:UsingtheFireMonkeycomponents
Section1:GettingStarted
ThisbookisnotsomuchtodescribetoyouthemanynewcomponentsthatoffersFireMonkey.Forthatyoucanusethesuppliedprogramexamplesthatgiveagoodoverviewhere.Forexample,opentheincludeddemo“ControlDemo”.TheFireMonkeydemoscanbefoundas:
FromtheStartmenuselecttheEmbarcaderoprogramgrouptogotothedemodirectory(examples).
Thisbookismoreabouttoremovethestumblingblocks,ifyouplantoconvertapreviouslydevelopedVCL-programintoaFireMonkey-programorstartnewprojectswithFireMonkey.
IfyouwanttohaveprogrammatichelptoconvertexistingVCLapplications,youcanusetheMidaconverter.ForawhiletheconverterwasasafreegiftwithpurchaseofDelphiXE7inaBASICversionhere:
InthepaidProversion(fromabout90,-€)alsothirdcomponentswillbeconverted(forexampleTMScomponents).Inaddition,thesourcecodeisrestatedasfaraspossible(siteofthemanufacturer:http://www.midaconverter.com).
InChapter1,Section5,IpresentthebasicuseoftheprogramandhavealookatthespecificsofPro/Studioversionoftheprogram.
Section2:NewFireMonkeyproject
DoubtlessthequestionarisesofhowandwhentogointodetailabouttheindividualdifferencesbetweenWindowsandFireMonkey.
ThebestwayisprobablytosimplydescribetheusualstepsofprogramdevelopmentandemphasizethedifferencesbetweentheknownVCLcomponentsandFireMonkeycomponentsattherelevantpoints.
FireMonkeydesktopapplication(MultiDeviceApplication)
So,thereyougo.AswithanyWindowsprojectyoustartdevelopingaFireMonkeyprogramtorunbothonWindowsandtheMacunderthe“File”menuwiththecommand“NewMultiDeviceApplication”.
NewsinceDelphiXE7:Therewillbeshownatemplatesdialog,whichallowsyoutogenerateeitheran“EmptyApplication”,a“3Dapplication”oranapplicationthatalreadyhascertaincontrolsintheform:
The“EmptyApplication”correspondstotheformerHDapplicationfromDelphiXE6orearlier.Theothertemplatesyouwouldnormallyuseratherformobileapplications(i.e.iOSandAndroid).Butherewewantdevelopanormaldesktopapplication,soselectthe
“BlankApplication”.ThenclickOK.
Result:
Bydefaultthe“TargetPlatforms”ontherightsideofthenodearecollapsed.Unfolded,itlookslikethis:
The32-bitWindowsplatformisenabledbydefault.Ifyouwouldcompiletheprogramnow,youwillgeta32-bitWindowsprogram.DelphihasalsobeenautomaticallyaddedaMACOSplatformandthemobileplatformsiOSandAndroid(butyoucanignorethemobileplatforms,ifyoudon’tneedit).
Youcouldsimplyselectoneofthelistedtargetplatformsbydoubleclickandcompileit,andyouwouldhaveacorrespondingtargetplatformapplication.TheprogramwillbeableactuallytorunonaMacoronamobiledevice,butyoustillhavetosetuptheenvironmentaccordingtoDelphi.HowtodothisfortheMAC,describedlaterinthisbook.
UsingtheMultiDeviceDesigner(FireUI)
Hereyoucannowgetstartedtoaddcomponents,settheirproperties,andwriteeventhandlerstointeractwiththeuser.Ifyoudevelopacross-platformproject,itisgenerallyrecommendedtoaddattheverybeginningaviewfortheMACOSPlatform.SoyoucanalsocompiletheMAC-versionfromtimetotimeandlook,howitworks.
However,youshouldworkasmuchaspossibleattheview“Master”andonlyusetheotherviewstomakespecificsettingsfortherelevantoperatingsystem.
Withtheleftdrop-downlist,youcanalsousethemasterviewwithdifferentstyles.
Inthevariouscreatedviews(OSX,Windows,etc.)youcanaddingcomponentsandchangetheirproperties,butnotdeletethem.Ifyouwanttoremoveacomponent,youhavetoactivatethemasterviewandthenremovethecomponent.Logically,itisthenremovedfromtheotherviews.Thesameprocedureisvalidforthenameofthecomponent:Herealsoyoucan-becausethenamemustbeunique-changeitonlyintheMasterView.
TheuseoftheMultiDeviceDesignerfacilitatesthedevelopmentofcrosscompile
applicationsenormous.Forexample,thebuttonsindialogboxesarelocatedatthebottomleftonWindows,andunderMACOSatthebottomright.Also,buttonsonMACOSnormallydonothavegraphics.
SohereistheviewofadialogfrommyaccountingprogramthatisavailableforWindowsandMacOSX:
Atthebottomleftarethebuttonswiththeimages.NowtotheMACOSXview:
Here,thebuttonsaremovedtotherightsideandimagesintheObjectinspectoraresettoVisible=false.Thecheckboxhasbeenmovedtotheleftside.
Thenicethingis,thatyousimplycanusethedesignerhereandnothavetomakeanyprogrammatictransformationsforthedialogatruntime.Inthepastthatwasthecommonandrathertime-consumingworking-method.
ForminheritancewiththeMultiDeviceDesigner
Also,itisaspace-savingmethod,becauseDelphicreatesforeachviewaseparateformandonlythisisincludedfortheappropriateplatform.Ifyoulookatthefilescreatedonceinafilemanager,thenthislooksasfollows:
FMandant.fmxisthemasterformfile.Thisfileservesasamasterfortherespectivegeneratedplatform.SoifwehadonlythismasterformandcreatetheprogramforMACOSX,thenonlythismasterformisused.
SincewewanttocreatebothaWindowsprogramaswellasoneforMACOSX,wehavehereselected“OSXDesktop”asviewintherightdrop-downlistsothatDelphithencreateaformforthisplatform.Thisisthefile“FMandant.Macintosh.fmx“.
Thespecificplatformformsworkontheprincipleofforminheritance(similarlikethe“TFrame”).WhenweopentheMACforminatexteditor,thenitlookslikethis:
Whiletheformmasterfileconsistsof250lines,thederivedMAC-formfilehasonly42lines.Thereasonis,thatonlythechangesinrelationtotherecognizedmasterformwillbesavedhere.Thebuttonisnowattherightside,insteadoftheleft,soithasadifferenthorizontalpositionandthisvalue(hereingreen)willbedetected.TheimagesymbolinthebuttonshouldbevisibleunderWindows,butnotunderMACOSXsoeventhisisnotedaccordingly(bluebackground).
Soyoucansimplymodifyforaparticularplatformtheposition,thecolor,thesizeorothercontent(e.g.textoflabels,buttons).
Reverttoinheritedsettings
Ifyouhavemadechangestoacontrolonadifferentplatformview,youcanpartiallyorcompletelyrevertthesettings,tothevaluesthatareinthemasterview.Example:
HerethebuttonhasbeenmovedontheMACviewtotherightandthetextwaschangedto“MAC”.
Ifyouwanttorestoreallsettingsofthebuttonsbacktotheinitialstate,clickwiththerightmousebuttononthebuttonandselectthe“ReverttoInherited”command:
ThebuttonisthenintheMACviewagainontheleftsideandhasagainthecaption“Button1”.
Doyouwante.g.onlyrestoretheoriginaltextofthebutton,clickonceonthebuttonwiththeleftmousebuttontoselectit.Then,intheObjectinspector,right-clickontheword“Text”(ieleftsideandnotintheeditbox)andselectthecommand“Reverttoinherited”.
Thebuttonstaysontheright,buthislabelwaschangedfrom“MAC”in“Button1”.
Soitispossibleonlytobringbackindividualpropertiestotheinitialstate,ifnecessary.
CreatingaPlatform-specificeventhandlerwiththeMultiDeviceDesigner
However,theMultiDeviceDesigneroffersyetafurthersimplification:Untilnowyou
haveeditedinanOnClickeventthesourcecodefortheusefordifferentplatforms.NormallyyouusetheIFDEFswitchtomakethesourcecodecompileableforeitherWindowsorforMac.Evenactuallyitmightbetherightway,ifyouhaveonlyafewdifferentplatform-specificstatementsinthecode.Butshouldthecodeverydifferentforeachplatform,youhavealotoftextintheeventhandlerandthemattercanbecomeunmanageable.ItlendsitselfdirectlytocreateaseparateeventhandlerfortheMACplatform.SoifyouhavecreatedanOnClickeventalreadyinthemaster,gototheView“MACOSDesktop”.
ThenselectthebuttonandaddintheOnClickeventsimplythewords“Mac”asviewableintheimageaboveandpresstheenterkey.AlreadyDelphicreatesanewemptyeventhandler,andyoucanfillitwithspecificsourcecodefortheMAC.
Smalldrawback:Ifthesourcecodecontainsplatform-specificcalls,youstillhavetoworkagainwithIFDEF,butonlyonce,becauseyoucansouroundthewholeinnerframewith{$IFDEFMACOS}…{$ENDIF}.
Overall,itisalwaysamatterofthecase,whichapproachisthebetter.
ThereisanotherinnovationinXE7:Foranumberofcomponentsyoucanforcertainproperties,e.g.insteadof“Top”or“Bottom”intheTTabPositionaTabControlsselecttheproperty“PlatformDefault”asinitialsetting.
SoevenifyouhaveonlyoneMasterViewanddonotgenerateplatform-specificviews,becauseofthe“PlatformDefault”anallplatformseverythingwouldbeoninitsproperplace.ThefunctionalitymentionedinthisexamplewouldbemorerelevantforiOSandAndroid,becausetherethetabsshouldbeinastandardequipmentdownorup.
Section3:SelectedFireMonkeycomponents
Onthebasisoftheindividualcomponentsandfeaturesthatyounormallyusehere,I’mnowgoingonebyonetotheindividualcomponents.
TButton(withTrimming)
Unfortunately,theFireMonkeybuttonhasnotsomanyskills,suchasthisonefromtheVCL,whowoninthelastfewyearsenormouspossibilities.
ButthereisalsosomethingthattheVCLbuttondoesnothave:Theproperty“Trimming“.Withthatyoucanspecifyhowtodealwiththedisplayedtextofthebutton,ifthetextistoolongtodisplay.
ThedefaultsettingisnowttCharacter.Soifthetextwillbedisplayedwithaletterand3pointsbetweentheouterbuttonborder.E.g.inthetext“Repetition”like“Repet…”.
IfyouselectttNone,soitbehavesasdescribedintheVCL,textisdisplayeduntiltheborderofthebutton,thencutoff.
WithttWordifavailable,thebordertodrawingsissetatafullword-border.
So“PrintandClose”wouldbeshortenedto“Printingand…”whentheword“Close”wouldnotfitinthedisplayareaofthebutton.
Finally,aTButtoncanhavealsoaconstantlypresseddownstatus.Youcandothisbysettingtheproperty“IsPressed“,butalsotheproperty“StayPressed”mustbeenabled.
Youcan’tassignanimagetothebutton(aswellastheTSpeedButtonnot).Soyouneedtoaddanextraimageintothebuttonwhenitistohaveasymbol.
Unfortunately,theTButtonandallotherFireMonkeycomponentshasnottheproperty“Hint”.Again,initiallyremainstheprincipleofhope,thathereacorrespondingpropertywillbeaddedinthefollowingupdates.
ButyoucanusemyHS_FMXHints.pasunitwithwhichyoucaneasilyaddhintstothecontrols.LookhereinChapter6,athint24attheendofthebook.
TEdit(withoutPasswordChar)
Ifaneditfieldshallnotdisplayitscontents,youwillnotreachthisasintheVCLwith
Edit.passwordchar:=‘#’;
butwith
Edit.password:=True;
NewsinceXE7:Thereisanewfeature“ControlType”,whichissetbydefaultto“Styled”.Ifyousetthisto“Platform”thenativeelementistusedontheplatforminyourform(currentlyonlyoniOS).Thiscanbeanadvantageforsomethings,whenitcomesaboutbytheuseofascreenreaderorsimilarthings,oreventogeta100%integrationplatformappearance.Ifyouchoosethisoption,itisalreadyshowingthiscontrolchangesatdesigntime:
InadditiontotheTEdit,the“ControlType”propertyisalsoavailablefortheTCalendarcontrol,effectiveonlyforiOS.Imentionthisfunctionalityhere,soyouknownowthemeaning,anontheotherhand,Iassumethatovertime,furthercontrolsareprovidedwiththispropertyandifyoualsouseiOSandAndroidnexttotheMACOSplatform,thisisanimportantinformationforyou.
TForm(furthermorewithcaption)
WhileactuallyallFireMonkeycomponentsarenotusingthe“caption”property,butthe“Text”property,theformalsohasfurthermoretheproperty“caption”.
FireMonkeyformshas(currently)noKeyPreviewproperty,asintheVCLform.However,theFormcangetalsotheFormKeyDown-info,evenifanothercontrolhasthefocus.Soifyouwriteaprogramwhereitisimportanttohaveacentrallocationwherethekeyboardinputwillbechecked,soyoucouldusetheKeyEventroutineofthefocusedcontrolandthenforwardittotheform.
Withtheproperty“FullScreen”youcanhavetheformdirectlystartinfullscreenmode.
Thispropertyisnottobeconfusedwith“WindowState”,whereyoucanbringasunderWindowsasusualthewindowtothemaximumsizeofthefreedesktoparea(wsMaximize).The“ShowFullScreenIcon”option,activatethefullscreenicononMAC(underWindowsithasnoeffect,becausethereisnofullscreeniconforthewindowarea).Atruntime,youcan-underbothWindowsandMACOSX-withthe“FullScreen:=True”or“FullScreen:=false”switchbetweenthetwomodes.
TFrame
SinceXE4framesarealsoavailable,basicallyitworkslikeinWindows.Under“File”,“New”,“Other”menu,youcanfirstcreateaTFrame.Thenyoucanclickfromthecomponentpaletteontheframeiconandattachtotheformaregioninwhichtheframeistobeinserted.Thiswillopenadialogwhereyoucanselectoneoftheavailableframes.
TPanel(nocaptionortext-property,alignwithqualifier)
Aspecialityhasthecomponent“TPanel”whichdoesnothavetheproperty“Caption”northe“Text”property.ToachievetheresultsknownunderWindows,youhavetopasteaTLabelcomponentintoit.
IfyouwanttoalignaTPanelobjectatruntime,itisnotenoughasintheVCLfrom,forexample,tosettoAlign-propertyto“alClient”.Evenhereyouhavetouseaqualifier,theterm“TAlignLayout”.
Example:Panel.Align:=TAlignLayout.alClient;
TCheckbox,TRadioButton(IsChecked)
WhenusingtheTCheckbox-componentortheTRadioButton,youdonotevenneedtosearchfortheproperty“Checked”,lookinsteadfor“IsChecked”.
Likethe(newer)VCLcomponentsalsotheFireMonkeycomponentswithtextelementshavetheproperty“Wordwrap”,whichisactuallyquiteuseful.LikeallFireMonkeycomponents,thediscussedcontrolshere,hasseveralfeaturestorepresentitsappeareance(“Styles”,e.g.“StyledSettings”,“StyleLookup”and“StyleName”,butmoreonthatlater).
TSwitch(AlternativetotheTCheckbox)
ThiscomponenthasnoequivalentintheVCL.UnderFireMonkeyyoucanuseitasanalternativetoTCheckBoxtorepresentanONorOFFstate.Theseswitchescanbefoundveryofteninmobileapplications,butnowfindtheirwayalsotothedesktop.
Thus,thecomponentlookslikethis:
TImage(WrapModefordisplay-modi)
WiththeVCLTImagecomponent,youcanusetheproperty“Picture”toloadanimageandinfluencethewayofdisplayitwiththeproperties“Stretch”and“Proportional”.UnderFireMonkeythereisalsoaTImagecomponent.Hereyoucanusetheproperty“MultiResBitmap”toloadtheimageintothecomponent:
Thismeans,thatyoucandirectlysaveasmanybitmapsasyouneedfortheseresolutionsaccordingtouse.Ifyoucallherethewizardforediting,youmaygetthefollowingdialog:
Hereyoucanaddindividualbitmapsbyfirstclickonthefirsticononthetopleft(forneworadd).Thenyoucanclicktheopeniconinthetextinputlinetoselectaimagefile.Bydefault,thepreviewwillnotbedisplayedontherightsideofthedialog.Thiscanbeenabledbyclickingonthemagnifyingglassiconinthetoolbar.
Note:Ifyouclickonthegreencheckarrow,aqueryarise,whethertheinformationavailableatdesigntime,aretobedeleted.Ifyouconfirmthiswith“Yes”,theinformationtothesourcedirectoryofthebitmapswillnotbedisplayedatthenexttimewhenusingthedialog.Aslongasyouarestillneedthisinformation,youshouldclosethedialogueontheredwindowclosebutton.
Atruntime,youaccesstheindividualbitmapsondemandviatheitemsproperty,egasfollows:
MyBitmap.assign(Image1.MultiResBitmap.Items[0].bitmap);
Itisnotnecessarytosaveinthiscomponentimageswithvariousresolutions.Youcanalsosavedifferentpictureswithsameresolution,thusquasiachieveareplacementfortheVCLImageList.However,youcan’tenter2timesthesamescalevalue.
ItishandythatyoucandragfromWindowsExploreroranyotherfilemanagerfilesdirectlyintothisimageeditor.
Detailsandfurtherinformationyoucandisplay,bypressingtheF1keyintheopenbitmapeditordialog,yougetafairlydetaileddescriptionofthedialog.
Theproperty“WrapMode”youcanusetoinfluencethetypeofpresentation.
Thefollowingmodesareavailablehere:
WrapMode:
iwFit (Default)-adjuststheimagetothesquareofthecomponentbytheimageproportions(theratiobetweenthewidthandheight)aremaintained.
iwOriginal Displaystheimagewiththeoriginaldimensions.
iwStretch Enlargestheimagesothatitfillstheentirerectangleofthecomponent
iwTile Tilestheimagesothatitfillsthewholerectangleofthecomponent.
iwCenter Centerstheimagehorizontallyandverticallycenteredinthecomponent
TImageControl(automaticscaling)
Agraphicthatisloadedbyusingtheproperty“bitmap”,isscaledautomatically.Whenyouclickatruntimeatthedisplayedimage,afile-opendialogwillbeopened,whereyoucanloadanimageintothecomponent.Ifthisdefaultbehaviorshouldnotbegivenasastandard,youcanavoidthiswiththefollowingassignment:
Imagecontrol1.EnableOpenDialog:=false;
TImageViewer(picturesscaledbyuser)
TheImageViewerisaveryinterestingcomponent.Again,youloadthegraphicswiththeproperty“bitmap”intothecomponent.Bydefault,theproperties“MouseScaling”and“Mousetracking”areactivatedhere.Asaresult,theusercanscalethesizeofthegraphicwiththemousewheel,whenheiswiththemouseoverthedisplayedelement.Ifyouwanttodisablethisbehavior,youmustdisablethelast-mentionedproperties.
TImageViewer (tousewiththeLiveBindings-Designer)
Thecomponentisidealforproducingadynamiclinktoanothercomponentusingtheso-called“livebindings”.Hereisoncedemonstratedhowsuchathinggoeswiththetrackbarcomponent:
HereintheObjectinspectorwehaveclicked“Bindvisually”ontheproperty“LiveBindings”.Thiswillautomaticallyshowthe“LiveBindings-Designer”.Thereyoucannowusetheproperty“BitmapScale”fromthe“ImageViewer1”withtheproperty“Value”toconnectthe“Trackbar1”component.
JustclickontheImageViewer1elementonthe3dots,itwillpopupthenadialog“BindableMembers”.Thereselecttheproperty“BitmapScale”andconfirmwith“OK”:
Thenclickatthe“Trackbar1”withthemouseonthe“Value”anddragitwiththemouseontothe“BitmapScale”of“ImageViewer1”andthenreleasethemousebutton.
Result:
Ifyoudothisforthefirsttime,youcanalsousealsothe“LiveBindings-Expert”.Todothis,clickintheLiveBindingsdesignerontheitem“ImageViewer1”.Thenclickontheiconwiththemagicwand.Thisopensadialogwhereyoucanspecifythesettings:
Select“Linkacomponentpropertywithacontrol,”andthenclick“Next”.
Inthedropdownlistselectthecomponent“ImageViewer1”.Selectthenasproperty“BitmapScale“:
Thenclick“Next”andselectonthenextpagethecontrol“Trackbar1”.Thenclick“Finish”.
Inthedesignernowyoucansee,thattheproperty“BitmapScale”fromthe“ImageViewer1”islinkedtothe“Value”propertyof“Trackbar1”.
Becausetheproperty“BitmapScale”withvalueof1standsfor100%imagesize,setthepropertyvalue“Max”ofthe“trackbar”componentalsoto“1”(ifyouwantallowmagnifications,setittoe.g.2or3).Atruntime,youcanchangethedisplaysizeofthegraphicwithusingtheslider.
Thisisveryeasytodo,andtherewasnoneedtotocreateasinglelineofsourcecodeforit.Belowtheruntimeresult:
TLabel(NewpropertyFontColor)
Ifyouwantedtochangethecolorofalabel,forexample,inthefirstversionofFireMonkey,youhadtocreateacustomstyleandthensettheproperty“Fill”ofthetextelementwiththedesiredcolor.SinceFireMonkey2,thisismucheasier.JustusethepropertyintheObjectinspector“TextSettings”andthen“FontColor”andselectthedesiredcolor.
Note:TheselectioninFontColoronlyworksif“StyledSettings”inthe“FontColor”optionisdisabled:
Whetherthismixingofthevarioussettings(directviaStylesandsettingsintheObjectinspector)isusefulornot,Ijustletstandthere.Anyway,youshouldknowthis,becauseotherwiseyoumaybelookingquitelong,whythecolorthatyouchoosein“FontColor”isnotdisplayed.Theproperty“StyledSettings”referstothecurrentstyle.Thiscanbethedefaultstyle,butthiscanalsobeauser-definedstyle.ThesettingsofthestyleoverrideanyothersettingswhenitisenabledhereinStyledSettings.
TImageList(Notavailable-butcompensationpossible)
ThesoextremelyusefulVCLTImageListthatservesasacontainerforgraphicsandiseditableatdesigntimeviaaconvenientdialog,doesnotexistunderFireMonkey.Aworkaroundcouldbe,totakeanextraformthatyoucanprovidewithmanyTImagecomponents.
IfyouboughttheFireMonkeyPackfromTMSsoftware,thereisthe
componentTMSFMXBitmapContainerofinterest.Thisgivesnamelyaneasywaytoloadonaslipmultiplegraphicsfilesatthecomponentoraddsomeone’stoit.
Todothis,clicktherightmousebuttononthecomponentandselectoneofthetoptwocommandsfromthepop-upmenu.Ifyouhavealreadyloadedfilesandwantmoretoadd,select“Addfiles”andNOT“Loadfiles”,otherwiseexistingentriesareremovedandonlythenewonesareaddedandstaysinthelist:
Youcanselectabitmapanddeleteit,usingthedeletekeyhereinthetreeview.IntheObjectinspector,youcancheckbyname,ifyou’vehitthecorrectbitmap:
Warning:Donotusethedialog“BitmapEditor”.Whenyoudeletethebitmapinthisdialog,thebitmapisnotremoved,butthecontentisdeleted!
Atruntime,youcanusethebitmapsfromthecontaineregtodisplaytheminagrid,hereisanexample:
Forthispurpose,theappropriatecodewouldbe:
procedureTF_MainImg.Grid1GetValue(Sender:TObject;constCol,Row:Integer;
varValue:TValue);
var
aCol:TColumn;
begin
aCol:=Grid1.ColumnByIndex(Col);
ifaCol=StringColumn1then
Value:=intToStr(Row+1)
elseifaCol=ImageColumn1thenbegin
ifRow<=TMSFMXBitmapContainer1.Items.Count-1thenbegin
Value:=TMSFMXBitmapContainer1.Items[Row].Bitmap;
end;
end;
end;
Anotherusefulfeatureofthecontaineris“FindBitmap”,withthatyoucanaccessthestoredbitmapbyname:
Bitmap:=TMSFMXBitmapContainer1.FindBitmap(‘computer.bmp’);
Ilikethisverymuch.ItisbetterthantheTImageListwhereyoucouldaccessthebitmapsonlywithintegervalues.
Thedemocanbedownloadedat:
http://www.devpage.de/download/fmbook3/ImageListDemo.zip
ButyouneedtheTMSFMXStringGridinstalledoraninstalledTMSPackforFireMonkey.
TListBox(noTCheckListbox,butShowCheckboxes)
Don’twastetimesosearchaTCheckListBox,thereisnotsuchacomponentforFireMonkey.Ifyouwanttodisplayalistboxwithcheckboxes,selectinsteadthe“ShowCheckBoxes”option.Quiteinterestingisperhapstheproperty“ListStyle”,herebyyoucandisplaytheentriesinthelistboxnexttoeachother(lshorizontal).
Here’sanexample:
AlthoughthelistboxworksingeneralthingsalsoliketheVCLcomponent(egItems.LoadfromFile,etc.),however,istodistinguishwhetheryouareworkingatthedesignoratruntimeusing“Items”or“ListItems”:
Intheillustratedlistboxontheleftside,theentrieswereaddedbyusingthe“Strings”propertyofthelistbox.InthesecondlistboxIhadclickedwiththerightmousebuttononthelistboxandthenselectthecommand“AddListBoxItem”.
Asyoucanseeinthestructure-overview,onlythesecondlistboxhasindividualentriesforeach“ListBoxItems”.SoonlyinthesecondlistboxyoucanatdesigntimeselecttheindividualListBoxItemsbymouseclickandmaketheappropriatechangestothesettingsintheObjectinspector.
Warning:ifyouinthesecondlistboxnowedittheentriesovertheproperty
“Strings”andtakethechanges,allListBoxItemswouldbeconvertedtonormalstrings,sothatalltheirprevioussettingswouldbelosttotheindividualListBoxItems.Sohereyouhavetotakecarehowtoproceed.
Hereanexamplehowtoaccesstheitems.Atdesigntime:
Atruntime,youcanaddresstheindividualitemsofaListBoxbothontheproperty“ltems”(=listofstrings)andthepropertiesof“ListItems”(listofTListboxItems).Seetheexamplesourcecodewiththisresult:
procedureTForm19.FormCreate(Sender:TObject);
begin
Listbox1.Items[0]:=‘Textsetvia“Items”-property’;
Listbox1.ListItems[1].Text:=‘Textsetvia“ListItems”’;
Listbox2.Items[1]:=‘Textsetwith“Items”-property’;
end;
ToreadifinaListBoxItemstheproperty“IsChecked”isset,doitlikethis:
ifListbox1.items[0].isCheckedthen…
Listbox1.items[0].isChecked:=True;
EnhancementoftheListbox-Items
BeginningwithDelphiXE4:Ifyouclicktherightmousebuttononthelistbox,youcanaddnotonlysimpletextentries,buthaveachoice:
WithaTListboxGroupHeaderyoucane.g.useinthelistboxsubheadings.VeryusefulistheTSearchBox.Whenyouentertextthere,thelistboxitemswillbefilteredanddisplayedsothatonlythematchingentriesareshown.Forexample,ifthelistboxofferquitealotofoptions,theusercanenterakeywordandheimmediatelyreceivestheappropriatesettingoption.Thatissomethingcomfortable,whattheVCLlistboxdoesnotoffer!
AlistboxwithagroupheaderandaSearchBox:
AndnowthislistboxaftertheuserhasmadesometextinputintotheSearchBox:
IfyouclickatdesigntimewiththerightmousebuttonontheSearchBox,youcan,forexample,addaClearEditbuttonthatclearstheinputatruntimeinthetextSearchBoxagain.Asthefilteringhappensallautomatically,youneednotwriteasinglelineofsourcecode!
NoOwnerdrawforlistboxes
Sincethereisno“OnDrawItemevent”forthelistbox,youhavetoworkwiththeappropriatesettingsfortheListBoxItemtogetthewantedrepresentation.
Explorethesuppliedexample“CustomListbox”intheexampledirectory.Youwillfinditusuallyhere:
C:\Users\Public\Documents\Embarcadero\Studio\15.0\Samples\ObjectPascal\FireMonkeyDesktop\CustomListBox
Hereisthecompiledsample:
Ifyoulookatthesourcecode,youcanseehowalistboxentryisgenerated:
procedureTfrmCustomList.Button2Click(Sender:TObject);
var
Item:TListBoxItem;
begin
//createcustomitem
Item:=TListBoxItem.Create(nil);
Item.Parent:=ListBox1;
Item.StyleLookup:=‘CustomItem’;
Item.Text:=‘item‘+IntToStr(Item.Index);//setfilename
ifOdd(Item.Index)then
Item.ItemData.Bitmap:=Image1.Bitmap//setthumbnail
else
Item.ItemData.Bitmap:=Image2.Bitmap;//setthumbnail
Item.StylesData[‘resolution’]:=‘1024x768px’;//setsize
Item.StylesData[‘depth’]:=‘32bit’;
Item.StylesData[‘visible’]:=true;//setCheckboxvalue
//setOnChangevalue
Item.StylesData[‘visible.OnChange’]:=TValue.From<TNotifyEvent>(DoVisibleChange);//setOnClickvalue
Item.StylesData[‘info.OnClick’]:=TValue.From<TNotifyEvent>(DoInfoClick);
end;
Alsonotehoweasyitis,toassignaneventhandlertotheentry:
Item.StylesData[‘visible.OnChange’]:=TValue.From<TNotifyEvent>(DoVisibleChange);
Itisthereforeveryflexible,itmightevenbepossibletoassigntothoseentries,differenteventhandlers,dependingonthecontentormeaning.
Youwillunderstandthewholethingright,whenyouviewthepropertiesthatareintheTStyleBookused.Todothis,double-clicktheTStyleBookcomponenttotakeacloserlooktotheproperties.Inthestructureview,youcanselecttheindividualelements:
However,thereisalsoanotheroption,todrawthelistboxelement(textcolorandbackground).CheckoutHint15hereinthebook“OnDrawItemeventoftheListBoxfromVCLreplacewithOnPaintingeventoftheTListBoxItems”
AllComponents(excepttheform)
WhiletheTFormcontainspropertiessuchas“Left”and“Top”,allothercomponentsdoesnothavethat.Rather,hereusingtheproperty“Position”withtheXandYvaluesfortheleftandtopposition.Itshouldalsobenotedthatnoneofthemareintegervaluesbuthasthetype“extended”.
SeveralComponents(Propertieswithadditionaltype-qualifying)
InmanycomponentsyoucanuseknownpropertiesasintheVCLcomponentsintheObjectinspector.InaTMemoyoucanusetheproperty“Align”toassignthevalue“alClient”.However,atruntimeyoumustprovideadditionaltypequalifiers.
Not:memo1.Align:=alClient;
But:memo1.Align:=TAlignLayout.alClient;
Evennot:memo1.FontColor:=clRed;
But:memo1.FontColor:=TAlphaColors.Red;
Thewell-known“clColorName”oftheVLCcomponentsworkshereunderFireMonkeyalwayswithout“cl”,butwithTAlphaColorsbeforeit.
Soifyou’regoingtomakeyousupposedlycommonassignmentsandtheprogramdoesnotacceptthat,trytofigureoutwhetheranadditionaltypequalifierisrequired.
TAlphaColorsitselfisdeclaredintheunit“System.UITypes”:
TypeTAlphaColors=TAlphaColorRec;
Butifyouknowthisonce,itisOK.
However,there’salsoatricktobypasstheuseofthisqualifier:Simplylinktheunit“System.UIConsts”,thenyoucanusecolorvaluessuchas“claRed,”“claBlack”.
Uses
System.UIConsts;
…
memo1.FontColor:=claRed;//Alternative:
TMenuItem(withoutImageIndex)
Youdonotneedtosearchfortheproperty“ImageIndex”,whichdoesnotexisthere.Butyoucanusetheproperty“Bitmap”(intheVCLcomponentitis“Glyph”)toassignaimage.ItshouldalsobenotedherethatinFireMonkeycomponent“IsChecked”isusedinsteadif“Checked”.
TMainMenu(HandlingMACMenus)
Ifyoucompileyourprogram,youhaveintheWindowsversionusuallythe“File”menu.IntheMACforthecompiledversionthenameoftheprogram(thatisthe“.exefile”-withoutextension)isusedtosetthefirstmenuname.Inthis“SystemMenu”oftheprogramareusuallyonlycommandstoexittheprogramoracommandtocallasettingsdialogorthencommand“Informationabout…”.Soitisrecommendedtocreateanothermenunameasafirstentry:
IntheWindowsversionyouhavetoselect“TEditor”invisible,intheMACversion,youmakeitvisibleandhide“Exit”fromFile-menu.Heretheimplementationinthesourcecode:
procedureTF_Main.FormCreate(Sender:TObject);
begin
{$IFDEFMACOS}
mTEditor.Visible:=True;
mFileExit.Visible:=false;//fromMenuFile
{$ENDIF}
//…
end;
AndsoitlookslikeontheMac:
Thissolutionyoucanuse,ifyouonlyhaveonemasterviewandnospecificviewfortheMACplatform.However,ifyouhaveaspecificformfortheMac,youcanalsodirecteditthepropertiesofthemenubytheObjectinspector.Inthe“OSXDesktop”viewyouchangeyousetthetextforthe“TEditor”-menutovisibleandhidethe“Exit”-entryunderfile-menu,becausetheexit-commandnormallyisinthesystem-menuunderMAC.Don’tforget,thatthenameofthefirstvisiblemenuitemisreplacedwithMACOSalwaysbytheprogramname(executable).Therefore,youshouldchoosetheprojectnameoftheapplicationcarefully.
TMemo(CaretPosition,noModified,FindNext-replacement)
UnderWindowsyoucandeterminethecurrentrowandcolumninthememoasfollows:
Line:=SendMessage(memo.handle,EM_LINEFROMCHAR,Memo.SelStart,0);
column:=Memo.SelStart-SendMessage(memo.handle,EM_LINEINDEX,line,0);
UnderFireMonkeyitworkslikethat(useproperty“caretposition”):
line:=Memo.CaretPosition.Line+1;
column:=Memo.CaretPosition.Pos+1;
ThepropertyoftheVCLcomponent“Modified”doesnotexistunderFireMonkey.Youmustinsteadmanageitbyyourselfundertheevent“OnChange”or“OnChangeTracking”.Hereyoucansetavariable(e.g.the“Tag”variableoftheTMemo),whetherthememohasbeenalteredornotandthen,forexample,resetit,forexample,ifyouhavesavedthecontentsofthememoontheharddisk.
Clipboard.HasFormat(CF_Text)-replacement
UndertheVCLthereisthe“Clipboard”,underFireMonkeynot.Althoughforthememocomponentareexistingfunctionssuchas“Memo.CopyToClipboard”or“Memo.CutToClipBoard”and“Memo.PasteFromClipboard”.Buthowtodetermineiftheclipboardcontainstext(forexample,tomakeabuttonenabled)?HereyouhavetouseaPlatformService.
Example(unit“FMX.Platform”isrequired):
procedureTF_Main.Timer1Timer(Sender:TObject);
var
s:String;
ClipService:IFMXClipboardService;
begin
try
ifTPlatformServices.Current.SupportsPlatformService(IFMXClipboardService,IInterface(ClipService))
thenbegin
try
s:=ClipService.GetClipboard.ToString;
bnPaste.Enabled:=(s<>”)and(s<>‘(empty)’);
finally
end;
end;
finally
end;
end;
Spellchecking
AwholenewpossibilityarisessinceXE5withtheabilitytomakeaspellcheckforthetexthasbeenentered.
AnditworkswithaservicethatisautomaticallyavailableunderMAC.
Youenableordisablethespellcheckerjustwiththebooleanvariable“CheckSpelling”(eitherintheObjectInspectororatruntime).ForthespellcheckingaPlatformserviceisused.Thus,itcouldbeagoodideatocheckfirstwhethertheserviceforthePlatformcurrentlyusedisavailable.Toquerythis,youneedtoincludetheunits“FMX.Platform”and“FMX.SpellChecker”.Thequerylookslikethis:
IFTPlatformServices.Current.SupportsPlatformService(IFMXSpellCheckerService)thenbegin
memo1.CheckSpelling:=True;
end;
ForWindowsnoSpellCheckerserviceisavailable.Butinprincipleitshouldnotbedifficulttoimplementanown.Asatemplate,youcantaketheimplementationfortheMACplatformandrewriteitaccordinglyforWindows.Well,thedictionaryfunctionassuch,onemustofcoursealsocreate,buttherearealsoseveralsolutionsonthemarket.
FindNext-Replacement
UnfortunatelyfortheFireMonkeyMemocomponentwehavenotthemostusefulfunction“Memo.FindNext(Memo.text,StartPos,EndPos,SearchOptions)”available,asitexistsintheVCL.Eveninthestandardactions,thereisnocorrespondingaction.Soyouhavetouseaownsolution.Subsequently,Idemonstrateasimpleapproach,whichIhaveusedinmyprogram“TEditor”fortheMacandWindows(http://www.hastasoft.de/TEditor.htm).HereisasimplifiedversionoftheTEditorprogramfortheFindReplaceDemo:
Whenyouclickonthesearchicon(magnifyingglass),thefollowingroutineisexecuted:
procedureTF_MainFindReplace.SearchEditButton1Click(Sender:TObject);
var
P,L:Integer;
cp:TCaretPosition;
begin
forL:=0toMemo1.lines.count-1dobegin
ifcbGreat.IsCheckedthenbegin
P:=Pos(edfind.Text,Memo1.lines[L]);
endelsebegin
P:=Pos(ANSIUPPERCASE(edfind.Text),ANSIUPPERCASE(Memo1.lines[L]));
end;
ifP<>0thenbegin
cp.Line:=L;
cp.Pos:=P-1;
break;
end;
end;
ifP<>0thenbegin
Memo1.SetFocus;
Memo1.CaretPosition:=cp;
Memo1.SelStart:=Memo1.PosToTextPos(cp);//-(cp.Line)-1;
Memo1.SelLength:=Length(edfind.Text);
ifcbReplace.IsCheckedthenbegin
bnReplace.Enabled:=True;
bnReplaceAll.Enabled:=True;
end;
endelsebegin
ifcbReplace.IsCheckedthenbegin
bnReplace.Enabled:=false;
bnReplaceAll.Enabled:=false;
end;
end;
end;
Thiswasthefunctionforthefirstsearchrun.Ifyouwanttosearchafterthatdirectlythenextitem,youhavetostartfromthecurrentposition,behindtheselection.
Thesourcecodeforthisisasfollows:
procedureTF_MainFindReplace.bnFindNextClick(Sender:TObject);
var
P,L:Integer;
cp:TCaretPosition;
part:String;
begin
forL:=Memo1.CaretPosition.linetoMemo1.lines.count-1dobegin
ifcbGreat.IsCheckedthenbegin
ifL=Memo1.CaretPosition.linethenbegin
P:=Instr(Memo1.CaretPosition.pos+Memo1.selLength,Memo1.lines[L],edFind.Text);
endelsebegin
P:=Pos(edfind.Text,Memo1.lines[L]);
end;
endelsebegin
ifL=Memo1.CaretPosition.linethenbegin
P:=Instr(Memo1.CaretPosition.pos+Memo1.selLength,ANSIUPPERCASE(Memo1.lines[L]),ANSIUpperCase(edFind.Text));
endelsebegin
P:=Pos(ANSIUPPERCASE(edfind.Text),ANSIUPPERCASE(Memo1.lines[L]));
end;
end;
ifP<>0thenbegin
cp.Line:=L;
cp.Pos:=P-1;
break;
end;
end;
ifP<>0thenbegin
Memo1.CaretPosition:=cp;
Memo1.SetFocus;
Memo1.SelStart:=Memo1.PosToTextPos(cp);;
Memo1.SelLength:=Length(edfind.Text);
ifcbReplace.IsCheckedthenbegin
bnReplace.Enabled:=True;
bnReplaceAll.Enabled:=True;
end;
endelsebegin
ifcbReplace.IsCheckedthenbegin
bnReplace.Enabled:=false;
bnReplaceAll.Enabled:=false;
end;
end;
end;
//Note:TheexampleusedintheInstrfunctionisitsownhelpfunctionthatlookslikethis:
functionInstr(AtPos:Integer;S:String;toFind:String):Integer;
var
R:Integer;
begin
S:=Copy(S,AtPos,(Length(S)-AtPos)+1);
R:=Pos(toFind,S);
ifR<>0thenResult:=R+AtPos-1elseResult:=0;
end;
Ifyouwanttoreplacetheselectedtext,clickon“OK”,thenthefollowingroutineisexecuted:
procedureTF_MainFindReplace.bnReplaceClick(Sender:TObject);
begin
ifMemo1<>NILthenbegin
ifAnsiuppercase(Memo1.SelText)=Ansiuppercase(edFind.text)thenbegin
Memo1.DeleteSelection;
Memo1.InsertAfter(Memo1.CaretPosition,edReplace.Text,[TInsertOption.ioSelected]);
end;
end;
end;
Hereisatypicalcliffbythewayonceagain.Ittakessometime,untilIfindoutthatIhavetouse“TInsertOption”asaqualifier.Anyway,youcandownloadthesimplifiedFindReplaceDemo(whichthenalsocontainsthesourcecodefortheclickonthebutton“ReplaceAll”)frommyhomepage.Intheroutine,youwillthenfind3positionswhere“Application.ProcessMessages”isexecuted.Sometimesthisfunctionisneeded,whenanyinteractionshastotakeplacewiththehostsystem,e.g.tosetthepositionofthecursor.
Here’sthelinktodownloadthedemos:
http://www.devpage.de/download/fmbook3/FindReplaceDemo.zip
Ofcourse,italsoworksontheMAC.
Bytheway,aREDOfunction(likeonVCL)isnotavailable.
PrintingaTMemo
PrintingamemoisunderFireMonkeyunfortunatelyabitmorecomplicated.WhileundertheVCLcomponentfunctionssuchas“Canvas.textout(x,y,‘Text’)”,etc.wereavailableandhavefunctionsthatcauseanautomaticlinebreakforlongertexts,hereyoumustcalculatethisbyyourown.
ThefunctiontouseunderFireMonkeyisCanvas.Filltext().
ItshouldbenotedthatthedataforthepagewidthandheightaredifferentontheMacbyafactorof10,whichistakenintoaccountinthecalculationwithcorrespondingIFDEF’s.SohereisasimpleexamplethatworksonbothWindowsandtheMac(theunitFMX.Printerrequired):
procedureTF_Main.mPrintClick(Sender:TObject);
var
r,rPage:TRectF;
i:Integer;
tf:tfilltextflags;
a:TTextalign;
T,th:Extended;
sl:TStringList;
z:Integer;
functionGetFormattedLines(s:string):TStringList;
var
x,L,p:Integer;
pw:Extended;
label
StartAgain;
begin
sl.Clear;
ifS=”thenbegin
sl.Text:=#13#10;
Result:=sl;
exit;
end;
{$IFDEFWin32ORWin64}
pw:=(rPage.Right-rPage.Left)-100;
{$ENDIF}
{$IFDEFMACOS}
pw:=(rPage.Right-rPage.Left)-1;
{$ENDIF}
StartAgain:
forL:=1toLength(s)dobegin
ifPrinter.Canvas.TextWidth(Copy(S,1,L))>pwthenbegin
forP:=LDownto1dobegin
ifS[P]=‘‘thenbegin
sl.Add(Copy(S,1,P-1));
S:=Copy(S,P+1,Length(s));
GotoStartAgain;
end;
end;
sl.Add(Copy(S,1,L-1));
S:=Copy(S,L,Length(s));
GotoStartAgain;
end;
end;
ifS<>”thenbegin
sl.Add(s);
end;
Result:=sl;
end;
begin
ifActiveMemo<>NILthenbegin
ifPrintDialog1.Executethenbegin
tf:=[];
a:=TTextAlign.taLeading;
sl:=TStringList.Create;
withPrinterdobegin
{$IFDEFWin32ORWin64}
r:=RectF(200,200,(Pagewidth-200),(PageHeight-200));
rPage:=RectF(200,200,(Pagewidth-200),(PageHeight-200));
{$ENDIF}
{$IFDEFMACOS}
r:=RectF(20,20,(Pagewidth-20),(PageHeight-20));
rPage:=RectF(20,20,(Pagewidth-20),(PageHeight-20));
{$ENDIF}
BeginDoc;
th:=canvas.TextHeight(‘Test’);
T:=0;
fori:=0toActiveMemo.Lines.Count-1dobegin
sl:=GetFormattedLines(ActiveMemo.Lines.Strings[i]);
forz:=0tosl.Count-1dobegin
T:=T+Th;
ifT+th+th>=rpage.Bottom-rpage.Topthenbegin
T:=0;
r.top:=rPage.top;
Printer.NewPage;
end;
r.top:=r.top+th;
Canvas.FillText(r,sl[z],True,1,tf,a,a);
end;
end;
Canvas.Font.Family:=ActiveMemo.Font.Family;
Printer.ActivePrinter;
EndDoc;
end;
end;
end;
end;
TDropTarget(howDrag&DropworksinFireMonkey)
WhohasbeenprogrammedwithWindowsandtheVCLcomponentsmayknowthatonehadtoactivatedraganddropwiththeWindowsExplorerintheapplicationonlyoncebycallingthefunction“DragAcceptFiles”.ThenwehadtoensurethatthemessageWM_DROPFILESwasprocessedintheformandwriteaneventhandlerforit.Alittlebitcomplicated,Imean.
Soherewetakealookatthesupplied“ControlsDemo”,Imentionedbefore.Generally,itisthought,thatoneusestheTDropTargetcomponentinitsapplication,wheretheusercandrag&droponeormorefiles(butyoucanalsousenormalcomponentsonwhichyoucandraganddropanything).Soatruntime,movetherectangleorthecircleonthedroptargeticonandreleasethemousebutton.Intheeditboxisthendisplayedasaresulttheclassnameofthedraggedelement.
Let’sseeintheDragOvereventoftheDropTargetinside:
procedureTfrmCtrlsDemo.DropTarget1DragOver(Sender:TObject;
constData:TDragObject;constPoint:TPointF;varOperation:TDragOperation);
begin
Operation:=TDragOperation.Link;
end;
Withthatwetelltheprogram,thatthedropoperationis“allowed”.Bydefaultthedefaultvalueof“Operation”is“None”,whichmeans,thatitisnotallowed.Inadditionitexistsyet“Move”and“Copy”.Aspecialvisualfeedbackyouhave(underWindows)onlywhenyoudragafilefromExplorerontothetarget:
ForMacOSX,anappropriateiconappearsevenwhenyoudragthecomponentsinsidetheform.
Bytheway,doesthedemoworksonlysinceXE7,inallpreviousversionsofthedemo,theproperty“HitTest”ofaparentcomponentwasdisallowed,sonoevaluationofthecorrespondingmouseactionswasprocessed.
Whentheproperty“Filter”isempty,alldraggedfilesareaccepted.Doyouwant,thatonlycertainfiletypescanbedraggedonthedroptarget,enteranappropriatefilter.Soyouchangethesettingofthefilterto“*.txt”inWindowsorMAC,ifonlytextfilesshallbeallowedfordrag&dropactions.
Butdonotusethepipesymboltoseparategroups,useinsteadonlythesemicolon:“*.EXE;*.DOC;*.PAS”.
Note:Currently(03/10/2015)thefilterdoesnotwork,duetoabuginDelphi.IhavemadehereaentryforQualityCentral,IhopetherewillsoonbeaBugFix.
Thearrowisanimatedanditappearsthesymbol“Link”(or“Copy”or“Move”),whichmeans,thattheDropTargetwillacceptthefiles.Nowthequestionis,howtoevaluateit,whentheuserreleasedthemousebutton.Unfortuanallyforthedemowasnotcreateda
procedurethatshowstheevaluationoftheDragDropeventonce.Iherebydeliverit:
procedureTfrmCtrlsDemo.DropTarget1DragDrop(Sender:TObject;
constData:TDragObject;constPoint:TPointF);
var
I:Integer;
begin
forI:=Low(Data.Files)toHigh(Data.Files)do
ShowMessage(Data.Files[i]);
end;
Theonthetargetdroppedfilesareincludedin“Data.Files”,where“Files”anisanarrayofstrings.IntheprocedureaboveIshowyouhowtogetaccesstoit.OfcourseitwillworkalsoontheMac:
UnfortunatelyIcannotcopythelinkarrowthatappearssimilartoWindows,withtheMACscreenshotprogram.Itcanbeseenhereonlyweakthenameofthedraggedfile.
Hereagain,thefilenameisdisplayedwhenyouhavedroppedthefilesontheDropTarget.
Ofcourseyoucanalsodraganddropsomefilesonanormaleditbox.Toseehowtodothis,lookattipR31below.
So,nownothingshouldpreventyou,tousedraganddropinyourapplications.
TRichEdit(Notavailalbe-butreplacementvia3rd-partypossible)
ARichEditcomponentisforFireMonkeynotyetavailable,youhavetouseacomponentfromthirdparties.Mygreathopeishere,thattheTRichViewcomponentsusedbyme(withthatisalsowrittenmywordprocessorprogram“Tipptext”)willsoonbeavailableforFireMonkey.Accordingtothemanufacturer(withwhomIamine-mailcontactforyears),itisplannedtoimplement,butitisbe“notsoon”.Update:(03/10/2015)ThemanufacturerhasnotyetstartedtodevelopaFireMonkeyimplementation.Itmightbeworthwhile,therefore,tolookaroundforalternatives.
Andinfact,thereisrecentausefulRichEditcomponentfromTMSavailable,theTAdvRichEditor:http://www.tmssoftware.com/site/advricheditor.asp.
IdepositedashortvideoonYoutube:
https://www.youtube.com/watch?v=_BjlRX_CjX4
Recently,thereisevenaspellcheckeroffered,whatisreallyhelpful.
TPageControl(Notavailable-butreplacementavailable)
ThisunderWindowssoextraordinarilyusefulcomponentcannotbefoundatFireMonkey,however,theTTabControlcomponentprovidesareasonablecompensation(seebelow).
TStringGrid (worksdifferent)
ThiscomponentisalsoveryoftenusedinVCLprograms.LiketheFireMonkeylistboxalsotheFireMonkeyStringGriddoesnothaveanOwnerDrawevent(butanOnPainting-event,latermoreonthat).
WhenyouinsertanewStringGridintheform,youwillgetthissituation:
Asyoucansee,thegridelementcontainsnocolumns.YouwillalsointheObjectinspectornotfindaproperty“Columns”.
Youcanaddcolumns,byclickingontheStrinGrid-componentwiththerightmousebuttonandfromthecontextmenu,selecteithertheentry“ItemsEditor”or“AddTStringColumn”command.
Howyoucanseebelow,wehaveaddtwocolumnswiththeentryeditor.Youcannowclickona“StringColumn”andsetdifferentproperties,forexample,theproperty“header”.Theheaderisactuallynottherownumberzeroofthegrid,butaseparateelement.Theheaderyoumayshoworhidewiththeproperty“ShowHeader”.
Theproperty“RowCount”existsalsointheFireMonkeyStringGrid.Bydefault,agridhasinitially100lines.Therefore,theassignmentshereinthe“FormCreate”shown,areworkingcorrect:
Assignmentsmade,withoutaddingColumnsbefore,wouldnotwork.Thecolumnscanofcoursealsobegeneratedonlyforthedurationoftheprogram,ifnecessary.
Buthowcanthefontdisplay(e.g.bold,font,fontcolor)bechanged?
TheStringColumncomponenthasnotafont-property,buttheStringGrid-componenthasoneunder“Textsettings”.Butallthesettingsyoucanmakehere,arevalidforthewholegrid.Ifyouwantitdifferentforoneormorecolumnsorcells,youcanusethe“OnDrawColumnCell”event.
Example:
procedureTForm9.StringGrid1DrawColumnCell(Sender:TObject;
constCanvas:TCanvas;constColumn:TColumn;constBounds:TRectF;
constRow:Integer;constValue:TValue;constState:TGridDrawStates);
var
Flags:TFillTextFlags;
ar:TRectF;
begin
Flags:=[TFillTextFlag.ftRightToLeft];
canvas.BeginScene;
ar:=bounds;
ar.Inflate(-1,-1);
canvas.ClearRect(ar);
canvas.Font.Style:=[TFontStyle.fsBold];
canvas.Fill.Color:=TAlphaColors.Red;
canvas.FillText(bounds,Value.ToString,True,1,flags,TTextAlign.taTrailing,TTextAlign.taCenter);
canvas.EndScene;
end;
Theresultlookslikethis:
Soasrequested,thetextoutputisinboldandinredtextcolor.Onenote:thereisaseparateeventfordrawingonthecolumnheader:“OnDrawColumnHeader”.
Youcanthendrawthetextofthecolumnleftorright,orcentered,dependingontheprogramlogic.Andyousetthisbyusingthedifferent“TTextAlign”parameters,eg“TTextAlign.taCenter”forcenteredtext.
TGrid(ImageandotherelementsintheGrid)
WhatyouhavedoneinVCL(String-)Gridcomponent,withthe“OwnerDraw”events,forexample,displayingimagesorcheckboxesyoucandoeasilywiththeTGrid.BesideTStringColumnsyoucanalsouse“TImageColums”or“TCheckBoxColums”.
ClickwiththerightmousebuttonontheTGridandselecttherequiredentry:
InXE7(andalreadyinXE6)isnewherethe“TDateColumn”and“TTimeColumn”.Theremarkablethingis,whenyoueditthesefields,specificeditorsareofferedtherefore(aTDateEditandaTTimeEdit).
Todisplaytextorimages,youdonotuse“OnApplyStyleLookUp”eventbutthe“GridGetvalue”event.
Thefirstpictureshowstheformindesignstateandthesecondatruntime:
Hereyoucanseethesourcecodeused:
procedureTForm2.Grid1GetValue(Sender:TObject;constCol,Row:Integer;
varValue:TValue);
var
aCol:TColumn;
begin
aCol:=Grid1.ColumnByIndex(Col);
ifaCol=StringColumnTextthen
Value:=intToStr(Row+1)
elseifaCol=ImageColumn1thenbegin
ifodd(Row)then
Value:=Image1.Bitmap
elseValue:=Image2.Bitmap;
end;
end;
Dependingontheprogram-logicyouneed,youcanshowimages,textorcheckboxes.SowiththeTGridaveryflexiblecomponentisprovided,whichisultimatelymuchmorepowerfulthantheVCLTStringGrid.
TStringGrid-alternative (TMSFMXGrid)
Asalong-timeuseroftheTMScomponentsIwillgladlyreferyoutoanalternativethatcouldmakethethetransitionfromtheVCLStringGridtoaFireMonkeyGridmoreeasyly:TheTMSFMXGridwhich,inadditiontotheusualVCLproperties(ColCountDefaultRowHeight,etc.)hasmanyotherskills(suchasexporttoanExcelfile),youusuallycanhardlyfindelsewhereanyway.Formoreinfo:
http://www.tmssoftware.com/site/tmsfmxpack.asp.
Thereareprovidedanumberofdemosthatyouwillfindafterinstallingthegrid-componentunder
C:\Users\Public\Documents\tmssoftware\TMSFMXPackDemos
WiththeexporttoExcel,youcan,forexample,lookhowthisworkswiththeXLSIOdemounder
C:\Users\Public\Documents\tmssoftware\TMSFMXPackDemos\TMSFMXGridFeatureDemos\XLSIO
TheExcel-filegeneratedbytheTMSFMXGrid:
Icanopenexactlyinmyspreadsheetprogram(PC-Spreadsheet-andofcourseinExcelitself):
ToaddressthecapabilitiesoftheGridmoreindetailhere,isbeyondthescopeofthebook.Butthat’salsonotnecessary,becauseintheWindowsprogramgroupyouwillfindalinktoadetailedPDFdocumentationofthegrid(over130pages):
THeader(nosections,butitems)Thecomponentherealsoexists.IntheVCLwehavesections,hereinFireMonkeywehave“Items”instead,whichare“THeaderItems”.
THeaderControl(isnotavailableunderFireMonkey)
ThiscomponentdoesnotexistunderFireMonkey.UseTHeadertherefore.SincetheindividualTHeaderitemsalsoworksasacontainer,youcaneasilyinserthererequiredelements,suchascheckboxesorbitmaps.
TProgressBar (not“position”butvalue)ThiscomponentworksinessentialliketheVCLcomponent,butyoudonotuseinFireMonkeycomponentstheproperty“Position”,but“Value”.Positionis,afterall,usedwithPosition.xandPosition.ytosetthehorizontalandverticaloffsetofthecomponent.
TTabControl(noOwnerdraw)
ThiscomponentworksmuchliketheVCLversion.SothismeansthatyougenerateindividualTTabItemelementsandtheninserthereyourcomponents.YoucanTTabItemsofcoursefirstcreateatruntimeandthenpasteintotheTTabControl.
An“OwnerDraw”propertyandan“OnDrawTab”eventdoesnotexist,butyouhavea“OnPaint”andan“OnPainting”event,whereyoucanusethecanvastodrawtext.BesidethisthereexistalsothecorrespondingStyles“TabControlStyle”and“TabItemStyle”throughwhichyoucanmakethecorrespondingopticaladjustmentsviathe“OnApplyStyleLookup”event.
TTrackbar (helpfulproperty“tracking”)
TheFireMonkeycomponentcontains,incontrasttotheVCLcomponent,theproperty“Tracking”.Ifyousetthisto“False”,thismeans,thattheOnChangeeventisonlytriggeredwhenthemoveoftheTrackbuttonhasbeencompleted.Thiscanbeespeciallyhelpful,ifeventheslightestchangeinpositionisassociatedwithaprogramtaskthattriggersaveryintensiveprocess(e.g.complexgraphicsroutines).Thentheeventisfiredonlyjustwhenthemovingofthetrackbuttonisdone(theuserhasreleasedthemousebutton).
TSpeedButton(withoutBitmap)
WhileintheVCLcomponentwiththeproperty“Glyph”ofthespeedbutton,animagecanbeassigned,thisisunderFireMonkeyunfortunatelynotpossible.ButtheFireMonkeybuttonworksasakindofcontainer,youcaninsertaTImagecomponent,andthenusethisasbuttonimage.
Tomakethejobeasier,youcancreateacomponenttemplatefromtheFireMonkeycomponents.Soselectthebutton,whichincludestherelevantitemsandchoosefromthemenu“Component”thecommand“CreateComponentTemplate”.Inthedialogthatappears,enterthenanappropriatenameandclickOK.
Asofnow,thenewcomponentisavailableontheToolPalette.Ingeneratingthetemplate,youshouldensure,thattheindividualelementsarenotyetconnectedtoeventhandlers.Foreventhesourcecodewouldotherwisecopied(whichmightbeusefulonlyinspecialcases).
TStatusbar(awaytocompensatethemissing“Panels”)
IntheVCLstatusbaryoucandisplayatexteitherontheproperty“Panels”orviathe“SimpleText”property.IntheFireMonkeystatusbarthereisnothingofthesort,notextproperty.Soyoucouldusethestatusbarascontainerandthere,forexample,insertLabels.
Bettersolution:JusttakeaTGrid!Asyoucanseeinthescreenshotshownbelow,IhaveincludedintheStatusBaraTGrid.IntheTGridIhave2TStringColumns,aTImageColumn,againaTStringColumnandaTProgressColumnadded.
IntheObjectinspector,IhavesetfortheTGrid:
ShowHeader=false
Canfocus=false
HitTest=false
ShowHorzLines=false
ShowScrollbars=false
ShowSelectedCells=false
DisableMouseWheel=true
Furthermore,onemusthaveauserdefinedstyleforthegridtocreateandchangethesettingsthereunder“content”for“focus”and“selection”fortheFillColor,otherwisethegridwillappearinasortofbluishcolor:
AtruntimeyoucanthensettherequiredvaluesforthecorrespondingTColumnsinthe“OnGetValue”eventoftheTGrid.Inthefollowingexample,wecallthefunction“Grid.Repaint”inatimer(callevery500ms).Thiscausesthen,thatthe“OnGetValue”eventistriggeredwhereyoucandothefollowing:
procedureTF_Main.gStatusGetValue(Sender:TObject;constCol,Row:Integer;
varValue:TValue);
var
aCol:TColumn;
begin
aCol:=gStatus.ColumnByIndex(Col);
ifaCol=StrPosthenbegin
Value:=‘Row,Col:‘+IntToStr(Memo1.CaretPosition.Line+1)+‘,‘+IntToStr(Memo1.CaretPosition.pos+1);
end;
ifaCol=strSizethenbegin
value:=‘Len:‘+intToStr(length(memo1.text));
end;
ifaCol=ImgInfothenbegin
iflength(memo1.lines.text)>10thenbegin
Value:=Image1.Bitmap;
endelsebegin
Value:=NIL;//ImgINfo.Width:=0;
end;
end;
ifACol=progressInfothenbegin
VAlue:=Memo1.PosToTextPos(Memo1.CaretPosition);
end;
end;
Atruntime,itlookslikethis:
Intheexampleabove,therow,thecolumnandthelengthofthetextaredisplayedinthestatusbar.Ifthetextislongerthan10characters,theyellowwarning-imageappears.Thegreenprogressbarshowsalwaysthetotallengthofthetext.
Note:Thisdemoisusedonlytodemonstratethebasicoperations.Whetherthiscouldreallybeausefulsolutionforyou,willdependonyourneeds.
TheStatusbarwithTGridsolutionisalittleeffort,butitismucheasiertohideaTColumn,insteadofworkingwithagroupofTLabels.Alsoitisaneasywaytoshowimages,processindicatorsortext.
Theprogramcanbedownloadedasademo,under
http://www.devpage.de/download/fmbook3/StatusbarDemo.zip
MessageDlg (e.g.notdirectlyusablewithmtWarning)
Withthisdialog,thereisaproblem,ifyoutrytouseitlikeintheVCL.
WhatworkedhereunderWindows:
ifMessageDlg(‘Info’,mtWarning,[mbYes,mbNo],0)=mrYesthenbegin
//dosomethingornot
end;
doesnotworkwithFireMonkeyso.
Hereitshouldlookasfollows:
ifMessageDlg(‘Info’,TMsgDlgType.mtWarning,[TMsgDlgBtn.mbYes,TMsgDlgBtn.mbNo],0)=mrYesthenbegin
//dosomethingornot
end;
Soheretoo,thequalifiersareagainrequired…
Ihavesometimeneededtofigureouthowitworks.Thisshouldbesparedtoyouwiththisinformation.
Section4:TheFireMonkeyStyle-Designer
a)UsingtheStylesEditor
Double-clickonapossiblyexistingStyleBookorright-clickonacontrolandselectthecommand“EditCustomStyle”ifyouonlywanttochangethestyleforacomponentorselect“EditDefaultStyle”,ifyouwanttochangethedefaultforallcomponentsofthistypeinthecurrentproject.
Youwillthenseethefollowingscreen:
Ontheleftside,inthetreeview,youcanselecttheindividualstylesandassignappropriatevaluesintheObjectinspector.Overthetreeviewontop,thereisabutton,whichallowsyoutodeletelayoutelementsfromthetreeview.
Anddeletingelementsworksonlywiththisbutton,thedeletekeydoesnotworkhere.
TheStyleBookandthestyleshavefortheworkwithFireMonkeyaverygreatimportance,sincethiswayisoftenusedtoachievethesameopticalresultsthatcouldbeachievedintheVCLcomponentsusingtheOwnerDraweventsotherwise.
WhenyouhavecalledthestyleeditorunderDelphiXE2,thecontrolwasthenselectedinthetreeviewandinthemiddleoftheIDE-windowthecontrolitselfwasdrawn.UnfortunatelythatissinceXE3notmoreso.Onthelefthandside,onlythestructurelistisshownandnoitemisselected.Accordingly,thestyleelementisnotdisplayedinthemiddleofthewindow.Ifthestructurelistisslightlylonger,it’snotsoeasytofindthedesireditemquickly.Manyuserunderstandsnotatallwhattheycandohereatthisplace.
AlittlehelpyoucangetwiththeusingofDDevExtensionsbyAndreasHausladen.ThisisafreeDelphiextensionthatintegrateinDelphia“StructureViewSearch”:
Overthetreeviewasearchboxisinstalled,whereyoucansearchforthenameoftheelement.Soifyouaresearchingfor“listboxItem7style1”inthesearchline,itissufficientwhenapartterm“7style1”isenteredandyougetdirectlythedesiredelement.Byclickingtheentrywiththemouseorpressingenter,theitemwillbeselectedinthetreelistandthecorrespondingobjectisdisplayedinthecenterofthewindow
ClickheretodownloadtheDDevExtensions.:
http://andy.jgknet.de/blog/ide-tools/ddevextensions/
b)StylesinFireMonkey-anoverview
A“Style”describesaparticulartypeofrepresentationofacontrol,aform,oranentireapplication.SuchstylesalsoknownintheVCL.SinceXE2inaVCLapplication,thereisunder“Project,options,applicationappearance,”theabilitytoselectaparticularstyleforWindowsapplication.WiththefollowingXEversionsthethingsofarhasbeenrefinedonVCLside,becauseeachcontrolhasreceivedtheproperty“StyleElements”.Thisallows
youtospecifyhowtodisplayelements(clientarea,borders,fonts)andifthestyleshouldbeapplied-ornot.AsimilarpropertyofcomparablemeaningisalsoavailableforanumberofcomponentsinFireMonkey:“StyledSettings”
FireMonkeyandVCLhavesofarmovedcloserbythetime,thatsinceXE3theBitmap-StyleDesignerisavailabletoedittheVCLstylesandyoucansavetheseVCL-stylesasFireMonkeyStyles,too.
Let’slookfirstofallatthebasicsoftheFireMonkeystyles.FireMonkeystylesareincludedwithDelphiandshippedasstylefiles.YoucanfindtheseinXE7e.g.under
C:\ProgramFiles(x86)\Embarcadero\Studio\15.0\Redist\styles\Fmx
Thesearetextfiles,whichyoucanloadintoatexteditor.Loaditonce,togetanideaofsuchafile.InyourprogramyoucanloadthisstylefileseitheratdesigntimeinaStyleBookcomponentoratruntime.
Inaddition,thestandardstylesareintegratedalongsidealreadyfixedinDelphi.Evenifyouputnostylebookonyourform,thereisalreadyastandardstyle,whichalwaysisusedwhenyoudon’tloadanotherstyle.ThisstandardstylethenensuresthatyourprogramlooksasatypicalWindowsprogramunderWindowsandundertheMacasatypicalMACOSXprogram.
Howdoesitworknow,thataTButtonwillbedrawnwiththehelpofstyles,sothatitreflectsanormalstate,afocusedorapressedstate?
Well,beforeIanswerthat,letmeremarkthathereisshownthehighdynamicsinthedevelopmentoftheFireMonkeyframework.InXE2toXE4therewasalwaysanothertechnicalsolutionforthat.I’llspareyouthedetails(referifnecessaryonmybookstothecorrespondingversions)andshowhereonlyasitproceedsunderXE7:
ThebackgroundofTButtonsisdefinedin“background”,wherethebackgroundisa“TButtonStyleObject”.Thisobjectcontainsareferencetoasectionofabitmap.Ifyouclickon“background”,thenyoucanfindintheObjectinspectortheitems“NormalLink”,“Focusedlink”,“HotLink”and“PressedLink”thatallas“TBitmaplinks”aredefined:
Ifyouclickonsucha“TBitmapLink”,youwillgetthefollowingview.Thereferencestothestylebitmap,youwillfindunder“SourceRect”:
Bytheway,youcanalsofindfortheTSpeedButtona“FocusedLink”,butithasthesamesource-positionasthe“NormalLink”,becausethestate“Focused”doesnotexistfortheTSpeedButton.
Asmentionedearlier,inthestylesforXE2ithasnobitmap-links.ItstartswithXE3.Thismakesitpossibletodrawtherepresentationofthecomponentsmorequicklybecauseitiseasiertouseareadybitmapforthedrawing,astodrawacompicateappeareanceatruntime.
Youcaneasilycreateyourownbitmapstylebysavingthe“Windows7Style”bitmap(orthe“Windows8desktopstyle.png”asafile,changethecolorbalanceinagraphicseditorandthenuploadthemodifiedfilebackwiththebitmapdialog.
Youwillgetaccesstothebitmapovertheproperty“MultiResBitmap”.HereintheObjectinspector,clickonthebuttonwiththe3points.
Thefollowingdialogappears:
Clickonceonthebitmapthatisshowninthedialogabove.Hereyouhavethenagainaproperty“Bitmap”intheObjectinspector.Clickalsohereonthebuttonwiththethreepoints,the“BitmapEditor”willbeshown,whereyoucanloadandsavetheimage:
OnYouTubeyouwillfindalsoashortvideoforit(Germanlanguage):https://www.youtube.com/watch?v=gcMPZQiVR-w
HereIhavedonethisoncewithmyprogram“PixPowerPhoto&Draw6.x”(command“Specialcolors”):
ThetrickhereistosavethebitmapasaPNGfileandthenloaditgainasaPNGfileintothe“BitmapEditor”dialogofDelphi,otherwiseitwillnotwork.Withtheeditedbitmaphere,buttonsareunderWindowsthene.g.displayedinalightblue,andselectedorfocusedwithaslightorange(youmayneedtoclosetheformandreopenitagainforthechangestobeapplied).
Knowledgeofthestandardstylesisimportantifyouwanttocreateacustomstyleforacomponentforexample,aTButton.
ThestructureofabuttonstyleisstilldifferentifyouareusingthesuppliedFireMonkeystyles.Ifyou,forexample,loadthe“DarkStyle”intoastylebook,yougetfortheTButton
thefollowingstructurepresentedtheinthetreeview:
HereconsiststhebackgroundsimplyofaTRectanglethatcontainstwoTColorAnimationsandaTGlowEffect.Thefilecontainsnostylebitmap.Soyoucansee,thatthereareultimatelyseveralwaystodefinetheappearanceofacomponent.Ingeneral,itisworthtolookonceatthebasicstylesoftheindividualcomponentsinthestyleeditor,thatwillincreasesourunderstandingoftheuseofFireMonkeycomponents.
Anothernoteaboutthe“MetroplisXXX.Styles”.TheMetropolis-stylefilesalsocontainastylebitmap.However,thisiswithapproximately935pixelsx907pixelssignificantlylargerthanthebitmapWindows7Style(400x400pixels).FortheBlue-styleitlookslikethis:
c)ConvertVCLStylestoFireMonkeyStyles
Veryuseful,thatyoucanconvertVCLStylesinFireMonkeystyles,anduseitinFireMonkeyapplications.
Proceedasfollows:InDelphi,youcallinthe“Tools”menuthecommand“BitmapStyleDesigner”.Thenopene.g.under
C:\ProgramFiles(x86)\Embarcadero\Studio\15.0\Redist\styles\vcl
Luna-Style(Luna.vsf).
Inthe“File”menu,thencallthecommand“SaveAs”.Inthedialogthatappears,thenselectthefiletype“FireMonkeyStyle”:
Savethefiletoafolderofyourchoice(preferablyinonethatisnotdeletedwhenyouuninstallDelphi).
InaFireMonkeyproject,youcanthenloadandusethisstylefileinastylebook.Itwilllooklikethis:
d)UsingFireMonkeyStyles
Howdoesyourapplicationnowcometothestyleyouwant?Thereareseveralpossibilities.Eitheryouloadoneofthesuppliedstylefilesinastylebookcomponentthatyouputonyourform(donotforgettoassigntheformproperty“StyleBook”withthenameofyourstylebookcomponent).
OryouprovideoneormorestylefileswithyourapplicationandthenloadthewantedatruntimeintotheStyleBook.Thus,e.g.theusercanselectastylethathelikes.
Youcandothis,forexample,likethis(thepathmustofcoursebeadaptedtoyourprogramenvironment):
StyleBook1.Resource.LoadFromFile(‘D:\LunaStyle.style’);
Ifyouwanttoassignthestyletotheentireapplication,sothatmultipleformsthataregeneratedatruntimelaterhasaconsistentstyle,usingforthispurposetheTStyleManager,egatthecreateeventofthemainform:
TStyleManager.SetStyleFromFile(‘D:\LunaStyle.style’);
Forgettingthistowork,youmustincludetheunit“FMX.Styles”inyourunitslist.
Section5:ConvertVCLprogramsintoFireMonkeyprograms
IfyouhavedevelopedaWindowsVCLprogramwithperhapsmorethan100,000linesofcodethathasevenoverseveraldozenforms,thequestionariseshowtochangeallthistotheFireMonkeyplatform.
Onepossibilitywouldbetostartfromscratchagain,sodesigningnewforms,usecopyandpasteandcodeaslongasit’sgoingtotake.Itellyou,forgetaboutthis.Withasmallapplication,onecoulddoitthisway.Butwhatreallyneedstime,istodesigningforms,entertextandgraphics,dotheassignmentsofeventhandlers,andsoon.Aprogramthatyouhavedevelopedovertheyears,youdonotwanttodevelopagain,evenifitmighttakeayear“only”.
SoyouneedtoworkwithsolutionsthatassistyouinconvertingaVCLprogramintoaFireMonkeyprogram.
a)WorkingwiththeMidaBasicEdition
OnacquisitionofDelphiXE7youcandownloadfromtheEmbarcaderoDeveloperNetworkMidatheconverter.
Afterrunningthesetup-program,intheintheDelphimenu“Tools”youwillfindthenewcommand“Mida-VCLtoFireMonkey”.
Theworkwithitisverysimple:
With“SourceDirectory”,youselectyourVCL-projectdirectoryandwith“Destinationdirectory”theplacewheretheprogramwillbestoredasFireMonkeyversion.Afterhat,clickon“ConvertProject”.
TheRegister“CustomComponent”isintheBasicversionwithoutfunction,whatit’sallabout,IwillreportlaterinmyremarkstothePro/Studioversion.Thesameappliestothetab“Options”,whereyoucandisableorenableonlytheuseofLiveBindingsBasic.
ThebluebarindicateshowmuchVCLcomponentscanbeidentifybytheprogram.Soifyouclickonthebutton“ConvertProject”,aredprogress-barappearsduringtheconversionprocessinadditiontothebluebar.ThisreflectshowmuchVCLcomponentscouldbeconvertedintoFireMonkeycomponents.
YoucannowgotoDelphitothedestinationdirectoryandopentheFMX-projectfile.Foramoreextensiveprojectnormallyavarietyofmessageswillappearstatingthatcertainpropertiesofcomponentsaremissing.
Youneedtoclick“ignore”italways.
Theprojectisthennormallynotdirectlycompileableandexecutable.Hereisasmallexample:
SoherewehaveaTButton,aTBitBtn,aTSpeedButton.Thefirstthreebuttonsareassociatedwithaneventhandlerthatshowsintheonclick-event,whichclassthebuttonhas.
ThelastbuttonisassociatedwithaSchowMessageprocedure.
Besides,thereisaTImage,whichcontainsaTBitmap.
Thesourcecodeforthisisasfollows:
procedureTForm5.BitBtn1Click(Sender:TObject);
begin
MessageDlg(‘Clicked:‘+Sender.ClassName,mtInformation,[mbOK],0);
end;
procedureTForm5.Button2Click(Sender:TObject);
begin
ShowMessage(‘ThisisaTest.’);
end;
Afterconversionofthedialog,itlookslikebelow:
·ThegraphicsinTSpeedButtonismissing.OK,inFireMonkeytheTSpeedButtonhasindeednographics.
·TheTBitBtnmissinggraphicsandtext.OK,therearenoTBitBtninFireMonkey,sothat’sthereasonwhyitwasconvertedtoaTButton.
·TheTImagehasnobitmap.
IfwewanttocompiletheFireMonkeyproject,wegetthefollowingmessage:
Thesearetypicalcaseswherethetypequalifiersarerequired,whichIhadearlierbeenaddressed.Ifweadjustitasfollows,itruns:
procedureTForm5.BitBtn1Click(Sender:TObject);
begin
MessageDlg(‘Geklickt:‘+Sender.ClassName,
TMsgDlgType.mtInformation,[TMsgDlgBtn.mbOK],0);
end;
Verygood,itrunsonWindowsnow.ButwewanttouseFireMonkeytodevelopmulti-platformprojects.
However,wecanaddontherightsideoftheProjectwindowonlyMACOSasthetarget(byright-clickingtargetplatforms).
Thenyoucandothefollowing:Letdisplaythesourcecodeoftheprogram(Projectmenu)andcopyallthetexttotheclipboard.Thenclosetheproject.Afterthat,createanewFireMonkeyprojectunderthe“File”menu,chooseherethecommand“New”,“MultiDeviceApplication”.Whenthenewprojectwillbeshown,removedirectlythedefaultmainformwithnosavingandthensavetheprojectunderthenamethatyouhadconvertedbefore,inotherwords,overwriteit.Thengetbacktowith“Project,ViewSource”tothe
programsourcecodeandpastethetextfromtheclipboard.
Nowwhenyouaddeverythingandsaveaspreviouslydescribedabove,theMacOSXplatform,worksasdesired.
HereistheprogramrunningontheMac:
Bytheway,therearestillmanyotheraspectsofshiftingnotes,ifyouworkprimarilywithareal,comprehensiveproject.AllthisIwilldescribehereinthesamesectionlaterinc),firstI’mstillontheMidaPro/Studioversion,whichcanbepurchasedasanupgrade.
b)WorkingwiththeMidaPro/StudioVersion
IfyouhavepurchasedtheProorStudioversionandenteryourdatain“License”-Field,theprogramisfullyavailable.
Butperhapsafewwordsonhowtoinstalltheprogram.Ithasnoseparatesetupprocedure,youwillreceiveazipfilethatyoumustunzipitintoadirectoryofyourchoice:
Mida.exeistheactualprogramfile.
Incontrasttothebasicversionitrunsnon-modal,soyoucanthedialogleaveopen,whileyouarestillmakingadjustmentsorchangeswithDelphi.
WhatmakesMida_FM_addon.exe,Iamnotentirelyclear.
ToinstalltheMidaImageButtonsandMidaSpeedbuttonsIhaveaprojectinDCU_Win_32bitopened,compiledandinstalledthecomponents.TrieswiththesameprojectinDCU_MAC,didnotwork,itappearsas“.dlyb”andtheinstallentryintheProjectmenuismissing.Butisapparentlynotrequiredtoworkatall.However,youmustsetbothintheFireMonkeyandWindowsplatformaswellthesearchpathstothesedirectories,soeachonDCU_Win_32bitandDCU_MAC_32Bit.
WiththePro/Studioversionyou’llhaveaccessto“Options”andsomoreopportunitiestoinfluencetheconversionprocess:
Here,especiallylooktattheoptions“UpdateSourcenotcompatiblewithFireMonkey”and“ConvertTImage”.
Sowhenwecompilethesameprojectasabove(andearlierdonothavetheMidapackcomponentsinstalled),theresultisasfollows:
Asyoucansee,the“TSpeedButton”hasnowanimage-thatisbecauseitwasconvertedtothe“TImageButton”,aMidacomponent.
AlsothebitmapisnowavailableintheTImagecomponent.
Ifourbuttonswouldhavebeenbold,anappropriatestylewouldhavebeencreatedinthisstylebook.
Inthesourcecodeitselfhasmoredone,incomparisontotheMidaBasicconversion:
procedureTForm5.BitBtn1Click(Sender:TObject);
begin
MessageDlg(‘Geklickt:‘+Sender.ClassName,TMsgDlgType.mtInformation,
[TMsgDlgBtn.mbOk],0);
end;
Here,the“TMsgDlgBtn”before“mbOK”and“TMsgDlgType”before“mtInformation”wasinsertedbytheconverter.
AnotherimportantsupportfortheconversioninthePro/Studioversioncanbefoundunderthe“CustomComponent”.Hereyouhavethepossibilitytomakeacustomconversionwithowncomponentsorthoseofthirdpartynotsupportedyet(thescreenshotisslightlymodifiedinordertosavespace):
YoucanenterthenameoftheVCLcomponentintheupperwindowandthenameoftheFireMonkeycomponentwhichistobeusedfortheconversion.
Inthelowerwindow,youcanspecifyagaintheVCLcomponentandthenthenameoftheVCLpropertyandthenameoftheFireMonkeyproperty.
Conclusion:Eventhebasicversionisabighelp.Withtheirhelp,ImanagedtoconvertasimplecalculatorprojectwithinafewhoursintoaFireMonkeyproject,afewdayslater,theprogramwasalreadyavailableintheMacAppStore!
TheProandStudioversionofferevenmuchmorethanthebasicversion.
Inparticular,theacquisitionofimages,andtheadaptationofthesourcecodeareanotheressentialassistanceifyouneedtoconvertlargerprojects.Themanyhoursoftimesavedjustifyinanycases,theinvestmentintheProortheStudioversion.ThestudioversionoffersbesidestheconversionofreportsalsotheMidaComponentPack(currentlyMidaImageButtonandMidaSpeedbutton).
Infact,theprogramisessentialforthemigrationoflargerVCLprogramprojectsintoaFireMonkeyCrossCompileproject.
c)StrategicapproachtoworkingwiththeMidaConverter
Inlargeprojects,youmustfirstmakeaseriesofpreparatorywork,beforeyoustarttheconversion.Asanexample:makeacopyofyourproject,performaconversionwiththeMida-Converterandthenopentheconvertedproject.Thereareprobablyalotofmessagesaboutmissingfeaturesandcomponents.
Example:
TheslightlyolderTNoteBookcomponentisnotsupportedbytheconverter.Sobeforeyoustarttheconversion,youmusttransfermanuallyinyourVCLprogramthecontentsfromtheTNoteBookintoaTPageControl.
Youcanthenviewtheindividualformsonceandcheckoutwhatismissingorwhatnot.
SoyouneedtomodifyinmanycasesyourexistingVCLprojectpreviouslyoftheconversionoftheVCLprogram.HeretheVCLform:
AndheretheconvertedFireMonkeyForm:
Currently-orratherstill,ayearagoitwasalreadyso-TTreeViewcomponentswillonlybeconvertedwithouttextcontent.
WhentheTTreeViewconsistedofonlyonelevel,youcanreplaceitintheVCLprogramthroughaTListBoxcomponentandprovidethosewiththerequiredtextentries.TheTNoteBookyoucanreplacewithaTPageControl.YoucangivetheTabItemsoftheTPageControlswithoutproblemsthesamenameastheTabsoftheTNoteBook.Soitsupportsthenfurthermoretheprogramlogic.TheMidaConverterprogramconvertstheTPageControlthenintoaTTabControl.YoucaninTTabControlsettheproperty“TabPosition”to“TpNone”tohidethenamesoftheregistersandsotheTTabControlworksliketheTNoteBook(onlymorecomfortable).
Bytheway,againthenote:Thecolordesignofthelabeltexts(bluishshownhere)isadoptedusingthestylebookcomponentonlyfromMidaConverterProversion.IntheBasicversionwouldallthesetextsbeinblack.
Forprocessingindetail:
·OnlyFormsandunitsthatarelocatedintheprojectdirectorywillbecoveredbytheconverter.Soifyouhaveso-called“standardforms”thatyouuseinmultipleprojectsandthesearelocatedinaseparatedirectory,youmustcopythemintotheprojectdirectory.ItisbestwaythatyouclickintheProjectGroupWindowtosuchastandardformwiththerightmousebuttonandchoosing“saveas”.Thatleavesyouroriginalformattheoriginalplace,butmakesacopytotheprojectdirectoryandplaceavalidlinkintheProjectManagertothecorrectlocation.
·OftenVCLTPanelsareusedtodisplayatextwiththecaptionpropertyinarectangle.ButinFireMonkeytheTPanelhasneitheracaption,noratextproperty,thetextcontentthereforeislostwiththeconversion(!).Therefore,youshouldpreviouslyinsertaTLabelcomponentintheVCLprogramintotheTPanelcomponentandapplythetextoftheTPanelcaptionpropertytothecaptionpropertyoftheTLabel.
·Unfortunately,theMidaConverterconvertsalwaysentireprojects.So,ifyouconvertedtheprojectagainandhavealreadyworkedwiththepreviouslyconvertedforms,itwouldoverwritethisformwitharenewedconversionandyourpreviouschangeswouldbelost.Eitherthereforeconverteachtimeinadifferentdestinationdirectoryandcopyneededformsinyourprojectdirectory.Orconvertanytimeintothesametargetdirectory,butfirstsavethealreadyconvertedformsinan“OK”directory(withthecommand“SaveAs)andleaveitthereandcontinuetoworkwiththis(soIdoit).Butdonotforgettospecifythe“.dpr”or“.dproj”filesbecausetheycontainotherimportantsettingsthatyoudonotwanttodoediteachtime.YoushouldcopyittotheOK-directoyalsoanduseitfromthisplace.
·Starttheconversionwithunitsorformsthatdonotrequireadditionalunitsorotherforms.Forexample,an“Infoaboutdialog”ora“Registration”dialog.SoifyouhavecompletedallthepreparatoryworkontheVCLproject,goaheadandmakethefirstconversion.Afterthat,removefromtheFireMonkeyprojectallforms.Youcanuse“RemovefromProject”underthe“Project”menu,whereyoucanselectallforms.Thenjustaddaformandworkonit,untiltheprogramiscompiledandexecutedwiththisform(eg,the“About”dialog).Thenaddanotherformtoyourproject,setitasthemainformandworkonit,untiltheformcanbecompiledandtheprogramisexecuted.
·Evenatthisearlystage,youshouldrelativelyquicklyaddaMACOSXplatformandlettheprogramrunontimesevenontheMAC.Soyouseequickly,wherespecificadjustmentsfortheMacmustbemadeandyoucanreactintime.Otherwise,itmayhappenthatyourprogramrunsunderWindowsfine,butundertheMACplatformithailserrormessageswithoutend.
·TakeintoaccounttheMACpeculiarities,whendesigningyourdialogs.Forexample,buttonsthatarelocatedatthebottomofthewindow,theOKbuttonisdisplayedontherightsideinMACdialogs,butinWindowsdialogsattheleft.Sothebestwouldbe,youareusingaspecificMAC-Viewforthis.
·Backup,backup!!Veryfastanyproblemsoccurred,e.g.aformwillbedestroyedandisnolongerusable,whatever.Therefore,youshouldmakeadequate
backups,soyouhaveaccesstopreviousversionsandyoucancontinueyourworkfromthat.
·DependingonhoweasyordifficultityoursourcecodecanbeputintoFireMonkeylogic,itcanbeusefultoenableordisableanumberoffunctionsoreventhandlerswithIFDEFdeclarations.Youwillthenhavethewholesourcecodeinyourformandprocedurebyprocedureyoucanthenworkthroughandmakethenecessaryadjustments.Advantageishere,thattheprogramiscompilingandyouhaveabase,youcanaddthingsonebyone.Afterall,youwillhaveevenmorefuntovisuallyseehowtheconversionprogressgoesonwitharunningprogram.AndsoyoucanhaveanearlylookofyourprogramattheMacplatform.
·IfyouhavealsotheTMSFMXGrid-componentavailable,youshouldconsidertolettheconverterconvertyourVCL-GridintoaTMSFMXGridandnotintoaFireMonkeyTStringGrid(seetheappropriatesettingunder“Options”).ThisisbecausetheTMSGridistheVCLStringGridinmanyfunctionsmoresimilarlythantheFireMonkeyTStringGridandalsooffersmanyotherskills.Tip:YoumayneedtospecifytheconversionofTStringGridtoTTMSFMXGridundertheregistercustomcomponentexplicitlyinMidaconverter,onlyactivatingthecheckbox“TMSTTMSFMXGrid”onthe“Options”pagehashadintheMidaversionavailabletome,noeffect.
·TShapesarenotok,accordingtomyexperience,soyoushouldreplaceitwithaTPanelbeforeconversion.
·IfupdatesoftheMidaConverterwillbeavailable,readexactlywhathaschanged.Possiblythenextversionresolvesanexistingproblemorhasabilitiesthatreducethenecessarypreparatorywork.
Ifyoufollowtheseaspects,youwillmakeitsafetoconvertstepbystepyourVCLprogramintoaFireMonkeyprogramandbeabletoofferyourprogramfortheMACOSXplatformsimultaneously.
Meanwhile,Ihaveconvertedamedium-sizedproject(CopyBackupwithnofewerthan50,000linesofcode:http://www.hastasoft.de/CopyBack-info.htm)fromVCLtoFireMonkey:WithintwoweeksitwasfinishedandaweeklatertheMACversionwasready(whichrequiresalittlemoretimebecauseofsomespecialfeaturesoftheMAC-filesystem).
Thesecond,alargerproject(PC-Rechnung:http://www.pc-rechnung.de)withover100,000linesthenwentagainmucheasier.Ifyouhaveoncemakeyourexperiences,thanyouknowwhattolookfor,andyoucanrelativelyquicklygoaheadwiththeconversion.
Chapter2:TipsandtricksforCross-PlatformDevelopment
Section1:startingotherprograms
DependingonwhetheryouwanttostartanotherprogramonWindowsorMac(orwanttoopenafilewiththedefaultviewer)foreachsystemsotherroutinesareneeded.ThisisachievedwiththeuseofdifferentunitsandtheuseofIFDEFdirectives.
IntheUsesclause,usethefollowing:
{$IFDEFMACOS}
POSIX.Stdlib,
$ENDIF}
{$IFDEFMSWINDOWS}
ShellApi,
{$ENDIF}
Intheimplementationsectionitlooksasfollows:
procedureRunProg(prog:string);
begin
{$IFDEFMSWINDOWS}
ShellExecute(0,‘open’,Pchar(prog),nil,nil,0);
{$ELSE}
_system(PAnsiChar(‘open‘+AnsiString(prog)));
{$ENDIF}
end;
Ifyouneedtofindout,werefunctionsandproceduresfortheMacarelocated,lookintothecorrespondingfilesinthefollowingdirectories:
C:\ProgramFiles(x86)\Embarcadero\Studio\15.0\source\rtl\osx
C:\ProgramFiles(x86)\Embarcadero\Studio\15.0\source\rtl\posix
Section2:Gettheprogramdirectoryandprogramdatadirectory
TheprogramdirectoryoftheexecutablefileyoucanfindoutbothunderWindowsandtheMacusingthecommand“ParamStr(0)”.Infact,thewholenameoftheexecutablefileisstoredhere,butsoit’seasytoidentifytheprogramdirectory.
Itisbesttouseaunit,inwhichyousetupthecorrespondingvariablesintheinitializationsection.Here’sanexample:
unitMyData;
interface
uses
{$IFDEFMACOS}
POSIX.Stdlib,
{$ENDIF}
{$IFDEFMSWINDOWS}
ShellApi,
{$ENDIF}
FMX.Forms;
var
AppPath:String;
AppName:String;
AppExeName:string;
AppIniName:String;
ProgDataPath:String;
Implementation
begin
AppExeName:=paramstr(0);
AppPath:=ExtractFileDir(paramstr(0));
AppName:=ChangeFileExt(ExtractFileName(paramstr(0)),”);//withoutext.
{$IFDEFMACOS}
ProgDataPath:=IncludeTrailingPathDelimiter(GetHomePath)+
‘.config/’+AppName;
AppIniName:=ProgDataPath+PathDelim+AppName+‘.cfg’;
{$ENDIF}
{$IFDEFMSWINDOWS}
ProgDataPath:=IncludeTrailingPathDelimiter(GetHomePath)+‘hastasoft’+
PathDelim+AppName;
AppIniName:=IncludeTrailingPathDelimiter(GetHomePath)+‘hastasoft’+
PathDelim+AppName+PathDelim+AppName+‘.ini’;
{$ENDIF}
ifNotDirectoryExists(ProgDataPath)thenbegin
ForceDirectories(ProgDataPath);
end;
end;
Herepleasereplacekindly“Hastasoft”withyourowncompanynameorleavethispartcompletelyfree.Thissupplementisespeciallyrecommendedifyouhavedevelopedmanyprogramsandcustomersuseseveralofyourprograms.The“Config”folderisafolderontheMACwhereprogramscanstoretheirconfigurationfiles.ThisfolderisnormallyinvisibleintheFinder.Youcanofcourseuseanalternativefilemanagerthatcan(egFileIOforMAC)showthesefoldersanyway.
YoucanturnonandoffthevisibilityofthenormallyhiddenfoldersintheFinder,with:
defaultswritecom.apple.finderAppleShowAllFilesYES//unvisiblewithNO
Section3:Catchtotheprogrampassedstart-upparameters
WhatunderWindowscouldbedoneveryeasywith“StartParam=ParamStr(1)”,willbeconductedinMACOSXconsiderablymoredifficult,asthereitworksquitedifferently.Ifyou,forexample,intheFinderclickwiththerightmousebuttononafileandfromthemenuchoose“Openwith”,thesystemstartstheprogramandthensendsamessagetotheprogramthatitshouldopentheselectedfile.
Soyouhavetosetupamechanismtoensurethatmessagescanbereceivedfromyourprogram.
InMACAPI.AppKitisaNSApplicationDelegateavailable,whichbasicallyhandledunderMACOSX,themessage“OpenFile”.Unfortunately,thisfeaturehasnotyetbeenintegratedintotheDelphiFireMonkeyimplementation.
Sothereiscurrentlynochoice,astomakeitbyyourself.Thefollowingisthesourcecodeoftheunitthatdoestheimplementation.InthemainprogramthenanOpeneventproceduremustbeimplementedandbepassedongenerationofowndelegates.Itlookslikeso:
––––––––––––-uniths_NSApplicationDelegate;
//WillimplementanownNSApplicationdelegate;bythis
//application:openFile:messagesfromMAC-OS-Systemcanbehandled.
interface
uses
Macapi.ObjectiveC,Macapi.CoreFoundation,Macapi.AppKit,Macapi.Foundation,Macapi.CocoaTypes;
type
//AlthoughweonlyneedtheOpenFilefunctionhere,wemustalso
//stillimplementDidFinischLaunchingandWilltTerminate,without
//ican’tcompile
//fromMacapi.AppKit
NSApplicationDelegateExtended=interface(NSApplicationDelegate)
//hierineigenemProgrammmitStrg+Umsch+GeineneigenenGUIDerzeugen//verwendenSieNICHTdenuntenstehenden
[‘{2F5BD639-88DE-46F7-950A-D4469A125229}’]
functionapplication(theApplication:Pointer;openFile:CFStringRef):Boolean;cdecl;
end;
TOpenFileEvent=referencetoprocedure(constAFileName:string);
TNSApplicationDelegateExtended=class(TOCLocal,NSApplicationDelegateExtended)
private
FOnOpenFile:TOpenFileEvent;
public
constructorCreate(constAOnOpenFile:TOpenFileEvent);
functionapplication(theApplication:Pointer;openFile:CFStringRef):Boolean;cdecl;
functionapplicationShouldTerminate(Notification:NSNotification):NSInteger;cdecl;
procedureapplicationWillTerminate(Notification:NSNotification);cdecl;
procedureapplicationDidFinishLaunching(Notification:NSNotification);cdecl;
functionapplicationDockMenu(sender:NSApplication):NSMenu;cdecl;
end;
procedureStartExtendedApplicationDelegate(constAOnOpenFile:TOpenFileEvent);
var
ExtendedDelegate:NSApplicationDelegateExtended;
implementation
uses
FMX.Forms;
constructorTNSApplicationDelegateExtended.Create(constAOnOpenFile:TOpenFileEvent);
begin
inheritedCreate;
FOnOpenFile:=AOnOpenFile;
end;
procedureTNSApplicationDelegateExtended.applicationDidFinishLaunching(
Notification:NSNotification);
begin
//Dummy
end;
functionTNSApplicationDelegateExtended.applicationDockMenu(
sender:NSApplication):NSMenu;
begin
//dummy
end;
functionTNSApplicationDelegateExtended.applicationShouldTerminate(
Notification:NSNotification):NSInteger;
begin
inherited;
//dummy
end;
procedureTNSApplicationDelegateExtended.applicationWillTerminate(Notification:NSNotification);
begin
//NichtFreeAndNil,verwenden,sonstgibtesFehler!!
FMX.Forms.Application.Free;
FMX.Forms.Application:=NIL;
end;
functionTNSApplicationDelegateExtended.application(theApplication:Pointer;openFile:CFStringRef):Boolean;
var
Range:CFRange;S:string;
begin
Result:=True;
Range.location:=0;
Range.length:=CFStringGetLength(openFile);
SetLength(S,Range.length);
CFStringGetCharacters(openFile,Range,PChar(S));
TRY
FOnOpenFile(S);
EXCEPT
FMX.Forms.Application.HandleException(ExceptObject);
Result:=False;
END;
end;
procedureStartExtendedApplicationDelegate(constAOnOpenFile:TOpenFileEvent);
var
NSApp:NSApplication;
begin
Assert(ExtendedDelegate=nil);
NSApp:=TNSApplication.Wrap(TNSApplication.OCClass.sharedApplication);
ifAssigned(NSApp)thenbegin
ExtendedDelegate:=TNSApplicationDelegateExtended.Create(AOnOpenFile);
NSApp.setDelegate(ExtendedDelegate);
end;
end;
end.
–––––––––—
Inthemainform,thenthefollowingmusthappen:
procedureTF_Main.FormCreate(Sender:TObject);
begin
{$IFDEFMACOS}
StartExtendedApplicationDelegate(OnOpenParaFile);
{$ENDIF}
end;
Intheform-declaration,intheprivatesection:
private
{Private-Deklarationen}
procedureOnOpenParaFile(constS:String);
Intheimplementation-section:
procedureTF_Main.OnOpenParaFile(constS:String);
begin
if(s<>”)andfileExists(s)thenbegin
memo.lines.LoadFromFile(fn);
end;
end;
Unfortunatelyabitcomplicated,butitworks.
Itshouldalsobenotedthatitisalsonecessarytoenterintheinfo.plisttheinformation,whichtypesoffilesyourprogramwillbeabletoopen.
Hereanexampleofanadditiontotheinfo.plistfile,givingtheMAC-OStheinformation,thatyourprogramcanopentextfilesand.xml,andalsosourcecodefiles:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>public.plain-text</string>
<string>public.source-code</string>
<string>public.xml</string>
</array>
</dict>
</array>
Withoutthis,theFinderdoesnotprovideforyourprogramnameanentryinthe“OpenWithmenu”.ButthatdoesnotpreventotherprogramsastheFindertotransmitparameterstotheprogramforopeningfiles,socanmyFileManager“FileIOforMAC”starteveryprogramandgiveatthesametimeastartupparametertotheprogram(thisismanagedbytheMACsystemmanagedinthebackground).
Section4:“HelloWorld”-Multilingualprogramsandnewmarkets
TheAppleAppStoreisidealtosellyourprogramsinternationallyanditcanthusopenupentirelynewmarkets.Forthispurpose,however,youshouldnotonlyofferyourprograminyourownlanguage(inmycaseGerman),butalsoofferinganEnglishlanguageversion.
ForthisintentionhelpsusFireMonkeyinaverysimplewaywiththeTLangcomponent,ofcourse,inacross-platformway.Ifyouhavedevelopedyourapplication,addtothemainformaTLang-component.
Example:
DoubleclickingonthisTLangcomponent,itopensthefollowingdialog:
Youmightbesurprisedthateventheentry“ÖffnemichMac”isoffered.Thissourceisfromtheview“OSXDesktop”.Butitwillonlyshowup,whenIhavehereagainclickedonthebutton”Scanforstrings”.Thatway,youmustalwaysdo,whenyouaddnewcontrolstotheformandwanttoeditthetextsinthelanguagedesigner.
Inthefield“Two-letterlanguagecode”enter“en”andclickonthe“Add”button.
Thedialogthenchangesitsappearanceandbehindtherecognizedtextisnowforeachentryaninputfieldavailable,whereyoucanentertheEnglishequivalents:
Nowclosethedialogsimply,thechangesareapplieddirectly.
ToswitchnowfromthestandardlanguagetoEnglish,youhavetodothisviaasoftwarecommand.
Auserwouldnaturallychooseonamenuentryhislanguage.Hischoiceyoucansaveintheprogrambyanentryintheini-fileandrestoreitthenextlaunch.
Forthisdemoherewedothisdirectlyattheprogramstart,intheFormCreateevent:
procedureTForm13.FormCreate(Sender:TObject);
begin
Lang1.Lang:=‘EN’;
end;
WARNING:Ifyouwrite“en”inlowercase,itdoesnotwork,youmustenterthelanguagecodehereinuppercase,asshownabove.
Theformlooksafterprogramstartlikethis:
Whatyoucanseehere,isthatthetextoftheform(“caption”)isnotcompiledfromtheTLangcomponent.Notevenwhenyouusethe“Add”buttonontherightsideoftheLanguageDesignerdialogtoaddanentrywithatranslationfor“Form13”manually.
Youmustthereforemakethechangesinthesourcecodebyyourself,suchas:
procedureTForm13.FormCreate(Sender:TObject);
begin
//Sprachevorgeben
Lang1.Lang:=‘EN’;
//JenachSprache:
ifLang1.Lang=‘EN’thenbegin
Caption:=‘Title’;
end;
ifLang1.Lang=‘DE’thenbegin
Caption:=‘Überschrift’;
end;
end;
Evenmenutextsarenottranslated.ThemainmenuentriesareshownintheLanguageDesignerandtheyareeditable,buttherearenottranslatedatruntime.
Youmust-aslongasthisbugisnotresolvedinDelphi-makeaseparateprocedure,whereyoucanassigneachmenuitemwiththeneededtext(i.e.“mnu_Optionen.text=‘Options’,etc.).Itistobehopedthatthemenuitemsarecoveredbythiscomponentinasubsequentversion.
Andthinkonit,thatyouhavealsotomakeadifferenceindialogs,whenyouprogramrunsinEnglishorotherlanguagemode.
Ifacomponentshouldnotbetranslated,youcandisablethefeature“AutoTranslate”intheObjectinspector.
Doyouhavemultipleformsintheproject,thesearesearchedforstrings,butpossiblyonlyafteryouhaveclickedonthebutton“ScanforStrings”.Thisbuttonmustalsobepressedagain,whenyouaddadditionalformstoyourproject.YouneedonlyoneTlangcomponentforyourwholeproject,becauseeverysecondTLangcomponentwouldagainsearchtheentireprojectforstringsandwouldaddthemtoyourproject.
Butinanyatruntimenewlycreatedforms,youmustusetheformcreateeventtotelltheTLangcomponentwhichlanguageitshoulduse.
Itoffersitselfheretocreateaseparateproceduresothatyoucanswitchatruntimebetweendifferentlanguages.Butyoucanonlychangefromtheoriginallanguagetootherlanguages.Totheoriginallanguageyoucanonlycomebackwhenyourestarttheprogramandusetheoriginallanguageasdefault.ORyouspecifyanewlanguageinadditiontotheoriginallanguage,ie“AddLanguage”herewiththeswitch“de”(whenGermanisthedefaultlanguage).Sinceitobviouslymakesnosenseforittoprovideatranslation,itsimplyrestoredtheoriginalentries.Inthiscase,youcanalsoswitchbacksoatruntimetotheoriginallanguage.
ThedataoftheTLang-componentwillbestoredbydefaultinthetheforminanunreadableencodedformat.Alternatively,youcansavethetextsoutsideofthefile.
WARNING:Ifyouhavealreadytranslatedtextsandthenuncheck“StoreInForm”intheObjectinspector:
Youlostyourpreviouswork;thereisnoconfirmationprompt.So,beforeyouuncheckit,use“SaveFile”intheLanguageDesignerdialogboxtosaveyourlanguagefile.Thisfilecontainsallpreviouslyappliedlanguages,itautomaticallyreceivesthefileextension“.lng”.Atruntime,youcanthenloadthefilewith“Lang.LoadfromFile(filename)”intothecomponent.
Hereatthedemoeverythingworksthewayitshould.InalargerapplicationthatIhaveprovidedherewiththeTLangcomponent,thecomponentdoesnotkeeptrackofalltranslations.ThereforeIhavesavedtranslationsasatextfile(withthebutton“Createtxt-filetemplate”)andthenreloadedagainandagainforrevisions.YouloadthisfilebacktotheDesigner,whenyouaddalanguagebyusingthebutton“Fromtxtfile”.
Anyway,everythinghasworkedsatisfactorilyattheend.
Section5:ApplysandboxingandEntitlementsproperly
IfyouwanttobringyourapplicationintotheAppStore,itmustsupportthesandboxingmodel.FromJuly01,2012on,thisistheduty.Therefore,firstafewwordsaboutthesandboxingmodel.Tounderstandit,itisbeshownhowyourapplicationworkswithandwithoutsandboxing:
Withoutthesandboxyourapplicationhasfullaccesstoallthefilesandfoldersoftheoperatingsystem(itmightbe,thatforsomeactionsyourprogrammustberunwithadministratorrights).Youmayobtainaccesstothedocumentsfolderortheimagesormusicfoldersoatanytime.Also,yourapplicationcouldwritetotheApplicationBundlesofotherapplications,whichsofarisatargetforthesecurityoftheoperatingsystemhere.
Ifyourapplicationonlyplays“inthesandbox”,itdoesnotrealizewhatelserunaroundit,ordoesnothaveaccesstodatathatareoutsidethesandbox.Theremaybeapplications
whereitisenoughtoplayinthesandbox.Butoftenitisso,thatyouwantgivetheworkingresultsofyourapplicationtootherapplicationsorewantgetaccesstodata-outputfromotherapplications.
Here,the“Entitlements”comeintoplay.So,ifyourapplicationwouldalsoliketoplayoutsideofthesandbox,youneedtorequesttheappropriaterights.
ThefundamentalrightscanbesetwithDelphiunderProject,Options,“EntitlementList”(hereagaintonotethatthisinformationisonlydisplayedifyouhaveMACOSXsetasthetargetplatform):
Whatmostapplicationswillprobablyneedistherighttoreadandsavefiles,overwhichonecantakeaccesswiththedialog“Open”and“Save”.ThisisalsoasettingwhereAppleitletsyougothroughifyounotdescribe,whyitisneeded,whenregisteringtheapplicationintheAppStore.
Incontrast,ifyouneeddirectreadandwriteaccesstotheothermentionedfolders(pictures,music,etc.),youmustitspecifyexactlyiniTunesConnect,whyyouneedtheserights.Ifyoudon’tdothis,yourwillhaveagoodchance,thatyourapplicationwillberejected(alreadyhappenedtome).
WhenyouclickiniTunesConnectonthepageofyourapplicationonthe“ViewDetails”,button,youcanedittheso-calledmeta-dataoftheapplication.Thisisthedescriptionofwhatyourapplicationdoesandyoucanaddafewscreenshotsthatshowsyourapplication.
Underthesection“AppReviewInformation”thereisabutton“AddEntitlement”:
Herejustclickonit,selecttherequirerightandthenbrieflydescribe(inEnglishofcourse)whyyourapplicationneedstheright.Itshouldalsobereallynecessaryoratleastbecomprehensible.
Onethingisimportanttoknow:thatyoucanrequestmorerightsthanyoucanselectintheDelphipermissionlist.
Indeed,itispossibletorequestarightthatyourapplicationcanreadorwritetoaspecificfolder,forexample,intothedesktop-folder.
However,youmustmanuallyedittheEntitlementslist.Itisbesttogoaheadsothatyouareeditingthislistrightintheapplicationbundle.ButyouhavetodisableinDelphiintheDeploymentthetransmittingoftheEntitlementslisttotheMACcomputersinceDelphiwouldotherwiseoverwriteyourmanualentriesagain.
However,itissothatanalreadyonthetargetexistingfileisdeletedifitisnottobetransferred.Sowehaveto“outsmart”thedeploymentmanagerhere.I’vesolveditsothatIhavecreatedaseparateEntitlementsfileanditsownInfo.plistfile.ThesearethentransmittedinsteadofthefilesthatDelphimanaged.Youhavetomaintainthefiles
yourselfmanually,butthisisnotaproblemtodoso.Thisworkflowisalsorequiredinsomeothercases,whenyouhavetoentermultilineentries(<ARRAY>)intotheInfo.plistfile(whichDelphican’tmanage).Thefollowingscreenshotshowsitonce.Theyellow,byDelphicreatedfilesaredisabledandmyfilesmarkedingreen,aretransmitted:
Hereisaconcreteexampleofanextensionofthelist:
<?xmlversion=“1.0”encoding=“UTF-8”?>
<!DOCTYPEplistPUBLIC“-//Apple//DTDPLIST1.0//EN”“http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
<plistversion=“1.0”>
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key>
<array>
<string>/Desktop/</string>
</array>
</dict>
</plist>
Therelevantentryisinthiscasethekey:
„ com.apple.security.temporary-exception.files.home-relative-path.read-write”
Hereyoucanenterinanarraylist,forwhichfolders,relativetothehomedirectoryofyourapplication,adirectreadandwriterightwillbeneeded.
Thedirectorymustalwaysbeginwitha“/”andendwitha“/”.
Ageneraldescriptionoftheuseofentitlements,seetheAppleonlinedocumentation,here:
http://developer.apple.com/library/mac/#documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AboutEntitlements.html
Whereinasandbox-appthedataarethenactuallyreally?
Mymulti-applicationscreenshotshowsasavedimagecopywiththefollowingpath:
„/Users/harrystahl/Library/Containers/com.hastasoft.multiscreenshot/Data/Desktop/Bildkopie1.jpg“.
Asyoucansee,hereisjustasymlink,soanaliasthatisareferencetotheactuallocationofthedesktop:
Howtorequestfurtherrightsfordirectaccesstospecificfolders,isdescribedhere:
http://developer.apple.com/library/mac/#documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AppSandboxTemporaryExceptionEntitlements.html#//apple_ref/doc/uid/TP40011195-CH5-SW1
Thereforeitwillevenbepossibleaccessingfolderswithabsolutedirectoryinformation:
<?xmlversion=“1.0”encoding=“UTF-8”?>
<!DOCTYPEplistPUBLIC“-//Apple//DTDPLIST1.0//EN”“http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
<plistversion=“1.0”>
<dict>
<key>com.apple.security.app-sandbox</key>
<true/><key>com.apple.security.temporary-exception.files.absolute-path.read-write</key>
<array>
<string>/Volumes/</string>
</array>
</dict>
</plist>
Withthatyoucangetdirectlyaccessthefolder“Volumes”andthusonmounteddrives.
Andindeed,forthislocationshowsmyScreenShotprogramasstoragelocationthen
“/Volumes/D/Bildkopie1.jpg”.
Sothismeansthattheabsolutereadandwriteaccesses,isatthereallocationsandnotonlyvirtualviathedatacontainer.
Bytheway,therewillalwaysbeaninterestingquestionwhetheryoucanactuallyconvincetheApplereviewers,thatyourapplicationabsolutelyrequirestheserights.Anapplicationshouldatleast,accordingtothespecificationsofApple,requestonlyasmuchrightsasyouneedtomeetyourrequirements.
Youshouldthereforegivethedescriptionofwhyyourapplicationrequirestheserightsreallyhardandnevermakeacompletelymeaninglessstatement.Formymulti-screen-shotprogramIhadnosuccesswithit,soIhavethereforechangedthestrategyandthestoragelocationsfortheprogram.
WhentalkingaboutsandboxingIwanttellyou,thatthereisuntilnowanotherunsolvedproblem:theOpenandSavedialogsunfortunatelydonotworkonMACOSXLion.Theapplicationfreezesonlybriefly,nothinghappens,thatwasit.
ForMacSnowLeopardasandboxedapplicationloadedfromtheAppleStoreworksproperly,butnotsurprising,becauseSnowLeoparddoesnotsupportsandboxingatall.
Furthermore,IhavetheimpressionthatperhapstheApplerevieweronlyormainlyalwaysconducttheirtestswiththelatestAppleoperatingsystem,sountilrecentlywithMACOSXMountainLionandnowwiththenewMavericks(MACOSX10.9)andYosemite(MACOSX10.10).ButfrommyownexperienceIknowthatapplicationscanstillcome
intothestore,althoughtheOpenandSavedialogsinLionnotwork.
Asadeveloper,youcanactuallynotbeveryenthusiasticaboutthesandboxing,becauseitisverydifficulttouseanditisnoteasytoaddusefulfunctionalitiestoyourprogram.
ItmaythereforebetherightdecisiontodeveloptheapplicationwithoutsandboxingandthensellitjustoutsidetheAppStore.Whichofcoursehascertaindisadvantages,becausethemarketpowerthatApplehasnowwithhisstore,youcannormallynotconcurwith.Unlessyouarerepresentingyourselfasalargerormultinationalcompany.
Section6:SandboxingandpersistentrequeststoBookmarks
Theoptionsetoutinsection5isuseful,ifyouwanttohavefromthebeginningonadirectaccesstogivendirectories.
Ifitwillbeclear,thattheuserneedspersistentaccesstofoldersandfilesoutsidethesandbox,butnotclear,whichfolders,youcanuseherebookmarksinconnectionwithcertainentitlement-keys.
Therearetwotypesofbookmarks,youcanusehere:
App-ScopedBookmarks
com.apple.security.files.bookmarks.app-scope
Thisgetsyourapplicationaccesstothefoldersandtheircontents,selectedbytheuserifthebookmarkwascreatedforafolder,ortoasinglefile,whenthebookmarkwascreatedforafile.
Document-ScopedBookmarks
com.apple.security.files.bookmarks.document-scope
Allowsyoutocreateabookmarkforonefile.Whileonlyyourownapplicationhasaccesstotheresourcewiththeapp-scopedbookmark,withtheDocument-ScopedBookmarkanyapplicationgetsaccesstothefilewhentheyhaveaccesstothebookmark(andthefile).ArequirementforDocument-Scopedbookmarkisstill,thatthefiletowhichthebookmarkreferences,isnotinafolderthatisonlyusedbythesystem,suchas“private”or“Library”.
Heretheprocessisdescribed,howyouwillgetapermanentaccessviaapp-scopedbookmarkstoafolder(anditscontents)thattheuserhasselectedandhowyoucanaccessthemagain:
Step1
Theuserselectsafolderwiththe“MACSelectDirectory”functiondescribedinAnnex2.Inthismomentafunctionwillbecalled,thatAppledescribesasthePowerBoxmechanism.Indeed,iftheoriginalMACOpenandSavedialogsused-andonlythen-thePowerBoxfunctionmakesthattheprogramduringitsruntimewouldcontinuetohaveaccesstotheselectedresource(i.e.inthisexamplethefolderanditscontents).
Step2Ifwewant,thattheprogram-evenafterrestarting-hasagainaccesstotheselectedfolder,withoutre-usingtheopendialog,wehavetouse“SecurityScoped”bookmarkshere.
Whatisextremelyimportanthere(withoutitwillnotwork):Youhavetocreatethisbookmarkatacertainmoment.AndthismomentisaftertheuserhasselectedthefolderwiththeOpendialog(aNSPanelobject)andbeforethepanelisreleased.TheoperatingsystemprovidestheabilitytocreateaSecurityScopedBookmarkonlyinthisbriefmomenttoyourapplication.
Therefore,wecannotusetheOpenandSavedialogcomponents,thatcomeswithDelphi.Iftheexecute-functionoftheopendilaogreturns,theNSOpenPanelisreleased.AlthoughthePowerBoxgrantedfurthermoreaccessatthedurationoftheruntimeoftheprogram,thereisnowaytocreateaSecurityScopedBookmarkafterclosingthedialog.
Ihavethereforethefunction,describedinAnnex2,hereforthesandboxingadjusted(simplifiedillustration):
{$IFDEFMACOS}
functionMACSelectDirectory(constATitle:string;varADir:string;CreateBookMark:Boolean):Boolean;
var
LOpenDir:NSOpenPanel;
LInitialDir:NSURL;
LDlgResult:NSInteger;
Data:NSData;
begin
Result:=False;
LOpenDir:=TNSOpenPanel.Wrap(TNSOpenPanel.OCClass.openPanel);
LOpenDir.setAllowsMultipleSelection(False);
LOpenDir.setCanChooseFiles(False);
LOpenDir.setCanChooseDirectories(True);
ifADir<>”then
begin
LInitialDir:=TNSURL.Create;
LInitialDir.initFileURLWithPath(NSSTR(ADir));
LOpenDir.setDirectoryURL(LInitialDir);
end;
ifATitle<>”then
LOpenDir.setTitle(NSSTR(ATitle));
LOpenDir.retain;
try
LDlgResult:=LOpenDir.runModal;
ifLDlgResult=NSOKButtonthen
begin
ADir:=string(TNSUrl.Wrap(LOpenDir.URLs.objectAtIndex(0)).relativePath.UTF8String);
Result:=True;
{$IFDEFUseSandbox}
if(AppScopedBookMarksEnabled)and(CreateBookMark)thenbegin
CreateAppScopedBookMark(Data,LOpenDir.URL);
end;
{$ENDIF}
end;
finally
LOpenDir.release;
end;
end;
{$ENDIF}
Toensure,thatthedialogdoesnotpermanentlycreatebookmarks,wheninvoked,youcancallthefunctionwithapassedvariable,thatwillhandlethis(Booleanvariable“CreateBookmark”)and,secondly,whetherthesandboxfunctionalityshouldgenerallybesupportedbytheapplication(compilerdirective“UseSandbox”).Thecompilerdirectiveallowsyoutocompiletheapplication,ifrequired,withoutsandboxing.Soyouhaveaneasyoption,tosellyourprogramalsooutsideoftheAppStore,ifyouwantthat.
Furthermore,itischeckedwhetherAppScopedbookmarksaresupportedatall,thatisonlyfromMACOSX10.7.3onornewerthecase(checkedwiththe“AppScopedBookmarksEnabled”,whichyoucanreadintheappendix).
Theyellowmarkedtextrepresentsthetime,atwhichthebookmarkshouldbecreated.
Here,beforetheNSOpenPanelisreleased.
IntheCreateAppScopedBookMarkfunctionIhavedonetheDelphiimplementationoftheNSURLfunction“bookmarkDataWithOptions”(definedinMACApi.Foundation.pas),whichgeneratesthebookmark:
functionCreateAppScopedBookMark(vardata:NSDATA;URL:MacApi.Foundation.NSURL):Boolean;
var
err2:NSError;
includingResourceValuesForKeys:NSArray;
relativeToURL:MacApi.Foundation.NSURL;
begin
Result:=False;
//Checkwhetherthisalreadycontainsanentryexists
//ReturnbutexistingDataObject
//Forsimplicityhereaway
//…
//Bookmarkdoesnotexist,thereforecreate
err2:=TNSError.Create;
err2:=NIL;
includingResourceValuesForKeys:=NIL;
relativeToURL:=NIL;
Data:=URL.bookmarkDataWithOptions(
NSURLBookmarkCreationWithSecurityScope,
includingResourceValuesForKeys,
relativeToURL,//NIL=App-Scope
@Err2);
try
ifnotAssigned(err2)thenbegin
Result:=True;
ifData<>NILthenbegin
//BookmarkindasBookmarkverzeichnisspeichern
//z.B.Data.writeToFile(NSSTr([Dateiname]),true);
endelsebegin
Result:=false;
//ShowMessage(‘DataisNIL’);
end;
endelsebegin
ShowMessage(String(err2.localizedDescription));
end;
except
ShowMessage(‘Problemwitherr2inCreateAppscopeBookmark’);
end;
end;
Step3
Ifyou(afterrestartingtheprogram)wanttoaccessthefolderlater,youmustloadthepreviouslysavedbookmarkandresolveit,byusingthe“ URLByResolvingBookmarkData “,function,whichisalsoafunctionoftheNSURLinterface.
functionGetResolvedAppScopedBookMark(data:NSData;varNEWURL:NSURL;varADir:String):Boolean;
var
Err:NSError;
relativeToURL:MACAPI.Foundation.NSURL;
begin
Result:=False;
err:=NIL;
RelativeToURL:=NIL;
NewUrl:=TNSURL.Wrap(TNSURL.OCClass.URLByResolvingBookmarkData(
Data,
NSURLBookmarkResolutionWithSecurityScope,
RelativeToURL,
0,
@Err));
if(NewUrl<>NIL)and(notAssigned(err))thenbegin
Result:=True;
end;
end;
Step4
Ifthebookmarkwassuccessfullyresolved,youhaveaNSURLobject,withthatyoucangetaccesstothefolderthen.
However,youmustusethereforethefunction
„URL.startAccessingSecurityScopedResource“
IfthisreturnsthevalueTrue,youcantakeaccesstothefolderorthefilestherein.
Step5
Thenyouterminateaccesstotheresourcewith
URL.stopAccessingSecurityScopedResource
Itisimportantthatthecallsto“start”and“stop”functionsarealwaysbalanced.Youcanmakeacoupleofcallswith“start”insucsession,butyouhavemakeallthe“stop”callsagaininreverseorderwiththerespectiveURLs.Ifyoucomehereoutofbalance,youcannotcallanymoreresourcesduringtheexecutionoftheprogram(sothenonlyafteryourestarttheprogram).
Thementioned“start…”and“stop…”functionsarenotimplementedinDelphiXE3-XE7inthe“MACApi.Foundation.pas”fileintheNSURLinterface.IhavealreadyleftthisatEmbarcaderobyQualityCentralaswish,thattheywillimplementdirectlytheseimportantfunctions.Butuntilnow,itonlyremainstomakethemissingfeaturesinaseparateinterfaceimplementationinanaccessiblelocation.Thismeans,unfortunatelyanunnecessarycomplicationoftheprogramlogic.
Tomakethewholethingalittleeasier,Ihaveincludedattheendofthebookasanattachmentmycurrentunit“HSW.FMXSandbox.pas”.YouwillalsofindmissingconstantvaluesthatyouneedtousetheMACfunctions.Befreetousetheunit,butatyourownrisk.ThereisnoguaranteethatIhaveeverythingproperlyimplemented.Evenifthethingnowseemstoworksuccessfullyinmyprograms,youshouldstilltesttheuseindetailinyourprograms.Ifyoufindanyerrorsorifyounoticeinconsistencies,Iamgratefulforahint.
IntheunitthatIhavegeneratedarealreadyintegratedthefunctionstostoreandmanagebookmarks,thatwillsignificantlysimplifiesaccesstotheresources.Thislooksthanlike
this:
FunctionLoadLastFile(FileName:string):boolean;
Var
{$IFDEFMACOS}
{$IFDEFUseSandbox}
URL:HSW.FMXSandbox.NSURL;
HasAccess:Boolean;
{$ENDIF}
{$ENDIF}
Begin
Result:=false;
{$IFDEFMACOS}
{$IFDEFUseSandbox}
ifAppScopedBookMarksEnabledthenbegin
//Resourceanfordern
ifGetAppScopedAccessToResource(Filename,url)thenbegin
HasAccess:=True;
endelsebegin
ShowMessage(‘Can’tgetaccessto‘+Filename);
Exit;
end;
end;
{$ENDIF}
{$ENDIF}
//here:openfile…
//Result=Trueorfalse
{$IFDEFMACOS}
{$IFDEFUseSandbox}
ifAppScopedBookMarksEnabledthenbegin
ifHasAccess=Truethenbegin
ifnotCanStopAccessingSecurityScopedResource(Url)thenbegin
//giveanerrormessage
end;
end;
end;
{$ENDIF}
{$ENDIF}
End;
Andsothedatastructurewouldthenbecreatedinyourapplication:
Inthecontainer,inAppSupportPaththeindividualbookmarksaresavedinthe“AppScopeBookmarks.dat”file(asimpleTStringList);
Thelistentriesconsistofthenameofthefolder(orfiles),whichhasassignedabookmarkname.Togrant,thatthebookmarknamesareunique,IuseGUIDnamestherefore.ThebookmarksthemselvesarestoredunderthenameoftheGUIDidentifierinthefolder“Bookmarks”.
Atleastitisasolutionthatworks.Eachbookmarkfilesconsistofalettersandnumbers,probablywithbinarycontent.
Specialcase“savenewlycreatedfilesoutsidethesandbox”
Ifyouwanttocreateanewfileoutsideofthesandbox,thereare2options.
Option1:Youfirstlettheuserselectafoldertostorefilesandtherebycreatedabookmark.Thentheyhaveduringruntimeaccesstothefolderandcancreatefilesandhavealsoafterrestartingtheprogramaccesstothem.
Option2:Moreoften,however,probablytheotherwaywouldbeused:Theuserofyourprogramhasanewtextdocumentoragraphicorwhatevercreated,andthecontentshouldnowbesavedtoafile.Aslongasyousavethecontentsinthesandboxenvironment-noproblem,thisyoucandoofcoursealways.
Buttheusermaywanttoplacethefileinthefolder“Documents”or“MyPictures”or
“Desktop”.Ofcourseyoudonotwanttoasktheuserineachcaseinafirststeptoselectafolderandinasecondstep,thefilenameforit.
Thereforeitisobviousthatyouwanttousethe“SaveAs”dialog.Again,youcannotusetheTSaveDialogofDelphi,becausethesavingofabookmarkcanbeappliedonlyduringthelifetimeoftheNSSavePanel.
NowitissothataBookmarkonlyforanexistingfolderoranexistingfilecanbecreated.Butyourfiledoesnotyetexist,youcancreateitfirst,aftertheuserhasgivenanameforit.
Thetrickthatwewillusehere,isthattheextendedNSSavePaneldialogwilldirectly-afterselectingthefilename-createadummyfilewiththeselectednameandthenitdirectlygeneratesthebookmark.Thenthedialogwillbereleasedandyouhaveafile,intothatyoucanwritethecontentyouneed.Withthebookmarkyoucanalsogetaccesstothefileafterrestartingtheprogram,e.g.ifthefilewillbeloadedautomaticallyas“Openlastfile”afteryourprogramlogic.
Withthissecondwaythereisasmallcaveat:Youcan’tchangethenameafterselectingthefilenamebytheuser,becausethebookmarkwascreatedalreadyforthenameselectedbytheuser.Iftheuserthereforeindicatesanunusualextension,theprogramcan’tchangeitanymore.
Soifyouneedthataspecificfileextensionismandatory,youmustmodifythefunction,whereIhaveimplementedtheNSSavePaneldialog.Youthanhavetoensure,thatthefilenamewillbeadjusted,beforethebookmarkiscreated.
Conclusion:Theissueofsandboxingisnotaverysimpleissue.That’swhyIhopeevenmore,thatthisinformationwillhelpyoutomakeyourappasdesiredsandbox-enabled.
Section7:UseMACAPIs(POSIX,COREandCocoa)inDelphi
TheMACOSXoperatingsystemessentiallyworkswith3-layersystems:
POSIXCOREAPICOCOAFramework
WhilethefirsttwolayersareaddressedoveraconventionalC-interface,theCOCOAlayerisaccessibleviaaspecialObjective-Cinterface.Asetoffunctionsisultimatelyinallorseverallayers.Forexample,youcanquerythecomputernamewithaPOSIXfunction(gethostname)ortheCOCOAinterfaceNSHost(Host.Name).
Inthefollowingwefocusontheindividuallayers:
POSIX
ThePOSIXinterfaceofferstypicallow-leveloperatingsystemfunctions,whichcanalsobefoundinotherUnixorLinuxsystems.
Soyoucan,forexample,askforthenameofthecomputeronwhichyourprogramisrunning,withthePOSIXfunction“gethostname”.Thisfunctionisdefinedinthe“Posix.Unistd.pas”.Youcouldhereinterprettheabbreviation“UniStd”perhapsas“Unixstandard”becausetherearejusttypicalforUnixstandardfeaturestofind.
Strictlyspeaking,thefunctionisinthe“UniStdAPI.inc”filetofind.It’sworthittosayhere2-3wordsonthestructure,withthatEmbarcaderohasimplementedtheAPIfunctions.
Under
C:\ProgramFiles(x86)\Embarcadero\Studio\15.0\source\rtl\posix
youcanfindthePOSIXfolder.Hehasasubdirectorycalled“OSX”.Theconcretimplementationsarefoundinthe“.INC”-files(.INCstandsfor“include”).Ifyoulookat
theindividualunitsofthePOSIXfolder,forexample,inthe“Posix.Stdlib.pas”youwillfindIFDEFdeclarationsinthefollowingway:
{$IFDEFMACOS}
{$Iosx/StdlibTypes.inc}
{$ENDIFMACOS}
{$IFDEFLINUX}
{$Ilinux/StdlibTypes.inc}
{$ENDIFLINUX}
TheLinuxfolderdoesnotexistyet,butwemaysafelyconcludethatEmbarcaderoisworkingonaLinuximplementation.
SothefunctionintheUniStdApi.incisimplemented:
functiongethostname(name:MarshaledAString;namelen:size_t):Integer;cdecl;
externallibcname_PU+‘gethostname’;
{$EXTERNALSYMgethostname}
TomakethemsuitableforDelphi,wecandothisasfollows:
…
Uses
Posix.unistd.pas,
…
functionmac_GetComputerName:string;
Implemtation
functionmac_GetComputerName:string;
var
buf:Array[0..255]ofAnsiChar;
begin
ifgethostname(buf,sizeOf(Buf))<>-1thenbegin
Result:=UTF8ToUnicodeString(buf);
ifpos(‘.local’,Result)<>0thenbegin
Result:=copy(Result,1,pos(‘.local’,Result)-1);
end;
end;
end;
ProbablythecomputernameonUnixmaybesignificantlylesstimethanthe256charactersthatIreservehere,butsinceIdonotknow,I’llgoonthesafeside.
ForMacOSX,thereturnnameisnormallysupplementedwitha“.local”,bywhichhecanbeaddressedoverallinthenetwork.Forourpurposes,however,weonlyneedthenameasitwillusuallyappearinthesystem.
AsimilarlyusefulfeaturethatyoucouldstillimplementfromthePosix.unistd,wouldbethe“getLogin”functionwithwhichyoucanquerytheusername.Sinceweneedherenobufferinwhichthenamemustbestored,wecanusethisfunctiondirectlyas:
„strUsername:=UTF8ToUnicodeString(GetLogin)“.
COREAPI
MostofthecoreApisyouwillfindintheunit“Macapi.CoreFoundation.pas”.Again,takealookattheDelphifolderstructure.TheMacApi.Corefoundation.pascanbefoundat:
C:\ProgramFiles(x86)\Embarcadero\Studio\15.0\source\rtl\osx
Alsolookonceintotheunit,it’sashortunitthatintegratesanumberofincludefiles,suchastheCFString.incfilethatimplementssomeCFStringfunctionsinDelphi,whichweneedtoworkwithCFStringobjects.
AlsotheCoreAPIisaccessedviaaCcallsystematic.ThemaindifferencewiththePOSIXfunctionsisthatbehindtheso-calledCoreAPIare“Referencecountedobjects”.Thatmeans,thatbehindfunctionsordatastructures,areintruthobjects,sometimesevenCOCOAobjects.
Soifyouwanttousestringsaspassingparametershere,thiscan’tbeDelphistrings,butitmustbeCFStrings,soreferencecountedstringobjects.
HereisanexamplethatdemonstratestheuseoftheCFStrings:
proceduremac_ShowMessageNative(aHead,AMsg:string);
var
CFHead,CFMsg:CFStringRef;
AResult:CFOptionFlags;
begin
CFHead:=CFStringCreateWithCharactersNoCopy
(NIL,PChar(AHead),Length(aHead),kcfAllocatorNull);
CFMsg:=CFStringCreateWithCharactersNoCopy
(NIL,PChar(AMsg),Length(aMsg),kcfAllocatorNull);
try
CFUserNotificationDisplayAlert
(0,1,NIL,NIL,NIL,CFHead,CFMsg,NIL,NIL,NIL,AResult);
finally
CFRelease(CFHead);
CFRelease(CFMsg);
end;
end;
HeretheCoreFoundationfunction“CFUserNotifiationDisplayAlert”isimplemented.Weusehereonlyaverysimpleimplementation.Ifrequired,thealertfunctioncouldevenbeenshownwithatime-outtime,withafurtherbuttonanduser-definedtextforthebutton.TheexactskillsofthefunctionyoucanseeintheMacDeveloperLibraryonthefollowingpage:
http://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFUserNotificationRef/Reference/reference.html
TheCFStringCreateWithCharactersNoCopyfunctioncomesfromtheMacAPI.CoreFoundation.pas(orCFString.inc).ItcreatestheStringobject.
Allfunctionswhosenamescontainsa“Create”or“Copy”,cause,whentheyarecalled,thatthereferencecounttotheobjectisincreasedbythevalueof“1”.Afterusingtheobjectsyoumustthereforereleasethemwiththe“CFRelease”sothatthereferencecountcanbereducedbythevalueof“1”again.Ifthereferencecountisresettozero,theobjectistotalreleased.Ifyoudonot,youretainresidualobjectsinmemorythatwillremainaftercompletionofyourprogram.IdonotknowhowtheprogramsarecheckedforMAC
Appstoreforsuchshortcomings,Iwouldadvisetoworkcarefullywithsuchobjects.
Animportantadditionistomentioninthiscontextforfunctionsthathavea“Get”inthename.Thereobjectsarenotcopiedornewcreated,youjustusetheoriginal.Directlyafterusingthe“Getxxx”function,youhavetocallthefunction“CFRetain”beforeyoucontinuewiththeStringobjects.CFRetainthenalsoperformsanincreaseofthereferencecounterandassuresyouthefurtheruseoftheobject.Aftercompletionoftheworkwiththeobjectyougiveitfreewith“CFRelease”,whatthereferencecountdecrementby“1”.
COCOAAPI
TheAPI’sfromtheCOCOAFrameworkarespecificallytailoredforusewithObjective-C.Manyobjectsandfunctions,youwillfindinthe“Macapi.Foundation.pas”implemented,forexample,theentireURLfunctionsIuseintheHSW.FMXSandbox.pasunit.LookonceinthisMacApifile,youfinditinDelphiiXE7alsointhefollowingfolder:
C:\ProgramFiles(x86)\Embarcadero\Studio\15.0\source\rtl\osx
TheCOCOA“objects”(Objetive-Cclasses,metaclassesandprotocols)areusuallyimplementedasinterfaces.Therefore,fortheNSStringobject/classyouwillfindtwointerfaceimplementations,onceas
NSString=interface(NSObject)
andalsoas
NSStringClass=interface(NSObjectClass).
Thesameapplies,forexample,forNSURLandNSURLCLASS.Itisthereforeimportanttoknow,becauseyouwillsometimesneedfunctionsoftheoneandoftheotherinterface.Bytheway,hereisimplementedmuch,butnotall,nowandthentherewillbetimeswhenyouneedtoupgradeindividualfunctionsthroughare-implementationitself,likeIhavemadeitforexampleintheHSW.FMXSandbox.pasunit.
Butagain,let’slookatanexampleofhowCOCOAobjectsaretobeused,hereweuseaNSWorkspaceobject(theunitMacApi.Appkit.pasmustbeincluded).However,Iwillshowyoufirsthowitdoesnotwork,becausethatiswhatyouwouldnormallytry(andoftenseesinforumswherethenaskswhythissodoesnotwork):
var
URL:NSURL;
Workspace:NSWorkspace;
begin
URL:=TNSURL.Create;
URL.initWithString(NSSTR(’http://www.hastasoft.de’));
Workspace:=TNSWorkspace.Create;
Workspace.openURL(URL);
URL.release;
Workspace.release;
Itwillthereforenotwork,becauseasaresultoftheDelphiObjective-Cbridgejusta“Raw”objectissuppliedbackthatissonotusable.MoreoverappliestotheWorkspaceobjectthatforeachprogramonlyonesharedworkspaceobjectisavailable,whichmustbeaccessedviathe“sharedWorkspace”.
Detailstothisextremelyusefulobjectcanbefoundhere:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/Reference/Reference.html
Properlythereforetheuseofitlookslikethis:
var
URL:NSURL;
Workspace:NSWorkspace;
begin
url:=TNSURL.Wrap(TNSURL.OCClass.URLWithString(NSSTR(’http://www.hastasoft.de’)));
WorkSpace:=TNSWorkspace.Wrap(TNSWorkspace.OCClass.sharedWorkspace);
Workspace.openURL(URL);
Thereisthenalsononeedtoreleaseanything,becausenothingiscreated,onlyvariables(objects)weresetwithcontent.
ThemostCOCOAobjectsarereferencecounted,butthisusuallyworksautomatically,in
theexamplecase,therefore,neithera“retain”nora“release”aftertheuseoftheobjectisrequired.
InCOCOAobjectsitisonlyinexceptionalcasesrequiredtoworkwith“Release”functions,thissimplifiesworkingwiththeseobjectsverymuch.
Chapter3:RequirementsforCross-PlatformDevelopment
Section1:SettingupWindowsPCandMACPC
YoumusthaveaWindowsPCandaMAC,whichareconnectedviaanetwork(wirelessorwirednetwork).Alternatively,youcanalsosetWindowsinavirtualmachineontheMAC.Ifindthelattersolutionnotsogood,becauseI’verunonmyMacdifferentpartitionswithdifferentMACOSXversions,thatIrunasneeded.Andthenit’sanadvantagetohaveyourownWindowsPC,withDelphithereinstalled.
Preparation
OnyourWindowsPC,refertothefollowingdirectory
C:\ProgramFiles(x86)\Embarcadero\Studio\15.0\PAServer
tothefile“PAServer15.0.pkg”.CopythisfiletotheMAC(e.g.tothedesktop).ThealsointheWindowsdirectoryexistingfile“Setup_paserver.exe”-isnotneeded(itisused,ifyouwanttoperformonanotherWindowsmachinearemotedebugging-andthanyoumustinstalleditontheotherWindowsmachine).
Smalldigression:HowtosetupadriveconnectingfromyourMactoyourPC:OpentheFinderandchoosefromthemenu“Goto”the“ConnecttoServer”command:
ClickConnect.Ifyoudotheconnectionforthefirsttime,thisdialogwillappear:
UnderName,entertheusernameoftheWindowsPC’sandalsothelocalpassword(notthepasswordoftheMAC-PC).
Iftheconnectionworks,youhaveontheMAC,anewvolume(drive)available.Withthisdriveyoucanshareyourfiles.
ThenclickontheMAConthe.PKGfile,itisautomaticallyextracted.Thendouble-clicktheunpackedfile“Setup_paserver”.Thefollowingsetupprogramstarts,whichinstallsthePAServerontheMAC.
ThePCAServerprogramisaninterfaceprogramthatDelphineedstotransmittfilesoveraTCP-IPconnectionfromyourWindowsPCtotheMACandthenstartthemthere.
ThePAServerisfoundafterinstallationontheMACintheprogramfolder:
/Applications/PAServer15.0.app
AttheInternetitisalsodescribedinadocwikihowtheservershouldbeinstalledandrunning:
http://docwiki.embarcadero.com/RADStudio/XE7/en/Installing_the_Platform_Assistant_on_a_Mac
Tostarttheserver,gototheApplicationsfolderandlaunchtheapplication“PAServer15.0”
Aconsolewindowwillbeopenthen,whereyoujustconfirmthestartwithreturn,ifyoudon’twanttoassignapassword(inmyopinionisthatneverneeded).
ThenApplewillpromptyoutoenteryouradminpassword.Ifyouhavedonethis,theserverprogramisreadyandwaitingforaconnection.
DelphitransmitthentheontheWindowsPCproducedMACapplication,usingthePAServer,totheMACintotheso-called“ScratchDir”.Thiscanusuallybefoundhere:
/Users/YourUserName/PAServer/Scratch-Dir.
HereI’vecreatedalinkinmyMACOSFilemanagerdirectytothisfolder:
Section2:EnablingMACOSXPlatform
Bydefault,inthe“MultiDeviceApplication”theMACPlatformisautomaticallyasatargetintheProjectManager.Ifyouhavethisdeletedonceordonotfindinaninheritedproject,addyourFireMonkeyprojectbyusingtherightmousebuttononthe“TargetPlatforms”buttonintheProjectManagerandselectfromthepopupmenu“AddPlatform”command,
Ifyoudothisforthefirsttime,theplatformmustfirstbeestablished.
YoumayneedalsotomakesettingsfortheSDK.Forthispurpose,thePAServerontheMACmustalreadyberunning.Clickwiththerightmousebuttonontheplatformnameandselect“EditSDK”command:
BeforeIgointothisdialog:Itcouldbe,thatbeforethisdialogappears,youhavetoinputaprofilename.Andnowtothedialog:
ForHostName,IrecommendinsteadspecifythenameoftheMACPC,totaketheIP-addressfromtheMACinthenetwork(youwillfindtheIPaddressoftheMACthereunder“ControlPanel,Network”).Theconnectionworksfasterinmixedwiredandwirelessnetworks,andsometimesitworksonlywiththeIPaddress(TIP:IftheIPaddressisdynamicallyassignedbyyourrouter,theIPaddresswillchangeatthenextday,whenyouworkagainwithyourconection.SoifsubmissiontotheMACdoesnotwork(itthenonlygivesthemessage“Error”,withoutfurtherexplanation),thisshouldbethefirstthingtocheck.YoucanalsoadjustthesystemsettingsoftheMAC’s,sothatherealwayswillbeusedaspecificIPaddress,thenyouwillneverneedtochangeanythinghere(Ihavedoneitlikethat,allworksgreat!).
Leavetheportnumberhereasspecified,underpasswordleaveitblank,orenterapasswordforthePAServerifyouhavesetupone(noneedusuallythereforanditcomplicatesmattersevenunnecessary).
Butnowbacktothe“EditSDK”dialog.HereyoucanselectasuitableSDK.Thereareherenormallyavailableoneorseveraltochoosefrom.Normallyselectthenewest.Orselectonolderone,ifthereareproblemswiththelatestSDK.SDKversionsaredirectlyrelatedtotherespectiveOSversions.
IfyouhaveaddedaplatformthanitcouldlooklikeintheProjectwindowlikethis:
ImportantNote:DependingonwhetheryouselectthetargetplatformWindowsorMACOSX,alsothepresentationoftheProjectOptionsdialogchanges.Specialattentionishereaboutthe“VersionInfo”.Becausewiththatitislatermadetheinfo.plistfilethatmustaccompanyeachMACprogram.
AlsoofimportanceintheProjectOptionsisthe“EntitlementList”,whereyoucanspecifywhichrightsareavailabletoyourprogram.Inparticular,ifyouwanttodistributeyourprogramsontheAPPLEAppStore,youshouldchoosecarefullytheinformationhere.
FromtheEntitlementListDelphigeneratesthe“.Entitlements”filethatisdistributedwiththeprogram(whichisespeciallyforthesandboxingmodelofrelevance).
TheEntitlementList:
TheVersionInfo:
Ifneeded,youcanaddanewkeyhere.Thisisbyclickingtherightmousebuttononthetableheader“Key”(seeinyellow):
Thebelowdialogwillthenappearwhereyoucanenterthenewkeyname:
Unfortunately,youcanonlyentersinglevaluesinthegridlist.Ifyouaddanarraystructure,thislooksatfirstsightgood:
However,theeditorinsertsanadditional<String>entrysothatthekeygroupultimatelycan’tbeutilized.Therefore(andaslongitisnotfixedinDelphi),youmustedittheinfo.plistfilebyyourselfinatexteditor,sothattheentriesarevalidhere.
Section3:Provisioninganddeployment
Undermenu“Project”,youcallthecommand“Deployment”andtheregister“Deployment”willbeshown.Hereyoucanaddmorefilestoyourproject.Thatcan,forexample,beHTMLHelpfilesorplaintextfilesorgraphicsthatarerequiredbyyourprogram.Thisisareallyhandything:
The.rsmfileisafile,whichisrequiredonlyforthedebugger.Ifyoulatercreatethefoldermanuallyfroma.dmgfile(DiskImage)(i.e.fordistributionoutsidetheappstore),youmustdeletethefileinthescratch-directoryfromtheMacOSfolderbefore.Howitworksandhowtocreatethe.dmgfile,seebelow.The.rsmfileisalsonotrequiredfordistributionintheMACAppstore.Therefore,forallcompletedprojects,deletethisfilefromtheapplicationbundle,asfarasDelphihasnotalreadydonethisforyou.
IfDelphihascreatedtheprogram,DelphitransmitsittotheMACintotheScratch-Dirdirectorythatyoucanfindnormallyunder
“/Users/YourUserName/PAServer/Scratch-Dir”.
Theexecutablefileisusuallylocatedinthe“MacOS”folder.Under“Contents”,youwillfind“info.plist”andthe“Entitlements.plist”.Thelybcgunwind.1.0.dlybfile,youneedinthedeliveryversionoftheprogram,otherwiseitwillnotrun.
1.SubmissiontotheAPPLEAppStore
ForthisyouhaveunderthetargetplatformMACOSentrytoswitchfrom“Normal”to“ApplicationStore”:
Underthemenu“Project,Options,Provisioning”,checkthatthereisselectedasabuildtype“OSXApplicationStore”.Andofcourseyouusethe“Releaseconfiguration”.Inthetwoinputlinesyoumakeentriesaslistedbelow:
Theinstallationprofile“3rdpartyMacDeveloperInstaller”isacertificatethatisusedtosigntheinstallerfile.
FirstcheckonyourMacinthe“KeychainAccess”ifyouhavealreadyinstalledtherequiredcertificatesonyourMAC:
Ifyoudonotalreadyhavesuchacertificate,youcangetonefromApple,butyoudefinitelyneedanAppleDeveloperIDforit(seebelowunder3a).
Ifyouhavecalledthecommand“Deploy<Projectname>”intheDelphiProjectmenu,DelphitransferstherequiredfilestoyourMacandcallsthecodesigningtoolthatinitiallysignedtheexecutable(youwillbepromptedtoallowthis).Rightafterthat,Delphicallsthe“ProductBuildTool”thatproducesaso-called“Package”,afilewiththeextension“.pkg”willbecreated.This“package”isthenalsotheinstallationfile,andthatiswhyitisthenalsowiththecodesigningtoolsignedwiththeInstallationCertificate(youmustalsoallowthis,unlessyoualwaysallowthis).
ThiscompletedPackage,youcanthenuploadtoApplewiththeAppleprogram“ApplicationLoader”.First,however,youmustcreateadataentryforyourprogramthroughiTunesConnectandcompleteyourinputwith“Readytouploadbinary”.Aftertwomoreconfirmationsfordatasecurity,youcanthenuploadyourapplication(thepackage)withtheApplicationLoader:
IfyourprogramisattheAppleemployeesforreview,itusuallytakes3-7daysuntilactuallyanassessmentiscarriedout.
Tostayinformedabouttheprogress,youcanuseafreeappfromApple(ITC-Mobile),ImadebelowascreenshotofmyiPhone.
Thisallowsyoutomanagetheappstoacertainextentandexamineyoursales.
Theprogramswiththegreendotsarealreadyinthestore.Thesewithorangedotsareeitherwaitingforarevieworarejustinreview.Inthegrayboxyoucanseethe“OSX”or“iOS”label.Thatwillindicate,ifitisaprogramfortheMACorforiPhone/IPad.Under“Sales/Trends”youcancheckforadayorforaweek,howmanyproductshavebeensold.Alsoyoucanseethecountriesinwhichyouhavesoldyourprograms.Surprisingly,IwasabletofindevenwithmyonlyGerman-languageprogramsbuyersintheUS,Russia,Thailand,Japan,Italy,MexicoandSlovakia.
AfteramonthyouwillgetfromAppleoneormore“FinancialReports”viae-mail,youcanviewitoverthewebiniTunesConnect.Thereyoucanseehowmanyunitshaveyousoldinthecountriesandwhatamountofmoneyisowedtoyou.Andyouwillseewhenthenextpaymentdateis,whenApplewilltransferyourshare.Butmoneywillonlybepaidifyoutakemorethantheequivalentof$150.Thefirstregularpaymentdateisbetween1-2months,dependingonthetimeatwhichyouhaveentered.
Atip:BeforeyouloadaprogramintotheApplestore,lookatApple’s“AppStoreReviewGuidelines”on.Hereisthelinkto:
https://developer.apple.com/app-store/review/guidelines
2.Createa.dmgfilefordistributionoutsidetheAppleAppStore
IsellmyprogramsevenwithouttheAppStore.Thiscanyoudoalsoasfollows:AfteryouhavecreatedwithDelphiaMACOSXreleaseversion(withtheprojectsetting“Normal”andnot“AppStore”),runontheMacthe“DiskUtility”(Applicationsfolder,“Utilities).Selectthe“File”menuandselect“New”,“DiskImagefromFolder”:
TheFinderdialogisthandisplayed,soyoucannavigatetothedirectorywhereyourprogramis:
Selecttheprogramfileandclickon“Image”.Thefollowingdialogisdisplayed:
Irecommendtoremovetheextension“.app”andchoosethelocationofthedesk.TheinthiscasecreatedTEditor.dmgfile,youcannowdistributeitovertheInternettoyourcustomers.
Afterdownloading,thecustomerclicksdoubleonthe.dmg-fileandfromthewindowthatappears,dragsthefileintotheprogramfolderandtheprogramisthaninstalled.
3.CreateyourownsetuppackagewithApplicationDeveloperID/Installer
Anotherpossibilitywouldbetosigntheprogramwiththe“DeveloperApplicationID”certificate,andthencreateyourownsetuppackageandsignthiswiththe“DeveloperIDInstaller”certificate.
Whatarethebenefits?
Well,sinceMACOS10.7.5Appleusestheso-called“Gatekeeper”functionthatcanbeenabledinthesystemsettingsunder“Security”,“General”tab:
Evenifthereiswritten“AllowDownload”-itisprobablymeant“Run”.Becauseyoucandownloadaprogram,whichislocatedina.dmgfilefromtheInternetandeventhendragtheapplicationintotheApplicationsfolder.
Iftheuserhasthesettingsshownasaboveactivated,andisnowtryingtorunyourprogramthatyouhavesoldasdescribedaboveundernumber2,itwillbeshownadialogasfollows:
Soifyouwanttoplayitsafe,youneedtohaveyourprogramwiththecertificatesdescribedabove.InyourKeychainAccess,youmusthavethefollowing(colored)certificates:
Ifyoualreadyhavethesecertificates,youcanskipthenextsubsectionandreadmoreunder3b)“WorkingwithCodeSigntoolandPackageMaker”.
a)RequestaDeveloperIDcertificateandanApplicationDeveloperInstallerID
Ifyoudonotalreadyhavesuchcertificates,loginunderhttps://developer.apple.comandclickatthetoprightonto“MemberCenter”.
Thenclickonthelinkbelowisshown,thenyouwillbeforwardedintotheareawhereyoucanmanageyourcertificates:
Thenclickonthetable:“MACApps,Certificates”.ThistakesyoutothepagewherethemanagementofMACcertificatestakesplace.Clickrightthereontopontheplusbutton,thenyouwillgetthepagewhereyoucanchoosethecertificateaccordingtoyourneeds.
Ultimatelyyouneedallcertificatesthatareofferedhere(ifyoualsowanttousetheAppStore).Forourpurposesitisatthemomentsufficient,whenyouaskforthethelowercertificates(“DeveloperID”,notvisibleinthescreenshotabove).DownloadittoyourMAC,doubleclickonthefile,soitwillbeinstalledintoyourKeychainAccess.
Idonotwanttotakeallthestepshere,butmaybetwotipsonthis:
Tip1:Beforeyoucaninstallyourcertificates,youneedthe“WWDRIntermediateCertificate”andthe“DeveloperIDIntermediateCertificate”.Thisnoticeisalsodisplayedonthepageshownabove,ifyouscrolldown.
Tip2:Tobeabletocreateyourcertificatesingeneral,youneeda“CertifacteSigningRequest”file.
Wheredoyougetthemfrom?
UsingtheKeychainAccess.Hereyougoto“KeychainAccess”,“CertificateWizard,”“Requestacertificatefromacertificateauthority”:
Thenthefollowingdialogwillbedisplayed:
Onceyouhavefinishedthisdialog,youwillreceivetherequestedfile.InmycaseIhadtocreatethisfilebythewayseveraltimes,becauseofthedifferentcertificatesanddifferentrequestfileswereneeded.
Alsoanimportanthint:afterIgotmyDeveloperID’sandthiswastransferredtotheKeychainAccess,firstitwasdisplayed,thattheywereinvalidbecauseitweresignedbya“UnknownCertificateAuthority”.Ithashelpedtowait:24hourslater,Ihavedownloadedthecertificatesagainfromthecertificatesareaandre-installedit.Thanallwasperfect.
b)WorkingwiththecodesigningtoolandPackageMaker
IfyouhaveDelphirunningwithyourMacOSXprofileandcreatedyourMACprogram,youmustsignitthen.
SincetheprogramisnotfortheAppStore,youselectas‘Build-type”intheprojectoptions“OSX-Normal“.
AfterrunningtheprogramandthetransfertotheMAC,youmustusethecodesigningtoolmanually,tosignyourpackage.Youcanuseitbyinvokingitfromthecommandline.Todothis,openanotherterminalwindowandchangetothedirectorywhereyourapplicationresides.
Thentypeintheterminalwindow:
codesign-s“DeveloperIDApplication”TEditor.App
Whichthenofcoursereplace“TEditor.App”withthenameofyourapplication.
You’llagainbepromptedtoallowthesign:
So,nowyouhavesignedyourprogram,buthowdoyoucreatethesetuppackage?
Youcandothisusingthe“PackageMaker“,whichyouwillfindbydefaultnolongeronnewerMACOSversions.SoyoumustdownloaditfromApple.Thisisdonethereinthe
downloadarea(https://developer.apple.com/downloads/index.action?name=PackageMaker).Ifindoubt,searchfor“AuxiliaryToolsforX-Code”.
Then,installthedownloadedprogrampackage,openitandstartthePackageMakerprogram.
ForMacOSXMaverickorlatertheprogramcan’tbeused(apparently)becauseitkeepscrashing.I’vesimplyinstalleditonanolderMacOSXversion(MountainLion)anduseditthere.Hereyoucansee,thatitcouldsometimesbeusefultohavealsoolderMACOSSystemsinaccess(IhavethisonseveralofpartitionsonmyMac).Ifthat’stoomuchtrouble,youmustuseoneofthecommercialprogramswithwhichyoucancreateinstallationpackagesfortheMAC.IfyoucanusethePackageMaker,proceedasfollows:
At“Certificate”,youclickthearrow,andthenenteryour“DeveloperIDInstaller”certificate.Alsoyoucanmakebasicsettings,e.g.asthetitleofthesetup,wheretheprogrammaybeinstalled,andsoon.
Nextimage:justdragyourprogramfromtheFinderintotheleftsideofthewindow,orclickbesidetotheline“Install”onthebuttonandgotoyourfolderwherethecompiledandalreadysignedprogramis:
Soyoucaneasilyusethisdialogtomakethenecessarysettingsforyoursetuppackage.
Ifyouclickon“EditInterface”onthetopright,adialogappearswhereyoucanselecttheindividualinstallationstepsindetail:
Hereyoucandirectlyinsertyourcontractortextsorrefertoafile.Thelicenseinformationmustbeacceptedbytheuser,otherwisetheprogramcan’tbeinstalled.
Thenclickthe“Build”or“BuildandRun”,thenyoursetuppackageiscreated.Youwillbepromptedagaintoallowthesigningandthenyoursetuppackageisready.
Anditworkswellwiththegatekeeperiftheusershouldhavesettheinstallationrestrictionsdescribedatthebeginningofthissection.Forme,thisiscertainlynowthepreferreddistributionmethod(IuseditalsoformypreviouslywithLazaruscreatedMACprograms).
Note:Whenrunningthesetup-programonmyMac,thePackageMaker-Setupinstalledagainandagaintheprogramintothedirectorywheretheprogramwasdeveloped.Icouldnotstopthat,butultimatelyithasnonegativeimpacttootherMACPC’s,thereitworks,asexpected.
Chapter4:WorkingwithGraphicsinFireMonkey1.FireMonkeyTBitmapversusWindowsTBitmap
TheWindowsbitmapandFireMonkeybitmaparedifferent.Thisparticularlyconcernsthepixelformats.WhiletheWindowsbitmaphasbesidethe24-bitmapformatotherbitmapformatsavailable,themostwithFireMonkeylackthereofintheoutputformat.Inparticular,for1-bit,4-bit,8-bit,16-bitand24-bitoutputformatisnotavailableifyouwanttosavethebitmapwiththeextension“.bmp”.
WhileyoucanreadandwriteinWindowsTBitmaptheproperty“PixelFormat”,itisinFireMonkeyjustasareadingpropertyavailable.Thereisaprivate“SetPixelFormat”procedureintheTBitmapclass.Youcouldperhapsmakethisavailablewithaclasshelperfunction,butprobablyitwillnotmakemuchsense,sinceinternallyFireMonkeyalwaysoperateswitha32-bitbitmap.
However,whenusingtheformats,thattypicallyincludeanalphachannel,e.g.theformat“PNG”or“TIF”,theserelevantinformationisalsowrittentotheimagefile.
Whenloadingan8-bitbitmapfileinBMPformatandstoresagain,thecolorsaretakenovercorrectlyintheFireMonkeybitmap,butaftersavingthebitmapfile,itisa32-bitbitmapfile.
Andunfortunatelyinformationislost,ifoneloadsaWindows32-bitmapfilethathasanalphachannelintoaFireMonkeybitmap.Thevaluesofthealphachannelwillnotbetakeintoaccount,thisallhaveallthevalueof255(thatiscompletelyvisible).Iamnotquiteclearwhetherthisisabug.
2.TBitmapDatainsteadScanLineforbitmapmanipulation
WhileyoucanintheVCLprocessbitmapswiththe“ScanLine”function,itwillbereplacedwithFireMonkeysinceDelphiXE3bytheTBitmapDatarecord.
Thisrecordisdefinedasfollows:
TBitmapData=record
private
FPixelFormat:TPixelFormat;
public
Data:Pointer;
Pitch:Integer;
propertyPixelFormat:TPixelFormatreadFPixelFormat;
functionGetPixel(constX,Y:Integer):TAlphaColor;
procedureSetPixel(constX,Y:Integer;constAColor:TAlphaColor);
end;
Beforeyoucanaccessthepixels,youmustwith“MAP”askfortheaccesstothebitmap.Sowiththat,dependingonthetypeofaccess,thiswillbethandisabledforotherprocesses.Thiscontributestothethreadsafetyofbitmapediting.Ifyouhavefinishededitingthebitmap,youreleasetheaccesswith“UNMAP”.
TheaccesstypeisdeterminedbyTMapAccess,whichisdefinedasfollows:
TMapAccess=(maRead,maWrite,maReadWrite);
Forexample,tomanipulateaparticularpixelinabitmap,thatlooksinawholeasfollows(assumingmyBitmapisaglobalbitmapvariable):
procedureTF_Main.Image1MouseMove(Sender:TObject;Shift:TShiftState;X,
Y:Single);
var
vBitMapData:TBitmapData;
vPixelColor:TAlphaColor;
begin
ifMyBitmap.Map(TMapAccess.maWrite,vBitMapData)thenbegin
vBitmapData.SetPixel(Round(x),Round(y),TAlphaColors.Red);
MxBitmap.Unmap(vBitMapData);
end;
end;
Ifyoualsoneedareadingaccess,youmustuse“maReadWrite”.Ifyouneedonlyreadaccess,use“maRead”.
Theexampleissimple,andsoeasiertounderstand,butinyourownprojects,youshouldofcourseworkwithTRY…FINALLYconstructs,toensurethattheblockingofthebitmapwillbecanceledintheeventofafault.
3.ChangethealphachannelofaTBitmap
Ifyouwanttochangeonlythealphavalueofapixel,youneedtousealittletrick.UseheretheTAlphaColorRectogetdirectlyaccesstothebyte,whichisresponsibleforthevalueofthealphachannel.
Thisworksasfollows:
procedureTF_Main.SetAlpha(X,Y:Integer;AVal:Byte);
var
vBitMapData:TBitmapData;
vPixelColor:TAlphaColor;
begin
ifMyBitmap.Map(TMapAccess.maReadWrite,vBitMapData)thenbegin
vPixelColor:=vBitmapData.GetPixel(x,y);
TAlphaColorRec(vPixelColor).A:=AVal;
vBitmapData.SetPixel(x,y,vPixelColor);
MxBitmap.Unmap(vBitMapData);
end;
end;
4.Drawonthecanvasofabitmap
AsisknownfromtheWindowsbitmap,itisalsopossibletodrawonacanvasfortheFireMonkeybitmap.Youcanusethisas“Bitmap.canvas.fillrect”,“Bitmap.canvas.Fillelipse”etc.TheunderWindowswellknown“canvas.textout”functionisherenotavailable,butinsteaduse“canvas.Filltext”.
Finally,aspecialnote:Youmustrunbeforeallthedrawingactionsonthecanvas“Canvas.BeginScene”andaftercompletion“Canvas.EndScene”.
Sothiswouldlooklike,forexample,asfollows:
IfBitmap.canvas.beginscenethenbegin
try
Bitmap.canvas.Filltext(…);
finally
Bitmap.canvas.EndScene;
End;
End;
5.Turngraphics,flip,invertorcolortogray
AseriesofgraphicsprocessingfunctionsisalreadyavailableinFireMonkeybydefault.
Rotateaimageleftorright?Nothingeasierthanthat:
Bitmap.rotate(270);//Turnbitmapleft
Bitmap.Rotate(90);//Turnbitmapright
Agraphichorizontallyorverticallyreflect:
Bitmap.Fliphorizontal;//mirroringthepicturehorizontal
Bitmap.FlipVertical;//mirroringthepicturevertical
ToinvertanimageortocolorgrayoneusesoneofthemostextensiveinFireMonkeysuppliedfilterfunctions.
AmongtheFireMonkeydemosisafineexamplethatdemonstrateswhatfiltersareavailableinFireMonkey,youwillfinditintheexamplesfolderinthesubfolder“shaderfilter”.
Althoughthedemocandemonstratealot,unfortunately,itiswrittenso,thatmuchisgeneratedatruntimeordynamically.Ifwelookintothesourcecode,itisthanunfortunatelynotveryhelpful.
Ihavethereforecreatedasimplefunction,whichillustratestheuseoffilters.
Forexample,toinvertanexistingbitmap,itissufficienttocallthefunctionasfollows:
MyBitmap.Assign(ImgByFilter(MyBitmap,‘Invert’));
Andsothefunctionlooks(theunit“FMX.Filter”isrequired):
functionImgByFilter(bm:TBitmap;FilterName:string):TBitmap;
var
bmold:TBitmap;
Filter:FMX.Filter.TFilter;
begin
Filter:=TFilterManager.FilterByName(FilterName);
bmold:=TBitmap.Create(0,0);
bmOld.Assign(bm);
ifFilter<>nilthen
begin
//setinput
Filter.ValuesAsBitmap[‘Input’]:=bmOld;
//setTargetonlyfortransition
Filter.ValuesAsBitmap[‘Target’]:=bm;
//applyandgetintoresult
Result:=TBitmap(Filter.ValuesAsBitmap[‘output’]);
Filter.Free;
end;
bmOld.Free;
end;
Tocolorabitmaptogray,callthefilterfunctionasfollows:
Myitmap.Assign(ImgByFilter(MyBitmap,‘Monochrome’));
Ofcoursewiththisfunction,onlythefiltersareappliedthatworkwithoutadditionalsettings.
IntheFireMonkeydemomentionedabove,theuseoffilterswithsettingshasbeensolvedso,thatdynamicallyatruntimeafilterattributerecordwiththepossibleattributenamesandsettingvaluesofthefilterisfilled.ThisinformationisthenusedtodynamicallygenerateTTrackbarsthataregeneratedwiththeminimumandmaximumvaluesofthefilterattributes.ThisTTrackbarsalsobeassociatedwithaneventhandlertorespondtochangesinTTrackbarsandthenapplythefilteraccordingtothebitmaps.
Thisisalsothereasonwhythedemoisnotquitesoeasytounderstandwhenyoulookatthesourcecodeofthedemo.
Soyoucouldextendthe“ImgByFilter”function,byexampletointegratethefollowingsettings:
Filter.ValuesAsPoint
Filter.ValuesAsColor
Filter.ValuesAsFloat
Filter.ValuesAsTexture
Forexample,applyingthesepiafilterwithaspecificvalueintheabovefunction,youwould-beforecallingtheValuesAsBitmapwiththe“output”option-usethefollowinglinebefore:
Filter.ValuesAsFloat[‘Amount’]:=0.2;
Hereyouwouldgeta20%sepiacoloringofthebitmap.
Itshouldbenoted,however,thatnotallsettingsareusedwith“Amount”.Someuseforexample,“Levels”,“Length”,“Opacity”andthesettingsarenotalwaysbetween0and1,theycanhavealsootherminimumandmaximumvalues.
Forthatthementionedshaderfilterdemohelpsyou.Thereyoucansimplyclickonthefilternameandyouwillgettheindividualsettingsnameandthevaluerangeswillbeshown.Hereisanexampleofthe“sharpening”function:
Andhereisanexampleofthe“Emboss”function,whichusestwosettingattributes:
Overall,IfindthegraphicscapabilitiesofFireMonkeyquiteimpressive.
6.Drawingabitmapscaled
FromtheVCLyouknow,forexample,the“StretchDraw”function,whichallowsyoutodrawscaledgraphics.InFireMonkeyyouusethefunction“DrawBitmap”.IusethisforexamplealsoinmyAppStoreprogram“MultiScreenCopy”:
SojustassumedyouhaveabitmapintheTImagecomponent“Bild”insize1680x1050pixels.Itcanbescaledforexample,asshownhere,scaleddownto1024x640pixels(proportional).
ifShowModal=mrOKthenbegin
bm:=TBitmap.Create(StrToInt(ceNewWidth.text),StrToInt(ceNewHeight.text));
bm.Canvas.BeginScene;
bm.canvas.DrawBitmap(Bild.Bitmap,RectF(0,0,Bild.Bitmap.Width,Bild.Bitmap.Height),RectF(0,0,StrToInt(ceNewWidth.text),StrToInt(ceNewHeight.text)),1,False);
bm.Canvas.EndScene;
End;
Soyoufirstcreatethebitmapinthedesirednewsize,andthenpaintonthecanvaswith“DrawBitmap”.Asaparameteryouusetheoriginalbitmap,whosesizeandthenewdesiredoutputsize.Thetransparencyyouleaveto“1”forfullyvisible,theinterpolation-modeleavewith(“HighSpeed”=)“False”.
Chapter5:UsefulthirdpartycomponentsforFireMonkey
1.TMS-Components
SeveraltimesinthisbooktheTMScomponentshavealreadybeenmentioned.ThemostusefulTMSFMXBitmapcontainercomponenthasalreadybeenmentioned,becauseitispracticallya(better)replacementfortheTImageListfromtheVCLworld.ButalsoprovidestheTTMSFMXGridanumberofusefulfeatures,e.g.incombinationwiththepossibilitytoexportthecontentofthegridasExcel,RTForPDFfile(TTMSFMXGridExcelIO,TTMSFMXGridRTFIO,TTMSFMXGridPDFIO).
AlsobrieflymentionedwastheTTMSFMXRichEditor,whichisaworthyreplacementfortheVCLRichEditcomponent.WiththeTTMSFMXRichEditorFormatToolbarcomponentandtheTTMSFMXRichEditorEditToolbartworeadytouseToolBarcomponentsaredelivered,thatyoucanassociatewiththeeditor.
TheTMS-RichEditorcansave,ifnecessary,histextasRTForHTMLfile.
FortheRichEditorIhaveplacedashortvideoonYouTube,justhavealookhere:
https://www.youtube.com/watch?v=_BjlRX_CjX4
Thisisgoodnews:Asofversion2.9.0.0ofTMSPackforFireMonkey,onecanusetheRichEditorevenwithacompletespellcheck.Thedictionariesareincluded,interalia,inGerman,English,French,SpanishandItalian.
HereisthelinktotheTMSPackforFireMonkey:
http://www.tmssoftware.com/site/tmsfmxpack.asp
Finally,wemustmentiontheTMScloudPackforFireMonkey:WiththatyouwillgetaccessunderWindows,Mac,iOSandAndroidtothevariouscloudservices,whichareofferedforthoseplatforms.Soforinstance,DropBox,GoogleDrive,WindowsOneDriveBOXandservicesoniOS.
HereisthelinktotheTMSCloudPackforFireMonkey:
http://www.tmssoftware.com/site/tmsfmxcloudpack.asp
EvenifyouhaveorwanttousecertainnativecomponentsforMAC(oriOS),TMShelpsyouwiththemCLcomponents(oriCLoniOS).
Inacrosscompileproject,however,youhavewiththeuseofthiscomponentsalittlemoreeffort,becauseofcoursefortheWindowsdesktopthisnativeMACOScomponentswillnotwork.Butsometimesit’stheonlywaytorealizeaprojectforMACatall.
Forexample,todisplayPDFfilesinmyinvoiceprogramontheMacinaseparateform,IusetheTMSFMXNativeNSViewcomponentfromTMS(whileIuseunderWindowsawithaHydra-moduleintegratedcomponentofGnostice).
ThedirectgenerationofPDFinvoicefileI’lldoonMACwiththeTMSFMXNativeMacPDFLibcomponent.
HereisthelinktotheTMSmCLcomponents:
http://www.tmssoftware.com/site/tmsmcl.asp
However,TMShasmoreFireMonkeycomponentstooffer.AcompleteoverviewoftheTMSComponentsforFireMonkeycanbefoundhere:
http://www.tmssoftware.com/site/products.asp?t=fm
2.ReportgeneratorFastReportFMX
WithFastReportsFMX,youcancreatereportslikeyou’reusedtodoitundertheWindowsVCL.InDelphiXE7althoughastandardversionofFastReportsFMXisincluded,however,itcontainsnoredistributablereportdesigner.ThisisobtainedwhenoneacquiresthecommercialversionofFastReports.
HeretheFastReportdesigneratdesigntime:
Together,thesecomponentsprovidealargeamountofoptionsforcreatingreports(incl.PDFoutput).
Hereisthelink:
https://www.fast-report.com/en/product/fast-report-fmx
3.RemObjects-ApplicationFramework(Hydra)
Thesearenotcomponents,butaframeworkthatallowsyoutomixVCLandFMXcomponentsinaform.ForexampleyoucaninaVCLFormsdirectlyintegrateFMXcomponents(ortheotherwayaround).
ThisissurelyawaythatshouldnotbethefocusofyourworkwithFireMonkey,butitoffersadditionalwaysandopportunitiestotopickallpossibilitiesfromtheVCLandFMXworldsout.
Hereisthedirectlink:
http://www.hydra4.com/hydra/default.aspx
Here,too,IdepositedashortvideoinYouTube,whichyouarewelcomedtoview,ifwanted:
https://www.youtube.com/watch?v=0K8mEzDmlaM
4.Othercomponents
OthercomponentsmanufacturershaveannouncedsupportforFireMonkey(e.g.Gnostice)orwaitingtomakeadecisionforit(DevExpress,ImageEn,TRichView).ButtherearealsootherdevelopersandFMXsupporters,whohavedevelopedasetofcomponentsandprovidethempredominantlyfreeofchargeforyou.
HereIrecommendyoutotakealookoverathttp://www.fmxexpress.comandtoexaminewhetherthereissomethingforyourneedshere(wheremostcomponentsratherrefertoiOSorAndroiddevelopment).
IhopeIcanthismeagerlistheresoonextendalittlebit…
DoyouknowgoodFMXcomponentsthatIshouldmentionhere?Ifyouwant,sendmeyoursuggestionwithashorte-mailto([email protected]).
Chapter6:Howto-tips&tricksforFMX
Hereyouwillfindanumberofdifferentquestionsandanswers.Asatributetomybeloved“cookbook”-DelphiseriesbyWalterDoberenzandThomasKowaslkiIleantotheR-numberingfromthesebooks,whichstandsfor“recipes”(inGermanitmeansakindofwrittendescriptiontohandlesomething).
R1…Getthedisplayresolution?
Forthispurpose,weusedaplatformservice.
Example:
procedureTfrm_Main.FormCreate(Sender:TObject);
var
ScreenSvc:IFMXScreenService;
Size:TPointF;
begin
ifTPlatformServices.Current.SupportsPlatformService(IFMXScreenService,IInterface(ScreenSvc))
thenbegin
Size:=ScreenSvc.GetScreenSize;
end;
end;
Size.xgivethewidthandtheheightisinSize.y.
R2…CheckiftheEscape,CtrlorAltkeyispressed
Sometimesyouneedforanongoingprocessanoffertointerruptorcancelit.UnderWindows,intheVCLitworkslikethis:
if(Getkeystate(VK_CONTROL)<0)thenbegin//IstheShift-keypressed?
//yes—>
exit;
end;
AndsodoyoudoitunderFireMonkey:
{$IFDEFMACOS}
Uses
MacApi.AppKit,MacApi.Foundation,Macapi.CocoaTypes;
{$ENDIF}
functionIsControlKeyPressed:Boolean;
begin
Result:=NSControlKeyMaskandTNSEvent.OCClass.modifierFlags=
NSControlKeyMask;
end;
InaCross-platformwayyouhandleitbestlikethis(whichIunfortunatelystilldon’tknowisthesolutiontoquerytheESCkeyonMAC(doesanyoneknowthesolution?):
functionIsControlKeyPressed:Boolean;
begin
{$IFDEFMSWINDOWS}
Result:=GetKeyState(VK_CONTROL)<0;
{$ELSE}
Result:=NSControlKeyMaskandTNSEvent.OCClass.modifierFlags=NSControlKeyMask;
{$ENDIF}
end;
functionIsShiftKeyPressed:Boolean;
begin
{$IFDEFMSWINDOWS}
Result:=GetKeyState(VK_SHIFT)<0;
{$ELSE}
Result:=NSShiftKeyMaskandTNSEvent.OCClass.modifierFlags=NSShiftKeyMask;
{$ENDIF}
end;
functionIsESCKeyPressed:Boolean;
begin
{$IFDEFMSWINDOWS}
Result:=GetKeyState(VK_Escape)<0;
{$ELSE}
//Result:=NSEscapeKeyMaskandTNSEvent.OCClass.modifierFlags=//NSEscapeKeyMask;(soitdoesnotwork)
{$ENDIF}
end;
HereIshouldmention,thatIhavestoredthisfunctionsina“shared.plattform.pas”unit,whichiscurrentlyonlyavailableforWindowsandMAC.Butitwouldbebetter,onewouldmaketheIFDEF’swith“MSWINDOWS”and“MACOS”,thenitwouldbe,forexample,easiertoextendthefunctionslatertousewithLinux.
R3…UsefoldernamesunderWindowsandMACproperly
OnWindows,youusethe“\”charactertospecifydirectoriesandfilesinafilepath,e.g.,
“D:\Data\Forms\File.doc”.
UnderMAC,itisthe“/”characterwhichistobeused.Example:
“/Users/harrystahl/Documents/Datei.doc”.
Tobeshure,thatthatyoualwaysusetherightdelimiter,usethedefinedconstant“Pathdelim”.DependingonwhetheryoucompileonWindowsorMac,thecorrectversionisused.Thebelowexcerptfromtheunit“System.SysUtils”shows,thatalsotheconstants“DriveDelim”and“PathSep”areavailable:
const
PathDelim={$IFDEFMSWINDOWS}‘';{$ELSE}‘/’;{$ENDIF}
DriveDelim={$IFDEFMSWINDOWS}‘:’;{$ELSE}”;{$ENDIF}
PathSep={$IFDEFMSWINDOWS}‘;’;{$ELSE}‘:’;{$ENDIF}
Thus,therootdirectoryalwaysstartswith“/”undertheMAC.Ifyouwanttofindonthe
MACassociateddrives,youmustquerytheentriesofthefirst-leveldirectoryunder
“/Volumes”.
Thefollowingsourcecodereadstheexistingdrives(“volumes”)onmyMac(underWindowsitworkslikethis,ofcoursenot):
//UnitsSystem.IOUtilsundSystem.Typeswillbeneeded
procedureTForm9.FormCreate(Sender:TObject);
var
sdaDrives:TStringDynArray;
sDrive:string;
begin
sdaDrives:=TDirectory.GetDirectories(‘/Volumes’);
forsDriveinsdaDrivesdobegin
Listbox1.Items.Add(sDrive);
end;
end;
Herearetheresults(with“Add(copy(sDrives,10,255))youwouldgetonlythedrivename):
R4…Usesearchmaskfor“allfiles”inWindowsandMACproperly
IfyouwanttoviewinfilesearchesonWindows“allfiles”usethemask“*.*”.UnderMACitseemsalsotoworkwell.However,“*”istherightmask.The“*.*”woulde.g.notdisplayfiles,thatarewithoutafileextension.
Ithereforeusethefollowingsolutionwithaconstant,whichalwayshastherightcontentintheusedcontext:
{$IFDEFMSWINDOWS}
const
AllMask=‘*.*’;
{$ENDIF}
{$IFDEFMACOS}
const
AllMask=‘*’;
{$ENDIF}
R5…Avoidloopingsymlinkfolders(Alias)
OntheMac,youcancreatean“alias”forfoldersandfiles.Analiasfolderisultimatelyonlyareferencetothefolderthatisontheharddriveonanotherlocation.Ifyouareinafolderandallsubfolderssearchesforfilesinarecursivesearch,herecanpossiblyariseaclosedloopsituation.Ifanaliasinafolder“A”pointstoafolder“C”andthisfolderisfoundareferencebacktothefolder“A”,thesearchcontinuesendlessly.
Therefore,inarecursivefilesearchtheattribute“faSymlink”hastobefilteredout.
Hereisanexamplethatsearchesforallthe“.pas”filesinthefolder“D:\Delphi”andstorestheresultinaTStringList:
//Findsallfilesthatmatchthespecifiedcriteria
procedureFindThisFiles(pa:String;subDirs:Boolean;sl:TSTringList);
var
Search:TSearchRec;
begin
ifFindFirst(Pa,faAnyFile-faDirectory,Search)=0thenrepeat
sl.add(ExtractFilepath(pa)+Search.name);
untilFindNext(Search)<>0;
FindClose(Search);
ifSubDirsthenbegin
ifFindFirst(ExtractFilePath(pa)+‘*’,faDirectory-faSymLink,Search)=0
thenbegin
Repeat
if((search.attrandfaDirectory)=faDirectory)
and(search.name[1]<>‘.’)
thenbegin
FindThisFiles(ExtractFilePath(pa)+Search.Name+PathDelim+
ExtractFileName(pa),SubDirs,sl);
end;
untilFindNext(Search)<>0;
end;
FindClose(Search);
end;
end;
procedureTForm11.Button1Click(Sender:TObject);
var
sl:TSTringList;
begin
sl:=TStringList.Create;
FindThisfiles(‘D:\Delphi\*.pas’,true,sl);
end;
R6…Inwhichsituationsfilesymlinksfunctionsplayaroleotherwise
Ifyouwanttogettheattributesfromafileyouuse“TFile.GetAttributes”
Attributes:=TFile.GetAttributes(‘MyFileName’);
Butitmaybe,thatthefileisanalias.Bydefault,youwillreceivenottheattributesofthealiasfile,buttheattributesofthefiletowhichthealiasfilepoints(targetfile).
Youcanuse“TFile.GetAttributes”thereforebyaddinganotherparameter(which,ifnotspecified,isjusttrue)inordertoavoidthepointingtothetargetfile:
Attributes:=TFile.GetAttributes(‘MyFileName’,false);
Thefunctionisdeclaredin“System.ioutils”asfollows:
classfunctionGetAttributes(constPath:string;FollowLink:Boolean=true):TFileAttributes;inline;static;
TheFollowLinkparametersalsoexistinanumberofotherfilefunctionssuch“TFile.exists”,“TDirectory.Exists”,etc.
Whenusingfilefunctions,soyoushouldletdisplayalwaystheparametersthatyoucanusethere,becausepossiblythereareevenmorethanyouwouldexpect.
Uncheckedsymlinkpropertiescanleadtounwantedresultsnowandthen.Let’ssayyouwanttocopyafile.Ifitisanalias,sodonotcopytheperhapsonly30byteswidealiasfile,buttheTargetfilethatispossiblyseveralgigabytesinsize.Thiscaneverleadtosurpriseswhenyou,forexample,performabackupoffilesinadirectory.Youshouldthereforealwayscheckfilestosee,ifthereisanaliasfileandthenreacttoitasneeded.
Normallyitshouldbesufficienttoexaminetheattribute“faSymlink”inthefileattributes.However,I’vefoundundertheMACthathere(forwhateverreason)somefileshavetheattribute,althoughtherewereobviouslynoaliasfiles.
Justtobeonthesafeside,youcouldusethefollowingexampletocheckthis:
FunctionIsASymlinkfile(Filename:string):Boolean;
Var
SymlinkRec:TSymLinkRec;
attr:TFileAttributes;
begin
Result:=False;
TFile.GetAttributes(Filename,false);
ifTFileAttribute.faSymLinkinAttrthenbegin
TFile.GetSymLinkTarget(Filename,SymlinkRec);
ifSymlinkRec.TargetName<>”thenbegin
Result:=True;
end;
end;
end;
R7…Determinethecontrolunderthemouseposition
Ifyouwanttofindoutwhichcontroliscurrentlylocatedatthecurrentmouseposition,youcandosowiththe“ObjectAtPoint”.
Hereisanexample,togettheclassnameoftheobjectoverwhichthemousepointerisjust:procedureTForm9.Timer1Timer(Sender:TObject);
var
obj:IControl;
begin
obj:=ObjectAtPoint(Screen.MousePos);
ifobj<>NILthenbegin
Label2.Text:=TControl(obj).ClassName;
end;
end;
R8…findoutonwhichMACOSXoperatingsystemtheprogramisrunning
Intheunit“System.SysUtils”therecord“TOSVersion”isavailable,thatallowsyoutoquerytheoperatingsystembothunderWindowsandMACOSX.
With“TOSVersion.ToString”yougetseveralrelevantparameterssummarizedtogether.UnderWindows7,theexamplelookshereasfollows:
UnderMacOSX,thenso:
Ifyoulookatthestructureoftherecordonce,youseewhatfeaturesanddataareavailableintotal:
TOSVersion=record
publictype
TArchitecture=(arIntelX86,arIntelX64);
TPlatform=(pfWindows,pfMacOS);
private
classvarFArchitecture:TArchitecture;
classvarFBuild:Integer;
classvarFMajor:Integer;
classvarFMinor:Integer;
classvarFName:string;
classvarFPlatform:TPlatform;
classvarFServicePackMajor:Integer;
classvarFServicePackMinor:Integer;
classconstructorCreate;
public
classfunctionCheck(AMajor:Integer):Boolean;overload;static;inline;
classfunctionCheck(AMajor,AMinor:Integer):Boolean;overload;static;inline;
classfunctionCheck(AMajor,AMinor,AServicePackMajor:Integer):Boolean;overload;static;inline;
classfunctionToString:string;static;
classpropertyArchitecture:TArchitecturereadFArchitecture;
classpropertyBuild:IntegerreadFBuild;
classpropertyMajor:IntegerreadFMajor;
classpropertyMinor:IntegerreadFMinor;
classpropertyName:stringreadFName;
classpropertyPlatform:TPlatformreadFPlatform;
classpropertyServicePackMajor:IntegerreadFServicePackMajor;
classpropertyServicePackMinor:IntegerreadFServicePackMinor;
end;
R9…determinethecurrentusernameinMacOSX/Windows
Yousimplytakethe2ndentryfromthedirectory“GetHomepath”command:
{$IFDEFMACOS}
functionmac_GetComputerUserName:string;
begin
result:=GetfieldStr(PathDelim,GetHomePath,3);
end;
{$ENDIF}
IfyoudonothaveacomparableGetFieldStrfunction,youmakeitso:
{$IFDEFMACOS}
functionmac_GetComputerUserName:string;
var
sl:TStringList;
begin
sl:=TStringList.Create;
sl.Text:=StringReplace(GetHomePath,PathDelim,#13#10,[rfReplaceAll]);
result:=sl[2];
sl.Free;
end;
{$ENDIF}
Bearinmind,onWindowsitgoeslikethis(theWindowsunitisrequired):
{$IFDEFMSWINDOWS}
functionWin_GetComputerUserName:String;
var
P:PChar;
dw:dword;
ms:String;
begin
dw:=255;
P:=StrAlloc(256);
GetUserName(p,dw);
ifdw>0thenbegin
ms:=String(P);
end;
Result:=ms;
end;
{$ENDIF}
OnWindows,theresultlookslikethis:
AndthenontheMACas:
R10…Sendfilesasanattachmentofane-mailwiththesystemmailprogram
Afrequentlyusedfunctionisthesendingoffilesthatwascreatedinyourownprogram.Thesimplestsolutionistotransferthefilestothee-mailprogram,thatisusedbytheoperatingsystem.
OnWindows,youcanusetheMicrosoftMAPI.I’massumingthatyouprobablyalready
knowhowtodothatinWindowsusingtheMAPIinterface.Ifnot,youcandownloadfrommydevpagewebsitemyunit“uSendMail.pas”containingthehereused“SendFiles”function:
http://www.devpage.de/download/fmbook/uSendMail.pas
Thecross-platformsolutionforWindowsandMACisthenasfollows(usingtheexampleofaformwithalistbox,inwhichyouhaveselectedoneormorefiles,andthenclickabutton“sendmail”):
…Uses
{$IFDEFMACOS}
POSIX.Stdlib,
{$ENDIF}
{$IFDEFMACOS}
uSendMail.pas,
{$ENDIF}
…
procedureTf_Main.SendMailClick(Sender:TObject);
var
L:Integer;
app,s:String;
sl:TSTringList;
begin
sl:=TStringList.create;
{$IFDEFMSWINDOWS}
forL:=0tolbBilder.count-1dobegin
iflbBilder.listitems[L].isSelectedthenbegin
sl.add(lbBilder.Items[L]);
end;
end;
//E-mailthisherewiththeMAPIfiles
SendFiles(sl);
{$ENDIF}
{$IFDEFMACOS}
forL:=0tolbBilder.count-1dobegin
iflbBilder.listitems[L].isSelectedthenbegin
sl.add(‘”’+lbBilder.Items[L]+‘”’);
end;
end;
s:=StringReplace(Trim(sl.text),#10,”,[rfReplaceAll]);
app:=‘/Applications/Mail.app’;
_system(PAnsiChar(‘open-a’+AnsiString(app+‘‘+s)));
{$ENDIF}
sl.free;
end;
IntheMACOSXsolutionastringismadeofthefilestobesentthatholdsthefilenameinquotesandseparatedbyaspace.AsamailprogramheretheAppleMailprogramisused.
Theparameters‘open-a’simplymeansthatthefunction“_system”shouldstartanapplication,andthenpassoverthefilestobesentseparatedbyaspace.
Note:Ifyouwanttogivetheusertheoptiontouseanothermailprogram,youcouldofferappropriateoptionsinasettingdialog.HewouldthensimplyselectthemailprograminAppleApplicationsfolder,whichheused.
Insteadof
app:=‘/Applications/Mail.app’;
youwoulduse
app:=‘/Applications/UserMailprog.app’;
where“UserMailprog”theselectedusermailprogramwouldbe.Onerequirementwouldbe,ofcourse,thatthise-mailprogramwouldalsohavetosupportthetransferoffilesasparameters.
R11…providetheuserwithhelpfilesunderWin&MAC
OnWindows,youwilleitherhavebeentheMicrosoftHTMLHelpWorkshopusedtocreatea“.chm”helpfileoranotherprofessionalprogramwhichgeneratesthesefiles.
TheHelpWorkshopusedassourceHTMLfiles.OtherprofessionalprogramsworkeitherwithHTMLfilesorcanoutputthehelptext,atleastinsuchaformat.
Andthisisalsothesolution:useunderbothWindowsandMACOSXHTMLfilesthatallowyoutocallandviewhelpfromyourprogram.
Thefilesarethendisplayedinthebrowser.
InDelphi,youcanincludetherequiredHTMLfilesinyourapplicationbundlebyusingtheDeploymentfeature,thattransferthefilestotheMACOSfolder.
Hereyoucanseeanexampleofthe“index-en.htm”,whichcontainsthehelptextfortheEnglishlanguageversionandthe“index.htm”fortheGermanlanguageversion.FormoreextensiveprogramsyoucanalsocreatemultipleHTMLhelptextsandincludetheminyourprogram.
Inyourprogram,thencallasneededtheHTMLfiles(“AktLang”ishereaglobalvariable,managedbytheprogram.Itkeepstheinformationaboutthecurrentlyusedlanguage):
procedureTf_Main.mnu_ContentClick(Sender:TObject);
var
fn:string;
begin
ifAktLang=‘DE’thenbegin
fn:=IncludeTrailingPathDelimiter(AppPath)+‘index.htm’;
end;
ifAktLang=‘EN’thenbegin
fn:=IncludeTrailingPathDelimiter(AppPath)+‘index-en.htm’;
end;
pf_ShowHelp(fn);
end;
Wherebytheprocedure“pf_ShowHelp”isdefinedasfollows:
…
Uses
{$IFDEFMSWindows}
uses
Windows,
ShellApi,
Classes;
{$ENDIF}
{$IFDEFMACOS}
Uses
System.Classes,
POSIX.Stdlib;
{$ENDIF}
…
procedurepf_ShowHelp(HTMLFile:string);
begin
{$IFDEFMSWINDOWS}
ShellExecute(0,‘open’,Pchar(HTMLFile),nil,nil,0);
{$ELSE}
_system(PAnsiChar(‘open‘+AnsiString(HTMLFile)));
{$ENDIF}
end;
Bytheway,Irecommendthatyoudonotdirectlycallawindowsfunctionfromaunit,whereyourprogramlogicisin.Itisbettertoplacethisinextra-units,e.g.a“WinOnly.pas”anda“Shared.plattform.pas”.In“WinOnly.pas”youshipfeaturesthatonlyexistunderWindowsandin“Shared.plattform.pas”thefunctionsthatareavailableforseveralplatforms.
ThisapproachalsohastheadvantagethatyoudonothavetoconstantlyworkwithIFDEF’sinyourprogramlogic.Thenitislateralsomucheasiertoexpandyourprogramforanotherplatform(e.g.Linux).
R12…AfteruploadingtoAppStore:Invalidbinary-causesandremedies
Whathasjustcostmehalfanight:AftercreatingtheapplicationIhavemadeanentryiniTunesConnectandthenuploadedthebinary(i.e.thepackage).Thisinsofarworks,but2minuteslater,thefilehasbeenfeaturediniTunesConnectas“Invalidbinary”.
Thefirstmistakewas,thatIdidnotlookinmye-mails.TherewasnoexplanationsiniTunesConnect,butApplehadsentmeinstructionsviaemail.
Theexplanationinthemailwas:
FilesOnlyReadableByTheRootUser-Theinstallerpackageincludesfilesthatareonlyreadablebytherootuser.Thiswillpreventverificationoftheapplication’scodesignaturewhenyourappisrun.Ensurethatnon-rootuserscanreadthefilesinyourapp.
Howcouldthatbe?“Filesonlyreadablebytherootuser”?
Thiswasonmyseconderror(historically,thefirstmistake):
InbetweenIhadinstalledthesetuppackagefortestingonthedevelopmentsystem.ButstrangelytheinstallationwasexactlydonetotheplacewhereDelphistorestheapplicationprogram.BecausetheSetuppackagewasinstalledwithadminrights(itwasdonea
passwordprompt)theexistingfilesareoverwrittenwithcorrespondingattributes.
LaterIhadcompilemyapplicationagainwithDelphi,butviatransfertothemac,notallfileswereoverwrittenatthedestination,becauseIhadexceptsomefilesinthedeploymentlist.Sothat’ssomeofthefilesweremarkedsothatonlytheadminhasreadaccessontheapplicationbundle.
ThesolutionoftheproblemwasthentoshipthewholeapplicationbundletothetrashandletitcreateDelphiagainfromscratch.
Andtheneverythingworksagainasdesired.
R13…Applicationrejected:Somereasonsforrefusal,whichyoucanavoid
MissingCommand-QBefehl
TheuserinterfaceisnotconsistentwiththeOSXHumanInterfaceGuidelines
WehavefoundthatwhentheuserpressesCommand-Q,theappdoesnotquit.
SoInsertintothemenu,whichbearsthenameofyourprogram,evenacommandwiththetext“Quit[YourProgramname]”andassigntheshortcut“CommandQ”tothatcommand,youcanselectitattheObjectinspector.
DifferentnameoftheapplicationintheSystemMenuandintheProgramsfolder
TheappnametobedisplayedontheAppStoredoesnotsufficientlymatchthenameoftheappwheninstalledonMacOSX.
iTunesConnectName:MultiScreenShot.AppNamewhenInstalled:MScrShot.
SothenamethatappearsrightnexttotheAppleiconinthetoolbar,hastomatchwiththenameiniTunesConnectandintheApplicationsfolder.IdidindeedmanagetoletitdisplayinthemenubarasdesiredwithachangedentryinthepInfolist(bundlenameandbundledisplayname)name.ButthatithadnoeffectforthenameoftheApplicationsfolder.HereIwouldalsohavetochangetheexecutablename,butDelphiallowsnoprojectnamewithaspaceinit.SoIhadtochangetheprogramnamein“MultiScreenShot”,justwithnospaces.SonowistheexecutablefileandtheentryiniTunesConnect.
Thepurposesrequiredrights(entitlements)hasnofurtherexplanation
Inordertocontinuereviewingthisapp,werequireadditionalinformation.
Thisappusesoneormoreentitlementswhichdonotappeartohavematchingfunctionalitywithintheapp.PleasedescribehowandwheretheappusesthefollowingentitlementsbyaddingyourcommentstotheResolutionCenter.
IdemandedintheEntitlementsfile(fortheApplesandboxmodelrelevant)morerightsthanareactuallywasneeded.Forexample,Ihadrequestedadirectreadaccesstotheglobalimagesfolderofmyapplication.Actually,thisoptionwouldhavebeenenough:
Normally,youshouldthereforeonly“ReadandwriteaccesstofilesselectedwiththeOpenorSavedialog”tickhere.Ifmorerightsareneeded,youmustdescribeitinmoredetailduringtheupload.Anyway,withoutdescription,theapplicationisotherwiserejected.
Again,youselectthisrightsinDelphiundertheProjectmenu,Options,“EntitlementList”.
R14…UsingActiveControl
Ifyouhaveplacedinaformvariouscontrolsandmakeaqueryonthevariable“ActiveControl”,thisis-unlikeundertheVCL-alwaysNIL.Whetherthisisintentionalorabug,itisnotclear.Anyway,whiletheprogramisrunning,itwillsometimesbeusefulltoknow,whatisjusttheactivecontrol(i.e.theonethathasthefocus).
Youcandirectlyusethepropertyoftheform“Focused”here.Thisisnamelythecontrol,whichhasthefocus.
SoifyouwantsettheActiveControlvariable,youcandosointheevent“OnFocusChanged”oftheform:
procedureTForm9.FormFocusChanged(Sender:TObject);
begin
ActiveControl:=TControl(Focused.GetObject);
//ifActiveControl<>NILthenbegin
//Label1.Text:=‘AktivesControl:‘+ActiveControl.ClassName+‘(‘+ActiveControl.Name+‘)’;
//end;
end;
Youcanactivateherethedisabledlinesintheabovesource-codeexampleandseewhatwillbeshownwhenyouchangethefocustoanindividualcontrol(and,forexampleinagridwithF2togetintoeditmode).
R15…ReplaceOnDrawItemeventoftheListBoxfromVCLwiththeOnPaintingeventoftheTListBoxItems
IndeedtheListBoxhasno“OnDrawItem”eventastheVCLlistbox,buttheListBoxItemhasan“OnPaint”or“OnPainting”event.
Hereyoucan,justlikeintheoldVCL-way,dodrawingsasyouwant.Thatis,forexample,usefulifyouhavelotsofdatastoredininternalobjectsthathavebeenconnectedtotheListBoxItems(orotherwiseholdssomewhereinalistordatabase).ForthebelowdemoIhavekeptitsimpleanddemonstrateitwithnoassociateddataobjects.TheListBoxItemsdoescontainnotext(andtheyshouldnot,becauseitwillbedrawedbydefaultbyFireMonkey).ThetextIgetherefromthenameoftheListBoxItems.Soonlythedrawingprocessisdemonstratedhere:
procedureTForm22.ListBoxItem1Painting(Sender:TObject;Canvas:TCanvas;
constARect:TRectF);
varFlags:TFillTextFlags;beginWithTListBoxItem(sender)dobegincanvas.BeginScene;
canvas.Fill.Kind:=TBrushKind.bkSolid;
Flags:=[TFillTextFlag.ftRightToLeft];
ifName=‘ListBoxItem3’thenbeginifListBox1.ListItems[ListBox1.ItemIndex]<>TListBoxItem(sender)thenbeginCanvas.Clearrect(Arect,TAlphaColorRec.Yellow);end;end;
ifName=‘ListBoxItem2’thenCanvas.Fill.Color:=TAlphaColorRec.redelseCanvas.Fill.Color:=TAlphaColorRec.black;
Canvas.FillText(ARect,name,true,1,flags,TTextAlign.taTrailing,TTextAlign.taCenter);
canvas.EndScene;end;end;
Noteagain:Thetextproperty“Text”oftheTListboxItemshavenocontentitself,ofcourse,becausethiswouldresultinduplicatedrawings(textoverlays).Youcanuse“Tagstring”insteadofthe“Text”propertyofaListboxItem,ifyouwanttokeepdatainaListbox-Item(butbestwouldbe,youuseitonlyfordrawing).
Sothatthenitlooksintheresult:
R16…LoadBitmapfromresourcefile(forretinadisplay)
SinceDelphiXE5youcaneasilyaddresourcestoyourprogramthatcanlaterbeloadedintoacomponent.Todistinguish:herewetalkaboutaprogramresourceandnotabouttheMultiResBitmapintowhichyoucanloadmultiplebitmapsatdesigntime(alsowithdifferentresolutions-insofarthistiphereisanalternativesolution).
WhenyouruntheprogramonMACOSX,itmaybeso,thattheuserusesascreenwithtwicetheresolution,theso-called.Retinadisplay.Ifitisimportantforyourprogramthatyouareusingcertainbitmapsthatyoucanalsodisplaywithtwicetheresolution,youcancreateabitmapwithanormalresolution,andsaveonewithtwicetheresolutionintheprogramresource.
Atruntime,examinethepresentresolution,andthenloadtheappropriatebitmapinyourimagecomponent.
First,howdoyougetthebitmapsintheprogramresource?Hereyoucanuseunderthe“Projects”menuthecommand“ResourcesandImages”.
Addanormalsizedimageandyouprovidethefilenameattheendwitha“1”.Imageswithtwicetheresolutionyouwillprovidewitha“2”attheend.Renamealsotheidentifierto“Dia1”.Warning:Upperandlowercaseisnecessary!Soifyouenterhere“Dia1”anduseinsourcecode“dia1”,theresourcewillnotbefound.
Itisalsoimportantthatyouchangetheresourcetypeof“BITMAP”to“RCDATA”,otherwiseitwillnotwork.
Atruntim,forexample,intheOnCreateevent,youcandoitlikethis:
//UnitFMX.Platformisrequired
procedureTForm1.FormCreate(Sender:TObject);
var
RS:TResourceStream;
ScreenSrv:IFMXScreenService;
scale:single;
begin
ifTPlatformServices.Current.SupportsPlatformService(IFMXScreenService,IInterface(ScreenSrv))then
Scale:=ScreenSrv.GetScreenScale
else
Scale:=1.0;
ifScale<2.0thenbegin
RS:=TResourceStream.Create(HInstance,‘dia1’,RT_RCDATA);
Image1.MultiResBitmap.LoadItemFromStream(RS,1.0);
endelsebegin
RS:=TResourceStream.Create(HInstance,‘dia2’,RT_RCDATA);
Image1.MultiResBitmap.LoadItemFromStream(RS,2.0);
end;
FreeAndNil(RS);
end;
SocheckfirstwiththeScreenService,whichscreenresolutionispresent.“1.0”wouldbenormal,everythingelsehasahigherresolution.Thenyoucreatearesourcestream,loadthebitmapintoitandthenloadthebitmapitfromthestreamintotheimagecomponent.Thedetourviatheresourcestreamisunfortunatelynecessarybecausetheimagecomponentcan’tloaddirectlytheimagefromaprogramresource.
R17…Swapitemsinalistbox
FromtheVCLyouknowthefunction
ListBox1.Items.Exchange();
Toswaptwoitems,forexample,youcouldusethefollowingsourcecode(assumedinthelistboxwouldbe10entries):
procedureTForm38.Button1Click(Sender:TObject);
begin
Listbox1.Items.BeginUpdate;
Listbox1.Items.Exchange(2,1);
Listbox1.Items.EndUpdate;
end;
UnderFireMonkeyyouhavetodothis:
procedureTForm4.btExchangeClick(Sender:TObject);begin
lb.ItemsExchange(lb.ListItems[2],lb.ListItems[1]);end;
UnlikeundertheVCL,soyoumustleavetheBeginUpdateResourceandEndUpdate.
Explanationofthis:Internallythefunction“Items.Exchange”usedbyitselfa“Listbox.BeginUpdate”anda“Listbox.Endupdate”.Ifyouuseyourself“BeginUpdate”before,theinternalroutineassumesthatthelistboxisbeingupdatedanddoesnotperformthechange.
Unfortunately,thisisnotdocumentedanywhere,butifyouevenknowOK.
R18…SwapitemsinaListboxviaDrag&Drop
Thelistboxproperty“Allowdrag”mustsetto“True”.Inthe“OnDragDrop”eventyoumustrespondtothedrop:
procedureTForm1.ListBox1DragDrop(Sender:TObject;constData:TDragObject;constPoint:TPointF);varLI:TListBoxItem;beginifData.SourceisTListboxItemthenbeginLI:=ListBox1.ItemByPoint(Point.X,Point.Y);Listbox1.ItemsExchange(LI,TListboxItem(Data.Source));end;end;
R19…UsingFMXfunctionsinaVCLapplicationviaDLL
ConvertinganexistingVCLapplicationtoaFireMonkeyapplicationcanbedoneinaradicalapproach.Soconverteverythinginonego.Thedisadvantageis,thatthiscantakealongtimeonalargerVCLprojectandinthemeantimeyoucan’tchangemuchinthecurrentapplication.
Thealternativecouldbeasmoothtransition.Byexample,outsourcestepbystepdialoguesandrelatedfunctionsintoaFireMonkeyDLL.Hereyoucanalsousetheextendedcapabilities(graphics,GPS-functions,etc.),sothatthecurrentapplicationcandirectlybenefitfromit.
PerhapsyoumayingeneralnotwanttoconverttheprojecttoFireMonkey,butforcertainfunctionalitiesyouwouldliketouseFireMonkey.Inbothvariants,itmakessensetoprovidethisfunctionacrossaFireMonkeyDLL.Thisisnotthatdifficult,itworksmuchlikeundertheVCL.
HereIshowyouanexampleofhowIhaveaddedtotheVCL-imageeditingprogram“PixPowerPhoto&Draw”anewfiltereffectoveraFireMonkeyDLL.AlthoughtheDLLhasgeneratedasizeofabout4MB,itaffectsmyinstallationpackageonlywithavalueof1.3MB.
SointheVCLapplicationIhaveabitmapthatIsaveasabitmapstreamandpassittotheFMX-DLL.ThebitmapIcan’tdirectlypassedas“TBitmap”totheDLLunfortunately,becauseVCLandFireMonkeybitmapsareincompatiblewitheachother.
IntheDLL,thebitmapisdisplayedinadialoginthe”ImageViewer”component,whichI’veaddeda“PaperSketch”effect.Theintensityisadjustedviaatrackable.
Iftheuserthenconfirmedtheresultwith“OK”,theeffectisactuallyappliedtothebitmapandwritethebitmapbackintothestream.Here,alittletrickisused,becausebydefaultFireMonkeywritesabitmapasaPNGstream.Therefore,aseparateclass“TMyBitmap”derivedfromTBitmapisusedandoverwritethesavestreamprocedureandadaptedsothatthestreamcanbesavedasbitmapstream.
That’sthewayhowtodoit:Createonthemenu“File”,command“New”aDynamic-linklibrary:
libraryFMXFilters;
uses
FMX.Forms,
System.SysUtils,
System.Classes,
FrmFilterin‘FrmFilter.pas’{F_Filters};
{R*.res}
exports
ShowBitmapFromStream;
begin
end.
Ifyouhavecreatedthelibrary,theelementsmarkedhereinboldarenotavailableyet.
YouhavetoaddmanuallytheUnitFMX.Forms,sothatitisclearthatthereshouldbeaFireMonkeyDLL.DependingonwhetheryouarecreatingthelibraryinanalreadyopenVCLprojectorseparately,itmaybethatDelphiindicatingthattheDLLhereishandledasFireMonkeyprojectandthereforeanappropriatemarkingshouldbemade.Thisqueryyoucanpositivelyconfirm.
TheUnitFrmFilterisaformunitthatIhavecreatedunder“File”,“New”,“FireMonkeyForm”:
Note:ThiscommandisonlydisplayedwhenyouviewtheDLLprojectintheProject
Explorer:
Theformlookslikethis:
Inthestructureviewitlookslikethis:
InthesourcecodeIhavedefinedthefollowingfunctionbeforethe“Implementation”:
FunctionShowBitmapFromStream(ms:TMemoryStream):Boolean;export;
Thisisthefunctionthatisprovidedasexternallycallablefunctionavailablethroughthe“exports”statementinthelibraryfile.
Note1:IfyouwanttopassastringinsteadabitmaptotheDLL,youshoulduseeitheraShortString,PCharorWideString.Soyoudon’tneedtotaketheShareMemunitintotheunitusesandyoudon’tneedtodelivertheBORLNDMM.DLLwithyourapplication.
Note2:IfyouwanttoensurethatthegeneratedDLLcanbecallednotonlyfromDelphiprograms,butalsoprogramsthatwerecreatedbyotherdevelopmentenvironments,youshouldusean“IStream”ratherthanamemorystream.
Hereistheimplementationofthisfunctionintheformfile(underusestheunitsFMX.Filter,FMX.Effects,FMX.Filter.EffectsandFMX.Surfacesarerequired):
functionShowBitmapFromStream(ms:TMemoryStream):Boolean;
var
Filter:FMX.Filter.TFilter;
begin
Filter:=TFilterManager.FilterByName(‘PaperSketch’);
try
F_Filters:=TF_Filters.Create(Application);
F_Filters.ImageViewer1.bitmap.LoadFromStream(ms);
ifF_Filters.ShowModal=mrOKthenbegin
Filter.ValuesAsBitmap[‘Input’]:=F_Filters.ImageViewer1.bitmap;
Filter.ValuesAsFloat[‘BrushSize’]:=F_Filters.TrackBar1.Value;
F_Filters.ImageViewer1.bitmap:=TBitmap(Filter.ValuesAsBitmap[‘Output’]);
TMyBitmap(F_Filters.ImageViewer1.bitmap).SaveToStream(ms);
Result:=True;
endelsebegin
Result:=False;
end;
finally
F_Filters.Free;
end;
end;
Herearetherequiredadjustmenttostorethebitmapstreams:
Type
TMyBitmap=class(TBitmap)
procedureSaveToStream(Stream:TStream);
end;
Implementation
procedureTMyBitmap.SaveToStream(Stream:TStream);
var
Surf:TBitmapSurface;
begin
Surf:=TBitmapSurface.Create;
try
Surf.Assign(Self);
TBitmapCodecManager.SaveToStream(Stream,Surf,‘.bmp’);
finally
Surf.Free;
end;
end;
WiththeFilterManagerandthenameofthefilterfunctionthewantedeffect“PaperSketch”isselected.
ThentheFMXdialogiscreated,thebitmapstreamisloadedintothebitmapoftheImageViewer.
Iftheuserhassetthedesiredintensityoftheeffectwiththetrackbarandthanheconfirmsthiswith“OK”,thesettingofthetrackbar-valuewillbeapplied.
Thenthemodifiedbitmapwiththederivedclassisstoredas“BMP’bitmapstream(i.e.,abitmapinRGBformat).
InthefollowingVCLapplicationunitisnowadded:unituFMXLink;
interface
uses
Windows,Dialogs,Classes;
type
TShowBitmapFromStream=function(ms:TMemoryStream):Boolean;
var
ShowBitmapFromStream:TShowBitmapFromStream=nil;
DllHandle:THandle;
implementation
initialization
ifDllHandle=0thenbegin
DllHandle:=LoadLibrary(‘FMXFilters.dll’);
ifDllHandle>0thenbegin
@ShowBitmapFromStream:=GetProcAddress(DllHandle,‘ShowBitmapFromStream’);
endelsebegin
MessageDlg(‘Thefunction„ShowBitmapFromStream“/theDLL-file„FMFilters.dll“istnotavailable’,mtInformation,[mbOK],0);
end;
end;
finalization
ifDLLHandle<>0then
FreeLibrary(DLLHandle);
end.
Under“Type”,afunctionisdefinedwhichcorrespondstotheexportfunctionoftheDLL.
UnderVaris“ShowBitMapFromStream”thenintroducedasprocedurevariable.
Intheinitializationsection,theDLLisloadedandourpreviouslydeclaredprocedurewillthenbeassignedtothememory-adressofthefunctionintheDLL.
IfyouincludethisVCL-unitinyourVCLapplicationyoucanthencallfromtherethefunction“ShowBitmapFromStream”.
So,forexample:Var
MemStream:TMemorySteam;
TempFileName:String;
Begin
TempFileName:=…//imposetemporaryfilename
ABitmap.saveToStream(MemStream);//SavethebitmapasaMemoryStream
MemStream.position:=0;
IfShowBitmapFromStream(MemStream)thenbegin
MemStream.Position:=0;
ABitmap.LoadFromStream(ms)//DateiwiederinBitmapladen
end;
End;
NOTE:TogetthistoworkyouhavetoincludeinthemainformofyourVCLapplicationtheunit“Winapi.GDIPOBJ”directlyintotheUSESsectionoftheinterface-section(notinausesclauseintheimplementationsection,thatwouldbenotenough).
ThisunitisrequiredsothattheGDIfunctioncanbeinitializedalsofortheVCLapplication.Thiscanbedoneonlythroughthemainprogram,itnotbelongsintotheFireMonkeyunit.
Ifyouareinterested,youcanlookatthefunctionalityintheprogramoncehere(www.Pixpower.info)orsimplyinaYouTubevideoinmyPixPowerchannel,whereIhavethisfilter:http://youtu.be/W21uxyPsJvc.
R20…DrawtextinTGridright,orcentered
SinceXE6,theTGridcontainsthepossibility,under“TextSettings”choosewith“HorzAlign”ifthetextshownisintendedtobeleft-justified,centeredorright-justified.
However,thissettingappliestoallcolumnsthataredisplayingtext.TheTColumsorTStringColumsyouhaveplacedintotheTGridhavenopropertyliketheTGrid.Butthatcouldbenecessary,ifthetextinonecolumnsouldbedisplayedjustifiedontheleftandinanothercolumntotheright(e.g.monetaryamounts).
HereitisusefulinitiallytosettheTag-valueoftheTStringColumnsthatshouldbealignedtotherightwiththevalueof“1”.Forallcolumnsthathavethevalue“1”,thenwedeliverinGrid.GetValueanemptyvaluebacksothatthegriditselfdoesnotcarryoutadrawingactionhere:
procedureTForm9.Grid1GetValue(Sender:TObject;constCol,Row:Integer;
varValue:TValue);
begin
ifTGrid(sender).ColumnByIndex(col).Tag=1thenbegin
exit;
end;
….
end;
IntheeventoftheOnDrawColumnCelloftheTGridwecallforallcolumnstheDrawCellRightthefunction:
procedureTForm9.Grid1DrawColumnCell(Sender:TObject;constCanvas:TCanvas;
constColumn:TColumn;constBounds:TRectF;constRow:Integer;
constValue:TValue;constState:TGridDrawStates);
begin
DrawCellRight(Sender,Column,canvas,bounds,Row,Value);
end;
Here,theuser-definedfunction“DrawCellRight”iscalled.WealsousetheOnGetValueeventoftheTGridtogetthevalueofthecell.TemporarywesettheTagvalueoftheTColumnto“0”sothatthevalueisreturnedtous(remember:WehaveaboveasupplementinstalledsothatthevalueisonlyreturnediftheTagvalueofthecolumnhasthevalue“0”.
Itwilllooklikethis:
procedureDrawCellRight(Sender:TObject;Column:TColumn;canvas:TCanvas;bounds:TRectF;Row:Integer;Value:TValue);
var
B:TRectF;
V:TValue;
begin
ifColumn.Tag=1thenbegin
Column.Tag:=0;
V:=Value;
TGrid(Sender).OnGetValue(Sender,column.Index,Row,V);
B:=Bounds;
B.Right:=B.Right-1;
DrawTextEx(canvas,B,TAlphaColorRec.black,V.tostring,TTextAlign.Trailing);
Column.Tag:=1;
end;
end;
Andhereisthehelpfunction,whichthendrawsforusthetext:
procedureDrawTextEx(Z:TCanvas;aRec:TRectF;ATextColor:TAlphaColor;S:String;a:TTextalign);
var
r:TRectF;
tf:tfilltextflags;
h:TTextalign;
begin
h:=TTextAlign.taCenter;
Z.Fill.Color:=ATextColor;
Z.BeginScene;
Z.FillText(arec,S,false,1,tf,a,h);
Z.EndScene;
end;
Theresultisthatallcolumnswhosetag-valueis“0”,thedrawingstartsatheleftmargin(donebytheTGriditself),allwhosetag-valueis“1”,thetextisrightjustified(donebyourdrawingfunction):
R21…DrawtextinTStringGridright,orcentered
AlsoinTStringGridyoucansetthetextoutputforthecolumnsonlyforallthesame.Soalsoherewehavetodrawthetextbyourselfinthe“OnDrawColumnCell”-event.Thissolutionassumesthatthestringgriddoesnotholdthedata,itonlydisplaytherelevantcontent(asitshouldbe).So,leavee.g.theleft-alignedtextoutputasdefaultandsettheTagvalueofthecolumnthatyouwanttoberight-justifiedto“1”andtobeoutputcenteredto“2”.Sothenitlooksinthedraw-event:procedureTForm9.StringGrid1DrawColumnCell(Sender:TObject;
constCanvas:TCanvas;constColumn:TColumn;constBounds:TRectF;
constRow:Integer;constValue:TValue;constState:TGridDrawStates);
var
Flags:TFillTextFlags;
ar:TRectF;
S:string;
begin
Flags:=[TFillTextFlag.ftRightToLeft];
canvas.BeginScene;
ar:=bounds;
ar.Inflate(-1,-1);
canvas.ClearRect(ar);
canvas.Fill.Color:=TAlphaColorrec.Black;
S:=Row.ToString;
casecolumn.Tagof
0:canvas.FillText(bounds,S,True,1,flags,TTextAlign.taTrailing,TTextAlign.Center);
1:canvas.FillText(bounds,S,True,1,flags,TTextAlign.taLeading,TTextAlign.Center);
2:canvas.FillText(bounds,S,True,1,flags,TTextAlign.taCenter,TTextAlign.Center);
end;
canvas.EndScene;
end;
Heretheresult:
R22…Dealingwiththe“visible”propertyofcontrols
NewsinceXE7:
SinceXE7thereisnolongertheproperty“DesignVisible”thatwasbeforebesidetheproperty“Visible”.Whenyouputacontrolatdesigntimeto“Visible=False”,itisalsonolongervisibleatdesigntime.Thatmakesitalittledifficulttochoosethecontrole.g.forsettingstomakeintheObjectinspector.Useinthiscasethetreeviewwhereyoucaneasilyselectandactivatetheinvisiblecontrol.Soifyouupgradeaprojectfromaprevious
versionofDelphi,andacontrolnolongerappearstobeavailable,checkifyoucanfinditinthestructureviewandifthe“Visible”propertyjuststoodto“False”.
R23…PreventunintendedshorteningofTLabeltext
SinceXE6isavailable,under“TextSettings,isfor“Trimming”thedefaultsetting“character”todisplaythelabeltext.AutoSizeisturnedoff.Thiscansometimesleadto,thatthetextisshortenedatruntimewith“…”whenthedisplaywidthisnotwideenough.Youshouldeitherset“AutoSizeto”Trueorturnoff“AutoSize”andthetrimmingandleavemorespaceforthedisplayofthetextinadvance.Thisisalsotobeconsideredbecause,forexample,underWindowsthedisplaywidthissufficientbutnotunderMACOS,becausethefontsjustyetsometimesareslightlydifferent.Itisthereforeadvisableunderallplatformstolookatthedialogsatruntime.
CurrentlyIsuggestrathertoturnoffAutoSize,becauseIhavefoundthatrelativelymanytimesthetextisnotdisplayedcorrectlyinthehight(e.g.“g”and“p”hasmissingpartsaboveandbelow),inparticularonMACOSX.YoushouldthenalsotheheightoftheLabelcomponentslightlyenlarge.
R24…UsehintsinFireMonkey:Howitgoes
IfyouarelookingintheObjectinspectorfortheproperty“Hint”forTButton,TSpeedButtonorTLabel,youwillnotfindanything.
Inreality,allthecomponentsmentionedhere,hastheHintproperty(alsoforothers,forexampleTListBox),butitisnotpublished.Ifyoucouldsetthepropertyatruntime(whichusuallyisnotpossible)thehintfeaturewouldalsonotbesupported.
Ihavethereforedevelopedaunit(HS_FMXHints.pas)thatyouneedtoincludeonlytotheforminwhichyouwanttousehintsinthecontrolsmentionedabove.Theunitusesaknown“hacktrick”.Thehintpropertyismadeavailableanditcanbesetatruntime.Inaddition,withthe“SetAHint”functionan“OnMouseEnter”andan“OnMouseLeave”eventisthenaddedtothecontrols.
Atrun-time,e.g.intheOnCreateeventoftheform,thensetthehintsasfollows:SetAHint(Button1‘ShowHelp’);
SetAHint(Label1‘Sumoverallquarters’);
Attention:Ifyouusealabel,youhavebyyourselfsetintheObjectinspectortheproperty“HitTest”totrue,otherwisetheOnMouseEntereventisnotpassed(setatruntimecuriouslyhasnoeffect).
Ifyourunyourprogramandmovethemousepointeroveracontrol,thenacorrespondinghintisdisplayed.Itlookse.g.likethis:
Dependingonwhetherthehintappearsattheleftorrightedge,theuppertriangleisdisplayedjusttotheleftorright.Ifatextisincluded,whichismorethan200pixelswide,thehintisdisplayedonseverallines.IftheHintdisplayedatthebottom,itmayappearoverthecontrolbecauseitcouldnolongerbedisplayedintheformotherwise.
Youcanalsomakeaninitialization,overwritingthedefaultpredefinedvalues:SetHintSetting(TimeBeforeShow,TimeToShow:Integer;DynamicShow:Boolean;TC:TAlphaColor)
Thevaluefor“TimeBeforeShow”indicateswhenthemousepointerisoverthecontrolis,howlongtowaituntilthehintwillbedisplayed.
Thevalue“TimeToShow”indicateshowlongthehintwillbedisplayed.
Ifyouset“DynamicShow”toTrue,thedisplaytimeisextendedbyacalculatedvalueforlongertext,averyusefuloption.With“TC”youcanspecifyadifferenthintcolor.
Restrictions:
Becausethecallof”SetAHint”overridesanyexistingownOnMouseEnterorOnMouseLeaveevents(whichyou’vesetupthemselvesinyourprogram),youcan’tusethisworkaroundinthiscase.However,ifrequiredyoucanexpandtheunitbycheckingwhensettingthehintstringifthecontrolhasaneventfunctionalreadyassigned,andmakethensomeaddaptions.
IhopethatmaybefromDelphiXE8on,thehintfeatureissupporteddirectly,thenwedonotneedsuchworkarounds.
Theunitcanbedownloadedhere:
http://www.devpage.de/download/HS_FMXHints.zip
Foryou,thereaderofthisbook,isexclusiveasmalltoolavailable,whichIhadwrittenformetoreadthehintsfromtheoldVCLformsandtomakethemsoveryquicklyforconvertedFMXprogramavailable.JustopenwiththeprogramsimplytheVCLform(button“SelectVCLForm”)andyougetinthememoallhintslisted,readytouseforthefunction“SetAHint”.Youcantheneasilycopythistexte.g.intotheOnCreateeventoftheFMXformandhavethanallthehintsthereagain.
Theprogram,whichyoumayusefreelyforyourneeds,youuseatyourownrisk(disclosuretothirdpartiesisthereforenotallowed).Youcandownloadithere:
http://www.devpage.de/download/GetHints.zip
Extractthecontained“.exe”andcopyitsomewhere,fromwhereyouwanttousetheprogram.
Ashortvideo,howtousethehint-unitandthetool,youcanviewonYouTube:
http://youtu.be/eWGbhOexrJk
R25…Determinethedocumentdirectory
Forallplatforms,youcanusefromtheunit“System.IOUtils”thatyouneedtoincludeinyourproject,therecord“TPath”,whichcontainsalotofinformationaboutdirectoriesthatcanbeaccessedwiththecorrespondingfunctions:
procedureTForm2.FormCreate(Sender:TObject);
var
DocPath:String;
begin
DocPath:=TPath.GetDocumentsPath;
end;
ResultforDocPathhereonmyMacorWindowscomputer:
underMACOSX:„/Users/harrystahl/Documents“
underWindows:„C:\Users\Harry-Dev\Documents“
Tip:ClickoncebyholdingdowntheCtrlkeyon“TPath”,DelphiwillthenshowyoutheTPathrecord.Exploretheindividualvariablesandfunctionsthatareavailablethere(inthepublicsector).Thatitisreallyworthit,someofityoucanuseforshureagainlater.Forexample,youcanalsousetheimagesdirectory(TPath.GetPicturesPath)orthevideodirectory(TPath.GetMoviesPath).
R26…Improvethefontquality(especiallyonWindows)
UnderWindows,thedisplayqualityofthefontsunderFireMonkeyisnotoptimal.Imanageitso,thatIturnofftheDirect2Dfunctionalityintheprojectfile,beforetheinitialization:beginFMX.Types.GlobalUseDirect2D:=False;Application.Initialize;Application.CreateForm(TF_Main,F_Main);Application.Run;end.
Hint:TheFMX.Typestheunitmustbeincluded.
R27…Selectafolderwithadialog
With“SelectDirectory”youcannowselectafolder:procedureTForm2.Button1Click(Sender:TObject);
var
dir,root:string;
begin
root:=”;
//root:=System.IOUtils.TPath.GetPicturesPath;
ifSelectDirectory(‘BittewählenSieeinVerzeichnis’,root,dir)thenbegin
ShowMessage(‘Siehaben‘+dir+‘gewählt’);
end;
end;
TheUnitFMX.Dialogsmustbeintegrated.Thefirstparameterisusedtospecifyadescriptionthatisdisplayedinthetitlebarofthedialog.Inthesecondparameter,youcanspecifyadirectorytowhichthedialogisdisplayed(defaultisthedocumentdirectory),thethirdisaVARparameterandreturnesbacktheselecteddirectoryname.
Note:Ifyouwanttousetheselectiondialogalongwithabookmarkfunctioninasandboxedapplication,youmustusethereplacementfunctionshowninAnnex2.
R28…GetaccesstoacellcontrolofTGrids
WhilewecansetatruntimeinaTStringGridwith
Stringrid1.cells[0,1]:=‘Test’;
thecellswithvaluesorcanretrievevaluesfromthecells,thatgoesunfortunatelynotwiththeTGrid,evenifitcontainsaTStringColumn.TheTGrid,ortheTColumscontainonly“StyledControls”,whichcanthentaketheappropriatevalues.
Beforehereawayisshownhowonestillcomestothevalueandcanchangeit(ifnecessary),thefollowingexplanation:FireMonkeywasalwaysevolvingwitheachversion,intermsofthespeedofthedisplay.
WithTGriditisso,thatactuallyneededdisplayelements(cells=StyledControls)aregeneratedatruntime.So,forexample,whenagridhas1000rowsandtherearedisplayedonlythefirst20rows,thenonlythefirst20rowsandcellsaregeneratedatruntime.Therewillthanalsobeonlyforthefirst20rows“GetValue”queries.
Thisofcourseismemory-friendlyandgoodforspeed.Butyoumusttakenotethatyoucanthereforegiveonlysetorretrievevaluesforthegridinthecellswhenthecellshavebeengeneratedforitalready.Youcanassumethatacellallreadyexists,ifyouwanttomanipulatethevalueofacellisshown.
Wewillsubsequentlyuseaclasshelperfunctiontogetaccesstotheprivatefunction“CellControlByRow”.ThisfunctionisdefinedasfollowsinFMX.Grid:
functionTColumn.CellControlByRow(Row:Integer):TStyledControl;
begin
if(Grid<>nil)andGrid.IsSelected(Row)then
Result:=CellControl
else
Result:=nil;
end;
Soyoualreadyrecognizesthatareturnvalueisreturnedbythefunctiononlywhenjustthedesiredrowinthegrid-andthusthecell-isselected.Becauseonlyjustthisensures,thatthecellalreadyexists.
SowedeclareinourmainformthefollowingClassHelper:
type
THelperColumn=classhelperforTColumn
procedureSetACell(aRow:Integer;AVAL:TValue);
functionGetACell(aRow:Integer):TStyledControl;
end;
Theimplementationslooksthenasfollows:
{THelperColumn}
functionTHelperColumn.GetACell(aRow:Integer):TStyledControl;
begin
result:=TStyledControl(CellControlByRow(aRow));
end;
procedureTHelperColumn.SetACell(aRow:Integer;AVAL:TValue);
var
sc:TStyledControl;
begin
ifselfisTStringColumnthenbegin
sc:=TStyledControl(CellControlByRow(aRow));
ifsc<>NILthenbegin
TTextCell(CellControlByRow(aRow)).Text:=AVAL.tostring;
end;
end;
ifselfisTImageColumnthenbegin
sc:=TStyledControl(CellControlByRow(aRow));
ifsc<>NILthenbegin
TImageCell(CellControlByRow(aRow)).Bitmap:=AVAL.AsType<TBitmap>;
end;
end;
end;
WecannowseteachTStringColumnandeveryTImageColumn,forexample,likethis:
imgInfo.SetACell(0,Image1.Bitmap);//BitmapinaTImageColumn
strSize.SetACell(0,‘Len:‘+intToStr(length(memo1.text)));//Text
OrjustthewaytogodirectlytogetthewholeStyledControltosetseveralproperties:
TProgressbar(progressInfo.GetACell(0)).max:=length(memo1.text);
TProgressbar(progressInfo.GetACell(0)).value:=Memo1.PosToTextPos(Memo1.CaretPosition);
YoumaybewonderingfortheexampleofwhyboththeTImageColumnandtheTSTringColumncanusethehelperfunction“SetACell”althoughtheHelperclasswasintroducedfortheTColumn.Thisisjustthemagicofinheritance,becauseTColumnisboththeancestorofTStringColumnaswellasTImageColumnsothatbothsuccessorsinherittheenhancedcapabilitiesofthepreviousclass.
Itisreallybeenagreatthing,that.Theclasshelpershashelpedmeinmanycaseswhereotherwiseonlythecomponentdevelopercoulddeliverasolution,ifhewouldchangehiscomponent(whichunderstandablythemostdevelopersdon’twant).
Youwillneedsuchdirectaccesstoacellofagridratherrare,butifiteverbecomesnecessaryinspecialcases(e.g.ifyouwanttooutputcertaininformationaboutthecurrentlyselectedcell),thenyouknowhowyoucandoit.
R29…Showpop-upmenuataspecialposition
Maybeyouarefacedwiththesituationthattheusercanclickonaspecificitemandthenaboveorbelowapop-upmenushouldbedisplayed.Hereisanexamplewheretheuserclicksonapanelandthenthenapop-upmenuwillbedisplayedbelowit:
Youcandoitlikethis:procedureTfrm_Main.pnMahnungRangeMouseUp(Sender:TObject;Button:
TMouseButton;Shift:TShiftState;X,Y:Single);
begin
hs_ShowPopup(self,pnMahnungRange,popMahnung,unten);
end;
Wherethefunction“hs_ShowPopup”isdefinedasfollows:procedurehs_ShowPopUp(frm:TForm;bn:TControl;pop:TPopupMenu;ObenUnten:Integer);
var
FP:TPointF;
begin
FP.X:=0;
FP.Y:=0;
//Transposesthecoordinatesinthecontextoftheform.
FP:=bn.LocalToAbsolute(FP);
//Transposesthecoordinatesinthecontextofthescreen.
FP:=frm.ClientToScreen(FP);
//Displaythepopupmenuatthecalculatedcoordinates.
pop.Popup(FP.X,FP.Y+bn.height);
end;
Thisfeaturealsoworksonmulti-monitorsystems.
R30…Storeadditionalinformationinstandardobjects
FromtheVCLyoumightknowtheproperty“Tag”,whichhasalmostanyobjectandrepresentsanintegervalue.
InFireMonkeyyoucansetsuchaTagvaluealso,forexample,withtheObjectinspector.Butbeyondthatthereareexistingforanobjectoracontrol(TFMXObject)theproperties“TagString”,“TagFloat”and“TagObject”.Thisisextremelyusefulifyouwanttostoreadditionaldata,informationorobjectstoaspecificcontrol.Thesevalues,however,canbereadorwriteonlyprogrammatically(i.e.notviatheObjectinspector).Overall,averyusefulextensiononFMX.
R31…Draganddroptextfromexternalsource(egbrowser)toaTEditbox
Attherequestofacustomer,Ihaveintegratedafunctioninmyaccountingprogramwiththatonecandragaselectedtextfromthebrowseronaneditfieldoftheprogramandthetextisplacedthere.
Forthis,youonlyneedtocompletetherelevanteventsasfollows:procedureTf_Bill.Edit1DragOver(Sender:TObject;
constData:TDragObject;constPoint:TPointF;varOperation:TDragOperation);
begin
Operation:=TDragOperation.Copy;
end;
procedureTf_Bill.Edit1DragDrop(Sender:TObject;constData:TDragObject;
constPoint:TPointF);
begin
if(Data.Data.TypeInfo<>NIL)and(Data.Data.TypeData<>NIL)thenbegin
TEdit(Sender).Text:=Data.Data.ToString;
end;
end;
ApartfromWindowsInternetExplorer,thisfunctionalityissupportedbymostbrowsersandindeedacrossplatforms(e.g.evenSafariontheMAC).Also,thedraganddropwithtextworkswithmanywordprocessingprograms.
R32…Acolumninastringgridshouldoccupytheremainingspace
Youhaveastringgridwithmultiplecolumnsandwantatchangesinsizeofthegridthatalwaysaparticularcolumnrespondstothechangesinsizeandoccupythefreespaceinthegrid.Itshouldbenotedthatthecalculationcanonlyworkproperly,ifthegridhasbeenshownonce,sotheOnShow-eventoftheformhasbeenrunthroughcompletelyonce.Forthispurpose,youmustuseatimerthatyousetenabledintheOnShoweventwiththeintervallof25ms.Atthefirsttimethetimereventistriggered,youdisablethetimerandmakethedesiredcalculation(lateryoudothecalculationsontheOnResizeeventoftheformorthegrid).
Itcouldlookawholeas:var
Form9:TForm9;
VarCol:Integer;
implementation
{$R*.fmx}
procedureTForm9.FormCreate(Sender:TObject);
begin
VarCol:=0;//fürdieErste
//VarCol:=StringGrid1.ColumnCount-1;//z.B.fürdieletzteSpalte
end;
functionAColWidth(Grid:TStringGrid;VarCol:Integer):Extended;
vari:
integer;
aWidth:Extended;
begin
aWidth:=0;
fori:=0toGrid.ColumnCount-1do
ifi<>VarColthenaWidth:=aWidth+Grid.Columns[i].Width+1;
Result:=Grid.ClientWidth-aWidth;
end;
procedureTForm9.FormResize(Sender:TObject);
begin
TStringColumn(StringGrid1.ColumnByIndex(VarCol)).width:=AColWidth(StringGrid1,VarCol);
end;
procedureTForm9.FormShow(Sender:TObject);
begin
Timer1.Enabled:=True;
end;
procedureTForm9.Timer1Timer(Sender:TObject);
begin
Timer1.Enabled:=false;
TStringColumn(StringGrid1.ColumnByIndex(VarCol)).width:=AColWidth(StringGrid1,VarCol);
end;
Chapter7:UpgradingfromDelphiXE3-XE6toXE7
ForuserswhohavealreadyworkedwithDelphiXE3,Iwouldliketogivesomehintshere:
DelphiXE7andInstallationSinceDelphiXE5IworkwiththeEnterpriseversion,becausethenitiseasiertoinstalltheMobile-Pack,becausethepointisoffereddirectlyduringtheinstallationofDelphi(besidesofcoursethereareotheradvantagesintheEnterpriseversion).
DelphiXE4IhadpurchasedasaProfessionalversion.IntheProfessionalversion,youmustfirstinstallandactivateDelphiitself.Thenstartthesetupprogramagain,select“Upgrade”(herestillscreenshotsofXE4/5):
Clickonthe“Next-switch”andenteronthenextpagetheserialnumberthatyoureceivedwhenyoupurchasedthepackMobile:
ChecktheserialnumberidentifiesthesetupprogramnowthatyouwanttoinstalltheMobile-Packandproceedsaccordingly.
Itcouldbe,thatitischangedinthemeantimealsofortheProfessional-version.Someonetoldme,thatyoucanalsodothewholeinstallationdirectlywiththeserialfromtheMobile-Pack.
DelphiXE7andusableMACOSXversionsMountainLionandMaverickscanbeusedasdevelopmentplatforms.ButYosemitegoesalso.LiondoessupporttheAppStorenotperfectandyoucancertainthingsonthissystemnottest,soyoushouldatleastworkingwithMountainLionorMavericks,atleastifyouwanttodevelopMACOSXprogramsfortheAppStore.Allothersystemsshouldofcoursebeavailableandbeusedasatestsystem.IhaveaniMac,whereonthesystemdiskevenLeopardandSnowLeopardareinstalled.OnaconnectedFireWireharddriveIhaveinstalledondifferentpartitionsLion,MountainLion,MavericksandYosemite.
Inaddition,IhaveaMacAirnotebook,whereIcanuseMountainLionandMavericksas“Fresh”system(i.e.withoutanyinstalleddevelopmentenvironmentsthatcoulddistortanytestresults.Physicistsalwayssaythatthetestenvironmentitselfhasanimpactonthetestresult,that’sright).
Veryoften,ApplechangedanythingontheOSandsoitcansometimesbethatsomethingdoesnotworkanymoreinthedevelopmentprocess.EMBAstrivesalwaysherequicklytoprovidehotfixes.ExplorethereforefromtimetotimeinEMBAinthedevelopercommunity(EDNDeveloperNetwork).
TakeoverofprojectsXE3-XE6ForthetransferofyourprojectfromXE3-XE5toXE7youshouldfirstopenitagaininthepreviousversionofDelphi.Thereyoushouldthenclosealltheformsanddisplayonlytheprojectsourcecode.Thenyoucansavetheprojectandcloseit.ThisapproachisrecommendedbecauseotherwiseDelphimayagainusetheoldfolderifyoutakeovertheprojecttoadifferentfolder(Ialsorenametheolderfolder,becausesometimesDelphitriestoopenthereunitsagain).Thencopyallthefilestoanotherfolder,andopentheproject.
ThenjustadjustthepathsunderProject,Options,Directories!!
Whenyouloadtheforms,itcouldbe,thatyougetabadrepresentationofyourforms.Forexample,theregistersoftheTabControlcouldbemuchsmaller,inthememocomponentthecursorwillnotbedisplayed,ifnotextwasin,and,and,and…
SolutionwastoopentheStylescomponentandselect“RemoveAll”andreadthestyleagainfromtheharddisk.Obviously,changesintheStylesbeenmadewhichhaveaninfluenceonthefunctionofthecomponents.TheFMXstylescanbefoundontheharddriveunder:
C:\ProgramFiles(x86)\Embarcadero\Studio\15.0\Redist\styles\FMX
Afterthat,everythingwassoagain,asitshouldbe.
Butitshouldbenotedthatherethelossofcustomchangesthreatensthathadpossiblymadetothestyles.Insteadof“RemoveAll”youcouldtrytoselect“Add”possibly.ThecomponentslookafteragainOK,ifcustomsettingsareretained,youneedtotestitbyyourself,Ididnothaveanyinhere.
Otherobstacles:TLabelandTButton“unknown”AlreadyusingDelphiXE4therehasbeenare-orderingofthefunctionsandcomponentsintheindividualFireMonkeyunits.
Adescriptionofthedetailscanbefoundhere:
http://docwiki.embarcadero.com/RADStudio/XE4/en/Refactored_FireMonkey_Classes_in_XE4
ButtonandlabelsarenowlocatesintheunitFMX.StdCtrls,thisyoumustaddtothe“Uses”asaunit.Delphiisheresometimesalittleovereagerandthenautomaticallyaddsadditionalunitssometimestwice.Sodon’twonderifyouhavetheappropriateerrormessagesduringcompilation,butjustthenremovetheduplicateentries.
Inaddition,theprogramhasbitchedfortheWindowsplatformthat“Application.mainform.handle”isnotcompatiblewith“TWindowHandle”.Icouldcurrentlyonlydealwithawork-around,asIhaveheresimplyreplacedas“Application.mainform.handle”with“0”.Butthiscan’tbeapermanentsolution.
Tousethe“TFillTextFlags”inconnectionwiththeprintfunction,Ihadtoincludetheunit“FMX.Graphics”,alsoaconsequenceoftheabove-mentionedreorganization.Soif
somethingisdeclaredas“unknown”,remberofthisreorderingandsearchthemissinguntisandadjustitaccordingly.
ProblemswiththemultiviewDesigner(Fire-UI)BeforeDelphiXE7,youaddaplatformintheProjectwindow.InXE7bycreatinga“MultiDeviceApplication”theotherplatformsareavailableastargetsautomatically:
InaprojecttakenfromDelphibeforeXE7,theprojectlistlookslikethis:
Sotheotherplatformsaremissing.
Anditdoesnothelp,ifyoutrytochooseanotherstyle(e.g.IOSorAndroid)fromView-dropdownlist.Thiswillendonlywithanerrormessage.
Thesolutionisagaintore-createtheproject.First,copythetextcontentoftheprojectfile(DPR)totheclipboardandthencreateanewprojectwiththesamenameagain.ThanreplacethenewtextintheDPRfilewiththeolderfromtheclipboard.Nowyouhaveavailableintheprojectwindowalsotheothertargets(Android,iOS)andyoucanexpandyourprojecttooneoftheplatformsifrequired.
Chapter8:Outlook
Othertopics
Thebookisstillintheexpansionphase,foraperiodoftimeothertopicswillbeadded.FornowI’amconvertingmorevcl-projectstofmx-projects.SoIwilladdmyexperienceherewhenIupdatethisbook(solooksometimesatamazonatthetimeanddateofthebook).
Perhapsthereisalsoaspecialtopic,whereyougetstuck.Ifitisapointthatmightfitwellhereinthisbook,donothesitatetosendmeane-mailwithadescriptionoftheproblem.IwillbehappytocheckwhetherIcantakeitasatopichere-withanappropriatesolution.
Ifyoushouldnoticedobviouserrorsoroutdatedinformationinthebook,Iwouldalsoappreciateanote.
Attachment1:UnitHSW.FMXSandbox.pas
unitHSW.FMXSandbox;
{**CopyrightbyHarryStahlSoftware,Bonn,www.hastasoft.de**}
{**Peoplewhobuyedmyebook”Cross-PlatformDevelopmentmitDelphiXE7}
{**&FireMonkeyfürWindows&MACOSX”arefreetousethisunit}
{**Othersmayaskme}
{**Useitatyourownrisk,theauthorwillnotberesponsibleforany}
{**damages}
{**DontforgettoDefine“UseSandbox”inyourcompilerconditions}
{**Andaddtheentitlementkey}
{**com.apple.security.files.bookmarks.app-scope}
{**toyourEntitlement-file}
interface
uses
System.SysUtils,System.Classes,FMX.Dialogs,
Macapi.CoreFoundation,Macapi.ObjectiveC,MACAPI.coreservices,
MacApi.AppKit,Macapi.CocoaTypes,MacApi.Foundation;
type
NSURL=interface(NSObject)
//[‘{BB3BDECA-2E3A-4326-BDD8-6C339A277E34}’]OriginalausMACApi.Foundation
//eingeeigeneImplementationistleidererforderlich,weildieseFunktionen
//dringendfürdieSandbox-Funktionalitätbenötigtwerden,inderMACApi-Unit
//aberleidervergessenwurden.
//FallsdasineinemdernächstenDelphi-Updatesnachgeholtwird,kannmandiese
//Implementierungwiederentfernen
[‘{4997B641-85B2-4BE9-A9A9-45F64CE34955}’]
functionstartAccessingSecurityScopedResource:Boolean;cdecl;
functionstopAccessingSecurityScopedResource:Boolean;cdecl;
end;
TNSURL=class(TOCGenericImport<NSURLClass,HSW.FMXSandbox.NSURL>)end;
functionAppScopedBookMarksEnabled:boolean;
functionCreateAppScopedBookMark(vardata:NSDATA;URL:MacApi.Foundation.NSURL):Boolean;
functionGetResolvedAppScopedBookMark(data:NSData;varNEWURL:NSURL):Boolean;
functionCanStartAccessingSecurityScopedResource(url:NSURL):boolean;
functionCanStopAccessingSecurityScopedResource(url:NSURL):boolean;
functionGetAppScopedBookMarkFromList(ADir:String;vardata:NSData):Boolean;
functionExistsAppScopedBookmarkForRes(ADir:String):Boolean;
functionGetAppScopedAccessToRes(ADir:String;varurl:NSURL):Boolean;
functionGetBaseAppScopedAccessToRes(ADir:String;varurl:NSURL):Boolean;
const
//DieseKonstantenwerdenzumAufrufderBookmark-Funktionenbenötigt
//einweitererPunkt,womandieMACApi.Foundationerweiternmüsste
NSURLBookmarkCreationWithSecurityScope:NSUInteger=(1shl11);
NSURLBookmarkResolutionWithSecurityScope:NSUInteger=(1shl10);
NSURLBookmarkResolutionWithoutMounting:NSUInteger=(1shl9);
NSURLBookmarkResolutionWithoutUI:NSUInteger=(1shl8);
//SetzenSiedieseVariableaufTrue,wennSieimDebugmodus
//erweiterteInformationenerhaltenwollen,wannetwasfunktioniert
//undwannnicht.
ShowDebugInfos:Boolean=false;
var
//Liste,welchedieRessourcen(=OrdneroderDateien)unddiedamit
//verknüpftenBookmarkdateienenthält
slBookMarks:TStringList;
//VariablefürdenDateinnamenderBookmark-Liste
SandboxAppScopeBookmarksfile:string;
//OrdernamenfürOrterderBookmark-ListeunddieBookmark-Dateien
SandboxAppSupportPath:string;
SandBoxBookmarksFolder:string;
{$IFDEFUseSandbox}
UseSandboxing:Boolean=True;
{$ELSE}
UseSandboxing:Boolean=false;
{$ENDIF}
implementation
//DieseFunktionmussaufgerufenwerden,bevormaneine
//RessourceaußerhalbderSandboxnutzenwill
//UrlenthältdabeidenNamendesOrdnersoderderDatei
functionCanStartAccessingSecurityScopedResource(url:NSURL):boolean;
begin
Result:=false;
ifUrl<>NILthenbegin
try
ifnotURL.startAccessingSecurityScopedResourcethenbegin
{$IFDEFDebug}
ifShowDebugInfosthenShowMessage(‘StartAccessingerfolglos’);
{$ENDIF}
endelsebegin
Result:=True;
end;
except
{$IFDEFDebug}
ifShowDebugInfosthenShowMessage(‘FehlerinStartAccessing’);
{$ENDIF}
end;
end;
end;
//DieseFunktionmussaufgerufenwerden,wennderZugriffaufeine
//RessourceaußerhalbderSandboxbeendetwerdensoll
//Achtung:DieAufrufederstartundstopfunktionenmüssenausbalanciertsein!
//IstdasnichtderFall,verliertdieAnwendungwährendderaktuellenLaufzeit
//dieFähigkeitSecurityScopedBookmarksnutzenzukönnen
functionCanStopAccessingSecurityScopedResource(url:NSURL):boolean;
begin
Result:=false;
ifUrl<>NILthenbegin
try
ifnotURL.stopAccessingSecurityScopedResourcethenbegin
ShowMessage(‘Kannnichtstoppen’);
endelsebegin
Result:=True;
//ShowMessage(‘HasAccess’);
end;
except
ShowMessage(‘FehlerinstopAccessingSecurityScopedRessource’);
end;
end;
end;
//PrüftobdasbenutzteOSBetriebssystemüberhauptinderLageist,
//SecurityScopedBookmarkszuerzeugen
//DasisterstabLion,Version10.7.3möglich
functionAppScopedBookMarksEnabled:boolean;
begin
Result:=false;
ifTOSVersion.Major>10thenbegin
Result:=true;
end;
ifTOSVersion.Major>=10thenbegin
if(TOSVersion.Minor>=8)thenbegin
Result:=true;
end;
if(TOSVersion.Minor=7)thenbegin
if(TOSVersion.ServicePackMajor>=3)thenbegin
Result:=true;
end;
end;
end;
end;
//LegteineApp-ScopedSecurityBookmarkan,Ressourcekanndabei
//einOrdnerodereineDateisein
functionCreateAppScopedBookMark(vardata:NSDATA;URL:MacApi.Foundation.NSURL):Boolean;
var
err2:NSError;
includingResourceValuesForKeys:NSArray;
relativeToURL,AURL:MacApi.Foundation.NSURL;
ag:System.TGuid;
aGuid,ADir:string;
begin
Result:=False;
ifURL=NILthenexit;
//Prüfen,obhierfürschoneinEintragbesteht,dannnichtnochmalanlegen
ADir:=URL.path.UTF8String;
ifGetAppScopedBookMarkFromList(ADir,data)thenbegin
Result:=True;
exit;
end;
//Bookmarkexistiertnochnicht,daheranlegen
err2:=TNSError.Create;
err2:=NIL;
includingResourceValuesForKeys:=NIL;
relativeToURL:=NIL;
Data:=URL.bookmarkDataWithOptions(
NSURLBookmarkCreationWithSecurityScope,
includingResourceValuesForKeys,
relativeToURL,//NIL=App-Scope
@Err2);
try
ifnotAssigned(err2)thenbegin
Result:=True;
ifData<>NILthenbegin
CreateGuid(ag);
aGuid:=GUIDToString(ag);
slBookmarks.Add(URL.path.UTF8String+#1+aGuid);
aGuid:=IncludeTrailingPathDelimiter(SandBoxBookmarksFolder)+aGuid;
//BookmarkindasBookmarkverzeichnisspeichern
Data.writeToFile(NSSTr(aGuid),true);
//ListederBookmarksspeichern
slBookmarks.SaveToFile(SandboxAppScopeBookmarksfile);
endelsebegin
Result:=false;
{$IFDEFDEBUG}
ifShowDebugInfosthenShowMessage(‘DatainCreateBookmarkisNIL’);
{$ENDiF}
end;
endelsebegin
Result:=false;
{$IFDEFDEBUG}
ifShowDebugInfosthenShowMessage(‘ERRinCreateBookmarkis<>NIL’);
{$ENDIF}
end;
except
{$IFDEFDEBUG}
ifShowDebugInfosthenShowMessage(‘ProblemwithERRinCreateBookmark’);
{$ENDIF}
end;
end;
//DieseFunktionerhältalsInputdasData-Objekt,welchesdieBookmarkenthält
//DieResolveBookmark-FunktionliefertdanndieRessourcezurück,
//diedannspätermitdemeinleitenden“StartAccessing”genutztwerdenkann
functionGetResolvedAppScopedBookMark(data:NSData;varNEWURL:NSURL):Boolean;
var
Err:NSError;
relativeToURL:MACAPI.Foundation.NSURL;
begin
Result:=False;
err:=TNSError.Create;
err:=NIL;
RelativeToURL:=NIL;
NewURL:=NIL;
NewUrl:=TNSURL.Wrap(TNSURL.OCClass.URLByResolvingBookmarkData(
Data,
NSURLBookmarkResolutionWithSecurityScope,
relativeToURL,
0,
@Err));
if(NewUrl<>NIL)and(notAssigned(err))thenbegin
Result:=True;
endelsebegin
{$IFDEFDEBUG}
ifShowDebugInfosthenshowMessage(‘ERR<>NILinResolvedBookmark’);
{$ENDIF}
end;
end;
//DieseHilfsfunktionprüft,obfürdieangeforderteRessourcezuvor
//schonmaleineBookmarkerzeugtwurdeundliefertindiesemFalldasentsprechende
//Data-Objekt(mitderBookmark)zurück
//Hinweis:ADirkanneinOrdnerodereineDateisein
functionGetAppScopedBookMarkFromList(ADir:String;vardata:NSData):Boolean;
var
P,L:Integer;
Dir,AMark:string;
begin
Result:=false;
forL:=0toslBookmarks.Count-1dobegin
P:=Pos(#1,slBookMarks[L]);
ifP<>0thenbegin
Dir:=Copy(slBookMarks[L],1,P-1);
AMark:=Copy(slBookMarks[L],P+1,5000);
AMark:=IncludeTrailingPathDelimiter(SandBoxBookmarksFolder)+AMark;
end;
ifDir=ADirthenbegin
data:=TNSDATA.Wrap(TNSData.OCClass.dataWithContentsOfFile(NSSTr(AMark)));
Result:=True;
break;
end;
end;
end;
//DieseHilfsfunktionprüft,obfüreineRessource,dieeinenOrdnerrepräsentiert
//bereitseinübergeordneterOrdnermiteinerBookmarkexistiert.Fallsja,
//wirddieBookmarkimData-Objektzurückgeliefert
//DavondieserRessource/diesemOrdnerZugriffeaufalleUnterordner
//möglichsind,mussmannichtfürdutzendevonUnterordnerBookmarks
//anlegen.
functionGetBaseAppScopedBookMarkFromList(ADir:String;vardata:NSData):Boolean;
var
P,L,start:Integer;
Dir,AMark:string;
begin
Result:=false;
forL:=0toslBookmarks.Count-1dobegin
P:=Pos(#1,slBookMarks[L]);
ifP<>0thenbegin
Dir:=Copy(slBookMarks[L],1,P-1);
AMark:=Copy(slBookMarks[L],P+1,5000);
AMark:=IncludeTrailingPathDelimiter(SandBoxBookmarksFolder)+AMark;
end;
start:=pos(Ansilowercase(Dir),Ansilowercase(ADir));
ifstart=1thenbegin
data:=TNSDATA.Wrap(TNSData.OCClass.dataWithContentsOfFile(NSSTr(AMark)));
Result:=True;
break;
end;
end;
end;
//Prüft,obeineBookmarkfürdieangefragteRessourceexistiert
//ADirkanneinOrdnerodereineDateisein
functionExistsAppScopedBookmarkForRes(ADir:String):Boolean;
var
P,L:Integer;
Dir,AMark:string;
NS:NSString;
begin
Result:=false;
forL:=0toslBookmarks.Count-1dobegin
P:=Pos(#1,slBookMarks[L]);
ifP<>0thenbegin
Dir:=Copy(slBookMarks[L],1,P-1);
end;
ifDir=ADirthenbegin
Result:=True;
break;
end;
end;
end;
//DieseFunktionvereinfachtdenZugriffaufRessoruceperApp-ScopedBookmark,
//indemmehrereSchritteineinerFunktionzusammengefasstwerdenund
//somitdasHandlingdeutlichvereinfachtwird.
//NachdemAufrufdieserFunktionundderNutzungderRessource
//mussimProgrammablaufdieFunktion
//CanStopAccessingSecurityScopedResourceaufgerufenwerden,
//damitdiestartundstopAufrufewiederausbalanciertsind!!
functionGetAppScopedAccessToRes(ADir:String;varurl:NSURL):Boolean;
var
Data:NSData;
begin
Result:=false;
ifGetAppScopedBookMarkFromList(ADir,data)thenbegin
url:=HSW.FMXSandbox.TNSURL.Create;
ifGetResolvedAppScopedBookMark(Data,url)thenbegin
ifCanStartAccessingSecurityScopedResource(url)thenbegin
Result:=True;
endelsebegin
{$IFDEFDEBUG}
ifShowDebugInfosthenbegin
ShowMessage(‘KeinZugriffaufdasDirectorymöglich.BitteerneutmitÖffnen-Dialgauswählen.’);
end;
{$ENDIF}
end;
end;
end;
end;
//WiedieFunktionzuvor,nurdassbeiOrdnerngeprüftwird,
//obbereitseineBookmarkfüreinenübergeordnetenOrdnerbesteht
//unddieseverwendetwerdensoll
//AufrufmachtjenachProgrammlogikSinn
functionGetBaseAppScopedAccessToRes(ADir:String;varurl:NSURL):Boolean;
var
Data:NSData;
begin
Result:=false;
ifGetBaseAppScopedBookMarkFromList(ADir,data)thenbegin
url:=HSW.FMXSandbox.TNSURL.Create;
ifGetResolvedAppScopedBookMark(Data,url)thenbegin
ifCanStartAccessingSecurityScopedResource(url)thenbegin
Result:=True;
endelsebegin
{$IFDEFDEBUG}
ifShowDebugInfosthenbegin
ShowMessage(‘KeinZugriffaufdasDirectorymöglich.BitteerneutmitÖffnen-Dialgauswählen’);
end;
{$ENDIF}
end;
end;
end;
end;
Initialization
ifAppScopedBookMarksEnabledthenbegin
//WenndieNutzungderAppscopedBookmarksvomSystemunterstütztwerden
//hierschonmaldieStringlistezurVerwaltungderBookmarks-Liste
//unddieentsprechendenAblageorteerzeugen
SandboxAppSupportPath:=IncludeTrailingPathDelimiter(GetHomePath)+‘Library’+
PathDelim+‘ApplicationSupport’+PathDelim+ChangeFileExt(ExtractFileName(paramstr(0)),”);
ifnotDirectoryExists(SandboxAppSupportPath)thenbegin
ForceDirectories(SandboxAppSupportPath);
end;
SandBoxBookmarksFolder:=IncludeTrailingPathDelimiter(SandboxAppSupportPath)+‘Bookmarks’;
ifnotDirectoryExists(SandBoxBookmarksFolder)thenbegin
ForceDirectories(SandBoxBookmarksFolder);
end;
SandboxAppScopeBookmarksfile:=IncludeTrailingPathDelimiter(SandboxAppSupportPath)+‘AppScopeBookmarks.dat’;
slBookMarks:=TStringList.Create;
slBookMarks.Duplicates:=dupIgnore;
ifFileExists(SandboxAppScopeBookmarksfile)thenbegin
slBookMarks.LoadFromFile(SandboxAppScopeBookmarksfile);
end;
endelsebegin
UseSandboxing:=false;
end;
Finalization
ifAppScopedBookMarksEnabledthenbegin
slBookMarks.Free;
end;
end.
Atachment2:NewlyimplementedOpenandSavedialogsforsandboxing
BelowyouwillfindthemycurrentimplementationsoftheOpenandSavedialogs,andadirectoryselectiondialog.Thesedialoguesallowsandboxing.
ThisfunctionmustbeimplementedinadifferentunitthantheHSW.FMXSandbox.pasown,sincetheNSURLfunctionsareused,whichareimplementedintheMACApi.Foundation.pasandnotmyalternativeimplementation.Itwouldbepossible,purelytoincreasethesefunctionsinthesandboxunit,onewouldhaveinthecorrespondingNSURLfunctionsspecifytheMACApi.Foundationunitscope.IhavealsodonesonotbecauseIneedjustasinmyownprogramandUnitsystematics.
{$IFDEFMACOS}
//Functiontoselectafoldertocreatethepossibilityofabookmark
functionMACSelectDirectory(constATitle:string;varADir:string;CreateBookMark:Boolean):Boolean;
var
LOpenDir:NSOpenPanel;
LInitialDir:NSURL;
LDlgResult:NSInteger;
Data:NSData;
begin
Result:=False;
LOpenDir:=TNSOpenPanel.Wrap(TNSOpenPanel.OCClass.openPanel);
LOpenDir.setAllowsMultipleSelection(False);
LOpenDir.setCanChooseFiles(False);
LOpenDir.setCanChooseDirectories(True);
LOpenDir.setPrompt(NSSTR(‘Auswählen’));
//LOpenDir.setCanCreateDirectories(True);
ifADir<>”then
begin
LInitialDir:=TNSURL.Create;
LInitialDir.initFileURLWithPath(NSSTR(ADir));
LOpenDir.setDirectoryURL(LInitialDir);
end;
ifATitle<>”thenLOpenDir.setTitle(NSSTR(ATitle));
LOpenDir.retain;
try
LDlgResult:=LOpenDir.runModal;
ifLDlgResult=NSOKButtonthen
begin
ifLOpenDir.URL<>NILthenbegin
ADir:=LOpenDir.URL.path.UTF8String;
endelsebegin
ifLOpenDir.URLs.objectAtIndex(0)<>NILthenbegin
ADir:=string(TNSUrl.Wrap(LOpenDir.URLs.objectAtIndex(0)).relativePath.UTF8String);
end;
end;
ifFileExists(ADir)thenbegin
ADir:=ExtractFilePath(ADir);
end;
Result:=True;
{$IFDEFUseSandbox}
if(AppScopedBookMarksEnabled)and(CreateBookMark)thenbegin
CreateAppScopedBookMark(Data,LOpenDir.URL);
end;
{$ENDIF}
end;
finally
LOpenDir.release;
end;
end;
//Functiontoselectthefile(open)withtheabilitytocreateabookmark
functionMACSelectFile(constATitle:string;varAFile:string;CreateBookMark:Boolean):Boolean;
var
LOpenDir:NSOpenPanel;
LInitialDir:NSURL;
LDlgResult:NSInteger;
Data:NSData;
begin
Result:=False;
LOpenDir:=TNSOpenPanel.Wrap(TNSOpenPanel.OCClass.openPanel);
LOpenDir.setAllowsMultipleSelection(False);
LOpenDir.setCanChooseFiles(True);
LOpenDir.setCanChooseDirectories(false);
LOpenDir.setPrompt(NSSTR(‘Öffnen’));
//LOpenDir.setCanCreateDirectories(True);
ifAFile<>”then
begin
LInitialDir:=TNSURL.Create;
//LInitialDir.initFileURLWithPath(NSSTR(ExtractFilePath(AFile)));
ifFileExists(AFile)thenbegin
LInitialDir:=TNSURL.Wrap(TNSURL.OCClass.fileURLWithPath(NSSTR(AFile)));
endelsebegin
LInitialDir:=TNSURL.Wrap(TNSURL.OCClass.fileURLWithPath(NSSTR(ExtractFilePath(AFile))));
end;
LOpenDir.setDirectoryURL(LInitialDir);
end;
ifATitle<>”then
LOpenDir.setTitle(NSSTR(ATitle));
LOpenDir.retain;
try
LDlgResult:=LOpenDir.runModal;
ifLDlgResult=NSOKButtonthen
begin
AFile:=string(TNSUrl.Wrap(LOpenDir.URLs.objectAtIndex(0)).relativePath.UTF8String);
Result:=True;
{$IFDEFUseSandbox}
if(AppScopedBookMarksEnabled)and(CreateBookMark)thenbegin
CreateAppScopedBookMark(Data,LOpenDir.URL);
end;
{$ENDIF}
end;
finally
LOpenDir.release;
end;
end;
//Functiontochooseanameforafilewiththepossibilityofthisfile
//Createandbookmarkit
functionMACSaveFile(constATitle:string;varAFile:string;CreateBookMark:Boolean):Boolean;
var
panelSaveDir:NSSavePanel;
pnInitialDir:NSURL;
LDlgResult:NSInteger;
Data:NSData;
Error:NSError;
begin
Result:=False;
panelSaveDir:=TNSSavePanel.Wrap(TNSSavePanel.OCClass.SavePanel);
panelSaveDir.setNameFieldStringValue(NSSTr(ExtractFileName(AFile)));
panelSaveDir.setAllowsOtherFileTypes(True);
panelSaveDir.setPrompt(NSSTR(‘Sichern’));
panelSaveDir.setNameFieldLabel(NSStr(‘Sichernals:’));
ifAFile<>”thenbegin
pnInitialDir:=TNSURL.Create;
ifFileExists(AFile)thenbegin
pnInitialDir:=TNSURL.Wrap(TNSURL.OCClass.fileURLWithPath(NSSTR(ExtractFilePath(AFile))));
endelsebegin
pnInitialDir:=TNSURL.Wrap(TNSURL.OCClass.fileURLWithPath(NSSTR(ExtractFilePath(AFile))));
end;
panelSaveDir.setDirectoryURL(pnInitialDir);
end;
ifATitle<>”then
panelSaveDir.setTitle(NSSTR(ATitle));
panelSaveDir.retain;
try
LDlgResult:=panelSaveDir.runModal;
ifLDlgResult=NSOKButtonthenbegin
//AFile:=string(TNSUrl.Wrap(panelSaveDir.URLs.objectAtIndex(0)).relativePath.UTF8String);
ifpanelSaveDir.directoryURL<>NILthenbegin
AFile:=IncludeTrailingPathDelimiter(panelSaveDir.directoryURL.path.UTF8String)+panelSaveDir.nameFieldStringValue.UTF8String;
endelsebegin
ifpanelSaveDir.URL<>NILthenbegin
AFile:=panelSaveDir.URL.path.UTF8String;
end;
end;
Result:=True;
{$IFDEFUseSandbox}
if(AppScopedBookMarksEnabled)and(CreateBookMark)thenbegin
//ZunächstmussdieDateierzeugtwerden,daetwasvorhanden
//seinmuss,wodieBookmarkanknüpfenkann.
ifpanelSaveDir.directoryURL<>NILthenbegin
ifnotFileExists(AFile)thenbegin
NSStr(AFile).writeToFile(NSStr(AFile),false);
end;
endelsebegin
ifpanelSaveDir.URL<>NILthenbegin
ifnotFileExists(panelSaveDir.URL.path.UTF8String)thenbegin
panelSaveDir.URL.path.writeToFile(panelSaveDir.URL.path,false);
end;
end;
end;
ifpanelSaveDir.directoryURL<>NILthenbegin
CreateAppScopedBookMark(Data,panelSaveDir.URL);
end;
end;
{$ENDIF}
end;
finally
panelSaveDir.release;
end;
end;
{$ENDIF}