Creating xml based Gadget for Windows 7

gadget

About this article: Posted in: Blog
By: Blockcoder ( Admin ) / 17.01.2012
Stats: 4 responses / Views: 6,868
Tags: , , , ,

http://www.facebook.com/hashflarepromo/ the latest hashflare discount in January 2018.

Overview

There isn’t very much info about “how to make a windows gadget” in the Internet. You need to search and search and search but you only find little. That is why I made this blog post about it!

This article is about making a Windows 7 Sidebar Gadget that uses XML-formed RSS feed data to display the information. It works of course with Windows Vista too, but I developed with W7.

The gadget uses data from RSS-feeds from Iltalehti.fi, and more specifically the digi-news. During the developement, I realized that all the rss-feed tags are always written in the same way and hierarchy, so it’s actually possible to get any rss feeds working in the gadget just by changing the direct address to the xml-file. This makes the gadget very versatile for many users and purposes.

This blog post is big, so I divided it to chapters:


What does it do?

The gadget displays the selected news, in this case the “digi news” in time order from the newest to the oldest one still available on the service provider’s site.


Our goal

Our goal is to achieve this kind of gadget. In the start view of the gadget are shown the headlines and their timestamps in chronological order. The headlines work as links to open the additional information displayed in sidebar called “Flyout”. The layout of the main gadget is very simplified, and it works logically with a vertical scrollbar.

The main view of the news gadget.


The Flyout overview

The Flyout displays the news description provided in the rss-formed xml file from the service provider. In this view the whole text works as a hyperlink to the site of the actual whole news story. The link address is also provided in the xml-file ( from the RSS-feed ).

It looks like this:

Making Windows 7 Gadget - The Flyout

And it’s gonna pop up when you click any of the links on the main gadget.


Let’s get down to the business

Alright, now you know what we are going to do, so there won’t be much surprises. Let’s focus into how to make it!

Tips before you start making it:

  • Put your gadget files here: C:\Users\User\AppData\Local\Microsoft\Windows Sidebar\Gadgets
  • You can check your js-files from errors when you upload your gadget’s js-files and use ( firebug maybe ;) )
  • Name your gadget folder like “NewsGadget.Gadget”. Always add the “.Gadget” extension to the folder name.

The first tip is important: please develope your gadget in that folder because it will be very frustrating if you don’t. You will see what I mean if you try another folder where to develop gadgets, which is: C:\Program Files\Windows Sidebar\Gadgets. It is said that no third party gadget should not be installed to that directory.

If the javascript codes didn’t work as they should, I always moved the js-files to my domain and checked the errors with Firebug and then copied them back to gadget’s folder.

You have another option for debugging your javascript codes:

This registry key has been added to Windows 7, which enables the display of script errors at run time.
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Sidebar] “ShowScriptErrors”=dword:00000001

What files you need to create:

  • newsgadget.html
  • flyout.html
  • styles.css
  • scripts.js
  • gadget.xml

Let’s see what those 5 files are keeping inside.


The gadget.xml

Gadget.xml is a manifest file. It must be done so let’s do that first.

The gadget manifest is an XML file that contains general information for a gadget such as name and copyrights etc. This information is displayed in the Gadgets Gallery as gadget and developer details, along with functional or informational icons. Each gadget package must include a manifest.

Information what manifest-file offers:

Creating RSS-feed Windows 7 Gadget - Manifest file offer

You see the red circles in the picture, those are the information that the manifest file requires to be set.

The gadget.xml ( manifest file ):

<?xml version="1.0" encoding="utf-8" ?>
<gadget>
  <name>Digi news</name>
  <namespace>windows.sdk</namespace>
  &ltversion>1.0</version>
  <author name="Microsoft">
    <info url="msdn.microsoft.com" />
    <logo src="logo.png" />
  </author>
  <copyright>© Microsoft Corporation.</copyright>
  <description>News gadget.</description>
  <icons>
    <icon height="66" width="66" src="icon.png" />
  </icons>
  <hosts>
    <host name="sidebar">
      <base type="HTML" apiVersion="1.0.0" src="newsgadget.html" />
      <permissions>Full</permissions>
      <platform minPlatformVersion="1.0" /> 
      <defaultImage src="icon.png" />
    </host>
  </hosts>
</gadget>

After this is done, you can move to next step.


The newsgadget.html

