I have an SWT Table with multiple columns. In this example, let's say there are three columns. The cells of each column all contain an image followed by some text (example pictured below). However, as you can see there is no spacing between the 2 - 3 column lines and their cells.
Is there a built-in way to add a buffer zone to those cells so the icons don't appear right on the edge line? Such as an offset property of some kind? I don't see any property overtly listed in either Table or TableColumn.
If not, is there a workaround beyond just adding white space to the cell images?
Please let me know if there's anything I can do to make my question clearer.
Table:
I don't think there is a designated way to adjust the margin and spacing of image and text within a cell. Apart from adding transparent pixels to the image (as you already suggested), you can use a PaintListener to gain control over how a cell is rendered.
The example below draws image and text with adjustable margin and spacing:
Listener paintListener = new Listener() {
int leftMargin = 40;
int rightMargin = 10;
int imageSpacing = 200;
#Override
public void handleEvent( Event event ) {
TableItem item = ( TableItem )event.item;
Rectangle imageBounds = image.getBounds();
Point textExtent = event.gc.textExtent( item.getText() );
switch( event.type ) {
case SWT.MeasureItem: {
event.width += leftMargin + imageBounds.width + imageSpacing + textExtent.x + rightMargin;
event.height = Math.max( event.height, imageBounds.height + 2 );
event.height = Math.max( event.height, textExtent.y + 2 );
break;
}
case SWT.PaintItem: {
int x = event.x + leftMargin;
int imageOffset = ( event.height - imageBounds.height ) / 2;
event.gc.drawImage( image, x, event.y + imageOffset );
x += imageSpacing;
int textOffset = ( event.height - textExtent.y ) / 2;
event.gc.drawText( item.getText(), x, event.y + textOffset );
break;
}
case SWT.EraseItem: {
event.detail &= ~SWT.FOREGROUND;
}
}
}
};
table.addListener( SWT.MeasureItem, paintListener );
table.addListener( SWT.PaintItem, paintListener );
table.addListener( SWT.EraseItem, paintListener );
For a more thorough understanding of owner-drawn items in SWT please read the Custom Drawing Table and Tree Items article.
If you are using a JFace TableViewer, there is also an OwnerDrawLabelProvider as shown in this example:
http://www.vogella.com/tutorials/EclipseJFaceTableAdvanced/article.html#styledcelllabelprovider-and-ownerdrawlabelprovider
Related
Im using a grid pane view to populate a grid using the add() method. By design of the add() the grid populates each row from top to bottom where location (0,0) is at the very top left most of the grid. Adding more rows are then appended below it and so forth. Is it possible to populate my grid so that the first row is at the bottom and add rows upward so that the location (0,0) is located at the bottom left? What is needed to achieve this? Ive looked at the different methods within GridPane but could not find how this is done. My hunch is I need to override the add() method but Im not sure how to implement this behavior.
I would prefer not to mirror this since I am dealing with images.
Here is the gist of code extracted from classes and helper methods for simplicity:
public enum LawnType
{
GRASS,
CRATER
}
lawnData = new LawnType[][] {
{CRATER,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
{GRASS,GRASS,GRASS,GRASS,GRASS,GRASS,},
};
GridPane lawnViewer = new GridPane();
for (int x = 0 ; x < data.length ; x++) {
for (int y = 0 ; y < data[x].length ; y++) {
ImageView imageView;
switch(data[x][y]){
case GRASS:
imageView = new ImageView(new Image("mower/resources/longgrass1.png"));
imageView.setFitWidth(gridPixelSize);
imageView.setFitHeight(gridPixelSize);
gridPane.add(imageView,x,y);
break;
case CRATER:
imageView = new ImageView(new Image("mower/resources/crater.png"));
imageView.setFitWidth(gridPixelSize);
imageView.setFitHeight(gridPixelSize);
gridPane.add(imageView, x, y);
break;
default:
break;
}
}
}
Output:
You can add each image at a row relative to the bottom row, which is data[x].length - 1.
Replace each of these:
gridPane.add(imageView, x, y);
with this:
gridPane.add(imageView, x, data[x].length - 1 - y)
I am trying to add tiled diagonal watermarks to the pdf, but it seems that pattern fills in iText are always tiled from the bottom left of the page, meaning that the tiles at the top and right side of the page can be cut abruptly. Is there an option to tile from the top left or with an offset instead?
Here is a sample of the code:
List<String> watermarkLines = getWatermarkLines();
Rectangle watermarkRect = getWatermarkRect();
PdfContentByte over = stamper.getOverContent(1);
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight();
for (int x = 0; x < watermarkLines.size(); x++) {
AffineTransform trans = getWatermarkTransform(watermarkLines, x);
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
}
over.setColorFill(new PatternColor(painter));
over.rectangle(0, 0, pageSize.getWidth(), pageSize.getHeight());
over.fill();
I tried changing the x and y of the rectangle function to negative or positive values, but it seems that the watermark is still stamped in the pattern as if it was tiled from the bottom left, cutting it in the same place as before.
First of, I cannot fathom which iText version you are using,
List<String> watermarkLines = getWatermarkLines();
...
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
implies that the third parameter of the ColumnText.showTextAligned method you use is typed as String or Object. The iText 5 version I have at hand, though, requires a Phrase there. Below I'll show how to apply an offset with the current iText 5.5.13. You'll have to check whether it also works for your version.
Yes, you can apply an offset... in the pattern definition!
If instead of
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight());
you create the pattern like this
PdfPatternPainter painter = over.createPattern(2 * watermarkRect.getWidth(), 2 * watermarkRect.getHeight(),
watermarkRect.getWidth(), watermarkRect.getHeight());
you have the same step size of pattern application (watermarkRect.getWidth(), watermarkRect.getHeight()) but a canvas twice that width and twice that height to position you text on. By positioning the text with an offset, you effectively move the whole pattern by that offset.
E.g. if you calculate the offsets as
Rectangle pageSize = pdfReader.getCropBox(1);
float xOff = pageSize.getLeft();
float yOff = pageSize.getBottom() + ((int)pageSize.getHeight()) % ((int)watermarkRect.getHeight());
and draw the text using
ColumnText.showTextAligned(painter, 0, new Phrase(watermarkLines.get(x)), (float) trans.getTranslateX() + xOff, (float) trans.getTranslateY() + yOff, 45f);
the pattern should fill the page as if starting at the top left corner of the visible page.
You haven't supplied getWatermarkLines, getWatermarkRect, and getWatermarkTransform. If I use
static AffineTransform getWatermarkTransform(List<String> watermarkLines, int x) {
return AffineTransform.getTranslateInstance(6 + 15*x, 6);
}
static Rectangle getWatermarkRect() {
return new Rectangle(65, 50);
}
static List<String> getWatermarkLines() {
return Arrays.asList("Test line 1", "Test line 2");
}
your original code for me creates a top left corner like this
and the code with the above offset creates one like this
I want to create a 3D shadow effect on images that I place in a pdf. I am using itextpdf.
The question is similar to :
Adding shadow effect on iText elements
but with images and not texts.
My images are placed in table cells. As long as the table is not completed, no way to get the actual size of the image nor its coordinates in the page, this makes it tricky.
Any brilliant id ?
Thanks and regards,
Sylvain
4 hours later, I found a way to do it using PdfCellEvent and drawing what I need inside the cell's padding.
static class ShadowRectangle implements PdfPCellEvent {
public void cellLayout(PdfPCell cell, Rectangle rect,
PdfContentByte[] canvas) {
PdfContentByte lCb = canvas[PdfPTable.LINECANVAS];
// Paddings for the border
int paddingHorizontal = 8;
int paddingVertical = 8;
// Width of the shadow
int shadowwidth = 7;
// Calculate border location and size
float left = rect.getLeft() + paddingHorizontal;
float bottom = rect.getBottom() + paddingVertical;
float width = rect.getWidth() - 2 * paddingHorizontal;
float height = rect.getHeight() - 2 * paddingVertical;
lCb.saveState();
lCb.setColorFill(BaseColor.GRAY);
// Draw the shadow at the bottom
lCb.rectangle(left + shadowwidth, bottom - shadowwidth, width, shadowwidth);
lCb.fill();
// Draw the shadow at the right
lCb.rectangle(left + width, bottom - shadowwidth, shadowwidth, height);
lCb.fill();
//lCb.setColorStroke(BaseColor.RED);
// Draw the border
//lCb.rectangle(left, bottom, width, height);
lCb.stroke();
lCb.restoreState();
}
}
// And I fill the PdfPTable this way
PdfPTable lImages = new PdfPTable(NB_PICTURE_PER_ROW);
lImages.getDefaultCell().setBorder(Rectangle.NO_BORDER);
// ... in a loop
lImageBoucle = Image.getInstance(lFile.getCanonicalPath() + File.separator + lTabPhotos[i]);
PdfPCell lCell = new PdfPCell();
lCell.setImage(lImageBoucle);
lCell.setBorder(Rectangle.NO_BORDER);
lCell.setPadding(8);
PdfPCellEvent lShadowRectangle = new ShadowRectangle();
lCell.setCellEvent(lShadowRectangle);
lImages.addCell(lCell);
Using a different background color for odd and even rows is a commonly used trick to improve readability of large tables.
I want to use this effect in Swing's JTable. I started out by creating a custom table renderer, but this can only be used to paint actual cells, and I also want to add stripes to the "white" part of the table where there might be no cells. I can subclass JTable and override paintComponent(), but I would prefer an option where I can just change the table's rendering.
Is there a better way of doing this?
Edit: According to the answers so far this seems to be impossible without extending JTable. However, when I override JTable.paintComponent() it also only paints the area where there are rows. How can I paint the rest?
Use getCellRect( getRowCount() - 1, 0, true ).y to get the top y-coordinate of the empty space, and then paint some Rectangles and (Grid-)Lines with paintComponent( Graphics g ).
To make it much easier for you, here's a long (but complete) solution ;-)
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
public class StripedEvenInWhitePartsTable extends JTable
{
public StripedEvenInWhitePartsTable( String[][] data, String[] fields )
{
super( data, fields );
setFillsViewportHeight( true ); //to show the empty space of the table
}
#Override
public void paintComponent( Graphics g )
{
super.paintComponent( g );
paintEmptyRows( g );
}
public void paintEmptyRows( Graphics g )
{
Graphics newGraphics = g.create();
newGraphics.setColor( UIManager.getColor( "Table.gridColor" ) );
Rectangle rectOfLastRow = getCellRect( getRowCount() - 1, 0, true );
int firstNonExistentRowY = rectOfLastRow.y; //the top Y-coordinate of the first empty tablerow
if ( getVisibleRect().height > firstNonExistentRowY ) //only paint the grid if empty space is visible
{
//fill the rows alternating and paint the row-lines:
int rowYToDraw = (firstNonExistentRowY - 1) + getRowHeight(); //minus 1 otherwise the first empty row is one pixel to high
int actualRow = getRowCount() - 1; //to continue the stripes from the area with table-data
while ( rowYToDraw < getHeight() )
{
if ( actualRow % 2 == 0 )
{
newGraphics.setColor( Color.ORANGE ); //change this to another color (Color.YELLOW, anyone?) to show that only the free space is painted
newGraphics.fillRect( 0, rowYToDraw, getWidth(), getRowHeight() );
newGraphics.setColor( UIManager.getColor( "Table.gridColor" ) );
}
newGraphics.drawLine( 0, rowYToDraw, getWidth(), rowYToDraw );
rowYToDraw += getRowHeight();
actualRow++;
}
//paint the column-lines:
int x = 0;
for ( int i = 0; i < getColumnCount(); i++ )
{
TableColumn column = getColumnModel().getColumn( i );
x += column.getWidth(); //add the column width to the x-coordinate
newGraphics.drawLine( x - 1, firstNonExistentRowY, x - 1, getHeight() );
}
newGraphics.dispose();
} //if empty space is visible
} //paintEmptyRows
public Component prepareRenderer( TableCellRenderer renderer, int row, int column )
{
Component c = super.prepareRenderer( renderer, row, column );
if ( !isRowSelected( row ) )
{
c.setBackground( row % 2 == 0 ? getBackground() : Color.ORANGE );
}
return c;
}
public static void main( String[] argv )
{
String data[][] = { { "A0", "B0", "C0" }, { "A1", "B1", "C1" }, { "A2", "B2", "C2" }, { "A3", "B3", "C3" }, { "A4", "B4", "C4" } };
String fields[] = { "A", "B", "C" };
JFrame frame = new JFrame( "a JTable with striped empty space" );
StripedEvenInWhitePartsTable table = new StripedEvenInWhitePartsTable( data, fields );
JScrollPane pane = new JScrollPane( table );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.add( pane );
frame.setSize( 400, 300 );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
This example could be extended to:
fix the painted pseudogrid for variable RowHeights (I'm using the lowest height used in any row)
explain to the user why nothing happens if he clicks in the empty space to edit the cells (via tooltip)
add an extra row to the table model if the user clicks in the empty space (nooo! no Excel please!)
use the empty space to draw a reflection of the table (including all rendered data ( what for? ;-) )
The JXtable provided by swingx allow you to implements such rendering
Take a look at http://swinglabs.org/docs/components/JXTable/tutorial.jsp?step=3#RowHighlighting
Use Table Row Rendering concepts which is easier than dealing with individual renderers.
This approach only works for rendered cells. If you want to paint outside the bounds of the table, then you will need to override the paintComponent() method to add custom painting.
You can also use a custom TableCellRenderer which will pick the color for you, with a part of code like this inside:
if (isSelected) //color remains the same while selected
{
lFgColor = table.getSelectionForeground();
lBgColor = table.getSelectionBackground();
}
else
{
lFgColor = table.getForeground();
if (row%2 != 0) //once out of two rows, change color
lBgColor = table.getBackground();
else
{
//New look and feels like nimbus declare this property, try to use it
lBgColor = UIManager.getColor("Table.alternateRowColor");
if (lBgColor == null) //If not, choose your own color
lBgColor = UIManager.getColor("Table.light");
}
}
}
Edit: I missed the fact that you tried that already, and that you need to extend this color to the space without cells. To my knowledge, this is not possible with the current implementation of JTable or JXTable. The highlighters from JXTable are mostly sophisticated renderers, they still cater only to cells.
To extend the space, the only possibilities I see are:
draw it yourself, in your own component.
"hack" a way by adding a fake last column, with a custom JTableHeader which wouldn't display the last column header (and a renderer avoiding the grid for this last column). Also, the table resizing mode should be AUTO_RESIZE_LAST_COLUMN, in this case. This is a lot of conditions to make it work, and I'm not really sure it would work anyway.
Either use JXTable or if you are super-lazy ( or super time-short :-)) ) you can just use "Nimbus" look-and-feel, JTable looks there stripped by default :)
I have created JTextpane and inserted components inside textpane (components like Jtextarea). (vertical scrollbar of )Jscrollpane of JTextpane is automatically set to bottom when I insert new components in that JTextpane. I want to keep it to be set to the top position. How can I do this
Thanks
Sunil Kumar Sahoo
Here's a utility class I use. It can be used to scroll to the top, bottom, left, right or horizonatal / vertical center of a JScrollPane.
public final class ScrollUtil {
public static final int NONE = 0, TOP = 1, VCENTER = 2, BOTTOM = 4, LEFT = 8, HCENTER = 16, RIGHT = 32;
private static final int OFFSET = 100; // Required for hack (see below).
private ScrollUtil() {
}
/**
* Scroll to specified location. e.g. <tt>scroll(component, BOTTOM);</tt>.
*
* #param c JComponent to scroll.
* #param part Location to scroll to. Should be a bit-wise OR of one or moe of the values:
* NONE, TOP, VCENTER, BOTTOM, LEFT, HCENTER, RIGHT.
*/
public static void scroll(JComponent c, int part) {
scroll(c, part & (LEFT|HCENTER|RIGHT), part & (TOP|VCENTER|BOTTOM));
}
/**
* Scroll to specified location. e.g. <tt>scroll(component, LEFT, BOTTOM);</tt>.
*
* #param c JComponent to scroll.
* #param horizontal Horizontal location. Should take the value: LEFT, HCENTER or RIGHT.
* #param vertical Vertical location. Should take the value: TOP, VCENTER or BOTTOM.
*/
public static void scroll(JComponent c, int horizontal, int vertical) {
Rectangle visible = c.getVisibleRect();
Rectangle bounds = c.getBounds();
switch (vertical) {
case TOP: visible.y = 0; break;
case VCENTER: visible.y = (bounds.height - visible.height) / 2; break;
case BOTTOM: visible.y = bounds.height - visible.height + OFFSET; break;
}
switch (horizontal) {
case LEFT: visible.x = 0; break;
case HCENTER: visible.x = (bounds.width - visible.width) / 2; break;
case RIGHT: visible.x = bounds.width - visible.width + OFFSET; break;
}
// When scrolling to bottom or right of viewport, add an OFFSET value.
// This is because without this certain components (e.g. JTable) would
// not scroll right to the bottom (presumably the bounds calculation
// doesn't take the table header into account. It doesn't matter if
// OFFSET is a huge value (e.g. 10000) - the scrollRectToVisible method
// still works correctly.
c.scrollRectToVisible(visible);
}
}
I have found that the easiest way to do this is the following:
public void scroll(int vertical) {
switch (vertical) {
case SwingConstants.TOP:
getVerticalScrollBar().setValue(0);
break;
case SwingConstants.CENTER:
getVerticalScrollBar().setValue(getVerticalScrollBar().getMaximum());
getVerticalScrollBar().setValue(getVerticalScrollBar().getValue() / 2);
break;
case SwingConstants.BOTTOM:
getVerticalScrollBar().setValue(getVerticalScrollBar().getMaximum());
break;
}
}
I placed this in an object which extended JScrollPane but you could also add the name of your JScrollPane before all the getVertivalScrollBar(). There is two setValue()s for CENTER because getMaximum() returns the bottom of the JScrollBar, not the lowest value it goes to. This also works for Horizontal Scrolling using getHorizontalScrollBar() in place of getverticalScrollBar().
It should be possible to set the DefaultCaret update policy to NEVER_UPDATE. See the article Text Area Scrolling for other uses.
There are various methods that you can use, depending on what is inside the scrollpane. See the tutorial, the very last section.
This works too:
JTextArea. myTextArea;
// ...
myTextArea.select(0, 0); // force the scroll value to the top
jScrollPane.getVerticalScrollBar().setValue(1);
ScrollPane's scroll value is always between ( 0.0 - 1 )
for example
0.0 = 0%
0.1 = 10%
0.2 = 20%
0.25 = 25%
.... so on
And you can adjust the scroll position using these values. For example, in JavaFX
// suppose this is the scrollpane
ScrollPane pane = new ScrollPane();
// to scroll the scrollpane horizontally 10% from its current position
pane.setHvalue(pane.getHvalue() + 0.1);
// to scroll 100%
pane.setHvalue(1);
and so on...
apply logic as you need