// Copyright 1996, Marimba Inc. All Rights Reserved.


// @(#)FolderWidget.java, 1.61, 11/28/96





package marimba.gui;





import java.awt.*;


import java.util.*;





import marimba.persist.*;





/**


 * A tabbed folder widget. This widget can only contain PageWidgets.


 *


 * @author	Arthur van Hoff


 * @author	Klaas Waslander


 * @version 	1.61, 11/28/96


 */


public class FolderWidget extends ContainerWidget {


    public static Font  defaultTabFont = new Font("Dialog", Font.PLAIN, 10);


    public static Font  defaultSelectFont = new Font("Dialog", Font.PLAIN, 10);





    /**


     * The font that is used for the text in the tab controls.


     * @see #getTabFont


     * @see #setTabFont


     */


    public Font  tabFont = defaultTabFont;





    /**


     * The font used for the text in the tab controls when


     * the page related with that tab control is shown.


     * @see #getSelectFont


     * @see #setSelectFont


     */


    public Font  selectFont = defaultSelectFont;





    /**


     * When the page is being switched, the folder can automatically focus


     * on the first widget anywhere or the first widget within the folder,


     * or just do nothing in order to let the tab control get the focus.


     * @see #getFocusMode


     * @see #setFocusMode


     * @see #focusOptions


     */


    public int  focusMode = INSIDE;





    /**


     * The possible options for the autofocus.


     * @see #getFocusOptions


     * @see #focusMode


     */


    public static Options  focusOptions = new Options();


    static {


	focusOptions.add("none", NONE);


	focusOptions.add("anywhere", ANYWHERE);


	focusOptions.add("folder", INSIDE);


    }





    /**


     * A folder can hide the tab controls (none)


     * or display them at the top (top) of the folder.


     * @see #getTabMode


     * @see #setTabMode


     * @see #tabOptions


     */


    public int  tabMode = TOP;





    /**


     * The possible options for the tabMode.


     * @see #getTabOptions


     * @see #tabMode


     */


    public static Options  tabOptions = new Options();


    static {


	tabOptions.add("none", NONE);


	tabOptions.add("top", TOP);


    }





    /**


     * The number of the current page.


     * @see #getIntegerValue


     * @see #currentPageNumber


     * @see #getValue


     * @see #setValue


     */


    public int  current = -1;








    /**


     * Create a folder with one page titled "page".


     */


    public FolderWidget() {


	newPage();


	currentPage().setText("page");


    }





    /**


     * Create a folder with the specified number of pages


     * titled "page 1" to "page npages".


     */


    public FolderWidget(int npages) {


	for (int index = 0; index < npages; index++) {


	    newPage();


	    currentPage().setText("page " + (index+1));


	}


    }





    /**


     * Create a folder with the specified number of pages


     * and page titles.


     */


    public FolderWidget(int npages, String pagetitles[]) {


	for (int index = 0; index < npages; index++) {


	    newPage();


	    currentPage().setText(pagetitles[index]);


	}


    }





    /**


     * Get the properties.


     */


    public void getProperties(PropertyList list) {


	super.getProperties(list);


	list.setBoolean("disabled", disabled, false);


	list.setOption("autoFocus", focusOptions, focusMode, INSIDE);


	list.setOption("tabmode", tabOptions, tabMode, TOP);


	list.setFont("tabfont", tabFont, defaultTabFont);


	list.setFont("selectfont", selectFont, defaultSelectFont);


    }





    /**


     * Set the properties.


     */


    public void setProperties(PropertyList list) {


	super.setProperties(list);


	disabled = list.getBoolean("disabled", false);


	setFocusMode(list.getOption("autoFocus", focusOptions, INSIDE));


	setTabMode(list.getOption("tabmode", tabOptions, TOP));


	tabFont = list.getFont("tabfont", defaultTabFont);


	selectFont = list.getFont("selectfont", defaultSelectFont);


	for (int i = nwidgets ; i-- > 0 ;) {


	    Widget w = widgets[i];


	    w.hide();


	}





	////// REMIND: remove after version 0.9 ///////


	if (!list.getBoolean("clickable", true)) {


	    disabled = true;


	}


	boolean oldFocusMode = list.getBoolean("autofocus", false);


	if (oldFocusMode) {


	    focusMode = ANYWHERE;


	}


	////////////// remove until here //////////////


	


	current = -1;


	gotoPage(0);


    }





