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    }