Mac OS users can count on widgets since Mac OS X 10.4 (Tiger). Widgets are small applications that float on the user desktop and do a particular job. The most known are clock, calendar, weather, the converter of currencies,…
Ideally, to be successful, gadgets have to be useful. This is why I chose to create an events manager that can notify users of upcoming events by sending them an email before the event's date.
The major part of code that deals with event management is located in
AddEvent.js. The manager is responsible for adding events, deleting and displaying them. To better understand the way it works, it's important to know the structure of an event used by my gadget.
In C#, an event structure should look like this:
Adding an event
To add an event, the user clicks on "Add Event". This link calls the function
ShowAddEventFlyout which display a flyout window (AddEvent.htm) :
System.Gadget.Flyout.file = "AddEvent.htm";
System.Gadget.Flyout.show = true;
System.Gadget.Flyout.document.MainScript = this;
System.Gadget.Flyout.document.EventId = CurrentEventId;
System.Gadget.Flyout.document.EditEvent = false;
Once the user filled all the fields for the event, fields content is validated by
ValidateData. If data is valid,
AddEvent saves events data using
System.Gadget.Settings.write with a special format:
"Event" + eventId + Property (see Event Structure):
var radical = "Event" + eventId;
System.Gadget.Settings.write(radical+"EventDate", date + " " + time);
Deleting an event
With the gadget API, there is no official way to delete an entry written with
System.Gadget.Settings.writeString. But there is a trick. In Events Manager,
RemoveEvent is the function that "deletes" events:
As you see, we don't really delete the event, we only disable it. The
Id property is set to -1. Normally,
Id is equal to
LoadEvents is responsible for displaying all events. This function is often called to refresh the events in order to take any recent modification into account.
for(i = 0, j=0; j<EventsNumber; i++)
if(System.Gadget.Settings.read("Event"+i+"Id")==i) avoid the
for loop to display deleted events. On the other hand, the function
AddLineToEventsList uses DOM to add events to the main html document (Document.htm).
While working on my gadget, I want it to be customizable. The sidebar lets gadgets specify an html file for settings. In the case of my gadget, the html page
Settings.htm and the script
Settings.js deal with the gadget's settings. Events Manager set the configuration file in the function
Init (the first function called):
System.Gadget.settingsUI = "Settings.htm";
With my gadget, you can set several parameters:
- Your email
- Events per page
- Enable sound
- Delete old events after x days
- Server-side script to send emails
When you run my gadget the first time, Events Manager has many default values. These values are hard-coded in the file
Settings.js and are accessible via the function
GetDefaultSetting is used by
SetSettings to initialize global variables.
SetSettings check for each parameter if the user has specified a custom value. If yes, the value from the user is used instead of the default value. Otherwise, the default value is used.
EventsNumber = GetDefaultSetting("EventsNumber");
EventsNumber = System.Gadget.Settings.read("EventsNumber");
Events Manager allow user to search events by entering a keyword in the search box. The function
Search is used for the search. Like
Search iterates through all the events and uses
indexOf to check if there is an event name that contains the keyword:
var offset = eventName.indexOf(keyword);
if(offset != -1)
AddLineToEventsList(i, true, offset, offset + keyword.length);
There is an implicit rule in gadget world that says that gadgets have to be visually attractive. The problem is that developers are rarely good designers. In order to achieve a good design, I chose to use the Contacts gadget design as a base for my gadget's design.
With the gadget API, we can register to some important events to make our gadget more interactive gadget. Since we speak about design, two events retain our attention:
We register to an event this way:
System.Gadget.onUndock = DockUndock;
System.Gadget.onDock = DockUndock;
DockUndock updates the size of the gadget when it docks on the sidebar or when it undocks from the sidebar.
System.Gadget.docked is used to determine whether the gadget is docked or not:
document.getElementById("Body").style.width = "134px";
document.getElementById("Body").style.width = "268px";
document.getElementById("LeftPage").style.visibility = "visible";
How to send email?
There is no function in the gadget API that sends emails. However, we can use the ActiveX object
Microsoft.XMLHTTP to send an HTTP request to a server-side script. The script will do the rest of the job.
Let's see the function responsible of sending email:
var radical = "Event" + eventId;
var result = true;
var xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
var email = System.Gadget.Settings.read("YourEmail");
var name = System.Gadget.Settings.read(radical + "Name");
var description = System.Gadget.Settings.read(radical + "Description");
var date = System.Gadget.Settings.read(radical + "EventDate");
var message = "You have an upcoming event : " + name + ".\nDate : " +
date + "\nDescription :\n" + description +
"\n\nSent by Event Manager Gadget.";
if(email != "")
xmlHttp.send("address="+email+"&subject=notification : "+
if(xmlHttp.status != 200)
result = false;
result = false;
The server-side script url is retrieved via
System.Gadget.Settings.read("ServerSideScript"). The script should accept three parameters in POST mode:
message. For example, this is the code of a little PHP script compatible of my gadget:
$adresse = $_POST['address'];
$sujet = $_POST['subject'];
$message = $_POST['message'];
$message = wordwrap($message, 70);
mail($adresse, $sujet, $message);
Debugging a gadget is really easy. Indeed, it's as easy as debugging a script in Internet Explorer. First, runs IE and in the menu: Tools -> Internet Options -> Advanced -> uncheck Disable Script Debugging (Internet Explorer) and Disable Script Debugging (Other).
Now, if an error occurs, you can use Visual Studio (or other script debugger) to debug.
Usually, you will see a window that looks like this:
Deploy the gadget
To deploy your gadget, follow these steps:
- Compress all the files inside your gadget folder (zip or cab)
- Change the .zip or .cab extension to .gadget
- Double-click your .gadget file to install your gadget
It's easy but these steps can take a considerable time when you often deploy your gadget. This is why, to make deployment easier, I developed a little application called Deploy Gadget:
More information about deployment here.
With globalisation support, gadgets can be multilingual. You just have to create for each culture, a specific folder with the codename of the culture. It's not necessary to duplicate all the files, only those of the user interface. Events Manager supports two languages: French and English. This is my gadget's folder tree:
More details about globalisation here.
Complements and reference
Sound and music
In the gadget API, there are two functions that play sounds and music:
The first one generates a simple beep. The second function plays sound file. Events Manager uses playSound to notify user of upcoming events:
When the number of events become superior to the number of events per page, my gadget display a scrollbar to allow users to see all the events in the list:
I use a free control from Tigara. You can download it here.
Calendars are really useful to easily pick up dates:
You can download this nice control from Matt Kruse website. From the same author, I also used his date's management library.
Each gadget's have one manifest. This xml file contains information about your gadget like the author's name, copyright, gadget's name,… The data in you manifest file is used by the sidebar to load and display correctly your gadget. The manifest file is named "gadget.xml" (the name is not an option).
A typical manifest file looks like this:
<name>Name of the gadget</name>
<author name=" Author name ">
<info url="http://www.example.com" />
<copyright> Copyright </copyright>
<description> Gadget description </description>
<icon height="48" width="48" src="icon.png" />
<base type="HTML" src="YourMainPage.html" />
<platform minPlatformVersion="1.0" />
<defaultImage src="drag.png" />
You can find a list of all fields and a complete description for each of them here