    /**


     * Get the font for tab controls.


     * @see #tabFont


     */


    public Font getTabFont() {


	return tabFont;


    }





    /**


     * Set the font for tab controls.


     * @see #tabFont


     */


    public void setTabFont(Font tabFont) {


	if (tabFont != null && !tabFont.equals(this.tabFont)) {


	    this.tabFont = tabFont;


	    layout();


	    repaint();


	}


    }





    /**


     * Get the font for the selected tab control.


     * @see #selectFont


     */


    public Font getSelectFont() {


	return selectFont;


    }





    /**


     * Set the font for the selected tab control.


     * @see #selectFont


     */


    public void setSelectFont(Font selectFont) {


	if (selectFont != null && !selectFont.equals(this.selectFont)) {


	    this.selectFont = selectFont;


	    layout();


	    repaint();


	}


    }





    /**


     * Get the content.


     */


    public Widget getContent() {


	return currentPage();


    }





    /**


     * Get the given page.


     */


    public PageWidget getPage(int pageno) {


	return (PageWidget) widgets[pageno];


    }





    /**


     * Get a page by name.


     */


    public PageWidget getPage(String name) {


	return (PageWidget)getWidget(name);


    }





    /**


     * Get the possible options for the focusMode.


     * @see #focusOptions


     */


    public Options getFocusOptions() {


	return focusOptions;


    }





    /**


     * Get the focus mode.


     * @see #focusMode


     */


    public int getFocusMode() {


	return focusMode;


    }





    /**


     * Set the focus mode.


     * @see #focusMode


     */


    public void setFocusMode(int focusMode) {


	if (this.focusMode != focusMode) {


	    this.focusMode = focusMode;


	}


    }





    /**


     * Get the possible options for the tabMode.


     * @see #tabOptions


     */


    public Options getTabOptions() {


	return tabOptions;


    }





    /**


     * Get the tab mode.


     * @see #tabMode


     */


    public int getTabMode() {


	return tabMode;


    }





    /**


     * Set the tab mode.


     * @see #tabMode


     */


    public void setTabMode(int tabMode) {


	if (this.tabMode != tabMode) {


	    this.tabMode = tabMode;


	    invalidate();


	    repaint();


	}


    }





    /**


     * Return the current page number. This will return -1


     * if there are no pages.


     * @see #current


     */


    public int getIntegerValue() {


	return current;


    }





    /**


     * Return the current page number. This will return -1


     * if there are no pages.


     * @see #current


     */


    public int currentPageNumber() {


	return current;


    }





    /**


     * Get the current page name.


     */


    public String getStringValue() {


	return (current <= 0) ? null : widgets[current].getName();


    }





    /**


     * Get the current page number.


     * @see #current


     */


    public Object getValue() {


	return new Integer(getIntegerValue());


    }





    /**


     * Set the current page.


     * @see #current


     */


    public void setValue(Object value) {


	if (value instanceof String) {


	    gotoPage((String)value);


	} else if (value instanceof Number) {


	    gotoPage(((Number)value).intValue());


	} else if (value instanceof Widget) {


	    gotoPage((Widget)value);


	}


    }





    /**


     * Return the current page.


     */


    public PageWidget currentPage() {


	return (current >= 0) ? getPage(current) : null;


    }





    /**


     * Layout the page.


     */


    public void layout() {


	if (current >= 0) {


	    Widget w = widgets[current];


	    int h = getTabSpace();


	    if (tabMode == NONE) {


		w.reshape(0, h, width, height-h);


	    } else {


		w.reshape(0, h, width, height-h);


	    }


	    w.validate();


	}


    }





    /**


     * Add a new empty page.


     */


