How to highlight part of a JLabel? - java

Before any one suggests HTML, I explain later why thats not an option here. I have a table that contains a column with text cells in it. I need to be able to highlight some of the text in each cell. So for example if the cell contained "cat foo dog"... I might want to highlight foo.
My current method is to use a custom TableCellRenderer that puts html into a JLabel component that gets rendered and for a time it was good. Then I noticed that when the text in the cell became too long to fit in the column width it just truncated the text without the normal "..." that happens normally in this case. Thus users didnt know there was more text they were not seeing. The other problem was that if the original text itself contained HTML, which in my case it does at times, the cell would not render correctly. I know I could just escape the html but I would still have the prevous problem.
If I use a component other than a jlabel then it makes my table's cells look strange. Does any one have any suggestions? Thanks

Well, here is a solution.
In short, you can subclass JLabel to draw the highlight manually. Override the paintComponent method to do the actual drawing and use FontMetrics to calculate where the highlighted region should be drawn.
Here is that answer in excruciating detail:
Basically, you can make a subclass of JLabel that can highlight stuff. I would do that like this; you may want to do it somewhat differently:
Add a method that tells the label which part to highlight. This could be something like this, assuming you just need one highlighted region:
public void highlightRegion(int start, int end) {
// Set some field to tell you where the highlight starts and ends...
}
If you need multiple regions, just have an ArrayList instead of a simple field. A method for dehighlighting would probably be useful too.
Now, you need to override the paintComponent method of JLabel. Here you need to do several discrete steps, which you may want to organize in different methods or something. For simplicity, I'll put it all in the paint method.
#Override
protected void paintComponent(Graphics g) {
...
First, you need to figure out the physical dimensions of the highlight, which you can do using the nice FontMetrics class. Create the FontMetrics class for the Font you're using.
FontMetrics metrics = new FontMetrics(getFont());
Now you can get all the information you need to create a rectangle that will be the highlight. You'll need the starting position, the height and the width. To get this, you'll need two substrings of the JLabel's text as follows:
String start = getText().substring(0, startOfHighlight);
String text = getText().substring(startOfHighlight, endOfHighlight);
//You may also need to account for some offsets here:
int startX = metrics.stringWidth(start);
int startY = 0; //You probably have some vertical offset to add here.
int length = metrics.stringWidth(text);
int height = metrics.getHeight();
Now you can draw the highlighted region before drawing the rest of the label:
g.fillRect(startX, startY, length, height);
super.paintComponent(g);
}
Of course, if you want the highlight to span multiple rows, that will require more work.
If you were wondering, I have actually written something like this before. On a whim, I decided to write my own text area type component from a JPanel, and this was basically the way I handled highlighting. Reinventing the wheel may be stupid in an actual project, but it does teach you random stuff that may come in useful...

Can't resist to throw the SwingX renderer decoration mechanism into the ring: its way to solve the requirement is to implement a Highlighter doing it. Which in fact is already done (though not yet in the officially supported) but hidden in the SwingLabs-Demos project, named X/MatchingTextHighlighter (you would need both) and recently fixed to cope with icons, RToL-ComponentOrientation, alignment, ellipses ..

Thats a great answer, and probably the best solution. But an alternative that some might find simpler is to use a JTextfield instead of a JLabel for rendering then you can use JTextfields highlighting capabilities i.e
void highlightWhitespaceText(JTextField text)
{
text.setHighlighter(AbstractTableCellRenderer.defaultHighlighter);
try
{
Matcher m = AbstractTableCellRenderer.whitespaceStartPattern.matcher(text.getText());
if (m.matches())
{
text.getHighlighter().addHighlight(m.start(1), m.end(1), AbstractTableCellRenderer.highlightPainter);
}
m = AbstractTableCellRenderer.whitespaceEndPattern.matcher(text.getText());
if (m.matches())
{
text.getHighlighter().addHighlight(m.start(1), m.end(1), AbstractTableCellRenderer.highlightPainter);
}
}
catch (BadLocationException ble)
{
//
}
}
You can change the properties of a JTextfield so it looks like a jLabel in other respects.

Related

RichTextFX line numbers column extended for entire text area

