/* * Copyright 2012 Axel Winkler, Daniel DunĂ©r * * This file is part of Daxplore Presenter. * * Daxplore Presenter is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * Daxplore Presenter 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Daxplore Presenter. If not, see <http://www.gnu.org/licenses/>. */ package org.daxplore.presenter.client.ui; import java.util.Iterator; import java.util.List; import org.daxplore.presenter.client.event.QueryUpdateEvent; import org.daxplore.presenter.client.event.QueryUpdateHandler; import org.daxplore.presenter.client.event.SelectionUpdateEvent; import org.daxplore.presenter.client.json.Groups; import org.daxplore.presenter.client.json.UITexts; import org.daxplore.presenter.client.resources.UIResources; import org.daxplore.presenter.shared.QueryDefinition; import org.daxplore.presenter.shared.QuestionMetadata; import com.google.gwt.event.logical.shared.OpenEvent; import com.google.gwt.event.logical.shared.OpenHandler; import com.google.gwt.event.logical.shared.SelectionEvent; import com.google.gwt.event.logical.shared.SelectionHandler; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Tree; import com.google.gwt.user.client.ui.TreeItem; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.inject.Inject; import com.google.web.bindery.event.shared.EventBus; /** * A widget that contains a tree of all the questions that can be shown * in a chart. * * <p>The tree is made up from groups of questions, that the user can * select.</p> */ public class QuestionPanel extends Composite implements QueryUpdateHandler{ private final EventBus eventBus; private Tree treeRoot; private QuestionTreeItem selected; private VerticalPanel vp = new VerticalPanel(); @Inject protected QuestionPanel(QuestionMetadata questions, Groups groups, EventBus eventBus, UITexts uiTexts, UIResources uiResources) { this.eventBus = eventBus; treeRoot = new Tree(uiResources, false); Label header = new Label(uiTexts.pickAQuestionHeader()); header.addStyleName("daxplore-QuestionPanel-header"); vp.add(header); vp.setCellHeight(header, "30px"); // Set up questiontree treeRoot.addSelectionHandler(new QuestionSelectionHandler()); treeRoot.addOpenHandler(new GroupOpenHandler()); for (int i = 0; i < groups.getGroupCount(); i++) { String txt = groups.getGroupName(i); SafeHtmlBuilder html = new SafeHtmlBuilder(); html.appendHtmlConstant("<span class=\"daxplore-QuestionPanel-branch\"> "); html.appendEscaped(txt); html.appendHtmlConstant(" </span>"); GroupItem gr = new GroupItem(html.toSafeHtml()); List<String> qlist = groups.getQuestionIDs(i); for (String q : qlist) { html = new SafeHtmlBuilder(); html.appendHtmlConstant(" "); html.appendEscaped(questions.getShortText(q)); if(questions.hasSecondary(q)) { html.appendHtmlConstant(" <span class=\"super\">"); html.appendEscaped(uiTexts.secondaryFlag()); html.appendHtmlConstant("</span>"); } html.appendHtmlConstant(" "); QuestionTreeItem qi = new QuestionTreeItem(html.toSafeHtml(), q); qi.setTitle(questions.getFullText(q)); gr.addItem(qi); } treeRoot.addItem(gr); } vp.add(treeRoot); vp.setCellVerticalAlignment(treeRoot, HasVerticalAlignment.ALIGN_TOP); vp.setWidth("100%"); initWidget(vp); QueryUpdateEvent.register(eventBus, this); } /** * {@inheritDoc} */ @Override public void onQueryUpdate(QueryUpdateEvent event) { setQueryDefinition(event.getQueryDefinition()); } /** * Set a new query definition, updating which group is open and * what question is selected. * * @param queryDefinition * the new query definition */ public void setQueryDefinition(QueryDefinition queryDefinition) { Iterator<TreeItem> iter = treeRoot.treeItemIterator(); while (iter.hasNext()) { TreeItem curr = iter.next(); if (curr instanceof QuestionTreeItem) { if (((QuestionTreeItem) curr).getQuestionID().equalsIgnoreCase(queryDefinition.getQuestionID())) { treeRoot.setSelectedItem(null, false); curr.setSelected(true); selected = (QuestionTreeItem) curr; curr.getParentItem().setState(true, true); } else { curr.setSelected(false); } } } treeRoot.ensureSelectedItemVisible(); } /** * Get the questionID of the currently selected question. * * @return the questionID */ public String getQuestionID() { if (selected != null) { return selected.getQuestionID(); } return ""; } /** * A handler that manages the automatic closing of groups. */ private class GroupOpenHandler implements OpenHandler<TreeItem> { @Override public void onOpen(OpenEvent<TreeItem> event) { TreeItem t = event.getTarget(); for (int i = 0; i < treeRoot.getItemCount(); i++) { TreeItem gr = treeRoot.getItem(i); if (gr instanceof GroupItem) { GroupItem g = (GroupItem) gr; if (g.getState() == true && (g != t) && !(g.hasSelectedChild())) { g.setState(false, false); } } } treeRoot.ensureSelectedItemVisible(); } } /** * A handler that manages what happens when questions are selected, * including firing events on the system's eventbus. */ private class QuestionSelectionHandler implements SelectionHandler<TreeItem> { GroupItem prev; @Override public void onSelection(SelectionEvent<TreeItem> event) { if (event.getSelectedItem() instanceof QuestionTreeItem) { QuestionTreeItem qi = (QuestionTreeItem) event.getSelectedItem(); selected = qi; Iterator<TreeItem> iter = treeRoot.treeItemIterator(); while (iter.hasNext()) { TreeItem n = iter.next(); if (n instanceof QuestionTreeItem) { QuestionTreeItem curr = (QuestionTreeItem) n; if (curr != selected) { curr.setSelected(false); GroupItem p = (GroupItem) curr.getParentItem(); if (!p.hasSelectedChild()) { p.setState(false); } } } else if (n instanceof GroupItem) { if (n.getState() && !(((GroupItem) n).hasSelectedChild())) { n.setState(false); } } } eventBus.fireEvent(new SelectionUpdateEvent()); } else if (event.getSelectedItem() instanceof GroupItem) { GroupItem gi = (GroupItem) event.getSelectedItem(); gi.setSelected(false); if (selected != null) { selected.setSelected(true); } if (gi.getState() == true && (!gi.hasSelectedChild())) { prev = gi; gi.setState(false); } else if (gi.getState() == false) { if (prev != null && prev == gi) { prev = null; } else { gi.setState(true); prev = null; } } } } } /** * A class that defines a group as displayed in the question tree. */ private static class GroupItem extends TreeItem { GroupItem(SafeHtml html) { super(html); } boolean hasSelectedChild() { boolean out = false; for (int i = 0; i < getChildCount(); i++) { out = out || getChild(i).isSelected(); } return out; } } }