/*
GNU General Public License
CacheWolf is a software for PocketPC, Win and Linux that
enables paperless caching.
It supports the sites geocaching.com and opencaching.de
Copyright (C) 2006 CacheWolf development team
See http://www.cachewolf.de/ for more information.
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; version 2 of the License.
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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package CacheWolf.controls;
import CacheWolf.MainForm;
import CacheWolf.Preferences;
import CacheWolf.Profile;
import CacheWolf.database.CacheDB;
import CacheWolf.database.CacheHolder;
import CacheWolf.database.CacheHolderDetail;
import CacheWolf.utils.Files;
import CacheWolf.utils.MyLocale;
import ewe.io.File;
import ewe.sys.Handle;
import ewe.ui.CellConstants;
import ewe.ui.CheckBoxGroup;
import ewe.ui.Event;
import ewe.ui.Form;
import ewe.ui.FormBase;
import ewe.ui.Gui;
import ewe.ui.ProgressBarForm;
import ewe.ui.mCheckBox;
import ewe.ui.mLabel;
import ewe.util.Iterator;
import ewe.util.Vector;
/**
* This class moves or copies the database files of selected caches from one directory to another.
* It provides also the possibility to delete cachefiles.
*/
public class DataMover {
private CacheDB srcDB;
public DataMover() {
srcDB = MainForm.profile.cacheDB;
}
public void deleteCaches(boolean ask) {
int mode = 1;
if (ask) {
mode = confirmAction(251, "All waypoints will be deleted");
if (mode == -1) {
return;
}
}
processCaches(new Deleter(MyLocale.getMsg(143, "Delete")), mode);
MainForm.profile.saveIndex(Profile.SHOW_PROGRESS_BAR);
}
public void copyCaches(String targetDir) {
if (targetDir.equals(MainForm.profile.dataDir) || targetDir.length() == 0)
return;
int mode = confirmAction(253, "All waypoints will be copied");
if (mode == -1) {
return;
}
Profile dstProfile = new Profile();
dstProfile.dataDir = targetDir;
File ftest = new File(dstProfile.dataDir + "index.xml");
if (ftest.exists()) {
dstProfile.readIndex(null, dstProfile.dataDir);
}
processCaches(new Copier(MyLocale.getMsg(141, "Copy"), dstProfile), mode);
dstProfile.saveIndex(Profile.SHOW_PROGRESS_BAR);
}
public void moveCaches(String targetDir) {
if (targetDir.equals(MainForm.profile.dataDir) || targetDir.length() == 0)
return;
int mode = confirmAction(252, "All waypoints will be moved");
if (mode == -1) {
return;
}
Profile dstProfile = new Profile();
dstProfile.dataDir = targetDir;
File ftest = new File(dstProfile.dataDir + "index.xml");
if (ftest.exists()) {
dstProfile.readIndex(null, dstProfile.dataDir);
}
processCaches(new Mover(MyLocale.getMsg(142, "Move"), dstProfile), mode);
dstProfile.saveIndex(Profile.SHOW_PROGRESS_BAR);
MainForm.profile.saveIndex(Profile.SHOW_PROGRESS_BAR);
}
/**
* Shows the message before copying/moving/deleting waypoints.
* It returns the mode selected by the user.
* 0 means all visible
* 1 means all ticked
* 2 means all visible and ticked cache
* -1 means the user has pressed `Cancel' and no action has to be taken.
*
* @return mode selected by the user
*/
private int confirmAction(int actionTextNr, String defaultValue) {
DataMoverForm cpf = new DataMoverForm(makeTickedText(), makeVisibleText(), makeVisibleTickedText(), MyLocale.getMsg(actionTextNr, defaultValue));
int dialogResult = cpf.execute(null, Gui.CENTER_FRAME);
if (dialogResult != FormBase.IDOK) {
return -1;
}
int mode = cpf.getMode();
return mode;
}
private String makeTickedText() {
int size = srcDB.size();
int countMainWP = 0;
int countAddiWP = 0;
// Count the number of caches to move/delete/copy
for (int i = 0; i < size; i++) {
if (srcDB.get(i).isChecked && !srcDB.get(i).isAddiWpt()) {
countMainWP++;
} else if (srcDB.get(i).isChecked && srcDB.get(i).isAddiWpt()) {
countAddiWP++;
}
}
return MyLocale.getMsg(255, "All ticked") + " (" + countMainWP + ' ' + MyLocale.getMsg(257, "Main") + ", " + countAddiWP + MyLocale.getMsg(258, " Addi") + ')';
}
private String makeVisibleText() {
int size = srcDB.size();
int countMainWP = 0;
int countAddiWP = 0;
// Count the number of caches to move/delete/copy
for (int i = 0; i < size; i++) {
if (srcDB.get(i).isVisible() && !srcDB.get(i).isAddiWpt()) {
countMainWP++;
} else if (srcDB.get(i).isVisible() && srcDB.get(i).isAddiWpt()) {
countAddiWP++;
}
}
return MyLocale.getMsg(254, "All visible") + " (" + countMainWP + ' ' + MyLocale.getMsg(257, "Main") + ", " + countAddiWP + MyLocale.getMsg(258, " Addi") + ')';
}
private String makeVisibleTickedText() {
int size = srcDB.size();
int countMainWP = 0;
int countAddiWP = 0;
// Count the number of caches to move/delete/copy
for (int i = 0; i < size; i++) {
if (srcDB.get(i).isVisible() && srcDB.get(i).isChecked && !srcDB.get(i).isAddiWpt()) {
countMainWP++;
} else if (srcDB.get(i).isVisible() && srcDB.get(i).isChecked && srcDB.get(i).isAddiWpt()) {
countAddiWP++;
}
}
return MyLocale.getMsg(256, "All visible and ticked") + " (" + countMainWP + ' ' + MyLocale.getMsg(257, "Main") + ", " + countAddiWP + MyLocale.getMsg(258, " Addi") + ')';
}
/**
* This function carries out the copy/delete/move with a progress bar.
* The Executor class defines what operation is to be carried out.
* mode defines if visible/marked or visible and markes caches are be processed.
* 0 means all visible
* 1 means all marked
* 2 means all visible and marked
*
* @param exec
* @param tickeOnly
* if set to <code>true</true> only caches with the checked-flag will be handled.
*/
private void processCaches(Executor exec, int mode) {
// First empty the cache so that the correct cache details are on disk
CacheHolder.saveAllModifiedDetails();
int size = srcDB.size();
int count = 0;
// Count the number of caches to move/delete/copy
// and remember the index of the files process, makes it a little bit easier
boolean processSet[] = new boolean[size];
for (int i = 0; i < size; i++) {
switch (mode) {
case 0:
if (srcDB.get(i).isVisible()) {
count++;
processSet[i] = true;
}
break;
case 1:
if (srcDB.get(i).isChecked) {
count++;
processSet[i] = true;
}
break;
case 2:
if (srcDB.get(i).isVisible() && srcDB.get(i).isChecked) {
count++;
processSet[i] = true;
}
break;
}
}
myProgressBarForm pbf = new myProgressBarForm();
Handle h = new Handle();
pbf.setTask(h, exec.title);
pbf.exec();
int nProcessed = 0;
// Now do the actual work
for (int i = size - 1; i >= 0; i--) {
CacheHolder srcHolder = srcDB.get(i);
if (processSet[i]) {
h.progress = ((float) nProcessed++) / (float) count;
h.changed();
//Now do the copy/delete/move of the cache
exec.doIt(i, srcHolder);
}
if (pbf.isClosed)
break;
}
pbf.exit(0);
}
class myProgressBarForm extends ProgressBarForm {
boolean isClosed = false;
protected boolean canExit(int exitCode) {
isClosed = true;
return true;
}
}
//////////////////////////////////////////////////////////////////////
// Executor
//////////////////////////////////////////////////////////////////////
private abstract class Executor {
String title;
Profile dstProfile;
public abstract void doIt(int i, CacheHolder srcHolder);
protected void deleteCacheFiles(Vector fileNames, String dataDir) {
for (Iterator ite = fileNames.iterator(); ite.hasNext();) {
String fileName = (String) ite.next();
deleteFile(fileName, dataDir);
}
}
protected void copyCacheFiles(Vector fileNames, String srcDataDir, String dstDataDir) {
for (Iterator ite = fileNames.iterator(); ite.hasNext();) {
String fileName = (String) ite.next();
Files.copy(srcDataDir + fileName, dstDataDir + fileName);
}
}
protected void moveCacheFiles(Vector fileNames, String srcDataDir, String dstDataDir) {
for (Iterator ite = fileNames.iterator(); ite.hasNext();) {
String fileName = (String) ite.next();
(new File(srcDataDir + fileName)).move(new File(dstDataDir + fileName));
}
}
protected Vector getFileNames(CacheHolder ch) {
Vector fileNames = new Vector();
CacheHolderDetail chD = ch.getDetails();
//chD.images : Description Images, Spoiler Images
for (int i = 0; i < chD.images.size(); i++) {
fileNames.add(chD.images.get(i).getFilename());
}
//chD.logImages
for (int i = 0; i < chD.logImages.size(); i++) {
fileNames.add(chD.logImages.get(i).getFilename());
}
//chD.userImages
for (int i = 0; i < chD.userImages.size(); i++) {
fileNames.add(chD.userImages.get(i).getFilename());
}
// Cache XML
fileNames.add(ch.getCode().toLowerCase() + ".xml");
return fileNames;
}
private void deleteFile(String fileName, String dataDir) {
// MainForm.profile.dataDir + FileName + ".xml"
String FileName = dataDir + fileName;
boolean exists = (new File(FileName)).exists();
if (exists) {
(new File(FileName)).delete();
}
boolean exists2 = (new File(FileName.toLowerCase())).exists();
if (exists2) {
(new File(FileName.toLowerCase())).delete();
}
}
}
public class Deleter extends Executor {
Deleter(String title) {
this.title = title;
}
public void doIt(int i, CacheHolder srcHolder) {
deleteCacheFiles(getFileNames(srcHolder), MainForm.profile.dataDir);
srcDB.removeElementAt(i);
}
}
private class Copier extends Executor {
Copier(String title, Profile dstProfile) {
this.title = title;
this.dstProfile = dstProfile;
}
public void doIt(int i, CacheHolder srcHolder) {
srcHolder.saveCacheDetails();
Vector srcFiles = getFileNames(srcHolder);
deleteCacheFiles(srcFiles, dstProfile.dataDir);
copyCacheFiles(srcFiles, MainForm.profile.dataDir, dstProfile.dataDir);
// does cache exists in destDB ?
// Update database
//*wall* when copying addis without their maincache, the maincache in the srcDB will be set to null on saving the dstProfile later.
//Therefore it will be shown twice in the cachelist.
//To prevent this, addis will be cloned and to save memory only addis will be clones.
//TODO clone addis only when the maincache will not be copied.
// if (srcHolder.isAddiWpt()){
// try {
// srcHolder = (CacheHolder) srcHolder.clone();
// } catch (CloneNotSupportedException e) {
// //ignore, CacheHolder implements Cloneable ensures this methods
// }
// }
int dstPos = dstProfile.getCacheIndex(srcHolder.getCode());
if (dstPos >= 0) {
dstProfile.cacheDB.set(dstPos, srcHolder);
} else {
dstProfile.cacheDB.add(srcHolder);
}
}
}
private class Mover extends Executor {
Mover(String title, Profile dstProfile) {
this.title = title;
this.dstProfile = dstProfile;
}
public void doIt(int i, CacheHolder srcHolder) {
Vector srcFiles = getFileNames(srcHolder);
deleteCacheFiles(srcFiles, dstProfile.dataDir);
moveCacheFiles(srcFiles, MainForm.profile.dataDir, dstProfile.dataDir);
int dstPos = dstProfile.getCacheIndex(srcHolder.getCode());
if (dstPos >= 0) {
dstProfile.cacheDB.set(dstPos, srcHolder);
} else {
dstProfile.cacheDB.add(srcHolder);
}
srcDB.removeElementAt(i);
}
}
}
class DataMoverForm extends Form {
private mCheckBox ticked, visible, tickedVisible;
private CheckBoxGroup chkFormat = new CheckBoxGroup();
private mLabel firstLine;
public DataMoverForm(String tickedText, String visibleText, String tickedVisibleText, String firstLineText) {
title = MyLocale.getMsg(144, "Warning");
ticked = new mCheckBox(MyLocale.getMsg(254, "All visible"));
ticked.setGroup(chkFormat);
visible = new mCheckBox(MyLocale.getMsg(255, "All ticked"));
visible.setGroup(chkFormat);
tickedVisible = new mCheckBox(MyLocale.getMsg(256, "All visible and ticked"));
tickedVisible.setGroup(chkFormat);
firstLine = new mLabel("");
firstLine.anchor = CellConstants.CENTER;
addLast(firstLine);
addLast(visible);
addLast(ticked);
addLast(tickedVisible);
mLabel continueQuestion = new mLabel(MyLocale.getMsg(259, "Do You really want to continue?"));
continueQuestion.anchor = CellConstants.CENTER;
addLast(continueQuestion);
doButtons(FormBase.YESB | FormBase.CANCELB);
setModefromPref();
ticked.text = tickedText;
visible.text = visibleText;
tickedVisible.text = tickedVisibleText;
firstLine.text = firstLineText;
}
/**
* Gets the last mode from the preferences
*/
private void setModefromPref() {
switch (Preferences.itself().processorMode) {
case 1:
ticked.setState(true);
break;
case 2:
tickedVisible.setState(true);
break;
case 0:
visible.setState(true);
break;
}
}
public void onEvent(Event ev) {
if (ev.target == yes || ev.target == no) {
Preferences.itself().processorMode = getMode();
}
super.onEvent(ev);
}
public int getMode() {
if (visible.getState()) {
return 0;
} else if (ticked.getState()) {
return 1;
} else if (tickedVisible.getState()) {
return 2;
} else {
throw new IllegalStateException("No radiobutton selected");
}
}
}