package org.rr.jeborker.gui;
import static org.rr.commons.utils.StringUtil.EMPTY;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.table.TableModel;
import org.rr.commons.log.LoggerFactory;
import org.rr.commons.mufs.IResourceHandler;
import org.rr.commons.mufs.ResourceHandlerFactory;
import org.rr.commons.swing.SwingUtils;
import org.rr.commons.swing.dialogs.JDirectoryChooser;
import org.rr.commons.utils.CommonUtils;
import org.rr.commons.utils.StringUtil;
import org.rr.jeborker.app.BasePathList;
import org.rr.jeborker.app.FileWatchService;
import org.rr.jeborker.app.preferences.APreferenceStore;
import org.rr.jeborker.app.preferences.PreferenceStoreFactory;
import org.rr.jeborker.db.item.EbookPropertyItem;
import org.rr.jeborker.db.item.EbookPropertyItemUtils;
import org.rr.jeborker.event.ApplicationEvent;
import org.rr.jeborker.event.EventManager;
import org.rr.jeborker.gui.model.EbookPropertyDBTableModel;
import org.rr.jeborker.gui.model.EbookPropertyFileTableModel;
import org.rr.jeborker.gui.model.EbookSheetPropertyModel;
import org.rr.jeborker.gui.model.ReloadableTableModel;
import org.rr.jeborker.metadata.IMetadataReader;
import org.rr.jeborker.metadata.IMetadataReader.COMMON_METADATA_TYPES;
import org.rr.jeborker.metadata.MetadataHandlerFactory;
import org.rr.jeborker.metadata.MetadataProperty;
import com.l2fprod.common.propertysheet.Property;
import com.l2fprod.common.propertysheet.PropertySheetTableModel;
public class MainController {
private static long startupTime = System.currentTimeMillis();
private final APreferenceStore preferenceStore = PreferenceStoreFactory.getPreferenceStore(PreferenceStoreFactory.DB_STORE);
private MainView mainWindow;
/**
* The controller singleton.
*/
private static MainController controller;
/**
* No public instantiation. The {@link #getController()} method is
* used for creating a new {@link MainController} instance because
* the {@link MainController} is a singleton.
*/
private MainController() {
}
/**
* Gets the controller instance. because we have only one main window
* We have a singleton here.
* @return The desired EBorkerMainController.
*/
public static MainController getController() {
if(controller == null) {
try {
controller = new MainController();
controller.initialize();
} catch (Exception e) {
LoggerFactory.getLogger(MainController.class).log(Level.SEVERE, "Startup failed.", e);
}
}
return controller;
}
/**
* Gets the handler which handles the base path and file system tree.
*/
public MainViewTreeComponentHandler getMainTreeHandler() {
return mainWindow.getTreeComponentHandler();
}
/**
* Gets the handler which handles the main table
*/
public MainViewEbookTableComponentHandler getEbookTableHandler() {
return mainWindow.getEbookTableHandler();
}
public MainViewPropertySheetHandler getPropertySheetHandler() {
return mainWindow.getPropertySheetHandler();
}
/**
* Gets the controller which handles the preference dialog.
*/
public PreferenceController getPreferenceController() {
return PreferenceController.getInstance();
}
/**
* Gets the controller which handles the preference dialog.
*/
public LoggerController getLoggerController() {
return LoggerController.getInstance(mainWindow);
}
/**
* Gets the controller which is used by the converter classes to
* ask the user for some options.
*/
public ConverterPreferenceController getConverterPreferenceController() {
return ConverterPreferenceController.getInstance();
}
public MetadataDownloadController getMetadataDownloadController() {
return MetadataDownloadController.getInstance(mainWindow);
}
public RenameFileController getRenameFileController() {
return RenameFileController.getInstance(mainWindow);
}
/**
* Tells if the {@link MainController} is already initialized.
* @return <code>true</code> if the {@link MainController} is initialized and <code>false</code> otherwise.
*/
public static boolean isInitialized() {
return controller != null;
}
private void initialize() throws Exception {
final String lookAndFeel = PreferenceStoreFactory.getPreferenceStore(PreferenceStoreFactory.PREFERENCE_KEYS.LOOK_AND_FEEL)
.getEntryAsString(PreferenceStoreFactory.PREFERENCE_KEYS.LOOK_AND_FEEL);
Toolkit.getDefaultToolkit().setDynamicLayout(false);
SwingUtils.setLookAndFeel(lookAndFeel);
mainWindow = new MainView(this);
mainWindow.initialize();
mainWindow.initListeners();
mainWindow.restoreComponentProperties();
MainMenuBarController.getController().restoreProperties();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
mainWindow.setVisible(true);
LoggerFactory.getLogger(this).log(Level.INFO, (System.currentTimeMillis() - startupTime) + "ms startup time");
MainController.getController().getMainWindow().getGlassPane().setVisible(true);
initMainModel();
}
});
BasePathList basePath = preferenceStore.getBasePath();
FileWatchService.addWatchPath(basePath);
FileWatchService.addWatchPath(EbookPropertyItemUtils.fetchPathElements());
}
/**
* Model loading and setup
*/
private void initMainModel() {
getProgressMonitor().setEnabled(false);
new SwingWorker<Void, Void>() {
EbookPropertyDBTableModel ebookPropertyDBTableModel;
@Override
protected Void doInBackground() throws Exception {
EbookPropertyDBTableModel tableModel = changeToDatabaseModel();
ebookPropertyDBTableModel = new EbookPropertyDBTableModel(tableModel, false);
ebookPropertyDBTableModel.getRowCount();
return null;
}
@Override
protected void done() {
try {
setMainEbookTableModel(ebookPropertyDBTableModel);
} finally {
getMainWindow().getGlassPane().setVisible(false);
LoggerFactory.getLogger(this).log(Level.INFO, (System.currentTimeMillis() - startupTime) + "ms until data model loaded");
getProgressMonitor().setEnabled(true);
}
}
}.execute();
}
private void setMainEbookTableModel(TableModel model) {
mainWindow.getEbookTableHandler().setModel(model);
}
public EbookPropertyFileTableModel changeToFileModel(List<IResourceHandler> resources) {
EbookPropertyFileTableModel fileModel = new EbookPropertyFileTableModel(resources);
setMainEbookTableModel(fileModel);
return fileModel;
}
public EbookPropertyDBTableModel changeToDatabaseModel() {
EbookPropertyDBTableModel model = mainWindow.getEbookTableHandler().getDBModel();
setMainEbookTableModel(model);
return model;
}
public boolean isEbookPropertyFileTableModel() {
return mainWindow.getEbookTableHandler().getModel() instanceof EbookPropertyFileTableModel;
}
public void refreshTableItem(int[] selectedRows, boolean refreshMetadataSheet) {
mainWindow.getEbookTableHandler().refreshTableItem(selectedRows);
if(refreshMetadataSheet) {
getPropertySheetHandler().refreshSheetProperties();
}
}
/**
* Refresh the selected table rows.
*/
public void refreshTableSelectedItem(final boolean refreshMetadataSheet) {
int[] selectedRows = getSelectedEbookPropertyItemRows() ;
refreshTableItem(selectedRows, refreshMetadataSheet);
}
/**
* Refresh the given table rows.
* @param rows The rows to be refreshed.
* @param refreshMetadataSheet do also refresh the metadata sheet.
*/
public void refreshTableRows(final int[] rows, boolean refreshMetadataSheet) {
if (rows == null || rows.length == 0) {
return;
} else {
mainWindow.getEbookTableHandler().refreshTableRows(rows);
int[] selectedEbookPropertyItemRows = getSelectedEbookPropertyItemRows();
for (int i = 0; i < rows.length; i++) {
for (int j = 0; j < selectedEbookPropertyItemRows.length; j++) {
if(refreshMetadataSheet && selectedEbookPropertyItemRows[j] == rows[i]) {
getPropertySheetHandler().refreshSheetProperties();
}
}
}
}
}
/**
* Sets the image which is provided by the given {@link IResourceHandler} to the
* image viewer in the main view.
* @param imageResource The {@link IResourceHandler} instance providing the image
* data to be displayed. <code>null</code>if no image should be displayed.
*/
public void setImageViewerResource(IResourceHandler imageResource) {
mainWindow.setImageViewerResource(imageResource);
}
/**
* Gets the {@link IResourceHandler} for the image which is displayed in the image viewer.
* @return The desired {@link IResourceHandler} or <code>null</code>.
*/
public IResourceHandler getImageViewerResource() {
return mainWindow.getImageViewerResource();
}
/**
* Gets the {@link BufferedImage} for the image which is displayed in the image viewer.
* @return The desired {@link BufferedImage} or <code>null</code>.
*/
public BufferedImage getImageViewerImage() {
return mainWindow.getImageViewerImage();
}
/**
* Removes the metadata property which is currently selected in the property sheet.
*/
public void removeSelectedMetadataProperty() {
Property selectedMetadataProperty = getSelectedMetadataProperty();
if (selectedMetadataProperty != null) {
PropertySheetTableModel model = getPropertySheetHandler().getModel();
model.removeProperty(selectedMetadataProperty);
}
}
/**
* Adds the given property to the end of the property sheet.
* @param property The property to be added.
*/
public void addMetadataProperty(Property property) {
mainWindow.getPropertySheetHandler().addMetadataProperty(property);
EventManager.fireEvent(EventManager.EVENT_TYPES.METADATA_SHEET_CONTENT_CHANGE, new ApplicationEvent(controller.getSelectedEbookPropertyItems(), property, property));
}
/**
* Get the currently selected metadata property from the metadata sheet.
* @return The desired {@link Property} instance or <code>null</code> if no selection is made.
*/
public Property getSelectedMetadataProperty() {
return mainWindow.getSelectedMetadataProperty();
}
/**
* Set the given property as selected one in the metadata sheet.
* @param property The property to be set as selected.
*/
public void setSelectedMetadataProperty(final Property property) {
mainWindow.setSelectedMetadataProperty(property);
}
/**
* Sets the rating value (between 1 and 10).
* @param rating The rating value.
*/
public void setRatingToSelectedEntry(int rating) {
final EbookSheetPropertyModel model = getPropertySheetHandler().getModel();
final Property ratingProperty = model.getRatingProperty();
final int ratingIdx = model.getRatingIndex();
if(ratingIdx >= 0) {
mainWindow.getPropertySheetHandler().getSelectionModel().setSelectionInterval(ratingIdx, ratingIdx);
}
if(ratingProperty != null) {
ratingProperty.setValue(rating);
} else {
List<EbookPropertyItem> selectedEbookPropertyItems = getSelectedEbookPropertyItems();
for (EbookPropertyItem item : selectedEbookPropertyItems) {
IMetadataReader reader = MetadataHandlerFactory.getReader(ResourceHandlerFactory.getResourceHandler(item.getFile()));
List<MetadataProperty> metadataByType = reader.getMetadataByType(true, model.getAllMetadata(), COMMON_METADATA_TYPES.RATING);
if(!metadataByType.isEmpty()) {
MetadataProperty ratingMetadata = metadataByType.get(0);
final Property createProperty = EbookSheetPropertyModel.createProperty(ratingMetadata, Collections.singletonList(item), 0);
createProperty.setValue(rating);
this.addMetadataProperty(createProperty);
} else {
LoggerFactory.getLogger(this).log(Level.WARNING, "Rating entry is not available.");
}
}
}
}
/**
* Saves/Writes the metadata properties if something has changed.
*/
public void saveMetadataProperties(int minSelectionIndex, int maxSelectionIndex) {
EbookSheetPropertyModel sheetModel = getPropertySheetHandler().getModel();
List<MetadataProperty> sheetProperties = sheetModel.getAllMetadata();
List<IResourceHandler> propertyResourceHandler = sheetModel.getPropertyResourceHandler();
if(getPropertySheetHandler().getModel().isChanged()) {
MainControllerUtils.writeProperties(sheetProperties, propertyResourceHandler);
ReloadableTableModel tableModel = getModel();
int rowCount = tableModel.getRowCount();
if(minSelectionIndex >= 0 && minSelectionIndex < rowCount) {
tableModel.reloadEbookPropertyItemAt(minSelectionIndex);
}
if(maxSelectionIndex >= 0 && maxSelectionIndex != minSelectionIndex && maxSelectionIndex < rowCount) {
tableModel.reloadEbookPropertyItemAt(maxSelectionIndex);
}
if(minSelectionIndex < 0 || maxSelectionIndex < 0) {
int[] selectedRows = mainWindow.getEbookTableHandler().getSelectedRows();
for (int selectedRow : selectedRows) {
tableModel.reloadEbookPropertyItemAt(selectedRow);
}
}
refreshTableSelectedItem(false);
//a repaint does a refresh to all visible table rows.
mainWindow.getEbookTableHandler().repaint();
}
}
/**
* Adds the given {@link EbookPropertyItem} to the model. The added
* {@link EbookPropertyItem} will be shown to the ui.
* @param item The item to be added.
*/
public void addEbookPropertyItem(final EbookPropertyItem item, final int row) {
if(preferenceStore.isBasePathVisible(item.getBasePath())) {
ReloadableTableModel model = getModel();
getEbookTableHandler().clearSelection();
model.addRow(item, row);
mainWindow.getEbookTableHandler().stopEdit();
}
}
/**
* Gets all selected items from the main table.
* @return The selected items. Never returns <code>null</code>.
*/
public List<EbookPropertyItem> getSelectedEbookPropertyItems() {
return mainWindow.getEbookTableHandler().getSelectedEbookPropertyItems();
}
/**
* Gets all selected rows from the main table.
* @return all selected rows or an empty array if no row is selected. Never returns <code>null</code>.
*/
public int[] getSelectedEbookPropertyItemRows() {
if(mainWindow != null) {
return mainWindow.getEbookTableHandler().getSelectedEbookPropertyItemRows();
}
return new int[0];
}
public List<Field> getSelectedSortColumnFields() {
return mainWindow.getSortColumnSelectedFields();
}
/**
* Removes the given {@link EbookPropertyItem} from the model. This
* change the view automatically.
* @param item The {@link EbookPropertyItem} to be removed.
*/
public boolean removeEbookPropertyItem(EbookPropertyItem item) {
ReloadableTableModel model = controller.getModel();
mainWindow.getEbookTableHandler().clearSelection();
mainWindow.getEbookTableHandler().editingStopped(new ChangeEvent(this));
return model.removeRow(item);
}
/**
* gets the current model for the main table.
* @return The desired model. <code>null</code> if the model is not initialized.
*/
public ReloadableTableModel getModel() {
return mainWindow.getEbookTableHandler().getModel();
}
/**
* Gets the progress indicator.
* @return The desired monitor instance of <code>null</code> if the monitor is not ready to use.
*/
public MainMonitor getProgressMonitor() {
if(mainWindow != null) {
return mainWindow.getProgressMonitor();
}
return null;
}
/**
* Opens a {@link JFileChooser} and returns the selected folder or
* <code>null</code> if no folder was selected.
* @return The selected folder or <code>null</code>.
*/
public List<File> getDirectorySelection() {
String lastEbookFolder = preferenceStore.getGenericEntryAsString("lastEbookFolder");
IResourceHandler lastEbookFolderResourceLoader = ResourceHandlerFactory.getResourceHandler(lastEbookFolder);
if(lastEbookFolderResourceLoader == null || !lastEbookFolderResourceLoader.isDirectoryResource()) {
lastEbookFolder = null;
}
final JDirectoryChooser chooser = new JDirectoryChooser(true);
//restore directory chooser location
String location = preferenceStore.getGenericEntryAsString("JDirectoryChooserLocation");
if(location != null && location.indexOf(';') != -1) {
String[] split = location.split(";");
chooser.setDialogLocation(new Point(CommonUtils.toNumber(split[0]).intValue(), CommonUtils.toNumber(split[1]).intValue()));
}
//restore directory chooser size
String size = preferenceStore.getGenericEntryAsString("JDirectoryChooserSize");
if(size != null && size.indexOf(';') != -1) {
String[] split = size.split(";");
chooser.setDialogSize(new Dimension(CommonUtils.toNumber(split[0]).intValue(), CommonUtils.toNumber(split[1]).intValue()));
}
List<File> selectedDirectory = chooser.getDirectorySelections(lastEbookFolder, mainWindow);
if(selectedDirectory!=null && !selectedDirectory.isEmpty()) {
preferenceStore.addGenericEntryAsString("lastEbookFolder", selectedDirectory.get(selectedDirectory.size() - 1).toString());
}
//store directory chooser size and location
preferenceStore.addGenericEntryAsString("JDirectoryChooserLocation", chooser.getDialogLocation().x + ";" + chooser.getDialogLocation().y);
preferenceStore.addGenericEntryAsString("JDirectoryChooserSize", chooser.getDialogSize().width + ";" + chooser.getDialogSize().height);
return selectedDirectory;
}
/**
* Tries to get localized Text from the bundle.
*
* @param name The name to be translated.
* @return The translated name.
*/
public String getLocalizedString(final String name) {
if(name == null) {
return null;
}
String localized = StringUtil.replace(name.toLowerCase(), new String[] {"/", " ", ":"}, EMPTY);
localized = Bundle.getString(localized);
if(localized != null && localized.length() > 0) {
return StringUtil.capitalize(localized);
} else {
return StringUtil.capitalize(name);
}
}
public void dispose() {
mainWindow.storeApplicationProperties();
MainMenuBarController.getController().storeProperties();
}
/**
* Gets the application main window. Needed for having modal dialogs.
* @return The main window instance.
*/
public JFrame getMainWindow() {
return mainWindow;
}
/**
* Shows a dialog to the user.
* @param message The message of the dialog
* @param title The dialog title.
* @param option The dialog option: JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.YES_NO_OPTION, JOptionPane.OK_CANCEL_OPTION
* @param defaultValue The value which is stored if the user do not want to see the dialog again.
* -1 for store the result value instead.
* @param isConfirmDialog Tells if the dialog to be shown is a confirm (Yes/No) Dialog or a simple OK Message-Box
* @return 0: yes/ok, 1: no, 2:cancel, -1 none
*/
public int showMessageBox(String message, String title, int option, String showAgainKey, int defaultValue, boolean isConfirmDialog) {
return mainWindow.showMessageBox(message, title, option, showAgainKey, defaultValue, isConfirmDialog);
}
public List<Field> getSelectedFilterFields() {
return mainWindow.getSelectedFilterFields();
}
public void setFilterColorEnabled(boolean enabled) {
mainWindow.setFilterColorEnabled(enabled);
}
public String getFilterText() {
return mainWindow.getFilterText();
}
public void addFilterFieldSearch(String filterText) {
mainWindow.addFilterFieldSearch(filterText);
}
}