I have been tasked with creating a work order application where the
output is PDF for printing from a browser. The output has to be one PDF
document containing one or more "work orders". Due to variable content length each work order might be more than one printed page. Each work order must have its own "page" numbering. I am using Spring MVC and Itext 5.
So imagine we have three work orders. The first will fit on one printed
page, the second requires two printed pages, and the third requires one printed page. We have a total of four printed pages.
How do make so that:
Work order one would have a page number of "1"
Work order two would have page numbers "1, 2"
Work order three would page number "1"
So basically the PDF document would be a container for multiple inner
documents that are independent from each other.
I have tried to reset the page numbers and use the footer for output but that does not seem to work. I am wondering if there some way to set a custom page counter that I can control.
public class PDFBuilder extends AbstractITextPdfView
{
#Override
protected void buildPdfDocument(Map<String, Object> model, Document doc,
PdfWriter writer, HttpServletRequest request, HttpServletResponse response)
throws Exception
{
#SuppressWarnings("unchecked") // TODO replace this with some generic goodness
List<WorkOrderDto> workOrders = (List<WorkOrderDto>) model.get("workOrders");
Integer pageCounter = 1;
// define table header cell
PdfPCell cell = new PdfPCell();
cell.setBackgroundColor(BaseColor.BLUE);
cell.setPadding(5);
// define font for table header row
Font font = FontFactory.getFont(FontFactory.HELVETICA);
font.setColor(BaseColor.WHITE);
writer.setPageEvent(new Footer(pageCounter));
for (WorkOrderDto dto : workOrders)
{
PdfPTable woHeader = new PdfPTable(4);
woHeader.setWidthPercentage(100.0f);
woHeader.setSpacingBefore(10);
if (dto.getNumber() != null)
{
woHeader.addCell("Number: " + dto.getNumber());
}
else
{
woHeader.addCell("Number: ");
}
if (dto.getOwnerNumber() != null)
{
woHeader.addCell("Owner: " + dto.getOwnerNumber());
}
else
{
woHeader.addCell("Owner: ");
}
if (dto.getTypeNumber() != null)
{
woHeader.addCell("Type: " + dto.getTypeNumber());
}
else
{
woHeader.addCell("Type: ");
}
if (dto.getScheduleDate() != null)
{
woHeader.addCell("Open Date: " + dto.getScheduleDate().toString());
}
else
{
woHeader.addCell("Open Date: ");
}
doc.add(woHeader);
PdfPTable servicesTable = new PdfPTable(4);
for (ServiceDto serviceDto : dto.getCurrentServices())
{
servicesTable.addCell("Number: " + serviceDto.getNumber());
servicesTable.addCell("Name: " + serviceDto.getName());
servicesTable.addCell("Description: " + serviceDto.getDescription());
servicesTable.addCell("Status: " + serviceDto.getStatus());
}
doc.add(servicesTable);
doc.newPage();
pageCounter++;
}
}
}
public class Footer implements PdfPageEvent
{
Font ffont = new Font(Font.FontFamily.UNDEFINED, 10, Font.ITALIC);
Integer pageCounter;
public Footer(Integer pageCounter)
{
this.pageCounter = pageCounter;
}
#Override
public void onOpenDocument(PdfWriter writer, Document document)
{
}
#Override
public void onStartPage(PdfWriter writer, Document document)
{
}
#Override
public void onEndPage(PdfWriter writer, Document document)
{
PdfContentByte cb = writer.getDirectContent();
Phrase footer = new Phrase(pageCounter + "", ffont);
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER,
footer,
(document.right() - document.left()) / 2 + document.leftMargin(),
document.bottom() - 10, 0);
}
#Override
public void onCloseDocument(PdfWriter writer, Document document)
{
}
#Override
public void onParagraph(PdfWriter writer, Document document, float paragraphPosition)
{
}
#Override
public void onParagraphEnd(PdfWriter writer, Document document, float paragraphPosition)
{
}
#Override
public void onChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title)
{
}
#Override
public void onChapterEnd(PdfWriter writer, Document document, float paragraphPosition)
{
}
#Override
public void onSection(PdfWriter writer, Document document, float paragraphPosition, int depth, Paragraph title)
{
}
#Override
public void onSectionEnd(PdfWriter writer, Document document, float paragraphPosition)
{
}
#Override
public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text)
{
}
}
I came up with my own solution which turned out to be very simple. I created a class called PageCounter with some properties to represent various counters. I pass that to the footer and then manipulate the properties while building out the pages.
public class PageCounter
{
private Integer currentPage = 1;
private Integer totalPages = 1;
public Integer getCurrentPage()
{
return currentPage;
}
public void setCurrentPage(Integer currentPage)
{
this.currentPage = currentPage;
}
public Integer getTotalPages()
{
return totalPages;
}
public void setTotalPages(Integer totalPages)
{
this.totalPages = totalPages;
}
public void increment()
{
this.currentPage++;
}
public void reset()
{
this.currentPage = 1;
}
}
public class PDFBuilder extends AbstractITextPdfView
{
#Override
protected void buildPdfDocument(Map<String, Object> model, Document doc,
PdfWriter writer, HttpServletRequest request, HttpServletResponse response)
throws Exception
{
#SuppressWarnings("unchecked") // TODO replace this with some generic goodness
List<WorkOrderDto> workOrders = (List<WorkOrderDto>) model.get("workOrders");
PageCounter pageCounter = new PageCounter();
// define table header cell
PdfPCell cell = new PdfPCell();
cell.setBackgroundColor(BaseColor.BLUE);
cell.setPadding(5);
// define font for table header row
Font font = FontFactory.getFont(FontFactory.HELVETICA);
font.setColor(BaseColor.WHITE);
writer.setPageEvent(new Footer(pageCounter));
for (WorkOrderDto dto : workOrders)
{
PdfPTable woHeader = new PdfPTable(4);
woHeader.setWidthPercentage(100.0f);
woHeader.setSpacingBefore(10);
if (dto.getNumber() != null)
{
woHeader.addCell("Number: " + dto.getNumber());
}
else
{
woHeader.addCell("Number: ");
}
if (dto.getOwnerNumber() != null)
{
woHeader.addCell("Owner: " + dto.getOwnerNumber());
}
else
{
woHeader.addCell("Owner: ");
}
if (dto.getTypeNumber() != null)
{
woHeader.addCell("Type: " + dto.getTypeNumber());
}
else
{
woHeader.addCell("Type: ");
}
if (dto.getScheduleDate() != null)
{
woHeader.addCell("Open Date: " + dto.getScheduleDate().toString());
}
else
{
woHeader.addCell("Open Date: ");
}
doc.add(woHeader);
PdfPTable servicesTable = new PdfPTable(4);
for (ServiceDto serviceDto : dto.getCurrentServices())
{
servicesTable.addCell("Number: " + serviceDto.getNumber());
servicesTable.addCell("Name: " + serviceDto.getName());
servicesTable.addCell("Description: " + serviceDto.getDescription());
servicesTable.addCell("Status: " + serviceDto.getStatus());
}
doc.add(servicesTable);
doc.newPage();
pageCounter.reset();
}
public class Footer implements PdfPageEvent
{
Font ffont = new Font(Font.FontFamily.UNDEFINED, 10, Font.ITALIC);
PageCounter pageCounter;
public Footer(PageCounter pageCounter)
{
this.pageCounter = pageCounter;
}
#Override
public void onOpenDocument(PdfWriter writer, Document document)
{
}
#Override
public void onStartPage(PdfWriter writer, Document document)
{
}
#Override
public void onEndPage(PdfWriter writer, Document document)
{
PdfContentByte cb = writer.getDirectContent();
Phrase footer = new Phrase(pageCounter.getCurrentPage() + "", ffont);
pageCounter.increment();
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER,
footer,
(document.right() - document.left()) / 2 + document.leftMargin(),
document.bottom() - 10, 0);
}
#Override
public void onCloseDocument(PdfWriter writer, Document document)
{
}
#Override
public void onParagraph(PdfWriter writer, Document document, float paragraphPosition)
{
}
#Override
public void onParagraphEnd(PdfWriter writer, Document document, float paragraphPosition)
{
}
#Override
public void onChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title)
{
}
#Override
public void onChapterEnd(PdfWriter writer, Document document, float paragraphPosition)
{
}
#Override
public void onSection(PdfWriter writer, Document document, float paragraphPosition, int depth, Paragraph title)
{
}
#Override
public void onSectionEnd(PdfWriter writer, Document document, float paragraphPosition)
{
}
#Override
public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text)
{
}
}
Related
How do I get the text of two JLabels when copying JLabel's text with TransferHandler?
Label1111111 How can I keep the text of both JLabels when copied to Label2222222.
For this reason, I will take control of two Jlabel's texts. This shape can only get the text of JLabel, which was first held. Thank you in advance for your help.
public class deneme2 extends JFrame {
private static final int COPY = 0;
private static final int NONE = 0;
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
deneme2 frame = new deneme2();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public deneme2() {
JPanel panel= new JPanel();
MouseListener listener = new DragMouseAdapter();
JLabel label1 = new JLabel("Label1111111", JLabel.CENTER);
handlerLabel(label1);
label1.addMouseListener(listener);
panel.add(label1);
JLabel label2 = new JLabel("Label2222222", JLabel.CENTER);
handlerLabel(label2);
label2.addMouseListener(listener);
panel.add(label2);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.add(panel);
setContentPane(contentPane);
}
private void handlerLabel (JLabel lbl)
{
lbl.setTransferHandler(new TransferHandler("text") {
#Override
protected void exportDone(JComponent source, Transferable data, int action) {
if (action == COPY){
((JLabel)lbl.getDropTarget().getDropTargetContext().getComponent()).getText();
//((JLabel) source).setText("LabelEmpty");
}
}
});
}
private class DragMouseAdapter extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
JComponent comp = (JComponent)e.getSource();
TransferHandler handler = comp.getTransferHandler();
handler.exportAsDrag(comp, e, TransferHandler.COPY);
}
}
}
Maybe in this way. I added inner class MyLabel and console output in your handlerLabel. Ctrl+v this under your main. It will print to console the original String property of each MyLabels.
public class MyLabel extends JLabel {
String original;
public MyLabel (String text)
{
super(text);
this.original=text;
}
public String getOriginal() {
return original;
}
public void setOriginal(String original) {
this.original = original;
}
}
public deneme2() {
JPanel panel= new JPanel();
MouseListener listener = (MouseListener) new DragMouseAdapter();
MyLabel label1 = new MyLabel("Label1111111");
handlerLabel(label1);
label1.addMouseListener(listener);
panel.add(label1);
MyLabel label2 = new MyLabel("Label2222222");
handlerLabel(label2);
label2.addMouseListener(listener);
panel.add(label2);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.add(panel);
setContentPane(contentPane);
}
private void handlerLabel(MyLabel lbl) {
lbl.setTransferHandler(new TransferHandler("text") {
#Override
protected void exportDone(JComponent source, Transferable data, int action) {
if (action == COPY) {
System.out.println(((MyLabel) lbl.getDropTarget().getDropTargetContext().getComponent()).getOriginal());
}
}
});
}
Edit: check this out. Ctrl+V this under your main. It will print to console text of source label and text of target label, also the original property. Class MyLabel implements DropTargetListener , which is then registered by new DropTarget(this, this). Then we define what will happen in overriden drop method.
public class MyLabel extends JLabel implements DropTargetListener {
String original;
public MyLabel(String text) {
super(text);
this.original = text;
new DropTarget(this, this);
this.setTransferHandler(new TransferHandler("text"));
final MouseListener listener = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent me) {
final MyLabel label = (MyLabel) me.getSource();
final TransferHandler handler = label.getTransferHandler();
handler.exportAsDrag(label, me, TransferHandler.COPY);
}
};
this.addMouseListener(listener);
}
public String getOriginal() {
return original;
}
public void setOriginal(String original) {
this.original = original;
}
#Override
public void dragEnter(DropTargetDragEvent dtde) {
}
#Override
public void dragOver(DropTargetDragEvent dtde) {
}
#Override
public void dropActionChanged(DropTargetDragEvent dtde) {
}
#Override
public void dragExit(DropTargetEvent dte) {
}
#Override
public void drop(DropTargetDropEvent dtde) {
try {
final String sourceString = (String) dtde.getTransferable().getTransferData(new DataFlavor("application/x-java-jvm-local-objectref; class=java.lang.String"));
System.out.println("Source: " + sourceString + " target: " + this.getText());
this.setText(sourceString);
System.out.println("Original target: "+this.getOriginal());
} catch (final UnsupportedFlavorException | IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public deneme2() {
JPanel panel = new JPanel();
MyLabel label1 = new MyLabel("Label1111111a");
panel.add(label1);
MyLabel label2 = new MyLabel("Label2222222b");
panel.add(label2);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.add(panel);
setContentPane(contentPane);
}
}
EDIT2: Following code generates this result see result at the end of this answer:
or
It also prints original property to console. Those could be organised differently (MyLabel, MyLabelTransferable, MyLabelDropTargetListener, MyLabelTransferHandler classes, just to give an idea for future refactoring) but it works in this way too. Consider it a quickfix for your use case.
So main class is this:
public class Deneme2 extends JFrame {
private static final int COPY = 0;
private static final int NONE = 0;
public static void main(String[] args) {
Deneme2 mainFrame = new Deneme2();
SwingUtilities.invokeLater(() -> {//let's get that frame on EDT rollin lambda style:)
mainFrame.setVisible(true);
});
}
public Deneme2() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
MyLabel label1 = new MyLabel("Label1111111a");
panel.add(label1, BorderLayout.WEST);
MyLabel label2 = new MyLabel("Label2222222b");
panel.add(label2, BorderLayout.EAST);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(100, 100, 450, 300);
panel.setBorder(new EmptyBorder(5, 5, 5, 5));
this.add(panel);
}
}
Then MyLabel.java :
public class MyLabel extends JLabel implements DropTargetListener, Transferable {
String original;
protected static final DataFlavor MYLABEL_DATA_FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + "; class=\"" + MyLabel.class.getCanonicalName() + "\"",
"MyLabel label");
protected static final DataFlavor[] SUPPORTED_FLAVORS = {MYLABEL_DATA_FLAVOR};
public MyLabel(String text) {
super(text);
this.original = text;
new DropTarget(this, this);
this.setTransferHandler(new MyLabelTransferHandler()); //here we use our custom TransferHandler
final MouseListener listener = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent me) {
final MyLabel label = (MyLabel) me.getSource();
final TransferHandler handler = label.getTransferHandler();
handler.exportAsDrag(label, me, TransferHandler.COPY);
}
};
this.addMouseListener(listener);
}
public String getOriginal() {
return original;
}
public void setOriginal(String original) {
this.original = original;
}
#Override
public void dragEnter(DropTargetDragEvent dtde) {
if (dtde.getTransferable().isDataFlavorSupported(MyLabel.MYLABEL_DATA_FLAVOR)) {
System.out.println("Drop accept - MyLabel");
dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
} else {
dtde.rejectDrag();
}
}
#Override
public void dragOver(DropTargetDragEvent dtde) {
//System.out.println("Drag over");
}
#Override
public void dropActionChanged(DropTargetDragEvent dtde) {
System.out.println("Action changed");
}
#Override
public void dragExit(DropTargetEvent dte) {
System.out.println("Exited");
}
#Override
public void drop(DropTargetDropEvent dtde) {
System.out.println("Drop detected");
if (dtde.getTransferable().isDataFlavorSupported(MyLabel.MYLABEL_DATA_FLAVOR)) {
Transferable t = dtde.getTransferable();
if (t.isDataFlavorSupported(MyLabel.MYLABEL_DATA_FLAVOR)) {
try {
Object transferData = t.getTransferData(MyLabel.MYLABEL_DATA_FLAVOR);
if (transferData instanceof MyLabel) {
MyLabel mySourceLabel = (MyLabel) transferData;
if (!(mySourceLabel.equals(this))) {
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
this.setText(mySourceLabel.getText());
mySourceLabel.setText("Empty");
System.out.println(mySourceLabel.getOriginal() + " " + this.getOriginal());
} else {
dtde.rejectDrop();
System.out.println("Drop rejected - the same MyLabel");
}
} else {
dtde.rejectDrop();
}
} catch (UnsupportedFlavorException | IOException ex) {
dtde.rejectDrop();
}
} else {
dtde.rejectDrop();
}
}
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return SUPPORTED_FLAVORS;
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(MYLABEL_DATA_FLAVOR) || flavor.equals(DataFlavor.stringFlavor);
}
#Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (flavor.equals(MYLABEL_DATA_FLAVOR)) {
return this;
} else if (flavor.equals(DataFlavor.stringFlavor)) {
return this.getText();
} else {
throw new UnsupportedFlavorException(flavor);
}
}
}
And then MyLabelTransferHandler.java :
public class MyLabelTransferHandler extends TransferHandler {
#Override
public boolean canImport(TransferHandler.TransferSupport support) {
return (support.getComponent() instanceof MyLabel) && support.isDataFlavorSupported(MyLabel.MYLABEL_DATA_FLAVOR);
}
#Override
public boolean importData(JComponent src, Transferable transferable) {
return src instanceof MyLabel;
}
#Override
public int getSourceActions(JComponent c) {
return DnDConstants.ACTION_COPY;
}
#Override
protected Transferable createTransferable(JComponent c) {
Transferable t = (MyLabel)c;
return t;
}
#Override
protected void exportDone(JComponent source, Transferable data, int action) {
System.out.println("Export done.");
}
}
After final edit result looks like this:
Little clarification:
protected static final DataFlavor MYLABEL_DATA_FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + "; class=\"" + MyLabel.class.getCanonicalName() + "\"",
"MyLabel label");
in MyLabel. That's important line. This will make getTransferData() return the same instance of MyLabel. If you would do it like:
protected static final DataFlavor MYLABEL_DATA_FLAVOR = new DataFlavor(MyLabel.class, "MyLabel label");
you won't be able to change mySourceLabel text to "Empty" in overridden drop() method, since you would be given a copy of that object.
Also Why shouldn't you extend JFrame and other components? . And you can provide checking for "Empty" text (if getText() returns "Empty", then don't change text in target MyLabel).
I am trying to delete a record trough listview from database. I am somehow got suceed in removing the record from list but didn't find any help to delete it from database.
Below is the code of my adapter. Any help will be appreciated.
Thanks in advance.
public class Row_keyskill extends BaseAdapter implements View.OnClickListener {
private ArrayList<String> listName=new ArrayList<String>();
private Activity activity;
private Context context;
String id;
SQLiteDatabase db;
int position,position1;
public Row_keyskill(Activity activity, ArrayList<String> listName) {
super();
this.listName = listName;
this.activity = activity;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return listName.size();
}
#Override
public String getItem(int position) {
// TODO Auto-generated method stub
return listName.get(position);
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public void onClick(View v) {
Toast.makeText(context,"row item clicked"+listName.get(position),Toast.LENGTH_LONG).show();
/* int position = (Integer) v.getTag();
id = listName.get(position);
// listName.remove(position);
Log.e("Row_keyskill", "position id: "+id);
position1=position+1;
Log.e("Row_keyskill", "position1 id: "+position1);
db.execSQL("DELETE FROM USER_SKILLS WHERE U_SKILL_NAME ='" + id + "'");
// itemDelete();*/
}
public static class ViewHolder
{
public TextView txtName;
public ImageView btn_delete;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder view;
LayoutInflater inflator = activity.getLayoutInflater();
if(convertView==null)
{
view = new ViewHolder();
convertView = inflator.inflate(R.layout.rowdata_keyskill,null);
view.txtName = (TextView) convertView.findViewById(R.id.keyskill_row);
// view.btn_delete=(ImageView)convertView.findViewById(R.id.btn_delete);
// view.btn_delete.setTag(position);
convertView.setTag(view);
}
else
{
view = (ViewHolder) convertView.getTag();
}
view.txtName.setText(listName.get(position));
//view.btn_delete.setOnClickListener(this);
ImageView btn_delete=(ImageView)convertView.findViewById(R.id.btn_delete);
btn_delete.setTag(position);
btn_delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//do something
int position = (Integer) v.getTag();
// SQLiteDatabase db = context.openOrCreateDatabase("JOB_PORTAL", Context.MODE_PRIVATE, null);
id = listName.get(position);
// listName.remove(position);
Log.e("Row_keyskill", "position id: " + id);
position1=position+1;
Log.e("Row_keyskill", "position1 id: "+position1);
listName.remove(position);
itemDelete(id);
//
//id = listName.get(position);
// db.execSQL("DELETE * FROM USER_SKILLS WHERE U_SKILL_NAME ='" + position + "'");
// db.delete("USER_SKILLS", new String("U_SKILL_NAME=?"), new String[]{id});
//or some other task*/
notifyDataSetChanged();
}
});
return convertView;
}
}
private void itemDelete(String id) {
//db = null;
try {
// db = context.openOrCreateDatabase("JOB_PORTAL", Context.MODE_PRIVATE,null);
// db.delete("USER_SKILLS", new String("U_SKILL_ID=?"), new String[]{id});
db.execSQL("DELETE FROM USER_SKILLS WHERE U_SKILL_NAME ='" + id + "'");
} catch (SQLException se) {
Toast.makeText(context, "Database error: " + se, Toast.LENGTH_LONG).show();
}
}
TextCellEditor passEdit has SWT.PASSWORD style. But when Add button was clicked original characters were shown instead of default * echo characters. Also focus listeners on passEdit were not working. i.e When user double clicks it should show original characters and on focus lost it should show password echo characters.
How to fix this?
public class UserAddSolution {
public static boolean flag = true;
private TextCellEditor userNameEdit;
private TextCellEditor passEdit;
class UserNamePassword {
private final String name;
private final String password;
public UserNamePassword(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
}
public UserAddSolution(final Shell shell) {
shell.setLayout(new GridLayout(2, false));
GridData layoutData = new GridData();
layoutData.heightHint = 300;
shell.setLayoutData(layoutData);
final TableViewer viewer = new TableViewer(shell, SWT.BORDER
| SWT.FULL_SELECTION | SWT.MULTI);
viewer.setContentProvider(ArrayContentProvider.getInstance());
Table table = viewer.getTable();
table.setLayout(new GridLayout());
table.setLayoutData(layoutData);
userNameEdit = new TextCellEditor(table);
passEdit = new TextCellEditor(table, SWT.PASSWORD);
final Text tex = (Text) passEdit.getControl();
final char echar = tex.getEchoChar();
tex.addFocusListener(new FocusListener() {
#Override
public void focusLost(FocusEvent e) {
tex.setEchoChar(echar);
}
#Override
public void focusGained(FocusEvent e) {
tex.setEchoChar('\0');
}
});
viewer.setCellEditors(new CellEditor[] { userNameEdit, passEdit });
viewer.setCellModifier(new ICellModifier() {
#Override
public boolean canModify(Object element, String property) {
return true;
}
#Override
public Object getValue(Object element, String property) {
UserNamePassword ele = (UserNamePassword) element;
if (property.equals("1")) {
return ele.getName();
} else if (property.equals("2")) {
return ele.getPassword();
}
return "";
}
#Override
public void modify(Object element, String property, Object value) {
}
});
viewer.setColumnProperties(new String[] { "1", "2" });
ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(
viewer) {
#Override
protected boolean isEditorActivationEvent(
ColumnViewerEditorActivationEvent event) {
return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
|| event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION
|| event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
}
};
int feature = ColumnViewerEditor.TABBING_HORIZONTAL
| ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
| ColumnViewerEditor.TABBING_VERTICAL
| ColumnViewerEditor.KEYBOARD_ACTIVATION;
TableViewerEditor.create(viewer, actSupport, feature);
TableViewerColumn userNameColumn;
userNameColumn = new TableViewerColumn(viewer, SWT.NONE);
userNameColumn.getColumn().setWidth(200);
userNameColumn.getColumn().setMoveable(true);
userNameColumn.getColumn().setText("Name");
userNameColumn.setLabelProvider(new ColumnLabelProvider() {
#Override
public Image getImage(Object element) {
return super.getImage(element);
}
#Override
public String getText(Object element) {
UserNamePassword fdf = (UserNamePassword) element;
return super.getText(fdf.getName());
}
});
TableViewerColumn passwordColumn;
passwordColumn = new TableViewerColumn(viewer, SWT.NONE);
passwordColumn.getColumn().setWidth(200);
passwordColumn.getColumn().setMoveable(true);
passwordColumn.getColumn().setText("Password");
passwordColumn.setLabelProvider(new ColumnLabelProvider() {
#Override
public Image getImage(Object element) {
return super.getImage(element);
}
#Override
public String getText(Object element) {
UserNamePassword fdf = (UserNamePassword) element;
return super.getText(fdf.getPassword());
}
#Override
public void update(ViewerCell cell) {
super.update(cell);
}
});
viewer.setInput(new ArrayList<UserNamePassword>());
table.setLinesVisible(true);
table.setHeaderVisible(true);
viewer.refresh();
Button add = new Button(shell, SWT.PUSH);
add.setText("Add");
add.addSelectionListener(new SelectionListener() {
#Override
public void widgetSelected(SelectionEvent e) {
viewer.add(new UserNamePassword("abc", "xyz"));
}
#Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
new UserAddSolution(shell);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
The ColumnLabelProvider is responsible for displaying the data in the table. The text widget appears only when you edit a cell. That's why you need to tell the label provider to mask characters and not to display the plain text.
As a consequence you don't need to set SWT.PASSWORD style to the passEdit since you want to display the password in the editor as plain text:
passEdit = new TextCellEditor(table, SWT.NONE);
You also don't need the FocusListener and you can set the echo char as a constant:
private static final String ECHARSTR = Character.toString((char)9679);
So you can remove 15 lines below the initialization of passEdit
I don't know if there is a solution for this in JFace (I think not) but you can easily solve this by modifying your existing label provider. My suggestion is to modify the getText() method of ColumnLabelProvider for passwordColumn as follows:
#Override
public String getText(Object element)
{
UserNamePassword fdf = (UserNamePassword)element;
return fdf.getPassword().replaceAll(".", ECHARSTR);
}
I am implementing a directory structure like Windows Explorer. I want to re-render a specific node of tree after any folder operations is done such as add folder, remove folder... etc
private ListDataProvider<Object> dataProvider= new ListDataProvider<Object>();
private Object current;//store object of currentNode;
private Map<Object, ListDataProvider<Object>> keyprovider =
new HashMap<Object,ListDataProvider<Object>>();
private CellTree tree;
// keeps a map for storing dataproviders in each hierarchy ,
public void setListToCurrentNode(List<Object> newList){
//adding this newlist to current data provider not reflecting to display
keyprovider.get(currentObject).setList(newList);
}
public void onModuleLoad(){
treeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
public void onSelectionChange(SelectionChangeEvent event) {
//setting current object as selected node
current = treeSelectionModel.getSelectedObject();
}
});
}
Data providers force to re-render corresponding DOM. Code below demonstrates how it works.
This example generates 2-leveled tree, where you can refresh (re-generate) leaf nodes by pressing Enter key on selected root node.
I tried to keep code as simple as possible, all needed stuff contained right here.
public class TestEntryPoint implements EntryPoint {
private static final Logger LOG = Logger.getLogger(TestEntryPoint.class.getName());
class Item {
String name;
boolean leaf;
public Item(String name, boolean leaf) {
this.name = name;
this.leaf = leaf;
}
#Override
public String toString() {
return "Item {name=" + name + ", leaf=" + leaf + "}";
}
}
class ItemCell extends AbstractCell<Item> {
public ItemCell() {
super("keydown");
}
#Override
public void render(Context context, Item value, SafeHtmlBuilder sb) {
if (value != null) sb.appendEscaped(value.name);
}
#Override
protected void onEnterKeyDown(
Context context, Element parent, Item value, NativeEvent event, ValueUpdater<Item> valueUpdater) {
LOG.info("ItemCell.onEnterKeyDown: value=" + value);
if (value == null || value.leaf) return;
ListDataProvider<Item> leafDataProvider = leafDataProviders.get(value.name);
if (leafDataProvider == null) return;
// -->> Here we generate new childs of selected root node
leafDataProvider.setList(generateLeafs());
}
}
class ItemTreeViewModel implements TreeViewModel {
#Override
public <T> NodeInfo<?> getNodeInfo(T value) {
return new DefaultNodeInfo<Item>(
value == null ? rootDataProvider : leafDataProviders.get(((Item) value).name),
new ItemCell());
}
#Override
public boolean isLeaf(Object value) {
return (value instanceof Item) && ((Item) value).leaf;
}
}
private int id = 0;
private CellTree cellTree;
private ListDataProvider<Item> rootDataProvider;
private Map<String, ListDataProvider<Item>> leafDataProviders = new HashMap<String, ListDataProvider<Item>>();
private List<Item> generateLeafs() {
List<Item> leafList = new ArrayList<Item>(5);
for (int j = 0; j < 5; ++j)
leafList.add(new Item("Leaf Item " + (++id), true));
return leafList;
}
public void onModuleLoad() {
List<Item> rootList = new ArrayList<Item>(10);
for (int i = 0; i < 10; ++i) {
Item rootItem = new Item("Root Item " + (++id), false);
rootList.add(rootItem);
leafDataProviders.put(rootItem.name, new ListDataProvider<Item>(generateLeafs()));
}
rootDataProvider = new ListDataProvider<Item>(rootList);
cellTree = new CellTree(new ItemTreeViewModel(), null);
cellTree.setAnimationEnabled(true);
RootLayoutPanel.get().add(cellTree);
}
}
Solved by Changing Listdataprovider into AsyncDataProvider ,Implementation is below
private final class DataProvider extends AsyncDataProvider<Object> {
private Object value;
private List Objs;
DataProvider(Object value) {
this.value = value;
}
public void update(List objs) {//Call update when you wanted to refresh tree
this.objs=objs;
for (HasData<Object> disp : getDataDisplays()) {
this.onRangeChanged(disp);
break;
}
}
#Override
protected void onRangeChanged(final HasData<Object> display) {
updateRowData(0, objs);
updateRowCount(objs.size(), true);
}
Using GWT 2.4...
I am building upon a complex Composite dual view/edit mode implementation that is backed GWT's DataGrid and MultiSelectionModel. My goal is for a user to be able to click a checkbox in each row that they'd like to post updates for.
Here's a screenshot from a semi-functional interface:
Note the selected (highlighted) rows.
Now the problem is that when I type something in any of the cells (e.g., the first row's $ cell under the $/Mw 1 composite cell header), then click that row's checkbox (or any other row's checkbox for that matter) to select or de-select, the value gets reset to the original value when the screen's data was first requested. Not desired behavior by any stretch!
Let's take a look at my custom implementation for the grid. (Excuse the length).
public abstract class ToggleableGrid<T extends Identifiable<?>> extends Composite {
private static final int CHKBOX_COLUMN_WIDTH = App.INSTANCE.checkboxColumnWidth();
private static final DisplayMode DEFAULT_MODE = DisplayMode.VIEW;
private ProvidesKey<T> keyProvider;
private DataGrid<T> grid;
private MultiSelectionModel<T> selectionModel;
private ListDataProvider<T> dataProvider;
private int tabIndex = 0;
public ToggleableGrid() {
final DataGridConfiguration config = new DefaultDataGridConfiguration();
initGrid(config);
}
public ToggleableGrid(DataGridConfiguration config) {
initGrid(config);
}
private void initGrid(DataGridConfiguration config) {
keyProvider = new ProvidesKey<T>() {
#Override
public Object getKey(T item) {
return item == null ? null : item.getId();
}
};
grid = new DataGrid<T>(config.getPageSize(), config.getResources(), keyProvider);
// Set the message to display when the table is empty.
grid.setEmptyTableWidget(new Label(UiMessages.INSTANCE.no_results()));
initWidget(grid);
setVisible(true);
}
public void setInput(List<T> content) {
setInput(content, DEFAULT_MODE);
}
public void setInput(List<T> content, DisplayMode mode) {
resetTableColumns();
if (isInEditMode(mode)) {
// Add a selection model so we can select cells
selectionModel = new MultiSelectionModel<T>(keyProvider);
grid.setSelectionModel(selectionModel, DefaultSelectionEventManager.<T> createCheckboxManager(0));
addRowSelector();
}
dataProvider = new ListDataProvider<T>(content);
final ListHandler<T> sortHandler = new ListHandler<T>(dataProvider.getList());
grid.addColumnSortHandler(sortHandler);
initializeStructure(constructMetadata(), sortHandler, mode);
dataProvider.addDataDisplay(grid);
}
// see https://stackoverflow.com/questions/3772480/remove-all-columns-from-a-celltable
// concrete classes are forced to maintain a handle on all columns added
private void resetTableColumns() {
for (final Column<T, ?> column: allColumns()) {
grid.removeColumn(column);
}
allColumns().clear();
}
protected boolean isInEditMode(DisplayMode currentDisplayMode) {
boolean result = false;
if (currentDisplayMode.equals(DisplayMode.EDIT)) {
result = true;
}
return result;
}
protected abstract Set<Column<T, ?>> allColumns();
protected abstract TableMetadata constructMetadata();
protected abstract void initializeStructure(TableMetadata metadata, ListHandler<T> sortHandler, DisplayMode mode);
protected void setColumnHorizontalAlignment(Column<T, ?> column, HorizontalAlignmentConstant alignment) {
column.setHorizontalAlignment(alignment);
}
// TODO figure out how to add a checkbox to column header that provides select/de-select all capability
// see https://stackoverflow.com/questions/6174689/gwt-celltable-programmatically-select-checkboxcell
protected void addRowSelector() {
final Column<T, Boolean> rowSelectColumn = new Column<T, Boolean>(new CheckboxCell(true, false)) {
#Override
public Boolean getValue(T value) {
Boolean result;
// check for null value and return null;
if(value == null || value.getId() == null) {
result = null;
} else { // get value from the selection model
result = selectionModel.isSelected(value);
}
return result;
}
};
addColumn(rowSelectColumn, UiMessages.INSTANCE.select());
setColumnWidth(rowSelectColumn, CHKBOX_COLUMN_WIDTH, Unit.PX);
setColumnHorizontalAlignment(rowSelectColumn, HasHorizontalAlignment.ALIGN_CENTER);
}
protected void setColumnWidth(Column<T, ?> column, int width, Unit unit) {
grid.setColumnWidth(column, width, unit);
}
protected void addColumn(Column<T, ?> column, String columnHeaderName) {
addColumn(column, columnHeaderName, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected void addColumn(Column<T, ?> column, String columnHeaderName, HorizontalAlignmentConstant alignment) {
final SafeHtmlBuilder sb = new SafeHtmlBuilder();
final String divStart = "<div align=\""+ alignment.getTextAlignString() + "\" class=\"" +UiResources.INSTANCE.style().word_wrap() + "\">";
sb.appendHtmlConstant(divStart).appendEscaped(columnHeaderName).appendHtmlConstant("</div>");
final SafeHtml header = sb.toSafeHtml();
grid.addColumn(column, header);
allColumns().add(column);
}
protected CompositeCell<T> generateCompositeCell(final List<HasCell<T, ?>> hasCells) {
final CompositeCell<T> compositeCell = new CompositeCell<T>(hasCells) {
#Override
public void render(Context context, T value, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<table><tbody><tr>");
super.render(context, value, sb);
sb.appendHtmlConstant("</tr></tbody></table>");
}
#Override
protected Element getContainerElement(Element parent) {
// Return the first TR element in the table.
return parent.getFirstChildElement().getFirstChildElement().getFirstChildElement();
}
#Override
protected <X> void render(Context context, T value,
SafeHtmlBuilder sb, HasCell<T, X> hasCell) {
final Cell<X> cell = hasCell.getCell();
sb.appendHtmlConstant("<td>");
cell.render(context, hasCell.getValue(value), sb);
sb.appendHtmlConstant("</td>");
}
};
return compositeCell;
}
// FIXME not working quite the way we'd expect, index incremented within column for each row, not each row by column
protected int nextTabIndex() {
tabIndex++;
return tabIndex;
}
protected AbstractCellTable<T> getGrid() {
return grid;
}
/**
* Gets the selected (row(s) of) data from grid (used in edit mode)
* #return the selected data (as per selection model)
*/
public List<T> getSelectedData() {
final List<T> data = new ArrayList<T>();
data.addAll(selectionModel.getSelectedSet());
return data;
}
/**
* Gets all (row(s) of) data in grid (used in edit mode)
* #return all data as list
*/
public List<T> getAllData() {
return dataProvider.getList();
}
/**
* Clears the currently selected (row(s) of) data (used in edit mode)
*/
public void clearSelectedData() {
selectionModel.clear();
grid.redraw();
}
}
So, the interesting methods to stare at above (I think) are setInput, generateCompositeCell and addRowSelector.
We initialize the grid with List data and set a display mode in setInput. It's here as well that the selection model is initialized. It uses GWT's DefaultSelectionEventManager createCheckboxManager().
I've been trying to grok the event model, but it eludes me. I've visited the following sources online, but have come up short on avenues to solving this problem.
-- https://groups.google.com/forum/?fromgroups#!topic/google-web-toolkit/k5sfURxDaVg
AbstractInputCell's getConsumedEventsImpl adds focus, blur and keydown, so this (I believe) is not a track I need to explore
-- GWT CellTable programmatically select CheckBoxCell
The various ways you can instantiate a CheckBoxCell got me curious, and I've tried many constructor argument permutations, but the one I settled on (true, false) is (I believe) the right one
Agreeing here and now (before being reprimanded) that there's perhaps some unnecessary complexity in my implementation, but I am looking for guidance nonetheless. Thanks!
Update
If it helps here's an impl of the aforementioned ToggleableGrid. If anything it gives you more detail on what goes into each CompositeCell. For details on AbstractValidatableColumn and ValidatableInputCell, see: In search of a GWT validation example... where art thou?.
public class EnergyOfferGrid extends ToggleableGrid<EnergyOfferDTO> {
public EnergyOfferGrid() {
super();
}
public EnergyOfferGrid(DataGridConfiguration config) {
super(config);
}
private static final int MAX_NUMBER_OF_MW_PRICE_POINTS = App.INSTANCE.maxNoOfMwPricePoints();
private Set<Column<EnergyOfferDTO, ?>> columns = new HashSet<Column<EnergyOfferDTO, ?>>();
#Override
protected Set<Column<EnergyOfferDTO, ?>> allColumns() {
return columns;
}
#Override
protected TableMetadata constructMetadata() {
final TableMetadata metadata = new TableMetadata();
// TODO Consider a predefined set of ReferenceData to be held in a common package
// Use Slope
metadata.addColumnMetadata(UiMessages.INSTANCE.use_slope(), new String[] {UiMessages.INSTANCE.yes(), UiMessages.INSTANCE.no()}, new String[] {"true", "false"});
return metadata;
}
#Override
protected void initializeStructure(TableMetadata metadata, ListHandler<EnergyOfferDTO> sortHandler, DisplayMode currentDisplayMode) {
addHourColumn(sortHandler);
addUseSlopeColumn(metadata, sortHandler, currentDisplayMode);
for (int i = 0; i < MAX_NUMBER_OF_MW_PRICE_POINTS; i++) { // zero-based indexing
addPriceMwColumn(i, currentDisplayMode);
}
}
protected void addHourColumn(ListHandler<EnergyOfferDTO> sortHandler) {
final Column<EnergyOfferDTO, String> hourColumn = new Column<EnergyOfferDTO, String>(new TextCell()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
String result = "";
if (energyOffer.getId() != null) {
final String isoDateTime = energyOffer.getId().getOperatingHour();
if (isoDateTime != null && !isoDateTime.isEmpty()) {
final Date dateTime = CSTimeUtil.isoToDate(isoDateTime);
if (dateTime != null) {
result = CSTimeUtil.dateToHour(dateTime);
}
}
}
return result;
}
};
hourColumn.setSortable(true);
sortHandler.setComparator(hourColumn, new Comparator<EnergyOfferDTO>() {
#Override
public int compare(EnergyOfferDTO eo1, EnergyOfferDTO eo2) {
final String date1 = eo1.getId() != null ? eo1.getId().getOperatingHour() : "";
final String date2 = eo2.getId() != null ? eo2.getId().getOperatingHour() : "";
return date1.compareTo(date2);
}
});
// We know that the data is sorted by hour by default.
getGrid(). getColumnSortList().push(hourColumn);
addColumn(hourColumn, UiMessages.INSTANCE.hour());
setColumnWidth(hourColumn, 45, Unit.PX);
setColumnHorizontalAlignment(hourColumn, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected void addUseSlopeColumn(TableMetadata metadata, ListHandler<EnergyOfferDTO> sortHandler, DisplayMode currentDisplayMode) {
final ReferenceData refData = metadata.allColumnMetadata().get(UiMessages.INSTANCE.use_slope());
Column<EnergyOfferDTO, String> useSlopeColumn;
Cell<String> cell;
if (isInEditMode(currentDisplayMode)) {
cell = new ReferenceDataBackedSelectionCell(refData);
} else {
cell = new TextCell();
}
useSlopeColumn = new Column<EnergyOfferDTO, String>(cell) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
return refData.getDisplayValueForSubmitValue(Boolean.toString(energyOffer.isSlopeUsed()));
}
};
useSlopeColumn.setSortable(true);
sortHandler.setComparator(useSlopeColumn, new Comparator<EnergyOfferDTO>() {
#Override
public int compare(EnergyOfferDTO eo1, EnergyOfferDTO eo2) {
final String slopeUsed1 = String.valueOf(eo1.isSlopeUsed());
final String slopeUsed2 = String.valueOf(eo1.isSlopeUsed());
return slopeUsed1.compareTo(slopeUsed2);
}
});
addColumn(useSlopeColumn, UiMessages.INSTANCE.use_slope());
setColumnWidth(useSlopeColumn, 75, Unit.PX);
setColumnHorizontalAlignment(useSlopeColumn, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected void addPriceMwColumn(final int colIndex, DisplayMode currentDisplayMode) {
// Construct a composite cell for energy offers that includes a pair of text inputs
final List<HasCell<EnergyOfferDTO, ?>> columns = new ArrayList<
HasCell<EnergyOfferDTO, ?>>();
// this DTO is passed along so that price and mw values for new entries are kept together
final OfferPriceMwPair newOfferPriceMwPair = new OfferPriceMwPair();
// Price
final Column<EnergyOfferDTO, String> priceColumn = generatePriceColumn(colIndex, newOfferPriceMwPair, currentDisplayMode);
columns.add(priceColumn);
// MW
final Column<EnergyOfferDTO, String> mwColumn = generateMwColumn(colIndex, newOfferPriceMwPair, currentDisplayMode);
columns.add(mwColumn);
// Composite
final CompositeCell<EnergyOfferDTO> priceMwColumnInnards = generateCompositeCell(columns);
final IdentityColumn<EnergyOfferDTO> priceMwColumn = new IdentityColumn<EnergyOfferDTO>(priceMwColumnInnards);
final StringBuilder colHeader = new StringBuilder();
colHeader.append(UiMessages.INSTANCE.price_mw_header()).append(" ").append(String.valueOf(colIndex + 1));
addColumn(priceMwColumn, colHeader.toString());
setColumnWidth(priceMwColumn, 7, Unit.EM);
setColumnHorizontalAlignment(priceMwColumn, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected Column<EnergyOfferDTO, String> generatePriceColumn(final int colIndex, final OfferPriceMwPair newOfferPriceMwPair, DisplayMode currentDisplayMode) {
Column<EnergyOfferDTO, String> priceColumn;
if (isInEditMode(currentDisplayMode)) {
priceColumn = new BigDecimalValidatableColumn<EnergyOfferDTO, OfferPriceMwPair>(nextTabIndex(), getGrid()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
return obtainPriceValue(colIndex, energyOffer, false);
}
#Override
public void doUpdate(int index, EnergyOfferDTO energyOffer, String value) {
if (value != null && !value.isEmpty()) {
// number format exceptions should be caught and handled by event bus's handle method
final double valueAsDouble = NumberFormat.getDecimalFormat().parse(value);
final BigDecimal price = BigDecimal.valueOf(valueAsDouble);
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final OfferPriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair == null) { // we have a new price value
newOfferPriceMwPair.setPrice(price);
offerPriceCurve.add(newOfferPriceMwPair);
} else {
offerPriceMwPair.setPrice(price);
}
}
}
#Override
protected String getPropertyName() {
return "price";
}
#Override
protected Class<OfferPriceMwPair> getPropertyOwner() {
return OfferPriceMwPair.class;
}
};
} else {
priceColumn = new Column<EnergyOfferDTO, String>(new TextCell()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
final String result = obtainPriceValue(colIndex, energyOffer, true);
return result;
}
};
}
return priceColumn;
}
private String obtainPriceValue(final int colIndex, EnergyOfferDTO energyOffer, boolean withCurrency) {
String result = "";
if (energyOffer != null) {
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final int numberOfPairs = offerPriceCurve.size();
if (colIndex < numberOfPairs) {
final OfferPriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair != null) {
final BigDecimal price = offerPriceMwPair.getPrice();
if (price != null) {
final double value = price.doubleValue();
if (withCurrency) {
result = NumberFormat.getCurrencyFormat().format(value);
} else {
result = NumberFormat.getDecimalFormat().format(value);
}
}
}
}
}
return result;
}
protected Column<EnergyOfferDTO, String> generateMwColumn(final int colIndex, final OfferPriceMwPair newOfferPriceMwPair, DisplayMode currentDisplayMode) {
Column<EnergyOfferDTO, String> mwColumn;
if (isInEditMode(currentDisplayMode)) {
mwColumn = new BigDecimalValidatableColumn<EnergyOfferDTO, PriceMwPair>(nextTabIndex(), getGrid()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
return obtainMwValue(colIndex, energyOffer);
}
#Override
public void doUpdate(int index, EnergyOfferDTO energyOffer, String value) {
if (value != null && !value.isEmpty()) {
// number format exceptions should be caught and handled by event bus's handle method
final double valueAsDouble = NumberFormat.getDecimalFormat().parse(value);
final BigDecimal mw = BigDecimal.valueOf(valueAsDouble);
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final OfferPriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair == null) { // we have a new price value
newOfferPriceMwPair.setMw(mw);
offerPriceCurve.add(newOfferPriceMwPair);
} else {
offerPriceMwPair.setMw(mw);
}
}
}
#Override
protected String getPropertyName() {
return "mw";
}
#Override
protected Class<PriceMwPair> getPropertyOwner() {
return PriceMwPair.class;
}
};
} else {
mwColumn = new Column<EnergyOfferDTO, String>(new TextCell()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
final String result = obtainMwValue(colIndex, energyOffer);
return result;
}
};
}
return mwColumn;
}
private String obtainMwValue(final int colIndex, EnergyOfferDTO energyOffer) {
String result = "";
if (energyOffer != null) {
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final int numberOfPairs = offerPriceCurve.size();
if (colIndex < numberOfPairs) {
final PriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair != null) {
final BigDecimal mw = offerPriceMwPair.getMw();
if (mw != null) {
result = NumberFormat.getDecimalFormat().format(mw);
}
}
}
}
return result;
}
}
All that custom work w.r.t. WrapperCell and CompositeValidatableColumn was unnecessary.
It turns out that there's a way you should not construct CompositeCells. See http://code.google.com/p/google-web-toolkit/issues/detail?id=5714. My CompositeCells were not receiving events. So, I changed the way I construct them in ToggleableGrid.
protected CompositeCell<T> generateCompositeCell(final List<HasCell<T, String>> hasCells) {
final CompositeCell<T> compositeCell = new CompositeCell<T>(hasCells) {
// to not run afoul of http://code.google.com/p/google-web-toolkit/issues/detail?id=5714
#Override
public void render(Context context, T value, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<div style=\"display: inline\">");
super.render(context, value, sb);
sb.appendHtmlConstant("</div>");
}
#Override
protected Element getContainerElement(Element parent) {
// Return the first element in the DIV.
return parent.getFirstChildElement();
}
};
return compositeCell;
}
After that change and incorporating my other validation-oriented classes: ValidatableFieldUpdater, AbstractValidatableColumn (and derivatives), ValidatableInputField and ConversionResult, life couldn't be more grand!