/*
* CCVisu is a tool for visual graph clustering
* and general force-directed graph layout.
* This file is part of CCVisu.
*
* Copyright (C) 2005-2007 Dirk Beyer
*
* CCVisu is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* CCVisu 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CCVisu; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please find the GNU Lesser General Public License in file
* license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
*
* Dirk Beyer (firstname.lastname@sfu.ca)
* Simon Fraser University (SFU), B.C., Canada
*/
package ccvisu;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.Vector;
/*****************************************************************
* Writer for displaying the layout on the screen device.
* @version $Revision$; $Date$
* @author Dirk Beyer
*****************************************************************/
public class WriterDataGraphicsDISP extends WriterDataGraphics {
private ScreenDisplay display;
private Vector<Set<String>> xMap;
private Vector<Set<String>> yMap;
private Vector<Vector<Set<Integer>>> edgeMap;
private boolean[] edgeAnnot;
//group of nodes
private Vector<Cluster> clusters;
//write-color (computed once and stored)
private Color frontColor;
// The name of the input file (or empty if input was read from standard input).
protected String inputName;
//cmd to open a URL
private String browser;
// Temporarily associated during callback from ScreenDisplay.
private Graphics area;
private int insetleft;
private int insetbottom;
private int xSize = 0;
private int ySize = 0;
/**
* Constructor.
* @param graph Graph representation, contains the positions of the vertices.
* @param minVert Diameter of the smallest vertex.
* @param fontSize Font size of vertex annotations.
* @param backColor Background color.
* @param blackCircle If true, draw black circle around each vertex.
* @param showEdges If true, draw the edges between the vertices (if possible).
* @param openURL is Opening nodes as url allowed
* @param inputName the windows title
* @param browser the browser cmd
*/
public WriterDataGraphicsDISP(GraphData graph,
float minVert,
int fontSize,
Color backColor,
boolean blackCircle,
boolean showEdges,
boolean openURL,
String inputName,
String browser) {
super(graph, minVert, fontSize, backColor, blackCircle, showEdges, openURL);
this.inputName = inputName;
this.browser = browser;
//init
clusters = new Vector<Cluster>();
adjustFrontColor();
//create default cluster with all the nodes in it
Cluster.init(this,graph);
Cluster defaultCluster = new Cluster("default",CCVisu.green);
addCluster(defaultCluster);
for (int i = 0; i < graph.vertices.size(); ++i) {
defaultCluster.addNodeByIndex_WO_COLOR(i);
}
//edges annotation
if(!graph.edges.isEmpty()){
int size = graph.edges.size();
edgeAnnot = new boolean[size];
for(int i = 0; i < size; ++i){
edgeAnnot[i] = false;
}
}
display = new ScreenDisplay(this);
if (display == null) {
System.err.println("Runtime error: Could not open ScreenDisplay.");
System.exit(1);
}
}
/*****************************************************************
* Nothing to do here.
* The constructor initializes the ScreenDisplay (frame and canvas),
* and that calls back to the methods below
* (writeDISP, writeLAY, toggleVertexNames, getVertexNames).
*****************************************************************/
public void write() {
}
/*****************************************************************
* Write graphics layout.
* @param size Size of output area (e.g., number of pixel).
*****************************************************************/
public void writeGraphicsLayout(int size) {
float xPosMin = 1000000;
float xPosMax = -1000000;
float yPosMin = 1000000;
float yPosMax = -1000000;
float zPosMin = 1000000;
float zPosMax = -1000000;
for (int i = 0; i < graph.vertices.size(); ++i) {
if ( graph.vertices.get(i).showVertex ) {
xPosMin = Math.min(xPosMin, graph.pos[i][0]);
xPosMax = Math.max(xPosMax, graph.pos[i][0]);
yPosMin = Math.min(yPosMin, graph.pos[i][1]);
yPosMax = Math.max(yPosMax, graph.pos[i][1]);
zPosMin = Math.min(zPosMin, graph.pos[i][2]);
zPosMax = Math.max(zPosMax, graph.pos[i][2]);
}
}
float layoutDist;
layoutDist = Math.max(xPosMax - xPosMin, yPosMax - yPosMin);
layoutDist = Math.max(layoutDist, zPosMax - zPosMin);
float xOffset = - xPosMin + 0.05f * layoutDist;
float yOffset = - yPosMin + 0.05f * layoutDist;
float zOffset = - zPosMin + 0.05f * layoutDist;
float scale = 0.9f * size / layoutDist;
//draw edges
if(showEdges && !graph.edges.isEmpty()){
int end = graph.edges.size();
for (int i = 0; i < end; ++i) {
GraphEdgeInt e = (GraphEdgeInt) graph.edges.get(i);
writeEdge( i,
(int)((graph.pos[e.x][0]+ xOffset) * scale),
(int)((graph.pos[e.x][1]+ yOffset) * -scale + size),
(int)((graph.pos[e.x][2]+ zOffset) * scale),
(int)((graph.pos[e.y][0]+ xOffset) * scale),
(int)((graph.pos[e.y][1]+ yOffset) * -scale + size),
(int)((graph.pos[e.y][2]+ zOffset) * scale));
}
}
int end = clusters.size();
//draw the vertices
for (int i = 0; i < end; ++i){
Cluster clt = clusters.get(i);
if(clt.visible){
//draw the vertices that are not annotated
Iterator<Integer> it = clt.Iterator();
while(it.hasNext()){
int index = it.next().intValue();
GraphVertex curVertex = graph.vertices.get(index);
if (curVertex.showVertex && !curVertex.showName) {
int radius = (int) Math.max(Math.pow(curVertex.degree, 0.5) * minVert,
minVert);
int xPos = (int) ((graph.pos[index][0] + xOffset) * scale);
int yPos = (int) ((graph.pos[index][1] + yOffset) * -scale + size);
int zPos = (int) ((graph.pos[index][2] + zOffset) * scale);
writeVertex(curVertex, xPos, yPos, zPos, radius);
}
}
//Draw the annotated vertices.
it = clt.Iterator();
while(it.hasNext()){
int index = it.next().intValue();
GraphVertex curVertex = graph.vertices.get(index);
if (curVertex.showVertex && curVertex.showName) {
int radius = (int) Math.max(Math.pow(curVertex.degree, 0.5) * minVert,
minVert);
int xPos = (int) ((graph.pos[index][0] + xOffset) * scale);
int yPos = (int) ((graph.pos[index][1] + yOffset) * -scale + size);
int zPos = (int) ((graph.pos[index][2] + zOffset) * scale);
writeVertex(curVertex, xPos, yPos, zPos, radius);
}
}
}
}
//draw the cluster specific information (except default cluster)
for (int i = 1; i < end; ++i){//set begin to 1, 0 only for test
Cluster clt = clusters.get(i);
if(clt.visible && clt.info){
int x = (int)((clt.getX() + xOffset)*scale + insetleft);
int y = (int)((clt.getY() + yOffset)* -scale + size - insetbottom);
int l = x-5;
int r = x+5;
int u = y-5;
int b = y+5;
area.setColor(Color.BLACK);
area.drawLine(l, u, r, b);
area.drawLine(r, u, l, b);
area.setColor(clt.getColor());
area.drawLine(l, y, r, y);
area.drawLine(x, u, x, b);
int radius = (int)(clt.getAverageRadius()*scale);
int diam = (radius+radius);
area.drawOval(x-radius, y-radius, diam, diam);
area.setColor(Color.BLACK);
}
}
}
/**
* Writes the layout on the screen device (DISP output format).
* Call-back method, invoked from <code>ScreenDisplay</code>.
* @param size Size of the output drawing square.
* @param area The drawing area of the canvas.
* @param xCanvasSize Width of the canvas.
* @param yCanvasSize Height of the canvas.
* @param insetleft Left inset of the drawing frame.
* @param insetbottom Bottom inset of the drawing frame.
*/
public void writeDISP(int size,
Graphics area,
int xCanvasSize, int yCanvasSize,
int insetleft,
int insetbottom) {
this.area = area;
this.insetbottom = insetbottom;
this.insetleft = insetleft;
// Maps for getting the vertices at mouse positions.
xMap = new Vector<Set<String>>(xCanvasSize);
yMap = new Vector<Set<String>>(yCanvasSize);
for(int i = 0; i < xCanvasSize; ++i) {
xMap.add(new TreeSet<String>());
}
for(int i = 0; i < yCanvasSize; ++i) {
yMap.add(new TreeSet<String>());
}
//Maps for getting the edges at mouse positions.
if(showEdges){
if(xSize == xCanvasSize && ySize == yCanvasSize){
for(int x = 0; x < xCanvasSize; ++x){
Vector<Set<Integer>> lVec = edgeMap.get(x);
for(int y = 0; y < yCanvasSize; ++y){
lVec.get(y).clear();
}
}
}else{
edgeMap = new Vector<Vector<Set<Integer>>>(xCanvasSize);
for(int x = 0; x < xCanvasSize; ++x){
Vector<Set<Integer>> lVec = new Vector<Set<Integer>>(yCanvasSize);
edgeMap.add(lVec);
for(int y = 0; y < yCanvasSize; ++y){
lVec.add(new TreeSet<Integer>());
}
}
xSize = xCanvasSize;
ySize = yCanvasSize;
}
}
writeGraphicsLayout(size);
}
/**
* Writes a vertex on screen.
* @param curVertex The vertex object, to access vertex attributes.
* @param xPos x coordinate of the vertex.
* @param yPos y coordinate of the vertex.
* @param zPos z coordinate of the vertex.
* @param radius Radius of the vertex.
*/
public void writeVertex(GraphVertex curVertex,
int xPos, int yPos, int zPos, int radius) {
// Correction for inset.left.
xPos = xPos + insetleft;
// Correction for inset.bottom.
yPos = yPos - insetbottom;
int startX = xPos - radius;
int startY = yPos - radius;
// Draw the vertex.
int diam = 2*radius;
area.setColor(curVertex.color);
area.fillOval(startX, startY, diam, diam);
if (blackCircle) {
area.setColor(frontColor);
area.drawOval(startX, startY, diam, diam);
}
if (curVertex.showName) {
// Draw annotation.
// Use inverted background color for the annotation.
area.setColor(frontColor);
area.drawString(curVertex.name, xPos + radius + 3, yPos + 3);
}
// For interactive annotation: Store vertex names at their positions in the maps.
int endX = Math.min(xPos + radius, xMap.size() - 1);
for (int pos = Math.max(startX, 0); pos <= endX; ++pos) {
xMap.get(pos).add(curVertex.name);
}
int endY = Math.min(yPos + radius, yMap.size() - 1);
for (int pos = Math.max(startY, 0); pos <= endY; ++pos) {
yMap.get(pos).add(curVertex.name);
}
}
/**
* Writes an edge.
* @param index index of the edge in graph.edges
* @param xPos1 x coordinate of the first point.
* @param yPos1 y coordinate of the first point.
* @param zPos1 z coordinate of the first point.
* @param xPos2 x coordinate of the second point.
* @param yPos2 y coordinate of the second point.
* @param zPos2 z coordinate of the second point.
*/
public void writeEdge(int index, int xPos1, int yPos1, int zPos1,
int xPos2, int yPos2, int zPos2){
//reflexive edges are not allowed by specification
if(xPos1 == xPos2 && yPos1 == yPos2){
return;
}
GraphEdgeInt edge = graph.edges.get(index);
//Correction for inset.left
xPos1 = xPos1 + insetleft;
xPos2 = xPos2 + insetleft;
// Correction for inset.bottom
yPos1 = yPos1 - insetbottom;
yPos2 = yPos2 - insetbottom;
//////
//Draw
//////
area.setColor(frontColor);
area.drawLine(xPos1, yPos1, xPos2, yPos2);
//Draw the annotation
if(edgeAnnot[index]){
int xPos = (xPos1+xPos2+fontSize)/2 ;
int yPos = (yPos1+yPos2+fontSize)/2 ;
area.drawString(edge.relName, xPos, yPos);
}
///////////////////////////////////////////////////
//store for annotation (Bresenham's line algorithm)
boolean steep = Math.abs(yPos2 - yPos1) > Math.abs(xPos2 - xPos1);
if(steep){
int tmp = xPos1;
xPos1 = yPos1;
yPos1 = tmp;
tmp = xPos2;
xPos2 = yPos2;
yPos2 = tmp;
}
if(xPos1 > xPos2){
int tmp = xPos1;
xPos1 = xPos2;
xPos2 = tmp;
tmp = yPos1;
yPos1 = yPos2;
yPos2 = tmp;
}
int deltax = xPos2 - xPos1;
int deltay = Math.abs(yPos2 - yPos1);
float error = 0;
float deltaerr = ((float)deltay) / deltax;
int y = yPos1;
int ystep;
if(yPos1 < yPos2){
ystep = 1;
}else{
ystep = -1;
}
for(int x = xPos1; x <= xPos2; ++x){
if(steep){
if(y >= 0 && y < xSize && x >= 0 && x < ySize){
edgeMap.get(y).get(x).add(new Integer(index));
}
}else{
if(x >= 0 && x < xSize && y >= 0 && y < ySize){
edgeMap.get(x).get(y).add(new Integer(index));
}
}
error += deltaerr;
if(error >= 0.5){
y += ystep;
error -= 1.0;
}
}
}
/*****************************************************************
* Writes layout to file using an implementation of class <code>WriterData</code>.
* Call-back method, invoked from within ScreenDisplay.
* @param fileName Name of the output file to write the layout to.
*****************************************************************/
public void writeFileLayout(String fileName) {
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
WriterData dataWriter = new WriterDataLAY (graph, out); // Default, also .lay.
if (fileName.endsWith(".svg")) {
dataWriter = new WriterDataGraphicsSVG (graph, out,
minVert, fontSize,
backColor, blackCircle,
showEdges, openURL,
1.0f, inputName);
} else if (fileName.endsWith(".wrl")) {
dataWriter = new WriterDataGraphicsVRML (graph, out,
minVert, fontSize,
backColor, blackCircle,
showEdges, openURL,
1.0f);
}
dataWriter.write();
out.close();
System.err.println("Wrote layout to output file '" + fileName + "'.");
}
catch (Exception e) {
System.err.println("Exception while writing file '" + fileName + "': ");
System.err.println(e);
}
}
/*****************************************************************
* Marks all vertices whose node names match the given regular expression.
* Call-back method, invoked from within ScreenDisplay.
* @param regEx Regular expression.
*****************************************************************/
public void markVertices(String regEx) {
Color color = CCVisu.red;
for (int i = 0; i < graph.vertices.size(); ++i) {
GraphVertex curVertex = graph.vertices.get(i);
if (curVertex.name.matches(regEx)) {
curVertex.color = color;
curVertex.showName = true;
}
}
}
/****************************************************************************
* Toggle the showName flag of the vertices and edges at the given position.
* Call-back method, invoked from within ScreenDisplay.
* @param p coordinates of the vertex.
* @return number of names toggled
***************************************************************************/
public int toggleNames(Point p) {
int xPos = (int) p.getX();
int yPos = (int) p.getY();
Set<String> tmp = new TreeSet<String>(xMap.get(xPos));
tmp.retainAll(yMap.get(yPos));
Iterator<String> it = tmp.iterator();
int nb = 0;
while (it.hasNext()) {
++nb;
String name = it.next();
GraphVertex curVertex = graph.nameToVertex.get(name);
curVertex.showName = !curVertex.showName;
}
//edges
if(showEdges){
Set<Integer> edgesIndex = edgeMap.get(xPos).get(yPos);
Iterator<Integer> edgeIt = edgesIndex.iterator();
while(edgeIt.hasNext()){
++nb;
int index = edgeIt.next().intValue();
edgeAnnot[index] = !edgeAnnot[index];
}
}
return nb;
}
/*****************************************************************
* Show all labels (vertices and edges names).
* Call-back method, invoked from within ScreenDisplay.
*****************************************************************/
public void showAllLabels() {
//vertices
for (int i = 0; i < graph.vertices.size(); ++i) {
GraphVertex curVertex = graph.vertices.get(i);
if (curVertex.showVertex) {
curVertex.showName = true;
}
}
//edges
int end = graph.edges.size();
for(int i = 0; i < end; ++i){
edgeAnnot[i] = true;
}
}
/*****************************************************************
* Hide all labels (vertice names).
* Call-back method, invoked from within ScreenDisplay.
*****************************************************************/
public void hideAllLabels() {
//vertices
for (int i = 0; i < graph.vertices.size(); ++i) {
GraphVertex curVertex = graph.vertices.get(i);
if (curVertex.showVertex) {
curVertex.showName = false;
}
}
//edges
int end = graph.edges.size();
for(int i = 0; i < end; ++i){
edgeAnnot[i] = false;
}
}
/**
* show/hide the name of a vertex
* @param vertex a vertex
*/
public void showLabel(GraphVertex vertex, boolean show){
if(show){
if (vertex.showVertex) {
vertex.showName = true;
}
}else{
if (vertex.showVertex) {
vertex.showName = false;
}
}
}
/************************************************************************
* Compute list of names of the vertices and edges at the given position.
* Call-back method, invoked from within ScreenDisplay.
* @param p coordinates.
***********************************************************************/
public String getNames(Point p) {
int xPos = (int) p.getX();
int yPos = (int) p.getY();
if (xPos >= xMap.size() || yPos >= yMap.size()) {
return new String("");
}
Set<String> tmp = new TreeSet<String>(xMap.get(xPos));
tmp.retainAll(yMap.get(yPos));
//edges
if(showEdges){
Set<Integer> edgesIndex = edgeMap.get(xPos).get(yPos);
Iterator<Integer> it = edgesIndex.iterator();
while(it.hasNext()){
int index = it.next().intValue();
tmp.add(graph.edges.get(index).relName);
}
}
return(tmp.toString());
}
/*****************************************************************
* Restrict the set of vertices displayed on the screen to
* the vertices within the given rectangular (i.e., zoom).
* Call-back method, invoked from within ScreenDisplay.
* @param pTopLeft coordinates of the top left corner of the rectangular.
* @param pBottomRight coordinates of the bottom right corner of the rectangular.
*****************************************************************/
public void restrictShowedVertices(Point pTopLeft,
Point pBottomRight) {
int end = (int)Math.min(pBottomRight.getX(),xMap.size());
Set<String> xNodes = new TreeSet<String>();
for (int i = (int) pTopLeft.getX(); i < end; ++i) {
xNodes.addAll(xMap.get(i));
}
end = (int)Math.min(pBottomRight.getY(),yMap.size());
Set<String> yNodes = new TreeSet<String>();
for (int i = (int) pTopLeft.getY(); i < end; ++i) {
yNodes.addAll(yMap.get(i));
}
Set<String> lNodesToKeep = xNodes;
lNodesToKeep.retainAll(yNodes);
for (int i = 0; i < graph.vertices.size(); ++i) {
GraphVertex curVertex = graph.vertices.get(i);
if (!lNodesToKeep.contains(curVertex.name)) {
curVertex.showVertex = false;
}
}
}
/*****************************************************************
* Reset vertex restriction that was set by restrictShowedVertices.
* Call-back method, invoked from within ScreenDisplay.
*****************************************************************/
public void resetRestriction() {
// Handle vertex options.
for (int i = 0; i < graph.vertices.size(); ++i) {
GraphVertex curVertex = graph.vertices.get(i);
// hideSource (do not show vertex if it is source of an edge).
if (CCVisu.getHideSource() && curVertex.isSource) {
curVertex.showVertex = false;
} else {
curVertex.showVertex = true;
}
}
}
/*****************************************************************
* Sets the local graph representation (layout) to a new value.
* Call-back method, invoked from within ScreenDisplay.
* @param layout Graph/layout representation to switch to.
*****************************************************************/
public void setGraphData(GraphData layout) {
this.graph = layout;
//create default cluster with all the nodes in it
Cluster.init(this,graph);
Cluster defaultCluster = new Cluster("default",CCVisu.green);
this.clusters.removeAllElements();
addCluster(defaultCluster);
for (int i = 0; i < graph.vertices.size(); ++i) {
defaultCluster.addNodeByIndex_WO_COLOR(i);
}
showEdges = showEdges && !graph.edges.isEmpty();
//edges annotation
if(!graph.edges.isEmpty()){
int size = graph.edges.size();
edgeAnnot = new boolean[size];
for(int i = 0; i < size; ++i){
edgeAnnot[i] = false;
}
}
display.refresh();
}
/**
* get showEdges
*/
public boolean isshowEdgesPossible(){
return !graph.edges.isEmpty();
}
/**
* set showEdges
*/
public void setshowEdges(boolean se){
this.showEdges = se && isshowEdgesPossible();
}
/*****************************************************************
* Set a color to all vertices
* Call-back method, invoked from within ScreenDisplay.
*****************************************************************/
public void setColorToAll(Color color) {
for (int i = 0; i < graph.vertices.size(); ++i) {
GraphVertex curVertex = graph.vertices.get(i);
if (curVertex.showVertex) {
curVertex.color = color;
}
}
}
/**
* adjust frontColor
*/
public void adjustFrontColor(){
frontColor = new Color( 0xffffffff - backColor.getRGB() );
//problem when using gray: colors too close => hard to read
//ignore alpha
if( Math.abs(frontColor.getRed() - backColor.getRed()) < 10 &&
Math.abs(frontColor.getBlue() - backColor.getBlue()) < 10 &&
Math.abs(frontColor.getGreen() - backColor.getGreen()) < 10 ) {
frontColor = Color.BLACK;
}
}
/**
* the color of the text
* @return the color of the text
*/
public Color getWriteColor() {
return frontColor;
}
/**
* add a new cluster in the list
* @param clt
*/
public void addCluster(Cluster clt){
clusters.add(clt);
}
/**
* remove the cluster at the specified index
* @param index
*/
public void removeCluster(int index){
Cluster clt = clusters.get(index);
Cluster defaultClt = clusters.get(0);
int end = clt.size();
GraphVertex[] nodes = new GraphVertex[end];
for(int i = 0; i < end; ++i){
nodes[i] = clt.getNode(i);
}for(int i = 0; i < end; ++i){
defaultClt.addNode(nodes[i]);
}
clusters.remove(index);
}
/**
* return the cluster at the specified index
* @param index
* @return the cluster at the specified index
*/
public Cluster getCluster(int index){
if(index >= 0 && index < clusters.size()){
return clusters.elementAt(index);
}else{
return null;
}
}
/**
* return the cluster with the specified name
* @param name
* @return the cluster with the specified name
*/
public Cluster getCluster(String name){
Iterator<Cluster> it = clusters.iterator();
while(it.hasNext()){
Cluster clt = it.next();
if(clt.getName().equals(name)){
return clt;
}
}
return null;
}
/**
* get the number of cluster
* @return return the number of cluster
*/
public int getNbOfCluster(){
return clusters.size();
}
/**
* move the cluster at index one place higher in the list
* => cluster drawn sooner
* @param index
*/
public void moveClusterUp(int index){
if(index > 1){
Cluster tmp = clusters.get(index);
clusters.remove(index);
clusters.insertElementAt(tmp, index -1);
}
}
/**
* move the cluster at index one place lower in the list
* => drawn later (more on top)
* @param index
*/
public void moveClusterDown(int index){
if(index < clusters.size() -1 && index > 0){
Cluster tmp = clusters.get(index);
clusters.remove(index);
clusters.insertElementAt(tmp, index +1);
}
}
/**
* tells the cluster that the graph has changed => recompute some data
*
*/
public void refreshCluster(){
for(int i = 0; i < clusters.size(); ++i){
clusters.get(i).graphchanged();
}
}
/**
* Open the name of what is under the cursor as if it is an URL.
* @param p Coordinates
*/
public void openURL(Point p){
String targets = getNames(p);
int lght = targets.length();
if(lght <= 2){
return;
}
StringTokenizer st = new StringTokenizer(targets.substring(1,lght-1));
if(st.hasMoreTokens()){
String URL = st.nextToken();
if(URL.startsWith("\"") && URL.endsWith("\"")){
URL = URL.substring(1,URL.length()-1);
}
if(browser == null){
if(!guessBrowser(URL)){
System.err.println("Unable to find browser");
return;
}
}else{
String cmd[] = {browser,URL};
System.err.println("opening: "+URL);
try{
Runtime rt = Runtime.getRuntime();
rt.exec(cmd);
} catch (Throwable t){
t.printStackTrace();
}
}
}
}
private boolean guessBrowser(String URL){
String[] possibility = {
"firefox",
"mozilla",
"opera",
"safari",
"iexplorer",
"epiphany",
"konqueror"
};
for(int i = 0; i < possibility.length; ++i){
browser = possibility[i];
String cmd[] = {browser,URL};
try{
Runtime rt = Runtime.getRuntime();
rt.exec(cmd);
return true;
}catch(Throwable t){}
}
browser = null;
return false;
}
/**
* @return the display
*/
public ScreenDisplay getDisplay() {
return display;
}
};