How Android beats Windows 7 laptops

Lately, many have started to use their phone for tasks, for which they previously used a PC - including updating online spreadsheets etc. The PC features much better input and output devices (keyboard, mouse, screen), so why use the phone for these tasks? Here is a list of why it makes sense to use an typical Android phone instead of a Windows 7 laptop for many tasks: * The laptop does not have
Read More

Google Android reviewed by a Delphi developer

With the major part of my background in Delphi, but also some in C, C++, Assembler, Java, C# and others, I threw myself at the Android platform in order to figure out how it works. The main language is Java, the obvious IDE is Eclipse, and after installing the Android SDK things are well integrated with each other. The fundamental structure is very similar to Delphi - but things have different
Read More

Random Thoughts on the Passing Scene #153

Andreano has a new blog, or at least it is new to me – just found it today.  He has two items there that caught my eye: His RADTweet project – a Delphi-based Twitter client – is online.  He has wisely put it on SourceForge for easy access, downloading, and updating.  A project that shows how to do HTTP authentication with DataSnap While you are at it, check out his post on “Using LINQ to Objetcs in Delphi Prism”. The inestimable Mike Rozlog is on a hot streak, and he continues to give cool and interesting Webinars.  His latest is “Mastering Database Application Development with Delphi”.  Once again, he’ll be giving the webinar at three times during the day of 14 April 2010, so no matter where you are in the world, you should be able to attend one of them.  These marketing people are busy.  What to know about RAD In Action regarding building database applications?  There’s a web page for that.  Julian Bucknall, the CTODX (Chief Technology Officer of Developer Express) has an update for their VCL customers. My mentioning of our move caused a bit of a stir in the comment section.   A couple of more thoughts on it.  I can only speak for myself, but so far it is working out pretty well.  I like the new space.  I like my cube.  I like that everyone is fairly close together but not too close.  Our previous space was waaaaay  to big for us, and you could go weeks without seeing someone from Sales or Support.  Now, we are are all in one space, and it feels more like we are one team, which of course we are.  I like that this new place is a good fit.  I like that this place is significantly more appropriate and significantly less expensive that our previous space.  I like that this place is a new start. I like that we have a gigabit network.  I like that we have projectors hanging from the ceilings in the conference rooms.  I like that it is closer to the shopping mall across the street. But most of all, I like that it represents a significant investment in and commitment to our team. So for me, this is a big win. Share This | Email this page to a friend
Read More

Simplify your Delphi Code using some basic rules, OO techniques and some refactoring (Part 3)

