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 JexerImageViewer app
= new JexerImageViewer();
35 (new Thread(app
)).start();
39 * Public constructor chooses the ECMA-48 / Xterm backend.
41 public JexerImageViewer() throws Exception
{
42 super(BackendType
.XTERM
);
44 // The stock tool menu has items for redrawing the screen, opening
45 // images, and (when using the Swing backend) setting the font.
48 // We will have one menu containing a mix of new and stock commands
49 TMenu fileMenu
= addMenu("&File");
51 // Stock commands: a new shell, exit program.
52 fileMenu
.addDefaultItem(TMenu
.MID_SHELL
);
53 fileMenu
.addSeparator();
54 fileMenu
.addDefaultItem(TMenu
.MID_EXIT
);
56 // Filter the files list to support image suffixes only.
57 List
<String
> filters
= new ArrayList
<String
>();
58 filters
.add("^.*\\.[Jj][Pp][Gg]$");
59 filters
.add("^.*\\.[Jj][Pp][Ee][Gg]$");
60 filters
.add("^.*\\.[Pp][Nn][Gg]$");
61 filters
.add("^.*\\.[Gg][Ii][Ff]$");
62 filters
.add("^.*\\.[Bb][Mm][Pp]$");
63 setDesktop(new ImageViewerDesktop(this, ".", filters
));
69 * The desktop contains a tree view on the left, list of files on the top
70 * right, and image view on the bottom right.
72 class ImageViewerDesktop
extends TDesktop
{
75 * The left-side tree view pane.
77 private TTreeViewWidget treeView
;
80 * The data behind treeView.
82 private TDirectoryTreeItem treeViewRoot
;
85 * The top-right-side directory list pane.
87 private TDirectoryList directoryList
;
90 * The bottom-right-side image pane.
92 private TImage imageWidget
;
97 * @param application the TApplication that manages this window
98 * @param path path of selected file
99 * @param filters a list of strings that files must match to be displayed
100 * @throws IOException of a java.io operation throws
102 public ImageViewerDesktop(final TApplication application
, final String path
,
103 final List
<String
> filters
) throws IOException
{
108 // Add directory treeView
109 treeView
= addTreeViewWidget(0, 0, getWidth() / 2, getHeight() - 1,
112 TTreeItem item
= treeView
.getSelected();
113 File selectedDir
= ((TDirectoryTreeItem
) item
).getFile();
115 directoryList
.setPath(selectedDir
.getCanonicalPath());
116 if (directoryList
.getList().size() > 0) {
117 setThumbnail(directoryList
.getPath());
119 if (imageWidget
!= null) {
120 getChildren().remove(imageWidget
);
125 } catch (IOException e
) {
126 // If the backend is Swing, we can emit the stack
127 // trace to stderr. Otherwise, just squash it.
128 if (getScreen() instanceof SwingTerminal
) {
135 treeViewRoot
= new TDirectoryTreeItem(treeView
, path
, true);
137 // Add directory files list
138 directoryList
= addDirectoryList(path
, getWidth() / 2 + 1, 0,
139 getWidth() / 2 - 1, getHeight() / 2,
143 setThumbnail(directoryList
.getPath());
149 setThumbnail(directoryList
.getPath());
154 // This appears to be a bug. If I pass keystrokes to the tree view,
155 // it will center correctly.
156 treeView
.onKeypress(new TKeypressEvent(kbDown
));
157 treeView
.onKeypress(new TKeypressEvent(kbUp
));
159 if (directoryList
.getList().size() > 0) {
160 activate(directoryList
);
161 setThumbnail(directoryList
.getPath());
168 * Handle window/screen resize events.
170 * @param event resize event
173 public void onResize(final TResizeEvent event
) {
175 // Resize the tree and list
177 treeView
.setWidth(getWidth() / 2);
178 treeView
.setHeight(getHeight() - 1);
179 treeView
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
181 treeView
.getHeight()));
182 treeView
.getTreeView().onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
183 treeView
.getWidth() - 1,
184 treeView
.getHeight() - 1));
185 directoryList
.setX(getWidth() / 2 + 1);
186 directoryList
.setY(1);
187 directoryList
.setWidth(getWidth() / 2 - 1);
188 directoryList
.setHeight(getHeight() / 2 - 1);
189 directoryList
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
190 directoryList
.getWidth(),
191 directoryList
.getHeight()));
193 // Recreate the image
194 if (imageWidget
!= null) {
195 getChildren().remove(imageWidget
);
198 if (directoryList
.getList().size() > 0) {
199 activate(directoryList
);
200 setThumbnail(directoryList
.getPath());
209 * @param keypress keystroke event
212 public void onKeypress(final TKeypressEvent keypress
) {
214 if (treeView
.isActive() || directoryList
.isActive()) {
215 if ((keypress
.equals(kbEnter
))
216 || (keypress
.equals(kbUp
))
217 || (keypress
.equals(kbDown
))
218 || (keypress
.equals(kbPgUp
))
219 || (keypress
.equals(kbPgDn
))
220 || (keypress
.equals(kbHome
))
221 || (keypress
.equals(kbEnd
))
223 // Tree view will be changing, update the directory list.
224 super.onKeypress(keypress
);
226 // This is the same action as treeView's enter.
227 TTreeItem item
= treeView
.getSelected();
228 File selectedDir
= ((TDirectoryTreeItem
) item
).getFile();
230 if (treeView
.isActive()) {
231 directoryList
.setPath(selectedDir
.getCanonicalPath());
233 if (directoryList
.getList().size() > 0) {
234 activate(directoryList
);
235 setThumbnail(directoryList
.getPath());
237 if (imageWidget
!= null) {
238 getChildren().remove(imageWidget
);
243 } catch (IOException e
) {
244 // If the backend is Swing, we can emit the stack trace
245 // to stderr. Otherwise, just squash it.
246 if (getScreen() instanceof SwingTerminal
) {
255 super.onKeypress(keypress
);
263 CellAttributes background
= getTheme().getColor("tdesktop.background");
264 putAll(' ', background
);
266 vLineXY(getWidth() / 2, 0, getHeight(),
267 GraphicsChars
.WINDOW_SIDE
, getBackground());
269 hLineXY(getWidth() / 2, getHeight() / 2, (getWidth() + 1) / 2,
270 GraphicsChars
.WINDOW_TOP
, getBackground());
272 putCharXY(getWidth() / 2, getHeight() / 2,
273 GraphicsChars
.WINDOW_LEFT_TEE
, getBackground());
277 * Set the image thumbnail.
279 * @param file the image file
281 private void setThumbnail(final File file
) {
285 if (!file
.exists() || !file
.isFile()) {
289 BufferedImage image
= null;
291 image
= ImageIO
.read(file
);
292 } catch (IOException e
) {
293 // If the backend is Swing, we can emit the stack trace to
294 // stderr. Otherwise, just squash it.
295 if (getScreen() instanceof SwingTerminal
) {
301 if (imageWidget
!= null) {
302 getChildren().remove(imageWidget
);
304 int width
= getWidth() / 2 - 1;
305 int height
= getHeight() / 2 - 1;
307 imageWidget
= new TImage(this, getWidth() - width
,
308 getHeight() - height
, width
, height
, image
, 0, 0, null);
310 // Resize the image to fit within the pane.
311 imageWidget
.setScaleType(TImage
.Scale
.SCALE
);
313 imageWidget
.setActive(false);
314 activate(directoryList
);