/************************************************************************** * $Id: Display.java,v 1.1.1.1 2000/03/29 22:58:14 kkeys Exp $ * * File: Display.java Name: Display * Classes: Display,Hover_Check * Class: Display * Goal: Create a extended panel which will allow for display * and editing of nodes and lines. It will also displays messages * to the user throught a status bar which will appear on the * bottom of the display. * * Mode: effects how Display handles events and what message * is displayed in the status bar * ADD_NODE: will add nodes on mouse down events * ADD_LINK: will start a line on mouse down and finish it * on mouse up * SELECT: will the nearest object that is with in it size * PROPERT: Not current implemented * REMOVE: will remove the nearest object * FORMAT: set when all events are to be ignored * ROOT_SELECT: Not current implemented * Should allow the user to select a parent and all * nodes which are its children * MOVE: changed where the screen is current focused on mouse * down events * ZOOM: will zoom with centers the points mouse down event is occured * * and links. It will also allow a GUI for thier * creation, manipulation, and destruction * * Class: Hover_Check * Goal: To have a class which checks the x, y ever so often and * checks to see if the is a object near it and tells display to * print it. This means that it must be a thread. * * Written: Bradley Huffaker (12/17/97) * Modified: Jaeyeon Jung (2/20/97) * To make it display World Map * * For:Cooperative Association for Internet Data Analysis *************************************************************************** *************************************************************************** By accessing this software, DISPLAY, you are duly informed of and agree to be bound by the conditions described below in this notice: This software product, DISPLAY, is developed by Bradley Huffaker, and Jaeyeon Jung copyrighted(C) 1998 by the University of California, San Diego (UCSD), with all rights reserved. UCSD administers the NLANR Cache grants, NCR-9796082 and NCR-9616602, under which most of this code was developed. There is no charge for DISPLAY software. You can redistribute it and/or modify it under the terms of the GNU General Public License, v. 2 dated June 1991 which is incorporated by reference herein. DISPLAY is distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that the use of it will not infringe on any third party's intellectual property rights. You should have received a copy of the GNU GPL along with the DISPLAY program. Copies can also be obtained from http://www.gnu.org/copyleft/gpl.html or by writing to University of California, San Diego SDSC/CAIDA/NLANR 9500 Gilman Dr., MS-0505 La Jolla, CA 92093 - 0505 USA Or contact INFO@NLANR.NET **************************************************************************/ package bhuffake.plankton; import bhuffake.image.WorldProducer; import bhuffake.image.WorldProducerInterface; import java.net.*; import java.awt.image.FilteredImageSource; import java.io.*; import bhuffake.tools.Strings; import bhuffake.tools.StatusBar; import java.awt.*; import java.util.Date; import java.lang.Math; import java.awt.Dimension; public class Display extends Panel implements WorldProducerInterface, Runnable { // Used to display the current status and other messages protected StatusBar statusBar; // Flag for when to redraw the whole image or use the old copy protected boolean full_image_changed; // Flag for when to redraw the left image or use the old copy protected boolean left_image_changed; // Flag for when to redraw the right image or use the old copy protected boolean right_image_changed; // Flag to indicate redrawing the whole map when size is changed protected boolean size_changed; // Flag to indicate redrawing the whole image when mouse is down // at MOVE mode protected boolean mouse_down = false; // Drop redraw request when it is in the middle of drawing for SPEED protected boolean is_drawing = false; // Used to hold the offscreen image protected Image full_offscreen; protected int full_offscreen_height = 0; protected int full_offscreen_width = 0; // Used for displaying both image protected Image left_offscreen; protected int left_offscreen_height= 0; protected int left_offscreen_width = 0; protected Image right_offscreen; protected int right_offscreen_height = 0; protected int right_offscreen_width = 0; protected Image both_screen; protected int both_screen_height = 0; protected int both_screen_width = 0; // The Rectangle that contains all nodes protected int node_height = 0; protected int node_width = 0; // The net size of panel where node will be placed protected int width, height; // Map stuff - jjung protected double top_lat, top_lon; protected double bot_lat, bot_lon; protected double source_top_lat, source_top_lon; protected double source_bot_lat, source_bot_lon; protected Image image; protected Image source_image; protected Image default_image; protected boolean showMap = false; protected boolean viewGeo = false; protected boolean setMap = false; protected boolean stepBystep = false; WorldProducer worldProducer; protected int current_step; final double MAX_ZOOM = .003; double scaler; // List of nodes and links // List of selected nodes and links protected DList dlist; protected DList selected; protected DList ordered; // This handles mouse motion stuff protected DisplayObject mouse_down_selected = null; protected boolean mouse_dragged = false; // The date of the file protected Date date = null; static final public int FULL = 0; static final public int RIGHT = 1; static final public int LEFT = 2; // Holds the mode that the display is current in protected int mode; static final public int ADD_NODE = 0; static final public int ADD_LINK = 1; static final public int SELECT = 2; static final public int ROOT_SELECT = 3; static final public int SELECT_TREE = 4; static final public int PROPERT = 5; static final public int REMOVE = 6; static final public int FORMAT = 7; static final public int ANIMATE = 9; static final public int ZOOM = 10; static final public int STEP_BY_STEP = 11; static final public int MOVE = 12; static final public String ADD_NODE_STR = "Add Node"; static final public String ADD_LINK_STR = "Add Link"; static final public String SELECT_STR = "Select Object"; static final public String ROOT_SELECT_STR = "Select Root"; static final public String SELECT_TREE_STR = "Select Tree"; static final public String PROPERT_STR = "Properties"; static final public String REMOVE_STR = "Remove Object"; static final public String FORMAT_STR = "Formating Nodes"; static final public String ANIMATE_STR = "Animating"; static final public String ZOOM_STR = "Zoom"; static final public String STEP_BY_STEP_STR = "Step by Step"; static final public String MOVE_STR = "Move"; // Holds the current resolution of the objects protected int res; static final public int LOW = 0; static final public int HIGH = 1; static final public String LOW_STRING = "Low Res"; static final public String HIGH_STRING = "High Res"; // Old x and Old y are used in a mouse drag to find the change // and move all the selected objects by that change int old_x, old_y; // Used in the handleEvent to tell if the user is hovering over // a object rather just scanning to it. int MIN_MOUSE_HOVER = 10; // This is used by the addlink methods to add a link between // two nodes Node addlink_node; Link addlink_link; // Color by HTTP or DOMAIN static final public int COLOR_HTTP = 0; static final public int COLOR_DOMAIN = 1; protected boolean viewBoth = false; // This allows for mouse motion int x_shift = 0; int y_shift = 0; // This limits the number to times it checks for closeness Check_Hover check_hover; // Keeps track of the number of files are current being used. int num_files = 1; int current_fileIndex =0; // ------- Animation Stuff ----------------------------- // A array of images one for each file. Which it then flips throw // for the animation. Image[] slids; // The controlling thread Thread myThread; // The sleep time long sleep_length= 1000; // the number of times to go throught the slid show; int slid_times = 1; // The animator class DisplayInterface animator; // Color stuff ColorMaskFrame colorMaskFrame; public Display() { setBackground(Color.white); statusBar = new StatusBar(); printMessage(""); dlist = new DList(); selected = new DList(); check_hover = new Check_Hover(this); check_hover.start(); setViewBoth(false); width = size().width; height = size().height - statusBar.getHeight(); full_image_changed = true; } public Display(Image image, double top_lat_input, double top_lon_input, double bot_lat_input, double bot_lon_input) { this(); source_image = image; source_top_lat = top_lat_input; source_top_lon = top_lon_input; source_bot_lat = bot_lat_input; source_bot_lon = bot_lon_input; } /******************************************************** * Purpose: To set up the shift created by the scroll bars * Currently not implement because Plankton can't seem * to get the scoll bars to work * *********************************************************/ public void Shift_X(float shift) {} public void Shift_Y(float shift) {} /******************************************************** * Purpose: To set the current mode and display it in the * status bar. *********************************************************/ public void setMode(int mode_type) { mode = mode_type; switch (mode) { case ADD_NODE: statusBar.setString(ADD_NODE_STR,0); break; case ADD_LINK: statusBar.setString(ADD_LINK_STR,0); break; case SELECT: statusBar.setString(SELECT_STR,0); break; case ROOT_SELECT: statusBar.setString(ROOT_SELECT_STR,0); break; case SELECT_TREE: statusBar.setString(SELECT_TREE_STR,0); break; case PROPERT: statusBar.setString(PROPERT_STR,0); break; case REMOVE: statusBar.setString(REMOVE_STR,0); break; case FORMAT: statusBar.setString(FORMAT_STR,0); break; case ANIMATE: statusBar.setString(ANIMATE_STR,0); break; case ZOOM: statusBar.setString(ZOOM_STR,0); break; case STEP_BY_STEP: statusBar.setString(STEP_BY_STEP_STR,0); break; case MOVE: statusBar.setString(MOVE_STR,0); break; default: mode = MOVE; statusBar.setString(MOVE_STR,0); } repaintStatusBar(); } /******************************************************** * Purpose: To get the current mode *********************************************************/ public int getMode() { return mode; } /******************************************************** * Purpose: To set the current resolution. *********************************************************/ public void setRes(int res_type) { setResNoPaint(res_type); repaintNodes(); } /******************************************************** * Purpose: To set the current resolution. *********************************************************/ public void setResNoPaint(int res_type) { res = res_type; boolean little_on = false; if (res == LOW) { statusBar.setString(LOW_STRING,1); little_on = true; } else { statusBar.setString(HIGH_STRING,1); little_on = false; } if (dlist == null) return; DList mylist = dlist.DList(); mylist.reset(); while(!mylist.end()) mylist.next().setLittle(little_on); repaintStatusBar(); } /******************************************************** * Purpose: resize of the box *********************************************************/ public void mapSizeChange() { if(image == null) return; if (worldProducer == null ) { worldProducer = new WorldProducer(source_image, top_lon, top_lat, bot_lon, bot_lat, width, height); worldProducer.setParent(this); } else worldProducer.setSize(width,height); image = createImage(worldProducer); full_image_changed = true; right_image_changed = true; size_changed = true; } /******************************************************** * Purpose: Allow for other Classes to print things in the * Status bar. *********************************************************/ public void printMessage(String message) { statusBar.setString(message,2); repaintStatusBar(); } /******************************************************** * Purpose: Gets the current string in the status Bar *********************************************************/ public String getMessage() { return(statusBar.getString(2)); } /******************************************************** * Purpose: Sets the current display list (ie the list of * nodes and lines) *********************************************************/ public void setList(DList list) { setListNoPaint(list); repaintNodes(); } /******************************************************** * Purpose: Sets the current display list (ie the list of * nodes and lines) with out redrawing the image * used when loading. *********************************************************/ public void setListNoPaint(DList list) { dlist = list; if (colorMaskFrame != null) colorMaskFrame.setList(dlist); selected.clear(); full_image_changed = true; left_image_changed = true; right_image_changed = true; } /******************************************************** * Purpose:Sets the date for the current list *********************************************************/ public void setDate(Date date_input) { date = date_input; } public void setMap() { top_lat = source_top_lat; top_lon = source_top_lon; bot_lat = source_bot_lat; bot_lon = source_bot_lon; // bhuffake To remove the possible of crash caused // by lack of image. if (source_image != null) { worldProducer = new WorldProducer(source_image, top_lon, top_lat,bot_lon, bot_lat, width,height); worldProducer.setParent(this); image = createImage(worldProducer); default_image = image; size_changed = false; setMap = true; } } public void showAllNames() { showNames(true); } public void showRootNames() { showNames(false); } protected void showNames(boolean showAll) { DList mylist = dlist.DList(); mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) { if (showAll || ((Node)object).isRoot()) ((Node)object).showName(); else ((Node)object).hideName(); } } repaintNodes(); } public void hideNames() { DList mylist = dlist.DList(); mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) ((Node)object).hideName(); } repaintNodes(); } public void setShowMap(boolean flag) { showMap = flag; } public boolean isShowMap() { return showMap; } public void setViewGeo(boolean flag) { viewGeo = flag; } public boolean isViewGeo() { return viewGeo; } public void setViewBoth(boolean flag) { if(viewBoth && !flag) width = size().width; if(!viewBoth && flag) width = size().width / 2; size_changed = true; viewBoth = flag; } public boolean isViewBoth() { return viewBoth; } public void setMap(boolean flag) { setMap = flag; } public boolean isSetMap() { return setMap; } public void RootORGeo() { DList mylist = dlist.DList(); mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); object.onMap(isViewGeo()); if (object instanceof Node ) { Node node = (Node)object; if (isViewGeo()) { node.setToGeoXYNoFlag(); } else { // This made it so that the objects // could not be moved. But is required // To make this zoom. Needs to be // fixed. // Braldey Huffaker node.setToRootXYNoFlag(); } } } } public void forcedDraw() { is_drawing = false; } /******************************************************** * Purpose: *********************************************************/ public DList getList() { return dlist; } /******************************************************** * Purpose: *********************************************************/ public DList getOrderedList() { return ordered; } public void setOrderedList(DList list) { ordered = list; } /******************************************************** * Purpose:To select which fileIndex to use *********************************************************/ public void setFileIndex(int fileIndex) { setFileIndexNoPaint(fileIndex); if (colorMaskFrame != null && colorMaskFrame.inUse()) colorMaskFrame.Color(); else repaintNodes(); } /******************************************************** * Purpose:To select which fileIndex to use *********************************************************/ public void setFileIndexNoPaint(int fileIndex) { current_fileIndex = fileIndex; DList mylist = dlist.DList(); mylist.reset(); while(!mylist.end()) { DisplayObject object = mylist.next(); object.setFileIndex(fileIndex); } } /******************************************************** * Purpose:To set the number of files current in use *********************************************************/ public void setNumFiles(int numFiles) { num_files = numFiles; } /******************************************************** * Purpose:To get the number of files the list has *********************************************************/ public int getNumFiles() { return num_files; } /******************************************************** * Purpose: *********************************************************/ public void deselectAll() { while (!selected.empty()) selected.pop().setSelect(false); while (!nearest_objects.empty()) nearest_objects.pop().setHighlight(false); repaintNodes(); } /******************************************************** * Purpose:To take in a string find all nodes that contain * that string in their name *********************************************************/ public void selectByName(String string) { printMessage("Searching ..."); char[] array = string.toCharArray(); // Remove white space at the front int start = 0; while (start < array.length-1 && (array[start] == ' ' || array[start] == '\t')) start++; // Remove white space at the end and check to see // if it is a ip address int end = start; while (end < array.length && array[end] != ' ' && array[end] != '\t') end++; string = new String(array,start,end-start); while (!selected.empty()) selected.pop().setSelect(false); DList mylist = dlist.DList(); mylist.reset(); boolean found = false; int num_found = 0; while(!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) { Node node = (Node) object; if (node.getName().indexOf(string) >= 0) { found = true; node.setRoot(true); //node.setSelect(true); //selected.push(node); num_found++; } else node.setRoot(false); } } if (!found) printMessage("Unable to find any nodes"); else printMessage("Nodes matched:"+ num_found); repaintNodes(); } public void lineSize(int size_input) { int min = size_input; DList mylist = dlist.DList(); mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Link) { int old = object.getSize(); if (old + size_input <= 0) { if(old -1 < (-1*min)) min = 1 - old; } } } mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Link) { int old = object.getSize(); object.setSize(min+old); } } if (min != size_input) printMessage("Link size can't be smaller than 0 "); else printMessage(""); repaintNodes(); } public void nodeSize(int size_input) { int min = size_input; DList mylist = dlist.DList(); mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) { int old = object.getSize(); if (old + size_input <= 0) { if(old -1 < (-1*min)) min = 1 - old; } } } mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) { int old = object.getSize(); object.setSize(min+old); } } if (min != size_input) printMessage("Node size can't be smaller than 0 "); else printMessage(""); repaintNodes(); } public void rotate(double degree_input) { double radian = (degree_input/180) * Math.PI; double theta; double x,y; int[] root_xy; int[] center_xy = centerRootXY(); double r; DList mylist = dlist.DList(); mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) { root_xy = ((Node)object).getRootXY(); r = Math.sqrt (Math.pow((double) (root_xy[0]-center_xy[0]), (double)2)+ Math.pow((double) (root_xy[1]-center_xy[1]), (double)2)); double delta_x = (double)(root_xy[0]- center_xy[0]); double delta_y = (double)(root_xy[1]- center_xy[1]); if(delta_x==0) { if(delta_y > 0) theta = Math.PI/2; else theta = 3*Math.PI/2; } else { theta = Math.atan(delta_y/delta_x); if(delta_x > 0) theta = 2*Math.PI+theta; else theta = Math.PI+theta; } x = r * Math.cos(theta+radian) + center_xy[0]; y = r * Math.sin(theta+radian) + center_xy[1]; ((Node)object).setRootXY((int)x,(int)y); } } if(isViewBoth()) repaintLeftNodes(); else repaintNodes(); } /******************************************************** * Purpose:To scale all the nodes xy by some scaler *********************************************************/ public void scale(double scaler_input) { scaler = scaler_input; printMessage("Click the point you are in terested"); /* if (isViewGeo()) { scaler = 1/scaler_input; printMessage("Click the point you are in terested"); } else { scaler = scaler_input; printMessage("Click the point you are in terested"); //printMessage("Zooming ... Please wait"); //zoomNoMap(scaler_input); //printMessage(""); //recenter(); //repaintNodes(); } */ } public void zoom(double scaler_input) { double lon = (top_lon + bot_lon)/2; double lat = (top_lat + bot_lat)/2; zoom(lon,lat,scaler_input); } public void zoom(double t_lon, double t_lat, double b_lon, double b_lat) { while(t_lon >= b_lon) b_lon += 360; double lon = (t_lon + b_lon)/2; double lat = (t_lat + b_lat)/2; double scaler = (b_lon - t_lon)/(bot_lon - top_lon); zoom(lon,lat,scaler); } public void zoom(int x, int y, double scaler_input) { double latlong[] = FromMap(x,y); if (isViewGeo()) zoom(latlong[1],latlong[0],scaler_input); else { printMessage("Zooming ... Please wait"); zoomNoMap(scaler_input); recenter(); } } public void zoom(double m_lon, double m_lat, double scaler_input) { double lat_size, lon_size; if (top_lat < bot_lat) lat_size = (bot_lat - top_lat) * scaler_input; else lat_size = (top_lat - bot_lat) * scaler_input; if (top_lon < bot_lon) lon_size = (bot_lon - top_lon) * scaler_input; else lon_size = (top_lon - bot_lon) * scaler_input; if (lon_size/360 < MAX_ZOOM) return; if (lat_size >180) { lon_size = lon_size* 180/lat_size; lat_size = 180; } if (lon_size >360) { lat_size = lat_size* 360/lon_size; lon_size = 360; } double t_lat = m_lat + lat_size/2; double t_lon = m_lon - lon_size/2; double b_lat = m_lat - lat_size/2; double b_lon = m_lon + lon_size/2; while (t_lon < -180) { t_lon += 360; b_lon += 360; } while (t_lon > 180) { t_lon -= 360; b_lon -= 360; } while (t_lat > 90) { b_lat -= t_lat -90; t_lat = 90; } while (b_lat < -90) { t_lat -= b_lat +90; b_lat = -90; } top_lon = t_lon; top_lat = t_lat; bot_lon = b_lon; bot_lat = b_lat; if(isShowMap()|| isViewBoth()) { printMessage("Loading Image, Please wait...."); if (worldProducer != null) worldProducer.abort(); worldProducer.setBox(top_lon,top_lat,bot_lon,bot_lat); image = createImage(worldProducer); if(size_changed) mapSizeChange(); } ChangeGeoXY(); } public void ChangeGeoXY() { DList mylist = dlist.DList(); mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) { double lat = object.getLatitude(); double lon = object.getLongitude(); int[] xy = ToMap(lat,lon); ((Node)object).setGeoXY(xy[0],xy[1]); } } } public void ChangeRootXY() { int[] minmax = MinMaxRootXY(); int min_x = minmax[0]; int min_y = minmax[1]; int max_x = minmax[2]; int max_y = minmax[3]; if (max_x*max_y*width*height !=0) { node_width = width + 10; node_height = height + 10; if (max_x > width || max_y > height) { if((double)max_x/width > (double)max_y/height) zoomNoMap((double)width/max_x); else zoomNoMap((double)height/max_y); } else { if((double)width/max_x > (double)height/max_y) zoomNoMap((double)height/max_y); else zoomNoMap((double)width/max_x); } } recenter(); } protected int[] MinMaxRootXY () { DList mylist = dlist.DList(); mylist.reset(); int min_x = 10; int min_y = 20; int max_x = 0; int max_y = 0; int[] minmax = new int[4]; while(!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) { int[] xy = ((Node)object).getRootXY(); if (xy[0] < min_x) min_x = xy[0]; if (xy[0] > max_x) max_x = xy[0]; if (xy[1] < min_y) min_y = xy[1]; if (xy[1] > max_y) max_y = xy[1]; } } if (min_x != 10 || min_y != 20) { mylist.reset(); min_x = 10 - min_x; min_y = 20 - min_y; while(!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) ((Node)object).changeRootXY(min_x,min_y); } max_x += min_x; max_y += min_y; } minmax[0] = min_x; minmax[1] = min_y; minmax[2] = max_x; minmax[3] = max_y; return minmax; } public void zoomNoMap(double scaler_input) { DList mylist = dlist.DList(); mylist.reset(); while (!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) ((Node)object).scaleRootXY(scaler_input); } } public int[] ToMap(double lat, double lon) { int[] xy = new int[2]; double boundary = (top_lon + bot_lon)/2 - 180; while (lon < boundary ) lon += 360.0; xy[0] = (int) ( ((lon - top_lon) * width) / (bot_lon - top_lon)); xy[1] = (int) ( ((top_lat - lat) * height) / (top_lat - bot_lat)); return xy; } public double[] FromMap(int x, int y) { double[] node_lat_lon = new double[2]; node_lat_lon[0] = top_lat - ((double) y * (top_lat - bot_lat) / (double) height); node_lat_lon[1] = top_lon + ((double) x * (bot_lon - top_lon) / (double) width); return node_lat_lon; } /******************************************************** * Purpose:To display all objects step by step *********************************************************/ public void stepBystepNoPaint(boolean flag) { stepBystep = flag; } public void stepBystep(boolean flag) { stepBystep = flag; repaintNodes(); } public void stepBystep(int current_step_input) { stepBystep = true; current_step = current_step_input; repaintNodes(); } public boolean isStepByStep() { return stepBystep; } /******************************************************** * Purpose:To color all objects by there top level domain *********************************************************/ public void colorByMask(int http_domain) { if (colorMaskFrame != null) colorMaskFrame.hide(); switch (http_domain) { case COLOR_HTTP: colorMaskFrame = new ColorMaskFrame(this,dlist, num_files,current_fileIndex, http_domain, "Mask/Color by HTTP Request"); break; case COLOR_DOMAIN: colorMaskFrame = new ColorMaskFrame(this,dlist, num_files,current_fileIndex, http_domain, "Mask/Color by Top Level Domain Name"); break; } colorMaskFrame.inUse(true); colorMaskFrame.toFront(); colorMaskFrame.show(); colorMaskFrame.Color(); } /******************************************************* * Purpose: To color by defualt colors *******************************************************/ public void colorByDefault() { DList mylist = dlist.DList(); mylist.reset(); while(!mylist.end()) mylist.next().setToDefaultColor(); printMessage("Colored by Default"); repaintNodes(); } /********************************************************* * Purpose:To paint out the current date of the network **********************************************************/ public void paintDate() { String string = "none given"; if (date != null) { string = "Network date: " +date.getDate()+"/"; switch (date.getMonth()) { case 1: string = string + "JAN"; break; case 2: string = string + "FEB"; break; case 3: string = string + "MAR"; break; case 4: string = string + "APR"; break; case 5: string = string + "MAY"; break; case 6: string = string + "JUN"; break; case 7: string = string + "JUL"; break; case 8: string = string + "AGU"; break; case 9: string = string + "SEP"; break; case 10: string = string + "OCT"; break; case 11: string = string + "NOV"; break; case 12: string = string + "DEC"; break; } string = string + "/" + (date.getYear() + 1900); } printMessage("Network date: "+string); } /******************************************************** * Purpose: *********************************************************/ public void Clear() { dlist.clear(); colorByDefault(); printMessage("Network Cleared"); } /******************************************************** * Purpose: *********************************************************/ public void paint(Graphics g) { Dimension dim = size(); FontMetrics fm = getFontMetrics(getFont()); statusBar.draw(g,dim,fm); paintNodes(g, dim); } /******************************************************** * Purpose: *********************************************************/ public void repaintStatusBar() { Font font = getFont(); // This checks to see if the font has been initialized if (font != null) { Graphics g = getGraphics(); Dimension dim = size(); FontMetrics fm = getFontMetrics(font); statusBar.draw(g,dim,fm); } } /******************************************************** * Purpose: *********************************************************/ public void repaintNodes() { full_image_changed = true; left_image_changed = true; right_image_changed = true; paintNodes(getGraphics(), size()); } public void repaintLeftNodes() { left_image_changed = true; paintNodes(getGraphics(), size()); } public void repaintRightNodes() { right_image_changed = true; paintNodes(getGraphics(), size()); } /******************************************************** * Purpose: ruturn the dimension for nodes and links (with extracting the area for statusBar) *********************************************************/ public Dimension displaySize() { return new Dimension(width,height); } protected void paintNodes(Graphics g, Dimension dim) { Image half_screen = null; int x_shift_paint = x_shift; int y_shift_paint = y_shift; if (is_drawing){ printMessage("Please wait, it is busy drawing images now...."); return; } is_drawing = true; printMessage("Drawing images...."); if ((isViewBoth() && (width != dim.width/2 || height != dim.height - statusBar.getHeight())) || (!isViewBoth() && (width != dim.width || height != dim.height - statusBar.getHeight()))) { size_changed = true; full_image_changed = true; left_image_changed = true; right_image_changed = true; if (isViewBoth()) width = dim.width/2; else width = dim.width; height = dim.height - statusBar.getHeight(); if(isShowMap() || isViewBoth()) mapSizeChange(); ChangeGeoXY(); ChangeRootXY(); } // Redraw the offscreen image (ie that complete image) // if you have to if(!isViewBoth() && full_image_changed) { CheckRootNodeSize(); redrawOffscreen(FULL); } if (isViewBoth() &&left_image_changed) { setShowMap(false); setViewGeo(false); CheckRootNodeSize(); redrawOffscreen(LEFT); } if (isViewBoth() &&right_image_changed) { setShowMap(true); setViewGeo(true); redrawOffscreen(RIGHT); } int statusBarheight = statusBar.getHeight(); int clip_height = dim.height - statusBarheight; // Redraw from the original image onto the clipped image g.clipRect(0,0,dim.width,clip_height); g.setColor(getBackground()); if(!isViewBoth()) { if (isViewGeo()) { x_shift_paint = 0; y_shift_paint = 0; } g.drawImage(full_offscreen,x_shift_paint,y_shift_paint,this); // Not to fill background with bizarre color g.setColor(getBackground()); if (x_shift_paint > 0) g.fillRect(0,0,x_shift_paint,clip_height); if (dim.width > full_offscreen_width + x_shift_paint) { int erase_x = full_offscreen_width + x_shift_paint; int erase_width = dim.width - erase_x; g.fillRect(erase_x,0,erase_width,clip_height); } if (clip_height > full_offscreen_height + y_shift_paint) { int erase_y = full_offscreen_height + y_shift_paint; int erase_height = clip_height - erase_y; g.fillRect(0,erase_y,width,erase_height); } } else { //to draw the offscreen image on only half of the screen //at the expense of speed //otherwise, it produce weird images sometimes if(half_screen == null) half_screen = createImage(width,height); Graphics half_graphics = half_screen.getGraphics(); half_graphics.clipRect(0,0,width,height); half_graphics.setColor(getBackground()); half_graphics.fillRect(0,0,width,height); half_graphics.drawImage(left_offscreen,x_shift_paint,y_shift_paint,this); g.drawImage(half_screen,0,0,this); //g.drawImage(left_offscreen,x_shift_paint,y_shift_paint,this); // Not to fill background with bizarre color g.setColor(getBackground()); if (x_shift_paint > 0) g.fillRect(0,0,x_shift_paint,clip_height); if (width > left_offscreen_width + x_shift_paint) { int erase_x = left_offscreen_width + x_shift_paint; int erase_width = width - erase_x; g.fillRect(erase_x,0,erase_width,clip_height); } if (clip_height > left_offscreen_height + y_shift_paint) { int erase_y = left_offscreen_height + y_shift_paint; int erase_height = clip_height - erase_y; g.fillRect(0,erase_y,width,erase_height); } half_screen.flush(); half_graphics.setColor(getBackground()); half_graphics.fillRect(0,0,width,height); half_graphics.drawImage(right_offscreen,0,0,this); g.drawImage(half_screen,width,0,this); //g.drawImage(right_offscreen,dim.width/2,0,this); } // The following four ifs take care of the times when the image // of the network is shifted off the screen. leaving blank space. if (y_shift_paint > 0) g.fillRect(0,0,width,y_shift_paint); if (statusBarheight <= 0) { g.clipRect(0,0,dim.width,dim.height); FontMetrics fm = getFontMetrics(getFont()); statusBar.draw(g,dim,fm); } printMessage(""); is_drawing = false; } protected void drawStepByStep(Graphics g) { DList ordered_mylist; ordered_mylist = ordered.DList(); int rank = -1; int count = ordered_mylist.count(); //when reverse order is needed //int index = count; int index = 0; Node[] nodes = new Node[count]; DList links = new DList(); DList node_list = new DList(); ordered_mylist.reset(); while(!ordered_mylist.end()) { DisplayObject object = ordered_mylist.next(); if(object instanceof Node) { Node node = (Node)object; if(object.exists()) nodes[index++] = node; //when reverse order is needed // nodes[--index] = node; } } DList mylist = dlist.DList(); mylist.reset(); while(!mylist.end()) mylist.next().hide(); int node_rank = 0; for(int i = 0; i < index; i ++) { node_rank = nodes[i].getRank(); if(node_rank > current_step) { if(current_step > 0) printMessage ("Current level: " +current_step); else if (current_step == 0) printMessage ("Click [Next] to start"); break; } if(rank != node_rank && i > 0 ) { links.reset(); while(!links.empty()) { DisplayObject object = links.pop(); if(object.exists()) { object.show(); //System.err.println(((Link)object).getString()); object.draw(g,displaySize(),getLatLon()); } //(links.pop()).draw(g,displaySize(),getLatLon()); } node_list.reset(); while(!node_list.end()) { DisplayObject object = node_list.next(); if(object.exists()) { object.show(); object.draw(g,displaySize(),getLatLon()); } //(node_list.next()).draw(g,displaySize(),getLatLon()); } } else { DList node_link = nodes[i].getLink(); node_link.reset(); while(!node_link.end()) links.enqueue(node_link.next()); } nodes[i].show(); nodes[i].draw(g,displaySize(),getLatLon()); node_list.enqueue(nodes[i]); rank = node_rank; } if (node_rank <= current_step) printMessage ("The end of hierarchy"); //stepBystepNoPaint(false); full_image_changed = true; left_image_changed = true; right_image_changed = true; } protected void redrawOffscreen(int which_screen) { Image offscreen = null; int offscreen_width = 0; int offscreen_height = 0; boolean image_changed = false; switch (which_screen) { case FULL: offscreen = full_offscreen; offscreen_width = full_offscreen_width; offscreen_height = full_offscreen_height; image_changed = full_image_changed; break; case LEFT: offscreen = left_offscreen; offscreen_width = left_offscreen_width; offscreen_height = left_offscreen_height; image_changed = left_image_changed; break; case RIGHT: offscreen = right_offscreen; offscreen_width = right_offscreen_width; offscreen_height = right_offscreen_height; image_changed = right_image_changed; break; } if (node_width < width) node_width = width; if (node_height < height) node_height = height; if (offscreen == null || node_width > offscreen_width || node_height > offscreen_height) { image_changed = true; if (node_width > offscreen_width) offscreen_width = node_width; if (node_height > offscreen_height) offscreen_height = node_height; if (offscreen != null) offscreen.flush(); offscreen = createImage(offscreen_width ,offscreen_height); } if(!image_changed) return; Graphics g = offscreen.getGraphics(); FontMetrics fm = getFontMetrics(getFont()); // Need to clear the slate clear g.setColor(getBackground()); g.fillRect(0,0,offscreen_width,offscreen_height); if (image != null && isShowMap()) g.drawImage(image,0,0,this); if (which_screen == LEFT) setViewGeo(false); else if (which_screen == RIGHT) setViewGeo(true); RootORGeo(); DList mylist; if(!this.isStepByStep()) { mylist = dlist.DList(); mylist.reset(); while(!mylist.end()) { DisplayObject object = mylist.next(); object.draw(g,displaySize(),getLatLon()); } } else drawStepByStep(g); switch (which_screen) { case FULL: full_offscreen = offscreen; full_offscreen_width = offscreen_width; full_offscreen_height = offscreen_height; full_image_changed = false; break; case LEFT: left_offscreen = offscreen; left_offscreen_width = offscreen_width; left_offscreen_height = offscreen_height; left_image_changed = false; break; case RIGHT: right_offscreen = offscreen; right_offscreen_width = offscreen_width; right_offscreen_height = offscreen_height; right_image_changed = false; break; } } /*********************************************************** * Purpose: to find out if any of the nodes will be draw outside * of the offscreen image. If so it finds where the * new bondray should be. ************************************************************/ protected void CheckRootNodeSize() { int[] minmax = MinMaxRootXY(); int min_x = minmax[0]; int min_y = minmax[1]; int max_x = minmax[2]; int max_y = minmax[3]; if (max_x > node_width - 10 || max_y > node_height - 10) { node_width = max_x + 10; node_height = max_y + 10; } } /******************************************************** * Purpose: *********************************************************/ public void recenter() { // It doesn't make sense when nodes are formatted with geo if (isViewGeo()) return; CheckRootNodeSize(); /* boolean set = false; int xy[] = null; int num_nodes = 0; int center_x = 0; int center_y = 0; DList mylist = dlist.DList(); mylist.reset(); while(!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) { num_nodes++; if (set) { xy = ((Node)object).getRootXY(); center_x += xy[0]; center_y += xy[1]; } else { xy = ((Node)object).getRootXY(); center_x = xy[0]; center_y = xy[1]; set = true; } } } if (num_nodes > 0) */ int[] center_xy = centerRootXY(); int center_x = center_xy[0]; int center_y = center_xy[1]; if (center_x >= 0 && center_y >= 0) { Dimension dim = displaySize(); x_shift = dim.width/2 - center_x; y_shift = dim.height/2- center_y; //x_shift = dim.width/2 - (center_x/num_nodes); //y_shift = dim.height/2- (center_y/num_nodes); } } public int[] centerRootXY() { boolean set = false; int xy[] = null; int num_nodes = 0; int center_xy[] = new int[2]; int center_x = 0; int center_y = 0; DList mylist = dlist.DList(); mylist.reset(); while(!mylist.end()) { DisplayObject object = mylist.next(); if (object instanceof Node) { num_nodes++; if (set) { xy = ((Node)object).getRootXY(); center_x += xy[0]; center_y += xy[1]; } else { xy = ((Node)object).getRootXY(); center_x = xy[0]; center_y = xy[1]; set = true; } } } if (num_nodes > 0) { center_xy[0] = center_x/num_nodes; center_xy[1] = center_y/num_nodes; return center_xy; } center_xy[0] = -1; center_xy[1] = -1; return center_xy; } /******************************************************** * Purpose:To handle the mouse events *********************************************************/ Date last_move_date = new Date(); public boolean handleEvent(Event event) { // Shifts the give x,y by the shift int x_shift_handle_event = x_shift; int y_shift_handle_event = y_shift; boolean event_at_right = false; if (isViewBoth() && event.x > width) { event.x = event.x - width; x_shift_handle_event = 0; y_shift_handle_event = 0; event_at_right = true; } else if (!isViewBoth() && isViewGeo()) { x_shift_handle_event = 0; y_shift_handle_event = 0; event_at_right = true; } else event_at_right = false; event.x -= x_shift_handle_event; event.y -= y_shift_handle_event; if (mode == FORMAT || mode == ANIMATE) return super.handleEvent(event); switch(event.id) { case Event.ACTION_EVENT: if (event.target == colorMaskFrame) { if (colorMaskFrame.isHide()) colorMaskFrame.inUse(false); repaintNodes(); } break; case Event.MOUSE_MOVE: //if (mode != ZOOM && mode != STEP_BY_STEP ) { if (mode != ZOOM ) { check_hover.set(event.x,event.y,event_at_right); } break; case Event.MOUSE_DOWN: old_x = event.x; old_y = event.y; if (mode == ADD_NODE) AddNode(event.x, event.y); else if (mode == ADD_LINK) AddLink(event.x,event.y); else if (mode == SELECT || mode == SELECT_TREE) DownSelect(event.x,event.y); else if (mode == PROPERT) Propert(event.x,event.y); else if (mode == REMOVE) Remove(event.x,event.y); else if (mode == ROOT_SELECT) RootSelect(event.x,event.y); else if (mode == ZOOM) { if(isViewBoth()) { if(event_at_right) setViewGeo(true); else setViewGeo(false); } if(isViewGeo() && scaler !=0) scaler = 1/scaler; zoom(event.x,event.y,scaler); if (isViewBoth()) { if(event_at_right) repaintRightNodes(); else repaintLeftNodes(); } else repaintNodes(); printMessage(""); setMode(MOVE); } else { mouse_down = true; check_hover.set(); } break; case Event.MOUSE_DRAG: mouse_dragged = true; if (mode == ADD_LINK) AddLink_Drag(event.x,event.y); else if (mode == SELECT || mode == ADD_NODE || mode == SELECT_TREE) ChangeXY(event.x, event.y); else if (mode != ZOOM || mode == MOVE) { if(isViewBoth()) { if(event_at_right) setViewGeo(true); else setViewGeo(false); } Move(event.x,event.y); } break; case Event.MOUSE_UP: if (mode == ADD_LINK) AddLink_Up(event.x,event.y); else if (mode == SELECT || mode == SELECT_TREE) UpSelect(event.x,event.y); break; } return super.handleEvent(event); } /************************************************** * Purpose: To display the objects information * * nearest_object: is used to reduce needless redrawing * of the information ***************************************************/ DList nearest_objects = new DList(); public void GetString(int x, int y) { if (x > size().width || y > size().height) return; DList objects = nearObjects(x,y); String answer = new String(); boolean equal = true; objects.reset(); while (!objects.end() && equal) { if(!nearest_objects.exists(objects.next())) equal = false; } if (equal && !mouse_down) return; nearest_objects.reset(); while (!nearest_objects.end()) { DisplayObject old = nearest_objects.next(); if (old.isHighlighted()) { //remove old.setHighlight(false); nearest_objects.drop(old); } } if (objects.empty()) printMessage(null); else { objects.reset(); while(!objects.end()) { DisplayObject object = objects.next(); if (answer.length() == 0) answer = object.getString(); else answer = answer + " : " + object.getString(); //remove if(mouse_down) object.setHighlight(true); } //remove if(mouse_down) { repaintNodes(); mouse_down = false; } printMessage(answer); } nearest_objects = objects; } /******************************************************** * Purpose: *********************************************************/ int counter = 0; public void AddNode(int x, int y) { DisplayObject object = near(x,y); // Don't allow nodes to be added on top of each other // If some does attempt to do so. Toggle the node // to Selected = !Selected if (object instanceof Node) { if (object.isSelected()) { object.setSelect(false); selected.drop(object); } else { object.setSelect(true); selected.add(object); } repaintNodes(); return; } Node node = new Node("Node "+ counter++,x,y); node.setSelect(true); dlist.add(node); selected.add(node); printMessage(node.getString()); repaintNodes(); } /******************************************************** * Purpose: *********************************************************/ public void AddLink(int x, int y) { DisplayObject object = near(x,y); if (object instanceof Node) { addlink_node = new Node("AddLink Node",x,y); addlink_link = new Link((Node)object,addlink_node,Link.ONE); dlist.add(addlink_link); } else { addlink_node = null; addlink_link = null; } full_image_changed = true; repaintNodes(); } /******************************************************** * Purpose: *********************************************************/ public void AddLink_Drag(int x, int y) { if (addlink_node != null && addlink_link != null) addlink_node.setXY(x,y); full_image_changed = true; repaintNodes(); } /******************************************************** * Purpose: *********************************************************/ public void AddLink_Up(int x, int y) { DisplayObject object = near(x,y); if (addlink_link != null && object instanceof Node && addlink_link.getParent() != object) addlink_link.setChild((Node)object); else dlist.drop(addlink_link); addlink_link = null; addlink_node = null; full_image_changed = true; repaintNodes(); } /******************************************************** * Purpose: *********************************************************/ public void DownSelect(int x, int y) { mouse_down_selected = near(x,y); mouse_dragged = false; } /******************************************************** * Purpose: *********************************************************/ public void UpSelect(int x, int y) { if (mouse_dragged) return; int near_distance = -1; int distance; DisplayObject object = mouse_down_selected; if (object instanceof Node) { if (mode == SELECT) { if (object.isSelected()) { object.setSelect(false); selected.drop(object); } else { object.setSelect(true); selected.enqueue(object); } } else ((Node)object).selectTree(!object.isSelected() ,selected); // Makes sure the the current state of the object // is correct full_image_changed = true; repaintNodes(); } if (object != null) printMessage(object.getString()); } /******************************************************** * Purpose: *********************************************************/ public void Propert(int x, int y) {} /******************************************************** * Purpose: *********************************************************/ public void Remove(int x,int y) { DisplayObject object = near(x,y); if (object != null) { selected.drop(object); dlist.drop(object); DisplayObject[] objects = object.remove(); for (int index=0; index < objects.length; index++) { object = objects[index]; object.remove(); dlist.drop(object); selected.drop(object); } full_image_changed = true; repaintNodes(); } } /******************************************************** * Purpose: *********************************************************/ public void RootSelect(int x, int y) { DisplayObject object = near(x,y); if (object instanceof Node) { ((Node)object).swapRoot(); repaintNodes(); } } /******************************************************** * Purpose: *********************************************************/ public void ChangeXY(int x, int y) { int delta_x = x - old_x; old_x = x; int delta_y = y - old_y; old_y = y; selected.reset(); if (mouse_down_selected != null && !mouse_down_selected.isSelected()) { full_image_changed = true; left_image_changed = true; if (mouse_down_selected instanceof Node) ((Node)mouse_down_selected) .changeRootXY(delta_x, delta_y); else mouse_down_selected .changeXY(delta_x, delta_y); } else { while (!selected.end()) { DisplayObject object = selected.next(); if (!object.isSelected()) selected.drop(object); else if (object instanceof Node) { full_image_changed = true; left_image_changed = true; ((Node)object) .changeRootXY(delta_x, delta_y); } } } if (!isViewBoth() && full_image_changed) repaintNodes(); if (isViewBoth() && left_image_changed) repaintLeftNodes(); } /******************************************************** * Purpose: *********************************************************/ public void Move(int x, int y) { int delta_x = x - old_x; int delta_y = y - old_y; if(isViewGeo()) { double[] old_latlon = FromMap(old_x,old_y); double[] latlon = FromMap(x,y); double delta_lon = latlon[1] - old_latlon[1]; double delta_lat = latlon[0] - old_latlon[0]; zoom (top_lon-delta_lon,top_lat-delta_lat,bot_lon-delta_lon,bot_lat-delta_lat); if(isViewBoth()) repaintRightNodes(); else repaintNodes(); } else { x_shift += delta_x; y_shift += delta_y; //paintNodes(getGraphics(),size()); //if(isViewBoth()) // repaintLeftNodes(); //else // repaintNodes(); repaint(); } printMessage(""); old_x = x - delta_x; old_y = y - delta_y; } /******************************************************** * Purpose: *********************************************************/ protected DisplayObject near(int x, int y) { double near_distance = -1; double distance; DisplayObject near_object = null; dlist.reset(); while(!dlist.end()) { DisplayObject object = dlist.next(); distance = object.near(x,y); if (near_distance < 0 && distance > 0) { near_distance = distance; near_object = object; } else if (distance > 0 && ( (near_object instanceof Link && object instanceof Node) || distance < near_distance)) { near_distance = distance; near_object = object; } } return near_object; } protected DList nearObjects(int x, int y) { double near_distance = -1; double distance; DList near_objects = new DList(); DisplayObject near_object = null; dlist.reset(); while(!dlist.end()) { DisplayObject object = dlist.next(); distance = object.near(x,y); if (near_distance < 0 && distance > 0) { near_distance = distance; near_objects.enqueue(object); near_object = object; } else if (distance > 0 && ( (near_object instanceof Link && object instanceof Node) || distance <= near_distance)) { near_distance = distance; near_objects.enqueue(object); near_object = object; } } near_objects.reset(); while(!near_objects.end()) { DisplayObject object = near_objects.next(); distance = object.near(x,y); if (distance > near_distance || (near_object instanceof Node && object instanceof Link)) near_objects.drop(object); } return near_objects; } /******************************************************** * Purpose:To allow for the Display to sleep *********************************************************/ public void setAnimator(DisplayInterface animator_input) { animator = animator_input; } /******************************************************** * Purpose:To allow for the Display to sleep *********************************************************/ public void setThread(Thread thread) { myThread = thread; } /******************************************************** * Purpose:Sets the amout of time that it would sleep. *********************************************************/ public void setSleep(long sleep) { sleep_length = sleep; } /******************************************************** * Purpose:Sets the number of times it should loop *********************************************************/ public void setTimes(int time) { slid_times = time; } /******************************************************** * Purpose: To get boundary lat/longs -jjung *********************************************************/ public double[] getLatLon() { double[] lat_lon = new double[4]; lat_lon[0] = top_lat; lat_lon[1] = top_lon; lat_lon[2] = bot_lat; lat_lon[3] = bot_lon; return lat_lon; } public void setLatLon(double t_lat, double t_lon, double b_lat, double b_lon) { source_top_lat = top_lat = t_lat; source_top_lon = top_lon = t_lon; source_bot_lat = bot_lat = b_lat; source_bot_lon = bot_lon = b_lon; } /******************************************************** * Purpose:To create a group of images that will be presented * in a side show. *********************************************************/ public void run() { int current_mode = mode; setMode(ANIMATE); slids = new Image[num_files]; current_fileIndex = 0; int times = 0; while(times < slid_times) { animator.setFileIndex(current_fileIndex); if (slids[current_fileIndex] == null) { full_offscreen = null; setFileIndex(current_fileIndex); slids[current_fileIndex] = full_offscreen; } else { full_offscreen = slids[current_fileIndex]; repaint(); } try { myThread.sleep(sleep_length); } catch (InterruptedException exc) { System.err.println ("Display.run():" + exc.toString()); } current_fileIndex = (current_fileIndex+1) %num_files; if (current_fileIndex == 0) times++; } setMode(current_mode); } } class Check_Hover extends Thread { protected final int WAIT = 10; protected boolean set = false; protected int x,y; protected Display display; protected boolean hover_set_at_right = false; public Check_Hover(Display display_input) { display = display_input; } public void run() { try { while(true) { if (set) { if(display.isViewBoth()) { if(hover_set_at_right) display.setViewGeo(true); else display.setViewGeo(false); display.RootORGeo(); } display.GetString(x,y); } set = false; sleep(WAIT); } } catch (InterruptedException e) { System.err.println("Hover_check.run():" + e.toString()); } } synchronized public void set() { set = true; } synchronized public void set(int x_input,int y_input,boolean event_at_right) { x = x_input; y = y_input; set = true; if (event_at_right) hover_set_at_right = true; else hover_set_at_right = false; } }