package org.geogebra.web.web.gui.browser; import java.util.ArrayList; import java.util.List; import org.geogebra.common.main.Feature; import org.geogebra.common.main.Localization; import org.geogebra.common.move.ggtapi.models.Chapter; import org.geogebra.common.move.ggtapi.models.Material; import org.geogebra.common.move.ggtapi.models.Material.MaterialType; import org.geogebra.common.util.StringUtil; import org.geogebra.common.util.debug.Log; import org.geogebra.web.html5.Browser; import org.geogebra.web.html5.gui.FastClickHandler; import org.geogebra.web.html5.gui.textbox.GTextBox; import org.geogebra.web.html5.gui.tooltip.ToolTipManagerW; import org.geogebra.web.html5.gui.view.browser.MaterialListElementI; import org.geogebra.web.html5.main.AppW; import org.geogebra.web.web.gui.GuiManagerW; import org.geogebra.web.web.gui.dialog.DialogManagerW; import org.geogebra.web.web.gui.images.AppResources; import org.geogebra.web.web.gui.util.SaveDialogW; import org.geogebra.web.web.gui.util.StandardButton; 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.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.Widget; /** * GUI Element showing a Material as search Result * * @author Matthias Meisinger * */ public class MaterialListElement extends FlowPanel implements MaterialListElementI { public enum State { Default, Selected, Disabled; } private FlowPanel materialElementContent; private FlowPanel background; protected FlowPanel infoPanel; protected boolean isLocal; protected boolean isOwnMaterial; protected Label title; protected Label sharedBy; private TextBox renameTitleBox; protected Material material; protected final AppW app; protected final Localization loc; protected final GuiManagerW guiManager; protected State state = State.Default; Runnable editMaterial; protected StandardButton viewButton; protected StandardButton editButton; protected StandardButton renameButton; private FlowPanel confirmDeletePanel; private StandardButton confirm; private StandardButton cancel; private StandardButton deleteButton; private StandardButton favoriteButton; private ShowDetailsListener showListener; private Runnable deleteCallback = new Runnable() { @Override public void run() { Log.debug("DELETE finished"); MaterialListElement.this.app.getGuiManager().getBrowseView() .setMaterialsDefaultStyle(); } }; /** * * @param m {@link Material} * @param app {@link AppW} * @param isLocal boolean */ public MaterialListElement(final Material m, final AppW app, boolean isLocal) { this.app = app; this.loc = app.getLocalization(); this.guiManager = (GuiManagerW) app.getGuiManager(); this.material = m; this.isLocal = isLocal; this.isOwnMaterial = app.getLoginOperation().owns(m); this.setStyleName("materialListElement"); this.addStyleName("default"); if (!isLocal) { // this.material.setSyncStamp(-1); } this.editMaterial = new Runnable() { @Override public void run() { onEdit(); } }; initMaterialInfos(); this.materialElementContent = new FlowPanel(); this.materialElementContent.addStyleName("materialElementContent"); this.add(this.materialElementContent); addPreviewPicture(); addInfoPanel(); showDetails(false); this.addDomHandler(new ClickHandler() { @Override public void onClick(final ClickEvent event) { event.preventDefault(); if (state == State.Disabled) { return; } else if (state == State.Default) { markSelected(); event.stopPropagation(); } else { event.stopPropagation(); } } }, ClickEvent.getType()); setLabels(); } protected void initMaterialInfos() { if (!isLocal()) { this.title = new Label(this.material.getTitle()); this.sharedBy = new Label(this.material.getAuthor()); this.sharedBy.setStyleName("sharedPanel"); } else { String key = this.material.getTitle(); this.title = new Label(extractTitle(key)); } this.title.addStyleName("fileTitle"); } private void addInfoPanel() { this.infoPanel = new FlowPanel(); this.infoPanel.setStyleName("infoPanel"); addTextInfo(); addSeperator(); addOptions(); this.materialElementContent.add(this.infoPanel); } protected void addTextInfo() { this.infoPanel.add(this.title); if (isOwnMaterial) { initRenameTextBox(); } if (!isLocal()) { this.infoPanel.add(this.sharedBy); } } protected void addOptions() { if (isOwnMaterial && !isLocal()) { addEditButton(); addViewButton(); addRenameButton(); addDeleteButton(); } else if (isLocal()) { addEditButton(); addRenameButton(); addDeleteButton(); } else { addEditButton(); addViewButton(); } } private void addRenameButton() { this.renameButton = new StandardButton(BrowseResources.INSTANCE.document_rename(), "", 20); this.infoPanel.add(this.renameButton); this.renameButton.addFastClickHandler(new FastClickHandler() { @Override public void onClick(Widget source) { onRename(); } }); } void onRename() { this.renameTitleBox.setVisible(true); this.renameTitleBox.setText(this.title.getText()); this.title.setVisible(false); this.renameTitleBox.setSelectionRange(0, this.renameTitleBox.getText().length()); this.renameTitleBox.setFocus(true); } void doRename() { if (this.renameTitleBox.getText().length() < 1 || this.renameTitleBox.getText().equals(this.title.getText())) { //no changes this.title.setVisible(true); this.renameTitleBox.setVisible(false); return; } final String oldTitle = this.title.getText(); this.title.setText(this.renameTitleBox.getText()); this.renameTitleBox.setVisible(false); this.title.setVisible(true); if (app.getNetworkOperation().isOnline() && this.material.getId() > 0) { this.material.setTitle(this.title.getText()); ((GeoGebraTubeAPIW) app.getLoginOperation().getGeoGebraTubeAPI()) .uploadRenameMaterial(this.material, new MaterialCallback() { @Override public void onLoaded( List<Material> parseResponse, ArrayList<Chapter> meta) { if (parseResponse.size() != 1) { app.showError( loc.getError("RenameFailed")); title.setText(oldTitle); } else { Log.debug("RENAME local"); material.setModified(parseResponse.get( 0).getModified()); material.setSyncStamp(parseResponse .get(0) .getModified()); if (material.getLocalID() <= 0) { return; } Log.debug("RENAME CALLBACK" + oldTitle + "->" + title.getText()); material.setTitle(oldTitle); app.getFileManager().rename( title.getText(), material); } } }); } else { this.material.setModified(Math.max( SaveDialogW.getCurrentTimestamp(app), material.getSyncStamp() + 1)); this.app.getFileManager().rename(this.title.getText(), this.material); } } private void initRenameTextBox() { this.renameTitleBox = new GTextBox(); this.renameTitleBox.addStyleName("renameBox"); this.infoPanel.add(this.renameTitleBox); this.renameTitleBox.setVisible(false); this.renameTitleBox.addBlurHandler(new BlurHandler() { @Override public void onBlur(BlurEvent event) { doRename(); } }); this.renameTitleBox.addKeyDownHandler(new KeyDownHandler() { @Override public void onKeyDown(KeyDownEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { renameTitleBox.setFocus(false); } } }); } private void addSeperator() { FlowPanel separator = new FlowPanel(); separator.setStyleName("Separator"); this.infoPanel.add(separator); } private void addPreviewPicture() { SimplePanel previewPicturePanel = new SimplePanel(); previewPicturePanel.addStyleName("fileImage"); previewPicturePanel.addDomHandler(new ClickHandler() { @Override public void onClick(final ClickEvent event) { if (state == State.Selected) { openDefault(); } else if (state == State.Disabled) { return; } else { markSelected(); event.stopPropagation(); } } }, ClickEvent.getType()); background = new FlowPanel(); background.setStyleName("background"); setPictureAsBackground(); previewPicturePanel.add(background); this.materialElementContent.add(previewPicturePanel); addSyncDecoration(); } private void addSyncDecoration() { if(this.material.getType() == Material.MaterialType.book){ final Label deco = new Label(); deco.setStyleName("bookDecoration"); background.add(deco); } if ((this.app.getFileManager().shouldKeep(this.material.getId()) || this.app .has(Feature.LOCALSTORAGE_FILES)) && this.material.getType() != MaterialType.book && this.material.getSyncStamp() > 0 && this.material.getModified() <= this.material.getSyncStamp()) { final Label deco = new Label(); deco.setStyleName("syncDecoration"); background.add(deco); } if (this.app.getLoginOperation().isLoggedIn()) { addFavoriteButton(); } } private void setPictureAsBackground() { final String thumb = this.material.getThumbnail(); if (thumb != null && thumb.length() > 0) { background .getElement() .getStyle() .setBackgroundImage( "url(" + Browser.normalizeURL(thumb) + ")"); } else { background .getElement() .getStyle() .setBackgroundImage( "url(" + AppResources.INSTANCE.geogebra64() .getSafeUri().asString() + ")"); } } private void addDeleteButton() { this.deleteButton = new StandardButton(BrowseResources.INSTANCE.document_delete()); this.infoPanel.add(this.deleteButton); this.deleteButton.addFastClickHandler(new FastClickHandler() { @Override public void onClick(Widget source) { onDelete(); } }); initConfirmDeletePanel(); } private void addFavoriteButton() { this.favoriteButton = new StandardButton( this.material.isFavorite() ? BrowseResources.INSTANCE .favorite() : BrowseResources.INSTANCE .not_favorite()); this.favoriteButton.addStyleName("ggbFavorite"); this.background.add(this.favoriteButton); this.favoriteButton.addFastClickHandler(new FastClickHandler() { @Override public void onClick(Widget source) { onFavorite(); } }); } void onFavorite(){ app.getLoginOperation().getGeoGebraTubeAPI() .favorite(this.material.getId(), !this.material.isFavorite()); this.material.setFavorite(!this.material.isFavorite()); updateFavoriteText(); if (this.material.isFavorite()) { if (app.getFileManager().shouldKeep(this.material.getId())) { this.app.getFileManager().getFromTube(this.material.getId(), this.material.isFromAnotherDevice()); } } else if (this.material.isFromAnotherDevice()) { this.app.getFileManager().delete(this.material, true, this.deleteCallback); } } void onDelete() { this.deleteButton.addStyleName("deleteActive"); if (this.editButton != null) { this.editButton.setVisible(false); } if (this.viewButton != null) { this.viewButton.setVisible(false); } if (this.renameButton != null) { this.renameButton.setVisible(false); } this.confirmDeletePanel.setVisible(true); this.deleteButton.setIcon(BrowseResources.INSTANCE.document_delete_active()); } private void initConfirmDeletePanel() { this.confirm = new StandardButton(this.loc.getPlain("Delete")); this.confirm.addStyleName("gwt-Button"); this.confirm.addStyleName("deleteButton"); this.confirm.addFastClickHandler(new FastClickHandler() { @Override public void onClick(Widget source) { onConfirmDelete(); } }); this.cancel = new StandardButton(this.loc.getPlain("Cancel")); this.cancel.addStyleName("cancelButton"); this.cancel.addStyleName("gwt-Button"); this.cancel.addStyleName("minor"); this.cancel.addFastClickHandler(new FastClickHandler() { @Override public void onClick(Widget source) { onCancel(); } }); this.confirmDeletePanel = new FlowPanel(); this.confirmDeletePanel.add(this.cancel); this.confirmDeletePanel.add(this.confirm); this.confirmDeletePanel.setStyleName("confirmDelete"); this.confirmDeletePanel.setVisible(false); this.infoPanel.add(this.confirmDeletePanel); } void onConfirmDelete() { this.setVisible(false); setAllMaterialsDefault(); final Material toDelete = this.material; if (app.getNetworkOperation().isOnline() && toDelete.getId() > 0) { ((GeoGebraTubeAPIW) app.getLoginOperation().getGeoGebraTubeAPI()) .deleteMaterial(toDelete, new MaterialCallback() { @Override public void onLoaded(List<Material> parseResponse, ArrayList<Chapter> meta) { Log.debug("DELETE local"); remove(); MaterialListElement.this.app.getFileManager() .delete(toDelete, true, MaterialListElement.this.deleteCallback); } @Override public void onError(Throwable exception) { Log.debug("DELETE backup"); MaterialListElement.this.app.getFileManager() .delete(toDelete, false, MaterialListElement.this.deleteCallback); setVisible(true); app.showError(loc.getMenu("DeleteFailed")); } }); } else { Log.debug("DELETE permanent"); this.app.getFileManager().delete(toDelete, toDelete.getId() <= 0, this.deleteCallback); } } protected void remove() { app.getGuiManager().getBrowseView() .removeMaterial(MaterialListElement.this.material); } protected void setAllMaterialsDefault() { app.getGuiManager().getBrowseView().setMaterialsDefaultStyle(); } void onCancel() { showDetails(true); } /** * */ protected void openDefault() { if (isOwnMaterial) { ((DialogManagerW) app.getDialogManager()).getSaveDialog().showIfNeeded(editMaterial); } else { onView(); } } protected void addEditButton() { this.editButton = new StandardButton(BrowseResources.INSTANCE.document_edit(), "", 20); this.infoPanel.add(this.editButton); this.editButton.addFastClickHandler(new FastClickHandler() { @Override public void onClick(Widget source) { app.checkSaved(editMaterial); } }); } /** * */ protected void onEdit() { if (!isLocal) { Log.debug(material.getType().toString()); if(material.getType() == MaterialType.book){ ((GeoGebraTubeAPIW) app.getLoginOperation() .getGeoGebraTubeAPI()).getBookItems(material.getId(), new MaterialCallback() { @Override public void onLoaded(final List<Material> response, final ArrayList<Chapter> chapters) { guiManager.getBrowseView().clearMaterials(); guiManager.getBrowseView().onSearchResults( response, chapters); } }); return; } final long synced = material.getSyncStamp(); if (material.getType() == MaterialType.ws) { ((GeoGebraTubeAPIW) app.getLoginOperation() .getGeoGebraTubeAPI()).getWorksheetItems( material.getId(), new MaterialCallback() { @Override public void onLoaded(final List<Material> response, ArrayList<Chapter> meta) { if (response.size() != 1 || StringUtil.empty(material .getBase64())) { // guiManager.getBrowseView().clearMaterials(); // guiManager.getBrowseView().onSearchResults( // response, null); Browser.openTubeWindow( material.getEditUrl()); } else { material = response.get(0); material.setSyncStamp(synced); app.getGgbApi().setBase64( material.getBase64()); app.setActiveMaterial(material); } } }); return; } ToolTipManagerW.sharedInstance().showBottomMessage( loc.getMenu("Loading"), false, app); loadGGBfromTube(); } else { ToolTipManagerW.sharedInstance().showBottomMessage( loc.getMenu("Loading"), false, app); if (!this.app.getFileManager().hasBase64(this.material)) { loadGGBfromTube(); } else { this.app.getFileManager().openMaterial(this.material); this.app.setActiveMaterial(material); } } } private void loadGGBfromTube() { final long synced = material.getSyncStamp(); ((GeoGebraTubeAPIW) app.getLoginOperation().getGeoGebraTubeAPI()) .getItem(material.getSharingKeyOrId() + "", new MaterialCallback() { @Override public void onLoaded( final List<Material> parseResponse, ArrayList<Chapter> meta) { if (parseResponse.size() == 1) { material = parseResponse.get(0); material.setSyncStamp(synced); if (material .getType() == MaterialType.csv) { app.openCSV(Browser.decodeBase64( material.getBase64())); } else { app.getGgbApi().setBase64( material.getBase64()); } app.setActiveMaterial(material); } else { app.showError(loc .getError("LoadFileFailed")); } } @Override public void onError(Throwable error) { app.showError(loc.getError( "LoadFileFailed")); } }); } protected void closeBrowseView() { this.guiManager.getBrowseView().close(); } protected void addViewButton() { this.viewButton = new StandardButton(BrowseResources.INSTANCE.document_view(), "", 20); this.viewButton.addStyleName("viewButton"); this.infoPanel.add(this.viewButton); this.viewButton.addFastClickHandler(new FastClickHandler() { @Override public void onClick(Widget source) { onView(); } }); } /** * marks the material as selected and disables the other materials */ protected void markSelected() { this.guiManager.getBrowseView().disableMaterials(); this.guiManager.getBrowseView().rememberSelected(this); this.state = State.Selected; this.removeStyleName("unselected"); this.removeStyleName("default"); this.addStyleName("selected"); showDetails(true); } /** * sets the default style */ public void setDefaultStyle() { this.state = State.Default; this.removeStyleName("selected"); this.removeStyleName("unselected"); this.addStyleName("default"); showDetails(false); } /** * Disables the material. */ public void disableMaterial() { this.state = State.Disabled; this.addStyleName("unselected"); this.removeStyleName("selected"); this.removeStyleName("default"); } /** * */ public void setLabels() { if(this.deleteButton != null){ this.deleteButton.setText(loc.getPlain("Delete")); } if(this.cancel != null){ this.cancel.setText(this.loc.getPlain("Cancel")); } if(this.confirm != null){ this.confirm.setText(this.loc.getPlain("Delete")); } if(this.editButton != null){ this.editButton.setText(loc.getMenu("Edit")); } if(this.viewButton != null){ this.viewButton .setText(loc.getMenu(getInsertWorksheetTitle(material))); } if (this.renameButton != null) { this.renameButton.setText(loc.getMenu("Rename")); } } private void updateFavoriteText() { this.favoriteButton .setIcon(material.isFavorite() ? BrowseResources.INSTANCE .favorite() : BrowseResources.INSTANCE.not_favorite()); } /** * * @return the {@link Material} */ public Material getMaterial() { return this.material; } protected void showDetails(final boolean show) { /* * if (this.favoriteButton != null) { * this.favoriteButton.setVisible(show); } */ if (isOwnMaterial && !isLocal()) { this.sharedBy.setVisible(true); this.viewButton.setVisible(show); this.deleteButton.setVisible(show); this.deleteButton.removeStyleName("deleteActive"); this.deleteButton.setIcon(BrowseResources.INSTANCE.document_delete()); this.confirmDeletePanel.setVisible(false); this.renameButton.setVisible(show); this.editButton.setVisible(show); } else if (isLocal()) { this.deleteButton.setVisible(show); this.deleteButton.removeStyleName("deleteActive"); this.deleteButton.setIcon(BrowseResources.INSTANCE.document_delete()); this.confirmDeletePanel.setVisible(false); this.renameButton.setVisible(show); this.editButton.setVisible(show); } else { this.sharedBy.setVisible(true); this.viewButton.setVisible(show); this.editButton.setVisible(show); } if (show) { this.infoPanel.addStyleName("detailed"); showListener.onShowDetails(materialElementContent); } else { this.infoPanel.removeStyleName("detailed"); } } /*** LAF dependent methods **/ public String getInsertWorksheetTitle(final Material m) { return "ViewMaterial"; } /** * Opens GeoGebraTube material in a new window (overwritten for tablet app, smart widget) */ protected void onView() { this.guiManager.getBrowseView().setMaterialsDefaultStyle(); Browser.openTubeWindow(material.getURL()); } public void setMaterial(Material mat) { this.material = mat; if (isLocal()) { String key = mat.getTitle(); this.title.setText(extractTitle(key)); } else { this.title.setText(this.material.getTitle()); } if (!app.getLoginOperation().owns(mat)) { this.sharedBy.setText(this.material.getAuthor()); } this.background.clear(); setPictureAsBackground(); addSyncDecoration(); } public void setShowDetailsListener(ShowDetailsListener listener){ this.showListener = listener; } private static String extractTitle(String key) { return key.substring(key.indexOf("_", key.indexOf("_")+1)+1); } /** * @return true if this material is saved local */ public boolean isLocal() { return this.material.getId() <= 0; } /** * @return true if this material belongs to the signed in user */ public boolean isOwn() { return this.isOwnMaterial; } }