home *** CD-ROM | disk | FTP | other *** search
- package sub_arctic.lib;
- import sub_arctic.output.style;
- import sub_arctic.output.style_manager;
- import sub_arctic.output.drawable;
- import sub_arctic.output.loaded_image;
- import sub_arctic.input.pressable;
- import sub_arctic.input.event;
-
- import java.util.Vector;
- import java.util.Hashtable;
- import java.awt.Font;
- import java.awt.Rectangle;
- import java.awt.Point;
-
- /**
- * This class implements a style-neutral menu bar. It should be able to
- * handle mac, windoze, and motif/unix style menu bars, but not NextStep
- * style.
- * @author Ian Smith
- */
- public class menubar
- extends base_interactor implements pressable, menu_notifier {
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- /**
- * This is where we store the cached value of our width
- */
- protected int cached_width=-1;
-
- /**
- * This is where we hold the hotspots for the menubar's left objects
- */
- protected Vector left_hotspots;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is where we hold the hotspots for the menubar's right objects
- */
- protected Vector right_hotspots;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Our current menubar image.
- */
- protected loaded_image bar_image;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This hashtable keeps track of the mapping from the menu items
- * to the buttons on the menu.
- */
- protected Hashtable button_to_menu;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Left hand set of items.
- */
- protected Vector left_items;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Right hand set of items.
- */
- protected Vector right_items;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Left hand set of images
- */
- protected Vector left_images;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Right hand set of images
- */
- protected Vector right_images;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the number of the currently depressed item. If no item
- * is currently depressed, this value will be -1.
- */
- protected int _currently_depressed=-1;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Which item is currently depressed? This returns -1 if there
- * is no currently depressed item.
- *
- * @return int the number of the currently depressed item.
- */
- public int currently_depressed() {return _currently_depressed;}
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Set the currently depressed item. This may involve the popping
- * up of menus.
- * @param int i the item number you want depressed.
- * @param boolean t true if the item is in the left hand set of objects.
- * @param event evt the event which is causing this button to be depressed.
- */
- public void set_currently_depressed(int i, boolean t, event evt) {
- _currently_depressed=i;
- set_current_is_left(t);
- Object tmp;
- menu m;
- Point p;
-
- /* look for item on the left side */
- if (_current_is_left) {
- /* find the item */
- tmp=left_items.elementAt(_currently_depressed);
- } else {
- /* its on the right side */
- tmp=right_items.elementAt(_currently_depressed);
- }
-
- /* is there a menu for this object? */
- if (!button_to_menu.containsKey(tmp)) {
- System.err.println("No menu for that item!");
- return;
- }
-
- /* it is there, extract it */
- m=(menu)button_to_menu.get(tmp);
-
- /* derive the coordinates of the new menu in global coords*/
- if (_current_is_left) {
- p=compute_menu_location(((Rectangle)left_hotspots.
- elementAt(_currently_depressed)),m);
- } else {
- p=compute_menu_location(((Rectangle)right_hotspots.
- elementAt(_currently_depressed)),m);
- }
-
- /* set the coordinates to those returned*/
- m.set_x(p.x);
- m.set_y(p.y);
- get_top_level().add_child(m);
-
- /* tell the menu agent to go */
- menu.agent().set_focus_to(m,evt,null,this);
-
- /* we are now damaged */
- damage_self();
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a boolean which says if the selected item is in the right
- * or the left hand set. This value should only be consulted when
- * the value of _current is not -1.
- */
- protected boolean _current_is_left=true;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Retrieve a boolean indicating if the currently selected item
- * is on the left.
- *
- * @return boolean returns true if the item is in the left hand set of
- * objects.
- */
- public boolean current_is_left() { return _current_is_left;}
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Set the flag indicating if the currently selected item is on the left.
- * @param boolean n the new value.
- */
- public void set_current_is_left(boolean n) { _current_is_left=n;}
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Create a menubar from two Vectors of Strings.
- * @param Vector left the strings to start the menus with from the left.
- * @param Vector right the strings to start the menus with from the right.
- * @param int w width of the menu bar.
- */
- public menubar(Vector left, Vector right,int w) {
- super(0,0,w,10);
-
- int i;
- button_to_menu=new Hashtable();
-
- /* we are just going to assume we don't need a copy here */
- /* we know that the style code will copy them for us */
- left_items=left;
- right_items=right;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function is called to derive the font from the default
- * font. This is here as a hook for easy overriding.
- *
- * @param Font f the default font.
- * @return Font the font to use on the menubar.
- */
- public Font derive_font(Font f) {
- return new Font(f.getName(), f.getStyle(), f.getSize()+2);
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Make the menubar images, given a set of left and right objects.
- * @param Vector left the array of strings or images for the left menus
- * @param Vector right the array of strings or images for the right menus
- * @param int w the width of the bar
- */
- public void style_changed() {
- loaded_image bar;
- style cs=style_manager.current_style();
- int w=w(),i;
- Vector left_tmp, right_tmp;
-
- /* we have to make a copy of the items here because we are going to
- * muck with these items in the first one in a minute*/
- left_tmp=new Vector();
- left_tmp.setSize(left_items.size());
- for(i=0; i<left_items.size(); ++i) {
- left_tmp.setElementAt(left_items.elementAt(i),i);
- }
-
- right_tmp=new Vector();
- right_tmp.setSize(right_items.size());
- for(i=0; i<right_items.size(); ++i) {
- right_tmp.setElementAt(right_items.elementAt(i),i);
- }
-
- /* make the hotspots */
- left_hotspots=new Vector();
- right_hotspots=new Vector();
-
- /* draw the bar */
- bar_image=cs.make_menubar_images(left_items,right_items,w,
- derive_font(style_manager.default_font()),
- left_hotspots, right_hotspots);
-
- /* now the images are in left_items and right_items */
- left_images=left_items;
- right_images=right_items;
-
- /* now we can put the items back */
- left_items=left_tmp;
- right_items=right_tmp;
-
- /* can't resize in y */
- set_intrinsic_h(bar_image.height());
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Draw the object on a given drawable.
- * @param drawable d the surface to draw on
- */
- public void draw_self_local(drawable d) {
- loaded_image down_image;
- style cs;
- int offset;
- Rectangle r;
-
- /* can't make a menubar arbitrarily small */
- if (bar_image==null) {
- return;
- }
- d.drawImage(bar_image,0,0);
-
- /* check for possible selection */
- if (currently_depressed()!=-1) {
-
- /* get the image... is it left or right side? */
- if (current_is_left()) {
- down_image=(loaded_image)left_images.
- elementAt(currently_depressed());
- r=(Rectangle)left_hotspots.elementAt(currently_depressed());
- } else {
- down_image=(loaded_image)right_images.
- elementAt(currently_depressed());
- r=(Rectangle)right_hotspots.elementAt(currently_depressed());
- }
-
- /* find out the offset from the style system */
- cs=style_manager.current_style();
- offset=cs.menubar_image_shift();
-
- /* got the image and the location so blit it */
- d.drawImage(down_image,r.x,offset);
- }
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Indicate that we intrinsically constrain height.
- * Thus, it is not modifiable by either the programmer or user.
- * @return int return the constant indicating the correct intrinsic dimension
- */
- public int intrinsic_constraints() {
- return H ;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Dispatch mouse button press input to the object. Return true if
- * this event was consumed.
- *
- * @param event evt the event to dispatch.
- * @param Object user_info the pick-time value.
- */
- public boolean press(event evt, Object user_info) {
- /* walk the hotspots, looking for this coord */
- int i,selection=0;
- boolean got_it=false,on_left=true;
- Rectangle r;
-
- /* if we don't have any image, we can't do anything */
- if (bar_image==null) return false;
-
- /* look at left hand hotspots */
- for (i=0; i<left_hotspots.size(); ++i) {
- r=(Rectangle)left_hotspots.elementAt(i);
- /* inside? */
- if ((evt.local_x()>=r.x) &&
- (evt.local_x()<=r.x+r.width) &&
- (evt.local_y()>=r.y) &&
- (evt.local_y()<=r.y+r.height)) {
- /* it is inside */
- got_it=true;
- selection=i;
- break;
- }
- }
-
- /* if its not a left one, its might be a right one */
- if (!got_it) {
- /* look at left hand hotspots */
- for (i=0; i<right_hotspots.size(); ++i) {
- r=(Rectangle)right_hotspots.elementAt(i);
- /* inside? */
- if ((evt.local_x()>=r.x) &&
- (evt.local_x()<=r.x+r.width) &&
- (evt.local_y()>=r.y) &&
- (evt.local_y()<=r.y+r.height)) {
- /* it is inside */
- got_it=true;
- on_left=false;
- selection=i;
- }
- }
- }
-
- /* if they didn't press on anything, we still want to consume the
- event so stuff behind us doesn't end up with it */
- if (got_it==false) {
- return true;
- }
-
- /* set the menu to go down */
- set_currently_depressed(selection,on_left,evt);
-
- /* we're done, the menu agent will handle it from here */
- return true;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Dispatch mouse button release to the object. Return true if event
- * was consumed.
- * @param event evt the event to dispatch.
- * @param Object user_info the pick-time value.
- */
- public boolean release(event evt, Object user_info) {
- return false;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Set the menu associated with a particular item (String or
- * loaded_image). If you try to set a menu for a value that is not in the
- * current set of objects, we ignore this call. This call
- * will break if you have multiple items on the menu which
- * have the same string; however, this is so bad from a UI design
- * standpoint that I am not going to worry about it.
- *
- * @param Object obj the menu item's string or image.
- * @param menu m the menu to associate with that item.
- */
- public void associate_menu(Object obj, menu m) {
-
- /* look for a left item set */
- if (left_items.indexOf(obj)!=-1) {
- button_to_menu.put(obj,m);
- return;
- }
-
- /* might be on the right */
- if (right_items.indexOf(obj)!=-1) {
- button_to_menu.put(obj,m);
- return;
- }
- /* we just ignore calls which don't correspond to things that are
- on the menu */
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function computes the menu location given a hotspot rectangle
- * in our coordinate system. This is here as a hook for easy overriding
- * in case you want to put a menubar at the bottom or sides of the
- * screen.
- *
- * @param Rectangle r the rectangle of the hotspot the user moused on.
- * @param menu m the menu to pop up.
- * @return Point the point to place the menu at (in the coord sys of the
- * top level that this menubar is in).
- */
- public Point compute_menu_location(Rectangle r,menu m) {
-
- /* get this in the coord system of the top level */
- Point p=local_to_global(new Point(r.x,r.y));
-
- /* now just put it just below the rectangle */
- p.y+=h();
-
- /* make sure it fits in the top_level */
- if (p.x+m.w()>get_top_level().w()) {
- /* right justify it if it didn't fit */
- p.x=get_top_level().w()-m.w();
- }
-
- return p;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function gets called when the menubar's popped down menu
- * gets finished. This function sets the menu bar back to its default
- * state.
- */
- public void menu_done() {
- _currently_depressed=-1;
- damage_self();
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function is called to alert the notifier that the interaction
- * is now over their area. The notifier should return true if it
- * modified the set of objects in the menu focus in response to
- * this call.
- *
- * @param int x the x coordinate (in the notifiers coordinate system) of
- the cursor.
- * @param int y the y coordinate (in the notifiers coordinate system) of
- * the cursor.
- * @param event evt the event we are testing.
- * @return boolean true if the notifier modified the focus set of the menu
- * agent in response to this.
- */
- public boolean menu_modify(int x,int y,event evt) {
- int i;
- Rectangle r;
- boolean is_left=true;
- int selection=-1;
-
- /* look at the left hotspots */
- for (i=0; i<left_hotspots.size(); ++i) {
- r=(Rectangle)left_hotspots.elementAt(i);
- /* inside? */
- if ((x>=r.x) && (x<=r.x+r.width) &&
- (y>=r.y) && (y<=r.y+r.height)) {
- selection=i;
- break;
- }
- }
-
- /* did we find it on the left */
- if (selection==-1) {
- /* look at the right hotspots */
- for (i=0; i<right_hotspots.size(); ++i) {
- r=(Rectangle)right_hotspots.elementAt(i);
- /* inside? */
- if ((x>=r.x) && (x<=r.x+r.width) &&
- (y>=r.y) && (y<=r.y+r.height)) {
- selection=i;
- is_left=false;
- break;
- }
- }
- }
-
- /* did we find it at all? */
- if (selection==-1) return false;
-
- /* did we find it but its already down? */
- if ((selection==currently_depressed()) &&
- (is_left==current_is_left())) return false;
-
- /* clear the focus set */
- menu.agent().clear_focus(evt);
-
- /* depress this object */
- set_currently_depressed(selection,is_left,evt);
-
- /* its ok , we fixed it */
- return true;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Configure the object to ensure it has its proper bounds and has declared
- * all its damage.
- */
- public void configure() {
- super.configure();
- /* if we are a different width, recompute the image */
- if (w()!=cached_width) {
- cached_width=w();
- style_changed();
- }
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- }
- /*=========================== COPYRIGHT NOTICE ===========================
-
- This file is part of the subArctic user interface toolkit.
-
- Copyright (c) 1996 Scott Hudson and Ian Smith
- All rights reserved.
-
- The subArctic system is freely available for most uses under the terms
- and conditions described in
- http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html
- and appearing in full in the lib/interactor.java source file.
-
- The current release and additional information about this software can be
- found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
-
- ========================================================================*/
-