2 * Copyright 2005-2010 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.wamblee.photos.wicket;
18 import java.io.ByteArrayOutputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.Serializable;
22 import java.util.logging.Logger;
23 import javax.inject.Inject;
24 import javax.servlet.ServletContext;
26 import org.apache.wicket.PageParameters;
27 import org.apache.wicket.markup.ComponentTag;
28 import org.apache.wicket.markup.html.WebMarkupContainer;
29 import org.apache.wicket.markup.html.basic.Label;
30 import org.apache.wicket.markup.html.image.Image;
31 import org.apache.wicket.markup.html.link.Link;
32 import org.apache.wicket.markup.html.panel.Panel;
33 import org.apache.wicket.markup.repeater.RepeatingView;
34 import org.wamblee.general.ValueHolder;
35 import org.wamblee.photos.model.Album;
36 import org.wamblee.photos.model.Photo;
37 import org.wamblee.photos.model.PhotoEntry;
38 import org.wamblee.photos.model.plumbing.AuthorizedPhotos;
43 public class AlbumPanel extends Panel {
45 public static class MyValueHolder<T extends Serializable> extends ValueHolder<T> implements Serializable {
46 public MyValueHolder(T aValue) {
50 public MyValueHolder() {
55 private static final Logger LOGGER = Logger.getLogger(AlbumPanel.class.getName());
57 private static final long serialVersionUID = 1L;
58 public static final int MAX_ROWS = 5;
59 public static final int MAX_COLUMNS = 5;
63 private transient Album authorized;
66 private ServletContext context;
68 private class SerializableEntryLink extends Link {
72 public SerializableEntryLink(String aId, String aPath) {
78 public void onClick() {
79 PageParameters pars = new PageParameters();
80 pars.put("path", path);
82 setResponsePage(HomePage.class, pars);
90 * Constructor that is invoked when page is invoked without a session.
92 * @param parameters Page parameters
94 public AlbumPanel(String aId, final PageParameters parameters) throws Exception {
97 path = parameters.getString("path", "/");
98 if (!path.startsWith("/")) {
99 info("Invalid album '" + path + "', showing root album instead");
102 add(new Label("path", path));
105 String indexString = parameters.getString("index", "0");
107 index = Integer.parseInt(indexString);
109 catch (NumberFormatException e) {
110 // use default value 0
116 Link prevLink = new Link("prevLink") {
118 public void onClick() {
119 PageParameters pars = new PageParameters();
120 pars.put("path", path);
121 pars.put("index", index - MAX_ROWS * MAX_COLUMNS);
122 setResponsePage(HomePage.class, pars);
126 public boolean isEnabled() {
127 return index - MAX_ROWS * MAX_COLUMNS >= 0;
132 // Avoid implicit references to the album to keep the link objects
133 // small and serializable.
134 final int albumSize = getAlbum().size();
135 Link nextLink = new Link("nextLink") {
137 public void onClick() {
138 PageParameters pars = new PageParameters();
139 pars.put("path", path);
140 pars.put("index", index + MAX_ROWS * MAX_COLUMNS);
141 setResponsePage(HomePage.class, pars);
145 public boolean isEnabled() {
146 return index + MAX_ROWS * MAX_COLUMNS < albumSize;
151 Link parentLink = new Link("parentLink") {
153 if ("/".equals(path)) {
159 public void onClick() {
160 PageParameters pars = new PageParameters();
161 String parentPath = path.substring(0, path.lastIndexOf("/"));
162 if (parentPath.length() == 0) {
165 pars.put("path", parentPath);
166 pars.put("index", 0);
167 setResponsePage(HomePage.class, pars);
172 RepeatingView pageLinks = new RepeatingView("pageLinks");
174 Album album = getAlbum();
175 for (int i = 0; i < album.size() / MAX_ROWS / MAX_COLUMNS; i++) {
176 final int istart = i * MAX_ROWS * MAX_COLUMNS;
177 Link pageLink = new Link("pageLink") {
179 if (istart == index) {
185 public void onClick() {
186 PageParameters pars = new PageParameters();
187 pars.put("path", path);
188 pars.put("index", istart);
189 setResponsePage(HomePage.class, pars);
192 pageLink.add(new Label("label", i + ""));
193 pageLinks.add(pageLink);
194 WebMarkupContainer container = new WebMarkupContainer(pageLinks.newChildId());
195 container.add(pageLink);
196 pageLinks.add(container);
199 RepeatingView row = new RepeatingView("row") {
201 protected void onPopulate() {
203 final ValueHolder<Integer> ientry = new MyValueHolder<Integer>(index);
205 Album album = getAlbum();
206 while (irow < MAX_ROWS && ientry.getValue() < album.size()) {
207 WebMarkupContainer columns = new WebMarkupContainer(newChildId());
209 RepeatingView column = new RepeatingView("column") {
211 protected void onPopulate() {
214 Album album = getAlbum();
215 while (icolumn < MAX_COLUMNS && ientry.getValue() < album.size()) {
216 WebMarkupContainer thumbnail = new WebMarkupContainer(newChildId());
219 final PhotoEntry entry = album.getEntry(ientry.getValue());
220 Link link = new SerializableEntryLink("thumbnail", entry.getPath());
222 //ImageData data = getData(entry);
224 // TODO very inefficient. all data is loaded when generating the page.
225 //link.add(new Image("image",
226 // new ByteArrayResource(data.getContentType(), data.getData())));
228 //link.add(new Image("image",
229 // new ContextRelativeResource("image/thumbnail" + entry.getPath())));
231 //final String url = "/image/thumbnail/" + entry.getPath();
233 if (entry instanceof Photo) {
234 link.add(new Image("image") {
236 protected void onComponentTag(ComponentTag tag) {
238 context.getContextPath() + "/image/thumbnail/" + entry.getPath());
242 link.add(new Image("image") {
244 protected void onComponentTag(ComponentTag tag) {
245 //tag.put("src", context.getContextPath() + "/resources/folder.png" +
247 tag.put("src", context.getContextPath() + "/image/resource/folder.png");
252 link.add(new Label("name", album.getEntry(ientry.getValue()).getId()));
254 ientry.setValue(ientry.getValue() + 1);
267 if (path.equals("/"))
270 add(new WebMarkupContainer("uploadPanel"));
274 add(new UploadPanel("uploadPanel", path));
278 private Album getAlbum() {
279 PhotoEntry current = authorized.getEntry(path);
280 if (current instanceof Photo) {
281 throw new RuntimeException("AlbumPanel can only show album: " + current.getClass().getName());
283 return (Album) current;
286 public static final class ImageData {
287 private String contentType;
290 public ImageData(String aContentType, byte[] aData) {
291 contentType = aContentType;
295 public String getContentType() {
299 public byte[] getData() {
304 private ImageData getData(PhotoEntry aEntry) {
305 if (aEntry instanceof Photo) {
306 return getData((Photo) aEntry);
307 } else if (aEntry instanceof Album) {
308 return getData((Album) aEntry);
310 throw new RuntimeException("Unsupported type " + aEntry.getClass().getName());
314 private ImageData getData(Photo aPhoto) {
315 try (InputStream is = aPhoto.getThumbNail()) {
316 return new ImageData("image/jpeg", getBytes(is));
318 catch (IOException e) {
320 throw new RuntimeException("Cannot read photo", e);
324 private byte[] getBytes(InputStream is) throws IOException {
325 ByteArrayOutputStream bos = new ByteArrayOutputStream();
326 byte[] block = new byte[1024];
327 int n = is.read(block);
329 bos.write(block, 0, n);
332 return bos.toByteArray();
335 private ImageData getData(Album aAlbum) {
336 try (InputStream is = getClass().getResourceAsStream("folder.png")) {
337 return new ImageData("image/png", getBytes(is));
339 catch (IOException e) {
340 throw new RuntimeException("Cannot read album jpg", e);