/* * #%L * NICTA t3as SNOMED CT GWT UI * %% * Copyright (C) 2014 NICTA * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with H2, GWT, or JavaBeans Activation Framework (JAF) (or a * modified version of those libraries), containing parts covered by the * terms of the H2 License, the GWT Terms, or the Common Development and * Distribution License (CDDL) version 1.0 ,the licensors of this Program * grant you additional permission to convey the resulting work. * #L% */ package org.t3as.snomedct.gwt.client.gwt; import com.google.gwt.cell.client.CheckboxCell; import com.google.gwt.cell.client.FieldUpdater; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsonUtils; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.http.client.Request; import com.google.gwt.http.client.RequestBuilder; import com.google.gwt.http.client.RequestCallback; import com.google.gwt.http.client.RequestException; import com.google.gwt.http.client.Response; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.TextColumn; import com.google.gwt.user.client.Cookies; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.StackPanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.view.client.AsyncDataProvider; import com.google.gwt.view.client.HasData; import org.t3as.snomedct.gwt.client.Messages; import org.t3as.snomedct.gwt.client.SemanticType; import javax.ws.rs.core.MediaType; import java.util.List; import java.util.Map; public class ConfigureHandler implements ClickHandler { private static final int GLASS_TOP_MARGIN_PX = 30; private static final int GLASS_LEFT_MARGIN_PX = 40; private final ConfigurePopup popup; public ConfigureHandler(final List<SemanticType> types, final Map<String, String> typeCodeToDescription) { popup = new ConfigurePopup(types, typeCodeToDescription); } @Override public void onClick(final ClickEvent event) { popup.setPositionAndShow(); } private static class ConfigurePopup extends PopupPanel { private final Messages messages = GWT.create(Messages.class); ConfigurePopup(final List<SemanticType> types, final Map<String, String> typeCodeToDescription) { // close when clicking outside the widget super(true); // cover the entire screen with a glass panel to disable all other interaction setGlassEnabled(true); final CellTable<SemanticType> table = new CellTable<>(); // setup the columns final Column<SemanticType, Boolean> checkCol = new Column<SemanticType, Boolean>(new CheckboxCell(true, true)) { @Override public Boolean getValue(final SemanticType type) { return type.isSelected(); } }; checkCol.setFieldUpdater(new FieldUpdater<SemanticType, Boolean>() { @Override public void update(final int index, final SemanticType type, final Boolean value) { type.setSelected(value); } }); table.addColumn(checkCol); final TextColumn<SemanticType> codeCol = new TextColumn<SemanticType>() { @Override public String getValue(final SemanticType type) { return type.getCode(); } }; table.addColumn(codeCol); final TextColumn<SemanticType> descriptionCol = new TextColumn<SemanticType>() { @Override public String getValue(final SemanticType type) { return type.getDescription(); } }; table.addColumn(descriptionCol); // the DataProvider to get the data async final SemanticTypeDataProvider dataProvider = new SemanticTypeDataProvider(types, typeCodeToDescription); dataProvider.addDataDisplay(table); // setup the user interface final TickUntickAllClickHandler tickAllClickHandler = new TickUntickAllClickHandler(true, types, dataProvider); final TickUntickAllClickHandler untickAllClickHandler = new TickUntickAllClickHandler(false, types, dataProvider); final DefaultsClickHandler defaultsClickHandler = new DefaultsClickHandler(types, dataProvider); final VerticalPanel vp = new VerticalPanel(); // top buttons final Button tickAllTop = new Button(messages.tickAllButton()); tickAllTop.addClickHandler(tickAllClickHandler); final Button untickAllTop = new Button(messages.untickAllButton()); untickAllTop.addClickHandler(untickAllClickHandler); final Button defaultsButtonTop = new Button(messages.defaultsButton()); defaultsButtonTop.addClickHandler(defaultsClickHandler); final Button okTop = new Button(messages.okButton()); okTop.addClickHandler(new OkClickHandler(this)); final HorizontalPanel topTickPanel = new HorizontalPanel(); topTickPanel.setSpacing(5); topTickPanel.add(tickAllTop); topTickPanel.add(untickAllTop); topTickPanel.add(defaultsButtonTop); final HorizontalPanel topButtons = new HorizontalPanel(); topButtons.setWidth("100%"); topButtons.setSpacing(5); topButtons.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); topButtons.add(topTickPanel); topButtons.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); topButtons.add(okTop); vp.add(topButtons); vp.add(table); // bottom buttons final Button tickAllBottom = new Button(messages.tickAllButton()); tickAllBottom.addClickHandler(tickAllClickHandler); final Button untickAllBottom = new Button(messages.untickAllButton()); untickAllBottom.addClickHandler(untickAllClickHandler); final Button defaultsButtonBottom = new Button(messages.defaultsButton()); defaultsButtonBottom.addClickHandler(defaultsClickHandler); final Button okBottom = new Button(messages.okButton()); okBottom.addClickHandler(new OkClickHandler(this)); final HorizontalPanel bottomTickPanel = new HorizontalPanel(); bottomTickPanel.setSpacing(5); bottomTickPanel.add(tickAllBottom); bottomTickPanel.add(untickAllBottom); bottomTickPanel.add(defaultsButtonBottom); final HorizontalPanel bottomButtons = new HorizontalPanel(); bottomButtons.setWidth("100%"); bottomButtons.setSpacing(5); bottomButtons.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); bottomButtons.add(bottomTickPanel); bottomButtons.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); bottomButtons.add(okBottom); vp.add(bottomButtons); final StackPanel stack = new StackPanel(); stack.add(vp, messages.configureSemanticTypesHeading()); setWidget(new ScrollPanel(stack)); } public void setPositionAndShow() { setPopupPositionAndShow(new PopupPanel.PositionCallback() { public void setPosition(final int offsetWidth, final int offsetHeight) { // don't let the popup be too wide or tall final int width = Math.min(offsetWidth, Window.getClientWidth() - GLASS_LEFT_MARGIN_PX * 2); final int height = Math.min(offsetHeight, Window.getClientHeight() - GLASS_TOP_MARGIN_PX * 2); setPixelSize(width, height); // centre of the screen regardless of size setPopupPosition((Window.getClientWidth() - width) / 2, (Window.getClientHeight() - height) / 2); } }); } } private static class SemanticTypeDataProvider extends AsyncDataProvider<SemanticType> { private final String webserviceUrl; private final List<SemanticType> types; private final Map<String, String> typeCodeToDescription; private SemanticTypeDataProvider(final List<SemanticType> types, final Map<String, String> typeCodeToDescription) { this.types = types; this.typeCodeToDescription = typeCodeToDescription; final String hostAndPort = Cookies.getCookie("webserviceHost"); this.webserviceUrl = (hostAndPort.isEmpty() ? "" : "http://" + hostAndPort) + "/snomed-coder-web/rest/v1.0/semanticTypes"; } @Override protected void onRangeChanged(final HasData<SemanticType> display) { final RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, webserviceUrl); builder.setHeader("Accept", MediaType.APPLICATION_JSON); builder.setCallback(new RequestCallback() { @Override public void onResponseReceived(final Request request, final Response response) { final JsArray<SemanticType> receivedTypes = JsonUtils.safeEval(response.getText()); types.clear(); for (int i = 0; i < receivedTypes.length(); i++) { final SemanticType type = receivedTypes.get(i); type.setSelected(type.isOnByDefault()); types.add(type); typeCodeToDescription.put(type.getCode(), type.getDescription()); } updateRowData(0, types); for (final HasData<SemanticType> display : getDataDisplays()) { display.setVisibleRange(0, types.size()); } } @Override public void onError(final Request request, final Throwable exception) { // TODO finish this } }); try { builder.send(); } catch (final RequestException e) { // TODO: figure out what to do here e.printStackTrace(); } } } private static class TickUntickAllClickHandler implements ClickHandler { private final boolean tick; private final List<SemanticType> types; private final SemanticTypeDataProvider dataProvider; public TickUntickAllClickHandler(final boolean tick, final List<SemanticType> types, final SemanticTypeDataProvider dataProvider) { this.tick = tick; this.types = types; this.dataProvider = dataProvider; } @Override public void onClick(final ClickEvent event) { // tick or untick all the checkboxes for (final SemanticType t : types) { t.setSelected(tick); } dataProvider.updateRowData(0, types); } } private static class DefaultsClickHandler implements ClickHandler { private final List<SemanticType> types; private final SemanticTypeDataProvider dataProvider; public DefaultsClickHandler(final List<SemanticType> types, final SemanticTypeDataProvider dataProvider) { this.types = types; this.dataProvider = dataProvider; } @Override public void onClick(final ClickEvent event) { for (final SemanticType t : types) { t.setSelected(t.isOnByDefault()); } dataProvider.updateRowData(0, types); } } private static class OkClickHandler implements ClickHandler { private final ConfigurePopup popup; public OkClickHandler(final ConfigurePopup popup) { this.popup = popup; } @Override public void onClick(final ClickEvent event) { popup.hide(); } } }