URLImage
I wanted to create a gadget that used the network, as that provided a degree of external activity that we hadn't really seen in gadgets. The URLImage gadget was the result and, as the name suggests, provided a way to render an image fetched from a URL. I envisaged it being used for something like favicon indications in a browser, or maybe some simple images, like !RSS had for site logos. Because the intended use was pretty undefined, the requirements weren't at all defined, and I just implemented what I thought was useful.
I had URL fetching buffers lying around from the earlier work (they had been used in the !NetSurf URLFetcher replacement, but even then they had come from another project), and they were used to fetch the data. They were extended to provide more feedback and useful progress meters. The gadget exposed the progress by showing how much data had been received at during the fetching period.
The handler would treat the image according to the Content Type header,
which meant that some images didn't come out right. It was common to find
GIF or .ico
files that were delivered with the type
'text/plain
' or 'application/octet-stream
'. In
order to support these, there were some special checks for the type to
make sure that if we thought it was a GIF or .ico
we would
treat it as those types, regardless of the Content Type.
The gadget would queue requests, so that it wasn't fetching more data at a time than was sensible - I think it used something like 10 or 16 fetches at a time. Once fetched, the image could be rendered directly through the ImageFileGadget as the source data (which required less memory), or converted to a sprite first before passing to the ImageFileGadget (which would be faster but generally took more memory).
It didn't handle errors amazingly well, but it did work quite happily in the background, loading images when the window containing the gadget was shown. You could click on them and URLImage toolbox messages would be raised.
I was hoping to add a little caching, and some better memory handling to it before ever letting it out. It would probably have been used for the Mozilla browser, as part of its image handling, although even that might be more complicated because it would need to pass through authentication details and maybe cookies.
ColourSwatch
The ColourSwatch gadget.
The configuration tools from RISC OS 4 onwards had begun to provide
more configurable colours for the various WindowManager (and
other) settings. Many of these settings were now able to take 24
bit colour specifications, rather than just the standard 16 colour
Wimp palette which had been used previously.
In these cases, the applications provided their own implementation of a 'colour swatch' - a small representative colour which can be changed through a colour dialogue. This seemed a little wasteful, as the code could easily be shared between the applications, so I created a simple gadget that would do the same thing.
The gadget itself is made up of a display field using the 24 bit colour specification validation strings that were added in WindowManager 3.98 (the 'nested Wimp'), and a menu button which opened the dialogue box. The original implementation in the applications used 2 colour sprites, with a palette entry that was manipulated. Whilst this was relatively simple, it is simpler to use the 24 bit colour specification validation string.
Although it provides a better way of handling the colour selections, it isn't possible to use this gadget on earlier OS versions - which includes RISC OS 3.7 and before. This was a decision I made when implementing the gadget - that I didn't care about those versions. The configuration tools which used the gadget were not intended to be used on the earlier systems so it wasn't a problem.
The ColourSwatch was also one of the first Toolbox gadgets to be documented in the !PRMinXML format from the start. It wasn't significantly easier than writing a text document to describe it, but it also wasn't much harder - and gave the benefit that the gadget was ready to be documented for public use without any additional work. And that was the purpose of the !PRMinXML work, so I think that was pretty neat.
The gadget never had any real external visibility, I believe, but it would have been useful for quite a few configuration and settings windows in applications. Assuming, of course, that people were to use the Toolbox to develop applications.
AMPlayerGadget
Having timers is all well and good, but if you've not got something that is time based, it isn't all that interesting. The Animated Icon gadget showed the principle, but even a simple animation using sprites isn't all that interesting. I had implemented basic digital clock gadgets, which I haven't shown here because... well, they are really, really dull, and everyone has seen a digital clock before. I considered creating an analogue clock - and it would have been pretty easy to just grab the code from !Clock and use that, but it wouldn't really show much more of the interface than the Bouncing Ball did. It would just be a timed event, albeit tied to a clock, rather than free running.
AMPlayer had a simple interface, and lent itself to having a different set of gadgets to provide its controls. In some respects, it is just an implementation of the !AMPlayer application. However, as the controls it provides through the gadget handle the 'basic' AMPlayer operations, it means that any application can get on with any other features it wants to handle - play lists, visualisations, or any other special features.
The gadget itself is a little different to many of the others, in that it is a single gadget which can be provide many different functions and representations. One field in the gadget selects the type of gadget it provides - a button, a volume or position slider, a flag indicator, or a display of ID3 or MPEG information from the playing file. Part of this stems from being a single gadget providing a lot of functions, and part from the way that I had implemented the object controls in the !AMFiler plug-in.
It is possible that instead of having different sub-types, these should have been separate gadget types - there would have been quite a few gadgets if I did that. Or maybe each group should be different gadget types, with the buttons based on one type, the information based on another type, and the sliders based in a third.
As the control was a little specific, I'm not sure it would be as useful as some other more general gadgets, but that was never really the point. The point was to create a gadget which could demonstrate that they need not be very general. If an application required some general way to display whatever detail it provided, it could be done through a gadget. It makes creating different interfaces much simpler, and they can be done through the regular !ResEd and !ResTest tools.
Testing the AMPlayerGadget never needed an actual application that used it. All it needed was to be loaded into !ResTest and it was functional.
(Animated version (71K))
There are always going to be gadgets which are general, some general type gadgets that are less used, and then application specific gadgets. Having the ability to provide all three is very important. The general gadgets were provided by the basic Window Object. The lesser used gadgets, like dials, special types of buttons, animations, and meters and graphs weren't really provided by the standard set (unless you consider the ToolAction gadget to be one special form of button). The ImageFileGadget provided one lesser used, but quite useful, gadget function. The TextGadgets module provided at least the Text Area and Scrolling List, which were pretty useful but don't fit into the common general category.
The AMPlayerGadget really falls into that latter category of application specific gadgets. Flexible for their specific use, which they're very good at.
I had wanted to write a few other types of gadget. One was a ZapRedraw gadget, which could handle much of the work of rendering either an array of text, or a sequence of text in ZapRedraw encoded format, and which just dealt with the rendering in whatever font was configured. It would have been pretty simple, but have made writing such terminal type applications a lot simpler - and more fun because you could just get on with the application rather than worrying about the rendering.
Another gadget I would have liked to write, which is directly related, was a terminal gadget - to which you would supply terminal control codes, much as you would get over a remote link, and it would perform layout. Essentially this might have taken the Nettle terminal core, and placed it in front of the previously mentioned ZapRedraw gadget to provide an 'easy' terminal implementation which was reusable. This would be the application specific gadget, but the reusability would mean that other applications could make use of it if they wanted.
I have already mentioned dials and graphs, which I would have liked to have in some form, and although the scrolling list had been extended to have columns I also wanted a proper table gadget. Not entirely sure how that would have worked, but I had discussed with Rik Griffin some of the ideas he'd had for the Tree View gadget, and many of those ideas were transferable to a table. I had already got a partial spreadsheet application written, which handled a lot of the layout and positioning for the tables. Repurposing that to a gadget would not have been too hard. As with many of the more advanced gadgets, the duplication of data - having part stored in the application and part in the gadget - was always going to be a concern, but I had not really thought much more about it.
I still wanted to move the Mozilla engine into a gadget. This had been one of the plans from the outset of that project, albeit a little bit of wishful thinking. It would be nice to either just request that a gadget render a URL, or to provide it with a bunch of data to render as HTML. Very vague work had gone towards that, and because it was a goal, the implementation of the code in the application was intentionally modular, such that it could be moved into a gadget if necessary in the future. The concerns about partial layout come a little from the thought that the gadget might process HTML content on idle polls under a gadget's control.
With the ability to perform rendering directly into a window, rather than being into a separate nested window (as the Text Area or Scrolling List had done) meant that the gadget could appear much more a part of the application than as a plug-in. The concerns over speed (and memory use) had led me to think later in the development of Mozilla that it might be more general to provide a gadget which was a browser plug-in manager. This led to some of the work in Mozilla to support both ends of the plug-in handling. The plug-in manager itself could have moved into a gadget, which the Mozilla application could use to invoke plug-ins.
It was still a quite bit of wishful thinking, and maybe it wouldn't have worked, but I always wanted to push things in ways that might be useful and haven't been done before. At least not on RISC OS.
Map gadget
The URLImage gadget was of limited use in general. It was obviously handy in a few cases, 'favicons' - the icons used to represent icons in URL bars and bookmarks - being a good example. But I really wanted to make it useful for something, without being sure what. The Map gadget was an attempt to try to use it, and do something vaguely interesting at the same time. For reasons I forget, I wanted to use MultiMap to provide a way to view the map.
It has a relatively simple web interface, and doesn't require any complicated negotiation, so would work with the plain URLImage without any changes. Again, for reasons I forget, I decided to have an interface that presented you with a map based on the postcode, rather than any form of grid coordinates. I am sure there was all some sane reason behind it, but I don't remember any more. So, despite the MultiMap API being presented in grid units measured (it seems) in metres, the gadget took a postcode - and then converted that to coordinates.
The postcodes database costs money from the Post Office, but there are freely available versions online. One that I used (which is still available at Jibble.org) comes as a Zipped CSV file. So... I created a Postcode library which could convert from postcodes to metre coordinates, by downloading the file to a cache (or using the cache if it was available), decompressing (to a cache) and parsing the CSV file.
The little Postcode library had its own simple test application to make sure that the CSV parser worked, and that the postcode conversion did the right thing. It exported the library in the usual way, as discussed previously, providing the 4 major variants of the library. The Map gadget would use this to perform the conversion, polling until the postcode library was able to do its work (as the download might take time), and when complete, the URLImage would be given the correct URL to show. After a few moments to download the image, you would have your map.
When shown in !ResEd, the gadget would appear as the configured postcode and the scale it was configured to display at, as a couple of lines of text in the region covered by the bounding box. It wasn't pretty, but it did the job. It was actually quite nice to see it working, albeit not that useful in general. I don't remember whether I even had a planned purpose for the gadget other than to show that you could do something reasonably interesting.
It reminds me a little of the Opera Skins IconBorder module
in the sense that that module would unzip the Skin archive into memory,
decode the .ini
file that described the Skin, then decode the
PNGs from the zip into a sprite cache to be used for rendering the
components of the buttons. In that case, I had to create the
.ini
file reader just to get at the data, and of course the
image caching and unzipping were pretty simple but had to be implemented
as well.
In the case of the Map gadget, the CSV parser was created from scratch, and the unzipping had to be implemented as well - not quite as complicated but it certainly is similar.
I guess a question that might be asked might be why create the parsers from scratch rather than using other free implementations? Well... there were a number of reasons. First and foremost is that of a licence under which the 'open' source is offered. GPL is obviously impossible to work with, LGPL difficult to work with, BSD is lovely, ZLib licence is equally nice, and then there's things like the SQLite 'licence' that give you a freedom that makes you realise that there are really great people out there.
Another is the quality of the code, and the support. If you grab a bunch of 'free' code which was written for some random project, and did what was needed for them, then maybe that was fine for them. But for the use you want to put it, it might not be acceptable. Code that assumes it can build up strings on the stack do not play as well within modules. In many cases, the concept of 'error checking' is alien to libraries out there - there is a lot of really bad 'free' code, and you get what you pay for.
Which isn't to say that there isn't some good code available, but you need to understand what it is that you are trying to gain by using open source code. The idea is to reduce the maintenance cost by having code that has already been written, and you just have to audit. If the code is bad, you can fix it. If it is very bad, then you may spend a lot of time fixing it. If it is badly designed, or unsuitable for your purpose, then you might find yourself rewriting the code anyhow.
If the time saved is less than the time it would take to write something from scratch - which of course can be used without restrictions, etc - then there isn't a benefit in using the open source components. There are arguments about bugs being found because more eyes are on the code, but that doesn't apply to all open source - and really only comes into its own in very high uptake applications and libraries.
I realise that this is not news to many people, and might seem like a bit
of a waste of time to say, but it occurs to me as I'm writing about such
simple libraries as CSV and .ini
parsing. I do remember looking
at libraries for both. I found a few, after excluding those in languages
that didn't help (Delphi and Java implementations need not apply!), and the
licences that weren't acceptable there were only a few. The few that I had
found were poor, or the code exhibited assumptions about the environment
in which they would run which wouldn't be true (or were just compiler
specific, which made them non-portable). It was just easier to write my own
than waste more time trying to find some library that would do the job.
OptionsWindow
As I mentioned in the Configuration ramble, I created a new object specially for the configuration of applications, called the OptionsWindow. The object needed to create a scrollable window with a number of sections, in the same style as was popularised by !Browse, and was being introduced into some of the Toolbox applications that were being written.
In some respects, having a very large number of options is counter productive. It overwhelms and makes it difficult to find what you want. It also means that those options need to be tested, and this can become a real burden for a lone developer - or you can be that clever that you know what the implications are of the options and just Know that it's all safe (which I felt at the time - I'm still pretty sure that the combinations of options that you can set are not dangerous, but I wouldn't feel quite as confident as I did at the time).
The alternative view is that hiding options means that they might as well not be options in the first place, and it provides the user with the power and trust to do what they want, rather than being forced into what might be a narrow view of the developer. I tried to favour this point of view, although there were always going to be areas that exposing options became more dangerous. Consider the inclusion of firewall support in the Internet module - the options were there for years, and never (to the best of my knowledge) exploited until Select introduced a tool to configure them (despite there never being a front end for it). The support was actually quite reasonable, despite not being stateful, and could have been used all that time with no bad effects.
Anyhow, if you favour the ability to configure features in their multitude, you need a means to group the options together so that they can be found. The Window configuration tool, which controlled most of the options for the WindowManager, had the first need of this as I began to provide the new options.
The number of configuration options available in the original configuration window were limited to those that had been present in earlier versions of the configuration application. I wrote an implementation early in Select 2 development for the tool, and began adding options. The code was actually separate at this point from the main application, kept in a different directory, but built as part of the application.
The intention at that time had been to move the library to Lib
and make it export itself together with the other libraries. When it came to
creating the Icon configuration tool, which controlled the presentation of
icons - their 3D effect, round borders, and so on - I didn't move the
library out, but just copied wholesale to the new application. From my notes
it looks like the reason was purely so that I could get on with creating the
configuration tool and not worry about the library at that time.
Whilst working on the Toolbox gadgets, I remembered how I'd wanted to make the library more accessible, and it was obvious that it should be a Toolbox Object. By being an shared object it would take advantage of the central support of the Toolbox module and would encourage its use. Well, 'encourage' in as much as Toolbox was actually being used by developers.
Much of the original code was retained as the object was created, as much of the code had been intended to be made into a library in the first place. The library had only supported the ability to associate the panes at run time in the code - the application had to call functions to add the panes to the window. As Toolbox object, though, this could be automated by associating a list of panes with the object (as a comma separated list). This meant that the entire operation of the OptionsWindow can be tested in !ResTest as just a Resource file, with no other support from the application.
It was still possible to manually associate panes with the object, through Object methods, and to directly select a specific pane. Adding and removing panes at run time wasn't really a very user friendly thing to do, as it meant that the options were inherently variable. However, there are likely to be cases where that's more suitable than fixing them in the Resource file, so they're still useful. Similarly, the forceful selection of an options window was a rarely needed function, but could be useful to direct a user to a particular group of options if invoked from a specific configuration button in the application.
The object reduced the work needed by the application by limiting the configuration to individual panes. As a pane became visible, its settings would be requested from the application. If a second pane was selected from the options list, its current state would be requested. Returning to an already set pane would obviously not reset its settings to the current state. Pressing the 'Default' button would request that the application reset all the panes to their default state.
The Object could be configured to display a selection of buttons, 'Set', 'Save', 'Default', and 'Cancel' were all available. Operations like adjust-click on 'Cancel' to restore to the current state were provided by the Object. Like the Print dialogues, it was possible to replace the default template window with a customised version, which allowed other buttons to be added, although they would have to be supported by the application, obviously.
The object retains its own 'modified' flag state, and watches for the
*_ValueChanged
events from gadgets in the panes, which cause it
to update the modified state. There were a few documented restrictions on
how the pane windows should be created, so that they worked with the
OptionsWindow object, but these were pretty simple.
The most obvious of the
restrictions was that the title bar was disabled, but still had text present
- the value of which was used as the radio button's label.
I had toyed with the idea that the label be looked up in the Messages file implicitly, to allow it to be internationalised. This was unnecessary, though, because by keeping the value in the Resource file - which had to be translated in any case - it was beside the place where it was used, which made much more sense from a design point of view.
The documentation was all written, from the start, using the !PRMinXML
format, so that it could be incorporated directly into the revised PRMs.
Looking at it now, it is a little rough around the edges, and has quite a few
<fixme>
regions that need to be addressed, but these
are mostly structural - the pages referred to aren't in a known location
yet, so cannot be referenced.
The object was pretty much completed and the Window configuration tool had been converted over to use it, to prove that it was actually functionally useful. The down side to making the configuration use the Object was that the Object had to be available on all versions of the OS which used it. Two choices were available (actually more, but these were the ones I favoured) - either restrict the use of the OptionsWindow to the later versions of the OS, and make the WindowSetup tool only load on those later versions, or to make the OptionsWindow object available in the Select distribution as a softload component as well as built into the ROM.
I had intended to follow the latter method, as the usefulness of the object when it isn't available to others is reduced. That didn't necessarily mean that it would have been available as part of a public released Toolbox module set, but I was certainly leaning that way. It's great to give things away, but when people have paid for the development it is counter productive to just give things away.
Disclaimer: By submitting comments through this form you are implicitly agreeing to allow its reproduction in the diary.