Tuesday, March 30, 2010

Firefox extension programming: play sound


A Firefox extension can play sound with the nsISound interface. In my next release of the extension Take A Break, I will add the sound to remind the user of a break. The interface is very easy to use.

1. Create a sound instance.
   sound = Components.classes["@mozilla.org/sound;1"]
            .createInstance(Components.interfaces.nsISound);
2. Beep.
   sound.beep();
That is how simple it can be. The Take A Break extension beeps every 3 seconds during the short break -- while the icon is flashing. So the user can close his/her eyes for a rest. When the beeping stops, the user can know the short break is over.

To do beeping every 3 seconds, we use the setTimeout() function. First, define a function doBeep().
   function doBeep()
   {
      if (sound)   // the "sound" instance we created
      {
         // Get the current time in milli-seconds.
         var currentTimeMs = (new Date()).getTime();

         // flashEndTimeMs tells when the short break will finish.
         // flashEndTimeMs was set before this function is called.
         if (currentTimeMs > flashEndTimeMs)
         {
            // Flashing ends. Stop beeping.
            beepTimer = null;
            return;
         }

         sound.beep();

         // restart timer.
         // After 3 seconds, this function is called back again.
         beepTimer = window.setTimeout(function(){ doBeep(); },
                        3000);   // 3 seconds
      }
   }
As you can see, inside the function doBeep(), we register itself as a callback function for the 3 seconds timeout event. So doBeep() runs every 3 seconds until the short break ends.

Secondly, when it is time for the short break, we call the function doBeep() to start the first beeping.
   // Set the end time for the short break.
   var currentTimeMs = (new Date()).getTime();
   flashEndTimeMs = secOfFlash * 1000 + currentTimeMs;

   // Start flashing and beeping.
   doFlash();
   doBeep();
This feature will be released in version 1.1 of Take A Break. The sample code here is modified a little to make it more understandable.

Thursday, March 25, 2010

Measure the linear distance of two locations in Google Earth


You can measure the linear distance of two addresses in Google Earth. There is a "Show Ruler" icon in the Toolbar of Google Earth. If you cannot see the Toolbar, you can show it by checking the "Toolbar" item under main menu "View".

When you click the "Show Ruler" icon in the Toolbar, a "Ruler" dialog pops up. You can drag it aside if it blocks the view of the map.

To measure a distance, click on the first location in the map. Release the button and a point is marked. Now when you move the mouse, the distance from your mouse to that point is shown in the "Ruler" dialog as "Length". How many degrees you are away from north is also shown as "Heading".

Click on the second location (the destination) to measure the linear distance between two points. A line is drawn between them. When you are done, click "Clear" button to remove the line on the map.

Wednesday, March 24, 2010

Trim leading or trailing white spaces off a string in C++


Unlike Java, there is no built-in function to remove leading or trailing white spaces from a string in C++.

We can write a function to implement this.

  1 void trim(string& s)
  2 {
  3    size_t p = s.find_first_not_of(" \t");
  4    s.erase(0, p);
  5 
  6    p = s.find_last_not_of(" \t");
  7    if (string::npos != p)
  8       s.erase(p+1);
  9 }

Line 1: "s" is a reference parameter through which the original string is passed in and the result will be passed out.

Line 3: the string's built-in function find_first_not_of() returns the position of the first character which is not in the list of white spaces (' ' or '\t'). Since we are searching from the first character -- position 0, the result is also the number of leading white spaces.

Line 4: erase "p" characters from the beginning of the string, i.e. the leading white spaces. If there is no non-white-space character, line 3 will return string::npos. s.erase(0, string::npos) will then erase the whole string.

Line 6: the string's built-in function find_last_not_of() returns the position of the last character which is not a white space.

Line 7: if there is not trailing white spaces, line 6 returns string::npos. Therefore, our job is done.

Line 8: Otherwise, erase the trailing white spaces which starts from the next character to "p".

To test it, run this:

#include <string>
#include <iostream>

using namespace std;

void trim(string& s)
{
   size_t p = s.find_first_not_of(" \t");
   s.erase(0, p);

   p = s.find_last_not_of(" \t");
   if (string::npos != p)
      s.erase(p+1);
}

int main()
{
   string a = "   a   ";
   string b = "";
   string c = "       ";
   string d = "   \t  ";

   trim(a);
   trim(b);
   trim(c);
   trim(d);

   cout << ":" << a
        << ":" << b
        << ":" << c
        << ":" << d
        << ":" << endl;
   
   return 0;
}

Thursday, March 18, 2010

Linux: SETGID on directory


SETGID stands for SET Group ID. We can use the command chmod to set the group ID bit for a directory.

   chmod g+s mydir

