private void loadOfflineImages() {

    if (OfflineLoadManager.isLoading()) {
      return;
    }

    final int selectedZoomLevel = _targetZoomLevels[_comboTargetZoom.getSelectionIndex()];

    final int tileSize = _selectedMp.getTileSize();

    final int worldStartX = _offlineWorldStart.x;
    final int worldStartY = _offlineWorldStart.y;
    final int worldEndX = _offlineWorldEnd.x;
    final int worldEndY = _offlineWorldEnd.y;

    double worldX1 = Math.min(worldStartX, worldEndX);
    double worldX2 = Math.max(worldStartX, worldEndX);
    double worldY1 = Math.min(worldStartY, worldEndY);
    double worldY2 = Math.max(worldStartY, worldEndY);

    for (int zoomLevel = _validMapZoomLevel; zoomLevel <= selectedZoomLevel; zoomLevel++) {

      final int maxMapTileSize = _selectedMp.getMapTileSize(zoomLevel).width;

      final int areaPixelWidth = (int) (worldX2 - worldX1);
      final int areaPixelHeight = (int) (worldY2 - worldY1);

      final int numTileWidth = (int) Math.ceil((double) areaPixelWidth / (double) tileSize);
      final int numTileHeight = (int) Math.ceil((double) areaPixelHeight / (double) tileSize);

      int tilePosMinX = (int) Math.floor(worldX1 / tileSize);
      int tilePosMinY = (int) Math.floor(worldY1 / tileSize);
      int tilePosMaxX = tilePosMinX + numTileWidth;
      int tilePosMaxY = tilePosMinY + numTileHeight;

      // ensure tiles are within the map
      tilePosMinX = Math.max(0, tilePosMinX);
      tilePosMinY = Math.max(0, tilePosMinY);
      tilePosMaxX = Math.min(tilePosMaxX, maxMapTileSize);
      tilePosMaxY = Math.min(tilePosMaxY, maxMapTileSize);

      for (int tilePosX = tilePosMinX; tilePosX <= tilePosMaxX; tilePosX++) {
        for (int tilePosY = tilePosMinY; tilePosY <= tilePosMaxY; tilePosY++) {

          // create offline tile
          final Tile offlineTile = new Tile(_selectedMp, zoomLevel, tilePosX, tilePosY, null);
          offlineTile.setBoundingBoxEPSG4326();
          _selectedMp.doPostCreation(offlineTile);

          _offlineManager.addOfflineTile(_selectedMp, offlineTile);
        }
      }

      // set next zoom level, zoom into the map
      worldX1 *= 2;
      worldX2 *= 2;
      worldY1 *= 2;
      worldY2 *= 2;
    }

    // initialize progress bar
    _maxQueue = MP.getTileWaitingQueue().size();
    _progbarQueue.setMaximum(_maxQueue);
  }
  @Override
  public void tileEvent(final TileEventId tileEventId, final Tile tile) {

    final int tileWaitingQueueSize = MP.getTileWaitingQueue().size();

    _updateCounter[0]++;

    _display.asyncExec(
        new Runnable() {

          final int _runnableUpdateCounter = _updateCounter[0];

          public void run() {

            // check if a new runnable was created
            if (_runnableUpdateCounter != _updateCounter[0]) {
              // a new runnable was created
              return;
            }

            // check if widgets are disposed
            if (_comboMapProvider.isDisposed()) {
              return;
            }

            final float progress = _maxQueue - tileWaitingQueueSize;
            final float percent = _maxQueue == 0 ? 0 : progress * 100 / _maxQueue;

            final StringBuilder sb = new StringBuilder();
            sb.append(Integer.toString(tileWaitingQueueSize));
            sb.append(UI.DASH_WITH_SPACE);
            sb.append(_nf.format(percent));
            sb.append('%');

            _progbarQueue.setSelection((int) progress);
            _txtQueue.setText(sb.toString());

            /*
             * update state when all images are downloaded, it's possible that not all images
             * are downloaded when queue size is 0
             */
            if (tileWaitingQueueSize == 0) {

              final UIJob uiJob = new UIJob(_display, "update offline state") { // $NON-NLS-1$

                    final int _uiJobUpdateCounter = _updateCounter[0];

                    @Override
                    public IStatus runInUIThread(final IProgressMonitor monitor) {

                      // check if a new runnable was created
                      if (_uiJobUpdateCounter != _updateCounter[0]) {
                        // a new runnable was created
                        return Status.OK_STATUS;
                      }

                      getOfflineImageState();
                      enableControls(true);

                      _comboTargetZoom.setFocus();

                      return Status.OK_STATUS;
                    }
                  };
              uiJob.setSystem(true);
              uiJob.schedule(200);
            }
          }
        });
  }