Showing posts with label REALbasic. Show all posts
Showing posts with label REALbasic. Show all posts

Saturday, 31 October 2009

RBScript opensource site

Back in July, I was wondering aloud how I could help improve RB and whether I should start up and host a testing suite.

Well I had a few abortive attempts at designing the suite and never really came up with a decent spec.

I don't have extensive knowledge of the existing bugs, I don't have consistent access to an Intel Mac and I'm not convinced I could do a good job.

So I was casting around for something I might be able to help with and I came across a few questions about RBScript.

So I decided to set up an opensource site and hope it will make a reasonable contribution to the sum of RBScript knowledge in the RB community.

The site revolves around a subversion repository. I chose this because I make quite a lot of use of TortoiseSVN on Windows and SVN workbench on Linux so I'm reasonably comfortable with it.

I like RBScript and I've used it for a number of personal project, one of which I may tidy up and add to rbscriptjottings but the starting point is new harness project.

This harness will contain a number of RBScript subclasses, some context classes, some RBscript class definitions (some of which should probably be hidden from the RBScript coder) and some sample RBScript code. All this will be organised as mini-projects within the harness program which starts at harness.rbvcp

So far I've dabbled in RBScript subclasses with extensions to

  • print methods
  • input methods
  • error-reporting

I've added contexts which provide
  • basic ui (multiple msgbox styles, speak, debuglog)
  • controlled read/write access to RectControls such as ListBox & ProgressBar
  • a reasonable emulation of the RB Date class
Each context currently has its own demo window. I tend to write major class definitions in the constant editor of the appropriate module.

The contexts all need more work but they can all be chained together by making them subclasses of each other and therefore provide access to as much current functionality as is required.

I expect to work on the harness program on and off for quite a while with varying amounts of concentration.

Hopefully, once I've spent a while on it, there will be something for everyone to learn from the site. I've already found out several things I didn't know about the subject at hand.

It is my fervent hope that others will come on board at some stage and add to the RB code and/or to a store of scripts.

The test scripts are in need of lots of rethinking. They tend to be just odd bits of script I used when writing the contexts to make sure they worked. A bit of design effort will need to go into them at some point.

That's one of the advantages of using version control. It's nice and simple to keep making changes without messing things up.

A quick look at the issues list will show that I've got a lot of small things planned for the project. I could easily swamp the list with lots more ideas but I need to see how quickly the current work gets done first.

If anyone has any particular requests or comments, feel free to raise a new issue on the site. Or if you don't have a gmail or google login, leave a comment here.

And of course if you have any contributions to make, please let me know.

A couple of comments before I stop writing:
  1. I've used EditFields in the project for backward compatibility and intend to keep them there until Cocoa in RB makes them obsolete.
  2. For those who are just curious, I've put a single file (rbp) version of the project at the downloads page. It is the same as the version-control version at the time of writing but I make no promises about keeping it up-to-date. The current source is in the repository.
And so to bed.

Thursday, 1 October 2009

Obfuscating

Low-level security is often needed in a compiled app.
Things like hiding a secret string from the casual hacker with a hex-editor.

When the pro edition of RB of allowed me to use IDEScripts, I had a simple script that changed the currently selected code to a series of chr() functions.

Recently, I found a need for a similar facility but I don't have REAL Studio. So I decided to write the beginnings of a standalone app whose purpose is to allow me to generate useful bits of code to paste into the source I am currently developing.

Under these circumstances, I was able to do a slightly more thorough job of hiding the string and making it more difficult for future enhancements to the compiler to optimise my code away.

The general idea is that I give the helper app my password and from it, the code produces a new function with a meaningless name and no reference to the original string.

In the main app, this function uses a MemoryBlock to convert integers in random sequence to a Base64Encoded string representing the password. The function then returns the Base64Decoded version of the string.

I've uploaded the source as part of code.google.com/rbjottings where the whole set of snippets can be downloaded by SVN or you can look at the source for these routines at obfuscateTest Window1 because all the code is contained in that window.

The methods involved are

Function generator _
(inText As String, ByRef functionName As String) As String
Function randomIdentifier _
(minLength As Integer = 6, maxLength As Integer = 10) As String
which are called using code like
dim fName As String
taCode.Text = generator( tfPlainText.Text, fName )
if chkAddcaller.Value Then
taCode.Text = taCode.Text + EndOfLine + EndOfLine + _
"Sub Caller() " +EndOfLine + _
" Dim s as String = " + fName + "()" + EndOfLine + _
"End Sub"
end if
where taCode is a TextArea and tfPlainText is a TextField