    public void newPage() {


	PageWidget page = new PageWidget();


	page.fillMode = NONE;


	page.lineMode = NONE;


	if (tabMode != NONE) {


	    page.setText("page");


	}


	addPage(page);


    }





    /**


     * Add a new page.


     */


    public void addPage(PageWidget page) {


	add(current + 1, page);


	nextPage();


    }





    /**


     * Delete the current page.


     */


    public void deletePage() {


	if (nwidgets > 0) {


	    Widget w = widgets[current];


	    previousPage();


	    remove(w);


	    if (current >= nwidgets) {


		current = nwidgets-1;


	    }


	}


	if (nwidgets == 0) {


	    newPage();


	    current = 0;


	}


    }





    /**


     * Goto a page by name.


     */


    public void gotoPage(String name) {


	gotoPage(name, true);


    }





    /**


     * Goto a page by name, with or without the set autoFocus.


     */


    void gotoPage(String name, boolean autoFocus) {


	Widget w = findWidget(name);


	if (w != null) {


	    gotoPage(w, autoFocus);


	} else {


	    System.out.println("gotoPage: no page called '" + name + "'");


	}


    }





    /**


     * Goto a page by value.


     */


    public void gotoPage(Widget w) {


	gotoPage(w, true);


    }





    /**


     * Goto a page by value, with or without the set autoFocus.


     */


    void gotoPage(Widget w, boolean autoFocus) {


	if (w != null) {


	    for (int i = 0 ; i < nwidgets ; i++) {


		if (widgets[i] == w) {


		    gotoPage(i, autoFocus);


		    return;


		}


	    }


	}


    }





    /**


     * Goto a new page by pagenumber.


     */


    public void gotoPage(int n) {


	gotoPage(n, true);


    }





    /**


     * Goto a new page by pagenumber, with or without the set autoFocus


     */


    void gotoPage(int n, boolean autoFocus) {


	if (n < 0) {


	    n = 0;


	} else if (n >= nwidgets) {


	    n = nwidgets-1;


	}


	if (n != current) {


	    if (current >= 0) {


		widgets[current].hide();


	    }


	    current = n;


	    widgets[n].show();


	    if (autoFocus) {


		focusFolder();


	    }


	    repaint();


	}


    }





    /**


     * Goto the next page.


     */


    public void nextPage() {


	nextPage(true);


    }





    /**


     * Goto the next page, with or without the set autoFocus.


     */


    void nextPage(boolean autoFocus) {


	gotoPage((current+1 >= nwidgets) ? 0 : current+1, autoFocus);


    }





    /**


     * Goto the previous page.


     */


    public void previousPage() {


	previousPage(true);


    }





    /**


     * Goto the previous page, with or without the set autoFocus.


     */


    void previousPage(boolean autoFocus) {


	gotoPage((current == 0) ? nwidgets-1 : current-1, autoFocus);


    }





    /**


     * Goto the first page.


     */


    public void firstPage() {


	firstPage(true);


    }





    /**


     * Goto the first page, with or without the set autoFocus.


     */


    void firstPage(boolean autoFocus) {


	gotoPage(0, autoFocus);


    }





    /**


     * Goto the last page.


     */


    public void lastPage() {


	lastPage(true);


    }





    /**


     * Goto the last page, with or without the set autoFocus.


     */


    void lastPage(boolean autoFocus) {


	gotoPage(nwidgets-1, autoFocus);


    }





    /**


     * Goto the next page with a title.


     */


    public void nextTitledPage() {


	nextTitledPage(true);


    }





    /**


     * Goto the next page with a title,


     * with or without the set autoFocus.


     */


    void nextTitledPage(boolean autoFocus) {


	if (hasTitels()) {


	    nextPage(autoFocus);


	    while (currentPage().title == null) {


		nextPage(autoFocus);


	    }


	}


    }





    /**


     * Goto the previous page with a title.


     */


    public void previousTitledPage() {


	previousTitledPage(true);


    }





    /**


     * Goto the previous page with a title,


     * with or without the set autoFocus.


     */


