experimental 24-bit image protocol
[nikiroo-utils.git] / examples / JexerImageViewer.java
CommitLineData
5434cb2b
KL
1import java.awt.image.BufferedImage;
2import java.io.File;
3import java.io.IOException;
4import java.util.ArrayList;
5import java.util.List;
6import javax.imageio.ImageIO;
7
8import jexer.TAction;
9import jexer.TApplication;
10import jexer.TDesktop;
11import jexer.TDirectoryList;
12import jexer.TImage;
13import jexer.backend.SwingTerminal;
14import jexer.bits.CellAttributes;
15import jexer.bits.GraphicsChars;
16import jexer.event.TKeypressEvent;
17import jexer.event.TResizeEvent;
18import jexer.menu.TMenu;
19import jexer.ttree.TDirectoryTreeItem;
20import jexer.ttree.TTreeItem;
21import jexer.ttree.TTreeViewWidget;
22import static jexer.TKeypress.*;
23
24/**
25 * Implements a simple image thumbnail file viewer. Much of this code was
26 * stripped down from TFileOpenBox.
27 */
28public class JexerImageViewer extends TApplication {
29
30 /**
31 * Main entry point.
32 */
33 public static void main(String [] args) throws Exception {
5434cb2b
KL
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 */
72class 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
61e84896 109 treeView = addTreeViewWidget(0, 0, getWidth() / 2, getHeight(),
5434cb2b
KL
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
5434cb2b
KL
154 if (directoryList.getList().size() > 0) {
155 activate(directoryList);
156 setThumbnail(directoryList.getPath());
157 } else {
158 activate(treeView);
159 }
160 }
161
162 /**
163 * Handle window/screen resize events.
164 *
165 * @param event resize event
166 */
167 @Override
168 public void onResize(final TResizeEvent event) {
169
170 // Resize the tree and list
171 treeView.setY(1);
172 treeView.setWidth(getWidth() / 2);
173 treeView.setHeight(getHeight() - 1);
174 treeView.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
175 treeView.getWidth(),
176 treeView.getHeight()));
177 treeView.getTreeView().onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
178 treeView.getWidth() - 1,
179 treeView.getHeight() - 1));
180 directoryList.setX(getWidth() / 2 + 1);
181 directoryList.setY(1);
182 directoryList.setWidth(getWidth() / 2 - 1);
183 directoryList.setHeight(getHeight() / 2 - 1);
184 directoryList.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
185 directoryList.getWidth(),
186 directoryList.getHeight()));
187
188 // Recreate the image
189 if (imageWidget != null) {
190 getChildren().remove(imageWidget);
191 }
192 imageWidget = null;
193 if (directoryList.getList().size() > 0) {
194 activate(directoryList);
195 setThumbnail(directoryList.getPath());
196 } else {
197 activate(treeView);
198 }
199 }
200
201 /**
202 * Handle keystrokes.
203 *
204 * @param keypress keystroke event
205 */
206 @Override
207 public void onKeypress(final TKeypressEvent keypress) {
208
209 if (treeView.isActive() || directoryList.isActive()) {
210 if ((keypress.equals(kbEnter))
211 || (keypress.equals(kbUp))
212 || (keypress.equals(kbDown))
213 || (keypress.equals(kbPgUp))
214 || (keypress.equals(kbPgDn))
215 || (keypress.equals(kbHome))
216 || (keypress.equals(kbEnd))
217 ) {
218 // Tree view will be changing, update the directory list.
219 super.onKeypress(keypress);
220
221 // This is the same action as treeView's enter.
222 TTreeItem item = treeView.getSelected();
223 File selectedDir = ((TDirectoryTreeItem) item).getFile();
224 try {
225 if (treeView.isActive()) {
226 directoryList.setPath(selectedDir.getCanonicalPath());
227 }
228 if (directoryList.getList().size() > 0) {
229 activate(directoryList);
230 setThumbnail(directoryList.getPath());
231 } else {
232 if (imageWidget != null) {
233 getChildren().remove(imageWidget);
234 }
235 imageWidget = null;
236 activate(treeView);
237 }
238 } catch (IOException e) {
239 // If the backend is Swing, we can emit the stack trace
240 // to stderr. Otherwise, just squash it.
241 if (getScreen() instanceof SwingTerminal) {
242 e.printStackTrace();
243 }
244 }
245 return;
246 }
247 }
248
249 // Pass to my parent
250 super.onKeypress(keypress);
251 }
252
253 /**
254 * Draw me on screen.
255 */
256 @Override
257 public void draw() {
258 CellAttributes background = getTheme().getColor("tdesktop.background");
259 putAll(' ', background);
260
261 vLineXY(getWidth() / 2, 0, getHeight(),
262 GraphicsChars.WINDOW_SIDE, getBackground());
263
264 hLineXY(getWidth() / 2, getHeight() / 2, (getWidth() + 1) / 2,
265 GraphicsChars.WINDOW_TOP, getBackground());
266
267 putCharXY(getWidth() / 2, getHeight() / 2,
268 GraphicsChars.WINDOW_LEFT_TEE, getBackground());
269 }
270
271 /**
272 * Set the image thumbnail.
273 *
274 * @param file the image file
275 */
276 private void setThumbnail(final File file) {
277 if (file == null) {
278 return;
279 }
280 if (!file.exists() || !file.isFile()) {
281 return;
282 }
283
284 BufferedImage image = null;
285 try {
286 image = ImageIO.read(file);
287 } catch (IOException e) {
288 // If the backend is Swing, we can emit the stack trace to
289 // stderr. Otherwise, just squash it.
290 if (getScreen() instanceof SwingTerminal) {
291 e.printStackTrace();
292 }
293 return;
294 }
295
296 if (imageWidget != null) {
297 getChildren().remove(imageWidget);
298 }
299 int width = getWidth() / 2 - 1;
300 int height = getHeight() / 2 - 1;
301
302 imageWidget = new TImage(this, getWidth() - width,
303 getHeight() - height, width, height, image, 0, 0, null);
304
be528f1a
KL
305 // Resize the image to fit within the pane.
306 imageWidget.setScaleType(TImage.Scale.SCALE);
5434cb2b
KL
307
308 imageWidget.setActive(false);
309 activate(directoryList);
310 }
311
312}