Next we will look the main html-file which represents the main layout of our gadget. It is a skeleton like file, you just write empty divs which we will fill later on with all the news we want to download from rss sources.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Digi news</title>
        <link rel="stylesheet" href="styles.css" type="text/css" media="screen" />
		<script type="text/javascript" src="scripts.js"></script>
    </head>
	<body onload="RSSRequest();">
		<div id="gadgetContent">

			<h3 id="main_head"></h3>
			<div id="status" style="display:none" ></div>
			<div id="content">
				<ul id="ourList"></ul>
			</div>
			<div id="errorView"></div>
		</div>
    </body>
</html>

Noticed the function call “RSSRequest();“? Next when we look at the scripts.js file I’ll tell you what it does, but make sure you call it in the “body onload” of this file. :)


The styles.css

We need some styles for our gadget. I’m now going to make it look like as in the pictures above.

The styles.css:



html { overflow: hidden; }

body{
	font-family: verdana;
	font-size: 12px;
	color: #fff;
	margin: 0px;
	width: 202px;
	height: 400px;
	background: #444;
	overflow: auto !important;
	line-height: 15px;
}

h3 { font-size: 14px; padding: 20px 10px 5px; text-align: center; }

a:link { text-decoration: none; }
a:active {  }
a:hover { cursor: pointer; text-decoration: underline; }

/*GADGET*/
#main_head { border-bottom: 1px solid #000; background: #222; padding-bottom: 20px; }
#gadgetContent {
	width: 183px;
	color: #fff;
	text-align: left;
	border: 1px solid #000;
}

#ourList li {
	list-style: none;
	margin-left: -30px; 
	margin-bottom: 10px;
	font-size: 11px;
	padding-right: 3px;
}
#ourList li:first-child { padding-top: 0px; }
#ourList li a:link { color: #fff; }
#ourList li a:hover { text-decoration: underline; }
#ourList li p { margin-bottom: 3px; }
#ourList li p:hover { text-decoration: none !important; }

/*FLYOUT*/
#flyout {
	background: #444;
	overflow: hidden;
}
#flyout .heading {
	color: #fff;
	width: 299px;
	height: 71px;
}
#flyout .heading td { vertical-align: middle; }
#flyout .heading h3 { padding: 20px 17px 0px !important; text-align: left !important; font-size: 15px; }  
#flyout .heading h3 span { font-size: 12px; color: #aaa; }
#details { color: #fff; padding: 0px 20px 20px; width: 250px; background: #444; }



/*italics for the times*/
#flyout .heading h3 span, #ourList li p { font-style: italic; }

/*same colours*/
#ourList li p, a:link { color: #a0d3e9; }
#main_head, #flyout .heading h3 { color: #7be1fe; }

And it is done. If you copy it, you don’t need to worry about styles in this tutorial, or should I say blog post.


The scripts.js

The scripts.js file contains all of the functions which will make the gadget dynamic and get information to it. A moment ago I talked about the body onload function “RSSRequest();” and now I’m going to tell you what it does:

// make the XMLHttpRequest Object
var RSSRequestObject = false; 

// refresth the gadget 2 minutes interval
window.setInterval("RSSRequest()", 120000); 

// try to create XMLHttpRequest - not used in stardard gadget
if ( window.XMLHttpRequest ) {
		RSSRequestObject = new XMLHttpRequest();
	}

// if ActiveXObject use the Microsoft.Msxml2.XMLHTTP
if ( window.ActiveXObject )	{
		RSSRequestObject = new ActiveXObject("Msxml2.XMLHTTP");
	}

function RSSRequest() {

	// gArticles must be initialized because otherwise it will put the new articles and old articles in row ( won't just refresh them )
	gArticles.splice( 0, gArticles.lenght );

	// Here the wanted rss-address
	var Backend = 'http://www.iltalehti.fi/rss/digi.xml';

	// change the status to requesting data
	HideShow('status');
	document.getElementById("status").innerHTML = "";

	// lets open the request with wanted address
	RSSRequestObject.open("GET", Backend , true);

	// set the onreadystatechange
	RSSRequestObject.onreadystatechange = ReqChange;

	// lähetetään
	RSSRequestObject.send( null ); 
}

// hide & show function ( shows or hides the status messages )
function HideShow(id){
	var el = GetObject(id);
	if( el.style.display == "none" )
		el.style.display='';
	else
		el.style.display='none';
}

function GetObject(id){
	var el = document.getElementById(id);
	return(el);
}

