/** * Forum.java 1.00 (1381) 12.18.96 Michael Shoffner * * Copyright (c) 1996 Prominence Dot Com, Inc. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * for non-commercial purposes and without fee is hereby granted * provided that this copyright notice appears in all copies. * * http://prominence.com/ shoffner@prominence.com * * */ import java.awt.*; import java.util.*; import ForumLauncher; import ForumComm; public class Forum extends Frame { ForumLauncher parent; TextArea console, read, post; Panel display, selector, control; Menu file, modes, identity, help; CardLayout displayout; List threadList, articleList; String mode; Button prButton; TextField id; String selectedThread, failedPostThread; Hashtable articles; AboutBox about; ForumComm comm; // Messages static final String DEFAULT_HEADER = ""; static final String ID_MESSAGE = "Please enter a name in the box in the lower left-hand corner.\nThen press ."; static final String POSTED_MESSAGE = "Your article was posted!"; static final String REPOST_ERROR_MESSAGE = "You just posted that article!"; static final String HELP_MESSAGE = "Please make sure to use the latest versions of Navigator and Explorer,\nas they are less buggy and more secure.\n\nSelect 'Change My Idenity' from the Identity menu, enter your name in\nthe box in the lower left-hand corner, and hit if you wish to\n'post' articles.\n\nSelect a discussion thread by double-clicking on it in the top list.\nThe 'business' topic is intentionally left blank.\n\nYou can select articles by clicking on them individually in the lower\nlist, or by clicking on the list box and using the arrow keys.\n\nEnjoy!"; // control options (and events) static final String CLOSE = "Close"; static final String READ = "Read"; static final String NEW = "New"; static final String REPLY = "Reply"; static final String POST = "Post"; static final String ID = "Change My Identity"; static final String ABOUT = "About"; static final String RELOAD_THREADS = "Refresh Threads"; static final String HELP = "Help..."; static final String DELIMITER = "\u0000"; static final String MESSAGE_NEWLINE = "/"; static final String QUOTE_CHARS = "> "; static final int LIST_HEIGHT = 6; static int HEIGHT = 25; static int WIDTH = 60; public Forum () { // set up MenuBar MenuBar bar = new MenuBar (); bar.add (file = new Menu ("File")); bar.add (modes = new Menu ("Mode")); bar.add (identity = new Menu ("Identity")); bar.setHelpMenu (bar.add (help = new Menu ("Help"))); file.add (new MenuItem ("Close")); file.add (new MenuItem (RELOAD_THREADS)); modes.add (new MenuItem (READ)); modes.add (new MenuItem (NEW)); identity.add (new MenuItem (ID)); help.add (new MenuItem (ABOUT)); help.add (new MenuItem (HELP)); setMenuBar (bar); // display panel: application console, read, and post display = new Panel(); display.setLayout (displayout = new CardLayout()); display.add ("console", console = new TextArea (HEIGHT, WIDTH)); display.add ("read", read = new TextArea (HEIGHT, WIDTH)); display.add ("post", post = new TextArea (HEIGHT, WIDTH)); read.setEditable (false); console.setEditable (false); // control control = new Panel(); control.setLayout(new BorderLayout()); control.add ("West", new Button (READ)); control.add ("Center", new Button (NEW)); control.add ("East", prButton = new Button (REPLY)); control.add ("South", id = new TextField()); id.hide(); // selector panel: thread and article selectors, control selector = new Panel(); selector.setLayout (new BorderLayout()); selector.add ("North", threadList = new List (LIST_HEIGHT, false)); selector.add ("Center", articleList = new List()); selector.add ("South", control); add ("Center", display); add ("West", selector); pack(); } public boolean handleEvent (Event e) { // launch Event if ((e.id == -1) && (e.target == this)) { Vector receive = (Vector) e.arg; Enumeration en = receive.elements(); parent = (ForumLauncher) en.nextElement(); // Load threads from server comm = new ForumComm (parent); articles = comm.loadAllThreads(); updateThreadList(); setTitle ((String) en.nextElement()); Color c = (Color) en.nextElement(); if (c != null) setBackground (c); String welcome = null; try { welcome = (String) en.nextElement(); StringBuffer w = new StringBuffer(); int idx = 0, nIdx; while ((nIdx = welcome.indexOf (MESSAGE_NEWLINE, idx)) >= 0) { w.append (" \n").append (welcome.substring (idx, nIdx)); idx = nIdx + 1; } if (idx < welcome.length ()) w.append (" \n").append (welcome.substring (idx)); welcome = new String (w); welcome = welcome.substring (2); } catch (NoSuchElementException ex) {} if (welcome == null) welcome = ""; console.setText (welcome); } // WINDOW_DESTROY Events else if ((e.id == Event.WINDOW_DESTROY) && (e.target == this)) hide(); else if ((e.id == Event.ACTION_EVENT) && ((String) e.arg).equals(CLOSE)) hide(); // RELOAD_THREADS Event else if ((e.id == Event.ACTION_EVENT) && (e.arg).equals(RELOAD_THREADS)) { articles = comm.loadAllThreads(); updateThreadList(); } // READ, NEW, and REPLY mode change Events else if ((e.id == Event.ACTION_EVENT) && (((String) e.arg).equals(READ))) { displayout.show (display, "read"); prButton.setLabel (REPLY); mode = READ; } else if ((e.id == Event.ACTION_EVENT) && (((String) e.arg).equals(NEW))) { post.setText(""); displayout.show(display, "post"); prButton.setLabel (POST); mode = NEW; failedPostThread = null; } else if ((e.id == Event.ACTION_EVENT)&&(((String) e.arg).equals(REPLY))) { if ((read.getText()).equals("")) { console.setText ("Reply to what?"); displayout.show (display, "console"); } else{ // quote reply StringBuffer repl = new StringBuffer (); String art = read.getText(); int idx = 0, nIdx; while ((nIdx = art.indexOf ("\n", idx)) >= 0) { repl.append (QUOTE_CHARS).append (art.substring (idx, nIdx + 1)); idx = nIdx + 1; } if (idx < art.length ()) repl.append (QUOTE_CHARS).append (art.substring (idx)); repl.append ("\n\n"); post.setText(repl.toString()); displayout.show(display, "post"); prButton.setLabel (POST); mode = REPLY; failedPostThread = null; } } // POST Event else if ((e.id == Event.ACTION_EVENT) && (((String) e.arg).equals(POST))) { // check for previously-failed post String t = failedPostThread; if (t == null) t = selectedThread; if (postArticle (post.getText(), t)) { console.setText (POSTED_MESSAGE); displayout.show (display, "console"); mode = POST; } } // Identity -> ID event else if ((e.id == Event.ACTION_EVENT) && (((String) e.arg).equals(ID))) { id.show(); inval (id); validate(); console.setText (ID_MESSAGE); displayout.show (display, "console"); } // TextField return Event (the end of ID mode) else if ((e.id == Event.ACTION_EVENT) && (e.target == id)) { id.hide(); inval (id); validate(); if (mode == POST) displayout.show (display, "post"); else if (mode == READ) displayout.show (display, "read"); else displayout.show (display, "new"); } // Thread selection else if ((e.id == Event.ACTION_EVENT) && (e.target == threadList)) { selectedThread = threadList.getSelectedItem(); articles.put (selectedThread, comm.loadThreadArticles (selectedThread)); read.setText(""); displayout.show (display, "read"); updateArticleList(); } // Article selection else if ((e.id == Event.LIST_SELECT) && (e.target == articleList)) { Vector threadArts = (Vector) articles.get (selectedThread); String a = (String)threadArts.elementAt(articleList.getSelectedIndex()); // pull off header (if any) String art = new String(); StringTokenizer strtok = new StringTokenizer (a, DELIMITER); if (strtok.hasMoreTokens()) art = strtok.nextToken(); if (strtok.hasMoreTokens()) art = strtok.nextToken(); read.setText (art); postEvent (new Event (this, Event.ACTION_EVENT, READ)); } // Help->About... else if ((e.id==Event.ACTION_EVENT)&&(((String)e.arg).equals(ABOUT))) { if (about == null) about = new AboutBox(); else about.show(); } else if ((e.id==Event.ACTION_EVENT)&&(((String)e.arg).equals(HELP))) { console.setText (HELP_MESSAGE); displayout.show (display, "console"); } return super.handleEvent(e); } // end handleEvent boolean postArticle (String art, String t) { // check for null message body if (art.equals("") || art == null) { console.setText ("Can't post a null message body..."); displayout.show (display, "console"); return false; } // check for selected thread if (t == null) { console.setText ("Please select a thread for your post."); displayout.show (display, "console"); return false; } // check id if ((id.getText()).equals("")) { postEvent (new Event (this, Event.ACTION_EVENT, ID)); return false; } // check for attempted repost if (mode == POST) { console.setText (REPOST_ERROR_MESSAGE); displayout.show (display, "console"); return false; } // it's ok, so add headers and post String wholeArt = id.getText() + DELIMITER + art; if (! comm.postArticle (wholeArt, t)) { console.setText ("Post failed due to a network problem. Please try again.\n\nIf you change mode to " + READ + " or " + NEW + ", or select another article,\nthis post will be lost."); displayout.show (display, "console"); failedPostThread = t; return false; } // refresh. if it fails, fake it Vector a = comm.loadThreadArticles (t); if (a.isEmpty()) { a = (Vector) articles.get (t); a.addElement (wholeArt); } articles.put (t, a); updateArticleList(); failedPostThread = null; return true; } // end postArticle void updateThreadList() { if (threadList.countItems() > 0) threadList.clear(); for (Enumeration en = articles.keys(); en.hasMoreElements();) { threadList.addItem ((String) en.nextElement()); } inval (threadList); validate(); } void updateArticleList () { if (articleList.countItems() > 0) articleList.clear(); Vector articlesInThread = (Vector) articles.get (selectedThread); for (Enumeration en = articlesInThread.elements(); en.hasMoreElements();) { // Extract header String art = (String) en.nextElement(); String header = DEFAULT_HEADER; StringTokenizer strtok = new StringTokenizer (art, DELIMITER); if ((strtok.hasMoreElements()) && (strtok.countTokens() > 1)) header = strtok.nextToken(); articleList.addItem (header); } } // manual layout for Netscape's layout bug void inval (Component c) { Component d = c.getParent(); if (d != null) inval (d); if (c instanceof Container) { Container e = (Container) c; for (int i = 0; i < e.countComponents(); ++ i) e.getComponent (i).invalidate(); c.invalidate(); } } }