org.i_chera.wolfensteineditor.fragments.LevelFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.i_chera.wolfensteineditor.fragments.LevelFragment.java

Source

package org.i_chera.wolfensteineditor.fragments;

/*
 * Wolfenstein 3D editor for Android
 * Copyright (C) 2015  Ioan Chera
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.ActionBarDrawerToggle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;

import org.i_chera.wolfensteineditor.Global;
import org.i_chera.wolfensteineditor.MainActivity;
import org.i_chera.wolfensteineditor.Palette;
import org.i_chera.wolfensteineditor.R;
import org.i_chera.wolfensteineditor.document.Document;
import org.i_chera.wolfensteineditor.document.LevelContainer;
import org.i_chera.wolfensteineditor.ui.HXScrollView;
import org.i_chera.wolfensteineditor.ui.ScrollViewListener;
import org.i_chera.wolfensteineditor.ui.VXScrollView;
import org.i_chera.wolfensteineditor.ui.VisibilityGrid;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;

/**
 * Created by ioan_chera on 15.01.2015.
 */
public class LevelFragment extends SwitchableFragment
        implements AdapterView.OnItemClickListener, LevelContainer.Observer, CompoundButton.OnCheckedChangeListener,
        View.OnClickListener, View.OnTouchListener, VisibilityGrid.Delegate, ScrollViewListener {
    static final String TAG = "LevelFragment";

    private static final String EXTRA_CURRENT_LEVEL = "currentLevel";
    private static final String EXTRA_SCROLL_X = "scrollX";
    private static final String EXTRA_SCROLL_Y = "scrollY";
    private static final String EXTRA_SCROLL_LOCK = "scrollLock";
    private static final String EXTRA_CURRENT_WALL_CHOICE = "currentWallChoice";
    private int mCurrentLevel;
    private int mCurrentWallChoice;

    // widgets
    private RelativeLayout mGridLayout;
    private VXScrollView mVerticalScroll;
    private HXScrollView mHorizontalScroll;
    private ListView mWallList;
    private LinearLayout mLeftDrawer;
    private CheckBox mScrollLockCheck;
    private RelativeLayout mCentralContent;
    private ActionBarDrawerToggle mDrawerToggle;

    // display
    private int mTileSize;
    private ImageView[][] mTileViews;
    private Point mViewportSize;
    private VisibilityGrid mVisGrid;

    // other data
    private JSONArray mWallChoices;
    private boolean mDeferAddObserver;

    // Item click control
    private boolean mPressDown;

    // Drawer
    private DrawerLayout mDrawerLayout;
    private boolean mDoNotPropagateScroll;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle b = getActualState(savedInstanceState);
        if (b != null) {
            mCurrentLevel = b.getInt(EXTRA_CURRENT_LEVEL);
        }

        mTileSize = (int) (48 * Global.getScale());
        setHasOptionsMenu(true);

        readWallChoices();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.level, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        switch (item.getItemId()) {
        case R.id.action_prev:
            mCurrentLevel--;
            updateGridLayout();
            return true;
        case R.id.action_next:
            mCurrentLevel++;
            updateGridLayout();
            return true;
        default:
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, final Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_level, container, false);
        mDrawerLayout = (DrawerLayout) v;

        mGridLayout = (RelativeLayout) v.findViewById(R.id.grid_layout);
        mVerticalScroll = (VXScrollView) v.findViewById(R.id.vertical_scroll);
        mHorizontalScroll = (HXScrollView) v.findViewById(R.id.horizontal_scroll);
        mWallList = (ListView) v.findViewById(R.id.wall_list);
        mLeftDrawer = (LinearLayout) v.findViewById(R.id.left_drawer);
        mScrollLockCheck = (CheckBox) v.findViewById(R.id.scroll_lock_check);
        mCentralContent = (RelativeLayout) v.findViewById(R.id.central_content);

        mHorizontalScroll.setScrollingEnabled(false);
        mHorizontalScroll.setOnTouchListener(this);
        mVerticalScroll.setOnTouchListener(this);
        mVerticalScroll.setAlwaysInterceptMove(true);
        mCentralContent.setOnTouchListener(this);
        //        mVerticalScroll.setClickable(true);
        //        mHorizontalScroll.setOnClickListener(this);
        mHorizontalScroll.setScrollViewListener(this);
        mVerticalScroll.setScrollViewListener(this);
        mScrollLockCheck.setOnClickListener(this);
        mScrollLockCheck.setOnCheckedChangeListener(this);

        int tColor = MainActivity.FLOOR_COLOUR;
        tColor = Color.argb(200, Color.red(tColor), Color.green(tColor), Color.blue(tColor));
        mScrollLockCheck.setBackgroundColor(tColor);

        mLeftDrawer.setBackgroundColor(MainActivity.FLOOR_COLOUR);
        mWallList.setAdapter(new WallListAdapter());
        mWallList.setOnItemClickListener(this);

        mGridLayout.getLayoutParams().width = mGridLayout.getLayoutParams().height = LevelContainer.MAPSIZE
                * mTileSize;

        ViewTreeObserver observer = mVerticalScroll.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            // Can't get rid of warning otherwise. Can't use the recommended
            // function here
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                mVerticalScroll.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                mViewportSize = new Point(mVerticalScroll.getMeasuredWidth(), mVerticalScroll.getMeasuredHeight());
                Bundle b = getActualState(savedInstanceState);
                if (b != null) {
                    mHorizontalScroll.scrollTo(b.getInt(EXTRA_SCROLL_X), 0);
                    mVerticalScroll.scrollTo(0, b.getInt(EXTRA_SCROLL_Y));
                    mCurrentWallChoice = b.getInt(EXTRA_CURRENT_WALL_CHOICE);
                    boolean checked = b.getBoolean(EXTRA_SCROLL_LOCK);
                    mScrollLockCheck.setChecked(checked);
                }
                updateData();
            }
        });

        return v;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mDrawerToggle = new ActionBarDrawerToggle(getActivity(), mDrawerLayout, R.drawable.ic_drawer,
                R.string.contentdesc_open_level_drawer, R.string.contentdesc_close_level_drawer);
        mDrawerLayout.setDrawerListener(mDrawerToggle);
        ((ActionBarActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        ((ActionBarActivity) getActivity()).getSupportActionBar().setHomeButtonEnabled(true);
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public void saveState(Bundle target) {
        target.putInt(EXTRA_CURRENT_LEVEL, mCurrentLevel);
        target.putInt(EXTRA_SCROLL_X, mHorizontalScroll.getScrollX());
        target.putInt(EXTRA_SCROLL_Y, mVerticalScroll.getScrollY());
        target.putInt(EXTRA_CURRENT_WALL_CHOICE, mCurrentWallChoice);
        target.putBoolean(EXTRA_SCROLL_LOCK, mScrollLockCheck.isChecked());
    }

    @Override
    public void onResume() {
        super.onResume();
        if (Document.getInstance().isLoaded()) {
            Log.i(TAG, "Adding observer");
            Document.getInstance().getLevels().addObserver(this);
        } else
            mDeferAddObserver = true;
    }

    @Override
    public void onPause() {
        super.onPause();
        if (Document.getInstance().isLoaded()) {
            Log.i(TAG, "Removing observer");
            Document.getInstance().getLevels().removeObserver(this);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mGridLayout.removeAllViews();
    }

    public void updateData() {
        if (mGridLayout == null)
            return; // will be done anyway upon creation
        if (Document.getInstance().isLoaded() && mDeferAddObserver) {
            Log.i(TAG, "Adding deferred observer");
            mDeferAddObserver = false;
            Document.getInstance().getLevels().addObserver(this);
        }
        updateGridLayout();
        ((WallListAdapter) mWallList.getAdapter()).notifyDataSetChanged();
    }

    private void updateGridLayout() {
        Document document = Document.getInstance();
        if (!document.isLoaded())
            return;

        if (mCurrentLevel < 0)
            mCurrentLevel = 0;
        else if (mCurrentLevel >= LevelContainer.NUMMAPS)
            mCurrentLevel = LevelContainer.NUMMAPS - 1;

        if (getActivity() instanceof ActionBarActivity)
            ((ActionBarActivity) getActivity()).getSupportActionBar()
                    .setTitle(document.getLevels().getLevelName(mCurrentLevel));

        int ceilingColour = Palette.WL6[LevelContainer.getCeilingColour(mCurrentLevel)];

        mGridLayout.setBackgroundColor(ceilingColour);

        Point viewportPosition = new Point(mHorizontalScroll.getScrollX(), mVerticalScroll.getScrollY());
        Log.i(TAG, "Point: " + viewportPosition);
        Rect viewportRect = new Rect(viewportPosition.x, viewportPosition.y, viewportPosition.x + mViewportSize.x,
                viewportPosition.y + mViewportSize.y);
        mGridLayout.removeAllViews();
        if (mVisGrid == null)
            mVisGrid = new VisibilityGrid();

        mVisGrid.create(new Point(LevelContainer.MAPSIZE * mTileSize, LevelContainer.MAPSIZE * mTileSize),
                viewportRect, mTileSize, this);
    }

    ////////////////////////////////////////////////////////////////////////////
    // View.OnTouchListener
    ////////////////////////////////////////////////////////////////////////////

    private void hitTileOnView(MotionEvent event, int k, boolean makeSound) {
        int i, j;
        j = (int) (MotionEventCompat.getX(event, k) + mHorizontalScroll.getScrollX()) / mTileSize;
        i = (int) (MotionEventCompat.getY(event, k) + mVerticalScroll.getScrollY()) / mTileSize;
        if (j >= 0 && j < LevelContainer.MAPSIZE && i >= 0 && i < LevelContainer.MAPSIZE)
            if (mTileViews[i][j] != null) {
                onClick(mTileViews[i][j]);
                if (makeSound)
                    mTileViews[i][j].playSoundEffect(SoundEffectConstants.CLICK);
            }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = MotionEventCompat.getActionMasked(event);

        if (mScrollLockCheck.isChecked()) {
            // We also need to avoid interfering with the left drawer
            // Cancel DOWN if too left. But don't cancel MOVE if too left
            if (v == mCentralContent && !mDrawerLayout.isDrawerVisible(Gravity.LEFT)) {
                int count = MotionEventCompat.getPointerCount(event);
                for (int k = 0; k < count; ++k) {
                    if (action != MotionEvent.ACTION_DOWN && action != MotionEventCompat.ACTION_POINTER_DOWN
                            || MotionEventCompat.getX(event, k) >= mTileSize) {
                        hitTileOnView(event, k, false);
                    }
                }
                return true;
            }
            return false;
        }
        if (v == mVerticalScroll) {
            // LOL trickery needed to scroll the below horiz view while this is
            // getting operated on
            if (mVerticalScroll.isScrollable() && mDoNotPropagateScroll == false) {
                mHorizontalScroll.setScrollingEnabled(true);
                mHorizontalScroll.dispatchTouchEvent(event);
                mHorizontalScroll.setScrollingEnabled(false);
            }
            if (action == MotionEvent.ACTION_DOWN || action == MotionEventCompat.ACTION_POINTER_DOWN) {
                mPressDown = true;
                //return true;
            } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) {
                if (mPressDown) {
                    hitTileOnView(event, (event.getAction()
                            & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT,
                            true);
                }
            }
            return false;
        }
        return false;
    }

    ////////////////////////////////////////////////////////////////////////////
    // ScrollViewListener
    ////////////////////////////////////////////////////////////////////////////

    @Override
    public void onScrollChanged(FrameLayout scrollView, int x, int y, int oldx, int oldy) {
        if (scrollView == mHorizontalScroll || scrollView == mVerticalScroll) {
            if (mPressDown)
                mPressDown = false; // cancel any pressed thing

            Document document = Document.getInstance();
            if (document == null || !document.isLoaded())
                return;
            if (mVisGrid == null)
                return;

            oldx /= mTileSize;
            x /= mTileSize;
            oldy /= mTileSize;
            y /= mTileSize;

            x = Global.boundValue(x, 0, LevelContainer.MAPSIZE - 1);
            y = Global.boundValue(y, 0, LevelContainer.MAPSIZE - 1);
            oldx = Global.boundValue(oldx, 0, LevelContainer.MAPSIZE - 1);
            oldy = Global.boundValue(oldy, 0, LevelContainer.MAPSIZE - 1);

            if (x != oldx)
                mVisGrid.columnUpdate(oldx, x);
            else if (y != oldy)
                mVisGrid.rowUpdate(oldy, y);
        }

    }

    @Override
    public void onOverScrolled(FrameLayout scrollView, int scrollX, int scrollY, boolean clampedX,
            boolean clampedY) {
        // unreliable
        //      if(scrollView == mHorizontalScroll || scrollView == mVerticalScroll)
        //      {
        //         mPressDown = false;
        //      }
    }

    ////////////////////////////////////////////////////////////////////////////
    // VisibilityGrid.Delegate
    ////////////////////////////////////////////////////////////////////////////

    @Override
    public void clearVisTiles() {
        if (mTileViews == null)
            mTileViews = new ImageView[LevelContainer.MAPSIZE][LevelContainer.MAPSIZE];
        else
            for (int i = 0; i < LevelContainer.MAPSIZE; ++i)
                for (int j = 0; j < LevelContainer.MAPSIZE; ++j)
                    mTileViews[i][j] = null;
    }

    @Override
    public void createVisTile(int i, int j) {
        int ms = LevelContainer.MAPSIZE - 1;
        if (!Global.inBounds(i, 0, ms) || !Global.inBounds(j, 0, ms)) {
            //         Log.d(TAG, "Refused create " + i + " " + j);
            return;
        }
        if (getActivity() == null)
            return;
        mTileViews[i][j] = new ImageView(getActivity());
        mTileViews[i][j].setId(i * LevelContainer.MAPSIZE + j);
        RelativeLayout.LayoutParams rllp = new RelativeLayout.LayoutParams(mTileSize, mTileSize);
        rllp.leftMargin = j * mTileSize;
        rllp.topMargin = i * mTileSize;
        mTileViews[i][j].setLayoutParams(rllp);
        //      mTileViews[i][j].setOnClickListener(this);
        //      mTileViews[i][j].setOnTouchListener(this);
        //      mTileViews[i][j].setClickable(true);
        mGridLayout.addView(mTileViews[i][j]);

        updateGraphics(mTileViews[i][j], j, i);
    }

    @Override
    public void moveVisTile(int i1, int j1, int i2, int j2) {
        int ms = LevelContainer.MAPSIZE - 1;
        if (!Global.inBounds(i2, 0, ms) || !Global.inBounds(j2, 0, ms)) {
            //         Log.d(TAG, "Refused move " + i1 + " " + j1 + " " + i2 + " " + j2);
            return;
        }
        ImageView iv = null;
        if (Global.inBounds(i1, 0, ms) && Global.inBounds(j1, 0, ms))
            iv = mTileViews[i1][j1];
        // let's be careful, okay?
        if (iv == null) {
            // either just create or update
            iv = mTileViews[i2][j2];
            if (iv == null)
                createVisTile(i2, j2);
            else
                updateGraphics(iv, j2, i2);
        } else {
            // delete or move
            ImageView iv2 = mTileViews[i2][j2];
            if (iv2 == null) {
                RelativeLayout.LayoutParams rllp = (RelativeLayout.LayoutParams) iv.getLayoutParams();
                rllp.leftMargin = j2 * mTileSize;
                rllp.topMargin = i2 * mTileSize;
                mTileViews[i1][j1] = null;
                mTileViews[i2][j2] = iv;
                mTileViews[i2][j2].setId(i2 * LevelContainer.MAPSIZE + j2);

                //            mGridLayout.invalidate(new Rect(rllp.leftMargin, rllp.topMargin,
                //                  rllp.leftMargin + mTileSize, rllp.topMargin + mTileSize));
                updateGraphics(iv, j2, i2);
            } else {
                // delete the old one
                mGridLayout.removeView(iv);
                mTileViews[i1][j1] = null;
                updateGraphics(iv2, j2, i2);
            }
        }
    }

    @Override
    public void finalizeMoveVisTiles() {
        mGridLayout.requestLayout();
    }

    private void updateGraphics(ImageView iv, int x, int y) {
        Document document = Document.getInstance();
        //      short[][] level = document.getLevels().getLevel(mCurrentLevel);

        //       short[] wallplane = level[0];
        //       short[] actorplane = level[1];

        int cell = document.getLevels().getTile(mCurrentLevel, 0, x, y);

        if (!setBitmapFromMapValue(document, iv, cell)) {
            cell = Global.getActorSpriteMap().get(document.getLevels().getTile(mCurrentLevel, 1, x, y), -1);
            if (cell == -1)
                iv.setImageBitmap(null);
            else
                iv.setImageBitmap(document.getVSwap().getSpriteBitmap(cell));
        }
    }

    private boolean setBitmapFromMapValue(Document document, ImageView iv, int cell) {
        if (cell >= 90 && cell <= 100 && cell % 2 == 0) {
            iv.setImageResource(R.drawable.door_vertical);
            return true;
        } else if (cell >= 91 && cell <= 101 && cell % 2 == 1) {
            iv.setImageResource(R.drawable.door_horizontal);
            return true;
        } else {
            int texture = 2 * (cell - 1);
            if (texture >= 0 && texture < document.getVSwap().getSpriteStart()) {
                iv.setImageBitmap(document.getVSwap().getWallBitmap(texture));
                return true;
            }
        }
        return false;
    }

    ////////////////////////////////////////////////////////////////////////////
    // Wall list adapter
    ////////////////////////////////////////////////////////////////////////////

    private class WallListAdapter extends BaseAdapter {
        private Document mDocument;

        WallListAdapter() {
            mDocument = Document.getInstance();
        }

        @Override
        public int getCount() {
            return mWallChoices != null ? mWallChoices.length() : 0;
        }

        @Override
        public Object getItem(int position) {
            return mWallChoices != null ? mWallChoices.optJSONObject(position) : null;
        }

        @Override
        public long getItemId(int position) {
            if (mWallChoices == null)
                return -1;
            else {
                JSONObject object = (JSONObject) getItem(position);
                if (object != null)
                    return object.optLong("id");
                else
                    return -1;
            }
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView item;
            if (convertView == null) {
                item = new ImageView(getActivity());
                AbsListView.LayoutParams alvlp = new AbsListView.LayoutParams((int) (48 * Global.getScale()),
                        (int) (48 * Global.getScale()));
                item.setLayoutParams(alvlp);
                item.setPadding(0, (int) (5 * Global.getScale()), 0, (int) (5 * Global.getScale()));
            } else
                item = (ImageView) convertView;
            int index = (int) getItemId(position);
            if (mCurrentWallChoice == position)
                item.setBackgroundResource(R.drawable.frame_selection);
            else
                item.setBackgroundResource(0);
            if (!mDocument.isLoaded() || !setBitmapFromMapValue(mDocument, item, index))
                item.setImageDrawable(null);

            return item;
        }

    }

    ////////////////////////////////////////////////////////////////////////////

    void readWallChoices() {
        // Called upon creation
        InputStream is = null;
        StringBuilder sb;
        try {
            sb = new StringBuilder(5000);
            is = getActivity().getAssets().open("wall_choices.json");

            byte[] buffer = new byte[512];
            while (is.read(buffer) > 0)
                sb.append(new String(buffer, "UTF-8"));

        } catch (IOException e) {
            e.printStackTrace();
            return;
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        String jsonString = sb.toString();
        JSONArray array = null;
        try {
            array = new JSONArray(jsonString);
        } catch (JSONException e) {
            e.printStackTrace();
            Global.showErrorAlert(getActivity(), "Internal error", "Failed to read the wall choice list!");
            return;
        }
        if (array != null) {
            mWallChoices = array;
            Global.boundValue(mCurrentWallChoice, 0, mWallChoices.length() - 1);
        }
    }

    ////////////////////////////////////////////////////////////////////////////

    @Override
    public void onClick(View v) {
        if (v instanceof ImageView) {
            if (mWallChoices == null || !Global.inBounds(mCurrentWallChoice, 0, mWallChoices.length() - 1))
                return;
            JSONObject obj = mWallChoices.optJSONObject(mCurrentWallChoice);
            if (obj == null)
                return;
            //         Log.i(TAG, "Clicked " + v.getId() % 64 + " " + v.getId() / 64 + " real " +
            //            ((RelativeLayout.LayoutParams)v.getLayoutParams()).leftMargin / v.getWidth() + " " +
            //            ((RelativeLayout.LayoutParams)v.getLayoutParams()).topMargin / v.getHeight());
            Document.getInstance().getLevels().setTile(mCurrentLevel, 0, v.getId(), (short) obj.optInt("id"));
        }
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (buttonView == mScrollLockCheck)
            mVerticalScroll.setScrollingEnabled(!isChecked);
    }

    @Override
    public void observeLocalChange(int level, int plane, int i, short value) {
        int x = i % LevelContainer.MAPSIZE;
        int y = i / LevelContainer.MAPSIZE;
        if (mTileViews[y][x] != null && level == mCurrentLevel)
            updateGraphics(mTileViews[y][x], x, y);
        else if (level == mCurrentLevel) {
            mHorizontalScroll.smoothScrollTo(x * mTileSize - mViewportSize.x / 2, 0);
            mVerticalScroll.smoothScrollTo(0, y * mTileSize - mViewportSize.y / 2);
        }
    }

    @Override
    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        if (arg0 == mWallList) {
            mCurrentWallChoice = arg2;
            ((WallListAdapter) mWallList.getAdapter()).notifyDataSetChanged();
        }
    }

    @Override
    public boolean handleBackButton() {
        if (Document.getInstance().isLoaded() && Global.inBounds(mCurrentLevel, 0, LevelContainer.NUMMAPS - 1)
                && Document.getInstance().getLevels().hasUndo(mCurrentLevel)) {
            Document.getInstance().getLevels().undo(mCurrentLevel);
            return true;
        }
        return false;
    }

}