Obviously this simple obfuscation is completely useless where real encryption is required.

However, where the aim is merely to hide slightly sensitive information from prying eyes, this will force them to work much harder to find the string.

Sunday, 26 July 2009

How can I help improve RB?

When I was developing large apps for other people to use, one of my recurring nightmares was that old bugs, once fixed, would return in a future version and make me look unprofessional.

Consequently, one of my most important tools was an ever-growing suite of test scenarios which I ran my app through before issuing a new release. I also asked my beta testers to run these scenarios where practical.

It seems to me that if my app were a programming language, this sort of suite would be particularly easy to produce.
No doubt RS have an extensive testing suite but they don't make it public for users to test. Also, I sometimes wonder if they share my nightmare.

I keep wondering about starting an open-source project which would host tests of current and previous bugs.

  • Have I the time and enthusiasm to produce the original framework?
  • Do I know enough about the current bugs?
  • Would someone else be more appropriate as the project owner?

Of course, there was Joe Strout's excellent ROTOR which ran several tests that could be automated and which many of us ran on each new beta and release for a while but those tests all seem to pretty much pass these days (except the encoding of external files) and it has fallen out of sight.

My preference now would be for something that runs unit tests where possible but which also includes tests which required user-involvement.
There would be a project (or projects) with windows describing what the user needed to do and what result was being looked for.
This would allow tests to be run on as many platforms as possible by beta testers, new users and anyone who starts using the latest version.

Hopefully, if there were user reports quoting problems found by the testing suite, RS would be able to check it out for themselves.

It seems to me that this would be a more positive step than just producing a list of bugs.

Perhaps I'll manage to get this started at some point. Or perhaps someone else will come up with the idea and the enthusiasm first and run with it.

Sunday, 8 February 2009

Editfields that update each other

I've been creating a number of EditFields lately where the TextChange event of each updates all the others.

I pretty soon got fed up with wrapping every bit of update code in "alreadyUpdating" flags, so I decided to create a simple EditField subclass with a Changed event that only fires if the EditField has the focus.

As I thought this might be useful for others, I've put a sample project on my website. it uses the subclass to allow entry of hex, binary or decimal numbers, while the two other fields are updated.

I've also added the source to the SVN repository at Google Code.

Saturday, 31 January 2009

Open Source projects on Google Code

I went through a patch when I tried to answer quite a number of questions on the forums and one or two on the NUG.

Whenever I post code, I always try to test that it at least compiles.

Many of the test projects I used ended up on a website that is alas no more.

I am now in the process of moving some of those snippets to an opensource project on Google Code so that they don't just go missing.

There's nothing very startling and much of it is incomplete but some of it may prove of use to someone eventually.

The first class I ever wrote in RB was an attempt at a faster string. You can see it converted to Vesrion Control Format at Google Code.

Thursday, 8 May 2008

Telling the truth

We all know that REAL Software can't be bothered to document REALbasic properly, so it comes as no surprise that I can find no reference in the docs to the pairs class, the only new feature RS is advertising for 2008r2.

Of course, Aaron has stepped in and documented this on his blog so it's no great hardship for me personally, as I check his blog most days.

What does come as a bit more of a surprise is that, even now, the RS website is claiming

Windows: Unlike most other Windows development tools, REALbasic generates self-contained executables which are not dependent on external dlls or pre-installed frameworks. The single .exe file produced by the REALbasic compiler is all you need to distribute to you customers.
This has been mentioned on the forums, I've PM'd members of RS staff about it hours ago, I've cleared every browser cache in sight and connected via different ISPs. It's still there.

Does the U.S.A. have no laws about false advertising? In a litigious society like the U.S. are people actually putting up with this sort of thing?

The single executable is a central claim about the way that RB can be deployed and should have been removed from the website as soon as RS realised they might be going to change it.

Of course there will be changes to how RB works over time but anyone who has bought or renewed RB recently could be forgiven for feeling they have been badly misled.

It's a real shame because 2008r2 seems to be a really nice release. Why doe RB's documentation and advertising so often end up wrecking the positive feel we would otherwise get?

Tuesday, 22 April 2008

Feedback changes

