001 /*
002 * This file is part of muCommander, http://www.mucommander.com
003 * Copyright (C) 2002-2008 Maxence Bernard
004 *
005 * muCommander is free software; you can redistribute it and/or modify
006 * it under the terms of the GNU General Public License as published by
007 * the Free Software Foundation; either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * muCommander is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU General Public License for more details.
014 *
015 * You should have received a copy of the GNU General Public License
016 * along with this program. If not, see <http://www.gnu.org/licenses/>.
017 */
018
019 package com.mucommander.ui.dialog.file;
020
021 import com.mucommander.file.AbstractFile;
022 import com.mucommander.file.PermissionAccesses;
023 import com.mucommander.file.PermissionTypes;
024 import com.mucommander.file.util.FileSet;
025 import com.mucommander.job.ChangeFileAttributesJob;
026 import com.mucommander.text.Translator;
027 import com.mucommander.ui.action.MuAction;
028 import com.mucommander.ui.dialog.DialogToolkit;
029 import com.mucommander.ui.layout.YBoxPanel;
030 import com.mucommander.ui.main.MainFrame;
031 import com.mucommander.ui.text.SizeConstrainedDocument;
032
033 import javax.swing.*;
034 import javax.swing.event.DocumentEvent;
035 import javax.swing.event.DocumentListener;
036 import javax.swing.text.AttributeSet;
037 import javax.swing.text.BadLocationException;
038 import javax.swing.text.Document;
039 import java.awt.*;
040 import java.awt.event.ActionEvent;
041 import java.awt.event.ActionListener;
042 import java.awt.event.ItemEvent;
043 import java.awt.event.ItemListener;
044
045 /**
046 * This dialog allows the user to change the permissions of the currently selected/marked file(s). The permissions can be
047 * selected either by clicking individual read/write/executable checkboxes for each of the user/group/other accesses,
048 * or by entering an octal permission value.
049 *
050 * @author Maxence Bernard
051 */
052 public class ChangePermissionsDialog extends JobDialog
053 implements ActionListener, ItemListener, DocumentListener, PermissionTypes, PermissionAccesses {
054
055 private JCheckBox permCheckBoxes[][];
056
057 private JTextField octalPermTextField;
058
059 private JCheckBox recurseDirCheckBox;
060
061 /** If true, ItemEvent events should be ignored */
062 private boolean ignoreItemEvent;
063 /** If true, DocumentEvent events should be ignored */
064 private boolean ignoreDocumentEvent;
065
066 private JButton okButton;
067 private JButton cancelButton;
068
069
070 public ChangePermissionsDialog(MainFrame mainFrame, FileSet files) {
071 super(mainFrame, MuAction.getStandardLabel(com.mucommander.ui.action.ChangePermissionsAction.class), files);
072
073 YBoxPanel mainPanel = new YBoxPanel();
074
075 mainPanel.add(new JLabel(MuAction.getStandardTooltip(com.mucommander.ui.action.ChangePermissionsAction.class)+" :"));
076 mainPanel.addSpace(10);
077
078 JPanel gridPanel = new JPanel(new GridLayout(4, 4));
079 permCheckBoxes = new JCheckBox[5][5];
080 JCheckBox permCheckBox;
081
082 AbstractFile firstFile = files.fileAt(0);
083 int permSetMask = firstFile.getChangeablePermissions().getIntValue();
084 boolean canSetPermission = permSetMask!=0;
085 int defaultPerms = firstFile.getPermissions().getIntValue();
086
087 gridPanel.add(new JLabel());
088 gridPanel.add(new JLabel(Translator.get("permissions.read")));
089 gridPanel.add(new JLabel(Translator.get("permissions.write")));
090 gridPanel.add(new JLabel(Translator.get("permissions.executable")));
091
092 for(int a=USER_ACCESS; a>=OTHER_ACCESS; a--) {
093 gridPanel.add(new JLabel(Translator.get(a==USER_ACCESS ?"permissions.user":a==GROUP_ACCESS?"permissions.group":"permissions.other")));
094
095 for(int p=READ_PERMISSION; p>=EXECUTE_PERMISSION; p=p>>1) {
096 permCheckBox = new JCheckBox();
097 permCheckBox.setSelected((defaultPerms & (p<<a*3))!=0);
098
099 // Enable the checkbox only if the permission can be set in the destination
100 if((permSetMask & (p<<a*3))==0)
101 permCheckBox.setEnabled(false);
102 else
103 permCheckBox.addItemListener(this);
104
105 gridPanel.add(permCheckBox);
106 permCheckBoxes[a][p] = permCheckBox;
107 }
108 }
109
110 mainPanel.add(gridPanel);
111
112 octalPermTextField = new JTextField(3);
113 // Constrains text field to 3 digits, from 0 to 7 (octal base)
114 Document doc = new SizeConstrainedDocument(3) {
115 public void insertString(int offset, String str, AttributeSet attributeSet) throws BadLocationException {
116 int strLen = str.length();
117 char c;
118 for(int i=0; i<strLen; i++) {
119 c = str.charAt(i);
120 if(c<'0' || c>'7')
121 return;
122 }
123
124 super.insertString(offset, str, attributeSet);
125 }
126 };
127 octalPermTextField.setDocument(doc);
128 // Initializes the field's value
129 updateOctalPermTextField();
130
131 if(canSetPermission) {
132 setInitialFocusComponent(octalPermTextField);
133 doc.addDocumentListener(this);
134 }
135 // Disable text field if no permission bit can be set
136 else {
137 octalPermTextField.setEnabled(false);
138 }
139
140 mainPanel.addSpace(10);
141 JPanel tempPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
142 tempPanel.add(new JLabel(Translator.get("permissions.octal_notation")));
143 tempPanel.add(octalPermTextField);
144 mainPanel.add(tempPanel);
145
146 mainPanel.addSpace(15);
147
148 recurseDirCheckBox = new JCheckBox(Translator.get("recurse_directories"));
149 // Disable check box if no permission bit can be set
150 recurseDirCheckBox.setEnabled(canSetPermission && (files.size()>1 || ((AbstractFile)files.elementAt(0)).isDirectory()));
151 mainPanel.add(recurseDirCheckBox);
152
153 // Create file details button and OK/cancel buttons and lay them out a single row
154 JPanel fileDetailsPanel = createFileDetailsPanel();
155
156 okButton = new JButton(Translator.get("change"));
157 cancelButton = new JButton(Translator.get("cancel"));
158
159 mainPanel.add(createButtonsPanel(createFileDetailsButton(fileDetailsPanel),
160 DialogToolkit.createOKCancelPanel(okButton, cancelButton, getRootPane(), this)));
161 mainPanel.add(fileDetailsPanel);
162
163 getContentPane().add(mainPanel, BorderLayout.NORTH);
164
165 if(!canSetPermission) {
166 // Disable OK button if no permission bit can be set
167 okButton.setEnabled(false);
168 }
169
170 getRootPane().setDefaultButton(canSetPermission?okButton:cancelButton);
171 setResizable(false);
172 }
173
174
175 /**
176 * Creates and returns a permissions int using the values of the permission checkboxes.
177 */
178 private int getPermInt() {
179 JCheckBox permCheckBox;
180 int perms = 0;
181
182 for(int a=USER_ACCESS; a>=OTHER_ACCESS; a--) {
183 for(int p=READ_PERMISSION; p>=EXECUTE_PERMISSION; p=p>>1) {
184 permCheckBox = permCheckBoxes[a][p];
185
186 if(permCheckBox.isSelected())
187 perms |= (p<<a*3);
188 }
189 }
190
191 return perms;
192 }
193
194
195 /**
196 * Updates the octal permissions text field's value to reflect the permission checkboxes' values.
197 */
198 private void updateOctalPermTextField() {
199 String octalStr = Integer.toOctalString(getPermInt());
200 int len = octalStr.length();
201 for(int i=len; i<3; i++)
202 octalStr = "0"+octalStr;
203
204 octalPermTextField.setText(octalStr);
205 }
206
207
208 /**
209 * Updates the permission checkboxes' values to reflect the octal permissions text field.
210 */
211 private void updatePermCheckBoxes() {
212 JCheckBox permCheckBox;
213 String octalStr = octalPermTextField.getText();
214
215 int perms = octalStr.equals("")?0:Integer.parseInt(octalStr, 8);
216
217 for(int a=USER_ACCESS; a>=OTHER_ACCESS; a--) {
218 for(int p=READ_PERMISSION; p>=EXECUTE_PERMISSION; p=p>>1) {
219 permCheckBox = permCheckBoxes[a][p];
220
221 // if(permCheckBox.isEnabled())
222 permCheckBox.setSelected((perms & (p<<a*3))!=0);
223 }
224 }
225
226 }
227
228
229 ///////////////////////////////////
230 // ActionListener implementation //
231 ///////////////////////////////////
232
233 public void actionPerformed(ActionEvent e) {
234 Object source = e.getSource();
235
236 if(source==okButton) {
237 dispose();
238
239 // Starts copying files
240 ProgressDialog progressDialog = new ProgressDialog(mainFrame, Translator.get("progress_dialog.processing_files"));
241 ChangeFileAttributesJob job = new ChangeFileAttributesJob(progressDialog, mainFrame, files, getPermInt(), recurseDirCheckBox.isSelected());
242 progressDialog.start(job);
243 }
244 else if(source==cancelButton) {
245 dispose();
246 }
247 }
248
249
250 /////////////////////////////////
251 // ItemListener implementation //
252 /////////////////////////////////
253
254 // Update the octal permission text field whenever one of the permission checkboxes' value has changed
255
256 public void itemStateChanged(ItemEvent e) {
257 if(ignoreItemEvent)
258 return;
259
260 ignoreDocumentEvent = true;
261 updateOctalPermTextField();
262 ignoreDocumentEvent = false;
263 }
264
265
266 //////////////////////////////
267 // DocumentListener methods //
268 //////////////////////////////
269
270 // Update the permission checkboxes' values whenever the octal permission text field has changed
271
272 public void changedUpdate(DocumentEvent e) {
273 if(ignoreDocumentEvent)
274 return;
275
276 ignoreItemEvent = true;
277 updatePermCheckBoxes();
278 ignoreItemEvent = false;
279 }
280
281 public void insertUpdate(DocumentEvent e) {
282 if(ignoreDocumentEvent)
283 return;
284
285 ignoreItemEvent = true;
286 updatePermCheckBoxes();
287 ignoreItemEvent = false;
288 }
289
290 public void removeUpdate(DocumentEvent e) {
291 if(ignoreDocumentEvent)
292 return;
293
294 ignoreItemEvent = true;
295 updatePermCheckBoxes();
296 ignoreItemEvent = false;
297 }
298 }