Tuesday, December 29, 2009

The latest bright idea

Today I was leafing through Alan Cooper's "About Face - the essentials of user interface design". I bought this book years ago - the cover bears the proud sticker "Covers Windows 95" - and I try to read it every few years. I see that the book is now in its third edition, but the ideas presented in the original edition are just as valid now as they were 14 years ago.

Cooper pours much criticism on the overuse of modal dialog boxes, especially those which report on some error. What really annoys him is when a dialog box pops up to inform the user, and the user has to press OK! Here is the paragraph (page 131 in my edition, from chapter 11) which caught my imagination today:

When the program has information or feedback for the user, it has several ways to present it. The most common method is to pop up a dialog box on the screen. This technique is modal: it puts the program into a mode that must be dealt with before it can return to its normal state, and before the user can continue with his task. A better way to inform the user is with modeless feedback.

In the management program which I have just completed (for the time being), there are several dialog boxes which the user fills in. This is a justifiable use of the modal dialog box. When the user presses the 'ok' button, telling the dialog that the data can be entered into the database, a few checks are made, ensuring that key fields have values in them (for instance, a field holding a therapist's name should not be left blank). The check itself is very easy, but should the field be empty, a modal dialog box pops up, telling the user that the field is indeed empty. This is exactly what Cooper was writing about.

How can the program provide modeless feedback? The solution is actually very simple: add a statusbar to the dialog; the statusbar will normally not contain any text and its colour will be neutral.  When the 'ok' button is pressed and an empty text field is found, the error message is displayed on the statusbar, whose colour has now been changed to red. At the same time, the focus returns to the empty edit box. As soon as the user presses a key while the focus in on the edit box, the statusbar reverts to its original state. This solution requires a variable declared global to the form, which I've called 'errflag'; initially its value is false. Here is the specific code:

procedure TEditTherapist.OKBtnClick(Sender: TObject);
begin
 if dbedit1.Text = '' then
  begin
   with sb do
    begin
     simpletext:= 'You must enter a value for this field';
     color:= clRed;
     errflag:= true;
    end;
   dbedit1.setfocus;
   modalresult:= 0;
   exit
  end;
end;

procedure TEditTherapist.DBEdit1KeyPress(Sender: TObject; var Key: Char);
begin
 if errflag then
  with sb do
   begin
    simpletext:= '';
    color:= clSkyBlue;
    errflag:= false
   end
end;

Monday, December 28, 2009

What is the connection between King Crimson and Organisational Behaviour

The other night, we learnt about the Expectancy Theory of Vroom. Maybe Robert Fripp and his merry men were thinking of this when they released the "Vroom"  mini cd?

A new Word automation technique

Often in my Word automation code, I need to create a table in which to display data. To the best of my knowledge until now, there have been two ways to create and populate a table; in the first method, one declares a table (wrdTable:= WrdDoc.Tables.Add (wrdapp.selection.Range, 10, 5, 1, 2)), which is then populated by accessing specific cells in the table. The other method is to create a huge string in which the cells are separated by a specific character (for example, ^); after the huge string is created, the ConvertToTable method is called which converts the long string into a table.

Both methods have their problems: the first requires that the number of rows be known before the table is declared, whereas the second does not always work (maybe one day I should investigate this further, although I should point out that one time the code worked perfectly on my computer, but not on my client's computer, despite the fact that we have the same operating system and same version of Word).

Today I had to write Word code to populate a table, not knowing in advance how many rows would be in the table. Which horn of the dilemma should I choose? At first, I tried the ConvertToTable method, and as usual, it didn't work. So I used the first method. In the past, I have used a query inside the Delphi program to calculate how many rows would be returned from the real query, but this doesn't seem to be the optimal approach. Remembering that if one creates a table in Word, one can always add rows to it using the 'Add row' command. So I ran a quick macro check in Word, and discovered that there is a 'InsertRowsBelow' method belonging to the current selection.

So my code became (in pseudocode):
create a table of size one row and how ever many columns
insert the row headers into the first row
for every tuple returned from my query,
 add a row to the word table
 insert into this row the various fields of the tuple
until there are no more tuples
Voila! A perfectly sized table. Here is the Delphi code to do the above:      
         wrdTable:= WrdDoc.Tables.Add (wrdapp.selection.Range, 1, 5, 1, 2);
         wrdSel.paragraphformat.alignment:= wdAlignParagraphRight;
         wrdTable.cell (1, 1).range.text:= 'column 1';
         wrdTable.cell (1, 2).range.text:= '
column 2';
         wrdTable.cell (1, 3).range.text:= '
column 3';
         wrdTable.cell (1, 4).range.text:= '
column 4';
         wrdTable.cell (1, 5).range.text:= '
column 5
';
         row:= 1;
         while not eof do
          begin
           inc (row);
           wrdSel.InsertRowsBelow (1);  // add new row
           wrdTable.cell (row, 1).range.text:= qYomanTaarich.asstring;
           wrdTable.cell (row, 2).range.text:= qYomanForename.asstring;
           wrdTable.cell (row, 3).range.text:= qYomanSurname.asstring;
           wrdTable.cell (row, 4).range.text:= qYomanName.asstring;
           wrdTable.cell (row, 5).range.text:= qYomanTherPrice.AsString;
           next
          end;

         wrdTable.borders.insidelinestyle:= true;
         wrdTable.borders.outsidelinestyle:= true;
         wrdTable.AutoFitBehavior (wdAutoFitContent);
         wrdTable.cell (row, 1).select;
         wrdSel.MoveDown (wdLine, 3); // Get out of the table
 
I suppose that the next step is to go over all my 'legacy' code and update those tables in which I don't know in advance how many rows there will be. This type of coding could be referred to as 'overhead' or 'excise', and a case could be made that my client should not have to pay for this. Fortunately, I think that there only two or three cases, so it shouldn't take very long to correct them. I often discover a new technique and then retrofit it into existing programs. It makes my maintenance easier, and generally improves the programs' performance.

Sunday, December 27, 2009

Dollhouse 2

Despite writing previously that I wouldn't watch the program again, I was drawn to it like a moth to a light. True, the first few episodes weren't very good, but around the fifth episode things started to get better, and indeed there has been a continual improvement ever since (except for the current episode, the 10th, which was a return to the opening episodes).

Most television programmes could be described as moderately good ideas executed well; occasionally there is the really good idea executed well, in which case the result is excellent. Unfortunately, "Dollhouse" seems to be a good idea executed poorly.

I think that the programme can't decide what it's supposed to be. Is it a run of the mill adventure series centered on "programmed people" ("actives")? Is it a thriller about an FBI agent trying to find the Dollhouse? Is it a philosophical programme about the morality of "programming" people, taking away their identity and establishing new albeit temporary ones?

When the programme is an adventure series, then it is dull and uninteresting. When it's a thriller, it's more interesting (especially when the agent's girlfriend suddenly turned out to be an active herself, a possibility which occurred to me long before it occurred to the FBI agent, and just before it was shown to be true), but it's at its best when it's working on the philosophical level. Unfortunately the proportions of screen time seem to be about 60%, 30% and 10% (an unscientific observation, more of a guess).

Only three more episodes until the end of the series ... I wonder whether there will be any form of resolution. Apparently there is a second series, after which the show was cancelled.

Saturday, December 26, 2009

Neat tricks in the 'management' program

I wrote about two weeks ago about a management program which I am developing for my weekend job. This program incorporates many techniques which I've never used before, and I want to talk about two of them here. The use of the ttabcontrol is such a first, as was the use of the tdbnavigator, although that has now been retired and has been replaced with a tdbgrid.The navigator control was causing more problems in terms of ease of use than it was solving, and doing away with it enabled me to remove three dialogs from the program, making it simpler and smaller.

The first technique which I want to share here is the solution to a problem which has plagued me since I started working with the dbExpress components and the Firebird database. In order to develop the program, the data module contains an TSQLConnection component which holds data regarding the location of the database. This component has to be left open during development time, but I aways had to remember to remove the database location from the component before installing the program on my client's computer. I found the other day a better discussion of the problem, and the article cited also purports to give a solution.

I have learnt a great deal about Delphi programming from Zarko Gajic's 'About Delphi' site, but I have to say that in this case, he is wrong - or at least, his solution does not work. There was enough information given which enabled me to find a working solution, which is what I present here.

The source problem is that the sqlconnection gets opened almost immediately on program invocation, even before code in the project file (dpr) executes. Thus any attempt at correcting or changing the connections' properties post facto are doomed to failure. What Gajic gets right is that the sqlconnection component's "BeforeConnect" event has to be hooked into. This is code which - as its name suggests - gets called before the connection to the database is opened. Gajic's solution involves creating a new constructor and various boolean fields, but seems to be lacking a line or two, without which the component does not know that it has to change its properties. A much simper solution follows, which does not involve private constructors nor any fol-de-rol. One simply has to write an event handler for the 'BeforeConnect' event as shown below:
procedure TDM.SQLConnection1BeforeConnect(Sender: TObject);
var
 dir: string;

begin
 with TRegIniFile.create (regpath) do
  begin
   dir:= ReadString ('firebird', progname, '');
   free
  end;

 with sqlconnection1 do
  begin
   close;
   params.values['database']:= dir;
   loginprompt:= false;
  end;
end;
I keep the database locations in an .INI file; this is what is being retrieved in the first half of the code (with TRegIniFile.create ...). The second half (with sqlconnection1 do ...) is where the action happens; the first statement closes the connection, which presumably was left open at design time. The second statement inserts the new location of the database, and the third statement is optional - sometimes I forget to set the loginprompt property. This simple code solves the problem! After this event fires, Delphi will open the connection, and as now the database location is correct, the database will open correctly. 

I hate to think how much time I have wasted because of this problem. Several times I have installed my firebird programs with the sqlconnection open and pointing to the wrong location, causing embarrassment and lost time when I have tried to demonstrate the programs. This is one technique which will soon be migrating to other programs!

The second technique is to do with non-modal dialogs. Until now, I have never used such dialogs, preferring instead to use their modal siblings. Whilst I was aware that maybe the modal dialog is not the correct solution to every problem, it seemed much easier (from a user's point of view). The management program is the first program that I have written that uses non-modal dialogs seriously. There are several tables containing data which could be classed as 'secondary' - customers, therapists, types of activity and so on. These tables are accessed rarely after the data have been defined, but they still have to be accessible. I chose to define a non-modal dialog which allows data entry for all of these tables; one (non-modal) dialog displays all the names of the various records for all the tables, and then specific (modal) dialogs allow editing of all the chosen record's fields. 

I was asked how one could display two such dialogs at the same time. No problem, I replied, as I swiftly displayed one dialog, clicked on the main dialog, then displayed a second dialog. But wait a minute: where had the first dialog gone? I knew that I hadn't closed it, but I couldn't see it anywhere. It turns out that it had "hidden itself" behind the main dialog and thus to all intents and purposes had disappeared. After thinking about it, I realised that I had to write an event handler for an 'OnDeactivate' event (if such an event existing), minimising the dialog. I assumed that if there were an 'OnActivate' event (one which used to be ubiquitous with me, but now I don't use it, preferring 'OnShow' instead as this gets called only once in a program's lifetime), then there should be an 'OnDeactivate' event. Indeed there is, and the implementation is simple. 

procedure TDoTables.FormDeactivate(Sender: TObject);
begin
 showwindow (handle, sw_minimize)
end;

Although I promised only to write about two techniques, here is a third which I have started to use. Frequently, my programs' main dialog would be defined as a modal dialog box; such a form cannot be resized which is what I wanted. Unfortunately, this also meant that such dialogs cannot be minimised; I developed a technique of defining the F1 key as a 'minimise key', trapping this keypress and minimising the dialog. A much better solution is to define the form's borderstyle as 'bsSingle' and to set the bordericons property as [biSystemMenu, biMinimize]. 

I know that these are techniques which I should have absorbed ten years ago, but at the time there was so much material to assimilate that these little points got missed. Programs got written, they worked and they even solved problems, so what is basic Windows behaviour was ignored. Better late than never.....

Friday, December 25, 2009

Tim Hart, RIP

Christmas Day in the workhouse.

I heard today about the death of Tim Hart, who played guitar and sang in most of the lineups of Steeleye Span. Tim died at the age of 61 after a long battle with cancer. No doubt there are other tributes about him scattered elsewhere around the Web , but this is going to be a more personal memoir.

As one recalls, Steeleye was the band formed by Ashley Hutchings after he left Fairport Convention at the the end of 1969. A first lineup, including Tim Hart, made the 'Hark the village wait!' record, but this lineup split before the record was released. A second lineup, which added Martin Carthy and Peter Knight, was more long-lived, and it was this group that I saw in Bristol's Assembly Rooms in early 1971. All that I remember about this is Hutchings' blue Fender Mustang bass and Carthy playing a Telecaster. 

I knew quite well the two records released by this lineup, "Please to see the King" and "Ten man mop", although I never purchased them (a mistake rectified 35 years later when I bought a two cd compilation of their first three albums titled "The Early Years"). There was something about their sound which both attracted and repelled me. I used to call it "severe", but by chance I heard this morning a similar sounding adjective which seems more suitable - "austere". Maybe it was the lack of drums, maybe the lack of a distinguished instrumentalist (pace Richard Thompson), maybe it was a surmised lack of humour or fun. It wasn't too clear what Hart's instrumental function was, apart from the occasional turn on the electric dulcimer (the chilling "When I was on horseback").

My second meeting with the band was at the Lacock Festival, held in a small village "the other side" of Bath in May 1972. I went with my school friend Jonathan along with someone else whose name I can't recall (actually, I don't remember who the third person was at all, let alone his name). Maybe my mother or maybe one of Jonathan's parents took us to the village on the Friday afternoon, and I remember that we returned by bus. Steeleye were 'topping the bill', but arrived early at the village and hung out for a day before their show - it was an informal festival. There were singing and dancing workshops - I remember appearing at one of the open mic shows with my ratty acoustic guitar and spotting to my mortification Maddy Prior (lead singer with Steeleye) sitting in the front row. 

At some stage Jonathan and I approached the group and informed them that we had appointed ourselves as their official groupies (lackeys would be a better term) for the duration of the festival. I don't recall now what this function consisted of besides buying them the occasional drink. We were too cool to take mutual photographs or even ask for autographs, so all that is left is my failing memory. By this time, Bob Johnson and Rick Kemp had replaced Martin Carthy and Ashley Hutchings, and Johnson certainly brought a more fun approach to the group's music. 

This was when I first learnt how to Morris dance. At the time, I was fairly well versed (so I thought) in Israeli folk dancing, and had become aware of the fact that there was a vocabulary of dance moves; later on, dancing became a test of memory in which one had to recall the specific sequence of moves (I call them 'phonemes' for the moment, in imitation of spoken language) instead of letting the body react automatically. Anyway, I was sufficiently aware of the possibility of assigning a 'grammar' to the dance that I was able to distinguish the 'phonemes' of the Morris dance and catch on quite quickly. Let us not forget that this was maybe six or seven months after Ashley Hutchings' (again!) groundbreaking "Morris On" record, which taught polite public schoolboys like myself that there was a whole branch of English music out there of which we had never heard.

At the time I considered myself to be a folkie, what with the occasional visit to the Bristol Troubadour (mentioned elsewhere on this blog) and my connections to the Village Thing label. I vowed to find a Morris Dancing team in Bristol on my return; I did in fact find out where they met but never went. I did buy a concertina, though, and even learnt after a fashion how to play it, although tunes escaped me.

I saw Steeleye for the third and final time in 1973 at the Colston Hall; I have somewhere a cassette tape of this performance. This was the time between "Below the Salt" and "Parcel of rogues"; the music was much less austere than it was before - but still I didn't buy the records. 

When I came back to Britain in 1974 and went to live in London as a student, I bought Steeleye's "Now we are six" album as a raffle prize for an unsuccessful folk club which I was helping to run. I listened to it, probably taped it, and forgot about it. Maybe my tastes had changed (I certainly didn't consider myself an English folkie after spending a year in Israel!); whatever the reasons, Steeleye and I parted company.

It's interesting to note how much the repertoire of the early Steeleye stayed with me, and for that I have to thank Tim, Maddy, Ashley, Martin and Peter. When I came to record my 'Folktronics' disc a few years ago, I was surprised how many Steeleye tunes came to mind, even though at the time I hadn't heard the records for years (I should point out that all the tunes were traditional; I should have written "how many traditional tunes that Steeleye had brought to my attention").

Saturday, December 12, 2009

Displaying tabs on the right of a TTabControl

I started a new MBA course in 'Organisational Behaviour' yesterday; I should be more excited than I am about it at the moment. I hope that I will become more enthusiastic about it as time passes.

At the moment I'm more excited about a program which I am writing for my occupational psychologist. This program is supposed to be one which manages all non-primary information about the examinees: who everybody is, where they come from, which tests they did, who interviewed them, etc. One of the outputs of the program will be a calculation for each psychologist how many clients they interviewed during the month, and what their fee should be. The program is very much open ended at the moment, so I've configned myself to doing basic design, including a few input screens.

The program's main data structure/table is the docket, to which are connected people, to which are connected treatments (exams), to which are connected pyschologists. I made a nice ERD before I started coding, and so far I've completed everything in that design. But there'll be a lot more to come.

I went through several options before I came upon what is at the moment the final design for the main screen. Instead of displaying a dbgrid with docket numbers and dates, and possibly the names of the people connected to those dockets (utilising a left join, because there can be a docket with no people connected), I decided to take a different approach, utilising a component which I have never used before, the dbNavigator. This component allows one to page back and forth between different dockets, and I've connected a few dbText controls to display the data in the current docket tuple (a docket is never entered manually, as it only has two fields: number and date, both of which are inserted automatically).

For each docket there is a wealth of information to be displayed, although at the moment I only have two screens' worth: the people connected to the docket and the exams that those people did. After considering various options, I decided to use (for the first time) the TTabControl. There is a similar TPageControl, which allows different pages containing different controls; I tried this briefly, but didn't like it. As the the container control will hold a grid and a few buttons, it makes more sense to use the TTabControl (which uses the same child controls for all the 'pages', forcing the programmer to switch the data) as opposed to the TPageControl.

Here is a screen shot of the TTabControl in its naked form:



The TTabControl has two tabs, 'One' and 'Two', along with a grid. This would be ok if the program were working outside of Israel, but we need right to left controls. Although the TTabControl does have a BiDiMode (bidirectional) property, this controls how text appears and not the layout of the tabs. Obviously the solution is draw the tabs on the right hand side of the top of the TTabControl. Easier said than done.

I found a magic invocation which I have used before to get controls to draw themselves from right to left. Here is the result of using a little magic

Whilst the tabs are now drawn as I wanted, on the right, the grid seems to have disappeared. Thus the 'magic option' has very limited uses in the real world (such as none).

There is a property called TabAlign, which can display the tabs on the top (as I originally did), at the bottom, on the left or on the right. Is this the answer? Not really.




But we're nearly there.... All I need to do is to have the tabs draw their text horizontally, and then at least I will have a reasonable solution to what I want. The way to do this is to mark the TTabControl as owner drawn and to handle the tab drawing code by myself.



The sharp of sight will notice two things: the grid is now displayed right to left (by setting its BiDiMode property), and more importantly, the text of the tabs is wider than the tab. So the tab width has to be set manually? Not quite. If one remembers that the tabs have effectively been rotated by 90 degrees, then increasing their width effectively increases their height (at least, as how we see it). So to get the effect that I want, I have to increase their height, which will increase the onscreen width (who said that programming was easy?).

If I'm already owner drawing the tabs, why not add colour to the selected tab? Here is a screen shot of the final dialog, complete with a little data.



In case it isn't clear, there are two tabs on the right of the TTabControl: the selected tab has red text on a light blue background, whereas the tab below it has blue text on the common green background.

I have to say that I much prefer to concentrate on a program's logic, to get it to do what it's supposed to do with the minimum of effort from the user, instead of focusing on what seems to be arcane minutiae of the user interface. But sometimes there's no escaping this, especially if one works in a right to left environment. Looking back, I'm quite pleased with what I accomplished, even if it isn't exactly what I originally wanted. I hope that the client will also be pleased.....


Wednesday, December 09, 2009

After the accounting exam

Well, my accountancy exam has been and gone. It was much easier than I expected, for which I am truly grateful. The exam consisted of two sections: one part multiple choice, and one part containing two scenarios which have to be dealt with in depth. The 30 multiple choice questions covered most of the syllabus, and some were harder than I expected, but none of them were really hard. Theoretically, an hour and a half should be devoted to this section; I finished after an hour, and that included checking the answers.

The two in depth questions were much easier than I expected - or putting it another way, they were in areas which I know very well. I had been expecting a question about cash flow statements, which is a subject which I haven't grasped very well and so I have devoted a fair amount of time over the past week to solving questions in this area. My hopes were dashed - darn! - when it turned out that the first questions was simple accounting: create a balance sheet from opening balances and a series of transactions, some of which were given and some had to be deduced. Once the transactions had been recorded, one had to make a profit and loss account from them, as well as a trial balance. Solid work, but straightforward. After that, one had to comment on the financial state of the balance sheet - not very good; the company's current liabilities outweigh their assets.

The second question was even easier, if that's possible: a group of people want to put on a theatrical show which runs for a week. The theatre costs so much, the programmes cost, the theatre staff cost, etc. If the theatre seats 800 people and they expect 50% capacity on two days, 60% on another two days and 100% for the remaining performances, what is their financial status if a ticket costs 12 pounds? If a ticket costs 16 pounds? How many tickets do they need to sell to break even? I solved a very similar question to this a few months ago and the memory stayed in my mind; as it happened, one of the practice questions which I solved yesterday was a simpler version of this question (a group of students want to hold an end of term party).

Unfortunately, I won't get the results for another two months, but I am very optimistic about my chances. Actually, there are only passes and fails; a pass is 50%, and theoretically there is no difference between a pass at 51% and a pass at 90%. Maybe a future employer will want to look at the actual marks attained, but in my case, that would seem to be purely theoretical. Still, one wants to do as well as possible.

I remarked the other day about the 'fight' between the amygdalae and the cortex in the brain. Their fight was very pronounced today, with the amygdalae saying "check, check the answers", and the cortex saying that I've checked and that it's ok.

I forgot to try out a learning technique which I had read about. One of the few things which I had to memorise was the five advantages and five disadvantages of maintaining a budget, as I was convinced that there would be an in-depth question about budgets, and knowing the above ten words would give me ten marks. The technique states to place a little perfume on the back of one's hand when learning the material, and to place the same perfume on the hand when sitting the exam. It is well known that smells are a key to memories (the smell of eucalyptus trees in the damp always brings to mind Army basic training), so this perfume smell is supposed to unlock the memory of the learnt material. As I say, I forgot to try the technique, and anyway we only had a few multiple choice questions about budgets, which was really variance analysis.

Sunday, December 06, 2009

Accounting exam

The first semester of my MBA course is drawing to a close and the concluding exam is on Wednesday morning. I have been a witness for the past week to an ongoing battle between different parts of my brain: the mid-brain (probably the amygdala) keeps on saying that there is an important exam in a few days and one has to revise, revise, revise, whereas the top brain (the cortex) is calming the amygdalae down, saying that yes, there is an exam, but this is accountancy after all, a subject which I have been practising for years, and judging by my performance on the test exams, I'm going to get 75% without even trying very hard.

In the previous week (not the one just finished but the one before that), we had a guest lecturer from the home university come and give us three lessons. This is apparently a rare occurrence, which happens once every three-four years, so my fellow students and I should consider ourselves lucky. For reasons unknown to me (although I can guess), only about a third of the class attended these lectures. I think that this is a shame, because the lecturer went over test exams, taking a more tactical approach to that taken by our normal lecturer, and I found  this approach complimentary to what we had already learnt. In other words, those lectures will help me get a higher mark. The lecturer is the man who actually writes the exam questions so of course he has insight on how to solve them. He was careful not to give away which subjects will appear in the exam paper, claiming that he writes the questions two years in advance (as they have to be checked and translated) and so does not remember what will be in any specific exam. I think that there was a certain amount of truth in his statement, but felt that he probably did know what was in the paper.

I am going to take the middle course: devote an hour or two each evening to the areas in which I don't feel confident and which haven't been examined in the past four courses (the lecturer left us a list of which subjects have appeared in which exams). In my opinion, most of the areas are straight forward, and one only needs common sense aligned with mathematical sense to answer the questions. Of course, 'mathematical sense' is not shared equally between all students; for me, accountancy is easy because it aligns with my skills. It is considered one of the 'harder' subjects in the MBA constellation, both in the sense of 'hard' (as opposed to 'soft') and 'hard' (as opposed to 'easy'). Next semester I am taking the course in organisational behaviour, which is supposed to be both soft and easy. I suspect that the exam will be harder for me than the accountancy exam.

'Next semester' starts this Friday; the winter semester is short and compact. Our timetable reads as follows: Friday 4 Dec – last accountancy lecture; Wednesday 9 Dec – accountancy exam; Friday 11 Dec – first lecture in winter semester; Sunday 13 Dec – second lecture in winter semester.

Thursday, December 03, 2009

Swedish Fly Girls

Sorry about disappearing for what seems to have been all of November. I had two very busy weeks which didn't leave me any time for blogging (work and MBA), and anyway the system administrator at work has removed access to all blogspot sites, meaning that I couldn't read many of my favourite blogs at work, let alone add to my own. Although the pressure eased up this week, I managed to catch a bad cold, so I wasn't able to focus much. But now I'm back.

One of the rarer items in the Sandy Denny discography is the handful of songs which she sang for a forgotten film from 1970 called 'Swedish fly girls'. This was shown on tv last night and I had the presence of mind to record it ... only I needn't have bothered. The film has not aged well, and probably wouldn't have been well thought of even when it was released. Then it would have been considered somewhat risque, as the eponymous air stewardess (only one, and she's Danish) spent about a third of the film naked. But let's say that the nudity was non-sexual; this certainly wasn't a precursor to 'Emmanuelle'.

Sandy sings maybe ten lines in the film, although apparently the soundtrack featured four songs with her singing. Digging through the old letters from the Sandy mailing list, I found this gem from Moses Henry (grammar slightly corrected by me):

I was in London for 12 weeks the spring and summer of 1970. Manfred Mann and I co-produced the music for "Christa". The rhythm section was the band who did the London Production of 'Hair" and we used London Philharmonic strings and brass. At times a 30 piece rock orchestra for the film. I wrote all of the music, Dereck Wadsworth arranged it [and] Manfred [was the] # 1 Producer. I was associate producer without credits. I sang "I Need You" with Sandy. Sandy was ill and after she saw the lead sheets of the music she came to the studio to record it anyway, She told me 'What Will I Do With Tomorrow' was the most beautiful song she had ever heard. In my book she sang it like an angel. When she sang the final take for that recording the entire studio was lifted to another place and time words cannot describe. The recording said it well enough.

I also sang lead vocals on 'Make it to the Land that I Know', 'She is Free', and 'Love is All I Need'.

The film opened in New York in Three Theaters; I got to go to one in a Limo and I asked the owner of a theater [in] uptown New York why he had booked the film. He (not knowing I had written and sung the music) said the music touched his heart. The film titled 'Christa' did
not make enough money as that title so it was re-titled 'Swedish Fly Girls'. I know that more people saw Christa because of the new title and that is good. It will always be 'Christa' to me. I have 54 pages of lists of countries from BMI (Broadcast Music Inc.) of all the countries the film played in, some for over six years. The film played all over the earth for 18 years.

Almost every movie since then has been modeled on 'Christa'. It was the first film edited to the beat of the music and other film makers are using a lot more music in their soundtracks. We were the first to edit to the beat of the music with 'Christa'.

A wonderous project. I am still doing concerts: read 'The Miracle Concert' on my website and 'The Last Breath' film treatment. It is my true story; I,  however,  did not want the hassle of the publicity for that film project.

Let's keep touch; perhaps I will be doing a concert in your area some day. and we could put together a video salute for Sandy. She truly has the voice of an angel.

One Heart

Moses

Friday, November 06, 2009

Eliza Carthy, "Red"

Today's listening: Eliza Carthy, "Red". We were on holiday in Britain when this was released in 1998; I must have found out about it via Mojo. I consider this disc to be one of the top folk-rock records of all time, along with "Liege and Lief" and "Rise up like the sun". Traditional songs have never sounded so hip as they do here, accompanied by rhythms foreign to the songs' sources.

That holiday was very Fairport orientated: I saw them at a free concert at Gravesend a few days after arriving in Britain, spending some time with the band before their show, and met up with them a few weeks later at Cropredy. At the traditional cricket match which serves as the festival's closer, I spoke a little with Chris Leslie and recommended that he listen to Eliza's album. I don't know whether he took me up on that recommendation, but if he did, none of Eliza's perkiness made it through to subsequent Fairport records.

One of the songs on "Red" (not be confused with the King Crimson record of the same name) is "Walk away" by Ben Harper. Whilst there are a few vocal stylings which I would have deleted from this track, it's still far better than the composer's version which I heard once. The song contains the lines "If you love somebody, you have to set them free" (or similar); this seemed familiar but I couldn't place from where I knew those words.

Much later it became clear: one of the books which I was reading that holiday was Colin Dexter's "The daughters of Cain", an Inspector Morse novel. Somewhere in the novel (I riffed through the pages just now, but couldn't find the quote) is the same couplet, here expanded to six lines. Something along the lines of "if you love somebody, set them free. If they love you, they'll come back. If not, then there was never love at all". I love the synchronicity of this: hearing the couplet in a song and then reading it in a book.

One of the ladies in the book, Ms Ellie Smith, had the habit of dying her hair. For some reason, she has always been linked in my mind with Eliza Carthy: both of them date (for me) from 1998 and both had dyed hair.

"Red" actually came as part of a double set; its twin is called "Rice" and is composed of traditional tunes, this time with a traditional instrumentation. I think that I've listened to this disc maybe twice, and then not all the way through.

Wednesday, November 04, 2009

Hejira

Drove to Carmiel and back today; five hours driving in less than ten hours.

Listening material:
  • Randy Newman, "Live at the LA Largo" - a recording of the show Randy gave to introduce the public last year to his new "Harps and Angels" disc. The show has all of the H&A songs in the correct order, along with four extra songs at the end. I made a picture sleeve for the disc and intended to get Randy to autograph it last year, when he was supposed to come to Israel for a concert.
  • William Orbit, "Pieces in a modern style".
    I found this a few years ago when looking for interpretations of Ravel. His sublime "Pavane pour une infante defunte" is here, although this version doesn't contain the entire piece and so is less heart rending than other versions. The Ravel is one of the few pieces which is actually recognisable, but I find this disc to be relaxing listening.
  • Joni Mitchell, "Hejira". I remember reading a review of this record in "Melody Maker" when it was released in 1976; a friend bought a copy and I promptly taped it. I bought my own cd copy several years ago. It was a distinct pleasure to listen to the disc in the car stereo; the guitars (some of which don't sound like guitars at all) and vibraphone made pointillistic love in the late afternoon sun.

Monday, October 26, 2009

Entity relation diagrams (ERD)

These are diagrams which show database tables, the fields in each table and the relationships between tables. I've been looking on and off for a simple and free program which will display erds, and which will preferably extract the information needed automatically from a database.

Today I found such a program: IBUtils. Loading a database and displaying its structure is a bit awkward initially, but once the data is on screen, it's plain sailing.

Here is the erd for the database which I partially described the other day:

I have yet to determine whether red lines are different from green lines.

Sunday, October 25, 2009

The importance of building indexes

Or as I would say, with the benefit of a classical education, the importance of building indices.

I have written a psychological testing application, in which the user is presented with a list of words, and s/he has to choose ten words which very much describe himself, then choose words which partially describe himself, and words which do not describe himself. The application itself works fine, but I was interested in exploring the meta-data possibilities: which words have been most frequently chosen in the first category, and which words have never been chosen in the first category. The first query was not a problem, but the second (which words have never been chosen) leaves me stumped.

The table structure is as follows:
table words: id, name
table choices: pid (person id), wid (word id), class (value between 1-6)

Presumably the answer involves a left join between words and choices, but there has to be a modifying statement - where choices.class = 1 - and this is causing me problems.
I posted this question on the excellent Stack Overflow site, and received some interesting answers. One person suggested the following query:

SELECT name
FROM words
WHERE id NOT IN
(SELECT DISTINCT wid  
FROM choices
WHERE class = 1)

When I ran this query, it took a staggeringly long 36 seconds to produce the answer! I had been using a two query strategy which took maybe one or two seconds, so to receive a pure SQL answer which took much longer seemed beside the point. When I responded, telling how long the query was taking, it was suggested that I add an index on the fields (wid, class) to the choices table.

Once I did this, the execution time dropped to 62 milliseconds. Yes, that's right: 580 times faster!! Later someone suggested this query

SELECT name
FROM words
WHERE NOT EXISTS
(SELECT 1
FROM choices
WHERE choices.class = 1 and choices.wid = words.id)

I don't know how long this would have taken without the index, but with the index it took only 31 milliseconds. When running the program, it seems that the data appears before they have been requested. This query is fascinating in its simplicity and I shall try to remember it. It's actually the same syntax which is used in a report at work to discover parts which don't have a bill of materials.

So: it's very important to index fields which are used in a query. Armed with this knowledge, I checked a few other queries, both in this application and in our flagship app. After adding a few indices (all right, indexes), one query became three times faster and another twice as fast.

Even without the speed-up gained by the index, there is another point to be considered: the algorithm. In one part of the program (effectively the most important part, where the user's results are displayed, so speeding this part up is much more important that improving a non-essential query), I was calling the same query in a loop which ran six times (each loop invocation would call the query with a different parameter). It wasn't too difficult to see that I could remove the loop, call the query once for all the data, and then massage the returned data in code.

My attitude has always been to get the program running first with simple code and correct results; sometimes I forget to revise the program and substitute more complicated code which runs much faster.

Saturday, October 24, 2009

Wizz Jones: The legendary me - and musings on music samplers

I wrote some time ago about my musical adventures in 1971, a year which in retrospect was very important for the formation of my musical tastes. In that entry, there was a reference to local (to Bristol) folk artist Dave Evans; Dave recorded for a local record company which has since apparently become legendary, "The Village Thing". They had an office close to my school, and during lunch-hours I often used to go to that office.

The first release that I purchased from TVT was a four track mini-single called "The Great White Dap" (a dap is the primordial ancestor of what is now called a trainer, ie a soft shoe which was worn in sporty events). This featured one song each from the first VT releases: Ian A. Anderson, The Pigsty Hill Light Orchestra (somewhat similar to The Temperance Seven or The Bonzos), a Welsh duo called 'The Sun Also Rises' and guitarist Wizz Jones.

Jones' contribution was called "See how the time is flying", which I learnt to play in short order. This was always an intriguing song harmonically, as its verse starts in Dm and ends in Em. Instead of trying to make a harmonically pleasing transaction between these keys, Jones simply starts the next verse with an abrupt change to Dm.

Later on, TVT released a sampler lp with twelve tracks, including Jones' "Beggarman". Again, I enjoyed this song, but apparently not enough in order to buy its parent album, "The legendary me". Obviously my money at the time had more important claimants.

Fast forward maybe thirty five years; money is no longer a problem and anyway my record buying days are virtually over. I began a search for those Village Thing records of my youth, and quickly found Dave Evans' initial record which I wrote about in that blog entry, "The words inbetween". Lately I have found as downloads Hunt and Turner's "Magic Landscape" and Ian A. Anderson's "A vulture is not a bird you can trust". I have yet to find its predecessor, "Royal York Crescent", named after the street where Anderson lived at the time, and where he recorded some of Evans' songs. I used to visit him there.

- digression
I think that Jim Hunter, my English teacher at school, lived there as well, and he told me that another resident at the time was novelist Angela Carter, whose early book "Several perceptions" had become essential reading for me, and even the basis for an exercise in turning prose into poetry. I realised that the district in which Carter's book was set was indeed the arty Clifton district of Bristol, where the above crescent is situated. Hunter wrote a biography of poet Gerard Manley Hopkins, which led to the school graffito "Hunter puts the man into Gerard Manley Hopkins".
-end of digression

For several years, I have been on the trail of a cd release of Wizz Jones' "The legendary me", and eventually found it a few weeks ago. I've been listening to it on and off the past week, and reluctantly have come to the conclusion that the two songs which I knew in 1971 were the highlights of the disk, and that had I heard some of the other songs, I wouldn't have bothered tracking down this disk. In certain aspects, Jones might have been a role model for my music - clean acoustic guitar picking, roughish vocals - had my harmonic vocabulary not been irrevocably twisted by Sandy Denny's "North Star Grassman" in the autumn of 1971.

In the early 1970s, the sampler was a legitimate way of allowing impecunious people the chance to hear a wide variety of artists signed to a label and so inform them of the quality of those artists. The Island samplers ("You can all join in", "Nice enough to eat", "Bumpers" and "El pea") immediately come to mind, but there were also successful samplers from CBS ("The rock machine turns you on"), Harvest, Vertigo and others. Versions of these samplers can now be found in the many cd compilations possibly saturating the market (for example, "Refugees: A Charisma Records Anthology 1969-1978 [Box set]".

I have often mused on the selection criteria for those samplers: did the compilers pick the best track on an album for inclusion in the sampler? Did they pick the most accessible song? Did they pick the most representative song? In terms of Wizz Jones, they (presumably the afore mentioned Anderson) definitely picked the best songs, but when I consider "Nice enough to eat", which sufficed as many a young gentleman's introduction to 'underground' music (and to the Island label), then I'm not so sure.

Certainly, Fairport Convention's track on the sampler, "Cajun woman", was neither the best, most accessible nor most representative track from its parent album, "Unhalfbricking". In my opinion, it was the worst track on this ultimate record, not too accessible and certainly not representative (it was more an experiment in applying a Cajun arrangement to what would shortly be called a modern song in the trad idiom).

Another criterion seemed to be that compilers would pick the opening song from an album, presuming that each album's producer knew what he was doing when sequencing. Thus we have "Time has told me", which opened Nick Drake's "Five leaves left", followed by King Crimson's "21st century schizoid man", which similarly held the pole position on their "Court of the crimson king" debut album. On the other hand, neither Jethro Tull's nor Blodwyn Pig's contribution were the opening track.

Another problem which I have with listening to familiar songs from forty years ago along with unfamiliar songs is that the familiar will always sound better. I hope that I am free of this bias regarding "The legendary me", and can explain lucidly why the two songs which have become part of my musical fabric are better than anything else on this album. Incidentally, the cd release has three bonus tracks added at the end; as I didn't know the original, I can't complain that these songs stick out like a sore thumb, but even so.... The first is a recording of Leonard Cohen's "Sisters of mercy"; whilst adding nothing to the original, Jones inexplicably alters the song's chord sequence, making listening to it a jarring experience for someone versed in the original.

Thursday, October 22, 2009

Dollhouse

One of our satellite tv stations showed the first episode of Fox's new series, "Dollhouse". Intrigued by the promos which have been screened daily for the past few weeks, I decided to record the programme (as it's broadcast at an inconvenient hour for me).

Yesterday morning, I watched the first ten minutes or so, completely unable to fathom what was happening. In fact, I spent most of that time trying to figure out why the leading actress (Eliza Dushko) looked so familiar*, and in doing so, completely missed the fact that an actress that I had admired only the day before (Olivia Williams) was appearing.

During the day, I looked up the IMDB reference and began to understand what the show might be telling me. In the evening, I watched the programme from beginning to end. I needn't have bothered. After the very confusing first ten minutes (consisting of three or four scenes which appear to have absolutely nothing in common aside from the aforementioned Ms Dushko), the programme then turned into a high action, low meaning adventure story which did not interest me in the slightest. Once again, I am reminded why I don't like watching American tv series (aside from The West Wing and certain cartoon families).

On the basis of this opening salvo, and considering the programme's broadcast hours, I can't see myself watching it again.

* Yes, Ms Dushko was a cheerleader in "Bring it on". She does look vaguely like singer Natalie Imbruglia.

Tuesday, October 20, 2009

Lucky break

I saw the film "Lucky Break" the other night and thoroughly enjoyed it. I don't know why most of the IMDB reviewers dissed it. True, I doubt very much that prison life is as depicted in the film; I would be more afraid of my fellow prisoners and less of the prison guards (there was a moment of such fear when Jimmy Hands had his head covered in a plastic bin liner and taken to see the new block 'king').

Any film with James Nesbitt, Bill Nighy, Timothy Spall and especially Olivia Williams is going to be a pleasurable experience. Nighy and Spall join forces again after their appearance in "Still crazy".

Side note: Nesbitt's sidekick, Rudy, is played by Lennie James. I was surprised to see that Lennie appeared in 'Cold feet' as Rachel's first husband (Kris "with a K"), whereas Nesbitt was Rachel's second husband (Adam). So, in one instance, actors Nesbitt and James were antagonists whereas in another instance, they were best mates.

A smaller but similar coincidence happened in the BBC series 'Survivors' about which I was going to write but decided not to; in my opinion, this series did not live up to its potential and was very scattered at the end. Anyway, lead actress was Julie Graham, and one of her tribe was played by the ubiquitous Paterson Joseph, who seems to be cast in a huge number of television shows (he even appeared in one or two episodes of 'Cold feet'). Julie played the eponymous Mary in 'William and Mary', and Paterson played Reuben, the father of her children.

Thursday, October 15, 2009

Another day not wasted

Today I was considering how to improve the general look of one class of my programs. These have a dbgrid and several buttons, and whilst they are functional, they are somewhat old fashioned. As a picture is worth a thousand words, here is a screen shot of the main form of one of the programs (I should point out that these programs display personal data, which in the interests of privacy I am not going to show here, so I'm showing the form as it appears at design time. Imagine that the grid is populated).

The various components which appear in the grid are non-visible.

The first improvement was to take all the buttons and place them on a gray panel

This is certainly better, but the real problem is the grid, which is "so Win 3.1". What can be done? Dispose of the grid! I decided to display the data in a listview control, which is where the fun begins. I need the listview to be displayed from right to left, which requires a magic code. Once I had done that, I discovered that the header of the listview considers itself to be autonomous; this resolutely stayed as left to right as the lines displayed right to left, making an ugly sight. More searching of the internet revealed how to get a pointer to the header of the listview, which was then fed the magic code. Success!

Of course, I couldn't leave things as they were; I had to figure out how to change the sort order data in the listview whenever a header was clicked. This turned out to be slightly easier than I thought. I also added code to allow editing (via a subform) of a row, and to display any change that might be made in the data. This required an extra query component to get the new data for the chosen record.

Here is the new screen (note that the header is still left to right):


And here is (some of) the code:

type
 TForm1 = class(TForm)
   lv: TListView;
   Panel1: TPanel;
   EditBtn: TBitBtn;
   BitBtn2: TBitBtn;
   SQLConnection1: TSQLConnection;
   SQLQuery1: TSQLQuery;
   qPerson: TSQLQuery;
   procedure FormCreate(Sender: TObject);
   procedure BitBtn2Click(Sender: TObject);
   procedure EditBtnClick(Sender: TObject);
   procedure FormActivate(Sender: TObject);
   procedure lvColumnClick(Sender: TObject; Column: TListColumn);
 private
   LastSortedColumn: integer;
 public
end;


var
 Form1: TForm1;

implementation
{$R *.dfm}
uses unit2;

Function SortByColumn (Item1, Item2: TListItem; Data: integer): integer; stdcall;
begin
 case data of
   0: Result:= AnsiCompareText (Item1.Caption, Item2.Caption);
   1, 2: Result:= AnsiCompareText(Item1.SubItems[0], Item2.SubItems[0]);
   3: if strtodate (Item1.SubItems[2]) < strtodate (Item2.SubItems[2])
       then result:= 1
       else result:= -1
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
const
 LVM_FIRST = $1000; // ListView messages
 LVM_GETHEADER = LVM_FIRST + 31;

var
 ListItem: TListItem;
 header: thandle;
 i: integer;

begin
 header:= SendMessage (lv.Handle, LVM_GETHEADER, 0, 0);
 SetWindowLong (header, GWL_EXSTYLE, GetWindowLong (header, GWL_EXSTYLE) or
                                WS_EX_LAYOUTRTL or WS_EX_NOINHERITLAYOUT);
 SetWindowLong (lv.Handle, GWL_EXSTYLE, GetWindowLong (lv.Handle, GWL_EXSTYLE) or
                                WS_EX_LAYOUTRTL or WS_EX_NOINHERITLAYOUT);
  lv.invalidate; // get the list view to display right to left
  lv.items.beginupdate; // reduce flicker

  with sqlquery1 do
   begin
    open;
    while not eof do
    begin
     ListItem:= lv.Items.Add;
     ListItem.Caption:= fieldbyname ('zehut').asstring;
     for i:= 1 to 4 do ListItem.SubItems.Add (fields[i].asstring);
     next
     end;
    close
   end;

 lv.Items.endupdate;
 qPerson.Prepared:= true;
 LastSortedColumn:= -1;
end;

procedure TForm1.lvColumnClick(Sender: TObject; Column: TListColumn);
begin
 LastSortedColumn:= Column.Index;
 TListView(Sender).CustomSort(@SortByColumn, Column.Index);
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
 lv.setfocus
end;

procedure TForm1.BitBtn2Click(Sender: TObject);
begin
 close
end;

procedure TForm1.EditBtnClick(Sender: TObject);
var
 flag: boolean;
 id: longint;
 i: integer;

begin
 if lv.selected = nil then exit;
 id:= strtoint (lv.selected.SubItems[3]);
 with TGetDetails.Create (nil) do
   try
    flag:= execute (id);
   finally
    free
   end;

 if flag then
   begin
    with qPerson do
     begin
      params[0].asinteger:= id;
      open;
      lv.selected.Caption:= fieldbyname ('zehut').AsString;
      for i:= 1 to 3 do lv.selected.subitems[i - 1]:= fields[i].asstring;
      close
     end
   end;
 lv.setfocus
end;

end.

Sunday, October 11, 2009

This tickled my fancy

My dog sheds hair all the time, and someone suggested improving her food by adding a teaspoon of olive oil each day. I'll check the dietary values of her food this evening.

Whilst idling googling dogs and olive oil, I came across this pearl of wisdom:

Q: If you give olive oil to your dog, will it make my dog's nose any wetter?


To which the reply was "No, your dog's nose won't get wetter if I give my dog olive oil".

Someone has a problem with pronouns.

Friday, October 09, 2009

Firebird triggers and autoincrements

I often say that a day in which nothing new is learnt is a day wasted. Today will not be wasted.

One of the good things about the Borland Database Engine (BDE) is that it has an 'autoincrement' type; I would use a field of this type to provide primary keys for almost all database tables.

Firebird (FB) per se does not have an autoincrement type, which at first was a setback when I first started developing for this database manager. I use an oldish version of "EMS SQL Manager 2005 for InterBase & Firebird Lite" to create the databases, and this allows one to define fields as autoincrements. Under the hood, this program is creating two entities connected to the autoincrement field: a generator and a trigger. The generator starts with a seed value and whenever it is called, it increments the seed and returns the new seed; thus every call to the generator is guaranteed to get a value which does not exist in the database. Since I have been porting applications to FB, I have been calling the generator manually and getting the new key value.

Until now I have ignored the trigger which is also defined. Today I was defining a new database and decided to look at the trigger:
CREATE TRIGGER BI_PEOPLE_ID FOR PEOPLE
ACTIVE BEFORE INSERT
POSITION 0
AS
BEGIN
IF (NEW.ID IS NULL) THEN
NEW.ID = GEN_ID(PEOPLE_ID_GEN, 1);
END;

Turned into English, whenever a program is about to enter (insert) new data into the 'people' table, if the value of the 'id' field is null then the trigger will automatically call the generator and obtain the new value. This means that I don't have to call the generator manually which means that I can simplify my program code.

Almost all the programs I write for my occupational psychologist read in a data file which has been prepared by an 'exam' program run by an examinee; these data files' contents can be divided into two parts, where the first part is the examinee's personal data and the second part is the examinee's results.

The first part will go into the 'people' table, and I will need the id number of the inserted tuple in order to use it as a foreign key during the insertion of the 'results' tuple. Thus it's more efficient to call the generator manually for the 'people' table before inserting the data as I can save the value for later. But as the 'results' table has an autoincrement field as its primary key, I don't need to call the generator manually for this as I don't need the resulting value for anything other than as a primary key for the current tuple.

To give an explicit example, if the 'results' table has the structure
id: autoincrement (primary key)
pid: longint (foreign key, points to person whose results these are)
subject: longint
score: smallint

Then instead of writing queries like this
insert into results
values (:p1, :p2, :p3, :p4)

I can now write this query
insert into results (pid, subject, score)
values (:p2, :p3, :p4)

and have the pre-insert trigger automatically generate the value for the primary key. If the 'insert' statement does not have a list of named fields then it automatically inserts data starting with the first field in the table.

Actually, I know that in the above situation, I could probably do away with the 'id' field, and have the primary key based on the fields 'pid' and 'subject', in which case using a trigger is beside the point.

Unfortunately, it seems that I will be able to use the trigger method (as opposed to calling the generator manually) infrequently, and then only for tables whose values are entered programmatically. Many tables are populated by data entry screens, where the database table is buffered by a client dataset, and in such cases it is impossible to get the trigger to fire, as a 'field requires value' message appears.

What is important about today's efforts is that I am thinking how to use the dbExpress/Firebird combination in the most efficient manner. This is all still relatively new material to me, whereas the BDE code has had over a decade to settle and improve.

Thursday, October 08, 2009

The girl who played with fire


In view of the favourable, but mixed, reaction that I had to Stieg Larsson's first book, "The girl with the dragon tattoo" (GDT), I decided to purchase and read its followup, "The girl who played with fire" (GPF). In retrospect, I wish that I hadn't bothered. The good parts of GFT far outweighed its flaws, but there's no way that I can say that about GPF. This book is in sore need of an editor, and unfortunately it's far too late for that.

Again, I am torn between holding back the spoilers for those who have not read the book, and giving specific examples of poor editing. When I write "editing", I don't mean copy editing, which is a technical function, but rather the equivalent of what a record producer does: gives the raw material a direction, chops out the irrelevant and sequences the raw material in the best order possible.

The first two hundred pages (approximately) are almost totally irrelevant to the rest of the book. The material about Salander would be better served by placing it in a collection of novellas and removing it from this book; it feels like a warm-up exercise and has no part in GPF. I am reminded of the late Roger Zelazny who prior to beginning a novel would write short stories about his main characters in order to get a feel for them. These pages give one the (post-reading) feeling that Larsson was making the whole thing up as he went along, and inserted events (or "hooks", as the musician or computer scientist might call them) as they occurred to him. If later events revolve around prior knowledge which is given by these hooks, then the reader feels satisfied, but if the hooks are left unresolved, then the feeling is awkward. I found myself reading the opening pages wondering where the events described would lead, and in the end, they led absolutely nowhere.

I find myself comparing the structure of (the rest of) the book to that of a police procedural. Such books generally begin with a murder, and then focus on the detective's attempts to solve the crime. Had GPF began with the murders and then focused on the detective team's attempts, then it would have been on more solid ground (and perhaps more predictable). Larsson makes the book extremely complicated by having three (or even four) different teams trying to solve the murders and it makes for exceedingly complicated reading, whilst dissipating the thrust of the story. Where was Salander all the time her liberally enhanced sordid past was being splashed over all the newspapers in Sweden?

I sincerely hope that Swedish policemen are more professional than some of those portrayed in this book, as their attitude towards someone who is supposed to be a witness and possibly a suspect is remarkably tabloid (and some of the policemen's attitude towards one character who wasn't even a witness was deplorable).

After a while, the police procedural theme disappears and the book becomes more of a crime thriller, at which point any points which the book might have accrued slip away with remarkable speed. There are still unresolved hooks spread around (for example, those connected with Erika Berger) which dilute the story's impact.

In order to check the book's literary style, I opened the book a few times at random. The first thing that I noticed was that most of the pages were written in direct conversation between characters; the second was that the non-literal descriptions were sorely lacking in adjectives. This gives the prose a very straight-forward and brisk attitude but it doesn't make reading it a pleasure.

On the basis of GPF, I can't see myself reading the third part of the "Millenium trilogy", "The girl who kicked the hornets' nest". I would probably see any film made from these stories but I won't be investing any more money or time in these books.

Wednesday, October 07, 2009

Pressure cooker

Whilst reading the cookery column of Friday's newspaper the other day, I resolved to buy a pressure cooker. After a little research on the Internet, I went down to the local shopping mall and found myself a 9 litre cooker for 230 shekels (about $55). At first I thought there must be something wrong with the pot as the lid didn't seem to fit, but one of the sales staff showed me how to close the lid hermetically and thus keep all the steam inside.

I don't have a recipe book yet for this type of cooking, and I was warned not to try and experiment. I found a recipe for "Italian inspired chuck roast with herbed red wine gravy" which seemed like a good place to start. I marinaded overnight chunks of goulash beef in a mixture of olive oil, wine and vegetable soup, then drained and dried the chunks. In the pressure cooker I heated oil and then browned the meat; after removing it, I fried a chopped onion, then added two cups of water, the meat, and chopped sweet potato and pumpkin. I then closed the lid of the pressure cooker as instructed and waited for steam to emerge; when it did, I closed a valve on the cooker's lid, reduced the heat and waited 30 minutes. After that time, I removed the pot from the heat, waited about five minutes, then opened the valve, allowing all the steam to escape. When the cooker showed signs of returning to normal, I removed the lid and decanted the contents into a bowl.

Somehow the amount of liquid had doubled during cooking, leaving me with a large amount of broth. The meat was tender and good. My wife had been very doubtful about my purchasing the cooker, but when she tasted the meal - and heard that the cooking time was only 30 minutes - she strongly endorsed the purchase. She has made goulash with this meat before, and has normally boiled the meat for anywhere between two and four hours.

Guess what we're having for dinner on Friday night....