or with numeric mode:

   chmod 2775 mydir

After the change, the permission of the directory "mydir" becomes "drwxrwsr-x".

   drwxrwsr-x 3 zen zen 4096 2010-03-18 19:57 mydir

But what is so special about setting the group ID for a directory? The trick is that when another user creates a file or directory under such a directory "mydir", the new file or directory will have its group set as the group of the owner of "mydir", instead of the group of the user who creates it.

For example, if user2 belongs to the groups "user2" (main group) and "zen", and he creates a file "newfile" under the diretory "mydir", "newfile" will be owned by the group of "zen" instead of user2's main group ID "user2".

   -rw-r--r-- 1 user2 zen   10 2010-03-18 20:01 newfile

Even if user2 does not belong to the group "zen", the files or directories he creates under "mydir" (if "mydir" grants the write permission to "others") will also get owned by group "zen".

You can use such feature to share files within the group. Create a directory which permits the group to write, and set the group ID bit. Every files or directories created under it will have the same group ownership. Therefore, the whole group can share them.

Wednesday, March 17, 2010

Firefox extension programming: add actions to status bar icon


Note: This is a successive post of put an icon on the status bar.

After we add our icon to the status bar of Firefox, we want to make it useful. In this tip, we show how to implement the following three functions.

   1. When the mouse hovers over the icon, a tooltip pops up to show some text.
   2. Clicking on the icon changes its color.
   3. Right-clicking on the icon pops up a context menu.

Tooltip when mouse over

We add a handler for the "mouseover" event to the status-bar panel we created before:

   <statusbarpanel id="ourPanel"
         onmouseover="document.getElementById('ourPanel').tooltipText='Hello'">
      <image id="ourIcon" />
   </statusbarpanel>

The handler will set the tooltip of our status-bar panel to "Hello". Since the status-bar panel contains only one element, our icon, when the mouse hovers over the icon, "Hello" will show up.

Clicking on the icon changes its color

We add a handler for the "click" event to our status-bar panel.

   <statusbarpanel id="ourPanel"
         onmouseover="document.getElementById('ourPanel').tooltipText='Hello'"
         onclick="ourExtensionClickHander(event)">
      <image id="ourIcon" />
   </statusbarpanel>

In the included JavaScript "our_extension.js", we define the ourExtensionClickHander() function to switch the icon between green and grey:

   function ourExtensionClickHander(event)
   {
      if (0 == event.button)
      {
         // left button is clicked.

         var theIcon = document.getElementById("ourIcon");
         if (theIcon)
         {
            var attr = theIcon.getAttribute("value");

            if (attr == "grey")
               theIcon.setAttribute("value", "green");
            else
               theIcon.setAttribute("value", "grey");
         }
      }
   }

In the function, we first verify that the first button (left button) of the mouse is pressed. Then we get the node of our icon on the status bar. We retrieve its value and make the change. How we define the color values for the icon can be found in my previous post.

Context menu

We define a context menu for our icon in the same XUL file. To simplify things, in the context menu, we add only one menu item.

   <popupset id="mainPopupSet">
      <menupopup id="ourContext">
         <menuitem id="ourMenuItem" label="Our Item" onclick="alert('Hello World')" />
      </menupopup>
   </popupset>

It overlays the main popup set with our popup menu "ourContext". Inside our popup, we define one menu item "ourMenuItem".

After defining the context menu, we can add it to our icon.

   <statusbarpanel id="ourPanel"
         onmouseover="document.getElementById('ourPanel').tooltipText='Hello'"
         onclick="ourExtensionClickHander(event)
         context="ourContext">
      <image id="ourIcon" />
   </statusbarpanel>

Now, when you right-click on the icon, the menu "Our Item" shows up. Clicking on it pops up a dialog saying "Hello World".

To see a live example, check out my extension Take A Break.

Monday, March 15, 2010

Customize the Firefox Toolbar


By default, Firefox shows Menu Bar, Navigation Toolbar, Bookmarks Toolbar at the top. They take some space and makes the view panel smaller.

If you are using a netbook or a small display, you may want to customize the toolbar to hide the less used items. To do that, right click on the Menu Bar and select "Custmize..." from the context menu.

A Customize Toolbar dialog pops up. You can drag and drop the unwanted icons (such as the Home icon) into the dialog box to remove them. You can also drag and drop the icons or the input boxes from the Navigation Toolbar to the Menu Bar to relocate them.

I prefer to move everything I need to the Menu Bar. And then hide the Navigation Toolbar and the Bookmarks Toolbar. To hide them, click menu "View" and uncheck "Navigation Toolbar" and "Bookmarks Toolbar" from the submenu.

