/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.sample.expenses.client; import com.google.gwt.cell.client.AbstractCell; import com.google.gwt.cell.client.Cell; import com.google.gwt.cell.client.IconCellDecorator; import com.google.gwt.cell.client.TextCell; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style.Overflow; import com.google.gwt.safehtml.client.SafeHtmlTemplates; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.sample.expenses.client.style.Styles; import com.google.gwt.sample.expenses.shared.EmployeeProxy; import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory; import com.google.gwt.user.cellview.client.CellTree; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.view.client.AsyncDataProvider; import com.google.gwt.view.client.HasData; import com.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.ProvidesKey; import com.google.gwt.view.client.Range; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SingleSelectionModel; import com.google.gwt.view.client.TreeViewModel; import com.google.web.bindery.requestfactory.gwt.ui.client.EntityProxyKeyProvider; import com.google.web.bindery.requestfactory.shared.EntityProxyId; import com.google.web.bindery.requestfactory.shared.Receiver; import java.util.List; /** * The employee tree located on the left of the app. */ public class ExpenseTree extends Composite { /** * Custom listener for this widget. */ public interface Listener { /** * Called when the user selects a tree item. * * @param department the selected department name * @param employee the selected employee */ void onSelection(String department, EntityProxyId<EmployeeProxy> employeeId); } interface Template extends SafeHtmlTemplates { @Template("<span class=\"{0}\">{1}</span>") SafeHtml span(String classes, String userName); } /** * A {@link AbstractCell} that represents an {@link EmployeeProxy}. */ private class EmployeeCell extends IconCellDecorator<EmployeeProxy> { public EmployeeCell() { super(Styles.resources().userIcon(), new AbstractCell<EmployeeProxy>() { private final String usernameStyle = Styles.common().usernameTreeItem(); private final String usernameStyleSelected = Styles.common().usernameTreeItemSelected(); @Override public boolean dependsOnSelection() { return true; } @Override public void render(Context context, EmployeeProxy value, SafeHtmlBuilder sb) { if (value != null) { StringBuilder classesBuilder = new StringBuilder(usernameStyle); if (lastEmployee != null && lastEmployee.getId().equals(value.getId())) { classesBuilder.append(" ").append(usernameStyleSelected); } sb.appendEscaped(value.getDisplayName()); sb.appendHtmlConstant("<br>"); sb.append(template.span(classesBuilder.toString(), value.getUserName())); } } }); if (template == null) { template = GWT.create(Template.class); } } } /** * The {@link ListDataProvider} used for Employee lists. */ private class EmployeeListDataProvider extends AsyncDataProvider<EmployeeProxy> { private final String department; public EmployeeListDataProvider(String department) { super(null); this.department = department; } @Override public void addDataDisplay(HasData<EmployeeProxy> display) { super.addDataDisplay(display); // Request the count anytime a view is added. requestFactory.employeeRequest().countEmployeesByDepartment(department).fire( new Receiver<Long>() { @Override public void onSuccess(Long response) { updateRowCount(response.intValue(), true); } }); } @Override protected void onRangeChanged(HasData<EmployeeProxy> view) { Range range = view.getVisibleRange(); requestFactory.employeeRequest().findEmployeeEntriesByDepartment( department, range.getStart(), range.getLength()).with( getEmployeeMenuProperties()).fire( new Receiver<List<EmployeeProxy>>() { @Override public void onSuccess(List<EmployeeProxy> response) { updateRowData(0, response); } }); } } /** * The {@link TreeViewModel} used to browse expense reports. */ private class ExpensesTreeViewModel implements TreeViewModel { /** * The department cell singleton. */ private final Cell<String> departmentCell = new TextCell(); /** * The {@link EmployeeCell} singleton. */ private final EmployeeCell employeeCell = new EmployeeCell(); public <T> NodeInfo<?> getNodeInfo(T value) { if (value == null) { // Top level. return new DefaultNodeInfo<String>(departments, departmentCell, selectionModel, null); } else if (isAllDepartment(value)) { // Employees are not displayed under the 'All' Department. return null; } else if (value instanceof String) { // Second level. EmployeeListDataProvider dataProvider = new EmployeeListDataProvider( (String) value); return new DefaultNodeInfo<EmployeeProxy>(dataProvider, employeeCell, selectionModel, null); } return null; } /** * @return true if the object is the All department */ public boolean isAllDepartment(Object value) { return departments.getList().get(0).equals(value); } /** * @return true if the object is a department */ public boolean isDepartment(Object value) { List<String> list = departments.getList(); String string = value.toString(); return list.contains(string); } public boolean isLeaf(Object value) { return value != null && (!isDepartment(value) || isAllDepartment(value)); } } private static Template template; /** * The data provider that provides departments. */ private final ListDataProvider<String> departments = new ListDataProvider<String>(); /** * The last selected department. */ private String lastDepartment; /** * The last selected employee. */ private EmployeeProxy lastEmployee; /** * The listener of this widget. */ private Listener listener; /** * The factory used to send requests. */ private final ExpensesRequestFactory requestFactory; /** * The shared {@link SingleSelectionModel}. */ private final SingleSelectionModel<Object> selectionModel = new SingleSelectionModel<Object>( new ProvidesKey<Object>() { EntityProxyKeyProvider<EmployeeProxy> keyProvider = new EntityProxyKeyProvider<EmployeeProxy>(); public Object getKey(Object item) { if (item instanceof EmployeeProxy) { return keyProvider.getKey((EmployeeProxy) item); } return item; } }); /** * The main widget. */ private CellTree tree; public ExpenseTree(ExpensesRequestFactory requestFactory) { this.requestFactory = requestFactory; // Initialize the departments. List<String> departmentList = departments.getList(); departmentList.add("All"); for (String department : ExpensesApp.DEPARTMENTS) { departmentList.add(department); } // Initialize the widget. createTree(); initWidget(tree); getElement().getStyle().setOverflow(Overflow.AUTO); } public void setListener(Listener listener) { this.listener = listener; } /** * Create the {@link CellTree}. */ private void createTree() { final ExpensesTreeViewModel model = new ExpensesTreeViewModel(); // Listen for selection. We need to add this handler before the CellBrowser // adds its own handler. selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { public void onSelectionChange(SelectionChangeEvent event) { Object selected = selectionModel.getSelectedObject(); if (selected == null) { lastEmployee = null; lastDepartment = ""; } else if (selected instanceof EmployeeProxy) { lastEmployee = (EmployeeProxy) selected; lastDepartment = lastEmployee.getDepartment(); } else if (selected instanceof String) { lastEmployee = null; if (model.isAllDepartment(selected)) { lastDepartment = ""; } else { lastDepartment = (String) selected; } } if (listener != null) { listener.onSelection(lastDepartment, lastEmployee == null ? null : lastEmployee.stableId()); } } }); // Create a CellBrowser. tree = new CellTree(model, null); tree.setAnimationEnabled(true); } private String[] getEmployeeMenuProperties() { return new String[] {"displayName", "userName"}; } }