Commit | Line | Data |
---|---|---|
5434cb2b KL |
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 { | |
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 | */ | |
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 | |
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 | } |