I probably should not write this first paragraph publicly. The beta list has been awash lately with phrases like, "I really like this new spirit of openness," "thanks for keeping us informed" and even "thanks for listening, the changes are much better than your original proposal."

And then comes the announcement that the current feedback system is being replaced and will no longer be searchable. We can no longer see other people's reports.

Within seconds, I hit the bookmark to my watchlist, so I could pdf my outstanding reports.
But they had already gone.

That's what really irritates me. The sudden withdrawal of the data I keep around to remind me of workarounds to bugs I or others have already found.

If RS need a better project management system, it hardly comes as a surprise.

If they want to use its built-in bug-tracking, I hope and expect it will work better for them.

If they have decided to use a hosted system rather than host their own copy, who am I to argue.

And if they don't want to waste resources creating non-standard versions of FogBugz, nobody will suggest they look like a company with engineers to spare.

Other people complain that thousands of customers are being asked to waste their time to save a bit of centralized time at RS. This may be true but it may well be worthwhile.

It's the lack of notice that leads to the conspiracy theories.

What could be a possible motive for withdrawing the old data?

  • RS don't want the world at large to be able to see how many bugs they have?

What could be a possible motive for giving no notice?
  • To make it more difficult for some third-party to compile a website (wiki?) listing outstanding bugs?

It may well be that neither of these is the case.

It may well be that RS's normal policy of secrecy has engendered an attitude where, when considering a change, nobody even thinks to ask until the last minute, "How should we communicate with our customers to cause least upset?"

Personally, I really hope FogBugz works well for RS and I'll live with whatever decisions they make about customer access.

If I find a bug that doesn't affect me personally, it's quite likely I will assume somebody else has reported it.

If somebody else posts a bug to a mailing list or forum that does affect me, it's quite likely I will assume they may not have reported it and will paste a copy of it (altered as required to fit my personal prejudices) straight into the feedback system.

I'm sure somebody else will provide hosting for a public bug-list out of RS's control.
If so, I will do my best to contribute.

And just for the record, my business doesn't rely on RB at all.

So if I see RS getting heavy-handed with anyone that tries to maintain a public bug-list, I will just decide that RS is not a company with whom I need a business relationship any more.

It's a real shame I am even beginning to feel that way. My goodwill towards RS has been sky-high lately because stuff seemed to be moving in the right direction.

If I had had a few days notice that my watchlist was going to disappear, I would be slightly irritated but would now be posting "give it a while to see how it works" messages in reply to the current storm of complaints.

Sunday, 23 March 2008

Self and Me

A question that comes up a lot in REALbasic is

What is the difference between the Self and Me keywords?
Everybody eventually thinks they have it sorted out in their own minds but it's surprising how many of us then find we're wrong.

I think that essentially it goes something like this:
Self and Me always refer to the same thing, except in the specific instance where you are editing the method handlers of a class in a Window. In those circumstances, Self refers to the item you're editing (the window) and Me refers to the class instance which exposes the method.
The best advice I ever received on this was (I think) from Mars.

If you want to know what Self refers to, while you're coding, look up at the top of the IDE.

If the title bar says
Realbasic - [Untitled] - Class1
then Self refers to Class1. On the other hand, if the title bar says
Realbasic - [Untitled] - Window1
then Self refers to Window1

Sunday, 16 March 2008

Threads - Cooperative and Preemptive

Lots of developers talk about multiple processors in the same dubious
way they've always spoken of bigger screens and higher screen resolutions.

If the user happens to have more, how can I fill it up?
All those resources aren't there so the computer can always run one app more greedily. In general, they're about allowing a richer multi-tasking experience.

I admit that a few apps will benefit from more processing power than one processor can provide but it's not a good idea to rely on high-end CPUs for most programs in any language.

REALbasic "threads" are not preemptive thread such as programmers coming from other environments might expect, they are cooperative threads which some people are surprised to learn are still exposed by the various operating systems.

We'll ignore the argument that what Microsoft call "fibres" (I can't bring myself to use MS spelling) might not really be entitled to the name "thread" on Windows any more.

There are ways (MBS is one but not the only) of attempting to use preemptive threads with RB but RS engineers assure us there is no safe way to use the framework with preemptive threads that does not effectively cripple them back to cooperative.

I don't try to use preemptive threads in RB because I believe it may eventually give crashes or (possibly worse) unpredictable results.
(This is a very slightly stronger statement than I make about using App.DoEvents in a gui app. I might try that in one particular set of circumstances.)

