package org.diretto.web.richwebclient.view.sections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Vector; import org.diretto.api.client.base.data.BoundingBox; import org.diretto.api.client.base.data.ResultSet; import org.diretto.api.client.base.data.TimeRange; import org.diretto.api.client.base.types.LoadType; import org.diretto.api.client.main.core.CoreService; import org.diretto.api.client.main.core.entities.Document; import org.diretto.api.client.main.core.entities.DocumentID; import org.diretto.api.client.main.core.entities.Tag; import org.diretto.api.client.main.core.entities.Time; import org.diretto.api.client.main.feed.FeedService; import org.diretto.api.client.main.feed.event.DocumentListener; import org.diretto.api.client.session.UserSession; import org.diretto.api.client.util.Observable; import org.diretto.web.richwebclient.RichWebClientApplication; import org.diretto.web.richwebclient.view.base.AbstractSection; import org.diretto.web.richwebclient.view.base.Section; import org.diretto.web.richwebclient.view.util.ResourceUtils; import org.diretto.web.richwebclient.view.util.StyleUtils; import org.diretto.web.richwebclient.view.widgets.googlemap.client.base.MapType; import org.diretto.web.richwebclient.view.widgets.googlemap.server.ExploreGoogleMap; import org.diretto.web.richwebclient.view.widgets.googlemap.server.event.LoadingProcessListener; import org.diretto.web.richwebclient.view.widgets.googlemap.server.event.MapSectionListener; import org.diretto.web.richwebclient.view.windows.ConfirmWindow; import org.diretto.web.richwebclient.view.windows.MainWindow; import org.diretto.web.richwebclient.view.windows.event.SectionChangeListener; import org.joda.time.DateTime; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.event.ShortcutListener; import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.Embedded; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.PopupDateField; import com.vaadin.ui.TabSheet; import com.vaadin.ui.TabSheet.Tab; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.themes.Reindeer; /** * This class represents an {@code ExploreSection}. * * @author Tobias Schlecht */ public final class ExploreSection extends AbstractSection { private static final long serialVersionUID = -7792913701381814698L; private final RichWebClientApplication application; private final CoreService coreService; private final FeedService feedService; private boolean componentsAdded = false; private MainWindow mainWindow; private TextField addTagField; private TabSheet tabSheet; private ExploreGoogleMap exploreGoogleMap; private DocumentListener documentListener; private Date startTime = null; private Date endTime = null; private List<String> tags = new Vector<String>(); private BoundingBox boundingBox = null; /** * Constructs an {@link ExploreSection}. * * @param application The corresponding {@code RichWebClientApplication} */ public ExploreSection(RichWebClientApplication application) { super(application.getAuthenticationRegistry(), false, true, "Explore", "Explore the data"); this.application = application; coreService = application.getCoreService(); feedService = application.getFeedService(); } @Override public synchronized void addComponents() { if(!componentsAdded) { startTime = new DateTime().minusDays(1).toDate(); endTime = new DateTime().plusDays(1).toDate(); mainWindow = (MainWindow) getWindow(); documentListener = new DocumentListener() { @Override public void onDocumentAdded(DocumentID documentID) { if(startTime != null && endTime != null && boundingBox != null) { Document document = coreService.getDocument(documentID, LoadType.SNAPSHOT, false); if(document.isLocatedWithinAndContainsTags(boundingBox, new TimeRange(startTime, endTime), tags)) { exploreGoogleMap.addDocument(document); } } } }; setHeight("100%"); removeTitleComponent(); HorizontalLayout captionLayout = new HorizontalLayout(); HorizontalLayout labelLayout = new HorizontalLayout(); labelLayout.setMargin(false, true, false, false); labelLayout.addComponent(StyleUtils.getLabelH1(title)); captionLayout.addComponent(labelLayout); final Embedded loadingFeedback = new Embedded(null, ResourceUtils.BASE_AJAX_LOADER_BIG_RESOURCE); loadingFeedback.setType(Embedded.TYPE_IMAGE); loadingFeedback.setImmediate(true); loadingFeedback.setVisible(false); loadingFeedback.setWidth("27px"); loadingFeedback.setHeight("27px"); captionLayout.addComponent(loadingFeedback); addComponent(captionLayout); VerticalLayout mainLayout = new VerticalLayout(); mainLayout.setSizeFull(); HorizontalLayout controlsLayout = new HorizontalLayout(); controlsLayout.setWidth("100%"); controlsLayout.setMargin(false); controlsLayout.setSpacing(false); HorizontalLayout timeLayout = new HorizontalLayout(); timeLayout.setStyleName(Reindeer.LAYOUT_BLACK); timeLayout.setMargin(true); timeLayout.setSpacing(true); Label startTimeLabel = StyleUtils.getLabelBoldHTML("Start Time "); timeLayout.addComponent(startTimeLabel); timeLayout.setComponentAlignment(startTimeLabel, Alignment.MIDDLE_LEFT); final PopupDateField startTimeField = new PopupDateField() { private static final long serialVersionUID = -2578883361250692593L; @Override protected Date handleUnparsableDateString(String dateString) throws ConversionException { ConfirmWindow confirmWindow = new ConfirmWindow(mainWindow, "Start Time", StyleUtils.getLabelHTML("The format of the Start Time you have entered is not valid.")); mainWindow.addWindow(confirmWindow); return startTime; } }; startTimeField.addListener(new ValueChangeListener() { private static final long serialVersionUID = -8259141374026913509L; @Override public void valueChange(ValueChangeEvent event) { Date newStartTime = (Date) event.getProperty().getValue(); if(!newStartTime.equals(startTime)) { if(newStartTime.before(endTime)) { startTime = newStartTime; requestDocuments(); } else { ConfirmWindow confirmWindow = new ConfirmWindow(mainWindow, "Start Time", StyleUtils.getLabelHTML("The Start Time has to be before the End Time.")); mainWindow.addWindow(confirmWindow); startTimeField.setValue(startTime); } } } }); startTimeField.setImmediate(true); startTimeField.setWidth("185px"); startTimeField.setLocale(Locale.US); startTimeField.setDateFormat(Time.BIG_DISTANCE_DATE_TIME_PATTERN); startTimeField.setResolution(PopupDateField.RESOLUTION_SEC); startTimeField.setValue(startTime); timeLayout.addComponent(startTimeField); timeLayout.setComponentAlignment(startTimeField, Alignment.MIDDLE_LEFT); Label endTimeLabel = StyleUtils.getLabelBoldHTML("     End Time "); timeLayout.addComponent(endTimeLabel); timeLayout.setComponentAlignment(endTimeLabel, Alignment.MIDDLE_LEFT); final PopupDateField endTimeField = new PopupDateField() { private static final long serialVersionUID = -2578883361250692593L; @Override protected Date handleUnparsableDateString(String dateString) throws ConversionException { ConfirmWindow confirmWindow = new ConfirmWindow(mainWindow, "End Time", StyleUtils.getLabelHTML("The format of the End Time you have entered is not valid.")); mainWindow.addWindow(confirmWindow); return endTime; } }; endTimeField.addListener(new ValueChangeListener() { private static final long serialVersionUID = 2847964810733180875L; @Override public void valueChange(ValueChangeEvent event) { Date newEndTime = (Date) event.getProperty().getValue(); if(!newEndTime.equals(endTime)) { if(newEndTime.after(startTime)) { endTime = newEndTime; requestDocuments(); } else { ConfirmWindow confirmWindow = new ConfirmWindow(mainWindow, "End Time", StyleUtils.getLabelHTML("The End Time has to be after the Start Time.")); mainWindow.addWindow(confirmWindow); endTimeField.setValue(endTime); } } } }); endTimeField.setImmediate(true); endTimeField.setWidth("185px"); endTimeField.setLocale(Locale.US); endTimeField.setDateFormat(Time.BIG_DISTANCE_DATE_TIME_PATTERN); endTimeField.setResolution(PopupDateField.RESOLUTION_SEC); endTimeField.setValue(endTime); timeLayout.addComponent(endTimeField); timeLayout.setComponentAlignment(endTimeField, Alignment.MIDDLE_LEFT); controlsLayout.addComponent(timeLayout); controlsLayout.setComponentAlignment(timeLayout, Alignment.MIDDLE_LEFT); VerticalLayout wrapperLayout = new VerticalLayout(); wrapperLayout.setSizeFull(); wrapperLayout.setMargin(false, false, false, true); wrapperLayout.setSpacing(false); HorizontalLayout tagsLayout = new HorizontalLayout(); tagsLayout.setStyleName(Reindeer.LAYOUT_BLACK); tagsLayout.setWidth("100%"); tagsLayout.setMargin(true); tagsLayout.setSpacing(true); Label tagsLabel = StyleUtils.getLabelBoldHTML("Tags "); tagsLabel.setSizeUndefined(); tagsLayout.addComponent(tagsLabel); tagsLayout.setComponentAlignment(tagsLabel, Alignment.MIDDLE_LEFT); addTagField = new TextField(); addTagField.setImmediate(true); addTagField.setInputPrompt("Enter new Tag"); addTagField.addShortcutListener(new ShortcutListener(null, KeyCode.ENTER, new int[0]) { private static final long serialVersionUID = -5625100360296912223L; @Override public void handleAction(Object sender, Object target) { if(target == addTagField) { addTag(); } } }); tagsLayout.addComponent(addTagField); tagsLayout.setComponentAlignment(addTagField, Alignment.MIDDLE_LEFT); tabSheet = new TabSheet(); tabSheet.setStyleName(Reindeer.TABSHEET_SMALL); tabSheet.addStyleName("view"); tabSheet.addListener(new ComponentDetachListener() { private static final long serialVersionUID = -4555446069389829560L; @Override public void componentDetachedFromContainer(ComponentDetachEvent event) { tags.remove(tabSheet.getTab(event.getDetachedComponent()).getCaption()); requestDocuments(); } }); Button addTagButton = new Button("Add", new Button.ClickListener() { private static final long serialVersionUID = 4577061377601988261L; @Override public void buttonClick(ClickEvent event) { addTag(); } }); addTagButton.setStyleName(Reindeer.BUTTON_DEFAULT); tagsLayout.addComponent(addTagButton); tagsLayout.setComponentAlignment(addTagButton, Alignment.MIDDLE_LEFT); Label spaceLabel = StyleUtils.getLabelHTML(""); spaceLabel.setSizeUndefined(); tagsLayout.addComponent(spaceLabel); tagsLayout.setComponentAlignment(spaceLabel, Alignment.MIDDLE_LEFT); tagsLayout.addComponent(tabSheet); tagsLayout.setComponentAlignment(tabSheet, Alignment.MIDDLE_LEFT); tagsLayout.setExpandRatio(tabSheet, 1.0f); wrapperLayout.addComponent(tagsLayout); wrapperLayout.setComponentAlignment(tagsLayout, Alignment.MIDDLE_LEFT); controlsLayout.addComponent(wrapperLayout); controlsLayout.setExpandRatio(wrapperLayout, 1.0f); mainLayout.addComponent(controlsLayout); VerticalLayout mapLayout = new VerticalLayout(); mapLayout.setSizeFull(); mapLayout.setMargin(true, false, false, false); exploreGoogleMap = new ExploreGoogleMap(application, 12, 48.42255269321401d, 9.956477880477905d, MapType.HYBRID); exploreGoogleMap.setWidth("100%"); exploreGoogleMap.setHeight("100%"); mainWindow.addSectionChangeListener(new SectionChangeListener() { @Override public void onSectionChanged(Section section) { if(componentsAdded && section == ExploreSection.this) { exploreGoogleMap.activatePolling(); feedService.addDocumentListener(documentListener); } else { exploreGoogleMap.deactivatePolling(); feedService.removeListener(documentListener); } } }); exploreGoogleMap.addMapSectionListener(new MapSectionListener() { @Override public void onMapSectionChanged(BoundingBox boundingBox) { ExploreSection.this.boundingBox = boundingBox; requestDocuments(); } }); mapLayout.addComponent(exploreGoogleMap); mainLayout.addComponent(mapLayout); mainLayout.setExpandRatio(mapLayout, 1.0f); addComponent(mainLayout); setExpandRatio(mainLayout, 1.0f); exploreGoogleMap.addLoadingProcessListener(new LoadingProcessListener() { @Override public void onLoadingProcessStarted() { loadingFeedback.setVisible(true); } @Override public void onLoadingProcessFinished() { loadingFeedback.setVisible(false); } }); feedService.addDocumentListener(documentListener); componentsAdded = true; } } /** * Adds a new {@link Tag} specified by the current value of the * {@link TextField} as an additional filter setting and induces that the * relevant {@link Document}s will be requested. */ private void addTag() { String value = ((String) addTagField.getValue()).trim(); if(tags.contains(value)) { addTagField.setValue(""); } else if(value.length() >= 3 && value.length() <= 25) { Tab tab = tabSheet.addTab(new Label(), value, null); tab.setClosable(true); tags.add(value); addTagField.setValue(""); requestDocuments(); } else { ConfirmWindow confirmWindow = new ConfirmWindow(mainWindow, "Tags", StyleUtils.getLabelHTML("The number of characters has to be between 3 and 25.")); mainWindow.addWindow(confirmWindow); addTagField.setValue(""); } } /** * Requests the {@link Document}s which are relevant according to the * specified filter settings and updates the {@link ExploreGoogleMap}. */ private void requestDocuments() { if(startTime != null && endTime != null && boundingBox != null) { ResultSet<DocumentID, Document> documents = coreService.getDocuments(tags, boundingBox, new TimeRange(new DateTime(startTime), new DateTime(endTime)), LoadType.SNAPSHOT); exploreGoogleMap.updateDocuments(documents); } } @Override public void update(Observable<UserSession> observable, UserSession userSession) { } }