    void previousTitledPage(boolean autoFocus) {


	if (hasTitels()) {


	    previousPage(autoFocus);


	    while (currentPage().title == null) {


		previousPage(autoFocus);


	    }


	}


    }





    /**


     * Goto the first page with a title.


     */


    public void firstTitledPage() {


	firstTitledPage(true);


    }





    /**


     * Goto the first page with a title,


     * with or without the set autoFocus.


     */


    void firstTitledPage(boolean autoFocus) {


	if (hasTitels()) {


	    firstPage(autoFocus);


	    while (currentPage().title == null) {


		nextPage(autoFocus);


	    }


	}


    }





    /**


     * Goto the last page with a title.


     */


    public void lastTitledPage() {


	lastTitledPage(true);


    }





    /**


     * Goto the last page with a title,


     * with or without the set autoFocus.


     */


    void lastTitledPage(boolean autoFocus) {


	if (hasTitels()) {


	    lastPage(autoFocus);


	    while (currentPage().title == null) {


		previousPage(autoFocus);


	    }


	}


    }





    /**


     * Set the input focus, which depends on the focusMode.


     */


    void focusFolder() {


	switch (focusMode) {


	    case NONE:


		break;


	    case ANYWHERE:


		firstFocus();


		break;


	    case INSIDE:


		firstInFocus();


		break;


	}


	if (currentFocus() == null) {


	    requestFocus();


	}


    }





    /**


     * Find a widget in this presentation.


     */


    public Widget findWidget(String nm) {


	Widget w = super.findWidget(nm);


	if (w == null) {


	    if (nm.equals("next")) {


		return (nwidgets <= 0) ? null : widgets[(current+1) >= nwidgets ? 0 : current+1];


	    }


	    if (nm.equals("previous")) {


		return (nwidgets <= 0) ? null : widgets[current == 0 ? nwidgets-1 : current-1];


	    }


	    if (nm.equals("first")) {


		return (nwidgets <= 0) ? null : widgets[0];


	    }


	    if (nm.equals("last")) {


		return (nwidgets <= 0) ? null : widgets[nwidgets-1];


	    }


	    if (nm.equals("current")) {


		return (current < 0) ? null : widgets[current];


	    }


	}


	return w;


    }





    /**


     * Get the space between the top of the widget and


     * the bottom of the tab-controls.


     */


    int getTabSpace() {


	switch (tabMode) {


	  case TOP:


	    int h = Math.max(getFontMetrics(selectFont).getHeight(),


			    getFontMetrics(tabFont).getHeight()) + 8;


	    return ((h+4)/5)*5;





	  default:


	    return 0;


	}


    }





    /**


     * Get the height of the tabs.


     */


    int getTabHeight() {


	switch (tabMode) {


	  case TOP:


	    return Math.max(getFontMetrics(selectFont).getHeight(),


			    getFontMetrics(tabFont).getHeight()) + 8;





	  default:


	    return 0;


	}


    }





    /**


     * Checks if there is a page that has a title.


     * Returns false if all pages have nulltitels.


     */


    boolean hasTitels() {


	boolean  result = false;


	for (int index = 0; index < nwidgets; index++) {


	    if (getPage(index).title != null) {


		result = true;


		break;


	    }


	}


	return result;


    }





    /**


     * Returns the number of pages with no title next to the given


     * pagenumber at the right (pagenr starts at zero and ends at npages-1).


     */


    int nullTitelsRightOf(int pagenr) {


	int  result = 0;


	for (int offset = 1; pagenr + offset < nwidgets; offset++) {


	    if (getPage(pagenr + offset).title == null) {


		result++;


	    } else {


		break;


	    }


	}


	return result;


    }





    /**


     * Returns the number of pages with no title next to the given


     * pagenumber at the left (pagenr starts at zero and ends at npages-1).


     */


    int nullTitelsLeftOf(int pagenr) {


	int  result = 0;


	for (int offset = 1; pagenr - offset >= 0; offset++) {


	    if (getPage(pagenr - offset).title == null) {


		result++;


	    } else {


		break;


	    }


	}


	return result;


    }





    /**


     * Paint the folders.


     */