After doing that, you may find that the Address box and Google Search box are too small. You can then install the extension Packed Menu to hide the menus if you are not so frequently using them. After the installation of Packed Menu, only an "M" menu is shown in the toolbar, and more space is given to the Address box and Google Search box. Clicking on "M" will bring you the hidden menus.

You can also install another extension Autohide Tabbar that brings you more space and privacy.

Saturday, March 13, 2010

Firefox Extension: Packed Menu


Note: this extension conflicts with my another extension Packed Menu 2 Nav, which puts the button "M" on the Navigation Toolbar. If you have installed Packed Menu 2 Nav, you must uninstall it first before you install this extension.

This extension adds a button "M" to the menu bar. And the original main menus are hidden. When you click the "M" button or press Alt-M, the original main menus are shown under the "M" button.


After the installation, the menu bar will show just one item and you will get a lot of space there. You can do a little customization on the tool-bar to move things to the menu bar and get rid of the navigation toolbar if you like a compact tool-bar.

This extension has nearly no impact to the Firefox's performance. All it does is just adding a menu "M" to the tool-bar and moving the original "main menu bar" to the popup of the menu "M".


This extension can be downloaded and installed from Mozilla Firefox's official extension web site:
https://addons.mozilla.org/en-US/firefox/addon/95358

Because there is no impact on functions of the original main menu bar, you can still use the short-cut keys to access the submenus. For example, Alt-F will open the popup menu under "File". But the "main menu bar" itself will not show up -- This is not a bug.

Install another extension Autohide Tabbar together and you would get a neater interface.


Thursday, March 11, 2010

Convert string to number in C++


There are several methods to convert a string to number in C++. We use floating point numbers as examples. There are similar functions for integer.

Method 1: use function atof().

The syntax of atof() is:
   #include <stdlib.h>
   double atof(const char *nptr);
The conversion code could be:
   const char *a = "234.5";
   double d;

   d = atof(a);
There is a little problem with this function. It does not detect errors. If it can not convert the string, it returns 0. By examing the return value, you can not know whether it has encountered errors or the string is "0".

Method 2: use function strtod().

The syntax of strtod() is:
   #include <stdlib.h>
   double strtod(const char *nptr, char **endptr);
The conversion code could be:
   const char *a = "234.5";
   double d;
   char *b;

   d = strtod(a, &b);
   if (0 == d && a == b) {
      // error handling.
   }
strtod() is similar to atof() except that it detects errors. If errors occur, it returns 0 and set pointer b (the second parameter) to the value of a (the first parameter).

Method 3: use function sscanf().

The syntax of sscanf() is:
   #include <stdio.h>
   int sscanf(const char *str, const char *format, ...);
The conversion code could be:
   const char *a = "234.5";
   double d;
   int r;

   r = sscanf(a, "%lf", &d);
   if (r <= 0) {
      // error handling.
   }
sscanf() returns the number of items converted. So if it returns 0, it means it can not do the conversion. If it returns -1 (EOF), it means there is no more input.

Method 4: use stream.

We use the istringstream class. This method is more OO-style.
   #include <sstream>

   const char *a = "234.5";
   double d;

   istringstream iss(a);
   iss >> d;
   if (iss.bad() || iss.fail()) {
      // error handling.
   }

Wednesday, March 10, 2010

Firefox extension programming: put an icon on the status bar


To better understand this article, you should have the basic knowledge of building a Firefox extension. You can refer to my another post Learn how to create a Firefox extension in 10 minutes if needed.

To put an icon on the status bar of Firefox, we first need to "overlay" the status bar in our main *.xul file. Let us name it "overlay.xul":
   <overlay ...

      <script src="chrome://our_extension/content/our_extension.js" />

      <statusbar id="status-bar">
         <statusbarpanel id="ourPanel">
            <image id="ourIcon" />
         </statusbarpanel>
      </statusbar>

   </overlay>
The Firefox status bar has an element name of "statusbar" and an ID of "status-bar". As you can see, we add our own statusbarpanel with the ID of "ourPanel". Inside the panel, we add an image holder "ourIcon". When we change the attribute of "ourIcon", we can display an icon on the status bar. In our example, we apply the image via the list-style-image style property.

You may have noticed that we add a "script" element before the "statusbar" element. It inserts our main JavaScript "our_extension.js" which locates under the "content" directory.

We want to create two icons so that when the user clicks on the icon, it can switch to show the other one. These icons should be in PNG format. We name these two icons iconGreen.png and iconGrey.png, and put them under the "chrome://our_extension/skin/" directory.

