mirror of https://github.com/sipwise/jitsi.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
641 lines
20 KiB
641 lines
20 KiB
/*
|
|
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
|
|
*
|
|
* Distributable under LGPL license.
|
|
* See terms of license at gnu.org.
|
|
*/
|
|
package net.java.sip.communicator.util;
|
|
|
|
import java.awt.*;
|
|
import java.awt.font.*;
|
|
import java.awt.geom.*;
|
|
import java.lang.reflect.*;
|
|
import java.util.*;
|
|
import java.util.List;
|
|
|
|
import javax.swing.*;
|
|
|
|
/**
|
|
* The <tt>StringUtils</tt> class is used through this ui implementation for
|
|
* some special operations with strings.
|
|
*
|
|
* @author Yana Stamcheva
|
|
* @author Lyubomir Marinov
|
|
* @author Adam Netocny
|
|
*/
|
|
public class GuiUtils
|
|
{
|
|
/**
|
|
* The list of all <tt>Window</tt>s owned by this application.
|
|
*/
|
|
private static final List<Window> WINDOW_LIST;
|
|
|
|
private static final Calendar c1 = Calendar.getInstance();
|
|
|
|
private static final Calendar c2 = Calendar.getInstance();
|
|
|
|
/**
|
|
* Number of milliseconds in a second.
|
|
*/
|
|
public static final long MILLIS_PER_SECOND = 1000;
|
|
|
|
/**
|
|
* Number of milliseconds in a standard minute.
|
|
*/
|
|
public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
|
|
|
|
/**
|
|
* Number of milliseconds in a standard hour.
|
|
*/
|
|
public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
|
|
|
|
/**
|
|
* Number of milliseconds in a standard day.
|
|
*/
|
|
public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
|
|
|
|
// These mappings map a character (key) to a specific digit that should replace it for
|
|
// normalization purposes. Non-European digits that may be used in phone numbers are mapped to a
|
|
// European equivalent.
|
|
private static final Map<Character, Character> DIGIT_MAPPINGS;
|
|
|
|
static
|
|
{
|
|
HashMap<Character, Character> digitMap
|
|
= new HashMap<Character, Character>(50);
|
|
|
|
digitMap.put('0', '0');
|
|
digitMap.put('\uFF10', '0'); // Fullwidth digit 0
|
|
digitMap.put('\u0660', '0'); // Arabic-indic digit 0
|
|
digitMap.put('1', '1');
|
|
digitMap.put('\uFF11', '1'); // Fullwidth digit 1
|
|
digitMap.put('\u0661', '1'); // Arabic-indic digit 1
|
|
digitMap.put('2', '2');
|
|
digitMap.put('\uFF12', '2'); // Fullwidth digit 2
|
|
digitMap.put('\u0662', '2'); // Arabic-indic digit 2
|
|
digitMap.put('3', '3');
|
|
digitMap.put('\uFF13', '3'); // Fullwidth digit 3
|
|
digitMap.put('\u0663', '3'); // Arabic-indic digit 3
|
|
digitMap.put('4', '4');
|
|
digitMap.put('\uFF14', '4'); // Fullwidth digit 4
|
|
digitMap.put('\u0664', '4'); // Arabic-indic digit 4
|
|
digitMap.put('5', '5');
|
|
digitMap.put('\uFF15', '5'); // Fullwidth digit 5
|
|
digitMap.put('\u0665', '5'); // Arabic-indic digit 5
|
|
digitMap.put('6', '6');
|
|
digitMap.put('\uFF16', '6'); // Fullwidth digit 6
|
|
digitMap.put('\u0666', '6'); // Arabic-indic digit 6
|
|
digitMap.put('7', '7');
|
|
digitMap.put('\uFF17', '7'); // Fullwidth digit 7
|
|
digitMap.put('\u0667', '7'); // Arabic-indic digit 7
|
|
digitMap.put('8', '8');
|
|
digitMap.put('\uFF18', '8'); // Fullwidth digit 8
|
|
digitMap.put('\u0668', '8'); // Arabic-indic digit 8
|
|
digitMap.put('9', '9');
|
|
digitMap.put('\uFF19', '9'); // Fullwidth digit 9
|
|
digitMap.put('\u0669', '9'); // Arabic-indic digit 9
|
|
DIGIT_MAPPINGS = Collections.unmodifiableMap(digitMap);
|
|
|
|
/*
|
|
* WINDOW_LIST is flawed because there are more calls to addWindow than
|
|
* to removeWindow. Java 6 has introduced Window#getWindows so try to
|
|
* use it instead.
|
|
*/
|
|
Method Window_getWindows = null;
|
|
|
|
try
|
|
{
|
|
Window_getWindows = Window.class.getMethod("getWindows");
|
|
}
|
|
catch (NoSuchMethodException nsme)
|
|
{
|
|
/*
|
|
* Ignore the exception because we are just checking whether the
|
|
* method exists.
|
|
*/
|
|
}
|
|
catch (SecurityException se)
|
|
{
|
|
}
|
|
WINDOW_LIST
|
|
= (Window_getWindows == null) ? new ArrayList<Window>() : null;
|
|
}
|
|
|
|
/**
|
|
* Replaces some chars that are special in a regular expression.
|
|
* @param text The initial text.
|
|
* @return the formatted text
|
|
*/
|
|
public static String replaceSpecialRegExpChars(String text)
|
|
{
|
|
return text.replaceAll("([.()^&$*|])", "\\\\$1");
|
|
}
|
|
|
|
/**
|
|
* Returns the width in pixels of a text.
|
|
* @param c the component where the text is contained
|
|
* @param text the text to measure
|
|
* @return the width in pixels of a text.
|
|
*/
|
|
public static int getStringWidth(Component c, String text)
|
|
{
|
|
return SwingUtilities.computeStringWidth(c
|
|
.getFontMetrics(c.getFont()), text);
|
|
}
|
|
|
|
/**
|
|
* Returns the size of the given text computed towards to the given
|
|
* component.
|
|
* @param c the component where the text is contained
|
|
* @param text the text to measure
|
|
* @return the dimensions of the text
|
|
*/
|
|
public static Dimension getStringSize(Component c, String text)
|
|
{
|
|
// get metrics from the graphics
|
|
FontMetrics metrics = c.getFontMetrics(c.getFont());
|
|
// get the height of a line of text in this font and render context
|
|
int hgt = metrics.getHeight();
|
|
// get the advance of my text in this font and render context
|
|
int adv = metrics.stringWidth(text);
|
|
// calculate the size of a box to hold the text with some padding.
|
|
return new Dimension(adv+2, hgt+2);
|
|
}
|
|
|
|
/**
|
|
* Returns the bounds of the given string.
|
|
* @param text the string to measure
|
|
* @return the bounds of the given string
|
|
*/
|
|
public static Rectangle2D getDefaultStringSize(String text)
|
|
{
|
|
Font font = UIManager.getFont("Label.font");
|
|
FontRenderContext frc = new FontRenderContext(null, true, false);
|
|
TextLayout layout = new TextLayout(text, font, frc);
|
|
|
|
return layout.getBounds();
|
|
}
|
|
|
|
/**
|
|
* Counts occurrences of the <tt>needle</tt> character in the given
|
|
* <tt>text</tt>.
|
|
* @param text the text in which we search
|
|
* @param needle the character we're looking for
|
|
* @return the count of occurrences of the <tt>needle</tt> chat in the
|
|
* given <tt>text</tt>
|
|
*/
|
|
public static int countOccurrences(String text, char needle)
|
|
{
|
|
int count = 0;
|
|
|
|
for (char c : text.toCharArray())
|
|
{
|
|
if (c == needle)
|
|
++count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Compares the two dates. The comparison is based only on the day, month
|
|
* and year values. Returns 0 if the two dates are equals, a value < 0 if
|
|
* the first date is before the second one and > 0 if the first date is after
|
|
* the second one.
|
|
* @param date1 the first date to compare
|
|
* @param date2 the second date to compare with
|
|
* @return Returns 0 if the two dates are equals, a value < 0 if
|
|
* the first date is before the second one and > 0 if the first date is after
|
|
* the second one
|
|
*/
|
|
public static int compareDates(Date date1, Date date2)
|
|
{
|
|
return date1.compareTo(date2);
|
|
}
|
|
|
|
/**
|
|
* Compares the two dates. The comparison is based only on the day, month
|
|
* and year values. Returns 0 if the two dates are equals, a value < 0 if
|
|
* the first date is before the second one and > 0 if the first date is after
|
|
* the second one.
|
|
* @param date1 the first date to compare
|
|
* @param date2 the second date to compare with
|
|
* @return Returns 0 if the two dates are equals, a value < 0 if
|
|
* the first date is before the second one and > 0 if the first date is after
|
|
* the second one
|
|
*/
|
|
public static int compareDates(long date1, long date2)
|
|
{
|
|
return (date1 < date2 ? -1 : (date1 == date2 ? 0 : 1));
|
|
}
|
|
|
|
/**
|
|
* Compares the two dates. The comparison is based only on the day, month
|
|
* and year values. Returns 0 if the two dates are equals, a value < 0 if
|
|
* the first date is before the second one and > 0 if the first date is
|
|
* after the second one.
|
|
* @param date1 the first date to compare
|
|
* @param date2 the second date to compare with
|
|
* @return Returns 0 if the two dates are equals, a value < 0 if
|
|
* the first date is before the second one and > 0 if the first date is
|
|
* after the second one
|
|
*/
|
|
public static int compareDatesOnly(long date1, long date2)
|
|
{
|
|
c1.setTimeInMillis(date1);
|
|
c2.setTimeInMillis(date2);
|
|
|
|
int day1 = c1.get(Calendar.DAY_OF_MONTH);
|
|
int month1 = c1.get(Calendar.MONTH);
|
|
int year1 = c1.get(Calendar.YEAR);
|
|
|
|
int day2 = c2.get(Calendar.DAY_OF_MONTH);
|
|
int month2 = c2.get(Calendar.MONTH);
|
|
int year2 = c2.get(Calendar.YEAR);
|
|
|
|
if (year1 < year2)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (year1 == year2)
|
|
{
|
|
if (month1 < month2)
|
|
return -1;
|
|
else if (month1 == month2)
|
|
{
|
|
if (day1 < day2)
|
|
return -1;
|
|
else if (day1 == day2)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
else
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Formats the given date. The result format is the following:
|
|
* [Month] [Day], [Year]. For example: Dec 24, 2000.
|
|
* @param date the date to format
|
|
* @return the formatted date string
|
|
*/
|
|
public static String formatDate(Date date)
|
|
{
|
|
return formatDate(date.getTime());
|
|
}
|
|
|
|
/**
|
|
* Formats the given date. The result format is the following:
|
|
* [Month] [Day], [Year]. For example: Dec 24, 2000.
|
|
* @param date the date to format
|
|
* @return the formatted date string
|
|
*/
|
|
public static String formatDate(final long date)
|
|
{
|
|
StringBuffer strBuf = new StringBuffer();
|
|
|
|
formatDate(date, strBuf);
|
|
return strBuf.toString();
|
|
}
|
|
|
|
/**
|
|
* Formats the given date as: Month DD, YYYY and appends it to the given
|
|
* <tt>dateStrBuf</tt> string buffer.
|
|
* @param date the date to format
|
|
* @param dateStrBuf the <tt>StringBuffer</tt>, where to append the
|
|
* formatted date
|
|
*/
|
|
public static void formatDate(long date, StringBuffer dateStrBuf)
|
|
{
|
|
c1.setTimeInMillis(date);
|
|
|
|
dateStrBuf.append(GuiUtils.processMonth(c1.get(Calendar.MONTH)));
|
|
dateStrBuf.append(' ');
|
|
GuiUtils.formatTime(c1.get(Calendar.DAY_OF_MONTH), dateStrBuf);
|
|
dateStrBuf.append(", ");
|
|
GuiUtils.formatTime(c1.get(Calendar.YEAR), dateStrBuf);
|
|
}
|
|
|
|
/**
|
|
* Formats the time for the given date. The result format is the following:
|
|
* [Hour]:[Minute]:[Second]. For example: 12:25:30.
|
|
* @param date the date to format
|
|
* @return the formatted hour string
|
|
*/
|
|
public static String formatTime(Date date)
|
|
{
|
|
return formatTime(date.getTime());
|
|
}
|
|
|
|
/**
|
|
* Formats the time for the given date. The result format is the following:
|
|
* [Hour]:[Minute]:[Second]. For example: 12:25:30.
|
|
* @param time the date to format
|
|
* @return the formatted hour string
|
|
*/
|
|
public static String formatTime(long time)
|
|
{
|
|
c1.setTimeInMillis(time);
|
|
|
|
StringBuffer timeStrBuf = new StringBuffer();
|
|
|
|
GuiUtils.formatTime(c1.get(Calendar.HOUR_OF_DAY), timeStrBuf);
|
|
timeStrBuf.append(':');
|
|
GuiUtils.formatTime(c1.get(Calendar.MINUTE), timeStrBuf);
|
|
timeStrBuf.append(':');
|
|
GuiUtils.formatTime(c1.get(Calendar.SECOND), timeStrBuf);
|
|
return timeStrBuf.toString();
|
|
}
|
|
|
|
/**
|
|
* Subtracts the two dates.
|
|
* @param date1 the first date argument
|
|
* @param date2 the second date argument
|
|
* @return the date resulted from the substracting
|
|
*/
|
|
public static Date substractDates(Date date1, Date date2)
|
|
{
|
|
long d1 = date1.getTime();
|
|
long d2 = date2.getTime();
|
|
long difMil = d1-d2;
|
|
long milPerDay = 1000*60*60*24;
|
|
long milPerHour = 1000*60*60;
|
|
long milPerMin = 1000*60;
|
|
long milPerSec = 1000;
|
|
|
|
long days = difMil / milPerDay;
|
|
int hour = (int)(( difMil - days*milPerDay ) / milPerHour);
|
|
int min
|
|
= (int)(( difMil - days*milPerDay - hour*milPerHour ) / milPerMin);
|
|
int sec
|
|
= (int)(( difMil - days*milPerDay - hour*milPerHour - min*milPerMin)
|
|
/ milPerSec);
|
|
|
|
c1.clear();
|
|
c1.set(Calendar.HOUR, hour);
|
|
c1.set(Calendar.MINUTE, min);
|
|
c1.set(Calendar.SECOND, sec);
|
|
|
|
return c1.getTime();
|
|
}
|
|
|
|
/**
|
|
* Gets the display/human-readable string representation of the month with
|
|
* the specified zero-based month number.
|
|
*
|
|
* @param month the zero-based month number
|
|
* @return the corresponding month abbreviation
|
|
*/
|
|
private static String processMonth(int month)
|
|
{
|
|
String monthStringKey;
|
|
|
|
switch (month)
|
|
{
|
|
case 0: monthStringKey = "service.gui.JANUARY"; break;
|
|
case 1: monthStringKey = "service.gui.FEBRUARY"; break;
|
|
case 2: monthStringKey = "service.gui.MARCH"; break;
|
|
case 3: monthStringKey = "service.gui.APRIL"; break;
|
|
case 4: monthStringKey = "service.gui.MAY"; break;
|
|
case 5: monthStringKey = "service.gui.JUNE"; break;
|
|
case 6: monthStringKey = "service.gui.JULY"; break;
|
|
case 7: monthStringKey = "service.gui.AUGUST"; break;
|
|
case 8: monthStringKey = "service.gui.SEPTEMBER"; break;
|
|
case 9: monthStringKey = "service.gui.OCTOBER"; break;
|
|
case 10: monthStringKey = "service.gui.NOVEMBER"; break;
|
|
case 11: monthStringKey = "service.gui.DECEMBER"; break;
|
|
default: return "";
|
|
}
|
|
|
|
return UtilActivator.getResources().getI18NString(monthStringKey);
|
|
}
|
|
|
|
/**
|
|
* Adds a 0 in the beginning of one digit numbers.
|
|
*
|
|
* @param time The time parameter could be hours, minutes or seconds.
|
|
* @param timeStrBuf the <tt>StringBuffer</tt> to which the formatted
|
|
* minutes string is to be appended
|
|
*/
|
|
private static void formatTime(int time, StringBuffer timeStrBuf)
|
|
{
|
|
String timeString = Integer.toString(time);
|
|
|
|
if (timeString.length() < 2)
|
|
timeStrBuf.append('0');
|
|
timeStrBuf.append(timeString);
|
|
}
|
|
|
|
/**
|
|
* Formats the given long to X hour, Y min, Z sec.
|
|
* @param millis the time in milliseconds to format
|
|
* @return the formatted seconds
|
|
*/
|
|
public static String formatSeconds(long millis)
|
|
{
|
|
long[] values = new long[4];
|
|
values[0] = millis / MILLIS_PER_DAY;
|
|
values[1] = (millis / MILLIS_PER_HOUR) % 24;
|
|
values[2] = (millis / MILLIS_PER_MINUTE) % 60;
|
|
values[3] = (millis / MILLIS_PER_SECOND) % 60;
|
|
|
|
String[] fields = { " d ", " h ", " min ", " sec" };
|
|
|
|
StringBuffer buf = new StringBuffer(64);
|
|
boolean valueOutput = false;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
long value = values[i];
|
|
|
|
if (value == 0)
|
|
{
|
|
if (valueOutput)
|
|
buf.append('0').append(fields[i]);
|
|
}
|
|
else
|
|
{
|
|
valueOutput = true;
|
|
buf.append(value).append(fields[i]);
|
|
}
|
|
}
|
|
|
|
return buf.toString().trim();
|
|
}
|
|
|
|
/**
|
|
* Returns an array of all {@code Window}s, both owned and ownerless,
|
|
* created by this application.
|
|
* If called from an applet, the array includes only the {@code Window}s
|
|
* accessible by that applet.
|
|
* <p>
|
|
* <b>Warning:</b> this method may return system created windows, such
|
|
* as a print dialog. Applications should not assume the existence of
|
|
* these dialogs, nor should an application assume anything about these
|
|
* dialogs such as component positions, <code>LayoutManager</code>s
|
|
* or serialization.
|
|
*
|
|
* @return Returns an array of all {@code Window}s.
|
|
*/
|
|
public static Window[] getWindows()
|
|
{
|
|
if (WINDOW_LIST == null)
|
|
{
|
|
Method Window_getWindows = null;
|
|
|
|
try
|
|
{
|
|
Window_getWindows = Window.class.getMethod("getWindows");
|
|
}
|
|
catch (NoSuchMethodException nsme)
|
|
{
|
|
/* Ignore it because we cannot really do anything useful. */
|
|
}
|
|
catch (SecurityException se)
|
|
{
|
|
}
|
|
|
|
Object windows = null;
|
|
|
|
if (Window_getWindows != null)
|
|
{
|
|
try
|
|
{
|
|
windows = Window_getWindows.invoke(null);
|
|
}
|
|
catch (ExceptionInInitializerError eiie)
|
|
{
|
|
/* Ignore it because we cannot really do anything useful. */
|
|
}
|
|
catch (IllegalAccessException iae)
|
|
{
|
|
}
|
|
catch (IllegalArgumentException iae)
|
|
{
|
|
}
|
|
catch (InvocationTargetException ite)
|
|
{
|
|
}
|
|
catch (NullPointerException npe)
|
|
{
|
|
}
|
|
}
|
|
|
|
return
|
|
(windows instanceof Window[])
|
|
? (Window[]) windows
|
|
: new Window[0];
|
|
}
|
|
else
|
|
{
|
|
synchronized (WINDOW_LIST)
|
|
{
|
|
return WINDOW_LIST.toArray(new Window[WINDOW_LIST.size()]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a {@link Window} into window list
|
|
* @param w {@link Window} to be added.
|
|
*/
|
|
public static void addWindow(Window w)
|
|
{
|
|
if (WINDOW_LIST != null)
|
|
{
|
|
synchronized (WINDOW_LIST)
|
|
{
|
|
if (!WINDOW_LIST.contains(w))
|
|
WINDOW_LIST.add(w);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes a {@link Window} into window list
|
|
* @param w {@link Window} to be removed.
|
|
*/
|
|
public static void removeWindow(Window w)
|
|
{
|
|
if (WINDOW_LIST != null)
|
|
{
|
|
synchronized (WINDOW_LIST)
|
|
{
|
|
WINDOW_LIST.remove(w);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A simple minded look and feel change: ask each node in the tree
|
|
* to <code>updateUI()</code> -- that is, to initialize its UI property
|
|
* with the current look and feel.
|
|
*
|
|
* @param c UI component.
|
|
*/
|
|
public static void updateComponentTreeUI(Component c)
|
|
{
|
|
updateComponentTreeUI0(c);
|
|
c.invalidate();
|
|
c.validate();
|
|
c.repaint();
|
|
}
|
|
|
|
/**
|
|
* Returns the index of the given component in the given container.
|
|
*
|
|
* @param c the Component to look for
|
|
* @param container the parent container, where this component is added
|
|
* @return the index of the component in the container or -1 if no such
|
|
* component is contained in the container
|
|
*/
|
|
public static int getComponentIndex(Component c, Container container)
|
|
{
|
|
for (int i = 0, count = container.getComponentCount(); i < count; i++)
|
|
{
|
|
if (container.getComponent(i).equals(c))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Repaints UI tree recursively.
|
|
* @param c UI component.
|
|
*/
|
|
private static void updateComponentTreeUI0(Component c)
|
|
{
|
|
if (c instanceof JComponent)
|
|
{
|
|
JComponent jc = (JComponent) c;
|
|
jc.invalidate();
|
|
jc.validate();
|
|
jc.repaint();
|
|
JPopupMenu jpm =jc.getComponentPopupMenu();
|
|
if(jpm != null && jpm.isVisible() && jpm.getInvoker() == jc)
|
|
{
|
|
updateComponentTreeUI(jpm);
|
|
}
|
|
}
|
|
Component[] children = null;
|
|
if (c instanceof JMenu)
|
|
{
|
|
children = ((JMenu)c).getMenuComponents();
|
|
}
|
|
else if (c instanceof java.awt.Container)
|
|
{
|
|
children = ((java.awt.Container)c).getComponents();
|
|
}
|
|
if (children != null)
|
|
{
|
|
for(int i = 0; i < children.length; i++)
|
|
updateComponentTreeUI0(children[i]);
|
|
}
|
|
}
|
|
}
|