blob: 4c12cc05ff5d547cf780b920c3f64fedce14acd4 [file] [log] [blame]
//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.Pos;
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(
Pos.create(start, 0),
Pos.create(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);
}
private void expandSideBefore(int cnt) {
FromTo range = textMarker.find();
int oldStart = range.from().line();
int newStart = oldStart + cnt;
int end = range.to().line();
clearMarkerAndWidget();
collapse(newStart, end, true);
updateSelection();
}
void expandSideAll() {
clearMarkerAndWidget();
removeFromParent();
}
private void expandAfter() {
FromTo range = textMarker.find();
int start = range.from().line();
int oldEnd = range.to().line();
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.from(), sel.to());
}
}
@UiHandler("skipNum")
void onExpandAll(@SuppressWarnings("unused") ClickEvent e) {
expandAll();
updateSelection();
otherBar.updateSelection();
cm.focus();
}
private void expandAll() {
expandSideAll();
otherBar.expandSideAll();
manager.remove(this, otherBar);
}
@UiHandler("upArrow")
void onExpandBefore(@SuppressWarnings("unused") ClickEvent e) {
expandBefore(NUM_ROWS_TO_EXPAND);
cm.focus();
}
@UiHandler("downArrow")
void onExpandAfter(@SuppressWarnings("unused") ClickEvent e) {
expandAfter();
otherBar.expandAfter();
cm.focus();
}
}