First we set the address ( var Backend ) from where we want to request rss-feed. Then if you want, you can change the “status” div content to tell whats going on ( if it’s still downloading or if the requested data is ready to use ). Then we send the request to server and set the onreadystatechange function.

Before moving forward, I’ll show you a simplified version of the “ReqChange();” function so there isn’t too much information at one time:

function ReqChange() {

	// If requested data has finished downloading and is ready to use
	if ( RSSRequestObject.readyState == 4 ) {

              // do whatever you want with the data here if its state is "fully complete"

	}	
        else document.getElementById("status").innerHTML = RSSRequestObject.readyState;
}

The above function is very self explaining, I assume you got the idea.

There is four different states that the readyState function can offer:

  • State 1: Has not started loading yet
  • State 2: Is loading
  • State 3: Has loaded enough and the user can interact with it
  • State 4: Fully loaded

How do we fetch the requested data? You can use the “RSSRequestObject.responseXML” function that returns you the xml dom version of the rss-feed. Then it is easy parse the xml-data.

The above function “responseXML” gives us the rss feed in this form:


<?xml version="1.0" encoding="iso-8859-1" ?>
 <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
  <title>Iltalehti.fi tuoreimmat digiuutiset</title>
  <link>http://www.iltalehti.fi/etusivu/</link>
  <description>Iltalehden 20 tuoreinta digiuutista</description>
  <language>fi</language>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <lastBuildDate>Tue, 17 Jan 2012 12:54:00 +0200</lastBuildDate>
  <image>
   <url>http://static.iltalehti.fi/kuvat/navi/logo.gif</url>
   <title>Iltalehti.fi tuoreimmat digiuutiset</title>
   <link>http://www.iltalehti.fi/etusivu/</link>
   <width>143</width>
   <height>29</height>
  </image>

<item>
 <title>Applen perustaja kehuu Androidia</title>
 <link>http://www.iltalehti.fi/digi/2012011715087385_du.shtml</link>
 <guid>http://www.iltalehti.fi/digi/2012011715087385_du.shtml</guid>
 <description>Steve Wozniakin mukaan Android-käyttöjärjestelmässä moni asia on paremmin toteutettu.
  Applen yhdessä edesmenneen Steve Jobsin kanssa perustanut Steve Wozniak on edelleen uskollinen myös Applelle, mutta suitsuttaa avoimesti joitakin kilpailevan Android-käyttöjärjestelmän etuja.
  Wozniak nähtiin jonottamassa ensimmäisten joukossa uutta iPhone 4S -puhelinta, mutta hän on myös aktiivinen Android-käyttäjä.</description>
 <pubDate>Tue, 17 Jan 2012 12:54:00 +0200</pubDate>
</item>

<item>
 <title>Amerikkalaismedialla rajuja paljastuksia kännykkätehtaasta - iPhonen tuotantolinjalla 13-vuotiaita lapsia</title>
 <link>http://www.iltalehti.fi/digi/2012011615083696_du.shtml</link>
 <guid>http://www.iltalehti.fi/digi/2012011615083696_du.shtml</guid>
 <description>Amerikkalaismedia paljasti, ettei Applen alihankkija juuri tarkista työntekijöidensä ikää.
  Kiinalainen Shenzhenin kaupunki on paikka, josta merkittävä osa päivittäin käytössämme olevasta tekniikasta on peräisin.
  Kaupungissa toimii myös jättimäinen alihankkijayritys Foxconn, joka rakentaa tuotteita maailman suurimmille mobiilialan yhtiöille. Yksi asiakkaista on Apple.
 </description>
 <pubDate>Mon, 16 Jan 2012 15:13:00 +0200</pubDate>
</item>
...............
// it goes on like this..


Let’s do the parsing:

