1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package com.google.gwt.sample.showcase.client.content.cell; 17 18 import com.google.gwt.cell.client.Cell.Context; 19 import com.google.gwt.cell.client.CheckboxCell; 20 import com.google.gwt.cell.client.ClickableTextCell; 21 import com.google.gwt.cell.client.EditTextCell; 22 import com.google.gwt.cell.client.FieldUpdater; 23 import com.google.gwt.cell.client.NumberCell; 24 import com.google.gwt.cell.client.SelectionCell; 25 import com.google.gwt.cell.client.TextCell; 26 import com.google.gwt.core.client.GWT; 27 import com.google.gwt.core.client.RunAsyncCallback; 28 import com.google.gwt.dom.builder.shared.DivBuilder; 29 import com.google.gwt.dom.builder.shared.TableCellBuilder; 30 import com.google.gwt.dom.builder.shared.TableRowBuilder; 31 import com.google.gwt.dom.client.Style.Cursor; 32 import com.google.gwt.dom.client.Style.OutlineStyle; 33 import com.google.gwt.dom.client.Style.Unit; 34 import com.google.gwt.i18n.client.Constants; 35 import com.google.gwt.i18n.client.NumberFormat; 36 import com.google.gwt.resources.client.ClientBundle; 37 import com.google.gwt.resources.client.CssResource; 38 import com.google.gwt.safehtml.shared.SafeHtml; 39 import com.google.gwt.safehtml.shared.SafeHtmlBuilder; 40 import com.google.gwt.sample.showcase.client.ContentWidget; 41 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData; 42 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseRaw; 43 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource; 44 import com.google.gwt.sample.showcase.client.content.cell.ContactDatabase.Category; 45 import com.google.gwt.sample.showcase.client.content.cell.ContactDatabase.ContactInfo; 46 import com.google.gwt.text.shared.AbstractSafeHtmlRenderer; 47 import com.google.gwt.text.shared.SafeHtmlRenderer; 48 import com.google.gwt.uibinder.client.UiBinder; 49 import com.google.gwt.uibinder.client.UiField; 50 import com.google.gwt.user.cellview.client.AbstractCellTable.Style; 51 import com.google.gwt.user.cellview.client.AbstractCellTableBuilder; 52 import com.google.gwt.user.cellview.client.AbstractHeaderOrFooterBuilder; 53 import com.google.gwt.user.cellview.client.Column; 54 import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; 55 import com.google.gwt.user.cellview.client.ColumnSortList; 56 import com.google.gwt.user.cellview.client.ColumnSortList.ColumnSortInfo; 57 import com.google.gwt.user.cellview.client.DataGrid; 58 import com.google.gwt.user.cellview.client.Header; 59 import com.google.gwt.user.cellview.client.SimplePager; 60 import com.google.gwt.user.cellview.client.SimplePager.TextLocation; 61 import com.google.gwt.user.cellview.client.TextHeader; 62 import com.google.gwt.user.client.rpc.AsyncCallback; 63 import com.google.gwt.user.client.ui.HasHorizontalAlignment; 64 import com.google.gwt.user.client.ui.Label; 65 import com.google.gwt.user.client.ui.Widget; 66 import com.google.gwt.view.client.DefaultSelectionEventManager; 67 import com.google.gwt.view.client.MultiSelectionModel; 68 import com.google.gwt.view.client.SelectionModel; 69 70 import java.util.ArrayList; 71 import java.util.Comparator; 72 import java.util.Date; 73 import java.util.HashSet; 74 import java.util.List; 75 import java.util.Set; 76 77 /** 78 * Defines a custom table that displays a contact in each row. This is an 79 * example that shows how to completely customize the appearance of the headers, 80 * data rows, and footers in a CellTable. 81 */ 82 @ShowcaseRaw({"ContactDatabase.java", "CwCustomDataGrid.ui.xml", "CwCustomDataGrid.gss"}) 83 public class CwCustomDataGrid extends ContentWidget { 84 85 /** 86 * The constants used in this Content Widget. 87 */ 88 @ShowcaseSource 89 public static interface CwConstants extends Constants { cwCustomDataGridColumnAddress()90 String cwCustomDataGridColumnAddress(); 91 cwCustomDataGridColumnAge()92 String cwCustomDataGridColumnAge(); 93 cwCustomDataGridColumnCategory()94 String cwCustomDataGridColumnCategory(); 95 cwCustomDataGridColumnFirstName()96 String cwCustomDataGridColumnFirstName(); 97 cwCustomDataGridColumnLastName()98 String cwCustomDataGridColumnLastName(); 99 cwCustomDataGridDescription()100 String cwCustomDataGridDescription(); 101 cwCustomDataGridEmpty()102 String cwCustomDataGridEmpty(); 103 cwCustomDataGridName()104 String cwCustomDataGridName(); 105 } 106 107 /** 108 * The UiBinder interface used by this example. 109 */ 110 @ShowcaseSource 111 interface Binder extends UiBinder<Widget, CwCustomDataGrid> { 112 } 113 114 /** 115 * The resources used by this example. 116 */ 117 @ShowcaseSource 118 interface Resources extends ClientBundle { 119 120 /** 121 * Get the styles used but this example. 122 */ 123 @Source("CwCustomDataGrid.gss") styles()124 Styles styles(); 125 } 126 127 /** 128 * The CSS Resources used by this example. 129 */ 130 @ShowcaseSource 131 interface Styles extends CssResource { 132 /** 133 * Indents cells in child rows. 134 */ childCell()135 String childCell(); 136 137 /** 138 * Applies to group headers. 139 */ groupHeaderCell()140 String groupHeaderCell(); 141 } 142 143 /** 144 * Renders custom table headers. The top header row includes the groups "Name" 145 * and "Information", each of which spans multiple columns. The second row of 146 * the headers includes the contacts' first and last names grouped under the 147 * "Name" category. The second row also includes the age, category, and 148 * address of the contacts grouped under the "Information" category. 149 */ 150 @ShowcaseSource 151 private class CustomHeaderBuilder extends AbstractHeaderOrFooterBuilder<ContactInfo> { 152 153 private Header<String> firstNameHeader = new TextHeader(constants 154 .cwCustomDataGridColumnFirstName()); 155 private Header<String> lastNameHeader = new TextHeader(constants 156 .cwCustomDataGridColumnLastName()); 157 private Header<String> ageHeader = new TextHeader(constants.cwCustomDataGridColumnAge()); 158 private Header<String> categoryHeader = new TextHeader(constants 159 .cwCustomDataGridColumnCategory()); 160 private Header<String> addressHeader = 161 new TextHeader(constants.cwCustomDataGridColumnAddress()); 162 CustomHeaderBuilder()163 public CustomHeaderBuilder() { 164 super(dataGrid, false); 165 setSortIconStartOfLine(false); 166 } 167 168 @Override buildHeaderOrFooterImpl()169 protected boolean buildHeaderOrFooterImpl() { 170 Style style = dataGrid.getResources().style(); 171 String groupHeaderCell = resources.styles().groupHeaderCell(); 172 173 // Add a 2x2 header above the checkbox and show friends columns. 174 TableRowBuilder tr = startRow(); 175 tr.startTH().colSpan(2).rowSpan(2) 176 .className(style.header() + " " + style.firstColumnHeader()); 177 tr.endTH(); 178 179 /* 180 * Name group header. Associated with the last name column, so clicking on 181 * the group header sorts by last name. 182 */ 183 TableCellBuilder th = tr.startTH().colSpan(2).className(groupHeaderCell); 184 enableColumnHandlers(th, lastNameColumn); 185 th.style().trustedProperty("border-right", "10px solid white").cursor(Cursor.POINTER) 186 .endStyle(); 187 th.text("Name").endTH(); 188 189 // Information group header. 190 th = tr.startTH().colSpan(3).className(groupHeaderCell); 191 th.text("Information").endTH(); 192 193 // Get information about the sorted column. 194 ColumnSortList sortList = dataGrid.getColumnSortList(); 195 ColumnSortInfo sortedInfo = (sortList.size() == 0) ? null : sortList.get(0); 196 Column<?, ?> sortedColumn = (sortedInfo == null) ? null : sortedInfo.getColumn(); 197 boolean isSortAscending = (sortedInfo == null) ? false : sortedInfo.isAscending(); 198 199 // Add column headers. 200 tr = startRow(); 201 buildHeader(tr, firstNameHeader, firstNameColumn, sortedColumn, isSortAscending, false, false); 202 buildHeader(tr, lastNameHeader, lastNameColumn, sortedColumn, isSortAscending, false, false); 203 buildHeader(tr, ageHeader, ageColumn, sortedColumn, isSortAscending, false, false); 204 buildHeader(tr, categoryHeader, categoryColumn, sortedColumn, isSortAscending, false, false); 205 buildHeader(tr, addressHeader, addressColumn, sortedColumn, isSortAscending, false, true); 206 tr.endTR(); 207 208 return true; 209 } 210 211 /** 212 * Renders the header of one column, with the given options. 213 * 214 * @param out the table row to build into 215 * @param header the {@link Header} to render 216 * @param column the column to associate with the header 217 * @param sortedColumn the column that is currently sorted 218 * @param isSortAscending true if the sorted column is in ascending order 219 * @param isFirst true if this the first column 220 * @param isLast true if this the last column 221 */ buildHeader(TableRowBuilder out, Header<?> header, Column<ContactInfo, ?> column, Column<?, ?> sortedColumn, boolean isSortAscending, boolean isFirst, boolean isLast)222 private void buildHeader(TableRowBuilder out, Header<?> header, Column<ContactInfo, ?> column, 223 Column<?, ?> sortedColumn, boolean isSortAscending, boolean isFirst, boolean isLast) { 224 // Choose the classes to include with the element. 225 Style style = dataGrid.getResources().style(); 226 boolean isSorted = (sortedColumn == column); 227 StringBuilder classesBuilder = new StringBuilder(style.header()); 228 if (isFirst) { 229 classesBuilder.append(" " + style.firstColumnHeader()); 230 } 231 if (isLast) { 232 classesBuilder.append(" " + style.lastColumnHeader()); 233 } 234 if (column.isSortable()) { 235 classesBuilder.append(" " + style.sortableHeader()); 236 } 237 if (isSorted) { 238 classesBuilder.append(" " 239 + (isSortAscending ? style.sortedHeaderAscending() : style.sortedHeaderDescending())); 240 } 241 242 // Create the table cell. 243 TableCellBuilder th = out.startTH().className(classesBuilder.toString()); 244 245 // Associate the cell with the column to enable sorting of the column. 246 enableColumnHandlers(th, column); 247 248 // Render the header. 249 Context context = new Context(0, 2, header.getKey()); 250 renderSortableHeader(th, context, header, isSorted, isSortAscending); 251 252 // End the table cell. 253 th.endTH(); 254 } 255 } 256 257 /** 258 * Renders custom table footers that appear beneath the columns in the table. 259 * This footer consists of a single cell containing the average age of all 260 * contacts on the current page. This is an example of a dynamic footer that 261 * changes with the row data in the table. 262 */ 263 @ShowcaseSource 264 private class CustomFooterBuilder extends AbstractHeaderOrFooterBuilder<ContactInfo> { 265 CustomFooterBuilder()266 public CustomFooterBuilder() { 267 super(dataGrid, true); 268 } 269 270 @Override buildHeaderOrFooterImpl()271 protected boolean buildHeaderOrFooterImpl() { 272 String footerStyle = dataGrid.getResources().style().footer(); 273 274 // Calculate the age of all visible contacts. 275 String ageStr = ""; 276 List<ContactInfo> items = dataGrid.getVisibleItems(); 277 if (items.size() > 0) { 278 int totalAge = 0; 279 for (ContactInfo item : items) { 280 totalAge += item.getAge(); 281 } 282 ageStr = "Avg: " + totalAge / items.size(); 283 } 284 285 // Cells before age column. 286 TableRowBuilder tr = startRow(); 287 tr.startTH().colSpan(4).className(footerStyle).endTH(); 288 289 // Show the average age of all contacts. 290 TableCellBuilder th = 291 tr.startTH().className(footerStyle).align( 292 HasHorizontalAlignment.ALIGN_CENTER.getTextAlignString()); 293 th.text(ageStr); 294 th.endTH(); 295 296 // Cells after age column. 297 tr.startTH().colSpan(2).className(footerStyle).endTH(); 298 tr.endTR(); 299 300 return true; 301 } 302 } 303 304 /** 305 * Renders the data rows that display each contact in the table. 306 */ 307 @ShowcaseSource 308 private class CustomTableBuilder extends AbstractCellTableBuilder<ContactInfo> { 309 310 private final int todayMonth; 311 312 private final String childCell = " " + resources.styles().childCell(); 313 private final String rowStyle; 314 private final String selectedRowStyle; 315 private final String cellStyle; 316 private final String selectedCellStyle; 317 318 @SuppressWarnings("deprecation") CustomTableBuilder()319 public CustomTableBuilder() { 320 super(dataGrid); 321 322 // Cache styles for faster access. 323 Style style = dataGrid.getResources().style(); 324 rowStyle = style.evenRow(); 325 selectedRowStyle = " " + style.selectedRow(); 326 cellStyle = style.cell() + " " + style.evenRowCell(); 327 selectedCellStyle = " " + style.selectedRowCell(); 328 329 // Record today's date. 330 Date today = new Date(); 331 todayMonth = today.getMonth(); 332 } 333 334 @SuppressWarnings("deprecation") 335 @Override buildRowImpl(ContactInfo rowValue, int absRowIndex)336 public void buildRowImpl(ContactInfo rowValue, int absRowIndex) { 337 buildContactRow(rowValue, absRowIndex, false); 338 339 // Display information about the user in another row that spans the entire 340 // table. 341 Date dob = rowValue.getBirthday(); 342 if (dob.getMonth() == todayMonth) { 343 TableRowBuilder row = startRow(); 344 TableCellBuilder td = row.startTD().colSpan(7).className(cellStyle); 345 td.style().trustedBackgroundColor("#ccf").endStyle(); 346 td.text(rowValue.getFirstName() + "'s birthday is this month!").endTD(); 347 row.endTR(); 348 } 349 350 // Display list of friends. 351 if (showingFriends.contains(rowValue.getId())) { 352 Set<ContactInfo> friends = ContactDatabase.get().queryFriends(rowValue); 353 for (ContactInfo friend : friends) { 354 buildContactRow(friend, absRowIndex, true); 355 } 356 } 357 } 358 359 /** 360 * Build a row. 361 * 362 * @param rowValue the contact info 363 * @param absRowIndex the absolute row index 364 * @param isFriend true if this is a subrow, false if a top level row 365 */ 366 @SuppressWarnings("deprecation") buildContactRow(ContactInfo rowValue, int absRowIndex, boolean isFriend)367 private void buildContactRow(ContactInfo rowValue, int absRowIndex, boolean isFriend) { 368 // Calculate the row styles. 369 SelectionModel<? super ContactInfo> selectionModel = dataGrid.getSelectionModel(); 370 boolean isSelected = 371 (selectionModel == null || rowValue == null) ? false : selectionModel 372 .isSelected(rowValue); 373 boolean isEven = absRowIndex % 2 == 0; 374 StringBuilder trClasses = new StringBuilder(rowStyle); 375 if (isSelected) { 376 trClasses.append(selectedRowStyle); 377 } 378 379 // Calculate the cell styles. 380 String cellStyles = cellStyle; 381 if (isSelected) { 382 cellStyles += selectedCellStyle; 383 } 384 if (isFriend) { 385 cellStyles += childCell; 386 } 387 388 TableRowBuilder row = startRow(); 389 row.className(trClasses.toString()); 390 391 /* 392 * Checkbox column. 393 * 394 * This table will uses a checkbox column for selection. Alternatively, 395 * you can call dataGrid.setSelectionEnabled(true) to enable mouse 396 * selection. 397 */ 398 TableCellBuilder td = row.startTD(); 399 td.className(cellStyles); 400 td.style().outlineStyle(OutlineStyle.NONE).endStyle(); 401 if (!isFriend) { 402 renderCell(td, createContext(0), checkboxColumn, rowValue); 403 } 404 td.endTD(); 405 406 /* 407 * View friends column. 408 * 409 * Displays a link to "show friends". When clicked, the list of friends is 410 * displayed below the contact. 411 */ 412 td = row.startTD(); 413 td.className(cellStyles); 414 if (!isFriend) { 415 td.style().outlineStyle(OutlineStyle.NONE).endStyle(); 416 renderCell(td, createContext(1), viewFriendsColumn, rowValue); 417 } 418 td.endTD(); 419 420 // First name column. 421 td = row.startTD(); 422 td.className(cellStyles); 423 td.style().outlineStyle(OutlineStyle.NONE).endStyle(); 424 if (isFriend) { 425 td.text(rowValue.getFirstName()); 426 } else { 427 renderCell(td, createContext(2), firstNameColumn, rowValue); 428 } 429 td.endTD(); 430 431 // Last name column. 432 td = row.startTD(); 433 td.className(cellStyles); 434 td.style().outlineStyle(OutlineStyle.NONE).endStyle(); 435 if (isFriend) { 436 td.text(rowValue.getLastName()); 437 } else { 438 renderCell(td, createContext(3), lastNameColumn, rowValue); 439 } 440 td.endTD(); 441 442 // Age column. 443 td = row.startTD(); 444 td.className(cellStyles); 445 td.style().outlineStyle(OutlineStyle.NONE).endStyle(); 446 td.text(NumberFormat.getDecimalFormat().format(rowValue.getAge())).endTD(); 447 448 // Category column. 449 td = row.startTD(); 450 td.className(cellStyles); 451 td.style().outlineStyle(OutlineStyle.NONE).endStyle(); 452 if (isFriend) { 453 td.text(rowValue.getCategory().getDisplayName()); 454 } else { 455 renderCell(td, createContext(5), categoryColumn, rowValue); 456 } 457 td.endTD(); 458 459 // Address column. 460 td = row.startTD(); 461 td.className(cellStyles); 462 DivBuilder div = td.startDiv(); 463 div.style().outlineStyle(OutlineStyle.NONE).endStyle(); 464 div.text(rowValue.getAddress()).endDiv(); 465 td.endTD(); 466 467 row.endTR(); 468 } 469 } 470 471 /** 472 * The main DataGrid. 473 */ 474 @ShowcaseData 475 @UiField(provided = true) 476 DataGrid<ContactInfo> dataGrid; 477 478 /** 479 * The pager used to change the range of data. 480 */ 481 @ShowcaseData 482 @UiField(provided = true) 483 SimplePager pager; 484 485 /** 486 * An instance of the constants. 487 */ 488 @ShowcaseData 489 private final CwConstants constants; 490 491 /** 492 * The resources used by this example. 493 */ 494 @ShowcaseData 495 private Resources resources; 496 497 /** 498 * Contains the contact id for each row in the table where the friends list is 499 * currently expanded. 500 */ 501 @ShowcaseData 502 private final Set<Integer> showingFriends = new HashSet<Integer>(); 503 504 /** 505 * Column to control selection. 506 */ 507 @ShowcaseData 508 private Column<ContactInfo, Boolean> checkboxColumn; 509 510 /** 511 * Column to expand friends list. 512 */ 513 @ShowcaseData 514 private Column<ContactInfo, String> viewFriendsColumn; 515 516 /** 517 * Column displays first name. 518 */ 519 @ShowcaseData 520 private Column<ContactInfo, String> firstNameColumn; 521 522 /** 523 * Column displays last name. 524 */ 525 @ShowcaseData 526 private Column<ContactInfo, String> lastNameColumn; 527 528 /** 529 * Column displays age. 530 */ 531 @ShowcaseData 532 private Column<ContactInfo, Number> ageColumn; 533 534 /** 535 * Column displays category. 536 */ 537 @ShowcaseData 538 private Column<ContactInfo, String> categoryColumn; 539 540 /** 541 * Column displays address. 542 */ 543 @ShowcaseData 544 private Column<ContactInfo, String> addressColumn; 545 546 /** 547 * Constructor. 548 * 549 * @param constants the constants 550 */ CwCustomDataGrid(CwConstants constants)551 public CwCustomDataGrid(CwConstants constants) { 552 super(constants.cwCustomDataGridName(), constants.cwCustomDataGridDescription(), false, 553 "ContactDatabase.java", "CwCustomDataGrid.ui.xml", "CwCustomDataGrid.css"); 554 this.constants = constants; 555 } 556 557 @Override hasMargins()558 public boolean hasMargins() { 559 return false; 560 } 561 562 @Override hasScrollableContent()563 public boolean hasScrollableContent() { 564 return false; 565 } 566 567 /** 568 * Initialize this example. 569 */ 570 @ShowcaseSource 571 @Override onInitialize()572 public Widget onInitialize() { 573 resources = GWT.create(Resources.class); 574 resources.styles().ensureInjected(); 575 576 // Create a DataGrid. 577 578 /* 579 * Set a key provider that provides a unique key for each contact. If key is 580 * used to identify contacts when fields (such as the name and address) 581 * change. 582 */ 583 dataGrid = new DataGrid<ContactInfo>(ContactDatabase.ContactInfo.KEY_PROVIDER); 584 dataGrid.setWidth("100%"); 585 586 /* 587 * Do not refresh the headers every time the data is updated. The footer 588 * depends on the current data, so we do not disable auto refresh on the 589 * footer. 590 */ 591 dataGrid.setAutoHeaderRefreshDisabled(true); 592 593 // Set the message to display when the table is empty. 594 dataGrid.setEmptyTableWidget(new Label(constants.cwCustomDataGridEmpty())); 595 596 // Attach a column sort handler to the ListDataProvider to sort the list. 597 ListHandler<ContactInfo> sortHandler = 598 new ListHandler<ContactInfo>(ContactDatabase.get().getDataProvider().getList()); 599 dataGrid.addColumnSortHandler(sortHandler); 600 601 // Create a Pager to control the table. 602 SimplePager.Resources pagerResources = GWT.create(SimplePager.Resources.class); 603 pager = new SimplePager(TextLocation.CENTER, pagerResources, false, 0, true); 604 pager.setDisplay(dataGrid); 605 606 // Add a selection model so we can select cells. 607 final SelectionModel<ContactInfo> selectionModel = 608 new MultiSelectionModel<ContactInfo>(ContactDatabase.ContactInfo.KEY_PROVIDER); 609 dataGrid.setSelectionModel(selectionModel, DefaultSelectionEventManager 610 .<ContactInfo> createCheckboxManager()); 611 612 // Initialize the columns. 613 initializeColumns(sortHandler); 614 615 // Specify a custom table. 616 dataGrid.setTableBuilder(new CustomTableBuilder()); 617 dataGrid.setHeaderBuilder(new CustomHeaderBuilder()); 618 dataGrid.setFooterBuilder(new CustomFooterBuilder()); 619 620 // Add the CellList to the adapter in the database. 621 ContactDatabase.get().addDataDisplay(dataGrid); 622 623 // Create the UiBinder. 624 Binder uiBinder = GWT.create(Binder.class); 625 return uiBinder.createAndBindUi(this); 626 } 627 628 @Override asyncOnInitialize(final AsyncCallback<Widget> callback)629 protected void asyncOnInitialize(final AsyncCallback<Widget> callback) { 630 GWT.runAsync(CwCustomDataGrid.class, new RunAsyncCallback() { 631 632 @Override 633 public void onFailure(Throwable caught) { 634 callback.onFailure(caught); 635 } 636 637 @Override 638 public void onSuccess() { 639 callback.onSuccess(onInitialize()); 640 } 641 }); 642 } 643 644 /** 645 * Defines the columns in the custom table. Maps the data in the ContactInfo 646 * for each row into the appropriate column in the table, and defines handlers 647 * for each column. 648 */ 649 @ShowcaseSource initializeColumns(ListHandler<ContactInfo> sortHandler)650 private void initializeColumns(ListHandler<ContactInfo> sortHandler) { 651 /* 652 * Checkbox column. 653 * 654 * This table will uses a checkbox column for selection. Alternatively, you 655 * can call dataGrid.setSelectionEnabled(true) to enable mouse selection. 656 */ 657 checkboxColumn = new Column<ContactInfo, Boolean>(new CheckboxCell(true, false)) { 658 @Override 659 public Boolean getValue(ContactInfo object) { 660 // Get the value from the selection model. 661 return dataGrid.getSelectionModel().isSelected(object); 662 } 663 }; 664 dataGrid.addColumn(checkboxColumn); 665 dataGrid.setColumnWidth(0, 40, Unit.PX); 666 667 // View friends. 668 SafeHtmlRenderer<String> anchorRenderer = new AbstractSafeHtmlRenderer<String>() { 669 @Override 670 public SafeHtml render(String object) { 671 SafeHtmlBuilder sb = new SafeHtmlBuilder(); 672 sb.appendHtmlConstant("(<a href=\"javascript:;\">").appendEscaped(object) 673 .appendHtmlConstant("</a>)"); 674 return sb.toSafeHtml(); 675 } 676 }; 677 viewFriendsColumn = new Column<ContactInfo, String>(new ClickableTextCell(anchorRenderer)) { 678 @Override 679 public String getValue(ContactInfo object) { 680 if (showingFriends.contains(object.getId())) { 681 return "hide friends"; 682 } else { 683 return "show friends"; 684 } 685 } 686 }; 687 viewFriendsColumn.setFieldUpdater(new FieldUpdater<ContactInfo, String>() { 688 @Override 689 public void update(int index, ContactInfo object, String value) { 690 if (showingFriends.contains(object.getId())) { 691 showingFriends.remove(object.getId()); 692 } else { 693 showingFriends.add(object.getId()); 694 } 695 696 // Redraw the modified row. 697 dataGrid.redrawRow(index); 698 } 699 }); 700 dataGrid.addColumn(viewFriendsColumn); 701 dataGrid.setColumnWidth(1, 10, Unit.EM); 702 703 // First name. 704 firstNameColumn = new Column<ContactInfo, String>(new EditTextCell()) { 705 @Override 706 public String getValue(ContactInfo object) { 707 return object.getFirstName(); 708 } 709 }; 710 firstNameColumn.setSortable(true); 711 sortHandler.setComparator(firstNameColumn, new Comparator<ContactInfo>() { 712 @Override 713 public int compare(ContactInfo o1, ContactInfo o2) { 714 return o1.getFirstName().compareTo(o2.getFirstName()); 715 } 716 }); 717 firstNameColumn.setFieldUpdater(new FieldUpdater<ContactInfo, String>() { 718 @Override 719 public void update(int index, ContactInfo object, String value) { 720 // Called when the user changes the value. 721 object.setFirstName(value); 722 ContactDatabase.get().refreshDisplays(); 723 } 724 }); 725 dataGrid.addColumn(firstNameColumn); 726 dataGrid.setColumnWidth(2, 20, Unit.PCT); 727 728 // Last name. 729 lastNameColumn = new Column<ContactInfo, String>(new EditTextCell()) { 730 @Override 731 public String getValue(ContactInfo object) { 732 return object.getLastName(); 733 } 734 }; 735 lastNameColumn.setSortable(true); 736 sortHandler.setComparator(lastNameColumn, new Comparator<ContactInfo>() { 737 @Override 738 public int compare(ContactInfo o1, ContactInfo o2) { 739 return o1.getLastName().compareTo(o2.getLastName()); 740 } 741 }); 742 lastNameColumn.setFieldUpdater(new FieldUpdater<ContactInfo, String>() { 743 @Override 744 public void update(int index, ContactInfo object, String value) { 745 // Called when the user changes the value. 746 object.setLastName(value); 747 ContactDatabase.get().refreshDisplays(); 748 } 749 }); 750 dataGrid.addColumn(lastNameColumn); 751 dataGrid.setColumnWidth(3, 20, Unit.PCT); 752 753 // Age. 754 ageColumn = new Column<ContactInfo, Number>(new NumberCell()) { 755 @Override 756 public Number getValue(ContactInfo object) { 757 return object.getAge(); 758 } 759 }; 760 ageColumn.setSortable(true); 761 sortHandler.setComparator(ageColumn, new Comparator<ContactInfo>() { 762 @Override 763 public int compare(ContactInfo o1, ContactInfo o2) { 764 return o1.getAge() - o2.getAge(); 765 } 766 }); 767 dataGrid.addColumn(ageColumn); 768 dataGrid.setColumnWidth(4, 7, Unit.EM); 769 770 // Category. 771 final Category[] categories = ContactDatabase.get().queryCategories(); 772 List<String> categoryNames = new ArrayList<String>(); 773 for (Category category : categories) { 774 categoryNames.add(category.getDisplayName()); 775 } 776 SelectionCell categoryCell = new SelectionCell(categoryNames); 777 categoryColumn = new Column<ContactInfo, String>(categoryCell) { 778 @Override 779 public String getValue(ContactInfo object) { 780 return object.getCategory().getDisplayName(); 781 } 782 }; 783 categoryColumn.setFieldUpdater(new FieldUpdater<ContactInfo, String>() { 784 @Override 785 public void update(int index, ContactInfo object, String value) { 786 for (Category category : categories) { 787 if (category.getDisplayName().equals(value)) { 788 object.setCategory(category); 789 } 790 } 791 ContactDatabase.get().refreshDisplays(); 792 } 793 }); 794 dataGrid.addColumn(categoryColumn); 795 dataGrid.setColumnWidth(5, 130, Unit.PX); 796 797 // Address. 798 addressColumn = new Column<ContactInfo, String>(new TextCell()) { 799 @Override 800 public String getValue(ContactInfo object) { 801 return object.getAddress(); 802 } 803 }; 804 addressColumn.setSortable(true); 805 sortHandler.setComparator(addressColumn, new Comparator<ContactInfo>() { 806 @Override 807 public int compare(ContactInfo o1, ContactInfo o2) { 808 return o1.getAddress().compareTo(o2.getAddress()); 809 } 810 }); 811 dataGrid.addColumn(addressColumn); 812 dataGrid.setColumnWidth(6, 60, Unit.PCT); 813 } 814 } 815