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