/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.plugin.desktoputil.plaf; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.plaf.*; import javax.swing.text.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.util.skin.*; import org.jitsi.service.resources.*; import org.jitsi.util.*; /** * The <tt>SearchTextFieldUI</tt> is the one responsible for the search field * look & feel. It draws a search icon inside the field and adjusts the bounds * of the editor rectangle according to it. * * @author Yana Stamcheva * @author Adam Netocny * @author Marin Dzhigarov * @author Damian Minkov */ public class SearchFieldUI extends SIPCommTextFieldUI implements Skinnable { /** * Creates a UI for a SearchFieldUI. * * @param c the text field * @return the UI */ public static ComponentUI createUI(JComponent c) { return new SearchFieldUI(); } /** * The default icon of the call button. */ private Image callIcon; /** * The pressed icon of the call button. */ private Image callPressedIcon; /** * The roll over icon of the call button. */ private Image callRolloverIcon; /** * The default icon of the sms button. */ private Image smsIcon; /** * The pressed icon of the sms button. */ private Image smsPressedIcon; /** * The roll over icon of the sms button. */ private Image smsRolloverIcon; /** * The call button tool tip string. */ private final String callString = DesktopUtilActivator.getResources() .getI18NString("service.gui.CALL"); /** * The sms button tool tip string. */ private final String smsString = DesktopUtilActivator.getResources() .getI18NString("service.gui.SMS"); /** * Indicates if the call button is enabled in this search field. */ private boolean isCallButtonEnabled = true; /** * Indicates if the sms button is enabled in this search field. */ private boolean isSMSButtonEnabled = false; /** * Indicates if the call icon is currently visible. */ protected boolean isCallIconVisible = false; /** * Indicates if the mouse is currently over the call button. */ private boolean isCallMouseOver = false; /** * Indicates if the mouse is currently over the call button. */ private boolean isCallMousePressed = false; /** * Indicates if the sms icon is currently visible. */ protected boolean isSMSIconVisible = false; /** * Indicates if the mouse is currently over the sms button. */ private boolean isSMSMouseOver = false; /** * Indicates if the mouse is currently over the sms button. */ private boolean isSMSMousePressed = false; /** * The icon indicating that this is a search field. */ private ImageIcon searchIcon; /** * The separator icon shown between the call icon and the close. */ private Image separatorIcon; /** * Creates a <tt>GenericSearchFieldUI</tt>. */ public SearchFieldUI() { isCallButtonEnabled = false; loadSkin(); } /** * Paints the background of the associated component. * @param g the <tt>Graphics</tt> object used for painting */ @Override protected void customPaintBackground(Graphics g) { Graphics2D g2 = (Graphics2D) g.create(); try { AntialiasingManager.activateAntialiasing(g2); super.customPaintBackground(g2); JTextComponent c = this.getComponent(); int dy = c.getHeight() / 2 - searchIcon.getIconHeight()/2; g2.drawImage(searchIcon.getImage(), 3, dy, null); if (c.getText() != null && c.getText().length() > 0 && !StringUtils.containsLetters(c.getText()) && isSMSButtonEnabled) { // Paint sms button. Rectangle smsRect = getSMSButtonRect(); int dx = smsRect.x; dy = smsRect.y; if (isSMSMousePressed) g2.drawImage(smsPressedIcon, dx, dy, null); else if (isSMSMouseOver) { g2.drawImage(smsRolloverIcon, dx, dy, null); } else g2.drawImage(smsIcon, dx, dy, null); isSMSIconVisible = true; } else isSMSIconVisible = false; if (c.getText() != null && c.getText().length() > 0 && isCallButtonEnabled) { // Paint call button. Rectangle callRect = getCallButtonRect(); int dx = callRect.x; dy = callRect.y; if (isCallMousePressed) g2.drawImage(callPressedIcon, dx, dy, null); else if (isCallMouseOver) g2.drawImage(callRolloverIcon, dx, dy, null); else g2.drawImage(callIcon, dx, dy, null); g2.drawImage( separatorIcon, dx + callRect.width + 3, dy + (callRect.height - separatorIcon.getHeight(null))/2, null); isCallIconVisible = true; } else isCallIconVisible = false; } finally { g2.dispose(); } } /** * Calculates the call button rectangle. * * @return the call button rectangle */ protected Rectangle getCallButtonRect() { Component c = getComponent(); Rectangle rect = c.getBounds(); int dx = getDeleteButtonRect().x - callRolloverIcon.getWidth(null) - 8; int dy = (rect.height) / 2 - callRolloverIcon.getHeight(null)/2; return new Rectangle( dx, dy, callRolloverIcon.getWidth(null), callRolloverIcon.getHeight(null)); } /** * Calculates the sms button rectangle. * * @return the sms button rectangle */ protected Rectangle getSMSButtonRect() { Component c = getComponent(); Rectangle rect = c.getBounds(); int dx = getDeleteButtonRect().x - smsRolloverIcon.getWidth(null) - 8; if(isCallIconVisible) dx -= callRolloverIcon.getWidth(null) + 4; int dy = (rect.y + rect.height) / 2 - smsRolloverIcon.getHeight(null)/2; return new Rectangle( dx, dy, smsRolloverIcon.getWidth(null), smsRolloverIcon.getHeight(null)); } /** * If we are in the case of disabled delete button, we simply call the * parent implementation of this method, otherwise we recalculate the editor * rectangle in order to leave place for the delete button. * @return the visible editor rectangle */ @Override protected Rectangle getVisibleEditorRect() { Rectangle rect = super.getVisibleEditorRect(); // Fixes NullPointerException if the rectangle is null for some reason. if (rect == null) return null; if ((rect.width > 0) && (rect.height > 0)) { rect.x += searchIcon.getIconWidth() + 5; rect.width -= (searchIcon.getIconWidth() + 5); if (isSMSIconVisible) rect.width -= (smsRolloverIcon.getWidth(null) + 12); if (isCallIconVisible) rect.width -= (callRolloverIcon.getWidth(null) + 12); else rect.width -= 8; return rect; } return null; } /** * Reloads UI icons. */ @Override public void loadSkin() { super.loadSkin(); ResourceManagementService r = DesktopUtilActivator.getResources(); searchIcon = r.getImage("service.gui.icons.SEARCH_ICON"); if (isCallButtonEnabled) { callIcon = r.getImage("service.gui.buttons.SEARCH_CALL_ICON").getImage(); callRolloverIcon = r.getImage("service.gui.buttons.SEARCH_CALL_ROLLOVER_ICON") .getImage(); callPressedIcon = r.getImage("service.gui.buttons.SEARCH_CALL_PRESSED_ICON") .getImage(); } if (isSMSButtonEnabled) { loadSMSIcons(); } if(isSMSButtonEnabled || isCallButtonEnabled) separatorIcon = r.getImage("service.gui.icons.SEARCH_SEPARATOR").getImage(); } /** * Loads sms icons. */ private void loadSMSIcons() { ResourceManagementService r = DesktopUtilActivator.getResources(); smsIcon = r.getImage("service.gui.buttons.SEARCH_SMS_ICON").getImage(); smsRolloverIcon = r.getImage("service.gui.buttons.SEARCH_SMS_ROLLOVER_ICON") .getImage(); smsPressedIcon = r.getImage("service.gui.buttons.SEARCH_SMS_PRESSED_ICON") .getImage(); } /** * Updates the call button when the mouse was clicked. * @param e the <tt>MouseEvent</tt> that notified us of the click */ @Override public void mouseClicked(MouseEvent e) { super.mouseClicked(e); if(isCallButtonEnabled || isSMSButtonEnabled) updateIcon(e); } /** * Updates the delete icon when the mouse is dragged over. * @param e the <tt>MouseEvent</tt> that notified us */ @Override public void mouseDragged(MouseEvent e) { super.mouseDragged(e); if(isCallButtonEnabled || isSMSButtonEnabled) updateIcon(e); } /** * Updates the call button when the mouse is enters the component area. * @param e the <tt>MouseEvent</tt> that notified us */ @Override public void mouseEntered(MouseEvent e) { super.mouseEntered(e); if(isCallButtonEnabled || isSMSButtonEnabled) updateIcon(e); } /** * Updates the call button when the mouse exits the component area. * @param e the <tt>MouseEvent</tt> that notified us */ @Override public void mouseExited(MouseEvent e) { super.mouseExited(e); if(isCallButtonEnabled || isSMSButtonEnabled) updateIcon(e); } /** * Updates the delete icon when the mouse is moved over. * @param e the <tt>MouseEvent</tt> that notified us */ @Override public void mouseMoved(MouseEvent e) { super.mouseMoved(e); if(isCallButtonEnabled || isSMSButtonEnabled) updateIcon(e); } @Override public void mousePressed(MouseEvent e) { super.mousePressed(e); if(isCallButtonEnabled || isSMSButtonEnabled) updateIcon(e); } @Override public void mouseReleased(MouseEvent e) { super.mouseReleased(e); if(isCallButtonEnabled || isSMSButtonEnabled) updateIcon(e); } /** * Implements parent paintSafely method and enables antialiasing. * @param g the <tt>Graphics</tt> object that notified us */ @Override protected void paintSafely(Graphics g) { customPaintBackground(g); super.paintSafely(g); } /** * Enables/disabled the call button in the search field. * * @param isEnabled indicates if the call button is enabled */ public void setCallButtonEnabled(boolean isEnabled) { JTextComponent c = this.getComponent(); if(isCallButtonEnabled != isEnabled) { this.isCallButtonEnabled = isEnabled; loadSkin(); if(c != null && c.isVisible()) c.repaint(); } } /** * Enables/disabled the sms button in the search field. * * @param isEnabled indicates if the sms button is enabled */ public void setSMSButtonEnabled(boolean isEnabled) { loadSMSIcons(); this.isSMSButtonEnabled = isEnabled; } /** * Updates the delete icon, changes the cursor and deletes the content of * the associated text component when the mouse is pressed over the delete * icon. * * @param evt the mouse event that has prompted us to update the delete * icon. */ protected void updateIcon(MouseEvent evt) { int x = evt.getX(); int y = evt.getY(); boolean outsideButtons = true; if (isCallIconVisible && getCallButtonRect().contains(x, y)) { JTextComponent c = getComponent(); String searchText = c.getText(); if (searchText == null) return; outsideButtons = false; // Show a tool tip over the call button. getComponent().setToolTipText(callString + " " + searchText); ToolTipManager.sharedInstance().mouseEntered( new MouseEvent(c, 0, x, y, x, y, // X-Y of the mouse for the tool tip 0, false)); if (evt.getID() == MouseEvent.MOUSE_PRESSED) { isCallMouseOver = false; isCallMousePressed = true; } else { isCallMouseOver = true; isCallMousePressed = false; } // Update the default cursor. getComponent().setCursor(Cursor.getDefaultCursor()); } if (isSMSIconVisible && getSMSButtonRect().contains(x, y)) { JTextComponent c = getComponent(); String searchText = c.getText(); if (searchText == null) return; outsideButtons = false; // Show a tool tip over the call button. getComponent().setToolTipText(smsString + " " + searchText); ToolTipManager.sharedInstance().mouseEntered( new MouseEvent(c, 0, x, y, x, y, // X-Y of the mouse for the tool tip 0, false)); if (evt.getID() == MouseEvent.MOUSE_PRESSED) { isSMSMouseOver = false; isSMSMousePressed = true; } else { isSMSMouseOver = true; isSMSMousePressed = false; } // Update the default cursor. getComponent().setCursor(Cursor.getDefaultCursor()); } if(outsideButtons) { // Remove the call button tool tip when the mouse exits the call // button area. getComponent().setToolTipText(""); ToolTipManager.sharedInstance().mouseExited( new MouseEvent(getComponent(), 0, x, y, x, y, // X-Y of the mouse for the tool tip 0, false)); isCallMouseOver = false; isCallMousePressed = false; isSMSMouseOver = false; isSMSMousePressed = false; } getComponent().repaint(); } }