GWT - How to get the client browser name

Do you need to implement specific code for a given browser, due to different causes, bugs or workarounds? Here are some helper static native functions to figure out what browser the client uses. Obviously these functions must be called only on the GWT client-side, due to the native JavaScript scriptlet.

/**
* Gets the name of the used browser.
*/
public static native String getBrowserName() /*-{
    return navigator.userAgent.toLowerCase();
}-*/;

/**
* Returns true if the current browser is Chrome.
*/
public static boolean isChromeBrowser() {
    return getBrowserName().toLowerCase().contains("chrome");
}

/**
* Returns true if the current browser is Firefox.
*/
public static boolean isFirefoxBrowser() {
    return getBrowserName().toLowerCase().contains("firefox");
}


/**
* Returns true if the current browser is IE (Internet Explorer).
*/
public static boolean isIEBrowser() {
    return getBrowserName().toLowerCase().contains("msie");
}

For the other browsers (opera, safari, seamonkey etc) you can have your own function made after the previous 3 models.

Cheers!

GXT (Ext GWT) - How to maintain selection in a grid even after refresh

Problem:
I have a grid with a store and a loader. There is a paging tool bar too. Now when I click on the refresh button from the paging tool bar, the selection goes. I need to maintain this selection after refresh.

Solution:

1. First superclass the PagingToolBar class like here:

public MyPagingToolBar extends PagingToolBar {
    public Button getRefreshButton() {
        return refresh;
    }
}


2. Secondly, add a "Select" or "BeforeSelect" event listener on the refresh button, like in this code extract:

MyPagingToolBar  toolbar = new MyPagingToolBar ();
toolbar.getRefreshButton().addListener (Events.Select, new Listner<ButtonEvent>(){
     public void handleEvent (ButtonEvent be) {
        /* here you save your selection row */       
     }
});


3. On the grid loader success notification you can re-select the row programatically.

gridLoader = new BasePagingLoader<pagingloadresult<modeldata>>(proxy, reader) {
    protected void onLoadFailure(Object loadConfig, Throwable t) {
        super.onLoadFailure(loadConfig, t);
    }

     protected void onLoadSuccess(Object loadConfig, PagingLoadResult result) {
         super.onLoadSuccess(loadConfig, result);
         /* here you do the re-selection */
    }
}); 
  
Cheers!

GWT Optimization - Speed up the compilation in Eclipse

Do you have a computer with n-cores (in my case I have 4) and you are frustrated that the GWT compilation, on such powerful machines, works so slow?

Don't despair ...there is a solution here. Here's how we improve this in Eclipse:
- click on the "GWT Compile" button, like in the below image:



- in the next dialog set the compilation parameter:
-localWorkers 4



How to know the number of worker threads used by the GWT compiler? very simple check out the "Task Manager" of your computer (CTRL+ALT+DEL and choose option 'Show Task Manager'), and see how many "CPU Usage History graphs" do you have (as I said before, I have 4).




In my case, the improvement was about 3x times, from the ~180sec to about ~60sec. Not bad, ehh?

Cheers,
Happy compiling!

GXT (Ext GWT) - How to search remotely in grid with StoreFilterField ?

First of all we have to enrich the GXT control StoreFilterField, like in the below code extract, to be able to perform the remotely search in the grid:

public abstract class RemoteStoreFilterField<M extends ModelData> extends StoreFilterField<M> {
    public static final String PARM_FIELDS = “fields“;
    public static final String PARM_QUERY = “query”;
   
   /* disable the locally default behavior */

   @Override
   public void bind (Store<M> store) {
       /* do nothing here – to avoid calling locally the grid filter */
   }
   @Override
    protected boolean doSelect (Store<M> store, M parent, M record, String property, String filter) {
        /* do nothing here – to avoid calling locally the grid filter */
        return true;
    }

    /* prepare the notifications for the remote searching */

