Fix comment
[nikiroo-utils.git] / examples / JexerImageViewer.java
1 import java.awt.image.BufferedImage;
2 import java.io.File;
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.List;
6 import javax.imageio.ImageIO;
7
8 import jexer.TAction;
9 import jexer.TApplication;
10 import jexer.TDesktop;
11 import jexer.TDirectoryList;
12 import jexer.TImage;
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.*;
23
24 /**
25 * Implements a simple image thumbnail file viewer. Much of this code was
26 * stripped down from TFileOpenBox.
27 */
28 public class JexerImageViewer extends TApplication {
29
30 /**
31 * Main entry point.
32 */
33 public static void main(String [] args) throws Exception {
34 JexerImageViewer app = new JexerImageViewer();
35 (new Thread(app)).start();
36 }
37
38 /**
39 * Public constructor chooses the ECMA-48 / Xterm backend.
40 */
41 public JexerImageViewer() throws Exception {
42 super(BackendType.XTERM);
43
44 // The stock tool menu has items for redrawing the screen, opening
45 // images, and (when using the Swing backend) setting the font.
46 addToolMenu();
47
48 // We will have one menu containing a mix of new and stock commands
49 TMenu fileMenu = addMenu("&File");
50
51 // Stock commands: a new shell, exit program.
52 fileMenu.addDefaultItem(TMenu.MID_SHELL);
53 fileMenu.addSeparator();
54 fileMenu.addDefaultItem(TMenu.MID_EXIT);
55
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));
64 }
65
66 }
67
68 /**
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.
71 */
72 class ImageViewerDesktop extends TDesktop {
73
74 /**
75 * The left-side tree view pane.
76 */
77 private TTreeViewWidget treeView;
78
79 /**
80 * The data behind treeView.
81 */
82 private TDirectoryTreeItem treeViewRoot;
83
84 /**
85 * The top-right-side directory list pane.
86 */
87 private TDirectoryList directoryList;
88
89 /**
90 * The bottom-right-side image pane.
91 */
92 private TImage imageWidget;
93
94 /**
95 * Public constructor.
96 *
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
101 */
102 public ImageViewerDesktop(final TApplication application, final String path,
103 final List<String> filters) throws IOException {
104
105 super(application);
106 setActive(true);
107
108 // Add directory treeView
109 treeView = addTreeViewWidget(0, 0, getWidth() / 2, getHeight() - 1,
110 new TAction() {
111 public void DO() {
112 TTreeItem item = treeView.getSelected();
113 File selectedDir = ((TDirectoryTreeItem) item).getFile();
114 try {
115 directoryList.setPath(selectedDir.getCanonicalPath());
116 if (directoryList.getList().size() > 0) {
117 setThumbnail(directoryList.getPath());
118 } else {
119 if (imageWidget != null) {
120 getChildren().remove(imageWidget);
121 }
122 imageWidget = null;
123 }
124 activate(treeView);
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) {
129 e.printStackTrace();
130 }
131 }
132 }
133 }
134 );
135 treeViewRoot = new TDirectoryTreeItem(treeView, path, true);
136
137 // Add directory files list
138 directoryList = addDirectoryList(path, getWidth() / 2 + 1, 0,
139 getWidth() / 2 - 1, getHeight() / 2,
140
141 new TAction() {
142 public void DO() {
143 setThumbnail(directoryList.getPath());
144 }
145 },
146 new TAction() {
147
148 public void DO() {
149 setThumbnail(directoryList.getPath());
150 }
151 },
152 filters);
153
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));
158
159 if (directoryList.getList().size() > 0) {
160 activate(directoryList);
161 setThumbnail(directoryList.getPath());
162 } else {
163 activate(treeView);
164 }
165 }
166
167 /**
168 * Handle window/screen resize events.
169 *
170 * @param event resize event
171 */
172 @Override
173 public void onResize(final TResizeEvent event) {
174
175 // Resize the tree and list
176 treeView.setY(1);
177 treeView.setWidth(getWidth() / 2);
178 treeView.setHeight(getHeight() - 1);
179 treeView.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
180 treeView.getWidth(),
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()));
192
193 // Recreate the image
194 if (imageWidget != null) {
195 getChildren().remove(imageWidget);
196 }
197 imageWidget = null;
198 if (directoryList.getList().size() > 0) {
199 activate(directoryList);
200 setThumbnail(directoryList.getPath());
201 } else {
202 activate(treeView);
203 }
204 }
205
206 /**
207 * Handle keystrokes.
208 *
209 * @param keypress keystroke event
210 */
211 @Override
212 public void onKeypress(final TKeypressEvent keypress) {
213
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))
222 ) {
223 // Tree view will be changing, update the directory list.
224 super.onKeypress(keypress);
225
226 // This is the same action as treeView's enter.
227 TTreeItem item = treeView.getSelected();
228 File selectedDir = ((TDirectoryTreeItem) item).getFile();
229 try {
230 if (treeView.isActive()) {
231 directoryList.setPath(selectedDir.getCanonicalPath());
232 }
233 if (directoryList.getList().size() > 0) {
234 activate(directoryList);
235 setThumbnail(directoryList.getPath());
236 } else {
237 if (imageWidget != null) {
238 getChildren().remove(imageWidget);
239 }
240 imageWidget = null;
241 activate(treeView);
242 }
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) {
247 e.printStackTrace();
248 }
249 }
250 return;
251 }
252 }
253
254 // Pass to my parent
255 super.onKeypress(keypress);
256 }
257
258 /**
259 * Draw me on screen.
260 */
261 @Override
262 public void draw() {
263 CellAttributes background = getTheme().getColor("tdesktop.background");
264 putAll(' ', background);
265
266 vLineXY(getWidth() / 2, 0, getHeight(),
267 GraphicsChars.WINDOW_SIDE, getBackground());
268
269 hLineXY(getWidth() / 2, getHeight() / 2, (getWidth() + 1) / 2,
270 GraphicsChars.WINDOW_TOP, getBackground());
271
272 putCharXY(getWidth() / 2, getHeight() / 2,
273 GraphicsChars.WINDOW_LEFT_TEE, getBackground());
274 }
275
276 /**
277 * Set the image thumbnail.
278 *
279 * @param file the image file
280 */
281 private void setThumbnail(final File file) {
282 if (file == null) {
283 return;
284 }
285 if (!file.exists() || !file.isFile()) {
286 return;
287 }
288
289 BufferedImage image = null;
290 try {
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) {
296 e.printStackTrace();
297 }
298 return;
299 }
300
301 if (imageWidget != null) {
302 getChildren().remove(imageWidget);
303 }
304 int width = getWidth() / 2 - 1;
305 int height = getHeight() / 2 - 1;
306
307 imageWidget = new TImage(this, getWidth() - width,
308 getHeight() - height, width, height, image, 0, 0, null);
309
310 // Resize the image down until it can fit within the pane.
311 while ((imageWidget.getRows() > height)
312 || (imageWidget.getColumns() > width)
313 ) {
314 imageWidget.onKeypress(new TKeypressEvent(kbAltDown));
315 }
316
317 imageWidget.setActive(false);
318 activate(directoryList);
319 }
320
321 }