Could someone tell me how to implement RichTextFX CodeArea with line numbers section extended till the end of the area?
This is how it looks like now:
I don't need line numbers after line 12 but I would like to see this grey bar to fill the entire text area.
Something like here:
P.S. I'm not sure if this is even possible.
I know this is a rather old question, but since I had the same problem, let me share the solution I came up with.
What I did is to smuggle in a rectangle and make sure it is the bottom-most element (i.e. basically part of the background). However, there are a few gotchas when doing this, because the underlying CodeArea is not aware of our new node. If you just insert the rectangle, it might get removed when the CodeArea decides to rebuild the nodes. And getting the right width is a bit tricky because the width of line numbers can basically change at any time and the line-number labels themselves fade in and out of existence whenever you scroll.
So, in order to address these issues, my code sits in the layoutChildren() method and is thus called whenever the nodes in the editor have changed. First we check that the rectangle is actually there as the bottom-most node or insert it if missing. Second, we set the width of the rectangle to the width of the first visible line-number label (which might fail if there are no paragraphs at the moment).
The code itself here is in Scala, but probably easy enough to be quickly adapted to Java.
class MyCodeArea extends CodeArea {
protected val gutterRect = new Rectangle()
gutterRect.heightProperty.bind(this.heightProperty)
gutterRect.getStyleClass.add("lineno")
override protected def layoutChildren(): Unit = {
try {
val children = getChildren
if (!(children.get(0) eq gutterRect))
children.add(0, gutterRect)
val index = visibleParToAllParIndex(0)
val wd = getParagraphGraphic(index).prefWidth(-1)
gutterRect.setWidth(wd)
} catch {
case _: Throwable =>
}
super.layoutChildren()
}
}
Unfortunately, the colour of the rectangle must be assimilated manually. The reason is that the Labels used for line numbers use -fx-background-color, whereas the Rectangle uses -fx-fill. Hence, just setting the same CSS class "lineno" (as I did in my code above) does little to get the colour right. But it allowed me to put both into the same CSS class and therefore have one place where I can change it:
.lineno {
-fx-fill: ivory; // or whatever colour you like
-fx-background-color: -fx-fill;
}

How to get Alternative row colours using a gridControl component

I'm currently working on redoing the GUI of a project which uses the component GridControl which is an extension of gridView, found here:
http://techpubs.borland.com/books/jbuilder/jbuilder2/jbuilder/reference/borland.jbcl.control.GridControl.html
I'm not allowed to change the components used so I have to find a way to do this with gridControl, however I'm relatively new at programming, so don't really know what I'm doing. I've been asked to get the alternative rows of the table to be grey. I can't find a specific function of the component which caters for this like JTabel seems to have so I was hoping that someone would be able to help. The only one which seems related is .setBackground which seems to only affect the full table.
Thanks very much in advance.
Check out THIS link. It has a mention of this function:
public Color getBackgroundColor(..):callback method automatically invoked by the grid to retrieve the background color for each grid's cell. A programmer can override this method to specify distinct colors for grid's cells.
If the method is not overridden, then rows are colored alternatively with two different colors: one color is defined through ClientSettings.GRID_CELL_BACKGROUND attribute and the other color is slightly different from the first one, defined through getDeltaColor() method.
now, the defaule colour values for ClientSettings.GRID_CELL_BACKGROUND is rgb (238,238,238) which is grey, and for deltaColor is rgb (235,235,235) also grey.
so, the cells should all be grey, unless there's been an override for the function public Color getBackgroundColor(..), something like this:
public Color getBackgroundColor(int row,String attributedName,Object value) {
return new Color(255,255,255); /*for all cells to be White*/
}
In order to make alternate rows grey (assuming the other color white) find the override of the above method and change its body to get your required colours, for eg:
public Color getBackgroundColor(int row,String attributedName,Object value) {
if (row%2==0)
return new Color(255,255,255); /*sets White background for even rows*/
else
return new Color(238,238,238); /*sets Grey background for odd rows*/
}
I'm relatively new at programming, so don't really know what I'm doing hmm.. there are a lot of help/examples available online. you need to learn to search intelligently and follow it up with some background theory to learn on your own.. and you'll survive (for now).

ListCellRenderer - JLabel - String - n rows