    @Override
    protected void onTriggerClick (ComponentEvent ce) {
        String filter = getRawValue();
        setValue (null);
        if (filter != null && !filter.isEmpty()) {
            handleCancelFilter();
        }
    }
    @Override
    protected void onFilter () {
        focus();
        String filter = getRawValue();
        if (filter != null && !filter.isEmpty()) {
            handleOnFilter (filter);
        }
    }

    /* handlers for the remote searching */

    /** need to perform cancellation of the remote filter */
    protected abstract void handleCancelFilter();

    /** need to perform the remote filtering */
    protected abstract void handleOnFilter(String filter);
}
So, we have to use this base code when we build the grid, which must be populated by a “store loader”.
public class GridHolderPanel extends ContenPanel {
    private String gridSearchText_;
    private Grid grid_;
    
    private void createGrid () {
        /* create a RPC data proxy */ 
        final RpcProxy<Data> proxy = new RpcProxy<Data>() {
           /* in this function we add the searching parameters: PARM_QUERY and PARM_FIELDS */
           @Override
           public void load( Object loadCfg, AsyncCallback<Data> callback) {
               LoadConfig gridLoadCfg = (LoadConfig) loadCfg;
               ...
               if (gridSearchText_ != null && !gridSearchText.isEmpty()) {
                   gridLoadCfg.set(RemoteStoreFilterField.PARM_QUERY, gridSearchTExt_);
                   /* fileds string has the list of column names, separated by comma for example – 
                      it’s your format here – you decide how you do it */
                   String fileds = "";
                   for (ColumnConfig cc : grid_.getColumnModel().getColumns()) {
                       if (cc.isHidden()) continue;
                       if (fields.length() > 0) fields += “,”;
                       fields += cc.getDataIndex();
                   }
                   gridLoadCfg.set(RemoteStoreFilterField.PARM_FIELDS, fields);
               }
           }
        };
        /* create a grid loader */ 
        final BaseListLoader<M> gridLoader = ...
        /* create a grid */ 
        ...
        grid_ = new Grid<M>(store, columnsModel);
        /* creates the toolbar where to put the search field */
        ToolBar tb = new ToolBar();
        /* create the search field */
        RemoteStoreFilterField<M> searcher = new RemoteStoreFilterField () {
           /* handle filtering – this is a call after each key pressed – it might be improved to be 
              deferred executed *** keep watching this blog I have a neat solution for this, 
              called LastCallEventBurstManager */
           @Override
           protected void handleOnFilter (String filter) {
               gridSearchText_ = filter;
               gridLoader.load();
           }
           /* handles the filtering cancellation */
           @Override
           protected void handleCancelFilter () {
               gridSearchText_ = null;
               gridLoader.load();
            }
        };
        /* build the UI */ 
        tb.add (searcher);
        this.add (grid_);
        this.setBottomComponent(tb);
    }
}
So the main points here are: RemoteStoreFilterField, data proxy for the grid, grid loader, and the grid columns model. All mixed in the way I’ve described above.

GWT - Update the CSS images to be used with the ClientBundles

GWT 2.0 simplified the managing of the resources in bundles (images, CSS, strings). For this the main 'actor' is the interface ClientBundle, which can be extended and populated with function definitions.

For example if we have the module com.mycompany.module1 then we can add a new package to the client code com.mycompany.module1.client.resources where we'll put our image resources.

Here we need to add our "bundle descriptor" Java interface, and copy the images (*.gif, *.png) under it. The link between the image file and the function is done through an annotation Source. So far nothing new. The point that I want to emphasise is what we do we our code that uses in lots of places the image key strings or image CSS classes (i.e. button.setIconsStyle("ICON-add")) ?

One solution is to replace everywhere the CSS references of the images with the call of the "bundle descriptor" interface, and another solution will be to build some helpers that will do the this "resources translation" for us, and the old code remains unchanged. The later I will present here:


public interface MyImagesBundle extends ClientBundle {

   /* create an instance of images bundle - static public one */
   MyImagesBundle INSTANCE = GWT.create(MyImagesBundle.class);

