Java tutorial
/* * ============================================================= * Copyright (C) 2007-2011 Edgenius (http://www.edgenius.com) * ============================================================= * License Information: http://www.edgenius.com/licensing/edgenius/2.0/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2.0 * as published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * http://www.gnu.org/licenses/gpl.txt * * **************************************************************** */ package com.edgenius.wiki.gwt.client.page; import java.util.Iterator; import java.util.List; import com.edgenius.wiki.gwt.client.Css; import com.edgenius.wiki.gwt.client.GwtClientUtils; import com.edgenius.wiki.gwt.client.i18n.Msg; import com.edgenius.wiki.gwt.client.model.DiffListModel; import com.edgenius.wiki.gwt.client.model.DiffModel; import com.edgenius.wiki.gwt.client.model.PageItemModel; import com.edgenius.wiki.gwt.client.page.widgets.FunctionWidget; import com.edgenius.wiki.gwt.client.render.TextRenderPanel; import com.edgenius.wiki.gwt.client.server.utils.GwtUtils; import com.edgenius.wiki.gwt.client.server.utils.SharedConstants; import com.edgenius.wiki.gwt.client.widgets.ClickLink; import com.edgenius.wiki.gwt.client.widgets.MessageWidget; import com.edgenius.wiki.gwt.client.widgets.UserProfileLink; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Hyperlink; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.UIObject; import com.google.gwt.user.client.ui.VerticalPanel; /** * Abstract panel makes children class panel support Diff list on panel. * @author Dapeng.Ni */ public abstract class DiffPanel extends MessagePanel { public VersionConflictAsync versionAsync = new VersionConflictAsync(); protected TextRenderPanel diffContent = new TextRenderPanel(); protected FunctionWidget functionBtnBar; protected PageMain main; protected MessageWidget diffMessage = new MessageWidget(); private boolean isConflictMerged = false; public DiffPanel(final PageMain main) { this.main = main; diffContent.setVisible(false); //function buttons functionBtnBar = new FunctionWidget(main); } /** * NOTE: <b>Override method must call super.diffRendered();</b> * sub class could call this method to make diff panel visible. */ protected void diffRendered() { diffMessage.setVisible(true); message.setVisible(false); } /** * NOTE: <b>Override method must call super.diffResume();</b> */ protected void diffResume() { diffMessage.setVisible(false); message.setVisible(true); } protected String getDiffMergeResult() { if (!isConflictMerged) return null; //restore edit page from Diff compare panel StringBuffer text = new StringBuffer(); if (isConflictMerged) { //in edit mode, so although page modified, no need submit immediately. Just refresh editor text box for (Iterator iter = diffContent.iteratorContent(); iter.hasNext();) { Object obj = iter.next(); if (obj instanceof String) { text.append((String) obj); } else if (obj instanceof ClickLink) { DiffModel m = (DiffModel) ((ClickLink) obj).getObject(); text.append(m.content); } } } //This is reverse handling is refer to DiffServiceImpl.split(); return text.toString().replaceAll("<br>", "\r"); } //******************************************************************** // Private class //******************************************************************** /* * There are 2 different type DiffListModel returned from server side: * <li>History comparison: Just flat html and only one DiffModel in DiffListModel.revs(DiffModel) list </li> * <li>Saving version conflict: Has multiple DiffModel in list, need show popup menu to ask user "reject" or "merge". </li> */ private void fillDiffContent(DiffListModel model) { List<DiffModel> diffList = model.revs; //now render diff object diffContent.clear(); isConflictMerged = false; int idx = 0; for (Iterator<DiffModel> iter = diffList.iterator(); iter.hasNext();) { final DiffModel diff = iter.next(); if (model.type == DiffListModel.FLAT_TYPE) { diffContent.add(diff.content); //only one element exist, so not necessary to continue; break; } diff.index = idx++; if (diff.type == DiffModel.NOCHANGE) { diffContent.add(diff.content); } else { ClickLink link = new ClickLink(diff.content); if (diff.type == DiffModel.ADD) link.addStyleName(Css.DIFF_INSERTION); else link.addStyleName(Css.DIFF_DELETION); link.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { UIObject sender = (UIObject) event.getSource(); DiffActionMenu menu = new DiffActionMenu(diff); int left = sender.getAbsoluteLeft() + 10; int top = sender.getAbsoluteTop() + 10; menu.setPopupPosition(left, top); menu.show(); //TODO: need add windowResizeListener } }); link.setObject(diff); diffContent.add(link); } } diffContent.submit(); } //******************************************************************** // Inner Class //******************************************************************** private class VersionConflictAsync implements AsyncCallback<DiffListModel> { public void onFailure(Throwable error) { GwtClientUtils.processError(error); } public void onSuccess(DiffListModel model) { if (!GwtClientUtils.preSuccessCheck(model, diffMessage)) { return; } diffMessage(model); //show diffContent panel diffRendered(); fillDiffContent(model); } } private void diffMessage(final DiffListModel model) { diffMessage.cleanMessage(); String id1 = HTMLPanel.createUniqueId(); //head StringBuffer buf = new StringBuffer("<div class='historyAction diff'><div class='msg' id='").append(id1) .append("'></div>"); buf.append("<div class='action'>"); buf.append( "<div class='indicator diff-text'><div class='diff-insertion'>Added</div> <div class='diff-deletion'>Deleted</div></div>"); //diff 1 String idp1 = null, idp2 = null, idp3 = null, idn1 = null, idn2 = null, idn3 = null; idn1 = HTMLPanel.createUniqueId(); idn2 = HTMLPanel.createUniqueId(); idn3 = HTMLPanel.createUniqueId(); buf.append("<div class='round next'><div class='version' id='").append(idn1) .append("'></div><div class='author' id='").append(idn2).append("'></div><div class='date' id='") .append(idn3).append("'></div></div>"); //diff 2 idp1 = HTMLPanel.createUniqueId(); idp2 = HTMLPanel.createUniqueId(); idp3 = HTMLPanel.createUniqueId(); buf.append("<div class='current'>vs</div>"); buf.append("<div class='round prev'><div class='version' id='").append(idp1) .append("'></div><div class='author' id='").append(idp2).append("'></div><div class='date' id='") .append(idp3).append("'></div></div>"); //end buf.append("</div></div>"); //put this message into HTMLPanel msgPanel = new HTMLPanel(buf.toString()); msgPanel.add(new Label(Msg.consts.comparing_view()), id1); diffActionMsg(msgPanel, model.prevHistoryItem, idn1, idn2, idn3); diffActionMsg(msgPanel, model.nextHistoryItem, idp1, idp2, idp3); HorizontalPanel panel = new HorizontalPanel(); panel.add(msgPanel); diffMessage.warning(panel, false); } private void diffActionMsg(HTMLPanel msgPanel, final PageItemModel history, String idp1, String idp2, String idp3) { String spaceUname = PageMain.getSpaceUname(); Hyperlink preLink; if (history.version == SharedConstants.CURRENT) { //latest page preLink = new Hyperlink(Msg.consts.latest(), GwtUtils.getSpacePageToken(spaceUname, history.title)); } else { preLink = new Hyperlink(Msg.consts.revision() + " " + history.version, GwtUtils.buildToken(PageMain.TOKEN_HISTORY, spaceUname, String.valueOf(history.uid))); } UserProfileLink modifier = new UserProfileLink(history.modifier, spaceUname, history.modifierUsername, history.modifierPortrait); Label modifiedDate = new Label(GwtClientUtils.toDisplayDate(history.modifiedDate)); msgPanel.add(preLink, idp1); msgPanel.add(modifier, idp2); msgPanel.add(modifiedDate, idp3); } private class DiffActionMenu extends PopupPanel { public DiffActionMenu(final DiffModel diff) { //autohide popup: when mouse out click, this menu will hide super(true); VerticalPanel panel = new VerticalPanel(); Label accept = new Label(Msg.consts.accept()); Label deny = new Label(Msg.consts.deny()); accept.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { DiffActionMenu.this.hide(); accept(diff); } }); deny.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { DiffActionMenu.this.hide(); deny(diff); } }); accept.setWidth("120"); deny.setWidth("120"); panel.add(accept); panel.add(deny); setStyleName(Css.DIFF_CONFLICT_HANDLE_MENU); setWidget(panel); } private void accept(DiffModel diff) { modify(diff, true); } private void deny(DiffModel diff) { modify(diff, false); } private void modify(DiffModel diff, boolean accept) { //mark dirty flag so that must render this part code to server for preview isConflictMerged = true; int size = diffContent.getContentWidgetCount(); for (int idx = 0; idx < size; idx++) { Object obj = diffContent.getContentWidget(idx); if (obj instanceof ClickLink) { ClickLink link = (ClickLink) obj; DiffModel model = (DiffModel) link.getObject(); //same object, do accept if (model.index == diff.index) { if (diff.type == DiffModel.ADD) { //if this piece different is "ADD", if user click accept, remove old DiffModel add HTML. //Otherwise user choose deny, just remove old DiffModel (means delete) diffContent.removeCotentWidget(idx); if (accept) { //TODO: there is bug in GWT to insert in FlowPanel.need waiting new version to validate or add patch myself. if (size == idx + 1) { diffContent.add(diff.content); } else { diffContent.insert(diff.content, idx); } } } else if (diff.type == DiffModel.DEL) { //it do reverse action with "ADD": if accept, only remove DiffModel(means delete) //,otherwise, remove DiffModel and add HTML diffContent.removeCotentWidget(idx); if (!accept) { if (size == idx + 1) diffContent.add(diff.content); else { diffContent.insert(diff.content, idx); } } } break; } } } diffContent.submit(); } } }