Why does target-counter report the last page number instead of the first page the anchor appears in - itext

We have a case where content inside a div with given anchor/id spreads over different pages.
While retrieving the page number via css using this via a defined style
content: target-counter(attr(href), page);
It resolves to the last page number the content extends over, while for example building a table of contents you expect it to be the 1st page on which the referenced target occurs.
Doing some research points us to the following method where it goes wrong: com.itextpdf.layout.renderer.TargetCounterHandler.addPageByID(IRenderer)
Subsequent pages for a given page id overwrite the tracked page number, which is then used by the logic to inject it in the content later on.

We have applied following fix to test our assumptions, which makes it work but preferably a better solution is available?
public static void addPageByID(IRenderer renderer) {
final String id = renderer.<String>getProperty(Property.ID);
if (id != null) {
final TargetCounterHandler targetCounterHandler = getTargetCounterHandler(renderer);
if (targetCounterHandler != null && renderer.getOccupiedArea() != null) {
final int currentPageNumber = renderer.getOccupiedArea().getPageNumber();
// Make sure to track the 1st occurrence of the page
// otherwise we create page references in TOCs to the last one who passed by here
Integer storedPageNumber = targetCounterHandler.renderersPages.get(id);
if(storedPageNumber==null || storedPageNumber>currentPageNumber) {
targetCounterHandler.renderersPages.put(id, currentPageNumber);
}
}
}
}

Related

Reorder items in Apache Wicket's repeating views

Is it possible to reorder items that have been added to a repeating view (more precisely a ListView) in Apache Wicket?
I tried reordering them in the attached list, like shown in the following code, but this had no effect:
int indexA = itemList.indexOf(itemA);
int indexB = itemList.indexOf(itemB);
itemList.set(indexA, itemB);
itemList.set(indexB, itemA);
As this had no effect I tried resetting the list property of the ListView:
listView.setList(itemList);
Of course I remembered to trigger an according repaint for the web page, but all in all it had no effect.
In some further attempts I tried to add a new item not to the end of the list but to the beginning:
itemList.add(0, newItem);
Instead of just
itemList.add(newItem);
While the latter one works (and always worked fine), the first obviously works for the first item but throws an exception for the second item.
Last cause: Unable to find component with id 'list-component' in [ListItem [Component id = 0]]
Expected: 'list-container:list-items:0:list-component'.
Found with similar names: 'list-container:list-items:1:list-component'
Where list-container is the WebMarkupContainer surrounding the ListView, list-items is the ListView itself and list-component is the id of the item to be added.
So, is it not possible to reorder items after they have been added to a repeating view? Can new items only be added at the end of it? Or am I missing something here, probably a class different than ListView that implements such features?
My main goal is to be able to reorder items, the "add at the beginning"-approach was just a test if it would at least work to remove the items from the view and re-add them at the desired position.
The link that has 'move Up' works like this, you could use this as inspiration :)
public final Link<Void> moveUpLink(final String id, final ListItem<T> item)
{
return new Link<Void>(id)
{
private static final long serialVersionUID = 1L;
/**
* #see org.apache.wicket.markup.html.link.Link#onClick()
*/
#Override
public void onClick()
{
final int index = item.getIndex();
if (index != -1)
{
addStateChange();
// Swap items and invalidate listView
Collections.swap(getList(), index, index - 1);
ListView.this.removeAll();
}
}
#Override
public boolean isEnabled()
{
return item.getIndex() != 0;
}
};
}

Preloader in Wicket Application