Now that we have a basic idea of what we want to achieve and how we could do it, it's time to write some code and create some classes.Introduction So, basically we need a class / object which we can use to read and write some application settings from and to the Windows Registry. Sounds pretty straightforward ... but we did some additional thinking and found out that we might need to add some stuff in the future. Basic Requirements of the code Compatibility with Delphi 7 Although quite a few new features were added to the language in the last years, we won't be using them 'yet'. Our goal is that the code should compile / work under Delphi 7. You may be asking yourselves "Who going though all that trouble for an old Delphi version ?", well I noticed that even today some of my clients still have older projects which get compiled in Delphi 7. In later articles I might show you how to do it using some newer techniques, but for now lets just stick with something that will compile under Delphi 7 Not limited to the Windows Registry Although we will be writing the code based on using the Windows Registry to Load and Save our data. We want an easy way to adapt our code for some other things like storing the data in XML or an Ini file. After all, we don't know what the future will bring yet. We might be able to build applications for Windows Mobile, the Mac, iPhone or even iPad in the future (Would that be nice), and the Windows Registry might not be available on those devices. For now we will focus on the Windows Registry, but as you notice it is a good idea to keep a few other possibilities ready. In the end, the only thing we care about is that we can store / load some settings. How or where these settings are stored isn't all that important, it just needs to get done ! Additional Things So far, we know we will need something to hold a set of settings. We need to be able to load and save those settings. We will probably be supplying a name for each individual setting, maybe even a default value and a description. We need to be able to store Integers, Strings, but who knows even Passwords, Dates, ... Lets get Coding ! ... well almost ... Well, ... actually before we start coding we could take a look at how some of these things get solved in the VCL. Of course, we could do all the coding ourselves, but it might be a good idea to let our new classes inherit from some existing classes. Since we need a list of things, you might want to take a look at the TList for example. In my case, I knew I want to have a Setting which I will use to store Strings, one to store Integers and another one to store Booleans. Once I had those, I quickly noticed I wanted some others for DateTime values and quite a few other things as well. I actually ended up doing something quite similar to TField and TIntegerField, TStringField, ... So, now that I know I will be using different types of Setting objects and I want to keep a list of those Setting Objects, I quickly decided that the TObjectList was a very good class to start with. Creating the TdvSetting class The explanation Basically I need an object with a few properties like an Identifier (or Name, Caption), a Description (or Hint) and of course a Value. I will need to be able to read the value from the Registry and write it to the registry. Additionally when reading the value from the Registry I want to check if there is already something in the registry for the setting, and if nothing is found, the Default value should be used. Just as with the TField and TStringField, I want to be able to get the Value of the TdvSetting as a String or as a Variant, so I added that code as well. Additionally I want to set the value of the TdvSetting as well. In the end, the descendant classes will implement most of this, but like with the TField in the VCL, I added some code which will raise an exception if a descendant class doens't implement a specific accessor. This might sound a bit complex, but lets compare it with the TField and TStringField again. With a TStringField, you can set the value using aField.Value := theValue or with aField.AsString := theValue. Both things will work, but if aField is in instance of TField instead of TStringField an exception will be raised. What I did was implement that functionality as well. For now we will focus on the Windows Registry, but as you notice it is a good idea to keep a few other possibilities ready. In the end, the only thing we care about is that we can store / load some settings. How or where these settings are stored isn't all that important, it just needs to get done ! The Code TdvSetting = class( TObject ) private FValue : Variant; FDefaultValue : Variant; FIdentifier : String; FCaption : String; procedure SetCaption(const Value: String); procedure SetVisible(const Value: Boolean); protected function GetAsBoolean: Boolean; virtual; function GetAsDateTime: TDateTime; virtual; function GetAsFloat: Double; virtual; function GetAsInteger: Longint; virtual; function GetAsString: string; virtual; function GetAsVariant: Variant; virtual; procedure SetAsBoolean(const Value: Boolean); virtual; procedure SetAsDateTime(const Value: TDateTime); virtual; procedure SetHint(const Value: String); procedure SetAsFloat(const Value: Double); virtual; procedure SetIdentifier(const Value: String); procedure SetAsInteger(const Value: Longint); virtual; procedure SetAsString(const Value: string); virtual; procedure SetAsVariant(const Value: Variant); virtual; protected function AccessError(const TypeName: string): Exception; dynamic; procedure SetVarValue( const Value : Variant ); virtual; public Constructor Create( const aIdentifier, aCaption : String; const aDefaultValue : Variant ); virtual; destructor Destroy; override; procedure SaveToRegIni ( aRegIni : TRegistryIniFile; const aSection : String ); virtual; procedure LoadFromRegIni( aRegIni : TRegistryIniFile; const aSection : String ); virtual; procedure Clear; virtual; property DefaultValue : Variant read FDefaultValue; property AsBoolean : Boolean read GetAsBoolean write SetAsBoolean; property AsDateTime : TDateTime read GetAsDateTime write SetAsDateTime; property AsFloat : Double read GetAsFloat write SetAsFloat; property AsInteger : Longint read GetAsInteger write SetAsInteger; property AsString : string read GetAsString write SetAsString; property AsVariant : Variant read GetAsVariant write SetAsVariant; property Identifier : String read FIdentifier write SetIdentifier; property Caption : String read FCaption write SetCaption; property Value : Variant read GetAsVariant write SetAsVariant; end; ... function TdvSetting.AccessError(const TypeName: string): Exception; resourcestring SSettingAccessError = 'Cannot access Setting ''%s'' (%s) as type %s'; begin Result := Exception.CreateResFmt( @SSettingAccessError, [ Identifier, Caption, TypeName ] ); end; procedure TdvSetting.Clear; begin FValue := Null; end; constructor TdvSetting.Create(const aIdentifier, aCaption: String; const aDefaultValue: Variant); begin Create( aIdentifier, aCaption, aCaption, True, aDefaultValue ); end; function TdvSetting.GetAsBoolean: Boolean; begin raise AccessError('Boolean'); { Do not localize } end; function TdvSetting.GetAsDateTime: TDateTime; begin raise AccessError('DateTime'); { Do not localize } end; function TdvSetting.GetAsFloat: Double; begin raise AccessError('Float'); { Do not localize } end; function TdvSetting.GetAsInteger: Longint; begin raise AccessError('Integer'); { Do not localize } end; function TdvSetting.GetAsString: string; begin Result := ClassName; end; function TdvSetting.GetAsVariant: Variant; begin raise AccessError('Variant'); { Do not localize } end; procedure TdvSetting.LoadFromRegIni(aRegIni: TRegistryIniFile; const aSection: String); begin Assert( Assigned( aRegIni ), 'The aRegIni parameter should contain a TRegIni Instance' ); end; procedure TdvSetting.SaveToRegIni(aRegIni: TRegistryIniFile; const aSection: String); begin Assert( Assigned( aRegIni ), 'The aRegIni parameter should contain a TRegIni Instance' ); end; procedure TdvSetting.SetAsBoolean(const Value: Boolean); begin raise AccessError('Boolean'); { Do not localize } end; procedure TdvSetting.SetAsDateTime(const Value: TDateTime); begin raise AccessError('DateTime'); { Do not localize } end; procedure TdvSetting.SetAsFloat(const Value: Double); begin raise AccessError('Float'); { Do not localize } end; procedure TdvSetting.SetAsInteger(const Value: Longint); begin raise AccessError('Integer'); { Do not localize } end; procedure TdvSetting.SetAsString(const Value: string); begin raise AccessError('String'); { Do not localize } end; procedure TdvSetting.SetAsVariant(const Value: Variant); begin if ( VarIsNull( Value ) ) then begin Clear; end else begin SetVarValue( Value ); end; end; procedure TdvSetting.SetCaption(const Value: String); begin FCaption := Value; end; procedure TdvSetting.SetHint(const Value: String); begin FHint := Value; end; procedure TdvSetting.SetIdentifier(const Value: String); begin FIdentifier := Value; end; procedure TdvSetting.SetVarValue(const Value: Variant); begin raise AccessError('Variant'); { Do not localize } end; What does it do ? Actually this piece of code doesn't do all that much. It just provides us with a base class we can now use as an ancestor for our other classes. Basically we have some ErrorHandling and a skeleton for our specific Setting classes. Continued in Part 4 For some reason I had problems fitting everything into one single post, so I had to split it up an two parts. Go ahead and read the rest in part 4.
Read More

