/* Copyright (C) 2004 by Peter Eastman

   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. */

package artofillusion.ui;

import artofillusion.*;
import buoy.event.*;
import buoy.widget.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;

/**
 * This class provides a variety of static methods for performing useful UI related operations.
 */

public class UIUtilities
{
  
  /** Given a Window, center it in the screen. */
  
  public static void centerWindow(WindowWidget win)
  {
    Dimension d1 = Toolkit.getDefaultToolkit().getScreenSize();
    Rectangle d2 = win.getBounds();
    int x, y;
    
    x = (d1.width-d2.width)/2;
    y = (d1.height-d2.height)/2;
    if (x < 0)
      x = 0;
    if (y < 0)
      y = 0;
    win.setBounds(new Rectangle(x, y, d2.width, d2.height));
  }

  /** Given a BDialog, center it relative to a parent window. */
  
  public static void centerDialog(BDialog dlg, WindowWidget parent)
  {
    Rectangle r1 = parent.getBounds(), r2 = dlg.getBounds();
    int x, y;
    
    x = r1.x+(r1.width-r2.width)/2;
    y = r1.y+20;
    if (x < 0)
      x = 0;
    if (y < 0)
      y = 0;
    dlg.setBounds(new Rectangle(x, y, r2.width, r2.height));
  }

  
  /** Set up a Widget and all of its children to have the default font for the program. */
  
  public static void applyDefaultFont(Widget w)
  {
    if (ModellingApp.defaultFont == null)
      return;
    w.setFont(ModellingApp.defaultFont);
    if (w instanceof WidgetContainer && !(w instanceof BMenuBar))
    {
      Iterator children = ((WidgetContainer) w).getChildren();
      while (children.hasNext())
        applyDefaultFont((Widget) children.next());
    }
  }
  
  /** Set up a Widget and all of its children to have the default background color for the program. */
  
  public static void applyDefaultBackground(Widget w)
  {
    applyBackground(w, ModellingApp.APP_BACKGROUND_COLOR);
  }

  /** Set up a Widget and all of its children to have a specific background color. */
  
  public static void applyBackground(Widget w, Color color)
  {
    if (w instanceof WidgetContainer)
    {
      w.setBackground(color);
      Iterator children = ((WidgetContainer) w).getChildren();
      while (children.hasNext())
        applyBackground((Widget) children.next(), color);
    }
    else if (w instanceof BLabel)
      w.setBackground(color);
    else if (w instanceof BButton || w instanceof BComboBox)
      ((JComponent) w.getComponent()).setOpaque(false);
  }
  
  /** Given an BList, create an appropriate container for it.  This involves a properly configured
      BScrollPane, with an outline around it. */
  
  public static WidgetContainer createScrollingList(BList list)
  {
    BScrollPane scroll = new BScrollPane(list, BScrollPane.SCROLLBAR_AS_NEEDED, BScrollPane.SCROLLBAR_ALWAYS);
    scroll.setBackground(list.getBackground());
    scroll.setForceWidth(true);
    return BOutline.createBevelBorder(scroll, false);
  }
  
  /** Given a Widget, find the window that contains it.  If the Widget is not in a window, return null. */
  
  public static WindowWidget findWindow(Widget w)
  {
    if (w instanceof WindowWidget)
      return (WindowWidget) w;
    if (w == null)
      return null;
    return findWindow(w.getParent());
  }
  
  /** Given a Widget, find its parent BFrame.  If the Widget is inside a BFrame, that frame will be
      returned.  If it is inside a BDialog, this returns the dialog's parent frame.  Otherwise, 
      this returns null. */
  
  public static BFrame findFrame(Widget w)
  {
    if (w instanceof BFrame)
      return (BFrame) w;
    if (w == null)
      return null;
    return findFrame(w.getParent());
  }
  
  /** Break a string into lines which are short enough to easily display in a window. */
  
  public static String [] breakString(String s)
  {
    int lines = (s.length()/60)+1;
    if (lines < 2)
      return new String [] {s};
    int lineLength = s.length()/lines;
    Vector line = new Vector();
    int index = 0;
    while (index+lineLength < s.length())
    {
      int next = s.indexOf(' ', index+lineLength);
      if (next == -1)
        next = s.length();
      line.addElement(s.substring(index, next).trim());
      index = next;
    }
    if (index < s.length())
      line.addElement(s.substring(index).trim());
    String result[] = new String [line.size()];
    line.copyInto(result);
    return result;
  }
  

  /** Internal method for recursion. */
  private static void getMenuItemList(BMenu menu, Vector result)
  {
    int l = menu.getChildCount();
    MenuWidget m;

    for(int i = 0; i < l; ++i)
    {
      m = menu.getChild(i);
      if (m instanceof BMenu)
        getMenuItemList((BMenu)m, result);
      else
        result.add(m);
    }
  }

  /** Returns all MenuItems and CheckboxMenuItems (no Menus) of menubar. */
  public static MenuWidget[] getMenuItemList(BMenuBar menubar)
  {
    if (menubar == null)
      return new MenuWidget[0];
    Vector result = new Vector();
    int l = menubar.getChildCount();
    BMenu m;

    for(int i = 0; i < l; ++i)
    {
      m = menubar.getChild(i);
      getMenuItemList(m, result);
    }

    return (MenuWidget[])result.toArray(new MenuWidget[0]);
  }
  
  /** Returns all MenuItems and CheckboxMenuItems (no Menus) of menu. */
  public static MenuWidget[] getMenuItemList(BMenu menu)
  {
    Vector result = new Vector();
    getMenuItemList(menu, result);

    return (MenuWidget[])result.toArray(new MenuWidget[0]);
  }
  
  /** Returns all MenuItems and CheckboxMenuItems (no Menus) of menu. */
  public static MenuWidget[] getMenuItemList(BPopupMenu menu)
  {
    if (menu == null)
      return new MenuWidget[0];
    
    Vector result = new Vector();
    int l = menu.getChildCount();
    MenuWidget m;

    for(int i = 0; i < l; ++i)
    {
      m = menu.getChild(i);
      
      if (m instanceof BMenu)
        getMenuItemList((BMenu)m, result);
      else
        result.add(m);
    }

    return (MenuWidget[])result.toArray(new MenuWidget[0]);
  }

  /** Internal version for recursion. */
  protected static void recursivelyAddKeyPressedListeners(Widget w, Object listener)
  {
    if (!(w instanceof TextWidget))
      w.addEventLink(KeyPressedEvent.class, listener, "keyPressed");
    if (w instanceof WidgetContainer)
      {
        Iterator children = ((WidgetContainer) w).getChildren();
        while (children.hasNext())
          recursivelyAddKeyPressedListeners((Widget) children.next(), listener);
      }
  }
  
  /** We need to be notified of key events which take place anywhere in the window.  To make
      sure this happens, we need to add the window as a KeyPressed listener to every component
      contained in it. */
  
  public static void recursivelyAddKeyPressedListeners(Widget w)
  {
    recursivelyAddKeyPressedListeners(w, w);
  }

}
