Friday, August 21, 2009

Multi cell selection in a row using shift and control key – grid GXT

This post is an extension to this previous post. Code in the previous post is used to multi select cell in a row using the shift keys. It is updated to add functionality to select cells in a particular row using control key and also highlight the row that we are currently working on.

import java.util.ArrayList;
import java.util.List;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.GridEvent;
import com.extjs.gxt.ui.client.widget.grid.CellSelectionModel;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
/**
 * This class is an extension of CellSelectionModel. 
 * The CellSelectionModel allows us to select only one cell at a particular time. 
 * This Class allows us to select multiple cells in a particular row in a grid.
 * Multi select is performed by pressing the shift key with the mouse click
 *  
 * @author Ravikanth Kolli
 * @version 1.0
 * 
 * 
 * @param <M>
 */
public class RowCellSelectionModel<M extends ModelData> extends CellSelectionModel<M> { 
int lastSelectedRow = -1;
int lastSelectedCol = -1;
private CellSelection selected ;
List<CellSelection> cellsSelected = new ArrayList<CellSelection>();
/**
 * Overriding the handleMouseDown event.
 * MultiSelect is done by pressing the shiftkey 
 */
@Override
protected void handleMouseDown(GridEvent e) {
super.handleMouseDown(e);
if(lastSelectedRow == -1)
{
selected = new CellSelection(listStore.getAt(e.getRowIndex()),e.getRowIndex(),e.getColIndex());
cellsSelected.add(selected);
lastSelectedRow = e.getRowIndex();
lastSelectedCol = e.getColIndex();
}else if (lastSelectedRow == e.getRowIndex() && (e.isShiftKey() || e.isControlKey())) {
// control key press adding or removing cell from the cellsSelected list. 
if(e.isShiftKey())
{
if(cellsSelected.size() > 0)
El.fly(grid.getView().getRow(cellsSelected.get(0).row)).removeStyleName("x-grid3-row-selected");
cellsSelected.clear();
for (int i = 1 ; i < e.getGrid().getColumnModel().getColumnCount(); i++) {
Element cell = grid.getView().getCell(lastSelectedRow, i);
if (cell != null) {
El.fly(cell).removeStyleName("x-grid3-cell-selected");
}
}
if(lastSelectedCol > e.getColIndex()){
for (int iter = e.getColIndex(); iter < lastSelectedCol+1 ; iter++)
{
selected = new CellSelection(listStore.getAt(e.getRowIndex()),e.getRowIndex(),iter);
cellsSelected.add(selected);
}
lastSelectedRow = e.getRowIndex();
}else
{
for (int iter = lastSelectedCol; iter < e.getColIndex() + 1 ; iter++)
{
selected = new CellSelection(listStore.getAt(e.getRowIndex()),e.getRowIndex(),iter);
cellsSelected.add(selected);
}
lastSelectedRow  = e.getRowIndex();
} 
}
// control key press adding or removing cell from the cellsSelected list. 
else if(e.isControlKey())
{
if(cellsSelected.size() > 0)
El.fly(grid.getView().getRow(cellsSelected.get(0).row)).removeStyleName("x-grid3-row-selected");
boolean isSelected = false;
for (int i =0 ; i < cellsSelected.size();i++)
{
El.fly(grid.getView().getCell(cellsSelected.get(i).row, cellsSelected.get(i).cell)).removeStyleName("x-grid3-cell-selected");
if(cellsSelected.get(i).row == e.getRowIndex() && cellsSelected.get(i).cell == e.getColIndex())
{
isSelected = true;
cellsSelected.remove(i);
}
}
if(!isSelected)
{
selected = new CellSelection(listStore.getAt(e.getRowIndex()), e.getRowIndex(), e.getColIndex());
cellsSelected.add(selected);
}
lastSelectedRow = e.getRowIndex();
}
}
else
{
if(!e.isRightClick() 
||  cellsSelected.size() == 0
|| !(cellsSelected.get(0).row == e.getRowIndex() && cellsSelected.get(0).cell <= e.getColIndex() && e.getColIndex() <= cellsSelected.get(cellsSelected.size() - 1).cell) 
||  cellsSelected.get(0).row != e.getRowIndex()){
if(cellsSelected.size() > 0)
El.fly(grid.getView().getRow(cellsSelected.get(0).row)).removeStyleName("x-grid3-row-selected");
cellsSelected.clear();
for (int i = 1 ; i < e.getGrid().getColumnModel().getColumnCount(); i++)
{
Element cell = grid.getView().getCell(lastSelectedRow, i);
if (cell != null) {
El.fly(cell).removeStyleName("x-grid3-cell-selected");
}
}
selected = new CellSelection(listStore.getAt(e.getRowIndex()),e.getRowIndex(),e.getColIndex());
cellsSelected.add(selected);
lastSelectedRow = e.getRowIndex();
lastSelectedCol = e.getColIndex();
}
}
/**
 * Add the cell selected style after all the events are handled
 */
DeferredCommand.addCommand(new Command() {
public void execute() {
      if(cellsSelected.size() > 0)
       {
El.fly(grid.getView().getRow(cellsSelected.get(0).row)).addStyleName("x-grid3-row-selected");
       }
for (int iter = 0 ; iter < cellsSelected.size() ; iter++) {
Element element = grid.getView().getCell(cellsSelected.get(iter).row,cellsSelected.get(iter).cell);
if (element != null) {
El.fly(element).addStyleName("x-grid3-cell-selected");
}
El.fly(grid.getView().getHeaderCell(cellsSelected.get(iter).cell)).setStyleName("x-setItalics");
}     
}
});
}
/**
 * Method to get the list of all the selected cells at a particular time
 * @return list of selected CellSelections.
 */
public List<CellSelection> getSelectedCells()
{
return cellsSelected;
}
/**
 * clear the selections in cellsSelected
 */
public void clearSelections()
{
cellsSelected.clear();
}
}