Nexus One: touch screens do not solve all problems

When I first learned about Android and iPhone, it was fascinating to see, but I didn't leave my old Nokia phones before I saw somebody demonstrate to me, that it was actually possible to operate these phones with one hand. My Nexus One is still mostly handled with one hand, meaning that I don't use multitouch pinch-to-zoom much, even though it is available, instead I use the zoom buttons
Read More

Windows Phone 7 – A party pack of sweets for developers

They are playing advertisements for Pascall's Party Pack on TV at the moment. The tag line is "All sweets you love, and one you can't stand." That pretty much sums up Windows Phone 7 for developers. Lots of goodness with a few things that may leave a bad taste in your mouth.Despite the name, Windows Phone 7 Series is not a successor to Windows Mobile 6.x. Instead the name is a marketing nod towards Windows 7. Although both WP7 and WM6 are based on Windows CE, there is no resemblance at a higher level. From a user point of view, WP7 is closer to the iPhone than to WM6. A more accurate name would be Windows Phone 1.After far too much time reading, watching and playing, here are my thoughts on WP7 as relates to development:Tasty:Development is done in c# and Silverlight/XNA. While native code would be nice, C# is easier to get into than Objective C. I haven't used either Silverlight or XNA previously but they look a lot more promising than the XP era Windows Forms used by Windows Mobile.The tools are much better than those for the iPhone, and freePush notifications look at lot easier to do than on the iPhoneApparently xbox live integration is really good (I wouldn't know)There are metric shitloads of tutorials, walk throughs and documentation (Twitter apps are the new hello world)Games programming looks really, really niceMeh:The tools don't work well in virtual machines. You can make it work in a Win 7 32bit VM but it will run like a dog. The tools don't work at all in Win 7 x64. If you don't want to install onto your main system, you can try booting from a virtual hard drive.Yuck:No database support (apparently it's not necessary coz you have xml and the cloud). SQL Server CE is built into the rom, but no access is provided.No multitasking (push notifications only)No access to the file system. Application files all go into Isolated Storage in the app folder.No "sideloading" of applications. Apps can only be installed from the Marketplace, or by Visual StudioNo built in file synchronisation (it's this cloud thing again). No Silverlight support in Windows Mobile 6.x (but there is Silverlight for Symbian wtf) and no Win forms support in WP7. I.e. even if your win mobile app was written in c# it needs to be rewritten for WP7.Limited APIs (no access to contacts, no sockets, ...)No native code - no c, c++, pascal etc (and thus no firefox :()I haven't felt this conflicted since the vfr 1200. Some nice things balanced by some not so nice.As mentioned, so this is essentially version 1. Some of the issues are planned to be fixed after the initial release (copy/paste, database access).LinksMicrosoftWindows Phone 7 toolsCode samplesDeveloper networkMix10 videos (check out the keynote)UI GuideSilverlightGet started in SilverlightGet started with Silverlight in WM7Example appsTwitter clientAnother twitter client (Scott Gu)LabyrinthSqlite databaseBlogsArtificial Ignorance10remMobile development
Read More