   /* add the definition of the icons */

   @Source ("add.png")
   ImageResource addIcon();

   @Source ("del.png")
   ImageResource delIcon();

}


This is nice here, but the some controls may not use directly the ImageResource class, but the AbstractImagePrototype, so we need the helper class to translate the "strings" to the "AbstractImagePrototype". And for this we'll add a nested static class to out bundle descriptor interface "MyImagesBundle", so we have the next code sample:

public interface MyImagesBundle extends ClientBundle {
   MyImagesBundle INSTANCE = GWT.create (MyImagesBundle.class);

   public static class Util {

      /* mapping of the CSS icon classes and the image resources */
      private static HashMap<String, ImageResource> ICONS = 
           new HashMap<String, ImageResource> ();

      /* here we build the mapping */
      static  {
          add ("ICON-add", INSTANCE.addIcon ());
          add ("ICON-del", INSTANCE.delIcon ());
      }

      /* conversion function */
      public static AbstractImagePrototype get (String imageCls) {
         ImageResource ir = ICONS.get (imageCls);
         if (ir != null)
            return AbstractImagePrototype.create (ir);
         return null;
      }

      /* adding helper function */
      protected static void add (String imageCls, ImageResource ir) {
         ICONS.put (imageCls, ir);
      }
     
   }

   @Source ("add.png")
   ImageResource addIcon ();

   @Source ("del.png")
   ImageResource delIcon ();
}

The calls of the resources can be done like here:

Button b = ...
b.setImage (MyImagesBundle.Util.get ("ICON-add"));

or

Button b = ...
b.setImage(AbstractImagePrototype.create(MyImagesBundle.INSTANCE.addIcon()));

If we need to have more bundles, we can use the above Util class as a base for the subclassed ones, like in the below sample:

public interface MySecondBundle extends ClientBundle {
    MySecondBundle INSTANCE = GWT.create (MySecondBundle.class);

    public static class Util extends MyImagesBundle.Util {
        /* here we just add the new mappings */
        static {
           add ("ICON-import", INSTANCE.importIcon();
           ...
        }
    }

    @Source ("import.gif")
    ImageResource importIcon ();
}

so the second bundle is much simpler, and it can "override" the icons with the same "icon css class", which can be good or bad. But either way goes, we still have the possibility to chose the right icon through the INSTANCE variable:

MyScondBundle.Util.get ("ICON-import");
MySecondBundle.INSTANCE.importIcon();
MyImagesBundle.INSTANCE.addIcon();
MyImagesBundle.Util.get ("ICON-add");

Cheers!

GXT (Ext GWT) - How can I disable the browser's default right click response?

1. Using the GXT (Ext GWT) library from the ExtJS is very simple.

You can write a static function like this:
/**
* Disable the browser's default right click response.
*/
public static void disableDefaultContextMenu() {
Document.get().addListener(Events.OnContextMenu,
new Listener() {
public void handleEvent(ComponentEvent be) {
be.preventDefault();
}
});
Document.get().sinkEvents(Event.ONCONTEXTMENU);
}

and you call it first thing in your "onModuleLoad()" function.

Now, all default right-click menus are gone, and only the ones defined by your application will appear.

2. For pure GWT implementation try this:
- first define the static function:
/**
* Returns the document element.
*/
public static native com.google.gwt.user.client.Element getDocElement() /*-{
return $doc;
}-*/;

- and then call this code first thing in the "onModuleLoad()":
Event.addNativePreviewHandler(new Event.NativePreviewHandler(){
@Override
public void onPreviewNativeEvent(NativePreviewEvent event) {
if (event.getTypeInt() == Event.ONCONTEXTMENU){
event.getNativeEvent().preventDefault();
}
}
});
DOM.sinkEvents(getDocElement(), Event.ONCONTEXTMENU);


I hope you'll be happy after that...

Cheers!

Update your application to GWT 2.0

Hoourey GWT 2.0 was released, it's time for new project update.

