|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionIn this article I would like to help you make your second gadget. As you have probably already found out, the Gadget APIs are not so well documented yet (and not very rich), there are some bugs here and there, and not everything works the way you want it to the first time around. Moreover, being an experienced .NET or C++ developer does not spare you from some perfidy in JavaScript and DHTML. I've decided to share my experience of getting somewhat further into gadgets and I hope I will save you some unnecessary moments... What you will need:
What you will not find here:
What you get:
Before you begin the second gadgetHow long ago did you create your first gadget? A month ago? Half a year? This will determine what changes you will need to make it so that your gadget will work with the sidebar in the release version of Windows Vista. Working with beta versions of software has a disadvantage: breaking changes may be made. First of all, I would strongly recommend that you read the known bugs list managed by Jonathan. It may help you with some issues you could run into. Also, please any report bugs you find, either to the MSDN forums, or to Jonathan's list if you have an aeroxp.org account. Sidebar Gadget ManifestTo make our gadgets discoverable by the sidebar, we need to make a What if the sidebar 'does not work'The bad thing with the manifest is that if something is messed up inside
it, sidebar will silently overlook your gadget even if it is in the gadgets directory,
and will remain quiet even if you try to install it. If this happens to you, check
the gadgets directory for the current user (usually
Check to be sure that the file is a valid xml file. If you can't find the mistake,
I would recommend that you start over by modifying any manifest which obviously
works (perhaps try some from EncodingIf you come across a message during the installation that says that the manifest is invalid ("this is not a valid gadget package"), first check that you have the file saved using the encoding specified in the xml document element. This should be UTF-8, but if you started your manifest from scratch in notepad, it will have been saved using ANSI encoding by default. This should not be a problem, though, unless you use Unicode characters. But if you use UTF-16, you may have problems. The easiest way to find out how a text file is encoded is to open it in Notepad and click Save As... . The encoding selected in the Save dialog box is the one that is currently used. If there are no problems there, look for the problem somewhere else or start over. SchemaThe (informally-defined) schema has changed slightly from the Beta 2 version. The very bad decision at the Microsoft side (at least in my opinion) was to not declare any formal schema/namespace for the gadget manifests to use. This means that no formal validation is available - see above. This also means that I cannot give you a schema that you can use in Visual Studio for Intellisense. Our Garfield's gadget manifest looks like this: <?xml version="1.0" encoding="utf-8" ?>
<gadget>
<name>Garfield Comics</name>
<namespace>UAM.InformatiX.Windows.Gadgets</namespace>
<version>2.5</version>
<author name="Jan Kuèera">
<info url="http://www.codeproject.com/" text="The Code Project" />
<logo src="Logo.png"/>
</author>
<copyright>© Pawn, Inc. since 1978</copyright>
<description>Watch Garfield daily comics!</description>
<icons>
<icon height="48" width="48" src="Garfield128.png" />
</icons>
<hosts>
<host name="sidebar">
<base type="HTML" src="Garfield.html" />
<permissions>full</permissions>
<platform minPlatformVersion="1.0" />
<defaultImage src="Garfield128.png" />
</host>
</hosts>
</gadget>
It has all possible elements and attributes that are supported (I believe), so feel free to copy this manifest and modify it for your purposes (don't forget to save in UTF-8). The elements marked in green are required by the sidebar. The grey elements are not used by sidebar version 1.0 and are reserved for future use. Here are some notes on usage that you might find useful:
In all image-related tags you can specify any image stored in format supported by GDI+ 1.0: PNG, GIF, JPEG, BMP, TIFF, EMF, WMF and icons (ICO). Some of these statements are from Sidebar Gadget Manifest at MSDN, where you can get more (but not much more) detailed info. In the Image Feed article you can find a screenshot showing how these tags are used in the Gadget Gallery. Gadget script changesDepending on how long before you made the first gadget, some changes apply to the Gadget APIs and script behaviour. Some gadgets written for Vista Beta 2 release may fail to function correctly. alert() is goneNote to experienced JavaScript developers: just ignore this paragraph. :-) Probably the most deceitful change you find out very quickly is that alert()
message boxes are being suppressed now. I don't want to speculate on whether
this change is good or bad, but it's there and we have to work with it. If you used
the message box to show a message to the user, use some non-modal way for notifying
users instead. If you used it to watch variables then you have to add a status element
or use some kind of tooltip. But using a debugger would be easier and more desirable.
And now, the important question: How do I break in the debugger if I cannot put
the magic
If you already have the debugger attached, you can use Dropped APIsHere is a quick list of the functionality that was removed in RC1. The functions should be accessible from Windows Management Instrumentation, through Shell or using FileSystemObject scripting. I don't know what the actual recommended replacements are because I haven't needed them, but if you are interested, let me know, and we will try to find that out. Be sure to take a look at RC1 Changes to the Sidebar APIs at Gadget Corner for the full list of changes.
The development environmentIf you did the last gadget in Notepad, consider switching to an IDE, most likely Visual Studio. Not only do you get the syntax highlighting (which I found quite useful as the code grew), but most importantly, you get comfortable debugging capabilities - you can easily watch every object's state and properties (and events and methods in Orcas as well), which is invaluable when working with DHTML. Browser vs. sidebarIf we are going to create HTML stuff, should we test it in the web browser at first? I would say not necessarily. The things you would likely have troubles with are sidebar-specific. You don't have the dimensions you will have in the sidebar, you can't test docking, dragging, flyouts, settings, etc. Moreover, if your favorite browser is not Internet Explorer, you will probably face some layout differences, since the sidebar engine uses IE, of course, and I'm not so sure you are able to change it. On the other hand that means that you can use some IE-specific stuff, like filters or expressions in CSS. There is one situation where you will find Internet Explorer useful, though. If you have problems with the HTML/layout part of your gadget, you may want to use the Internet Explorer Developer Toolbar, which displays your HTML structure, and even allows you to make changes to the document. So you can try things to find out what would work and then include it in your code.
If you want to work in the web browser, keep in mind that the Gadget APIs are not available. It makes sense to track this state in the code to avoid script errors and retain functionality: // true if the gadget is being hosted as a gadget,
// false otherwise (eg. viewing in web browser)
var IsGadget = (window.System != undefined);
Edit and continueThey are two common ways of installing the gadget. You can either execute the .gadget ZIP archive or simply copy the files to the sidebar folder. As a developer, you should try both of them. First, pack your manifest into a ZIP archive, rename it to .gadget and see what happens. If everything is ok, you are ready to write the code. In order to maximize the comfort of development, work directly in the gadget's folder.
Just use the Open web site command from Visual Studio and point it to the
user's gadget folder, As you probably know, edit and continue is not supported for scripting. If you find a mistake in your code, you have to stop debugging to be able to correct it. What should you do next? Reinstall the gadget? Restart the sidebar? You don't have to. After changing:
Enable script debuggingIf you have for any reason disabled script debugging in Internet Explorer, you will have to enable it for the debugger to work. You can find this option in Internet Options, Advanced tab, Browsing group. Uncheck Disable script debugging (Internet Explorer) to debug your gadget in IE, and uncheck Disable script debugging (Other) to debug your gadget in the sidebar. You may also find it useful to check the Display a notification about every script error option, if you find it hard to notice the little exclamation mark in the IE's status bar. Sinking deeperBefore I give you a few general hints I discovered, I'll say a few things about the attached source code. ..and this is my cat, Garfield.The purpose of my gadget is to display daily Garfield comics. I have to say that in the middle of writing this article, Rajesh Lal posted his article Daily Dilbert 1.0 - A simple sidebar gadget, with pretty much the same aim. I have decided, however, to finish and post this article, so I hope that you aren't too bored with the idea, and I will bring you some new or helpful things. I believe that examples are a very efficient way of learning, and I have commented the code as much as I could, so take it as an important part of the article. Hints and TipsGeneralSecurityThis paragraph is here only to note that some security precautions may impact the functionality. Gadgets:
I made this list from the Sidebar Security post at Gadget Corner, where you can find more details on this topic. Docking and undocking practicesYou can supply only one shared HTML page for both the docked and the undocked state of your gadget. The bad thing is that you have to manage the layout changes yourself. The good thing is that you don't need to synchronize variables between these two states. So the normal solution is to put the two layouts into containers and display only one of them at a time: <body>
<div id="DockedModeDisplayArea"> ... </div>
<div id="UnDockedModeDisplayArea"> ... </div>
</body>
You do this in the handlers for the The minimum size that a gadget can be (both in docked and undocked mode) is 20x57 pixels. Anything smaller will get filled with white. So you cannot create a wide, thin gadget with a label - but on the other hand this size limitation comes in handy when something go wrong (it makes it so that you don't have lots of almost-invisible gadgets all over the place). In my opinion, 20x20 would do as well - the inability to create 'label' gadgets can be troublesome. Update: You can of course use a transparent background to work around this, but remember that you can place elements on opaque areas only. TransitionsEver wondered what the function UIChange()
{
// after this, the image of the gadget is frozen
System.Gadget.beginTransition();
// now do any changes you want - dimensions, content, whatever
// the changes will not be visible
System.Gadget.endTransition(System.Gadget.TransitionType.morph, 1);
// now, the frozen image will morph into the new one
// over the course of one second
}
Unfortunately it does not always work as expected. I couldn't use it in the Garfield gadget, because I'm changing the layout. You can try this if you follow the comments and description in the attached code. Briefly speaking, use the transition only when you want to get a zoom effect. Be careful of the timing - the sidebar is almost unresponsive during the transition and the time unit is in seconds. At the time of writing this article, the MSDN documentation was still archaic,
so it was not possible to find out which Draggability of your gadgetsNormally you can drag gadgets only by using the sidebar move button ( <body unselectable="on">
// you can drag by clicking anywhere inside the gadget
</body>
Note that you cannot drag a gadget when the flyout is being displayed. Refreshing dataIf your gadget monitors something, you would probably like to refresh data or
the information you display. You have two options to do that: var cancelID = 0;
function StartRefreshing()
{
// calls RefreshMyGadget every second
cancelID = window.setInterval(RefreshMyGadget, 1000);
}
function StopRefreshing()
{
// pause continuous refreshing, to resume call StartRefreshing
window.clearInterval(cancelID);
}
var pendingID = 0;
function RefreshOnce()
{
// calls RefreshMyGadget after second
pendingID = window.setTimeout(RefreshMyGadget, 1000);
}
function CancelPendingRefresh()
{
window.clearTimeout(pendingID);
}
function RefreshMyGadget()
{
...
// uncomment to simulate setInterval by setTimeout
// pendingID = window.setTimeout(RefreshMyGadget, 1000);
}
You typically use Function pointers are used in the example, however, you can use strings as well. This gives you the ability to call functions periodically with different parameters and if you draw this up, you can change the timeout on the fly to get more cool animations: var aniHandle = 0; //timeout handle for animation
function animateHeight(desiredHeight, delta, timeout)
{
// desired height not missed?
if ((delta > 0 && document.body.style.posHeight < desiredHeight) ||
(delta < 0 && document.body.style.posHeight > desiredHeight))
{
// magic delinealiser
timeout = timeout * 1.3;
document.body.style.posHeight += delta;
aniHandle = window.setTimeout('animateHeight(' + desiredHeight +
',' + delta + ',' + timeout + ')', timeout);
}
else
{
// modify the height to exactly
// match the desired one
document.body.style.posHeight = desiredHeight;
aniHandle = 0;
}
}
You may like adjusting the Visibility & performanceIf you refresh data or update UI periodically, you should ensure that there is
a reason for that and that you don't waste computer resources. Check the
As described in the
Handling
gadget visibility changes post at the
Gadget Corner. Of course,
you don't have to poll the Downloading and saving filesHow do I download a file? This is quite common question and here is the answer. At first you need to download the file and then you have to save it to the disk. Here is the script: function DownloadFile(url, savePath)
try
{
var xmlRequest = new XMLHttpRequest();
// synchronous open
xmlRequest.open("GET", url, false);
xmlRequest.send(null);
// thus, always xmlRequest.readyState = Loaded here
if (xmlRequest.status == 200) // HTTP 200 OK (we have data)
{
var stream = new ActiveXObject("ADODB.Stream");
stream.Type = 1; // binary mode
stream.Open();
stream.Write(xmlRequest.responseBody); // write data to the stream
stream.SaveToFile(savePath, 2); // overwrite if exists
stream.Close;
stream = null;
}
else // HTTP error, like not found or access denied
{ ... } // text available at xmlRequest.statusText}
catch(exception) { ... } // something else went wrong
For getting the response, you need to instantiate an var xmlRequest;
var savePath = "";
function DownloadFileAsync(url)
{
xmlRequest = new XMLHttpRequest();
xmlRequest.open("GET", url, true); // asynchronous today
// somebody needs to get notified
xmlRequest.onreadystatechange = SaveFile;
// when the response is available
xmlRequest.send(null);
}
function CancelDownloading()
{
xmlRequest.abort(); // this also removes the onreadystatechange handler
}
function SaveFile()
{
// You need to check whether the object is ready because
// this function gets called also on open, send and receive
if (xmlRequest.readyState < 4) return;
if (xmlRequest.status == 200) // HTTP 200 OK (we have data)
{
var stream = new ActiveXObject("ADODB.Stream");
stream.Type = 1; // binary mode, default is 2 - text
stream.Open();
stream.Write(xmlRequest.responseBody); // write data to the stream
stream.SaveToFile(savePath, 2); // overwrite if exists
stream.Close;
stream = null;
}
}
...you need to supply a pointer to the function that will handle all the
Also:
In order to save data to the disk, you have to create an You may want to ask the user where the file should be saved. You can use
AccessibilityYou should take care of keyboard users. Believe it or not, the sidebar can be accessed using keyboard:
At that point you can usually use the Tab key to cycle through elements
on your gadget HTML, so it makes sense to make your gadget keyboard accessible.
If you heavily use <!-- using href="#" causes reload -->
<a href="javascript:void(0)" onclick="this.blur()"><img onclick="..."/></a>
(Do not include the function keyboardNavigate()
{
switch (event.keyCode)
{
case 9: break; // Tab
case 13: break; // Enter
case 27: break; // Escape - good practice to hide flyout here
case 32: break; // Space
case 33: break; // Page Up
case 34: break; // Page Down
case 35: break; // End
case 36: break; // Home
case 37: break; // Left Arrow
case 38: break; // Up Arrow
case 39: break; // Right Arrow
case 40: break; // Down Arrow
case 79: // O
if (event.ctrlKey) ... // Open (Ctrl+O)
break;
case 83: // S
if (event.ctrlKey) ... // Save (Ctrl+S)
break;
}
}
Key codes are not case-sensitive. Don't forget to set focus to the body during
load ( SettingsOpening the boxYou cannot open the settings dialog box from code (unless of course, you make
a DLL that will emulate some crazy keystrokes :)). If you need to display the settings
page, the best you can do is to load it into a flyout. Remember in this case that
you won't have the OK and Cancel buttons, so you will have to create them. How can
you find out if the code is displayed in the flyout or in the settings dialog box?
You could compare the // The place you show the settings from code
function ShowSettings()
{
// set it prior the flyout file because
// the flyout may be already open
System.Gadget.Settings.write("SettingsInFlyout", true)
System.Gadget.Flyout.file = System.Gadget.settingsUI;
// remember to simulate closing the dialog box as well
// if you handle it in the main script
System.Gadget.Flyout.onHide = SettingsClosedFunction;
System.Gadget.Flyout.show = true;
}
// Located in the settings HTML
function SettingsLoad()
{
...
if (System.Gadget.Settings.read("SettingsInFlyout"))
{
// do not forget to clear the state for next call
System.Gadget.Settings.write("SettingsInFlyout", false);
divButtons.style.display = 'block';
// <div id="divButtons" style="text-align: right; display: none">
// <input type="button" value="OK" onclick="CommitSettings" />
// <input type="button" value="Cancel" onclick="CancelSettings" />
// </div>
}
}
You should also handle Enter and Escape keys to turn this into perfection... Settings storageYou cannot access elements on the settings page within the main gadget either
vice versa. The only way to communicate between these two is to use DefaultsFrom the point of instantiating your gadget up to the first committing of settings dialog box, there are no settings set. If you try to read a setting that does not exist, you get an empty string. It is a good idea to specify a set of default settings: var defaultSettings = []; // store some defaults
defaultSettings['AutoSave'] = false; // in array
defaultSettings['FavouriteNumber'] = 25;
function readSetting(name)
{
var r = System.Gadget.Settings.read(name);
// if none found, return default settings
if (r == '') r = defaultSettings[name];
return r;
}
You can place the array in localized folder, if you need to specify different defaults for different cultures. See the Localization chapter below. UI notesI ran into three surprises when I was building the user interface:
Testing settingsWhen you try to set settings on a gadget, it works well. Now, when you want to see if the settings are persisted, you might close the gadget and instantiate a new one, but the settings are gone! This is because it is another instance of the gadget and the settings are saved per instance. It makes sense if you realize that you can have multiple instances of the same gadget shown at the same time. And when you have multiple instances, it is unlikely that you want them all to show the same thing, isn't it? ;-) The solution is easy. No system restarts, no re-logins, just exit (not close) the
sidebar. Leave the gadget placed on the screen, right-click on the system tray icon
( If you need store some settings that are persisted between instances, you can try the Persistent Gadget Settings library from Windows Sidebar team. FlyoutsShowing upWorking with flyouts is similar to working with settings, except that you have two-way
communication between the flyout and the gadget. The variables are not shared and
still the only common object is
Am I a flyout?If you use the main gadget file for both the gadget itself and the flyout - as do I in the Garfield gadget - it may come in handy to know whether the page is displayed as a gadget or in the flyout window. My solution looks like this: function loadGadget()
{
...
if (IsGadget)
{
// flyout and the actual gadget share the same System.Gadget object
// so this block will already be executed and settingsUI set if we
// are opening flyout from the gadget
IsFlyout = System.Gadget.settingsUI !== '';
if (!IsFlyout)
{
// oh yes - and we don't want to replace handlers already attached
System.Gadget.onDock = updateSize;
System.Gadget.onUndock = updateSize;
System.Gadget.settingsUI = "Settings.html";
System.Gadget.onSettingsClosed = settingsClosed;
// we do not change flyout contents so we can set it during setup
System.Gadget.Flyout.file = "Garfield.html";
}
else
updateSize();
}
...
}
LocalizationWhich culture is in use?You can have quite a lot of culture-specific settings. When you look at the
The results are:
Any relative URL that you define in any HTML or that you set in script is, regardless of any previous tries or other files, resolved if possible by using the current locale first, and if not found: <!-- every relative url is attempted to be localized first -->
<!-- if your UI culture is cs-cz, the following -->
<!-- locations are tried in written order until -->
<!-- match is found: -->
<!-- 1: cs-cz/js/localized.js -->
<!-- 2: cs/js/localized.js -->
<!-- 3: en-us/js/localized.js -->
<!-- 4: en/js/localized.js -->
<!-- 5: js/localized.js -->
<script src="js/localized.js" type="text/javascript"
language="javascript"></script>
So if you have a file in the English locale, you don't need to put it in the root folder as well. This applies to manifest(s) too. Take it seriouslyMaking your gadget localizable is a nice idea, unless you mess it up. If you have
decided to localize your gadget, don't forget that there are right-to-left
reading locales as well. Usually if you don't think of it during development, the
localizers will be unable to create satisfactorily-localized versions. You can handle
this case by checking if It is a good idea to keep as few localizable files as possible. The usual way is to have one localized script file, which defines the culture-dependent variables, and then a global, failure-tolerant function to access it: // localized script file:
var localizedStrings = []; // creates empty array
localizedStrings['SaveCurrent'] = 'Save image to your computer...';
localizedStrings['OpenCurrent'] = 'Open image in web browser';
// global function in culture independent script file:
function getText(key)
{
var r = key; // if something goes wrong, return key itself
try
{
r = localizedStrings[key]; // try to look for localized version
if (r === undefined) r = key; // if not found use key itself
}
catch(e) {}
Maybe in your language it is acceptable to say There are + pearsCount +
pear(s) on the table.. But in my language for example, pear(s)
would have to be hruška/šky/šek and moreover, we don't have any there
are. So when you are building sentences, you might want to place values on
different places in sentences, depending on the culture. If you are familiar with
.NET's // ============================================================ getText = //
// //
// Returns localized text from Localized.js by key if found; otherwise //
// the key itself. The text can contain "{0}", "{1}", etc. place holders //
// which will be filled from the fills parameter. //
// //
// Syntax: getText(string key, object fills) //
// //
// Parameters: key - a string containg the key to look for //
// fills - if non-array type then it goes instead of {0}; //
// if non-indexed array then the first element will //
// be placed instead of {0} (after conversion to //
// string), the second replaces {1} and so on; //
// if indexed array then use indexes in brackets, //
// like {firstindex}, {secondindex}, ... //
// //
// ---------------------------------------------------------------------- //
function getText(key, fills)
{
var r = key;
try // you already know this part from
{ // example above
r = localizedStrings[key];
if (r === undefined) r = key;
}
catch(e) {}
if (fills != undefined)
{
// place single value into array, beware of numbers, which are treated
// as expected array length
if (typeof(fills) != Array) fills = new Array(fills.toString());
for (fillIndex in fills) // iterate over indexes of array
r = r.replace("{" + fillIndex + "}", fills[fillIndex]);
}
// for some consistency with .NET
// however {{0}} will be treated as index
r = r.replace("{{", "{").replace("}}", "}");
return r;
}
// and sample usage:
// en-us culture ... localizedStrings['PearsStuff'] =
// 'There are {0} pears on the table.';
// cs-cz culture ... localizedStrings['PearsStuff'] =
// 'Hrušek na stole: {0}.';
// call getText('PearsStuff', 5)
Remember that the localized strings can be longer than you expect, so let the UI consume it. And the last thing: when you work with right-to-left cultures, the localization process will be quite a bit more understandable if you name your strings independent of the culture - for example, use LeftButton instead of BackButton. ;-) Override the cultureIf you don't agree with the fixed-culture behavior, you can fight against it, although it is a pretty advanced challenge. You can either create your own culture-aware system, like storing all the strings in some text or XML file, or you can use the sidebar's system. The pros and cons are clear: With the first, there are no surprises, you can do what you want, and you have it under control, but also it is a lot of work and cultures defined this way cannot be utilized by the sidebar. With the second, you have to be careful when you do things and what you do, and your code has to be written flexibly, but you have compatibility. This is what I've chosen in the Garfield gadget. First, here is the code. Assuming you have the localized strings in the culture/js/Localized.js folder, you can dynamically read it and execute it: function localize(culture)
{
var path = System.Gadget.path;
if (path.substring(path.length - 1) != '\\') path += '\\';
path += culture; // GadgetPath\culture
try
{
var script = "";
// using XMLHttpRequest to access /culture/js/Localized.js
// throws Access denied error
var stream = new ActiveXObject("ADODB.Stream");
stream.Open();
// eval does not like Chinese...
stream.CharSet = "UTF-8";
stream.LoadFromFile(path + "\\js\\Localized.js");
// read all the text of Localized.js
script = stream.ReadText();
// filter out all declarations since they would be treated as local
script = script.replace(/(\s|;|^|\*\/)+(var)(\s+)/gm, "$1;$3");
// and would hide the global ones we are trying to replace
stream.Close;
stream = null;
// run the script
// this will replace localizedStrings array
eval(script);
}
// you may want to revert the operation if it fails -
//see the Garfield gadget
catch (stringsError) { ... }
}
If you want to use a localized file resource, you need to set the absolute path - otherwise it will go through the culture-match chain described before: System.Gadget.settingsUI = "/" + culture + "/Settings.html";...and in this case you also need to ensure that the culture-independent links in
such resources are using absolute paths, since you are in another root
than you normally would be in:
<!-- we can use absolute paths if we don't
expect/want these files to be localized -->
<!-- we have to use absolute paths if we want
to override automatic culture selection -->
<link href="http://www.codeproject.com/css/settings.css" rel="stylesheet" type="text/css" />
<script src="http://www.codeproject.com/js/settings.js" type="text/javascript"
language="javascript"></script>
For the user's total comfort, you must have the available-cultures-selection box :). For a complete solution for changing the display locale, check the Garfield gadget's code. By the way, you can load any culture you create without waiting for Esperanto or some other release of Windows Vista! Final notesWell, we are almost done. Three final details came to my mind that I wanted to share with you: MiscellaneousHow to check if a file existsfunction exists(path)
{
if (!IsGadget) return false;
try { System.Shell.itemFromPath(path); }
catch (notFound) { return false; }
return true;
}
How to get the System.Shell.Folder item// System.Shell.Folder.parse does not work
try { var folder = System.Shell.itemFromPath(path).SHFolder; }
catch (notFound) { ... } // folder does not exist
If an internet connection is not availablePlease, think carefully when you finalize your gadget. Take into account these 2 possible situations:
DeployingOh yes, and when we are finished, we still have to deploy the gadget. As I already wrote a while ago, you can install a gadget either by copying it to a gadgets folder (e.g. user's, system's or default user's) or by unpackaging the ZIP archive. You can also pack it into a CAB archive. Why would you want to do that? Because the CAB archive can be signed. Fortunately the sidebar team knows how costly code signing certificates are, so they did not place any requirements that gadgets must be digitally signed. Follow me as I pack the Garfield gadget for you:
CopyrightsFor the Garfield gadget, the comics strips are copyrighted by Mr. Jim Davis, Pawn Incorporated. The pictures are freely available on the internet - I haven't needed any special steps to access them, and sidebar is a web browser. Thus I believe I am not doing anything wrong or illegal. The cost of this is that they can change the server, naming, costs, or otherwise change or disable the access as they wish without prior notification, and I cannot and do not give any guarantees that the gadget will work forever.Should I be wrong, please let me know. The only supplementary image I use I found using Google (actually there are plenty of this all around) and have downloaded from http://www.lacoctelera.com/myfiles/mariajo/Garfield Sleepy.jpg. I did not find any copyright or legal notice on it anywhere, so if you know who I should ask for permission, I am ready. If anybody believes I used part of his work in the article and did not mention it sufficiently, contact me and we will correct it FeedbackAs I have said before, I write what I think might be the right way to do it, and if anyone has a better idea or if anyone sees I am missing something or saying something incorrect, send a message. I hope I showed you something you didn't know, something that will help you, or something useful for you.Corrections on grammatical/stylistic mistakes are also welcomed - this is my first English article. ;-) Resources
History
| ||||||||||||||||||||||||||||||