├── .idea
├── .gitignore
├── encodings.xml
├── misc.xml
├── modules.xml
├── uiDesigner.xml
└── vcs.xml
├── README.md
├── img
└── Package controller.png
├── mvc_file_manager.iml
├── out
└── production
│ └── mvc_file_manager
│ ├── META-INF
│ └── mvc_file_manager.kotlin_module
│ ├── main
│ ├── Util
│ │ ├── FileCode.class
│ │ ├── FileCopy.class
│ │ ├── FileIconCreate.class
│ │ └── FileZip.class
│ ├── controller
│ │ ├── FileController$CopyListener.class
│ │ ├── FileController$CreateFileListener.class
│ │ ├── FileController$DecodeListener.class
│ │ ├── FileController$DeleteListener.class
│ │ ├── FileController$EncodeListener.class
│ │ ├── FileController$FileClickListener.class
│ │ ├── FileController$FileTreeSelListener.class
│ │ ├── FileController$GoListener.class
│ │ ├── FileController$PasteListener.class
│ │ ├── FileController$RedoListener.class
│ │ ├── FileController$UnZipListener.class
│ │ ├── FileController$UndoListener.class
│ │ ├── FileController$ZipListener.class
│ │ └── FileController.class
│ ├── model
│ │ ├── FileModel.class
│ │ └── FileTreeModel.class
│ └── view
│ │ ├── App.class
│ │ ├── FileDisplay.class
│ │ ├── FileListCellRenderer.class
│ │ ├── FilesTree$DefaultFileNodes.class
│ │ └── FilesTree.class
│ └── res
│ └── ui.png
└── src
├── main
├── Util
│ ├── FileCode.java
│ ├── FileCopy.java
│ ├── FileIconCreate.java
│ └── FileZip.java
├── controller
│ └── FileController.java
├── model
│ ├── FileModel.java
│ └── FileTreeModel.java
└── view
│ ├── App.java
│ ├── FileDisplay.java
│ ├── FileListCellRenderer.java
│ └── FilesTree.java
└── res
└── ui.png
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 设计思想
2 |
3 | ## `MVC`设计模式
4 |
5 | 本项目引入`mvc`设计模式。
6 |
7 | MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。
8 |
9 | - **Model(模型)** - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
10 | - **View(视图)** - 视图代表模型包含的数据的可视化。
11 | - **Controller(控制器)** - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
12 |
13 | `Java swing`的设计本身就是MVC设计的典范。在这里我们也采用MVC设计模式(尽管并不是那么贴合),与`swing`本身相结合。
14 |
15 | 对我们来说,**view**由`Java swing`负责显示,**model**是我们自己建立的数据结构,同时结合`swing`自身的**model**,所有操作的具体实现由**model**负责,即涉及到和系统文件层的交互等,**controller**由我们自行创建,负责添加事件监听并做出相应的逻辑操作,对界面的多个部分进行协同配合,完成视图的更新和数据的更新。
16 |
17 | # 各模块功能实现
18 |
19 | ## 功能实现清单
20 |
21 | 1. **界面跳转:**
22 | 1. 后退,逐级返回到父级目录。
23 | 2. 前进,按照后退的顺序依次进入子目录。
24 | 3. 当前目录路径显示,输入目录地址进行跳转。
25 | 2. **界面显示:**
26 | 1. 文件目录树显示(只显示文件夹)。
27 | 2. 文件列表显示(只显示当前目录下的文件夹和文件),带有系统图标。
28 | 3. 点击文件树中的结点,在文件列表同步显示。
29 | 3. **选择与打开**
30 | 1. 支持多选,选中文件高亮显示,点击空白区域取消选中。
31 | 2. 单击文件夹进入,单击文件打开应用。
32 | 4. **右键选中文件**
33 | 1. 批量复制,包括所有子目录和文件。
34 | 2. 批量压缩,格式为zip,将多个文件压缩成一个,自定义命名。
35 | 3. 单文件加密,自定义加密密钥。
36 | 5. **右键空白区域**
37 | 1. 创建文件夹,自定义命名;
38 | 2. 批量粘贴,粘贴的目录不能是源文件目录的子目录;
39 | 3. 批量解压,解压到当前目录下。
40 | 4. 单文件解密,输入加密密钥,若密钥不正确,加密后的文件仍无法打开。
41 |
42 | ## 类的设计
43 |
44 | 采用MVC设计模式。
45 |
46 | ### package view
47 |
48 | 1. #### **class App**
49 |
50 | 1. 总的界面窗口UI,继承自`JFrame`类,采用Border-layout布局。
51 |
52 | 2. 工具栏`toolbar`位于上方,实现后退、前进、当前文件路径显示和路径跳转。
53 |
54 | 3. 左侧是文件树,中部是文件显示区域。
55 |
56 | 4. 实现对应的`addListener`函数,如:
57 |
58 | ```java
59 | public void addUndoListener(ActionListener undo){
60 | undoButton.addActionListener(undo);
61 | }
62 | public void addRedoListener(ActionListener redo){
63 | redoButton.addActionListener(redo);
64 | }
65 | ```
66 |
67 | 2. #### **class FileDisplay**
68 |
69 | 1. 文件主显示区,继承自`JList`,以文件列表的形式显示。
70 | 2. 内嵌了`JPopMenu`,添加`JMenuItem`,用以实现功能。
71 | 3. 实现`updateView`函数,更新视图。
72 | 4. 实现对应的`addListener`函数,添加事件监听。
73 |
74 | 3. #### **class FilesTree**
75 |
76 | 1. 文件目录树,继承自`JTree`。
77 | 2. 实现了`loadingTree`函数,更新视图。
78 | 3. 实现对应的`addListener`函数,添加事件监听。
79 |
80 | 4. #### **class FileListCellRenderer**
81 |
82 | 1. 文件列表渲染类,继承自`DefaultListCellRenderer`。
83 |
84 | ### package model
85 |
86 | 1. #### **class FileModel**
87 |
88 | 1. 文件实体类,具体实现了文件管理的各个功能。
89 |
90 | 2. #### **class FileTreeModel**
91 |
92 | 1. 对应于文件树。
93 |
94 | ### package controller
95 |
96 | 1. #### **class FileController**
97 |
98 | 1. 包含了
99 |
100 | ```java
101 | private App app; // 窗体UI
102 | private FileDisplay fileDisplay; // 为了方便调用,内部存储
103 | private FilesTree filesTree; // 为了方便调用,内部存储
104 | private FileModel fileModel; // File Model
105 | private FileTreeModel treeModel; // Tree Model
106 | ```
107 |
108 | 2. 初始化的时候,由`model`,`view`参数:
109 |
110 | ```java
111 | public FileController(App app, FileModel fileModel, FileTreeModel treeModel)
112 | ```
113 |
114 | 3. 实现了对应的事件监听类接口,添加方法。
115 |
116 | 4. 调用`model`和`view`实现。
117 |
118 | ### package util
119 |
120 | 1. #### **class FileCode** 文件加密工具类
121 |
122 | 2. #### **class FileCopy** 文件复制工具类
123 |
124 | 3. #### **class FileIconCreate** 系统图标工具类
125 |
126 | 4. #### **class FileZip** 文件压缩工具类
127 |
128 |
129 |
130 | ## 算法设计
131 |
132 | 主要是`FileModel`的实现
133 |
134 | 1. ### 数据记录
135 |
136 | 1. 由`List defaultListModel`和`File currentFile`负责记录当前目录和目录下的文件、文件夹。
137 |
138 | 2. ### **初始化**
139 |
140 | 1. `initRoots`方法,将磁盘目录,如`C:\`,`D:\`,`E:\`添加进文件列表。
141 |
142 | 3. ### **文件更新**
143 |
144 | 1. `updateModels(File file)`方法,将当前文件设为`file`,并将其当前目录层级下的文件(夹)添加进文件列表。
145 |
146 | 4. ### **前进和后退**
147 |
148 | 1. 由`redoStack`负责记录操作路径,undo时,得到父级目录下文件和文件夹,更新文件列表,并将文件压入`redoStack`。
149 | 2. redo时,弹出栈顶文件,更新当前文件列表。
150 | 3. 细节,如果父级为`null`,说明当前是磁盘根目录,如`D:\`,则调用初始化方法`initRoots`。
151 |
152 | 5. ### **文件复制**
153 |
154 | 1. 由`copySources`负责存储粘贴板中的文件。
155 | 2. 选中文件后,加入到`copySources`中。
156 | 3. 粘贴时,判断是文件,还是文件夹
157 | 1. 如果是文件,则进行文件复制。
158 | 2. 如果是文件夹,递归调用。
159 | 4. 异常处理,粘贴的目录不能是源目录的子目录,由`isParent`进行判断。
160 |
161 | 6. ### **创建和删除**
162 |
163 | 1. 如果不存在,则创建。
164 | 2. 递归删除,由`deleteHelp`负责递归,如果删除的是文件夹,就递归调用,最终删除根文件。
165 | 3. 对选中的文件遍历执行,批量删除。
166 |
167 | 7. ### **压缩和解压**
168 |
169 | 1. 对于文件,直接压缩。
170 | 2. 对于文件夹,递归压缩。
171 |
172 | 8. ### **文件加密和解密**
173 |
174 | 1. 对文件读取字节内容,与密钥进行简单的异或加密。
175 | 2. 加密后得到新文件,删除原始文件,并将新文件重命名为原文件。(也可以不删除,防止出问题,重要文件)
176 | 3. 解密的时候,与加密过程相同,输入密钥,读取文件,异或解密。
177 |
178 | 9. ### **取消选中**
179 |
180 | 1. 重写`locationToIndex`函数,判断点击的位置是否有效
181 |
182 | ```java
183 | int index = super.locationToIndex(location);
184 | if (index != -1 && !getCellBounds(index, index).contains(location)) return -1;
185 | ```
186 |
187 | 10. ### **样式渲染**
188 |
189 | 1. 实现`FileListCellRenderer`类继承`DefaultListCellRenderer`
190 | 2. 设置图标,设置显示信息,磁盘根目录使用`getPath()`,其他目录使用`getName()`。
191 | 3. 选中设置颜色,取消选中恢复之前的颜色。
192 |
193 | 11. ### **文件树展开**
194 |
195 | 1. 实现`TreeSelectionListener`接口,采用延迟加载,即每次点击展开时再加载目录文件。
196 |
197 | # 软件调试分析
198 |
199 | ## 执行过程
200 |
201 | 1. 启动`App`类的`main`函数
202 |
203 | 2. 在`FileController`初始化的时候,完成对数据成员的初始化,包括添加事件监听,更新初始视图。
204 |
205 | ```java
206 | public FileController(App app, FileModel fileModel, FileTreeModel treeModel) {
207 | this.fileDisplay = app.getFileDisplay();
208 | this.app = app;
209 | this.fileModel = fileModel;
210 | this.filesTree = app.getFilesTree();
211 | this.treeModel = treeModel;
212 | // 添加事件监听
213 | fileDisplay.addCopyListener(new CopyListener());
214 | fileDisplay.addCreateListener(new CreateFileListener());
215 | fileDisplay.addPasteListener(new PasteListener());
216 | fileDisplay.addUnzipListener(new UnZipListener());
217 | fileDisplay.addZipListener(new ZipListener());
218 | fileDisplay.addDeleteListener(new DeleteListener());
219 | fileDisplay.addMyMouseListener(new FileClickListener());
220 | fileDisplay.addEncodeListener(new EncodeListener());
221 | fileDisplay.addDecodeListener(new DecodeListener());
222 | app.addUndoListener(new UndoListener());
223 | app.addRedoListener(new RedoListener());
224 | app.addGoListener(new GoListener());
225 | filesTree.addTreeSelectionListener(new FileTreeSelListener());
226 | filesTree.setRootVisible(false);
227 | // 初始化主视图
228 | fileDisplay.updateView(fileModel.getDefaultListModel());
229 | }
230 | ```
231 |
232 | 3. 监听到事件发生,调用`model`和`view`进行逻辑实现并更新视图。例如,展开文件目录树时,需要对三部分视图进行更新(文件树,文件列表,url地址栏)
233 |
234 | ```java
235 | class FileTreeSelListener implements TreeSelectionListener {
236 | @Override
237 | public void valueChanged(TreeSelectionEvent e) {
238 | File file = filesTree.getCurrentFile(); // 得到点击的文件结点
239 | // 加载目录树
240 | filesTree.loadingTree((DefaultMutableTreeNode)
241 | filesTree.getLastSelectedPathComponent(), treeModel.getFiles(file));
242 | // 更新文件数据模型
243 | fileModel.updateModels(file);
244 | // 更新文件列表视图
245 | fileDisplay.updateView(fileModel.getDefaultListModel());
246 | // 更新url地址显示
247 | String url = fileModel.getUrl();
248 | app.updateUrl(url);
249 | }
250 | }
251 | ```
252 |
253 | 再如,undo操作时,`model`进行undo,更新数据,然后视图更新
254 |
255 | ```java
256 | class UndoListener implements ActionListener{
257 | @Override
258 | public void actionPerformed(ActionEvent e) {
259 | fileModel.undo(); // 数据模型更新
260 | fileDisplay.updateView(fileModel.getDefaultListModel()); // 列表视图更新
261 | String url = fileModel.getUrl();
262 | app.updateUrl(url); // url地址栏更新
263 | }
264 | }
265 | ```
266 |
267 | ## 界面展示
268 |
269 |
270 |
271 | ### 主界面
272 |
273 |
274 |
275 | - 上侧可以后撤、前进,url地址栏显示
276 | - 左侧目录树展开,并同步更新右侧文件列表
277 |
278 | ### 文件及文件夹拷贝
279 |
280 |
281 |
282 | 右键粘贴,将子目录下所有文件(夹)粘贴
283 |
284 | ### 解压压缩
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 | ### 文件加密
293 |
294 | (以整数`int`类型为密钥,**加密需要等待一些时间**)
295 |
296 | 解密后,检查是否可以正常打开.
297 |
298 | **注:**对文件夹进行加密是无效的
299 |
300 | ### 其他
301 |
302 | 见项目
--------------------------------------------------------------------------------
/img/Package controller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/img/Package controller.png
--------------------------------------------------------------------------------
/mvc_file_manager.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/META-INF/mvc_file_manager.kotlin_module:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/Util/FileCode.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/Util/FileCode.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/Util/FileCopy.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/Util/FileCopy.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/Util/FileIconCreate.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/Util/FileIconCreate.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/Util/FileZip.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/Util/FileZip.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$CopyListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$CopyListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$CreateFileListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$CreateFileListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$DecodeListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$DecodeListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$DeleteListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$DeleteListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$EncodeListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$EncodeListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$FileClickListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$FileClickListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$FileTreeSelListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$FileTreeSelListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$GoListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$GoListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$PasteListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$PasteListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$RedoListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$RedoListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$UnZipListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$UnZipListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$UndoListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$UndoListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController$ZipListener.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController$ZipListener.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/controller/FileController.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/controller/FileController.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/model/FileModel.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/model/FileModel.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/model/FileTreeModel.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/model/FileTreeModel.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/view/App.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/App.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/view/FileDisplay.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/FileDisplay.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/view/FileListCellRenderer.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/FileListCellRenderer.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/view/FilesTree$DefaultFileNodes.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/FilesTree$DefaultFileNodes.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/main/view/FilesTree.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/main/view/FilesTree.class
--------------------------------------------------------------------------------
/out/production/mvc_file_manager/res/ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/out/production/mvc_file_manager/res/ui.png
--------------------------------------------------------------------------------
/src/main/Util/FileCode.java:
--------------------------------------------------------------------------------
1 | package main.Util;
2 |
3 | import java.io.*;
4 |
5 | /**
6 | * 文件加密工具类
7 | */
8 | public class FileCode {
9 | private static int dataOfFile = 0; //文件字节内容
10 |
11 | /**
12 | * 加密
13 | * @param srcFile 要加密的文件
14 | * @param encFile 加密后的文件
15 | * @param key 密钥
16 | * @throws Exception 异常
17 | */
18 | public static void EncFile(File srcFile, File encFile, int key) throws Exception {
19 | // 检查是否存在
20 | if(!srcFile.exists()){
21 | System.out.println("source file not exixt");
22 | return;
23 | }
24 | if(!encFile.exists()){
25 | System.out.println("encrypt file created");
26 | encFile.createNewFile();
27 | }
28 | InputStream fis = new FileInputStream(srcFile);
29 | OutputStream fos = new FileOutputStream(encFile);
30 |
31 | while ((dataOfFile = fis.read()) > -1) {
32 | fos.write(dataOfFile^key);
33 | }
34 |
35 | fis.close();
36 | fos.flush();
37 | fos.close();
38 | }
39 |
40 | /**
41 | * 解密
42 | * @param encFile 加密了的文件
43 | * @param decFile 要解密生成的文件
44 | * @param key 密钥
45 | * @throws Exception 异常
46 | */
47 | public static void DecFile(File encFile, File decFile, int key) throws Exception {
48 | if(!encFile.exists()){
49 | System.out.println("encrypt file not exixt");
50 | return;
51 | }
52 |
53 | if(!decFile.exists()){
54 | System.out.println("decrypt file created");
55 | decFile.createNewFile();
56 | }
57 | InputStream fis = new FileInputStream(encFile);
58 | OutputStream fos = new FileOutputStream(decFile);
59 | while ((dataOfFile = fis.read()) > -1) {fos.write(dataOfFile^key); }
60 |
61 | fis.close();
62 | fos.flush();
63 | fos.close();
64 | }
65 |
66 | /**
67 | * 测试类
68 | * @param args 参数
69 | */
70 | public static void main(String[] args) {
71 | File srcFile = new File("E:\\Alice\\html\\whale2.jpg"); //初始文件
72 | File encFile = new File("E:\\Alice\\html\\whale2Encode.jpg"); //加密文件
73 | File decFile = new File("E:\\Alice\\html\\whale2Decode.jpg"); //解密文件
74 |
75 | try {
76 | EncFile(srcFile, encFile,5123); //加密操作
77 | DecFile(encFile, decFile, 51223);
78 | } catch (Exception e) {
79 | e.printStackTrace();
80 | }
81 | }
82 |
83 | }
--------------------------------------------------------------------------------
/src/main/Util/FileCopy.java:
--------------------------------------------------------------------------------
1 | package main.Util;
2 |
3 | import javax.swing.*;
4 | import java.io.*;
5 |
6 | /**
7 | * 文件拷贝工具类
8 | */
9 | public class FileCopy {
10 | /**
11 | * 拷贝文件
12 | * @param sourceUrl 源文件
13 | * @param targetUrl 目标文件
14 | */
15 | public static void copyFile(String sourceUrl, String targetUrl){
16 | File source = new File(sourceUrl);
17 | File target = new File(targetUrl);
18 | try(BufferedInputStream bis=new BufferedInputStream(new FileInputStream(source));
19 | BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(target))) {
20 | int len = 0;
21 | byte[] flush = new byte[1024];
22 | while((len=bis.read(flush)) != -1) {
23 | bos.write(flush, 0, len);
24 | }
25 | bos.flush();
26 | } catch(IOException e) {
27 | e.printStackTrace();
28 | }
29 | }
30 |
31 | /**
32 | * 判断是否是其父级目录
33 | * @param f1 f1 is Parent?
34 | * @param f2 f2 is child?
35 | * @return true if f1 is parent of f2, else not
36 | */
37 | private static boolean isParent(File f1, File f2){
38 | if(f2.getParentFile() == null){
39 | return false;
40 | }else if(f2.getParentFile().equals(f1)){
41 | return true;
42 | }else{
43 | return isParent(f1, f2.getParentFile());
44 | }
45 | }
46 |
47 | /**
48 | * 拷贝文件夹
49 | * @param sourceUrl 源文件夹
50 | * @param targetUrl 目标文件夹
51 | */
52 | public static void copyDir(String sourceUrl, String targetUrl){
53 | File source = new File(sourceUrl);
54 | File target = new File(targetUrl);
55 | if(isParent(source, target)){
56 | System.out.println("Can't copy!");
57 | JOptionPane.showMessageDialog(null, "source path is father of target", "Copy Error",JOptionPane.ERROR_MESSAGE);
58 | return;
59 | };
60 | String[] filesUrl = source.list();
61 | // 新建
62 | if(!target.exists()){
63 | target.mkdir();
64 | }
65 | for(String fileUrl : filesUrl){
66 | String tmp1 = sourceUrl + File.separator + fileUrl;
67 | String tmp2 = targetUrl + File.separator + fileUrl;
68 | if(new File(tmp1).isDirectory()){
69 | copyDir(tmp1, tmp2);//递归调用
70 | }else{
71 | copyFile(tmp1, tmp2); // 直接拷贝文件
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/Util/FileIconCreate.java:
--------------------------------------------------------------------------------
1 | package main.Util;
2 |
3 | import javax.swing.*;
4 | import javax.swing.filechooser.FileSystemView;
5 | import java.io.File;
6 |
7 | /**
8 | * 创建文件图标工具类
9 | * 得到系统图标
10 | */
11 | public class FileIconCreate {
12 | public static Icon getSmallIcon(File f ) {
13 | if ( f != null && f.exists() ) {
14 | FileSystemView fsv = FileSystemView.getFileSystemView();
15 | return(fsv.getSystemIcon( f ) );
16 | }
17 | return(null);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/Util/FileZip.java:
--------------------------------------------------------------------------------
1 | package main.Util;
2 |
3 | import java.io.*;
4 | import java.util.Enumeration;
5 | import java.util.List;
6 | import java.util.zip.*;
7 |
8 | /**
9 | * 文件压缩工具类
10 | * 密钥(int)与文件字节异或加密
11 | */
12 | public class FileZip {
13 | static final int BUFFER = 8192;
14 |
15 | /**
16 | * 压缩
17 | * @param pathName 要压缩的文件列表
18 | * @param zipFilePath 压缩后的路径
19 | */
20 | public static void compress(List pathName, String zipFilePath) {
21 | File zipFile = new File(zipFilePath);
22 | ZipOutputStream out = null;
23 | try {
24 | FileOutputStream fileOutputStream = new FileOutputStream(zipFile);
25 | CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream,
26 | new CRC32());
27 | out = new ZipOutputStream(cos);
28 | String basedir = "";
29 | for (int i = 0; i < pathName.size(); i++) {
30 | compress(new File(pathName.get(i)), out, basedir);
31 | }
32 | out.close();
33 | } catch (Exception e) {
34 | throw new RuntimeException(e);
35 | }
36 | }
37 |
38 | /**
39 | * 压缩单个文件
40 | * @param srcPathName 源文件
41 | * @param targetPathName 目标地址
42 | */
43 | public static void compress(String srcPathName, String targetPathName) {
44 | File zipFile = new File(targetPathName);
45 | File file = new File(srcPathName);
46 | if (!file.exists())
47 | throw new RuntimeException(srcPathName + "不存在!");
48 | try {
49 | FileOutputStream fileOutputStream = new FileOutputStream(zipFile);
50 | CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream,
51 | new CRC32());
52 | ZipOutputStream out = new ZipOutputStream(cos);
53 | String basedir = "";
54 | compress(file, out, basedir);
55 | out.close();
56 | } catch (Exception e) {
57 | throw new RuntimeException(e);
58 | }
59 | }
60 |
61 | /**
62 | * 压缩
63 | * @param file 选中的文件
64 | * @param out 输出
65 | * @param basedir 基目录
66 | */
67 | private static void compress(File file, ZipOutputStream out, String basedir) {
68 | /* 判断是目录还是文件 */
69 | if (file.isDirectory()) {
70 | System.out.println("压缩:" + basedir + file.getName());
71 | compressDirectory(file, out, basedir);
72 | } else {
73 | System.out.println("压缩:" + basedir + file.getName());
74 | compressFile(file, out, basedir);
75 | }
76 | }
77 |
78 | /**
79 | * 压缩一个目录
80 | */
81 | private static void compressDirectory(File dir, ZipOutputStream out, String basedir) {
82 | if (!dir.exists())
83 | return;
84 |
85 | File[] files = dir.listFiles();
86 | for (int i = 0; i < files.length; i++) {
87 | /* 递归 */
88 | compress(files[i], out, basedir + dir.getName() + "/");
89 | }
90 | }
91 |
92 | /**
93 | * 压缩一个文件
94 | */
95 | private static void compressFile(File file, ZipOutputStream out, String basedir) {
96 | if (!file.exists()) {
97 | return;
98 | }
99 | try {
100 | BufferedInputStream bis = new BufferedInputStream(
101 | new FileInputStream(file));
102 | ZipEntry entry = new ZipEntry(basedir + file.getName());
103 | out.putNextEntry(entry);
104 | int count;
105 | byte data[] = new byte[BUFFER];
106 | while ((count = bis.read(data, 0, BUFFER)) != -1) {
107 | out.write(data, 0, count);
108 | }
109 | bis.close();
110 | } catch (Exception e) {
111 | throw new RuntimeException(e);
112 | }
113 | }
114 |
115 | /**
116 | * 解压文件
117 | * @param inputFile 要解压的文件
118 | * @param destDirPath 目标文件
119 | * @throws Exception 异常
120 | */
121 | public static void zipUncompress(String inputFile,String destDirPath) throws Exception {
122 | File srcFile = new File(inputFile);//获取当前压缩文件
123 | // 判断源文件是否存在
124 | if (!srcFile.exists()) {
125 | throw new Exception(srcFile.getPath() + "所指文件不存在");
126 | }
127 | ZipFile zipFile = new ZipFile(srcFile);//创建压缩文件对象
128 | //开始解压
129 | Enumeration> entries = zipFile.entries();
130 | while (entries.hasMoreElements()) {
131 | ZipEntry entry = (ZipEntry) entries.nextElement();
132 | // 如果是文件夹,就创建个文件夹
133 | if (entry.isDirectory()) {
134 | String dirPath = destDirPath + "/" + entry.getName();
135 | srcFile.mkdirs();
136 | } else {
137 | // 如果是文件,就先创建一个文件,然后用io流把内容copy过去
138 | File targetFile = new File(destDirPath + "/" + entry.getName());
139 | // 保证这个文件的父文件夹必须要存在
140 | if (!targetFile.getParentFile().exists()) {
141 | targetFile.getParentFile().mkdirs();
142 | }
143 | targetFile.createNewFile();
144 | // 将压缩文件内容写入到这个文件中
145 | InputStream is = zipFile.getInputStream(entry);
146 | FileOutputStream fos = new FileOutputStream(targetFile);
147 | int len;
148 | byte[] buf = new byte[1024];
149 | while ((len = is.read(buf)) != -1) {
150 | fos.write(buf, 0, len);
151 | }
152 | // 关流顺序,先打开的后关闭
153 | fos.close();
154 | is.close();
155 | }
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/main/controller/FileController.java:
--------------------------------------------------------------------------------
1 | package main.controller;
2 |
3 | import main.model.FileModel;
4 | import main.view.FileDisplay;
5 | import main.model.FileTreeModel;
6 | import main.view.FilesTree;
7 | import main.view.App;
8 |
9 | import java.awt.event.ActionEvent;
10 | import java.awt.event.ActionListener;
11 | import java.awt.event.MouseAdapter;
12 | import java.awt.event.MouseEvent;
13 | import java.io.File;
14 | import java.util.List;
15 |
16 | import javax.swing.JList;
17 | import javax.swing.JOptionPane;
18 | import javax.swing.event.TreeSelectionEvent;
19 | import javax.swing.event.TreeSelectionListener;
20 | import javax.swing.tree.DefaultMutableTreeNode;
21 |
22 | public class FileController {
23 | private FileDisplay fileDisplay; // 文件列表显示区
24 | private App app; // 主窗口UI
25 | private FileModel fileModel; // 文件数据
26 | private FilesTree filesTree; // 文件树
27 | private FileTreeModel treeModel; // 文件树数据
28 | public FileController(App app, FileModel fileModel, FileTreeModel treeModel) {
29 | // 初始化赋值
30 | this.fileDisplay = app.getFileDisplay();
31 | this.app = app;
32 | this.fileModel = fileModel;
33 | this.filesTree = app.getFilesTree();
34 | this.treeModel = treeModel;
35 | // 初始化添加事件监听
36 | fileDisplay.addCopyListener(new CopyListener());
37 | fileDisplay.addCreateListener(new CreateFileListener());
38 | fileDisplay.addPasteListener(new PasteListener());
39 | fileDisplay.addUnzipListener(new UnZipListener());
40 | fileDisplay.addZipListener(new ZipListener());
41 | fileDisplay.addDeleteListener(new DeleteListener());
42 | fileDisplay.addMyMouseListener(new FileClickListener());
43 | fileDisplay.addEncodeListener(new EncodeListener());
44 | fileDisplay.addDecodeListener(new DecodeListener());
45 | app.addUndoListener(new UndoListener());
46 | app.addRedoListener(new RedoListener());
47 | app.addGoListener(new GoListener());
48 | filesTree.addTreeSelectionListener(new FileTreeSelListener());
49 | filesTree.setRootVisible(false);
50 | // 初始化更新视图
51 | fileDisplay.updateView(fileModel.getDefaultListModel());
52 |
53 | }
54 |
55 | /**
56 | * copy 事件监听类
57 | */
58 | class CopyListener implements ActionListener{
59 | @Override
60 | public void actionPerformed(ActionEvent e) {
61 | @SuppressWarnings("unchecked")
62 | List valuesList = fileDisplay.getSelectedValuesList();
63 | fileModel.addCopySources(valuesList); // 添加进剪切板
64 | fileDisplay.updateView(fileModel.getDefaultListModel()); // 更新视图
65 | }
66 |
67 | }
68 |
69 | /**
70 | * 压缩监听类
71 | */
72 | class ZipListener implements ActionListener{
73 | @Override
74 | public void actionPerformed(ActionEvent e) {
75 | @SuppressWarnings("unchecked")
76 | List valuesList = fileDisplay.getSelectedValuesList();
77 | String name = JOptionPane.showInputDialog("input the name of zip");// 得到文件名
78 | fileModel.zipCompress(valuesList, name);// 压缩
79 | fileDisplay.updateView(fileModel.getDefaultListModel()); // 更新视图
80 | }
81 | }
82 |
83 | /**
84 | * 解压事件类
85 | */
86 | class UnZipListener implements ActionListener{
87 | @Override
88 | public void actionPerformed(ActionEvent e) {
89 | @SuppressWarnings("unchecked")
90 | List fileList = fileDisplay.getSelectedValuesList();
91 | fileModel.zipUnCompress(fileList);
92 | fileDisplay.updateView(fileModel.getDefaultListModel());
93 | }
94 | }
95 |
96 | /**
97 | * 粘贴事件类
98 | */
99 | class PasteListener implements ActionListener{
100 | @Override
101 | public void actionPerformed(ActionEvent e) {
102 | fileModel.paste();
103 | fileDisplay.updateView(fileModel.getDefaultListModel());
104 | }
105 | }
106 |
107 | /**
108 | * 新建文件夹事件类
109 | */
110 | class CreateFileListener implements ActionListener{
111 | @Override
112 | public void actionPerformed(ActionEvent e) {
113 | String name = JOptionPane.showInputDialog("please input the name of dict");
114 | if(name != null){
115 | fileModel.createFile(name);
116 | }
117 | fileDisplay.updateView(fileModel.getDefaultListModel());
118 | }
119 | }
120 |
121 | /**
122 | * 删除事件类
123 | */
124 | class DeleteListener implements ActionListener{
125 | @Override
126 | public void actionPerformed(ActionEvent e) {
127 | @SuppressWarnings("unchecked")
128 | List fileList = fileDisplay.getSelectedValuesList();
129 | fileModel.delete(fileList);
130 | fileDisplay.updateView(fileModel.getDefaultListModel());
131 | }
132 | }
133 |
134 | /**
135 | * 加密事件类
136 | */
137 | class EncodeListener implements ActionListener{
138 | @Override
139 | public void actionPerformed(ActionEvent e) {
140 | @SuppressWarnings("unchecked")
141 | List files = (List) fileDisplay.getSelectedValuesList();
142 | int key = Integer.parseInt(JOptionPane.showInputDialog("please input the key(int)"));
143 | fileModel.enCrypt(files, key);
144 | fileDisplay.updateView(fileModel.getDefaultListModel());
145 | }
146 | }
147 |
148 | /**
149 | * 解压事件类
150 | */
151 | class DecodeListener implements ActionListener{
152 | @Override
153 | public void actionPerformed(ActionEvent e) {
154 | @SuppressWarnings("unchecked")
155 | List files = (List) fileDisplay.getSelectedValuesList();
156 | int key = Integer.parseInt(JOptionPane.showInputDialog("please input the key(int)"));
157 | fileModel.decrypt(files, key);
158 | fileDisplay.updateView(fileModel.getDefaultListModel());
159 | JOptionPane.showMessageDialog(null, "Please check it.\nIf it can't be opened, make sure key is right");
160 | }
161 | }
162 |
163 | /**
164 | * 文件点击事件类
165 | */
166 | class FileClickListener extends MouseAdapter {
167 | @Override
168 | public void mouseClicked(MouseEvent e) {
169 | JList> list = (JList>) e.getSource();
170 | if (list.locationToIndex(e.getPoint()) == -1 && !e.isShiftDown()){
171 | list.clearSelection();
172 | }
173 | }
174 | @Override
175 | public void mousePressed(MouseEvent e) {
176 | if(e.getButton() == MouseEvent.BUTTON3){ // 如果是右键
177 | // 这是右键选中
178 | // int index = locationToIndex(e.getPoint());
179 | // setSelectedIndex(index);
180 | if(!fileDisplay.getSelectedValuesList().isEmpty()){ // 在空白区域点击
181 | fileDisplay.showFileMenu(e);
182 | }else{ // 选中文件点击
183 | fileDisplay.showDicMenu(e);
184 | }
185 | }else if(e.getButton() == MouseEvent.BUTTON1 && !fileDisplay.getSelectedValuesList().isEmpty()) { // 左键单击选中给定的文件
186 | if (e.getClickCount() == 2) { // 双击
187 | File file = (File) fileDisplay.getSelectedValue();
188 | if(!fileModel.openFile(file)){
189 | JOptionPane.showMessageDialog(null, "No application is associated with \nthe specified file for this operation");
190 | }
191 | fileDisplay.updateView(fileModel.getDefaultListModel());
192 | String url = fileModel.getUrl();
193 | app.updateUrl(url);
194 | }
195 | }
196 | }
197 | }
198 |
199 | /**
200 | * 撤回事件类
201 | */
202 | class UndoListener implements ActionListener{
203 | @Override
204 | public void actionPerformed(ActionEvent e) {
205 | fileModel.undo();
206 | fileDisplay.updateView(fileModel.getDefaultListModel());
207 | String url = fileModel.getUrl();
208 | app.updateUrl(url);
209 | }
210 | }
211 |
212 | /**
213 | * 前进事件类
214 | */
215 | class RedoListener implements ActionListener{
216 | @Override
217 | public void actionPerformed(ActionEvent e) {
218 | fileModel.redo();
219 | fileDisplay.updateView(fileModel.getDefaultListModel());
220 | String url = fileModel.getUrl();
221 | app.updateUrl(url);
222 | }
223 | }
224 |
225 | /**
226 | * 跳转事件类
227 | */
228 | class GoListener implements ActionListener{
229 | @Override
230 | public void actionPerformed(ActionEvent e) {
231 | String url = app.getUrlInfo();
232 | File file = new File(url);
233 | fileModel.updateModels(file);
234 | fileDisplay.updateView(fileModel.getDefaultListModel());
235 | }
236 | }
237 |
238 | /**
239 | * 文件树选择事件类
240 | */
241 | class FileTreeSelListener implements TreeSelectionListener {
242 | @Override
243 | public void valueChanged(TreeSelectionEvent e) {
244 | File file = filesTree.getCurrentFile();
245 | filesTree.loadingTree((DefaultMutableTreeNode) filesTree.getLastSelectedPathComponent(), treeModel.getFiles(file));
246 | fileModel.updateModels(file);
247 | fileDisplay.updateView(fileModel.getDefaultListModel());
248 | String url = fileModel.getUrl();
249 | app.updateUrl(url);
250 | }
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/src/main/model/FileModel.java:
--------------------------------------------------------------------------------
1 | package main.model;
2 |
3 | import main.Util.FileCode;
4 | import main.Util.FileCopy;
5 | import main.Util.FileZip;
6 |
7 | import java.awt.*;
8 | import java.io.File;
9 | import java.io.IOException;
10 | import java.util.ArrayList;
11 | import java.util.Arrays;
12 | import java.util.List;
13 | import java.util.Stack;
14 |
15 | /**
16 | * 文件数据模型
17 | */
18 | public class FileModel{
19 | private List defaultListModel; // 文件列表
20 | private List copySources; // 剪切板
21 | private File currentFile; // 当前文件路径
22 | private Stack redoStack; // redo 栈
23 |
24 | /**
25 | * 得到当前url
26 | * @return url地址
27 | */
28 | public String getUrl(){
29 | if(currentFile == null) return null;
30 | return currentFile.getPath();
31 | }
32 | public FileModel(){
33 | init();
34 | }
35 | public List getDefaultListModel() {
36 | return defaultListModel;
37 | }
38 |
39 | private void init(){
40 | defaultListModel = new ArrayList<>();
41 | copySources = new ArrayList<>();
42 | redoStack = new Stack<>();
43 | initRoots();
44 | }
45 |
46 | /**
47 | * 初始化根目录
48 | */
49 | public void initRoots(){
50 | defaultListModel.clear();
51 | defaultListModel.addAll(Arrays.asList(File.listRoots()));
52 | }
53 |
54 | /**
55 | * 更新文件数据
56 | * @param file 当前的文件
57 | */
58 | public void updateModels(File file){
59 | currentFile = file;
60 | defaultListModel.clear();
61 | File[] files = file.listFiles();
62 | defaultListModel.addAll(Arrays.asList(files));
63 | }
64 |
65 | /**
66 | * 添加文件到剪切板
67 | * @param files 文件列表
68 | */
69 | public void addCopySources(List files){
70 | for(File file: files){
71 | String str = file.getPath();
72 | copySources.add(str);
73 | }
74 | }
75 |
76 | /**
77 | * 压缩
78 | * @param files 文件列表
79 | * @param name 文件名
80 | */
81 | public void zipCompress(List files, String name){
82 | List zipFiles = new ArrayList<>();
83 | for(File file:files ){
84 | zipFiles.add(file.getPath());
85 | }
86 |
87 | if(name != null){
88 | FileZip.compress(zipFiles, currentFile.getPath() + File.separator + name + ".zip");
89 | updateModels(currentFile);
90 | }
91 | }
92 |
93 | /**
94 | * 解压
95 | * @param fileList 文件列表
96 | */
97 | public void zipUnCompress(List fileList){
98 | for(File f: fileList){
99 | try {
100 | FileZip.zipUncompress(f.getPath(), currentFile.getPath());
101 | } catch (Exception e) {
102 | e.printStackTrace();
103 | }
104 | }
105 | updateModels(currentFile);
106 | }
107 |
108 | /**
109 | * 剪切
110 | */
111 | public void paste(){
112 | for(String s : copySources){
113 | File tmp = new File(s);
114 | if(tmp.isFile()) {
115 | FileCopy.copyFile(s, currentFile.getPath() + File.separator + tmp.getName());
116 | } else {
117 | FileCopy.copyDir(s, currentFile.getPath() + File.separator + tmp.getName());
118 | }
119 | }
120 | copySources.clear();
121 | updateModels(currentFile);
122 | }
123 |
124 | /**
125 | * 新建文件夹爱
126 | * @param name 文件夹名字
127 | */
128 | public void createFile(String name){
129 | File dir = new File( currentFile.getPath() + File.separator + name);
130 | if (!dir.exists()) {// 判断目录是否存在
131 | dir.mkdir();
132 | getDefaultListModel().add(dir);
133 | }
134 | }
135 |
136 | /**
137 | * 递归删除函数
138 | * @param file 要递归删除的文件
139 | * @return 是否成功删除
140 | */
141 | private boolean deleteHelp(File file){
142 | if(!file.exists()){
143 | return false;
144 | }
145 | if(file.isDirectory()){
146 | File[] files = file.listFiles();
147 | for(File f : files){
148 | deleteHelp(f);
149 | }
150 | }
151 | return file.delete();
152 | }
153 |
154 | /**
155 | * 删除
156 | * @param fileList 要删除的文件列表
157 | */
158 | public void delete(List fileList){
159 | for(File f: fileList){
160 | defaultListModel.remove(f);
161 | deleteHelp(f);
162 | }
163 | }
164 |
165 | /**
166 | * 加密
167 | * @param files 要加密的文件列表
168 | * @param key 加密密钥
169 | */
170 | public void enCrypt(List files, int key){
171 | try {
172 | for(File file : files){
173 | if(file.isFile()){
174 | String name = file.getName();
175 | String parent = file.getParent();
176 | File tmp = new File(parent + File.separator + "encode_" + name);
177 | FileCode.EncFile(file, tmp, key);
178 | file.delete();
179 | tmp.renameTo(new File(parent + File.separator + name));
180 | }
181 |
182 | }
183 | } catch (Exception e) {
184 | e.printStackTrace();
185 | }
186 | }
187 |
188 | /**
189 | * 解密
190 | * @param files 要解密的文件
191 | * @param key 密钥
192 | */
193 | public void decrypt(List files, int key){
194 | try {
195 | for(File file:files){
196 | File tmp = new File(file.getParent() + File.separator + "decode_" + file.getName());
197 | FileCode.DecFile(file, tmp, key);
198 | if(!defaultListModel.contains(tmp)){
199 | defaultListModel.add(tmp);
200 | }
201 |
202 | }
203 | } catch (Exception e) {
204 | e.printStackTrace();
205 | }
206 | }
207 |
208 | /**
209 | * 打开文件
210 | * @param file 文件
211 | * @return 是否打开成功
212 | */
213 | public boolean openFile(File file){
214 | if(file.isFile()){
215 | try {
216 | Desktop.getDesktop().open(file);
217 | } catch (IOException ex) {
218 | return false;
219 | // ex.printStackTrace();
220 | }
221 | }else{
222 | updateModels(file);
223 | redoStack.clear();
224 | }
225 | return true;
226 | }
227 |
228 | /**
229 | * 后退操作
230 | */
231 | public void undo(){
232 | File file = currentFile;
233 | // 如果是根目录
234 | if(file == null){
235 | initRoots();
236 | currentFile = null;
237 | }
238 | // 如果父级目录是根目录
239 | if(file.getParentFile() == null){
240 | initRoots();
241 | currentFile = null;
242 | }else{
243 | updateModels(file.getParentFile());
244 | }
245 | redoStack.push(file);
246 | }
247 |
248 | /**
249 | * 前进操作
250 | */
251 | public void redo(){
252 | if(redoStack.isEmpty()){
253 | return;
254 | }
255 | File file = redoStack.pop();
256 | updateModels(file);
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/src/main/model/FileTreeModel.java:
--------------------------------------------------------------------------------
1 | package main.model;
2 |
3 | import java.io.File;
4 |
5 | /**
6 | * 文件目录树数据模型
7 | */
8 | public class FileTreeModel{
9 | public File[] getRoots(){
10 | return File.listRoots();
11 | }
12 |
13 | /**
14 | * 得到文件列表
15 | * @param currentFile 文件
16 | * @return 文件目录下的文件列表
17 | */
18 | public File[] getFiles(File currentFile) {
19 | return currentFile.listFiles();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/view/App.java:
--------------------------------------------------------------------------------
1 | package main.view;
2 |
3 | import main.controller.FileController;
4 | import main.model.FileModel;
5 | import main.model.FileTreeModel;
6 |
7 | import javax.swing.*;
8 | import javax.swing.border.EmptyBorder;
9 | import java.awt.*;
10 | import java.awt.event.ActionListener;
11 |
12 | /**
13 | * App 启动类
14 | */
15 | public class App extends JFrame{
16 | private JScrollPane jspList; // 文件列表的滑动块
17 | private JScrollPane jspTree; // 文件树的滑动块
18 | private JButton undoButton; // 后退
19 | private JButton redoButton; // 前进
20 | private FileDisplay fileDisplay; //
21 | private JToolBar toolBar; // 工具栏
22 | private JTextField urlBar; // 地址栏
23 | private FilesTree filesTree; // 文件树
24 | private JButton goButton; // 跳转
25 | public FileDisplay getFileDisplay() {
26 | return fileDisplay;
27 | }
28 |
29 | public App(){
30 | init();
31 | }
32 |
33 | /**
34 | * 得到url地址栏信息
35 | * @return url
36 | */
37 | public String getUrlInfo() {
38 | return urlBar.getText();
39 | }
40 |
41 | /**
42 | * 初始化
43 | */
44 | public void init(){
45 | setTitle("file manager");
46 | setSize(500, 400);
47 | setLocationRelativeTo(null);
48 |
49 | // toolbar
50 | toolBar = new JToolBar();
51 | toolBar.setAlignmentY(10.5F);
52 | toolBar.setBorder(null);
53 | toolBar.setFloatable(false);
54 | toolBar.setEnabled(false);
55 | toolBar.setFocusCycleRoot(true);
56 | toolBar.addSeparator(new Dimension(30, 10));
57 | add(toolBar, BorderLayout.NORTH);
58 | // undoButton 后退按钮
59 | undoButton = new JButton();
60 | undoButton.setText("undo");
61 | undoButton.setBorderPainted(false);
62 | undoButton.setBackground(new Color(203, 205, 205));
63 | toolBar.add(undoButton);
64 |
65 | // redoButton 前进按钮
66 | redoButton = new JButton();
67 | redoButton.setText("redo");
68 | redoButton.setBorderPainted(false);
69 | redoButton.setBackground(new Color(148, 152, 154));
70 |
71 | toolBar.add(redoButton);
72 | toolBar.addSeparator(new Dimension(20, 10));
73 |
74 | // 文件 List 滑动块
75 | fileDisplay = new FileDisplay();
76 | jspList = new JScrollPane(fileDisplay);
77 | jspList.setBorder(new EmptyBorder(0, 0, 0, 0));
78 | add(jspList, BorderLayout.CENTER);
79 |
80 | // url bar
81 | urlBar = new JTextField();
82 | toolBar.add(urlBar);
83 |
84 | // goButton 跳转按钮
85 | goButton = new JButton();
86 | goButton.setText("go");
87 | goButton.setBorderPainted(false);
88 | goButton.setBackground(new Color(203, 205, 205));
89 | toolBar.add(goButton);
90 |
91 |
92 | // filesTree 文件树
93 | filesTree = new FilesTree();
94 | jspTree = new JScrollPane(filesTree);
95 | jspTree.setPreferredSize(new Dimension(200, 0));
96 | add(jspTree, BorderLayout.WEST);
97 |
98 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
99 | }
100 |
101 | /**
102 | * 更新URL地址栏
103 | * @param info 地址栏信息
104 | */
105 | public void updateUrl(String info){
106 | urlBar.setText(info);
107 | }
108 |
109 | /**
110 | * 添加undo 监听
111 | * @param undo undoListener
112 | */
113 | public void addUndoListener(ActionListener undo){
114 | undoButton.addActionListener(undo);
115 | }
116 |
117 | /**
118 | * 添加redo监听
119 | * @param redo redoListener
120 | */
121 | public void addRedoListener(ActionListener redo){
122 | redoButton.addActionListener(redo);
123 | }
124 |
125 | /**
126 | * 添加go监听
127 | * @param go goListener
128 | */
129 | public void addGoListener(ActionListener go){goButton.addActionListener(go);}
130 |
131 | /**
132 | * 得到内置model
133 | * @return
134 | */
135 | public FilesTree getFilesTree() {
136 | return filesTree;
137 | }
138 |
139 | /**
140 | * 启动入口
141 | * @param args 命令行参数
142 | */
143 | public static void main(String[] args) {
144 | App app = new App(); // main.view
145 | FileModel fileModel = new FileModel(); // file main.model
146 | FileTreeModel treeModel = new FileTreeModel(); // file tree main.model
147 | FileController fileController = new FileController(app, fileModel, treeModel); // file main.controller
148 | app.setVisible(true); // 可见
149 | }
150 |
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/view/FileDisplay.java:
--------------------------------------------------------------------------------
1 | package main.view;
2 |
3 | import javax.swing.*;
4 | import javax.swing.border.EmptyBorder;
5 | import javax.swing.plaf.FontUIResource;
6 | import java.awt.*;
7 | import java.awt.event.ActionListener;
8 | import java.awt.event.MouseAdapter;
9 | import java.awt.event.MouseEvent;
10 | import java.io.File;
11 | import java.util.Enumeration;
12 | import java.util.List;
13 |
14 | public class FileDisplay extends JList {
15 |
16 | private JPopupMenu filePopupMenu; // 文件菜单
17 | private JPopupMenu dictPopupMenu; // 目录菜单
18 | private JMenuItem copyItem; // 拷贝菜单项
19 | private JMenuItem encodeItem; // 加密菜单项
20 | private JMenuItem decodeItem; // 解密菜单项
21 | private JMenuItem zipItem; // 压缩菜单项
22 | private JMenuItem unzipItem; // 解压菜单项
23 | private JMenuItem createItem; // 创建文件夹菜单项
24 | private JMenuItem pasteItem; // 粘贴菜单项
25 | private JMenuItem deleteItem; // 删除菜单项
26 |
27 | /**
28 | * 更新视图
29 | * @param files 文件列表
30 | */
31 | public void updateView(List files){
32 | DefaultListModel defaultListModel = new DefaultListModel<>();
33 | for(File file : files){
34 | defaultListModel.addElement(file);
35 | }
36 | setModel(defaultListModel);
37 | }
38 |
39 | @Override
40 | public int locationToIndex(Point location) {
41 | int index = super.locationToIndex(location);
42 | if (index != -1 && !getCellBounds(index, index).contains(location)) {
43 | return -1;
44 | } else {
45 | return index;
46 | }
47 | }
48 |
49 | /**
50 | * 初始化UI,全部字体默认fnt
51 | * @param fnt 字体
52 | */
53 | private void initGlobalFontSetting(Font fnt){
54 | FontUIResource fontRes = new FontUIResource(fnt);
55 | for(Enumeration keys = UIManager.getDefaults().keys(); keys.hasMoreElements();){
56 | Object key = keys.nextElement();
57 | Object value = UIManager.get(key);
58 | if(value instanceof FontUIResource)
59 | UIManager.put(key, fontRes);
60 | }
61 | }
62 |
63 | /**
64 | * 构造函数
65 | */
66 | public FileDisplay() {
67 | /**
68 | * 初始化UI, 采用windows10样式
69 | */
70 | try {
71 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
72 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
73 | | UnsupportedLookAndFeelException e) {
74 | e.printStackTrace();
75 | }
76 | Font f = new Font("微软雅黑", Font.PLAIN,12);
77 | initGlobalFontSetting(f);
78 |
79 | // pop menu
80 | filePopupMenu = new JPopupMenu();
81 | dictPopupMenu = new JPopupMenu();
82 | copyItem = new JMenuItem();
83 | encodeItem = new JMenuItem();
84 | decodeItem = new JMenuItem();
85 | zipItem = new JMenuItem();
86 | unzipItem = new JMenuItem();
87 | createItem = new JMenuItem();
88 | pasteItem = new JMenuItem();
89 | deleteItem = new JMenuItem();
90 |
91 | filePopupMenu.setBorderPainted(false);
92 | //---- styleMenuitem ----
93 | setMenuItemStyle(copyItem, "copy");
94 | filePopupMenu.add(copyItem);
95 |
96 | setMenuItemStyle(encodeItem, "encode");
97 | filePopupMenu.add(encodeItem);
98 |
99 | setMenuItemStyle(decodeItem, "decode");
100 | filePopupMenu.add(decodeItem);
101 |
102 | setMenuItemStyle(zipItem, "zip");
103 | filePopupMenu.add(zipItem);
104 |
105 | setMenuItemStyle(unzipItem, "unzip");
106 | filePopupMenu.add(unzipItem);
107 |
108 | setMenuItemStyle(createItem, "create");
109 | dictPopupMenu.add(createItem);
110 |
111 | dictPopupMenu.setBorderPainted(false);
112 | setMenuItemStyle(pasteItem, "paste");
113 | dictPopupMenu.add(pasteItem);
114 |
115 | setMenuItemStyle(deleteItem, "delete");
116 | filePopupMenu.add(deleteItem);
117 | // 渲染样式
118 | setCellRenderer(new FileListCellRenderer());
119 | setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
120 | }
121 |
122 | /**
123 | * 添加监听
124 | * @param copy copyListener
125 | */
126 | public void addCopyListener(ActionListener copy){
127 | copyItem.addActionListener(copy);
128 | }
129 | public void addPasteListener(ActionListener paste){
130 | pasteItem.addActionListener(paste);
131 | }
132 | public void addCreateListener(ActionListener create){
133 | createItem.addActionListener(create);
134 | }
135 | public void addZipListener(ActionListener zip){
136 | zipItem.addActionListener(zip);
137 | }
138 | public void addUnzipListener(ActionListener unzip){
139 | unzipItem.addActionListener(unzip);
140 | }
141 | public void addDeleteListener(ActionListener del){
142 | deleteItem.addActionListener(del);
143 | }
144 | public void addMyMouseListener(MouseAdapter mouseAdapter){
145 | addMouseListener(mouseAdapter);
146 | }
147 | public void showFileMenu(MouseEvent e){
148 | filePopupMenu.show(e.getComponent(), e.getX(), e.getY());
149 | }
150 | public void showDicMenu(MouseEvent e){
151 | dictPopupMenu.show(e.getComponent(), e.getX(), e.getY());
152 | }
153 |
154 | public void addEncodeListener(ActionListener encode) {encodeItem.addActionListener(encode);}
155 | public void addDecodeListener(ActionListener decode){decodeItem.addActionListener(decode);}
156 | private void setMenuItemStyle(JMenuItem styleMenuitem, String text){
157 | styleMenuitem.setText(text);
158 | styleMenuitem.setBorderPainted(false);
159 | }
160 |
161 | /**
162 | * 启动入口
163 | * @param args 命令参数,测试用
164 | */
165 | public static void main(String[] args) {
166 | JFrame frame = new JFrame("File tree");
167 | frame.setSize(500, 400);
168 | frame.setLocationRelativeTo(null);
169 | final JScrollPane jsp = new JScrollPane(new FileDisplay());
170 | jsp.setBorder(new EmptyBorder(0, 0, 0, 0));
171 | frame.add(jsp, BorderLayout.CENTER);
172 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
173 | frame.setVisible(true);
174 | }
175 |
176 |
177 | }
178 |
--------------------------------------------------------------------------------
/src/main/view/FileListCellRenderer.java:
--------------------------------------------------------------------------------
1 | package main.view;
2 |
3 | import main.Util.FileIconCreate;
4 |
5 | import javax.swing.*;
6 | import javax.swing.border.Border;
7 | import java.awt.*;
8 | import java.io.File;
9 |
10 | /**
11 | * 继承默认样式, 自定义
12 | */
13 | public class FileListCellRenderer extends DefaultListCellRenderer {
14 | Border lineBorder = BorderFactory.createLineBorder(new Color(0, 0, 0, 0),1, true);
15 |
16 | @Override
17 | public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
18 | File file = (File) value;
19 | setBorder(lineBorder);
20 | Icon ico = FileIconCreate.getSmallIcon(file);
21 | setIcon(ico);
22 | if(file.getName().equals("")){
23 | setText(file.getPath()); // 根路径
24 | }else{
25 | setText(file.getName()); // 非根路径
26 | }
27 | // 保存原始的颜色
28 | int color = getBackground().getRGB();
29 | if(isSelected){
30 | setBackground(new Color(19, 175, 137));
31 | setForeground(Color.white);
32 | }else{
33 | setBackground(Color.white);
34 | setForeground(Color.black);
35 | }
36 | return this;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/view/FilesTree.java:
--------------------------------------------------------------------------------
1 | package main.view;
2 |
3 |
4 | import javax.swing.*;
5 | import javax.swing.border.EmptyBorder;
6 | import javax.swing.tree.DefaultMutableTreeNode;
7 | import javax.swing.tree.DefaultTreeModel;
8 | import java.awt.*;
9 | import java.io.File;
10 |
11 | public class FilesTree extends JTree {
12 | class DefaultFileNodes extends DefaultMutableTreeNode{
13 | public DefaultFileNodes(){
14 | // 内部类 默认结点为系统盘
15 | for(File file : File.listRoots()){
16 | add(new DefaultMutableTreeNode(file));
17 | }
18 | }
19 | }
20 |
21 | /**
22 | * 默认结点
23 | */
24 | public FilesTree(){
25 | setModel(new DefaultTreeModel(new DefaultFileNodes()));
26 | }
27 |
28 | /**
29 | * 得到当前的文件路径
30 | * @return file
31 | */
32 | public File getCurrentFile(){
33 | DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode) getLastSelectedPathComponent();
34 | File file = (File) defaultMutableTreeNode.getUserObject();
35 | return file;
36 | }
37 |
38 | /**
39 | * 更新视图, 加载文件
40 | * @param node 加载的结点
41 | * @param files 要加载在结点下的文件
42 | */
43 | public void loadingTree(DefaultMutableTreeNode node, File[] files) {
44 | File file = (File) node.getUserObject(); // 得到文件
45 | // 获得用户选择的节点
46 | if (file.isDirectory()) {// 如果File类型对象是文件夹
47 | if(files == null) return;
48 | for (File f : files) {// 将符合条件的File类型对象增加到用户选择的节点中
49 | if(f.isDirectory()){
50 | node.add(new DefaultMutableTreeNode(f));
51 | }
52 | }
53 | } else{
54 | return;
55 | }
56 | }
57 |
58 | /**
59 | * 做测试
60 | * @param args 命令行参数
61 | */
62 | public static void main(String[] args) {
63 | JFrame frame = new JFrame("File tree");
64 | frame.setSize(500, 400);
65 | frame.setLocationRelativeTo(null);
66 | JScrollPane jsp = new JScrollPane(new FilesTree());
67 | jsp.setBorder(new EmptyBorder(0, 0, 0, 0));
68 | frame.add(jsp, BorderLayout.CENTER);
69 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
70 | frame.setVisible(true);
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/res/ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ifrozenwhale/file_manager/b586afaf635af00a435acc639250c65718ffe46c/src/res/ui.png
--------------------------------------------------------------------------------