blob: 023a8ebc5d494cf2673114f83135a44ef2d9562c [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*
* "ActiveRegion.java"
* ActiveRegion.java 1.8 01/07/16
*/
package org.netbeans.lib.terminalemulator;
import java.util.LinkedList;
import java.util.ListIterator;
public class ActiveRegion {
public Coord begin = new Coord();
public Coord end = new Coord();
// accessible to RegionManager
ActiveRegion parent;
boolean nested;
private LinkedList<ActiveRegion> children;
private boolean has_end;
private int parentAttrs; // attrs at the time this region began
ActiveRegion(ActiveRegion parent, Coord begin, boolean nested) {
this.parent = parent;
this.begin.copyFrom(begin);
this.nested = nested;
}
void setParentAttrs(int attrs) {
this.parentAttrs = attrs;
}
int getParentAttrs() {
return parentAttrs;
}
public ActiveRegion parent() {
return this.parent;
}
public Extent getExtent() {
if (has_end) {
return new Extent(begin, end);
} else {
return new Extent(begin, begin);
}
}
void setEnd(Coord end) {
this.end.copyFrom(end);
has_end = true;
}
void addChild(ActiveRegion child) {
if (children == null) {
children = new LinkedList<>();
}
children.add(child);
}
void removeChild(ActiveRegion child) {
if (children == null) {
return;
}
children.remove(child);
}
ActiveRegion contains(Coord coord) {
// System.out.println("ActiveRegion [ " + begin + "-" + end + "] vs " + // NOI18N
// coord);
boolean coord_past_end = has_end && coord.compareTo(end) > 0;
if (this.parent != null && (coord.compareTo(begin) < 0 ||
coord_past_end)) {
return null; // outside of us entirely
}
if (children != null) {
ListIterator<ActiveRegion> iter = children.listIterator();
while (iter.hasNext()) {
ActiveRegion child = iter.next();
if (coord.compareTo(child.begin) < 0) {
break; // short circuit
} // 'target' is 'child' or one if it's children
ActiveRegion target = child.contains(coord);
if (target != null) {
return target;
}
}
}
return this;
}
// absolute coordinate mgmt
void relocate(int delta) {
this.begin.row += delta;
this.end.row += delta;
if (children != null) {
for (ActiveRegion child : children)
child.relocate(delta);
}
}
void cull(int origin) {
// See RegionManager.cull() for culling strategy.
// This function isn't recursive. It's called only once on root.
if (children == null) {
return;
}
int nculled = 0;
ListIterator<ActiveRegion> iter = children.listIterator();
while (iter.hasNext()) {
ActiveRegion child = iter.next();
if (child.begin.row < origin) {
iter.remove();
nculled++;
} else {
break; // short circuit out
}
}
// System.out.println("cull'ed " + nculled + " regions"); // NOI18N
}
/**
* Mark this region as one that may be converted to a selection.
* <p>
* This is just a convenience state-keeping flag
*/
public void setSelectable(boolean selectable) {
this.selectable = selectable;
}
/**
* Return the value set using setSelectable().
*/
public boolean isSelectable() {
return selectable;
}
private boolean selectable;
/**
* Mark this region as one that will provide feedback when the mouse moves
* over it.
* <p>
* This is just a convenience state-keeping flag
*/
public void setFeedbackEnabled(boolean feedback) {
this.feedback_enabled = feedback;
}
/**
* Return the value set using setFeedback().
*/
public boolean isFeedbackEnabled() {
return feedback_enabled;
}
private boolean feedback_enabled;
/**
* Mark this region as one that will provide feedback in it's containing
* region.
*/
public void setFeedbackViaParent(boolean feedback_via_parent) {
this.feedback_via_parent = feedback_via_parent;
}
public boolean isFeedbackViaParent() {
return feedback_via_parent;
}
private boolean feedback_via_parent;
/*
* Mark this region as a "link".
*/
public void setLink(boolean link) {
this.link = link;
}
public boolean isLink() {
return link;
}
private boolean link;
/**
* Associate additional data with this ActiveRegion.
*/
public void setUserObject(Object object) {
this.user_object = object;
}
/**
* Retrieve the additional data associated with this ActiveRegion through
* setUserObject.
*/
public Object getUserObject() {
return user_object;
}
private Object user_object;
// siblings
//
// I've chosen to delegate finding of siblings to the parent instead of
// using explicit sibling links. Here are the reasons:
// - explicit links take up memory. I believe in the long run that is
// more costly than dumb searches.
// - AR's get created when stuff gets rendered. We want to render stuff
// as fast as possible. So instead of doing work at creation time
// which needs to be supra-human speed, we do the work at query time
// which is usualy in reponse to human actions and need not be too fast.
//
// Ultimately a Vector is probably a better substitute for LinkedList
// In that case each child could remember it's index in it's parents
// vector (less storage than two pointers, and vector has less overhead
// than linkedlist to compensate). Note that this will work only
// because ActiveRegions are not editable.
/**
* Return the first child of this
*/
public ActiveRegion firstChild() {
if (children == null) {
return null;
}
return children.getFirst();
}
/**
* Return the last child of this
*/
public ActiveRegion lastChild() {
if (children == null) {
return null;
}
return children.getLast();
}
/**
* Get the previous sibling of this region
*/
public ActiveRegion getPreviousSibling() {
if (parent != null) {
return parent.previous_sibling_of(this);
} else {
return null;
}
}
/**
* Get the next sibling of this region
*/
public ActiveRegion getNextSibling() {
if (parent != null) {
return parent.next_sibling_of(this);
} else {
return null;
}
}
private ActiveRegion previous_sibling_of(ActiveRegion child) {
ActiveRegion previousChild = null;
for (ActiveRegion candidate : children) {
if (candidate == child) {
// bug: iterator is already shifted, so following command
// returns again child
//return (ActiveRegion) (iter.hasPrevious()? iter.previous(): null);
return previousChild;
}
previousChild = candidate;
}
return null;
}
private ActiveRegion next_sibling_of(ActiveRegion child) {
ListIterator<ActiveRegion> iter = children.listIterator();
while (iter.hasNext()) {
ActiveRegion candidate = iter.next();
if (candidate == child) {
return iter.hasNext() ? iter.next() : null;
}
}
return null;
}
}