Web 2.0 programming with Object Pascal (Part 2)

As I promised in my last article, here I'll show you how to add CRUD (Create, Read, Update and Delete) operations to the sample application.The first step is to add a toolbar to the Grid, with three buttons, btnAdd, btnEdit and btnDelete in charge of Inserting, Updating and Deleting data. Also I'll create a new popup form where the user will work with that data.The new GridInstead of overwriting the files used in the last example, I recommend to create a new directory called samples2, containing all the files showed in this article.NOTE: the file grid2.html, has the same contents of grid1.html, so just copy it from the last example and paste in the new directory, and don't forget to rename to grid2.html. After that, you'll have to rename the div id "grid1" by "grid2".This is the code for grid2.js:Ext.onReady(function(){ var dataStore = new Ext.data.JsonStore({ //url: '/samples/customerslist.json', url: '/cgi-bin/customerslist', root: 'rows', method: 'GET', fields: [ {name: 'id', type: 'int'}, {name: 'firstname', type: 'string'}, {name: 'lastname', type: 'string'}, {name: 'age', type: 'int'}, {name: 'phone', type: 'string'} ] }); var btnAdd = new Ext.Toolbar.Button({ text: 'Add', handler: function(){ var win = new MyPeopleWindow(); // to refresh the grid after Insert win.afterPost = function(){ dataStore.load(); }; win.show(); } }); var btnEdit = new Ext.Toolbar.Button({ text: 'Edit', handler: function(){ var win = new MyPeopleWindow(selRecordStore.id); // to refresh the grid after Update win.afterPost = function(){ dataStore.load(); }; win.show(); } }); var btnDelete = new Ext.Toolbar.Button({ text: 'Delete', handler: function(){ Ext.Msg.confirm( 'Delete customer?', 'Are you sure to delete this customer?', function(btn){ if(btn == 'yes'){ var conn = new Ext.data.Connection(); conn.request({ url: '/cgi-bin/customerslist', method: 'POST', params: {"delete_person": selRecordStore.id}, success: function(response, options) { // refresh the grid after Delete JSonData = Ext.util.JSON.decode(response.responseText); if(JSonData.success) dataStore.load(); else Ext.Msg.alert('Status', JSonData.failure); }, failure: function(response, options) { Ext.Msg.alert('Status', 'An error ocurred while trying to delete this customer.'); } }); } } ); } }); var myGrid1 = new Ext.grid.GridPanel({ id: 'customerslist', store: dataStore, columns: [ {header: "First Name", width: 100, dataIndex: "firstname", sortable: true}, {header: "Last Name", width: 100, dataIndex: "lastname", sortable: true}, {header: "Age", width: 100, dataIndex: "age", sortable: true}, {header: "Phone", width: 100, dataIndex: "phone", sortable: true} ], sm: new Ext.grid.RowSelectionModel({ singleSelect: true, listeners: { rowselect: function(smObj, rowIndex, record){ selRecordStore = record; } } }), tbar: [ btnAdd, btnEdit, btnDelete ], autoLoad: false, stripeRows: true, height: 200, width: 500 }); dataStore.load(); myGrid1.render('grid2');});Now, the editor form:MyPeopleForm = Ext.extend(Ext.FormPanel, { initComponent: function(){ Ext.apply(this, { border:false, labelWidth: 80, defaults: { xtype:'textfield', width: 150 }, items:[ {xtype:'numberfield',fieldLabel:'Id',name:'id'}, {fieldLabel:'First Name',name:'firstname'}, {fieldLabel:'Last Name',name:'lastname'}, {xtype:'numberfield',fieldLabel:'Age',name:'age'}, {fieldLabel:'Phone',name:'phone'} ] }); MyPeopleForm.superclass.initComponent.call(this, arguments); }, setId: function(idPerson) { this.load( { method: 'POST', url: '/cgi-bin/customerslist', params: {'idperson': idPerson} } ); } }); MyPeopleWindow = Ext.extend(Ext.Window, { constructor: function(idPerson){ MyPeopleWindow.superclass.constructor.call(this, this.config); // if idPerson is not null, then edit record // otherwise it's a new record if(idPerson != null) this.form.setId(idPerson); }, afterPost: function(){ this.fireEvent('afterPost', this); }, initComponent: function(){ Ext.apply(this, { title: 'Loading data into a form', bodyStyle: 'padding:10px;background-color:#fff;', width:300, height:270, closeAction: 'close', items: [ this.form = new MyPeopleForm() ], buttons: [ { text:'Save', scope: this, handler: function(){ this.form.getForm().submit({ scope: this, url: '/cgi-bin/customerslist', method: 'POST', // here I add the param save_person // to let the cgi program decide // a course of action (save person data in this case). params: {'save_person':'true'}, success: function(form, action){ // on success I just close the form this.afterPost(); this.close(); }, failure: function(form, action){ Ext.Msg.alert("Error","There was an error processing your request\n" + action.result.message); } }); } }, { text:'Cancel', handler: function(){this.close();}, // important!, without "scope: this" // calling this.close() will try to close the Button!, // and we need to close the Window, NOT the button. scope: this } ] }); MyPeopleWindow.superclass.initComponent.call(this, arguments); } });That's all for the UI part. Now let's create our new customerslist.pp file, containing all the data required for the CGI application.program cgiproject1;{$mode objfpc}{$H+}uses Classes,SysUtils, httpDefs,custcgi, // needed for creating CGI applications fpjson, // needed for dealing with JSon data Db, SqlDb, ibconnection; // needed for connecting to Firebird/Interbase;Type TCGIApp = Class(TCustomCGIApplication) Private FConn: TSqlConnection; FQuery: TSqlQuery; FTransaction: TSqlTransaction; procedure ConnectToDataBase; function GetCustomersList: string; function GetCustomer(AIdPerson: string): string; procedure FillJSONObject(AJson: TJsonObject); function SavePerson(ARequest: TRequest): string; function DeletePerson(ARequest: TRequest): string; Public Procedure HandleRequest(ARequest : Trequest; AResponse : TResponse); override; end;procedure TCGIApp.ConnectToDataBase;begin FConn := TIBConnection.Create(nil); FQuery := TSqlQuery.Create(nil); FTransaction := TSqlTransaction.Create(nil); with FConn do begin DatabaseName := 'TARJETA'; UserName := 'SYSDBA'; Password := 'masterkey'; HostName := '192.168.1.254'; Connected := True; Transaction := FTransaction; FQuery.Database := FConn; end;end;procedure TCGIApp.FillJSONObject(AJson: TJsonObject);begin AJson.Add('id', TJsonIntegerNumber.Create(FQuery.FieldByName('IdCliente').AsInteger)); AJson.Add('firstname', TJsonString.Create(FQuery.FieldByName('Apellido').AsString)); AJson.Add('lastname', TJsonString.Create(FQuery.FieldByName('Nombres').AsString)); AJson.Add('age', TJSONIntegerNumber.Create(FQuery.FieldByName('IdCliente').AsInteger)); AJson.Add('phone', TJsonString.Create(FQuery.FieldByName('TelFijo').AsString));end;function TCGIApp.GetCustomersList: string;var lPerson: TJSONObject; lJson: TJSONObject; lJsonArray: TJSONArray;begin (* Query the database *) FQuery.Close; FQuery.Sql.Text := 'select * from clientes'; FQuery.Open; FQuery.First; lJsonArray := TJSONArray.Create; lJson := TJSONObject.Create; try while not FQuery.Eof do begin lPerson := TJSONObject.Create; fillJsonObject(lPerson); FQuery.Next; (* Fill the array *) lJsonArray.Add(lPerson); end; (* Add the array to rows property *) lJson.Add('rows', lJsonArray); Result := lJson.AsJSON; finally lJson.Free; end;end;function TCGIApp.GetCustomer(AIdPerson: string): string;var lPerson: TJSONObject; lJson: TJSONObject;begin (* Query the database *) FQuery.Close; FQuery.Sql.Text := 'select * from clientes where IdCliente=' + AIdPerson; FQuery.Open; FQuery.First; lJson := TJSONObject.Create; try lPerson := TJSONObject.Create; fillJsonObject(lPerson); (* Add the array to rows property *) lJson.Add('success', 'true'); lJson.Add('data', lPerson); Result := lJson.AsJSON; finally lJson.Free; end;end;function TCGIApp.SavePerson(ARequest: TRequest): string;var lId: string; lFirstName: string; lLastName: string; lPhone: string; lSql: string;begin lId := ARequest.ContentFields.Values['id']; lFirstName := ARequest.ContentFields.Values['firstname']; lLastName := ARequest.ContentFields.Values['lastname']; lPhone := ARequest.ContentFields.Values['phone']; if lId '' then lSql := 'update clientes set ' + 'nombres = ''' + lLastName + ''', ' + 'apellido = ''' + lFirstName + ''', ' + 'telfijo = ''' + lPhone + ''' where idcliente=' + lId else begin lSql := 'insert into clientes(IdCliente, Nombres, Apellido, TelFijo) ' + 'values(Gen_Id(SeqClientes, 1),''' + lFirstName + ''', ''' + lLastName + ''', ''' + lPhone + ''')'; end; try FQuery.Sql.Text := lSql; FConn.Transaction.StartTransaction; FQuery.ExecSql; FConn.Transaction.Commit; Result := '{''success'': ''true''}'; except on E: Exception do Result := '{''message'': "' + E.message + '"}'; end;end;function TCGIApp.DeletePerson(ARequest: TRequest): string;var lId: string;begin lId := ARequest.ContentFields.Values['delete_person']; try FQuery.Sql.Text := 'delete from clientes where idcliente=' + lId; FConn.Transaction.StartTransaction; FQuery.ExecSql; FConn.Transaction.Commit; Result := '{''success'': ''true''}'; except on E: Exception do Result := '{''failure'': ''Error deleting person.''}'; end;end;Procedure TCGIApp.HandleRequest(ARequest : TRequest; AResponse : TResponse);var lIdPerson: string;begin if ARequest.ContentFields.Values['delete_person'] '' then begin AResponse.Content := DeletePerson(ARequest); end else if ARequest.ContentFields.Values['save_person'] '' then begin AResponse.Content := SavePerson(ARequest); end else begin lIdPerson := ARequest.ContentFields.Values['idperson']; if lIdPerson '' then AResponse.Content := GetCustomer(lIdPerson) else AResponse.Content := GetCustomersList; end;end;begin With TCGIApp.Create(Nil) do try Initialize; ConnectToDatabase; Run; finally Free; end;end.To compile this file, I use a simple Bash script, that copies the compiled program to /var/www/cgi-bin directory, where my Apache2 is configured to host executable CGI programs.#!/bin/bashfpc -XX -Xs -b -v ./customerslist.ppcp ./customerslist /var/www/cgi-binIn this article, I showed how to create an HTML page containing a grid, with a toolbar that allow CRUD operations. After I published my last article, some of you told me that this was too much work for showing just a simple grid on a web page, and I agree, but when you start adding complexity to the application, the effort needed to add features is marginal, and the separation of concerns used here allows to use better ways to speed up the code creation. I mean, instead of using a simple text editor to create the ExtJs code as I did (well, VIM is not a simple editor), you could try the new ExtJs Designer, and for the Object Pascal part, it is very easy to replace it with higher level frameworks, like WebBroker or DataSnap.Some of you asked why I didn't use ExtPascal in this article. I didn't use it because I wanted to show all the parts involved in an ExtJs application (HTML, JS, CGI App), and ExtPascal creates the JavaScript code automatically hiding those internals to the programmer, I think it is very powerfull for programmers who already know how to work with plain ExtJs, and after this article, you already know how it works, so now I can write about ExtPascal!.Here are the files for this article.What's next?Before writing about ExtPascal, I'll show you how to replace the CGI part by a DataSnap Server application. See you in my next post!.
Read More

Random Thoughts on the Passing Scene #149

If you are a registered user of Delphi 2010, you can now download a digital copy of Marco Cantu’s new book, Delphi 2010 Handbook. (From Marco’s Site: “The book covers all the new features of Delphi 2010 for Win32, from Extended RTTI to new IDE features, from Windows 7 support to the improved DataSnap architecture. This is a brand new book, there is no overlapping material with the Delphi 2007 Handbook and Delphi 2009 Handbook (which you can consider buying along with this book in printed or electronic format).”) I think you all know by now how great Marco’s stuff is.  You can also order a hard-copy of the book as well from Marco’s site.  (Marco now is using CreateSpace as his publishing center.  The CreateSpace guys are actually here in our current building – I play basketball with a couple of the guys that work there….) Anders apparently can’t control himself and has put a few more items up for auction, including an autographed by Allen Bauer copy of Delphi 1. Michael Swindell sent me this link today:  The Secret Origin of Windows or as he called it “How Turbo Pascal Shaped Windows 1.0”.  Read and enjoy. Share This | Email this page to a friend
Read More

Time to say “Goodbye“

It‘s for me time to say goodbye ... don‘t worry, not to Delphi! I‘m a long time user of Rave Reports and a TeamNevrona member since 2002. Many know me from the newsgroups or met me on conferences and so on. During those years, I gave more than 50 trainings and a lot of reporting-sessions on conferences in Europe and the US about Rave Reports. I really love the power of RAVE Reports, but must admit that Rave has a high learning curve for developers who come from another report engines. In my trainings I tried to open „the door into Rave“ and I got a lot of positive feedback over the years from many attendees. All my reports in my applications use Rave and customers are happy with the performance and quality. But I noticed (like many others, I get every weeks many emails from Rave-users all over the world ) that it seems that Nevrona Design does not have the power to publish a stable Rave8 version or a BE-version for Delphi 2009 / 2010 which I can use in a production environment with complex reports ... (I don‘t talk about a new IDE, which they have announced in 2007, I mean only the support of Unicode and so on, the IDE is another story which I don‘t comment upon right now).It is unfortunate....I‘ll move on to a new reporting engine with my change to Delphi 2010. But believe me, it‘s not an easy way to take, however, I see no alternatives at the moment. A lot of developers must stay with Delphi 2007 or older because they need a stable Rave BEX with their additional components or customized sources, like me. More than two years we are anticipating the release of Rave 8, yet Nevrona Design has not published a stable version. I‘m in the situation to switch to Delphi 2010 with the really cool Datasnap-Framework and I need a stable reporting engine. I‘ve ordered FastReport 4.9 and have made my first steps in FR in the last weeks... I‘ll not update this blog in the future, only one new thread about my new blog will be coming in the next couple of weeks. The content of the new blog will be focused on Delphi & SQL-language & Database development (well, I‘m a database-junkie since over the last 20 years now). stay tuned...
Read More

April 12-13, 2010: Delphi / DataSnap Development Masterclass in UK

This 2-day Delphi 2010 / DataSnap 2010 masterclass in Upavon, Wiltshire, UK on April 12-13, 2010, is organised together with the UK Developers Group, featuring Bob Swart as presenter, and currently open for bookings (we have some registrations already and limited space, so don't wait too long to book your own place ;-)). The first day of the masterclass covers new features in Delphi since 2007, using Windows 7 (because some of the features require Windows 7), the second day is a fully hands-on day using DataSnap.
Read More

Delphi 2010 RTTI Contexts: how they work, and a usage note

Delphi 2010 includes extended support for RTTI, also known as run-time type info or reflection. Many design approaches that have previously only been possible with managed languages like C# and Java because of the code annotation and introspection they required should now be possible in the Delphi world. Something somewhat interesting about how the RTTI works is its approach to object pools. Delphi isn't a garbage collected language, so users need to be careful to free objects when they're no longer needed, either explicitly, or by designing or using some kind of ownership scheme, such as that used by TComponent, where the Owner takes care of destruction. Type information usage scenarios don't mesh particularly well with a TComponent-style of ownership. Typically, when working with RTTI, you want to do some kind of search for interesting objects, do something with them, and then go on your way. That means that many objects may get allocated for inspection, but not actually be used. Managing those objects' lifetimes independently would be tedious, so a different approach is used: there is a single global RTTI object pool. While there is at least one RTTI context active in the application, this object pool keeps all its objects alive. When the last context goes out of scope, the objects get freed up. The pool management works by using a Delphi record that contains an interface reference. The first time any given RTTI context is used, it fills in this interface reference. It can't fill it in any later than first use, because Delphi records don't support default constructors, which besides have their own problems. For example, how do you handle exceptions in default constructors, in all the places they can occur? Allocating arrays, thread-local variables, global variables, global variables in packages, temporary objects created in expressions, etc. It can get ugly, and in C++ it sometimes does. So, this first use allocates an interface, called a pool token. It acts as a reference-counted handle to the global object pool. For so long as this interface is alive, the the global object pool should stay alive. Even if the RTTI context is copied around, Delphi's built-in interface handling logic, designed along COM principles, will ensure that the interface doesn't gets disposed of prematurely or get its reference count muddled up. And when an RTTI context goes out of scope, either by being a local variable in a function that is exited, or a field in an object that is freed, the reference count is reduced. When it hits zero, the pool is emptied. The biggest upside of this approach is that RTTI usage should feel reasonably cheap, conceptually speaking. Code need only declare a variable of the appropriate type, and start using it: procedure Foo; var ctx: TRttiContext; t: TRttiType; begin t := ctx.GetType(TypeInfo(Integer)); Writeln(t.Name); end; A downside, however, is that lazy initialization can create a gotcha. Imagine this scenario: Library A declares an RTTI context A.C User code B declares an RTTI context B.C B pulls some RTTI objects O out of B.C, in order to hand them to library A B.C goes out of scope Library A now tries to work with O, but discovers much to its surprise, that the objects have been prematurely disposed, even though A already has an RTTI context, A.C The problem is that A never used A.C, so it never allocated a pool token. When B.C used its context, the pool came into being, and objects O were assigned to it; but after B.C went out of scope, the objects got freed. The solution to this problem is for Library A, knowing that it has a long-lived RTTI context and it expects to communicate with third-party code which allocates objects from its own RTTI context and hands them back, it should ensure that the long-lived context's pool token is allocated. A trivial way to do this is like this: type TFooManager = class FCtx: TRttiContext; // ... constructor Create; // ... end; constructor TFooManager.Create; begin FCtx.GetType(TypeInfo(Integer)); // ... end; This will allocate only a bare minimum of RTTI objects, those needed to represent the type System.Integer, but more importantly, will ensure that FCtx has a pool token and will keep the global RTTI pool alive. In future releases of Delphi, the static method TRttiContext.Create will make sure that its return value has a pool token allocated; currently, it does not. TRttiContext.Create was originally defined to make the TRttiContext record feel more like a class for people unfamiliar with the idiom of using interfaces for automated deterministic lifetime management. The corresponding TRttiContext.Free method disposes of the internal pool token, and should remain the same.
Read More