I'm developing a touch based windows 7 app using a tuio client-server setup and a touch library that extends multitouch 4 java.
One of the functionality I'm struggling with is to enable text highlighting when using touch.
I display a simple txt file using a JTextPane to display the text, the highlighting is done by through a drag action.
I get the clicked position where the drag event starts and then when it stops and tried to convert those coordinates to the text panel's space but I get different values from the ones that I should have, usually before the actual text.
The code I'm using to display the document is the following:
//Create the JDialog that is the container of it
window = new JDialog(parent);
window.setUndecorated(true);
//Create the JTextPane
text = new JTextPane();
text.setPage(newFile.toURI().toURL());
text.setEditable(false);
text.setHighlighter(null);
//ScrollPane that will be used to display the text
JScrollPane scroll = new JScrollPane(text);
scroll.setPreferredSize(new Dimension(500, 700));
window.getContentPane().add(scroll, BorderLayout.CENTER);
window.pack();
window.setVisible(true);
window.validate();
Where the JDialog parent is the main display component used in my app.
The drag is handled as follows:
#Override
public boolean processGestureEvent(GestureEvent ge) {
if((ge instanceof DragEvent) && this.component.isHighlight())
{
tapCount=0;
if(this.component.isHighlight())
{
//do highlighting
DragEvent drag = (DragEvent) ge;
switch (drag.getId()) {
case GestureEvent.GESTURE_STARTED:
Point start = drag.getFrom();
Point calcStart = new Point(start.x - compPosition.x, start.y - compPosition.y);
startPos = this.textDisplay.viewToModel(calcStart);
break;
case GestureEvent.GESTURE_ENDED:
Point end = drag.getTo();
Point calcEnd = new Point(end.x - compPosition.x, end.y - compPosition.y);
endPos = this.textDisplay.viewToModel(calcEnd);
System.out.println("I have this positions:" + startPos + "/" + endPos);
System.out.println("Should have " + this.textDisplay.getSelectionStart() + "/" + this.textDisplay.getSelectionEnd());
System.out.println("And the text is: " + this.textDisplay.getText().substring(startPos, endPos));
break;
case GestureEvent.GESTURE_CANCELED:
startPos = 0;
endPos = 0;
break;
}
}
return true;
}
In which the compPosition is the JDialog's position that holds the text pane. I'm simulating touch with the mouse so the correct text position for the highlighting I'm getting from the built-in highlighting functionality of the text pane with mouse.
Is the problem because of the JDialog and the JScroll pane that somehow skews the conversion?
The coordinate system for the point's I get from the touch are with the origin in the top left corner of the screen and the text pane's coordinate system origin is in the same top lef corner.
Any ideas on how can I solve the problem? Any suggestions are appreciated.
LE:
I was doing something wrong in that I was adding the gesture processor when I initialized the component and it's position was (0,0) and only afterwards I moved it where I wanted to.
I changed the position calculations as follows:
Point calcStart = new Point(start.x - this.component.getLocation().x, start.y -this.component.getLocation().y);
passing instead a reference to the actual component and getting the location when needed.
try instead of
Point calcStart = new Point(start.x - compPosition.x, start.y - compPosition.y);
to use
Point calcStart = new Point(start.x, start.y);
i wonder how it gonna end, so give us some value of what you get
Related
I have a JeditorPane in a JScrollPane. At certain points in the application, I would like to retrieve the text that is visible in the scrollPane (the text that is currently showing) and only this text. Is there a way to do this?
Thank you,
Elliott
You can use the viewport to get the view position and size.
JViewport viewport = scrollPane.getViewport();
Point startPoint = viewport.getViewPosition();
Dimension size = viewport.getExtentSize();
Point endPoint = new Point(startPoint.x + size.width, startPoint.y + size.height);
Once you know the start/end points of the viewport you can use:
int start = editorPane.viewToModel( startPoint );
int end = editorPane.viewToModel( endPoint );
Once you know the offsets of the text you want you can get the text from the component:
String text = editorPane.getText(start, end - start);
None of the code is tested.
I want to draw a moving cursor (in green) in my 2D plan view (in red) . I use a joystick to command it.
To access to the red panel I have to get components of components from the controller following this scheme :
JPanel => JScrollPane => JViewPort => PlanComponent (extends JComponent)
My cursor coordinates are continuously updated by reading a socket.
When the coordinates are changed, it calls the drawCross fonction.
My cursor is perfectly moved by the joystick but the red area blinks and the cursor blinks over more.
Coloured program screenshot
public ThreadGestionObjetImu(Home home, HomeController homeController) {
_sweetHome3dHomeControllerVar = homeController;
_jComponentLayer1 = (MultipleLevelsPlanPanel) _sweetHome3dHomeControllerVar.getPlanController().getView();
_jComponentLayer2 = (JPanel) _jComponentLayer1.getComponent(1);
_jComponentLayer3 = (JScrollPane) _jComponentLayer2.getComponent(0);
_jComponentLayer4 = (JViewport) _jComponentLayer3.getComponent(0);
_planComponent = (PlanComponent) _jComponentLayer4.getComponent(0);
}
public void update(Observable o, Object arg) {
//parsing socket signal
switch(XImuPlugin.state){
case PLAN:
drawCross();
break;
}
}
public void drawCross() {
_planComponent.getGraphics().drawLine(_crossPositionX + _intImuValueX, _crossPositionY + 25 + _intImuValueY, _crossPositionX + 50 + _intImuValueX, _crossPositionY + 25 + _intImuValueY);
_planComponent.getGraphics().drawLine(_crossPositionX + 25 + _intImuValueX, _crossPositionY + _intImuValueY, _crossPositionX + 25 + _intImuValueX, _crossPositionY + 50 + _intImuValueY);
if (_intImuValueX > 1 || _intImuValueX < -1 || _intImuValueY > 1 || _intImuValueY < -1) {
_planComponent.update(_planComponent.getGraphics());
// I tried update, repaint, updateUI and all the possible functions
}
}
EDIT:
I forgot to precise that I was stuck in only using the sweetHome3D Api because I'm creating a plug-in, I can't modify the original code.
Problem solved by using _planComponent.add(MyComponent);
I created a JComponent with an override of paintComponent.
This is a know problem, if you draw in a component already in the UI the result is this blinking (the component drawing his background color and then drawing the image).
To avoid that, you can use a "double buffer", drawing the new image in a hide image and then, when it is ready replace directly in your UI.
A nice explanation can be found in oracle doc page
And an example here in stackoverflow: double buffering in java SWING
I'm trying to take a window screenshot to use that as background in the application.
This is my code:
try {
Robot robot = new Robot();
Rectangle captureSize = new Rectangle(new MainWindow().getX(), new MainWindow().getY(), MainWindow.getWIDTH(), MainWindow.getHEIGHT());
RenderManager.backgroundGUIs = robot.createScreenCapture(captureSize);
GUIManager.ThisGui = new GUI("inventory", null, false);
} catch(AWTException e) {
System.err.println("Error taking screenshot!");
}
And these are the MainWindow().getY() and MainWindow().getX() methods:
public int getX() {
return (int) frame.getLocationOnScreen().getX();
}
public int getY() {
return frame.getY();
}
Now... it works fine, but there is a problem. frame.getLocationOnScreen().getX() and frame.getX() return the location with the window border, that hasn't to be in the screensot. Ok, i can manually calculate border size to subtract it, but from Windows 7 to Windows 8, from Windows 8 to Mac, etc. window border changes.
So... is there a method to get frame position or window border size to calculate what i need?
Thank you for your time and your answer!
As simple as this:
x = frame.getRootPane().getX();
As well, you can make all other calculations according to the root pane, just dropping the frame's.
As expected, the root pane is the top-level container inside any Frame/Window/Dialog etc. It includes the menubar too.
Use this
frame.getContentPane().getSize();
Check out the Screen Image class. You can create an image of any component displayed on the frame.
So you could use frame.getRootPane() as the component you want the image of.
Or frame.getContentPane() if you don't want the menu bar. I think this method returns a Container object so you will need to cast is to a JComponent.
int mouseX = MouseInfo.getPointerInfo().getLocation().getX() - frame.getX() - frame.getRootPane().getX();
int mouseY = MouseInfo.getPointerInfo().getLocation().getY() - frame.getY() - frame.getRootPane().getY();
This gives the top right point within the border / tab of a frame
Currently, I have a JScrollPane nested within a Panel having a BorderLayout. More importantly, within the JScrollPane, I intend to have multiple text areas listed vertically, with a width that does not exceed the JScrollPane's, and as much height as they need (with the scroll pane having only a vertical scroll bar).
My problem is that the text areas appear to be centered in the JScrollPane relative to the largest text area, and they are all one line.
Can anyone tell me, looking at my code, how I can get the text areas to wrap before the right side of the scroll pane, and how they can be left aligned?
private JPanel generateStateView(State state) {
//Create a new panel, and the info label.
JPanel container = new JPanel(new BorderLayout());
JLabel infoLabel = new JLabel("The state of " + state.getName() +
", with " + state.getElectoralVotes() + " Electoral Votes.");
container.add(infoLabel, BorderLayout.NORTH); //Put the label on top.
//Will scroll through polls.
JScrollPane pollViewer = new JScrollPane();
//This will be the scroll pane's "viewport".
JViewport pollViewport = new JViewport();
//And finally, this will actually hold the individual polls.
JPanel pollPanel = new JPanel(new GridLayout(
state.getPolls().size(), 1));
pollPanel.setAlignmentX(LEFT_ALIGNMENT);
//Iteratively add the polls to the container
for (Poll poll : state.getPolls()) {
//Holds individual polls
Box pollBox = Box.createHorizontalBox();
//Create the poll's panel and add it to the poll container.
pollBox.add(generatePollPanel(poll));
pollBox.add(Box.createHorizontalGlue()); //Fill to the right
pollBox.setAlignmentX(LEFT_ALIGNMENT);
pollPanel.add(pollBox); //Put the box into the pollPanel.
}
//Put the panel into the viewport.
pollViewport.add(pollPanel);
//Put the viewport "into" the scroll pane
pollViewer.setViewport(pollViewport);
//Put the pane into the state view.
container.add(pollViewer, BorderLayout.CENTER);
return container; //And give back the container.
}
/**
* Method: generatePollPanel
* Purpose: Generates a panel containing information on a particular poll.
* #param poll The poll to have information generated from.
* #return The JPanel.
*/
private JPanel generatePollPanel(Poll poll) {
//Create a new panel, then a text area to fill with the info.
JPanel pollPanel = new JPanel();
JTextArea pollInfo = new JTextArea("Conducted by " + poll.getAgency() +
" on day " + poll.getDate() + " for " + poll.getNumDays() +
" days, " + poll.getPercentVoteDem() +
"% voted Democrat, and " + poll.getPercentVoteRep() +
"% voted Republican.");
pollInfo.setEditable(false); //We don't want the user editing this.
pollPanel.add(pollInfo); //Throw the area in, and return the panel.
pollPanel.setAlignmentX(LEFT_ALIGNMENT);
return pollPanel;
}
/**
* Method: valueChanged
* Purpose: Handle the event of a different list item being selected.
* #param event The object containing information about this event.
*/
public void valueChanged(ListSelectionEvent event) {
//Instantiating JList with a type of ? seems to solve the issue of
//eclipse complaining about an unchecked cast (originally to
//JList<String>, so I should be able to cast child values directly
//to string later on anyways.
JList<?> stateList = (JList<?>) event.getSource();
//Account for keyboard and mouse actions
if (!event.getValueIsAdjusting()) {
State chosenState; //Keep track of the picked state.
try {
//Try to get the currently selected state
chosenState = db.findState((String) stateList.getSelectedValue());
}
catch (NoSuchElementException exception) {
//Somehow the picked state was not found, notify the user.
reportError("The selected state is not available.");
return;
}
//Find the container for the gui window.
Container container = getContentPane();
//Remove the empty state view container by generating a new one.
container.remove(currentStateView);
//Generate the state view and add that to the container.
currentStateView = generateStateView(chosenState);
container.add(currentStateView, BorderLayout.CENTER);
//Redraw everything.
revalidate();
}
}
Looks like a severe case of lost-in-nesting :-)
While it nesting can be a means to achieve layout requirements, it can be hard to find out what exactly goes wrong if it doesn't give the intended output. Also, there are general warning signals that something is basically wrong with the nesting
deep levels of containers with wildly varying managers
containers with only a single child
In your case, all except the top-level borderlayout have a single "netto" (that is, not intended to hack around a layout issue) child, with levels:
BorderLayout (the state view)
GridLayout (the panel that's the scrollPane's view)
BoxLayout (the panel that contains a single pollBox)
FlowLayout (the panel that contains the textArea)
The experienced problems:
text areas are single-line without ever wrapping
text areas are centered
are both due to the characteristics of the LayoutManagers, either partly (first) or completely (second):
The part that's not due the manager is that a textArea doesn't wrap by default, you have to explicitly configure it to do so. The part that's due to the manager (== FlowLayout) is that it always lays out its children according to their prefSize, nothing else. That boils down to have a fixed child size, no matter how much space is available for the parent. So even if the textArea were configured to wrap, it would stick to that initial size, no matter how wide its parent can get.
The inner FlowLayout aligns its children ... centered. So whatever alignment you set on the level of the BoxLayout doesn't have any effect. As doesn't the glue: it only takes all the excess space if the other child/ren have a maximum size in that dimension (which a panel with FlowLayout does not have, simply because it's that simple LayoutManager doesn't have the notion of max, only managers of type LayoutManager2 have)
Time to take a step back and review the inital requirement:
multiple text areas listed vertically, with a width that does not
exceed the JScrollPane's, and as much height as they need
The zeroth order solution is to
configure the textArea as needed
throw out all layout nesting (inside the scrollPane)
In code:
JComponent overview = new JPanel(new BorderLayout());
overview.add(new JLabel("All polls for state XY"), BorderLayout.NORTH);
JComponent pollPanel = new JPanel();
pollPanel.setLayout(new BoxLayout(pollPanel, BoxLayout.PAGE_AXIS));
for (int i = 0; i < 10; i++) {
pollPanel.add(generatePollTextArea(i)); //Put the box into the pollPanel.
}
pollPanel.add(Box.createVerticalGlue());
JScrollPane pollViewer = new JScrollPane(pollPanel);
overview.add(pollViewer);
showInFrame(overview, "layout text only");
protected JComponent generatePollTextArea(int i) {
String agency = "agency ";
// simulates different size text
for (int j = 0; j < i; j++) {
agency += i;
}
String text = "Conducted by " + agency +
" on day " + "today" + " for " + 20 +
" days, " + 99 +
"% voted Democrat, and " + 0.2 +
"% voted Republican.";
JTextArea pollInfo = new JTextArea(text);
// give it some reasonable initial width
// in terms of content
pollInfo.setColumns(20);
pollInfo.setLineWrap(true);
pollInfo.setWrapStyleWord(true);
pollInfo.setEditable(false); //We don't want the user editing this.
return pollInfo;
}
"Das Wort zum Dienstag": nesting layouts isn't a means of avoiding to learn the characteristics of the LayoutManagers used on the individual levels of the nested layout.
Firstly, I don't think Box is suppose to be used with anything other then the BoxLayout (I could be wrong, but that's how I read it)
Personally, I'd either use either GridBagLayout or VerticalLayout from SwingLabs.
In order to facilitate the horizontal restrictions, you will also want to take a look at the Scrollable interface, in particular getScrollableTracksViewportWidth
I have a JeditorPane in a JScrollPane. At certain points in the application, I would like to retrieve the text that is visible in the scrollPane (the text that is currently showing) and only this text. Is there a way to do this?
Thank you,
Elliott
You can use the viewport to get the view position and size.
JViewport viewport = scrollPane.getViewport();
Point startPoint = viewport.getViewPosition();
Dimension size = viewport.getExtentSize();
Point endPoint = new Point(startPoint.x + size.width, startPoint.y + size.height);
Once you know the start/end points of the viewport you can use:
int start = editorPane.viewToModel( startPoint );
int end = editorPane.viewToModel( endPoint );
Once you know the offsets of the text you want you can get the text from the component:
String text = editorPane.getText(start, end - start);
None of the code is tested.