I am rendering some images that are layered in a JEditorPane. I've read that JEditorPane is pretty rocky at best, however I am hoping that this is an issue with either my HTML code or something else. Here's how my content looks in the browser:
And how it looks in a JScrollBar(JEditorPane):
The HTML code: http://pastebin.com/EixG3WLH
The Java code:
File f = new File("index.html");
JEditorPane jep = new JEditorPane(f.toURI().toURL());
JScrollPane sp = new JScrollPane(jep);
JFrame frame = new JFrame();
frame.add(sp);
jep.setEditable(false);
frame.setVisible(true);
frame.setSize(500, 500);
frame.setTitle(wpj.getParse().getTitle());
I'd really rather not use FlyingSaucer if this issue can be resolved in a JEditorPane!
You can do it... but it's not really simple... because JEditorPane doesn't have CSS absolute positioning... so, you must first at all, recognize if some element had the position:absolute or position:fixed attribute extending the ViewFactory, something like:
public class ExtendedHTMLEditorKit extends HTMLEditorKit{
//.... other code here
public class MyHTMLFactory extends HTMLFactory{
//other code here
#Override
public View create(Element elem) {
if (isLayered(elem)){ //it means, it has position attribute
return new PositionedView(elem);
}
else
return super.create(elem);
}
boolean isLayered(Element elem){
SimpleAttributeSet sas = new SimpleAttributeSet(elem);
StyleSheet styles = (HTMLDocument elem.getDocument).getStyleSheet();
Tag tag = element.getAttributes().getAttribute(AttributeSet.NameAttribute);
sas.addAttributes(styleSheet.getRule(tag, element));
return sas.isDefined("position")
&& !sas.getAttribute("position").toString().equalsIgnorecase("static");
}
}
}
In this case, we need then to build a correct view for your element... I don't know if you're only positioning images (in this case, it could be simple) or a lot of things... I can see on your code, you're using divs...
Let me explain more or less what I do: I've created a ComponentView, and returning as a component a new JEditorPane, where I put the innerCode of the original element... and after that, move it on correct position of parent editor...
To synchronize this is really dificult to allow edit, but if you only whant to use them to display, it must be more simple...
ok.. the view must be like:
public class PositionedView extends ComponentView{
private JEditorPane view;
private JEditorPane parent;
#Override
public Component createComponent(){
if (view == null){
view = new JEditorPane();
}
String innerText = dumpInnerText(getElement());
view.setText(innerText);
view.setLocation(getAbsoluteX(), getAbsoluteY());
parent.add(view);
}
#Override
public void setParent(View parent) {
if (parent != null) {
java.awt.Container host = parent.getContainer();
if (host != null && host instanceof JEditorPane) {
parent = (JEditorPane) host;
}
}
super.setParent(parent);
}
protected int getAbsoluteX() {
//search for the attribute left or right and calculate the position over parent
}
protected int getAbsoluteY(){
//search for the attribute top or bottom and calculate the position over parent
}
protected String dumpInnerText(Element element){
//there are several ways to do it, I used my own reader/writer,
//because I've need add special tags support...
}
}
I hope this helps you... Ah! there are another thing: if you do this, you must secure your view is not opaque, and it means, all the view elements, on the other case, you will have a blank rect for your elements.
Another thing, you maybe need to check for the correct dimension of the view... do as getAbsoluteX / getAbsoluteY to obtain width / height attributes.
JEditorPane is not so good with CSS absolute positioning. I think you are trying to achieve more with JEditorPane than it is capable of delivering.
Related
I am working on a TreeView which represents a robot controlling program, each TreeCell represents a statement, and a TreeCell can be nested in an other one. Like in programming, statements can be nested in if or for statements.
Here I have created a simple demo, filled with some random blocks.
Demo Screenshot
To customize the rendering of TreeCell, I have create a class extending TreeCell:
public class TreeDataCell extends TreeCell<TreeData> {
public void updateItem(TreeData item, boolean empty) {
super.updateItem(item, empty);
setText(null);
if (item == null || empty) {
setGraphic(null);
} else {
setGraphic(getCellGraphic(item));
}
}
private Group getCellGraphic(TreeData data) {
Group grp = new Group();
VBox vbox = new VBox();
vbox.setMinWidth(100);
vbox.setMaxWidth(200);
vbox.setBorder(new Border(new BorderStroke(
Color.LIGHTGRAY.darker(),
BorderStrokeStyle.SOLID,
new CornerRadii(10.0),
new BorderWidths(2.0))));
vbox.setBackground(new Background(new BackgroundFill(Color.LIGHTGRAY, new CornerRadii(10.0), null)));
vbox.setEffect(new DropShadow(2.0, 3.0, 3.0, Color.DIMGRAY));
Region header = new Region();
header.setPrefHeight(5.0);
Region footer = new Region();
footer.setPrefHeight(5.0);
Label labTitle = new Label();
labTitle.setFont(new Font("San Serif", 20));
labTitle.setText(data.getTitle());
Label labDesc = null;
if (data.getDescription() != null) {
labDesc = new Label();
labDesc.setWrapText(true);
labDesc.setText(data.getDescription());
}
vbox.getChildren().addAll(header, labTitle);
if (labDesc != null) {
vbox.getChildren().add(labDesc);
}
vbox.getChildren().add(footer);
grp.getChildren().add(vbox);
return grp;
}
}
The TreeData is a simple class containing 2 Strings:
public class TreeData {
private String title;
private String desc;
/* getters + setters */
}
As you can see, the indentation between two levels are too small, and we can barely see statement nesting.
I am hard coding all the styles in Java, since I haven't learnt FXML+CSS yet.
I'd like to know if it is possible to set the size of indentation in Java? I cannot find any API for this purpose. In addition, is it possible to draw lines between parent node and its children like JTree in Swing ?
Thank you.
Regarding having lines like in JTree, there is no built in way to do that as of JavaFX 11. There is a feature request (JDK-8090579) but there doesn't seem to be any plans to implement it. You may be able to implement it yourself but I'm not sure how.
As to modifying the indent of the TreeCells, the easiest way is by using CSS.
As documented in the JavaFX CSS Reference Guide, TreeCell has a CSS property named -fx-indent whose value is a <size>. You can set this property by using a stylesheet or inline it via the style property. An example using inline styles:
public class TreeDataCell extends TreeCell<TreeData> {
public TreeDataCell() {
setStyle("-fx-indent: <size>;");
}
}
However, since you are currently not using CSS or FXML, there is another option that is purely code: Modifying the indent property of TreeCellSkin. This class became public API in JavaFX 9. There may be equivalent internal API in JavaFX 8 but I'm not sure.
By default, the Skin of a TreeCell will be an instance of TreeCellSkin. This means you can get this skin and set the indent value as needed. You have to be careful, though, as the skin is lazily created; it won't necessarily be available until the TreeView is actually part of a showing window.
If you only want to set the property once, one way is to intercept the skin inside createDefaultSkin():
public class TreeDataCell extends TreeCell<TreeData> {
#Override
protected Skin<?> createDefaultSkin() {
TreeCellSkin<?> skin = (TreeCellSkin<?>) super.createDefaultSkin();
skin.setIndent(/* your value */);
return skin;
}
}
You could also extend TreeCellSkin and customize it. Just remember to override createDefaultSkin() and return you custom skin implementation.
I'm using GWT UiBinder and I want to create the following Label
--------------------------------------------
You can upload only .jpg images
--------------------------------------------
<g:Label ui:field="imgInfo">You can upload only <b>.jpg</b> images</g:Label>
But of course it is incorrect example. I want to use Label, because I want to add PopupPanel
final PopupPanel popupImgInfo = new PopupInfo("Max size of the imagde:10Mb");
#UiHandler("imgInfo")
void doProtocol(MouseOverEvent event) {
popupImgInfo
.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
#Override
public void setPosition(int offsetWidth, int offsetHeight) {
int left = imgInfo.getAbsoluteLeft();
int top = imgInfo.getAbsoluteTop() - 120;
popupImgInfo.setPopupPosition(left, top);
popupImgInfo.setWidth("400px");
}
});
}
#UiHandler("imgInfo")
void doProtocolHide(MouseOutEvent event) {
popupImgInfo.hide();
}
So my problem is how to insert html Element in Label or you can provide other solutions to make text bold in GWT Label.
Use a HTML widget instead of a Label one:
<g:HTML ui:field="imgInfo">You can upload only <b>.jpg</b> images</g:HTML>
philfr49 is absolutelly right. Label use createTextNode, and you can't use HTML. If you still want to do it you can do this:
DirectionalTextHelper directionalTextHelper = new DirectionalTextHelper(imgInfo.getElement(), true);
directionalTextHelper.setTextOrHtml("You can upload only <b>.jpg</b>; images", true);
Update:
Both HTML(class="gwt-HTML") and Label(class="gwt-Label") produce DIV element as wrapper element. Both classes are empty in standard.css. So just make your choice which method suitable for you.
You can implement this using SafeHtmlBuilder class. You tell it what strings you want to escape and what strings you do not want to escape. It works like StringBuilder because you call append methods.
Please refer to link for similar answer:
GWT: Putting raw HTML inside a Label
You can use a DOM Element instead.
#UiField
Element myLabel;
public void setLabelText(String text)
{
myLabel.setInnerHTML(new SafeHtmlBuilder().appendEscaped(text).toSafeHtml().asString());
}
I'm using the CheckBoxTree class which is part of the JIDE Common Layer package (http://jidesoft.com/products/oss.htm). What I'd like to be able to do is save and load the state of the CheckBoxTreeSelectionModel which is what tracks what boxes are checked or not. I can save it by just saving selectionModel.getSelectionPaths(), but my problem is with loading it. When I selectionModel.setSelectionPaths() it only checks the boxes for the root and the leaf of the path, but nothing in between. Strangely enough, this also happens when I save the results of getSelectionPaths() then feed it directly into setSelectionPaths().
For the FileSystemModel, I'm using some code I found which likes to use File objects instead of TreeNodes. I have tried different combinations of FileSystemModels and CheckBoxTrees that I've found in various places on the Net, and the results are always the same. I've probably put close to 20 hours in on this issue... which is a bit embarrassing to admit. Any help is appreciated!
My code is as follows. This creates the CheckBoxTree and attempts to load it with "/Documents and Settings/Administrator" which results in "/" and "Administrator" and all it's children being checked, but not "Documents and Settings".
public class CheckBoxTreeFrame {
private FileSystemModel fileSystemModel = null;
private CheckBoxTree checkBoxTree = null;
private JFrame main_frame = null;
private CheckBoxTreeSelectionModel selectionModel = null;
public CheckBoxTreeFrame(){
// create the model
fileSystemModel = new FileSystemModel(new File(File.separator));
// use the model for the Tree
checkBoxTree = new CheckBoxTree(fileSystemModel);
checkBoxTree.setEditable(false);
// model for the checkboxes (not the directory structure)
selectionModel = checkBoxTree.getCheckBoxTreeSelectionModel();
// event listener
checkBoxTree.getCheckBoxTreeSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
System.out.println(selectionModel.getSelectionPath());
}
});
// setup a little UI window for the tree.
main_frame = new JFrame("Frame Title");
main_frame.add(checkBoxTree);
main_frame.setSize(400, 400);
main_frame.setVisible(true);
// run the loading test
runTest();
}
public void runTest(){
File[] finalPath = new File[3];
finalPath[0] = (File)selectionModel.getModel().getRoot();
finalPath[1] = new File(finalPath[0],"Documents and Settings");
finalPath[2] = new File(finalPath[1],"Administrator");
selectionModel.setSelectionPath(new TreePath(finalPath));
}
}
Thanks!!
The CheckBoxTreeSelectionModel is basically a DefaultTreeSelectionModel (as in Swing). The trick the tree path has to exist in the TreeModel. I don't think the way you create a TreePath in runTest will create the same tree path. It'd better to get the tree path from the tree. Try this, it will work.
checkBoxTree.getCheckBoxTreeSelectionModel().addSelectionPath(checkBoxTree.getPathForRow(2));
I am trying to create a very simple chat window that simply has the ability to display some text, which I add to from time to time. However I get the following run time error when attempting to append text to the window:
java.lang.ClassCastException: javax.swing.JViewport cannot be cast to javax.swing.JTextPane
at ChatBox.getTextPane(ChatBox.java:41)
at ChatBox.getDocument(ChatBox.java:45)
at ChatBox.addMessage(ChatBox.java:50)
at ImageTest2.main(ImageTest2.java:160)
Here is the class to handle the basic operations:
public class ChatBox extends JScrollPane {
private Style style;
public ChatBox() {
StyleContext context = new StyleContext();
StyledDocument document = new DefaultStyledDocument(context);
style = context.getStyle(StyleContext.DEFAULT_STYLE);
StyleConstants.setAlignment(style, StyleConstants.ALIGN_LEFT);
StyleConstants.setFontSize(style, 14);
StyleConstants.setSpaceAbove(style, 4);
StyleConstants.setSpaceBelow(style, 4);
JTextPane textPane = new JTextPane(document);
textPane.setEditable(false);
this.add(textPane);
}
public JTextPane getTextPane() {
return (JTextPane) this.getComponent(0);
}
public StyledDocument getDocument() {
return (StyledDocument) getTextPane().getStyledDocument();
}
public void addMessage(String speaker, String message) {
String combinedMessage = speaker + ": " + message;
StyledDocument document = getDocument();
try {
document.insertString(document.getLength(), combinedMessage, style);
} catch (BadLocationException badLocationException) {
System.err.println("Oops");
}
}
}
If there is a simpler way to do this, by all means let me know. I only need the text to be of a single font type, and uneditable by the user. Aside from that, I just need to be able to append text on the fly.
You have two options:
Store the JTextPane in a member variable and return that inside getTextPane().
Modify getTextPane to return the JViewPort's view, like this
return (JTextPane) getViewport().getView();
See the Swing tutorials for more detail.
Also, as camickr (and the tutorials) pointed out, using add with a JScrollPane is incorrect. You should be either passing the component to the constructor or using setViewportView.
As a side note, I try not to subclass Swing components unless it's absolutely necessary (preferring composition over inheritance). But that's not particularly relevant to the question.
Don't extend a JScrollPane. You are NOT adding any functionality to it.
It looks like the basic problem is that your are trying to add the text pane to the scrollpane. This is not the way it works. You need to add the text pane to the viewport. The easy way to do this is:
JTextPane textPane = new JTextPane();
JScrollPane scrollPane = new JScrollPane( textPane );
or
scrollPane.setViewportView( textPane );
public JTextPane getTextPane() {
return (JTextPane) this.getComponent(0);
}
this.getComponent(0) is returning the ScrollPane's JViewPort, not your JTextPane. It can't be casted, and so you get your exception.
I'm using Ext-GWT and I think ListView is the right layout for what I need. My problem is that I have to use a HTML template for all of my items, but I want to build GWT/Ext-GWT widgets instead, so I'm using div placeholders that I will replace with the proper widgets.
How can I replace my div with a widget? My first attempt was to use RootPanel.get('div-id'), but apparently you can't create a RootPanel that is in a widget (I used debug mode to step through the code till I found that silent exception).
public class TicketContainer extends LayoutContainer {
private ArrayList<BasicTicket> tickets;
public TicketContainer(ArrayList<BasicTicket> tickets) {
this.tickets = tickets;
}
#Override
protected void onRender(Element parent, int index) {
super.onRender(parent, index);
setLayout(new FlowLayout(1));
ListStore<BasicTicket> store = new ListStore<BasicTicket>();
store.add(this.tickets);
ListView<BasicTicket> view = new ListView<BasicTicket>(store);
view.addListener(Events.Refresh, new Listener<BaseEvent>() {
#Override
public void handleEvent(BaseEvent be) {
for (BasicTicket ticket : tickets) {
// At this point I need to find the div with the id
// "ticket_"+ticket.getId() and replace it with a GWT
// widget that I can add events to and enable drag and drop
}
}
});
add(view);
}
private native String getTemplate() /*-{
return ['<tpl for=".">',
'<div id="ticket_{id}"></div>',
'</tpl>'].join("");
}-*/;
}
The full source is at https://code.launchpad.net/~asa-ayers/+junk/Kanban if you need additional context in the code.
In "pure" GWT, the answer would be to use HTMLPanel:
String id = DOM.createUniqueId();
HTMLPanel panel = new HTMLPanel("<div class=\"content\" id=\"" + id + "\"></div>");
panel.add(new Label("Something cool"), id);
As you can see, the com.google.gwt.user.client.ui.HTMLPanel.add(Widget, String) takes the id of an element withing the HTMLPanel and places the Widget inside that element.
I haven't used Ext-GWT, but you can either use HTMLPanel or search for an exquivalent in Ext-GWT.
You can also wrap an existing div in an HTML Panel.
HTMLPanel newPanel = HTMLPanel.wrap(Document.get().getElementById("yourDivId"));
newPanel.add(your_widget);