 1. Point one
First thing we all know the GWT 2.0 comes with the new "development mode" instead of the "hosted mode" that allows us to run and test our applications faster and in the browser of our choice. GOOD point.

Another good point is the "Speed Tracer" plugin to help you understand here bottleneck is. Though, this tool is not very oriented on detail, but more on high level view, which is good, but could be improved in the next versions.

All those can be found here: http://code.google.com/webtoolkit/download.html

The starting of the "dev mode" is faster than the old "hosted mode", and you can use and test the application in the browser, so now we get rid of the problem "I don't know, it's working in the hosted mode, but not in the web mode".

2. Point two
For running in dev mode, though, we need to install the dev-mode plugin (download Google Web Toolkit Developer Plugin), from one of this direct URL links:
- Firefox
- Internet Explorer
- Chrome
- Safari

I've tried my application using the GWT2.0 in these 3 browsers (Chrome, IE8, and FF3.5), and as Chrome is the fastest in day to day operations ... I expected the GWT dev-mode plugin will be the same ... but false ... the order of speed in dev-mode is:

1. Firefox
2. Internet Explorer
...
3. Chrome (far away comes)

 Why? I hope we'll have soon an updated version !

3. Point three
Other difference is that the starting of the application requires a quite special URL, like this one, to be typed directly in your browser:
http : // localhost : 8080 / MyApp.html ? gwt.codesvr = localhost : 9997
The parameter "gwt.codesvr" is used by the dev-mode browser plugin to communicate with your "eclipse" project, so you can debug it nicely. If you miss this parameter then you just run your application in plain "web mode" with no debug possibility.

Note: 8080 is the default port for you app, 9997 is default the port for dev-mode plugin ... both of them can be changed (so pay attention to startup configuration)!  Here you can find how to change the GWT ports.

4. Point four
Question: what do we do with our older releases, which cannot be updated, and they are still on older GWT 1.7.1 versions?

Things to do when we update/change a library (GWT in our case):
- 0. Generate first a simple GWT 2.0 project using the Eclipse plugin, and then update your module MainApplication.gwt.xml and MainApplication.html according with the GWT recommendations.
- 1. put the new jar
- 2. update the classpath, build.xml files etc.
- 3. perform a clean up of the browser cache
- 4. clean up the "Temp" folder, here the GWT compiles the project (usualy is the OS temp file).
- 5. perform a clean build of the project
- 6. launch you application.