Thursday, August 13, 2009

Improving grid performance of an application GXT

For over a month now, I have been working on improving the performance of my application and get feedback from the clients for areas of improvement. My application was running really slow and the reason

1) IE being the only browser that our company supports, is pretty bad in handling white spaces(we do a lot of server side processing in ColdFusion which comes up with a lot white spaces)

Solution: shrink the ColdFusion file into a single line

2) JSONReader is taking a whole lot of time to extract the data and put it in a store.

Solution: After a lot of debugging, I figured out that “com.google.gwt.json.client.JSONObject” that is used in the JSONReader uses a HashMap to store the JSONValue and the get method of HashMap it the most time consuming.

GXT has FastMap which is pretty fast than HashMap. I try to override the mapping part of the JSONObject and try to generate my custom map using FastMap.

public Map<String, Object> decode(JSONObject jso) {
Map<String, Object> map = new FastMap<Object>();
for (String key : jso.keySet()) {
JSONValue j = jso.get(key);
map.put(key, j.isString().stringValue());
}
return map;
}

Whenever I try to get a single JSONValue i use the map to get the data. I tested it and is about 50-60% faster than the previous.

3) Grid rendering also takes a long time which increases with the number of rows.

Solution: I was going through the GXT blog and read about the buffered view for a grid, using which we can render only the rows in the grid that are visible, and each time the scroll bar is used previous rows are cleared from the DOM and new rows are added. This makes the rendering pretty fast and also the speed of the editing features in the grid is increased.

  1: BufferView view = new BufferView();
  2: view.setScrollDelay(10);
  3: view.setRowHeight(28);
  4: grid.setView(view);
The row height has to specified in the view and I think it is needed to decide the number of rows to be displayed.

Monday, July 20, 2009

Migration to GXT 2.0 from GXT 1.2.4

 

EXTJS released GXT 2.0 on July 6 2009.

GXT 2.0 uses GWT version of 1.6 and above. The project structure has been changed by using the WAR folder for all the configuration and public resource files. An eclipse plug-in  has been released at Google code which creates the project based on our required project structure, with the server configurations and WAR folder created for our convenience.

Coming to GXT there are a lot breaking changes in 2.0 and can be found under migration guide of your downloaded zip. some of the basic changes includes, variables that are accessed as such previously are changed to getter and setter methods, more events are added such as ButtonEvent, MessageBoxEvent etc, and also the ext-all.css that was used previously has been changed to gxt-all.css and has been placed under resources/css along with all the other images( this folder is also present within the downloaded zip).

Creating a project:

1) The eclipse plugin allows us to create a New Web Application Project that includes creating a new GWT module.

2) Copy resources/ folder from GXT into the WAR folder that was created by the wizard.

3) Add  <link rel="stylesheet" type="text/css" href="resources/css/gxt-all.css" /> to the HTML file that is also created in the WAR folder of the application.

4) The compiled code is also placed in the WAR folder

Monday, July 13, 2009

Disable the Browser’s context menu

The following lines of native JavaScript code can be used to disable the browsers context menu, when it is no more needed or if there is a user defined context menu. This method has to be called by the component that needs to have the menu disabled.

private static native void disableBrowserContextMenuJSNI() /*-{ 
         $doc.oncontextmenu = function() { return false; }; 
     }-*/; 

Tuesday, July 7, 2009

Error Messages gxt

Its been quite a while since i made a blog post. I have been working on generating a couple of reports for my application and generating those took forever. Now here i am finally done with my application and adding a few modifications to make it look good.

I was trying to get a TextField’s regex with the first character being a number. This post is mainly to change the error message we get with the little red exclamation mark beside the text field.

shiftNo.setRegex("[0-9][[0-9][a-z][A-Z]]*");
shiftNo.getMessages().setRegexText("shift number should always start with a number");