1 import java
.awt
.image
.BufferedImage
;
3 import java
.io
.IOException
;
4 import java
.util
.ArrayList
;
6 import javax
.imageio
.ImageIO
;
9 import jexer
.TApplication
;
10 import jexer
.TDesktop
;
11 import jexer
.TDirectoryList
;
13 import jexer
.backend
.SwingTerminal
;
14 import jexer
.bits
.CellAttributes
;
15 import jexer
.bits
.GraphicsChars
;
16 import jexer
.event
.TKeypressEvent
;
17 import jexer
.event
.TResizeEvent
;
18 import jexer
.menu
.TMenu
;
19 import jexer
.ttree
.TDirectoryTreeItem
;
20 import jexer
.ttree
.TTreeItem
;
21 import jexer
.ttree
.TTreeViewWidget
;
22 import static jexer
.TKeypress
.*;
25 * Implements a simple image thumbnail file viewer. Much of this code was
26 * stripped down from TFileOpenBox.
28 public class JexerImageViewer
extends TApplication
{
33 public static void main(String
[] args
) throws Exception
{
34 // For this application, we must use ptypipe so that the tile shells
35 // can be aware of their size.
37 JexerImageViewer app
= new JexerImageViewer();
38 (new Thread(app
)).start();
42 * Public constructor chooses the ECMA-48 / Xterm backend.
44 public JexerImageViewer() throws Exception
{
45 super(BackendType
.XTERM
);
47 // The stock tool menu has items for redrawing the screen, opening
48 // images, and (when using the Swing backend) setting the font.
51 // We will have one menu containing a mix of new and stock commands
52 TMenu fileMenu
= addMenu("&File");
54 // Stock commands: a new shell, exit program.
55 fileMenu
.addDefaultItem(TMenu
.MID_SHELL
);
56 fileMenu
.addSeparator();
57 fileMenu
.addDefaultItem(TMenu
.MID_EXIT
);
59 // Filter the files list to support image suffixes only.
60 List
<String
> filters
= new ArrayList
<String
>();
61 filters
.add("^.*\\.[Jj][Pp][Gg]$");
62 filters
.add("^.*\\.[Jj][Pp][Ee][Gg]$");
63 filters
.add("^.*\\.[Pp][Nn][Gg]$");
64 filters
.add("^.*\\.[Gg][Ii][Ff]$");
65 filters
.add("^.*\\.[Bb][Mm][Pp]$");
66 setDesktop(new ImageViewerDesktop(this, ".", filters
));
72 * The desktop contains a tree view on the left, list of files on the top
73 * right, and image view on the bottom right.
75 class ImageViewerDesktop
extends TDesktop
{
78 * The left-side tree view pane.
80 private TTreeViewWidget treeView
;
83 * The data behind treeView.
85 private TDirectoryTreeItem treeViewRoot
;
88 * The top-right-side directory list pane.
90 private TDirectoryList directoryList
;
93 * The bottom-right-side image pane.
95 private TImage imageWidget
;
100 * @param application the TApplication that manages this window
101 * @param path path of selected file
102 * @param filters a list of strings that files must match to be displayed
103 * @throws IOException of a java.io operation throws
105 public ImageViewerDesktop(final TApplication application
, final String path
,
106 final List
<String
> filters
) throws IOException
{
111 // Add directory treeView
112 treeView
= addTreeViewWidget(0, 0, getWidth() / 2, getHeight() - 1,
115 TTreeItem item
= treeView
.getSelected();
116 File selectedDir
= ((TDirectoryTreeItem
) item
).getFile();
118 directoryList
.setPath(selectedDir
.getCanonicalPath());
119 if (directoryList
.getList().size() > 0) {
120 setThumbnail(directoryList
.getPath());
122 if (imageWidget
!= null) {
123 getChildren().remove(imageWidget
);
128 } catch (IOException e
) {
129 // If the backend is Swing, we can emit the stack
130 // trace to stderr. Otherwise, just squash it.
131 if (getScreen() instanceof SwingTerminal
) {
138 treeViewRoot
= new TDirectoryTreeItem(treeView
, path
, true);
140 // Add directory files list
141 directoryList
= addDirectoryList(path
, getWidth() / 2 + 1, 0,
142 getWidth() / 2 - 1, getHeight() / 2,
146 setThumbnail(directoryList
.getPath());
152 setThumbnail(directoryList
.getPath());
157 // This appears to be a bug. If I pass keystrokes to the tree view,
158 // it will center correctly.
159 treeView
.onKeypress(new TKeypressEvent(kbDown
));
160 treeView
.onKeypress(new TKeypressEvent(kbUp
));
162 if (directoryList
.getList().size() > 0) {
163 activate(directoryList
);
164 setThumbnail(directoryList
.getPath());
171 * Handle window/screen resize events.
173 * @param event resize event
176 public void onResize(final TResizeEvent event
) {
178 // Resize the tree and list
180 treeView
.setWidth(getWidth() / 2);
181 treeView
.setHeight(getHeight() - 1);
182 treeView
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
184 treeView
.getHeight()));
185 treeView
.getTreeView().onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
186 treeView
.getWidth() - 1,
187 treeView
.getHeight() - 1));
188 directoryList
.setX(getWidth() / 2 + 1);
189 directoryList
.setY(1);
190 directoryList
.setWidth(getWidth() / 2 - 1);
191 directoryList
.setHeight(getHeight() / 2 - 1);
192 directoryList
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
193 directoryList
.getWidth(),
194 directoryList
.getHeight()));
196 // Recreate the image
197 if (imageWidget
!= null) {
198 getChildren().remove(imageWidget
);
201 if (directoryList
.getList().size() > 0) {
202 activate(directoryList
);
203 setThumbnail(directoryList
.getPath());
212 * @param keypress keystroke event
215 public void onKeypress(final TKeypressEvent keypress
) {
217 if (treeView
.isActive() || directoryList
.isActive()) {
218 if ((keypress
.equals(kbEnter
))
219 || (keypress
.equals(kbUp
))
220 || (keypress
.equals(kbDown
))
221 || (keypress
.equals(kbPgUp
))
222 || (keypress
.equals(kbPgDn
))
223 || (keypress
.equals(kbHome
))
224 || (keypress
.equals(kbEnd
))
226 // Tree view will be changing, update the directory list.
227 super.onKeypress(keypress
);
229 // This is the same action as treeView's enter.
230 TTreeItem item
= treeView
.getSelected();
231 File selectedDir
= ((TDirectoryTreeItem
) item
).getFile();
233 if (treeView
.isActive()) {
234 directoryList
.setPath(selectedDir
.getCanonicalPath());
236 if (directoryList
.getList().size() > 0) {
237 activate(directoryList
);
238 setThumbnail(directoryList
.getPath());
240 if (imageWidget
!= null) {
241 getChildren().remove(imageWidget
);
246 } catch (IOException e
) {
247 // If the backend is Swing, we can emit the stack trace
248 // to stderr. Otherwise, just squash it.
249 if (getScreen() instanceof SwingTerminal
) {
258 super.onKeypress(keypress
);
266 CellAttributes background
= getTheme().getColor("tdesktop.background");
267 putAll(' ', background
);
269 vLineXY(getWidth() / 2, 0, getHeight(),
270 GraphicsChars
.WINDOW_SIDE
, getBackground());
272 hLineXY(getWidth() / 2, getHeight() / 2, (getWidth() + 1) / 2,
273 GraphicsChars
.WINDOW_TOP
, getBackground());
275 putCharXY(getWidth() / 2, getHeight() / 2,
276 GraphicsChars
.WINDOW_LEFT_TEE
, getBackground());
280 * Set the image thumbnail.
282 * @param file the image file
284 private void setThumbnail(final File file
) {
288 if (!file
.exists() || !file
.isFile()) {
292 BufferedImage image
= null;
294 image
= ImageIO
.read(file
);
295 } catch (IOException e
) {
296 // If the backend is Swing, we can emit the stack trace to
297 // stderr. Otherwise, just squash it.
298 if (getScreen() instanceof SwingTerminal
) {
304 if (imageWidget
!= null) {
305 getChildren().remove(imageWidget
);
307 int width
= getWidth() / 2 - 1;
308 int height
= getHeight() / 2 - 1;
310 imageWidget
= new TImage(this, getWidth() - width
,
311 getHeight() - height
, width
, height
, image
, 0, 0, null);
313 // Resize the image down until it can fit within the pane.
314 while ((imageWidget
.getRows() > height
)
315 || (imageWidget
.getColumns() > width
)
317 imageWidget
.onKeypress(new TKeypressEvent(kbAltDown
));
320 imageWidget
.setActive(false);
321 activate(directoryList
);