function ReqChange() {

	// If requested data has finished downloading and is ready to use
	if ( RSSRequestObject.readyState == 4 ) {

		// if data is valid
		if (RSSRequestObject.responseText.indexOf('invalid') == -1) { 	

			// get the XML DOM 
			var node = RSSRequestObject.responseXML.documentElement; 

			// browse the items
			//var content = "";

			var channel = node.getElementsByTagName('channel').item(0);
			var title = channel.getElementsByTagName('title').item(0).firstChild.data;
			var link = channel.getElementsByTagName('link').item(0).firstChild.data;
			var description = channel.getElementsByTagName('description').item(0).firstChild.data;			

                        // here you can set the main title of the gadget
			document.getElementById("main_head").innerHTML = title;

			// intitialize important arrays
			var content2 = '';
			var items = channel.getElementsByTagName('item');
			var titles = new Array();
			var links = new Array();
			var description = new Array();
			var pubDate = new Array();

			// items parse loop
			for (var n=0; n < items.length; n++){

				var itemTitle = items[n].getElementsByTagName('title').item(0).firstChild.data;
				var itemLink = items[n].getElementsByTagName('link').item(0).firstChild.data;
				var itemDescription = items[n].getElementsByTagName('description').item(0).firstChild.data;

				var itemDescription = '';

				try {
				  itemDescription = items[n].getElementsByTagName('description').item(0).firstChild.data;
				} catch (e) {}

				try {
					var year = items[n].getElementsByTagName('pubDate').item(0).firstChild.data.substring(12,16);
					var day = items[n].getElementsByTagName('pubDate').item(0).firstChild.data.substring(5,7);
					var month = items[n].getElementsByTagName('pubDate').item(0).firstChild.data.substring(8,11);
					var time = items[n].getElementsByTagName('pubDate').item(0).firstChild.data.substring(17,22);

					// dd/mm/klo
					var itemPubDate =  day + "/" + month + " " + time;
				} 
				catch (e){ 
					var itemPubDate = '';
				}

				// store parsed items to corresponding arrays
				titles[n] = itemTitle;
				links[n] = itemLink;
				description[n] = itemDescription;
				pubDate[n] = itemPubDate;
				var article = new Article( titles[n], links[n], description[n], pubDate[n] ,n );
				gArticles[n] = article;
			}

                        // when parsed, let's render the parsed data to the gadget
			renderDocument();

			// Tell the reader the everything is done
			document.getElementById("status").innerHTML = "";

		}
		else {
			// if there were errors
			document.getElementById("status").innerHTML = "Error"; 
                } HideShow('status'); 
          } 
          else document.getElementById("status").innerHTML = RSSRequestObject.readyState; 
}

Okay, it may look like some big mess, but it is not. It’s actually really simple:

  • First we parse the MAIN HEADING of the RSS-feed ( what is the RSS about ), description and link if we want to use it
  • Second we initialize arrays for titles, links, description and publishing date
  • Third we loop through all the “items” from the requested xml data ( news this time ) and parse each one’s title, link, description and publishing date
  • Fourth we store them to corresponding arrays for further use

I think this needs to be explained more:

        //store parsed values to corresponding arrays
	titles[n] = itemTitle;
	links[n] = itemLink;
	description[n] = itemDescription;
	pubDate[n] = itemPubDate;

        // create Article object and store parsed  items ( news ) into it
        // gArticles stores all the Article objects so it is easier to modify
	var article = new Article( titles[n], links[n], description[n], pubDate[n] ,n );
	gArticles[n] = article;

So we need to create a function that allows us to use Article as an object. I hope you understand some object oriented programming ( OOP ), because the next part is going to include a little of that. Don’t be afraid, it’s not so complicated.


Making article objects for the gadget

It’s like making a class ( if you have done before ) and use it’s attributes via object.


//luodaan olio yhdestä artikkelista, helpompi käsitellä
function Article( title, link, description, pubdate, index ) {
    this.Title = title;
    this.Link = link;
    this.Description = description;
    this.PubDate = pubdate;
    this.Index = index;
}

Now it has been done. We made Article “class” and we can use its attributes like this:


// create an object from Article
var articleObject = new Article();

// and use it
var linkAddress = articleObject.Link;

Got it? Good, we can move forward.
Wondering when we are moving all this information into our gadget? It is not too far, couple of lines more.

Next, we are going to create a function which will fill our gadget ( not yet flyout ):


// fills the main gadget ( not flyout )
function renderDocument() {
	
        // this clears "ourlist"
	document.getElementById( 'ourList' ).innerHTML = "";
	
        // loop for all the articles thats has been stored
	for( i=0; i < gArticles.length; i++ ) {
		var article = gArticles[i];
		
		// creates date and rowspace
		var p = document.createElement('p');
		p.setAttribute( 'class', 'pvm' );
		var pvm = document.createTextNode( article.PubDate );
		p.appendChild(pvm);
		
		// create a textnode from article's heading
		var text = document.createTextNode( article.Title );
		
		// let's create <li> and <a> elements
		var li = document.createElement("li");
		var a = document.createElement("a");
		a.dataIndex = article.Index; // article's index

		// adds onclick handler in the link
		a.attachEvent('onclick', handleClick);

		// fills the new elements
		a.appendChild(text);
		li.appendChild(p);
		li.appendChild(a);
          
                // fills finally the gadget
		ourList.appendChild(li);		
	}
}

