2
* This file is part of the ZoRa project: http://www.photozora.org.
4
* ZoRa is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* ZoRa is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with ZoRa; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
* (c) 2015 Berthold Daum (berthold.daum@bdaum.de)
20
package com.bdaum.zoom.ui.internal.actions;
23
import java.io.IOException;
24
import java.lang.reflect.InvocationTargetException;
26
import java.util.List;
28
import org.eclipse.core.runtime.IAdaptable;
29
import org.eclipse.core.runtime.IProgressMonitor;
30
import org.eclipse.jface.action.IAction;
31
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
32
import org.eclipse.jface.operation.IRunnableWithProgress;
33
import org.eclipse.jface.resource.JFaceResources;
34
import org.eclipse.jface.viewers.ISelection;
35
import org.eclipse.osgi.util.NLS;
36
import org.eclipse.swt.SWT;
37
import org.eclipse.swt.events.ModifyEvent;
38
import org.eclipse.swt.events.ModifyListener;
39
import org.eclipse.swt.layout.GridData;
40
import org.eclipse.swt.layout.GridLayout;
41
import org.eclipse.swt.widgets.Button;
42
import org.eclipse.swt.widgets.Composite;
43
import org.eclipse.swt.widgets.Control;
44
import org.eclipse.swt.widgets.Label;
45
import org.eclipse.swt.widgets.Shell;
46
import org.eclipse.ui.IWorkbenchWindow;
47
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
49
import com.bdaum.zoom.cat.model.asset.AssetImpl;
50
import com.bdaum.zoom.cat.model.meta.Meta;
51
import com.bdaum.zoom.core.CatalogListener;
52
import com.bdaum.zoom.core.Constants;
53
import com.bdaum.zoom.core.Core;
54
import com.bdaum.zoom.core.Format;
55
import com.bdaum.zoom.core.IRecipeDetector;
56
import com.bdaum.zoom.core.IVolumeManager;
57
import com.bdaum.zoom.core.db.IDbErrorHandler;
58
import com.bdaum.zoom.core.db.IDbFactory;
59
import com.bdaum.zoom.core.db.IDbManager;
60
import com.bdaum.zoom.core.internal.CoreActivator;
61
import com.bdaum.zoom.program.BatchUtilities;
62
import com.bdaum.zoom.program.DiskFullException;
63
import com.bdaum.zoom.ui.dialogs.AcousticMessageDialog;
64
import com.bdaum.zoom.ui.dialogs.ZTitleAreaDialog;
65
import com.bdaum.zoom.ui.internal.HelpContextIds;
66
import com.bdaum.zoom.ui.internal.dialogs.OutputTargetGroup;
67
import com.bdaum.zoom.ui.internal.widgets.WidgetFactory;
69
@SuppressWarnings("restriction")
70
public class ArchiveAction implements IWorkbenchWindowActionDelegate,
73
public class SpaceDialog extends ZTitleAreaDialog {
75
private final int remoteImages;
76
private final int externalImages;
77
private final int localImages;
78
private final int voiceFiles;
79
private final int recipes;
80
private final long imageSize;
81
private final long catSize;
82
private OutputTargetGroup output;
83
private File targetFile;
84
private Button catReadonlyButton;
85
private Button fileReadonlyButton;
86
private boolean fileReadonly;
87
private boolean catReadonly;
89
public SpaceDialog(Shell parentShell, int remoteImages,
90
int externalImages, int localImages, int voiceFiles,
91
int recipes, long imageSize, long catSize) {
92
super(parentShell, HelpContextIds.ARCHIVE_DIALOG);
93
this.remoteImages = remoteImages;
94
this.externalImages = externalImages;
95
this.localImages = localImages;
96
this.voiceFiles = voiceFiles;
97
this.recipes = recipes;
98
this.imageSize = imageSize;
99
this.catSize = catSize;
103
public void create() {
105
getShell().setText(Constants.APPLICATION_NAME);
106
setTitle(Messages.ArchiveAction_archive_selection);
107
setMessage(Messages.ArchiveAction_select_an_archive_dest);
112
protected Control createDialogArea(Composite parent) {
113
Composite area = (Composite) super.createDialogArea(parent);
114
Composite comp = new Composite(area, SWT.NONE);
115
comp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
116
comp.setLayout(new GridLayout(2, false));
117
Label statLabel = new Label(comp, SWT.NONE);
118
statLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER,
119
false, false, 2, 1));
120
statLabel.setFont(JFaceResources.getHeaderFont());
121
statLabel.setText(Messages.ArchiveAction_statistics);
122
new Label(comp, SWT.NONE)
123
.setText(Messages.ArchiveAction_remote_images);
124
new Label(comp, SWT.NONE).setText(String.valueOf(remoteImages));
125
new Label(comp, SWT.NONE)
126
.setText(Messages.ArchiveAction_external_images);
127
new Label(comp, SWT.NONE).setText(String.valueOf(externalImages));
128
new Label(comp, SWT.NONE)
129
.setText(Messages.ArchiveAction_local_images);
130
new Label(comp, SWT.NONE).setText(String.valueOf(localImages));
131
new Label(comp, SWT.NONE)
132
.setText(Messages.ArchiveAction_voice_viles);
133
new Label(comp, SWT.NONE).setText(String.valueOf(voiceFiles));
134
new Label(comp, SWT.NONE).setText(Messages.ArchiveAction_recipes);
135
new Label(comp, SWT.NONE).setText(String.valueOf(recipes));
136
Label sizeLabel = new Label(comp, SWT.NONE);
137
sizeLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER,
138
false, false, 2, 1));
139
sizeLabel.setFont(JFaceResources.getHeaderFont());
140
sizeLabel.setText(Messages.ArchiveAction_required_space);
141
new Label(comp, SWT.NONE).setText(Messages.ArchiveAction_images);
142
new Label(comp, SWT.NONE).setText(Format.sizeFormatter
143
.toString(imageSize));
144
new Label(comp, SWT.NONE).setText(Messages.ArchiveAction_catalog);
145
new Label(comp, SWT.NONE).setText(Format.sizeFormatter
147
new Label(comp, SWT.NONE).setText(Messages.ArchiveAction_total);
148
new Label(comp, SWT.NONE).setText(Format.sizeFormatter
149
.toString(imageSize + catSize));
150
output = new OutputTargetGroup(comp, new GridData(SWT.FILL,
151
SWT.CENTER, true, false, 2, 1), new ModifyListener() {
152
public void modifyText(ModifyEvent e) {
156
catReadonlyButton = WidgetFactory.createCheckButton(comp,
157
Messages.ArchiveAction_cat_readonly, null);
158
fileReadonlyButton = WidgetFactory.createCheckButton(comp,
159
Messages.ArchiveAction_files_readonly, null);
163
public void validate() {
164
String localFolder = output.getLocalFolder();
165
String errorMessage = null;
166
if (localFolder == null || localFolder.length() == 0)
167
errorMessage = Messages.ArchiveAction_specify_output;
169
File folder = new File(localFolder);
170
if (!folder.exists())
171
errorMessage = Messages.ArchiveAction_destination_does_not_exist;
173
File[] list = folder.listFiles();
174
if (list != null && list.length > 0)
175
errorMessage = Messages.ArchiveAction_destination_must_be_empty;
177
long freeSpace = Core.getCore().getVolumeManager()
178
.getRootFile(folder).getFreeSpace();
179
long total = imageSize + catSize;
180
if (freeSpace < total)
182
.bind(Messages.ArchiveAction_output_dest_too_small,
184
.toString(freeSpace),
190
setErrorMessage(errorMessage);
191
getButton(OK).setEnabled(errorMessage == null);
195
protected void okPressed() {
196
targetFile = new File(output.getLocalFolder());
197
catReadonly = catReadonlyButton.getSelection();
198
fileReadonly = fileReadonlyButton.getSelection();
202
public File getTargetFile() {
207
* @return fileReadonly
209
public boolean isFileReadonly() {
214
* @return catReadonly
216
public boolean isCatReadonly() {
222
private IWorkbenchWindow window;
226
int remoteImages = 0;
227
int externalImages = 0;
232
public void run(IAction action) {
233
boolean result = AcousticMessageDialog.openQuestion(window.getShell(),
234
Messages.ArchiveAction_archive,
235
Messages.ArchiveAction_archive_message);
237
ProgressMonitorDialog dialog = new ProgressMonitorDialog(
240
dialog.run(false, true, new IRunnableWithProgress() {
242
public void run(IProgressMonitor monitor)
243
throws InvocationTargetException,
244
InterruptedException {
252
IDbManager dbManager = Core.getCore().getDbManager();
253
IVolumeManager volumeManager = Core.getCore()
255
List<IRecipeDetector> recipeDetectors = CoreActivator
256
.getDefault().getRecipeDetectors();
257
List<AssetImpl> assets = dbManager.obtainAssets();
258
assetCount = assets.size();
260
Messages.ArchiveAction_calculationg_space,
262
monitor.subTask(Messages.ArchiveAction_calculationg_image_space);
263
for (AssetImpl asset : assets) {
264
if (volumeManager.isRemote(asset))
267
URI uri = volumeManager.findExistingFile(asset,
273
File file = new File(uri);
274
imageSize += file.length() + 4096;
275
URI voiceUri = volumeManager
276
.findVoiceFile(asset);
277
if (voiceUri != null) {
279
imageSize += new File(voiceUri)
282
for (IRecipeDetector recipeDetector : recipeDetectors) {
283
File[] metafiles = recipeDetector
284
.getMetafiles(asset.getUri());
285
if (metafiles != null)
286
for (File metafile : metafiles) {
288
imageSize += metafile.length() + 4096;
293
if (monitor.isCanceled())
297
monitor.subTask(Messages.ArchiveAction_calculationg_catalog_space);
298
catSize = dbManager.getFile().length();
300
File indexPath = dbManager.getIndexPath();
301
if (indexPath != null) {
302
catSize += indexPath.length();
303
File[] list = indexPath.listFiles();
305
for (File file : list)
306
catSize += file.length();
311
} catch (InvocationTargetException e) {
312
AcousticMessageDialog.openError(window.getShell(),
313
Messages.ArchiveAction_error_during_space_calc, e
314
.getCause().getMessage());
316
} catch (InterruptedException e) {
317
AcousticMessageDialog.openInformation(window.getShell(),
318
Constants.APPLICATION_NAME,
319
Messages.ArchiveAction_archiving_aborted);
322
SpaceDialog spaceDialog = new SpaceDialog(window.getShell(),
323
remoteImages, externalImages, localImages, voiceFiles,
324
recipes, imageSize, catSize);
325
if (spaceDialog.open() == SpaceDialog.OK) {
326
final File output = spaceDialog.getTargetFile();
327
final boolean catReadOnly = spaceDialog.isCatReadonly();
328
final boolean fileReadOnly = spaceDialog.isFileReadonly();
329
ProgressMonitorDialog runDialog = new ProgressMonitorDialog(
332
runDialog.run(true, true, new IRunnableWithProgress() {
334
public void run(IProgressMonitor monitor)
335
throws InvocationTargetException,
336
InterruptedException {
337
CoreActivator coreActivator = CoreActivator
339
IDbFactory dbFactory = coreActivator.getDbFactory();
340
IDbErrorHandler errorHandler = dbFactory
342
IDbManager dbManager = coreActivator.getDbManager();
343
IVolumeManager volumeManager = coreActivator
345
monitor.beginTask(Messages.ArchiveAction_archiving,
347
monitor.subTask(Messages.ArchiveAction_archiving_cat);
348
File catFile = dbManager.getFile();
349
File indexPath = dbManager.getIndexPath();
350
File catBackupFile = new File(output, catFile
352
dbManager.backup(catBackupFile.getAbsolutePath());
354
monitor.subTask(Messages.ArchiveAction_archiving_index);
356
BatchUtilities.copyFolder(indexPath, new File(
357
output, indexPath.getName()), monitor);
358
} catch (IOException e1) {
361
Messages.ArchiveAction_error_during_archiving,
366
} catch (DiskFullException e1) {
369
Messages.ArchiveAction_error_during_archiving,
370
Messages.ArchiveAction_disk_full,
374
if (monitor.isCanceled()) {
377
Messages.ArchiveAction_operation_cancelled,
378
Messages.ArchiveAction_incomplete_archive,
383
final IDbManager newDbManager = dbFactory
385
catBackupFile.getAbsolutePath(),
386
false, false, false);
388
List<IRecipeDetector> recipeDetectors = coreActivator
389
.getRecipeDetectors();
390
monitor.subTask(Messages.ArchiveAction_archiving_images);
391
List<AssetImpl> assets = newDbManager
394
long start = System.currentTimeMillis();
395
for (AssetImpl asset : assets) {
396
URI uri = volumeManager.findExistingFile(
400
String name = Core.getFileName(uri,
402
File targetFile = BatchUtilities
410
BatchUtilities.copyFile(new File(
411
uri), targetFile, null);
413
targetFile.setReadOnly();
414
asset.setUri(targetFile.toURI()
416
String volume = volumeManager
417
.getVolumeForFile(targetFile);
418
asset.setVolume(volume);
419
URI voiceUri = volumeManager
420
.findVoiceFile(asset);
421
if (voiceUri != null) {
422
String voiceName = Core
423
.getFileName(uri, false);
424
File voiceTargetFile = BatchUtilities
434
BatchUtilities.copyFile(
436
voiceTargetFile, null);
440
asset.setVoiceFileURI(voiceTargetFile
441
.toURI().toString());
442
asset.setVoiceVolume(volume);
444
asset.setVoiceFileURI(null);
445
asset.setVoiceVolume(null);
447
for (IRecipeDetector recipeDetector : recipeDetectors)
448
recipeDetector.archiveRecipes(
449
output, uri.toString(),
452
newDbManager.storeAndCommit(asset);
453
} catch (IOException e) {
456
Messages.ArchiveAction_error_during_archiving,
460
.close(CatalogListener.EMERGENCY);
462
} catch (DiskFullException e) {
465
Messages.ArchiveAction_error_during_archiving,
466
Messages.ArchiveAction_disk_full,
469
.close(CatalogListener.EMERGENCY);
473
int remainingImages = localImages
475
if (remainingImages > 0) {
476
long current = System
477
.currentTimeMillis();
478
long elapsed = current - start;
479
long estimated = elapsed / i
482
.bind(Messages.ArchiveAction_elapsed_time,
484
.toString((int) ((elapsed + 30000) / 60000)),
486
.toString((int) ((estimated + 30000) / 60000))));
490
if (monitor.isCanceled()) {
493
Messages.ArchiveAction_operation_cancelled,
494
Messages.ArchiveAction_incomplete_archive,
502
Meta meta = newDbManager.getMeta(false);
504
meta.setReadonly(true);
505
newDbManager.storeAndCommit(meta);
509
newDbManager.close(CatalogListener.NORMAL);
514
} catch (InvocationTargetException e) {
515
AcousticMessageDialog.openError(window.getShell(),
516
Messages.ArchiveAction_error_during_archiving, e
517
.getCause().getMessage());
518
} catch (InterruptedException e) {
519
AcousticMessageDialog.openInformation(window.getShell(),
520
Constants.APPLICATION_NAME,
521
Messages.ArchiveAction_incomplete_archive);
527
public void selectionChanged(IAction action, ISelection selection) {
530
public void dispose() {
533
public void init(IWorkbenchWindow window) {
534
this.window = window;
537
public Object getAdapter(Class adapter) {
538
if (Shell.class == adapter)
539
return window.getShell();