 This is valid for trying to run older projects on the same machine with the GWT2.0 projects ... clean-up the browser cache and temporary folder "temp" is the "action of the day".

Happy GWT 2.0 updating !!!

5. My other related articles
       - Optimize the GWT compilation
       -  Replace CSS images with ClientBundle
       - Ideas for optimizing the GWT applications
       - Code examples of "How To Do" things in GWT
       - Change the default GWT Dev-Mode ports and parameters

Cheers!

HTML - Center a DIV in the page

Do you want to center anything in the page, like here?



Here's the HTML extract that will center a DIV element to the center of the HTML page:



Code:

<div style="display:table; width:400px; height:300px; position:relative; text-align:center; border:1px solid red; ">
<div style="display:table-cell; vertical-align:middle; width: 250px; height:80px; position: absolute; top:50%; left:50%; ">
<div style="width: 250px; height:80px; position:relative; top:-50%; left:-50%; ">
                 <div style="border:1px solid green;"> here you add what to be center: text, images etc. </div>
</div> </div>
</div>





Result:

here you add what to be center: text, images etc.



Happy 'center' !

Note: you still can play with the width:250px and height:80px attibutes to adapt the position right in the center !

GWT-Ext : Distinct Multiple Filter Columns

Did you need a separate filter on each grid column, instead of a single filter for all grid? If the answer is "yes" then this is your solution.

Here it's an image of the DMFC - distinct multiple filter columns - grid, which instead of implementing the standard filtering operation of "OR" with one value for all, it does "AND" with different value for each column.


And here is the result:



If you need the sources let me a comment and I'll come back to you!

Cheers!

[JOKE] - The Wedding Test

I was a very happy man. My wonderful girlfriend and I had been dating for over a year, and so we decided to get married. There was only one little thing bothering me.... It was her beautiful younger sister.

My prospective sister-in-law was twenty-two, wore very tight miniskirts, had great legs, and generally was bra-less. She would regularly bend down when she was near me, and I always got more than a nice view.. It had to be deliberate. Because she never did it when she was near anyone else.

One day the '
little' sister called and asked me to come over to check the wedding invitations . She was alone when I arrived, and she whispered to me that she had feelings and desires for me that she couldn't overcome. She told me that she wanted to make love to me just once before I got married and committed my life to her sister.

Well, I was in total shock, and couldn't say a word..

She said, '
I'm going upstairs to my bedroom, and if you want one last wild fling that you will never forget, just come up and get me.'

I was stunned and frozen in shock as I watched her go up the stairs. I stood there for a moment, then turned and made a beeline straight to the front door. I opened the door, and headed straight towards my car.

Lord... And behold, my entire future family was standing outside, all clapping!

With tears in his eyes, my future father-in-law hugged me and said, '
We are very happy that you have passed our little test. We couldn't ask for a better man for our daughter. Welcome to the family.'

And the moral of this story is:
Always keep your condoms in your car!

GWT-Ext automatically resize GridPanel columns

The problem here is that GWT-Ext framework has no API or support for automatically resizing of the GridPanel columns based on the content of the grid cells.

FORTUNATELY THERE IS A SOLUTION ! see it below what we need to do:

1) The resizing function.
2) Preparing the grid.

1. The resize static function
We have to define a static resize function that will work on any GridPanel or EditorGridPanel. Here's the code:
/**
* Automatically adjust Grid column widths. It gets the total of the
* adjustable columns and exclude fixed width columns from the target width.
*/
public static void resizeColumns(final Store gridStore,
final ColumnModel gridColsModel) {
if (gridStore == null || gridColsModel == null) {
return;
}
final BaseColumnConfig[] colCfgs = gridColsModel.getColumnConfigs();
if (colCfgs == null) {
return;
}
final int nbRows = gridStore.getCount();
if (nbRows > 0) {
final Record[] records = gridStore.getRecords();
/* Resize each column according the cell values */
for (int i = 0; i < colCfgs.length; i++) {
if (colCfgs[i] instanceof ColumnConfig) {
final ColumnConfig cfg = (ColumnConfig) colCfgs[i];
int colW = cfg.getHeader().length();
for (int r = 0; r < nbRows; r++) {
final String val = records[r].getAsString(cfg
.getDataIndex());
if (val != null && val.length() > colW) {
colW = val.length();
}
}
/* here's the weak of logic (6px for each char) */
gridColsModel.setColumnWidth(i, colW * 6 + 20);
}
}
}
}

2. Prepare the grid
For having this function called automatically we need to register 2 listeners:
  • one for the grid's store
  • second for the grid's wrapper (parent) panel
NOTES:
  1. Don't set the "auto fill" for the grid's view and don't set the auto-width column.
  2. Another big condtion is to keep the reference to the grid's ColumnModel object. This is used in the resizing function, and we cannot use the "grid.getColumnModel()" function because the rezult of this is a brand new instance, which doesn't have the list of ColumnConfig objects.

2.1 Grid store
When we create the GridPanel and set up its store we can register a StoreListener for this store.

Store gridStore = new Store (...);
....
/* need to preserve this object: gridColumnsModel */
ColumnModel gridColumnsModel = new Columnmodel (...);
....
gridStore .addStoreListener(new StoreListenerAdapter(){
public void onLoad(Store store, Record[] records) {
resizeColumns(store, gridColumnsModel);
}
});


2.2. Grid parent panel
When we add the grid to its parent panel (container) we have to register a "resize listener" for the parent.

