From 5baab026870aff6b27a4d32d4207853f1b945ac1 Mon Sep 17 00:00:00 2001 From: Pierre-Luc Paour Date: Fri, 7 May 2004 01:59:54 +0000 Subject: [PATCH] 2004-05-06 Pierre-Luc Paour (1.4-b7) * Added support for cropping before upload! This is done in the preview window. For best performance, this needs the patched version of jpegtran that adds a -crop option for lessless cropping. * Fixed New Album and Move Album. * Added support for pre-downloading all pictures in slideshow. This version is ready to replace 1.3.3 as the default branch Saving is still disabled, though. Be careful. Also on my machine, GR stalls without a message on Linux, both on Blackdown and Sun VMs. --- ChangeLog | 14 + com/gallery/GalleryRemote/AlbumInspector.java | 10 +- com/gallery/GalleryRemote/GRApplet.java | 12 +- .../GalleryRemote/GRAppletSlideshow.java | 1 - .../GalleryRemote/MoveAlbumDialog.java | 2 +- com/gallery/GalleryRemote/NewAlbumDialog.java | 2 +- com/gallery/GalleryRemote/PreviewFrame.java | 266 ++++++++++++++++-- com/gallery/GalleryRemote/SlideshowFrame.java | 204 ++------------ com/gallery/GalleryRemote/model/Gallery.java | 5 +- com/gallery/GalleryRemote/model/Picture.java | 39 ++- .../GalleryRemote/prefs/PreferenceNames.java | 2 + .../GalleryRemote/prefs/SlideshowPanel.java | 31 +- .../resources/GRResources.properties | 23 +- .../resources/GRResources_ca.properties | 4 +- .../resources/GRResources_de.properties | 5 +- .../resources/GRResources_fr.properties | 4 +- .../GalleryRemote/util/ImageUtils.java | 201 ++++++++++--- .../GalleryRemote/util/UrlMessageDialog.java | 2 +- defaults.properties | 7 +- jpegtran/jpegtran.preinstalled | 3 + jpegtran/linux/jpegtran | Bin 0 -> 105684 bytes jpegtran/linux/jpegtran.properties | 13 + jpegtran/win32/jpegtran.properties | 3 + 23 files changed, 564 insertions(+), 289 deletions(-) create mode 100644 jpegtran/linux/jpegtran create mode 100644 jpegtran/linux/jpegtran.properties diff --git a/ChangeLog b/ChangeLog index 2c11d06..252ac43 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2004-05-06 Pierre-Luc Paour (1.4-b7) + + * Added support for cropping before upload! This is done in + the preview window. For best performance, this needs the + patched version of jpegtran that adds a -crop option for + lessless cropping. + * Fixed New Album and Move Album. + * Added support for pre-downloading all pictures in slideshow. + + This version is ready to replace 1.3.3 as the default branch + Saving is still disabled, though. Be careful. + Also on my machine, GR stalls without a message on Linux, both + on Blackdown and Sun VMs. + 2004-04-28 Pierre-Luc Paour (1.4-b6) * Added GeekLog support. diff --git a/com/gallery/GalleryRemote/AlbumInspector.java b/com/gallery/GalleryRemote/AlbumInspector.java index 29e6b5a..bfa424f 100644 --- a/com/gallery/GalleryRemote/AlbumInspector.java +++ b/com/gallery/GalleryRemote/AlbumInspector.java @@ -241,9 +241,13 @@ public void actionPerformed(ActionEvent e) { } else if (source == jApply) { // todo } else if (source == jMove) { - //MoveAlbumDialog mad = new MoveAlbumDialog(mf, album.getGallery(), album); - //album.moveAlbumTo(GalleryRemote._().getCore().getMainStatusUpdate(), mad.getNewParent()); - album.moveAlbumTo(null, null); + MoveAlbumDialog mad = new MoveAlbumDialog(mf, album.getGallery(), album); + album.moveAlbumTo(GalleryRemote._().getCore().getMainStatusUpdate(), mad.getNewParent()); + + // todo: this is too drastic... + album.getGallery().reload(); + + //album.moveAlbumTo(null, null); } else if (source == jSlideshow) { mf.slideshow(); } else { diff --git a/com/gallery/GalleryRemote/GRApplet.java b/com/gallery/GalleryRemote/GRApplet.java index 69af563..ec9a855 100644 --- a/com/gallery/GalleryRemote/GRApplet.java +++ b/com/gallery/GalleryRemote/GRApplet.java @@ -97,12 +97,12 @@ protected AppletInfo getGRAppletInfo() { info.albumName = getParameter("gr_album"); - Log.log(Log.LEVEL_TRACE, MODULE, "Applet parameters:"); - Log.log(Log.LEVEL_TRACE, MODULE, "gr_url:" + url); - Log.log(Log.LEVEL_TRACE, MODULE, "gr_cookie_name:" + cookieName); - Log.log(Log.LEVEL_TRACE, MODULE, "gr_cookie_domain:" + cookieDomain); - Log.log(Log.LEVEL_TRACE, MODULE, "gr_cookie_path:" + cookiePath); - Log.log(Log.LEVEL_TRACE, MODULE, "gr_album:" + info.albumName); + Log.log(Log.LEVEL_TRACE, MODULE, "Applet parameters: "); + Log.log(Log.LEVEL_TRACE, MODULE, "gr_url: " + url); + Log.log(Log.LEVEL_TRACE, MODULE, "gr_cookie_name: " + cookieName); + Log.log(Log.LEVEL_TRACE, MODULE, "gr_cookie_domain: " + cookieDomain); + Log.log(Log.LEVEL_TRACE, MODULE, "gr_cookie_path: " + cookiePath); + Log.log(Log.LEVEL_TRACE, MODULE, "gr_album: " + info.albumName); if (cookieDomain == null || cookieDomain.length() < 1) { try { diff --git a/com/gallery/GalleryRemote/GRAppletSlideshow.java b/com/gallery/GalleryRemote/GRAppletSlideshow.java index 7da1f75..6865cde 100644 --- a/com/gallery/GalleryRemote/GRAppletSlideshow.java +++ b/com/gallery/GalleryRemote/GRAppletSlideshow.java @@ -54,7 +54,6 @@ public void startup() { ImageUtils.deferredTasks(); album = new Album(gallery); - //album.setSuppressEvents(true); album.setName(info.albumName); album.addListDataListener(this); diff --git a/com/gallery/GalleryRemote/MoveAlbumDialog.java b/com/gallery/GalleryRemote/MoveAlbumDialog.java index b6d22fb..a4e7963 100644 --- a/com/gallery/GalleryRemote/MoveAlbumDialog.java +++ b/com/gallery/GalleryRemote/MoveAlbumDialog.java @@ -101,7 +101,7 @@ private void jbInit() { jAlbum = new JComboBox(albums); jAlbum.setFont(UIManager.getFont("Label.font")); - jCancel.setText(GRI18n.getString("Common", "cancel")); + jCancel.setText(GRI18n.getString("Common", "Cancel")); jCancel.setActionCommand("Cancel"); jOk.setText(GRI18n.getString("Common", "OK")); jOk.setActionCommand("OK"); diff --git a/com/gallery/GalleryRemote/NewAlbumDialog.java b/com/gallery/GalleryRemote/NewAlbumDialog.java index 306b126..3ab15bf 100644 --- a/com/gallery/GalleryRemote/NewAlbumDialog.java +++ b/com/gallery/GalleryRemote/NewAlbumDialog.java @@ -106,7 +106,7 @@ private void jbInit() { jAlbum.setSelectedItem(defaultAlbum); } - jCancel.setText(GRI18n.getString("Common", "cancel")); + jCancel.setText(GRI18n.getString("Common", "Cancel")); jCancel.setActionCommand("Cancel"); jDescription.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED, Color.white, Color.lightGray, Color.darkGray, Color.gray)); jDescription.setLineWrap(true); diff --git a/com/gallery/GalleryRemote/PreviewFrame.java b/com/gallery/GalleryRemote/PreviewFrame.java index 77810ab..939ac28 100755 --- a/com/gallery/GalleryRemote/PreviewFrame.java +++ b/com/gallery/GalleryRemote/PreviewFrame.java @@ -28,15 +28,15 @@ import javax.swing.*; import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.event.*; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; -import HTTPClient.TransferListener; - public class PreviewFrame extends JFrame implements PreferenceNames { public static final String MODULE = "PreviewFrame"; @@ -48,6 +48,9 @@ public class PreviewFrame extends JFrame implements PreferenceNames { int previewCacheSize = 10; boolean ignoreIMFailure = false; CancellableTransferListener listener = null; + Rectangle imageRect = null; + + public static final Color darkGray = new Color(64, 64, 64, 128); public void initComponents() { setTitle(GRI18n.getString(MODULE, "title")); @@ -63,6 +66,10 @@ public void componentResized(ComponentEvent e) { }); previewCacheSize = GalleryRemote._().properties.getIntProperty("previewCacheSize"); + + CropGlassPane glass = new CropGlassPane(); + setGlassPane(glass); + glass.setVisible(true); } public void hide() { @@ -75,13 +82,18 @@ public void hide() { public void flushMemory() { imageIcons.clear(); + if (currentPicture != null) { + loadPicture = null; + currentImage = null; + displayPicture(currentPicture, true); + currentPicture = null; + } } public void displayPicture(Picture picture, boolean async) { if (picture == null) { - //currentImage = null; loadPicture = null; - //currentPicture = null; + imageRect = null; pictureReady(null, null); } else { @@ -153,9 +165,6 @@ public ImageIcon getSizedIconForce(Picture picture) { class ImageContentPane extends JPanel { public void paintComponent(Graphics g) { - //Log.log(Log.LEVEL_TRACE, MODULE, "Painting ImageContentPane..."); - //Log.logStack(Log.LEVEL_TRACE, MODULE); - Color c = GalleryRemote._().properties.getColorProperty(SLIDESHOW_COLOR); if (c != null) { g.setColor(c); @@ -165,13 +174,14 @@ public void paintComponent(Graphics g) { g.fillRect(0, 0, getSize().width, getSize().height); //g.clearRect(0, 0, getSize().width, getSize().height); - if (currentImage != null) { + if (currentImage != null && loadPicture != null) { ImageIcon tmpImage = ImageUtils.rotateImageIcon(currentImage, loadPicture.getAngle(), loadPicture.isFlipped(), this); - tmpImage.paintIcon(getContentPane(), g, - getLocation().x + (getWidth() - tmpImage.getIconWidth()) / 2, - getLocation().y + (getHeight() - tmpImage.getIconHeight()) / 2); + imageRect = new Rectangle(getLocation().x + (getWidth() - tmpImage.getIconWidth()) / 2, + getLocation().y + (getHeight() - tmpImage.getIconHeight()) / 2, + tmpImage.getIconWidth(), tmpImage.getIconHeight()); + tmpImage.paintIcon(getContentPane(), g, imageRect.x, imageRect.y ); } } } @@ -232,7 +242,7 @@ public Object put(Object key, Object value) { touch(key); super.put(key, value); - Log.log(Log.LEVEL_TRACE, MODULE, Runtime.getRuntime().freeMemory() + " - " + Runtime.getRuntime().totalMemory()); + //Log.log(Log.LEVEL_TRACE, MODULE, Runtime.getRuntime().freeMemory() + " - " + Runtime.getRuntime().totalMemory()); /*if (Runtime.getRuntime().freeMemory() < 2000000) { Log.log(Log.TRACE, MODULE, "Not enough free ram, shrinking..."); @@ -242,7 +252,7 @@ public Object put(Object key, Object value) { else */if (previewCacheSize > 0 && touchOrder.size() > previewCacheSize) { shrink(); } - Log.log(Log.LEVEL_TRACE, MODULE, Runtime.getRuntime().freeMemory() + " - " + Runtime.getRuntime().totalMemory()); + //Log.log(Log.LEVEL_TRACE, MODULE, Runtime.getRuntime().freeMemory() + " - " + Runtime.getRuntime().totalMemory()); return value; } @@ -262,7 +272,7 @@ public Object get(Object key, boolean touch) { } public void clear() { - Log.log(Log.LEVEL_TRACE, MODULE, Runtime.getRuntime().freeMemory() + " - " + Runtime.getRuntime().totalMemory()); + //Log.log(Log.LEVEL_TRACE, MODULE, Runtime.getRuntime().freeMemory() + " - " + Runtime.getRuntime().totalMemory()); // flush images before clearing hastables for quicker deletion Iterator it = values().iterator(); @@ -278,7 +288,7 @@ public void clear() { Runtime.getRuntime().gc(); - Log.log(Log.LEVEL_TRACE, MODULE, Runtime.getRuntime().freeMemory() + " - " + Runtime.getRuntime().totalMemory()); + //Log.log(Log.LEVEL_TRACE, MODULE, Runtime.getRuntime().freeMemory() + " - " + Runtime.getRuntime().totalMemory()); } public void touch(Object key) { @@ -324,4 +334,226 @@ public void pictureReady(ImageIcon image, Picture picture) { public void pictureStartDownload(Picture picture) {} public void pictureStartProcessing(Picture picture) {} + + public static void paintOutline(Graphics g, String s, int textX, int textY) { + g.setColor(darkGray); + g.drawString(s, textX + 1, textY + 1); + g.drawString(s, textX, textY + 1); + g.drawString(s, textX - 1, textY + 1); + g.drawString(s, textX + 1, textY); + g.drawString(s, textX, textY); + g.drawString(s, textX - 1, textY); + g.drawString(s, textX + 1, textY - 1); + g.drawString(s, textX, textY - 1); + g.drawString(s, textX - 1, textY - 1); + g.setColor(Color.white); + g.drawString(s, textX, textY); + } + + class CropGlassPane extends JComponent implements MouseListener, MouseMotionListener { + Color background = new Color(100, 100, 100, 150); + boolean inDrag; + Point2D start = null, end = null; + Rectangle oldRect = null; + boolean centerMode = false; + + public CropGlassPane() { + addMouseListener(this); + addMouseMotionListener(this); + } + + public void paint(Graphics g) { + oldRect = null; + + if (currentPicture == null || currentImage == null || currentPicture.isOnline()) { + return; + } + + if (imageRect != null && start != null && end != null) { + Rectangle ct = currentPicture.getCropTo(); + if (ct != null) { + AffineTransform t = ImageUtils.createTransform(getBounds(), imageRect, currentPicture.getDimension(), currentPicture.getAngle(), currentPicture.isFlipped()); + + Rectangle r = null; + try { + r = getRect(t.inverseTransform(ct.getLocation(), null), + t.inverseTransform(new Point(ct.x + ct.width, ct.y + ct.height), null)); + + g.setColor(background); + g.setClip(imageRect); + g.fillRect(0, 0, r.x, getHeight()); + g.fillRect(r.x, 0, getWidth() - r.x, r.y); + g.fillRect(r.x, r.y + r.height, getWidth() - r.x, getHeight() - r.y - r.height); + g.fillRect(r.x + r.width, r.y, getWidth() - r.x - r.width, r.height); + + g.setColor(Color.black); + g.drawRect(r.x, r.y, r.width, r.height); + + g.setClip(null); + } catch (NoninvertibleTransformException e) { + Log.logException(Log.LEVEL_ERROR, MODULE, e); + } + } + } + + paintInfo(g); + } + + public void paintInfo(Graphics g) { + String message = null; + + Rectangle cropTo = currentPicture.getCropTo(); + if (! inDrag) { + if (cropTo == null) { + message = GRI18n.getString(MODULE, "noCrop"); + } else { + message = GRI18n.getString(MODULE, "crop"); + } + } else { + message = GRI18n.getString(MODULE, "inCrop");; + } + + g.setFont(g.getFont()); + paintOutline(g, message, 5, getBounds().height - 5); + } + + public void updateRect() { + Graphics g = getGraphics(); + + if (oldRect != null) { + g.setXORMode(Color.cyan); + g.drawRect(oldRect.x, oldRect.y, oldRect.width, oldRect.height); + } + + if (inDrag) { + g.setXORMode(Color.cyan); + oldRect = getRect(start, end); + g.drawRect(oldRect.x, oldRect.y, oldRect.width, oldRect.height); + } + } + + public void mouseClicked(MouseEvent e) { + if (currentPicture == null) { + return; + } + + currentPicture.setCropTo(null); + repaint(); + } + + public void mouseEntered(MouseEvent e) {} + + public void mouseExited(MouseEvent e) {} + + public void mousePressed(MouseEvent e) { + if (currentPicture == null || imageRect == null || currentPicture.isOnline()) { + return; + } + + inDrag = true; + + start = validate(e.getPoint()); + + currentPicture.setCropTo(null); + repaint(); + } + + public void mouseReleased(MouseEvent e) { + inDrag = false; + centerMode = false; + + if (currentPicture == null || oldRect == null || currentPicture.isOnline()) { + return; + } + + AffineTransform t = ImageUtils.createTransform(getBounds(), imageRect, currentPicture.getDimension(), currentPicture.getAngle(), currentPicture.isFlipped()); + //currentPicture.setCropTo(getRect(t.transform(start, null), t.transform(end, null))); + + Rectangle tmpRect = new Rectangle(); + tmpRect.setFrameFromDiagonal(t.transform(oldRect.getLocation(), null), + t.transform(new Point(oldRect.x + oldRect.width, oldRect.y + oldRect.height), null)); + currentPicture.setCropTo(tmpRect); + + repaint(); + } + + public void mouseDragged(MouseEvent e) { + if (imageRect == null) { + return; + } + + Point2D p = validate(e.getPoint()); + + int modifiers = e.getModifiersEx(); + double px = p.getX(); + double py = p.getY(); + if ((modifiers & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) { + // constrain to a square + double dx = px - start.getX(); + double dy = py - start.getY(); + + if (Math.abs(dx) < Math.abs(dy)) { + py = start.getY() + (dy*dx > 0?dx:-dx); + } else { + px = start.getX() + (dx*dy > 0?dy:-dy); + } + + p.setLocation(px, py); + } else if ((modifiers & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK) { + // constrain to same aspect ratio + int dx = (int) (px - start.getX()); + int dy = (int) (py - start.getY()); + + Dimension target = new Dimension(dx, dy); + Dimension d = ImageUtils.getSizeKeepRatio(imageRect.getSize(), + target, false); + + p.setLocation(start.getX() + d.width, start.getY() + d.height); + } + + centerMode = (modifiers & InputEvent.ALT_DOWN_MASK) == InputEvent.ALT_DOWN_MASK; + + end = validate(p); + + updateRect(); + } + + public void mouseMoved(MouseEvent e) {} + + public Rectangle getRect(Point2D p1, Point2D p2) { + Rectangle r = new Rectangle(); + if (centerMode) { + r.setFrameFromCenter(p1, p2); + p1 = new Point2D.Double(r.getMinX(), r.getMinY()); + p2 = new Point2D.Double(r.getMaxX(), r.getMaxY()); + } + + r.setFrameFromDiagonal(validate(p1), validate(p2)); + + return r; + } + + public Point2D validate(Point2D p) { + double px = p.getX(); + double py = p.getY(); + + if (px < imageRect.x) { + px = imageRect.x; + } + + if (py < imageRect.y) { + py = imageRect.y; + } + + if (px > imageRect.x + imageRect.width - 1) { + px = imageRect.x + imageRect.width - 1; + } + + if (py > imageRect.y + imageRect.height - 1) { + py = imageRect.y + imageRect.height - 1; + } + + return new Point2D.Double(px, py); + } + } } diff --git a/com/gallery/GalleryRemote/SlideshowFrame.java b/com/gallery/GalleryRemote/SlideshowFrame.java index 2fdb01e..be5b3b8 100644 --- a/com/gallery/GalleryRemote/SlideshowFrame.java +++ b/com/gallery/GalleryRemote/SlideshowFrame.java @@ -3,6 +3,7 @@ import com.gallery.GalleryRemote.model.Picture; import com.gallery.GalleryRemote.util.DialogUtil; import com.gallery.GalleryRemote.util.GRI18n; +import com.gallery.GalleryRemote.util.ImageUtils; import com.gallery.GalleryRemote.prefs.PreferenceNames; import com.gallery.GalleryRemote.prefs.PropertiesFile; @@ -16,6 +17,8 @@ import java.util.List; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; +import java.io.File; /** * Created by IntelliJ IDEA. @@ -33,10 +36,6 @@ public class SlideshowFrame extends PreviewFrame implements Runnable, Preference boolean shutdown = false; long pictureShownTime = 0; - //JLabel jCaption = new JLabel(); - //JLabel jProgress = new JLabel(); - //JLabel jExtra = new JLabel(); - //JLabel jURL = new JLabel(); String caption = null; String progress = null; String extra = null; @@ -58,9 +57,6 @@ public class SlideshowFrame extends PreviewFrame implements Runnable, Preference long controllerUntil = 0; Thread controllerThread = null; - public static ImageIcon iForward = new ImageIcon(GalleryRemote.class.getResource("/forward.png")); - - public SlideshowFrame() { setUndecorated(true); setResizable(false); @@ -186,69 +182,10 @@ public void keyPressed(KeyEvent e) { setContentPane(cp); PropertiesFile pf = GalleryRemote._().properties; - /*jCaption.setUI((LabelUI) OutlineLabelUI.createUI(jCaption)); - jProgress.setUI((LabelUI) OutlineLabelUI.createUI(jProgress)); - jExtra.setUI((LabelUI) OutlineLabelUI.createUI(jExtra)); - jURL.setUI((LabelUI) OutlineLabelUI.createUI(jURL)); - - jCaption.setForeground(Color.white); - jProgress.setForeground(Color.white); - jExtra.setForeground(Color.white); - jURL.setForeground(Color.white); - - jCaption.setFont(jCaption.getFont().deriveFont(Font.BOLD)); - jProgress.setFont(jCaption.getFont().deriveFont(Font.BOLD)); - jExtra.setFont(jCaption.getFont().deriveFont(Font.BOLD)); - jURL.setFont(jCaption.getFont().deriveFont(Font.BOLD)); - - cp.setLayout(new GridBagLayout()); - - cp.add(new JLabel(), new GridBagConstraints(0, 10, 1, 1, 1.0, 1.0 - , GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); - cp.add(new JLabel(), new GridBagConstraints(1, 10, 1, 1, 1.0, 1.0 - , GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); - cp.add(new JLabel(), new GridBagConstraints(2, 20, 1, 1, 1.0, 1.0 - , GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); - - addComponent(cp, jProgress, 1, pf.getIntProperty(SLIDESHOW_PROGRESS)); - addComponent(cp, jCaption, 2, pf.getIntProperty(SLIDESHOW_CAPTION)); - addComponent(cp, jExtra, 3, pf.getIntProperty(SLIDESHOW_EXTRA)); - addComponent(cp, jURL, 4, pf.getIntProperty(SLIDESHOW_URL));*/ sleepTime = pf.getIntProperty(SLIDESHOW_DELAY) * 1000; } - /*private void addComponent(PreviewFrame.ImageContentPane cp, JLabel c, int mod, int value) { - if (value == 0) { - return; - } - - int col; - int cons; - switch (value % 10) { - case 2: - default: - col = 0; - cons = GridBagConstraints.WEST; - break; - - case 0: - col = 1; - cons = GridBagConstraints.CENTER; - break; - - case 4: - col = 2; - cons = GridBagConstraints.EAST; - break; - } - - cp.add(c, new GridBagConstraints(col, ((int) ((value - 10) / 10)) * 10 + mod, 1, 1, 0.0, 0.0 - , cons, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); - - c.setHorizontalAlignment(value % 10); - }*/ - public void start(ArrayList pictures) { if (GalleryRemote._().properties.getBooleanProperty(SLIDESHOW_RANDOM)) { this.pictures = new ArrayList(pictures); @@ -258,6 +195,21 @@ public void start(ArrayList pictures) { } new Thread(this).start(); + + if (GalleryRemote._().properties.getBooleanProperty(SLIDESHOW_PRELOADALL)) { + new Thread() { + public void run() { + for (Iterator it = SlideshowFrame.this.pictures.iterator(); it.hasNext();) { + if (shutdown) { + break; + } + + Picture picture = (Picture) it.next(); + ImageUtils.download(picture, getRootPane().getSize(), GalleryRemote._().getCore().getMainStatusUpdate(), null); + } + } + }.start(); + } } public void run() { @@ -399,19 +351,12 @@ public void pictureReady(ImageIcon image, Picture picture) { if (picture != null) { // todo: captions are not printed outline because they are HTML and that's a fucking mess - //jCaption.setText("" + picture.getCaption() + ""); - //jCaption.setText(picture.getCaption()); caption = picture.getCaption(); updateProgress(picture, STATE_NONE); extra = picture.getExtraFieldsString(); - //if (extraFields != null) { - //jExtra.setText(extraFields); - //} if (picture.isOnline()) { - //jURL.setText(picture.safeGetUrlFull().toString()); url = picture.safeGetUrlFull().toString(); } else { - //jURL.setText(picture.getSource().toString()); url = picture.getSource().toString(); } } @@ -437,9 +382,7 @@ private void updateProgress(Picture picture, int state) { if (picture == null) { return; } - //StringBuffer sb = new StringBuffer(); - //sb.append(pictures.indexOf(picture) + 1).append("/").append(pictures.size()); Object[] params = new Object[] {picture.getName(), new Integer(pictures.indexOf(picture) + 1), new Integer(pictures.size())}; @@ -487,91 +430,6 @@ public boolean dataTransferred(int transferred, int overall, double kbPerSecond, return true; } - //public static class OutlineLabelUI extends BasicLabelUI { - // protected static OutlineLabelUI labelUI = new OutlineLabelUI(); - /*private static Rectangle paintIconR = new Rectangle(); - private static Rectangle paintTextR = new Rectangle(); - private static Rectangle paintViewR = new Rectangle(); - private static Insets paintViewInsets = new Insets(0, 0, 0, 0);*/ - - // public static ComponentUI createUI(JComponent c) { - // return labelUI; - // } - - // protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY) { - // g.setColor(Color.darkGray); - // g.drawString(s,textX + 1, textY + 1); - // g.drawString(s,textX - 1, textY + 1); - // g.drawString(s,textX + 1, textY - 1); - // g.drawString(s,textX - 1, textY - 1); - // g.setColor(l.getForeground()); - // g.drawString(s, textX, textY); - // } - - // todo: captions are not printed outline because they are HTML and that's a fucking mess - /*public void paint(Graphics g, JComponent c) { - JLabel label = (JLabel)c; - String text = label.getText(); - Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon(); - - if ((icon == null) && (text == null)) { - return; - } - - FontMetrics fm = g.getFontMetrics(); - Insets insets = c.getInsets(paintViewInsets); - - paintViewR.x = insets.left; - paintViewR.y = insets.top; - paintViewR.width = c.getWidth() - (insets.left + insets.right); - paintViewR.height = c.getHeight() - (insets.top + insets.bottom); - - paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0; - paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0; - - String clippedText = - layoutCL(label, fm, text, icon, paintViewR, paintIconR, paintTextR); - - if (icon != null) { - icon.paintIcon(c, g, paintIconR.x, paintIconR.y); - } - - if (text != null) { - View v = (View) c.getClientProperty(BasicHTML.propertyKey); - if (v != null) { - Color color = label.getForeground(); - - label.setForeground(Color.darkGray); - label.setText(label.getText() + " "); - paintTextR.x -= 1; - v.paint(g, paintTextR); - paintTextR.y -= 1; - v.paint(g, paintTextR); - paintTextR.x += 2; - v.paint(g, paintTextR); - paintTextR.y += 2; - v.paint(g, paintTextR); - - label.setForeground(Color.white); - label.setText(label.getText() + " "); - paintTextR.x -= 1; - paintTextR.y -= 1; - v.paint(g, paintTextR); - } else { - int textX = paintTextR.x; - int textY = paintTextR.y + fm.getAscent(); - - if (label.isEnabled()) { - paintEnabledText(label, g, clippedText, textX, textY); - } - else { - paintDisabledText(label, g, clippedText, textX, textY); - } - } - } - }*/ - //} - public void updateFeedback(int feedback) { if (feedback != FEEDBACK_NONE) { controllerUntil = System.currentTimeMillis() @@ -615,6 +473,10 @@ public void run() { } public class FeedbackGlassPane extends JComponent { + Color background = new Color(100, 100, 100, 150); + Color normal = new Color(180, 180, 180, 180); + Color hilight = new Color(255, 255, 255, 180); + public void paint(Graphics g) { if (feedback != FEEDBACK_NONE || controllerUntil > System.currentTimeMillis()) { paintController(g); @@ -624,10 +486,6 @@ public void paint(Graphics g) { } private void paintController(Graphics g) { - Color background = new Color(100, 100, 100, 150); - Color normal = new Color(180, 180, 180, 180); - Color hilight = new Color(255, 255, 255, 180); - Dimension d = getSize(); int width = 475; int height = 150; @@ -691,7 +549,7 @@ public void paintInfo(Graphics g) { } public void paintInfo(Graphics g, String text, int position) { - if (position == 0) return; + if (position == 0 || text == null || text.length() == 0) return; Dimension d = getSize(); g.setFont(getFont()); @@ -736,22 +594,6 @@ public void paintInfo(Graphics g, String text, int position) { paintOutline(g, text, x, y); } - public final Color darkGray = new Color(64, 64, 64, 128); - protected void paintOutline(Graphics g, String s, int textX, int textY) { - g.setColor(darkGray); - g.drawString(s, textX + 1, textY + 1); - g.drawString(s, textX, textY + 1); - g.drawString(s, textX - 1, textY + 1); - g.drawString(s, textX + 1, textY); - g.drawString(s, textX, textY); - g.drawString(s, textX - 1, textY); - g.drawString(s, textX + 1, textY - 1); - g.drawString(s, textX, textY - 1); - g.drawString(s, textX - 1, textY - 1); - g.setColor(Color.white); - g.drawString(s, textX, textY); - } - private void drawText(Graphics g, Color hilight, FontMetrics fm, int x, int y, String text) { if ((feedback & FEEDBACK_HELP) != FEEDBACK_HELP) return; diff --git a/com/gallery/GalleryRemote/model/Gallery.java b/com/gallery/GalleryRemote/model/Gallery.java index 1c0c5ca..465fdea 100644 --- a/com/gallery/GalleryRemote/model/Gallery.java +++ b/com/gallery/GalleryRemote/model/Gallery.java @@ -21,6 +21,7 @@ package com.gallery.GalleryRemote.model; import com.gallery.GalleryRemote.*; +import com.gallery.GalleryRemote.util.GRI18n; import com.gallery.GalleryRemote.prefs.GalleryProperties; import com.gallery.GalleryRemote.prefs.PreferenceNames; import com.gallery.GalleryRemote.prefs.PropertiesFile; @@ -1070,7 +1071,9 @@ public Album createRootAlbum() { throw new IllegalStateException("Root album already exists"); } - setRoot(new Album(this)); + Album album = new Album(this); + album.setTitle(GRI18n.getString("Common", "rootAlbmTitle")); + setRoot(album); return (Album) getRoot(); } diff --git a/com/gallery/GalleryRemote/model/Picture.java b/com/gallery/GalleryRemote/model/Picture.java index d182204..e0251e9 100644 --- a/com/gallery/GalleryRemote/model/Picture.java +++ b/com/gallery/GalleryRemote/model/Picture.java @@ -60,12 +60,14 @@ public class Picture extends GalleryItem implements Serializable, PreferenceName Dimension sizeResized = null; URL urlThumbnail = null; Dimension sizeThumbnail = null; + Rectangle cropTo = null; Album albumOnServer = null; int indexOnServer = -1; transient double fileSize = 0; transient int indexCache = -1; + transient Dimension dimension = null; /** * Constructor for the Picture object @@ -183,9 +185,22 @@ public File getSource() { * @return The source value */ public File getUploadSource() { + boolean useLossyCrop = false; File picture = getSource(); Album album = getParentAlbum(); + // crop + if (cropTo != null) { + try { + picture = ImageUtils.losslessCrop(picture.getPath(), cropTo); + } catch (UnsupportedOperationException e) { + Log.log(Log.LEVEL_ERROR, MODULE, "Couldn't use ImageUtils to losslessly crop the image, will try lossy"); + Log.logException(Log.LEVEL_ERROR, MODULE, e); + useLossyCrop = true; + } + } + + // resize if (album.getResize()) { Dimension d = album.getResizeDimension(); @@ -205,16 +220,19 @@ public File getUploadSource() { } } - if (d != null) { + if (d != null || useLossyCrop) { try { - picture = ImageUtils.resize(picture.getPath(), d); + picture = ImageUtils.resize(picture.getPath(), d, useLossyCrop?cropTo:null); } catch (UnsupportedOperationException e) { Log.log(Log.LEVEL_ERROR, MODULE, "Couldn't use ImageUtils to resize the image, it will be uploaded at the original size"); Log.logException(Log.LEVEL_ERROR, MODULE, e); } } + } else if (useLossyCrop) { + picture = ImageUtils.resize(picture.getPath(), null, useLossyCrop?cropTo:null); } + // rotate if (angle != 0 || flipped) { try { picture = ImageUtils.rotate(picture.getPath(), angle, flipped, true); @@ -522,5 +540,22 @@ public boolean isHidden() { public void setHidden(boolean hidden) { this.hidden = hidden; } + + public Rectangle getCropTo() { + return cropTo; + } + + public void setCropTo(Rectangle cropTo) { + Log.log(Log.LEVEL_TRACE, MODULE, "setCropTo " + cropTo); + this.cropTo = cropTo; + } + + public Dimension getDimension() { + if (dimension == null) { + dimension = ImageUtils.getPictureDimension(this); + } + + return dimension; + } } diff --git a/com/gallery/GalleryRemote/prefs/PreferenceNames.java b/com/gallery/GalleryRemote/prefs/PreferenceNames.java index 1ae96aa..e395b7c 100644 --- a/com/gallery/GalleryRemote/prefs/PreferenceNames.java +++ b/com/gallery/GalleryRemote/prefs/PreferenceNames.java @@ -67,9 +67,11 @@ public interface PreferenceNames { public static final String SLIDESHOW_RECURSIVE = "slideshowRecursive"; public static final String SLIDESHOW_NOSTRETCH = "slideshowNoStretch"; public static final String SLIDESHOW_COLOR = "slideshowColor"; + public static final String SLIDESHOW_PRELOADALL = "slideshowPreloadAll"; // Other public static final String SUPPRESS_WARNING_IM = "suppressWarningIM"; public static final String SUPPRESS_WARNING_JPEGTRAN = "suppressWarningJpegtran"; + public static final String SUPPRESS_WARNING_JPEGTRAN_CROP = "suppressWarningJpegtranCrop"; public static final String SUPPRESS_WARNING_CORRUPTED = "suppressWarningCorrupted"; } diff --git a/com/gallery/GalleryRemote/prefs/SlideshowPanel.java b/com/gallery/GalleryRemote/prefs/SlideshowPanel.java index 07d4584..cb3ad08 100644 --- a/com/gallery/GalleryRemote/prefs/SlideshowPanel.java +++ b/com/gallery/GalleryRemote/prefs/SlideshowPanel.java @@ -2,6 +2,7 @@ import com.gallery.GalleryRemote.util.GRI18n; import com.gallery.GalleryRemote.util.ColorWellButton; +import com.gallery.GalleryRemote.util.ImageUtils; import com.gallery.GalleryRemote.GalleryRemote; import com.gallery.GalleryRemote.GalleryRemoteCore; import com.gallery.GalleryRemote.MainFrame; @@ -26,7 +27,7 @@ public class SlideshowPanel extends PreferencePanel implements PreferenceNames { public JPanel spacerPanel = new JPanel(); JLabel delay = new JLabel(); JTextField jDelay = new JTextField(); - JLabel delayHelp = new JLabel(); + JLabel help = new JLabel(); JLabel progress = new JLabel(); JLabel caption = new JLabel(); JLabel extra = new JLabel(); @@ -40,6 +41,7 @@ public class SlideshowPanel extends PreferencePanel implements PreferenceNames { JCheckBox jLowRez = new JCheckBox(); JCheckBox jRandom = new JCheckBox(); JCheckBox jNoStretch = new JCheckBox(); + JCheckBox jPreloadAll = new JCheckBox(); JPanel spacerPanel1 = new JPanel(); ColorWellButton jBackgroundColor = new ColorWellButton(Color.red); @@ -56,6 +58,7 @@ public void readProperties(PropertiesFile props) { jLowRez.setSelected(props.getBooleanProperty(SLIDESHOW_LOWREZ)); jRandom.setSelected(props.getBooleanProperty(SLIDESHOW_RANDOM)); jNoStretch.setSelected(props.getBooleanProperty(SLIDESHOW_NOSTRETCH)); + jPreloadAll.setSelected(props.getBooleanProperty(SLIDESHOW_PRELOADALL)); jDelay.setText("" + props.getIntProperty(SLIDESHOW_DELAY)); Color color = props.getColorProperty(SLIDESHOW_COLOR); jOverride.setSelected(color != null); @@ -68,11 +71,15 @@ public void readProperties(PropertiesFile props) { jLowRez.setEnabled(! props.isOverridden(SLIDESHOW_LOWREZ)); jRandom.setEnabled(! props.isOverridden(SLIDESHOW_RANDOM)); jNoStretch.setEnabled(! props.isOverridden(SLIDESHOW_NOSTRETCH)); + jPreloadAll.setEnabled(! props.isOverridden(SLIDESHOW_PRELOADALL)); jDelay.setEnabled(! props.isOverridden(SLIDESHOW_DELAY)); + jOverride.setEnabled(! props.isOverridden(SLIDESHOW_COLOR)); + jBackgroundColor.setEnabled(! props.isOverridden(SLIDESHOW_COLOR)); } public void writeProperties(PropertiesFile props) { if (jNoStretch.isSelected() != props.getBooleanProperty(SLIDESHOW_NOSTRETCH)) { + ImageUtils.purgeTemp(); GalleryRemote._().getCore().flushMemory(); } @@ -84,6 +91,7 @@ public void writeProperties(PropertiesFile props) { props.setBooleanProperty(SLIDESHOW_LOWREZ, jLowRez.isSelected()); props.setBooleanProperty(SLIDESHOW_RANDOM, jRandom.isSelected()); props.setBooleanProperty(SLIDESHOW_NOSTRETCH, jNoStretch.isSelected()); + props.setBooleanProperty(SLIDESHOW_PRELOADALL, jPreloadAll.isSelected()); props.setIntProperty(SLIDESHOW_DELAY, Integer.parseInt(jDelay.getText())); if (jOverride.isSelected()) { @@ -92,7 +100,7 @@ public void writeProperties(PropertiesFile props) { props.remove(SLIDESHOW_COLOR); } GalleryRemoteCore core = GalleryRemote._().getCore(); - if (core instanceof MainFrame) { + if (!GalleryRemote._().isAppletMode()) { ((MainFrame) core).previewFrame.repaint(); } } @@ -137,11 +145,13 @@ private void jbInit() { delay.setText(GRI18n.getString(MODULE, "delay")); delay.setLabelFor(jDelay); delay.setToolTipText(GRI18n.getString(MODULE, "delayHelp")); - delayHelp.setText(GRI18n.getString(MODULE, "delayDesc")); + help.setText(GRI18n.getString(MODULE, "delayDesc")); jRandom.setText(GRI18n.getString(MODULE, "random")); jRandom.setToolTipText(GRI18n.getString(MODULE, "randomHelp")); jNoStretch.setText(GRI18n.getString(MODULE, "noStretch")); jNoStretch.setToolTipText(GRI18n.getString(MODULE, "noStretchHelp")); + jPreloadAll.setText(GRI18n.getString(MODULE, "preloadAll")); + jPreloadAll.setToolTipText(GRI18n.getString(MODULE, "preloadAllHelp")); progress.setText(GRI18n.getString(MODULE, "progress")); progress.setLabelFor(jProgress); @@ -166,22 +176,19 @@ private void jbInit() { ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); this.add(apperancePanel, new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0 ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); - this.add(spacerPanel, new GridBagConstraints(0, 3, 1, 1, 1.0, 1.0 + this.add(help, new GridBagConstraints(0, 3, 2, 1, 0.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + this.add(spacerPanel, new GridBagConstraints(0, GridBagConstraints.REMAINDER, 1, 1, 1.0, 1.0 ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); progressionPanel.add(delay, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0 ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0)); progressionPanel.add(jDelay, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0 ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); - progressionPanel.add(delayHelp, new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0 - ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 10, 0, 0), 0, 0)); - //JPanel jRandomNoStretch = new JPanel(); - //jRandomNoStretch.add(jRandom); - //jRandomNoStretch.add(jNoStretch); progressionPanel.add(jRandom, new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0 ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); - //progressionPanel.add(jNoStretch, new GridBagConstraints(0, 3, 2, 1, 1.0, 0.0 - // ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + progressionPanel.add(jPreloadAll, new GridBagConstraints(0, 3, 2, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); apperancePanel.add(jLowRez, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0 ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); @@ -191,8 +198,6 @@ private void jbInit() { ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); apperancePanel.add(jBackgroundColor, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0 ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0)); - //apperancePanel.add(spacerPanel1, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0 - // ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); locationPanel.add(progress, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0 ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0)); diff --git a/com/gallery/GalleryRemote/resources/GRResources.properties b/com/gallery/GalleryRemote/resources/GRResources.properties index 933d5fc..9db1219 100644 --- a/com/gallery/GalleryRemote/resources/GRResources.properties +++ b/com/gallery/GalleryRemote/resources/GRResources.properties @@ -154,6 +154,9 @@ SlidePa.random=Random order SlidePa.randomHelp=Check this option to display the pictures in random order SlidePa.noStretch=Don't stretch small pictures SlidePa.noStretchHelp=Don't resize small pictures to fit screen size, to avoid distorting them +SlidePa.preloadAll=Preload all pictures +SlidePa.preloadAllHelp=If checked, all pictures will be preloaded, rather than the next picture.
\ + This increases temp storage and network usage, but improves performance. SlidePa.progressionTitle=Slideshow progression SlidePa.locationTitle=Location of text info SlidePa.appearanceTitle=Appearance @@ -162,7 +165,8 @@ SlidePa.backgroundColor=Override background Color #PrefsDlog module PrefsDlog.title = Gallery Remote preferences -PrefsDlog.revertTip = Revert this panel to its state before current changes. If the button is disabled, all or part of the changes in this panel cannot be reverted. +PrefsDlog.revertTip = Revert this panel to its state before current changes.
\ + If the button is disabled, all or part of the changes in this panel cannot be reverted. PrefsDlog.revert = Revert @@ -236,7 +240,6 @@ URLPa.new = New... URLPa.delete = Delete... URLPa.details = Details URLPa.delConfirm = Are you sure you want to discard login information for {0}? -URLPa.delete = Delete Gallery URLPa.gllryURL = Gallery URL: URLPa.pnLoginURL = PostNuke login URL: URLPa.pnGllryURL = PostNuke Gallery URL: @@ -353,7 +356,6 @@ GalComm2.fixCorruptedUrl = gallery_remote2.php?cmd=move-album&protocol_version=2 #NewAlbum module NewAlbum.title = New Album -NewAlbum.rootAlbmTitle = Gallery main page NewAlbum.albmNameTip = What do you want to name this album? The name cannot contain any \ of the following characters: \\ / * ? " ' & \\u | . + # or spaces. \ Those characters will be ignored in your new album name. @@ -366,7 +368,6 @@ NewAlbum.albmDesc = Album description #MoveAlbum module MoveAlbum.title = Move Album -MoveAlbum.rootAlbmTitle = Gallery main page MoveAlbum.moveAlbm = Moving album {0} to inside another album MoveAlbum.parentAlbm = Move to @@ -423,7 +424,10 @@ AlbmInspec.res2Frc = Force resize to #PreviewFrame module -PreviewFrame.title = Preview +PreviewFrame.title = Preview/Crop +PreviewFrame.noCrop = This picture is not cropped. To select a crop, click and drag on the picture. +PreviewFrame.crop = This picture has been cropped as shown by the greyed area. To undo the crop, click on the picture. +PreviewFrame.inCrop = Cropping... To crop from the center, hold Alt. To constrain to a square, hold Shift. To preserve the aspect ratio, hold Control. #StatusBar module @@ -480,6 +484,12 @@ ImageUtils.warningTextJpegtran=An optional part of Gallery Remote, jpeg You can find out how to fix this by going to this page: ImageUtils.warningUrlJpegtran=http://gallery.menalto.com/modules.php?op=modload&name=GalleryDocs&file=index&page=gallery-remote.problems.php ImageUtils.warningUrlTextJpegtran=Gallery Remote Known Problems +ImageUtils.warningTextJpegtranCrop=An optional part of Gallery Remote, jpegtran, could not be found
\ + or is malfunctioning for the purpose of cropping pictures. Gallery Remote will not be able to losslessly crop images before
\ + uploading them.
\ + You can find out how to fix this by going to this page: +ImageUtils.warningUrlJpegtranCrop=http://gallery.menalto.com/modules.php?op=modload&name=GalleryDocs&file=index&page=gallery-remote.problems.php +ImageUtils.warningUrlTextJpegtranCrop=Gallery Remote Known Problems # AppletMini @@ -514,4 +524,5 @@ ColorWell.colorChooserTitle = Pick color # Common Common.OK = OK -Common.Cancel = Cancel \ No newline at end of file +Common.Cancel = Cancel +Common.rootAlbmTitle = Gallery main page diff --git a/com/gallery/GalleryRemote/resources/GRResources_ca.properties b/com/gallery/GalleryRemote/resources/GRResources_ca.properties index e4daaef..ea54ffd 100644 --- a/com/gallery/GalleryRemote/resources/GRResources_ca.properties +++ b/com/gallery/GalleryRemote/resources/GRResources_ca.properties @@ -176,7 +176,6 @@ URLPa.new = Nou... URLPa.delete = Borra... URLPa.details = Detalls URLPa.delConfirm = Estàs segur de voler ignorar l'informació d'autenticació per '{0}'? -URLPa.delete = Borra Gallery URLPa.gllryURL = URL de Gallery: \ URLPa.pnLoginURL = URL autenticació PostNuke: \ @@ -293,7 +292,7 @@ NewAlbum.title = Nou Album NewAlbum.rootAlbmTitle = Pàgina principal de Gallery NewAlbum.cancel = Cancel·la NewAlbum.albmNameTip = Quin nom d'àlbum vols assignar? El nom no pot contenir \ - cap dels caràcters següents: \\ / * ? \" \' & \\u | . + # o espais. \ + cap dels caràcters següents: \\ / * ? " ' & \\u | . + # o espais. \ Aquests caràcters seran ignorats en el nom del nou Album. NewAlbum.OK = OK NewAlbum.createAlbm = Creant un nou àlbum a Gallery: \ @@ -316,7 +315,6 @@ PictInspec.upBtn = Mou amunt PictInspec.dnBtnTip = Mou imatge seleccionada avall PictInspec.dnBtn = Mou avall PictInspec.delBtnTip = Borra imatge(s) seleccionades de l'àlbum (no borra l'arxiu de disc) -PictInspec.Delete = Borra PictInspec.icon = icona PictInspec.rotLtTip = Rota la(es) imatge(s) seleccionada(es) 90° en el sentit contrari de les agulles del rellotge PictInspec.rotRtTip = Rota la(es) imatge(s) seleccionada(es) 90° en el sentit de les agulles del rellotge diff --git a/com/gallery/GalleryRemote/resources/GRResources_de.properties b/com/gallery/GalleryRemote/resources/GRResources_de.properties index e0ba923..84c40f3 100644 --- a/com/gallery/GalleryRemote/resources/GRResources_de.properties +++ b/com/gallery/GalleryRemote/resources/GRResources_de.properties @@ -28,7 +28,6 @@ MainFrame.noTitleHeader = MainFrame.OK_toSaveBeforeClose = Soll die aktuelle Datei gespeichert werden bevor sie geschlossen wird? MainFrame.menuNew = Neu MainFrame.menuFile = Datei -MainFrame.menuOpen = \u00d6ffnen... MainFrame.menuSave = Speichern... MainFrame.menuSaveAs = Speichern als... MainFrame.menuClose = Schliessen @@ -230,7 +229,6 @@ URLPa.new = Neu... URLPa.delete = L\u00f6schen... URLPa.details = Details URLPa.delConfirm = Are you sure you want to discard login information for {0}? -URLPa.delete = L\u00f6sche Gallery URLPa.gllryURL = Gallery URL: URLPa.pnLoginURL = PostNuke login URL: URLPa.pnGllryURL = PostNuke Gallery URL: @@ -352,7 +350,7 @@ NewAlbum.title = Neues Album NewAlbum.rootAlbmTitle = Gallery Hauptseite NewAlbum.cancel = Abbrechen NewAlbum.albmNameTip = What do you want to name this album? The name cannot contain any \ - of the following characters: \\ / * ? \" \' & \\u | . + # or spaces. \ + of the following characters: \\ / * ? " ' & \\u | . + # or spaces. \ Those characters will be ignored in your new album name. NewAlbum.OK = OK NewAlbum.createAlbm = Erstelle neues Album f\u00fcr Gallery {0} @@ -383,7 +381,6 @@ PictInspec.upBtn = Verschiebe nach oben PictInspec.dnBtnTip = Verschiebe Auswahl nach unten PictInspec.dnBtn = Verschiebe nach unten PictInspec.delBtnTip = Entferne ausgew\u00e4hlte Bilder -PictInspec.Delete = L\u00f6schen PictInspec.icon = icon PictInspec.rotLtTip = Rotiere Bild 90 gegen Uhrzeigersinn PictInspec.rotRtTip = Rotiere Bild 90 im Uhrzeigersinn diff --git a/com/gallery/GalleryRemote/resources/GRResources_fr.properties b/com/gallery/GalleryRemote/resources/GRResources_fr.properties index 4b28a99..4cf623e 100644 --- a/com/gallery/GalleryRemote/resources/GRResources_fr.properties +++ b/com/gallery/GalleryRemote/resources/GRResources_fr.properties @@ -227,7 +227,7 @@ MoveAlbum.title=D\u00E9placer l'album NewAlbum.OK=OK NewAlbum.albmDesc=Description de l'album NewAlbum.albmName=Nom de l'album -NewAlbum.albmNameTip=Quel nom voulez-vous donner \u00E0 cet album ? Le nom ne peut contenir les caract\u00E8res suivants \: \\ / * ? " ' & \\u | . + \# or espace. Ces caract\u00E8res seront ignor\u00E9s. +NewAlbum.albmNameTip=Quel nom voulez-vous donner \u00E0 cet album ? Le nom ne peut contenir les caract\u00E8res suivants \: \\ / * ? " ' & \\u | . + # or espace. Ces caract\u00E8res seront ignor\u00E9s. NewAlbum.albmTitle=Titre de l'album NewAlbum.cancel=Annuler NewAlbum.createAlbm=Cr\u00E9er un nouvel album dans la Gallery {0} @@ -317,7 +317,7 @@ URLPa.pnGllryURL=URL de Gallery sous PostNuke \: URLPa.pnLoginURL=URL d'identification PostNuke \: URLPa.username=Identifiant \: Update.dnURL=URL de t\u00E9l\u00E9chargement -Update.newVerAvail=Une nouvelle version de Gallery Remote est disponible \! +Update.newVerAvail=Une nouvelle version de Gallery Remote est disponible ! Update.openInBrwsr=Ouvrir dans un navigateur Update.relDate=Date de cette version Update.relNotes=Informations sur cette version diff --git a/com/gallery/GalleryRemote/util/ImageUtils.java b/com/gallery/GalleryRemote/util/ImageUtils.java index 19f4e9e..5b1cd57 100644 --- a/com/gallery/GalleryRemote/util/ImageUtils.java +++ b/com/gallery/GalleryRemote/util/ImageUtils.java @@ -70,6 +70,7 @@ public class ImageUtils { static boolean imIgnoreErrorCode = false; public static boolean useJpegtran = false; + public static boolean useJpegtranCrop = false; static String jpegtranPath = null; static boolean jpegtranIgnoreErrorCode = false; @@ -92,6 +93,7 @@ public class ImageUtils { public static boolean deferredStopUsingIM = false; public static boolean deferredStopUsingJpegtran = false; + public static boolean deferredStopUsingJpegtranCrop = false; public static ImageIcon load(String filename, Dimension d, int usage) { return load(filename, d, usage, false); @@ -110,24 +112,17 @@ public static ImageIcon load(String filename, Dimension d, int usage, boolean ig } if (useIM) { - //StringBuffer cmdline = new StringBuffer(imPath); ArrayList cmd = new ArrayList(); cmd.add(imPath); - //cmdline.append(" -size ").append(d.width).append("x").append(d.height); - //cmd.add("-size"); - //cmd.add(d.width + "x" + d.height); - if (filterName[usage] != null && filterName[usage].length() > 0) { //cmdline.append(" -filter ").append(filterName[usage]); cmd.add("-filter"); cmd.add(filterName[usage]); } - //cmdline.append(" \"").append(filename).append("\""); cmd.add(filename); - //cmdline.append(" -resize \"").append(d.width).append("x").append(d.height).append("\""); cmd.add("-resize"); if (GalleryRemote._().properties.getBooleanProperty(PreferenceNames.SLIDESHOW_NOSTRETCH)) { cmd.add(d.width + "x" + d.height + ">"); @@ -135,7 +130,6 @@ public static ImageIcon load(String filename, Dimension d, int usage, boolean ig cmd.add(d.width + "x" + d.height); } - //cmdline.append(" +profile \"*\" "); cmd.add("+profile"); cmd.add("*"); @@ -144,10 +138,8 @@ public static ImageIcon load(String filename, Dimension d, int usage, boolean ig if (!temp.exists()) { toDelete.add(temp); - //cmdline.append("\"" +temp.getPath() + "\""); cmd.add(temp.getPath()); - //int exitValue = exec(cmdline.toString()); int exitValue = exec((String[]) cmd.toArray(new String[0])); if ((exitValue != 0 && !imIgnoreErrorCode && !ignoreFailure) || ! temp.exists()) { @@ -239,46 +231,45 @@ private static ImageIcon loadJavaInternal(ImageIcon r, Dimension d, boolean noSt } public static File resize(String filename, Dimension d) { + return resize(filename, d, null); + } + + public static File resize(String filename, Dimension d, Rectangle cropTo) { File r = null; long start = System.currentTimeMillis(); - if (!GalleryFileFilter.canManipulateJpeg(filename)) { + if (!GalleryFileFilter.canManipulate(filename)) { return new File(filename); } if (useIM) { - //StringBuffer cmdline = new StringBuffer(imPath); ArrayList cmd = new ArrayList(); cmd.add(imPath); - //cmdline.append(" -size ").append(d.width).append("x").append(d.height); - //cmd.add("-size"); - //cmd.add(d.width + "x" + d.height); - if (filterName[UPLOAD] != null && filterName[UPLOAD].length() > 0) { - //cmdline.append(" -filter ").append(filterName[UPLOAD]); cmd.add("-filter"); cmd.add(filterName[UPLOAD]); } - //cmdline.append(" \"").append(filename).append("\""); cmd.add(filename); - //cmdline.append(" -resize \"").append(d.width).append("x").append(d.height).append(">\""); - cmd.add("-resize"); - cmd.add(d.width + "x" + d.height + ">"); + if (cropTo != null) { + cmd.add("-crop"); + cmd.add(cropTo.width + "x" + cropTo.height + "+" + cropTo.x + "+" + cropTo.y); + } - //cmdline.append("-gravity SouthEast -draw \"image Over 200,200 0,0 G:\\Projects\\Dev\\gallery_remote10\\2ni.png\" "); + if (d != null) { + cmd.add("-resize"); + cmd.add(d.width + "x" + d.height + ">"); + } - //cmdline.append(" -quality ").append(jpegQuality); cmd.add("-quality"); cmd.add("" + jpegQuality); r = deterministicTempFile("res" - , "." + GalleryFileFilter.getExtension(filename), tmpDir, filename + d); + , "." + GalleryFileFilter.getExtension(filename), tmpDir, filename + d + cropTo); toDelete.add(r); - //cmdline.append(" \"").append(r.getPath()).append("\""); cmd.add(r.getPath()); int exitValue = exec((String[]) cmd.toArray(new String[0])); @@ -435,12 +426,12 @@ public static File rotate(String filename, int angle, boolean flip, boolean rese } if (flip) { - r = jpegtranExec(filename, "-flip", "horizontal"); + r = jpegtranExec(filename, "-flip", "horizontal", false); filename = r.getPath(); } if (angle != 0) { - r = jpegtranExec(filename, "-rotate", "" + (angle * 90)); + r = jpegtranExec(filename, "-rotate", "" + (angle * 90), false); } /*if (resetExifOrientation) { @@ -463,42 +454,77 @@ public static File rotate(String filename, int angle, boolean flip, boolean rese return r; } - private static File jpegtranExec(String filename, String arg1, String arg2) throws IOException { + public static File losslessCrop(String filename, Rectangle cropTo) { + File r = null; + + if (!GalleryFileFilter.canManipulateJpeg(filename)) { + throw new UnsupportedOperationException("jpegtran doesn't support cropping anything but jpeg"); + } + + if (useJpegtran) { + File orig = null; + File dest = null; + try { + if (GalleryRemote.IS_MAC_OS_X) { + orig = new File(filename); + dest = deterministicTempFile("tmp" + , "." + GalleryFileFilter.getExtension(filename), tmpDir, filename + cropTo); + + orig.renameTo(dest); + filename = dest.getPath(); + } + + r = jpegtranExec(filename, "-crop", cropTo.width + "x" + cropTo.height + "+" + + cropTo.x + "+" + cropTo.y, true); + } catch (IOException e1) { + Log.logException(Log.LEVEL_ERROR, MODULE, e1); + } finally { + if (orig != null && dest != null) { + dest.renameTo(orig); + } + } + } + + if (!useJpegtran && r == null) { + throw new UnsupportedOperationException("jpegtran with CROP PATCH must be installed for this operation"); + } + + return r; + } + + private static File jpegtranExec(String filename, String arg1, String arg2, boolean crop) throws IOException { File r; - //StringBuffer cmdline = new StringBuffer(jpegtranPath); ArrayList cmd = new ArrayList(); cmd.add(jpegtranPath); - //cmdline.append(" -copy all"); cmd.add("-copy"); cmd.add("all"); //cmd.add("-debug"); - //cmdline.append(command); - //cmd.add(command); cmd.add(arg1); cmd.add(arg2); - r = deterministicTempFile("rot" + r = deterministicTempFile(crop?"crop":"rot" , "." + GalleryFileFilter.getExtension(filename), tmpDir, filename + arg1 + arg2); toDelete.add(r); - //cmdline.append(" -outfile \"").append(r.getPath()).append("\""); - //cmdline.append(" -outfile ").append(r.getPath()); cmd.add("-outfile"); cmd.add(r.getPath()); - //cmdline.append(" \"").append(filename).append("\""); cmd.add(filename); - //int exitValue = exec(cmdline.toString()); int exitValue = exec((String[]) cmd.toArray(new String[0])); if ((exitValue != 0 && !jpegtranIgnoreErrorCode) || ! r.exists()) { if (exitValue != -1 || ! r.exists()) { // don't kill jpegtran if it's just an InterruptedException - Log.log(Log.LEVEL_CRITICAL, MODULE, "jpegtran doesn't seem to be working. Disabling"); - stopUsingJpegtran(); + if (crop) { + Log.log(Log.LEVEL_CRITICAL, MODULE, "jpegtran doesn't seem to be working for cropping. Disabling"); + stopUsingJpegtranCrop(); + } else { + Log.log(Log.LEVEL_CRITICAL, MODULE, "jpegtran doesn't seem to be working. Disabling"); + stopUsingJpegtran(); + } } r = null; } @@ -546,6 +572,30 @@ public static ImageIcon rotateImageIcon(ImageIcon thumb, int angle, boolean flip return thumb; } + public static AffineTransform createTransform(Rectangle container, + Rectangle contentResized, + Dimension content, + int angle, boolean flipped) { + double scale = Math.sqrt(1.0F * content.width * content.height / contentResized.width / contentResized.height); + + AffineTransform transform = new AffineTransform(); + transform.translate(content.width / 2, content.height / 2); + + if (flipped) { + transform.scale(-scale, scale); + } else { + transform.scale(scale, scale); + } + + if (angle != 0) { + transform.rotate(-angle * Math.PI / 2); + } + + transform.translate(-container.width / 2, -container.height / 2); + + return transform; + } + public static LocalInfo getLocalFilenameForPicture(Picture p, boolean full) { URL u = null; Dimension d = null; @@ -597,8 +647,11 @@ public LocalInfo(String name, String ext, String filename, File file, URL url, D } public static File download(Picture p, Dimension d, StatusUpdate su, CancellableTransferListener tl) { + if (!p.isOnline()) { + return p.getSource(); + } + URL pictureUrl = null; - //Dimension pictureDimension = null; File f; String filename; LocalInfo fullInfo = getLocalFilenameForPicture(p, true); @@ -705,6 +758,34 @@ public static File download(Picture p, Dimension d, StatusUpdate su, Cancellable return f; } + public static Dimension getPictureDimension(Picture p) { + if (p.isOnline()) { + // can't find out size without downloading + return null; + } + + ImageInputStream iis = null; + try { + iis = ImageIO.createImageInputStream(p.getSource()); + + Iterator iter = ImageIO.getImageReaders(iis); + if (!iter.hasNext()) { + return null; + } + + ImageReader reader = (ImageReader)iter.next(); + reader.setInput(iis, true, false); + Dimension d = new Dimension(reader.getWidth(0), reader.getHeight(0)); + + iis.close(); + reader.dispose(); + return d; + } catch (IOException e) { + Log.logException(Log.LEVEL_ERROR, MODULE, e); + return null; + } + } + static { tmpDir = new File(System.getProperty("java.io.tmpdir"), "thumbs"); @@ -791,6 +872,7 @@ public static File download(Picture p, Dimension d, StatusUpdate su, Cancellable p.copyProperties(GalleryRemote._().properties); useJpegtran = p.getBooleanProperty("jp.enabled"); + useJpegtran = p.getBooleanProperty("jp.crop.enabled"); Log.log(Log.LEVEL_INFO, MODULE, "useJpegtran: " + useJpegtran); if (useJpegtran) { jpegtranPath = p.getProperty("jp.path"); @@ -829,15 +911,17 @@ public static Dimension getSizeKeepRatio(Dimension source, Dimension target, boo Dimension result = new Dimension(); - float sourceRatio = (float) source.width / source.height; - float targetRatio = (float) target.width / target.height; + float sourceRatio = Math.abs((float) source.width / source.height); + float targetRatio = Math.abs((float) target.width / target.height); - if (targetRatio > sourceRatio) { + if (Math.abs(targetRatio) > Math.abs(sourceRatio)) { result.height = target.height; - result.width = source.width * target.height / source.height; + result.width = (int) (target.height * sourceRatio * + (target.height * target.width > 0?1:-1)); } else { result.width = target.width; - result.height = source.height * target.width / source.width; + result.height = (int) (target.width / sourceRatio * + (target.height * target.width > 0?1:-1)); } return result; @@ -1013,6 +1097,12 @@ public static void deferredTasks() { stopUsingJpegtran(); } + + if (deferredStopUsingJpegtranCrop) { + deferredStopUsingJpegtranCrop = false; + + stopUsingJpegtranCrop(); + } } static void stopUsingIM() { @@ -1057,6 +1147,27 @@ static void stopUsingJpegtran() { } } + static void stopUsingJpegtranCrop() { + useJpegtranCrop = false; + + if (!GalleryRemote._().properties.getBooleanProperty(PreferenceNames.SUPPRESS_WARNING_JPEGTRAN_CROP)) { + if (GalleryRemote._().getMainFrame() != null + && GalleryRemote._().getMainFrame().isVisible()) { + UrlMessageDialog md = new UrlMessageDialog( + GRI18n.getString(MODULE, "warningTextJpegtranCrop"), + GRI18n.getString(MODULE, "warningUrlJpegtranCrop"), + GRI18n.getString(MODULE, "warningUrlTextJpegtranCrop") + ); + + if (md.dontShow()) { + GalleryRemote._().properties.setBooleanProperty(PreferenceNames.SUPPRESS_WARNING_JPEGTRAN_CROP, true); + } + } else { + deferredStopUsingJpegtranCrop = true; + } + } + } + public static void displayMetadata(Node root) { displayMetadata(root, 0); } diff --git a/com/gallery/GalleryRemote/util/UrlMessageDialog.java b/com/gallery/GalleryRemote/util/UrlMessageDialog.java index f66ac66..6b3c1bf 100644 --- a/com/gallery/GalleryRemote/util/UrlMessageDialog.java +++ b/com/gallery/GalleryRemote/util/UrlMessageDialog.java @@ -38,7 +38,7 @@ public UrlMessageDialog(String message, String url, String urlText) { jURL.setUrl(url); jDontShow.setText(GRI18n.getString(MODULE, "warningDontShow")); - jOk.setText(GRI18n.getString(MODULE, "warningOK")); + jOk.setText(GRI18n.getString("Common", "OK")); jOk.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setVisible(false); diff --git a/defaults.properties b/defaults.properties index 23f88b0..b517122 100644 --- a/defaults.properties +++ b/defaults.properties @@ -150,6 +150,9 @@ slideshowNoStretch=false # Color of the slideshow and preview backgrounds (R, G, B) # slideshowColor=100,100,100 +# Preload all images from the server, not just the next one +slideshowPreloadAll=false + # # --- Technical --- # @@ -185,6 +188,6 @@ updateUrlBeta=http://gallery.sourceforge.net/gallery_remote_version_check_beta.p # # --- Do not edit below this line --- # -version=1.4-b6 -releaseDate=2004/04/30 +version=1.4-b7 +releaseDate=2004/05/06 aboutText=Gallery Remote\n \n \nA part of the Gallery Open-Source Project\nhttp://gallery.sourceforge.net\n \n \nMaintained by:\n \nPierre-Luc Paour\n \n \nInitial version by Chris Smith\n \n \nContributors:\n \nTim Miller\nDolan Halbrook\nMarkus Cozowicz\nScott Gartner\nAmedeo Paglione\nChris Schwerdt\n \n \nArtwork by Ross A. Reyman\n \n \nBundled software:\n \nImageMagick\nJSX\nJava look and feel Graphics Repository icons\njpegtran, Guido Vollbeding's version\nMetadataExtractor diff --git a/jpegtran/jpegtran.preinstalled b/jpegtran/jpegtran.preinstalled index 63f353f..bc1c1ad 100644 --- a/jpegtran/jpegtran.preinstalled +++ b/jpegtran/jpegtran.preinstalled @@ -1,6 +1,9 @@ # should jpegtran support be enabled? jp.enabled=true +# should lossless cropping be enabled? +jp.crop.enabled=true + # path to the executable (must be a fully-qualified path) jp.path=jpegtran diff --git a/jpegtran/linux/jpegtran b/jpegtran/linux/jpegtran new file mode 100644 index 0000000000000000000000000000000000000000..4fc225b899296400573679357016c0ce881f50c2 GIT binary patch literal 105684 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¨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