package org.geogebra.web.web.gui.browser; import java.util.ArrayList; import java.util.List; import org.geogebra.common.main.MaterialsManager; import org.geogebra.common.move.ggtapi.models.Chapter; import org.geogebra.common.move.ggtapi.models.Material; import org.geogebra.common.util.debug.Log; import org.geogebra.web.html5.gui.ResizeListener; import org.geogebra.web.html5.gui.view.browser.MaterialListElementI; import org.geogebra.web.html5.main.AppW; import org.geogebra.web.web.gui.laf.GLookAndFeel; import org.geogebra.web.web.move.ggtapi.models.GeoGebraTubeAPIW; import org.geogebra.web.web.move.ggtapi.models.MaterialCallback; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.ScrollEvent; import com.google.gwt.event.dom.client.ScrollHandler; import com.google.gwt.event.dom.client.TouchMoveEvent; import com.google.gwt.event.dom.client.TouchMoveHandler; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Label; /** * contains all available materials */ public class MaterialListPanel extends FlowPanel implements ResizeListener, ShowDetailsListener { private final static long MIN_DIFFERNCE_TO_SCROLL = 1000; /** application */ protected AppW app; /** * last selected {@link MaterialListElement material} */ protected MaterialListElement lastSelected; /** * list of all shown {@link MaterialListElement materials} */ protected List<MaterialListElement> materials = new ArrayList<MaterialListElement>(); private MaterialCallback userMaterialsCB; private MaterialCallback ggtMaterialsCB; /** last call of scroll handler */ long lastScroll = 0; /** last call to setDefaultStyle */ long lastDefaultStyle = 0; /** * @param app {@link AppW} */ public MaterialListPanel(final AppW app) { this.app = app; this.userMaterialsCB = getUserMaterialsCB(); this.ggtMaterialsCB = getGgtMaterialsCB(); this.setPixelSize((int)app.getWidth() - GLookAndFeel.PROVIDER_PANEL_WIDTH, (int)app.getHeight() - GLookAndFeel.BROWSE_HEADER_HEIGHT); this.setStyleName("materialListPanel"); this.addDomHandler(new ClickHandler() { @Override public void onClick(final ClickEvent event) { if (lastSelected != null) { setDefaultStyle(true); } } }, ClickEvent.getType()); this.addDomHandler(new ScrollHandler() { @Override public void onScroll(final ScrollEvent event) { if (lastSelected != null && System.currentTimeMillis() - lastScroll > MIN_DIFFERNCE_TO_SCROLL) { setDefaultStyle(false); } } }, ScrollEvent.getType()); this.addDomHandler(new TouchMoveHandler() { @Override public void onTouchMove(final TouchMoveEvent event) { if (lastSelected != null) { setDefaultStyle(false); } } }, TouchMoveEvent.getType()); } private MaterialCallback getGgtMaterialsCB() { return new MaterialCallback() { @Override public void onError(final Throwable exception) { // FIXME implement Error Handling! exception.printStackTrace(); Log.debug(exception.getMessage()); } @Override public void onLoaded(final List<Material> response, ArrayList<Chapter> meta) { addGGTMaterials(response, meta); } }; } private MaterialCallback getUserMaterialsCB() { return new MaterialCallback() { @Override public void onLoaded(final List<Material> parseResponse, ArrayList<Chapter> meta) { addUsersMaterials(parseResponse); } }; } /** * sets all {@link MaterialListElement materials} to the * default style (not selected, not disabled) * @param force boolean */ public void setDefaultStyle(boolean force) { if(!force && System.currentTimeMillis() < this.lastDefaultStyle + 3000){ return; } this.lastDefaultStyle = System.currentTimeMillis(); this.lastSelected = null; for (final MaterialListElement mat : this.materials) { mat.setDefaultStyle(); } } /** * loads featured materials and (if user is logged in) users materials */ public void loadAllMaterials() { clearMaterials(); loadLocal(); if (this.app.getLoginOperation().isLoggedIn()) { ((GeoGebraTubeAPIW) app.getLoginOperation().getGeoGebraTubeAPI()) .getUsersMaterials(this.userMaterialsCB); } else { ((GeoGebraTubeAPIW) app.getLoginOperation().getGeoGebraTubeAPI()) .getFeaturedMaterials(this.ggtMaterialsCB); } } /** * loads users materials from ggt */ public void loadUsersMaterials() { ((GeoGebraTubeAPIW) app.getLoginOperation().getGeoGebraTubeAPI()) .getUsersMaterials(this.userMaterialsCB); } /** * adds the local saved materials (localStorage) of current loggedIn user */ private void loadLocal() { app.getFileManager().getUsersMaterials(); } /** * adds the new materials (matList) - GeoGebraTube only * * @param matList * List<Material> * @param chapters * list of book chapters */ public final void addGGTMaterials(final List<Material> matList, final ArrayList<Chapter> chapters) { if (chapters == null || chapters.size() < 2) { for (final Material mat : matList) { addMaterial(mat, true, false); } } else { for (int i = 0; i < chapters.size(); i++) { addHeading(chapters.get(i).getTitle()); int[] materialIDs = chapters.get(i).getMaterials(); for (int j = 0; j < materialIDs.length; j++) { addMaterial(findMaterial(matList, materialIDs[j]), true, false); } } } } private static Material findMaterial(List<Material> matList, int id) { for (int i = 0; i < matList.size(); i++) { if (matList.get(i).getId() == id) { return matList.get(i); } } return null; } private void addHeading(String title) { Label chapterLabel = new Label(title); chapterLabel.addStyleName("ggbChapterName"); add(chapterLabel); } /** * Adds the given {@link Material materials}. * @param matList List<Material> */ public void addUsersMaterials(final List<Material> matList) { for (int i = matList.size() - 1; i >= 0; i--) { addMaterial(matList.get(i), false, false); } } /** * adds the given material to the list of {@link MaterialListElement * materials} and the preview-panel. if the {@link Material material} * already exists, the {@link MaterialListElement preview} is updated * otherwise a new one is created. * * @param mat * {@link Material}, validated for NPE * @param insertAtEnd * boolean * @param isLocal * boolean */ public void addMaterial(final Material mat, final boolean insertAtEnd, final boolean isLocal) { if (mat == null) { return; } final MaterialListElement matElem = getMaterialListElement(mat); if (matElem != null) { int oldLocalID = matElem.getMaterial().getLocalID(); String oldThumbnail = matElem.getMaterial().getThumbnail(); boolean oldThumbnailIsBase64 = matElem.getMaterial() .thumbnailIsBase64(); if (mat.getLocalID() == -1) { mat.setLocalID(oldLocalID); mat.setSyncStamp(Math.max(matElem.getMaterial().getSyncStamp(), mat.getSyncStamp())); } if (mat.getThumbnail() == null || mat.getThumbnail().length() == 0) { if (oldThumbnailIsBase64) { mat.setThumbnailBase64(oldThumbnail); } else { mat.setThumbnailUrl(oldThumbnail); } } matElem.setMaterial(mat); this.remove(matElem); this.insert(matElem, 0); } else { addNewMaterial(mat, insertAtEnd, isLocal); } } /** * The actual creation happens in LAF as it needs to be different for phone / tablet / web / widgets * @param mat {@link Material} * @param isLocal boolean */ private final void addNewMaterial(final Material mat, final boolean insertAtEnd, final boolean isLocal) { final MaterialListElement preview = ((GLookAndFeel)app.getLAF()).getMaterialElement(mat, this.app, isLocal); this.materials.add(preview); preview.setShowDetailsListener(this); if (insertAtEnd) { this.add(preview); } else { this.insert(preview,0); } } /** * clears the list of existing {@link MaterialListElement materials} and the {@link MaterialListPanel preview-panel} */ public void clearMaterials() { this.materials.clear(); this.clear(); } /** * @return {@link MaterialListElement} last selected material */ public MaterialListElement getChosenMaterial() { return this.lastSelected; } /** * sets all materials to disabled */ public void disableMaterials() { for (final MaterialListElement mat : this.materials) { mat.disableMaterial(); } } /** * @param materialElement {@link MaterialListElement} */ public void rememberSelected(final MaterialListElementI materialElement) { this.lastSelected = (MaterialListElement) materialElement; } /** * clears the {@link MaterialListPanel} and adds the found materials to it. * if query == "" all materials (featured and users) are loaded. * @param query String */ public void displaySearchResults(final String query) { clearMaterials(); if ("".equals(query)) { loadAllMaterials(); return; } searchLocal(query); searchGgt(query); } /** * search for materials in localStorage (offline saved files) * @param query String */ private void searchLocal(final String query) { this.app.getFileManager().search(query); } /** * search GeoGebraTube * @param query String */ protected void searchGgt(final String query) { ((GeoGebraTubeAPIW) this.app.getLoginOperation().getGeoGebraTubeAPI()).search( query, this.ggtMaterialsCB); } /** * removes the given material from the list of {@link MaterialListElement materials} and the {@link MaterialListPanel preview-panel} * @param mat {@link Material} */ public void removeMaterial(final Material mat) { for(final MaterialListElement matElem : this.materials) { if (matElem.isLocal && mat.getTitle().equals(matElem.getMaterial().getTitle()) || matElem.isOwnMaterial && matElem.getMaterial().equals(mat)) { this.materials.remove(matElem); this.remove(matElem); return; } } } /** * */ public void setLabels() { for (final MaterialListElement e : this.materials) { e.setLabels(); } } @Override public void onResize(int appWidth, int appHeight) { this.setPixelSize(appWidth - GLookAndFeel.PROVIDER_PANEL_WIDTH, appHeight - GLookAndFeel.BROWSE_HEADER_HEIGHT); } /** * refreshes the preview of the {@link MaterialListElement material} * @param material {@link Material} * @param isLocal boolean */ public void refreshMaterial(final Material material, final boolean isLocal) { addMaterial(material, false, isLocal); } private MaterialListElement getMaterialListElement(final Material material) { for(final MaterialListElement matElem : this.materials) { if (matElem.getMaterial().getId() > 0 && matElem.getMaterial().getId() == material.getId() || matElem.isLocal && MaterialsManager .getFileKey(matElem.getMaterial()) .equals(MaterialsManager .getFileKey(material))) { return matElem; } } return null; } /** * remove users materials from {@link MaterialListPanel} */ public void removeUsersMaterials() { final List<Material> delete = new ArrayList<Material>(); for (final MaterialListElement elem : this.materials) { if (elem.isOwnMaterial) { delete.add(elem.getMaterial()); } } for (final Material deleteElem : delete) { removeMaterial(deleteElem); } } @Override public void onShowDetails(FlowPanel content) { int elementBottom = content.getAbsoluteTop() + content.getElement().getClientHeight(); int panelBottom = this.getAbsoluteTop() + this.getElement().getClientHeight(); if(panelBottom < elementBottom){ lastScroll = System.currentTimeMillis(); this.getElement().setScrollTop(this.getElement().getScrollTop() + elementBottom - panelBottom); } else if(content.getAbsoluteTop() < this.getAbsoluteTop()){ lastScroll = System.currentTimeMillis(); this.getElement().setScrollTop(this.getElement().getScrollTop() + content.getAbsoluteTop() - this.getAbsoluteTop()); } } /** * @return last selected {@link MaterialListElement material} */ public MaterialListElement getLastSelected() { return this.lastSelected; } }