grid.getView().setAutoFill(false);
grid.setStore(gridStore);
...
gridWrapperPane.add (grid);


gridWrapperPane.addListener(new ContainerListenerAdapter(){
@Override
public void onResize(BoxComponent component, int adjWidth, int adjHeight, int rawWidth, int rawHeight) {
if (grid != null) {
resizeColumns(grid.getStore(), gridColumnsModel);
}
}
});

DONE! now you don't need to worry about the grid columns size.
Enjoy it!



Synchronize SRT - Offset subtitle files

Here is a simple Java code for synchronizing (offset forward or backward) any .SRT subtitles file.

/**
* (c) 2009 by SCC
* 
* FREE-version. Anybody can do anything with this code.
* Enjoy it!
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* The class SyncSRTSubtitles reads a subtitles .SRT file and offsets all the
* timestamps with the same specific value in msec.
* 
* The format of the .SRT file is like this:
* 
* 123
* 00:11:23,456 --> 00:11:25,234
* subtitle #123 text here
* 
* 
* @author Sorinel CRISTESCU
*/
public class SyncSRTSubtitles {

/**
* Entry point in the program.
* 
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {

/* INPUT: offset value: negative = less (-) ... positive = more (+). */
long delta = (22 * 1000L + 000); /* msec */


/* INPUT: source & destination files */
String srcFileNm = "L:/DivX/Movies/TheMask.en.srt";
String destFileNm = "L:/DivX/Movies/TheMask.srt";


/* offset algorithm: START */
File outFile = new File(destFileNm);
outFile.createNewFile();
FileWriter ofstream = new FileWriter(outFile);
BufferedWriter out = new BufferedWriter(ofstream);

/* Open the file that is the first command line parameter */
FileInputStream fstream = new FileInputStream(srcFileNm);
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;

/* Read File Line By Line */
while ((strLine = br.readLine()) != null) {
   String[] atoms = strLine.split(" --> ");
   if (atoms.length == 1)
      out.write(strLine + "\n");
   else {
      String startTS = atoms[0];
      String endTS = atoms[1];
      out.write(offsetTime(startTS, delta) + " --> "
                + offsetTime(endTS, delta) + "\n");
   }
}

/* Close the input streams */
in.close();
out.close();

/* offset algorithm: END */
System.out.println("DONE! Check the rsult oin the file: " + destFileNm);
}

/**
* Computes the timestamp offset.
* 
* @param ts
*            String value of the timestamp in format: "hh:MM:ss,mmm"
* @param delta
*            long value of the offset in msec (positive or negative).
* @return String with the new timestamp representation.
*/
private static String offsetTime(String ts, long delta) {
  long tsMsec = 0;
  String atoms[] = ts.split("\\,");
  if (atoms.length == 2) {
    tsMsec += Integer.parseInt(atoms[1]);
  }
  atoms = atoms[0].split(":");
  tsMsec += Integer.parseInt(atoms[2]) * 1000L; /* seconds */
  tsMsec += Integer.parseInt(atoms[1]) * 60000L; /* minutes */
  tsMsec += Integer.parseInt(atoms[0]) * 3600000L; /* hours */
  tsMsec += delta; /* here we do the offset. */

  long h = tsMsec / 3600000L;
  String result = get2digit(h, 2) + ":";

  long r = tsMsec % 3600000L;
  long m = r / 60000L;
  result += get2digit(m, 2) + ":";

  r = r % 60000L;
  long s = r / 1000L;
  result += get2digit(s, 2) + ",";

  result += get2digit(r % 1000L, 3);

  return result;
}

/**
* Gets the string representation of the number, adding the prefix '0' to
* have the required length.
* 
* @param n
*            long number to convert to string.
* @param digits
*            int number of digits required.
* @return String with the required length string (3 for digits = 3 -->
*         "003")
*/
private static String get2digit(long n, int digits) {
  String result = "" + n;
  while (result.length() < digits) {
     result = "0" + result;
  }
  return result;
}

}
Cheers!