    public void paint(Graphics g, int cx, int cy, int cw, int ch) {


	switch (tabMode) {


	  case TOP:


	    int  tabHeight = getTabHeight();


	    int  tabSpace = getTabSpace();


	    int  emptySpace = tabSpace - tabHeight;


	    Color  brighter = Bevel.getBrighter(background, true);


	    Color  darker = Bevel.getDarker(background, true);





	    // draw the first outline with shadow (bottom, left and right border)


	    g.setColor(Color.black);


	    g.drawLine(0, height-1, width, height-1);


	    g.drawLine(width-1, tabSpace, width-1, height);


	    g.setColor(darker);


	    g.drawLine(1, height-2, width-2, height-2);


	    g.drawLine(width-2, tabSpace, width-2, height-2);


	    g.setColor(brighter);


	    g.drawLine(0, tabSpace+1, 0, height-2);





	    // draw tabs (top border)


	    if (cy < tabSpace + 2) {


		FontMetrics  fm1 = g.getFontMetrics(tabFont);


		FontMetrics  fm2 = g.getFontMetrics(selectFont);


		int  x = 2;





		for (int i = 0 ; i < nwidgets ; i++) {


		    String title = getPage(i).title;


		    if (title == null) {


			continue;


		    }


		    if (i == current) {


			g.setColor(brighter);


			g.drawLine(x-2, emptySpace+4, x-2, tabSpace+1);





			int strw = fm2.stringWidth(title);


			int w = strw + 14;


			


			g.setColor(Color.black);


			g.drawLine(x+w-1, emptySpace+4, x+w-1, tabSpace+1);


			g.drawLine(x+w-2, emptySpace+3, x+w-2, tabSpace+1);


			g.setColor(darker);


			g.drawLine(x+w-2, emptySpace+4, x+w-2, tabSpace+1);


			g.setColor(foreground);


			g.setFont(selectFont);


			g.drawString(title, x+(w-strw)/2-1, tabSpace-(fm2.getDescent()+3));


			g.setColor(brighter);


			g.drawLine(x, emptySpace+2, x+w-3, emptySpace+2);


			g.drawLine(x-1, emptySpace+3, x-1, emptySpace+3);





			if (hasFocus()) {


			    g.setColor(getFocusColor());


			    Dash.drawFocusRect(g, x+1, emptySpace+5, w-4, tabHeight-5);


			}


			x += w;


		    } else {


			int strw = fm1.stringWidth(title);


			int w = strw + 14;





			// draw '3D'-border at the right


			if (i + nullTitelsRightOf(i) + 1 != current) {


			    g.setColor(Color.black);


			    g.drawLine(x+w-2, emptySpace+6, x+w-2, tabSpace);


			    g.drawLine(x+w-3, emptySpace+5, x+w-3, emptySpace+5);


			    g.setColor(darker);


			    g.drawLine(x+w-3, emptySpace+6, x+w-3, tabSpace);


			}


			// draw text


			g.setColor(foreground);


			g.setFont(tabFont);


			g.drawString(title, x+(w-strw)/2-1, tabSpace-(fm1.getDescent()+1));





			// draw rest of borders


			g.setColor(brighter);


			if (i==0) {


			    g.drawLine(x-1, tabSpace+1, x+w-2, tabSpace+1);


			    g.drawLine(2, emptySpace+6, 2, tabSpace);


			    g.drawLine(3, emptySpace+5, 3, emptySpace+5);


			    // draw this line one pixel further to current tab


			    if (i + nullTitelsRightOf(i) + 1 != current) {


				g.drawLine(4, emptySpace+4, x+w-4, emptySpace+4);


			    } else {


				g.drawLine(4, emptySpace+4, x+w-3, emptySpace+4);


			    }


			} else {


			    if (i - nullTitelsLeftOf(i) - 1 != current) {


				g.drawLine(x-1, tabSpace+1, x+w-2, tabSpace+1);


				g.drawLine(x, emptySpace+5, x, emptySpace+5);


				g.drawLine(x-1, emptySpace+6, x-1, tabSpace);


				// draw this line one pixel further to current tab


				if (i + nullTitelsRightOf(i) + 1 != current) {


				    g.drawLine(x+1, emptySpace+4, x+w-4, emptySpace+4);


				} else {


				    g.drawLine(x+1, emptySpace+4, x+w-3, emptySpace+4);


				}


			    } else {


				g.drawLine(x, emptySpace+4, x+w-4, emptySpace+4);


				g.drawLine(x, tabSpace+1, x+w-2, tabSpace+1);


			    }


			}


			x += w;


		    }


		}


		g.setColor(brighter);


		if (current == nwidgets-1) {


		    g.drawLine(x, tabSpace+1, width-2, tabSpace+1);


		} else {


		    g.drawLine(x-1, tabSpace+1, width-2, tabSpace+1);


		}


	    }


	    g.clipRect(2, tabSpace + 2, width-4, height-(tabSpace+4));


	    break;


	}


    }