In a wicket application on search event it takes few secons and sometimes minutes to show the result as its a long data . I want to show a preloader while the data is fetched from the database to let the user know something is going on when they click search . I am very new to wicket application , dont understands the things very deeply but I find AjaxLazyPreloader but as I said I want to show the preloader when the search method is called ...I am sharing the SearchSubmit method ...
private void processSearchSubmit(AjaxRequestTarget ajaxRequestTarget) {
ajaxRequestTarget.add(tableHolder);
ajaxRequestTarget.add(productTableHolder);
if (zipcode == null) {
ajaxRequestTarget
.appendJavaScript("$().toastmessage('showWarningToast','Please enter a zipcode')");
} else if (!ZipCodeValidator.isValid(zipcode)) {
useZones = true;
currentZone = zipcode;
ajaxRequestTarget.add(tableHolder);
if (searchProduct != null) {
ajaxRequestTarget.add(productTableHolder);
if (lstProduct.getList().size() == 0) {
ajaxRequestTarget
.appendJavaScript("$().toastmessage('showErrorToast','Sorry! This product is not avialable .')");
}
}
} else if (lstMerchants.getList().size() == 0) {
ajaxRequestTarget
.appendJavaScript("$().toastmessage('showWarningToast','Sorry! There are currently no services')");
}
if (ZipCodeValidator.isValid(zipcode)) {
ajaxRequestTarget.add(tableHolder);
if (searchProduct != null && !searchProduct.equals("")) {
ajaxRequestTarget.add(productTableHolder);
if (lstProduct.getList().size() == 0) {
ajaxRequestTarget
.appendJavaScript("$().toastmessage('showErrorToast','Sorry! This product is not avialable in this zip code or zone.')");
}
}
}
}
I want when this method is called till the times it fetch the result data , it should show a preloader or spinner . Can anybody suggest how to do that .??
If you need to call long execution method by clicking button check this answer.
You can also use AjaxLazyLoadPanel, check this demo (it's Java part and html part)
Either use an AjaxLazyLoadPanel or an IndicatingAjaxLink/-Button. Both will work fine in either normal or Ajax calls.
To use an AjaxLazyLoadPanel: create a subclass of AjaxLazyLoadPanel which loads the panel you want to display and add it to the AjaxRequest.
IndicatingAjaxLinks just display a spinner while the request is being processed and can be used straightforward in your current application. Use this instead of the button/link you use for formsubmits now.

Custom cell in gwt CellTree

I've got a CellTree working with simple text cells.
Now I want, for every tree node, a different kind of cell depending on if it is a leaf or not.
This cells would be:
(1) if the node is a leaf (doesn't have children): a TextCell
(2) if the node is a root (it has >= children):.
- It must display the same string as the leafs
- + 1 clickable image (this is an icon that is used to rename the node's name).
- + 1 clickable image (this is an icon that is used to remove the node).
I've tried with:
(1) Custom cell, extending AbstractCell. The point here is that I can't get the clickable images to respond to de mouse click. So no action can be performed (edit name or remove node).
(2) CompositeCell. The point here is that altought I get the clickable images to respond on mouseclick, I can't get an implementation that chooses correctly which kind of cell must be displayed (based on if it has children or not, show TetxCell or custom cell with icons).
Could someone explain how I could achieve this? My code so far is:
public NodeInfo getNodeInfo(T value) {
if (value == null) {
return new DefaultNodeInfo<CellTreeNode>(treeData.getDataProvider(), new IconCell(!isLeaf(value)),
selectionModel, null);
}
else if (value instanceof CellTreeNode) {
CellTreeNode node = (CellTreeNode) value;
//data provider for this cell
ListDataProvider<CellTreeNode> nodeDataProvider = new ListDataProvider<CellTreeNode>(node.getChildren());
IconCell nodeCell = new IconCell(this, node, !isLeaf(node));
// add a reference to the visual representation of the element
node.setCell(nodeCell);
return new DefaultNodeInfo<CellTreeNode>(nodeDataProvider, nodeCell,
selectionModel, null);
}
// Unhandled type.
String type = value.getClass().getName();
throw new IllegalArgumentException(
"[CellLargeTreeListBox] Unsupported object type: " + type);
}
// Check if the specified value represents a leaf node. Leaf nodes cannot be
// opened.
public boolean isLeaf(Object value)
{
if (value == null) return false;
CellTreeNode node = (CellTreeNode) value;
return value instanceof CellTreeNode && !node.isRoot();
}
The cell won't depend on the value it'll display, but on the value of the parent node.
So, you'll need a generic cell that supports both renderings/behaviors and switches between them based on the value it's asked to display: for a value that you can tell is a leaf, then only render text, otherwise also render the images/buttons (note you can also render the same HTML –to make it possible/easier to use a CompositeCell– but switch visibility of the images/buttons using CSS (class=xxx rendered on a container element).

Eclipse RCP ListProvider tweaking

I have problem with changing this image list provider in to thumbnail provider. In case of need I will post View for it too.
public Object[] getElements(Object inputElement) {
if (iDirname == null)
return null;
File dir = new File(iDirname);
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File directory, String filename) {
if (filename.endsWith("jpg") || (filename.endsWith("bmp")) || (filename.endsWith("png") || (filename.endsWith("JPG") || (filename.endsWith("BMP")) || (filename.endsWith("PNG")))))
return true;
else
return false;
}
};
String[] dirList = null;
if (dir.isDirectory()) {
dirList = dir.list(filter);
for (int i=0; i<dirList.length;++i){
//dirList2[i] = new Image(device, dirList2[i]); added this to try passing array of Images - failed.
dirList[i] = iDirname + File.separatorChar + dirList[i];
}
}
return dirList;
}
And the view
public void createPartControl(Composite parent) {
iViewer = new ListViewer(parent);
iViewer.setContentProvider(new DirListProvider());
getSite().setSelectionProvider(iViewer);
makeActions();
hookContextMenu();
contributeToActionBars();
}
I don't know how to change provided path lists to the thumbnail displaying. Should I get the provided content in to Array and iterate through it creating Images? If so how?
Thanks in advance for your help.
EDIT:
I added
ImageDescriptor[] dirList = null;
if (dir.isDirectory()) {
String[] dirList2 = dir.list(filter);
for (int i=0; i<dirList2.length;++i){
dirList[i] = ImageDescriptor.createFromImageData(new ImageData(iDirname + File.separatorChar + dirList2[i]));
//dirList[i] = iDirname + File.separatorChar + dirList[i];
}
}
return dirList;
but this is not showing anything at all.
When you are telling me to use Composite, is it my parent variable? I still don't know how to display the images from paths passed by ListProvider. I am really green in this :/
What you are missing here is a LabelProvider. You can use a LabelProvider to provide an image for each element in your viewer's input.
However, Francis Upton is right, I don't think ListViewer will really suit your needs as you will end up with a single column of images. Although you won't be able to add the images directly to your Composite, you will need to set them as the background image of a label.
There are a couple of other things to consider:
You need to dispose() of your Images once you're done with them as they use up System handles. Therefore you need to keep track of the Images you create in your getElements(Object) method.
If the directories you are reading the images from do not already contain thumbnails, you will need to scale the images before presenting them on your UI.
Remember, the array type you return from your ContentProvider's getElements(Object) method defines the type that will get passed into your LabelProvider's methods. So you started off returning an array of strings representing paths to the images. Your LabelProvider would need to load these into images to be returned from the provider's getImage method - but bear in mind what I said about disposing of these images! Then you switched to returning an Array of image descriptors, in this case you would need to cast your incoming Object to an ImageDescriptor and use that to create the Image in the getImage method. Maybe once you have this working you can think about whether this meets your needs, and then possibly look at doing a different implementation, such as the composite/gridlayout/label approach.
I would not use a ListViewer for this. I would just create a Composite and then using GridLayout set up the number of columns you want and margins and so forth, and then just add the images directly to the composite. As far as I know you cannot put arbitrary things like imagines in an SWT List, so the ListViewer is not going to help you. You can do all of this in the createPartControl method.

GWT SimplePager LastButton issue

I am facing problem with lastButton of SimplePager.
I have 3 pages in celltable, Page size=11 (1 empty record + 10 records(with value)), Total record=26.
I used CustomerPager by extending SimplePager.
In 1st attempt 1+10 records display in celltable : Next & Last page button is enabled (First & Prev button disabled) which is correct.
But LastPage button not working... :( Dont know whats the issue... (event not fires)
Strange behavior:
#1 Last page button is working only when I visit to last page(3 page in my case).
#2 Assume I am on 1st page n I moved to 2nd page(Total 3 pages in celltable). that time all buttons are enabled which is correct.
In this case Last button is working but behave like Next Button
My GWT application integrated into one of our product so cant debug it from client side.
May be index value is improper in setPage(int index) method from AbstractPager
Code flow is as follows for Last button
//From SimplePager
lastPage.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
lastPage();
}
});
#Override
public void lastPage() {
super.lastPage();
}
// From AbstractPager
/**
* Go to the last page.
*/
protected void lastPage() {
setPage(getPageCount() - 1);
}
protected void setPage(int index) {
if (display != null && (!isRangeLimited || !display.isRowCountExact() || hasPage(index))) {
// We don't use the local version of setPageStart because it would
// constrain the index, but the user probably wants to use absolute page
// indexes.
int pageSize = getPageSize();
display.setVisibleRange(pageSize * index, pageSize);
}
}
or may be some conditions false from above code(from setPage())
actual record = 26 and 3 Empty record (1st Empty record/page)
May b problem with dataSize :|
How I can check number of pages based on the data size?
?
How can I solve this problem?
edit: I found out that the default constructor of the pager doesn't give you a "last" button, but a "fast forward 1000 lines" button instead (horrible, right?) .
call the following constructor like so, and see your problem solved:
SimplePager.Resources resources = GWT.create(SimplePager.Resources.class);
SimplePager simplePager = new SimplePager(TextLocation.CENTER, resources , false, 1000, true);
the first "false" flag turns off the "fastforward button" and the last "true" flag turns on the "last" button.
also the last button will work only if the pager knows the total amount of records you have.
you can call the table's setRowCount function to update the total like so:
int totalRecordsSize = 26; //the total amount of records you have
boolean isTotalExact = true; //is it an estimate or an exact match
table.setRowCount(totalRecordsSize , isTotalExact); //sets the table's total and updates the pager (assuming you called pager.setDisplay(table) before)
if you are working with an attached DataProvider, than all it's updateRowCount method instead (same usage).
Without seeing more of your code, this is a hard question to answer as there could be multiple places where things are going wrong.
I would make sure you call setDisplay(...) on your SimplePager so it has the data it needs calculate its ranges.
If you can't run in devmode, I recommend setting up some GWT logging in the browser (write the logs to a popup panel or something, see this discussion for an example).
I think the problem is related with condition in the setPage(). Try putting SOP before if condition or debug the code
Only added cellTable.setRowCount(int size, boolean isExact) in OnRange change method AsyncDataProvider. My problem is solved :)
protected void onRangeChanged(HasData<RecordVO> display) {
//----- Some code --------
cellTable.setRowCount(searchRecordCount, false);
//----- Some code --------
}