PageRenderTime 221ms CodeModel.GetById 135ms app.highlight 77ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/GUIUtilities.java

#
Java | 1555 lines | 855 code | 165 blank | 535 comment | 169 complexity | 6e30854ef73f0c57f31f636477f8a55f MD5 | raw file
   1/*
   2 * GUIUtilities.java - Various GUI utility functions
   3 * :tabSize=8:indentSize=8:noTabs=false:
   4 * :folding=explicit:collapseFolds=1:
   5 *
   6 * Copyright (C) 1999, 2004 Slava Pestov
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * as published by the Free Software Foundation; either version 2
  11 * of the License, or any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  21 */
  22
  23package org.gjt.sp.jedit;
  24
  25//{{{ Imports
  26
  27import java.awt.Color;
  28import java.awt.Component;
  29import java.awt.Dimension;
  30import java.awt.Font;
  31import java.awt.Frame;
  32import java.awt.GraphicsConfiguration;
  33import java.awt.GraphicsDevice;
  34import java.awt.GraphicsEnvironment;
  35import java.awt.Image;
  36import java.awt.Rectangle;
  37import java.awt.Window;
  38import java.awt.event.ComponentAdapter;
  39import java.awt.event.ComponentEvent;
  40import java.awt.event.MouseEvent;
  41import java.awt.event.WindowAdapter;
  42import java.awt.event.WindowEvent;
  43import java.net.URL;
  44import java.util.Hashtable;
  45import java.util.Locale;
  46import java.util.StringTokenizer;
  47
  48import javax.swing.*;
  49
  50import org.gjt.sp.jedit.browser.VFSFileChooserDialog;
  51import org.gjt.sp.jedit.gui.EnhancedButton;
  52import org.gjt.sp.jedit.gui.FloatingWindowContainer;
  53import org.gjt.sp.jedit.gui.SplashScreen;
  54
  55import org.gjt.sp.jedit.gui.VariableGridLayout;
  56import org.gjt.sp.jedit.menu.EnhancedCheckBoxMenuItem;
  57import org.gjt.sp.jedit.menu.EnhancedMenu;
  58import org.gjt.sp.jedit.menu.EnhancedMenuItem;
  59import org.gjt.sp.jedit.syntax.SyntaxStyle;
  60import org.gjt.sp.jedit.syntax.Token;
  61import org.gjt.sp.util.Log;
  62//}}}
  63
  64/**
  65 * Various GUI functions.<p>
  66 *
  67 * The most frequently used members of this class are:
  68 *
  69 * <ul>
  70 * <li>{@link #loadIcon(String)}</li>
  71 * <li>{@link #confirm(Component,String,Object[],int,int)}</li>
  72 * <li>{@link #error(Component,String,Object[])}</li>
  73 * <li>{@link #message(Component,String,Object[])}</li>
  74 * <li>{@link #showPopupMenu(JPopupMenu,Component,int,int)}</li>
  75 * <li>{@link #showVFSFileDialog(View,String,int,boolean)}</li>
  76 * <li>{@link #loadGeometry(Window,String)}</li>
  77 * <li>{@link #saveGeometry(Window,String)}</li>
  78 * </ul>
  79 *
  80 * @author Slava Pestov
  81 * @version $Id: GUIUtilities.java 5451 2006-06-20 13:19:47Z kpouer $
  82 */
  83public class GUIUtilities
  84{
  85	//{{{ Some predefined icons
  86	/**
  87	 * @deprecated Use <code>GUIUtilities.loadIcon("new.gif");</code>
  88	 * instead.
  89	 */
  90	public static Icon NEW_BUFFER_ICON;
  91
  92	/**
  93	 * @deprecated Use <code>GUIUtilities.loadIcon("dirty.gif");</code>
  94	 * instead.
  95	 */
  96	public static Icon DIRTY_BUFFER_ICON;
  97
  98	/**
  99	 * @deprecated Use <code>GUIUtilities.loadIcon("readonly.gif");</code>
 100	 * instead.
 101	 */
 102	public static Icon READ_ONLY_BUFFER_ICON;
 103
 104	/**
 105	 * @deprecated Use <code>GUIUtilities.loadIcon("normal.gif");</code>
 106	 * instead.
 107	 */
 108	public static Icon NORMAL_BUFFER_ICON;
 109
 110	/**
 111	 * @deprecated Use <code>GUIUtilities.loadIcon("jedit-icon.gif");</code>
 112	 * instead.
 113	 */
 114	public static Icon WINDOW_ICON;
 115	//}}}
 116
 117	//{{{ Icon methods
 118
 119	//{{{ setIconPath() method
 120	/**
 121	 * Sets the path where jEdit looks for icons.
 122	 * @since jEdit 4.2pre5
 123	 */
 124	public static void setIconPath(String iconPath)
 125	{
 126		GUIUtilities.iconPath = iconPath;
 127		if(icons != null)
 128			icons.clear();
 129	} //}}}
 130
 131	//{{{ loadIcon() method
 132	/**
 133	 * Loads an icon.
 134	 * @param iconName The icon name
 135	 * @since jEdit 2.6pre7
 136	 */
 137	public static Icon loadIcon(String iconName)
 138	{
 139		if(icons == null)
 140			icons = new Hashtable();
 141
 142		// check if there is a cached version first
 143		Icon icon = (Icon)icons.get(iconName);
 144		if(icon != null)
 145			return icon;
 146
 147		URL url;
 148
 149		try
 150		{
 151			// get the icon
 152			if(MiscUtilities.isURL(iconName))
 153				url = new URL(iconName);
 154			else
 155				url = new URL(iconPath + iconName);
 156		}
 157		catch(Exception e)
 158		{
 159			try
 160			{
 161				url = new URL(defaultIconPath + iconName);
 162			}
 163			catch(Exception ex)
 164			{
 165				Log.log(Log.ERROR,GUIUtilities.class,
 166					"Icon not found: " + iconName);
 167				Log.log(Log.ERROR,GUIUtilities.class,ex);
 168				return null;
 169			}
 170		}
 171
 172		icon = new ImageIcon(url);
 173
 174		icons.put(iconName,icon);
 175		return icon;
 176	} //}}}
 177
 178	//{{{ getEditorIcon() method
 179	/**
 180	 * Returns the default editor window image.
 181	 */
 182	public static Image getEditorIcon()
 183	{
 184		return ((ImageIcon)loadIcon("jedit-icon.gif")).getImage();
 185	} //}}}
 186
 187	//{{{ getPluginIcon() method
 188	/**
 189	 * Returns the default plugin window image.
 190	 */
 191	public static Image getPluginIcon()
 192	{
 193		return getEditorIcon();
 194	} //}}}
 195
 196	//}}}
 197
 198	//{{{ Menus, tool bars
 199
 200	//{{{ loadMenuBar() method
 201	/**
 202	 * Creates a menubar. Plugins should not need to call this method.
 203	 * @param name The menu bar name
 204	 * @since jEdit 3.2pre5
 205	 */
 206	public static JMenuBar loadMenuBar(String name)
 207	{
 208		return loadMenuBar(jEdit.getActionContext(),name);
 209	} //}}}
 210
 211	//{{{ loadMenuBar() method
 212	/**
 213	 * Creates a menubar. Plugins should not need to call this method.
 214	 * @param context An action context
 215	 * @param name The menu bar name
 216	 * @since jEdit 4.2pre1
 217	 */
 218	public static JMenuBar loadMenuBar(ActionContext context, String name)
 219	{
 220		String menus = jEdit.getProperty(name);
 221		StringTokenizer st = new StringTokenizer(menus);
 222
 223		JMenuBar mbar = new JMenuBar();
 224
 225		while(st.hasMoreTokens())
 226		{
 227			mbar.add(loadMenu(context,st.nextToken()));
 228		}
 229
 230		return mbar;
 231	} //}}}
 232
 233	//{{{ loadMenu() method
 234	/**
 235	 * Creates a menu. The menu label is set from the
 236	 * <code><i>name</i>.label</code> property. The menu contents is taken
 237	 * from the <code><i>name</i></code> property, which is a whitespace
 238	 * separated list of action names. An action name of <code>-</code>
 239	 * inserts a separator in the menu.
 240	 * @param name The menu name
 241	 * @see #loadMenuItem(String)
 242	 * @since jEdit 2.6pre2
 243	 */
 244	public static JMenu loadMenu(String name)
 245	{
 246		return loadMenu(jEdit.getActionContext(),name);
 247	} //}}}
 248
 249	//{{{ loadMenu() method
 250	/**
 251	 * Creates a menu. The menu label is set from the
 252	 * <code><i>name</i>.label</code> property. The menu contents is taken
 253	 * from the <code><i>name</i></code> property, which is a whitespace
 254	 * separated list of action names. An action name of <code>-</code>
 255	 * inserts a separator in the menu.
 256	 * @param context An action context; either
 257	 * <code>jEdit.getActionContext()</code> or
 258	 * <code>VFSBrowser.getActionContext()</code>.
 259	 * @param name The menu name
 260	 * @see #loadMenuItem(String)
 261	 * @since jEdit 4.2pre1
 262	 */
 263	public static JMenu loadMenu(ActionContext context, String name)
 264	{
 265		return new EnhancedMenu(name,
 266			jEdit.getProperty(name.concat(".label")),
 267			context);
 268	} //}}}
 269
 270	//{{{ loadPopupMenu() method
 271	/**
 272	 * Creates a popup menu.
 273
 274	 * @param name The menu name
 275	 * @since jEdit 2.6pre2
 276	 */
 277	public static JPopupMenu loadPopupMenu(String name)
 278	{
 279		return loadPopupMenu(jEdit.getActionContext(),name);
 280	} //}}}
 281
 282	//{{{ loadPopupMenu() method
 283	/**
 284	 * Creates a popup menu.
 285
 286	 * @param context An action context; either
 287	 * <code>jEdit.getActionContext()</code> or
 288	 * <code>VFSBrowser.getActionContext()</code>.
 289	 * @param name The menu name
 290	 * @since jEdit 4.2pre1
 291	 */
 292	public static JPopupMenu loadPopupMenu(ActionContext context, String name)
 293	{
 294		JPopupMenu menu = new JPopupMenu();
 295
 296		String menuItems = jEdit.getProperty(name);
 297		if(menuItems != null)
 298		{
 299			StringTokenizer st = new StringTokenizer(menuItems);
 300			while(st.hasMoreTokens())
 301			{
 302				String menuItemName = st.nextToken();
 303				if(menuItemName.equals("-"))
 304					menu.addSeparator();
 305				else
 306					menu.add(loadMenuItem(context,menuItemName,false));
 307			}
 308		}
 309
 310		return menu;
 311	} //}}}
 312
 313	//{{{ loadMenuItem() method
 314	/**
 315	 * Creates a menu item. The menu item is bound to the action named by
 316	 * <code>name</code> with label taken from the return value of the
 317	 * {@link EditAction#getLabel()} method.
 318	 *
 319	 * @param name The menu item name
 320	 * @see #loadMenu(String)
 321	 * @since jEdit 2.6pre1
 322	 */
 323	public static JMenuItem loadMenuItem(String name)
 324	{
 325		return loadMenuItem(jEdit.getActionContext(),name,true);
 326	} //}}}
 327
 328	//{{{ loadMenuItem() method
 329	/**
 330	 * Creates a menu item.
 331	 * @param name The menu item name
 332	 * @param setMnemonic True if the menu item should have a mnemonic
 333	 * @since jEdit 3.1pre1
 334	 */
 335	public static JMenuItem loadMenuItem(String name, boolean setMnemonic)
 336	{
 337		return loadMenuItem(jEdit.getActionContext(),name,setMnemonic);
 338	} //}}}
 339
 340	//{{{ loadMenuItem() method
 341	/**
 342	 * Creates a menu item.
 343	 * @param context An action context; either
 344	 * <code>jEdit.getActionContext()</code> or
 345	 * <code>VFSBrowser.getActionContext()</code>.
 346	 * @param name The menu item name
 347	 * @param setMnemonic True if the menu item should have a mnemonic
 348	 * @since jEdit 4.2pre1
 349	 */
 350	public static JMenuItem loadMenuItem(ActionContext context, String name,
 351		boolean setMnemonic)
 352	{
 353		if(name.startsWith("%"))
 354			return loadMenu(context,name.substring(1));
 355
 356		String label = jEdit.getProperty(name + ".label");
 357		if(label == null)
 358			label = name;
 359
 360		char mnemonic;
 361		int index = label.indexOf('$');
 362		if(index != -1 && label.length() - index > 1)
 363		{
 364			mnemonic = Character.toLowerCase(label.charAt(index + 1));
 365			label = label.substring(0,index).concat(label.substring(++index));
 366		}
 367		else
 368			mnemonic = '\0';
 369
 370		JMenuItem mi;
 371		if(jEdit.getBooleanProperty(name + ".toggle"))
 372			mi = new EnhancedCheckBoxMenuItem(label,name,context);
 373		else
 374			mi = new EnhancedMenuItem(label,name,context);
 375
 376		if(!OperatingSystem.isMacOS() && setMnemonic && mnemonic != '\0')
 377			mi.setMnemonic(mnemonic);
 378
 379		return mi;
 380	} //}}}
 381
 382	//{{{ loadToolBar() method
 383	/**
 384	 * Creates a toolbar.
 385	 * @param name The toolbar name
 386	 * @since jEdit 4.2pre2
 387	 */
 388	public static Box loadToolBar(String name)
 389	{
 390		return loadToolBar(jEdit.getActionContext(),name);
 391	} //}}}
 392
 393	//{{{ loadToolBar() method
 394	/**
 395	 * Creates a toolbar.
 396	 * @param context An action context; either
 397	 * <code>jEdit.getActionContext()</code> or
 398	 * <code>VFSBrowser.getActionContext()</code>.
 399	 * @param name The toolbar name
 400	 * @since jEdit 4.2pre2
 401	 */
 402	public static Box loadToolBar(ActionContext context, String name)
 403	{
 404		Box toolBar = new Box(BoxLayout.X_AXIS);
 405
 406		String buttons = jEdit.getProperty(name);
 407		if(buttons != null)
 408		{
 409			StringTokenizer st = new StringTokenizer(buttons);
 410			while(st.hasMoreTokens())
 411			{
 412				String button = st.nextToken();
 413				if(button.equals("-"))
 414					toolBar.add(Box.createHorizontalStrut(12));
 415				else
 416				{
 417					JButton b = loadToolButton(context,button);
 418					if(b != null)
 419						toolBar.add(b);
 420				}
 421			}
 422		}
 423
 424		toolBar.add(Box.createGlue());
 425
 426		return toolBar;
 427	} //}}}
 428
 429	//{{{ loadToolButton() method
 430	/**
 431	 * Loads a tool bar button. The tooltip is constructed from
 432	 * the <code><i>name</i>.label</code> and
 433	 * <code><i>name</i>.shortcut</code> properties and the icon is loaded
 434	 * from the resource named '/org/gjt/sp/jedit/icons/' suffixed
 435	 * with the value of the <code><i>name</i>.icon</code> property.
 436	 * @param name The name of the button
 437	 */
 438	public static EnhancedButton loadToolButton(String name)
 439	{
 440		return loadToolButton(jEdit.getActionContext(),name);
 441	} //}}}
 442
 443	//{{{ loadToolButton() method
 444	/**
 445	 * Loads a tool bar button. The tooltip is constructed from
 446	 * the <code><i>name</i>.label</code> and
 447	 * <code><i>name</i>.shortcut</code> properties and the icon is loaded
 448	 * from the resource named '/org/gjt/sp/jedit/icons/' suffixed
 449	 * with the value of the <code><i>name</i>.icon</code> property.
 450	 * @param context An action context; either
 451	 * <code>jEdit.getActionContext()</code> or
 452	 * <code>VFSBrowser.getActionContext()</code>.
 453	 * @param name The name of the button
 454	 * @since jEdit 4.2pre1
 455	 */
 456	public static EnhancedButton loadToolButton(ActionContext context,
 457		String name)
 458	{
 459		String label = jEdit.getProperty(name + ".label");
 460
 461		if(label == null)
 462			label = name;
 463
 464		Icon icon;
 465		String iconName = jEdit.getProperty(name + ".icon");
 466		if(iconName == null)
 467			icon = loadIcon("BrokenImage.png");
 468		else
 469		{
 470			icon = loadIcon(iconName);
 471			if(icon == null)
 472				icon = loadIcon("BrokenImage.png");
 473		}
 474
 475		String toolTip = prettifyMenuLabel(label);
 476		String shortcut1 = jEdit.getProperty(name + ".shortcut");
 477		String shortcut2 = jEdit.getProperty(name + ".shortcut2");
 478		if(shortcut1 != null || shortcut2 != null)
 479		{
 480			toolTip = toolTip + " ("
 481				+ (shortcut1 != null
 482				? shortcut1 : "")
 483				+ ((shortcut1 != null && shortcut2 != null)
 484				? " or " : "")
 485				+ (shortcut2 != null
 486				? shortcut2
 487				: "") + ")";
 488		}
 489
 490		return new EnhancedButton(icon,toolTip,name,context);
 491	} //}}}
 492
 493	//{{{ prettifyMenuLabel() method
 494	/**
 495	 * `Prettifies' a menu item label by removing the `$' sign. This
 496	 * can be used to process the contents of an <i>action</i>.label
 497	 * property.
 498	 */
 499	public static String prettifyMenuLabel(String label)
 500	{
 501		int index = label.indexOf('$');
 502		if(index != -1)
 503		{
 504			label = label.substring(0,index)
 505				.concat(label.substring(index + 1));
 506		}
 507		return label;
 508	} //}}}
 509
 510	//}}}
 511
 512	//{{{ Canned dialog boxes
 513
 514	//{{{ message() method
 515	/**
 516	 * Displays a dialog box.
 517	 * The title of the dialog is fetched from
 518	 * the <code><i>name</i>.title</code> property. The message is fetched
 519	 * from the <code><i>name</i>.message</code> property. The message
 520	 * is formatted by the property manager with <code>args</code> as
 521	 * positional parameters.
 522	 * @param comp The component to display the dialog for
 523	 * @param name The name of the dialog
 524	 * @param args Positional parameters to be substituted into the
 525	 * message text
 526	 */
 527	public static void message(Component comp, String name, Object[] args)
 528	{
 529		hideSplashScreen();
 530
 531		JOptionPane.showMessageDialog(comp,
 532			jEdit.getProperty(name.concat(".message"),args),
 533			jEdit.getProperty(name.concat(".title"),args),
 534			JOptionPane.INFORMATION_MESSAGE);
 535	} //}}}
 536
 537	//{{{ error() method
 538	/**
 539	 * Displays an error dialog box.
 540	 * The title of the dialog is fetched from
 541	 * the <code><i>name</i>.title</code> property. The message is fetched
 542	 * from the <code><i>name</i>.message</code> property. The message
 543	 * is formatted by the property manager with <code>args</code> as
 544	 * positional parameters.
 545	 * @param comp The component to display the dialog for
 546	 * @param name The name of the dialog
 547	 * @param args Positional parameters to be substituted into the
 548	 * message text
 549	 */
 550	public static void error(Component comp, String name, Object[] args)
 551	{
 552		hideSplashScreen();
 553
 554		JOptionPane.showMessageDialog(comp,
 555			jEdit.getProperty(name.concat(".message"),args),
 556			jEdit.getProperty(name.concat(".title"),args),
 557			JOptionPane.ERROR_MESSAGE);
 558	} //}}}
 559
 560	//{{{ input() method
 561	/**
 562	 * Displays an input dialog box and returns any text the user entered.
 563	 * The title of the dialog is fetched from
 564	 * the <code><i>name</i>.title</code> property. The message is fetched
 565	 * from the <code><i>name</i>.message</code> property.
 566	 * @param comp The component to display the dialog for
 567	 * @param name The name of the dialog
 568	 * @param def The text to display by default in the input field
 569	 */
 570	public static String input(Component comp, String name, Object def)
 571	{
 572		return input(comp,name,null,def);
 573	} //}}}
 574
 575	//{{{ inputProperty() method
 576	/**
 577	 * Displays an input dialog box and returns any text the user entered.
 578	 * The title of the dialog is fetched from
 579	 * the <code><i>name</i>.title</code> property. The message is fetched
 580	 * from the <code><i>name</i>.message</code> property.
 581	 * @param comp The component to display the dialog for
 582	 * @param name The name of the dialog
 583	 * @param def The property whose text to display in the input field
 584	 */
 585	public static String inputProperty(Component comp, String name,
 586		String def)
 587	{
 588		return inputProperty(comp,name,null,def);
 589	} //}}}
 590
 591	//{{{ input() method
 592	/**
 593	 * Displays an input dialog box and returns any text the user entered.
 594	 * The title of the dialog is fetched from
 595	 * the <code><i>name</i>.title</code> property. The message is fetched
 596	 * from the <code><i>name</i>.message</code> property.
 597	 * @param comp The component to display the dialog for
 598	 * @param name The name of the dialog
 599	 * @param def The text to display by default in the input field
 600	 * @param args Positional parameters to be substituted into the
 601	 * message text
 602	 * @since jEdit 3.1pre3
 603	 */
 604	public static String input(Component comp, String name,
 605		Object[] args, Object def)
 606	{
 607		hideSplashScreen();
 608
 609		String retVal = (String)JOptionPane.showInputDialog(comp,
 610			jEdit.getProperty(name.concat(".message"),args),
 611			jEdit.getProperty(name.concat(".title")),
 612			JOptionPane.QUESTION_MESSAGE,null,null,def);
 613		return retVal;
 614	} //}}}
 615
 616	//{{{ inputProperty() method
 617	/**
 618	 * Displays an input dialog box and returns any text the user entered.
 619	 * The title of the dialog is fetched from
 620	 * the <code><i>name</i>.title</code> property. The message is fetched
 621	 * from the <code><i>name</i>.message</code> property.
 622	 * @param comp The component to display the dialog for
 623	 * @param name The name of the dialog
 624	 * @param args Positional parameters to be substituted into the
 625	 * message text
 626	 * @param def The property whose text to display in the input field
 627	 * @since jEdit 3.1pre3
 628	 */
 629	public static String inputProperty(Component comp, String name,
 630		Object[] args, String def)
 631	{
 632		hideSplashScreen();
 633
 634		String retVal = (String)JOptionPane.showInputDialog(comp,
 635			jEdit.getProperty(name.concat(".message"),args),
 636			jEdit.getProperty(name.concat(".title")),
 637			JOptionPane.QUESTION_MESSAGE,
 638			null,null,jEdit.getProperty(def));
 639		if(retVal != null)
 640			jEdit.setProperty(def,retVal);
 641		return retVal;
 642	} //}}}
 643
 644	//{{{ confirm() method
 645	/**
 646	 * Displays a confirm dialog box and returns the button pushed by the
 647	 * user. The title of the dialog is fetched from the
 648	 * <code><i>name</i>.title</code> property. The message is fetched
 649	 * from the <code><i>name</i>.message</code> property.
 650	 * @param comp The component to display the dialog for
 651	 * @param name The name of the dialog
 652	 * @param args Positional parameters to be substituted into the
 653	 * message text
 654	 * @param buttons The buttons to display - for example,
 655	 * JOptionPane.YES_NO_CANCEL_OPTION
 656	 * @param type The dialog type - for example,
 657	 * JOptionPane.WARNING_MESSAGE
 658	 * @since jEdit 3.1pre3
 659	 */
 660	public static int confirm(Component comp, String name,
 661		Object[] args, int buttons, int type)
 662	{
 663		hideSplashScreen();
 664
 665		return JOptionPane.showConfirmDialog(comp,
 666			jEdit.getProperty(name + ".message",args),
 667			jEdit.getProperty(name + ".title"),buttons,type);
 668	} //}}}
 669
 670	//{{{ listConfirm() method
 671	/**
 672	 * Displays a confirm dialog box and returns the button pushed by the
 673	 * user. The title of the dialog is fetched from the
 674	 * <code><i>name</i>.title</code> property. The message is fetched
 675	 * from the <code><i>name</i>.message</code> property. The dialog
 676	 * also shows a list of entries given by the <code>listModel</code>
 677	 * parameter.
 678	 * @since jEdit 4.3pre1
 679	 */
 680	public static int listConfirm(Component comp, String name, String[] args,
 681		Object[] listModel)
 682	{
 683		JList list = new JList(listModel);
 684		list.setVisibleRowCount(8);
 685
 686		Object[] message = {
 687			jEdit.getProperty(name + ".message",args),
 688			new JScrollPane(list)
 689		};
 690
 691		return JOptionPane.showConfirmDialog(comp,
 692			message,
 693			jEdit.getProperty(name + ".title"),
 694			JOptionPane.YES_NO_OPTION,
 695			JOptionPane.QUESTION_MESSAGE);
 696	} //}}}
 697
 698	//{{{ showVFSFileDialog() method
 699	/**
 700	 * Displays a VFS file selection dialog box.
 701	 * @param view The view, should be non-null
 702	 * @param path The initial directory to display. May be null
 703	 * @param type The dialog type. One of
 704	 * {@link org.gjt.sp.jedit.browser.VFSBrowser#OPEN_DIALOG},
 705	 * {@link org.gjt.sp.jedit.browser.VFSBrowser#SAVE_DIALOG}, or
 706	 * {@link org.gjt.sp.jedit.browser.VFSBrowser#CHOOSE_DIRECTORY_DIALOG}.
 707	 * @param multipleSelection True if multiple selection should be allowed
 708	 * @return The selected file(s)
 709	 * @since jEdit 2.6pre2
 710	 */
 711	public static String[] showVFSFileDialog(View view, String path,
 712		int type, boolean multipleSelection)
 713	{
 714		// the view should not be null, but some plugins might do this
 715		if(view == null)
 716		{
 717			Log.log(Log.WARNING,GUIUtilities.class,
 718			"showVFSFileDialog(): given null view, assuming jEdit.getActiveView()");
 719			view = jEdit.getActiveView();
 720		}
 721
 722		hideSplashScreen();
 723
 724		VFSFileChooserDialog fileChooser = new VFSFileChooserDialog(
 725			view,path,type,multipleSelection);
 726		String[] selectedFiles = fileChooser.getSelectedFiles();
 727		if(selectedFiles == null)
 728			return null;
 729
 730		return selectedFiles;
 731	} //}}}
 732
 733	//}}}
 734
 735	//{{{ Colors and styles
 736
 737	//{{{ parseColor() method
 738	/**
 739	 * Converts a color name to a color object. The name must either be
 740	 * a known string, such as `red', `green', etc (complete list is in
 741	 * the <code>java.awt.Color</code> class) or a hex color value
 742	 * prefixed with `#', for example `#ff0088'.
 743	 * @param name The color name
 744	 */
 745	public static Color parseColor(String name)
 746	{
 747		return parseColor(name, Color.black);
 748	} //}}}
 749
 750	//{{{ parseColor() method
 751	public static Color parseColor(String name, Color defaultColor)
 752	{
 753		if(name == null)
 754			return defaultColor;
 755		else if(name.startsWith("#"))
 756		{
 757			try
 758			{
 759				return Color.decode(name);
 760			}
 761			catch(NumberFormatException nf)
 762			{
 763				return defaultColor;
 764			}
 765		}
 766		else if("red".equals(name))
 767			return Color.red;
 768		else if("green".equals(name))
 769			return Color.green;
 770		else if("blue".equals(name))
 771			return Color.blue;
 772		else if("yellow".equals(name))
 773			return Color.yellow;
 774		else if("orange".equals(name))
 775			return Color.orange;
 776		else if("white".equals(name))
 777			return Color.white;
 778		else if("lightGray".equals(name))
 779			return Color.lightGray;
 780		else if("gray".equals(name))
 781			return Color.gray;
 782		else if("darkGray".equals(name))
 783			return Color.darkGray;
 784		else if("black".equals(name))
 785			return Color.black;
 786		else if("cyan".equals(name))
 787			return Color.cyan;
 788		else if("magenta".equals(name))
 789			return Color.magenta;
 790		else if("pink".equals(name))
 791			return Color.pink;
 792		else
 793			return defaultColor;
 794	} //}}}
 795
 796	//{{{ getColorHexString() method
 797	/**
 798	 * Converts a color object to its hex value. The hex value
 799	 * prefixed is with `#', for example `#ff0088'.
 800	 * @param c The color object
 801	 */
 802	public static String getColorHexString(Color c)
 803	{
 804		String colString = Integer.toHexString(c.getRGB() & 0xffffff);
 805		return "#000000".substring(0,7 - colString.length()).concat(colString);
 806	} //}}}
 807
 808	//{{{ parseStyle() method
 809	/**
 810	 * Converts a style string to a style object.
 811	 * @param str The style string
 812	 * @param family Style strings only specify font style, not font family
 813	 * @param size Style strings only specify font style, not font family
 814	 * @exception IllegalArgumentException if the style is invalid
 815	 * @since jEdit 3.2pre6
 816	 */
 817	public static SyntaxStyle parseStyle(String str, String family, int size)
 818		throws IllegalArgumentException
 819	{
 820		return parseStyle(str,family,size,true);
 821	} //}}}
 822
 823	//{{{ parseStyle() method
 824	/**
 825	 * Converts a style string to a style object.
 826	 * @param str The style string
 827	 * @param family Style strings only specify font style, not font family
 828	 * @param size Style strings only specify font style, not font family
 829	 * @param color If false, the styles will be monochrome
 830	 * @exception IllegalArgumentException if the style is invalid
 831	 * @since jEdit 4.0pre4
 832	 */
 833	public static SyntaxStyle parseStyle(String str, String family, int size,
 834		boolean color)
 835		throws IllegalArgumentException
 836	{
 837		Color fgColor = Color.black;
 838		Color bgColor = null;
 839		boolean italic = false;
 840		boolean bold = false;
 841		StringTokenizer st = new StringTokenizer(str);
 842		while(st.hasMoreTokens())
 843		{
 844			String s = st.nextToken();
 845			if(s.startsWith("color:"))
 846			{
 847				if(color)
 848					fgColor = GUIUtilities.parseColor(s.substring(6), Color.black);
 849			}
 850			else if(s.startsWith("bgColor:"))
 851			{
 852				if(color)
 853					bgColor = GUIUtilities.parseColor(s.substring(8), null);
 854			}
 855			else if(s.startsWith("style:"))
 856			{
 857				for(int i = 6; i < s.length(); i++)
 858				{
 859					if(s.charAt(i) == 'i')
 860						italic = true;
 861					else if(s.charAt(i) == 'b')
 862						bold = true;
 863					else
 864						throw new IllegalArgumentException(
 865							"Invalid style: " + s);
 866				}
 867			}
 868			else
 869				throw new IllegalArgumentException(
 870					"Invalid directive: " + s);
 871		}
 872		return new SyntaxStyle(fgColor,bgColor,
 873			new Font(family,
 874			(italic ? Font.ITALIC : 0) | (bold ? Font.BOLD : 0),
 875			size));
 876	} //}}}
 877
 878	//{{{ getStyleString() method
 879	/**
 880	 * Converts a style into it's string representation.
 881	 * @param style The style
 882	 */
 883	public static String getStyleString(SyntaxStyle style)
 884	{
 885		StringBuffer buf = new StringBuffer();
 886
 887		if(style.getForegroundColor() != null)
 888		{
 889			buf.append("color:").append(getColorHexString(style.getForegroundColor()));
 890		}
 891
 892		if(style.getBackgroundColor() != null)
 893		{
 894			buf.append(" bgColor:").append(getColorHexString(style.getBackgroundColor()));
 895		}
 896
 897        Font font = style.getFont();
 898        if(!font.isPlain())
 899		{
 900            buf.append(" style:");
 901            if (font.isItalic())
 902                buf.append('i');
 903            if (font.isBold())
 904                buf.append('b');
 905		}
 906
 907		return buf.toString();
 908	} //}}}
 909
 910	//{{{ loadStyles() method
 911	/**
 912	 * Loads the syntax styles from the properties, giving them the specified
 913	 * base font family and size.
 914	 * @param family The font family
 915	 * @param size The font size
 916	 * @since jEdit 3.2pre6
 917	 */
 918	public static SyntaxStyle[] loadStyles(String family, int size)
 919	{
 920		return loadStyles(family,size,true);
 921	} //}}}
 922
 923	//{{{ loadStyles() method
 924	/**
 925	 * Loads the syntax styles from the properties, giving them the specified
 926	 * base font family and size.
 927	 * @param family The font family
 928	 * @param size The font size
 929	 * @param color If false, the styles will be monochrome
 930	 * @since jEdit 4.0pre4
 931	 */
 932	public static SyntaxStyle[] loadStyles(String family, int size, boolean color)
 933	{
 934		SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT];
 935
 936		// start at 1 not 0 to skip Token.NULL
 937		for(int i = 1; i < styles.length; i++)
 938		{
 939			try
 940			{
 941				String styleName = "view.style."
 942					+ Token.tokenToString((byte)i)
 943					.toLowerCase(Locale.ENGLISH);
 944				styles[i] = GUIUtilities.parseStyle(
 945					jEdit.getProperty(styleName),
 946					family,size,color);
 947			}
 948			catch(Exception e)
 949			{
 950				Log.log(Log.ERROR,GUIUtilities.class,e);
 951			}
 952		}
 953
 954		return styles;
 955	} //}}}
 956
 957	//}}}
 958
 959	//{{{ Loading, saving window geometry
 960
 961	//{{{ loadGeometry() method
 962	/**
 963	 * Loads a windows's geometry from the properties.
 964	 * The geometry is loaded from the <code><i>name</i>.x</code>,
 965	 * <code><i>name</i>.y</code>, <code><i>name</i>.width</code> and
 966	 * <code><i>name</i>.height</code> properties.
 967	 *
 968	 * @param win The window
 969	 * @param name The window name
 970	 */
 971	public static void loadGeometry(Window win, String name)
 972	{
 973		int x, y, width, height;
 974
 975		Dimension size = win.getSize();
 976		GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
 977		Rectangle gcbounds = gd.getDefaultConfiguration().getBounds();
 978
 979		x = gcbounds.x;
 980		y = gcbounds.y;
 981
 982		width = jEdit.getIntegerProperty(name + ".width",size.width);
 983		height = jEdit.getIntegerProperty(name + ".height",size.height);
 984
 985		Component parent = win.getParent();
 986		if(parent == null)
 987		{
 988			x += (gcbounds.width - width) / 2;
 989			y += (gcbounds.height - height) / 2;
 990		}
 991		else
 992		{
 993			Rectangle bounds = parent.getBounds();
 994			x += bounds.x + (bounds.width - width) / 2;
 995			y += bounds.y + (bounds.height - height) / 2;
 996		}
 997
 998		x = jEdit.getIntegerProperty(name + ".x",x);
 999		y = jEdit.getIntegerProperty(name + ".y",y);
1000
1001		int extState = jEdit.getIntegerProperty(name + ".extendedState", 0);
1002
1003		Rectangle desired = new Rectangle(x,y,width,height);
1004		try
1005		{
1006			if(!Debug.DISABLE_MULTIHEAD)
1007				adjustForScreenBounds(desired);
1008		}
1009		catch(Exception e)
1010		{
1011			/* Workaround for OS X bug. */
1012			Log.log(Log.ERROR,GUIUtilities.class,e);
1013		}
1014
1015		if(OperatingSystem.isX11() && Debug.GEOMETRY_WORKAROUND)
1016			new UnixWorkaround(win,name,desired,extState);
1017		else
1018		{
1019			win.setBounds(desired);
1020			if(win instanceof Frame)
1021				((Frame)win).setExtendedState(extState);
1022		}
1023	} //}}}
1024
1025	//{{{ adjustForScreenBounds() method
1026	/**
1027	 * Gives a rectangle the specified bounds, ensuring it is within the
1028	 * screen bounds.
1029	 * @since jEdit 4.2pre3
1030	 */
1031	public static void adjustForScreenBounds(Rectangle desired)
1032	{
1033		// Make sure the window is displayed in visible region
1034		Rectangle osbounds = OperatingSystem.getScreenBounds(desired);
1035
1036		if(desired.x < osbounds.x || desired.x+desired.width
1037			> desired.x + osbounds.width)
1038		{
1039			if (desired.width > osbounds.width)
1040				desired.width = osbounds.width;
1041			desired.x = (osbounds.width - desired.width) / 2;
1042		}
1043		if(desired.y < osbounds.y || desired.y+desired.height
1044			> osbounds.y + osbounds.height)
1045		{
1046			if (desired.height >= osbounds.height)
1047				desired.height = osbounds.height;
1048			desired.y = (osbounds.height - desired.height) / 2;
1049		}
1050	} //}}}
1051
1052	//{{{ UnixWorkaround class
1053	static class UnixWorkaround
1054	{
1055		Window win;
1056		String name;
1057		Rectangle desired;
1058		Rectangle required;
1059		long start;
1060		boolean windowOpened;
1061
1062		//{{{ UnixWorkaround constructor
1063		UnixWorkaround(Window win, String name, Rectangle desired,
1064			int extState)
1065		{
1066			this.win = win;
1067			this.name = name;
1068			this.desired = desired;
1069
1070			int adjust_x, adjust_y, adjust_width, adjust_height;
1071			adjust_x = jEdit.getIntegerProperty(name + ".dx",0);
1072			adjust_y = jEdit.getIntegerProperty(name + ".dy",0);
1073			adjust_width = jEdit.getIntegerProperty(name + ".d-width",0);
1074			adjust_height = jEdit.getIntegerProperty(name + ".d-height",0);
1075
1076			required = new Rectangle(
1077				desired.x - adjust_x,
1078				desired.y - adjust_y,
1079				desired.width - adjust_width,
1080				desired.height - adjust_height);
1081
1082			Log.log(Log.DEBUG,GUIUtilities.class,"Window " + name
1083				+ ": desired geometry is " + desired);
1084			Log.log(Log.DEBUG,GUIUtilities.class,"Window " + name
1085				+ ": setting geometry to " + required);
1086
1087			start = System.currentTimeMillis();
1088
1089			win.setBounds(required);
1090			if(win instanceof Frame)
1091				((Frame)win).setExtendedState(extState);
1092
1093			win.addComponentListener(new ComponentHandler());
1094			win.addWindowListener(new WindowHandler());
1095		} //}}}
1096
1097		//{{{ ComponentHandler class
1098		class ComponentHandler extends ComponentAdapter
1099		{
1100			//{{{ componentMoved() method
1101			public void componentMoved(ComponentEvent evt)
1102			{
1103				if(System.currentTimeMillis() - start < 1000)
1104				{
1105					Rectangle r = win.getBounds();
1106					if(!windowOpened && r.equals(required))
1107						return;
1108
1109					if(!r.equals(desired))
1110					{
1111						Log.log(Log.DEBUG,GUIUtilities.class,
1112							"Window resize blocked: " + win.getBounds());
1113						win.setBounds(desired);
1114					}
1115				}
1116
1117				win.removeComponentListener(this);
1118			} //}}}
1119
1120			//{{{ componentResized() method
1121			public void componentResized(ComponentEvent evt)
1122			{
1123				if(System.currentTimeMillis() - start < 1000)
1124				{
1125					Rectangle r = win.getBounds();
1126					if(!windowOpened && r.equals(required))
1127						return;
1128
1129					if(!r.equals(desired))
1130					{
1131 						Log.log(Log.DEBUG,GUIUtilities.class,
1132 							"Window resize blocked: " + win.getBounds());
1133						win.setBounds(desired);
1134					}
1135				}
1136
1137				win.removeComponentListener(this);
1138			} //}}}
1139		} //}}}
1140
1141		//{{{ WindowHandler class
1142		class WindowHandler extends WindowAdapter
1143		{
1144			//{{{ windowOpened() method
1145			public void windowOpened(WindowEvent evt)
1146			{
1147				windowOpened = true;
1148
1149				Rectangle r = win.getBounds();
1150 				Log.log(Log.DEBUG,GUIUtilities.class,"Window "
1151 					+ name + ": bounds after opening: " + r);
1152
1153				jEdit.setIntegerProperty(name + ".dx",
1154					r.x - required.x);
1155				jEdit.setIntegerProperty(name + ".dy",
1156					r.y - required.y);
1157				jEdit.setIntegerProperty(name + ".d-width",
1158					r.width - required.width);
1159				jEdit.setIntegerProperty(name + ".d-height",
1160					r.height - required.height);
1161
1162				win.removeWindowListener(this);
1163			} //}}}
1164		} //}}}
1165	} //}}}
1166
1167	//{{{ saveGeometry() method
1168	/**
1169	 * Saves a window's geometry to the properties.
1170	 * The geometry is saved to the <code><i>name</i>.x</code>,
1171	 * <code><i>name</i>.y</code>, <code><i>name</i>.width</code> and
1172	 * <code><i>name</i>.height</code> properties.
1173	 * @param win The window
1174	 * @param name The window name
1175	 */
1176	public static void saveGeometry(Window win, String name)
1177	{
1178		if(win instanceof Frame)
1179		{
1180			jEdit.setIntegerProperty(name + ".extendedState",
1181				((Frame)win).getExtendedState());
1182		}
1183
1184		Rectangle bounds = win.getBounds();
1185		jEdit.setIntegerProperty(name + ".x",bounds.x);
1186		jEdit.setIntegerProperty(name + ".y",bounds.y);
1187		jEdit.setIntegerProperty(name + ".width",bounds.width);
1188		jEdit.setIntegerProperty(name + ".height",bounds.height);
1189	} //}}}
1190
1191	//{{{ centerOnScreen() method
1192	/**
1193	 * Centers the given window on the screen. This method is needed because
1194	 * JDK 1.3 does not have a <code>JWindow.setLocationRelativeTo()</code>
1195	 * method.
1196	 * @since jEdit 4.2pre3
1197	 */
1198	public static void centerOnScreen(Window win)
1199	{
1200		GraphicsDevice gd = GraphicsEnvironment
1201			.getLocalGraphicsEnvironment()
1202			.getDefaultScreenDevice();
1203		Rectangle gcbounds = gd.getDefaultConfiguration().getBounds();
1204		int x = gcbounds.x + (gcbounds.width - win.getWidth()) / 2;
1205		int y = gcbounds.y + (gcbounds.height - win.getHeight()) / 2;
1206		win.setLocation(x,y);
1207	} //}}}
1208
1209	//}}}
1210
1211	//{{{ hideSplashScreen() method
1212	/**
1213	 * Ensures that the splash screen is not visible. This should be
1214	 * called before displaying any dialog boxes or windows at
1215	 * startup.
1216	 */
1217	public static void hideSplashScreen()
1218	{
1219		if(splash != null)
1220		{
1221			splash.dispose();
1222			splash = null;
1223		}
1224	} //}}}
1225
1226	//{{{ createMultilineLabel() method
1227	/**
1228	 * Creates a component that displays a multiple line message. This
1229	 * is implemented by assembling a number of <code>JLabels</code> in
1230	 * a <code>JPanel</code>.
1231	 * @param str The string, with lines delimited by newline
1232	 * (<code>\n</code>) characters.
1233	 * @since jEdit 4.1pre3
1234	 */
1235	public static JComponent createMultilineLabel(String str)
1236	{
1237		JPanel panel = new JPanel(new VariableGridLayout(
1238			VariableGridLayout.FIXED_NUM_COLUMNS,1,1,1));
1239		int lastOffset = 0;
1240		for(;;)
1241		{
1242			int index = str.indexOf('\n',lastOffset);
1243			if(index == -1)
1244				break;
1245			else
1246			{
1247				panel.add(new JLabel(str.substring(lastOffset,index)));
1248				lastOffset = index + 1;
1249			}
1250		}
1251
1252		if(lastOffset != str.length())
1253			panel.add(new JLabel(str.substring(lastOffset)));
1254
1255		return panel;
1256	} //}}}
1257
1258	//{{{ requestFocus() method
1259	/**
1260	 * Focuses on the specified component as soon as the window becomes
1261	 * active.
1262	 * @param win The window
1263	 * @param comp The component
1264	 */
1265	public static void requestFocus(final Window win, final Component comp)
1266	{
1267		win.addWindowFocusListener(new WindowAdapter()
1268		{
1269			public void windowGainedFocus(WindowEvent evt)
1270			{
1271				
1272				comp.requestFocusInWindow();
1273				
1274				win.removeWindowFocusListener(this);
1275			}
1276		});
1277	} //}}}
1278
1279	//{{{ isPopupTrigger() method
1280	/**
1281	 * Returns if the specified event is the popup trigger event.
1282	 * This implements precisely defined behavior, as opposed to
1283	 * MouseEvent.isPopupTrigger().
1284	 * @param evt The event
1285	 * @since jEdit 3.2pre8
1286	 */
1287	public static boolean isPopupTrigger(MouseEvent evt)
1288	{
1289		return isRightButton(evt.getModifiers());
1290	} //}}}
1291
1292	//{{{ isMiddleButton() method
1293	/**
1294	 * @param modifiers The modifiers flag from a mouse event
1295	 * @since jEdit 4.1pre9
1296	 */
1297	public static boolean isMiddleButton(int modifiers)
1298	{
1299		if (OperatingSystem.isMacOS())
1300		{
1301			if((modifiers & MouseEvent.BUTTON1_MASK) != 0)
1302				return ((modifiers & MouseEvent.ALT_MASK) != 0);
1303			else
1304				return ((modifiers & MouseEvent.BUTTON2_MASK) != 0);
1305		}
1306		else
1307			return ((modifiers & MouseEvent.BUTTON2_MASK) != 0);
1308	} //}}}
1309
1310	//{{{ isRightButton() method
1311	/**
1312	 * @param modifiers The modifiers flag from a mouse event
1313	 * @since jEdit 4.1pre9
1314	 */
1315	public static boolean isRightButton(int modifiers)
1316	{
1317		if (OperatingSystem.isMacOS())
1318		{
1319			if((modifiers & MouseEvent.BUTTON1_MASK) != 0)
1320				return ((modifiers & MouseEvent.CTRL_MASK) != 0);
1321			else
1322				return ((modifiers & MouseEvent.BUTTON3_MASK) != 0);
1323		}
1324		else
1325			return ((modifiers & MouseEvent.BUTTON3_MASK) != 0);
1326	} //}}}
1327
1328	//{{{ showPopupMenu() method
1329	/**
1330	 * Shows the specified popup menu, ensuring it is displayed within
1331	 * the bounds of the screen.
1332	 * @param popup The popup menu
1333	 * @param comp The component to show it for
1334	 * @param x The x co-ordinate
1335	 * @param y The y co-ordinate
1336	 * @since jEdit 4.0pre1
1337	 */
1338	public static void showPopupMenu(JPopupMenu popup, Component comp,
1339		int x, int y)
1340	{
1341		showPopupMenu(popup,comp,x,y,true);
1342	} //}}}
1343
1344	//{{{ showPopupMenu() method
1345	/**
1346	 * Shows the specified popup menu, ensuring it is displayed within
1347	 * the bounds of the screen.
1348	 * @param popup The popup menu
1349	 * @param comp The component to show it for
1350	 * @param x The x co-ordinate
1351	 * @param y The y co-ordinate
1352	 * @param point If true, then the popup originates from a single point;
1353	 * otherwise it will originate from the component itself. This affects
1354	 * positioning in the case where the popup does not fit onscreen.
1355	 *
1356	 * @since jEdit 4.1pre1
1357	 */
1358	public static void showPopupMenu(JPopupMenu popup, Component comp,
1359		int x, int y, boolean point)
1360	{
1361		int offsetX = 0;
1362		int offsetY = 0;
1363
1364		int extraOffset = (point ? 1 : 0);
1365
1366		Component win = comp;
1367		while(!(win instanceof Window || win == null))
1368		{
1369			offsetX += win.getX();
1370			offsetY += win.getY();
1371			win = win.getParent();
1372		}
1373
1374		if(win != null)
1375		{
1376			Dimension size = popup.getPreferredSize();
1377
1378			Rectangle screenSize = new Rectangle();
1379            
1380			GraphicsEnvironment ge = GraphicsEnvironment
1381				.getLocalGraphicsEnvironment();
1382			
1383			GraphicsDevice[] devices = ge.getScreenDevices();
1384			
1385			for (int j = 0; j < devices.length; j++) 
1386			{ 
1387				GraphicsDevice device = devices[j];
1388                
1389				GraphicsConfiguration[] gc =
1390					device.getConfigurations();
1391                
1392				for (int i=0; i < gc.length; i++) 
1393				{
1394					screenSize =
1395						screenSize.union(
1396							gc[i].getBounds());
1397				}
1398			} 
1399
1400			if(x + offsetX + size.width + win.getX() > screenSize.width
1401				&& x + offsetX + win.getX() >= size.width)
1402			{
1403				//System.err.println("x overflow");
1404				if(point)
1405					x -= (size.width + extraOffset);
1406				else
1407					x = (win.getWidth() - size.width - offsetX + extraOffset);
1408			}
1409			else
1410			{
1411				x += extraOffset;
1412			}
1413
1414			//System.err.println("y=" + y + ",offsetY=" + offsetY
1415			//	+ ",size.height=" + size.height
1416			//	+ ",win.height=" + win.getHeight());
1417			if(y + offsetY + size.height + win.getY() > screenSize.height
1418				&& y + offsetY + win.getY() >= size.height)
1419			{
1420				if(point)
1421					y = (win.getHeight() - size.height - offsetY + extraOffset);
1422				else
1423					y = -size.height - 1;
1424			}
1425			else
1426			{
1427				y += extraOffset;
1428			}
1429
1430			popup.show(comp,x,y);
1431		}
1432		else
1433			popup.show(comp,x + extraOffset,y + extraOffset);
1434
1435	} //}}}
1436
1437	//{{{ isAncestorOf() method
1438	/**
1439	 * Returns if the first component is an ancestor of the
1440	 * second by traversing up the component hierarchy.
1441	 *
1442	 * @param comp1 The ancestor
1443	 * @param comp2 The component to check
1444	 * @since jEdit 4.1pre5
1445	 */
1446	public static boolean isAncestorOf(Component comp1, Component comp2)
1447	{
1448		while(comp2 != null)
1449		{
1450			if(comp1 == comp2)
1451				return true;
1452			else
1453				comp2 = comp2.getParent();
1454		}
1455
1456		return false;
1457	} //}}}
1458
1459	//{{{ getParentDialog() method
1460	/**
1461	 * Traverses the given component's parent tree looking for an
1462	 * instance of JDialog, and return it. If not found, return null.
1463	 * @param c The component
1464	 */
1465	public static JDialog getParentDialog(Component c)
1466	{
1467		return (JDialog) SwingUtilities.getAncestorOfClass(JDialog.class, c);
1468	} //}}}
1469
1470	//{{{ getComponentParent() method
1471	/**
1472	 * Finds a parent of the specified component.
1473	 * @param comp The component
1474	 * @param clazz Looks for a parent with this class (exact match, not
1475	 * derived).
1476	 * @since jEdit 4.2pre1
1477	 */
1478	public static Component getComponentParent(Component comp, Class clazz)
1479	{
1480		for(;;)
1481		{
1482			if(comp == null)
1483				break;
1484
1485			if(comp instanceof JComponent)
1486			{
1487				Component real = (Component)((JComponent)comp)
1488					.getClientProperty("KORTE_REAL_FRAME");
1489				if(real != null)
1490					comp = real;
1491			}
1492
1493			if(comp.getClass().equals(clazz))
1494				return comp;
1495			else if(comp instanceof JPopupMenu)
1496				comp = ((JPopupMenu)comp).getInvoker();
1497			else if(comp instanceof FloatingWindowContainer)
1498			{
1499				comp = ((FloatingWindowContainer)comp)
1500					.getDockableWindowManager();
1501			}
1502			else
1503				comp = comp.getParent();
1504		}
1505		return null;
1506	} //}}}
1507
1508	//{{{ getView() method
1509	/**
1510	 * Finds the view parent of the specified component.
1511	 * @since jEdit 4.0pre2
1512	 */
1513	public static View getView(Component comp)
1514	{
1515		return (View)getComponentParent(comp,View.class);
1516	} //}}}
1517
1518	//{{{ Package-private members
1519
1520	//{{{ init() method
1521	static void init()
1522	{
1523		// don't do this in static{} since we need jEdit.initMisc()
1524		// run first so we have the jeditresource: protocol
1525		NEW_BUFFER_ICON = loadIcon("new.gif");
1526		DIRTY_BUFFER_ICON = loadIcon("dirty.gif");
1527		READ_ONLY_BUFFER_ICON = loadIcon("readonly.gif");
1528		NORMAL_BUFFER_ICON = loadIcon("normal.gif");
1529		WINDOW_ICON = loadIcon("jedit-icon.gif");
1530	} //}}}
1531
1532	//{{{ showSplashScreen() method
1533	static void showSplashScreen()
1534	{
1535		splash = new SplashScreen();
1536	} //}}}
1537
1538	//{{{ advanceSplashProgress() method
1539	static void advanceSplashProgress()
1540	{
1541		if(splash != null)
1542			splash.advance();
1543	} //}}}
1544
1545	//}}}
1546
1547	//{{{ Private members
1548	private static SplashScreen splash;
1549	private static Hashtable icons;
1550	private static String iconPath = "jeditresource:/org/gjt/sp/jedit/icons/";
1551	private static String defaultIconPath = "jeditresource:/org/gjt/sp/jedit/icons/";
1552
1553	private GUIUtilities() {}
1554	//}}}
1555}