So I have this situation:
I have a JList that displays a bunch of Strings.
However, these strings are really long and JList is really narrow. Meaning the strings won't fit.
What I want to do is make each entry to have two rows, like this:
|Word word word |
|word word wor...|
It would do wordWrap for the first row, and then finish the secon't row by cutting the rest of the string and appeinging the three dots to what is left in a way maximum space is filled.
It doesn't really matter what I do, the important thing is that I have to use FontMetrics to measure all this stuff so I can make it work. And that's the catch.
Until whole getListCellRendererComponent(...) method is executed, the component will not be painted, thus having no graphics, thus making any font measurement impossible.
How do I get around it?
P.S. I need to use the JLabel for the visuals.
the component will not be painted, thus having no graphics, thus making any font measurement impossible.
You don't need the Graphics to use the FontMetrics.
See Left Dot Renderer which is used for a JTable, but the concepts should be the same for a list renderer as well.
You need to use JLabels? It'd be a lot easier to have getListCellRendererComponent() return a JTextArea whose height has been set to 2 lines and which has setLineWrap() and setWrapStyleWord() both set to true. Make the JTextArea uneditable, and it'll look like a 2-line JLabel.

How to determine the length of a graphic string?

I'm creating a graphical timeline out of an excel document and I need to have small tags of the name of the event next to the marker for that event. Some of these are easy and are right justified but others are left justified and I need to figure out their width so that I can properly offset them.
window.drawString("7/4-Fourth of July",horizontalIndex-Offset,verticalIndex);
Currently I'm averaging the pixel width using an average of both font sizes 10 and 32, but this doesn't really cut it. Can someone help me get the exact offset?
This thread explains how to do it:
Calculate the display width of a string in Java
You should first get the font metrics, and then ask the metrics how wide a certain string is.
from a java.awt.Graphics object, you can call getFontMetrics. the FontMetrics object has a getStringBounds method that does what you need.
here's the documentation
and another good alternative is SwingUtilities#computeStringWidth(FontMetrics fm, String str)
As a (read: my ;-) general rule, never use the Graphics-level drawString methods. Instead, use a JLabel/CellRendererPane pair to "stamp" the text onto whatever component.
The advantages
anti-alias is handled automagically
size calculations are done in the labels' bowels, so positioning calculations dont require any low-level methods but simply based on the labels' prefSize
TextLayout, shown here, is another alternative.

Calculation of required display space for different subclasses of JComponent

For my current project i am writing a JTable based GUI. One of the main features is the ability to adjust the sizes of all cells at runtime, depending on the contents (which change over time). Currently all cells have the same height and width, when the application is started. I would like to change that to a more sophisticated approach. I was wondering if it would be somehow possible to determine the space needed by "the content" to be displayed properly. That is without to much empty space or cutting something of.
"The content" is a string for starters. It is loaded from a database and i can't make any assumptions whatsoever about it. It may be null. In this case there should be any kind of default size for the corresponding cell.
In the long run there will be all different kinds of content to be displayed, like pictures, video and so on.
I tried working with FontMetrics to calculate the length of the strings. But since i'm using JTextPanes to display them, i can't get it to work exactly. I think this has to do with JTextPanes automatic word wrapping because sometimes the lines aren't filled up. This screws up my calculations.
Well long story short: I need some kind of design guideline to achieve the feature descriped above. I'm sure one of you clever guys knows just how to do it.
Thanks in advance,
DeKay
Maybe the text pane size calculation in this How can I measure/calculate the size a Document needs to render itself? will help you out.
As you are using JTextPane for rendering, you may find this Q&A helpful.
The conversion textPane.modelToView() always comes out to null.
Note that modelToView "Returns: the coordinates as a rectangle ... or null if the component does not yet have a positive size."
I have still no idea, how to calculate the amount of space needed in general.
IIUC, the key to understanding #camickr's example is the use of setPreferredSize() to include the text pane's changed boundary, followed by validate() which "is used to cause a container to lay out its subcomponents again."
To set the height of a row in a JTable, look at here:
public void setRowHeight(int row, int rowHeight)
To set a column width, you have to look at the TableCOlumn API here:
public void setWidth(int width)
Hope this can help

Categories

Resources