Next, we create a CSS (Cascading Style Sheets) file "overlay.css" under the same directory. The CSS file tells where the icons are:
   #ourIcon[value="green"]
   {
       list-style-image: url(chrome://our_extension/skin/iconGreen.png);
   }

   #ourIcon[value="grey"]
   {
       list-style-image: url(chrome://our_extension/skin/iconGrey.png);
   }
We then need to include the style sheet in our "overlay.xul" in the beginning:
   <?xml version="1.0"?>
   <?xml-stylesheet href="chrome://our_extension/skin/overlay.css" type="text/css"?>
Now we are ready to apply the different icon to the "image" element in "overlay.xul" by setting a "value" attribute to it. We can do this in the main JavaScript "our_extension.js":
   var theIcon = document.getElementById("ourIcon");
   if (theIcon)
      theIcon.setAttribute("value", "green");  // or set to "grey"
The icon is now showing up on the status bar. But it is not so useful yet. We could add event listener to it or add a context menu to make it useful.

To see a live example, check out my extension Take A Break.

Monday, March 8, 2010

Setting the language in Firefox


There are a couple of language parameters of Firefox. You can change them to make your Firefox browser speak a different language to the Web.

It seems that different web sites utilize different parameters to detect your language. So you may want to try both of the following methods.

The first one is the User-Agent Locale parameter. To change it:
* input "about:config" into the Address box, and press Enter;
* Firefox wants you about the danger. Read and click the button to continue;
* input "useragent.locale" in the "Filter" box;
* double click the "general.useragent.locale" parameter and change it to your language, such as "fr-FR". (If you want to set it back, right click the parameter and choose "Reset" from the context menu.)

This changes the language part of the "User-Agent" in the HTTP request header to something like this:
   User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr-FR; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 (.NET CLR 3.5.30729)\r\n
After the change, go to www.wikipedia.org. It will pre-select your language for you.

The other one is Accept-Language parameter. To change it:
* open the "Options" dialog from the menu "Tools|Options" (or "Edit|Preferences" in Linux);
* select the "Content" icon;
* in the "Languages" section, click the "Choose..." button. A "Languages" dialog pops up;
* add the language you want from the drop-down menu "Select a language to add...";
* in the above language list, select the newly added language and use the "Move Up" button on the right side to move the new language to the top;
* click "OK" button to finish.

This changes the "Accept-Language" of the HTTP request header to something like this:
   Accept-Language: fr-fr,en-us;q=0.7,en;q=0.3\r\n
After the change, go to www.mozilla.com. You will see the Mozilla web site talks to you in your own language now.

Friday, March 5, 2010

Change the secret configuration parameters of Mozilla Firefox/Thunderbird


There are many secret parameters you can use to tweak your Mozilla Firefox or Thunderbird. It is different to access them between Firefox and Thunderbird.

In Firefox, input "about:config" in the address box. Firefox will warn you: "This might void your warranty!". It IS dangerous if you mess up with the parameters. But if you are confident, just click the "I'll be careful, I promise!" button to continue.

You can use the "Filter" box to search the parameter you want. Double click a parameter to change its value. If a parameter has been changed, it is in bold. If you want to reset a changed parameter to its default value, right click it and choose "Reset" from the context menu.

After you finish, just close the tab.

In Thunderbird, the configuration can be accessed from the Options/Preferences dialog. Click the "Advanced" icon in the dialog. Then select the "General" tab. Click the last button "Config Editor..." and the configuration dialog will pop up. It also warns you "This might void your warranty!". And you click the "I'll be careful, I promise!" button to continue. The rest are same as in Firefox.

Thursday, March 4, 2010

Slow SSH connection to remote server


When I tried to ssh connect to a new remote server, it took a longer time. Although it got connected finally. I was curious so I turned on the debug mode:
   ssh -v remote_server
The log showed that there was a pause when ssh client application was trying authentication method gssapi-with-mic. After 3 trials, ssh client tried the next authentication method and succeeded:
   debug1: Authentications that can continue: publickey,gssapi-with-mic,password
   debug1: Next authentication method: gssapi-with-mic
   debug1: Unspecified GSS failure.  Minor code may provide more information
   No credentials cache found

   debug1: Unspecified GSS failure.  Minor code may provide more information
   No credentials cache found

   debug1: Unspecified GSS failure.  Minor code may provide more information


   debug1: Next authentication method: publickey
We could skip the gssapi-with-mic authentication method and only tried publickey and password by using the PreferredAuthentications options:
   ssh -o PreferredAuthentications=publickey,keyboard-interactive remote_server
The ssh client first tried the private/public authentication method. If failed, it would prompt the user for password. There would be no long pause any more during the connection.

If you don't want to type such a long option in your command line, put it into a configuration file. Create a file ~/.ssh/config in your home directory. Inside the file, add this one line:
   PreferredAuthentications publickey,keyboard-interactive
 
Get This <