    /**


     * Find out which tab the mouse was clicked in. Returns -1 if


     * not clicked in a tab or clicked tab has no title.


     */


    public int findTab(int x, int y) {


	switch (tabMode) {


	    case TOP:


		FontMetrics fm1 = getFontMetrics(tabFont);


		FontMetrics fm2 = getFontMetrics(selectFont);


		int tx = 2;


		if (x < tx || y > getTabSpace() || y < (getTabSpace() - getTabHeight())) {


		    break;


		}


		// check the tab controls of all the pages


		for (int i = 0 ; i < nwidgets ; i++) {


		    String title = getPage(i).title;


		    if (title != null) {


			if (i == current) {


			    tx += fm2.stringWidth(title) + 14;


			} else {


			    tx += fm1.stringWidth(title) + 14;


			}


			if (x < tx && getPage(i).title != null) {


			    return i;


			}


		    }


		}


		break;


	}


	return -1;


    }





    /**


     * Get the tip for the tabs...


     */


    public String getTip(int x, int y) {


	int i = findTab(x, y);


	if (i >= 0) {


	    PageWidget pg = getPage(i);


	    String tip = pg.getTip();


	    if (tip != null) {


		return tip;


	    }


	}


	return super.getTip(x, y);


    }





    /**


     * Handle Events.


     */


    public boolean handleEvent(Event evt) {


	if (disabled) {


	    return super.handleEvent(evt);


	}


	switch (evt.id) {


	    case Event.MOUSE_DOWN:


		int i = findTab(evt.x, evt.y);


		if (i >= 0) {


		    if (i != current) {


			action();


			gotoPage(i, false);


			if (!hasFocus()) {


			    focusFolder();


			} else {


			    requestFocus();


			}


			repaint();


			return true;


		    } else {


			requestFocus();


		    }


		}


		return false;





	    case Event.KEY_PRESS:


		switch (evt.key) {


		    case '\t':


			if (evt.controlDown()) {


			    if (evt.shiftDown()) {


				action();


				previousTitledPage(false);


			    } else {


				action();


				nextTitledPage(false);


			    }


			    if (!hasFocus()) {


				focusFolder();


			    }


			    repaint();


			    return true;


			}


			break;


			


		}


		break;





	    case Event.KEY_ACTION:


		// don't do anything if the tab control


		// does not have the focus


		if (!focus) {


		    break;


		}


		switch (evt.key) {


		    case Event.LEFT:


		    case Event.UP:


			action();


			previousTitledPage(false);


			return true;





		    case Event.RIGHT:


		    case Event.DOWN:


			action();


			nextTitledPage(false);


			return true;





		    case Event.HOME:


			action();


			firstTitledPage(false);


			return true;





		    case Event.END:


			action();


			lastTitledPage(false);


			return true;


		}


		break;


	}


	return super.handleEvent(evt);


    }





    /**


     * A page was turned.


     */


    public void action() {


	postEvent(new Event(this, Event.ACTION_EVENT, currentPage()));


    }





    /**


     * This widget supports input focus.


     */


    public boolean focusInterest() {


	return !disabled && hasTitels() && tabMode != NONE;


    }


}


