// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.collide.client.editor.selection;
import com.google.collide.client.editor.FocusManager;
import com.google.collide.client.editor.renderer.LineRenderer;
import com.google.collide.shared.document.Line;
import com.google.collide.shared.document.Position;
import com.google.collide.shared.util.MathUtils;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
/**
* A line renderer that styles the lines contained in the user's selection.
*/
public class SelectionLineRenderer implements LineRenderer {
/**
* CssResource for the {@link SelectionLineRenderer}.
*/
public interface Css extends CssResource {
String selection();
String inactiveSelection();
}
/**
* ClientBundle for the {@link SelectionLineRenderer}.
*/
public interface Resources extends ClientBundle {
@Source("SelectionLineRenderer.css")
Css editorSelectionLineRendererCss();
}
/**
* Current chunk being rendered's position inside {@link #chunkLengths} and
* {@link #chunkStyles}
*/
private int curChunkIndex;
/**
* Length of each chunk (there are a maximum of three chunks: beginning
* non-selected text, selected text, and trailing non-selected text)
*/
private int[] chunkLengths = new int[3];
/** Style for each chunk */
private String[] chunkStyles = new String[3];
private final Css css;
private final FocusManager focusManager;
private final SelectionModel selectionModel;
SelectionLineRenderer(SelectionModel selectionModel, FocusManager focusManager, Resources res) {
this.focusManager = focusManager;
this.css = res.editorSelectionLineRendererCss();
this.selectionModel = selectionModel;
}
@Override
public void renderNextChunk(Target target) {
target.render(chunkLengths[curChunkIndex], chunkStyles[curChunkIndex]);
curChunkIndex++;
}
@Override
public boolean resetToBeginningOfLine(Line line, int lineNumber) {
if (!selectionModel.hasSelection()
|| !MathUtils.isInRangeInclusive(lineNumber, selectionModel.getSelectionBeginLineNumber(),
selectionModel.getSelectionEndLineNumber())) {
return false;
}
Position[] selection = selectionModel.getSelectionRange(false);
/*
* If this line is the first line of the selection, the column is the
* selection's column. Otherwise, the column is 0 since this line is
* entirely in the selection.
*/
int selectionBeginColumn =
selection[0].getLineInfo().number() == lineNumber ? selection[0].getColumn() : 0;
// Similar to above, except for the last line
int selectionEndColumnExclusive =
selection[1].getLineInfo().number() == lineNumber ? selection[1].getColumn() : line
.getText().length();
if (selectionEndColumnExclusive == 0) {
// This line doesn't actually have a selection
return false;
}
resetChunks(line, selectionBeginColumn, selectionEndColumnExclusive);
curChunkIndex = 0;
return true;
}
@Override
public boolean shouldLastChunkFillToRight() {
return true;
}
private void resetChunks(Line line, int selectionBeginColumn, int selectionEndColumnExclusive) {
int curChunkIndex = 0;
if (selectionBeginColumn > 0) {
/*
* The selection does not start at the beginning of the line, so the first
* chunk should be null
*/
chunkStyles[curChunkIndex] = null;
chunkLengths[curChunkIndex] = selectionBeginColumn;
curChunkIndex++;
}
chunkStyles[curChunkIndex] =
focusManager.hasFocus() ? css.selection() : css.inactiveSelection();
chunkLengths[curChunkIndex] = selectionEndColumnExclusive - selectionBeginColumn;
curChunkIndex++;
if (selectionEndColumnExclusive < line.getText().length()) {
chunkStyles[curChunkIndex] = null;
chunkLengths[curChunkIndex] = line.getText().length() - selectionEndColumnExclusive;
curChunkIndex++;
}
for (; curChunkIndex < chunkStyles.length; curChunkIndex++) {
chunkStyles[curChunkIndex] = null;
chunkLengths[curChunkIndex] = 0;
}
}
}