├── .classpath ├── .gitignore ├── .project ├── LICENSE ├── README.md ├── lib ├── LICENSE ├── gson-2.2.4-javadoc.jar ├── gson-2.2.4-sources.jar └── gson-2.2.4.jar ├── resource ├── DocumentIcon.png ├── FolderIcon.png └── MacProIcon.png ├── screenshot ├── edit - before exiting.png ├── edit.png ├── main - new file.png ├── main - new folder error.png ├── main - popup menu.png └── main.png └── src ├── Main.java ├── controller ├── DiskManager.java ├── IO.java ├── MainController.java └── SystemCore.java ├── model └── sys │ ├── Block.java │ ├── Config.java │ └── FCB.java └── view ├── Config.java ├── DocumentIconPanel.java ├── EditView.java └── MainView.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # workspace files are user-specific 2 | *.sublime-workspace 3 | 4 | # project files should be checked into the repository, unless a significant 5 | # proportion of contributors will probably not be using SublimeText 6 | *.sublime-project 7 | 8 | #sftp configuration file 9 | sftp-config.json 10 | 11 | # Eclipse 12 | *.pydevproject 13 | .metadata 14 | .gradle 15 | bin/ 16 | tmp/ 17 | *.tmp 18 | *.bak 19 | *.swp 20 | *~.nib 21 | local.properties 22 | .settings/ 23 | .loadpath 24 | 25 | # External tool builders 26 | .externalToolBuilders/ 27 | 28 | # Locally stored "Eclipse launch configurations" 29 | *.launch 30 | 31 | # CDT-specific 32 | .cproject 33 | 34 | # PDT-specific 35 | .buildpath 36 | 37 | # sbteclipse plugin 38 | .target 39 | 40 | # TeXlipse plugin 41 | .texlipse 42 | 43 | #Java 44 | *.class 45 | 46 | # Mobile Tools for Java (J2ME) 47 | .mtj.tmp/ 48 | 49 | # Package Files # 50 | # *.jar 51 | *.war 52 | *.ear 53 | 54 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 55 | hs_err_pid* 56 | 57 | # Mac OS Icon File 58 | *.icns 59 | 60 | # local virtual disk data 61 | disk/ 62 | *.bin 63 | fileSystem.jar 64 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | OS_File_Managment 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Tom Hu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OS File Management 2 | ================= 3 | 4 | 程序打开时会有点慢,因为需要初始化并读取数据,请耐心等待 5 | 6 | ## 目录 7 | 8 | - 项目简介 9 | - 界面简介 10 | - 项目结构 11 | - 实现思想 12 | - 改进之处 13 | - 代码文档 14 | 15 | ## 项目简介 16 | 17 | - 项目名称: 18 | 19 | 文件系统模拟程序 20 | 21 | - 开发环境: 22 | 23 | Eclipse + JDK SE 7 + Mac OS X 10.9.3 24 | 25 | - 项目概述: 26 | 27 | 本项目模拟了一个文件系统,用户可以做到新建文件、新建文件夹、编辑文件、删除文件、删除文件夹、格式化磁盘等操作,就像平时系统中真正的文件管理器一样,只是本项目将一些功能简化了,当用户退出时,所有的信息将会被保存在电脑真实的磁盘上,下次打开时重新读入 28 | 29 | ## 界面简介 30 | 31 | 整体界面借鉴了_[邱峰学长的项目](https://github.com/VioletHill/OS_FileSystem)_的界面 32 | 33 | - 主界面 34 | 35 | 1. 主界面,根目录 36 | 37 | ![Main](https://raw.githubusercontent.com/h1994st/OS_File_Managment/master/screenshot/main.png) 38 | 39 | 2. 右键点击弹出菜单 40 | 41 | ![Main](https://raw.githubusercontent.com/h1994st/OS_File_Managment/master/screenshot/main%20-%20popup%20menu.png) 42 | 43 | 3. 新建文件时,输入文件名 44 | 45 | ![New file](https://raw.githubusercontent.com/h1994st/OS_File_Managment/master/screenshot/main%20-%20new%20file.png) 46 | 47 | 4. 新建文件夹时出错 48 | 49 | ![New folder error](https://raw.githubusercontent.com/h1994st/OS_File_Managment/master/screenshot/main%20-%20new%20folder%20error.png) 50 | 51 | - 编辑界面 52 | 53 | 1. 双击打开某文件,弹出编辑界面 54 | 55 | ![Edit](https://raw.githubusercontent.com/h1994st/OS_File_Managment/master/screenshot/edit.png) 56 | 57 | 2. 关闭编辑界面时,如果有更改过文件内容,弹出对话框询问是否保存 58 | 59 | ![Edit](https://raw.githubusercontent.com/h1994st/OS_File_Managment/master/screenshot/edit%20-%20before%20exiting.png) 60 | 61 | ## 项目结构 62 | 63 | 本项目依照MVC的思想进行架构,各层次分明 64 | 65 | ``` 66 | src/ 67 | ├── controller/ 68 | │ ├── DiskManager.java 69 | │ ├── IO.java 70 | │ ├── MainController.java 71 | │ └── SystemCore.java 72 | │ 73 | ├── model/ 74 | │ └── sys/ 75 | │ ├── Block.java 76 | │ ├── Config.java 77 | │ └── FCB.java 78 | │ 79 | └── view/ 80 | ├── Config.java 81 | ├── DocumentIconPanel.java 82 | ├── EditView.java 83 | └── MainView.java 84 | ``` 85 | 86 | #### View 87 | 88 | 这一部分关乎整个项目的用户界面 89 | 90 | 1. Config.java 91 | 92 | 该文件与界面无关,主要定义了一些整个View中用到的一些公共的参数,比如窗口长宽、文件图标的大小等 93 | 94 | ``` 95 | package view; 96 | 97 | public class Config { 98 | 99 | // Window 100 | public static final int WINDOW_WIDTH = 800; 101 | public static final int WINDOW_HEIGHT = 600; 102 | 103 | // File icon 104 | public static final int FILE_ICON_SIZE = 80; 105 | public static final int FILE_ICON_PANEL_SIZE = 100; 106 | 107 | } 108 | ``` 109 | 110 | 2. DocumentIconPanel.java 111 | 112 | 该文件与文件的图标有关,添加了鼠标点击事件的监听,支持单击选中、双击打开、右键弹出菜单 113 | 114 | 3. EditView.java 115 | 116 | 该文件与文件的编辑界面有关,可以编辑文字 117 | 118 | 如果对文件进行过修改,窗口标题中的 ___"Edit"___ 会变成 ___"Edited"___ ,并且在退出时会询问用户是否需要保存;如果没有修改则直接退出 119 | 120 | 4. MainView.java 121 | 122 | 该文件与程序主界面有关 123 | 124 | 主界面上部分由 ___后退(Back)___ 、 ___前往(Go)___ 、 ___地址栏___ 组成 125 | 126 | 下部分为主要部分,展示当前目录下的文件和文件夹,添加了鼠标点击事件的监听,右键弹出菜单,可以选择 ___"新建文件"___ 、 ___"新建文件夹"___ 、 ___"格式化"(仅根目录下)___ 127 | 128 | #### Model 129 | 130 | 下文叙述时“磁盘”指本程序中的“虚拟磁盘”,“物理块”指“虚拟磁盘中的块”;当提到“真实物理磁盘”时,才是指用户电脑的“物理磁盘”磁盘中“物理块” 131 | 132 | 1. Config.java 133 | 134 | 该文件同样定义了一些整个Model中用到的一些公共参数 135 | 136 | ``` 137 | package model.sys; 138 | 139 | import java.nio.charset.Charset; 140 | 141 | public class Config { 142 | 143 | public static final int BLOCK_SIZE = 512; 144 | public static final int BLOCK_COUNT = 8000; 145 | 146 | public static final int SYS_BLOCK_COUNT = 256; 147 | 148 | public static final int FILE_MAX_BLOCKS = 10; 149 | 150 | public static enum FILE_TYPE { 151 | FILE, DIRECTORY 152 | }; 153 | 154 | public static final Charset CHARSET = Charset.forName("UTF-8"); 155 | } 156 | ``` 157 | 158 | 2. Block.java 159 | 160 | 该文件描述了磁盘中“物理块”的模型,它可以从真实物理磁盘中同步数据 161 | 162 | 3. FCB.java 163 | 164 | 该文件定义了文件系统中的文件控制块模型 165 | 166 | 167 | #### Controller 168 | 169 | 下文叙述时“磁盘”指本程序中的“虚拟磁盘”;当提到“真实物理磁盘”时,才是指用户电脑的“物理磁盘” 170 | 171 | 1. DiskManager.java 172 | 173 | 该部分负责磁盘位图的管理,以及请求/释放空间 174 | 175 | 2. IO.java 176 | 177 | 该部分负责对磁盘的读写 178 | 179 | 3. MainController.java 180 | 181 | 该部分是项目中最主要的部分,负责Model与View的交流,通过Model的数据来填充View并展示给用户 182 | 183 | 4. SystemCore.java 184 | 185 | 该部分是文件系统的核心,负责以下功能: 186 | 187 | - 创建文件 188 | - 创建文件夹 189 | - 删除文件 190 | - 删除文件夹 191 | - 读文件 192 | - 写文件 193 | - 进入目录 194 | - 离开目录 195 | - 格式化磁盘 196 | 197 | ## 实现思想 198 | 199 | - 文件存储空间管理:连续结构 200 | - 空间分配的方法:连续分配 201 | - 空闲空间的管理:位图法 202 | - 目录结构:树型目录结构 203 | 204 | 其中位图存放在前两个物理块中 205 | 206 | ## 改进之处 207 | 208 | 文件存储采用 ___连续结构___ 虽然容易实现,但是也有诸多缺点,文件不能动态增长,预留的空间如同一种浪费,采用 ___索引结构___ 会更好 209 | 210 | 另外,空间分配的方法其实与文件存储空间的管理方法有关,如果采用 ___索引结构___ ,那么空间可以按需分配,不需要给出预留空间 211 | 212 | ## 代码文档 213 | 214 | ___(见代码中的注释)___ 215 | -------------------------------------------------------------------------------- /lib/LICENSE: -------------------------------------------------------------------------------- 1 | Google Gson 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright 2008-2011 Google Inc. 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /lib/gson-2.2.4-javadoc.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/lib/gson-2.2.4-javadoc.jar -------------------------------------------------------------------------------- /lib/gson-2.2.4-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/lib/gson-2.2.4-sources.jar -------------------------------------------------------------------------------- /lib/gson-2.2.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/lib/gson-2.2.4.jar -------------------------------------------------------------------------------- /resource/DocumentIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/resource/DocumentIcon.png -------------------------------------------------------------------------------- /resource/FolderIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/resource/FolderIcon.png -------------------------------------------------------------------------------- /resource/MacProIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/resource/MacProIcon.png -------------------------------------------------------------------------------- /screenshot/edit - before exiting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/edit - before exiting.png -------------------------------------------------------------------------------- /screenshot/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/edit.png -------------------------------------------------------------------------------- /screenshot/main - new file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/main - new file.png -------------------------------------------------------------------------------- /screenshot/main - new folder error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/main - new folder error.png -------------------------------------------------------------------------------- /screenshot/main - popup menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/main - popup menu.png -------------------------------------------------------------------------------- /screenshot/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/main.png -------------------------------------------------------------------------------- /src/Main.java: -------------------------------------------------------------------------------- 1 | import controller.MainController; 2 | 3 | /** 4 | * 5 | * @author Tom Hu 6 | * 7 | */ 8 | public class Main { 9 | 10 | /** 11 | * @param args 12 | */ 13 | public static void main(String[] args) { 14 | // TODO Auto-generated method stub 15 | MainController mainController = new MainController(); 16 | mainController.showMainView(); 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/controller/DiskManager.java: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import model.sys.Config; 6 | import model.sys.FCB; 7 | 8 | public class DiskManager { 9 | 10 | private byte[] bitMap; 11 | public IO io; 12 | public boolean online; 13 | 14 | /** 15 | * 构造函数 16 | * 为bitMap和io赋初值 17 | */ 18 | public DiskManager() { 19 | super(); 20 | this.bitMap = new byte[Config.BLOCK_COUNT / 8]; 21 | this.io = new IO(); 22 | this.online = false; 23 | } 24 | 25 | /** 26 | * 初始化 27 | */ 28 | public void init() { 29 | if (this.online) { 30 | return; 31 | } 32 | 33 | // 初始化io 34 | this.io.init(); 35 | 36 | // 从Block中读取位图 37 | this.bitMap = this.io.read(0, 2).array(); 38 | 39 | this.online = true; 40 | } 41 | 42 | /** 43 | * 格式化磁盘 44 | */ 45 | public void format() { 46 | this.online = false; 47 | this.io.online = false; 48 | for (int i = 0; i < Config.BLOCK_COUNT; i++) { 49 | this.io.write(i, 1, ""); 50 | } 51 | 52 | // 重新初始化位图以及根目录 53 | this.io.initRootFile(); 54 | } 55 | 56 | /** 57 | * 更新位图,存入Block 58 | */ 59 | public void updateBitMap() { 60 | this.io.write(0, this.bitMap.length / 512 + 1, this.bitMap); 61 | } 62 | 63 | /** 64 | * 申请连续length个块 65 | * @param startBlockId 申请块的起始id 66 | * @param length 申请的块的个数 67 | */ 68 | public void alloc(int startBlockId, int length) { 69 | int max = Math.min(startBlockId + length, this.bitMap.length); 70 | for (int i = 0; i < max; i++) { 71 | this.bitMap[i / 8] |= (1 << (i % 8)); 72 | } 73 | this.updateBitMap(); 74 | } 75 | 76 | /** 77 | * 连续释放length个块 78 | * @param startBlockId 释放块的起始id 79 | * @param length 释放的块的个数 80 | */ 81 | public void free(int startBlockId, int length) { 82 | int max = Math.min(startBlockId + length, this.bitMap.length); 83 | for (int i = 0; i < max; i++) { 84 | this.bitMap[i / 8] &= (~(1 << (i % 8))); 85 | } 86 | this.updateBitMap(); 87 | } 88 | 89 | /** 90 | * 所有数据写回磁盘 91 | */ 92 | public void update() { 93 | this.io.update(); 94 | } 95 | 96 | /** 97 | * 搜索连续长度为length的空闲块 98 | * @param length 所请求的空闲块链的长度 99 | * @return 空闲块链起点 100 | */ 101 | public int getFreeSpace(int length) { 102 | return this.getFreeSpace(length, 256); 103 | } 104 | 105 | /** 106 | * 从startBlockId开始,搜索连续长度为length的空闲块链 107 | * @param length 所请求的空闲块链的长度 108 | * @param startBlockId 搜索的起点 109 | * @return 空闲块起点 110 | */ 111 | public int getFreeSpace(int length, int startBlockId) { 112 | startBlockId = Math.max(Config.SYS_BLOCK_COUNT, startBlockId); 113 | int max = this.bitMap.length; 114 | 115 | for (int i = startBlockId; i < max; ) { 116 | int j = i; 117 | while (j < max && (this.bitMap[j / 8] & (byte) (1 << (j % 8))) == 0) { 118 | j++; 119 | } 120 | if (j - i >= length) { 121 | return i; 122 | } 123 | i = j + 1; 124 | } 125 | return 0; 126 | } 127 | 128 | /** 129 | * 根据FCB来获取文件内容 130 | * @param fcb 131 | * @return 132 | */ 133 | public String readFile(FCB fcb) { 134 | ByteBuffer resultBuffer = this.io.read(fcb.dataStartBlockId, fcb.size); 135 | return new String(resultBuffer.array(), Config.CHARSET); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/controller/IO.java: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | import java.io.File; 4 | import java.nio.ByteBuffer; 5 | import java.util.Vector; 6 | 7 | import com.google.gson.Gson; 8 | 9 | import model.sys.Block; 10 | import model.sys.Config; 11 | import model.sys.FCB; 12 | 13 | public class IO { 14 | 15 | private Vector blocks = new Vector(); 16 | public boolean online = false; 17 | 18 | public IO() { 19 | super(); 20 | } 21 | 22 | /** 23 | * 初始化所有block,从磁盘同步内容 24 | */ 25 | public void init() { 26 | if (this.online) { 27 | return; 28 | } 29 | 30 | System.out.println("系统开始初始化..."); 31 | File diskDir = new File("disk"); 32 | boolean diskDirExists = diskDir.exists(); 33 | if (!diskDirExists) { 34 | System.out.println("!!!物理磁盘上模拟信息不存在"); 35 | System.out.println("在物理磁盘上新建目录..."); 36 | diskDir.mkdirs(); 37 | System.out.println("...在物理磁盘上新建目录完成"); 38 | } 39 | 40 | // 初始化物理块 41 | System.out.println("初始化物理块..."); 42 | for (int i = 0; i < Config.BLOCK_COUNT; i++) { 43 | Block block = new Block(i); 44 | block.sync(); 45 | this.blocks.add(block); 46 | } 47 | System.out.println("...物理块初始化完成"); 48 | this.online = true; 49 | 50 | // 本地已有文件信息,无需初始化 51 | if (diskDirExists) { 52 | return; 53 | } 54 | 55 | // 初始化位图以及根目录 56 | this.initRootFile(); 57 | } 58 | 59 | /** 60 | * 初始化位图以及根目录 61 | */ 62 | public void initRootFile() { 63 | 64 | // 初始化位图 65 | System.out.println("初始化位图..."); 66 | byte[] bitMap = new byte[Config.BLOCK_COUNT / 8]; 67 | bitMap[0] = 0x07; // 00000111,0和1号块被位图占用,2号块被根目录FCB占用 68 | bitMap[Config.SYS_BLOCK_COUNT / 8] |= (1 << (Config.SYS_BLOCK_COUNT % 8)); // 根目录的目录文件,放在256号块 69 | 70 | Gson gson = new Gson(); 71 | 72 | // 初始化根目录 73 | // 根目录FCB,放在2号块,占一个块 74 | // 根目录目录文件,放在256号块 75 | System.out.println("初始化根目录..."); 76 | FCB[] rootDir = new FCB[40]; 77 | String rootDirJSON = gson.toJson(rootDir); 78 | 79 | FCB rootDirFCB = new FCB("root", -1, Config.FILE_TYPE.DIRECTORY, 80 | Config.FILE_MAX_BLOCKS, Config.SYS_BLOCK_COUNT, 2); 81 | String rootDirFCBJSON = gson.toJson(rootDirFCB); 82 | 83 | // 保存根目录FCB以及根目录目录文件 84 | this.write(rootDirFCB.blockId, 1, rootDirFCBJSON); 85 | this.write(Config.SYS_BLOCK_COUNT, rootDirFCB.size, rootDirJSON); 86 | 87 | System.out.println("...根目录初始化完成"); 88 | 89 | // 保存位图 90 | this.write(0, bitMap.length / 512 + 1, bitMap); 91 | System.out.println("...位图初始化完成"); 92 | 93 | // 写回物理磁盘 94 | this.update(); 95 | System.out.println("...系统初始化完成"); 96 | } 97 | 98 | /** 99 | * 将所有块的数据写回物理磁盘 100 | */ 101 | public void update() { 102 | System.out.println("系统数据写回物理磁盘..."); 103 | for (int i = 0; i < Config.BLOCK_COUNT; i++) { 104 | this.blocks.get(i).update(); 105 | } 106 | System.out.println("...系统数据保存完毕"); 107 | } 108 | 109 | /** 110 | * 从Block中读取内容 111 | * 112 | * @param startBlockId 113 | * 起始的块的编号 114 | * @param length 115 | * 连续读入的块的个数 116 | * @return 返回ByteBuffer,包含所需要的字符串内容 117 | */ 118 | public ByteBuffer read(int startBlockId, int length) { 119 | ByteBuffer resultBuffer = ByteBuffer.allocate(0); 120 | 121 | for (int i = startBlockId; i < startBlockId + length; i++) { 122 | ByteBuffer currentBinDataBuffer = this.blocks.get(i).getBinData(); 123 | byte[] temp = resultBuffer.array(); 124 | resultBuffer.rewind(); 125 | resultBuffer.get(temp, 0, temp.length); 126 | 127 | // resize 128 | resultBuffer = ByteBuffer.allocate(temp.length 129 | + currentBinDataBuffer.limit()); 130 | 131 | // put back 132 | resultBuffer.put(temp, 0, temp.length); 133 | 134 | // append 135 | resultBuffer.put(currentBinDataBuffer.array(), 0, 136 | currentBinDataBuffer.limit()); 137 | 138 | if (currentBinDataBuffer.limit() < currentBinDataBuffer.capacity()) { 139 | break; 140 | } 141 | } 142 | 143 | resultBuffer.rewind(); 144 | return resultBuffer; 145 | } 146 | 147 | /** 148 | * 向Block中写入字符串内容 149 | * 150 | * @param startBlockId 151 | * 起始的块的编号 152 | * @param length 153 | * 连续写入的块的个数 154 | * @param content 155 | * 写入的内容 156 | */ 157 | public void write(int startBlockId, int length, String content) { 158 | int i = startBlockId; 159 | int offset = 0; 160 | int l = Math.min(Config.BLOCK_SIZE, content.length() - offset); 161 | while (l > 0 && i < startBlockId + length) { 162 | this.blocks.get(i).wipe(); // 清空该块,从头写起 163 | this.blocks.get(i).getBinData() 164 | .put(content.getBytes(Config.CHARSET), offset, l); // 写入数据 165 | this.blocks.get(i++).getBinData().flip(); // 修改limit,保证刚刚被写入的数据在恰好在有效范围内 166 | 167 | offset += l; 168 | l = Math.min(Config.BLOCK_SIZE, content.length() - offset); 169 | } 170 | 171 | // 内容大小小于写入的块,将之后的块清空 172 | while (i < startBlockId + length) { 173 | this.blocks.get(i).wipe(); 174 | this.blocks.get(i++).getBinData().flip(); 175 | } 176 | } 177 | 178 | /** 179 | * 向Block中写入byte[]数据 180 | * 181 | * @param startBlockId 182 | * 起始块的编号 183 | * @param length 184 | * 连续写入的块的个数 185 | * @param content 186 | * 需要写入的byte[]数据 187 | */ 188 | public void write(int startBlockId, int length, byte[] content) { 189 | int i = startBlockId; 190 | int offset = 0; 191 | int l = Math.min(Config.BLOCK_SIZE, content.length - offset); 192 | while (l > 0 && i < startBlockId + length) { 193 | this.blocks.get(i).wipe(); // 清空该块,从头写起 194 | this.blocks.get(i).getBinData().put(content, offset, l); // 写入数据 195 | this.blocks.get(i++).getBinData().flip(); // 修改limit,保证刚刚被写入的数据在恰好在有效范围内 196 | 197 | offset += l; 198 | l = Math.min(Config.BLOCK_SIZE, content.length - offset); 199 | } 200 | 201 | // 内容大小小于写入的块,将之后的块清空 202 | while (i < startBlockId + length) { 203 | this.blocks.get(i).wipe(); 204 | this.blocks.get(i++).getBinData().flip(); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/controller/MainController.java: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | import java.awt.event.ActionEvent; 4 | import java.awt.event.ActionListener; 5 | import java.awt.event.KeyEvent; 6 | import java.awt.event.MouseEvent; 7 | import java.awt.event.MouseListener; 8 | import java.awt.event.WindowEvent; 9 | import java.awt.event.WindowListener; 10 | 11 | import javax.swing.JFrame; 12 | import javax.swing.JMenuItem; 13 | import javax.swing.JOptionPane; 14 | import javax.swing.JPopupMenu; 15 | import javax.swing.KeyStroke; 16 | 17 | import model.sys.Config.FILE_TYPE; 18 | import model.sys.FCB; 19 | import view.DocumentIconPanel; 20 | import view.EditView; 21 | import view.MainView; 22 | 23 | import com.google.gson.Gson; 24 | 25 | /** 26 | * 27 | * @author Tom Hu 28 | * 29 | */ 30 | public class MainController { 31 | 32 | private MainView view; 33 | 34 | private SystemCore systemCore; 35 | 36 | /** 37 | * 构造函数 38 | */ 39 | public MainController() { 40 | super(); 41 | 42 | // Initialize systemCore 43 | this.systemCore = new SystemCore(); 44 | 45 | // UI Methods 46 | this.configureMainView(); 47 | } 48 | 49 | // UI Methods 50 | /** 51 | * 初始化主界面 52 | */ 53 | private void configureMainView() { 54 | // 根据当前目录文件初始化view 55 | this.view = new MainView(this.systemCore.currentDir); 56 | 57 | // 添加右键监听 58 | this.view.addRightClickListener(this.rightClickListener); 59 | 60 | // 添加后退按钮监听 61 | this.view.addBackButtonActionListener(this.backButtonActionListener); 62 | 63 | // 添加前往按钮监听 64 | this.view.addGoButtonActionListener(this.goButtonActionListener); 65 | 66 | // 添加关闭事件监听 67 | this.view.addWindowListener(this.mainWindowListener); 68 | 69 | this.configureContentPanel(); 70 | 71 | // 显示当前路径 72 | this.view.addressTextField.setText(this.systemCore.getCurrentPath()); 73 | } 74 | 75 | /** 76 | * 初始化内容面板 77 | */ 78 | private void configureContentPanel() { 79 | // 为每个图标添加监听 80 | this.addListenerForEachDocumentIcon(); 81 | } 82 | 83 | /** 84 | * 显示主界面 85 | */ 86 | public void showMainView() { 87 | this.view.showView(); 88 | } 89 | 90 | /** 91 | * 显示编辑界面 92 | * 93 | * @param fcb 94 | * 需要编辑的文件的FCB 95 | */ 96 | private void showEditView(FCB fcb) { 97 | // 弹出Edit View,根据FCB加载 98 | EditView editView = new EditView(fcb, 99 | MainController.this.systemCore.readFile(fcb)); 100 | 101 | // 为Edit Window添加监听 102 | editView.addWindowListener(MainController.this.editWindowListener); 103 | 104 | // 显示Edit View 105 | editView.setVisible(true); 106 | } 107 | 108 | // Listener 109 | /** 110 | * 主界面内容面板右键点击监听 111 | */ 112 | private MouseListener rightClickListener = new MouseListener() { 113 | 114 | @Override 115 | public void mouseClicked(MouseEvent e) { 116 | // Deselect documents 117 | if (e.getButton() == MouseEvent.BUTTON1) { 118 | MainController.this.view.deselectDocuments(); 119 | } 120 | 121 | // Popup menu 122 | if (e.getButton() == MouseEvent.BUTTON3) { 123 | boolean isRoot = (MainController.this.systemCore.currentDirFCB.fatherBlockId == -1); 124 | 125 | JPopupMenu menu = new JPopupMenu(); 126 | JMenuItem newFileMenu = new JMenuItem("New File", KeyEvent.VK_N); 127 | newFileMenu.setAccelerator(KeyStroke.getKeyStroke( 128 | KeyEvent.VK_N, ActionEvent.CTRL_MASK)); 129 | newFileMenu 130 | .addActionListener(MainController.this.newFileActionListener); 131 | menu.add(newFileMenu); 132 | 133 | JMenuItem newFolderMenu = new JMenuItem("New Folder", 134 | KeyEvent.VK_F); 135 | newFolderMenu.setAccelerator(KeyStroke.getKeyStroke( 136 | KeyEvent.VK_F, ActionEvent.CTRL_MASK)); 137 | newFolderMenu 138 | .addActionListener(MainController.this.newFolderActionListener); 139 | menu.add(newFolderMenu); 140 | 141 | if (isRoot) { 142 | menu.addSeparator(); 143 | 144 | JMenuItem formatMenu = new JMenuItem("Format"); 145 | formatMenu 146 | .addActionListener(MainController.this.formatMenuActionListener); 147 | menu.add(formatMenu); 148 | } 149 | 150 | menu.show(e.getComponent(), e.getX(), e.getY()); 151 | } 152 | } 153 | 154 | @Override 155 | public void mousePressed(MouseEvent e) { 156 | 157 | } 158 | 159 | @Override 160 | public void mouseReleased(MouseEvent e) { 161 | 162 | } 163 | 164 | @Override 165 | public void mouseEntered(MouseEvent e) { 166 | 167 | } 168 | 169 | @Override 170 | public void mouseExited(MouseEvent e) { 171 | 172 | } 173 | 174 | }; 175 | 176 | /** 177 | * 图标点击监听,包括单击选中,双击打开,右键弹出菜单 178 | */ 179 | private MouseListener documentIconPanelMouseListener = new MouseListener() { 180 | 181 | @Override 182 | public void mouseClicked(MouseEvent e) { 183 | // Select current document 184 | MainController.this.view.deselectDocuments(); 185 | DocumentIconPanel d = (DocumentIconPanel) e.getComponent(); 186 | d.setSelected(true); 187 | 188 | // 获取文件FCB 189 | FCB fcb = MainController.this.systemCore.getFCB(d.getFilename(), 190 | d.getType()); 191 | 192 | // Double click 193 | if (e.getClickCount() == 2) { 194 | // 判断双击的类型 195 | if (d.getType() == FILE_TYPE.DIRECTORY) { 196 | // 双击文件夹 197 | // model进入下一级文件夹 198 | MainController.this.systemCore.enterDir(d.getFilename()); 199 | 200 | // 重绘view 201 | MainController.this.refreshView(); 202 | 203 | } else { 204 | // 双击文件 205 | // 显示编辑窗口 206 | MainController.this.showEditView(fcb); 207 | } 208 | 209 | System.out.println("Double Click"); 210 | } 211 | 212 | // Right click 213 | if (e.getButton() == MouseEvent.BUTTON3) { 214 | JPopupMenu documentMenu = new JPopupMenu(); 215 | JMenuItem openMenuItem = new JMenuItem("Open", KeyEvent.VK_O); 216 | MainController.this.openMenuActionListener.fcb = fcb; 217 | openMenuItem 218 | .addActionListener(MainController.this.openMenuActionListener); 219 | documentMenu.add(openMenuItem); 220 | 221 | if (d.getType() == FILE_TYPE.FILE) { 222 | JMenuItem editMenuItem = new JMenuItem("Edit", 223 | KeyEvent.VK_E); 224 | MainController.this.editMenuActionListener.fcb = fcb; 225 | editMenuItem 226 | .addActionListener(MainController.this.editMenuActionListener); 227 | documentMenu.add(editMenuItem); 228 | } 229 | 230 | JMenuItem renameMenuItem = new JMenuItem("Rename", 231 | KeyEvent.VK_R); 232 | MainController.this.renameMenuActionListener.fcb = fcb; 233 | renameMenuItem 234 | .addActionListener(MainController.this.renameMenuActionListener); 235 | documentMenu.add(renameMenuItem); 236 | 237 | documentMenu.addSeparator(); 238 | 239 | JMenuItem getInfoMenuItem = new JMenuItem("Get info", 240 | KeyEvent.VK_I); 241 | MainController.this.getInfoMenuActionListener.fcb = fcb; 242 | getInfoMenuItem 243 | .addActionListener(MainController.this.getInfoMenuActionListener); 244 | documentMenu.add(getInfoMenuItem); 245 | 246 | documentMenu.addSeparator(); 247 | 248 | JMenuItem deleteMenuItem = new JMenuItem("Delete", 249 | KeyEvent.VK_D); 250 | MainController.this.deleteMenuActionListener.fcb = fcb; 251 | deleteMenuItem 252 | .addActionListener(MainController.this.deleteMenuActionListener); 253 | documentMenu.add(deleteMenuItem); 254 | 255 | documentMenu.show(e.getComponent(), e.getX(), e.getY()); 256 | } 257 | } 258 | 259 | @Override 260 | public void mousePressed(MouseEvent e) { 261 | 262 | } 263 | 264 | @Override 265 | public void mouseReleased(MouseEvent e) { 266 | 267 | } 268 | 269 | @Override 270 | public void mouseEntered(MouseEvent e) { 271 | 272 | } 273 | 274 | @Override 275 | public void mouseExited(MouseEvent e) { 276 | 277 | } 278 | 279 | }; 280 | 281 | /** 282 | * 监听新建文件的按钮 283 | */ 284 | private ActionListener newFileActionListener = new ActionListener() { 285 | 286 | @Override 287 | public void actionPerformed(ActionEvent e) { 288 | // 新建文件 289 | // 获取文件名 290 | String filename = (String) JOptionPane.showInputDialog( 291 | MainController.this.view, 292 | "Please enter your new file's name:", "New file", 293 | JOptionPane.INFORMATION_MESSAGE); 294 | 295 | if (filename == null) { 296 | // 用户取消 297 | return; 298 | } 299 | 300 | // 不允许文件名为空 301 | while (filename.equals("")) { 302 | filename = (String) JOptionPane 303 | .showInputDialog( 304 | MainController.this.view, 305 | "Filename cannot be empty! \nPlease enter your new file's name:", 306 | "New file", JOptionPane.WARNING_MESSAGE); 307 | 308 | if (filename == null) { 309 | // 用户取消 310 | return; 311 | } 312 | } 313 | 314 | if (MainController.this.systemCore.createFile(filename)) { 315 | // 添加到model成功,即创建文件成功 316 | // 添加到view 317 | DocumentIconPanel d = new DocumentIconPanel(FILE_TYPE.FILE, 318 | filename); 319 | d.addMouseListener(MainController.this.documentIconPanelMouseListener); 320 | MainController.this.view.addDocument(d); 321 | } else { 322 | // 创建文件失败 323 | // 可能是有重名,也可能空间不够 324 | // 弹出错误信息框 325 | JOptionPane 326 | .showMessageDialog( 327 | MainController.this.view, 328 | "Two possible reasons:\n1. The name \"" 329 | + filename 330 | + "\" is already taken. Please choose a different name.\n2. Not enough disk space available. Delete some files.", 331 | "New file error", JOptionPane.ERROR_MESSAGE); 332 | } 333 | 334 | } 335 | }; 336 | 337 | /** 338 | * 监听新建文件夹的按钮 339 | */ 340 | private ActionListener newFolderActionListener = new ActionListener() { 341 | 342 | @Override 343 | public void actionPerformed(ActionEvent e) { 344 | // 新建文件夹 345 | // 获取文件名 346 | String filename = (String) JOptionPane.showInputDialog( 347 | MainController.this.view, 348 | "Please enter your new folder's name:", "New folder", 349 | JOptionPane.INFORMATION_MESSAGE); 350 | 351 | if (filename == null) { 352 | // 用户取消 353 | return; 354 | } 355 | 356 | // 不允许文件名为空 357 | while (filename.equals("")) { 358 | filename = (String) JOptionPane 359 | .showInputDialog( 360 | MainController.this.view, 361 | "Folder's name cannot be empty! \nPlease enter your new folder's name:", 362 | "New folder", JOptionPane.WARNING_MESSAGE); 363 | 364 | if (filename == null) { 365 | // 用户取消 366 | return; 367 | } 368 | } 369 | 370 | if (MainController.this.systemCore.createDir(filename)) { 371 | // 添加到model成功,即创建文件夹成功 372 | // 添加到view 373 | DocumentIconPanel d = new DocumentIconPanel( 374 | FILE_TYPE.DIRECTORY, filename); 375 | d.addMouseListener(MainController.this.documentIconPanelMouseListener); 376 | MainController.this.view.addDocument(d); 377 | } else { 378 | // 创建文件夹失败 379 | // 可能是有重名,也可能空间不够 380 | // 弹出错误信息框 381 | JOptionPane 382 | .showMessageDialog( 383 | MainController.this.view, 384 | "Two possible reasons:\n1. The name \"" 385 | + filename 386 | + "\" is already taken. Please choose a different name.\n2. Not enough disk space available. Delete some files.", 387 | "New folder error", JOptionPane.ERROR_MESSAGE); 388 | } 389 | 390 | } 391 | 392 | }; 393 | 394 | private class CustomActionListener implements ActionListener { 395 | public FCB fcb = null; 396 | 397 | @Override 398 | public void actionPerformed(ActionEvent e) { 399 | 400 | } 401 | } 402 | 403 | /** 404 | * "编辑"按钮按键监听 405 | */ 406 | private CustomActionListener editMenuActionListener = new CustomActionListener() { 407 | 408 | @Override 409 | public void actionPerformed(ActionEvent e) { 410 | MainController.this.showEditView(this.fcb); 411 | } 412 | 413 | }; 414 | 415 | /** 416 | * "打开"按钮按键监听 417 | */ 418 | private CustomActionListener openMenuActionListener = new CustomActionListener() { 419 | 420 | @Override 421 | public void actionPerformed(ActionEvent e) { 422 | if (this.fcb.type == FILE_TYPE.DIRECTORY) { 423 | // model进入下一级文件夹 424 | MainController.this.systemCore.enterDir(this.fcb.filename); 425 | 426 | // 重绘view 427 | MainController.this.refreshView(); 428 | } else { 429 | // 显示编辑窗口 430 | MainController.this.showEditView(fcb); 431 | } 432 | } 433 | 434 | }; 435 | 436 | /** 437 | * "删除"按钮按键监听 438 | */ 439 | private CustomActionListener deleteMenuActionListener = new CustomActionListener() { 440 | 441 | @Override 442 | public void actionPerformed(ActionEvent e) { 443 | // 获取用户的选择 444 | int result = JOptionPane.showConfirmDialog( 445 | MainController.this.view, 446 | "Are you sure you want to permanently delete this item?", 447 | "Delete", JOptionPane.YES_NO_OPTION, 448 | JOptionPane.QUESTION_MESSAGE); 449 | 450 | if (result == 0) { 451 | // 确定删除 452 | if (this.fcb.type == FILE_TYPE.DIRECTORY) { 453 | // model删除文件夹 454 | MainController.this.systemCore.deleteDir(this.fcb.filename); 455 | } else { 456 | // model删除文件 457 | MainController.this.systemCore 458 | .deleteFile(this.fcb.filename); 459 | } 460 | 461 | // 重绘view 462 | MainController.this.refreshView(); 463 | } 464 | } 465 | 466 | }; 467 | 468 | /** 469 | * "重命名"按钮按键监听 470 | */ 471 | private CustomActionListener renameMenuActionListener = new CustomActionListener() { 472 | 473 | @Override 474 | public void actionPerformed(ActionEvent e) { 475 | // 获取文件名 476 | String filename = (String) JOptionPane.showInputDialog( 477 | MainController.this.view, "Folder name:", "Rename", 478 | JOptionPane.INFORMATION_MESSAGE, null, null, 479 | this.fcb.filename); 480 | 481 | if (filename == null) { 482 | // 用户取消 483 | return; 484 | } 485 | 486 | // 不允许文件名为空 487 | while (filename.equals("")) { 488 | filename = (String) JOptionPane.showInputDialog( 489 | MainController.this.view, "Folder name:", "Rename", 490 | JOptionPane.INFORMATION_MESSAGE, null, null, 491 | this.fcb.filename); 492 | } 493 | 494 | this.fcb.filename = filename; 495 | 496 | Gson gson = new Gson(); 497 | 498 | // 更新文件FCB 499 | MainController.this.systemCore.updateFCB(this.fcb); 500 | 501 | // 更新当前目录的目录文件 502 | MainController.this.systemCore.updateFile( 503 | MainController.this.systemCore.currentDirFCB, 504 | gson.toJson(MainController.this.systemCore.currentDir)); 505 | 506 | // 刷新界面 507 | MainController.this.refreshView(); 508 | } 509 | 510 | }; 511 | 512 | /** 513 | * "格式化"按钮按键监听 514 | */ 515 | private ActionListener formatMenuActionListener = new ActionListener() { 516 | 517 | @Override 518 | public void actionPerformed(ActionEvent e) { 519 | // 获取用户的选择 520 | int result = JOptionPane 521 | .showConfirmDialog( 522 | MainController.this.view, 523 | "All the data will be erased from the disk.\nAre you sure to FORMAT disk?", 524 | "FORMAT!!", JOptionPane.YES_NO_OPTION, 525 | JOptionPane.QUESTION_MESSAGE); 526 | 527 | if (result == 0) { 528 | // 确定格式化 529 | MainController.this.systemCore.format(); 530 | } 531 | 532 | // 刷新界面 533 | MainController.this.refreshView(); 534 | } 535 | 536 | }; 537 | 538 | /** 539 | * "属性"按钮按键监听 540 | */ 541 | private CustomActionListener getInfoMenuActionListener = new CustomActionListener() { 542 | 543 | @Override 544 | public void actionPerformed(ActionEvent e) { 545 | // 弹出详细信息框 546 | JOptionPane.showMessageDialog(MainController.this.view, 547 | MainController.this.systemCore.getFileInfo(this.fcb), 548 | "Info", JOptionPane.PLAIN_MESSAGE); 549 | } 550 | 551 | }; 552 | 553 | /** 554 | * "后退"按钮按键监听 555 | */ 556 | private ActionListener backButtonActionListener = new ActionListener() { 557 | 558 | @Override 559 | public void actionPerformed(ActionEvent e) { 560 | // 点击后退按钮 561 | 562 | if (MainController.this.systemCore.leaveDir()) { 563 | // 确认回到上一级目录 564 | // 重绘view 565 | MainController.this.refreshView(); 566 | } else { 567 | // 根目录 568 | JOptionPane.showMessageDialog(MainController.this.view, 569 | "Already in root directory", "Warning", 570 | JOptionPane.WARNING_MESSAGE); 571 | } 572 | } 573 | 574 | }; 575 | 576 | /** 577 | * "前往"按钮按键监听 578 | */ 579 | private ActionListener goButtonActionListener = new ActionListener() { 580 | 581 | @Override 582 | public void actionPerformed(ActionEvent e) { 583 | // 获取地址栏地址 584 | String path = MainController.this.view.addressTextField.getText(); 585 | 586 | if (path.charAt(0) != '/') { 587 | // 路径非法 588 | JOptionPane.showMessageDialog(MainController.this.view, 589 | "Directory doesn't exist!", "Warning", 590 | JOptionPane.WARNING_MESSAGE); 591 | 592 | return; 593 | } 594 | 595 | // 获取当前地址 596 | String currentPath = MainController.this.systemCore 597 | .getCurrentPath(); 598 | 599 | // 拆分 600 | String[] pathArray = path.split("/"); 601 | String[] currentPathArray = currentPath.split("/"); 602 | 603 | if (pathArray.length == 0) { 604 | pathArray = new String[1]; 605 | pathArray[0] = ""; 606 | } 607 | 608 | if (currentPathArray.length == 0) { 609 | currentPathArray = new String[1]; 610 | currentPathArray[0] = ""; 611 | } 612 | 613 | // 对比 614 | int length = Math.min(pathArray.length, currentPathArray.length); 615 | int i = 0; 616 | for (i = 0; i < length; i++) { 617 | if (!pathArray[i].equals(currentPathArray[i])) { 618 | break; 619 | } 620 | } 621 | 622 | if (pathArray.length == currentPathArray.length && i == length) { 623 | // 两拆分后的数组相同,即路径没变化,无需继续下面的步骤 624 | return; 625 | } 626 | 627 | // 计算 628 | // 向后退的步数 629 | int stepOut = currentPathArray.length - i; 630 | // 向前进的步数 631 | int stepIn = pathArray.length - i; 632 | 633 | // 临时保存当前目录FCB和目录文件 634 | FCB fcb = MainController.this.systemCore.currentDirFCB; 635 | FCB[] dir = MainController.this.systemCore.currentDir; 636 | 637 | // 开始后退再前进 638 | boolean success = true; 639 | // 后退 640 | for (int j = 0; j < stepOut; j++) { 641 | MainController.this.systemCore.leaveDir(); 642 | } 643 | 644 | // 前进 645 | for (int j = 0; j < stepIn; j++) { 646 | if (!MainController.this.systemCore.enterDir(pathArray[i++])) { 647 | success = false; 648 | break; 649 | } 650 | } 651 | 652 | if (success) { 653 | // 成功跳转 654 | // 刷新界面 655 | MainController.this.refreshView(); 656 | } else { 657 | // 目录不存在 658 | JOptionPane.showMessageDialog(MainController.this.view, 659 | "Directory doesn't exist!", "Warning", 660 | JOptionPane.WARNING_MESSAGE); 661 | 662 | // 恢复 663 | MainController.this.systemCore.currentDirFCB = fcb; 664 | MainController.this.systemCore.currentDir = dir; 665 | MainController.this.systemCore.countFiles(); 666 | } 667 | 668 | } 669 | 670 | }; 671 | 672 | /** 673 | * 主窗口的监听,主要是关闭事件 674 | */ 675 | private WindowListener mainWindowListener = new WindowListener() { 676 | 677 | @Override 678 | public void windowOpened(WindowEvent e) { 679 | 680 | } 681 | 682 | @Override 683 | public void windowClosing(WindowEvent e) { 684 | 685 | } 686 | 687 | @Override 688 | public void windowClosed(WindowEvent e) { 689 | System.out.println("窗口已关闭"); 690 | 691 | // 系统核心退出 692 | MainController.this.systemCore.exit(); 693 | } 694 | 695 | @Override 696 | public void windowIconified(WindowEvent e) { 697 | 698 | } 699 | 700 | @Override 701 | public void windowDeiconified(WindowEvent e) { 702 | 703 | } 704 | 705 | @Override 706 | public void windowActivated(WindowEvent e) { 707 | 708 | } 709 | 710 | @Override 711 | public void windowDeactivated(WindowEvent e) { 712 | 713 | } 714 | 715 | }; 716 | 717 | /** 718 | * 编辑窗口的监听 719 | */ 720 | private WindowListener editWindowListener = new WindowListener() { 721 | 722 | @Override 723 | public void windowOpened(WindowEvent e) { 724 | 725 | } 726 | 727 | @Override 728 | public void windowClosing(WindowEvent e) { 729 | 730 | EditView editView = (EditView) e.getComponent(); 731 | 732 | if (!editView.edited) { 733 | // 文本没有变化 734 | return; 735 | } 736 | 737 | // 获取用户的选择 738 | int result = JOptionPane.showConfirmDialog(editView, 739 | "Would you like to SAVE before leaving?", "Exit", 740 | JOptionPane.YES_NO_CANCEL_OPTION, 741 | JOptionPane.QUESTION_MESSAGE); 742 | 743 | if (result == 0) { 744 | // 退出并保存 745 | editView.saveOnExit = true; 746 | editView.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 747 | } else if (result == 1) { 748 | // 退出不保存 749 | editView.saveOnExit = false; 750 | editView.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 751 | } else { 752 | // 取消 753 | editView.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 754 | } 755 | } 756 | 757 | @Override 758 | public void windowClosed(WindowEvent e) { 759 | System.out.println("编辑窗口关闭"); 760 | 761 | EditView editView = (EditView) e.getComponent(); 762 | 763 | if (editView.edited && editView.saveOnExit) { 764 | // 保存文件 765 | MainController.this.systemCore.updateFile( 766 | editView.getDataFCB(), editView.getContent()); 767 | } 768 | } 769 | 770 | @Override 771 | public void windowIconified(WindowEvent e) { 772 | 773 | } 774 | 775 | @Override 776 | public void windowDeiconified(WindowEvent e) { 777 | 778 | } 779 | 780 | @Override 781 | public void windowActivated(WindowEvent e) { 782 | 783 | } 784 | 785 | @Override 786 | public void windowDeactivated(WindowEvent e) { 787 | 788 | } 789 | 790 | }; 791 | 792 | /** 793 | * 根据当前FCB对应的目录文件刷新View 794 | */ 795 | private void refreshView() { 796 | this.view.reloadContent(this.systemCore.currentDir); 797 | 798 | // 重新添加监听 799 | this.addListenerForEachDocumentIcon(); 800 | 801 | // 更新当前路径 802 | this.view.addressTextField.setText(MainController.this.systemCore 803 | .getCurrentPath()); 804 | } 805 | 806 | /** 807 | * 为每个图标添加监听 808 | */ 809 | private void addListenerForEachDocumentIcon() { 810 | this.view 811 | .addDocumentIconPanelMouseListener(this.documentIconPanelMouseListener); 812 | } 813 | } 814 | -------------------------------------------------------------------------------- /src/controller/SystemCore.java: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Date; 5 | 6 | import model.sys.Config; 7 | import model.sys.Config.FILE_TYPE; 8 | import model.sys.FCB; 9 | 10 | import com.google.gson.Gson; 11 | 12 | public class SystemCore { 13 | 14 | private DiskManager diskManager; 15 | public FCB currentDirFCB; 16 | public FCB[] currentDir; 17 | private int fileCount = 0; 18 | 19 | /** 20 | * 构造函数 21 | */ 22 | public SystemCore() { 23 | super(); 24 | this.diskManager = new DiskManager(); 25 | 26 | // 初始化系统核心 27 | this.init(); 28 | } 29 | 30 | /** 31 | * 初始化系统核心 32 | */ 33 | private void init() { 34 | // 初始化 35 | this.diskManager.init(); 36 | 37 | Gson gson = new Gson(); 38 | 39 | // 读取根目录FCB 40 | ByteBuffer currentDirFCBBuffer = this.diskManager.io.read(2, 1); 41 | String currentDirFCBJSON = new String(currentDirFCBBuffer.array(), 42 | Config.CHARSET); 43 | this.currentDirFCB = gson.fromJson(currentDirFCBJSON, FCB.class); 44 | 45 | // 读取根目录目录文件 46 | ByteBuffer currentDirJSONBuffer = this.diskManager.io.read( 47 | this.currentDirFCB.dataStartBlockId, this.currentDirFCB.size); 48 | String currentDirJSON = new String(currentDirJSONBuffer.array(), 49 | Config.CHARSET); 50 | this.currentDir = gson.fromJson(currentDirJSON, FCB[].class); 51 | 52 | // 计算文件个数 53 | this.countFiles(); 54 | } 55 | 56 | /** 57 | * 创建文件 58 | * 59 | * @param filename 60 | * 文件名 61 | * @return 是否创建成功 62 | */ 63 | public boolean createFile(String filename) { 64 | if (!this.checkFilename(filename, FILE_TYPE.FILE)) { 65 | return false; 66 | } 67 | 68 | int fcbBlockStart = this.diskManager.getFreeSpace(1); 69 | int fileDataBlockStart = this.diskManager.getFreeSpace( 70 | Config.FILE_MAX_BLOCKS, fcbBlockStart + 1); 71 | 72 | if (fcbBlockStart != 0 && fileDataBlockStart != 0) { 73 | FCB fcb = new FCB(filename, this.currentDirFCB.blockId, 74 | Config.FILE_TYPE.FILE, Config.FILE_MAX_BLOCKS, 75 | fileDataBlockStart, fcbBlockStart); 76 | 77 | // 申请分配空间 78 | this.diskManager.alloc(fcbBlockStart, 1); 79 | this.diskManager.alloc(fileDataBlockStart, Config.FILE_MAX_BLOCKS); 80 | 81 | // 当前文件夹下加入该文件 82 | this.currentDir[fileCount++] = fcb; 83 | 84 | Gson gson = new Gson(); 85 | 86 | // 更新目录FCB 87 | this.updateFCB(this.currentDirFCB); 88 | 89 | // 更新当前目录文件 90 | this.updateFile(this.currentDirFCB, gson.toJson(this.currentDir)); 91 | 92 | // 将新建文件的FCB写入Block 93 | this.diskManager.io.write(fcbBlockStart, 1, gson.toJson(fcb)); 94 | 95 | // 为新建的文件写内容 96 | this.updateFile(fcb, ""); 97 | } else { 98 | System.out.println("空间不够"); 99 | 100 | return false; 101 | } 102 | 103 | return true; 104 | } 105 | 106 | /** 107 | * 向FCB所指向的数据区更新内容 108 | * 109 | * @param fcb 110 | * 需要更新的文件的FCB 111 | * @param content 112 | * 所更新的内容 113 | */ 114 | public void updateFile(FCB fcb, String content) { 115 | this.diskManager.io.write(fcb.dataStartBlockId, fcb.size, content); 116 | } 117 | 118 | /** 119 | * 独处文件内容 120 | * 121 | * @param fcb 122 | * 文件对应的FCB 123 | * @return 返回文件内容 124 | */ 125 | public String readFile(FCB fcb) { 126 | return this.diskManager.readFile(fcb); 127 | } 128 | 129 | /** 130 | * 获取格式化后的文件信息 131 | * 132 | * @param fcb 133 | * 指定的FCB 134 | * @return 格式化后的文件信息 135 | */ 136 | public String getFileInfo(FCB fcb) { 137 | String result = ""; 138 | 139 | result += ("Name: " + fcb.filename + "\n"); 140 | result += ("Type: " 141 | + (fcb.type == FILE_TYPE.FILE ? "file" : "directory") + "\n"); 142 | result += ("Path: " + this.getCurrentPath() + "\n"); 143 | result += ("Created: " + fcb.createdDate + "\n"); 144 | result += ("Updated: " + fcb.updatedDate); 145 | 146 | return result; 147 | } 148 | 149 | /** 150 | * 创建文件夹 151 | * 152 | * @param filename 153 | * 文件夹名 154 | * @return 是否创建成功 155 | */ 156 | public boolean createDir(String filename) { 157 | if (!this.checkFilename(filename, FILE_TYPE.DIRECTORY)) { 158 | // 有重名 159 | return false; 160 | } 161 | 162 | int fcbBlockStart = this.diskManager.getFreeSpace(1); 163 | int fileDataBlockStart = this.diskManager.getFreeSpace( 164 | Config.FILE_MAX_BLOCKS, fcbBlockStart + 1); 165 | 166 | if (fcbBlockStart != 0 && fileDataBlockStart != 0) { 167 | FCB dirFcb = new FCB(filename, this.currentDirFCB.blockId, 168 | Config.FILE_TYPE.DIRECTORY, Config.FILE_MAX_BLOCKS, 169 | fileDataBlockStart, fcbBlockStart); 170 | FCB[] dir = new FCB[40]; 171 | 172 | // 申请分配空间 173 | this.diskManager.alloc(fcbBlockStart, 1); 174 | this.diskManager.alloc(fileDataBlockStart, Config.FILE_MAX_BLOCKS); 175 | 176 | // 当前文件夹下加入该文件 177 | this.currentDir[fileCount++] = dirFcb; 178 | 179 | Gson gson = new Gson(); 180 | 181 | // 更新目录FCB 182 | this.updateFCB(this.currentDirFCB); 183 | 184 | // 更新当前目录文件 185 | this.updateFile(this.currentDirFCB, gson.toJson(this.currentDir)); 186 | 187 | // 将新建文件的FCB写入Block 188 | this.diskManager.io.write(fcbBlockStart, 1, gson.toJson(dirFcb)); 189 | 190 | // 为新建的文件写内容 191 | this.updateFile(dirFcb, gson.toJson(dir)); 192 | } else { 193 | System.out.println("空间不够"); 194 | 195 | return false; 196 | } 197 | 198 | return true; 199 | } 200 | 201 | /** 202 | * 删除文件夹 203 | * 204 | * @param filename 205 | * 文件夹名 206 | */ 207 | public void deleteDir(String filename) { 208 | FCB fcb = this.getFCB(filename, FILE_TYPE.DIRECTORY); 209 | 210 | if (fcb == null) { 211 | return; 212 | } 213 | 214 | // 删除对应的FCB 215 | this.deleteFCB(fcb); 216 | 217 | // 更新目录FCB 218 | this.updateFCB(this.currentDirFCB); 219 | 220 | // 递归删除 221 | this.recursiveDeleteDir(fcb); 222 | } 223 | 224 | /** 225 | * 递归删除文件夹以及文件夹内所有文件 226 | * 227 | * @param fcb 228 | * 需要删除的文件夹对应的FCB 229 | */ 230 | private void recursiveDeleteDir(FCB fcb) { 231 | // 释放空间 232 | // 释放该FCB所在的块 233 | this.diskManager.free(fcb.blockId, 1); 234 | // 释放该FCB所指向的数据区 235 | this.diskManager.free(fcb.dataStartBlockId, fcb.size); 236 | 237 | Gson gson = new Gson(); 238 | 239 | // 获取目录下的所有文件 240 | ByteBuffer bf = this.diskManager.io 241 | .read(fcb.dataStartBlockId, fcb.size); 242 | FCB[] dir = gson.fromJson(new String(bf.array(), Config.CHARSET), 243 | FCB[].class); 244 | 245 | // 遍历,删除 246 | for (int i = 0; i < dir.length; i++) { 247 | if (dir[i] == null) { 248 | // 如果目录为空,直接跳出 249 | break; 250 | } 251 | 252 | if (dir[i].type == FILE_TYPE.DIRECTORY) { 253 | // 删除文件夹 254 | // 递归调用 255 | this.recursiveDeleteDir(dir[i]); 256 | } else { 257 | // 删除文件 258 | // 释放空间 259 | // 释放该FCB所在的块 260 | this.diskManager.free(fcb.blockId, 1); 261 | // 释放该FCB所指向的数据区 262 | this.diskManager.free(fcb.dataStartBlockId, fcb.size); 263 | } 264 | } 265 | } 266 | 267 | /** 268 | * 删除文件 269 | * 270 | * @param filename 271 | * 文件名 272 | */ 273 | public void deleteFile(String filename) { 274 | FCB fcb = this.getFCB(filename, FILE_TYPE.FILE); 275 | 276 | if (fcb == null) { 277 | return; 278 | } 279 | 280 | // 删除对应的FCB 281 | this.deleteFCB(fcb); 282 | 283 | // 更新目录FCB 284 | this.updateFCB(this.currentDirFCB); 285 | 286 | // 释放空间 287 | // 释放该FCB所在的块 288 | this.diskManager.free(fcb.blockId, 1); 289 | // 释放该FCB所指向的数据区 290 | this.diskManager.free(fcb.dataStartBlockId, fcb.size); 291 | } 292 | 293 | /** 294 | * 删除当前文件夹下FCB,并更新Block数据 295 | * 296 | * @param fcb 297 | */ 298 | private void deleteFCB(FCB fcb) { 299 | int i = 0; 300 | for (; i < this.currentDir.length; i++) { 301 | if (this.currentDir[i].filename.equals(fcb.filename) 302 | && this.currentDir[i].type == fcb.type) { 303 | this.currentDir[i] = null; 304 | break; 305 | } 306 | } 307 | 308 | // 保持文件目录的连续 309 | while (i < this.currentDir.length - 1) { 310 | if (this.currentDir[i + 1] == null) { 311 | break; 312 | } 313 | this.currentDir[i] = this.currentDir[++i]; 314 | this.currentDir[i] = null; 315 | } 316 | 317 | Gson gson = new Gson(); 318 | 319 | // 写回Block 320 | this.updateFile(this.currentDirFCB, gson.toJson(this.currentDir)); 321 | } 322 | 323 | /** 324 | * 进入下一级文件夹 325 | * 326 | * @param dirname 327 | * 文件夹名 328 | * @return 是否进入成功 329 | */ 330 | public boolean enterDir(String dirname) { 331 | FCB dirFCB = this.getFCB(dirname, FILE_TYPE.DIRECTORY); 332 | if (dirFCB == null) { 333 | // 所对应文件夹不存在 334 | System.out.println("找不到该目录"); 335 | return false; 336 | } 337 | 338 | Gson gson = new Gson(); 339 | 340 | // 替换currentDirFCB和currentDir 341 | this.currentDirFCB = dirFCB; 342 | ByteBuffer bf = this.diskManager.io.read( 343 | this.currentDirFCB.dataStartBlockId, this.currentDirFCB.size); 344 | String json = new String(bf.array(), Config.CHARSET); 345 | FCB[] dir = gson.fromJson(json, FCB[].class); 346 | this.currentDir = dir; 347 | 348 | // 计算文件个数 349 | this.countFiles(); 350 | 351 | return true; 352 | } 353 | 354 | /** 355 | * 返回上一级文件夹 356 | * 357 | * @return 是否返回成功 358 | */ 359 | public boolean leaveDir() { 360 | if (this.currentDirFCB.fatherBlockId == -1) { 361 | // 根目录,无法返回 362 | System.out.println("已经到根目录,不存在上级目录"); 363 | return false; 364 | } 365 | 366 | Gson gson = new Gson(); 367 | 368 | // 获取上一级文件夹的FCB 369 | ByteBuffer bf = this.diskManager.io.read( 370 | this.currentDirFCB.fatherBlockId, 1); 371 | FCB fcb = gson.fromJson(new String(bf.array(), Config.CHARSET), 372 | FCB.class); 373 | 374 | // 获取上一级文件夹的目录文件 375 | // 即获取包含其目录下所有FCB的数组 376 | ByteBuffer dirBf = this.diskManager.io.read(fcb.dataStartBlockId, 377 | fcb.size); 378 | FCB[] dir = gson.fromJson(new String(dirBf.array(), Config.CHARSET), 379 | FCB[].class); 380 | 381 | // 替换currentDirFCB和currentDir 382 | this.currentDir = dir; 383 | this.currentDirFCB = fcb; 384 | 385 | // 计算文件个数 386 | this.countFiles(); 387 | 388 | return true; 389 | } 390 | 391 | /** 392 | * 获取当前文件夹的路径 393 | * 394 | * @return 395 | */ 396 | public String getCurrentPath() { 397 | if (this.currentDirFCB.fatherBlockId == -1) { 398 | return "/"; 399 | } 400 | return this.recursiveGetPath(this.currentDirFCB); 401 | } 402 | 403 | /** 404 | * 通过给定FCB,递归向上获取该FCB的路径 405 | * 406 | * @param fcb 407 | * 给定的FCB 408 | * @return 给定FCB的路径 409 | */ 410 | private String recursiveGetPath(FCB fcb) { 411 | if (fcb.type != FILE_TYPE.DIRECTORY) { 412 | return null; 413 | } 414 | 415 | if (fcb.fatherBlockId == -1) { 416 | return ""; 417 | } 418 | 419 | Gson gson = new Gson(); 420 | 421 | // 获取父亲的FCB 422 | ByteBuffer fatherBf = this.diskManager.io.read(fcb.fatherBlockId, 1); 423 | String fatherFCBJson = new String(fatherBf.array(), Config.CHARSET); 424 | FCB fatherFCB = gson.fromJson(fatherFCBJson, FCB.class); 425 | 426 | return this.recursiveGetPath(fatherFCB) + "/" + fcb.filename; 427 | } 428 | 429 | /** 430 | * 格式化,清空所有数据 431 | */ 432 | public void format() { 433 | this.diskManager.format(); 434 | 435 | this.init(); 436 | } 437 | 438 | /** 439 | * 退出系统核心 440 | */ 441 | public void exit() { 442 | System.out.println("准备退出系统..."); 443 | // 写回磁盘 444 | this.diskManager.update(); 445 | System.out.println("退出"); 446 | } 447 | 448 | /** 449 | * 在当前目录下通过文件名找FCB 450 | * 451 | * @param filename 452 | * 文件名 453 | * @return 找到的FCB 454 | */ 455 | public FCB getFCB(String filename, FILE_TYPE type) { 456 | for (int i = 0; i < this.currentDir.length; i++) { 457 | if (this.currentDir[i] == null) { 458 | break; 459 | } 460 | if (this.currentDir[i].filename.equals(filename) 461 | && this.currentDir[i].type == type) { 462 | return this.currentDir[i]; 463 | } 464 | } 465 | return null; 466 | } 467 | 468 | /** 469 | * 更新所给的FCB 470 | * 471 | * @param fcb 472 | * 指定的FCB 473 | */ 474 | public void updateFCB(FCB fcb) { 475 | fcb.updatedDate = new Date(); 476 | 477 | Gson gson = new Gson(); 478 | 479 | this.diskManager.io.write(fcb.blockId, 1, gson.toJson(fcb)); 480 | } 481 | 482 | /** 483 | * 检查文件名,不允许一样的名字 484 | * 485 | * @param filename 486 | * 文件名 487 | * @param type 488 | * 文件类型 489 | * @return 布尔值 490 | */ 491 | private boolean checkFilename(String filename, FILE_TYPE type) { 492 | for (int i = 0; i < this.currentDir.length; i++) { 493 | if (this.currentDir[i] == null) { 494 | break; 495 | } 496 | if (this.currentDir[i].filename.equals(filename) 497 | && this.currentDir[i].type == type) { 498 | return false; 499 | } 500 | } 501 | return true; 502 | } 503 | 504 | /** 505 | * 计算当前文件夹下的文件个数 506 | */ 507 | public void countFiles() { 508 | for (this.fileCount = 0; this.fileCount < this.currentDir.length; this.fileCount++) { 509 | if (this.currentDir[this.fileCount] == null) { 510 | break; 511 | } 512 | } 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /src/model/sys/Block.java: -------------------------------------------------------------------------------- 1 | package model.sys; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.nio.ByteBuffer; 9 | import java.nio.channels.FileChannel; 10 | 11 | /** 12 | * 13 | * @author Tom Hu 14 | * 15 | */ 16 | public class Block { 17 | 18 | // Core data 19 | private int id; // id为该块的编号 20 | private ByteBuffer binData; // binData为该块存储内容的buffer 21 | private String filePath; // filePath为该块在物理磁盘上*.bin文件的存储路径 22 | 23 | /** 24 | * 构造函数 25 | * 26 | * @param id 27 | */ 28 | public Block(int id) { 29 | super(); 30 | this.id = id; 31 | this.binData = ByteBuffer.allocate(Config.BLOCK_SIZE); 32 | this.filePath = "disk/" + this.id + ".bin"; 33 | } 34 | 35 | /** 36 | * 将该块内容清空 37 | */ 38 | public void wipe() { 39 | this.binData.clear(); //并未真正清空数据,只是重置了标记 40 | } 41 | 42 | /** 43 | * 从物理磁盘上读入该块的内容,存入binData 44 | */ 45 | public void sync() { 46 | File binFile = new File(this.filePath); 47 | if (!binFile.exists()) { 48 | // System.out.println("Warning: \"" + this.filePath + "\" not found"); 49 | return; 50 | } 51 | 52 | FileChannel inputChannel; 53 | try { 54 | inputChannel = new FileInputStream(binFile).getChannel(); 55 | inputChannel.read(this.binData); 56 | this.binData.flip(); 57 | inputChannel.close(); 58 | } catch (FileNotFoundException e) { 59 | e.printStackTrace(); 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | 65 | /** 66 | * 将binData数据写回物理磁盘 67 | */ 68 | public void update() { 69 | File binFile = new File(this.filePath); 70 | 71 | FileChannel outputChannel; 72 | try { 73 | outputChannel = new FileOutputStream(binFile).getChannel(); 74 | this.binData.rewind(); 75 | outputChannel.write(this.binData); 76 | this.binData.flip(); 77 | outputChannel.close(); 78 | } catch (FileNotFoundException e) { 79 | e.printStackTrace(); 80 | } catch (IOException e) { 81 | e.printStackTrace(); 82 | } 83 | } 84 | 85 | /** 86 | * 获取当前块数据 87 | */ 88 | public ByteBuffer getBinData() { 89 | return this.binData; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/model/sys/Config.java: -------------------------------------------------------------------------------- 1 | package model.sys; 2 | 3 | import java.nio.charset.Charset; 4 | 5 | /** 6 | * 7 | * @author Tom Hu 8 | * 9 | */ 10 | 11 | public class Config { 12 | 13 | public static final int BLOCK_SIZE = 512; 14 | public static final int BLOCK_COUNT = 8000; 15 | 16 | public static final int SYS_BLOCK_COUNT = 256; 17 | 18 | public static final int FILE_MAX_BLOCKS = 10; 19 | 20 | public static enum FILE_TYPE { 21 | FILE, DIRECTORY 22 | }; 23 | 24 | public static final Charset CHARSET = Charset.forName("UTF-8"); 25 | } 26 | -------------------------------------------------------------------------------- /src/model/sys/FCB.java: -------------------------------------------------------------------------------- 1 | package model.sys; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | import model.sys.Config.FILE_TYPE; 7 | 8 | /** 9 | * 10 | * @author Tom Hu 11 | * 12 | */ 13 | public class FCB implements Serializable { 14 | 15 | private static final long serialVersionUID = -5262771405010721496L; 16 | 17 | public String filename; // 文件名 18 | public FILE_TYPE type; // FILE 或 DORECTORY 19 | public String address; // 文件地址 20 | public Date createdDate; 21 | public Date updatedDate; 22 | public int fatherBlockId; // 指向上一级FCB 23 | public int size; // 当前文件控制块所对应的数据区块个数,即占用空间 24 | public int dataStartBlockId; // 数据区Block的id 25 | public int blockId; // 当前FCB所在Block的id 26 | // public int realSize = 0; // FCB对应文件的实际大小,其中文件夹大小等于目录文件大小加上其目录下所有文件的大小 27 | 28 | // Constructor 29 | public FCB(String filename, int fatherBlockId, FILE_TYPE type, int size, 30 | int dataStartBlockId, int blockId) { 31 | this.filename = filename; 32 | this.fatherBlockId = fatherBlockId; 33 | this.type = type; 34 | this.size = size; 35 | this.createdDate = new Date(); 36 | this.updatedDate = (Date) this.createdDate.clone(); 37 | this.dataStartBlockId = dataStartBlockId; 38 | this.blockId = blockId; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/view/Config.java: -------------------------------------------------------------------------------- 1 | package view; 2 | 3 | public class Config { 4 | 5 | // Window 6 | public static final int WINDOW_WIDTH = 800; 7 | public static final int WINDOW_HEIGHT = 600; 8 | 9 | // File icon 10 | public static final int FILE_ICON_SIZE = 80; 11 | public static final int FILE_ICON_PANEL_SIZE = 100; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/view/DocumentIconPanel.java: -------------------------------------------------------------------------------- 1 | package view; 2 | 3 | import java.awt.Color; 4 | import java.awt.Dimension; 5 | import java.awt.Image; 6 | 7 | import javax.swing.BorderFactory; 8 | import javax.swing.BoxLayout; 9 | import javax.swing.ImageIcon; 10 | import javax.swing.JLabel; 11 | import javax.swing.JPanel; 12 | 13 | import model.sys.Config.FILE_TYPE; 14 | 15 | public class DocumentIconPanel extends JPanel { 16 | 17 | /** 18 | * 19 | */ 20 | private static final long serialVersionUID = 1952213928294715915L; 21 | private JLabel mainLabel; 22 | private String filename; 23 | private FILE_TYPE type; 24 | 25 | // Constructor 26 | public DocumentIconPanel(FILE_TYPE type, String filename) { 27 | super(); 28 | // TODO Auto-generated constructor stub 29 | 30 | this.filename = filename; 31 | this.type = type; 32 | 33 | this.setSize(new Dimension(Config.FILE_ICON_PANEL_SIZE, 34 | Config.FILE_ICON_PANEL_SIZE)); 35 | this.setPreferredSize(new Dimension(Config.FILE_ICON_PANEL_SIZE, 36 | Config.FILE_ICON_PANEL_SIZE)); 37 | 38 | // Set layout 39 | this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 40 | 41 | // Set background color 42 | this.setBackground(Color.WHITE); 43 | 44 | // UI Methods 45 | this.configureDocumentIcon(type, filename); 46 | // this.configureFileNameLabel(); 47 | } 48 | 49 | // UI Methods 50 | private void configureDocumentIcon(FILE_TYPE type, String filename) { 51 | 52 | // initialize icon 53 | ImageIcon icon = null; 54 | switch (type) { 55 | case FILE: 56 | icon = new ImageIcon("resource/DocumentIcon.png"); 57 | break; 58 | case DIRECTORY: 59 | icon = new ImageIcon("resource/FolderIcon.png"); 60 | break; 61 | default: 62 | System.out.println("error when load icon"); 63 | break; 64 | } 65 | 66 | // Scale image 67 | icon.setImage(icon.getImage().getScaledInstance(Config.FILE_ICON_SIZE, 68 | Config.FILE_ICON_SIZE, Image.SCALE_DEFAULT)); 69 | 70 | this.mainLabel = new JLabel(filename, icon, JLabel.CENTER); 71 | this.mainLabel.setBorder(BorderFactory.createEmptyBorder(0, 72 | (Config.FILE_ICON_PANEL_SIZE - Config.FILE_ICON_SIZE) / 2, 0, 73 | (Config.FILE_ICON_PANEL_SIZE - Config.FILE_ICON_SIZE) / 2)); 74 | this.mainLabel.setVerticalTextPosition(JLabel.BOTTOM); 75 | this.mainLabel.setHorizontalTextPosition(JLabel.CENTER); 76 | 77 | // Add to fileIconPanel 78 | this.add(this.mainLabel); 79 | } 80 | 81 | public void setSelected(boolean selected) { 82 | if (selected) { 83 | this.setBackground(Color.BLUE); 84 | this.mainLabel.setForeground(Color.WHITE); 85 | } else { 86 | this.setBackground(Color.WHITE); 87 | this.mainLabel.setForeground(Color.BLACK); 88 | } 89 | } 90 | 91 | public String getFilename() { 92 | return filename; 93 | } 94 | 95 | public void setFilename(String filename) { 96 | this.filename = filename; 97 | } 98 | 99 | public FILE_TYPE getType() { 100 | return type; 101 | } 102 | 103 | public void setType(FILE_TYPE type) { 104 | this.type = type; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/view/EditView.java: -------------------------------------------------------------------------------- 1 | package view; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Color; 5 | import java.awt.event.KeyEvent; 6 | 7 | import javax.swing.JFrame; 8 | import javax.swing.JMenu; 9 | import javax.swing.JMenuBar; 10 | import javax.swing.JMenuItem; 11 | import javax.swing.JTextPane; 12 | import javax.swing.event.DocumentEvent; 13 | import javax.swing.event.DocumentListener; 14 | 15 | import model.sys.FCB; 16 | 17 | // TODO - layout views 18 | public class EditView extends JFrame implements DocumentListener { 19 | 20 | private static final long serialVersionUID = 5359647733388619559L; 21 | private JTextPane textPane; 22 | 23 | private FCB dataFCB; 24 | 25 | public boolean saveOnExit = true; 26 | public boolean edited = false; 27 | 28 | public EditView(FCB fcb, String content) { 29 | super(); 30 | 31 | // initialize 32 | this.dataFCB = fcb; 33 | this.textPane = new JTextPane(); 34 | 35 | this.configureMenuBar(); 36 | this.configureTextPane(content); 37 | 38 | // Main View 39 | this.configureJFrame(); 40 | } 41 | 42 | // UI Method 43 | private void configureJFrame() { 44 | this.setTitle(this.dataFCB.filename + " - Edit");// Set title 45 | this.setSize(Config.WINDOW_WIDTH, Config.WINDOW_HEIGHT);// Set size of 46 | // window 47 | this.setResizable(false);// Can't change the size 48 | this.setLocationRelativeTo(null);// Set the position of the window - 49 | // Screen's Center 50 | this.setBackground(Color.WHITE); 51 | 52 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 53 | } 54 | 55 | private void configureMenuBar() { 56 | // Components 57 | JMenuBar menuBar; 58 | JMenu fileMenu; 59 | JMenu helpMenu; 60 | JMenuItem helpMenuItem; 61 | 62 | // Create the Menu Bar 63 | menuBar = new JMenuBar(); 64 | 65 | // Build File Menu 66 | fileMenu = new JMenu("Edit"); 67 | fileMenu.setMnemonic(KeyEvent.VK_F); 68 | fileMenu.setEnabled(false); 69 | 70 | // Build About Menu 71 | helpMenu = new JMenu("Help"); 72 | helpMenu.setMnemonic(KeyEvent.VK_H); 73 | 74 | // Add Menu Items to Menu "About" 75 | helpMenuItem = new JMenuItem("Help", KeyEvent.VK_H); 76 | helpMenu.add(helpMenuItem); 77 | 78 | // Add Menus "File" to Menu Bar 79 | menuBar.add(fileMenu); 80 | menuBar.add(helpMenu); 81 | 82 | // Add Components 83 | this.setJMenuBar(menuBar); 84 | } 85 | 86 | private void configureTextPane(String content) { 87 | this.textPane.setText(content); 88 | this.textPane.getDocument().addDocumentListener(this); 89 | this.add(this.textPane, BorderLayout.CENTER); 90 | } 91 | 92 | /** 93 | * 获取当前TextPane的内容 94 | * 95 | * @return TextPane中所有文字内容 96 | */ 97 | public String getContent() { 98 | return this.textPane.getText(); 99 | } 100 | 101 | public FCB getDataFCB() { 102 | return dataFCB; 103 | } 104 | 105 | // Document Listener 106 | @Override 107 | public void insertUpdate(DocumentEvent e) { 108 | this.edited = true; 109 | 110 | // 改变窗口标题 111 | this.setTitle(this.dataFCB.filename + " - Edited"); 112 | } 113 | 114 | @Override 115 | public void removeUpdate(DocumentEvent e) { 116 | this.edited = true; 117 | 118 | // 改变窗口标题 119 | this.setTitle(this.dataFCB.filename + " - Edited"); 120 | } 121 | 122 | @Override 123 | public void changedUpdate(DocumentEvent e) { 124 | 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/view/MainView.java: -------------------------------------------------------------------------------- 1 | package view; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Color; 5 | import java.awt.Component; 6 | import java.awt.Dimension; 7 | import java.awt.FlowLayout; 8 | import java.awt.event.ActionListener; 9 | import java.awt.event.ComponentEvent; 10 | import java.awt.event.ComponentListener; 11 | import java.awt.event.KeyEvent; 12 | import java.awt.event.MouseListener; 13 | 14 | import javax.swing.BorderFactory; 15 | import javax.swing.BoxLayout; 16 | import javax.swing.JButton; 17 | import javax.swing.JFrame; 18 | import javax.swing.JMenu; 19 | import javax.swing.JMenuBar; 20 | import javax.swing.JMenuItem; 21 | import javax.swing.JPanel; 22 | import javax.swing.JScrollPane; 23 | import javax.swing.JTextField; 24 | import javax.swing.ScrollPaneConstants; 25 | 26 | import model.sys.FCB; 27 | 28 | /** 29 | * 30 | * @author Tom Hu 31 | * 32 | */ 33 | public class MainView extends JFrame { 34 | 35 | private static final long serialVersionUID = 6313156717813295316L; 36 | 37 | private JButton backButton; 38 | public JTextField addressTextField; 39 | public JButton goButton; 40 | public JPanel contentPanel; 41 | 42 | // Constructor 43 | public MainView(FCB[] fcbDir) { 44 | super(); 45 | 46 | // initialize 47 | this.contentPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); 48 | this.backButton = new JButton("Back"); 49 | this.addressTextField = new JTextField("test"); 50 | this.goButton = new JButton("Go"); 51 | 52 | // UI Methods 53 | this.configureMenuBar(); 54 | this.configureToolPanel(); 55 | this.configureContentScrollPane(fcbDir); 56 | 57 | // Main View 58 | this.configureJFrame(); 59 | } 60 | 61 | // UI Method 62 | private void configureJFrame() { 63 | this.setTitle("File System Simulator");// Set title 64 | this.setSize(Config.WINDOW_WIDTH, Config.WINDOW_HEIGHT);// Set size of 65 | // window 66 | this.setResizable(false);// Can't change the size 67 | this.setLocationRelativeTo(null);// Set the position of the window - 68 | // Screen's Center 69 | this.setBackground(Color.WHITE); 70 | } 71 | 72 | private void configureMenuBar() { 73 | // Components 74 | JMenuBar menuBar; 75 | JMenu fileSystemMenu; 76 | JMenu aboutMenu; 77 | JMenuItem aboutMenuItem; 78 | 79 | // Create the Menu Bar 80 | menuBar = new JMenuBar(); 81 | 82 | // Build Elevator Menu 83 | fileSystemMenu = new JMenu("File System"); 84 | fileSystemMenu.setMnemonic(KeyEvent.VK_F); 85 | fileSystemMenu.setEnabled(false); 86 | 87 | // Build About Menu 88 | aboutMenu = new JMenu("About"); 89 | aboutMenu.setMnemonic(KeyEvent.VK_A); 90 | 91 | // Add Menu Items to Menu "About" 92 | aboutMenuItem = new JMenuItem("About", KeyEvent.VK_A); 93 | aboutMenu.add(aboutMenuItem); 94 | 95 | // Add Menus "File" and "Help" to Menu Bar 96 | menuBar.add(fileSystemMenu); 97 | menuBar.add(aboutMenu); 98 | 99 | // Add Components 100 | this.setJMenuBar(menuBar); 101 | } 102 | 103 | private void configureToolPanel() { 104 | // initialize toolPanel 105 | JPanel toolPanel = new JPanel(); 106 | 107 | // for debug 108 | toolPanel.setBackground(Color.BLACK); 109 | 110 | // Set Layout 111 | toolPanel.setLayout(new BoxLayout(toolPanel, BoxLayout.X_AXIS)); 112 | toolPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20)); 113 | 114 | // Add to toolPanel 115 | toolPanel.add(this.backButton); 116 | toolPanel.add(this.addressTextField); 117 | toolPanel.add(this.goButton); 118 | 119 | // Add to mainView 120 | this.add(toolPanel, BorderLayout.PAGE_START); 121 | } 122 | 123 | private void configureContentScrollPane(FCB[] fcbDir) { 124 | // Clear all children components 125 | this.contentPanel.removeAll(); 126 | 127 | // Set background color 128 | this.contentPanel.setBackground(Color.WHITE); 129 | 130 | // Add component listener for contentPanel 131 | this.contentPanel.addComponentListener(new ComponentListener() { 132 | 133 | @Override 134 | public void componentResized(ComponentEvent e) { 135 | System.out.println("resize"); 136 | 137 | Dimension d = MainView.this.contentPanel.getPreferredSize(); 138 | int con = MainView.this.contentPanel.getComponents().length; 139 | int col = Config.WINDOW_WIDTH 140 | / (Config.FILE_ICON_PANEL_SIZE + 5); 141 | int row = con / col + 1; 142 | int newHeight = row * (Config.FILE_ICON_PANEL_SIZE + 5) + 5; 143 | d.height = newHeight; 144 | d.width = Config.WINDOW_WIDTH; 145 | MainView.this.contentPanel.setPreferredSize(d); 146 | 147 | } 148 | 149 | @Override 150 | public void componentMoved(ComponentEvent e) { 151 | } 152 | 153 | @Override 154 | public void componentShown(ComponentEvent e) { 155 | } 156 | 157 | @Override 158 | public void componentHidden(ComponentEvent e) { 159 | } 160 | }); 161 | 162 | // For test 163 | for (int i = 0; i < fcbDir.length; i++) { 164 | if (fcbDir[i] == null) { 165 | break; 166 | } 167 | 168 | DocumentIconPanel t = new DocumentIconPanel(fcbDir[i].type, 169 | fcbDir[i].filename); 170 | 171 | this.contentPanel.add(t); 172 | } 173 | 174 | // initialize content scroll pane 175 | System.out.println("initialize contentScrollPane"); 176 | JScrollPane contentScrollPane = new JScrollPane(this.contentPanel); 177 | contentScrollPane 178 | .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); 179 | 180 | // Add to mainView 181 | System.out.println("add to mainView"); 182 | this.add(contentScrollPane, BorderLayout.CENTER); 183 | } 184 | 185 | // Show view 186 | public void showView() { 187 | System.out.println("show view"); 188 | 189 | // Show View 190 | this.setVisible(true); 191 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 192 | } 193 | 194 | // Actions 195 | public void addDocument(DocumentIconPanel documentIconPanel) { 196 | // For test 197 | this.contentPanel.add(documentIconPanel); 198 | this.contentPanel.revalidate(); 199 | } 200 | 201 | public void addRightClickListener(MouseListener rightClickListener) { 202 | this.contentPanel.addMouseListener(rightClickListener); 203 | } 204 | 205 | public void addBackButtonActionListener(ActionListener actionListener) { 206 | this.backButton.addActionListener(actionListener); 207 | } 208 | 209 | public void addGoButtonActionListener(ActionListener actionListener) { 210 | this.goButton.addActionListener(actionListener); 211 | } 212 | 213 | public void addDocumentIconPanelMouseListener( 214 | MouseListener documentIconPanelMouseListener) { 215 | System.out.println("add document listener"); 216 | for (Component item : this.contentPanel.getComponents()) { 217 | ((DocumentIconPanel) item) 218 | .addMouseListener(documentIconPanelMouseListener); 219 | } 220 | } 221 | 222 | public void deselectDocuments() { 223 | for (Component item : this.contentPanel.getComponents()) { 224 | ((DocumentIconPanel) item).setSelected(false); 225 | } 226 | } 227 | 228 | public void reloadContent(FCB[] fcbDir) { 229 | // 移除所有components 230 | this.contentPanel.removeAll(); 231 | 232 | // 重新添加 233 | for (int i = 0; i < fcbDir.length; i++) { 234 | if (fcbDir[i] == null) { 235 | break; 236 | } 237 | 238 | DocumentIconPanel t = new DocumentIconPanel(fcbDir[i].type, 239 | fcbDir[i].filename); 240 | 241 | this.contentPanel.add(t); 242 | } 243 | 244 | this.contentPanel.repaint(); 245 | this.contentPanel.revalidate(); 246 | } 247 | } 248 | --------------------------------------------------------------------------------