Mercurial > dive4elements > river
view flys-client/src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java @ 1470:eef55d18f424
Cosmetics, refactoring, renamed Master to CSMaster to avoid confusion.
flys-client/trunk@3512 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Felix Wolfsteller <felix.wolfsteller@intevation.de> |
---|---|
date | Wed, 21 Dec 2011 12:21:23 +0000 |
parents | 61521bc0ced3 |
children | a2b0b2af529f |
line wrap: on
line source
package de.intevation.flys.client.client.ui.chart; import java.util.HashMap; import java.util.Map; import java.util.LinkedHashMap; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; import com.smartgwt.client.util.SC; import com.smartgwt.client.types.Alignment; import com.smartgwt.client.types.ListGridFieldType; import com.smartgwt.client.widgets.Canvas; import com.smartgwt.client.widgets.grid.ListGrid; import com.smartgwt.client.widgets.grid.ListGridField; import com.smartgwt.client.widgets.grid.ListGridRecord; import com.smartgwt.client.widgets.layout.HLayout; import com.smartgwt.client.widgets.layout.VLayout; import com.smartgwt.client.widgets.form.fields.SpinnerItem; import com.smartgwt.client.widgets.form.DynamicForm; import com.smartgwt.client.widgets.form.fields.SelectItem; import com.smartgwt.client.widgets.form.fields.events.ChangeEvent; import com.smartgwt.client.widgets.form.fields.events.ChangedEvent; import com.smartgwt.client.widgets.form.fields.events.ChangedHandler; import com.smartgwt.client.widgets.form.fields.events.ChangeHandler; import de.intevation.flys.client.client.Config; import de.intevation.flys.client.shared.model.Artifact; import de.intevation.flys.client.shared.model.Collection; import de.intevation.flys.client.shared.model.Data; import de.intevation.flys.client.shared.model.DefaultArtifact; import de.intevation.flys.client.shared.model.DefaultData; import de.intevation.flys.client.shared.model.FacetRecord; import de.intevation.flys.client.shared.model.OutputMode; import de.intevation.flys.client.shared.model.Theme; import de.intevation.flys.client.shared.model.ThemeList; import de.intevation.flys.client.client.services.CrossSectionKMServiceAsync; import de.intevation.flys.client.client.services.FeedServiceAsync; import de.intevation.flys.client.client.services.LoadArtifactService; import de.intevation.flys.client.client.services.LoadArtifactServiceAsync; /** * ThemePanel much like ChartThemePanel, but shows an "Actions" column, * needed for interaction in the CrossSection Charts and a selector to * declare which cross section profile is "master". * Also can show 'area creation' context menus. */ public class CrossSectionChartThemePanel extends ChartThemePanel { /** Artifact Clone/Creation service. */ protected LoadArtifactServiceAsync loadService = GWT.create(LoadArtifactService.class); /** Service to query measurement points of cross sections. */ CrossSectionKMServiceAsync kmService = GWT.create( de.intevation.flys.client.client.services.CrossSectionKMService.class); /** UUID of the current "master" cross section. */ protected String currentCSMasterUUID; /** The layout (used for visual active/inactive feedback). */ protected VLayout layout; /** * Trivial constructor. */ public CrossSectionChartThemePanel( Collection collection, OutputMode mode) { super(collection, mode); } /** * Feed an artifact to let it know that it is master wrt cross-sections. * @param artifact uuid of an artifact. */ public void feedTellMaster(final String artifact) { Data[] feedData = DefaultData.createSimpleStringDataArray( "cross_section.master?", "1"); feedService.feed( Config.getInstance().getLocale(), new DefaultArtifact(artifact, "TODO:hash"), feedData, new AsyncCallback<Artifact>() { public void onFailure(Throwable caught) { GWT.log("Could not feed artifact (" + artifact + ") with master marker: " + caught.getMessage()); SC.warn(MSG.getString(caught.getMessage())); enable(); } public void onSuccess(Artifact artifact) { GWT.log("Successfully injected master mark to " + artifact); setCurrentCSMaster(artifact.getUuid()); requestRedraw(); enable(); } }); } /** * Sets currentCSMasterUUID. */ public String findCurrentCSMaster() { ThemeList themeList = getThemeList(); int count = getThemeList().getThemeCount(); String firstCSUuid = null; for (int i = 1; i <= count; i++) { Theme theme = themeList.getThemeAt(i); String value = theme.getCollectionItem().getData().get("cross_section.master?"); if (value != null) { if (firstCSUuid == null) { firstCSUuid = theme.getArtifact(); } if (!value.equals("0")) { setCurrentCSMaster(theme.getArtifact()); GWT.log("found a master: " + currentCSMasterUUID + "/" + theme.getDescription()); return theme.getDescription(); } } } // There is none selected. Take the first one. if (firstCSUuid != null) { setCurrentCSMaster(firstCSUuid); feedTellMaster(firstCSUuid); } return null; } /** * Create Layout, add a master selection box beneath. */ @Override protected VLayout createLayout() { layout = super.createLayout(); // Create "set master" combobox. SelectItem cbItem = new SelectItem(); cbItem.setTitle(MSG.chart_themepanel_set_master()); cbItem.setType("comboBox"); final LinkedHashMap<String, String> valueMap = getThemeList().toMapArtifactUUIDDescription("cross_section"); cbItem.setValueMap(valueMap); final DynamicForm form = new DynamicForm(); form.setWidth(200); form.setFields(cbItem); layout.addMember(form); Config config = Config.getInstance(); final String locale = config.getLocale(); findCurrentCSMaster(); cbItem.setValue(getCurrentCSMaster()); // Add Change Handler to first unset the old master and then set the // new master. cbItem.addChangeHandler(new ChangeHandler() { public void onChange(ChangeEvent event) { String selectedItem = (String) event.getValue(); final String artifact = selectedItem; disable(); // Tell current master that he is not master anymore. if (getCurrentCSMaster() != null) { Data[] feedData = DefaultData.createSimpleStringDataArray( "cross_section.master?", "0"); feedService.feed( locale, new DefaultArtifact(getCurrentCSMaster(), "TODO:hash"), feedData, new AsyncCallback<Artifact>() { public void onFailure(Throwable caught) { GWT.log("Could not un-master artifact ("+getCurrentCSMaster()+"): " + caught.getMessage()); SC.warn(MSG.getString(caught.getMessage())); enable(); } public void onSuccess(Artifact oldMaster) { GWT.log("Successfully un-mastered artifact."); feedTellMaster(artifact); } }); } else { feedTellMaster(artifact); } } }); return layout; } /** Disable the UI (becomes gray, inresponsive to user input). */ public void disable() { this.layout.setDisabled(true); } /** DisDisable the UI (becomes ungray, responsive to user input). */ public void enable() { this.layout.setDisabled(false); } /** * Returns a double from the list that has the smallest distance to the * given to value. In case of multiple values with the same difference, * the last one is taken. * @param in possible return values. * @param to the value to be as close to as possible. * @return value from in that is closest to to, -1 if none. */ public static double closest(Double[] in, double to) { if (in == null || in.length == 0) { return -1; } if (in[0] == to) { return to; } double minDiff = Math.abs(to - in[0]); double bestMatch = in[0]; for (int i = 1; i < in.length; i++) { if (in[i] == to) { return to; } double diff = Math.abs(to - in[0]); if (diff < minDiff) { minDiff = diff; bestMatch = in[i]; } } return bestMatch; } /** Feed the master (WINFO) artifact with the km of the crosssection, * such that it can be displayed in the diagrams subtitle. */ public void sendFeed(double kmD) { ; } /** Feed a single artifact with the km of the crosssection to display. */ public void sendFeed(FacetRecord facetRecord, double kmD) { Config config = Config.getInstance(); final String locale = config.getLocale(); Data[] feedData = DefaultData.createSimpleStringDataArray("cross_section.km", Double.valueOf(kmD).toString()); disable(); feedService.feed( locale, new DefaultArtifact( facetRecord.getTheme().getArtifact(), "TODO:hash"), feedData, new AsyncCallback<Artifact>() { @Override public void onFailure(Throwable caught) { GWT.log("Could not feed artifact " + caught.getMessage()); SC.warn(MSG.getString(caught.getMessage())); enable(); } @Override public void onSuccess(Artifact artifact) { GWT.log("Successfully fed"); requestRedraw(); // TODO also feed the winfo/masterartifact? (for plot subtitle) enable(); } }); } /** * Create the handler for ChangeEvents on the Spinner in the * facets that control km of cross section. * * @param feedService The FeedService to send (changed) data to. * @param facetRecord The FacetRecord (~row in table) where this * handler is added to (to a child, to be exact). */ public final ChangedHandler createSpinnerHandler( final FeedServiceAsync feedService, final FacetRecord facetRecord) { Config config = Config.getInstance(); final String locale = config.getLocale(); ChangedHandler handler = new ChangedHandler() { @Override public void onChanged(final ChangedEvent ce) { if (ce.getValue() == null) { return; } // Now, query the value with the kmService. Map<Integer, Double> map = new HashMap<Integer,Double>(); final double selected_km = Double.parseDouble(ce.getValue().toString()); int _dbid = -1; try { _dbid = Integer.valueOf(facetRecord.getTheme() .getCollectionItem() .getData().get("cross_section.dbid")); } catch (NumberFormatException nfe) { GWT.log("Could not extract cross-section db id from data."); } final int dbid = _dbid; map.put(dbid, Double.parseDouble(ce.getValue().toString())); disable(); kmService.getCrossSectionKMs(locale, map, 2, new AsyncCallback<Map<Integer, Double[]>>() { @Override public void onFailure(Throwable caught) { GWT.log("Could not get single km for " + dbid + ": "+ caught.getMessage()); SC.warn(MSG.getString(caught.getMessage())); updateCollection(); updateGrid(); enable(); } @Override public void onSuccess(Map<Integer, Double[]> obj) { Double[] kms = obj.get(dbid); double closest = CrossSectionChartThemePanel.closest(kms, selected_km); GWT.log("Got single km for " + dbid + ", it is " + closest); SpinnerItem item = (SpinnerItem) ce.getItem(); item.setValue(closest); sendFeed(facetRecord, closest); } }); } }; return handler; } /** * Create a "kilometer spinner" for CrossSection Facets. * @param facetRecord The respective Facet/Theme. * @return label, intialized SpinnerItem. */ public SpinnerItem createSpinnerItem(FacetRecord facetRecord) { SpinnerItem spinnerItem = new SpinnerItem(); spinnerItem.setShowTitle(false); spinnerItem.setTitle("Waterlevel-Spinner"); spinnerItem.setWidth(45); spinnerItem.setDefaultValue(facetRecord.getTheme() .getCollectionItem() .getData().get("cross_section.km")); // TODO actually use crosssectionkmservice with // min/max-extension spinnerItem.setMin(0); spinnerItem.setMax(1000); spinnerItem.setStep(0.5f); spinnerItem.setChangeOnKeypress(true); return spinnerItem; } /** * Create and configure the Grid to display. * @return ListGrid with Themes and related controls inside. */ @Override protected ListGrid createGrid() { ListGrid list = new ListGrid() { @Override protected Canvas createRecordComponent( final ListGridRecord record, Integer colNum) { // Only cross_section Facets display an action widget. final FacetRecord facetRecord = (FacetRecord) record; if (!facetRecord.getTheme().getFacet().equals( "cross_section")) { return null; } String fieldName = this.getFieldName(colNum); if (fieldName.equals(GRID_FIELD_ACTIONS)) { HLayout recordCanvas = new HLayout(3); recordCanvas.setHeight(22); recordCanvas.setAlign(Alignment.CENTER); // TODO Refactor in createSpinner(data, artifact) SpinnerItem spinnerItem = createSpinnerItem(facetRecord); spinnerItem.addChangedHandler( createSpinnerHandler(feedService, facetRecord)); DynamicForm formWrap = new DynamicForm(); formWrap.setFields(spinnerItem); formWrap.setTitlePrefix(""); formWrap.setTitleSuffix(""); recordCanvas.addMember(formWrap); return recordCanvas; } else { return null; } } }; list.setCanResizeFields(true); list.setShowRecordComponents(true); list.setShowRecordComponentsByCell(true); list.setShowAllRecords(true); list.setShowHeaderContextMenu(false); return list; } /** * Initializes the components (columns) of the theme grid. */ @Override protected void initGrid() { list.setCanEdit(true); list.setCanSort(false); list.setShowRecordComponents(true); list.setShowRecordComponentsByCell(true); list.setShowHeader(true); list.setWidth100(); list.setHeight100(); list.addEditCompleteHandler(this); ListGridField active = new ListGridField(GRID_FIELD_ACTIVE, " ", 20); active.setType(ListGridFieldType.BOOLEAN); ListGridField name = new ListGridField( GRID_FIELD_NAME, MSG.chart_themepanel_header_themes()); name.setType(ListGridFieldType.TEXT); ListGridField actions = new ListGridField(GRID_FIELD_ACTIONS, MSG.chart_themepanel_header_actions(), 50); list.setFields(active, name, actions); } /** Get Current Cross-section Masters uuid. */ public String getCurrentCSMaster() { return currentCSMasterUUID; } /** Set Current Cross-section Masters uuid. */ public void setCurrentCSMaster(String currentMasterUuid) { this.currentCSMasterUUID = currentMasterUuid; } /** Returns name of cross section area facets. */ protected String getAreaFacetName() { return "cross_section.area"; } /** * Return true if two themes are canditates for an area being * rendered between them. * TODO join with canArea, generalize to allow easier modification * in subclasses. */ protected boolean areAreaCompatible(Theme a, Theme b) { if (a.equals(b)) { return false; } return (a.getFacet().equals("cross_section") || a.getFacet().equals("cross_section_water_line")) && (b.getFacet().equals("cross_section") || b.getFacet().equals("cross_section_water_line")); } /** * True if context menu should contain 'create area' submenu on * this theme. */ protected boolean canArea(Theme a) { return a.getFacet().equals("cross_section") || a.getFacet().equals("cross_section_water_line"); } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :