/
ScryMUD/mud/
ScryMUD/mud/grrmud/Boards/
ScryMUD/mud/grrmud/Help/
ScryMUD/mud/grrmud/Pfiles/
ScryMUD/mud/grrmud/PlayerSacks/
ScryMUD/mud/grrmud/PlayerShops/
ScryMUD/mud/grrmud/help_filter/
ScryMUD/mud/hegemon/
ScryMUD/mud/hegemon/data/
ScryMUD/mud/hegemon/data/help/battle/
ScryMUD/mud/hegemon/data/help/client/
ScryMUD/mud/hegemon/data/help/communications/
ScryMUD/mud/hegemon/data/help/skills/
ScryMUD/mud/hegemon/data/help/spells/
ScryMUD/mud/include/
ScryMUD/mud/lib/
ScryMUD/mud/lib/bitfield/
ScryMUD/mud/lib/log/
ScryMUD/mud/lib/string2/
// $Id: HegemonScroll.java,v 1.9 1999/06/05 23:29:12 greear Exp $
// $Revision: 1.9 $  $Author: greear $ $Date: 1999/06/05 23:29:12 $

//
//Hegemon Client Code:  Java Client for ScryMUD Server Code
//Copyright (C) 1998  Ben Greear
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// To contact the Author, Ben Greear:  greear@cyberhighway.net, (preferred)
//                                     greearb@agcs.com
//

import java.util.*;
import java.awt.*;
import java.awt.image.*;
import java.text.*;
import java.awt.event.*;

/**  Stores a certain amount of data (text).  It also contains the logic
  needed to return vectors containing certain portions of the scroll.
  */
class HegemonScroll extends Panel {
   HegemonManager hm = null;
   ScrollComponentCollection components;
   HegemonDisplayProperties props;
   ScrollComponentText comp_in_progress;
   ScrollComponent last_added_component;
   HegemonScrollCanvas canvas;
   int vert_spacing;
   HegemonDisplay parent;
   int left_x;
   int top_y;
   boolean auto_scroll;
   Vector non_scrollables; /* vector of objects which are not
                              intended to scroll with the rest of
                              the text.
                           */

   ScrollComponentGraph hp;
   ScrollComponentGraph mana;
   ScrollComponentGraph mov;

   public HegemonScroll(HegemonManager h, HegemonDisplay par) {
      super();

      hm = h;
      parent = par;
      components = new ScrollComponentQueue(1000);
      vert_spacing = 1;

      setBackground(new Color(250, 250, 250));
      
      canvas = new HegemonScrollCanvas(parent);
      
      setLayout(new BorderLayout());
      this.add(canvas, "Center");
      
      props = new HegemonDisplayProperties(hm, canvas.getGraphics());
      comp_in_progress = new ScrollComponentText(vert_spacing, props);
      hp = new ScrollComponentGraph(5, canvas.getSize().height - 5,
                                    8, 55, 4, new Color(50, 0, 0),
                                    ScrollComponentGraph.O_EAST);
      mana = new ScrollComponentGraph(65, canvas.getSize().height - 5,
                                      8, 55, 4, new Color(0, 0, 50),
                                      ScrollComponentGraph.O_EAST);
      mov = new ScrollComponentGraph(125, canvas.getSize().height - 5,
                                     8, 55, 4, new Color(0, 50, 0),
                                     ScrollComponentGraph.O_EAST);

      non_scrollables = new Vector();
      non_scrollables.addElement(hp);
      non_scrollables.addElement(mana);
      non_scrollables.addElement(mov);
      
   }//constructor

   public void notifyScrollCanvasResized() {
      Dimension d = canvas.getSize();
      hp.setDefaultCoords(5, d.height - 10);
      mana.setDefaultCoords(65, d.height - 10);
      mov.setDefaultCoords(125, d.height - 10);
   }

   public void setPrompt(int h, int hm, int m, int mm,
                         int v, int vm) {
      int cc; /* color component */
      Color c;

      hp.setEnabled(true);
      mana.setEnabled(true);
      mov.setEnabled(true);

      hp.setPercent((float)h / (float)hm);
      cc = Math.max(200, 255 - Math.max(0, (hm / 8)));
      c = hp.getColor();
      if (c.getRed() != cc) {
         hp.setColor(new Color(cc, calcOtherColor(cc), calcOtherColor(cc)));
      }

      mana.setPercent((float)m / (float)mm);
      cc = Math.max(200, 255 - Math.max(0, (mm / 8)));
      c = mana.getColor();
      if (c.getBlue() != cc) {
         mana.setColor(new Color(calcOtherColor(cc), cc, calcOtherColor(cc)));
      }

      mov.setPercent((float)v / (float)vm);
      cc = Math.max(200, 255 - Math.max(0, (vm / 8)));
      c = mov.getColor();
      if (c.getGreen() != cc) {
         mov.setColor(new Color(calcOtherColor(cc), calcOtherColor(cc), cc));
      }
   }//setPrompt

