Mercurial > dive4elements > river
view flys-client/src/main/java/de/intevation/flys/client/client/ui/chart/CrossSectionChartThemePanel.java @ 1589:c9ae3c8504d5
Removed obsolete import.
flys-client/trunk@3881 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Felix Wolfsteller <felix.wolfsteller@intevation.de> |
---|---|
date | Thu, 02 Feb 2012 13:37:26 +0000 |
parents | e0d665661a1d |
children | 993871f8e2f2 |
line wrap: on
line source
package de.intevation.flys.client.client.ui.chart; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.LinkedHashMap; import java.util.List; 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.BlurEvent; import com.smartgwt.client.widgets.form.fields.events.BlurHandler; 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 com.smartgwt.client.widgets.form.fields.events.KeyPressEvent; import com.smartgwt.client.widgets.form.fields.events.KeyPressHandler; import com.smartgwt.client.widgets.menu.Menu; import com.smartgwt.client.widgets.menu.MenuItem; import com.smartgwt.client.widgets.menu.events.ClickHandler; import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent; import de.intevation.flys.client.client.Config; import de.intevation.flys.client.shared.model.Artifact; 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.LoadArtifactService; import de.intevation.flys.client.client.services.LoadArtifactServiceAsync; import de.intevation.flys.client.client.ui.CollectionView; /** * 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; /** Data item name for CrossSections selected km. */ protected static String CS_KM = "cross_section.km"; /** Data item name for CrossSections selected km. */ protected static String CS_IS_MASTER = "cross_section.master?"; /** Whether or not to move through all cross sections synchronously. */ protected boolean synchronNavigation; /** Data for master artifact combobox.*/ protected LinkedHashMap<String, String> masters; /** Combobox for master artifacts.*/ protected SelectItem masterCb; /** * Trivial constructor. */ public CrossSectionChartThemePanel( OutputMode mode, CollectionView view) { super(mode, view); } /** Create DefaultArtifact. */ public static DefaultArtifact artifactReference(String uuid) { return new DefaultArtifact(uuid, "TODO:hash"); } /** Access data of collection item of theme. */ public static String dataOf(Theme theme, String dataItemName) { if (theme != null && theme.getCollectionItem() != null && theme.getCollectionItem().getData() != null ) { return theme.getCollectionItem().getData().get(dataItemName); } return null; } /** * 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( CS_IS_MASTER, "1"); feedService.feed( Config.getInstance().getLocale(), artifactReference(artifact), 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; Theme firstCS = null; for (int i = 1; i <= count; i++) { Theme theme = themeList.getThemeAt(i); String value = dataOf(theme, CS_IS_MASTER); if (value != null) { if (firstCSUuid == null) { firstCSUuid = theme.getArtifact(); firstCS = theme; } 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. masterCb = new SelectItem(); masterCb.setTitle(MSG.chart_themepanel_set_master()); masterCb.setType("comboBox"); masters = getThemeList().toMapArtifactUUIDDescription("cross_section"); masterCb.setValueMap(masters); final DynamicForm form = new DynamicForm(); form.setWidth(200); form.setFields(masterCb); layout.addMember(form); Config config = Config.getInstance(); final String locale = config.getLocale(); findCurrentCSMaster(); masterCb.setValue(getCurrentCSMaster()); // Add Change Handler to first unset the old master and then set the // new master. masterCb.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( CS_IS_MASTER, "0"); feedService.feed( locale, artifactReference(getCurrentCSMaster()), 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 a single artifact with the km of the crosssection to display. * If its the selected master, also feed the collectionmaster. * * @param artifacts List of artifacts to feed. * @param kmD The km to set. */ public void sendFeed(final List<Artifact> artifacts, final double kmD) { Config config = Config.getInstance(); final String locale = config.getLocale(); Data[] feedData = DefaultData.createSimpleStringDataArray(CS_KM, Double.valueOf(kmD).toString()); disable(); feedService.feedMany( locale, artifacts, feedData, new AsyncCallback<List<Artifact>>() { @Override public void onFailure(Throwable caught) { GWT.log("Could not feed many artifacts " + caught.getMessage()); SC.warn(MSG.getString(caught.getMessage())); enable(); } @Override public void onSuccess(List<Artifact> artifact) { GWT.log("Successfully fed many with km"); requestRedraw(); enable(); } }); } /** * Feed a single artifact with the km of the crosssection to display. * If its the selected master, also feed the collectionmaster. * @param artUUID The UUID of the artifact to feed. * @param kmD The km to set. */ public void sendFeed(final String artUUID, final double kmD) { Config config = Config.getInstance(); final String locale = config.getLocale(); Data[] feedData = DefaultData.createSimpleStringDataArray(CS_KM, Double.valueOf(kmD).toString()); disable(); feedService.feed( locale, artifactReference(artUUID), 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 with km"); requestRedraw(); enable(); } }); } public void spinnerValueEntered(final SpinnerItem item, final double enteredKm, final FacetRecord facetRecord ) { disable(); Config config = Config.getInstance(); final String locale = config.getLocale(); Map<Integer, Double> map = new HashMap<Integer,Double>(); 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, enteredKm); 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, enteredKm); GWT.log("Got single km for " + dbid + ", it is " + closest); // Do not set value, as it will trigger strange // "javascript" bugs. //item.setValue(closest); if (synchronNavigation) { // Feed many ... // Find all activated cross section themes ThemeList themes = getThemeList(); int nThemes = themes.getThemeCount(); List<Artifact> artifacts = new ArrayList<Artifact>(); for (int i = 0; i < nThemes; i++) { final Theme theme = themes.getThemeAt(i+1); if (theme.getFacet().equals("cross_section") && theme.getActive() == 1) { artifacts.add(artifactReference(theme.getArtifact())); } } sendFeed(artifacts, closest); } else { sendFeed(facetRecord.getTheme().getArtifact(), closest); } } }); } /** * Create the handler for ChangeEvents on the Spinner in the * facets that control km of cross section. * * @param facetRecord The FacetRecord (~row in table) where this * handler is added to (to a child, to be exact). */ public final ChangedHandler createSpinnerHandler( final FacetRecord facetRecord) { ChangedHandler handler = new ChangedHandler() { @Override public void onChanged(final ChangedEvent ce) { if (ce.getValue() == null) { return; } String value = ce.getValue().toString(); // Now, query the value with the kmService. final double selected_km = Double.parseDouble(value); final SpinnerItem item = (SpinnerItem) ce.getItem(); spinnerValueEntered(item, selected_km, facetRecord); } }; 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(Double.valueOf(facetRecord.getTheme() .getCollectionItem() .getData().get(CS_KM))); spinnerItem.setMin(0); spinnerItem.setMax(2000); spinnerItem.setStep(0.1d); 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); // When losing focus, update values. final SpinnerItem spinnerItem = createSpinnerItem(facetRecord); spinnerItem.addBlurHandler(new BlurHandler() { @Override public void onBlur (BlurEvent be) { spinnerValueEntered(spinnerItem, Double.parseDouble(spinnerItem.getValue().toString()), facetRecord); } }); // Add KeyPressHandler to lose focus when Enter key is pressed. spinnerItem.addKeyPressHandler(new KeyPressHandler(){ public void onKeyPress(KeyPressEvent kpe) { if (kpe.getKeyName().equals("Enter")) { spinnerItem.blurItem(); } } }); 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"); } /** * Include synchron navigation item. */ @Override protected Menu getSingleContextMenu(final ListGridRecord[] records) { Menu contextMenu = super.getSingleContextMenu(records); // Synchron checking. MenuItem synchronNavigationMenuItem = new MenuItem(); if (synchronNavigation == true) { synchronNavigationMenuItem.setTitle("Einzeln navigieren"); } else { synchronNavigationMenuItem.setTitle("Synchron navigieren"); } synchronNavigationMenuItem.addClickHandler(new ClickHandler() { public void onClick(MenuItemClickEvent evt) { synchronNavigation = !synchronNavigation; } }); contextMenu.addItem(synchronNavigationMenuItem); return contextMenu; } /** * This method is used to clear the current theme grid and add new updated * data. */ @Override protected void updateGrid() { GWT.log("CrossSectionChartThemePanel.updateGrid"); ListGridRecord[] selected = list.getSelectedRecords(); clearGrid(); ThemeList themeList = getThemeList(); if (themeList == null) { GWT.log("ERROR: No theme list."); return; } int count = themeList.getThemeCount(); for (int i = 1; i <= count; i++) { Theme theme = themeList.getThemeAt(i); if (theme == null) { continue; } if (theme.getFacet().equals("empty.facet")) { theme.setVisible(0); } if (theme.getVisible() == 0) { continue; } if (theme.getFacet().equals("cross_section")) { addToReferences(theme); } FacetRecord newRecord = createRecord(theme); addFacetRecord(newRecord); String newArtifact = theme.getArtifact(); String newFacet = theme.getFacet(); int newIndex = theme.getIndex(); for (ListGridRecord r: selected) { FacetRecord sel = (FacetRecord) r; Theme oldTheme = sel.getTheme(); if (oldTheme.getArtifact().equals(newArtifact) && oldTheme.getFacet().equals(newFacet) && oldTheme.getIndex() == newIndex) { list.selectRecord(newRecord); } } } fireOutputParameterChanged(); } /** * Adds a cross section theme to the master artifacts combobox and finds * a new master if necessary. * * @param theme The cross section theme. */ protected void addToReferences(Theme theme) { masters.put(theme.getArtifact(), theme.getDescription()); masterCb.setValueMap(masters); findCurrentCSMaster(); if (masterCb.getSelectedRecord() == null) { masterCb.setValue(getCurrentCSMaster()); } } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :