Changing color of selected text in jTextPane - java

I am creating a text editor using a JTextPane that allows the user to change the color of selected text. But when the user selects the text, then chooses the option to change the color (say, to red) the text does not appear as red until the text is unselected. I tried using setSelectedTextColor to change the color of the selected text, but that doesn't work since that changes the text to red anytime text is selected afterwards. Is there a way to have selected text show up as it's actual color? Or like the way it works in Word where it's not the actual color of the text, but when text of different colors are selected they show up as different colors even when selected.
I use the following code to set up the JTextPane and button that changes the selected text to red:
JButton redButton = new JButton(new StyledEditorKit.ForegroundAction("red", Color.RED));
redButton.setFocusable(false);
buttonPanel.add(redButton);
The JTextPane is set up as with content type HTML and uses the HTMLEditorKit:
p=new JTextPane();
p.setSize(300, 300);
kit = new HTMLEditorKit();
p.setEditorKit(kit);
p.setDocument(kit.createDefaultDocument());
p.setContentType("text/html");
p.setEditable(true);
Let me know if you need more source code to understand the question. Thank You!

Take a look at the DefaultHighlightPainter inner class of DefaultHighlighter.
The method
public void paint(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
Rectangle alloc = bounds.getBounds();
try {
// --- determine locations ---
TextUI mapper = c.getUI();
Rectangle p0 = mapper.modelToView(c, offs0);
Rectangle p1 = mapper.modelToView(c, offs1);
// --- render ---
Color color = getColor();
if (color == null) {
g.setColor(c.getSelectionColor());
}
else {
g.setColor(color);
}
As you can see it uses either getColor() or getSelectionColor(). You can extend the class and adapt the highlight painting.
Or use a simpler approach to override your JTextPane's getSelectionColor(). In the method just check whether text is selected and use attributes of selected elements to get desired ccolor. If nothing is selected just return super.getSelectedColor()
UPDATE:
Actually applying colors for selection is used on low level GlyphView's
public void paint(Graphics g, Shape a) {
...
JTextComponent tc = (JTextComponent) c;
Color selFG = tc.getSelectedTextColor();
if (// there's a highlighter (bug 4532590), and
(tc.getHighlighter() != null) &&
// selected text color is different from regular foreground
(selFG != null) && !selFG.equals(fg)) {
Highlighter.Highlight[] h = tc.getHighlighter().getHighlights();
if(h.length != 0) {
boolean initialized = false;
int viewSelectionCount = 0;
for (int i = 0; i < h.length; i++) {
Highlighter.Highlight highlight = h[i];
int hStart = highlight.getStartOffset();
int hEnd = highlight.getEndOffset();
if (hStart > p1 || hEnd < p0) {
// the selection is out of this view
continue;
}
if (!SwingUtilities2.useSelectedTextColor(highlight, tc)) {
continue;
}
...
As you can see applying selection color vs default color of the view is defined in the SwingUtilities2.useSelectedTextColor(highlight, tc)
In the sources http://kickjava.com/src/com/sun/java/swing/SwingUtilities2.java.htm
public static boolean useSelectedTextColor(Highlighter.Highlight JavaDoc h, JTextComponent JavaDoc c) {
Highlighter.HighlightPainter JavaDoc painter = h.getPainter();
String JavaDoc painterClass = painter.getClass().getName();
if (painterClass.indexOf("javax.swing.text.DefaultHighlighter") != 0 &&
painterClass.indexOf("com.sun.java.swing.plaf.windows.WindowsTextUI") != 0) {
return false;
}
try {
DefaultHighlighter.DefaultHighlightPainter JavaDoc defPainter =
(DefaultHighlighter.DefaultHighlightPainter JavaDoc) painter;
if (defPainter.getColor() != null &&
!defPainter.getColor().equals(c.getSelectionColor())) {
return false;
}
} catch (ClassCastException JavaDoc e) {
return false;
}
return true;
}
So using the color depends on L&F and painter. If you define your onw painter the color won't be used.

It sounds like you may be using something other than a font family name. I re-factored this example to use JTextPane and saw the expected result. As noted there, the actions require a font family name, e.g. the default or face=SansSerif, as specified by the FontFamilyAction class nested in StyledEditorKit.
JTextPane textPane = new JTextPane();

Simplest way to change the color of selected Text :
int start = textPane.getSelectionStart();
int end = textPane.getSelectionEnd();
int selectedLength = end - start;
StyleDocument style = pane.getStyledDocument();
//this give your attribute set of selected Text.
AttributeSet oldSet = style.getCharacterElement(end-1).getAttributes();
//StyleContext for creating attribute set
StyleContext sc = StyleContext.getDefaultStyleContext();
// Attribute set which contains new color with old attributes
AttributeSet s = sc.addAttribute(oldSet, StyleConstants.Foreground, Color.RED);
//This set the color of the Text
style.setCharacterAttributes(start, selectedLength, s, true);

Adding my view. This could be further simple then above approaches.
JEditorPane ep = new JEditorPane() {
#Override
public Color getSelectionColor() {
return COLOR_YOU_WANT;
}
#Override
public Color getSelectedTextColor() {
return COLOR_YOU_WANT;
}
};

Related

How to detect when mouse (pointer) is over text in JList?

I am using a JList to display elements. I want to provide a popup menu to interact with the specific elements under the mouse. I am using a MouseInputListener, isPopupTrigger(), locationToIndex(), getCellBounds(), etc. I haven't posted code for this as it's not the point, just background for the question. What I ultimately want to do is only post the popup menu when the correct (platform- and UI-dependent) action occurs over the text in the JList cell - not just anywhere in the row. My JList is in a ScrollPane which is in a SplitPane. The width of the JList cells can be much larger than the text. If the user is able to post the popup by clicking far to the right of the text in the row when the SplitPane is much larger than the extent of the text, it will be unclear just which row is being operated on. I don't want to select the row that the user would be interacting with using the popup menu because selection has a different meaning in this context. So the basic question is: how can I determine if the mouse location when the popup trigger occurs is actually over the text in the row, rather than just in the row?
If the JList's cell renderer returns JLabels (which it will by default, or if you have set the renderer to a DefaultListCellRenderer), you can use SwingUtilities.layoutCompoundLabel to determine the bounds of the text:
static <E> boolean isOverText(Point location,
JList<E> list) {
int index = list.locationToIndex(location);
if (index < 0) {
return false;
}
E value = list.getModel().getElementAt(index);
ListCellRenderer<? super E> renderer = list.getCellRenderer();
Component c = renderer.getListCellRendererComponent(list, value, index,
list.isSelectedIndex(index),
list.getSelectionModel().getLeadSelectionIndex() == index);
if (c instanceof JLabel) {
JLabel label = (JLabel) c;
Icon icon = null;
if (!label.isEnabled()) {
icon = label.getDisabledIcon();
}
if (icon == null) {
icon = label.getIcon();
}
Rectangle listItemBounds =
SwingUtilities.calculateInnerArea(label, null);
Rectangle cellBounds = list.getCellBounds(index, index);
listItemBounds.translate(cellBounds.x, cellBounds.y);
listItemBounds.width = cellBounds.width;
listItemBounds.height = cellBounds.height;
Rectangle textBounds = new Rectangle();
Rectangle iconBounds = new Rectangle();
SwingUtilities.layoutCompoundLabel(label,
label.getFontMetrics(label.getFont()),
label.getText(),
icon,
label.getVerticalAlignment(),
label.getHorizontalAlignment(),
label.getVerticalTextPosition(),
label.getHorizontalTextPosition(),
listItemBounds,
iconBounds,
textBounds,
label.getIconTextGap());
return textBounds.contains(location);
}

Swing change foreground color of BasicArrowButton

In JSpinner class, how would I change the foreground color of two BasicArrowButton(up and down) components?
the component.setForeground(<<a_color>>) doesn't work.
Thanks in advance.
EDIT
private void set_colors(JSpinner spinner){
int n = spinner.getComponentCount();
for (int i=0; i<n; i++)
{
Component c = spinner.getComponent(i);
System.out.println(c);
if (c instanceof BasicArrowButton)
{
c.setForeground(ds_conn_text.getForeground());//doesn't work, doesn't change arrow color
c.setBackground(ds_conn_text.getBackground());
BasicArrowButton c0=(BasicArrowButton) c;c0.setBorder(ok_button.getBorder());
}
}
}
This is a follow-up question of Swing change the JSpinner back and fore colors
It is not possible to only set the color of the arrows without overriding the paint method. The reason is simply that the color for the arrows is the same color as the color that is used for the "shadows" of the buttons. So you could put the line
UIManager.getDefaults().put("controlDkShadow", Color.MAGENTA);
somewhere into your main, but this would not only change the arrow colors, but also other elements' colors which actually should not be changed.
An ugly/hacky way to set this color only for a particular instance would be
private static void hackilySetColor(JSpinner spinner, Color color)
{
int n = spinner.getComponentCount();
for (int i=0; i<n; i++)
{
Component c = spinner.getComponent(i);
if (c instanceof BasicArrowButton)
{
try
{
Field field = BasicArrowButton.class.getDeclaredField("darkShadow");
field.setAccessible(true);
field.set(c, color);
field.setAccessible(false);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
but this uses reflection, is really an ugly hack, and still replaces the color of the "dark shadow" border of the buttons.
You'll be better off with an own UI for things like this (or ... just don't change the color at all - this seems rather useless for me anyhow...)

JPanel in JTable gets focus on mouse click but not on mouse over

I have a table with one column containing JPanels. I have written the custom renderer and editor and they work fine. However, the panel contains a JLabel component with a tool tip. If I click into the cell and hover over the label the tool tip is displayed, but if I move the mouse into the cell and hover over the JLabel the tool tip wont display. I have added a mouse listener to the table as shown below where I get the Panel object and try to make it have focus.
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
int row = table.rowAtPoint(p);
int column = table.columnAtPoint(p);
System.out.println(row + " " + column);
Object o = table.getModel().getValueAt(row, column);
if (o instanceof FileInfoCellPanel) {
FileInfoCellPanel ficp = (FileInfoCellPanel)o;
ficp.requestFocusInWindow();
//ficp.revalidate();
}
}
I am definitely getting the correct JPanel object but it never seems to get focus. So clicking in the cell seems to give focus to the panel but moving the mouse does not.
If you only want to display text in your tooltip, it is sufficient to set the tooltip on the component which you return by the renderer. This can be seen if you look at the implementation of the JTable#getTooltipText method, which I copied below
public String getToolTipText(MouseEvent event) {
String tip = null;
Point p = event.getPoint();
// Locate the renderer under the event location
int hitColumnIndex = columnAtPoint(p);
int hitRowIndex = rowAtPoint(p);
if ((hitColumnIndex != -1) && (hitRowIndex != -1)) {
TableCellRenderer renderer = getCellRenderer(hitRowIndex, hitColumnIndex);
Component component = prepareRenderer(renderer, hitRowIndex, hitColumnIndex);
// Now have to see if the component is a JComponent before
// getting the tip
if (component instanceof JComponent) {
// Convert the event to the renderer's coordinate system
Rectangle cellRect = getCellRect(hitRowIndex, hitColumnIndex, false);
p.translate(-cellRect.x, -cellRect.y);
MouseEvent newEvent = new MouseEvent(component, event.getID(),
event.getWhen(), event.getModifiers(),
p.x, p.y,
event.getXOnScreen(),
event.getYOnScreen(),
event.getClickCount(),
event.isPopupTrigger(),
MouseEvent.NOBUTTON);
tip = ((JComponent)component).getToolTipText(newEvent);
}
}
// No tip from the renderer get our own tip
if (tip == null)
tip = getToolTipText();
return tip;
}
As can be seen, to determine the tooltip text the renderer is asked for a component, and that component is asked for its tooltip text. Of course this only works for texts and not for e.g. images

BasicTabbedPaneUI paint html text

i would extend the BasicTabbedPaneUi so i can design my own tabPane. I have one problem with the html text, is to set the color of the text once the tab is selected. I override the paintText method with the following, almost the same code as the orignal method:
#Override
protected void paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics, int tabIndex, String title, Rectangle textRect, boolean isSelected) {
g.setFont(font);
View v = getTextViewForTab(tabIndex);
if (v != null) {
// html
Color fg = tabPane.getForegroundAt(tabIndex);
if (isSelected && (fg instanceof UIResource)) {
Color selectedFG = UIManager.getColor(
"TabbedPane.selectedForeground");
if (selectedFG != null) {
fg = selectedFG;
}
}
v.paint(g, textRect);
} else {
// plain text
int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
Color fg = tabPane.getForegroundAt(tabIndex);
if (isSelected && (fg instanceof UIResource)) {
Color selectedFG = UIManager.getColor(
"TabbedPane.selectedForeground");
if (selectedFG != null) {
fg = selectedFG;
}
}
g.setColor(fg);
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
} else { // tab disabled
g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
SwingUtilities2.drawStringUnderlineCharAt(tabPane, g,
title, mnemIndex,
textRect.x - 1, textRect.y + metrics.getAscent() - 1);
}
}
}
In the case we have html text for the tab, it v is not null. If i set a color to the graphics object used in the paint method it does not change the text color.
I use html because i want to have my tab's text on two lines.
Thanks for helping changing the color.
I would be reluctant to develop a custom TabbedPaneUI unless it were part of a complete Look & Feel implmentation.
Instead, consider a custom tab component, as shown in TabComponentsDemo and discussed in How to Use Tabbed Panes. This will give you absolute control over the component's appearance, without sacrificing compatibility with the user's chosen Look & Feel.

Casting error in Java

In my application, I am reading a .xml file and writing the data in a JTable. Apart from the data for the table, the .xml file contains an attribute defining the background color of each row. My method for cell rendering looks something like this:
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int col) {
JComponent comp = new JLabel();
if (null != value) {
//reading the data and writing it in the comp
}
GenericTableModel model = (GenericTableModel) table.getModel();
GenericObject go = model.getRowObject(row);
Color test = new Color(255, 255, 255);
if (go.getValueByName("COLOR") == null){
}else{
test =(Color) go.getValueByName("COLOR");
}
comp.setBackground(test);
return comp;
}
The .xml file is initialized within the program. My problem is that I don't know how to define the color in the file so that the variable test will be able to save it as a color. I tried writing it as "Color.white", "white" and even "255, 255, 255" but i get a casting error when I try saving it in the variable.
Any ideas as to how could I define the color in the file?
I take it that GenericObject#getValueByName() returns a string, right? In that case you need to convert the string to something that can be used to create a Color instance. Assuming that the string is "R,G,B", then split the string on the comma, convert each component to an integer and create a color:
public static Color fromString(String rgb, Color deflt) {
String[] comp = rgb.split(",");
if (comp.length != 3)
return deflt;
int rc[] = new int[3];
for (int i = 0; i < 3; ++i) {
rc[i] = Integer.parseInt(comp[i].trim());
if (rc[i] < 0 || rc[i] > 255)
return deflt;
}
Color c = new Color(rc[0], rc[1], rc[2]);
return c;
}
The other alternative is to define the color field with color names matching the predefined static fields in Color (Color.BLACK, Color.RED, etc), and use reflection to get the correct field, but I leave that as an excercise.
As a followup to forty-two's answer, it really depends on how the color is supposed to be stored in the XML. It would also be possible to save the color's value as a single string (no commas), representing either the decimal or hex value of the color. (Hex is more human-readable for colors, e.g. "FFFF00" for Yellow instead of "16776960")
e.g. as decimal (and with no error checking, for the record, I like default values like forty-two used)
public static Color readColor(String decimalString) {
return new Color(Integer.parseInt(decimalString));
}
public String writeColor(Color color) {
return Integer.toString(color.getRGB());
}
e.g. as hex (you need avoidOverflows to handle colors with alpha values like F0123456)
public static Color readColor(String hexString) {
long avoidOverflows = Long.parseLong(hexString, 16);
return new Color((int)long);
}
public String writeColor(Color color) {
return Integer.toHexString(color.getRGB(), 16);
}
I've even seen the hex values preceded by a "#" to make them more HTML-like. So, it really depends on the spec for your XML.

Categories