   /** The other color component should always be less than the primary
    * color.  Also, as the primary reaches its strongest (200),
    * the other colors should drop to zero.  The goal is to go from
    * light shades for weaker players to deep, strong colors for more
    * powerful players.  However, the general hue:  Red==HP, Blue=MANA,
    * Green==MOVE, should remain the same.  p should range from 255
    * at the weakest, to 200 at the strongest.
    */
   protected int calcOtherColor(int p) {
      return (p - 200) * 2; //Yeesh, is it that simple!! 
   }


   public void clear() {
      props.reset();
      components.clear();
      canvas.clear();
      vert_spacing = 1;
      comp_in_progress = new ScrollComponentText(vert_spacing, props);
   }//clear
   
   public HegemonDisplay getPar() {
      return parent;
   }

   public void setScrollComponentCollection(ScrollComponentCollection scc) {
      components = scc;
   }

   public int getScrollHeight() {
      return components.getTotalHeight();
   }
   
   public final void setPaintAttributes(int x, int y) {
      left_x = x;
      top_y = y;
      canvas.setXOffset(left_x);
   }

   public void paint(boolean a_scroll) {
      Vector comps;
      auto_scroll = a_scroll;

      //Log.it("HegemonScroll.paint(" + auto_scroll + "), top_y:  "
      //       + top_y + " height:  " + canvas.getSize().height);
      
      if (auto_scroll) {
         comps = components.getBottomPage(canvas.getSize().height - 25);
      }
      else {
         comps = components.getVector(top_y, top_y +
                                      canvas.getSize().height - 25);
      }
         

      //Log.it("In HegemonScroll.paint(), component's size: "
      //     + comps.size());
      canvas.setComponents(comps, non_scrollables);
      canvas.paint();
   }//paint    
   

   public void paint(Graphics g) {
      Log.instance().trc("In HegemonScroll.paint(Graphics g)");
      
      if ((g != null) && (props.getGraphics() == null)) {
         Log.instance().dbg("Setting graphics in the Properties.");
         props.setGraphics(g);
      }
      
      Vector comps;

      // We can't send auto_scroll in here, but lets hope our cached value is
      // good enough....
      if (auto_scroll) {
         comps = components.getBottomPage(canvas.getSize().height - 15);
      }
      else {
         comps = components.getVector(top_y, top_y +
                                      canvas.getSize().height - 15);
      }

      canvas.setComponents(comps, non_scrollables);
      super.paint(g);
   }//paint

   final public HegemonDisplayProperties getProperties() {
      return props;
   }

   private final void addComponent(ScrollComponent sc) {
      if (sc == last_added_component) {
         //Log.it("\n\n       RETURNING FROM addComponent().\n\n");
         return; //no need to add it again
      }
      else {
         last_added_component = sc;
      }

      //Log.it("\naddComponent:  " + sc + "\n");
      try {
         components.push(sc);
      }
      catch (Exception e) {
         Log.instance().err("addComponent:  Trying to push: " + e);
      }
   }//addComponent
   
   /** appends text, using the current context, to the end of the scroll. */
   public synchronized void append(String str) {
      //Log.it("In HegemonScroll.append(), str -:  " + str + ":-");
      if (str == null)
         return;
      else {
         drawString(str);
         //parent.paintScroll();
      }
   }//append(String)

