I am currently building a word processor for use in a multi-window media annotation tool, written in Java. It is for film students to write essays and embed them with links to multimedia clips.
I want the user to be able to highlight text in an rtf document and create a link to a media file in the project. When clicked the link will display the media in its associated window.
I would like to know if it is possible to dynamically create hyperlinks in rtf documents in Java? As is possible in Word, for example.
At the moment I am using a JEditorPane with the Advanced RTF Editor Kit (http://java-sl.com/advanced_rtf_editor_kit.html). I am struggling to find any sort of a solution.
Any help or pointers greatly appreciated.
Thanks
Edit:
code, with parts 1 & 3 from # Eric's answer added
`item3.addActionListener(new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
//use FX thread to open FileChooser
Platform.runLater(new Runnable() {
#Override
public void run() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("create link");
String startDirectory = System.getProperty("user.home") + File.separator + "Pictures";
fileChooser.setInitialDirectory(new File(startDirectory));
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("JPEG files (*.jpg)", "*.jpg");
FileChooser.ExtensionFilter extFilter2 = new FileChooser.ExtensionFilter("PNG files (*.png)", "*.png");
FileChooser.ExtensionFilter extFilter3 = new FileChooser.ExtensionFilter("JPG files (*.jpeg)", "*.jpeg");
fileChooser.getExtensionFilters().addAll(extFilter,extFilter2, extFilter3);
File imageFile = fileChooser.showOpenDialog(stage);
if(imageFile != null){
Image image = ImageViewerController.getImage();
try {
image = new Image(imageFile.toURI().toURL().toExternalForm().toString());
int start = textArea.getSelectionStart();
int end = textArea.getSelectionEnd();
textArea.getDocument().remove(start, end);
String newString = "{\field{\*\fldinst HYPERLINK 'http://www.google.com/'}{\fldrslt http://www.google.com}}";
textArea.getDocument().insertString(start, newString , null);
textArea.addHyperlinkListener(new HyperlinkListener() {
#Override
public void hyperlinkUpdate(HyperlinkEvent hle) {
if (HyperlinkEvent.EventType.ACTIVATED.equals(hle.getEventType())) {
System.out.println(hle.getURL());
Desktop desktop = Desktop.getDesktop();
try {
desktop.browse(hle.getURL().toURI());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
});
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
});`
I think there are various parts in your question:
1. Replace selected text in a Document:
Get the selected range with:
int start = editorpane.getSelectionStart();
int end = editorpane.getSelectionEnd();
Replace the text with:
editorpane.getDocument().remove(start,end);
editorpane.getDocument().insertString(start, newString, null);
Note: replace null with actual attribute set if needed.
2. Create a RTF-formatted hyperlink. I think this post has everything.
3. React to hyperlink clicks: As explained in the docs, you must add a HyperlinkListener to the editor pane to open the corresponding media. However a condition for this to work is that the editor kit generates HyperlinkEvents when hyperlinks are clicked. This is definitely the case for HTML documents, but since you are using a 3rd party library, I cannot confirm it will work the same way...
Related
I am using OpenViewerFX (JPedal) to display PDF files in a JavaFX application. Some files are displayed well, others only show not printable characters (in documents created by myself and also documents from other people).
See for example this one:
This is my code:
private final PdfDecoderFX pdf = new PdfDecoderFX();
private Group group;
#Override
public void start(Stage stage) {
BorderPane bp = new BorderPane();
Scene scene = new Scene(bp, 400, 400);
stage.setScene(scene);
stage.show();
group = new Group();
group.getChildren().add(pdf);
bp.setCenter(group);
Platform.runLater(() -> {
try {
pdf.openPdfFile("D:\\Dokument1.pdf");
decodePage();
} catch (PdfException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
});
}
private void decodePage() {
try {
pdf.setPageParameters(2.0f, 0);
pdf.decodePage(1);
pdf.waitForDecodingToFinish();
} catch (final Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
The PDF file was created from a Word document with Adobe PDF printer. I used the standard font and only standard characters. I tested several settings when creating the file, including
Compatibility "PDF 1.3" to "PDF 1.7"
Enable/Disable web optimization
Include all fonts
Include Open-Type fonts
but always the same result.
What could I be doing wrong?
Seems like a font issue to me. The debugging output helped me in the past - at least I saw some error message in there. I'm not quite sure what the correct option was but try the following:
Did you try to enable jpedal logging?
//debug code
LogWriter.log_name="/yourpath/log.txt";
LogWriter.setupLogFile(true,0,"1.0","v",false);
What does the org.jpedal.PdfDecoder.getPageDecodeStatus(int type)state where type is any value Defined in org.jpedal.parser.DecodeStatus
We use a patched version of the ViewerFX - can you try that one?
Are you sure you specified the correct path to the fonts?
To add a whole directory of fonts via JVM flag set the JVM flag -Dorg.jpedal.fontdirs=dirList where dirList is a comma-separated list of possible directories.
What is your OS? Windows or Linx?
Did you also try the -Dorg.jpedal.inclusiveLogFilter= "memory,error"JVM option?
Try to set -Dverbose=true
Call GUI.debugFX=true; see here.
In my vaadin application I have a Table with an additional column containing a print Button. The Button calls the following util method to create a pdf and open it in a new window (ui parameter is the button):
public static void printPDF(Offer offer, AbstractComponent ui) throws IOException, DocumentException, TemplateException {
// ... create PDF
FileResource resource = new FileResource(pdfFile);
BrowserWindowOpener opener = new BrowserWindowOpener(resource);
opener.setFeatures("");
opener.extend(ui);
}
Now clicking the button the first time does not work. Clicking it the second time works. Clicking it the third time, opens two windows. This increases on every further click.
I also want to open the pdf using the context menu e.g.
table.addActionHandler(new Handler()...
There I don't even have a button to extend. I would prefer to, not use the .extend() part and just open a new window. How can I do that?
EDIT: This blocks the button from opening mulitple instances, still not a nice solution and the first click does not work.
Collection<Extension> extensions = ui.getExtensions();
for (Extension e : extensions) {
if (e instanceof BrowserWindowOpener) {
((BrowserWindowOpener) e).setResource(resource);
return;
}
}
I guess I would need to create a BrowserWindowOpener for every print Button in my Table.
Not a very clean solution, the table may contain lots of rows which would create a lot of BrowserWindowOpener instances which will never be used. The context menu problem would not be solved as well.
EDIT2: This is the other solution I tried:
ResourceReference rr = ResourceReference.create(resource, ui, "print");
Page.getCurrent().open(rr.getURL(), "blank_");
Here I get the following error:
Button (175) did not handle connector request for
print/2016_9090_R_1634500091131558445.pdf
You can use the FileDownloader to achieve what you want.
FileResource resource = new FileResource(pdfFile);
FileDownloader downloader = new FileDownloader(resource);
Button pdf= new Button("Download PDF");
downloader.extend(pdf);
Use this code
Window window = new Window();
((VerticalLayout) window.getContent()).setSizeFull();
window.setResizable(true);
window.setCaption("Exemplo PDF");
window.setWidth("800");
window.setHeight("600");
window.center();
StreamSource s = new StreamResource.StreamSource() {
#Override
public InputStream getStream() {
try {
File f = new File("C:/themes/repy.pdf");
FileInputStream fis = new FileInputStream(f);
return fis;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
};
StreamResource r = new StreamResource(s, "repy.pdf", mainLayout.getApplication());
Embedded e = new Embedded();
e.setSizeFull();
e.setType(Embedded.TYPE_BROWSER);
r.setMIMEType("application/pdf");
e.setSource(r);
window.addComponent(e);
getMainWindow().addWindow(window);
I would like to be able to display a list of files showing only the file name not the whole file path.
Currently I have a list of files. When I click one of these files the listener passes this to a method out of scope which loads the file.
This means if I was to just pass it a list of just file names it would no longer work as my listener requires a full file path. I do not have any ideas as to how I would go about both storing a list of filenames whilst simultaneously linking them to the full file path.
Happy to answer any questions you may have. Many thanks,
Note: the small for loop shows how I could potentially extract the filename from the file path, but I am not currently doing anything with it at this time. It's just an example to show you how far I have gotten.
public void GetFilesFromFolder(String dirName) throws IOException {
File dir = new File(dirName);
File[] files = dir.listFiles((File dir1, String filename) -> filename.endsWith(".mp3"));
String[] fileName = new String[files.length];
int x = 0;
for (File file : files) {
String fileTemp = file.toString();
fileTemp = fileTemp.substring(fileTemp.lastIndexOf("\\" + 1));
System.out.println(fileTemp);
fileName[x] = fileTemp;
System.out.println(fileName[x]);
x++;
}
observableList.clear();
observableList.addAll(files);
}
public void SetFileListView() throws IOException {
listView.setItems(null);
}
public VBox listStack() throws IOException {
vbox = new VBox();
vbox.getChildren().add(listView);
listView.setItems(observableList);
listView.setMinHeight(500);
MusicDataModel mdm = MainView.getMainView().musicDataModel;
MusicDataViewController mdv = MainView.getMainView().musicDataViewController;
listView.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends File> observable, File oldValue, File newValue) -> {
try {
mdm.load(newValue.toString());
mdv.SetValues();
} catch (UnsupportedTagException | InvalidDataException | IOException | NotSupportedException ex) {
Logger.getLogger(FileListView.class.getName()).log(Level.SEVERE, null, ex);
}
});
return vbox;
}
Populate the list view with Files, as you currently do, and use a cell factory on the list view to change the way the file is displayed:
listView.setCellFactory(lv -> new ListCell<File>() {
#Override
protected void updateItem(File file, boolean empty) {
super.updateItem(file, empty);
setText(file == null ? null : file.getName());
}
});
This will ensure each cell in the list view displays only the file name (the last component of the file's full path), though it still retains the File instance as its data (so you can still get the selected File, etc).
I'm developing an Android app which allows users to upload/download images to and from a database (powered by Amazon AWS). The problem I'm facing is that I can successfully download the files to a directory
/storage/emulated/0/data/myfile.jpg
But I cannot display them as a new ImageView.
Here are my methods that deal with displaying the methods. Note that RefreshFeedTask.downloadedFiles is a List of Bitmaps as shown here:
do {
objectListing = s3Client.listObjects(listObjectsRequest);
for (S3ObjectSummary objectSummary :
objectListing.getObjectSummaries()) {
keys.add(objectSummary.getKey());
}
listObjectsRequest.setMarker(objectListing.getNextMarker());
} while (objectListing.isTruncated());
Iterator<String> o = keys.iterator();
while(o.hasNext())
{
String n = o.next();
File file = new File(Environment.getExternalStorageDirectory()+"/data/", n);
if(!file.exists())
{
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
TransferObserver observer = transferUtility.download(
existingBucketName,
n,
file);
Bitmap m = BitmapFactory.decodeFile(file.getAbsolutePath());
private void refreshFeed()
{
new RefreshFeedTask(this).start();
for(Bitmap f : RefreshFeedTask.downloadedFiles)
{
displayImage(f);
}
}
private void displayImage(Bitmap f){
ImageView myImage = new ImageView(this);
myImage.setImageBitmap(f);
myImage.setVisibility(View.VISIBLE);
Log.i("Background","Displaying file");
}
Any help is appreciated, as I am somewhat new to Android development, but not Java development.
Looks like you have not added you new ImageView to any view, try adding your new ImageView to any container to display it.
parentLayout.addView(theNewImageView);
Another tip: You can try Glide if you want to display many images more efficiently.
One issue that you have here is Ownership.
Advise: That file you just downloaded to that location, your app may not own that location. If this is a closed ecosystem where you are the sole user and owner of that file, you should probably create an application specific directory upon running the application.
The best way to display something that you do not own in the Sdcard is to use the MediaStore API in Android to access them. You need a URI to the right path and just doing an absolute path is not generally advised, especially for image assets.
A good example of this is here:
https://dzone.com/articles/displaying-images-sd-card
I have a method that takes a txt file as an input. I used to use string by typing the direct path to the file.
But it became burdensome whenever I tried to use different file for an input. I try implementing JFileChooser but with no luck.
This is the code, but nothing happening.
public static JFileChooser choose;
File directory = new File("B:\\");
choose = new JFileChooser(directory);
choose.setVisible(true);
File openFile = choose.getSelectedFile();
FileReader fR = new FileReader(openFile);
BufferedReader br = new BufferedReader(fR);
As per Java tutorial on How to Use File Choosers:
Bringing up a standard open dialog requires only two lines of code:
//Create a file chooser
final JFileChooser fc = new JFileChooser();
...
//In response to a button click:
int returnVal = fc.showOpenDialog(aComponent);
The argument to the showOpenDialog method specifies the parent
component for the dialog. The parent component affects the position of
the dialog and the frame that the dialog depends on.
Note as per docs it can also be:
int returnVal = fc.showOpenDialog(null);
If the parent is null, then the dialog depends on no visible window,
and it's placed in a look-and-feel-dependent position such as the
center of the screen.
Also have a read on Concurrency in Swing if you haven't already.
No blocking code (as David Kroukamp suggest). It solves "not showing up" problem.
Runnable r = new Runnable() {
#Override
public void run() {
JFileChooser jfc = new JFileChooser();
jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if( jfc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION ){
selected = jfc.getSelectedFile();
}
}
}
SwingUtilities.invokeLater(r);
I personally found that the first dialog would show, but subsequent dialogs wouldn't show. I fixed it by reusing the same JFileChooser with this code.
JFileChooser jfc = new JFileChooser();
File jar = selectFile(jfc, "Select jar to append to");
File append = selectFile(jfc, "Select file to append");
//When done, remove the window
jfc.setVisible(false);
public static File selectFile(JFileChooser jfc, String msg) {
if (!jfc.isVisible()) {
jfc.setVisible(true);
jfc.requestFocus();
}
int returncode = jfc.showDialog(null, msg);
if (returncode == JFileChooser.APPROVE_OPTION) return jfc.getSelectedFile();
return null;
}
For JFileChoosers, you're supposed to call objectName.showOpenDialog(Component parent) or objectName.showOpenDialog(Component parent). These methods will return an integer, which you can use to compare to the static constants set in JFileChooser to determine whether the user clicked cancel or open/save. You then use getSelectedFile() to retrieve the file that the user has selected.
Ex (There might be small errors):
class Example {
public static void main(String[] args) {
JFileChooser jfc = new JFileChooser();
File selected;
if (jfc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
selected = jfc.getSelectedFile();
}
}
}
The Java API is a great resource for figuring out what objects can do what, and how. Here's the page for JFileChoosers
The API pages are usually found when you Google the object name. They're usually the first ones that come up as a result as well.