/*
* This file is part of muCommander, http://www.mucommander.com
* Copyright (C) 2002-2016 Maxence Bernard
*
* muCommander 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.
*
* muCommander 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/>.
*/
package com.mucommander.ui.action.impl;
import java.awt.event.KeyEvent;
import java.util.Map;
import javax.swing.KeyStroke;
import com.mucommander.commons.file.AbstractFile;
import com.mucommander.commons.file.filter.AbstractFilenameFilter;
import com.mucommander.commons.file.filter.ExtensionFilenameFilter;
import com.mucommander.commons.file.filter.FilenameFilter;
import com.mucommander.ui.action.AbstractActionDescriptor;
import com.mucommander.ui.action.ActionCategory;
import com.mucommander.ui.action.ActionCategory;
import com.mucommander.ui.action.ActionDescriptor;
import com.mucommander.ui.action.ActionFactory;
import com.mucommander.ui.action.MuAction;
import com.mucommander.ui.main.MainFrame;
import com.mucommander.ui.main.table.FileTable;
import com.mucommander.ui.main.table.FileTableModel;
/**
* Action that marks / unmarks all files with a specific extension.
* <p>
* Marking behaves as follows:
* <ul>
* <li>
* If the current selection is marked, all files whose extension matches that of the current selection will
* be unmarked.
* </li>
* <li>
* If the current selection isn't marked, all files whose extension matches that of the current selection will
* be marked.
* </li>
* </ul>
* </p>
* <p>
* By default, this action will mark all files whose extension match that of the current selection in a case-insensitive fashion.
* It can, however, be configured:
* <ul>
* <li>
* If the <code>extension</code> property is set, its value prepended by a <code>.</code> is always going to be used regardless of the
* current selection.
* </li>
* <li>
* If the <code>case_sensitive</code> property is set to <code>true</code>, extension matching will be done in a case sensitive fashion.
* </li>
* </ul>
* </p>
* @author Nicolas Rinaudo
*/
public class MarkExtensionAction extends MuAction {
// - Property names ------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/** Key that controls which extension should be matched. */
public static final String EXTENSION_PROPERTY_KEY = "extension";
/** Key that controls whether extension matching should be done in a case sensitive fashion (defaults to false). */
public static final String CASE_SENSITIVE_PROPERTY_KEY = "case_sensitive";
// - Initialization ------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Creates a new <code>MarkExtensionAction</code> with the specified parameters.
* @param mainFrame frame to which the action is attached.
* @param properties action's properties.
*/
public MarkExtensionAction(MainFrame mainFrame, Map<String,Object> properties) {
super(mainFrame, properties);
}
// - Properties retrieval ------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Returns the extension that was configured in the action's properties.
* @return the extension that was configured in the action's properties, <code>null</code> if none.
*/
private String getExtension() {
Object o;
// If the key wasn't set, return null.
if((o = getValue(EXTENSION_PROPERTY_KEY)) == null)
return null;
// If the value is a string, return it.
if(o instanceof String)
return (String)o;
// Otherwise, return null.
return null;
}
/**
* Returns <code>true</code> if the action must compare string in a case-sensitive fashion.
* @return <code>true</code> if the action must compare string in a case-sensitive fashion, <code>false</code> otherwise.
*/
private boolean isCaseSensitive() {
Object o;
// If the action hasn't been configured, defaults to false.
if((o = getValue(CASE_SENSITIVE_PROPERTY_KEY)) == null)
return false;
// Returns the configured value if it's a string, false otherwise.
if(o instanceof String)
return o.equals("true");
return false;
}
// - Action code ---------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Creates a {@link com.mucommander.commons.file.filter.FilenameFilter} that should be applied to all current files.
* <p>
* If the action has been configured using the <code>file.extension</code> property, the returned filter
* will match that extension. Otherwise, the currently selected file's extension will be used. If it doesn't
* have one, the returned filter will match all files such that
* <code>file.getExtension() == null</code>.
* </p>
* @param file currently selected file.
* @return the filter that should be applied by this action.
*/
private FilenameFilter getFilter(AbstractFile file) {
String ext;
ExtensionFilenameFilter filter;
// If no extension has been configured, analyse the current selection.
if((ext = getExtension()) == null) {
// If there is no current selection, abort.
if(file == null)
return null;
// If the current file doesn't have an extension, return a filename filter that
// match null extensions.
if((ext = file.getExtension()) == null)
return new AbstractFilenameFilter() {
public boolean accept(String name) {return AbstractFile.getExtension(name) == null;}
};
}
// At this point, ext contains the extension that should be matched.
filter = new ExtensionFilenameFilter("." + ext);
// Initialises the filter's case-sensitivy depending on the action's propeties.
filter.setCaseSensitive(isCaseSensitive());
return filter;
}
/**
* Marks all files whose extension matches the current selection.
*/
@Override
public void performAction() {
FileTable fileTable;
FileTableModel tableModel;
FilenameFilter filter;
int rowCount;
boolean mark;
// Initialization. Aborts if there is no selected file.
fileTable = mainFrame.getActiveTable();
if((filter = getFilter(fileTable.getSelectedFile(false, true))) == null)
return;
tableModel = fileTable.getFileTableModel();
rowCount = tableModel.getRowCount();
mark = !tableModel.isRowMarked(fileTable.getSelectedRow());
// Goes through all files in the active table, marking all that match 'filter'.
for(int i = tableModel.getFirstMarkableRow(); i < rowCount; i++)
if(filter.accept(tableModel.getCachedFileAtRow(i)))
tableModel.setRowMarked(i, mark);
fileTable.repaint();
// Notify registered listeners that currently marked files have changed on the FileTable
fileTable.fireMarkedFilesChangedEvent();
}
@Override
public ActionDescriptor getDescriptor() {
return new Descriptor();
}
public static class Factory implements ActionFactory {
public MuAction createAction(MainFrame mainFrame, Map<String, Object> properties) {
return new MarkExtensionAction(mainFrame, properties);
}
}
public static class Descriptor extends AbstractActionDescriptor {
public static final String ACTION_ID = "MarkExtension";
public String getId() { return ACTION_ID; }
public ActionCategory getCategory() { return ActionCategory.SELECTION; }
public KeyStroke getDefaultAltKeyStroke() { return null; }
public KeyStroke getDefaultKeyStroke() { return KeyStroke.getKeyStroke(KeyEvent.VK_ADD, KeyEvent.SHIFT_DOWN_MASK); }
}
}