home *** CD-ROM | disk | FTP | other *** search
/ Late Night VRML 2.0 with Java CD-ROM / code.zip / Ch18 / MultiUserClient.java < prev    next >
Text File  |  1997-02-21  |  34KB  |  796 lines

  1. // The Multi-User Client applet
  2.  
  3. // Written by Bernie Roehl, January 1997
  4.  
  5. import java.applet.*;
  6. import java.util.*;
  7. import java.awt.*;
  8. import java.io.*;
  9. import netscape.javascript.JSObject;
  10. import vrml.external.Browser;
  11. import vrml.external.Node;
  12. import vrml.external.field.*;
  13. import vrml.external.exception.*;
  14. import multi.*;
  15.  
  16. public class MultiUserClient
  17.     extends Applet
  18.     implements Runnable, EventOutObserver {
  19.  
  20.     Thread myThread;
  21.  
  22.     boolean running = true;
  23.  
  24.     public static final String version = "0.1";
  25.  
  26.     World world = null;       // the world we're connected to
  27.  
  28.     LocalEntity myEntity;     // our avatar
  29.  
  30.     String username = "";
  31.     String password = "";
  32.  
  33.     String hostname;
  34.     int port;
  35.  
  36.     String banner = "none";
  37.  
  38.     String[] info = new String[10];  // our public info
  39.  
  40.     Panel cardPanel;          // multi-page panel
  41.  
  42.     // Various controls
  43.  
  44.     Button disconnectButton, hideButton, muteButton, queryButton, selectButton;
  45.     Checkbox infoCheckbox, peopleCheckbox, filterCheckbox;
  46.     Checkbox avatarCheckbox, networkCheckbox, profileCheckbox;
  47.     Checkbox suspendCheckbox;
  48.     List avatarList;
  49.     List peopleList;
  50.     static final int SCROLLMAX = 50;
  51.     Scrollbar visualAcuitySlider, visualHorizonSlider;
  52. //    Scrollbar textAcuitySlider, textHorizonSlider;
  53.     TextField nicknameField, nameField, emailField, webpageField;
  54.     TextField geographyField, phoneField, faxField;
  55.  
  56.     TextField packetsField, connectedField, bannerField, statusField;
  57.  
  58.     Vector avatars = new Vector();   // URLs of avatars
  59.     Vector people = new Vector();    // entities in the world
  60.  
  61.     public String getAppletInfo() {
  62.         return "Multi-User Client" + version + ", written by Bernie Roehl";
  63.     }
  64.  
  65.     public String[][] getParameterInfo() {
  66.         String[][] info = {
  67.             { "host", "string", "name or IP address of filtering hsot" },
  68.             { "port", "integer", "IP port of filtering host" }
  69.         };
  70.         return info;
  71.     }
  72.  
  73.     // methods called by other applets:
  74.  
  75.     public World getWorld() { return world; }
  76.  
  77.     public LocalEntity getAvatar() { return myEntity; }
  78.  
  79.     // initializer
  80.  
  81.     public void init() {
  82.  
  83.         // get the applet parameters
  84.  
  85.         hostname = new String(getParameter("host"));
  86.         port = Integer.parseInt(getParameter("port"));
  87.  
  88.         if (hostname == null)   // default to the site this applet came from
  89.             hostname = getDocumentBase().getHost();
  90.  
  91.         // create the controls
  92.  
  93.         connectedField = new TextField(6);  // used to display connect time
  94.         connectedField.setEditable(false);  // display-only
  95.         packetsField = new TextField(6);    // displays # of packets received
  96.         packetsField.setEditable(false);    // display-only
  97.         bannerField = new TextField(12);    // used to display host's banner
  98.         bannerField.setEditable(false);     // display-only
  99.         statusField = new TextField(34);    // used to display current status
  100.         statusField.setEditable(false);     // display-only
  101.  
  102.         disconnectButton = new Button("Disconnect");   // disconnect from the world
  103.         muteButton = new Button("Mute/Unmute");   // mute a user
  104.         hideButton = new Button("Hide/Unhide");   // hide a user
  105.         queryButton = new Button("Query");        // query public info for a user
  106.         selectButton = new Button("Select");      // select an avatar
  107.  
  108.         // public info fields
  109.  
  110.         nicknameField = new TextField(8);
  111.         nameField = new TextField(8);
  112.         emailField = new TextField(8);
  113.         webpageField = new TextField(8);
  114.         geographyField = new TextField(8);
  115.         phoneField = new TextField(8);
  116.         faxField = new TextField(8);
  117.  
  118.         // radio buttons to select pages
  119.  
  120.         CheckboxGroup selector = new CheckboxGroup();
  121.         infoCheckbox = new Checkbox("Info", selector, true);
  122.         peopleCheckbox = new Checkbox("People", selector, false);
  123.         filterCheckbox = new Checkbox("Filter", selector, false);
  124.         avatarCheckbox = new Checkbox("Avatar", selector, false);
  125.         networkCheckbox = new Checkbox("Network", selector, false);
  126.         profileCheckbox = new Checkbox("Profile", selector, false);
  127.  
  128.         // sliders for filtering info
  129.  
  130.         visualAcuitySlider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 0, 0, SCROLLMAX);
  131.         visualHorizonSlider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 0, 0, SCROLLMAX);
  132. //        textAcuitySlider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 0, 0, SCROLLMAX);
  133. //        textHorizonSlider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 0, 0, SCROLLMAX);
  134.  
  135.         suspendCheckbox = new Checkbox();   // suspend updates from filter host
  136.  
  137.         peopleList = new List(10, false);   // list of users
  138.         peopleList.setFont(new Font("Courier", Font.PLAIN, 12));
  139.         avatarList = new List(10, false);   // list of avatars
  140.         avatarList.setFont(new Font("Courier", Font.PLAIN, 12));
  141.  
  142.         // populate the avatar list
  143.  
  144.         for (int i = 0; getParameter("avatarDesc" + i) != null; ++i) {
  145.             String desc = getParameter("avatarDesc" + i); 
  146.             String url = getParameter("avatarUrl" + i); 
  147.             if (url != null) {
  148.                 avatarList.addItem(new String(desc));
  149.                 avatars.addElement(new String(url));
  150.             }
  151.         }
  152.         avatarList.select(0);
  153.         avatarList.makeVisible(0);
  154.  
  155.         // and now, the panels
  156.  
  157.         GridBagLayout gbl;
  158.         GridBagConstraints gbc = new GridBagConstraints();
  159.         Label label;  // used for layout purposes
  160.  
  161.         Panel mainControls = new Panel();
  162.         mainControls.setLayout(new FlowLayout());
  163.         mainControls.setBackground(Color.lightGray);
  164.         mainControls.add(disconnectButton);
  165.  
  166.         Panel statusPanel = new Panel();
  167.         statusPanel.setLayout(new FlowLayout());
  168.         statusPanel.setBackground(Color.lightGray);
  169.         statusPanel.setForeground(Color.blue);
  170.         statusPanel.add(new Label("Status:"));
  171.         statusField.setForeground(Color.white);
  172.         statusPanel.add(statusField);
  173.  
  174.         // panel of radio buttons to select pages
  175.  
  176.         Panel selectorPanel = new Panel();
  177.         selectorPanel.setLayout(new GridLayout(6, 1));
  178.         selectorPanel.setBackground(Color.cyan);
  179.         selectorPanel.add(infoCheckbox);
  180.         selectorPanel.add(peopleCheckbox);
  181.         selectorPanel.add(filterCheckbox);
  182.         selectorPanel.add(avatarCheckbox);
  183.         selectorPanel.add(networkCheckbox);
  184.         selectorPanel.add(profileCheckbox);
  185.  
  186.         // program information panel
  187.  
  188.         Panel infoPanel = new Panel();
  189.         infoPanel.setLayout(new GridLayout(4, 1));
  190.         Label line1 = new Label("Multi-User Client", Label.CENTER);
  191.         line1.setFont(new Font("Helvetica", Font.BOLD, 24));
  192.         infoPanel.add(line1);
  193.         Label line2 = new Label("Version " + version, Label.CENTER);
  194.         line2.setFont(new Font("Helvetica", Font.PLAIN, 20));
  195.         infoPanel.add(line2);
  196.         Label line3 = new Label("Written by Bernie Roehl", Label.CENTER);
  197.         line3.setFont(new Font("Helvetica", Font.ITALIC, 16));
  198.         infoPanel.add(line3);
  199.         Label line4 = new Label("January 1997", Label.CENTER);
  200.         line4.setFont(new Font("Helvetica", Font.ITALIC, 16));
  201.         infoPanel.add(line4);
  202.  
  203.         // list of users
  204.  
  205.         Panel peoplePanel = new Panel();
  206.         gbl = new GridBagLayout();
  207.         peoplePanel.setLayout(gbl);
  208.         gbc.fill = GridBagConstraints.NONE;  gbc.anchor = GridBagConstraints.CENTER;
  209.         gbc.weightx = 100;  gbc.weighty = 100;
  210.         gbc.gridx = 0;  gbc.gridy = 0;  gbc.gridwidth = 1;  gbc.gridheight = 3;
  211.         gbl.setConstraints(peopleList, gbc); peoplePanel.add(peopleList);
  212.         gbc.anchor = GridBagConstraints.SOUTH;
  213.         gbc.gridy = 0;  gbc.gridx = 1;  gbc.gridheight = 1;
  214.         gbl.setConstraints(hideButton, gbc); peoplePanel.add(hideButton);
  215.         gbc.gridy = 1;  gbc.anchor = GridBagConstraints.CENTER;
  216.         gbl.setConstraints(muteButton, gbc); peoplePanel.add(muteButton);
  217.         gbc.gridy = 2;  gbc.anchor = GridBagConstraints.NORTH;
  218.         gbl.setConstraints(queryButton, gbc); peoplePanel.add(queryButton);
  219.  
  220.         // filtering options
  221.  
  222.         Panel filterPanel = new Panel();
  223.         gbl = new GridBagLayout();
  224.         filterPanel.setLayout(gbl);
  225.         gbc.fill = GridBagConstraints.NONE;
  226.         gbc.weightx = 100;  gbc.weighty = 100;  gbc.gridwidth = 1;  gbc.gridheight = 1;
  227.         gbc.gridx = 0;  gbc.gridy = 0;  gbc.anchor = GridBagConstraints.EAST;
  228.         gbc.gridy = 0; label = new Label("Visual acuity:"); gbl.setConstraints(label, gbc); filterPanel.add(label);
  229.         gbc.gridy = 1; label = new Label("Visual horizon:"); gbl.setConstraints(label, gbc); filterPanel.add(label);
  230. //        gbc.gridy = 2; label = new Label("Text distance:"); gbl.setConstraints(label, gbc); filterPanel.add(label);
  231. //        gbc.gridy = 3; label = new Label("Text horizon:"); gbl.setConstraints(label, gbc); filterPanel.add(label);
  232.         gbc.gridy = 4; label = new Label("Suspend:"); gbl.setConstraints(label, gbc); filterPanel.add(label);
  233.         gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL;
  234.         gbc.insets = new Insets(0, 0, 0, 20);
  235.         gbc.gridy = 0; gbl.setConstraints(visualAcuitySlider, gbc); filterPanel.add(visualAcuitySlider);
  236.         gbc.gridy = 1; gbl.setConstraints(visualHorizonSlider, gbc); filterPanel.add(visualHorizonSlider);
  237. //        gbc.gridy = 2; gbl.setConstraints(textAcuitySlider, gbc); filterPanel.add(textAcuitySlider);
  238. //        gbc.gridy = 3; gbl.setConstraints(textHorizonSlider, gbc); filterPanel.add(textHorizonSlider);
  239.         gbc.gridy = 4; gbl.setConstraints(suspendCheckbox, gbc); filterPanel.add(suspendCheckbox);
  240.  
  241.         // avatar selection
  242.  
  243.         Panel avatarPanel = new Panel();
  244.         gbl = new GridBagLayout();
  245.         avatarPanel.setLayout(gbl);
  246.         gbc.fill = GridBagConstraints.HORIZONTAL;  gbc.anchor = GridBagConstraints.CENTER;
  247.         gbc.weightx = 100;  gbc.weighty = 100;  gbc.insets = new Insets(10, 10, 10, 10);
  248.         gbc.gridx = 0;  gbc.gridy = 0;  gbc.gridwidth = 1;  gbc.gridheight = 1;
  249.         gbl.setConstraints(avatarList, gbc); avatarPanel.add(avatarList);
  250.         gbc.gridy = 1;  gbc.fill = GridBagConstraints.NONE;
  251.         gbl.setConstraints(selectButton, gbc); avatarPanel.add(selectButton);
  252.  
  253.         // network information
  254.  
  255.         Panel networkPanel = new Panel();
  256.         gbl = new GridBagLayout();
  257.         networkPanel.setLayout(gbl);
  258.         gbc.fill = GridBagConstraints.NONE;  gbc.insets = new Insets(0, 0, 0, 0);
  259.         gbc.weightx = 0;  gbc.weighty = 0;  gbc.gridwidth = 1;  gbc.gridheight = 1;
  260.         gbc.gridx = 0;  gbc.gridy = 0;  gbc.anchor = GridBagConstraints.EAST;
  261.         gbc.gridy = 0; label = new Label("Host:"); gbl.setConstraints(label, gbc); networkPanel.add(label);
  262.         gbc.gridy = 1; label = new Label("Port:"); gbl.setConstraints(label, gbc); networkPanel.add(label);
  263.         gbc.gridy = 2; label = new Label("Server:"); gbl.setConstraints(label, gbc); networkPanel.add(label);
  264.         gbc.gridy = 3; label = new Label("Packets rcvd:"); gbl.setConstraints(label, gbc); networkPanel.add(label);
  265.         gbc.gridy = 4; label = new Label("Connect time:"); gbl.setConstraints(label, gbc); networkPanel.add(label);
  266.         gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST;
  267.         gbc.gridy = 0; label = new Label(hostname); gbl.setConstraints(label, gbc); networkPanel.add(label);
  268.         gbc.gridy = 1; label = new Label(String.valueOf(port)); gbl.setConstraints(label, gbc); networkPanel.add(label);
  269.         gbc.gridy = 2; gbl.setConstraints(bannerField, gbc); networkPanel.add(bannerField);
  270.         gbc.gridy = 3; gbl.setConstraints(packetsField, gbc); networkPanel.add(packetsField);
  271.         gbc.gridy = 4; gbl.setConstraints(connectedField, gbc); networkPanel.add(connectedField);
  272.  
  273.         // user's public information (profile)
  274.  
  275.         Panel profilePanel = new Panel();
  276.         gbl = new GridBagLayout();
  277.         profilePanel.setLayout(gbl);
  278.         gbc.fill = GridBagConstraints.NONE;
  279.         gbc.insets = new Insets(10, 10, 10, 10);
  280.         gbc.weightx = 100;  gbc.weighty = 100;
  281.         gbc.gridwidth = 1;  gbc.gridheight = 1;
  282.         gbc.gridx = 0;  gbc.gridy = 0;  gbc.anchor = GridBagConstraints.EAST;
  283.         gbc.gridy = 0; label = new Label("Nickname:"); gbl.setConstraints(label, gbc); profilePanel.add(label);
  284.         gbc.gridy = 1; label = new Label("First/Last name:"); gbl.setConstraints(label, gbc); profilePanel.add(label);
  285.         gbc.gridy = 2; label = new Label("email address:"); gbl.setConstraints(label, gbc); profilePanel.add(label);
  286.         gbc.gridy = 3; label = new Label("Web page:"); gbl.setConstraints(label, gbc); profilePanel.add(label);
  287.         gbc.gridy = 4; label = new Label("Geographic location:"); gbl.setConstraints(label, gbc); profilePanel.add(label);
  288.         gbc.gridy = 5; label = new Label("Phone number:"); gbl.setConstraints(label, gbc); profilePanel.add(label);
  289.         gbc.gridy = 6; label = new Label("Fax number:"); gbl.setConstraints(label, gbc); profilePanel.add(label);
  290.         gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST;
  291.         gbc.gridy = 0; gbl.setConstraints(nicknameField, gbc); profilePanel.add(nicknameField);
  292.         gbc.gridy = 1; gbl.setConstraints(nameField, gbc); profilePanel.add(nameField);
  293.         gbc.gridy = 2; gbl.setConstraints(emailField, gbc); profilePanel.add(emailField);
  294.         gbc.gridy = 3; gbl.setConstraints(webpageField, gbc); profilePanel.add(webpageField);
  295.         gbc.gridy = 4; gbl.setConstraints(geographyField, gbc); profilePanel.add(geographyField);
  296.         gbc.gridy = 5; gbl.setConstraints(phoneField, gbc); profilePanel.add(phoneField);
  297.         gbc.gridy = 6; gbl.setConstraints(faxField, gbc); profilePanel.add(faxField);
  298.  
  299.         // multi-panel panel
  300.  
  301.         cardPanel = new Panel();
  302.         cardPanel.setLayout(new CardLayout());
  303.         cardPanel.add("Info", infoPanel);
  304.         cardPanel.add("People", peoplePanel);
  305.         cardPanel.add("Filter", filterPanel);
  306.         cardPanel.add("Avatar", avatarPanel);
  307.         cardPanel.add("Network", networkPanel);
  308.         cardPanel.add("Profile", profilePanel);
  309.  
  310.         // and finally, the overall layout of the main panel
  311.  
  312.         setLayout(new BorderLayout());
  313. //        add("North", mainControls);   No disconnect button yet
  314.         add("South", statusPanel);
  315.         add("East", selectorPanel);
  316.         add("Center", cardPanel);
  317.  
  318.     }
  319.  
  320.     public void start() {
  321.         if (myThread == null)
  322.             myThread = new Thread(this);
  323.         myThread.start();
  324.         if (world != null)
  325.             world.suspend(suspendCheckbox.getState());
  326.     }
  327.  
  328.     public void stop() {
  329.         if (world != null)
  330.             world.suspend(true);
  331.        if (myThread != null)
  332.            myThread.stop();
  333.     }
  334.  
  335.     public void run() {
  336.         new LoginDialog(this);
  337.         browserSetup();  // connect to the VRML browser
  338.         worldSetup();    // connect to the world
  339.         new TimeUpdater(this);   // keeps the time up-to-date
  340.         new PeopleReader(this);  // keeps list of users up-to-date
  341.         while (running) {
  342.             if (world != null) {
  343.                 Enumeration en = world.getEntities();
  344.                 // go through entities
  345.                 while (en.hasMoreElements())
  346.                     updateEntity((Entity) en.nextElement());
  347.             }
  348.             Thread.yield();
  349.         }
  350.     }
  351.  
  352.     private void updateEntity(Entity e) {
  353.         int state = e.getState();
  354.         if (state == Entity.STALE || state == Entity.GONE) {
  355.             // entity has left or expired
  356.             e.setState(Entity.DEAD);
  357.             e.setRepresentation(null);
  358.             return;  // done with this entity
  359.             }
  360.         // make sure the object has a representation
  361.         Thing rep = (Thing) e.getRepresentation();
  362.         if (rep == null || e.hasNewURL()) {
  363.             // need to create a representation
  364.             String url = e.getURL();
  365.             if (url == null)
  366.                 return;
  367.             try { rep = new Thing(browser, addAvatars, removeAvatars, url); }
  368.             catch (InvalidVrmlException ex) {
  369.                 System.out.println("couldn't add object: " + ex);
  370.                 return;
  371.             }
  372.             catch (InvalidEventInException ex) {
  373.                 System.out.println("couldn't add object: " + ex);
  374.                 return;
  375.             }
  376.             e.setRepresentation(rep);
  377.             e.unmarkNewURL();
  378.         }
  379.         if (e.hasChanged()) {
  380.             // update rep with location and orientation
  381.             rep.setLocation(e.getLocation());
  382.             rep.setOrientation(e.getOrientation());     
  383.             rep.setVisibility(!e.isHidden());
  384.             e.markUnchanged();
  385.         }
  386.     }
  387.  
  388.     public boolean handleEvent(Event evt) {
  389.        CardLayout layout = (CardLayout) cardPanel.getLayout();
  390.        if (evt.id == Event.ACTION_EVENT) {
  391.            if (evt.target instanceof Checkbox) {
  392.                if (evt.target == infoCheckbox)
  393.                    layout.show(cardPanel, "Info");
  394.                else if (evt.target == peopleCheckbox)
  395.                    layout.show(cardPanel, "People");
  396.                else if (evt.target == filterCheckbox)
  397.                    layout.show(cardPanel, "Filter");
  398.                else if (evt.target == avatarCheckbox)
  399.                    layout.show(cardPanel, "Avatar");
  400.                else if (evt.target == networkCheckbox)
  401.                    layout.show(cardPanel, "Network");
  402.                else if (evt.target == profileCheckbox)
  403.                    layout.show(cardPanel, "Profile");
  404.                else if (evt.target == suspendCheckbox && world != null)
  405.                    world.suspend(((Boolean) evt.arg).booleanValue());
  406.                return true;
  407.            }
  408.            else if (evt.target instanceof TextField) {
  409.                if (evt.target == nicknameField && myEntity != null)
  410.                    myEntity.setNickName((String) evt.arg);
  411.                else if (evt.target == nameField)
  412.                    info[0] = (String) evt.arg;
  413.                else if (evt.target == emailField)
  414.                    info[1] = (String) evt.arg;
  415.                else if (evt.target == webpageField)
  416.                    info[2] = (String) evt.arg;
  417.                else if (evt.target == geographyField)
  418.                    info[3] = (String) evt.arg;
  419.                else if (evt.target == phoneField)
  420.                    info[4] = (String) evt.arg;
  421.                else if (evt.target == faxField)
  422.                    info[5] = (String) evt.arg;
  423.                if (myEntity != null) {
  424.                    myEntity.setInfo(info); 
  425.                    try { myEntity.updateRegistry(); }
  426.                    catch (IOException ex) { }
  427.                    catch (PermissionDeniedException ex) { }
  428.                }
  429.                return true;
  430.            }
  431.            else if (evt.target instanceof Button) {
  432.                if (evt.target == disconnectButton)
  433.                    running = false;
  434.                else if (evt.target == selectButton && myEntity != null) {
  435.                    myEntity.setURL((String) avatars.elementAt(avatarList.getSelectedIndex()));
  436.                    try { myEntity.updateRegistry(); }
  437.                    catch (IOException ex) { }
  438.                    catch (PermissionDeniedException ex) { }
  439.                }
  440.                else if (evt.target == hideButton) {
  441.                    int n = peopleList.getSelectedIndex();
  442.                    Entity e = (Entity) people.elementAt(n);
  443.                    e.Hide(!e.isHidden());
  444.                    peopleList.replaceItem(peopleFormat(e), n);
  445.                    peopleList.select(n);
  446.                }
  447.                else if (evt.target == muteButton) {
  448.                    int n = peopleList.getSelectedIndex();
  449.                    Entity e = (Entity) people.elementAt(n);
  450.                    e.Mute(!e.isMuted());
  451.                    peopleList.replaceItem(peopleFormat(e), n);
  452.                    peopleList.select(n);
  453.                }
  454.                else if (evt.target == queryButton)
  455.                    new QueryDialog((Entity) people.elementAt(peopleList.getSelectedIndex()));
  456.                return true;
  457.            }
  458.            else if (evt.target instanceof Scrollbar && world != null) {
  459.                switch (evt.id) {
  460.                    case Event.SCROLL_LINE_UP:  case Event.SCROLL_LINE_DOWN:
  461.                    case Event.SCROLL_PAGE_UP:  case Event.SCROLL_PAGE_DOWN:
  462.                    case Event.SCROLL_ABSOLUTE:
  463.                        if (evt.target == visualAcuitySlider)
  464.                            world.setVisualAcuity(visualAcuitySlider.getValue() / (float) SCROLLMAX);
  465.                        else if (evt.target == visualHorizonSlider)
  466.                            world.setVisualHorizonCount(visualHorizonSlider.getValue());
  467.                        return true;
  468.                    default: return false;
  469.                }
  470.            }
  471.        }
  472.        return false;
  473.     }
  474.  
  475.     public static String peopleFormat(Entity e) {   // format an entry in the user list
  476.         String name = e.getNickName();
  477.         if (name == null)
  478.             name = "Guest";
  479.         return (e.isHidden() ? "H" : " ") + (e.isMuted() ? "M" : " ") + "  " + name;
  480.     }
  481.  
  482.     private Browser getBrowser() {
  483.         Browser browser = Browser.getBrowser();
  484.         if (browser == null) {
  485.             JSObject win = JSObject.getWindow(this);
  486.             if (win != null) {
  487.                 JSObject doc = (JSObject) win.getMember("document");
  488.                 JSObject embeds = (JSObject) doc.getMember("embeds");
  489.                 JSObject applets = (JSObject) doc.getMember("applets");
  490.                 JSObject frames = (JSObject) doc.getMember("frames");
  491.                 for (int i = 0; i < 10; ++i) {
  492.                     browser = (Browser) embeds.getSlot(0);
  493.                     if (browser != null)
  494.                         break;
  495.                     try { Thread.sleep(500); }
  496.                     catch (InterruptedException e) { }
  497.                 }
  498.             }
  499.         }
  500.         return browser;
  501.     }
  502.  
  503.     Browser browser;          // the VRML browser
  504.     Node root;                // root node of the loaded world
  505.     Node proxSensor;          // first one's a ProximitySensor
  506.     Node entityGroup;         // second one's a group for holding avatars
  507.     EventOutSFVec3f position_changed;   // eventOuts of ProximitySensor
  508.     EventOutSFRotation orientation_changed;
  509.     EventInMFNode addAvatars = null;    // eventIns of ObjGroup
  510.     EventInMFNode removeAvatars = null;
  511.  
  512.     private void browserSetup() {
  513.  
  514.         // find the VRML browser
  515.         browser = getBrowser();
  516.         if (browser == null)
  517.             System.out.println("Could not find VRML browser!");
  518.  
  519.         // find the root node of the loaded world
  520.         root = null;
  521.         try {
  522.             while (root == null)
  523.                 root = browser.getNode("Root");
  524.         }
  525.         catch (InvalidNodeException e) {
  526.             System.out.println("Could not find node named \"Root\"");
  527.         }
  528.  
  529.         EventInMFNode addChildren = null;
  530.         try { addChildren = (EventInMFNode) root.getEventIn("addChildren"); }
  531.         catch (InvalidEventInException e) {
  532.             System.out.println("Could not access addChildren eventIn on root node: " + e);
  533.         }
  534.  
  535.         Node[] ournodes = browser.createVrmlFromString(
  536.             "ProximitySensor { size 1e30 1e30 1e30 }\n" +
  537.             "Group { }\n"
  538.             );
  539.         addChildren.setValue(ournodes);
  540.         proxSensor = ournodes[0];
  541.         entityGroup = ournodes[1];
  542.  
  543.         // find the entityGroup's node's addChildren and
  544.         // removeChildren eventIns
  545.         try {
  546.             addAvatars = (EventInMFNode) entityGroup.getEventIn("addChildren");
  547.             removeAvatars = (EventInMFNode) entityGroup.getEventIn("removeChildren");
  548.         }
  549.         catch (InvalidEventInException e) {
  550.             System.out.println("Could not access eventIn on ObjGroup node: " + e);
  551.         }
  552.  
  553.         // capture the proxSensor's position_changed and
  554.         // orientation_changed eventIns
  555.         position_changed =
  556.             (EventOutSFVec3f) proxSensor.getEventOut("position_changed");
  557.         position_changed.advise(this, null);
  558.         orientation_changed =
  559.             (EventOutSFRotation) proxSensor.getEventOut("orientation_changed");
  560.         orientation_changed.advise(this, null);
  561.  
  562.     }
  563.  
  564.     private int n = 0;  // used to count callbacks
  565.  
  566.     public void callback(EventOut event, double time, Object userData) {
  567. System.out.println("callback!");
  568.         if (event.getType() == FieldTypes.SFVEC3F) {
  569.             EventOutSFVec3f pos = (EventOutSFVec3f) event;
  570.             float[] values = pos.getValue();
  571.             Vec3 loc = new Vec3(values[0], values[1], values[2]);
  572. System.out.println("location = " + loc);
  573.             if (world != null)
  574.                 world.setViewpoint(loc);
  575.             if (myEntity != null) {
  576.                 myEntity.setLocation(loc);
  577.                 try { myEntity.sendUpdate(); }
  578.                 catch (IOException e) { }
  579.             }
  580.         }
  581.         else if (event.getType() == FieldTypes.SFROTATION) {
  582.             if (myEntity != null) {
  583.                 EventOutSFRotation rot = (EventOutSFRotation) event;
  584.                 float[] values = rot.getValue();
  585.                 if (myEntity != null) { 
  586.                     myEntity.setOrientation(
  587.                         new Rotation(
  588.                             values[0], values[1], values[2], values[3]));
  589.                     try { myEntity.sendUpdate(); }
  590.                     catch (IOException e) { }
  591.                 }
  592.             }
  593.         }
  594.     }
  595.  
  596.     private void worldSetup() {
  597.  
  598.         // connect to the world
  599.  
  600.         try {
  601.             world = new World(hostname, port);
  602.             statusField.setText("Connected");
  603.             statusField.setForeground(Color.yellow);
  604.         }
  605.         catch (Exception ex) {
  606.             statusField.setText(ex.toString());
  607.             statusField.setForeground(Color.red);
  608.         }
  609.  
  610.         if (world != null) {
  611.             // set the banner
  612.             banner = world.getBanner();
  613.             if (banner == null)
  614.                 banner = "";
  615.             bannerField.setText(banner);
  616.             // create our avatar
  617.             try { world.identity(username, password); }
  618.             catch (IOException ex) {
  619.                 System.out.println("couldn't IDENT: " + ex);
  620.             }
  621.             catch (BadIdentityException ex) {
  622.                 System.out.println("couldn't IDENT: " + ex);
  623.             }
  624.             try {
  625.                 // we use the username as the cname
  626.                 // for the user's avatar:
  627.                 myEntity = new LocalEntity(world, username);
  628.                 // we also use it as our initial nickname:
  629.                 myEntity.setNickName(username);
  630.                 // use a default avatar:
  631.                 myEntity.setURL((String) avatars.elementAt(0));
  632.                 myEntity.updateRegistry();
  633.             }
  634.             catch (PermissionDeniedException ex) {
  635.                 System.out.println("couldn't create avatar: " + ex);
  636.             }
  637.             catch (IOException ex) {
  638.                 System.out.println("couldn't create avatar: " + ex);
  639.             }
  640.         }
  641.     }
  642.  
  643. }
  644.  
  645. class QueryDialog extends Dialog {
  646.  
  647.     protected Button dismissButton = new Button("Dismiss");
  648.  
  649.     public QueryDialog(Entity entity) {
  650.         super(new Frame(), "Query User", true);  // modal dialog box
  651.         GridBagLayout gbl = new GridBagLayout();
  652.         GridBagConstraints gbc = new GridBagConstraints();
  653.         setLayout(gbl);
  654.         Label label;
  655.         gbc.gridx = 0;  gbc.gridy = 0;  gbc.gridwidth = 1;  gbc.gridheight = 1;
  656.         gbc.weightx = 100;  gbc.weighty = 100;  gbc.insets = new Insets(10, 0, 10, 20);
  657.         gbc.anchor = GridBagConstraints.EAST;  gbc.fill = GridBagConstraints.NONE;
  658.         gbc.gridy = 0; label = new Label("First/Last name:"); gbl.setConstraints(label, gbc); add(label);
  659.         gbc.gridy = 1; label = new Label("email address:"); gbl.setConstraints(label, gbc); add(label);
  660.         gbc.gridy = 2; label = new Label("Web page:"); gbl.setConstraints(label, gbc); add(label);
  661.         gbc.gridy = 3; label = new Label("Geographic location:"); gbl.setConstraints(label, gbc); add(label);
  662.         gbc.gridy = 4; label = new Label("Phone number:"); gbl.setConstraints(label, gbc); add(label);
  663.         gbc.gridy = 5; label = new Label("Fax number:"); gbl.setConstraints(label, gbc); add(label);
  664.         gbc.gridx = 1; gbc.anchor = GridBagConstraints.WEST;
  665.         String[] info = entity.getInfo();
  666.         gbc.gridy = 0; label = new Label(info[0] == null ? "<unknown>" : info[0]); gbl.setConstraints(label, gbc); add(label);
  667.         gbc.gridy = 1; label = new Label(info[1] == null ? "<unknown>" : info[1]); gbl.setConstraints(label, gbc); add(label);
  668.         gbc.gridy = 2; label = new Label(info[2] == null ? "<unknown>" : info[2]); gbl.setConstraints(label, gbc); add(label);
  669.         gbc.gridy = 3; label = new Label(info[3] == null ? "<unknown>" : info[3]); gbl.setConstraints(label, gbc); add(label);
  670.         gbc.gridy = 4; label = new Label(info[4] == null ? "<unknown>" : info[4]); gbl.setConstraints(label, gbc); add(label);
  671.         gbc.gridy = 5; label = new Label(info[5] == null ? "<unknown>" : info[5]); gbl.setConstraints(label, gbc); add(label);
  672.         gbc.gridx = 0;  gbc.gridy = 6;  gbc.gridwidth = 2;
  673.         gbc.anchor = GridBagConstraints.CENTER;  gbc.insets = new Insets(10, 10, 10, 10);
  674.         gbc.gridy = 6; gbl.setConstraints(dismissButton, gbc);  add(dismissButton);
  675.         pack();
  676.         show();
  677.     }
  678.  
  679.     public boolean action(Event evt, Object whichAction) {
  680.         if (evt.target == dismissButton)
  681.             hide();
  682.         return true;
  683.     }
  684.  
  685. }
  686.  
  687. class LoginDialog extends Dialog {
  688.  
  689.     protected Button connectButton = new Button("Connect");
  690.     protected TextField usernameField, passwordField;
  691.  
  692.     public LoginDialog(MultiUserClient muclient) {
  693.         super(new Frame(), "Login", true);  // modal dialog box
  694.         GridBagLayout gbl = new GridBagLayout();
  695.         GridBagConstraints gbc = new GridBagConstraints();
  696.         setLayout(gbl);
  697.         Label label;
  698.         gbc.gridx = 0;  gbc.gridy = 0;
  699.         gbc.gridwidth = 1;  gbc.gridheight = 1;
  700.         gbc.weightx = 100;  gbc.weighty = 100;
  701.         gbc.insets = new Insets(10, 0, 10, 20);
  702.         gbc.anchor = GridBagConstraints.EAST;
  703.         gbc.fill = GridBagConstraints.NONE;
  704.         label = new Label("Userid:");
  705.         gbl.setConstraints(label, gbc); add(label);
  706.         gbc.gridy = 1;
  707.         label = new Label("Password:");
  708.         gbl.setConstraints(label, gbc); add(label);
  709.         gbc.anchor = GridBagConstraints.WEST;
  710.         gbc.gridx = 1;  gbc.gridy = 0;
  711.         usernameField = new TextField(16);
  712.         gbl.setConstraints(usernameField, gbc);  add(usernameField);
  713.         gbc.gridy = 1;
  714.         passwordField = new TextField(16);
  715.         passwordField.setEchoCharacter('*');
  716.         gbl.setConstraints(passwordField, gbc);  add(passwordField);
  717.         gbc.gridy = 2;
  718.         gbl.setConstraints(connectButton, gbc);  add(connectButton);
  719.         pack();
  720.         show();
  721.         muclient.username = usernameField.getText();
  722.         muclient.password = passwordField.getText();
  723.     }
  724.  
  725.     public boolean action(Event evt, Object whichAction) {
  726.         if (evt.target == connectButton)
  727.             hide();
  728.         return true;
  729.     }
  730.  
  731. }
  732.  
  733. class TimeUpdater extends Thread {
  734.  
  735.     MultiUserClient client;
  736.  
  737.     public TimeUpdater(MultiUserClient cli) {
  738.         client = cli;
  739.         setPriority(NORM_PRIORITY-1);
  740.         start();
  741.     }
  742.  
  743.     public void run() {
  744.         while (true) {
  745.             long secondsConnected =
  746.                 (System.currentTimeMillis()
  747.                - client.world.getConnectedTime())/1000;
  748.             long minutesConnected = secondsConnected/60;
  749.             long hours = minutesConnected / 60;
  750.             long minutes = minutesConnected % 60;
  751.             client.connectedField.setText(
  752.                hours + ":" + ((minutes < 10) ? "0" : "")
  753.                      + minutes);
  754.             client.packetsField.setText(
  755.                String.valueOf(client.world.getPacketCount()));
  756.             try sleep(1000); catch (InterruptedException ex) { }
  757.         }
  758.     }
  759.  
  760. }
  761.  
  762. class PeopleReader extends Thread {
  763.  
  764.     MultiUserClient client;
  765.  
  766.     public PeopleReader(MultiUserClient cli) {
  767.         client = cli;
  768.         setPriority(NORM_PRIORITY-1);
  769.         start();
  770.     }
  771.  
  772.     public void run() {
  773.         while (true) {
  774.             // clear the list so we can re-fill it:
  775.             client.peopleList.delItems(0,
  776.                 client.peopleList.countItems()-1);
  777.             client.people = new Vector();
  778.             Enumeration en = client.world.getEntities();
  779.             while (en.hasMoreElements()) {
  780.                 Entity e = (Entity) en.nextElement();
  781.                 if (e.isAvatar() == false)  // ignore non-avatars
  782.                     continue;
  783.                 if (e.needsInfo())  // not all there yet
  784.                     continue;
  785.                 client.people.addElement(e);
  786.                 client.peopleList.addItem(client.peopleFormat(e));
  787.             }
  788.             client.peopleList.select(0);
  789.             try Thread.sleep(10000);   // update every ten seconds
  790.             catch (InterruptedException ex) { }
  791.         }
  792.     }
  793.  
  794. }
  795.  
  796.