   private synchronized void drawString(String txt) {
      //Log.it("HegemonScroll.drawString:  comp_in_progress: "
      //       + comp_in_progress + "\nTEXT  -:" + txt + ":-");
      /* Assume text can and will be auto-wrapped.  All whitespace will be
       translated to spaces which will be consolidated as per HTML. */

      //this just screws things up in ScryMUD.  Perhaps in some other
      //application it would be more useful...
      if (false) { //!props.isModePRE()) {
         int len = txt.length();
         char sb[] = new char[len + 2];
         int sb_len;
      
         char ch;
         boolean last_was_ws = false;
         int j = 0;
         for (int i = 0; i<len; i++) {
            ch = txt.charAt(i);
            if (Character.isWhitespace(ch)) {
               if (last_was_ws) {
                  continue; //don't append it
               }
               else {
                  last_was_ws = true;
               }
            }//if was ws
            else {
               last_was_ws = false;
            }
            sb[j] = ch;
            j++;
         }//for (consolidate white space)      
         sb_len = j;

         
         /* when here:  sb contains the string we want to deal with. */
         /* now lets paint the string, wrapping as needed. */

         int width = this.getSize().width;
         FontMetrics fm = props.getFontMetrics();
         int max_adv = fm.getMaxAdvance();

         int str_width;
         int space_avail;
         int offset = 0;
         int num_chars;
         BreakIterator line_breaker = BreakIterator.getLineInstance();
         line_breaker.setText(new String(sb));

         int brk1 = 0;
         int brk2 = 0;
         int our_break = 0;
         
         while (true) { //lets do the entire array...
            //Log.it("In while:  sb: -:" + sb + ":- offset: -:" + offset +
            //       ":- sb.length: -:" + sb_len); 
            str_width = fm.charsWidth(sb, offset, sb_len - (offset + 1));
            space_avail = width - comp_in_progress.getWidth();
            
            if (str_width > space_avail) { /* gotta wrap the sucker */
               
               /* num chars we can paint... */
               num_chars = (int)(space_avail/max_adv); 
               
               brk1 = line_breaker.following(offset + num_chars);
               brk2 = line_breaker.previous(); /* this is the one we want */

               if (brk2 > offset) {
                  our_break = brk2;
               }
               else {
                  our_break = brk1;
               }

               comp_in_progress.addChars(sb, offset, (our_break - offset),
                                         props);

               offset = our_break + 1;
               addComponent(comp_in_progress);
               comp_in_progress = new ScrollComponentText(vert_spacing,
                                                          props);
            }//if had to wrap
            else {

               comp_in_progress.addChars(sb, offset, (our_break - offset),
                                         props);
               break;
            }
            
            if (offset >= (sb.length - 1)) { /* subtract one b/c starts at 0 */
               break; /* done */
            }
         }//while true
      }//if not in MODE_PRE
      else { //IN MODE PRE (we'll do line wrapping at least now)
         int len = txt.length();
         StringBuffer sb = new StringBuffer(100);
         char ch;
         int comp_len_sofar = 0;
         if (comp_in_progress != null) {
            comp_len_sofar = comp_in_progress.getWidth();
         }
         FontMetrics fm = props.getFontMetrics();
         Font font = props.getFont();
         int sz = font.getSize();
         int mx_size = parent.getSize().width - 40; //-20 is for scroll bar
         int last_ws_idx = 0;

         // Sanity checks for problems with closed frames and so on.
         if (mx_size < 200) {
            mx_size = 200;
         }

         for (int i = 0; i<len; i++) {
            ch = txt.charAt(i);

            if (Character.isWhitespace(ch)) {
               last_ws_idx = i;
            }

            if (fm == null) {
               /* gotta guestimate it then */
               comp_len_sofar += (sz >> 1);
            }//if
            else {
               comp_len_sofar += fm.charWidth(ch);
            }

            if (ch == '\t') { //if a tab
               sb.append("        ");
            }
            else if (ch == '\r') { //carriage return
               continue;
            }
            else if (comp_len_sofar > mx_size) {
               int my_len = sb.length();
               Log.instance().dbg("Comp_len_sofar: " + comp_len_sofar
                                  + " > mx_size: " + mx_size + " sb -:"
                                  + sb + "last_ws_idx: " + last_ws_idx
                                  + " sb.length(): " + sb.length());
               if (last_ws_idx > 0) {
                  my_len = sb.length() - (i - last_ws_idx);
                  i = last_ws_idx;
               }

               Log.instance().dbg("i: " + i + " my_len: " + my_len);
               char[] buf = new char[my_len + 1];
               sb.getChars(0, my_len, buf, 0);
               comp_in_progress.addChars(buf, 0, my_len, props);
               addComponent(comp_in_progress);
               comp_in_progress = new ScrollComponentText(vert_spacing, props);
               last_ws_idx = 0;
               sb = new StringBuffer(100);
               comp_len_sofar = 0;
            }
            else if (ch == '\n') { //newline
               //Log.it("Found a newline...\n");
               int my_len = sb.length();
               if (my_len > 0) {
                  //Log.it("\tLength > 0");
                  char[] buf = new char[my_len + 1];
                  sb.getChars(0, my_len, buf, 0);
                  comp_in_progress.addChars(buf, 0, my_len, props);
                  addComponent(comp_in_progress);
                  comp_in_progress = new ScrollComponentText(vert_spacing,
                                                             props);
                  sb = new StringBuffer(100);
               }//if the buffer contains something...
               else {
                  //Log.it("\tLength == 0.");
                  if (comp_in_progress.getWidth() > 0) {
                     addComponent(comp_in_progress);
                     comp_in_progress = new ScrollComponentText(vert_spacing,
                                                                props);
                  }//if
                  addComponent(new ScrollComponentNewline(12, vert_spacing));
               }
               last_ws_idx = 0;
               comp_len_sofar = 0;
            }
            else {
               sb.append(ch);
            }
         }//for

         if ((len = sb.length()) > 0) {
            //Log.it("\tWas some left over...");
            comp_in_progress.addString(sb.toString(), props);
            if (comp_in_progress != last_added_component) {
               addComponent(comp_in_progress);
            }//if
         }//if we have some left over...
      }//else, IN MODE_PRE
   }//drawString

   public synchronized void pushColor(String color_name) {
      try {
         Color c = ColorUtils.getColor(color_name);
         props.pushColor(c);
      }
      catch (ColorNameException e) {
         Log.instance().err(e.toString());
      }
   }//pushColor(String)
   

   public synchronized void pushColor(Color c) {
      props.pushColor(c);
   }//pushColor(Color)
   
   public synchronized void pushFont(Font f) {
      if (f != null) {
         props.pushFont(f);
      }//if
   }//setFont

   public synchronized void popColor() {
      props.popColor();
   }

   public synchronized void popFont() {
      props.popFont();
   }

   public void drawNewline() {
      addComponent(new ScrollComponentNewline(vert_spacing * 4,
                   props.figurePointSize((props.getTextSize()).intValue())));
   }

}//HegemonScroll