//Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.gerrit.client.diff; import com.google.gerrit.client.patches.PatchUtil; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.resources.client.CssResource; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.HTMLPanel; import net.codemirror.lib.CodeMirror; import net.codemirror.lib.Configuration; import net.codemirror.lib.LineWidget; import net.codemirror.lib.TextMarker; import net.codemirror.lib.TextMarker.FromTo; /** The Widget that handles expanding of skipped lines */ class SkipBar extends Composite { interface Binder extends UiBinder<HTMLPanel, SkipBar> {} private static final Binder uiBinder = GWT.create(Binder.class); private static final int NUM_ROWS_TO_EXPAND = 10; private static final int UP_DOWN_THRESHOLD = 30; interface SkipBarStyle extends CssResource { String noExpand(); } @UiField(provided=true) Anchor skipNum; @UiField(provided=true) Anchor upArrow; @UiField(provided=true) Anchor downArrow; @UiField SkipBarStyle style; private final SkipManager manager; private final CodeMirror cm; private LineWidget lineWidget; private TextMarker textMarker; private SkipBar otherBar; SkipBar(SkipManager manager, final CodeMirror cm) { this.manager = manager; this.cm = cm; skipNum = new Anchor(true); upArrow = new Anchor(true); downArrow = new Anchor(true); initWidget(uiBinder.createAndBindUi(this)); addDomHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { cm.focus(); } }, ClickEvent.getType()); } void collapse(int start, int end, boolean attach) { if (attach) { boolean isNew = lineWidget == null; Configuration cfg = Configuration.create() .set("coverGutter", true) .set("noHScroll", true); if (start == 0) { // First line workaround lineWidget = cm.addLineWidget(end + 1, getElement(), cfg.set("above", true)); } else { lineWidget = cm.addLineWidget(start - 1, getElement(), cfg); } if (isNew) { lineWidget.onFirstRedraw(new Runnable() { @Override public void run() { int w = cm.getGutterElement().getOffsetWidth(); getElement().getStyle().setPaddingLeft(w, Unit.PX); } }); } } textMarker = cm.markText( CodeMirror.pos(start, 0), CodeMirror.pos(end), Configuration.create() .set("collapsed", true) .set("inclusiveLeft", true) .set("inclusiveRight", true)); textMarker.on("beforeCursorEnter", new Runnable() { @Override public void run() { expandAll(); } }); int skipped = end - start + 1; if (skipped <= UP_DOWN_THRESHOLD) { addStyleName(style.noExpand()); } else { upArrow.setHTML(PatchUtil.M.expandBefore(NUM_ROWS_TO_EXPAND)); downArrow.setHTML(PatchUtil.M.expandAfter(NUM_ROWS_TO_EXPAND)); } skipNum.setText(PatchUtil.M.patchSkipRegion(Integer .toString(skipped))); } static void link(SkipBar barA, SkipBar barB) { barA.otherBar = barB; barB.otherBar = barA; } private void clearMarkerAndWidget() { textMarker.clear(); lineWidget.clear(); } void expandBefore(int cnt) { expandSideBefore(cnt); otherBar.expandSideBefore(cnt); manager.getOverviewBar().refresh(); } private void expandSideBefore(int cnt) { FromTo range = textMarker.find(); int oldStart = range.getFrom().getLine(); int newStart = oldStart + cnt; int end = range.getTo().getLine(); clearMarkerAndWidget(); collapse(newStart, end, true); updateSelection(); } void expandSideAll() { clearMarkerAndWidget(); removeFromParent(); } private void expandAfter() { FromTo range = textMarker.find(); int start = range.getFrom().getLine(); int oldEnd = range.getTo().getLine(); int newEnd = oldEnd - NUM_ROWS_TO_EXPAND; boolean attach = start == 0; if (attach) { clearMarkerAndWidget(); } else { textMarker.clear(); } collapse(start, newEnd, attach); updateSelection(); } private void updateSelection() { if (cm.somethingSelected()) { FromTo sel = cm.getSelectedRange(); cm.setSelection(sel.getFrom(), sel.getTo()); } } @UiHandler("skipNum") void onExpandAll(ClickEvent e) { expandAll(); updateSelection(); otherBar.updateSelection(); cm.focus(); } private void expandAll() { expandSideAll(); otherBar.expandSideAll(); manager.remove(this, otherBar); manager.getOverviewBar().refresh(); } @UiHandler("upArrow") void onExpandBefore(ClickEvent e) { expandBefore(NUM_ROWS_TO_EXPAND); cm.focus(); } @UiHandler("downArrow") void onExpandAfter(ClickEvent e) { expandAfter(); otherBar.expandAfter(); manager.getOverviewBar().refresh(); cm.focus(); } }