Sometimes to get around the lack of preemptive threads and still keep the gui responsive, I have to split long external reads up into shorter chunks. This can sometimes be a pain, especially with databases.

Sometimes I have to split out lengthy tasks into a helper app. If that is also a RB app, it significantly increases the size of my app's footprint on the disk.

I'd love to see preemptive threads in RB but my guess is
  1. we won't see them for a long time
  2. they'll take an even longer time to settle down when they do arrive
  3. they won't really make much difference to me
And of course there are many fixes and lots of new features (and their fixes) I'd like to see first.

Friday, 14 March 2008

Personal Messages on the Forums

I've been trying to be helpful on the forums for a while now and in general people seem to appreciate most of my efforts.

One thing that does happen quite a lot is that I post a reply to a query, then the original poster (or someone else) wants to start a follow-up discussion by personal message.

Usually I respond to this by giving a general reply to any follow-up questions, followed by a note something like

I prefer not to give too much advice by PM. If I'm going to try to be helpful, I like as many people as possible to benefit from it.

If you have any further questions, it works better if you ask them publicly and I (or someone else) respond on the forums.
Most people take the hint. Some apologise (for which there is no need at all) and some never speak to me again (or that's the way it seems to me)

Some people, however, just ignore the note and ask the next question in another PM.

I may give them the benefit of the doubt once and assume that our messages crossed, while the poster was composing his question.

I may reopen the correspondence myself by suddenly remembering something else I didn't put in my original reply. Then it's only reasonable to accept a couple more PMs before giving up.

To a subsequent question, if I'm feeling reasonably polite, I may reply
We really should discuss this publicly in the forum.
But after that, I really can't think of anything polite to write, except to tell them exactly what I charge for consultancy. I normally express this in USD per hour, even though my charges are nearly always in GBP.

Strangely enough, that usually ends the conversation.

Thursday, 13 March 2008

Names for colours

I needed to display names for lots of colours the other day and ended up with an Extends function to return x11 names.

It occurred to me it might be useful to someone else.

To use it, just create it as a method in a module. It does have to be in a module.

Then you can use it like this

dim c as Color = &cCD5C5C
MsgBox c.x11name

There is a demo project at http://rb.sgarman.net/x11colours.rbp which is just a small test harness I threw together for this function and its reverse.

Anyway, here's the method

Function
X11name(Extends c As Color, allowBlanks As Boolean = true) As String
Static Dict as Dictionary
if dict is nil then
dict = new Dictionary
Dict.Value( &c000000 ) = "Black"
Dict.Value( &c000080 ) = "Navy"
Dict.Value( &c00008B ) = "DarkBlue"
Dict.Value( &c0000CD ) = "MediumBlue"
Dict.Value( &c0000FF ) = "Blue"
Dict.Value( &c006400 ) = "DarkGreen"
Dict.Value( &c008000 ) = "Green"
Dict.Value( &c008080 ) = "Teal"
Dict.Value( &c008B8B ) = "DarkCyan"
Dict.Value( &c00BFFF ) = "DeepSkyBlue"
Dict.Value( &c00CED1 ) = "DarkTurquoise"
Dict.Value( &c00FA9A ) = "MediumSpringGreen"
Dict.Value( &c00FF00 ) = "Lime"
Dict.Value( &c00FF7F ) = "SpringGreen"
Dict.Value( &c00FFFF ) = "Aqua"
Dict.Value( &c00FFFF ) = "Cyan"
Dict.Value( &c191970 ) = "MidnightBlue"
Dict.Value( &c1E90FF ) = "DodgerBlue"
Dict.Value( &c20B2AA ) = "LightSeaGreen"
Dict.Value( &c228B22 ) = "ForestGreen"
Dict.Value( &c2E8B57 ) = "SeaGreen"
Dict.Value( &c2F4F4F ) = "DarkSlateGray"
Dict.Value( &c32CD32 ) = "LimeGreen"
Dict.Value( &c3CB371 ) = "MediumSeaGreen"
Dict.Value( &c40E0D0 ) = "Turquoise"
Dict.Value( &c4169E1 ) = "RoyalBlue"
Dict.Value( &c4682B4 ) = "SteelBlue"
Dict.Value( &c483D8B ) = "DarkSlateBlue"
Dict.Value( &c48D1CC ) = "MediumTurquoise"
Dict.Value( &c4B0082 ) = "Indigo"
Dict.Value( &c556B2F ) = "DarkOliveGreen"
Dict.Value( &c5F9EA0 ) = "CadetBlue"
Dict.Value( &c6495ED ) = "CornflowerBlue"
Dict.Value( &c66CDAA ) = "MediumAquamarine"
Dict.Value( &c696969 ) = "DimGray"
Dict.Value( &c6A5ACD ) = "SlateBlue"
Dict.Value( &c6B8E23 ) = "OliveDrab"
Dict.Value( &c708090 ) = "SlateGray"
Dict.Value( &c778899 ) = "LightSlateGray"
Dict.Value( &c7B68EE ) = "MediumSlateBlue"
Dict.Value( &c7CFC00 ) = "LawnGreen"
Dict.Value( &c7FFF00 ) = "Chartreuse"
Dict.Value( &c7FFFD4 ) = "Aquamarine"
Dict.Value( &c800000 ) = "Maroon"
Dict.Value( &c800080 ) = "Purple"
Dict.Value( &c808000 ) = "Olive"
Dict.Value( &c808080 ) = "Gray"
Dict.Value( &c87CEEB ) = "SkyBlue"
Dict.Value( &c87CEFA ) = "LightSkyBlue"
Dict.Value( &c8A2BE2 ) = "BlueViolet"
Dict.Value( &c8B0000 ) = "DarkRed"
Dict.Value( &c8B008B ) = "DarkMagenta"
Dict.Value( &c8B4513 ) = "SaddleBrown"
Dict.Value( &c8FBC8F ) = "DarkSeaGreen"
Dict.Value( &c90EE90 ) = "LightGreen"
Dict.Value( &c9370DB ) = "MediumPurple"
Dict.Value( &c9400D3 ) = "DarkViolet"
Dict.Value( &c98FB98 ) = "PaleGreen"
Dict.Value( &c9932CC ) = "DarkOrchid"
Dict.Value( &c9ACD32 ) = "YellowGreen"
Dict.Value( &cA0522D ) = "Sienna"
Dict.Value( &cA52A2A ) = "Brown"
Dict.Value( &cA9A9A9 ) = "DarkGray"
Dict.Value( &cADD8E6 ) = "LightBlue"
Dict.Value( &cADFF2F ) = "GreenYellow"
Dict.Value( &cAFEEEE ) = "PaleTurquoise"
Dict.Value( &cB0C4DE ) = "LightSteelBlue"
Dict.Value( &cB0E0E6 ) = "PowderBlue"
Dict.Value( &cB22222 ) = "FireBrick"
Dict.Value( &cB8860B ) = "DarkGoldenrod"
Dict.Value( &cBA55D3 ) = "MediumOrchid"
Dict.Value( &cBC8F8F ) = "RosyBrown"
Dict.Value( &cBDB76B ) = "DarkKhaki"
Dict.Value( &cC0C0C0 ) = "Silver"
Dict.Value( &cC71585 ) = "MediumVioletRed"
Dict.Value( &cCD5C5C ) = "IndianRed"
Dict.Value( &cCD853F ) = "Peru"
Dict.Value( &cD2691E ) = "Chocolate"
Dict.Value( &cD2B48C ) = "Tan"
Dict.Value( &cD3D3D3 ) = "LightGrey"
Dict.Value( &cD8BFD8 ) = "Thistle"
Dict.Value( &cDA70D6 ) = "Orchid"
Dict.Value( &cDAA520 ) = "Goldenrod"
Dict.Value( &cDB7093 ) = "PaleVioletRed"
Dict.Value( &cDC143C ) = "Crimson"
Dict.Value( &cDCDCDC ) = "Gainsboro"
Dict.Value( &cDDA0DD ) = "Plum"
Dict.Value( &cDEB887 ) = "BurlyWood"
Dict.Value( &cE0FFFF ) = "LightCyan"
Dict.Value( &cE6E6FA ) = "Lavender"
Dict.Value( &cE9967A ) = "DarkSalmon"
Dict.Value( &cEE82EE ) = "Violet"
Dict.Value( &cEEE8AA ) = "PaleGoldenrod"
Dict.Value( &cF08080 ) = "LightCoral"
Dict.Value( &cF0E68C ) = "Khaki"
Dict.Value( &cF0F8FF ) = "AliceBlue"
Dict.Value( &cF0FFF0 ) = "Honeydew"
Dict.Value( &cF0FFFF ) = "Azure"
Dict.Value( &cF4A460 ) = "SandyBrown"
Dict.Value( &cF5DEB3 ) = "Wheat"
Dict.Value( &cF5F5DC ) = "Beige"
Dict.Value( &cF5F5F5 ) = "WhiteSmoke"
Dict.Value( &cF5FFFA ) = "MintCream"
Dict.Value( &cF8F8FF ) = "GhostWhite"
Dict.Value( &cFA8072 ) = "Salmon"
Dict.Value( &cFAEBD7 ) = "AntiqueWhite"
Dict.Value( &cFAF0E6 ) = "Linen"
Dict.Value( &cFAFAD2 ) = "LightGoldenrodYellow"
Dict.Value( &cFDF5E6 ) = "OldLace"
Dict.Value( &cFF0000 ) = "Red"
Dict.Value( &cFF00FF ) = "Magenta"
Dict.Value( &cFF00FF ) = "Fuchsia"
Dict.Value( &cFF1493 ) = "DeepPink"
Dict.Value( &cFF4500 ) = "OrangeRed"
Dict.Value( &cFF6347 ) = "Tomato"
Dict.Value( &cFF69B4 ) = "HotPink"
Dict.Value( &cFF7F50 ) = "Coral"
Dict.Value( &cFF8C00 ) = "DarkOrange"
Dict.Value( &cFFA07A ) = "LightSalmon"
Dict.Value( &cFFA07A ) = "LightSalmon"
Dict.Value( &cFFA500 ) = "Orange"
Dict.Value( &cFFB6C1 ) = "LightPink"
Dict.Value( &cFFC0CB ) = "Pink"
Dict.Value( &cFFD700 ) = "Gold"
Dict.Value( &cFFDAB9 ) = "PeachPuff"
Dict.Value( &cFFDEAD ) = "NavajoWhite"
Dict.Value( &cFFE4B5 ) = "Moccasin"
Dict.Value( &cFFE4C4 ) = "Bisque"
Dict.Value( &cFFE4E1 ) = "MistyRose"
Dict.Value( &cFFEBCD ) = "BlanchedAlmond"
Dict.Value( &cFFEFD5 ) = "PapayaWhip"
Dict.Value( &cFFF0F5 ) = "LavenderBlush"
Dict.Value( &cFFF5EE ) = "Seashell"
Dict.Value( &cFFF8DC ) = "Cornsilk"
Dict.Value( &cFFFACD ) = "LemonChiffon"
Dict.Value( &cFFFAF0 ) = "FloralWhite"
Dict.Value( &cFFFAFA ) = "Snow"
Dict.Value( &cFFFF00 ) = "Yellow"
Dict.Value( &cFFFFE0 ) = "LightYellow"
Dict.Value( &cFFFFF0 ) = "Ivory"
Dict.Value( &cFFFFFF ) = "White"
end if
Dim retval as String
retval = dict.Lookup( c, "" )
If retval = "" and allowBlanks = false then
dim v as Variant
v = c
retval = mid( v.StringValue, 3 )
End If
Return retval
End Function

Saturday, 23 February 2008

How do I feel about RB?

I'm really confused about how I feel about REALbasic.

I like the engineers. Aaron, Mars, Mike and William are my friends. This is in much the same way that the soap stars are my cousin's friends.

I like the build engineer, Nathan. And who could fail to like Marco.

But I don't like the company much. Its public utterances, though mostly truthful, are often misleading and the company is far too secretive for my liking. I do not mean the utterances by my friends (see above) are misleading.

I like what REALbasic wants to be. I quite like a lot of what it is. But it is the most buggy development environment with which I work by a long way.

It has far too many features which have been included to add to the feature list and have never worked properly or have been left in an unreasonably basic state. Far too many bugs are fixed (or half fixed) and then come back later without anyone seeming to notice.
If your testing doesn't pick up recurrences of old bugs, what can you hope it will pick up?

Now because criticism of the product implies criticism of my friends, I become very defensive when the number one whiner starts again.

But then people tell him he should cut RS some slack because my friends are the only engineers. And then I get really irritable because the company refused to tell me how many engineers there are, even when I offered to sign a non-disclosure agreement and so my best customer won't let me write any of his stuff in RB.

So I join in with the whining. Then I feel guilty because I'm criticising the product that should be so wonderful because it has the right aspirations and is pretty good just as it is now.

So for now I just keep swapping sides and therefore disagreeing with nearly every post I see about the quality, usefulness and future of REALbasic

But for all that, most of the programming I do for myself or for fun I do in REALbasic and will continue to do so.

Tuesday, 15 January 2008

Another new feature

So, rb2008 will introduce introspection.

Geoff claims it's for the customers' benefit and there will be much screaming from the gallery that RS should be spending their time fixing bugs instead of adding features.

Of course they're concentrating mostly on bug-fixes but some people believe the new features are marketing-led.

What I ask myself is this. Are they spending time adding the features they feel they need to enable them to fix the bugs?

They've been eating their own dogfood for a while now and may be realising there's stuff it ain't so easy to do in RB and they need a few more features to do some of the things they need to do next.

Remember, writing a toolset exposes you to the unpredictable foibles of other developers, just about as badly as anything I can think of.

If you were building a toolset, might you not find you'd give your eye teeth for features like:

  • Introspection
  • Delegates
  • Control scope
  • destination directory and command line arguments for debug applications
  • plugin debuggin'
  • turning off implicit instantiation
  • namespaces
Is it just me, or are their stomachs just revolting against the lack of meat in the dogfood?

Sunday, 25 November 2007

Getting even grumpier as I age

Sometimes I just want to scream, "Don't you know it's a programming language? Write some code!"

It's usually on the forums but today's example happened to be in the NUG

I think it's incredible that the HTMLViewer
does not have two extremely basic functions: Back() and Forward() -
how can we be without those?
I suppose I should have done my usual helpful point to the history class at my website, (written in 2005r1, I think) but it's the assumptions behind the question that get to me.

Wednesday, 21 November 2007

Creating a simple canvas subclass

So I created a simple subclass to answer a question on the RB Forum and made the mistake of saying I would write a simple description of how I did it.

Some of the detail of these instructions is influenced by the fact that I created it in the Windows IDE.

The sample project is on my website.

In the Project tab of a new project, I created a new class and set its Name to sjgTextBox and its Super to Canvas.

I double-clicked the new sjgTextBox class and in the tab that opened, I created a few properties to store the text details and the mouseDown position.

  • BorderColour and TextColour are public Color properties with default values.
  • When setting Text, I need to change the width of the control appropriately, so I created a computed property.
  • Private properties mDownX and mDownY as Integer will come in handy for remembering where the mouseDown occurred, when dragging.
In the setter for Text, I added code to resize the control based on the string width plus blank space the size of "W" at each end.

In the Paint event, I added code to draw a border around the control and the text property inside it.

In the mouseDown event, I added code to record the position of the mouse on the parent window.

In the mouseDrag event, I check whether the control is already positioned according to the mouse and, if not, I move it to the correct position on the parent window.

Going back to the Project tab, I right-clicked the sjgTextBox class and selected "Property List Behavior...", where I checked the three public properties I had created, so they would appear in the control's property-list.

The control was now complete, so the next job was to add some to a window.

I opened the window's code view and in the popupmenu above the control palette on the left, I selected "Project controls" and dragged 3 sjgTextBoxes onto the Window.
For each I set its Text property and the two Colour properties.

Compile and test.

Monday, 18 June 2007

ValidDate function

This is just a test posting to see how awful large chunks of code will look on the blog.

The code for the drop-in replacement for REALbasic's ParseDate can be found on my website

Thanks to Norman Palardy, Joe Strout, Dennis Birch and Maximilian Tyrtania for their help and advice with this function on the NUG

Function validDate(text as String, ByRef value As Date, assumePastFuture as integer= 0) As Boolean
// If the year provided has only one or 2 digits, check assumePastFuture
// negative value means the past, positive means future, 0 means current century

// If no year is supplied, assumePastFuture has a granularity of 1 year

Static yearPos as Integer = -9
Static monthPos as Integer = -9
Static dayPos as Integer = -9

if yearPos = -9 then // first time through
yearPos = -1 // only try this once
// try to work out local date format
// assume Gregorian calendar
// assume shortDate contains all 3 numbers
// don't use NthField or Split in case it contains other characters
dim d as new date
// clear any time numbers, in case of unusual shortDate format
d.TotalSeconds = 0
// set unique values for year, month & date
d.SQLDate = "2005-12-31"
dim s as String = d.ShortDate
dim pos() As integer
dim thisPos as Integer

thisPos = InStr( 0, s, "05" )
if thisPos > 0 then pos.Append thisPos
thisPos = InStr( 0, s, "12" )
if thisPos > 0 then pos.Append thisPos
thisPos = InStr( 0, s, "31" )
if thisPos > 0 then pos.Append thisPos

if UBound( pos ) = 2 then
// we've found all three elements
// sort them by position in shortDate
dim typ() As string = Array( "y", "m", "d" )
pos.SortWith typ
yearPos = typ.IndexOf( "y" )
monthPos = typ.IndexOf( "m" )
dayPos = typ.IndexOf( "d" )
end if

end if
if yearPos < 0 or monthPos < 0 or dayPos < 0 then
// we don't know how to parse the date
// some might want to set defaults instead of returning false
Return false
end if

// now check the date has just numbers and two delimiters
Dim sep as String
Dim tmp as String
Dim i as Integer
Dim noYearSupplied as Boolean

tmp = text

// first figure out what separator they gave us .. have to both be the same one
for i = 0 to 9
tmp = replaceAll(tmp,format(i,"0"),"")
next

select case len(tmp)
case 0
// unable to understand the format entered
return false
case 1
sep = tmp
case 2

sep = mid(tmp,1,1)
if sep <> mid(tmp,2,1) then
// invalid - two different separators
return false
end if

else
return false
end select

//make array of elements
Dim dats() as String = Split( text, sep )

if UBound( dats ) <> 2 then
// add in the missing year ?
dim tmpDate as new date
dats.Insert yearPos, format(tmpDate.year,"0000")
noYearSupplied = True
end if

if UBound( dats ) <> 2 then
//invalid date - should never get here.
Return false
end if

dim yr As integer = CDbl( dats( yearPos ) )
if yr < 100 then
// fix short year by assuming current century
// proving that we learned nothing from y2k
dim today as new Date
dim century as integer
century = today.year \ 100
century = century * 100
yr = yr + century
// use any assumptions about whether the date is past or future to set century
if assumePastFuture < 0 then
if yr > today.Year then
yr = yr - 100
end if
elseif assumePastFuture > 0 and yr < today.Year then
yr = yr + 100
end if
dats( yearPos ) = CStr( yr )
elseif noYearSupplied then
// use any assumptions about whether the date is past or future to set year
dim mth as integer = CDbl( dats( monthPos ) )
dim dy as integer = CDbl( dats( dayPos ) )
dim today as new Date
if assumePastFuture < 0 then
if mth > today.Month or ( mth = today.Month and dy > today.Day ) then
yr = yr - 1
end if
elseif assumePastFuture > 0 and ( mth < today.Month or ( mth = today.Month and dy < today.Day ) ) then
yr = yr + 1
end if
dats( yearPos ) = CStr( yr )
end if

// put detail into a date object
dim retVal as new date
dim yy,mm,dd as Integer
yy = val( dats( yearPos ) )
mm = val( dats( monthPos ) )
dd = val( dats( dayPos ) )

retVal.TotalSeconds = 0
retVal.Year = yy
retVal.Month = mm
retVal.Day = dd

// check the date object is not making corrections
if retVal.Year <> yy or retVal.Month <> mm or retVal.Day <> dd then
//probably an invalid day of the month
Return false
end if

//populate value ( ByRef side-effect )
if value = nil Then
value = new Date
end if
value.totalseconds = retVal.TotalSeconds
return true

End Function

I really should put some sort of free use licence on the code but I need to speak to Norman about that first as he contributed a significant part of the code.

Sunday, 17 June 2007

So I've finally taken the plunge

I renewed my subscription to REALbasic Professional Windows edition. Now I'm licensed for all the releases until June 2008, so I feel clean. My last subscription expired in May 2006.

My wife's copy is licensed through to August this year and I've been playing with that. Probably against the terms of her licence but I've done no paid work with it.

She doesn't use it and won't be renewing. Pascal has her heart and Delphi her skill. She doesn't like RB's object model much and she doesn't find BASIC satisfying.

I'm not crazy about the latest version of REALbasic because they haven't addressed the bugs that annoyed me most but I think REAL Software, Inc. should be congratulated on producing a release that concentrates mostly on bug-fixes and much less on new features.

So I've stumped up my €275 ( including Value Added Tax ) and now I can produce cross-platform apps again with a clear conscience.

And I've rejoined the Beta program in my own right.