Again, the function is self explaining itself. Only thing that must be maybe explained more is this "a.dataIndex = article.Index;". You can add an "index" to your elements, now we are adding it for the link ( <a> element ). We need that for that we can easily figure out which link heading is clicked and which link's ( article's ) description ( excerpt ) is shown in the flyout.

Now that we has filled our main gadged view, we need to create the flyout anymore. Let's make it.


The flyout.html:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
	<head>
        <link rel="stylesheet" href="styles.css" type="text/css" media="screen" />
		<!--<script type="text/javascript" src="scripts.js"></script>-->
	</head>
	
	<script type="text/javascript">
	function initialize(){

		document.body.style.width = 300;
		document.body.style.height = 400;
		document.body.style.margin = 0;
		document.body.style.border = "1px solid #000";
		document.body.style.backgroundColor = "#444";
	}
	</script>
	
    <body onload="initialize();" >
		<div id="flyout">
			<table class="heading">
				<tr>
					<td><h3 id="news_heading"></h3></td>
				</tr>
			</table>				
			<div id="details"></div>				
		</div>
    </body>
</html>


I'll show you a different version of how to initialize gadget flyout. You can of course write your javascript code here within the html code. It is not recommended but you can do it. You can put that code in the scripts.js too. However what the initialize function does, is simple. It sets the flyout's height and width and background color etc.

What do we have anymore..? Almost forgot, when clicking a heading link in main gadged view, how to open the flyout and send content there?

Let's do that. Open the scripts.js again write down this:


// we need to initialize and tell to the computer that which file is the flyout file that we are referring
var flyout = System.Gadget.Flyout.file = "flyout.html"; 

// gArticles holds all the articles
var gArticles = new Array();

// when clicking of the news heading link, open the flyout
// when clicked another news heading link, close the old flyout and open new flyout with new content
function handleClick( event ) {
    var idx = event.srcElement.dataIndex ;

    if( idx == gSelectedIndex ) {
        gSelectedIndex = -1 ;
        System.Gadget.Flyout.show = false ;
    }else{
        gSelectedIndex = idx ;
        if(System.Gadget.Flyout.show) {
            addContentToFlyout();
        } else {
            System.Gadget.Flyout.show = true;
            System.Gadget.Flyout.onShow = function() {
                addContentToFlyout();
            }

            System.Gadget.Flyout.onHide = function() {
                gSelectedIndex = -1 ;
            }
        }
    }
}

As you can see, we can refer to the flyout.html by writing this: "var flyout = System.Gadget.Flyout.file = "flyout.html";". It's easy.

Next we create a function that sends the correct content to our flyout window!


// this function sends the correct news's data to the flyout
function addContentToFlyout() {
    try {
        if( System.Gadget.Flyout.show ) {
            var flyout = System.Gadget.Flyout.document;
            var article = gArticles[gSelectedIndex];
            flyout.getElementById("news_heading").innerHTML = article.Title + "
" + article.PubDate + ""; flyout.getElementById("details").innerHTML = article.Description; flyout.getElementById("details").innerHTML += "

Read more"; } } catch ( ex ) { //displayError( ex.message ); } }

Finally you can test the gadget, it should be working!


Epic gadget making conlusion

Now you only need to do icons and pictures for your gadget, if you want. Remember to name your gadget folder like "NewsGadget.Gadget" or "WhateverGadget.Gadget" and move it to the location offered in "Let's get down to the business" chapter.

To remind: C:\Users\Johe\AppData\Local\Microsoft\Windows Sidebar\Gadgets\.

When the gadget folder is in right place, we can check if our gadget shows in desktop.

Making Windows 7 Gadget - Desktop test

It shows there, working when tested, I hope it works with you guys too!
I end this tutorial here :) hope you got something out from it. Thanks for reading!


There are 4 responses on this post

Mollie
Hyvä Blogi jatka samaan malliin. Olen seurannut tätä jo jonkin aikaa. Also visit my homepage sähkötupakka kauppa
Jowon
cool
Halme
Exemplary work - once again. Good job.
jooseppi
Hianoa! Joskus tällaista tutoriaalia etin mutten löytänyt mistään. Nyt kun olisi vielä motivaatiota hyödyntää sitä :)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

9,644 Spam Comments Blocked so far by Spam Free Wordpress

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>