/*
* @copyright 2012 Philip Warner
* @license GNU General Public License
*
* This file is part of Book Catalogue.
*
* Book Catalogue 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.
*
* Book Catalogue 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 Book Catalogue. If not, see <http://www.gnu.org/licenses/>.
*/
package com.eleybourn.bookcatalogue.booklist;
import java.util.ArrayList;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.eleybourn.bookcatalogue.CatalogueDBAdapter;
import com.eleybourn.bookcatalogue.EditObjectList;
import com.eleybourn.bookcatalogue.R;
import com.eleybourn.bookcatalogue.UniqueId;
import com.eleybourn.bookcatalogue.utils.HintManager;
import com.eleybourn.bookcatalogue.utils.Logger;
import com.eleybourn.bookcatalogue.utils.Utils;
import com.eleybourn.bookcatalogue.utils.ViewTagger;
/**
* Activity to edit the list of styles and enable/disable their presence in the
* styles menu.
*
* @author Philip Warner
*/
public class BooklistStylesActivity extends EditObjectList<BooklistStyle> {
/** Database connection */
private CatalogueDBAdapter mDb ;
/** The row being edited. Set when an individual style is edited */
private int mEditedRow;
/**
* Constructor
*/
public BooklistStylesActivity() {
super(null, R.layout.booklist_styles_edit_list, R.layout.booklist_styles_edit_row);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
try {
// Superclass will call getList() which needs DB, so create DB before calling superclass.
mDb = new CatalogueDBAdapter(this);
mDb.open();
// We want context menus to be available
registerForContextMenu(getListView());
super.onCreate(savedInstanceState);
this.setTitle(R.string.preferred_styles);
//mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, mDbHelper.fetchAllSeriesArray());
//((AutoCompleteTextView)this.findViewById(R.id.series)).setAdapter(mAdapter);
if (savedInstanceState == null)
HintManager.displayHint(this, R.string.hint_booklist_styles_editor, null);
Utils.initBackground(R.drawable.bc_background_gradient_dim, this, R.id.list_wrapper, false);
} catch (Exception e) {
Logger.logError(e);
}
}
/**
* Fix background
*/
@Override
public void onResume() {
super.onResume();
Utils.initBackground(R.drawable.bc_background_gradient_dim, this, R.id.list_wrapper, false);
}
/**
* Required by parent class since we do not pass a key for the intent to get the list.
*/
@Override
protected ArrayList<BooklistStyle> getList() {
ArrayList<BooklistStyle> styles = new ArrayList<BooklistStyle>();
// get the preferred styles first
for(BooklistStyle s: BooklistStyles.getAllStyles(mDb)) {
styles.add(s);
}
return styles;
}
/**
* Required, not used
*/
@Override
protected void onAdd(View v) {
}
/**
* Holder pattern object for list items
*
* @author Philip Warner
*/
private class Holder {
BooklistStyle style;
TextView name;
TextView groups;
TextView kind;
ImageView preferred;
}
@Override
protected void onSetupView(View target, BooklistStyle style) {
Holder h;
h = (Holder)ViewTagger.getTag(target, R.id.TAG_HOLDER);
if (h == null) {
// No holder found, create one
h = new Holder();
h.name = (TextView)target.findViewById(R.id.name);
h.groups = (TextView)target.findViewById(R.id.groups);
h.kind = (TextView)target.findViewById(R.id.kind);
h.preferred = (ImageView)target.findViewById(R.id.preferred);
// Tag relevant views
ViewTagger.setTag(target, R.id.TAG_HOLDER, h);
ViewTagger.setTag(h.preferred, R.id.TAG_HOLDER, h);
// Make it flash
//target.setBackgroundResource(android.R.drawable.list_selector_background);
// Handle clicks on the tick/cross
h.preferred.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Holder h = (Holder)ViewTagger.getTag(v, R.id.TAG_HOLDER);
boolean newPref = !h.style.isPreferred();
h.style.setPreferred(newPref);
if (newPref) {
h.preferred.setImageResource(R.drawable.btn_check_clipped);
} else {
h.preferred.setImageResource(android.R.drawable.ic_delete);
}
onListChanged();
}});
}
// Set the volatile fields in the holder
h.style = style;
h.name.setText(style.getDisplayName());
h.groups.setText(style.getGroupListDisplayNames());
if (style.isUserDefined())
h.kind.setText(R.string.user_defined);
else
h.kind.setText(R.string.builtin);
if (style.isPreferred()) {
h.preferred.setImageResource(R.drawable.btn_check_clipped);
} else {
h.preferred.setImageResource(android.R.drawable.ic_delete);
}
}
/**
* Class used for an item in the pseudo-context menu.
*
* Context menus don't seem to work for EditObject subclasses, perhaps because we consume click events.
*
* @author Philip Warner
*/
private class ContextItem implements CharSequence {
/** String for this item */
private String mString;
/** ID of this item */
private int mId;
/**
* Constructor
*
* @param stringId ID of String for this item
* @param id ID of this item
*/
ContextItem(int stringId, int id) {
mString = getString(stringId);
mId = id;
}
/** Return the associated string */
public String toString() {
return mString;
}
/** Get the ID */
public int getId() {
return mId;
}
/** Use the string object to provide the CharSequence implementation */
@Override
public char charAt(int index) {
return mString.charAt(index);
}
/** Use the string object to provide the CharSequence implementation */
@Override
public int length() {
return mString.length();
}
/** Use the string object to provide the CharSequence implementation */
@Override
public CharSequence subSequence(int start, int end) {
return mString.subSequence(start, end);
}
}
/**
* Use the RowClick ti present a pseudo context menu.
*/
@Override
protected void onRowClick(View target, final int position, final BooklistStyle style) {
// Build the array of menu items based on the style we are editing
final ArrayList<ContextItem> items = new ArrayList<ContextItem>();
if (style.isUserDefined()) {
items.add(new ContextItem(R.string.delete_style, R.id.MENU_DELETE_STYLE));
items.add(new ContextItem(R.string.edit_style, R.id.MENU_EDIT_STYLE));
}
items.add(new ContextItem(R.string.clone_style, R.id.MENU_CLONE_STYLE));
// Turn the list into an array
CharSequence[] csa = new CharSequence[items.size()];
for(int i = 0 ; i < items.size(); i++)
csa[i] = items.get(i);
// Show the dialog
final AlertDialog dialog = new AlertDialog.Builder(getLayoutInflater().getContext()).setItems(csa, new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
switch(items.get(which).getId()) {
case R.id.MENU_DELETE_STYLE:
style.deleteFromDb(mDb);
// Refresh the list
setList(getList());
dialog.dismiss();
return;
case R.id.MENU_EDIT_STYLE:
editStyle(position, style, false);
dialog.dismiss();
return;
case R.id.MENU_CLONE_STYLE:
editStyle(position, style, true);
dialog.dismiss();
return;
}
}}).create();
dialog.show();
}
/**
* Edit the passed style, saving its details locally. Optionally for a clone.
*
* @param position Position in list
* @param style Actual style
* @param alwaysClone Force a clone, even if its already user-defined
*/
private void editStyle(int position, BooklistStyle style, boolean alwaysClone) {
Intent i = new Intent(this, BooklistStylePropertiesActivity.class);
// Save the current row
mEditedRow = position;
if (!style.isUserDefined() || alwaysClone) {
try {
style = style.getClone();
style.setRowId(0);
style.setName(style.getDisplayName());
} catch (Exception e) {
Logger.logError(e);
Toast.makeText(this, R.string.unexpected_error, Toast.LENGTH_LONG).show();
return;
}
}
i.putExtra(BooklistStylePropertiesActivity.KEY_STYLE, style);
startActivityForResult(i, UniqueId.ACTIVITY_BOOKLIST_STYLE);
}
@Override
protected void onListChanged() {
// Save the order whenever the list is modified.
BooklistStyles.SaveMenuOrder(mList);
};
/** Handle the results from a called activity */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case UniqueId.ACTIVITY_BOOKLIST_STYLE:
handleStyleResult(data);
break;
}
}
/**
* Called after a style has been edited.
*
* @param data Data passed to onActivityResult
*/
private void handleStyleResult(Intent data) {
// Make sure we have a style. If not, the user must have cancelled.
if (data == null || !data.hasExtra(BooklistStylePropertiesActivity.KEY_STYLE))
return;
try {
BooklistStyle result = (BooklistStyle) data.getSerializableExtra(BooklistStylePropertiesActivity.KEY_STYLE);
if (result == null) {
// Style was deleted. Refresh.
setList(getList());
} else if (mEditedRow < 0) {
// Was added. So put at top and mark as preferred
result.setPreferred(true);
mList.add(0, result);
BooklistStyles.SaveMenuOrder(mList);
} else {
BooklistStyle origStyle = mList.get(mEditedRow);
if (origStyle.getRowId() != result.getRowId()) {
if (!origStyle.isUserDefined()) {
// Working on a clone of a builtin style
if (origStyle.isPreferred()) {
// Replace the original row with the new one
mList.set(mEditedRow, result);
// And demote the original
origStyle.setPreferred(false);
mList.add(origStyle);
} else {
// Try to put it directly after original
mList.add(mEditedRow, result);
}
} else {
// A clone of an original. Put it directly after the original
mList.add(mEditedRow, result);
}
if (result.isPreferred())
BooklistStyles.SaveMenuOrder(mList);
} else {
mList.set(mEditedRow, result);
}
}
setList(mList);
} catch (Exception e) {
Logger.logError(e);
// Do our best to recover
setList(getList());
}
}
/**
* Cleanup
*/
@Override
public void onDestroy() {
super.onDestroy();
if (mDb != null)
mDb.close();
}
}