protected static void prepareConnection(HttpURLConnection conn) throws ProtocolException { conn.setRequestMethod("GET"); Settings s = Settings.getInstance(); conn.setConnectTimeout(1000 * s.httpConnectionTimeout); conn.setReadTimeout(1000 * s.httpReadTimeout); if (conn.getRequestProperty("User-agent") == null) conn.setRequestProperty("User-agent", s.getUserAgent()); conn.setRequestProperty("Accept", ACCEPT); }
@Override public String getTileUrl(int zoom, int tilex, int tiley) { String ticket = Settings.getInstance().osmHikingTicket; if (ticket != null && ticket.length() > 0) { return ABO + super.getTileUrl(zoom, tilex, tiley) + "/ticket/" + ticket; } else return null; }
public static void load() { File mapSourcesDir = Settings.getInstance().getMapSourcesDirectory(); File mapSourcesProperties = new File(mapSourcesDir, FILENAME); if (!mapSourcesProperties.isFile()) return; FileInputStream in = null; try { in = new FileInputStream(mapSourcesProperties); PROPERTIES.load(in); } catch (IOException e) { log.error("Failed to load mapsources.properties", e); } finally { Utilities.closeStream(in); } if (!SHUTDOWN_HOOK_REGISTERED) { Runtime.getRuntime() .addShutdownHook( new Thread() { @Override public void run() { save(); } }); } }
/** * Reads all available data from the input stream of <code>conn</code> and returns it as byte * array. If no input data is available the method returns <code>null</code>. * * @param conn * @return * @throws IOException */ protected static byte[] loadBodyDataInBuffer(HttpURLConnection conn) throws IOException { InputStream input = conn.getInputStream(); byte[] data = null; try { if (Thread.currentThread() instanceof MapSourceListener) { // We only throttle atlas downloads, not downloads for the preview map long bandwidthLimit = Settings.getInstance().getBandwidthLimit(); if (bandwidthLimit > 0) { input = new ThrottledInputStream(input); } } data = Utilities.getInputBytes(input); } catch (IOException e) { InputStream errorIn = conn.getErrorStream(); try { byte[] errData = Utilities.getInputBytes(errorIn); log.trace( "Retrieved " + errData.length + " error bytes for a HTTP " + conn.getResponseCode()); } catch (Exception ee) { log.debug("Error retrieving error stream content: " + e); } finally { Utilities.closeStream(errorIn); } throw e; } finally { Utilities.closeStream(input); } log.trace("Retrieved " + data.length + " bytes for a HTTP " + conn.getResponseCode()); if (data.length == 0) return null; return data; }
public static byte[] getImage(int x, int y, int zoom, HttpMapSource mapSource) throws IOException, InterruptedException, UnrecoverableDownloadException { MapSpace mapSpace = mapSource.getMapSpace(); int maxTileIndex = mapSpace.getMaxPixels(zoom) / mapSpace.getTileSize(); if (x > maxTileIndex) throw new RuntimeException("Invalid tile index x=" + x + " for zoom " + zoom); if (y > maxTileIndex) throw new RuntimeException("Invalid tile index y=" + y + " for zoom " + zoom); TileStore ts = TileStore.getInstance(); // Thread.sleep(2000); // Test code for creating random download failures // if (Math.random()>0.7) throw new // IOException("intentionally download error"); Settings s = Settings.getInstance(); TileStoreEntry tile = null; if (s.tileStoreEnabled) { // Copy the file from the persistent tilestore instead of // downloading it from internet. tile = ts.getTile(x, y, zoom, mapSource); boolean expired = isTileExpired(tile); if (tile != null) { if (expired) { log.trace("Expired: " + mapSource.getName() + " " + tile); } else { log.trace("Tile of map source " + mapSource.getName() + " used from tilestore"); byte[] data = tile.getData(); notifyCachedTileUsed(data.length); return data; } } } byte[] data = null; if (tile == null) { data = downloadTileAndUpdateStore(x, y, zoom, mapSource); notifyTileDownloaded(data.length); } else { byte[] updatedData = updateStoredTile(tile, mapSource); if (updatedData != null) { data = updatedData; notifyTileDownloaded(data.length); } else { data = tile.getData(); notifyCachedTileUsed(data.length); } } return data; }
public static void save() { if (PROPERTIES.size() == 0) return; File mapSourcesDir = Settings.getInstance().getMapSourcesDirectory(); File mapSourcesProperties = new File(mapSourcesDir, FILENAME); FileOutputStream out = null; try { out = new FileOutputStream(mapSourcesProperties); PROPERTIES.store(out, ""); } catch (IOException e) { log.error("", e); } finally { Utilities.closeStream(out); } }
public void actionPerformed(ActionEvent event) { Object source = event.getSource(); File atlasFolder = Settings.getInstance().getAtlasOutputDirectory(); if (openProgramFolderButton.equals(source)) { try { OSUtilities.openFolderBrowser(atlasFolder); } catch (Exception e) { log.error("", e); } } else if (dismissWindowButton.equals(source)) { downloadController = null; closeWindow(); } else if (abortAtlasCreationButton.equals(source)) { aborted = true; stopUpdateTask(); if (downloadController != null) downloadController.abortAtlasCreation(); else closeWindow(); } else if (pauseResumeDownloadButton.equals(source)) { if (downloadController != null) downloadController.pauseResumeAtlasCreation(); } }
public class TileDownLoader { public static String ACCEPT = "text/html, image/png, image/jpeg, image/gif, */*;q=0.1"; static { Object defaultReadTimeout = System.getProperty("sun.net.client.defaultReadTimeout"); if (defaultReadTimeout == null) System.setProperty("sun.net.client.defaultReadTimeout", "15000"); System.setProperty("http.maxConnections", "20"); } private static Logger log = Logger.getLogger(TileDownLoader.class); private static Settings settings = Settings.getInstance(); public static byte[] getImage(int x, int y, int zoom, HttpMapSource mapSource) throws IOException, InterruptedException, UnrecoverableDownloadException { MapSpace mapSpace = mapSource.getMapSpace(); int maxTileIndex = mapSpace.getMaxPixels(zoom) / mapSpace.getTileSize(); if (x > maxTileIndex) throw new RuntimeException("Invalid tile index x=" + x + " for zoom " + zoom); if (y > maxTileIndex) throw new RuntimeException("Invalid tile index y=" + y + " for zoom " + zoom); TileStore ts = TileStore.getInstance(); // Thread.sleep(2000); // Test code for creating random download failures // if (Math.random()>0.7) throw new // IOException("intentionally download error"); Settings s = Settings.getInstance(); TileStoreEntry tile = null; if (s.tileStoreEnabled) { // Copy the file from the persistent tilestore instead of // downloading it from internet. tile = ts.getTile(x, y, zoom, mapSource); boolean expired = isTileExpired(tile); if (tile != null) { if (expired) { log.trace("Expired: " + mapSource.getName() + " " + tile); } else { log.trace("Tile of map source " + mapSource.getName() + " used from tilestore"); byte[] data = tile.getData(); notifyCachedTileUsed(data.length); return data; } } } byte[] data = null; if (tile == null) { data = downloadTileAndUpdateStore(x, y, zoom, mapSource); notifyTileDownloaded(data.length); } else { byte[] updatedData = updateStoredTile(tile, mapSource); if (updatedData != null) { data = updatedData; notifyTileDownloaded(data.length); } else { data = tile.getData(); notifyCachedTileUsed(data.length); } } return data; } private static void notifyTileDownloaded(int size) { if (Thread.currentThread() instanceof MapSourceListener) { ((MapSourceListener) Thread.currentThread()).tileDownloaded(size); } } private static void notifyCachedTileUsed(int size) { if (Thread.currentThread() instanceof MapSourceListener) { ((MapSourceListener) Thread.currentThread()).tileLoadedFromCache(size); } } /** * Download the tile from the web server and updates the tile store if the tile could be * successfully retrieved. * * @param x * @param y * @param zoom * @param mapSource * @return * @throws UnrecoverableDownloadException * @throws IOException * @throws InterruptedException */ public static byte[] downloadTileAndUpdateStore(int x, int y, int zoom, HttpMapSource mapSource) throws UnrecoverableDownloadException, IOException, InterruptedException { return downloadTileAndUpdateStore( x, y, zoom, mapSource, Settings.getInstance().tileStoreEnabled); } public static byte[] downloadTile(int x, int y, int zoom, HttpMapSource mapSource) throws UnrecoverableDownloadException, IOException, InterruptedException { return downloadTileAndUpdateStore(x, y, zoom, mapSource, false); } public static byte[] downloadTileAndUpdateStore( int x, int y, int zoom, HttpMapSource mapSource, boolean useTileStore) throws UnrecoverableDownloadException, IOException, InterruptedException { if (zoom < 0) throw new UnrecoverableDownloadException("Negative zoom!"); HttpURLConnection conn = mapSource.getTileUrlConnection(zoom, x, y); if (conn == null) throw new UnrecoverableDownloadException( "Tile x=" + x + " y=" + y + " zoom=" + zoom + " is not a valid tile in map source " + mapSource); log.trace("Downloading " + conn.getURL()); prepareConnection(conn); conn.connect(); int code = conn.getResponseCode(); byte[] data = loadBodyDataInBuffer(conn); if (code != HttpURLConnection.HTTP_OK) throw new DownloadFailedException(conn, code); checkContentType(conn, data); checkContentLength(conn, data); String eTag = conn.getHeaderField("ETag"); long timeLastModified = conn.getLastModified(); long timeExpires = conn.getExpiration(); Utilities.checkForInterruption(); TileImageType imageType = Utilities.getImageType(data); if (imageType == null) throw new UnrecoverableDownloadException("The returned image is of unknown format"); if (useTileStore) { TileStore.getInstance() .putTileData(data, x, y, zoom, mapSource, timeLastModified, timeExpires, eTag); } Utilities.checkForInterruption(); return data; } public static byte[] updateStoredTile(TileStoreEntry tile, HttpMapSource mapSource) throws UnrecoverableDownloadException, IOException, InterruptedException { final int x = tile.getX(); final int y = tile.getY(); final int zoom = tile.getZoom(); final HttpMapSource.TileUpdate tileUpdate = mapSource.getTileUpdate(); switch (tileUpdate) { case ETag: { boolean unchanged = hasTileETag(tile, mapSource); if (unchanged) { if (log.isTraceEnabled()) log.trace("Data unchanged on server (eTag): " + mapSource + " " + tile); return null; } break; } case LastModified: { boolean isNewer = isTileNewer(tile, mapSource); if (!isNewer) { if (log.isTraceEnabled()) log.trace("Data unchanged on server (LastModified): " + mapSource + " " + tile); return null; } break; } } HttpURLConnection conn = mapSource.getTileUrlConnection(zoom, x, y); if (conn == null) throw new UnrecoverableDownloadException( "Tile x=" + x + " y=" + y + " zoom=" + zoom + " is not a valid tile in map source " + mapSource); if (log.isTraceEnabled()) log.trace(String.format("Checking %s %s", mapSource.getName(), tile)); prepareConnection(conn); boolean conditionalRequest = false; switch (tileUpdate) { case IfNoneMatch: { if (tile.geteTag() != null) { conn.setRequestProperty("If-None-Match", tile.geteTag()); conditionalRequest = true; } break; } case IfModifiedSince: { if (tile.getTimeLastModified() > 0) { conn.setIfModifiedSince(tile.getTimeLastModified()); conditionalRequest = true; } break; } } conn.connect(); Settings s = Settings.getInstance(); int code = conn.getResponseCode(); if (conditionalRequest && code == HttpURLConnection.HTTP_NOT_MODIFIED) { // Data unchanged on server if (s.tileStoreEnabled) { tile.update(conn.getExpiration()); TileStore.getInstance().putTile(tile, mapSource); } if (log.isTraceEnabled()) log.trace("Data unchanged on server: " + mapSource + " " + tile); return null; } byte[] data = loadBodyDataInBuffer(conn); if (code != HttpURLConnection.HTTP_OK) throw new DownloadFailedException(conn, code); checkContentType(conn, data); checkContentLength(conn, data); String eTag = conn.getHeaderField("ETag"); long timeLastModified = conn.getLastModified(); long timeExpires = conn.getExpiration(); Utilities.checkForInterruption(); TileImageType imageType = Utilities.getImageType(data); if (imageType == null) throw new UnrecoverableDownloadException("The returned image is of unknown format"); if (s.tileStoreEnabled) { TileStore.getInstance() .putTileData(data, x, y, zoom, mapSource, timeLastModified, timeExpires, eTag); } Utilities.checkForInterruption(); return data; } public static boolean isTileExpired(TileStoreEntry tileStoreEntry) { if (tileStoreEntry == null) return true; long expiredTime = tileStoreEntry.getTimeExpires(); if (expiredTime >= 0) { // server had set an expiration time long maxExpirationTime = settings.tileMaxExpirationTime + tileStoreEntry.getTimeDownloaded(); long minExpirationTime = settings.tileMinExpirationTime + tileStoreEntry.getTimeDownloaded(); expiredTime = Math.max(minExpirationTime, Math.min(maxExpirationTime, expiredTime)); } else { // no expiration time set by server - use the default one expiredTime = tileStoreEntry.getTimeDownloaded() + settings.tileDefaultExpirationTime; } return (expiredTime < System.currentTimeMillis()); } /** * Reads all available data from the input stream of <code>conn</code> and returns it as byte * array. If no input data is available the method returns <code>null</code>. * * @param conn * @return * @throws IOException */ protected static byte[] loadBodyDataInBuffer(HttpURLConnection conn) throws IOException { InputStream input = conn.getInputStream(); byte[] data = null; try { if (Thread.currentThread() instanceof MapSourceListener) { // We only throttle atlas downloads, not downloads for the preview map long bandwidthLimit = Settings.getInstance().getBandwidthLimit(); if (bandwidthLimit > 0) { input = new ThrottledInputStream(input); } } data = Utilities.getInputBytes(input); } catch (IOException e) { InputStream errorIn = conn.getErrorStream(); try { byte[] errData = Utilities.getInputBytes(errorIn); log.trace( "Retrieved " + errData.length + " error bytes for a HTTP " + conn.getResponseCode()); } catch (Exception ee) { log.debug("Error retrieving error stream content: " + e); } finally { Utilities.closeStream(errorIn); } throw e; } finally { Utilities.closeStream(input); } log.trace("Retrieved " + data.length + " bytes for a HTTP " + conn.getResponseCode()); if (data.length == 0) return null; return data; } /** * Performs a <code>HEAD</code> request for retrieving the <code>LastModified</code> header value. */ protected static boolean isTileNewer(TileStoreEntry tile, HttpMapSource mapSource) throws IOException { long oldLastModified = tile.getTimeLastModified(); if (oldLastModified <= 0) { log.warn( "Tile age comparison not possible: " + "tile in tilestore does not contain lastModified attribute"); return true; } HttpURLConnection conn = mapSource.getTileUrlConnection(tile.getZoom(), tile.getX(), tile.getY()); conn.setRequestMethod("HEAD"); conn.setRequestProperty("Accept", ACCEPT); long newLastModified = conn.getLastModified(); if (newLastModified == 0) return true; return (newLastModified > oldLastModified); } protected static boolean hasTileETag(TileStoreEntry tile, HttpMapSource mapSource) throws IOException { String eTag = tile.geteTag(); if (eTag == null || eTag.length() == 0) { log.warn("ETag check not possible: " + "tile in tilestore does not contain ETag attribute"); return true; } HttpURLConnection conn = mapSource.getTileUrlConnection(tile.getZoom(), tile.getX(), tile.getY()); conn.setRequestMethod("HEAD"); conn.setRequestProperty("Accept", ACCEPT); String onlineETag = conn.getHeaderField("ETag"); if (onlineETag == null || onlineETag.length() == 0) return true; return (onlineETag.equals(eTag)); } protected static void prepareConnection(HttpURLConnection conn) throws ProtocolException { conn.setRequestMethod("GET"); Settings s = Settings.getInstance(); conn.setConnectTimeout(1000 * s.httpConnectionTimeout); conn.setReadTimeout(1000 * s.httpReadTimeout); if (conn.getRequestProperty("User-agent") == null) conn.setRequestProperty("User-agent", s.getUserAgent()); conn.setRequestProperty("Accept", ACCEPT); } protected static void checkContentType(HttpURLConnection conn, byte[] data) throws UnrecoverableDownloadException { String contentType = conn.getContentType(); if (contentType != null) { contentType = contentType.toLowerCase(); if (!contentType.startsWith("image/")) { if (log.isTraceEnabled() && contentType.startsWith("text/")) { log.trace("Content (" + contentType + "): " + new String(data)); } throw new UnrecoverableDownloadException( "Content type of the loaded image is unknown: " + contentType, UnrecoverableDownloadException.ERROR_CODE_CONTENT_TYPE); } } } /** * Check if the retrieved data length is equal to the header value Content-Length * * @param conn * @param data * @throws UnrecoverableDownloadException */ protected static void checkContentLength(HttpURLConnection conn, byte[] data) throws UnrecoverableDownloadException { int len = conn.getContentLength(); if (len < 0) return; if (data.length != len) throw new UnrecoverableDownloadException( "Content length is not as declared by the server: retrived=" + data.length + " bytes expected-content-length=" + len + " bytes"); } }
public static byte[] updateStoredTile(TileStoreEntry tile, HttpMapSource mapSource) throws UnrecoverableDownloadException, IOException, InterruptedException { final int x = tile.getX(); final int y = tile.getY(); final int zoom = tile.getZoom(); final HttpMapSource.TileUpdate tileUpdate = mapSource.getTileUpdate(); switch (tileUpdate) { case ETag: { boolean unchanged = hasTileETag(tile, mapSource); if (unchanged) { if (log.isTraceEnabled()) log.trace("Data unchanged on server (eTag): " + mapSource + " " + tile); return null; } break; } case LastModified: { boolean isNewer = isTileNewer(tile, mapSource); if (!isNewer) { if (log.isTraceEnabled()) log.trace("Data unchanged on server (LastModified): " + mapSource + " " + tile); return null; } break; } } HttpURLConnection conn = mapSource.getTileUrlConnection(zoom, x, y); if (conn == null) throw new UnrecoverableDownloadException( "Tile x=" + x + " y=" + y + " zoom=" + zoom + " is not a valid tile in map source " + mapSource); if (log.isTraceEnabled()) log.trace(String.format("Checking %s %s", mapSource.getName(), tile)); prepareConnection(conn); boolean conditionalRequest = false; switch (tileUpdate) { case IfNoneMatch: { if (tile.geteTag() != null) { conn.setRequestProperty("If-None-Match", tile.geteTag()); conditionalRequest = true; } break; } case IfModifiedSince: { if (tile.getTimeLastModified() > 0) { conn.setIfModifiedSince(tile.getTimeLastModified()); conditionalRequest = true; } break; } } conn.connect(); Settings s = Settings.getInstance(); int code = conn.getResponseCode(); if (conditionalRequest && code == HttpURLConnection.HTTP_NOT_MODIFIED) { // Data unchanged on server if (s.tileStoreEnabled) { tile.update(conn.getExpiration()); TileStore.getInstance().putTile(tile, mapSource); } if (log.isTraceEnabled()) log.trace("Data unchanged on server: " + mapSource + " " + tile); return null; } byte[] data = loadBodyDataInBuffer(conn); if (code != HttpURLConnection.HTTP_OK) throw new DownloadFailedException(conn, code); checkContentType(conn, data); checkContentLength(conn, data); String eTag = conn.getHeaderField("ETag"); long timeLastModified = conn.getLastModified(); long timeExpires = conn.getExpiration(); Utilities.checkForInterruption(); TileImageType imageType = Utilities.getImageType(data); if (imageType == null) throw new UnrecoverableDownloadException("The returned image is of unknown format"); if (s.tileStoreEnabled) { TileStore.getInstance() .putTileData(data, x, y, zoom, mapSource, timeLastModified, timeExpires, eTag); } Utilities.checkForInterruption(); return data; }
/** * Download the tile from the web server and updates the tile store if the tile could be * successfully retrieved. * * @param x * @param y * @param zoom * @param mapSource * @return * @throws UnrecoverableDownloadException * @throws IOException * @throws InterruptedException */ public static byte[] downloadTileAndUpdateStore(int x, int y, int zoom, HttpMapSource mapSource) throws UnrecoverableDownloadException, IOException, InterruptedException { return downloadTileAndUpdateStore( x, y, zoom, mapSource, Settings.getInstance().tileStoreEnabled); }
private void createComponents() { background = new JPanel(new GridBagLayout()); windowTitle = new JLabel("<html><h3>ATLAS CREATION IN PROGRESS...</h3></html>"); title = new JLabel("Processing maps of atlas:"); mapInfoLabel = new JLabel( "Processing map ABCDEFGHIJKLMNOPQRSTUVWXYZ-nn " + "of layer ABCDEFGHIJKLMNOPQRSTUVWXYZ from map source ABCDEFGHIJKLMNOPQRSTUVWXYZ"); atlasMapsDone = new JLabel("000 of 000 done"); atlasPercent = new JLabel(String.format(TEXT_TENTHPERCENT, 100.0)); atlasTimeLeft = new JLabel("Time remaining: 00000 minutes 00 seconds", JLabel.RIGHT); atlasProgressBar = new JProgressBar(); mapDownloadTitle = new JLabel(TEXT_MAP_DOWNLOAD + "000"); mapDownloadElementsDone = new JLabel("1000000 of 1000000 tiles done"); mapDownloadPercent = new JLabel(String.format(TEXT_PERCENT, 100)); mapDownloadTimeLeft = new JLabel("Time remaining: 00000 minutes 00 seconds", JLabel.RIGHT); mapDownloadProgressBar = new JProgressBar(); mapCreation = new JLabel("Map Creation"); mapCreationProgressBar = new JProgressBar(); nrOfDownloadedBytesPerSecond = new JLabel("Average download speed"); nrOfDownloadedBytesPerSecondValue = new JLabel(); nrOfDownloadedBytes = new JLabel("Downloaded"); nrOfDownloadedBytesValue = new JLabel(); nrOfCacheBytes = new JLabel("Loaded from tile store"); nrOfCacheBytesValue = new JLabel(); activeDownloads = new JLabel("Active tile fetcher threads"); activeDownloadsValue = new JLabel(); retryableDownloadErrors = new JLabel("Transient download errors"); retryableDownloadErrors.setToolTipText( "<html><h4>Download errors for the current map and for the total atlas (transient/unrecoverable)</h4>" + "<p>Mobile Atlas Creator retries failed tile downloads up to two times. <br>" + "If the tile downloads fails the second time the tile will be counted as <br>" + "<b>unrecoverable</b> error and not tried again during the current map creation run.<br></p></html>"); retryableDownloadErrorsValue = new JLabel(); retryableDownloadErrorsValue.setToolTipText(retryableDownloadErrors.getToolTipText()); permanentDownloadErrors = new JLabel("Unrecoverable download errors"); permanentDownloadErrors.setToolTipText(retryableDownloadErrors.getToolTipText()); permanentDownloadErrorsValue = new JLabel(); permanentDownloadErrorsValue.setToolTipText(permanentDownloadErrors.getToolTipText()); totalDownloadTime = new JLabel("Total creation time"); totalDownloadTimeValue = new JLabel(); ignoreDlErrors = new JCheckBox( "Ignore download errors and continue automatically", Settings.getInstance().ignoreDlErrors); ignoreDlErrors.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { downloadController.setIgnoreErrors(ignoreDlErrors.isSelected()); } }); statusLabel = new JLabel("Status:"); Font f = statusLabel.getFont(); statusLabel.setFont(f.deriveFont(Font.BOLD)); abortAtlasCreationButton = new JButton("Abort creation"); abortAtlasCreationButton.setToolTipText("Abort current Atlas download"); dismissWindowButton = new JButton("Close Window"); dismissWindowButton.setToolTipText("Atlas creation in progress..."); dismissWindowButton.setVisible(false); openProgramFolderButton = new JButton("Open Atlas Folder"); openProgramFolderButton.setToolTipText("Atlas creation in progress..."); openProgramFolderButton.setEnabled(false); pauseResumeDownloadButton = new JButton("Pause/Resume"); GBC gbcRIF = GBC.std().insets(0, 0, 20, 0).fill(GBC.HORIZONTAL); GBC gbcEol = GBC.eol(); GBC gbcEolFill = GBC.eol().fill(GBC.HORIZONTAL); GBC gbcEolFillI = GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 0); // background.add(windowTitle, gbcEolFill); // background.add(Box.createVerticalStrut(10), gbcEol); background.add(mapInfoLabel, gbcEolFill); background.add(Box.createVerticalStrut(20), gbcEol); background.add(title, gbcRIF); background.add(atlasMapsDone, gbcRIF); background.add(atlasPercent, gbcRIF); background.add(atlasTimeLeft, gbcEolFill); background.add(atlasProgressBar, gbcEolFillI); background.add(Box.createVerticalStrut(20), gbcEol); background.add(mapDownloadTitle, gbcRIF); background.add(mapDownloadElementsDone, gbcRIF); background.add(mapDownloadPercent, gbcRIF); background.add(mapDownloadTimeLeft, gbcEolFill); background.add(mapDownloadProgressBar, gbcEolFillI); background.add(Box.createVerticalStrut(20), gbcEol); background.add(mapCreation, gbcEol); background.add(mapCreationProgressBar, gbcEolFillI); background.add(Box.createVerticalStrut(10), gbcEol); JPanel infoPanel = new JPanel(new GridBagLayout()); GBC gbci = GBC.std().insets(0, 3, 3, 3); infoPanel.add(nrOfDownloadedBytes, gbci); infoPanel.add(nrOfDownloadedBytesValue, gbci.toggleEol()); infoPanel.add(nrOfCacheBytes, gbci.toggleEol()); infoPanel.add(nrOfCacheBytesValue, gbci.toggleEol()); infoPanel.add(nrOfDownloadedBytesPerSecond, gbci.toggleEol()); infoPanel.add(nrOfDownloadedBytesPerSecondValue, gbci.toggleEol()); infoPanel.add(activeDownloads, gbci.toggleEol()); infoPanel.add(activeDownloadsValue, gbci.toggleEol()); infoPanel.add(retryableDownloadErrors, gbci.toggleEol()); infoPanel.add(retryableDownloadErrorsValue, gbci.toggleEol()); infoPanel.add(permanentDownloadErrors, gbci.toggleEol()); infoPanel.add(permanentDownloadErrorsValue, gbci.toggleEol()); infoPanel.add(totalDownloadTime, gbci.toggleEol()); infoPanel.add(totalDownloadTimeValue, gbci.toggleEol()); JPanel bottomPanel = new JPanel(new GridBagLayout()); bottomPanel.add(infoPanel, GBC.std().gridheight(2).fillH()); bottomPanel.add(ignoreDlErrors, GBC.eol().anchor(GBC.EAST)); bottomPanel.add(statusLabel, GBC.eol().anchor(GBC.CENTER)); GBC gbcRight = GBC.std().anchor(GBC.SOUTHEAST).insets(5, 0, 0, 0); bottomPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL)); bottomPanel.add(abortAtlasCreationButton, gbcRight); bottomPanel.add(dismissWindowButton, gbcRight); bottomPanel.add(pauseResumeDownloadButton, gbcRight); bottomPanel.add(openProgramFolderButton, gbcRight); background.add(bottomPanel, gbcEolFillI); JPanel borderPanel = new JPanel(new GridBagLayout()); borderPanel.add(background, GBC.std().insets(10, 10, 10, 10).fill()); add(borderPanel, GBC.std().fill()); abortAtlasCreationButton.addActionListener(this); dismissWindowButton.addActionListener(this); openProgramFolderButton.addActionListener(this); pauseResumeDownloadButton.addActionListener(this); }